Bug 1300296: P3. Ensure that a new or flushed decoder first h264 frame is always a keyframe. r?gerald draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 06 Sep 2016 11:47:20 +1000
changeset 411040 498b452722e3078cde1c2d3067b4f3c773f4bb84
parent 411034 09594036eca55b7ca44c8951dde74b1a96ce5bff
child 411041 f640f7ff143b32f6c2d8894bd7e233ecaf4f9bbd
push id28824
push userbmo:jyavenard@mozilla.com
push dateWed, 07 Sep 2016 14:05:26 +0000
reviewersgerald
bugs1300296
milestone51.0a1
Bug 1300296: P3. Ensure that a new or flushed decoder first h264 frame is always a keyframe. r?gerald MozReview-Commit-ID: s0NEB8hesj
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -54,16 +54,24 @@ H264Converter::Input(MediaRawData* aSamp
 {
   if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
     // We need AVCC content to be able to later parse the SPS.
     // This is a no-op if the data is already AVCC.
     return NS_ERROR_FAILURE;
   }
 
   if (mInitPromiseRequest.Exists()) {
+    if (mNeedKeyframe) {
+      if (!aSample->mKeyframe) {
+        // Frames dropped, we need a new one.
+        mCallback->InputExhausted();
+        return NS_OK;
+      }
+      mNeedKeyframe = false;
+    }
     mMediaRawSamples.AppendElement(aSample);
     return NS_OK;
   }
 
   nsresult rv;
   if (!mDecoder) {
     // It is not possible to create an AVCC H264 decoder without SPS.
     // As such, creation will fail if the extra_data just extracted doesn't
@@ -74,38 +82,47 @@ H264Converter::Input(MediaRawData* aSamp
       // Ignore for the time being, the MediaRawData will be dropped.
       return NS_OK;
     }
   } else {
     rv = CheckForSPSChange(aSample);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (mNeedKeyframe && !aSample->mKeyframe) {
+    mCallback->InputExhausted();
+    return NS_OK;
+  }
+
   if (!mNeedAVCC &&
       !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(aSample)) {
     return NS_ERROR_FAILURE;
   }
 
+  mNeedKeyframe = false;
+
   aSample->mExtraData = mCurrentConfig.mExtraData;
 
   return mDecoder->Input(aSample);
 }
 
 nsresult
 H264Converter::Flush()
 {
+  mNeedKeyframe = true;
   if (mDecoder) {
     return mDecoder->Flush();
   }
   return mLastError;
 }
 
 nsresult
 H264Converter::Drain()
 {
+  mNeedKeyframe = true;
   if (mDecoder) {
     return mDecoder->Drain();
   }
   mCallback->DrainComplete();
   return mLastError;
 }
 
 nsresult
@@ -152,16 +169,19 @@ H264Converter::CreateDecoder(DecoderDoct
     mLayersBackend,
     mGMPCrashHelper
   });
 
   if (!mDecoder) {
     mLastError = NS_ERROR_FAILURE;
     return NS_ERROR_FAILURE;
   }
+
+  mNeedKeyframe = true;
+
   return NS_OK;
 }
 
 nsresult
 H264Converter::CreateDecoderAndInit(MediaRawData* aSample)
 {
   RefPtr<MediaByteBuffer> extra_data =
     mp4_demuxer::AnnexB::ExtractExtraData(aSample);
@@ -185,21 +205,32 @@ H264Converter::CreateDecoderAndInit(Medi
   }
   return rv;
 }
 
 void
 H264Converter::OnDecoderInitDone(const TrackType aTrackType)
 {
   mInitPromiseRequest.Complete();
+  bool gotInput = false;
   for (uint32_t i = 0 ; i < mMediaRawSamples.Length(); i++) {
-    if (NS_FAILED(mDecoder->Input(mMediaRawSamples[i]))) {
+    const RefPtr<MediaRawData>& sample = mMediaRawSamples[i];
+    if (mNeedKeyframe) {
+      if (!sample->mKeyframe) {
+        continue;
+      }
+      mNeedKeyframe = false;
+    }
+    if (NS_FAILED(mDecoder->Input(sample))) {
       mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
     }
   }
+  if (!gotInput) {
+    mCallback->InputExhausted();
+  }
   mMediaRawSamples.Clear();
 }
 
 void
 H264Converter::OnDecoderInitFailed(MediaDataDecoder::DecoderFailureReason aReason)
 {
   mInitPromiseRequest.Complete();
   mCallback->Error(MediaDataDecoderError::FATAL_ERROR);
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -61,13 +61,14 @@ private:
   const RefPtr<TaskQueue> mTaskQueue;
   nsTArray<RefPtr<MediaRawData>> mMediaRawSamples;
   MediaDataDecoderCallback* mCallback;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
   RefPtr<GMPCrashHelper> mGMPCrashHelper;
   bool mNeedAVCC;
   nsresult mLastError;
+  bool mNeedKeyframe = true;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_H264Converter_h