dupengyue
2017-07-20 3a5f09c61a87adb8dba2cc4a5366893886ba1c1d
RtspFace/PL_AndroidMediaCodecDecoder.cpp
@@ -1,23 +1,38 @@
#include "PL_AndroidMediaCodecDecoder.h"
#include "MaterialBuffer.h"
#include "logger.h"
#include "MediaHelper.h"
#include <media/NdkMediaCodec.h>
#include <media/NdkMediaFormat.h>
#include <android/bitmap.h>
#include <stdlib.h>
#include <liveMedia/liveMedia.hh>
struct PL_AMCD_Internal
{
   uint8_t buffer[1920*1080*4];//#todo from config
   uint8_t buffer[1920*1080*3];//#todo new from config
   size_t buffSize;
   size_t buffSizeMax;
   MB_Frame lastFrame;
   const size_t buffSizeMax;
   size_t inputFrameCount;
   int lastOutputBuffIdx; // -1 is invalid for android omx
   MB_Frame lastMbfBuffIdx; // pm for bufidx
   MB_Frame lastMbfBuffer; // frame for buffer (decoded data)
   MB_Frame* lastMbfList[2];
   PL_AndroidMediaCodecDecoder_Config config;
   bool payError;
   AMediaCodec* codec;
   PL_AMCD_Internal() : 
      buffSize(0), buffSizeMax(sizeof(buffer)), lastFrame(),
      buffSize(0), buffSizeMax(sizeof(buffer)),
      inputFrameCount(0), lastOutputBuffIdx(-1),
      lastMbfBuffIdx(), lastMbfBuffer(),
      config(), 
      payError(true),
      codec(nullptr)
   {
   }
@@ -29,10 +44,14 @@
   void reset()
   {
      buffSize = 0;
      payError = true;
      
      MB_Frame _lastFrame;
      lastFrame = _lastFrame;
      inputFrameCount = 0;
      lastOutputBuffIdx = 0;
      lastMbfBuffIdx.reset();
      lastMbfBuffer.reset();
      lastMbfList[0] = &lastMbfBuffIdx;
      lastMbfList[1] = &lastMbfBuffer;
      
      PL_AndroidMediaCodecDecoder_Config _config;
      config = _config;
@@ -66,23 +85,73 @@
    AMediaFormat* format = AMediaFormat_new();
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, config.ak_mime.c_str());
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, config.ak_height);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, config.ak_width);
    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, config->ak_mime.c_str());
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, config->ak_height);
    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, config->ak_width);
