2017-12-11 56 views
0

私はImageReaderを持っています。その表面は、レンダリングするMediaCodecデコーダにアタッチされています。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はこのATMのようになります。

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); 
} 

TODOのコメントで示されているように、私は、さらなる処理のためにImageReaderから取得したImageデータをコピーしたいです。 Imageクラスで提供されるインターフェイスを使用すると、プレーンの数をクエリし、個々のプレーンデータをフェッチすることができますが、フレーム全体を一度に取得することに興味があります。どのように私はこれを達成するかもしれないかについての任意の提案?一言で言えば

は、私はさらなる処理のためYUV420NV21形式でそれらの全体のImageReaderからデコードされたビデオフレームをするImageReaderが所有表面にレンダリングし、そして最終的に グラブしたいMediaCodecビデオデコーダを使用しています。

答えて

0

3つのYUVプレーンがすべて必要な場合は、それらのYUVプレーンを必要な宛先バッファに1つずつコピーする必要があります。連続していることは期待できませんが、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は、各プレーンの開始に加え、それぞれの行ストライドに3つのポインタの形でデータを処理することができます。そうすれば、コピーを減らすことができます。クロマについては、まだそれがI420/NV12/NV21であるかどうかを識別し、それをそのままの状態のAPIに渡すことを試みることをお勧めします。デスティネーションAPIがサポートする特定のピクセル形式のレイアウトに一致させることができない場合は、サポートされている既知のレイアウトのローカルバッファに一般的に展開する必要があります。

関連する問題