2017-12-11 21 views
0

나는 표면이 렌더링 할 MediaCodec 디코더에 연결된 ImageReader를 사용하고 있습니다.AImageReader에서 가져온 이미지의 원시 데이터 가져 오기

 AMediaCodec *videoDecoder = nullptr; 
     ANativeWindow* surface = nullptr; 

     AImageReader* imageReader = nullptr; 
     AImageReader_ImageListener* imageListener = nullptr; 

     if ((videoDecoder = AMediaCodec_createDecoderByType(mime))) 
     { 
      if (AImageReader_new(mWidth, mHeight, AIMAGE_FORMAT_YUV_420_888, 2, &imageReader) == AMEDIA_OK) 
      { 
       if (AImageReader_getWindow(imageReader, &surface) == AMEDIA_OK) 
       { 
        if (AMediaCodec_configure(videoDecoder, mediaFormat, surface, NULL, 0) == AMEDIA_OK) 
        { 
         int32_t outputFormat{}; 
         AMediaFormat_getInt32(AMediaCodec_getOutputFormat(videoDecoder), AMEDIAFORMAT_KEY_COLOR_FORMAT, &outputFormat); 

         imageListener = new AImageReader_ImageListener(); 
         imageListener->onImageAvailable = &onImageAvailableCallback; 
         AImageReader_setImageListener(imageReader, imageListener); 

         if (AMediaCodec_start(videoDecoder) == AMEDIA_OK) 
         { 
          configCompleted = true; 
         } 
         else 
         { 
          TRACE("ImporterMP4Android", 0, "Failed to Start Video Decoder"); 
         } 
        } 
        else 
        { 
         TRACE("ImporterMP4Android", 0, "Failed to Configure Video Decoder"); 
        } 
       } 
       else 
       { 
        TRACE("ImporterMP4Android", 0, "Failed to Fetch Surface owned by the ImageReader"); 
       } 
      } 
      else 
      { 
       TRACE("ImporterMP4Android", 0, "Failed to Create ImageReader"); 
      } 
     } 
     else 
     { 
      TRACE("ImporterMP4Android", 0, "Failed to Create Decoder"); 
     } 

onImageAvailableCallback이 기압과 같습니다

TODO 주석에 표시된 바와 같이
void onImageAvailableCallback(void *context, AImageReader *reader) 
{ 
    int32_t format; 
    media_status_t status = AImageReader_getFormat (reader, &format); 

    AImage *image; 
    status = AImageReader_acquireLatestImage(reader, &image); 


    status = AImage_getFormat(image, &format); 

    // TODO: copy *raw data somewhere for downstream processing 

    AImage_delete(image); 
} 

, 내가 추가 처리를 위해 ImageReader에서 획득 한 Image원시 데이터를 복사 할. Image 클래스에서 제공하는 인터페이스를 사용하면 평면 수를 쿼리하고 개별 평면 데이터를 가져올 수 있지만 전체 프레임을 한 번에 잡아내는 데 관심이 있습니다. 내가 이것을 어떻게 성취 할 수 있을지에 대한 제안? 요컨대

는, I는 추가 처리를 위해 YUV420NV21 형식으로 그 전체하는 ImageReader로부터 디코딩 된 비디오 프레임들을 ImageReader를 소유 한 표면에 렌더링하고, 궁극적 잡아에 원하는 MediaCodec 비디오 디코더를 사용하고있다.

답변

0

세 개의 YUV 평면이 모두 필요한 경우 원하는 YUV 평면에 하나씩 복사해야합니다. 인접하지 않을 것으로 예상 할 수는 없지만 3 개의 평면 모두를 임의로 멀리 떨어 뜨릴 수 있습니다. 기억. 이 (안된)와 같은 무언가가 꽤 많이 필요한 것 무엇 :

