Bug 1219134: P2. Use LibAV/FFmpeg logic to detect invalid pts. r=edwin a=sylvestre
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 28 Oct 2015 21:56:22 +1100
changeset 296668 dc997e552ee6
parent 296667 ce3f2c696251
child 296669 93eddf049b08
push id5279
push userjyavenard@mozilla.com
push date2015-11-09 13:25 +0000
treeherdermozilla-beta@0056c6a320b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin, sylvestre
bugs1219134
milestone43.0
Bug 1219134: P2. Use LibAV/FFmpeg logic to detect invalid pts. r=edwin a=sylvestre The logic was extracted from LibAV cmdutils.c. FFmpeg provides an API for that (av_frame_get_best_effort_timestamp()) unfortunately this isn't provided by LibAV. So copy the logic instead in order to keep compatibility with the two forks.
dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -17,16 +17,55 @@
 #include "mozilla/PodOperations.h"
 
 typedef mozilla::layers::Image Image;
 typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
 namespace mozilla
 {
 
+FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::PtsCorrectionContext()
+  : mNumFaultyPts(0)
+  , mNumFaultyDts(0)
+  , mLastPts(INT64_MIN)
+  , mLastDts(INT64_MIN)
+{
+}
+
+int64_t
+FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::GuessCorrectPts(int64_t aPts, int64_t aDts)
+{
+  int64_t pts = AV_NOPTS_VALUE;
+
+  if (aDts != int64_t(AV_NOPTS_VALUE)) {
+    mNumFaultyDts += aDts <= mLastDts;
+    mLastDts = aDts;
+  }
+  if (aPts != int64_t(AV_NOPTS_VALUE)) {
+    mNumFaultyPts += aPts <= mLastPts;
+    mLastPts = aPts;
+  }
+  if ((mNumFaultyPts <= mNumFaultyDts || aDts == int64_t(AV_NOPTS_VALUE)) &&
+      aPts != int64_t(AV_NOPTS_VALUE)) {
+    pts = aPts;
+  } else {
+    pts = aDts;
+  }
+  return pts;
+}
+
+void
+FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::Reset()
+{
+  mNumFaultyPts = 0;
+  mNumFaultyDts = 0;
+  mLastPts = INT64_MIN;
+  mLastDts = INT64_MIN;
+}
+
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
   : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
   , mPictureWidth(aConfig.mImage.width)
   , mPictureHeight(aConfig.mImage.height)
@@ -49,31 +88,16 @@ FFmpegH264Decoder<LIBAV_VER>::Init()
   mCodecContext->get_buffer = AllocateBufferCb;
   mCodecContext->release_buffer = ReleaseBufferCb;
   mCodecContext->width = mPictureWidth;
   mCodecContext->height = mPictureHeight;
 
   return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
 }
 
-int64_t
-FFmpegH264Decoder<LIBAV_VER>::GetPts(const AVPacket& packet)
-{
-  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-#if LIBAVCODEC_VERSION_MAJOR == 53
-  if (mFrame->pkt_pts == 0) {
-    return mFrame->pkt_dts;
-  } else {
-    return mFrame->pkt_pts;
-  }
-#else
-  return mFrame->pkt_pts;
-#endif
-}
-
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
   size_t inputSize = aSample->Size();
 
@@ -153,17 +177,17 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
   if (bytesConsumed < 0) {
     NS_WARNING("FFmpeg video decoder error.");
     mCallback->Error();
     return DecodeResult::DECODE_ERROR;
   }
 
   // If we've decoded a frame then we need to output it
   if (decoded) {
-    int64_t pts = GetPts(packet);
+    int64_t pts = mPtsContext.GuessCorrectPts(mFrame->pkt_pts, mFrame->pkt_dts);
     FFMPEG_LOG("Got one frame output with pts=%lld opaque=%lld",
                pts, mCodecContext->reordered_opaque);
 
     VideoInfo info;
     info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
 
     VideoData::YCbCrBuffer b;
     b.mPlanes[0].mData = mFrame->data[0];
@@ -348,16 +372,23 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessDra
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   nsRefPtr<MediaRawData> empty(new MediaRawData());
   while (DoDecodeFrame(empty) == DecodeResult::DECODE_FRAME) {
   }
   mCallback->DrainComplete();
 }
 
+void
+FFmpegH264Decoder<LIBAV_VER>::ProcessFlush()
+{
+  mPtsContext.Reset();
+  FFmpegDataDecoder::ProcessFlush();
+}
+
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
 }
 
 AVCodecID
 FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -34,16 +34,17 @@ public:
                     MediaDataDecoderCallback* aCallback,
                     const VideoInfo& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
   virtual nsRefPtr<InitPromise> Init() override;
   virtual nsresult Input(MediaRawData* aSample) override;
   virtual void ProcessDrain() override;
+  virtual void ProcessFlush() 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();
@@ -54,20 +55,34 @@ private:
    * non-hardware accelerated image format that FFmpeg's H264 decoder is
    * capable of outputting.
    */
   int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
                                  AVFrame* aFrame);
 
   static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   static void ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
-  int64_t GetPts(const AVPacket& packet);
 
   nsRefPtr<ImageContainer> mImageContainer;
   uint32_t mPictureWidth;
   uint32_t mPictureHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
+
+  class PtsCorrectionContext {
+  public:
+    PtsCorrectionContext();
+    int64_t GuessCorrectPts(int64_t aPts, int64_t aDts);
+    void Reset();
+
+  private:
+    int64_t mNumFaultyPts; /// Number of incorrect PTS values so far
+    int64_t mNumFaultyDts; /// Number of incorrect DTS values so far
+    int64_t mLastPts;       /// PTS of the last frame
+    int64_t mLastDts;       /// DTS of the last frame
+  };
+
+  PtsCorrectionContext mPtsContext;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegH264Decoder_h__