Bug 973139 - Don't treat EOS of one stream as EOS of all streams when decoding with GStreamer. r=alessandro.d
authorChris Pearce <cpearce@mozilla.com>
Sat, 15 Feb 2014 22:32:12 +1300
changeset 169280 c4ff618cf72bfd8f5689a1eb72ce9c295e250326
parent 169279 b1feb66af4ff32db9f8abebcb565bc216148bff0
child 169281 f79959add4b3f5ba337e33ce2a24b17ee3bc36c3
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersalessandro
bugs973139
milestone30.0a1
Bug 973139 - Don't treat EOS of one stream as EOS of all streams when decoding with GStreamer. r=alessandro.d
content/media/gstreamer/GStreamerReader.cpp
content/media/gstreamer/GStreamerReader.h
--- a/content/media/gstreamer/GStreamerReader.cpp
+++ b/content/media/gstreamer/GStreamerReader.cpp
@@ -76,17 +76,18 @@ GStreamerReader::GStreamerReader(Abstrac
   mVideoSink(nullptr),
   mVideoAppSink(nullptr),
   mAudioSink(nullptr),
   mAudioAppSink(nullptr),
   mFormat(GST_VIDEO_FORMAT_UNKNOWN),
   mVideoSinkBufferCount(0),
   mAudioSinkBufferCount(0),
   mGstThreadsMonitor("media.gst.threads"),
-  mReachedEos(false),
+  mReachedAudioEos(false),
+  mReachedVideoEos(false),
 #if GST_VERSION_MAJOR >= 1
   mConfigureAlignment(true),
 #endif
   fpsNum(0),
   fpsDen(0)
 {
   MOZ_COUNT_CTOR(GStreamerReader);
 
@@ -529,17 +530,18 @@ nsresult GStreamerReader::ResetDecode()
     res = NS_ERROR_FAILURE;
   }
 
   mVideoQueue.Reset();
   mAudioQueue.Reset();
 
   mVideoSinkBufferCount = 0;
   mAudioSinkBufferCount = 0;
-  mReachedEos = false;
+  mReachedAudioEos = false;
+  mReachedVideoEos = false;
 #if GST_VERSION_MAJOR >= 1
   mConfigureAlignment = true;
 #endif
 
   LOG(PR_LOG_DEBUG, ("reset decode done"));
 
   return res;
 }
