diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 7f46d85650258331bfd533f0c89ed9d4e2d68e2c..633688e8eca898516feb206ea08e69601cb1bf22 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -674,6 +674,14 @@ VideoReceiver::startRecording(const QString &videoFile) gst_element_sync_state_with_parent(_sink->mux); gst_element_sync_state_with_parent(_sink->filesink); + // Install a probe on the recording branch to drop buffers until we hit our first keyframe + // When we hit our first keyframe, we can offset the timestamps appropriately according to the first keyframe time + // This will ensure the first frame is a keyframe at t=0, and decoding can begin immediately on playback + GstPad* probepad = gst_element_get_static_pad(_sink->queue, "src"); + gst_pad_add_probe(probepad, (GstPadProbeType)(GST_PAD_PROBE_TYPE_BUFFER /* | GST_PAD_PROBE_TYPE_BLOCK */), _keyframeWatch, this, NULL); // to drop the buffer or to block the buffer? + gst_object_unref(probepad); + + // Link the recording branch to the pipeline GstPad* sinkpad = gst_element_get_static_pad(_sink->queue, "sink"); gst_pad_link(_sink->teepad, sinkpad); gst_object_unref(sinkpad); @@ -802,6 +810,33 @@ VideoReceiver::_unlinkCallBack(GstPad* pad, GstPadProbeInfo* info, gpointer user } #endif +//----------------------------------------------------------------------------- +#if defined(QGC_GST_STREAMING) +GstPadProbeReturn +VideoReceiver::_keyframeWatch(GstPad* pad, GstPadProbeInfo* info, gpointer user_data) +{ + Q_UNUSED(pad); + if(info != NULL && user_data != NULL) { + GstBuffer* buf = gst_pad_probe_info_get_buffer(info); + if(GST_BUFFER_FLAG_IS_SET(buf, GST_BUFFER_FLAG_DELTA_UNIT)) { // wait for a keyframe + return GST_PAD_PROBE_DROP; + } else { + VideoReceiver* pThis = (VideoReceiver*)user_data; + // reset the clock + GstClock* clock = gst_pipeline_get_clock(GST_PIPELINE(pThis->_pipeline)); + GstClockTime time = gst_clock_get_time(clock); + gst_object_unref(clock); + gst_element_set_base_time(pThis->_pipeline, time); // offset pipeline timestamps to start at zero again + buf->dts = 0; // The offset will not apply to this current buffer, our first frame, timestamp is zero + buf->pts = 0; + qCDebug(VideoReceiverLog) << "Got keyframe, stop dropping buffers"; + } + } + + return GST_PAD_PROBE_REMOVE; +} +#endif + //----------------------------------------------------------------------------- void VideoReceiver::_updateTimer() diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 843abdf144ea6b0d3e18529ea9abfbd06df653cb..76aa3044cf17f4f47399703e6b2f79cd3f2fddc4 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -119,6 +119,7 @@ private: static gboolean _onBusMessage (GstBus* bus, GstMessage* message, gpointer user_data); static GstPadProbeReturn _unlinkCallBack (GstPad* pad, GstPadProbeInfo* info, gpointer user_data); + static GstPadProbeReturn _keyframeWatch (GstPad* pad, GstPadProbeInfo* info, gpointer user_data); void _detachRecordingBranch (GstPadProbeInfo* info); void _shutdownRecordingBranch(); void _shutdownPipeline ();