mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 01:45:33 +00:00
decklinksrc: Stop using the "hardware" timestamps and directly use the pipeline clock
The hardware timestamps have no relation to when frames were produced, only when frames arrived somewhere in the hardware. Especially there is no guarantee that audio and video will have the same hardware timestamps although they belong together, and even more important: the rate with which the hardware timestamps increase is completely unrelated to the rate with which the frames are captured! As such we can as well use the pipeline clock directly and stop doing complicated calculations. Also as a side effect this allows now running without any pipeline clock, by directly making use of the stream times as reported by the driver. https://bugzilla.gnome.org/show_bug.cgi?id=774850
This commit is contained in:
parent
423e4593aa
commit
881a08671e
6 changed files with 141 additions and 369 deletions
|
@ -617,7 +617,6 @@ struct _GstDecklinkClock
|
|||
{
|
||||
GstSystemClock clock;
|
||||
|
||||
GstDecklinkInput *input;
|
||||
GstDecklinkOutput *output;
|
||||
};
|
||||
|
||||
|
@ -722,99 +721,108 @@ public:
|
|||
GstElement *videosrc = NULL, *audiosrc = NULL;
|
||||
void (*got_video_frame) (GstElement * videosrc,
|
||||
IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode,
|
||||
GstClockTime capture_time, GstClockTime capture_duration, guint hours,
|
||||
guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags) =
|
||||
NULL;
|
||||
GstClockTime capture_time, GstClockTime stream_time,
|
||||
GstClockTime stream_duration, guint hours, guint minutes, guint seconds,
|
||||
guint frames, BMDTimecodeFlags bflags) = NULL;
|
||||
void (*got_audio_packet) (GstElement * videosrc,
|
||||
IDeckLinkAudioInputPacket * packet, GstClockTime capture_time,
|
||||
gboolean discont) = NULL;
|
||||
GstClockTime packet_time) = NULL;
|
||||
GstDecklinkModeEnum mode;
|
||||
BMDTimeValue capture_time = GST_CLOCK_TIME_NONE, capture_duration =
|
||||
GST_CLOCK_TIME_NONE;
|
||||
GstClockTime capture_time = GST_CLOCK_TIME_NONE;
|
||||
GstClockTime base_time;
|
||||
GstClock *clock = NULL;
|
||||
HRESULT res;
|
||||
IDeckLinkTimecode *dtc;
|
||||
uint8_t hours, minutes, seconds, frames;
|
||||
BMDTimecodeFlags bflags;
|
||||
|
||||
hours = minutes = seconds = frames = bflags = 0;
|
||||
if (video_frame == NULL)
|
||||
goto no_video_frame;
|
||||
|
||||
res =
|
||||
video_frame->GetHardwareReferenceTimestamp (GST_SECOND, &capture_time,
|
||||
&capture_duration);
|
||||
if (res != S_OK) {
|
||||
GST_ERROR ("Failed to get capture time: 0x%08x", res);
|
||||
capture_time = GST_CLOCK_TIME_NONE;
|
||||
capture_duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
if (m_input->videosrc) {
|
||||
/* FIXME: Avoid circularity between gstdecklink.cpp and
|
||||
* gstdecklinkvideosrc.cpp */
|
||||
videosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->videosrc));
|
||||
res =
|
||||
video_frame->
|
||||
GetTimecode (GST_DECKLINK_VIDEO_SRC (videosrc)->timecode_format,
|
||||
&dtc);
|
||||
|
||||
if (res != S_OK) {
|
||||
GST_DEBUG_OBJECT (videosrc, "Failed to get timecode: 0x%08x", res);
|
||||
dtc = NULL;
|
||||
} else {
|
||||
res = dtc->GetComponents (&hours, &minutes, &seconds, &frames);
|
||||
if (res != S_OK) {
|
||||
GST_ERROR ("Could not get components for timecode %p: 0x%08x", dtc,
|
||||
res);
|
||||
hours = 0;
|
||||
minutes = 0;
|
||||
seconds = 0;
|
||||
frames = 0;
|
||||
bflags = 0;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (videosrc, "Got timecode %02d:%02d:%02d:%02d", hours,
|
||||
minutes, seconds, frames);
|
||||
bflags = dtc->GetFlags ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_lock (&m_input->lock);
|
||||
|
||||
if (capture_time > (BMDTimeValue) m_input->clock_start_time)
|
||||
capture_time -= m_input->clock_start_time;
|
||||
else
|
||||
capture_time = 0;
|
||||
|
||||
if (capture_time > (BMDTimeValue) m_input->clock_offset)
|
||||
capture_time -= m_input->clock_offset;
|
||||
else
|
||||
capture_time = 0;
|
||||
|
||||
if (m_input->videosrc) {
|
||||
videosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->videosrc));
|
||||
clock = gst_element_get_clock (videosrc);
|
||||
base_time = gst_element_get_base_time (videosrc);
|
||||
got_video_frame = m_input->got_video_frame;
|
||||
}
|
||||
mode = gst_decklink_get_mode_enum_from_bmd (m_input->mode->mode);
|
||||
|
||||
if (m_input->audiosrc) {
|
||||
audiosrc = GST_ELEMENT_CAST (gst_object_ref (m_input->audiosrc));
|
||||
if (!clock) {
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (audiosrc));
|
||||
base_time = gst_element_get_base_time (audiosrc);
|
||||
}
|
||||
got_audio_packet = m_input->got_audio_packet;
|
||||
}
|
||||
g_mutex_unlock (&m_input->lock);
|
||||
|
||||
if (got_video_frame && videosrc) {
|
||||
got_video_frame (videosrc, video_frame, mode, capture_time,
|
||||
capture_duration, (guint8) hours, (guint8) minutes, (guint8) seconds,
|
||||
(guint8) frames, bflags);
|
||||
if (clock) {
|
||||
capture_time = gst_clock_get_time (clock);
|
||||
if (capture_time > base_time)
|
||||
capture_time -= base_time;
|
||||
else
|
||||
capture_time = 0;
|
||||
}
|
||||
|
||||
if (got_video_frame && videosrc && video_frame) {
|
||||
BMDTimeValue stream_time = GST_CLOCK_TIME_NONE;
|
||||
BMDTimeValue stream_duration = GST_CLOCK_TIME_NONE;
|
||||
IDeckLinkTimecode *dtc;
|
||||
uint8_t hours, minutes, seconds, frames;
|
||||
BMDTimecodeFlags bflags;
|
||||
|
||||
hours = minutes = seconds = frames = bflags = 0;
|
||||
|
||||
res =
|
||||
video_frame->GetStreamTime (&stream_time, &stream_duration,
|
||||
GST_SECOND);
|
||||
if (res != S_OK) {
|
||||
GST_ERROR ("Failed to get stream time: 0x%08x", res);
|
||||
stream_time = GST_CLOCK_TIME_NONE;
|
||||
stream_duration = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
if (m_input->videosrc) {
|
||||
/* FIXME: Avoid circularity between gstdecklink.cpp and
|
||||
* gstdecklinkvideosrc.cpp */
|
||||
res =
|
||||
video_frame->
|
||||
GetTimecode (GST_DECKLINK_VIDEO_SRC (videosrc)->timecode_format,
|
||||
&dtc);
|
||||
|
||||
if (res != S_OK) {
|
||||
GST_DEBUG_OBJECT (videosrc, "Failed to get timecode: 0x%08x", res);
|
||||
dtc = NULL;
|
||||
} else {
|
||||
res = dtc->GetComponents (&hours, &minutes, &seconds, &frames);
|
||||
if (res != S_OK) {
|
||||
GST_ERROR ("Could not get components for timecode %p: 0x%08x", dtc,
|
||||
res);
|
||||
hours = 0;
|
||||
minutes = 0;
|
||||
seconds = 0;
|
||||
frames = 0;
|
||||
bflags = 0;
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (videosrc, "Got timecode %02d:%02d:%02d:%02d",
|
||||
hours, minutes, seconds, frames);
|
||||
bflags = dtc->GetFlags ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
got_video_frame (videosrc, video_frame, mode, capture_time,
|
||||
stream_time, stream_duration, (guint8) hours, (guint8) minutes,
|
||||
(guint8) seconds, (guint8) frames, bflags);
|
||||
}
|
||||
|
||||
no_video_frame:
|
||||
if (got_audio_packet && audiosrc && audio_packet) {
|
||||
BMDTimeValue packet_time = GST_CLOCK_TIME_NONE;
|
||||
|
||||
res = audio_packet->GetPacketTime (&packet_time, GST_SECOND);
|
||||
if (res != S_OK) {
|
||||
GST_ERROR ("Failed to get stream time: 0x%08x", res);
|
||||
packet_time = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
m_input->got_audio_packet (audiosrc, audio_packet, capture_time,
|
||||
m_input->audio_discont);
|
||||
m_input->audio_discont = FALSE;
|
||||
packet_time);
|
||||
} else {
|
||||
m_input->audio_discont = TRUE;
|
||||
if (!audio_packet)
|
||||
GST_DEBUG ("Received no audio packet at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time));
|
||||
|
@ -822,6 +830,7 @@ public:
|
|||
|
||||
gst_object_replace ((GstObject **) & videosrc, NULL);
|
||||
gst_object_replace ((GstObject **) & audiosrc, NULL);
|
||||
gst_object_replace ((GstObject **) & clock, NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -919,9 +928,6 @@ init_devices (gpointer data)
|
|||
ret);
|
||||
} else {
|
||||
devices[i].input.device = decklink;
|
||||
devices[i].input.clock = gst_decklink_clock_new ("GstDecklinkInputClock");
|
||||
GST_DECKLINK_CLOCK_CAST (devices[i].input.clock)->input =
|
||||
&devices[i].input;
|
||||
devices[i].input.
|
||||
input->SetCallback (new GStreamerDecklinkInputCallback (&devices[i].
|
||||
input));
|
||||
|
@ -1073,7 +1079,6 @@ gst_decklink_acquire_nth_input (gint n, GstElement * src, gboolean is_audio)
|
|||
g_mutex_lock (&input->lock);
|
||||
if (is_audio && !input->audiosrc) {
|
||||
input->audiosrc = GST_ELEMENT_CAST (gst_object_ref (src));
|
||||
input->audio_discont = TRUE;
|
||||
g_mutex_unlock (&input->lock);
|
||||
return input;
|
||||
} else if (!input->videosrc) {
|
||||
|
@ -1148,88 +1153,46 @@ gst_decklink_clock_get_internal_time (GstClock * clock)
|
|||
BMDTimeValue time;
|
||||
HRESULT ret;
|
||||
|
||||
if (self->input != NULL) {
|
||||
g_mutex_lock (&self->input->lock);
|
||||
start_time = self->input->clock_start_time;
|
||||
offset = self->input->clock_offset;
|
||||
last_time = self->input->clock_last_time;
|
||||
time = -1;
|
||||
if (!self->input->started) {
|
||||
result = last_time;
|
||||
ret = -1;
|
||||
} else {
|
||||
ret =
|
||||
self->input->input->GetHardwareReferenceClock (GST_SECOND, &time,
|
||||
NULL, NULL);
|
||||
if (ret == S_OK && time >= 0) {
|
||||
result = time;
|
||||
if (start_time == GST_CLOCK_TIME_NONE)
|
||||
start_time = self->input->clock_start_time = result;
|
||||
|
||||
if (result > start_time)
|
||||
result -= start_time;
|
||||
else
|
||||
result = 0;
|
||||
|
||||
if (self->input->clock_restart) {
|
||||
self->input->clock_offset = result - last_time;
|
||||
offset = self->input->clock_offset;
|
||||
self->input->clock_restart = FALSE;
|
||||
}
|
||||
result = MAX (last_time, result);
|
||||
result -= offset;
|
||||
result = MAX (last_time, result);
|
||||
} else {
|
||||
result = last_time;
|
||||
}
|
||||
|
||||
self->input->clock_last_time = result;
|
||||
}
|
||||
result += self->input->clock_epoch;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
} else if (self->output != NULL) {
|
||||
g_mutex_lock (&self->output->lock);
|
||||
start_time = self->output->clock_start_time;
|
||||
offset = self->output->clock_offset;
|
||||
last_time = self->output->clock_last_time;
|
||||
time = -1;
|
||||
if (!self->output->started) {
|
||||
result = last_time;
|
||||
ret = -1;
|
||||
} else {
|
||||
ret =
|
||||
self->output->output->GetHardwareReferenceClock (GST_SECOND, &time,
|
||||
NULL, NULL);
|
||||
if (ret == S_OK && time >= 0) {
|
||||
result = time;
|
||||
|
||||
if (start_time == GST_CLOCK_TIME_NONE)
|
||||
start_time = self->output->clock_start_time = result;
|
||||
|
||||
if (result > start_time)
|
||||
result -= start_time;
|
||||
else
|
||||
result = 0;
|
||||
|
||||
if (self->output->clock_restart) {
|
||||
self->output->clock_offset = result - last_time;
|
||||
offset = self->output->clock_offset;
|
||||
self->output->clock_restart = FALSE;
|
||||
}
|
||||
result = MAX (last_time, result);
|
||||
result -= offset;
|
||||
result = MAX (last_time, result);
|
||||
} else {
|
||||
result = last_time;
|
||||
}
|
||||
|
||||
self->output->clock_last_time = result;
|
||||
}
|
||||
result += self->output->clock_epoch;
|
||||
g_mutex_unlock (&self->output->lock);
|
||||
g_mutex_lock (&self->output->lock);
|
||||
start_time = self->output->clock_start_time;
|
||||
offset = self->output->clock_offset;
|
||||
last_time = self->output->clock_last_time;
|
||||
time = -1;
|
||||
if (!self->output->started) {
|
||||
result = last_time;
|
||||
ret = -1;
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
ret =
|
||||
self->output->output->GetHardwareReferenceClock (GST_SECOND, &time,
|
||||
NULL, NULL);
|
||||
if (ret == S_OK && time >= 0) {
|
||||
result = time;
|
||||
|
||||
if (start_time == GST_CLOCK_TIME_NONE)
|
||||
start_time = self->output->clock_start_time = result;
|
||||
|
||||
if (result > start_time)
|
||||
result -= start_time;
|
||||
else
|
||||
result = 0;
|
||||
|
||||
if (self->output->clock_restart) {
|
||||
self->output->clock_offset = result - last_time;
|
||||
offset = self->output->clock_offset;
|
||||
self->output->clock_restart = FALSE;
|
||||
}
|
||||
result = MAX (last_time, result);
|
||||
result -= offset;
|
||||
result = MAX (last_time, result);
|
||||
} else {
|
||||
result = last_time;
|
||||
}
|
||||
|
||||
self->output->clock_last_time = result;
|
||||
}
|
||||
result += self->output->clock_epoch;
|
||||
g_mutex_unlock (&self->output->lock);
|
||||
|
||||
GST_LOG_OBJECT (clock,
|
||||
"result %" GST_TIME_FORMAT " time %" GST_TIME_FORMAT " last time %"
|
||||
GST_TIME_FORMAT " offset %" GST_TIME_FORMAT " start time %"
|
||||
|
|
|
@ -196,25 +196,21 @@ struct _GstDecklinkInput {
|
|||
IDeckLinkInput *input;
|
||||
IDeckLinkConfiguration *config;
|
||||
IDeckLinkAttributes *attributes;
|
||||
GstClock *clock;
|
||||
GstClockTime clock_start_time, clock_offset, clock_last_time, clock_epoch;
|
||||
gboolean started, clock_restart;
|
||||
|
||||
/* Everything below protected by mutex */
|
||||
GMutex lock;
|
||||
|
||||
/* Set by the video source */
|
||||
void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time, GstClockTime capture_duration, guint hours, guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags);
|
||||
void (*got_video_frame) (GstElement *videosrc, IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode, GstClockTime capture_time, GstClockTime stream_time, GstClockTime stream_duration, guint hours, guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags);
|
||||
/* Configured mode or NULL */
|
||||
const GstDecklinkMode *mode;
|
||||
BMDPixelFormat format;
|
||||
|
||||
/* Set by the audio source */
|
||||
void (*got_audio_packet) (GstElement *videosrc, IDeckLinkAudioInputPacket * packet, GstClockTime capture_time, gboolean discont);
|
||||
void (*got_audio_packet) (GstElement *videosrc, IDeckLinkAudioInputPacket * packet, GstClockTime capture_time, GstClockTime packet_time);
|
||||
|
||||
GstElement *audiosrc;
|
||||
gboolean audio_enabled;
|
||||
gboolean audio_discont;
|
||||
GstElement *videosrc;
|
||||
gboolean video_enabled;
|
||||
void (*start_streams) (GstElement *videosrc);
|
||||
|
|
|
@ -57,7 +57,6 @@ typedef struct
|
|||
{
|
||||
IDeckLinkAudioInputPacket *packet;
|
||||
GstClockTime capture_time;
|
||||
gboolean discont;
|
||||
} CapturePacket;
|
||||
|
||||
static void
|
||||
|
@ -422,27 +421,13 @@ gst_decklink_audio_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
|||
static void
|
||||
gst_decklink_audio_src_got_packet (GstElement * element,
|
||||
IDeckLinkAudioInputPacket * packet, GstClockTime capture_time,
|
||||
gboolean discont)
|
||||
GstClockTime packet_time)
|
||||
{
|
||||
GstDecklinkAudioSrc *self = GST_DECKLINK_AUDIO_SRC_CAST (element);
|
||||
GstDecklinkVideoSrc *videosrc = NULL;
|
||||
|
||||
GST_LOG_OBJECT (self, "Got audio packet at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time));
|
||||
|
||||
g_mutex_lock (&self->input->lock);
|
||||
if (self->input->videosrc)
|
||||
videosrc =
|
||||
GST_DECKLINK_VIDEO_SRC_CAST (gst_object_ref (self->input->videosrc));
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
if (videosrc) {
|
||||
gst_decklink_video_src_convert_to_external_clock (videosrc, &capture_time,
|
||||
NULL);
|
||||
gst_object_unref (videosrc);
|
||||
GST_LOG_OBJECT (self, "Actual timestamp %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time));
|
||||
}
|
||||
GST_LOG_OBJECT (self,
|
||||
"Got audio packet at %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time), GST_TIME_ARGS (packet_time));
|
||||
|
||||
g_mutex_lock (&self->lock);
|
||||
if (!self->flushing) {
|
||||
|
@ -457,8 +442,8 @@ gst_decklink_audio_src_got_packet (GstElement * element,
|
|||
|
||||
p = (CapturePacket *) g_malloc0 (sizeof (CapturePacket));
|
||||
p->packet = packet;
|
||||
p->capture_time = capture_time;
|
||||
p->discont = discont;
|
||||
p->capture_time =
|
||||
capture_time != GST_CLOCK_TIME_NONE ? capture_time : packet_time;
|
||||
packet->AddRef ();
|
||||
g_queue_push_tail (&self->current_packets, p);
|
||||
g_cond_signal (&self->cond);
|
||||
|
@ -523,7 +508,6 @@ retry:
|
|||
ap->input->AddRef ();
|
||||
|
||||
timestamp = p->capture_time;
|
||||
discont = p->discont;
|
||||
|
||||
// Jitter and discontinuity handling, based on audiobasesrc
|
||||
start_time = timestamp;
|
||||
|
@ -538,7 +522,7 @@ retry:
|
|||
|
||||
duration = end_time - start_time;
|
||||
|
||||
if (discont || self->next_offset == (guint64) - 1) {
|
||||
if (self->next_offset == (guint64) - 1) {
|
||||
discont = TRUE;
|
||||
} else {
|
||||
guint64 diff, max_sample_diff;
|
||||
|
|
|
@ -955,7 +955,8 @@ gst_decklink_video_sink_change_state (GstElement * element,
|
|||
gst_decklink_video_sink_stop (self);
|
||||
break;
|
||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:{
|
||||
if (gst_decklink_video_sink_stop_scheduled_playback (self) == GST_STATE_CHANGE_FAILURE)
|
||||
if (gst_decklink_video_sink_stop_scheduled_playback (self) ==
|
||||
GST_STATE_CHANGE_FAILURE)
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -89,7 +89,6 @@ static void gst_decklink_video_src_finalize (GObject * object);
|
|||
static GstStateChangeReturn
|
||||
gst_decklink_video_src_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
static GstClock *gst_decklink_video_src_provide_clock (GstElement * element);
|
||||
|
||||
static gboolean gst_decklink_video_src_set_caps (GstBaseSrc * bsrc,
|
||||
GstCaps * caps);
|
||||
|
@ -128,8 +127,6 @@ gst_decklink_video_src_class_init (GstDecklinkVideoSrcClass * klass)
|
|||
|
||||
element_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_decklink_video_src_change_state);
|
||||
element_class->provide_clock =
|
||||
GST_DEBUG_FUNCPTR (gst_decklink_video_src_provide_clock);
|
||||
|
||||
basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_decklink_video_src_get_caps);
|
||||
basesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_decklink_video_src_set_caps);
|
||||
|
@ -434,97 +431,19 @@ gst_decklink_video_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
|
|||
return caps;
|
||||
}
|
||||
|
||||
void
|
||||
gst_decklink_video_src_convert_to_external_clock (GstDecklinkVideoSrc * self,
|
||||
GstClockTime * timestamp, GstClockTime * duration)
|
||||
{
|
||||
GstClock *clock;
|
||||
|
||||
g_assert (timestamp != NULL);
|
||||
|
||||
if (*timestamp == GST_CLOCK_TIME_NONE)
|
||||
return;
|
||||
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||
if (clock && clock != self->input->clock) {
|
||||
GstClockTime internal, external, rate_n, rate_d;
|
||||
GstClockTimeDiff external_start_time_diff;
|
||||
|
||||
gst_clock_get_calibration (self->input->clock, &internal, &external,
|
||||
&rate_n, &rate_d);
|
||||
|
||||
if (rate_n != rate_d && self->internal_base_time != GST_CLOCK_TIME_NONE) {
|
||||
GstClockTime internal_timestamp = *timestamp;
|
||||
|
||||
// Convert to the running time corresponding to both clock times
|
||||
internal -= self->internal_base_time;
|
||||
external -= self->external_base_time;
|
||||
|
||||
// Get the difference in the internal time, note
|
||||
// that the capture time is internal time.
|
||||
// Then scale this difference and offset it to
|
||||
// our external time. Now we have the running time
|
||||
// according to our external clock.
|
||||
//
|
||||
// For the duration we just scale
|
||||
*timestamp =
|
||||
gst_clock_adjust_with_calibration (NULL, internal_timestamp, internal,
|
||||
external, rate_n, rate_d);
|
||||
|
||||
GST_LOG_OBJECT (self,
|
||||
"Converted %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT " (external: %"
|
||||
GST_TIME_FORMAT " internal %" GST_TIME_FORMAT " rate: %lf)",
|
||||
GST_TIME_ARGS (internal_timestamp), GST_TIME_ARGS (*timestamp),
|
||||
GST_TIME_ARGS (external), GST_TIME_ARGS (internal),
|
||||
((gdouble) rate_n) / ((gdouble) rate_d));
|
||||
|
||||
if (duration) {
|
||||
GstClockTime internal_duration = *duration;
|
||||
|
||||
*duration = gst_util_uint64_scale (internal_duration, rate_d, rate_n);
|
||||
|
||||
GST_LOG_OBJECT (self,
|
||||
"Converted duration %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT
|
||||
" (external: %" GST_TIME_FORMAT " internal %" GST_TIME_FORMAT
|
||||
" rate: %lf)", GST_TIME_ARGS (internal_duration),
|
||||
GST_TIME_ARGS (*duration), GST_TIME_ARGS (external),
|
||||
GST_TIME_ARGS (internal), ((gdouble) rate_n) / ((gdouble) rate_d));
|
||||
}
|
||||
} else {
|
||||
GST_LOG_OBJECT (self, "No clock conversion needed, relative rate is 1.0");
|
||||
}
|
||||
|
||||
// Add the diff between the external time when we
|
||||
// went to playing and the external time when the
|
||||
// pipeline went to playing. Otherwise we will
|
||||
// always start outputting from 0 instead of the
|
||||
// current running time.
|
||||
external_start_time_diff =
|
||||
gst_element_get_base_time (GST_ELEMENT_CAST (self));
|
||||
external_start_time_diff =
|
||||
self->external_base_time - external_start_time_diff;
|
||||
*timestamp += external_start_time_diff;
|
||||
} else {
|
||||
GST_LOG_OBJECT (self, "No clock conversion needed, same clocks");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_decklink_video_src_got_frame (GstElement * element,
|
||||
IDeckLinkVideoInputFrame * frame, GstDecklinkModeEnum mode,
|
||||
GstClockTime capture_time, GstClockTime capture_duration, guint hours,
|
||||
guint minutes, guint seconds, guint frames, BMDTimecodeFlags bflags)
|
||||
GstClockTime capture_time, GstClockTime stream_time,
|
||||
GstClockTime stream_duration, guint hours, guint minutes, guint seconds,
|
||||
guint frames, BMDTimecodeFlags bflags)
|
||||
{
|
||||
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||
|
||||
GST_LOG_OBJECT (self, "Got video frame at %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time));
|
||||
|
||||
gst_decklink_video_src_convert_to_external_clock (self, &capture_time,
|
||||
&capture_duration);
|
||||
|
||||
GST_LOG_OBJECT (self, "Actual timestamp %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (capture_time));
|
||||
GST_LOG_OBJECT (self,
|
||||
"Got video frame at %" GST_TIME_FORMAT " / %" GST_TIME_FORMAT " (%"
|
||||
GST_TIME_FORMAT ")", GST_TIME_ARGS (capture_time),
|
||||
GST_TIME_ARGS (stream_time), GST_TIME_ARGS (stream_duration));
|
||||
|
||||
g_mutex_lock (&self->lock);
|
||||
if (!self->flushing) {
|
||||
|
@ -542,8 +461,9 @@ gst_decklink_video_src_got_frame (GstElement * element,
|
|||
|
||||
f = (CaptureFrame *) g_malloc0 (sizeof (CaptureFrame));
|
||||
f->frame = frame;
|
||||
f->capture_time = capture_time;
|
||||
f->capture_duration = capture_duration;
|
||||
f->capture_time =
|
||||
capture_time != GST_CLOCK_TIME_NONE ? capture_time : stream_time;
|
||||
f->capture_duration = stream_duration;
|
||||
f->mode = mode;
|
||||
f->format = frame->GetPixelFormat ();
|
||||
bmode = gst_decklink_get_mode (mode);
|
||||
|
@ -659,7 +579,7 @@ gst_decklink_video_src_create (GstPushSrc * bsrc, GstBuffer ** buffer)
|
|||
vf->input = self->input->input;
|
||||
vf->input->AddRef ();
|
||||
|
||||
flags = f->frame->GetFlags();
|
||||
flags = f->frame->GetFlags ();
|
||||
if (flags & bmdFrameHasNoInputSource) {
|
||||
if (!self->no_signal) {
|
||||
self->no_signal = TRUE;
|
||||
|
@ -771,10 +691,6 @@ gst_decklink_video_src_open (GstDecklinkVideoSrc * self)
|
|||
self->input->mode = mode;
|
||||
self->input->got_video_frame = gst_decklink_video_src_got_frame;
|
||||
self->input->start_streams = gst_decklink_video_src_start_streams;
|
||||
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
|
||||
self->input->clock_epoch += self->input->clock_last_time;
|
||||
self->input->clock_last_time = 0;
|
||||
self->input->clock_offset = 0;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
return TRUE;
|
||||
|
@ -832,42 +748,14 @@ gst_decklink_video_src_start_streams (GstElement * element)
|
|||
|| self->input->audio_enabled)
|
||||
&& (GST_STATE (self) == GST_STATE_PLAYING
|
||||
|| GST_STATE_PENDING (self) == GST_STATE_PLAYING)) {
|
||||
GstClock *clock;
|
||||
|
||||
clock = gst_element_get_clock (element);
|
||||
if (!clock) {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED, (NULL),
|
||||
("Streams supposed to start but we have no clock"));
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Starting streams");
|
||||
|
||||
res = self->input->input->StartStreams ();
|
||||
if (res != S_OK) {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||
(NULL), ("Failed to start streams: 0x%08x", res));
|
||||
gst_object_unref (clock);
|
||||
return;
|
||||
}
|
||||
|
||||
self->input->started = TRUE;
|
||||
self->input->clock_restart = TRUE;
|
||||
|
||||
// Need to unlock to get the clock time
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
// Current times of internal and external clock when we go to
|
||||
// playing. We need this to convert the pipeline running time
|
||||
// to the running time of the hardware
|
||||
//
|
||||
// We can't use the normal base time for the external clock
|
||||
// because we might go to PLAYING later than the pipeline
|
||||
self->internal_base_time = gst_clock_get_internal_time (self->input->clock);
|
||||
self->external_base_time = gst_clock_get_internal_time (clock);
|
||||
|
||||
gst_object_unref (clock);
|
||||
g_mutex_lock (&self->input->lock);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (self, "Not starting streams yet");
|
||||
}
|
||||
|
@ -893,35 +781,8 @@ gst_decklink_video_src_change_state (GstElement * element,
|
|||
}
|
||||
break;
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
g_mutex_lock (&self->input->lock);
|
||||
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
|
||||
self->input->clock_epoch += self->input->clock_last_time;
|
||||
self->input->clock_last_time = 0;
|
||||
self->input->clock_offset = 0;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
gst_element_post_message (element,
|
||||
gst_message_new_clock_provide (GST_OBJECT_CAST (element),
|
||||
self->input->clock, TRUE));
|
||||
self->flushing = FALSE;
|
||||
break;
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||
GstClock *clock;
|
||||
|
||||
clock = gst_element_get_clock (GST_ELEMENT_CAST (self));
|
||||
if (clock) {
|
||||
if (clock != self->input->clock) {
|
||||
gst_clock_set_master (self->input->clock, clock);
|
||||
}
|
||||
|
||||
gst_object_unref (clock);
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (self, STREAM, FAILED,
|
||||
(NULL), ("Need a clock to go to PLAYING"));
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -934,19 +795,7 @@ gst_decklink_video_src_change_state (GstElement * element,
|
|||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_element_post_message (element,
|
||||
gst_message_new_clock_lost (GST_OBJECT_CAST (element),
|
||||
self->input->clock));
|
||||
gst_clock_set_master (self->input->clock, NULL);
|
||||
// Reset calibration to make the clock reusable next time we use it
|
||||
gst_clock_set_calibration (self->input->clock, 0, 0, 1, 1);
|
||||
g_mutex_lock (&self->input->lock);
|
||||
self->input->clock_start_time = GST_CLOCK_TIME_NONE;
|
||||
self->input->clock_epoch += self->input->clock_last_time;
|
||||
self->input->clock_last_time = 0;
|
||||
self->input->clock_offset = 0;
|
||||
self->no_signal = FALSE;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
gst_decklink_video_src_stop (self);
|
||||
break;
|
||||
|
@ -954,9 +803,6 @@ gst_decklink_video_src_change_state (GstElement * element,
|
|||
HRESULT res;
|
||||
|
||||
GST_DEBUG_OBJECT (self, "Stopping streams");
|
||||
g_mutex_lock (&self->input->lock);
|
||||
self->input->started = FALSE;
|
||||
g_mutex_unlock (&self->input->lock);
|
||||
|
||||
res = self->input->input->StopStreams ();
|
||||
if (res != S_OK) {
|
||||
|
@ -964,8 +810,6 @@ gst_decklink_video_src_change_state (GstElement * element,
|
|||
(NULL), ("Failed to stop streams: 0x%08x", res));
|
||||
ret = GST_STATE_CHANGE_FAILURE;
|
||||
}
|
||||
self->internal_base_time = GST_CLOCK_TIME_NONE;
|
||||
self->external_base_time = GST_CLOCK_TIME_NONE;
|
||||
break;
|
||||
}
|
||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:{
|
||||
|
@ -986,14 +830,3 @@ out:
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstClock *
|
||||
gst_decklink_video_src_provide_clock (GstElement * element)
|
||||
{
|
||||
GstDecklinkVideoSrc *self = GST_DECKLINK_VIDEO_SRC_CAST (element);
|
||||
|
||||
if (!self->input)
|
||||
return NULL;
|
||||
|
||||
return GST_CLOCK_CAST (gst_object_ref (self->input->clock));
|
||||
}
|
||||
|
|
|
@ -69,9 +69,6 @@ struct _GstDecklinkVideoSrc
|
|||
gboolean no_signal;
|
||||
|
||||
guint buffer_size;
|
||||
|
||||
GstClockTime internal_base_time;
|
||||
GstClockTime external_base_time;
|
||||
};
|
||||
|
||||
struct _GstDecklinkVideoSrcClass
|
||||
|
@ -80,8 +77,6 @@ struct _GstDecklinkVideoSrcClass
|
|||
};
|
||||
|
||||
GType gst_decklink_video_src_get_type (void);
|
||||
void gst_decklink_video_src_convert_to_external_clock (GstDecklinkVideoSrc * self,
|
||||
GstClockTime * timestamp, GstClockTime * duration);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
Loading…
Reference in a new issue