Bug 1002402 - Support RTP H.264 input data in WebRTC OMX decoder. r=jesup
authorJohn Lin <jolin@mozilla.com>
Mon, 28 Apr 2014 23:37:00 +0200
changeset 201055 336fe702ad1219be47fa9ea4fd05a32fa4b95bd6
parent 201054 652501df07279981c11656a97e6b7b82e20ef420
child 201056 0014c199761868de833be101495bf91b3f5511db
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1002402
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1002402 - Support RTP H.264 input data in WebRTC OMX decoder. r=jesup
media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp
--- a/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcOMXH264VideoCodec.cpp
@@ -40,16 +40,22 @@ using namespace android;
 #define CODEC_LOGV(...) CSFLogInfo(LOG_TAG, __VA_ARGS__)
 #define CODEC_LOGD(...) CSFLogDebug(LOG_TAG, __VA_ARGS__)
 #define CODEC_LOGI(...) CSFLogInfo(LOG_TAG, __VA_ARGS__)
 #define CODEC_LOGW(...) CSFLogWarn(LOG_TAG, __VA_ARGS__)
 #define CODEC_LOGE(...) CSFLogError(LOG_TAG, __VA_ARGS__)
 
 namespace mozilla {
 
+static uint8_t kNALStartCode[] = { 0x00, 0x00, 0x00, 0x01 };
+enum {
+  kNALTypeSPS = 7,
+  kNALTypePPS = 8,
+};
+
 // NS_INLINE_DECL_THREADSAFE_REFCOUNTING() cannot be used directly in
 // ImageNativeHandle below because the return type of webrtc::NativeHandle
 // AddRef()/Release() conflicts with those defined in macro. To avoid another
 // copy/paste of ref-counting implementation here, this dummy base class
 // is created to proivde another level of indirection.
 class DummyRefCountBase {
 public:
   // Use the name of real class for logging.
@@ -146,23 +152,21 @@ public:
 
   NS_IMETHODIMP Run() MOZ_OVERRIDE
   {
     MOZ_ASSERT(mThread);
 
     MonitorAutoLock lock(mMonitor);
     while (true) {
       if (mInputFrames.empty()) {
-        ALOGE("Waiting OMXOutputDrain");
         // Wait for new input.
         lock.Wait();
       }
 
       if (mEnding) {
-        ALOGE("Ending OMXOutputDrain");
         // Stop draining.
         break;
       }
 
       MOZ_ASSERT(!mInputFrames.empty());
       EncodedFrame frame = mInputFrames.front();
       bool shouldPop = false;
       {
@@ -234,44 +238,45 @@ public:
     }
     if (mCodec != nullptr) {
       mCodec->release();
       mCodec.clear();
     }
     mLooper.clear();
   }
 
-  // Parse SPS/PPS NALUs.
-  static sp<MetaData> ParseParamSets(sp<ABuffer>& aParamSets)
+  // Find SPS in input data and extract picture width and height if found.
+  static status_t ExtractPicDimensions(uint8_t* aData, size_t aSize,
+                                       int32_t* aWidth, int32_t* aHeight)
   {
-    return MakeAVCCodecSpecificData(aParamSets);
+    MOZ_ASSERT(aData && aSize > 1);
+    if ((aData[0] & 0x1f) != kNALTypeSPS) {
+      return ERROR_MALFORMED;
+    }
+    sp<ABuffer> sps = new ABuffer(aData, aSize);
+    FindAVCDimensions(sps, aWidth, aHeight);
+    return OK;
   }
 
-  // Configure decoder using data returned by ParseParamSets().
-  status_t ConfigureWithParamSets(const sp<MetaData>& aParamSets)
+  // Configure decoder using image width/height.
+  status_t ConfigureWithPicDimensions(int32_t aWidth, int32_t aHeight)
   {
     MOZ_ASSERT(mCodec != nullptr);
     if (mCodec == nullptr) {
       return INVALID_OPERATION;
     }
 
-    int32_t width = 0;
-    bool ok = aParamSets->findInt32(kKeyWidth, &width);
-    MOZ_ASSERT(ok && width > 0);
-    int32_t height = 0;
-    ok = aParamSets->findInt32(kKeyHeight, &height);
-    MOZ_ASSERT(ok && height > 0);
-    CODEC_LOGD("OMX:%p decoder config width:%d height:%d", this, width, height);
+    CODEC_LOGD("OMX:%p decoder width:%d height:%d", this, aWidth, aHeight);
 
     sp<AMessage> config = new AMessage();
     config->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
-    config->setInt32("width", width);
-    config->setInt32("height", height);
-    mWidth = width;
-    mHeight = height;
+    config->setInt32("width", aWidth);
+    config->setInt32("height", aHeight);
+    mWidth = aWidth;
+    mHeight = aHeight;
 
     sp<Surface> surface = nullptr;
     mNativeWindow = new GonkNativeWindow();
     if (mNativeWindow.get()) {
       // listen to buffers queued by MediaCodec::RenderOutputBufferAndRelease().
       mNativeWindow->setNewFrameCallback(this);
       surface = new Surface(mNativeWindow->getBufferQueue());
     }
@@ -281,42 +286,47 @@ public:
     }
     return result;
   }
 
   status_t
   FillInput(const webrtc::EncodedImage& aEncoded, bool aIsFirstFrame,
             int64_t& aRenderTimeMs)
   {
-    MOZ_ASSERT(mCodec != nullptr);
-    if (mCodec == nullptr) {
+    MOZ_ASSERT(mCodec != nullptr && aEncoded._buffer && aEncoded._length > 0);
+    if (mCodec == nullptr || !aEncoded._buffer || aEncoded._length == 0) {
       return INVALID_OPERATION;
     }
 
     size_t index;
     status_t err = mCodec->dequeueInputBuffer(&index,
       aIsFirstFrame ? START_DEQUEUE_BUFFER_TIMEOUT_US : DEQUEUE_BUFFER_TIMEOUT_US);
     if (err != OK) {
       CODEC_LOGE("decode dequeue input buffer error:%d", err);
       return err;
     }
 
-    uint32_t flags = 0;
-    if (aEncoded._frameType == webrtc::kKeyFrame) {
-      flags = aIsFirstFrame ? MediaCodec::BUFFER_FLAG_CODECCONFIG : MediaCodec::BUFFER_FLAG_SYNCFRAME;
-    }
-    size_t size = aEncoded._length;
-    MOZ_ASSERT(size);
+    // Prepend start code to buffer.
+    size_t size = aEncoded._length + sizeof(kNALStartCode);
     const sp<ABuffer>& omxIn = mInputBuffers.itemAt(index);
     MOZ_ASSERT(omxIn->capacity() >= size);
     omxIn->setRange(0, size);
     // Copying is needed because MediaCodec API doesn't support externallay
     // allocated buffer as input.
-    memcpy(omxIn->data(), aEncoded._buffer, size);
+    uint8_t* dst = omxIn->data();
+    memcpy(dst, kNALStartCode, sizeof(kNALStartCode));
+    memcpy(dst + sizeof(kNALStartCode), aEncoded._buffer, aEncoded._length);
     int64_t inputTimeUs = aEncoded._timeStamp * 1000 / 90; // 90kHz -> us.
+    // Assign input flags according to input buffer NALU and frame types.
+    uint32_t flags = 0;
+    if (aEncoded._frameType == webrtc::kKeyFrame) {
+      int nalType = dst[sizeof(kNALStartCode)] & 0x1f;
+      flags = (nalType == kNALTypeSPS || nalType == kNALTypePPS) ?
+              MediaCodec::BUFFER_FLAG_CODECCONFIG : MediaCodec::BUFFER_FLAG_SYNCFRAME;
+    }
     err = mCodec->queueInputBuffer(index, 0, size, inputTimeUs, flags);
     if (err == OK && !(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG)) {
       if (mOutputDrain == nullptr) {
         mOutputDrain = new OutputDrain(this);
         mOutputDrain->Start();
       }
       EncodedFrame frame;
       frame.mWidth = mWidth;
@@ -558,18 +568,16 @@ protected:
       encoded._frameType = (isParamSets || isIFrame) ?
                            webrtc::kKeyFrame : webrtc::kDeltaFrame;
       encoded._encodedWidth = aInputFrame.mWidth;
       encoded._encodedHeight = aInputFrame.mHeight;
       encoded._timeStamp = aInputFrame.mTimestamp;
       encoded.capture_time_ms_ = aInputFrame.mRenderTimeMs;
       encoded._completeFrame = true;
 
-      ALOGE("OMX:%p encode frame type:%d size:%u", mOMX, encoded._frameType, encoded._length);
-
       // Prepend SPS/PPS to I-frames unless they were sent last time.
       SendEncodedDataToCallback(encoded, isIFrame && !mIsPrevOutputParamSets);
       mIsPrevOutputParamSets = isParamSets;
     }
 
     // Tell base class not to pop input for parameter sets blob because they
     // don't have corresponding input.
     return !isParamSets;
@@ -795,30 +803,32 @@ WebrtcOMXH264VideoDecoder::Decode(const 
                                   const webrtc::RTPFragmentationHeader* aFragmentation,
                                   const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
                                   int64_t aRenderTimeMs)
 {
   if (aInputImage._length== 0 || !aInputImage._buffer) {
     return WEBRTC_VIDEO_CODEC_ERROR;
   }
 
-  ALOGE("WebrtcOMXH264VideoDecoder:%p will decode", this);
-
   bool configured = !!mOMX;
   if (!configured) {
-    // Search for SPS/PPS NALUs in input to get decoder config.
-    sp<ABuffer> input = new ABuffer(aInputImage._buffer, aInputImage._length);
-    sp<MetaData> paramSets = WebrtcOMXDecoder::ParseParamSets(input);
-    if (NS_WARN_IF(paramSets == nullptr)) {
-      // Cannot config decoder because SPS/PPS NALUs haven't been seen.
+    // Search for SPS NALU in input to get width/height config.
+    int32_t width;
+    int32_t height;
+    status_t result = WebrtcOMXDecoder::ExtractPicDimensions(aInputImage._buffer,
+                                                             aInputImage._length,
+                                                             &width, &height);
+    if (result != OK) {
+      // Cannot config decoder because SPS haven't been seen.
+      CODEC_LOGI("WebrtcOMXH264VideoDecoder:%p missing SPS in input");
       return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
     }
     RefPtr<WebrtcOMXDecoder> omx = new WebrtcOMXDecoder(MEDIA_MIMETYPE_VIDEO_AVC,
                                                         mCallback);
-    status_t result = omx->ConfigureWithParamSets(paramSets);
+    result = omx->ConfigureWithPicDimensions(width, height);
     if (NS_WARN_IF(result != OK)) {
       return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
     }
     CODEC_LOGD("WebrtcOMXH264VideoDecoder:%p start OMX", this);
     mOMX = omx;
   }
 
   bool feedFrame = true;