// see: https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities.html#COLOR_FormatYUV420Flexible
#define AMEDIA_COLOR_FormatYUV420Flexible 0x7f420888
    //AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, AMEDIA_COLOR_FormatYUV420Flexible);
    //AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_STRIDE, config->ak_width*2);
   // #19 COLOR_FormatYUV420Planar (I420)
   // #20 COLOR_FormatYUV420PackedPlanar (also I420)
   // #21 COLOR_FormatYUV420SemiPlanar (NV12)
   // #39 COLOR_FormatYUV420PackedSemiPlanar (also NV12)
   // #0x7f000100 COLOR_TI_FormatYUV420PackedSemiPlanar (also also NV12)
   //tegra: I420
   //qcom: NV12
    /*
        std::string fmtp(manager->get_param(PLGP_RTSP_FMTP));
        if (fmtp.empty())
            return false;
        uint32_t numSPropRecords = 0;
        SPropRecord *p_record = parseSPropParameterSets(fmtp.c_str(), numSPropRecords);
        if (numSPropRecords < 2)
        {
            LOG_WARN << "numSPropRecords < 2" << std::endl;
            return false;
        }
        SPropRecord &sps = p_record[0];
        SPropRecord &pps = p_record[1];
        AMediaFormat_setBuffer(format, "csd-0", sps.sPropBytes, sps.sPropLength); // sps
        AMediaFormat_setBuffer(format, "csd-1", pps.sPropBytes, pps.sPropLength); // pps
        //uint8_t sps[] = {0x0,0x0,0x0,0x1, 0x67, 0x42, 0x00, 0x2A, 0x95, 0xA8, 0x1E, 0x00, 0x89, 0xF9, 0x61, 0x00, 0x00, 0x07, 0x08, 0x00, 0x01, 0x5F, 0x90, 0x04};
        //uint8_t pps[] = {0x0,0x0,0x0,0x1, 0x68, 0xCE, 0x3C, 0x80};
        //AMediaFormat_setBuffer(format, "csd-0", sps, sizeof(sps)); // sps
        //AMediaFormat_setBuffer(format, "csd-1", pps, sizeof(pps)); // pps
    */
    // should like:
    // mime: string(video/avc), durationUs: int64(10000000), width: int32(480), height: int32(360), max-input-size: int32(55067), csd-0: data, csd-1: data}
    LOG_INFO << "AMediaFormat_toString: " << AMediaFormat_toString(format) << LOG_ENDL;
    in->codec = AMediaCodec_createDecoderByType(config.ak_mime.c_str());
    if (AMediaCodec_configure(in->codec, format, data.window, NULL, 0) != AMEDIA_OK)
    in->codec = AMediaCodec_createDecoderByType(config->ak_mime.c_str());
    // if no sureface android use software decoder
    // see: http://stackoverflow.com/questions/15500290/access-violation-in-native-code-with-hardware-accelerated-android-mediacodec-dec
    ANativeWindow* window = (config->renderFromOutputBuffIdx ? (ANativeWindow*)config->windowSurface : nullptr);
    if (AMediaCodec_configure(in->codec, format, window, NULL, 0) != AMEDIA_OK)
   {
      AMediaFormat_delete(format);
        LOG_ERROR << "AMediaCodec_configure error" << LOG_ENDL;
      return false;
   }
    if (AMediaCodec_start(in->codec) != AMEDIA_OK)
   {
      AMediaFormat_delete(format);
        LOG_ERROR << "AMediaCodec_start error" << LOG_ENDL;
      return false;
   }
    AMediaFormat_delete(format);
   return true;
}
@@ -90,6 +159,7 @@
{
   PL_AMCD_Internal* in = (PL_AMCD_Internal*)internal;
   //todo release codec
   // call AMediaCodec_stop
}
bool PL_AndroidMediaCodecDecoder::pay(const PipeMaterial& pm)
@@ -111,73 +181,44 @@
      LOG_ERROR << "Only support MBFT_H264_NALU" << std::endl;
      return false;
   }
    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(in->codec, 2000);
    static int framecount = 0;
    LOGP(DEBUG, "input buffer bufidx=%zd, framecount=%d", bufidx, framecount++);
    LOGP(DEBUG, "input buffer bufidx=%zd, inputFrameCount=%d", bufidx, in->inputFrameCount++);
    if (bufidx >= 0)
   {
        size_t bufsize;
        uint8_t* buf = AMediaCodec_getInputBuffer(in->codec, bufidx, &bufsize);
        uint8_t* inputBuff = AMediaCodec_getInputBuffer(in->codec, bufidx, &bufsize);
        size_t sampleSize = std::min(bufsize, frame->buffSize);
        memcpy(buf, buffer, sampleSize);
        //auto sampleSize = AMediaExtractor_readSampleData(d->ex, buf, bufsize);
        //if (sampleSize < 0) {
        //    sampleSize = 0;
        //    d->sawInputEOS = true;
        //    LOGV("EOS");
        //}
        //auto presentationTimeUs = AMediaExtractor_getSampleTime(d->ex);
        uint64_t presentationTimeUs = presentationTime.tv_sec * 1000 * 1000 + presentationTime.tv_usec; //microseconds
        memcpy(inputBuff, frame->buffer, sampleSize); // fill buffer
        media_status_t ms = AMediaCodec_queueInputBuffer(data.codec, bufidx, 0, sampleSize, presentationTimeUs, 0);
        //LOGV("media_status_t=%d", ms);
        //AMediaExtractor_advance(d->ex);
        uint64_t presentationTimeUs = timeval_to_microseconds(frame->pts); //microseconds
        media_status_t ms = AMediaCodec_queueInputBuffer(in->codec, bufidx, 0, sampleSize, presentationTimeUs, 0);
        LOGP(DEBUG, "media_status_t=%d", ms);
    }
   else
   {
      LOG_WARN << "bufidx=" << bufidx << LOG_ENDL;
      return false; // return true for gain
   }
   
