通过引用Aegonis的work 1和work 2,我也得到了H.264流,但颜色不正确。我正在使用HTC Butterfly进行开发。这是我的代码的一部分:
照相机:
parameters.setPreviewSize(width, height);
parameters.setPreviewFormat(ImageFormat.YV12);
parameters.setPreviewFrameRate(frameRate);
MediaCodec:
mediaCodec = MediaCodec.createEncoderByType("video/avc");
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 500000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
使用 COLOR_FormatYUV420Planar 时,错误显示“[OMX.qcom.video.encoder.avc]不支持颜色格式19”,所以我只能使用“ COLOR_FormatYUV420SemiPlanar ”。有谁知道没有支持的原因?
通过使用:
得到它int colorFormat = 0;
MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
for (int i = 0; i < capabilities.colorFormats.length && colorFormat == 0; i++) {
int format = capabilities.colorFormats[i];
Log.e(TAG, "Using color format " + format);
}
我们可以有颜色格式 21 (COLOR_FormatYUV420SemiPlanar)和 2130708361 (没有相应的格式),我认为格式会改变取决于设备。
然后,我尝试了work 1和work 2中的建议提供的颜色转换:
public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) {
/*
* COLOR_TI_FormatYUV420PackedSemiPlanar is NV12
* We convert by putting the corresponding U and V bytes together (interleaved).
*/
final int frameSize = width * height;
final int qFrameSize = frameSize/4;
System.arraycopy(input, 0, output, 0, frameSize); // Y
for (int i = 0; i < qFrameSize; i++) {
output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U)
output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V)
}
return output;
}
public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) {
/*
* COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed.
* So we just have to reverse U and V.
*/
final int frameSize = width * height;
final int qFrameSize = frameSize/4;
System.arraycopy(input, 0, output, 0, frameSize); // Y
System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V)
System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U)
return output;
}
public static byte[] swapYV12toI420(byte[] yv12bytes, int width, int height) {
byte[] i420bytes = new byte[yv12bytes.length];
for (int i = 0; i < width*height; i++)
i420bytes[i] = yv12bytes[i];
for (int i = width*height; i < width*height + (width/2*height/2); i++)
i420bytes[i] = yv12bytes[i + (width/2*height/2)];
for (int i = width*height + (width/2*height/2); i < width*height + 2*(width/2*height/2); i++)
i420bytes[i] = yv12bytes[i - (width/2*height/2)];
return i420bytes;
}
显然, YV12toYUV420PackedSemiPlanar 的颜色转换效果优于其他两种。它相对更好,但与真实颜色相比仍然看起来不同。我的代码有问题吗?任何评论将不胜感激。
答案 0 :(得分:3)
知道了,现在颜色看起来不错,测试基于HTC Butterfly。 将分辨率设置为320x240时,颜色转换应如下所示:
System.arraycopy(input, 0, output, 0, frameSize);
for (int i = 0; i < (qFrameSize); i++) {
output[frameSize + i*2] = (input[frameSize + qFrameSize + i - 32 - 320]);
output[frameSize + i*2 + 1] = (input[frameSize + i - 32 - 320]);
}
分辨率为640x480及以上,
System.arraycopy(input, 0, output, 0, frameSize);
for (int i = 0; i < (qFrameSize); i++) {
output[frameSize + i*2] = (input[frameSize + qFrameSize + i]);
output[frameSize + i*2 + 1] = (input[frameSize + i]);
}
对于帧速率问题,我们可以使用getSupportedPreviewFpsRange()来检查设备支持的帧速率范围:
List<int[]> fpsRange = parameters.getSupportedPreviewFpsRange();
for (int[] temp3 : fpsRange) {
System.out.println(Arrays.toString(temp3));}
播放编码的H.264 ES时,以下设置正常,
parameters.setPreviewFpsRange(29000, 30000);
//parameters.setPreviewFpsRange(4000,60000);//this one results fast playback when I use the FRONT CAMERA
答案 1 :(得分:2)
在阅读this discussion之后,事实证明,编码帧的方式更为通用
各种分辨率的方法是在将帧发送到MediaCodec
之前将色度平面对齐2048字节。这对于QualComm
(OMX.qcom.video.encoder.avc
)编码器来说是实际的,我相信HTC Butterfly有,但仍然不适用于所有分辨率。根据输出视频,720x480
和176x144
仍然会使色度平面未对齐。另外,请避免尺寸不能除以16的分辨率。
转型非常简单:
int padding = 0;
if (mediaCodecInfo.getName().contains("OMX.qcom")) {
padding = (width * height) % 2048;
}
byte[] inputFrameBuffer = new byte[frame.length];
byte[] inputFrameBufferWithPadding = new byte[padding + frame.length];
ColorHelper.NV21toNV12(frame, inputFrameBuffer, width, height);
# copy Y plane
System.arraycopy(inputFrameBuffer, 0, inputFrameBufferWithPadding, 0, inputFrameBuffer.length);
int offset = width * height;
# copy U and V planes aligned by <padding> boundary
System.arraycopy(inputFrameBuffer, offset, inputFrameBufferWithPadding, offset + padding, inputFrameBuffer.length - offset);