uint8_t *buf = new uint8_t[width*height + 2*(width+1)/2*(height+1)/2]; 
int32_t yPixelStride, yRowStride; 
uint8_t *yPtr; 
int yLength; 
AImage_getPlanePixelStride(image, 0, &yPixelStride); 
AImage_getPlaneRowStride(image, 0, &yRowStride); 
AImage_getPlaneData(image, 0, &yPtr, &yLength); 
if (yPixelStride == 1) { 
    // All pixels in a row are contiguous; copy one line at a time. 
    for (int y = 0; y < height; y++) 
     memcpy(buf + y*width, yPtr + y*yRowStride, width); 
} else { 
    // Highly improbable, but not disallowed by the API. In this case 
    // individual pixels aren't stored consecutively but sparsely with 
    // other data inbetween each pixel. 
    for (int y = 0; y < height; y++) 
     for (int x = 0; x < width; x++) 
      buf[y*width+x] = yPtr[y*yRowStride + x*yPixelStride]; 
} 
int32_t cbPixelStride, crPixelStride, cbRowStride, crRowStride; 
uint8_t *cbPtr, *crPtr; 
int cbLength, crLength; 
AImage_getPlanePixelStride(image, 1, &cbPixelStride); 
AImage_getPlaneRowStride(image, 1, &cbRowStride); 
AImage_getPlaneData(image, 1, &cbPtr, &cbLength); 
AImage_getPlanePixelStride(image, 2, &crPixelStride); 
AImage_getPlaneRowStride(image, 2, &crRowStride); 
AImage_getPlaneData(image, 2, &crPtr, &crLength); 
uint8_t *chromaBuf = &buf[width*height]; 
int chromaBufStride = 2*((width + 1)/2); 
if (cbPixelStride == 2 && crPixelStride == 2 && 
    cbRowStride == crRowStride && crPtr == cbPtr + 1) { 
    // The actual cb/cr planes happened to be laid out in 
    // exact NV21 form in memory; copy them as is 
    for (int y = 0; y < (height + 1)/2; y++) 
     memcpy(chromabuf + y*chromaBufStride, crPtr + y*crRowStride, chromaBufStride); 
} else if (cbPixelStride == 2 && crPixelStride == 2 && 
      cbRowStride == crRowStride && crPtr == cbPtr + 1) { 
    // The cb/cr planes happened to be laid out in exact NV12 form 
    // in memory; if the destination API can use NV12 in addition to 
    // NV21 do something similar as above, but using cbPtr instead of crPtr. 
    // If not, remove this clause and use the generic code below. 
} else { 
    if (cbPixelStride == 1 && crPixelStride == 1) { 
     // Continuous cb/cr planes; the input data was I420/YV12 or similar; 
     // copy it into NV21 form 
     for (int y = 0; y < (height + 1)/2; y++) { 
      for (int x = 0; x < (width + 1)/2; x++) { 
       chromaBuf[y*chromaBufStride + 2*x + 0] = crPtr[y*crRowStride + x]; 
       chromaBuf[y*chromaBufStride + 2*x + 1] = cbPtr[y*cbRowStride + x]; 
      } 
     } 
    } else { 
     // Generic data copying into NV21 
     for (int y = 0; y < (height + 1)/2; y++) { 
      for (int x = 0; x < (width + 1)/2; x++) { 
       chromaBuf[y*chromaBufStride + 2*x + 0] = crPtr[y*crRowStride + x*crPixelStride]; 
       chromaBuf[y*chromaBufStride + 2*x + 1] = cbPtr[y*cbRowStride + x*cbPixelStride]; 
      } 
     } 
    } 
} 

그러나, 많은 API는 각면의 시작, 플러스 각각의 행 보폭 세 포인터의 형태로 데이터를 처리 할 수 ​​있습니다. 그러면 훨씬 적은 복사로 도망 갈 수 있습니다. 크로마의 경우, 여전히 I420/NV12/NV21인지 여부를 식별하고 목적지 API로 그대로 전달하려고 할 것입니다. 대상 API가 지원하는 특정 픽셀 형식 레이아웃과 일치시키지 못하면 일반적으로 알려진 지원되는 레이아웃이있는 로컬 버퍼로 압축을 풀어야합니다.