Android 기기에서 카메라 애플리케이션을 구현하고 있습니다. 현재 Camera2 API와 ImageReader를 사용하여 이미지 데이터를 YUV_420_888
형식으로 가져 왔지만 이러한 데이터를 MediaCodec에 정확히 쓰는 방법을 모르겠습니다.YUV_420_888 및 MediaCodec과 함께 ImageReader를 올바르게 사용하여 비디오를 h264 형식으로 인코딩하는 방법은 무엇입니까?
여기 내 질문은 :
이YUV_420_888
무엇
- ?
가 오른쪽 YUV420P
, YUV420PP
, YUV420SP
YUV420PSP
과 같은 예 YUV420
가족에 속하는 임의의 형식 일 수 있기 때문에 형식 YUV_420_888
모호?
이미지의 세 평면 (# 0, # 1, # 2)에 액세스하면이 이미지의 Y (# 0), U (# 1), V (# 2) 값을 얻을 수 있습니다. 그러나 이러한 값의 배열은 다른 장치에서 동일하지 않을 수 있습니다. 예를 들어, YUV_420_888
이 실제로 YUV420P
을 의미하면, 평면 # 1과 평면 # 2의 크기는 평면 # 0의 크기의 1/4입니다. YUV_420_888
이 실제로 YUV420SP
을 의미하면 Plane # 1과 Plane # 2의 크기는 Plane # 0의 크기의 절반입니다 (Plane # 1과 Plane # 2는 각각 U, V 값을가집니다).
이미지의 세 평면에서이 데이터를 MediaCodec에 쓰려면 변환해야하는 형식은 무엇입니까? YUV420, NV21, NV12, ...?
COLOR_FormatYUV420Flexible
은 무엇입니까?
COLOR_FormatYUV420Flexible
형식은 YUV420
제품군에 속한 형식 일 수 있으므로 모호합니다. 맞습니까? MediaCodec 객체의 KEY_COLOR_FORMAT
옵션을 COLOR_FormatYUV420Flexible
으로 설정하면 MediaCodec 객체에 어떤 형식 (YUV420P, YUV420SP ...?)의 데이터를 입력해야합니까?
COLOR_FormatSurface
은 어떨까요? 내가 MediaCodec을 알고
내가 COLOR_FormatSurface
에 MediaCodec 객체의 KEY_COLOR_FORMAT
옵션을 설정하면 사용할 수 있습니다 자신의 표면을 가지고 있습니다. Camera2 API를 사용하면 MediaCodec 객체에 직접 데이터를 쓸 필요가 없습니다. 출력 버퍼를 비울 수 있습니다.
그러나 카메라에서 이미지를 변경해야합니다. 예를 들어, 다른 그림을 그리고 그 위에 텍스트를 쓰거나 POP (Picture of Picture)로 다른 비디오를 삽입합니다.
카메라에서 이미지를 읽으려면 ImageReader를 사용하고 다시 그리는 경우 MediaCodec 표면에 새 데이터를 쓰고 배출하십시오. 그렇게하는 방법?
EDIT1
제가 COLOR_FormatSurface
및 RenderScript를 사용하여 기능을 구현 하였다.
public static Bitmap YUV_420_888_toRGBIntrinsics(Context context, int width, int height, byte[] yuv) {
RenderScript rs = RenderScript.create(context);
ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs));
Type.Builder yuvType = new Type.Builder(rs, Element.U8(rs)).setX(yuv.length);
Allocation in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT);
Type.Builder rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height);
Allocation out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT);
Bitmap bmpOut = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
in.copyFromUnchecked(yuv);
yuvToRgbIntrinsic.setInput(in);
yuvToRgbIntrinsic.forEach(out);
out.copyTo(bmpOut);
return bmpOut;
}
MediaCodec :
onImageAvailable
방법 :
public void onImageAvailable(ImageReader imageReader) {
try {
try (Image image = imageReader.acquireLatestImage()) {
if (image == null) {
return;
}
Image.Plane[] planes = image.getPlanes();
if (planes.length >= 3) {
ByteBuffer bufferY = planes[0].getBuffer();
ByteBuffer bufferU = planes[1].getBuffer();
ByteBuffer bufferV = planes[2].getBuffer();
int lengthY = bufferY.remaining();
int lengthU = bufferU.remaining();
int lengthV = bufferV.remaining();
byte[] dataYUV = new byte[lengthY + lengthU + lengthV];
bufferY.get(dataYUV, 0, lengthY);
bufferU.get(dataYUV, lengthY, lengthU);
bufferV.get(dataYUV, lengthY + lengthU, lengthV);
imageYUV = dataYUV;
}
}
} catch (final Exception ex) {
}
}
변환 YUV_420_888 RGB 여기 내 코드입니다
:mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
...
mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
...
surface = mediaCodec.createInputSurface(); // This surface is not used in Camera APIv2. Camera APIv2 uses ImageReader's surface.
그리고 athother 스레드
이 방법은 효과가 있지만 성능은 좋지 않습니다. 30fps 비디오 파일을 출력 할 수 없습니다 (~ 12fps 만). 아마도 COLOR_FormatSurface
과 인코딩을 위해 표면의 캔버스를 사용해서는 안됩니다. 계산 된 YUV 데이터는 변환을 수행하는 표면없이 직접 mediaCodec에 기록되어야합니다. 그러나 나는 아직도 그것을하는 방법을 모른다.
감사합니다. COLOR_FormatSurface로 비디오를 인코딩하려고했지만 성능이 좋지 않습니다 (내 EDIT1 섹션 참조). COLOR_FormatYUV420Flexible (또는 다른 YUV 형식)을 사용하여 비디오 인코딩에 대한 예제가 있습니까? – user3032481
올바르게 이해하면 ** imageIUV **가 카메라 프레임의 편집 된 복사본이되도록 ** onImageAvailable() **에 더 많은 코드를 추가하려고합니다. 그렇지 않으면 단순히 ** image **에서 다른 ByteBuffer로 비행기를 복사 할 이유가 없기 때문입니다. –
여하튼 렌더링 스크립트를 사용하더라도 표시 절차가 차선책입니다. YUV420 입력을 즉시 RGB로 변환하는 셰이더를 사용하면 OpenGL을 직접 사용하는 것이 훨씬 빠릅니다. –