mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-30 12:49:40 +00:00
Seeking/indexing patch from vishnu
Original commit message from CVS: Seeking/indexing patch from vishnu
This commit is contained in:
parent
d4df18161e
commit
7518d22e9b
1 changed files with 204 additions and 70 deletions
274
ext/mad/gstmad.c
274
ext/mad/gstmad.c
|
@ -50,11 +50,14 @@ struct _GstMad {
|
||||||
guchar *tempbuffer;
|
guchar *tempbuffer;
|
||||||
glong tempsize; /* used to keep track of partial buffers */
|
glong tempsize; /* used to keep track of partial buffers */
|
||||||
gboolean need_sync;
|
gboolean need_sync;
|
||||||
|
guint64 base_byte_offset;
|
||||||
|
guint64 bytes_consumed; /* since the base_byte_offset */
|
||||||
guint64 base_time;
|
guint64 base_time;
|
||||||
guint64 framestamp; /* timestamp-like, but counted in frames */
|
guint64 framestamp; /* timestamp-like, but counted in frames */
|
||||||
guint64 total_samples; /* the number of samples since the sync point */
|
guint64 total_samples; /* the number of samples since the sync point */
|
||||||
|
|
||||||
gboolean restart;
|
gboolean restart;
|
||||||
|
guint64 segment_start;
|
||||||
|
|
||||||
/* info */
|
/* info */
|
||||||
struct mad_header header;
|
struct mad_header header;
|
||||||
|
@ -73,6 +76,9 @@ struct _GstMad {
|
||||||
/* negotiated format */
|
/* negotiated format */
|
||||||
gint rate;
|
gint rate;
|
||||||
gint channels;
|
gint channels;
|
||||||
|
|
||||||
|
GstIndex *index;
|
||||||
|
gint index_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstMadClass {
|
struct _GstMadClass {
|
||||||
|
@ -167,6 +173,9 @@ static void gst_mad_chain (GstPad *pad, GstBuffer *buffer);
|
||||||
static GstElementStateReturn
|
static GstElementStateReturn
|
||||||
gst_mad_change_state (GstElement *element);
|
gst_mad_change_state (GstElement *element);
|
||||||
|
|
||||||
|
static void gst_mad_set_index (GstElement *element, GstIndex *index);
|
||||||
|
static GstIndex* gst_mad_get_index (GstElement *element);
|
||||||
|
|
||||||
|
|
||||||
static GstElementClass *parent_class = NULL;
|
static GstElementClass *parent_class = NULL;
|
||||||
/* static guint gst_mad_signals[LAST_SIGNAL] = { 0 }; */
|
/* static guint gst_mad_signals[LAST_SIGNAL] = { 0 }; */
|
||||||
|
@ -261,6 +270,8 @@ gst_mad_class_init (GstMadClass *klass)
|
||||||
gobject_class->dispose = gst_mad_dispose;
|
gobject_class->dispose = gst_mad_dispose;
|
||||||
|
|
||||||
gstelement_class->change_state = gst_mad_change_state;
|
gstelement_class->change_state = gst_mad_change_state;
|
||||||
|
gstelement_class->set_index = gst_mad_set_index;
|
||||||
|
gstelement_class->get_index = gst_mad_get_index;
|
||||||
|
|
||||||
/* init properties */
|
/* init properties */
|
||||||
/* currently, string representations are used, we might want to change that */
|
/* currently, string representations are used, we might want to change that */
|
||||||
|
@ -305,6 +316,8 @@ gst_mad_init (GstMad *mad)
|
||||||
mad->tempsize = 0;
|
mad->tempsize = 0;
|
||||||
mad->need_sync = TRUE;
|
mad->need_sync = TRUE;
|
||||||
mad->base_time = 0;
|
mad->base_time = 0;
|
||||||
|
mad->base_byte_offset = 0;
|
||||||
|
mad->bytes_consumed = 0;
|
||||||
mad->framestamp = 0;
|
mad->framestamp = 0;
|
||||||
mad->total_samples = 0;
|
mad->total_samples = 0;
|
||||||
mad->new_header = TRUE;
|
mad->new_header = TRUE;
|
||||||
|
@ -312,6 +325,7 @@ gst_mad_init (GstMad *mad)
|
||||||
mad->vbr_average = 0;
|
mad->vbr_average = 0;
|
||||||
mad->vbr_rate = 0;
|
mad->vbr_rate = 0;
|
||||||
mad->restart = FALSE;
|
mad->restart = FALSE;
|
||||||
|
mad->segment_start = 0;
|
||||||
mad->header.mode = -1;
|
mad->header.mode = -1;
|
||||||
mad->header.emphasis = -1;
|
mad->header.emphasis = -1;
|
||||||
mad->metadata = NULL;
|
mad->metadata = NULL;
|
||||||
|
@ -328,11 +342,32 @@ gst_mad_dispose (GObject *object)
|
||||||
{
|
{
|
||||||
GstMad *mad = GST_MAD (object);
|
GstMad *mad = GST_MAD (object);
|
||||||
|
|
||||||
|
gst_mad_set_index (GST_ELEMENT (object), NULL);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||||
|
|
||||||
g_free (mad->tempbuffer);
|
g_free (mad->tempbuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_mad_set_index (GstElement *element, GstIndex *index)
|
||||||
|
{
|
||||||
|
GstMad *mad = GST_MAD (element);
|
||||||
|
|
||||||
|
mad->index = index;
|
||||||
|
|
||||||
|
if (index)
|
||||||
|
gst_index_get_writer_id (index, GST_OBJECT (element), &mad->index_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstIndex*
|
||||||
|
gst_mad_get_index (GstElement *element)
|
||||||
|
{
|
||||||
|
GstMad *mad = GST_MAD (element);
|
||||||
|
|
||||||
|
return mad->index;
|
||||||
|
}
|
||||||
|
|
||||||
static const GstFormat*
|
static const GstFormat*
|
||||||
gst_mad_get_formats (GstPad *pad)
|
gst_mad_get_formats (GstPad *pad)
|
||||||
{
|
{
|
||||||
|
@ -573,6 +608,115 @@ gst_mad_src_query (GstPad *pad, GstQueryType type,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
index_seek (GstMad *mad, GstPad *pad, GstEvent *event)
|
||||||
|
{
|
||||||
|
/* since we know the exact byteoffset of the frame,
|
||||||
|
make sure to try bytes first */
|
||||||
|
|
||||||
|
const GstFormat try_all_formats[] = {
|
||||||
|
GST_FORMAT_BYTES,
|
||||||
|
GST_FORMAT_TIME,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
const GstFormat *try_formats = try_all_formats;
|
||||||
|
const GstFormat *peer_formats;
|
||||||
|
|
||||||
|
GstIndexEntry *entry =
|
||||||
|
gst_index_get_assoc_entry (mad->index, mad->index_id,
|
||||||
|
GST_INDEX_LOOKUP_BEFORE, 0,
|
||||||
|
GST_EVENT_SEEK_FORMAT (event),
|
||||||
|
GST_EVENT_SEEK_OFFSET (event));
|
||||||
|
if (!entry)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
peer_formats = gst_pad_get_formats (GST_PAD_PEER (mad->sinkpad));
|
||||||
|
|
||||||
|
while (gst_formats_contains (peer_formats, *try_formats)) {
|
||||||
|
gint64 value;
|
||||||
|
|
||||||
|
if (gst_index_entry_assoc_map (entry, *try_formats, &value)) {
|
||||||
|
/* lookup succeeded, create the seek */
|
||||||
|
|
||||||
|
GstEvent *seek_event =
|
||||||
|
gst_event_new_seek (*try_formats |
|
||||||
|
GST_SEEK_METHOD_SET |
|
||||||
|
GST_SEEK_FLAG_FLUSH, value);
|
||||||
|
|
||||||
|
if (gst_pad_send_event (GST_PAD_PEER (mad->sinkpad), seek_event)) {
|
||||||
|
/* seek worked, we're done, loop will exit */
|
||||||
|
mad->restart = TRUE;
|
||||||
|
g_assert (GST_EVENT_SEEK_FORMAT (event) == GST_FORMAT_TIME);
|
||||||
|
mad->segment_start = GST_EVENT_SEEK_OFFSET (event);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try_formats++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
normal_seek (GstMad *mad, GstPad *pad, GstEvent *event)
|
||||||
|
{
|
||||||
|
gint64 src_offset;
|
||||||
|
gboolean flush;
|
||||||
|
GstFormat format;
|
||||||
|
const GstFormat *peer_formats;
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
|
format = GST_FORMAT_TIME;
|
||||||
|
|
||||||
|
/* first bring the src_format to TIME */
|
||||||
|
if (!gst_pad_convert (pad,
|
||||||
|
GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event),
|
||||||
|
&format, &src_offset))
|
||||||
|
{
|
||||||
|
/* didn't work, probably unsupported seek format then */
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* shave off the flush flag, we'll need it later */
|
||||||
|
flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
|
||||||
|
|
||||||
|
/* assume the worst */
|
||||||
|
res = FALSE;
|
||||||
|
|
||||||
|
peer_formats = gst_pad_get_formats (GST_PAD_PEER (mad->sinkpad));
|
||||||
|
|
||||||
|
/* while we did not exhaust our seek formats without result */
|
||||||
|
while (peer_formats && *peer_formats && !res) {
|
||||||
|
gint64 desired_offset;
|
||||||
|
|
||||||
|
format = *peer_formats;
|
||||||
|
|
||||||
|
/* try to convert requested format to one we can seek with on the sinkpad */
|
||||||
|
if (gst_pad_convert (mad->sinkpad, GST_FORMAT_TIME, src_offset,
|
||||||
|
&format, &desired_offset))
|
||||||
|
{
|
||||||
|
GstEvent *seek_event;
|
||||||
|
|
||||||
|
/* conversion succeeded, create the seek */
|
||||||
|
seek_event = gst_event_new_seek (format | GST_SEEK_METHOD_SET | flush,
|
||||||
|
desired_offset);
|
||||||
|
/* do the seek */
|
||||||
|
if (gst_pad_send_event (GST_PAD_PEER (mad->sinkpad), seek_event)) {
|
||||||
|
/* seek worked, we're done, loop will exit */
|
||||||
|
res = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* at this point, either the seek worked or res == FALSE */
|
||||||
|
if (res)
|
||||||
|
/* we need to break out of the processing loop on flush */
|
||||||
|
mad->restart = flush;
|
||||||
|
|
||||||
|
peer_formats++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_mad_src_event (GstPad *pad, GstEvent *event)
|
gst_mad_src_event (GstPad *pad, GstEvent *event)
|
||||||
{
|
{
|
||||||
|
@ -591,62 +735,16 @@ gst_mad_src_event (GstPad *pad, GstEvent *event)
|
||||||
break;
|
break;
|
||||||
/* the all-formats seek logic */
|
/* the all-formats seek logic */
|
||||||
case GST_EVENT_SEEK:
|
case GST_EVENT_SEEK:
|
||||||
{
|
if (!mad->can_seek) {
|
||||||
gint64 src_offset;
|
g_warning ("MAD: can't seek now, seek ignored");
|
||||||
gboolean flush;
|
break;
|
||||||
GstFormat format;
|
|
||||||
const GstFormat *peer_formats;
|
|
||||||
|
|
||||||
format = GST_FORMAT_TIME;
|
|
||||||
|
|
||||||
/* first bring the src_format to TIME */
|
|
||||||
if (!gst_pad_convert (pad,
|
|
||||||
GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event),
|
|
||||||
&format, &src_offset))
|
|
||||||
{
|
|
||||||
/* didn't work, probably unsupported seek format then */
|
|
||||||
res = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* shave off the flush flag, we'll need it later */
|
|
||||||
flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
|
|
||||||
|
|
||||||
/* assume the worst */
|
|
||||||
res = FALSE;
|
|
||||||
|
|
||||||
peer_formats = gst_pad_get_formats (GST_PAD_PEER (mad->sinkpad));
|
|
||||||
|
|
||||||
/* while we did not exhaust our seek formats without result */
|
|
||||||
while (peer_formats && *peer_formats && !res) {
|
|
||||||
gint64 desired_offset;
|
|
||||||
|
|
||||||
format = *peer_formats;
|
|
||||||
|
|
||||||
/* try to convert requested format to one we can seek with on the sinkpad */
|
|
||||||
if (gst_pad_convert (mad->sinkpad, GST_FORMAT_TIME, src_offset,
|
|
||||||
&format, &desired_offset))
|
|
||||||
{
|
|
||||||
GstEvent *seek_event;
|
|
||||||
|
|
||||||
/* conversion succeeded, create the seek */
|
|
||||||
seek_event = gst_event_new_seek (format | GST_SEEK_METHOD_SET | flush,
|
|
||||||
desired_offset);
|
|
||||||
/* do the seek */
|
|
||||||
if (gst_pad_send_event (GST_PAD_PEER (mad->sinkpad), seek_event)) {
|
|
||||||
/* seek worked, we're done, loop will exit */
|
|
||||||
res = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* at this point, either the seek worked or res == FALSE */
|
|
||||||
if (res)
|
|
||||||
/* we need to break out of the processing loop on flush */
|
|
||||||
mad->restart = flush;
|
|
||||||
|
|
||||||
peer_formats++;
|
|
||||||
}
|
}
|
||||||
|
if (mad->index)
|
||||||
|
res = index_seek (mad, pad, event);
|
||||||
|
else
|
||||||
|
res = normal_seek (mad, pad, event);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
res = FALSE;
|
res = FALSE;
|
||||||
break;
|
break;
|
||||||
|
@ -999,6 +1097,17 @@ gst_mad_handle_event (GstPad *pad, GstBuffer *buffer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_mad_check_restart (GstMad *mad)
|
||||||
|
{
|
||||||
|
gboolean yes = mad->restart;
|
||||||
|
if (mad->restart) {
|
||||||
|
mad->restart = FALSE;
|
||||||
|
mad->tempsize = 0;
|
||||||
|
}
|
||||||
|
return yes;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
{
|
{
|
||||||
|
@ -1006,6 +1115,7 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
gchar *data;
|
gchar *data;
|
||||||
glong size;
|
glong size;
|
||||||
gboolean new_pts = FALSE;
|
gboolean new_pts = FALSE;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
|
||||||
mad = GST_MAD (gst_pad_get_parent (pad));
|
mad = GST_MAD (gst_pad_get_parent (pad));
|
||||||
g_return_if_fail (GST_IS_MAD (mad));
|
g_return_if_fail (GST_IS_MAD (mad));
|
||||||
|
@ -1017,13 +1127,19 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gst_mad_check_restart (mad);
|
||||||
|
|
||||||
|
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
|
|
||||||
/* handle timestamps */
|
/* handle timestamps */
|
||||||
if (GST_BUFFER_TIMESTAMP (buffer) != -1) {
|
if (timestamp != -1) {
|
||||||
/* if there is nothing queued (partial buffer), we prepare to set the
|
/* if there is nothing queued (partial buffer), we prepare to set the
|
||||||
* timestamp on the next buffer */
|
* timestamp on the next buffer */
|
||||||
if (mad->tempsize == 0) {
|
if (mad->tempsize == 0) {
|
||||||
mad->base_time = GST_BUFFER_TIMESTAMP (buffer);
|
mad->base_time = timestamp;
|
||||||
mad->total_samples = 0;
|
mad->total_samples = 0;
|
||||||
|
mad->base_byte_offset = GST_BUFFER_OFFSET (buffer);
|
||||||
|
mad->bytes_consumed = 0;
|
||||||
}
|
}
|
||||||
/* else we need to finish the current partial frame with the old timestamp
|
/* else we need to finish the current partial frame with the old timestamp
|
||||||
* and queue this timestamp for the next frame */
|
* and queue this timestamp for the next frame */
|
||||||
|
@ -1061,6 +1177,7 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
guint nchannels;
|
guint nchannels;
|
||||||
guint nsamples;
|
guint nsamples;
|
||||||
gint rate;
|
gint rate;
|
||||||
|
guint64 time_offset;
|
||||||
|
|
||||||
mad_stream_buffer (&mad->stream, mad_input_buffer, mad->tempsize);
|
mad_stream_buffer (&mad->stream, mad_input_buffer, mad->tempsize);
|
||||||
|
|
||||||
|
@ -1157,7 +1274,30 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
mad->rate = rate;
|
mad->rate = rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GST_PAD_IS_USABLE (mad->srcpad)) {
|
if (mad->frame.header.samplerate == 0) {
|
||||||
|
g_warning ("mad->frame.header.samplerate is 0; timestamps cannot be calculated");
|
||||||
|
time_offset = mad->base_time + 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
time_offset = mad->base_time + (mad->total_samples * GST_SECOND
|
||||||
|
/ mad->frame.header.samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mad->index) {
|
||||||
|
guint64 x_bytes = mad->base_byte_offset + mad->bytes_consumed;
|
||||||
|
|
||||||
|
gst_index_add_association (mad->index, mad->index_id, 0,
|
||||||
|
GST_FORMAT_BYTES, x_bytes,
|
||||||
|
GST_FORMAT_TIME, time_offset,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_PAD_IS_USABLE (mad->srcpad) &&
|
||||||
|
mad->segment_start < time_offset) {
|
||||||
|
|
||||||
|
/* for sample accurate seeking, calculate how many samples
|
||||||
|
to skip and send the remaining pcm samples */
|
||||||
|
|
||||||
GstBuffer *outbuffer;
|
GstBuffer *outbuffer;
|
||||||
gint16 *outdata;
|
gint16 *outdata;
|
||||||
mad_fixed_t const *left_ch, *right_ch;
|
mad_fixed_t const *left_ch, *right_ch;
|
||||||
|
@ -1169,15 +1309,7 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
outbuffer = gst_buffer_new_and_alloc (nsamples * nchannels * 2);
|
outbuffer = gst_buffer_new_and_alloc (nsamples * nchannels * 2);
|
||||||
outdata = (gint16 *) GST_BUFFER_DATA (outbuffer);
|
outdata = (gint16 *) GST_BUFFER_DATA (outbuffer);
|
||||||
|
|
||||||
if (mad->frame.header.samplerate == 0) {
|
GST_BUFFER_TIMESTAMP (outbuffer) = time_offset;
|
||||||
g_warning ("mad->frame.header.samplerate is 0 !");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
guint64 increase;
|
|
||||||
increase = mad->total_samples * GST_SECOND
|
|
||||||
/ mad->frame.header.samplerate;
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuffer) = mad->base_time + increase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* output sample(s) in 16-bit signed native-endian PCM */
|
/* output sample(s) in 16-bit signed native-endian PCM */
|
||||||
if (nchannels == 1) {
|
if (nchannels == 1) {
|
||||||
|
@ -1203,13 +1335,13 @@ gst_mad_chain (GstPad *pad, GstBuffer *buffer)
|
||||||
* use for the next frame */
|
* use for the next frame */
|
||||||
if (new_pts) {
|
if (new_pts) {
|
||||||
new_pts = FALSE;
|
new_pts = FALSE;
|
||||||
mad->base_time = GST_BUFFER_TIMESTAMP (buffer);
|
mad->base_time = timestamp;
|
||||||
mad->total_samples = 0;
|
mad->total_samples = 0;
|
||||||
|
mad->base_byte_offset = GST_BUFFER_OFFSET (buffer);
|
||||||
|
mad->bytes_consumed = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mad->restart) {
|
if (gst_mad_check_restart (mad)) {
|
||||||
mad->restart = FALSE;
|
|
||||||
mad->tempsize = 0;
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1220,6 +1352,7 @@ next:
|
||||||
/* move out pointer to where mad want the next data */
|
/* move out pointer to where mad want the next data */
|
||||||
mad_input_buffer += consumed;
|
mad_input_buffer += consumed;
|
||||||
mad->tempsize -= consumed;
|
mad->tempsize -= consumed;
|
||||||
|
mad->bytes_consumed += consumed;
|
||||||
}
|
}
|
||||||
memmove (mad->tempbuffer, mad_input_buffer, mad->tempsize);
|
memmove (mad->tempbuffer, mad_input_buffer, mad->tempsize);
|
||||||
}
|
}
|
||||||
|
@ -1252,6 +1385,7 @@ gst_mad_change_state (GstElement *element)
|
||||||
mad->channels = 0;
|
mad->channels = 0;
|
||||||
mad->vbr_average = 0;
|
mad->vbr_average = 0;
|
||||||
mad->base_time = 0;
|
mad->base_time = 0;
|
||||||
|
mad->segment_start = 0;
|
||||||
mad->framestamp = 0;
|
mad->framestamp = 0;
|
||||||
mad->new_header = TRUE;
|
mad->new_header = TRUE;
|
||||||
mad->framecount = 0;
|
mad->framecount = 0;
|
||||||
|
|
Loading…
Reference in a new issue