diff options
author | marcus <marcus@FreeBSD.org> | 2007-12-17 13:22:36 +0800 |
---|---|---|
committer | marcus <marcus@FreeBSD.org> | 2007-12-17 13:22:36 +0800 |
commit | 25ff9e408f68b57bcd36acb9ba5d4a89e2b7a87c (patch) | |
tree | 6830f5eaa4724b525dd734be6af4a7dfa023cc15 /multimedia/gstreamer-plugins-ugly | |
parent | 65123d76a42d1ae257e395a17025c04f38e34ec3 (diff) | |
download | freebsd-ports-gnome-25ff9e408f68b57bcd36acb9ba5d4a89e2b7a87c.tar.gz freebsd-ports-gnome-25ff9e408f68b57bcd36acb9ba5d4a89e2b7a87c.tar.zst freebsd-ports-gnome-25ff9e408f68b57bcd36acb9ba5d4a89e2b7a87c.zip |
Merge the mpegaudioparse plug-in from gstreamer CVS to fix a problem with MP3
playback after upgrading to gstreamer-0.10.15.
Obtained from: GStreamer CVS
Diffstat (limited to 'multimedia/gstreamer-plugins-ugly')
3 files changed, 796 insertions, 1 deletions
diff --git a/multimedia/gstreamer-plugins-ugly/Makefile b/multimedia/gstreamer-plugins-ugly/Makefile index 0e25b4fc9b0e..06025445ed23 100644 --- a/multimedia/gstreamer-plugins-ugly/Makefile +++ b/multimedia/gstreamer-plugins-ugly/Makefile @@ -6,7 +6,7 @@ # $MCom: ports/multimedia/gstreamer-plugins-ugly/Makefile,v 1.8 2006/09/14 21:20:43 ahze Exp $ # -PORTREVISION= 0 +PORTREVISION= 1 CATEGORIES= multimedia COMMENT= Ugly gstreamer-plugins diff --git a/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.c b/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.c new file mode 100644 index 000000000000..6c1d5b594cd0 --- /dev/null +++ b/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.c @@ -0,0 +1,733 @@ +--- gst/mpegaudioparse/gstmpegaudioparse.c.orig 2007-12-17 00:16:51.000000000 -0500 ++++ gst/mpegaudioparse/gstmpegaudioparse.c 2007-12-17 00:17:13.000000000 -0500 +@@ -29,6 +29,7 @@ + GST_DEBUG_CATEGORY_STATIC (mp3parse_debug); + #define GST_CAT_DEFAULT mp3parse_debug + ++ + /* elementfactory information */ + static GstElementDetails mp3parse_details = { + "MPEG1 Audio Parser", +@@ -71,8 +72,9 @@ enum + + + static void gst_mp3parse_class_init (GstMPEGAudioParseClass * klass); +-static void gst_mp3parse_base_init (GstMPEGAudioParseClass * klass); +-static void gst_mp3parse_init (GstMPEGAudioParse * mp3parse); ++static void gst_mp3parse_base_init (gpointer klass); ++static void gst_mp3parse_init (GstMPEGAudioParse * mp3parse, ++ GstMPEGAudioParseClass * klass); + + static gboolean gst_mp3parse_sink_event (GstPad * pad, GstEvent * event); + static GstFlowReturn gst_mp3parse_chain (GstPad * pad, GstBuffer * buffer); +@@ -95,35 +97,11 @@ static gboolean mp3parse_bytepos_to_time + static gboolean + mp3parse_total_bytes (GstMPEGAudioParse * mp3parse, gint64 * total); + +-static GstElementClass *parent_class = NULL; +- + /*static guint gst_mp3parse_signals[LAST_SIGNAL] = { 0 }; */ + +-GType +-gst_mp3parse_get_type (void) +-{ +- static GType mp3parse_type = 0; +- +- if (!mp3parse_type) { +- static const GTypeInfo mp3parse_info = { +- sizeof (GstMPEGAudioParseClass), +- (GBaseInitFunc) gst_mp3parse_base_init, +- NULL, +- (GClassInitFunc) gst_mp3parse_class_init, +- NULL, +- NULL, +- sizeof (GstMPEGAudioParse), +- 0, +- (GInstanceInitFunc) gst_mp3parse_init, +- }; ++GST_BOILERPLATE (GstMPEGAudioParse, gst_mp3parse, GstElement, GST_TYPE_ELEMENT); + +- mp3parse_type = g_type_register_static (GST_TYPE_ELEMENT, +- "GstMPEGAudioParse", &mp3parse_info, 0); +- } +- return mp3parse_type; +-} +- +-static guint mp3types_bitrates[2][3][16] = { ++static const guint mp3types_bitrates[2][3][16] = { + { + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,}, +@@ -136,7 +114,7 @@ static guint mp3types_bitrates[2][3][16] + }, + }; + +-static guint mp3types_freqs[3][3] = { {44100, 48000, 32000}, ++static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000}, + {22050, 24000, 16000}, + {11025, 12000, 8000} + }; +@@ -218,13 +196,14 @@ mp3_caps_create (guint layer, guint chan + new = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 1, + "layer", G_TYPE_INT, layer, +- "rate", G_TYPE_INT, samplerate, "channels", G_TYPE_INT, channels, NULL); ++ "rate", G_TYPE_INT, samplerate, ++ "channels", G_TYPE_INT, channels, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); + + return new; + } + + static void +-gst_mp3parse_base_init (GstMPEGAudioParseClass * klass) ++gst_mp3parse_base_init (gpointer klass) + { + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + +@@ -265,8 +244,8 @@ gst_mp3parse_reset (GstMPEGAudioParse * + { + mp3parse->skip = 0; + mp3parse->resyncing = TRUE; +- mp3parse->cur_offset = -1; + mp3parse->next_ts = GST_CLOCK_TIME_NONE; ++ mp3parse->cur_offset = -1; + + mp3parse->tracked_offset = 0; + mp3parse->pending_ts = GST_CLOCK_TIME_NONE; +@@ -276,6 +255,7 @@ gst_mp3parse_reset (GstMPEGAudioParse * + + mp3parse->rate = mp3parse->channels = mp3parse->layer = -1; + mp3parse->version = 1; ++ mp3parse->max_bitreservoir = GST_CLOCK_TIME_NONE; + + mp3parse->avg_bitrate = 0; + mp3parse->bitrate_sum = 0; +@@ -285,21 +265,42 @@ gst_mp3parse_reset (GstMPEGAudioParse * + + mp3parse->xing_flags = 0; + mp3parse->xing_bitrate = 0; ++ ++ if (mp3parse->seek_table) { ++ g_list_foreach (mp3parse->seek_table, (GFunc) g_free, NULL); ++ g_list_free (mp3parse->seek_table); ++ mp3parse->seek_table = NULL; ++ } ++ ++ g_mutex_lock (mp3parse->pending_accurate_seeks_lock); ++ if (mp3parse->pending_accurate_seeks) { ++ g_slist_foreach (mp3parse->pending_accurate_seeks, (GFunc) g_free, NULL); ++ g_slist_free (mp3parse->pending_accurate_seeks); ++ mp3parse->pending_accurate_seeks = NULL; ++ } ++ g_mutex_unlock (mp3parse->pending_accurate_seeks_lock); ++ ++ if (mp3parse->pending_segment) { ++ GstEvent **eventp = &mp3parse->pending_segment; ++ ++ gst_event_replace (eventp, NULL); ++ } ++ ++ mp3parse->exact_position = FALSE; ++ gst_segment_init (&mp3parse->segment, GST_FORMAT_TIME); + } + + static void +-gst_mp3parse_init (GstMPEGAudioParse * mp3parse) ++gst_mp3parse_init (GstMPEGAudioParse * mp3parse, GstMPEGAudioParseClass * klass) + { + mp3parse->sinkpad = +- gst_pad_new_from_template (gst_static_pad_template_get +- (&mp3_sink_template), "sink"); ++ gst_pad_new_from_static_template (&mp3_sink_template, "sink"); + gst_pad_set_event_function (mp3parse->sinkpad, gst_mp3parse_sink_event); + gst_pad_set_chain_function (mp3parse->sinkpad, gst_mp3parse_chain); + gst_element_add_pad (GST_ELEMENT (mp3parse), mp3parse->sinkpad); + + mp3parse->srcpad = +- gst_pad_new_from_template (gst_static_pad_template_get +- (&mp3_src_template), "src"); ++ gst_pad_new_from_static_template (&mp3_src_template, "src"); + gst_pad_use_fixed_caps (mp3parse->srcpad); + gst_pad_set_event_function (mp3parse->srcpad, mp3parse_src_event); + gst_pad_set_query_function (mp3parse->srcpad, mp3parse_src_query); +@@ -307,6 +308,7 @@ gst_mp3parse_init (GstMPEGAudioParse * m + gst_element_add_pad (GST_ELEMENT (mp3parse), mp3parse->srcpad); + + mp3parse->adapter = gst_adapter_new (); ++ mp3parse->pending_accurate_seeks_lock = g_mutex_new (); + + gst_mp3parse_reset (mp3parse); + } +@@ -316,10 +318,14 @@ gst_mp3parse_dispose (GObject * object) + { + GstMPEGAudioParse *mp3parse = GST_MP3PARSE (object); + ++ gst_mp3parse_reset (mp3parse); ++ + if (mp3parse->adapter) { + g_object_unref (mp3parse->adapter); + mp3parse->adapter = NULL; + } ++ g_mutex_free (mp3parse->pending_accurate_seeks_lock); ++ mp3parse->pending_accurate_seeks_lock = NULL; + + G_OBJECT_CLASS (parent_class)->dispose (object); + } +@@ -329,6 +335,7 @@ gst_mp3parse_sink_event (GstPad * pad, G + { + gboolean res; + GstMPEGAudioParse *mp3parse; ++ GstEvent **eventp; + + mp3parse = GST_MP3PARSE (gst_pad_get_parent (pad)); + +@@ -343,6 +350,59 @@ gst_mp3parse_sink_event (GstPad * pad, G + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, + &format, &start, &stop, &pos); + ++ g_mutex_lock (mp3parse->pending_accurate_seeks_lock); ++ if (format == GST_FORMAT_BYTES && mp3parse->pending_accurate_seeks) { ++ MPEGAudioPendingAccurateSeek *seek = NULL; ++ GSList *node; ++ ++ for (node = mp3parse->pending_accurate_seeks; node; node = node->next) { ++ MPEGAudioPendingAccurateSeek *tmp = node->data; ++ ++ if (tmp->upstream_start == pos) { ++ seek = tmp; ++ break; ++ } ++ } ++ if (seek) { ++ GstSegment *s = &seek->segment; ++ ++ event = ++ gst_event_new_new_segment_full (FALSE, s->rate, s->applied_rate, ++ GST_FORMAT_TIME, s->start, s->stop, s->last_stop); ++ ++ mp3parse->segment = seek->segment; ++ ++ mp3parse->resyncing = FALSE; ++ mp3parse->cur_offset = pos; ++ mp3parse->next_ts = seek->timestamp_start; ++ mp3parse->pending_ts = GST_CLOCK_TIME_NONE; ++ mp3parse->tracked_offset = 0; ++ ++ gst_event_parse_new_segment_full (event, &update, &rate, ++ &applied_rate, &format, &start, &stop, &pos); ++ ++ GST_DEBUG_OBJECT (mp3parse, ++ "Pushing accurate newseg rate %g, applied rate %g, " ++ "format %d, start %lld, stop %lld, pos %lld\n", rate, ++ applied_rate, format, start, stop, pos); ++ ++ g_free (seek); ++ mp3parse->pending_accurate_seeks = ++ g_slist_delete_link (mp3parse->pending_accurate_seeks, node); ++ ++ g_mutex_unlock (mp3parse->pending_accurate_seeks_lock); ++ res = gst_pad_push_event (mp3parse->srcpad, event); ++ ++ return res; ++ } else { ++ GST_WARNING_OBJECT (mp3parse, ++ "Accurate seek not possible, didn't get an appropiate upstream segment"); ++ } ++ } ++ g_mutex_unlock (mp3parse->pending_accurate_seeks_lock); ++ ++ mp3parse->exact_position = FALSE; ++ + if (format == GST_FORMAT_BYTES) { + GstClockTime seg_start, seg_stop, seg_pos; + +@@ -356,8 +416,9 @@ gst_mp3parse_sink_event (GstPad * pad, G + GST_FORMAT_TIME, seg_start, seg_stop, seg_pos); + format = GST_FORMAT_TIME; + GST_DEBUG_OBJECT (mp3parse, "Converted incoming segment to TIME. " +- "start = %" G_GINT64_FORMAT ", stop = %" G_GINT64_FORMAT +- "pos = %" G_GINT64_FORMAT, seg_start, seg_stop, seg_pos); ++ "start = %" GST_TIME_FORMAT ", stop = %" GST_TIME_FORMAT ++ ", pos = %" GST_TIME_FORMAT, GST_TIME_ARGS (seg_start), ++ GST_TIME_ARGS (seg_stop), GST_TIME_ARGS (seg_pos)); + } + } + +@@ -368,6 +429,7 @@ gst_mp3parse_sink_event (GstPad * pad, G + event = gst_event_new_new_segment_full (update, rate, applied_rate, + GST_FORMAT_TIME, 0, GST_CLOCK_TIME_NONE, 0); + } ++ + mp3parse->resyncing = TRUE; + mp3parse->cur_offset = -1; + mp3parse->next_ts = GST_CLOCK_TIME_NONE; +@@ -379,12 +441,23 @@ gst_mp3parse_sink_event (GstPad * pad, G + GST_DEBUG_OBJECT (mp3parse, "Pushing newseg rate %g, applied rate %g, " + "format %d, start %lld, stop %lld, pos %lld\n", + rate, applied_rate, format, start, stop, pos); +- res = gst_pad_push_event (mp3parse->srcpad, event); ++ ++ gst_segment_set_newsegment_full (&mp3parse->segment, update, rate, ++ applied_rate, format, start, stop, pos); ++ ++ /* save the segment for later, right before we push a new buffer so that ++ * the caps are fixed and the next linked element can receive the segment. */ ++ eventp = &mp3parse->pending_segment; ++ gst_event_replace (eventp, event); ++ gst_event_unref (event); ++ res = TRUE; + break; + } + case GST_EVENT_FLUSH_STOP: + /* Clear our adapter and set up for a new position */ + gst_adapter_clear (mp3parse->adapter); ++ eventp = &mp3parse->pending_segment; ++ gst_event_replace (eventp, NULL); + res = gst_pad_push_event (mp3parse->srcpad, event); + break; + default: +@@ -397,14 +470,26 @@ gst_mp3parse_sink_event (GstPad * pad, G + return res; + } + ++static MPEGAudioSeekEntry * ++mp3parse_seek_table_last_entry (GstMPEGAudioParse * mp3parse) ++{ ++ MPEGAudioSeekEntry *ret = NULL; ++ ++ if (mp3parse->seek_table) { ++ ret = mp3parse->seek_table->data; ++ } ++ ++ return ret; ++} ++ + /* Prepare a buffer of the indicated size, timestamp it and output */ + static GstFlowReturn + gst_mp3parse_emit_frame (GstMPEGAudioParse * mp3parse, guint size) + { + GstBuffer *outbuf; + guint bitrate; +- +- GST_DEBUG_OBJECT (mp3parse, "pushing buffer of %d bytes", size); ++ GstFlowReturn ret = GST_FLOW_OK; ++ GstClockTime push_start; + + outbuf = gst_adapter_take_buffer (mp3parse->adapter, size); + +@@ -456,6 +541,22 @@ gst_mp3parse_emit_frame (GstMPEGAudioPar + } + } + ++ if (GST_BUFFER_TIMESTAMP (outbuf) == 0) ++ mp3parse->exact_position = TRUE; ++ ++ if (mp3parse->exact_position && (!mp3parse->seek_table || ++ (mp3parse_seek_table_last_entry (mp3parse))->byte < ++ GST_BUFFER_OFFSET (outbuf))) { ++ MPEGAudioSeekEntry *entry = g_new0 (MPEGAudioSeekEntry, 1); ++ ++ entry->byte = mp3parse->cur_offset; ++ entry->timestamp = GST_BUFFER_TIMESTAMP (outbuf); ++ mp3parse->seek_table = g_list_prepend (mp3parse->seek_table, entry); ++ GST_DEBUG_OBJECT (mp3parse, "Adding index entry %" GST_TIME_FORMAT ++ " @ offset 0x%08" G_GINT64_MODIFIER "x", ++ GST_TIME_ARGS (entry->timestamp), entry->byte); ++ } ++ + /* Update our byte offset tracking */ + if (mp3parse->cur_offset != -1) { + mp3parse->cur_offset += size; +@@ -483,7 +584,53 @@ gst_mp3parse_emit_frame (GstMPEGAudioPar + mp3parse->srcpad, taglist); + } + +- return gst_pad_push (mp3parse->srcpad, outbuf); ++ /* We start pushing 9 frames earlier (29 frames for MPEG2) than ++ * segment start to be able to decode the first frame we want. ++ * 9 (29) frames are the theoretical maximum of frames that contain ++ * data for the current frame (bit reservoir). ++ */ ++ ++ if (mp3parse->segment.start == 0) { ++ push_start = 0; ++ } else if (GST_CLOCK_TIME_IS_VALID (mp3parse->max_bitreservoir)) { ++ if (mp3parse->segment.start > mp3parse->max_bitreservoir) ++ push_start = mp3parse->segment.start - mp3parse->max_bitreservoir; ++ else ++ push_start = 0; ++ } else { ++ push_start = mp3parse->segment.start; ++ } ++ ++ if (G_UNLIKELY ((GST_CLOCK_TIME_IS_VALID (push_start) && ++ GST_BUFFER_TIMESTAMP (outbuf) + GST_BUFFER_DURATION (outbuf) ++ < push_start) ++ || (GST_CLOCK_TIME_IS_VALID (mp3parse->segment.stop) ++ && GST_BUFFER_TIMESTAMP (outbuf) >= mp3parse->segment.stop))) { ++ GST_DEBUG_OBJECT (mp3parse, ++ "Buffer outside of configured segment range %" GST_TIME_FORMAT ++ " to %" GST_TIME_FORMAT ", dropping, timestamp %" ++ GST_TIME_FORMAT ", offset 0x%08" G_GINT64_MODIFIER "x", ++ GST_TIME_ARGS (push_start), GST_TIME_ARGS (mp3parse->segment.stop), ++ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), ++ GST_BUFFER_OFFSET (outbuf)); ++ gst_buffer_unref (outbuf); ++ ret = GST_FLOW_OK; ++ } else { ++ GST_DEBUG_OBJECT (mp3parse, ++ "pushing buffer of %d bytes, timestamp %" GST_TIME_FORMAT ++ ", offset 0x%08" G_GINT64_MODIFIER "x", size, ++ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), ++ GST_BUFFER_OFFSET (outbuf)); ++ mp3parse->segment.last_stop = GST_BUFFER_TIMESTAMP (outbuf); ++ /* push any pending segment now */ ++ if (mp3parse->pending_segment) { ++ gst_pad_push_event (mp3parse->srcpad, mp3parse->pending_segment); ++ mp3parse->pending_segment = NULL; ++ } ++ ret = gst_pad_push (mp3parse->srcpad, outbuf); ++ } ++ ++ return ret; + } + + #define XING_FRAMES_FLAG 0x0001 +@@ -610,14 +757,41 @@ gst_mp3parse_handle_first_frame (GstMPEG + mp3parse->xing_bytes = 0; + + if (xing_flags & XING_TOC_FLAG) { +- gint i; ++ int i, percent = 0; ++ guchar *table = mp3parse->xing_seek_table; ++ ++ /* xing seek table: percent time -> 1/256 bytepos */ ++ memcpy (mp3parse->xing_seek_table, data, 100); + +- for (i = 0; i < 100; i++) { +- mp3parse->xing_seek_table[i] = data[0]; +- data++; ++ /* build inverse table: 1/256 bytepos -> 1/100 percent time */ ++ for (i = 0; i < 256; i++) { ++ while (percent < 99 && table[percent + 1] <= i) ++ percent++; ++ ++ if (table[percent] == i) { ++ mp3parse->xing_seek_table_inverse[i] = percent * 100; ++ } else if (table[percent] < i && percent < 99) { ++ gdouble fa, fb, fx; ++ gint a = percent, b = percent + 1; ++ ++ fa = table[a]; ++ fb = table[b]; ++ fx = (b - a) / (fb - fa) * (i - fa) + a; ++ mp3parse->xing_seek_table_inverse[i] = (guint16) (fx * 100); ++ } else if (percent == 98 && table[percent + 1] <= i) { ++ gdouble fa, fb, fx; ++ gint a = percent + 1, b = 100; ++ ++ fa = table[a]; ++ fb = 256.0; ++ fx = (b - a) / (fb - fa) * (i - fa) + a; ++ mp3parse->xing_seek_table_inverse[i] = (guint16) (fx * 100); ++ } + } ++ data += 100; + } else { + memset (mp3parse->xing_seek_table, 0, 100); ++ memset (mp3parse->xing_seek_table_inverse, 0, 256); + } + + if (xing_flags & XING_VBR_SCALE_FLAG) { +@@ -777,6 +951,9 @@ gst_mp3parse_chain (GstPad * pad, GstBuf + mp3parse->spf = 1152; + } + ++ mp3parse->max_bitreservoir = gst_util_uint64_scale (GST_SECOND, ++ ((version == 1) ? 10 : 30) * mp3parse->spf, mp3parse->rate); ++ + /* Check the first frame for a Xing header to get our total length */ + if (mp3parse->frame_count == 0) { + /* For the first frame in the file, look for a Xing frame after +@@ -930,6 +1107,45 @@ gst_mp3parse_change_state (GstElement * + return result; + } + ++static gboolean ++mp3parse_total_bytes (GstMPEGAudioParse * mp3parse, gint64 * total) ++{ ++ GstFormat fmt = GST_FORMAT_BYTES; ++ ++ if (gst_pad_query_peer_duration (mp3parse->sinkpad, &fmt, total)) ++ return TRUE; ++ ++ if (mp3parse->xing_flags & XING_BYTES_FLAG) { ++ *total = mp3parse->xing_bytes; ++ return TRUE; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++mp3parse_total_time (GstMPEGAudioParse * mp3parse, GstClockTime * total) ++{ ++ gint64 total_bytes; ++ ++ *total = GST_CLOCK_TIME_NONE; ++ ++ if (mp3parse->xing_flags & XING_FRAMES_FLAG) { ++ *total = mp3parse->xing_total_time; ++ return TRUE; ++ } ++ ++ /* Calculate time from the measured bitrate */ ++ if (!mp3parse_total_bytes (mp3parse, &total_bytes)) ++ return FALSE; ++ ++ if (total_bytes != -1 ++ && !mp3parse_bytepos_to_time (mp3parse, total_bytes, total)) ++ return FALSE; ++ ++ return TRUE; ++} ++ + /* Convert a timestamp to the file position required to start decoding that + * timestamp. For now, this just uses the avg bitrate. Later, use an + * incrementally accumulated seek table */ +@@ -937,12 +1153,38 @@ static gboolean + mp3parse_time_to_bytepos (GstMPEGAudioParse * mp3parse, GstClockTime ts, + gint64 * bytepos) + { ++ gint64 total_bytes; ++ GstClockTime total_time; ++ + /* -1 always maps to -1 */ + if (ts == -1) { + *bytepos = -1; + return TRUE; + } + ++ /* If XING seek table exists use this for time->byte conversion */ ++ if ((mp3parse->xing_flags & XING_TOC_FLAG) && ++ mp3parse_total_bytes (mp3parse, &total_bytes) && ++ mp3parse_total_time (mp3parse, &total_time)) { ++ gdouble fa, fb, fx; ++ gdouble percent = ++ CLAMP ((100.0 * gst_util_guint64_to_gdouble (ts)) / ++ gst_util_guint64_to_gdouble (total_time), 0.0, 100.0); ++ gint index = CLAMP (percent, 0, 99); ++ ++ fa = mp3parse->xing_seek_table[index]; ++ if (index < 99) ++ fb = mp3parse->xing_seek_table[index + 1]; ++ else ++ fb = 256.0; ++ ++ fx = fa + (fb - fa) * (percent - index); ++ ++ *bytepos = (1.0 / 256.0) * fx * total_bytes; ++ ++ return TRUE; ++ } ++ + if (mp3parse->avg_bitrate == 0) + goto no_bitrate; + +@@ -958,6 +1200,9 @@ static gboolean + mp3parse_bytepos_to_time (GstMPEGAudioParse * mp3parse, + gint64 bytepos, GstClockTime * ts) + { ++ gint64 total_bytes; ++ GstClockTime total_time; ++ + if (bytepos == -1) { + *ts = GST_CLOCK_TIME_NONE; + return TRUE; +@@ -968,61 +1213,33 @@ mp3parse_bytepos_to_time (GstMPEGAudioPa + return TRUE; + } + +- /* Cannot convert anything except 0 if we don't have a bitrate yet */ +- if (mp3parse->avg_bitrate == 0) +- return FALSE; +- +- *ts = (GstClockTime) gst_util_uint64_scale (GST_SECOND, bytepos * 8, +- mp3parse->avg_bitrate); +- return TRUE; +-} +- +-static gboolean +-mp3parse_total_bytes (GstMPEGAudioParse * mp3parse, gint64 * total) +-{ +- GstQuery *query; +- GstPad *peer; +- +- if ((peer = gst_pad_get_peer (mp3parse->sinkpad)) != NULL) { +- query = gst_query_new_duration (GST_FORMAT_BYTES); +- gst_query_set_duration (query, GST_FORMAT_BYTES, -1); +- +- if (gst_pad_query (peer, query)) { +- gst_object_unref (peer); +- gst_query_parse_duration (query, NULL, total); +- return TRUE; +- } +- gst_object_unref (peer); +- } +- +- if (mp3parse->xing_flags & XING_BYTES_FLAG) { +- *total = mp3parse->xing_bytes; +- return TRUE; +- } +- +- return FALSE; +-} ++ /* If XING seek table exists use this for byte->time conversion */ ++ if ((mp3parse->xing_flags & XING_TOC_FLAG) && ++ mp3parse_total_bytes (mp3parse, &total_bytes) && ++ mp3parse_total_time (mp3parse, &total_time)) { ++ gdouble fa, fb, fx; ++ gdouble pos = CLAMP ((bytepos * 256.0) / total_bytes, 0.0, 256.0); ++ gint index = CLAMP (pos, 0, 255); ++ ++ fa = mp3parse->xing_seek_table_inverse[index]; ++ if (index < 255) ++ fb = mp3parse->xing_seek_table_inverse[index + 1]; ++ else ++ fb = 10000.0; + +-static gboolean +-mp3parse_total_time (GstMPEGAudioParse * mp3parse, GstClockTime * total) +-{ +- gint64 total_bytes; ++ fx = fa + (fb - fa) * (pos - index); + +- *total = GST_CLOCK_TIME_NONE; ++ *ts = (1.0 / 10000.0) * fx * gst_util_guint64_to_gdouble (total_time); + +- if (mp3parse->xing_flags & XING_FRAMES_FLAG) { +- *total = mp3parse->xing_total_time; + return TRUE; + } + +- /* Calculate time from the measured bitrate */ +- if (!mp3parse_total_bytes (mp3parse, &total_bytes)) +- return FALSE; +- +- if (total_bytes != -1 +- && !mp3parse_bytepos_to_time (mp3parse, total_bytes, total)) ++ /* Cannot convert anything except 0 if we don't have a bitrate yet */ ++ if (mp3parse->avg_bitrate == 0) + return FALSE; + ++ *ts = (GstClockTime) gst_util_uint64_scale (GST_SECOND, bytepos * 8, ++ mp3parse->avg_bitrate); + return TRUE; + } + +@@ -1036,11 +1253,12 @@ mp3parse_handle_seek (GstMPEGAudioParse + gint64 cur, stop; + gint64 byte_cur, byte_stop; + +- /* FIXME: Use GstSegment for tracking our position */ +- + gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur, + &stop_type, &stop); + ++ GST_DEBUG_OBJECT (mp3parse, "Performing seek to %" GST_TIME_FORMAT, ++ GST_TIME_ARGS (cur)); ++ + /* For any format other than TIME, see if upstream handles + * it directly or fail. For TIME, try upstream, but do it ourselves if + * it fails upstream */ +@@ -1055,6 +1273,90 @@ mp3parse_handle_seek (GstMPEGAudioParse + + /* Handle TIME based seeks by converting to a BYTE position */ + ++ /* For accurate seeking get the frame 9 (MPEG1) or 29 (MPEG2) frames ++ * before the one we want to seek to and push them all to the decoder. ++ * ++ * This is necessary because of the bit reservoir. See ++ * http://www.mars.org/mailman/public/mad-dev/2002-May/000634.html ++ * ++ */ ++ ++ if (flags & GST_SEEK_FLAG_ACCURATE) { ++ MPEGAudioPendingAccurateSeek *seek = ++ g_new0 (MPEGAudioPendingAccurateSeek, 1); ++ GstClockTime start; ++ ++ seek->segment = mp3parse->segment; ++ ++ gst_segment_set_seek (&seek->segment, rate, GST_FORMAT_TIME, ++ flags, cur_type, cur, stop_type, stop, NULL); ++ ++ if (!mp3parse->seek_table) { ++ byte_cur = 0; ++ byte_stop = -1; ++ start = 0; ++ } else { ++ MPEGAudioSeekEntry *entry = NULL, *start_entry = NULL, *stop_entry = NULL; ++ GList *start_node, *stop_node; ++ ++ for (start_node = mp3parse->seek_table; start_node; ++ start_node = start_node->next) { ++ entry = start_node->data; ++ ++ if (cur - mp3parse->max_bitreservoir >= entry->timestamp) { ++ start_entry = entry; ++ break; ++ } ++ } ++ ++ if (!start_entry) { ++ start_entry = mp3parse->seek_table->data; ++ start = start_entry->timestamp; ++ byte_cur = start_entry->byte; ++ } else { ++ start = start_entry->timestamp; ++ byte_cur = start_entry->byte; ++ } ++ ++ for (stop_node = mp3parse->seek_table; stop_node; ++ stop_node = stop_node->next) { ++ entry = stop_node->data; ++ ++ if (stop >= entry->timestamp) { ++ stop_node = stop_node->prev; ++ stop_entry = (stop_node) ? stop_node->data : NULL; ++ break; ++ } ++ } ++ ++ if (!stop_entry) { ++ byte_stop = -1; ++ } else { ++ byte_stop = stop_entry->byte; ++ } ++ ++ } ++ g_mutex_lock (mp3parse->pending_accurate_seeks_lock); ++ event = gst_event_new_seek (rate, GST_FORMAT_BYTES, flags, cur_type, ++ byte_cur, stop_type, byte_stop); ++ if (gst_pad_push_event (mp3parse->sinkpad, event)) { ++ mp3parse->exact_position = TRUE; ++ seek->upstream_start = byte_cur; ++ seek->timestamp_start = start; ++ mp3parse->pending_accurate_seeks = ++ g_slist_prepend (mp3parse->pending_accurate_seeks, seek); ++ g_mutex_unlock (mp3parse->pending_accurate_seeks_lock); ++ return TRUE; ++ } else { ++ g_mutex_unlock (mp3parse->pending_accurate_seeks_lock); ++ mp3parse->exact_position = TRUE; ++ g_free (seek); ++ return TRUE; ++ } ++ } ++ ++ mp3parse->exact_position = FALSE; ++ + /* Convert the TIME to the appropriate BYTE position at which to resume + * decoding. */ + if (!mp3parse_time_to_bytepos (mp3parse, (GstClockTime) cur, &byte_cur)) diff --git a/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.h b/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.h new file mode 100644 index 000000000000..38c08d75e6c8 --- /dev/null +++ b/multimedia/gstreamer-plugins-ugly/files/patch-gst_mpegaudioparse_gstmpegaudioparse.h @@ -0,0 +1,62 @@ +--- gst/mpegaudioparse/gstmpegaudioparse.h.orig 2007-12-17 00:16:56.000000000 -0500 ++++ gst/mpegaudioparse/gstmpegaudioparse.h 2007-12-17 00:17:15.000000000 -0500 +@@ -40,13 +40,29 @@ G_BEGIN_DECLS + + typedef struct _GstMPEGAudioParse GstMPEGAudioParse; + typedef struct _GstMPEGAudioParseClass GstMPEGAudioParseClass; ++typedef struct _MPEGAudioSeekEntry MPEGAudioSeekEntry; ++typedef struct _MPEGAudioPendingAccurateSeek MPEGAudioPendingAccurateSeek; ++ ++ ++struct _MPEGAudioSeekEntry { ++ gint64 byte; ++ GstClockTime timestamp; ++}; ++ ++struct _MPEGAudioPendingAccurateSeek { ++ GstSegment segment; ++ gint64 upstream_start; ++ GstClockTime timestamp_start; ++}; + + struct _GstMPEGAudioParse { + GstElement element; + + GstPad *sinkpad, *srcpad; + ++ GstSegment segment; + GstClockTime next_ts; ++ + /* Offset as supplied by incoming buffers */ + gint64 cur_offset; + +@@ -62,6 +78,7 @@ struct _GstMPEGAudioParse { + guint skip; /* number of frames to skip */ + guint bit_rate; /* in kbps */ + gint channels, rate, layer, version; ++ GstClockTime max_bitreservoir; + gint spf; /* Samples per frame */ + + gboolean resyncing; /* True when attempting to resync (stricter checks are +@@ -79,9 +96,21 @@ struct _GstMPEGAudioParse { + guint32 xing_frames; + GstClockTime xing_total_time; + guint32 xing_bytes; ++ /* percent -> filepos mapping */ + guchar xing_seek_table[100]; ++ /* filepos -> percent mapping */ ++ guint16 xing_seek_table_inverse[256]; + guint32 xing_vbr_scale; + guint xing_bitrate; ++ ++ /* Accurate seeking */ ++ GList *seek_table; ++ GMutex *pending_accurate_seeks_lock; ++ GSList *pending_accurate_seeks; ++ gboolean exact_position; ++ ++ /* pending segment */ ++ GstEvent *pending_segment; + }; + + struct _GstMPEGAudioParseClass { |