2017-09-25 32 views
1

Android 기기에서 카메라 애플리케이션을 구현하고 있습니다. 현재 Camera2 API와 ImageReader를 사용하여 이미지 데이터를 YUV_420_888 형식으로 가져 왔지만 이러한 데이터를 MediaCodec에 정확히 쓰는 방법을 모르겠습니다.YUV_420_888 및 MediaCodec과 함께 ImageReader를 올바르게 사용하여 비디오를 h264 형식으로 인코딩하는 방법은 무엇입니까?

여기 내 질문은 :

YUV_420_888 무엇
  1. ?

가 오른쪽 YUV420P, YUV420PP, YUV420SPYUV420PSP과 같은 예 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, ...?

  1. COLOR_FormatYUV420Flexible은 무엇입니까?

COLOR_FormatYUV420Flexible 형식은 YUV420 제품군에 속한 형식 일 수 있으므로 모호합니다. 맞습니까? MediaCodec 객체의 KEY_COLOR_FORMAT 옵션을 COLOR_FormatYUV420Flexible으로 설정하면 MediaCodec 객체에 어떤 형식 (YUV420P, YUV420SP ...?)의 데이터를 입력해야합니까?

  1. 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에 기록되어야합니다. 그러나 나는 아직도 그것을하는 방법을 모른다.

답변

0

맞아요, YUV_420_888은 다른 YUV 420 형식을 래핑 할 수있는 형식입니다. 이 스펙은 U 평면과 V 평면의 배열은 규정되지 않았지만 특정 제한이 있다는 것을 신중하게 설명합니다. 예 : U 평면에 픽셀 보폭이 2 인 경우 V에도 동일하게 적용됩니다 (기본 바이트 버퍼는 NV21 일 수 있음).

COLOR_FormatYUV420FlexibleYUV_420_888의 동의어이지만 각각 MediaCodec 및 ImageFormat이라는 다른 클래스에 속합니다.

사양 설명 : LOLLIPOP_MR1부터 0 버퍼 : 2 :

모든 비디오 코덱은 유연한 YUV 4를 지원합니다.

COLOR_FormatSurface는 MediaCodec에 대해 최상의 성능을 제공 할 수있는 불투명 한 형식이지만 가격이 책정됩니다. 즉, 내용을 직접 읽거나 조작 할 수 없습니다. MediaCodec에 전달되는 데이터를 조작해야하는 경우 ImageReader를 사용하는 것이 좋습니다. ByteBuffer보다 더 효율적이든간에, 당신이하는 일과 어떻게하는지에 달려 있습니다. API 24+의 경우 camera2와 MediaCodec 모두 C++로 작업 할 수 있습니다.

MediaCodec에 대한 자세한 내용은 http://www.bigflake.com/mediacodec입니다. 2637 인코딩의 full example을 참조합니다.

+0

감사합니다. COLOR_FormatSurface로 비디오를 인코딩하려고했지만 성능이 좋지 않습니다 (내 EDIT1 섹션 참조). COLOR_FormatYUV420Flexible (또는 다른 YUV 형식)을 사용하여 비디오 인코딩에 대한 예제가 있습니까? – user3032481

+0

올바르게 이해하면 ** imageIUV **가 카메라 프레임의 편집 된 복사본이되도록 ** onImageAvailable() **에 더 많은 코드를 추가하려고합니다. 그렇지 않으면 단순히 ** image **에서 다른 ByteBuffer로 비행기를 복사 할 이유가 없기 때문입니다. –

+0

여하튼 렌더링 스크립트를 사용하더라도 표시 절차가 차선책입니다. YUV420 입력을 즉시 RGB로 변환하는 셰이더를 사용하면 OpenGL을 직접 사용하는 것이 훨씬 빠릅니다. –