//
//   AVFrame* pAVFrame = (AVFrame*)frame->buffer;
//   if (pAVFrame == nullptr)
//      return false;
//
//   const int height = pAVFrame->height;
//   const int width = pAVFrame->width;
//
////int I420ToBGRA(const uint8* src_y, int src_stride_y,
////               const uint8* src_u, int src_stride_u,
////               const uint8* src_v, int src_stride_v,
////               uint8* dst_argb, int dst_stride_argb,
////            int width, int height);
//
//   libyuv::I420ToBGRA(pAVFrame->data[0], width,
//                  pAVFrame->data[1], SUBSAMPLE(width, 2),
//                  pAVFrame->data[2], SUBSAMPLE(width, 2),
//                  in->buffer, 4 * width,
//                  width, height);
//
//   in->buffSize = in->buffSizeMax;
//   //in->buffer readly
//
//   in->lastFrame.type = MB_Frame::MBFT_BGRA;
//   in->lastFrame.buffer = in->buffer;
//   in->lastFrame.buffSize = in->buffSize;
//   in->lastFrame.width = width;
//   in->lastFrame.height = height;
//   in->lastFrame.pts = frame->pts;
//
//   //#test
//   //static size_t f=0;
//   //char fname[50];
//   //sprintf(fname, "%u.bgra", ++f);
//   //FILE * pFile = fopen (fname,"wb");
//   //fwrite (in->buffer , sizeof(char), in->buffSize, pFile);
//   //fclose(pFile);
//
   if (in->config.releaseOutputBuffIdxInPay)
   {
      AMediaCodecBufferInfo info;
      in->lastOutputBuffIdx = AMediaCodec_dequeueOutputBuffer(in->codec, &info, 0);
      LOG_WARN << "releaseOutputBuffIdxInPay bufidx=" << in->lastOutputBuffIdx << ", flags=" << info.flags << LOG_ENDL;
      if (in->lastOutputBuffIdx >= 0)
      {
         if (in->config.releaseOutputBuffIdx)
         {
            AMediaCodec_releaseOutputBuffer(in->codec, in->lastOutputBuffIdx, info.size != 0);
            in->lastOutputBuffIdx = -1;
         }
      }
   }
   return true;
}
@@ -185,9 +226,130 @@
{
   PL_AMCD_Internal* in = (PL_AMCD_Internal*)internal;
   pm.type = PipeMaterial::PMT_FRAME;
   pm.buffer = &(in->lastFrame);
   pm.buffSize = 0;
   pm.former = this;
   return true;
   AMediaCodecBufferInfo info;
   in->lastOutputBuffIdx = AMediaCodec_dequeueOutputBuffer(in->codec, &info, 0);
   if (in->lastOutputBuffIdx >= 0)
   {
      if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM)
      {
         LOGP(WARNING, "output EOS");
      }
      //int64_t presentationNano = info.presentationTimeUs * 1000;
      //if (d->renderstart < 0) {
      //   d->renderstart = systemnanotime() - presentationNano;
      //}
      //int64_t delay = (d->renderstart + presentationNano) - systemnanotime();
      //if (delay > 0) {
      //   usleep(delay / 1000);
      //}
        AMediaFormat* format = AMediaCodec_getOutputFormat(in->codec);
        if (format != NULL)
        {
            int32_t width, height, color;
            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, &color);
            AMediaFormat_delete(format);
            format = nullptr;
            LOGP(DEBUG, "output media format, w=%d, h=%d, c=%d", width, height, color);
        }
      in->lastMbfBuffIdx.reset();
      if (! in->config.releaseOutputBuffIdx)
      {
         in->lastMbfBuffIdx.type = MB_Frame::MBFT_INDEX;
         in->lastMbfBuffIdx.buffer = (void*)(in->lastOutputBuffIdx);
         in->lastMbfBuffIdx.buffSize = sizeof(in->lastOutputBuffIdx);
         in->lastMbfBuffIdx.width = in->config.ak_width;
         in->lastMbfBuffIdx.height = in->config.ak_height;
         microseconds_to_timeval(info.presentationTimeUs, in->lastMbfBuffIdx.pts);
      }
      in->lastMbfBuffer.reset();
      if ((in->config.generateDecodedDataPerFrame != 0) && (in->inputFrameCount % in->config.generateDecodedDataPerFrame == 0))
      {
            size_t outSize = in->buffSizeMax;
            uint8_t* outputBuff = AMediaCodec_getOutputBuffer(in->codec, in->lastOutputBuffIdx, &outSize);
            if (outputBuff != nullptr)
            {
                in->buffSize = std::min((size_t) info.size, in->buffSizeMax);
                memcpy(in->buffer, outputBuff + info.offset, in->buffSize);
                if (in->config.renderFromOutputBuff)
            {
                    ANativeWindow* window = (ANativeWindow*)(in->config.windowSurface);
               ANativeWindow_Buffer wbuffer;
               if (ANativeWindow_lock(window, &wbuffer, NULL) == 0)
                    {
                        size_t bitsSize = 0;
                        if (wbuffer.format == WINDOW_FORMAT_RGBA_8888 || wbuffer.format == WINDOW_FORMAT_RGBX_8888)
                            bitsSize = wbuffer.height*wbuffer.width*4;
                        else if (wbuffer.format == WINDOW_FORMAT_RGB_565)
                            bitsSize = wbuffer.height*wbuffer.width*2;
                        else
                            bitsSize = wbuffer.height*wbuffer.width;
                        memcpy(wbuffer.bits, in->buffer,  bitsSize);
                        //memcpy(in->buffer, wbuffer.bits,  bitsSize);//#test copy opposite
                  ANativeWindow_unlockAndPost(window);
               }
            }
                in->lastMbfBuffer.type = MB_Frame::MBFT_YUV420;
                in->lastMbfBuffer.buffer = in->buffer;
                in->lastMbfBuffer.buffSize = in->buffSize;
                in->lastMbfBuffer.width = in->config.ak_width;
                in->lastMbfBuffer.height = in->config.ak_height;
                microseconds_to_timeval(info.presentationTimeUs, in->lastMbfBuffer.pts);
            //if (in->lastMbfBuffer.buffSize > 10)
            //{
            //   static size_t f = 0;
            //   char fname[50];
            //   sprintf(fname, "/sdcard/face-%u.yuv", ++f);
            //   FILE *pFile = fopen(fname, "wb");
            //   fwrite(in->lastMbfBuffer.buffer, sizeof(char), in->lastMbfBuffer.buffSize, pFile);
            //   printf("write face file %s\n", fname);
            //   fclose(pFile);
            //   if (f > 50) exit(0);
            //}
            }
      }
      pm.type = PipeMaterial::PMT_FRAME_LIST;
      pm.buffer = *(in->lastMbfList); // in->lastMbfList is typeof MB_Frame**
      pm.buffSize = sizeof(in->lastMbfList) / sizeof(MB_Frame*); // 2
      pm.deleter = nullptr;
      pm.former = this;
      if (in->config.releaseOutputBuffIdx)
      {
         AMediaCodec_releaseOutputBuffer(in->codec, in->lastOutputBuffIdx, in->config.renderFromOutputBuffIdx);//info.size != 0
         in->lastOutputBuffIdx = -1;
      }
      return true;
   }
   else if (in->lastOutputBuffIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)
   {
      LOGP(DEBUG, "output buffers changed");
   }
   else if (in->lastOutputBuffIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED)
   {
      auto format = AMediaCodec_getOutputFormat(in->codec);
      LOGP(INFO, "format changed to: %s", AMediaFormat_toString(format));
      AMediaFormat_delete(format);
   }
   else if (in->lastOutputBuffIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER)
   {
      LOGP(DEBUG, "no output buffer right now");
   }
   else
   {
      LOGP(WARNING, "unexpected info code: %zd", in->lastOutputBuffIdx);
   }
   return false;
}