Bug 1197319: P2. Parse raw data to identify single frames before decoding. r=kentuckyfriedtakahe
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 09 Sep 2015 18:47:59 +1000
changeset 296085 dec6b701c9eaf417bee88b4fc2e6cdf4e5fe2516
parent 296084 47fd59525c65f3d08be9d0d9c6569f9e547be9ee
child 296086 3a5f6d92197239ad6c58b6c0e57b0324921a425a
push id962
push userjlund@mozilla.com
push dateFri, 04 Dec 2015 23:28:54 +0000
treeherdermozilla-release@23a2d286e80f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe
bugs1197319
milestone43.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 1197319: P2. Parse raw data to identify single frames before decoding. r=kentuckyfriedtakahe A VP9 or VP9 packet may contains alternate frames. They need to be fed separately to the ffmpeg decoder.
dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
dom/media/platforms/ffmpeg/FFmpegFunctionList.h
dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -37,19 +37,18 @@ public:
 protected:
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
   FlushableTaskQueue* mTaskQueue;
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
   nsRefPtr<MediaByteBuffer> mExtraData;
+  AVCodecID mCodecID;
 
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
-
-  AVCodecID mCodecID;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDataDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
+++ b/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
@@ -15,16 +15,19 @@ AV_FUNC(avcodec_default_get_buffer, 0)
 AV_FUNC(avcodec_default_release_buffer, 0)
 AV_FUNC(avcodec_find_decoder, 0)
 AV_FUNC(avcodec_flush_buffers, 0)
 AV_FUNC(avcodec_alloc_context3, 0)
 AV_FUNC(avcodec_get_edge_width, 0)
 AV_FUNC(avcodec_open2, 0)
 AV_FUNC(av_init_packet, 0)
 AV_FUNC(av_dict_get, 0)
+AV_FUNC(av_parser_init, 0)
+AV_FUNC(av_parser_close, 0)
+AV_FUNC(av_parser_parse2, 0)
 
 /* libavutil */
 AV_FUNC(av_image_fill_linesizes, 0)
 AV_FUNC(av_image_fill_pointers, 0)
 AV_FUNC(av_log_set_level, 0)
 AV_FUNC(av_malloc, 0)
 AV_FUNC(av_freep, 0)
 
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -28,16 +28,17 @@ FFmpegH264Decoder<LIBAV_VER>::FFmpegH264
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
   : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mMimeType))
   , mCallback(aCallback)
   , mImageContainer(aImageContainer)
   , mDisplayWidth(aConfig.mDisplay.width)
   , mDisplayHeight(aConfig.mDisplay.height)
+  , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mExtraData);
 }
 
 nsRefPtr<MediaDataDecoder::InitPromise>
@@ -65,21 +66,72 @@ FFmpegH264Decoder<LIBAV_VER>::GetPts(con
 #else
   return mFrame->pkt_pts;
 #endif
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
+  uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
+  size_t inputSize = aSample->Size();
+
+  if (inputSize && (mCodecID == AV_CODEC_ID_VP8
+#if LIBAVCODEC_VERSION_MAJOR >= 55
+      || mCodecID == AV_CODEC_ID_VP9
+#endif
+      )) {
+    if (!mCodecParser) {
+      mCodecParser = av_parser_init(mCodecID);
+      if (!mCodecParser) {
+        mCallback->Error();
+        return DecodeResult::DECODE_ERROR;
+      }
+      mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+    }
+    bool gotFrame = false;
+    while (inputSize) {
+      uint8_t* data;
+      int size;
+      int len = av_parser_parse2(mCodecParser, mCodecContext, &data, &size,
+                                 inputData, inputSize,
+                                 aSample->mTime, aSample->mTimecode,
+                                 aSample->mOffset);
+      if (size_t(len) > inputSize) {
+        mCallback->Error();
+        return DecodeResult::DECODE_ERROR;
+      }
+      inputData += len;
+      inputSize -= len;
+      if (size) {
+        switch (DoDecodeFrame(aSample, data, size)) {
+          case DecodeResult::DECODE_ERROR:
+            return DecodeResult::DECODE_ERROR;
+          case DecodeResult::DECODE_FRAME:
+            gotFrame = true;
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    return gotFrame ? DecodeResult::DECODE_FRAME : DecodeResult::DECODE_NO_FRAME;
+  }
+  return DoDecodeFrame(aSample, inputData, inputSize);
+}
+
+FFmpegH264Decoder<LIBAV_VER>::DecodeResult
+FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
+                                            uint8_t* aData, int aSize)
+{
   AVPacket packet;
   av_init_packet(&packet);
 
-  packet.data = const_cast<uint8_t*>(aSample->Data());
-  packet.size = aSample->Size();
+  packet.data = aData;
+  packet.size = aSize;
   packet.dts = aSample->mTimecode;
   packet.pts = aSample->mTime;
   packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
   packet.pos = aSample->mOffset;
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
     mCallback->Error();
@@ -311,16 +363,20 @@ FFmpegH264Decoder<LIBAV_VER>::Flush()
   nsresult rv = FFmpegDataDecoder::Flush();
   // Even if the above fails we may as well clear our frame queue.
   return rv;
 }
 
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
+  if (mCodecParser) {
+    av_parser_close(mCodecParser);
+    mCodecParser = nullptr;
+  }
 }
 
 AVCodecID
 FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
     return AV_CODEC_ID_H264;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -40,16 +40,17 @@ public:
   virtual nsresult Input(MediaRawData* aSample) override;
   virtual nsresult Drain() override;
   virtual nsresult Flush() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample);
+  DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
   void DoDrain();
   void OutputDelayedFrames();
 
   /**
    * This method allocates a buffer for FFmpeg's decoder, wrapped in an Image.
    * Currently it only supports Planar YUV420, which appears to be the only
    * non-hardware accelerated image format that FFmpeg's H264 decoder is
    * capable of outputting.
@@ -60,13 +61,14 @@ private:
   static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   static void ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   int64_t GetPts(const AVPacket& packet);
 
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<ImageContainer> mImageContainer;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
+  AVCodecParserContext* mCodecParser;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegH264Decoder_h__