diff --git a/ext/timidity/gstwildmidi.c b/ext/timidity/gstwildmidi.c index ee75775860..708a8f4eb3 100644 --- a/ext/timidity/gstwildmidi.c +++ b/ext/timidity/gstwildmidi.c @@ -284,7 +284,6 @@ gst_wildmidi_init (GstWildmidi * filter, GstWildmidiClass * g_class) filter->adapter = gst_adapter_new (); filter->bytes_per_frame = WILDMIDI_BPS; - filter->time_per_frame = GST_SECOND / WILDMIDI_RATE; } static void @@ -307,14 +306,14 @@ gst_wildmidi_src_convert (GstWildmidi * wildmidi, gboolean res = TRUE; gint64 frames; - if (src_format == *dest_format) { + if (src_format == *dest_format || src_value == -1) { *dest_value = src_value; goto done; } switch (src_format) { case GST_FORMAT_TIME: - frames = src_value / wildmidi->time_per_frame; + frames = gst_util_uint64_scale_int (src_value, WILDMIDI_RATE, GST_SECOND); break; case GST_FORMAT_BYTES: frames = src_value / (wildmidi->bytes_per_frame); @@ -329,7 +328,8 @@ gst_wildmidi_src_convert (GstWildmidi * wildmidi, switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = frames * wildmidi->time_per_frame; + *dest_value = + gst_util_uint64_scale_int (frames, GST_SECOND, WILDMIDI_RATE); break; case GST_FORMAT_BYTES: *dest_value = frames * wildmidi->bytes_per_frame; @@ -362,11 +362,13 @@ gst_wildmidi_src_query (GstPad * pad, GstQuery * query) switch (GST_QUERY_TYPE (query)) { case GST_QUERY_DURATION: gst_query_set_duration (query, GST_FORMAT_TIME, - wildmidi->o_len * wildmidi->time_per_frame); + gst_util_uint64_scale_int (wildmidi->o_len, GST_SECOND, + WILDMIDI_RATE)); break; case GST_QUERY_POSITION: gst_query_set_position (query, GST_FORMAT_TIME, - wildmidi->o_segment->last_stop * wildmidi->time_per_frame); + gst_util_uint64_scale_int (wildmidi->o_segment->last_stop, GST_SECOND, + WILDMIDI_RATE)); break; case GST_QUERY_CONVERT: gst_query_parse_convert (query, &src_format, &src_value, @@ -402,53 +404,115 @@ gst_wildmidi_src_query (GstPad * pad, GstQuery * query) return res; } -static GstSegment * -gst_wildmidi_get_segment (GstWildmidi * wildmidi, GstFormat format, - gboolean update) +static GstEvent * +gst_wildmidi_get_new_segment_event (GstWildmidi * wildmidi, GstFormat format) { gint64 start, stop, time; - GstSegment *segment = gst_segment_new (); - - gst_wildmidi_src_convert (wildmidi, - wildmidi->o_segment->format, wildmidi->o_segment->start, &format, &start); - - if (wildmidi->o_segment->stop == GST_CLOCK_TIME_NONE) { - stop = GST_CLOCK_TIME_NONE; - } else { - gst_wildmidi_src_convert (wildmidi, - wildmidi->o_segment->format, wildmidi->o_segment->stop, &format, &stop); - } - - gst_wildmidi_src_convert (wildmidi, - wildmidi->o_segment->format, wildmidi->o_segment->time, &format, &time); - - gst_segment_set_newsegment_full (segment, update, - wildmidi->o_segment->rate, wildmidi->o_segment->applied_rate, - format, start, stop, time); - - segment->last_stop = time; - - return segment; -} - -static GstEvent * -gst_wildmidi_get_new_segment_event (GstWildmidi * wildmidi, GstFormat format, - gboolean update) -{ GstSegment *segment; GstEvent *event; + GstFormat src_format; - segment = gst_wildmidi_get_segment (wildmidi, format, update); + segment = wildmidi->o_segment; + src_format = segment->format; - event = gst_event_new_new_segment_full (update, - segment->rate, segment->applied_rate, segment->format, - segment->start, segment->stop, segment->time); + /* convert the segment values to the target format */ + gst_wildmidi_src_convert (wildmidi, src_format, segment->start, &format, + &start); + gst_wildmidi_src_convert (wildmidi, src_format, segment->stop, &format, + &stop); + gst_wildmidi_src_convert (wildmidi, src_format, segment->time, &format, + &time); - gst_segment_free (segment); + event = gst_event_new_new_segment_full (FALSE, + segment->rate, segment->applied_rate, format, start, stop, time); return event; } +static gboolean +gst_wildmidi_do_seek (GstWildmidi * wildmidi, GstEvent * event) +{ + gdouble rate; + GstFormat src_format, dst_format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush, update, accurate; + gboolean res; + unsigned long int sample; + GstSegment *segment; + + if (!wildmidi->song) + return FALSE; + + gst_event_parse_seek (event, &rate, &src_format, &flags, + &start_type, &start, &stop_type, &stop); + + /* convert the input format to samples */ + dst_format = GST_FORMAT_DEFAULT; + res = TRUE; + if (start_type != GST_SEEK_TYPE_NONE) { + res = + gst_wildmidi_src_convert (wildmidi, src_format, start, &dst_format, + &start); + } + if (res && stop_type != GST_SEEK_TYPE_NONE) { + res = + gst_wildmidi_src_convert (wildmidi, src_format, stop, &dst_format, + &stop); + } + /* unsupported format */ + if (!res) + return res; + + flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); + accurate = ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE); + + if (flush) { + GST_DEBUG ("performing flush"); + gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_stop_task (wildmidi->sinkpad); + } + + segment = wildmidi->o_segment; + + GST_PAD_STREAM_LOCK (wildmidi->sinkpad); + + if (flush) { + gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_stop ()); + } + + /* update the segment now */ + gst_segment_set_seek (segment, rate, dst_format, flags, + start_type, start, stop_type, stop, &update); + + /* we need to seek to last_stop in the segment now, sample will be updated */ + sample = segment->last_stop; + + GST_OBJECT_LOCK (wildmidi); + if (accurate) { + WildMidi_SampledSeek (wildmidi->song, &sample); + } else { + WildMidi_FastSeek (wildmidi->song, &sample); + } + GST_OBJECT_UNLOCK (wildmidi); + + segment->start = segment->time = segment->last_stop = sample; + + gst_pad_push_event (wildmidi->srcpad, + gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME)); + + gst_pad_start_task (wildmidi->sinkpad, + (GstTaskFunction) gst_wildmidi_loop, wildmidi->sinkpad); + + wildmidi->discont = TRUE; + GST_PAD_STREAM_UNLOCK (wildmidi->sinkpad); + GST_DEBUG ("seek done"); + + return TRUE; +} + static gboolean gst_wildmidi_src_event (GstPad * pad, GstEvent * event) { @@ -459,66 +523,7 @@ gst_wildmidi_src_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: - { - gdouble rate; - GstFormat src_format, dst_format; - GstSeekFlags flags; - GstSeekType start_type, stop_type; - gint64 orig_start, start, stop; - gboolean flush, update; - - if (!wildmidi->song) - break; - - gst_event_parse_seek (event, &rate, &src_format, &flags, - &start_type, &orig_start, &stop_type, &stop); - - dst_format = GST_FORMAT_DEFAULT; - - gst_wildmidi_src_convert (wildmidi, src_format, orig_start, - &dst_format, &start); - gst_wildmidi_src_convert (wildmidi, src_format, stop, &dst_format, &stop); - - flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH); - - if (flush) { - GST_DEBUG ("performing flush"); - gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_start ()); - } else { - gst_pad_stop_task (wildmidi->sinkpad); - } - - GST_PAD_STREAM_LOCK (wildmidi->sinkpad); - - if (flush) { - gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_stop ()); - } - - gst_segment_set_seek (wildmidi->o_segment, rate, dst_format, flags, - start_type, start, stop_type, stop, &update); - - if ((flags && GST_SEEK_FLAG_SEGMENT) == GST_SEEK_FLAG_SEGMENT) { - GST_DEBUG_OBJECT (wildmidi, "received segment seek %d, %d", - (gint) start_type, (gint) stop_type); - } else { - GST_DEBUG_OBJECT (wildmidi, "received normal seek %d", - (gint) start_type); - update = FALSE; - } - - gst_pad_push_event (wildmidi->srcpad, - gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME, - update)); - - wildmidi->o_seek = TRUE; - - gst_pad_start_task (wildmidi->sinkpad, - (GstTaskFunction) gst_wildmidi_loop, wildmidi->sinkpad); - - GST_PAD_STREAM_UNLOCK (wildmidi->sinkpad); - GST_DEBUG ("seek done"); - } - res = TRUE; + res = gst_wildmidi_do_seek (wildmidi, event); break; default: break; @@ -551,33 +556,41 @@ gst_wildmidi_activatepull (GstPad * pad, gboolean active) static GstBuffer * gst_wildmidi_clip_buffer (GstWildmidi * wildmidi, GstBuffer * buffer) { + gint64 start, stop; gint64 new_start, new_stop; gint64 offset, length; GstBuffer *out; + guint64 bpf; + /* clipping disabled for now */ return buffer; + start = GST_BUFFER_OFFSET (buffer); + stop = GST_BUFFER_OFFSET_END (buffer); + if (!gst_segment_clip (wildmidi->o_segment, GST_FORMAT_DEFAULT, - GST_BUFFER_OFFSET (buffer), GST_BUFFER_OFFSET_END (buffer), - &new_start, &new_stop)) { + start, stop, &new_start, &new_stop)) { gst_buffer_unref (buffer); return NULL; } - if (GST_BUFFER_OFFSET (buffer) == new_start && - GST_BUFFER_OFFSET_END (buffer) == new_stop) + if (start == new_start && stop == new_stop) return buffer; - offset = new_start - GST_BUFFER_OFFSET (buffer); + + offset = new_start - start; length = new_stop - new_start; - out = gst_buffer_create_sub (buffer, offset * wildmidi->bytes_per_frame, - length * wildmidi->bytes_per_frame); + bpf = wildmidi->bytes_per_frame; + out = gst_buffer_create_sub (buffer, offset * bpf, length * bpf); GST_BUFFER_OFFSET (out) = new_start; GST_BUFFER_OFFSET_END (out) = new_stop; - GST_BUFFER_TIMESTAMP (out) = new_start * wildmidi->time_per_frame; - GST_BUFFER_DURATION (out) = (new_stop - new_start) * wildmidi->time_per_frame; + GST_BUFFER_TIMESTAMP (out) = + gst_util_uint64_scale_int (new_start, GST_SECOND, WILDMIDI_RATE); + GST_BUFFER_DURATION (out) = + gst_util_uint64_scale_int (new_stop, GST_SECOND, WILDMIDI_RATE) - + GST_BUFFER_TIMESTAMP (out); gst_buffer_unref (buffer); @@ -591,44 +604,50 @@ gst_wildmidi_get_buffer (GstWildmidi * wildmidi) size_t bytes_read; gint64 samples; GstBuffer *buffer; + GstSegment *segment; + guint8 *data; + guint size; + guint bpf; - buffer = gst_buffer_new_and_alloc (256 * wildmidi->bytes_per_frame); + bpf = wildmidi->bytes_per_frame; - bytes_read = - WildMidi_GetOutput (wildmidi->song, (char *) GST_BUFFER_DATA (buffer), - (unsigned long int) GST_BUFFER_SIZE (buffer)); + buffer = gst_buffer_new_and_alloc (256 * bpf); + + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + GST_OBJECT_LOCK (wildmidi); + bytes_read = WildMidi_GetOutput (wildmidi->song, (char *) data, + (unsigned long int) size); + GST_OBJECT_UNLOCK (wildmidi); if (bytes_read == 0) { gst_buffer_unref (buffer); return NULL; } - GST_BUFFER_OFFSET (buffer) = - wildmidi->o_segment->last_stop * wildmidi->bytes_per_frame; + /* adjust buffer size */ + size = GST_BUFFER_SIZE (buffer) = bytes_read; + + segment = wildmidi->o_segment; + + GST_BUFFER_OFFSET (buffer) = segment->last_stop; GST_BUFFER_TIMESTAMP (buffer) = - wildmidi->o_segment->last_stop * wildmidi->time_per_frame; + gst_util_uint64_scale_int (segment->last_stop, GST_SECOND, WILDMIDI_RATE); - if (bytes_read < GST_BUFFER_SIZE (buffer)) { - GstBuffer *old = buffer; + samples = size / bpf; + segment->last_stop += samples; - buffer = gst_buffer_create_sub (buffer, 0, bytes_read); - gst_buffer_unref (old); - } - - samples = GST_BUFFER_SIZE (buffer) / wildmidi->bytes_per_frame; - - wildmidi->o_segment->last_stop += samples; - - GST_BUFFER_OFFSET_END (buffer) = - wildmidi->o_segment->last_stop * wildmidi->bytes_per_frame; - GST_BUFFER_DURATION (buffer) = samples * wildmidi->time_per_frame; + GST_BUFFER_OFFSET_END (buffer) = segment->last_stop; + GST_BUFFER_DURATION (buffer) = + gst_util_uint64_scale_int (segment->last_stop, GST_SECOND, + WILDMIDI_RATE) - GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG_OBJECT (wildmidi, - "generated buffer %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT + "buffer ts: %" GST_TIME_FORMAT ", dur: %" GST_TIME_FORMAT " (%d samples)", - GST_TIME_ARGS ((guint64) GST_BUFFER_TIMESTAMP (buffer)), - GST_TIME_ARGS (((guint64) (GST_BUFFER_TIMESTAMP (buffer) + - GST_BUFFER_DURATION (buffer)))), samples); + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)), + GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), samples); return gst_wildmidi_clip_buffer (wildmidi, buffer); } @@ -647,13 +666,11 @@ gst_wildmidi_parse_song (GstWildmidi * wildmidi) data = gst_adapter_take (wildmidi->adapter, size); /* this method takes our memory block */ + GST_OBJECT_LOCK (wildmidi); wildmidi->song = WildMidi_OpenBuffer (data, size); - if (!wildmidi->song) { - GST_ELEMENT_ERROR (wildmidi, STREAM, DECODE, (NULL), - ("Unable to parse midi")); - return GST_FLOW_ERROR; - } + if (!wildmidi->song) + goto open_failed; WildMidi_LoadSamples (wildmidi->song); @@ -663,21 +680,33 @@ gst_wildmidi_parse_song (GstWildmidi * wildmidi) wildmidi->high_quality); info = WildMidi_GetInfo (wildmidi->song); + GST_OBJECT_UNLOCK (wildmidi); + wildmidi->o_len = info->approx_total_samples; outcaps = gst_caps_copy (gst_pad_get_pad_template_caps (wildmidi->srcpad)); gst_pad_set_caps (wildmidi->srcpad, outcaps); gst_caps_unref (outcaps); + /* we keep an internal segment in samples */ gst_segment_set_newsegment (wildmidi->o_segment, FALSE, 1.0, GST_FORMAT_DEFAULT, 0, GST_CLOCK_TIME_NONE, 0); gst_pad_push_event (wildmidi->srcpad, - gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME, FALSE)); + gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME)); GST_DEBUG_OBJECT (wildmidi, "Parsing song done"); return GST_FLOW_OK; + + /* ERRORS */ +open_failed: + { + GST_OBJECT_UNLOCK (wildmidi); + GST_ELEMENT_ERROR (wildmidi, STREAM, DECODE, (NULL), + ("Unable to parse midi data")); + return GST_FLOW_ERROR; + } } static GstFlowReturn @@ -686,27 +715,12 @@ gst_wildmidi_do_play (GstWildmidi * wildmidi) GstBuffer *out; GstFlowReturn ret; - if (wildmidi->o_seek) { - unsigned long int sample; - - /* perform a seek internally */ - sample = wildmidi->o_segment->time; - - if (wildmidi->accurate_seek) { - WildMidi_SampledSeek (wildmidi->song, &sample); - } else { - WildMidi_FastSeek (wildmidi->song, &sample); - } - - wildmidi->o_segment->last_stop = wildmidi->o_segment->time = sample; - } - if (!(out = gst_wildmidi_get_buffer (wildmidi))) goto eos; - if (wildmidi->o_seek) { + if (wildmidi->discont) { GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT); - wildmidi->o_seek = FALSE; + wildmidi->discont = FALSE; } gst_buffer_set_caps (out, GST_PAD_CAPS (wildmidi->srcpad)); @@ -718,7 +732,6 @@ gst_wildmidi_do_play (GstWildmidi * wildmidi) eos: { GST_LOG_OBJECT (wildmidi, "Song ended"); - wildmidi->o_seek = FALSE; return GST_FLOW_UNEXPECTED; } } @@ -782,7 +795,7 @@ gst_wildmidi_loop (GstPad * sinkpad) wildmidi->state = GST_WILDMIDI_STATE_PARSE; } else if (ret != GST_FLOW_OK) { GST_ELEMENT_ERROR (wildmidi, STREAM, DECODE, (NULL), - ("Unable to load song")); + ("Unable to read song")); goto pause; } else { GST_DEBUG_OBJECT (wildmidi, "pushing buffer"); @@ -792,15 +805,11 @@ gst_wildmidi_loop (GstPad * sinkpad) break; } case GST_WILDMIDI_STATE_PARSE: - { - if (!wildmidi->song) { - ret = gst_wildmidi_parse_song (wildmidi); - if (ret != GST_FLOW_OK) - goto pause; - } + ret = gst_wildmidi_parse_song (wildmidi); + if (ret != GST_FLOW_OK) + goto pause; wildmidi->state = GST_WILDMIDI_STATE_PLAY; break; - } case GST_WILDMIDI_STATE_PLAY: ret = gst_wildmidi_do_play (wildmidi); if (ret != GST_FLOW_OK) @@ -848,6 +857,7 @@ gst_wildmidi_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_READY_TO_PAUSED: wildmidi->offset = 0; wildmidi->state = GST_WILDMIDI_STATE_LOAD; + wildmidi->discont = FALSE; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; @@ -861,9 +871,11 @@ gst_wildmidi_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: + GST_OBJECT_LOCK (wildmidi); if (wildmidi->song) WildMidi_Close (wildmidi->song); wildmidi->song = NULL; + GST_OBJECT_UNLOCK (wildmidi); gst_adapter_clear (wildmidi->adapter); break; case GST_STATE_CHANGE_READY_TO_NULL: @@ -887,16 +899,20 @@ gst_wildmidi_set_property (GObject * object, guint prop_id, switch (prop_id) { case ARG_LINEAR_VOLUME: + GST_OBJECT_LOCK (object); wildmidi->linear_volume = g_value_get_boolean (value); if (wildmidi->song) WildMidi_SetOption (wildmidi->song, WM_MO_LINEAR_VOLUME, wildmidi->linear_volume); + GST_OBJECT_UNLOCK (object); break; case ARG_HIGH_QUALITY: + GST_OBJECT_LOCK (object); wildmidi->high_quality = g_value_get_boolean (value); if (wildmidi->song) WildMidi_SetOption (wildmidi->song, WM_MO_EXPENSIVE_INTERPOLATION, wildmidi->high_quality); + GST_OBJECT_UNLOCK (object); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -916,10 +932,14 @@ gst_wildmidi_get_property (GObject * object, guint prop_id, switch (prop_id) { case ARG_LINEAR_VOLUME: + GST_OBJECT_LOCK (object); g_value_set_boolean (value, wildmidi->linear_volume); + GST_OBJECT_UNLOCK (object); break; case ARG_HIGH_QUALITY: + GST_OBJECT_LOCK (object); g_value_set_boolean (value, wildmidi->high_quality); + GST_OBJECT_UNLOCK (object); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); diff --git a/ext/timidity/gstwildmidi.h b/ext/timidity/gstwildmidi.h index c33f5005e6..6527ee047a 100644 --- a/ext/timidity/gstwildmidi.h +++ b/ext/timidity/gstwildmidi.h @@ -66,16 +66,12 @@ struct _GstWildmidi guint64 offset; /* output data */ - gboolean o_new_segment, o_seek; + gboolean discont; GstSegment o_segment[1]; gint64 o_len; /* format of the stream */ - gint64 bytes_per_frame; - GstClockTime time_per_frame; - - /* options */ - gboolean accurate_seek; + guint bytes_per_frame; /* wildmidi settings */ gboolean high_quality;