@@ -548,17 +550,17 @@ bool GStreamerReader::DecodeAudioData()
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   GstBuffer *buffer = nullptr;
 
   {
     ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
 
-    if (mReachedEos) {
+    if (mReachedAudioEos && !mAudioSinkBufferCount) {
       return false;
     }
 
     /* Wait something to be decoded before return or continue */
     if (!mAudioSinkBufferCount) {
       if(!mVideoSinkBufferCount) {
         /* We have nothing decoded so it makes no sense to return to the state machine
          * as it will call us back immediately, we'll return again and so on, wasting
@@ -632,17 +634,17 @@ bool GStreamerReader::DecodeVideoFrame(b
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
 
   GstBuffer *buffer = nullptr;
 
   {
     ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
 
-    if (mReachedEos) {
+    if (mReachedVideoEos && !mVideoSinkBufferCount) {
       return false;
     }
 
     /* Wait something to be decoded before return or continue */
     if (!mVideoSinkBufferCount) {
       if (!mAudioSinkBufferCount) {
         /* We have nothing decoded so it makes no sense to return to the state machine
          * as it will call us back immediately, we'll return again and so on, wasting
@@ -686,17 +688,17 @@ bool GStreamerReader::DecodeVideoFrame(b
     ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
     timestamp = gst_segment_to_stream_time(&mVideoSegment,
                                            GST_FORMAT_TIME, timestamp);
   }
   NS_ASSERTION(GST_CLOCK_TIME_IS_VALID(timestamp),
                "frame has invalid timestamp");
 
   timestamp = GST_TIME_AS_USECONDS(timestamp);
-  int64_t duration;
+  int64_t duration = 0;
   if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer)))
     duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer));
   else if (fpsNum && fpsDen)
     /* add 1-frame duration */
     duration = gst_util_uint64_scale(GST_USECOND, fpsDen, fpsNum);
 
   if (timestamp < aTimeThreshold) {
     LOG(PR_LOG_DEBUG, ("skipping frame %" GST_TIME_FORMAT
@@ -704,17 +706,17 @@ bool GStreamerReader::DecodeVideoFrame(b
                        GST_TIME_ARGS(timestamp * 1000),
                        GST_TIME_ARGS(aTimeThreshold * 1000)));
     gst_buffer_unref(buffer);
     return true;
   }
 
   if (!buffer)
     /* no more frames */
-    return false;
+    return true;
 
 #if GST_VERSION_MAJOR >= 1
   if (mConfigureAlignment && buffer->pool) {
     GstStructure *config = gst_buffer_pool_get_config(buffer->pool);
     GstVideoAlignment align;
     if (gst_buffer_pool_config_get_video_alignment(config, &align))
       gst_video_info_align(&mVideoInfo, &align);
     gst_structure_free(config);
@@ -1055,26 +1057,34 @@ void GStreamerReader::NewAudioBuffer()
    */
   mAudioSinkBufferCount++;
   mon.NotifyAll();
 }
 
 void GStreamerReader::EosCb(GstAppSink* aSink, gpointer aUserData)
 {
   GStreamerReader* reader = reinterpret_cast<GStreamerReader*>(aUserData);
-  reader->Eos();
+  reader->Eos(aSink);
 }
 
-void GStreamerReader::Eos()
+void GStreamerReader::Eos(GstAppSink* aSink)
 {
   /* We reached the end of the stream */
   {
     ReentrantMonitorAutoEnter mon(mGstThreadsMonitor);
     /* Potentially unblock DecodeVideoFrame and DecodeAudioData */
-    mReachedEos = true;
+    if (aSink == mVideoAppSink) {
+      mReachedVideoEos = true;
+    } else if (aSink == mAudioAppSink) {
+      mReachedAudioEos = true;
+    } else {
+      // Assume this is an error causing an EOS.
+      mReachedAudioEos = true;
+      mReachedVideoEos = true;
+    }
     mon.NotifyAll();
   }
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     /* Potentially unblock the decode thread in ::DecodeLoop */
     mon.NotifyAll();
   }
--- a/content/media/gstreamer/GStreamerReader.h
+++ b/content/media/gstreamer/GStreamerReader.h
@@ -149,17 +149,20 @@ private:
 
   /* Called when buffers reach the sinks */
   static GstFlowReturn NewBufferCb(GstAppSink* aSink, gpointer aUserData);
   void NewVideoBuffer();
   void NewAudioBuffer();
 
   /* Called at end of stream, when decoding has finished */
   static void EosCb(GstAppSink* aSink, gpointer aUserData);
-  void Eos();
+  /* Notifies that a sink will no longer receive any more data. If nullptr
+   * is passed to this, we'll assume all streams have reached EOS (for example
+   * an error has occurred). */
+  void Eos(GstAppSink* aSink = nullptr);
 
   /* Called when an element is added inside playbin. We use it to find the
    * decodebin instance.
    */
   static void PlayElementAddedCb(GstBin *aBin, GstElement *aElement,
                                  gpointer *aUserData);
 
   /* Called during decoding, to decide whether a (sub)stream should be decoded or
@@ -216,17 +219,18 @@ private:
    * stream_duration]. They're set when the pipeline is started or after a seek.
    * Concurrent access guarded with mGstThreadsMonitor.
    */
   GstSegment mVideoSegment;
   GstSegment mAudioSegment;
   /* bool used to signal when gst has detected the end of stream and
    * DecodeAudioData and DecodeVideoFrame should not expect any more data
    */
-  bool mReachedEos;
+  bool mReachedAudioEos;
+  bool mReachedVideoEos;
 #if GST_VERSION_MAJOR >= 1
   bool mConfigureAlignment;
 #endif
   int fpsNum;
   int fpsDen;
 };
 
 } // namespace mozilla