gstreamer/gst/rawparse/gstrawparse.c
Wonchul Lee aa2ecf5e53 rawparse: send SEEKING query upstream first
Sometimes rawparse does not handle the seeking query
properly, the rawparse should send the query upstream
first. For example, upstream could support seeking in
TIME format (but not in BYTE format), so the BYTE format
seeking query that rawparse sends in push mode would
fail.

https://bugzilla.gnome.org/show_bug.cgi?id=722764
2014-11-22 17:51:01 +00:00

1088 lines
29 KiB
C

/* GStreamer
* Copyright (C) 2006 David A. Schleef <ds@schleef.org>
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
*
* gstrawparse.c:
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/* TODO: - Add locking where appropiate
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <string.h>
#include <gst/gst.h>
#include <gst/base/gstbasetransform.h>
#include <gst/base/gstadapter.h>
#include "gstrawparse.h"
static void gst_raw_parse_dispose (GObject * object);
static gboolean gst_raw_parse_sink_activate (GstPad * sinkpad,
GstObject * parent);
static gboolean gst_raw_parse_sink_activatemode (GstPad * sinkpad,
GstObject * parent, GstPadMode mode, gboolean active);
static void gst_raw_parse_loop (GstElement * element);
static GstStateChangeReturn gst_raw_parse_change_state (GstElement * element,
GstStateChange transition);
static GstFlowReturn gst_raw_parse_chain (GstPad * pad, GstObject * parent,
GstBuffer * buffer);
static gboolean gst_raw_parse_sink_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_raw_parse_src_event (GstPad * pad, GstObject * parent,
GstEvent * event);
static gboolean gst_raw_parse_src_query (GstPad * pad, GstObject * parent,
GstQuery * query);
static gboolean gst_raw_parse_convert (GstRawParse * rp, GstFormat src_format,
gint64 src_value, GstFormat dest_format, gint64 * dest_value);
static gboolean gst_raw_parse_handle_seek_pull (GstRawParse * rp,
GstEvent * event);
static void gst_raw_parse_reset (GstRawParse * rp);
static GstStaticPadTemplate gst_raw_parse_sink_pad_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS_ANY);
GST_DEBUG_CATEGORY_STATIC (gst_raw_parse_debug);
#define GST_CAT_DEFAULT gst_raw_parse_debug
static void gst_raw_parse_class_init (GstRawParseClass * klass);
static void gst_raw_parse_init (GstRawParse * clip, GstRawParseClass * g_class);
static GstElementClass *parent_class;
/* we can't use G_DEFINE_ABSTRACT_TYPE because we need the klass in the _init
* method to get to the padtemplates */
GType
gst_raw_parse_get_type (void)
{
static volatile gsize raw_parse_type = 0;
if (g_once_init_enter (&raw_parse_type)) {
GType _type;
_type = g_type_register_static_simple (GST_TYPE_ELEMENT,
"GstRawParse", sizeof (GstRawParseClass),
(GClassInitFunc) gst_raw_parse_class_init, sizeof (GstRawParse),
(GInstanceInitFunc) gst_raw_parse_init, G_TYPE_FLAG_ABSTRACT);
g_once_init_leave (&raw_parse_type, _type);
}
return raw_parse_type;
}
static void
gst_raw_parse_class_init (GstRawParseClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
gobject_class->dispose = gst_raw_parse_dispose;
gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_raw_parse_change_state);
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_raw_parse_sink_pad_template));
GST_DEBUG_CATEGORY_INIT (gst_raw_parse_debug, "rawparse", 0,
"rawparse element");
}
static void
gst_raw_parse_init (GstRawParse * rp, GstRawParseClass * g_class)
{
GstPadTemplate *src_pad_template;
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
rp->sinkpad =
gst_pad_new_from_static_template (&gst_raw_parse_sink_pad_template,
"sink");
gst_pad_set_chain_function (rp->sinkpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_chain));
gst_pad_set_event_function (rp->sinkpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_sink_event));
gst_pad_set_activate_function (rp->sinkpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activate));
gst_pad_set_activatemode_function (rp->sinkpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_sink_activatemode));
gst_element_add_pad (GST_ELEMENT (rp), rp->sinkpad);
src_pad_template = gst_element_class_get_pad_template (element_class, "src");
if (src_pad_template) {
rp->srcpad = gst_pad_new_from_template (src_pad_template, "src");
} else {
g_warning ("Subclass didn't specify a src pad template");
g_assert_not_reached ();
}
gst_pad_set_event_function (rp->srcpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_src_event));
gst_pad_set_query_function (rp->srcpad,
GST_DEBUG_FUNCPTR (gst_raw_parse_src_query));
gst_element_add_pad (GST_ELEMENT (rp), rp->srcpad);
rp->adapter = gst_adapter_new ();
rp->fps_n = 1;
rp->fps_d = 0;
rp->framesize = 1;
gst_raw_parse_reset (rp);
}
static void
gst_raw_parse_dispose (GObject * object)
{
GstRawParse *rp = GST_RAW_PARSE (object);
if (rp->adapter) {
g_object_unref (rp->adapter);
rp->adapter = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object);
}
void
gst_raw_parse_class_set_src_pad_template (GstRawParseClass * klass,
const GstCaps * allowed_caps)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
g_return_if_fail (GST_IS_RAW_PARSE_CLASS (klass));
g_return_if_fail (allowed_caps != NULL);
g_return_if_fail (GST_IS_CAPS (allowed_caps));
gst_element_class_add_pad_template (element_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
gst_caps_copy (allowed_caps)));
}
void
gst_raw_parse_class_set_multiple_frames_per_buffer (GstRawParseClass * klass,
gboolean multiple_frames)
{
g_return_if_fail (GST_IS_RAW_PARSE_CLASS (klass));
klass->multiple_frames_per_buffer = multiple_frames;
}
static void
gst_raw_parse_reset (GstRawParse * rp)
{
rp->n_frames = 0;
rp->discont = TRUE;
rp->negotiated = FALSE;
gst_segment_init (&rp->segment, GST_FORMAT_TIME);
gst_adapter_clear (rp->adapter);
}
static gboolean
gst_raw_parse_set_src_caps (GstRawParse * rp)
{
GstRawParseClass *rp_class = GST_RAW_PARSE_GET_CLASS (rp);
GstCaps *caps;
if (rp->negotiated)
return TRUE;
if (rp_class->get_caps) {
caps = rp_class->get_caps (rp);
} else {
GST_WARNING
("Subclass doesn't implement get_caps() method, using ANY caps");
caps = gst_caps_new_any ();
}
rp->negotiated = gst_pad_set_caps (rp->srcpad, caps);
gst_caps_unref (caps);
return rp->negotiated;
}
static GstFlowReturn
gst_raw_parse_push_buffer (GstRawParse * rp, GstBuffer * buffer)
{
GstFlowReturn ret;
gint nframes;
GstRawParseClass *rpclass;
rpclass = GST_RAW_PARSE_GET_CLASS (rp);
nframes = gst_buffer_get_size (buffer) / rp->framesize;
if (rp->segment.rate < 0) {
rp->n_frames -= nframes;
rp->discont = TRUE;
}
GST_BUFFER_OFFSET (buffer) = rp->n_frames;
GST_BUFFER_OFFSET_END (buffer) = rp->n_frames + nframes;
if (rp->fps_n) {
GST_BUFFER_TIMESTAMP (buffer) =
gst_util_uint64_scale (rp->n_frames, GST_SECOND * rp->fps_d, rp->fps_n);
GST_BUFFER_DURATION (buffer) =
gst_util_uint64_scale ((rp->n_frames + nframes) * GST_SECOND, rp->fps_d,
rp->fps_n) - GST_BUFFER_TIMESTAMP (buffer);
} else {
GST_BUFFER_TIMESTAMP (buffer) = rp->segment.start;
GST_BUFFER_DURATION (buffer) = GST_CLOCK_TIME_NONE;
}
if (rpclass->set_buffer_flags) {
rpclass->set_buffer_flags (rp, buffer);
}
if (rp->discont) {
GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DISCONT);
rp->discont = FALSE;
}
if (rp->segment.rate >= 0) {
rp->offset += gst_buffer_get_size (buffer);
rp->n_frames += nframes;
}
rp->segment.position = GST_BUFFER_TIMESTAMP (buffer);
GST_LOG_OBJECT (rp, "Pushing buffer with time %" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
ret = gst_pad_push (rp->srcpad, buffer);
return ret;
}
static GstFlowReturn
gst_raw_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
GstRawParse *rp = GST_RAW_PARSE (parent);
GstFlowReturn ret = GST_FLOW_OK;
GstRawParseClass *rp_class = GST_RAW_PARSE_GET_CLASS (rp);
guint buffersize, available;
if (G_UNLIKELY (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT))) {
GST_DEBUG_OBJECT (rp, "received DISCONT buffer");
gst_adapter_clear (rp->adapter);
rp->discont = TRUE;
}
if (!gst_raw_parse_set_src_caps (rp))
goto no_caps;
if (rp->start_segment) {
GST_DEBUG_OBJECT (rp, "sending start segment");
gst_pad_push_event (rp->srcpad, rp->start_segment);
rp->start_segment = NULL;
}
gst_adapter_push (rp->adapter, buffer);
available = gst_adapter_available (rp->adapter);
if (rp_class->multiple_frames_per_buffer) {
buffersize = available;
buffersize -= buffersize % rp->framesize;
} else {
buffersize = rp->framesize;
}
while (buffersize > 0 && gst_adapter_available (rp->adapter) >= buffersize) {
buffer = gst_adapter_take_buffer (rp->adapter, buffersize);
ret = gst_raw_parse_push_buffer (rp, buffer);
if (ret != GST_FLOW_OK)
break;
}
done:
return ret;
/* ERRORS */
no_caps:
{
GST_ERROR_OBJECT (rp, "could not set caps");
ret = GST_FLOW_NOT_NEGOTIATED;
goto done;
}
}
static void
gst_raw_parse_loop (GstElement * element)
{
GstRawParse *rp = GST_RAW_PARSE (element);
GstRawParseClass *rp_class = GST_RAW_PARSE_GET_CLASS (rp);
GstFlowReturn ret;
GstBuffer *buffer;
gint size;
if (G_UNLIKELY (rp->push_stream_start)) {
gchar *stream_id;
GstEvent *event;
stream_id =
gst_pad_create_stream_id (rp->srcpad, GST_ELEMENT_CAST (rp), NULL);
event = gst_event_new_stream_start (stream_id);
gst_event_set_group_id (event, gst_util_group_id_next ());
GST_DEBUG_OBJECT (rp, "Pushing STREAM_START");
gst_pad_push_event (rp->srcpad, event);
rp->push_stream_start = FALSE;
g_free (stream_id);
}
if (!gst_raw_parse_set_src_caps (rp))
goto no_caps;
if (rp->start_segment) {
GST_DEBUG_OBJECT (rp, "sending start segment");
gst_pad_push_event (rp->srcpad, rp->start_segment);
rp->start_segment = NULL;
}
if (rp_class->multiple_frames_per_buffer && rp->framesize < 4096)
size = 4096 - (4096 % rp->framesize);
else
size = rp->framesize;
if (rp->segment.rate >= 0) {
if (rp->offset + size > rp->upstream_length) {
GstFormat fmt = GST_FORMAT_BYTES;
if (!gst_pad_peer_query_duration (rp->sinkpad, fmt, &rp->upstream_length)) {
GST_WARNING_OBJECT (rp,
"Could not get upstream duration, trying to pull frame by frame");
size = rp->framesize;
} else if (rp->upstream_length < rp->offset + rp->framesize) {
ret = GST_FLOW_EOS;
goto pause;
} else if (rp->offset + size > rp->upstream_length) {
size = rp->upstream_length - rp->offset;
size -= size % rp->framesize;
}
}
} else {
if (rp->offset == 0) {
ret = GST_FLOW_EOS;
goto pause;
} else if (rp->offset < size) {
size -= rp->offset;
}
rp->offset -= size;
}
buffer = NULL;
ret = gst_pad_pull_range (rp->sinkpad, rp->offset, size, &buffer);
if (ret != GST_FLOW_OK) {
GST_DEBUG_OBJECT (rp, "pull_range (%" G_GINT64_FORMAT ", %u) "
"failed, flow: %s", rp->offset, size, gst_flow_get_name (ret));
buffer = NULL;
goto pause;
}
if (gst_buffer_get_size (buffer) < size) {
GST_DEBUG_OBJECT (rp, "Short read at offset %" G_GINT64_FORMAT
", got only %" G_GSIZE_FORMAT " of %u bytes", rp->offset,
gst_buffer_get_size (buffer), size);
if (size > rp->framesize) {
gst_buffer_set_size (buffer, gst_buffer_get_size (buffer) -
gst_buffer_get_size (buffer) % rp->framesize);
} else {
gst_buffer_unref (buffer);
buffer = NULL;
ret = GST_FLOW_EOS;
goto pause;
}
}
ret = gst_raw_parse_push_buffer (rp, buffer);
if (ret != GST_FLOW_OK)
goto pause;
return;
/* ERRORS */
no_caps:
{
GST_ERROR_OBJECT (rp, "could not negotiate caps");
ret = GST_FLOW_NOT_NEGOTIATED;
goto pause;
}
pause:
{
const gchar *reason = gst_flow_get_name (ret);
GST_LOG_OBJECT (rp, "pausing task, reason %s", reason);
gst_pad_pause_task (rp->sinkpad);
if (ret == GST_FLOW_EOS) {
if (rp->segment.flags & GST_SEEK_FLAG_SEGMENT) {
GstClockTime stop;
GST_LOG_OBJECT (rp, "Sending segment done");
if ((stop = rp->segment.stop) == -1)
stop = rp->segment.duration;
gst_element_post_message (GST_ELEMENT_CAST (rp),
gst_message_new_segment_done (GST_OBJECT_CAST (rp),
rp->segment.format, stop));
gst_pad_push_event (rp->srcpad,
gst_event_new_segment_done (rp->segment.format, stop));
} else {
GST_LOG_OBJECT (rp, "Sending EOS, at end of stream");
gst_pad_push_event (rp->srcpad, gst_event_new_eos ());
}
} else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
GST_ELEMENT_ERROR (rp, STREAM, FAILED,
("Internal data stream error."),
("stream stopped, reason %s", reason));
gst_pad_push_event (rp->srcpad, gst_event_new_eos ());
}
return;
}
}
static gboolean
gst_raw_parse_sink_activate (GstPad * sinkpad, GstObject * parent)
{
GstQuery *query;
gboolean pull_mode = FALSE;
query = gst_query_new_scheduling ();
if (gst_pad_peer_query (sinkpad, query))
pull_mode = gst_query_has_scheduling_mode_with_flags (query,
GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
gst_query_unref (query);
if (pull_mode) {
GST_DEBUG ("going to pull mode");
return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
} else {
GST_DEBUG ("going to push (streaming) mode");
return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
}
}
static gboolean
gst_raw_parse_sink_activatemode (GstPad * sinkpad, GstObject * parent,
GstPadMode mode, gboolean active)
{
GstRawParse *rp = GST_RAW_PARSE (parent);
gboolean result;
switch (mode) {
case GST_PAD_MODE_PULL:
if (active) {
GstFormat format;
gint64 duration;
/* get the duration in bytes */
format = GST_FORMAT_BYTES;
result = gst_pad_peer_query_duration (sinkpad, format, &duration);
if (result) {
GST_DEBUG_OBJECT (rp, "got duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration));
rp->upstream_length = duration;
/* convert to time */
gst_raw_parse_convert (rp, format, duration, GST_FORMAT_TIME,
&duration);
} else {
rp->upstream_length = -1;
duration = -1;
}
rp->segment.duration = duration;
rp->push_stream_start = TRUE;
result = gst_raw_parse_handle_seek_pull (rp, NULL);
rp->mode = mode;
} else {
result = gst_pad_stop_task (sinkpad);
}
return result;
case GST_PAD_MODE_PUSH:
rp->mode = mode;
return TRUE;
default:
return FALSE;
}
}
static GstStateChangeReturn
gst_raw_parse_change_state (GstElement * element, GstStateChange transition)
{
GstRawParse *rp = GST_RAW_PARSE (element);
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_segment_init (&rp->segment, GST_FORMAT_TIME);
rp->segment.position = 0;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_raw_parse_reset (rp);
break;
default:
break;
}
return ret;
}
static gboolean
gst_raw_parse_convert (GstRawParse * rp,
GstFormat src_format, gint64 src_value,
GstFormat dest_format, gint64 * dest_value)
{
gboolean ret = FALSE;
GST_DEBUG ("converting value %" G_GINT64_FORMAT " from %s (%d) to %s (%d)",
src_value, gst_format_get_name (src_format), src_format,
gst_format_get_name (dest_format), dest_format);
if (src_format == dest_format) {
*dest_value = src_value;
ret = TRUE;
goto done;
}
if (src_value == -1) {
*dest_value = -1;
ret = TRUE;
goto done;
}
/* bytes to frames */
if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_DEFAULT) {
if (rp->framesize != 0) {
*dest_value = gst_util_uint64_scale_int (src_value, 1, rp->framesize);
} else {
GST_ERROR ("framesize is 0");
*dest_value = 0;
}
ret = TRUE;
goto done;
}
/* frames to bytes */
if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_BYTES) {
*dest_value = gst_util_uint64_scale_int (src_value, rp->framesize, 1);
ret = TRUE;
goto done;
}
/* time to frames */
if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_DEFAULT) {
if (rp->fps_d != 0) {
*dest_value = gst_util_uint64_scale (src_value,
rp->fps_n, GST_SECOND * rp->fps_d);
} else {
GST_ERROR ("framerate denominator is 0");
*dest_value = 0;
}
ret = TRUE;
goto done;
}
/* frames to time */
if (src_format == GST_FORMAT_DEFAULT && dest_format == GST_FORMAT_TIME) {
if (rp->fps_n != 0) {
*dest_value = gst_util_uint64_scale (src_value,
GST_SECOND * rp->fps_d, rp->fps_n);
} else {
GST_ERROR ("framerate numerator is 0");
*dest_value = 0;
}
ret = TRUE;
goto done;
}
/* time to bytes */
if (src_format == GST_FORMAT_TIME && dest_format == GST_FORMAT_BYTES) {
if (rp->fps_d != 0) {
*dest_value = gst_util_uint64_scale (src_value,
rp->fps_n * rp->framesize, GST_SECOND * rp->fps_d);
} else {
GST_ERROR ("framerate denominator is 0");
*dest_value = 0;
}
ret = TRUE;
goto done;
}
/* bytes to time */
if (src_format == GST_FORMAT_BYTES && dest_format == GST_FORMAT_TIME) {
if (rp->fps_n != 0 && rp->framesize != 0) {
*dest_value = gst_util_uint64_scale (src_value,
GST_SECOND * rp->fps_d, rp->fps_n * rp->framesize);
} else {
GST_ERROR ("framerate denominator and/or framesize is 0");
*dest_value = 0;
}
ret = TRUE;
}
done:
GST_DEBUG ("ret=%d result %" G_GINT64_FORMAT, ret, *dest_value);
return ret;
}
static gboolean
gst_raw_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRawParse *rp = GST_RAW_PARSE (parent);
gboolean ret;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
case GST_EVENT_FLUSH_STOP:
/* Only happens in push mode */
gst_raw_parse_reset (rp);
ret = gst_pad_push_event (rp->srcpad, event);
break;
case GST_EVENT_SEGMENT:
{
GstSegment segment;
/* Only happens in push mode */
gst_event_copy_segment (event, &segment);
if (segment.format != GST_FORMAT_TIME) {
gst_event_unref (event);
ret =
gst_raw_parse_convert (rp, segment.format, segment.start,
GST_FORMAT_TIME, (gint64 *) & segment.start);
ret &= gst_raw_parse_convert (rp, segment.format, segment.time,
GST_FORMAT_TIME, (gint64 *) & segment.time);
ret &= gst_raw_parse_convert (rp, segment.format, segment.stop,
GST_FORMAT_TIME, (gint64 *) & segment.stop);
if (!ret) {
GST_ERROR_OBJECT (rp,
"Failed converting to GST_FORMAT_TIME format (%d)",
segment.format);
break;
}
segment.format = GST_FORMAT_TIME;
event = gst_event_new_segment (&segment);
}
gst_segment_copy_into (&segment, &rp->segment);
if (rp->start_segment)
gst_event_unref (rp->start_segment);
rp->start_segment = event;
ret = TRUE;
break;
}
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
static gboolean
gst_raw_parse_handle_seek_push (GstRawParse * rp, GstEvent * event)
{
GstFormat format;
gdouble rate;
GstSeekFlags flags;
GstSeekType start_type, stop_type;
gint64 start, stop;
gboolean ret = FALSE;
gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
&stop_type, &stop);
/* can't seek backwards yet */
if (rate <= 0.0)
goto wrong_rate;
/* First try if upstream handles the seek */
ret = gst_pad_push_event (rp->sinkpad, event);
if (ret)
return ret;
/* Otherwise convert to bytes and push upstream */
if (format == GST_FORMAT_TIME || format == GST_FORMAT_DEFAULT) {
ret = gst_raw_parse_convert (rp, format, start, GST_FORMAT_BYTES, &start);
ret &= gst_raw_parse_convert (rp, format, stop, GST_FORMAT_BYTES, &stop);
if (ret) {
/* Seek on a frame boundary */
start -= start % rp->framesize;
if (stop != -1)
stop += rp->framesize - stop % rp->framesize;
event =
gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, start_type,
start, stop_type, stop);
ret = gst_pad_push_event (rp->sinkpad, event);
} else {
GST_DEBUG_OBJECT (rp, "Seek failed: couldn't convert to byte positions");
}
} else {
GST_DEBUG_OBJECT (rp,
"seeking is only supported in TIME or DEFAULT format");
}
return ret;
/* ERRORS */
wrong_rate:
{
GST_DEBUG_OBJECT (rp, "Seek failed: negative rates not supported yet");
return FALSE;
}
}
static gboolean
gst_raw_parse_handle_seek_pull (GstRawParse * rp, GstEvent * event)
{
gdouble rate;
GstFormat format;
GstSeekFlags flags;
GstSeekType start_type, stop_type;
gint64 start, stop;
gint64 last_stop;
gboolean ret = FALSE;
gboolean flush;
GstSegment seeksegment;
if (event) {
gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
&stop_type, &stop);
/* convert input offsets to time */
ret = gst_raw_parse_convert (rp, format, start, GST_FORMAT_TIME, &start);
ret &= gst_raw_parse_convert (rp, format, stop, GST_FORMAT_TIME, &stop);
if (!ret)
goto convert_failed;
GST_DEBUG_OBJECT (rp, "converted start - stop to time");
format = GST_FORMAT_TIME;
gst_event_unref (event);
} else {
format = GST_FORMAT_TIME;
flags = 0;
}
flush = ((flags & GST_SEEK_FLAG_FLUSH) != 0);
/* start flushing up and downstream so that the loop function pauses and we
* can acquire the STREAM_LOCK. */
if (flush) {
GST_LOG_OBJECT (rp, "flushing");
gst_pad_push_event (rp->sinkpad, gst_event_new_flush_start ());
gst_pad_push_event (rp->srcpad, gst_event_new_flush_start ());
} else {
GST_LOG_OBJECT (rp, "pause task");
gst_pad_pause_task (rp->sinkpad);
}
GST_PAD_STREAM_LOCK (rp->sinkpad);
memcpy (&seeksegment, &rp->segment, sizeof (GstSegment));
if (event) {
/* configure the seek values */
gst_segment_do_seek (&seeksegment, rate, format, flags,
start_type, start, stop_type, stop, NULL);
}
/* get the desired position */
last_stop = seeksegment.position;
GST_LOG_OBJECT (rp, "seeking to %" GST_TIME_FORMAT,
GST_TIME_ARGS (last_stop));
/* convert the desired position to bytes */
ret =
gst_raw_parse_convert (rp, format, last_stop, GST_FORMAT_BYTES,
&last_stop);
/* prepare for streaming */
if (flush) {
GST_LOG_OBJECT (rp, "stop flush");
gst_pad_push_event (rp->sinkpad, gst_event_new_flush_stop (TRUE));
gst_pad_push_event (rp->srcpad, gst_event_new_flush_stop (TRUE));
}
if (ret) {
/* seek done */
/* Seek on a frame boundary */
last_stop -= last_stop % rp->framesize;
rp->offset = last_stop;
rp->n_frames = last_stop / rp->framesize;
GST_LOG_OBJECT (rp, "seeking to bytes %" G_GINT64_FORMAT, last_stop);
memcpy (&rp->segment, &seeksegment, sizeof (GstSegment));
if (rp->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT_CAST (rp),
gst_message_new_segment_start (GST_OBJECT_CAST (rp),
rp->segment.format, rp->segment.position));
}
/* for deriving a stop position for the playback segment from the seek
* segment, we must take the duration when the stop is not set */
if ((stop = rp->segment.stop) == -1)
stop = rp->segment.duration;
GST_DEBUG_OBJECT (rp, "preparing newsegment from %" G_GINT64_FORMAT
" to %" G_GINT64_FORMAT, rp->segment.start, stop);
/* now replace the old segment so that we send it in the stream thread the
* next time it is scheduled. */
if (rp->start_segment)
gst_event_unref (rp->start_segment);
rp->start_segment = gst_event_new_segment (&rp->segment);
}
rp->discont = TRUE;
GST_LOG_OBJECT (rp, "start streaming");
gst_pad_start_task (rp->sinkpad, (GstTaskFunction) gst_raw_parse_loop, rp,
NULL);
GST_PAD_STREAM_UNLOCK (rp->sinkpad);
return ret;
/* ERRORS */
convert_failed:
{
GST_DEBUG_OBJECT (rp, "Seek failed: couldn't convert to byte positions");
return FALSE;
}
}
static gboolean
gst_raw_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
GstRawParse *rp = GST_RAW_PARSE (parent);
gboolean ret;
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
if (rp->mode == GST_PAD_MODE_PUSH)
ret = gst_raw_parse_handle_seek_push (rp, event);
else
ret = gst_raw_parse_handle_seek_pull (rp, event);
break;
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
static gboolean
gst_raw_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
{
GstRawParse *rp = GST_RAW_PARSE (parent);
gboolean ret = FALSE;
GST_DEBUG ("src_query %s", gst_query_type_get_name (GST_QUERY_TYPE (query)));
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
GstFormat format;
gint64 time, value;
GST_LOG ("query position");
gst_query_parse_position (query, &format, NULL);
time = rp->segment.position;
ret = gst_raw_parse_convert (rp, GST_FORMAT_TIME, time, format, &value);
gst_query_set_position (query, format, value);
break;
}
case GST_QUERY_DURATION:{
gint64 duration;
GstFormat format;
GstQuery *bquery;
GST_LOG ("query duration");
ret = gst_pad_peer_query (rp->sinkpad, query);
if (ret)
goto done;
gst_query_parse_duration (query, &format, NULL);
/* We only handle TIME and DEFAULT format */
if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT)
goto error;
bquery = gst_query_new_duration (GST_FORMAT_BYTES);
ret = gst_pad_peer_query (rp->sinkpad, bquery);
if (!ret) {
gst_query_unref (bquery);
goto error;
}
gst_query_parse_duration (bquery, NULL, &duration);
gst_query_unref (bquery);
ret =
gst_raw_parse_convert (rp, GST_FORMAT_BYTES, duration, format,
&duration);
if (ret)
gst_query_set_duration (query, format, duration);
break;
}
case GST_QUERY_CONVERT:
{
GstFormat src_fmt, dest_fmt;
gint64 src_val, dest_val;
GST_LOG ("query convert");
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
ret = gst_raw_parse_convert (rp, src_fmt, src_val, dest_fmt, &dest_val);
if (!ret)
goto error;
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
break;
}
case GST_QUERY_SEEKING:{
GstFormat fmt;
ret = TRUE;
/* try upstream first */
if (gst_pad_peer_query (rp->sinkpad, query))
break;
gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
if (fmt != GST_FORMAT_TIME && fmt != GST_FORMAT_DEFAULT
&& fmt != GST_FORMAT_BYTES) {
gst_query_set_seeking (query, fmt, FALSE, -1, -1);
} else if (rp->mode == GST_PAD_MODE_PUSH) {
GstQuery *peerquery = gst_query_new_seeking (GST_FORMAT_BYTES);
gboolean seekable;
seekable = gst_pad_peer_query (rp->sinkpad, peerquery);
if (seekable)
gst_query_parse_seeking (peerquery, NULL, &seekable, NULL, NULL);
gst_query_unref (peerquery);
gst_query_set_seeking (query, fmt, seekable, seekable ? 0 : -1, -1);
} else {
gst_query_set_seeking (query, fmt, TRUE, 0, -1);
}
break;
}
default:
/* else forward upstream */
ret = gst_pad_query_default (pad, parent, query);
break;
}
done:
return ret;
/* ERRORS */
error:
{
GST_DEBUG_OBJECT (rp, "query failed");
goto done;
}
}
void
gst_raw_parse_set_framesize (GstRawParse * rp, int framesize)
{
g_return_if_fail (GST_IS_RAW_PARSE (rp));
g_return_if_fail (!rp->negotiated);
GST_DEBUG_OBJECT (rp, "framesize %d", framesize);
rp->framesize = framesize;
}
void
gst_raw_parse_set_fps (GstRawParse * rp, int fps_n, int fps_d)
{
g_return_if_fail (GST_IS_RAW_PARSE (rp));
g_return_if_fail (!rp->negotiated);
rp->fps_n = fps_n;
rp->fps_d = fps_d;
}
void
gst_raw_parse_get_fps (GstRawParse * rp, int *fps_n, int *fps_d)
{
g_return_if_fail (GST_IS_RAW_PARSE (rp));
if (fps_n)
*fps_n = rp->fps_n;
if (fps_d)
*fps_d = rp->fps_d;
}
gboolean
gst_raw_parse_is_negotiated (GstRawParse * rp)
{
g_return_val_if_fail (GST_IS_RAW_PARSE (rp), FALSE);
return rp->negotiated;
}