Support for playing speed rate and step-by-step forward.
[GB.MEDIA] * NEW: MediaPipeline: Pos is a new synonymous for the Position property. * NEW: MediaPipeline: Position is a new event that is raised when the media position has changed. * NEW: MediaPipeline: Seek() is a new method that allows to move the media to a specific position while specifying GStreamer seek flags. * NEW: MediaPipeline: Speed is a new property that allows to define the media playing speed rate. A negative speed will play backward. * NEW: MediaPipeline: Forward() is a new method that allows to move forward a specific amount of frames. Moving backward does not work at the moment. * NEW: Media: Add MediaPipeline.Seek() seek constants.
This commit is contained in:
parent
b8be1210b3
commit
3d68879247
2 changed files with 182 additions and 30 deletions
|
@ -633,6 +633,100 @@ static GB_IMG *get_last_image(void *_object)
|
|||
return MEDIA_get_image_from_sample(sample, TRUE);
|
||||
}
|
||||
|
||||
static GstIteratorResult iterator_next_pad(GstIterator *iter, GstPad **pad)
|
||||
{
|
||||
GstIteratorResult ret;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
ret = gst_iterator_next(iter, &value);
|
||||
if (ret == GST_ITERATOR_OK)
|
||||
{
|
||||
if (G_VALUE_HOLDS_BOXED(&value))
|
||||
*pad = g_value_get_boxed(&value);
|
||||
else
|
||||
*pad = (GstPad *)g_value_get_object(&value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstElement *find_sink(GstElement *pipeline)
|
||||
{
|
||||
int i;
|
||||
GstIterator *iter;
|
||||
GstElement *element;
|
||||
GstPad *pad;
|
||||
bool done;
|
||||
bool got_pad;
|
||||
|
||||
for (i = 0; i < gst_child_proxy_get_children_count(GST_CHILD_PROXY(pipeline)); i++)
|
||||
{
|
||||
element = (GstElement *)gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(pipeline), i);
|
||||
|
||||
iter = gst_element_iterate_src_pads(element);
|
||||
|
||||
done = FALSE;
|
||||
got_pad = FALSE;
|
||||
while (!done)
|
||||
{
|
||||
switch (iterator_next_pad(iter, &pad))
|
||||
{
|
||||
case GST_ITERATOR_OK:
|
||||
gst_object_unref(pad);
|
||||
got_pad = TRUE;
|
||||
done = TRUE;
|
||||
break;
|
||||
case GST_ITERATOR_RESYNC:
|
||||
gst_iterator_resync(iter);
|
||||
break;
|
||||
case GST_ITERATOR_ERROR:
|
||||
case GST_ITERATOR_DONE:
|
||||
done = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gst_iterator_free(iter);
|
||||
|
||||
if (!got_pad)
|
||||
return element;
|
||||
|
||||
gst_object_unref(element);
|
||||
}
|
||||
|
||||
GB.Error("Unable to find sink");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_pipeline_rate(void *_object)
|
||||
{
|
||||
gint64 pos;
|
||||
GstElement *sink;
|
||||
double rate;
|
||||
|
||||
if (THIS->state != GST_STATE_PLAYING && THIS->state != GST_STATE_PAUSED)
|
||||
return;
|
||||
|
||||
rate = THIS_PIPELINE->next_rate;
|
||||
if (rate == THIS_PIPELINE->rate)
|
||||
return;
|
||||
|
||||
sink = find_sink(ELEMENT);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &pos);
|
||||
|
||||
if (rate > 0)
|
||||
gst_element_seek(sink, rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_END, 0);
|
||||
else
|
||||
gst_element_seek(sink, rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos);
|
||||
|
||||
gst_object_unref(sink);
|
||||
|
||||
THIS_PIPELINE->rate = THIS_PIPELINE->next_rate;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//---- MediaSignalArguments -----------------------------------------------
|
||||
|
||||
|
@ -1179,23 +1273,6 @@ BEGIN_METHOD(MediaControl_LinkLaterTo, GB_OBJECT dest)
|
|||
|
||||
END_METHOD
|
||||
|
||||
static GstIteratorResult iterator_next_pad(GstIterator *iter, GstPad **pad)
|
||||
{
|
||||
GstIteratorResult ret;
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
ret = gst_iterator_next(iter, &value);
|
||||
if (ret == GST_ITERATOR_OK)
|
||||
{
|
||||
if (G_VALUE_HOLDS_BOXED(&value))
|
||||
*pad = g_value_get_boxed(&value);
|
||||
else
|
||||
*pad = (GstPad *)g_value_get_object(&value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void fill_pad_list(GB_ARRAY array, GstIterator *iter)
|
||||
{
|
||||
bool done = FALSE;
|
||||
|
@ -1553,6 +1630,7 @@ DECLARE_EVENT(EVENT_Tag);
|
|||
DECLARE_EVENT(EVENT_Event);
|
||||
DECLARE_EVENT(EVENT_Buffering);
|
||||
DECLARE_EVENT(EVENT_Duration);
|
||||
DECLARE_EVENT(EVENT_Position);
|
||||
DECLARE_EVENT(EVENT_Progress);
|
||||
DECLARE_EVENT(EVENT_AboutToFinish);
|
||||
|
||||
|
@ -1678,7 +1756,7 @@ static int cb_message(CMEDIAPIPELINE *_object)
|
|||
break;
|
||||
}
|
||||
case GST_MESSAGE_BUFFERING: GB.Raise(THIS, EVENT_Buffering, 0); break;
|
||||
case GST_MESSAGE_DURATION: GB.Raise(THIS, EVENT_Duration, 0); break;
|
||||
case GST_MESSAGE_DURATION_CHANGED: GB.Raise(THIS, EVENT_Duration, 0); break;
|
||||
case GST_MESSAGE_PROGRESS: GB.Raise(THIS, EVENT_Progress, 0); break;
|
||||
|
||||
case GST_MESSAGE_STREAM_START:
|
||||
|
@ -1699,16 +1777,21 @@ static int cb_message(CMEDIAPIPELINE *_object)
|
|||
|
||||
THIS_PIPELINE->in_message = FALSE;
|
||||
|
||||
if (THIS->state == GST_STATE_PLAYING && !THIS->error)
|
||||
if ((THIS->state == GST_STATE_PLAYING || THIS->state == GST_STATE_PAUSED) && !THIS->error)
|
||||
{
|
||||
gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &THIS_PIPELINE->pos);
|
||||
//fprintf(stderr, "%" PRId64 " / %" PRId64 "\n", THIS_PIPELINE->pos, THIS_PIPELINE->duration);
|
||||
if (!THIS_PIPELINE->about_to_finish && THIS_PIPELINE->pos > (THIS_PIPELINE->duration - 2000000000))
|
||||
gint64 pos;
|
||||
gst_element_query_position(ELEMENT, GST_FORMAT_TIME, &pos);
|
||||
if (pos >= 0 && pos != THIS_PIPELINE->pos)
|
||||
{
|
||||
THIS_PIPELINE->about_to_finish = TRUE;
|
||||
GB.Raise(THIS, EVENT_AboutToFinish, 0);
|
||||
THIS_PIPELINE->pos = pos;
|
||||
GB.Raise(THIS, EVENT_Position, 0);
|
||||
//fprintf(stderr, "%" PRId64 " / %" PRId64 "\n", THIS_PIPELINE->pos, THIS_PIPELINE->duration);
|
||||
if (!THIS_PIPELINE->about_to_finish && THIS_PIPELINE->pos > (THIS_PIPELINE->duration - 2000000000))
|
||||
{
|
||||
THIS_PIPELINE->about_to_finish = TRUE;
|
||||
GB.Raise(THIS, EVENT_AboutToFinish, 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
@ -1757,6 +1840,8 @@ BEGIN_METHOD(MediaPipeline_new, GB_INTEGER polling)
|
|||
THIS_PIPELINE->polling = polling;
|
||||
THIS_PIPELINE->watch = GB.Every(polling, (GB_TIMER_CALLBACK)cb_message, (intptr_t)THIS);
|
||||
}
|
||||
|
||||
THIS_PIPELINE->rate = THIS_PIPELINE->next_rate = 1.0;
|
||||
|
||||
END_METHOD
|
||||
|
||||
|
@ -1776,6 +1861,7 @@ BEGIN_METHOD_VOID(MediaPipeline_Play)
|
|||
THIS->eos = FALSE;
|
||||
MEDIA_set_state(THIS, GST_STATE_PLAYING, TRUE);
|
||||
cb_message(THIS_PIPELINE);
|
||||
set_pipeline_rate(THIS);
|
||||
|
||||
END_METHOD
|
||||
|
||||
|
@ -1802,6 +1888,24 @@ BEGIN_METHOD_VOID(MediaPipeline_Pause)
|
|||
|
||||
END_METHOD
|
||||
|
||||
static void pipeline_seek(void *_object, gint64 pos, GstSeekFlags flags)
|
||||
{
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
gst_element_seek(ELEMENT, THIS_PIPELINE->rate, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | flags, GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_NONE, 0);
|
||||
cb_message(THIS_PIPELINE);
|
||||
}
|
||||
|
||||
BEGIN_METHOD(MediaPipeline_Seek, GB_FLOAT position; GB_INTEGER flag)
|
||||
|
||||
gint64 pos = VARG(position) * 1E9;
|
||||
GstSeekFlags flags = (GstSeekFlags)VARGOPT(flag, GST_SEEK_FLAG_NONE);
|
||||
|
||||
pipeline_seek(THIS, pos, flags);
|
||||
|
||||
END_METHOD
|
||||
|
||||
BEGIN_PROPERTY(MediaPipeline_Position)
|
||||
|
||||
if (READ_PROPERTY)
|
||||
|
@ -1811,11 +1915,7 @@ BEGIN_PROPERTY(MediaPipeline_Position)
|
|||
else
|
||||
{
|
||||
gint64 pos = VPROP(GB_FLOAT) * 1E9;
|
||||
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
|
||||
gst_element_seek_simple(ELEMENT, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, (guint64)pos);
|
||||
pipeline_seek(THIS, pos, GST_SEEK_FLAG_ACCURATE);
|
||||
}
|
||||
|
||||
END_PROPERTY
|
||||
|
@ -1826,6 +1926,41 @@ BEGIN_PROPERTY(MediaPipeline_Duration)
|
|||
|
||||
END_PROPERTY
|
||||
|
||||
BEGIN_PROPERTY(MediaPipeline_Speed)
|
||||
|
||||
if (READ_PROPERTY)
|
||||
GB.ReturnFloat(THIS_PIPELINE->rate);
|
||||
else
|
||||
{
|
||||
double rate = VPROP(GB_FLOAT);
|
||||
if (fabs(rate) <= 1E-6)
|
||||
{
|
||||
GB.Error(GB_ERR_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
THIS_PIPELINE->next_rate = rate;
|
||||
set_pipeline_rate(THIS);
|
||||
}
|
||||
|
||||
END_PROPERTY
|
||||
|
||||
BEGIN_METHOD(MediaPipeline_Forward, GB_INTEGER count)
|
||||
|
||||
GstElement *sink;
|
||||
int count = VARGOPT(count, 1);
|
||||
|
||||
if (count <= 0)
|
||||
return;
|
||||
|
||||
sink = find_sink(ELEMENT);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
gst_element_send_event(sink, gst_event_new_step(GST_FORMAT_BUFFERS, fabs(count), 1.0, TRUE, FALSE));
|
||||
|
||||
END_METHOD
|
||||
|
||||
//---- Media --------------------------------------------------------------
|
||||
|
||||
BEGIN_METHOD(Media_Link, GB_OBJECT controls)
|
||||
|
@ -2035,12 +2170,17 @@ GB_DESC MediaPipelineDesc[] =
|
|||
|
||||
GB_PROPERTY("Position", "f", MediaPipeline_Position),
|
||||
GB_PROPERTY_READ("Duration", "f", MediaPipeline_Duration),
|
||||
GB_PROPERTY("Pos", "f", MediaPipeline_Position),
|
||||
GB_PROPERTY_READ("Length", "f", MediaPipeline_Duration),
|
||||
|
||||
GB_METHOD("Play", NULL, MediaPipeline_Play, NULL),
|
||||
GB_METHOD("Stop", NULL, MediaPipeline_Stop, NULL),
|
||||
GB_METHOD("Pause", NULL, MediaPipeline_Pause, NULL),
|
||||
GB_METHOD("Close", NULL, MediaPipeline_Close, NULL),
|
||||
|
||||
GB_METHOD("Seek", NULL, MediaPipeline_Seek, "(Position)f[(Flags)i]"),
|
||||
GB_PROPERTY("Speed", "f", MediaPipeline_Speed),
|
||||
GB_METHOD("Forward", NULL, MediaPipeline_Forward, "[(Frames)i]"),
|
||||
|
||||
GB_EVENT("Start", NULL, NULL, &EVENT_Start),
|
||||
GB_EVENT("End", NULL, NULL, &EVENT_End),
|
||||
|
@ -2049,6 +2189,7 @@ GB_DESC MediaPipelineDesc[] =
|
|||
GB_EVENT("Event", NULL, "(Message)MediaMessage;", &EVENT_Event),
|
||||
GB_EVENT("Buffering", NULL, NULL, &EVENT_Buffering),
|
||||
GB_EVENT("Duration", NULL, NULL, &EVENT_Duration),
|
||||
GB_EVENT("Position", NULL, NULL, &EVENT_Position),
|
||||
GB_EVENT("Progress", NULL, NULL, &EVENT_Progress),
|
||||
GB_EVENT("AboutToFinish", NULL, NULL, &EVENT_AboutToFinish),
|
||||
|
||||
|
@ -2070,6 +2211,15 @@ GB_DESC MediaDesc[] =
|
|||
GB_CONSTANT("Warning", "i", 1),
|
||||
GB_CONSTANT("Error", "i", 2),
|
||||
|
||||
GB_CONSTANT("SeekAccurate", "i", GST_SEEK_FLAG_ACCURATE),
|
||||
GB_CONSTANT("SeekKeyUnit", "i", GST_SEEK_FLAG_KEY_UNIT),
|
||||
GB_CONSTANT("SeekTrickMode", "i", GST_SEEK_FLAG_TRICKMODE),
|
||||
GB_CONSTANT("SeekSnapBefore", "i", GST_SEEK_FLAG_SNAP_BEFORE),
|
||||
GB_CONSTANT("SeekSnapAfter", "i", GST_SEEK_FLAG_SNAP_AFTER),
|
||||
GB_CONSTANT("SeekSnapNearest", "i", GST_SEEK_FLAG_SNAP_NEAREST),
|
||||
GB_CONSTANT("SeekTrickModeKeyUnit", "i", GST_SEEK_FLAG_TRICKMODE_KEY_UNITS),
|
||||
GB_CONSTANT("SeekTrickModeNoAudio", "i", GST_SEEK_FLAG_TRICKMODE_NO_AUDIO),
|
||||
|
||||
GB_STATIC_METHOD("Link", NULL, Media_Link, "(FirstControl)MediaControl;(SecondControl)MediaControl;."),
|
||||
GB_STATIC_METHOD("Time", "l", Media_Time, "(Seconds)f"),
|
||||
GB_STATIC_METHOD("URL", "s", Media_URL, "(Path)s"),
|
||||
|
|
|
@ -87,6 +87,8 @@ typedef
|
|||
int polling;
|
||||
gint64 pos;
|
||||
gint64 duration;
|
||||
double rate;
|
||||
double next_rate;
|
||||
unsigned in_message : 1;
|
||||
unsigned about_to_finish : 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue