Bug 1219134: P4. Properly assign the decoded sample duration. r=edwin a=sylvestre
authorJean-Yves Avenard <jyavenard@mozilla.com>
Sat, 31 Oct 2015 15:16:12 +1100
changeset 296670 0056c6a320b7
parent 296669 93eddf049b08
child 296671 c6d927b2b229
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: P4. Properly assign the decoded sample duration. r=edwin a=sylvestre We default to the previous logic if for some unlikely condition we couldn't find the frame's duration (using the last input frame's duration)
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
@@ -150,16 +150,23 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
 
   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;
 
+  // LibAV provides no API to retrieve the decoded sample's duration.
+  // (FFmpeg >= 1.0 provides av_frame_get_pkt_duration)
+  // As such we instead use a map using the dts as key that we will retrieve
+  // later.
+  // The map will have a typical size of 16 entry.
+  mDurationMap.Insert(aSample->mTimecode, aSample->mDuration);
+
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg h264 decoder failed to allocate frame.");
     mCallback->Error();
     return DecodeResult::DECODE_ERROR;
   }
 
   // Required with old version of FFmpeg/LibAV
   mFrame->reordered_opaque = AV_NOPTS_VALUE;
@@ -180,16 +187,29 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     return DecodeResult::DECODE_ERROR;
   }
 
   // If we've decoded a frame then we need to output it
   if (decoded) {
     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);
+    // Retrieve duration from dts.
+    // We use the first entry found matching this dts (this is done to
+    // handle damaged file with multiple frames with the same dts)
+
+    int64_t duration;
+    if (!mDurationMap.Find(mFrame->pkt_dts, duration)) {
+      NS_WARNING("Unable to retrieve duration from map");
+      duration = aSample->mDuration;
+      // dts are probably incorrectly reported ; so clear the map as we're
+      // unlikely to find them in the future anyway. This also guards
+      // against the map becoming extremely big.
+      mDurationMap.Clear();
+    }
 
     VideoInfo info;
     info.mDisplay = nsIntSize(mDisplayWidth, mDisplayHeight);
 
     VideoData::YCbCrBuffer b;
     b.mPlanes[0].mData = mFrame->data[0];
     b.mPlanes[0].mStride = mFrame->linesize[0];
     b.mPlanes[0].mHeight = mFrame->height;
@@ -207,17 +227,17 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     b.mPlanes[2].mHeight = (mFrame->height + 1) >> 1;
     b.mPlanes[2].mWidth = (mFrame->width + 1) >> 1;
     b.mPlanes[2].mOffset = b.mPlanes[2].mSkip = 0;
 
     nsRefPtr<VideoData> v = VideoData::Create(info,
                                               mImageContainer,
                                               aSample->mOffset,
                                               pts,
-                                              aSample->mDuration,
+                                              duration,
                                               b,
                                               !!mFrame->key_frame,
                                               -1,
                                               gfx::IntRect(0, 0, mCodecContext->width, mCodecContext->height));
     if (!v) {
       NS_WARNING("image allocation error.");
       mCallback->Error();
       return DecodeResult::DECODE_ERROR;
@@ -376,16 +396,17 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessDra
   }
   mCallback->DrainComplete();
 }
 
 void
 FFmpegH264Decoder<LIBAV_VER>::ProcessFlush()
 {
   mPtsContext.Reset();
+  mDurationMap.Clear();
   FFmpegDataDecoder::ProcessFlush();
 }
 
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
 }
 
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -3,16 +3,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegH264Decoder_h__
 #define __FFmpegH264Decoder_h__
 
 #include "FFmpegDataDecoder.h"
+#include "mozilla/Pair.h"
+#include "nsTArray.h"
 
 namespace mozilla
 {
 
 template <int V>
 class FFmpegH264Decoder : public FFmpegDataDecoder<V>
 {
 };
@@ -76,13 +78,49 @@ private:
   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;
+
+  class DurationMap {
+  public:
+    typedef Pair<int64_t, int64_t> DurationElement;
+
+    // Insert Dts and Duration pair at the end of our map.
+    void Insert(int64_t aDts, int64_t aDuration)
+    {
+      mMap.AppendElement(MakePair(aDts, aDuration));
+    }
+    // Sets aDuration matching aDts and remove it from the map if found.
+    // The element returned is the first one found.
+    // Returns true if found, false otherwise.
+    bool Find(int64_t aDts, int64_t& aDuration)
+    {
+      for (uint32_t i = 0; i < mMap.Length(); i++) {
+        DurationElement& element = mMap[i];
+        if (element.first() == aDts) {
+          aDuration = element.second();
+          mMap.RemoveElementAt(i);
+          return true;
+        }
+      }
+      return false;
+    }
+    // Remove all elements of the map.
+    void Clear()
+    {
+      mMap.Clear();
+    }
+
+  private:
+    nsAutoTArray<DurationElement, 16> mMap;
+  };
+
+  DurationMap mDurationMap;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegH264Decoder_h__