diff options
Diffstat (limited to 'ext/flac/gstflacdec.c')
-rw-r--r-- | ext/flac/gstflacdec.c | 455 |
1 files changed, 400 insertions, 55 deletions
diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c index a6a07e40..d8c41842 100644 --- a/ext/flac/gstflacdec.c +++ b/ext/flac/gstflacdec.c @@ -50,20 +50,39 @@ static void gst_flacdec_class_init (FlacDecClass *klass); static void gst_flacdec_init (FlacDec *flacdec); static void gst_flacdec_loop (GstElement *element); - -static void gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, +static GstElementStateReturn + gst_flacdec_change_state (GstElement *element); +static gboolean gst_flacdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, + GstFormat *dest_format, gint64 *dest_value); +static gboolean gst_flacdec_src_query (GstPad *pad, GstPadQueryType type, + GstFormat *format, gint64 *value); +static gboolean gst_flacdec_src_event (GstPad *pad, GstEvent *event); + +static FLAC__SeekableStreamDecoderReadStatus + gst_flacdec_read (const FLAC__SeekableStreamDecoder *decoder, + FLAC__byte buffer[], unsigned *bytes, void *client_data); +static FLAC__SeekableStreamDecoderSeekStatus + gst_flacdec_seek (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 position, void *client_data); +static FLAC__SeekableStreamDecoderTellStatus + gst_flacdec_tell (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 *position, void *client_data); +static FLAC__SeekableStreamDecoderLengthStatus + gst_flacdec_length (const FLAC__SeekableStreamDecoder *decoder, + FLAC__uint64 *length, void *client_data); +static FLAC__bool gst_flacdec_eof (const FLAC__SeekableStreamDecoder *decoder, + void *client_data); +static FLAC__StreamDecoderWriteStatus + gst_flacdec_write (const FLAC__SeekableStreamDecoder *decoder, + const FLAC__Frame *frame, const FLAC__int32 *buffer[], + void *client_data); +static void gst_flacdec_metadata_callback (const FLAC__SeekableStreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data); -static void gst_flacdec_error_callback (const FLAC__StreamDecoder *decoder, +static void gst_flacdec_error_callback (const FLAC__SeekableStreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); -static FLAC__StreamDecoderReadStatus gst_flacdec_read (const FLAC__StreamDecoder *decoder, - FLAC__byte buffer[], unsigned *bytes, void *client_data); -static FLAC__StreamDecoderWriteStatus gst_flacdec_write (const FLAC__StreamDecoder *decoder, - const FLAC__Frame *frame, const FLAC__int32 *buffer[], - void *client_data); - static GstElementClass *parent_class = NULL; /*static guint gst_flacdec_signals[LAST_SIGNAL] = { 0 }; */ @@ -96,6 +115,8 @@ gst_flacdec_class_init (FlacDecClass *klass) gstelement_class = (GstElementClass*)klass; parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + + gstelement_class->change_state = gst_flacdec_change_state; } static void @@ -103,76 +124,184 @@ gst_flacdec_init (FlacDec *flacdec) { flacdec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink"); gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->sinkpad); + gst_pad_set_convert_function (flacdec->sinkpad, NULL); gst_element_set_loop_function (GST_ELEMENT (flacdec), gst_flacdec_loop); flacdec->srcpad = gst_pad_new_from_template (dec_src_template, "src"); gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->srcpad); + gst_pad_set_query_function (flacdec->srcpad, gst_flacdec_src_query); + gst_pad_set_convert_function (flacdec->srcpad, gst_flacdec_convert_src); + gst_pad_set_event_function (flacdec->srcpad, gst_flacdec_src_event); + + flacdec->decoder = FLAC__seekable_stream_decoder_new (); + flacdec->total_samples = 0; + flacdec->init = TRUE; + flacdec->eos = FALSE; + flacdec->seek_pending = FALSE; + + FLAC__seekable_stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read); + FLAC__seekable_stream_decoder_set_seek_callback (flacdec->decoder, gst_flacdec_seek); + FLAC__seekable_stream_decoder_set_tell_callback (flacdec->decoder, gst_flacdec_tell); + FLAC__seekable_stream_decoder_set_length_callback (flacdec->decoder, gst_flacdec_length); + FLAC__seekable_stream_decoder_set_eof_callback (flacdec->decoder, gst_flacdec_eof); + FLAC__seekable_stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write); + FLAC__seekable_stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback); + FLAC__seekable_stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback); + FLAC__seekable_stream_decoder_set_client_data (flacdec->decoder, flacdec); +} - flacdec->decoder = FLAC__stream_decoder_new (); - flacdec->offset_left = 0; - flacdec->data_left = NULL; +static void +gst_flacdec_metadata_callback (const FLAC__SeekableStreamDecoder *decoder, + const FLAC__StreamMetaData *metadata, void *client_data) +{ + FlacDec *flacdec; - FLAC__stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read); - FLAC__stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write); - FLAC__stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback); - FLAC__stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback); - FLAC__stream_decoder_set_client_data (flacdec->decoder, flacdec); + flacdec = GST_FLACDEC (client_data); - FLAC__stream_decoder_init (flacdec->decoder); + flacdec->stream_samples = metadata->data.stream_info.total_samples; } static void -gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data) +gst_flacdec_error_callback (const FLAC__SeekableStreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, void *client_data) { + FlacDec *flacdec; + gchar *error; + + flacdec = GST_FLACDEC (client_data); + + switch (status) { + case FLAC__STREAM_DECODER_ERROR_LOST_SYNC: + error = "lost sync"; + break; + case FLAC__STREAM_DECODER_ERROR_BAD_HEADER: + error = "bad header"; + break; + case FLAC__STREAM_DECODER_ERROR_FRAME_CRC_MISMATCH: + error = "CRC mismatch"; + break; + default: + error = "unkown error"; + break; + } + + GST_DEBUG (0, error); + + gst_element_error (GST_ELEMENT (flacdec), error); } -static void -gst_flacdec_error_callback (const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +static FLAC__SeekableStreamDecoderSeekStatus +gst_flacdec_seek (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 position, void *client_data) { + FlacDec *flacdec; + + flacdec = GST_FLACDEC (client_data); + + GST_DEBUG (0, "seek %lld\n", position); + if (!gst_bytestream_seek (flacdec->bs, position, GST_SEEK_METHOD_SET)) { + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; + } + return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; } -static FLAC__StreamDecoderReadStatus -gst_flacdec_read (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) +static FLAC__SeekableStreamDecoderTellStatus +gst_flacdec_tell (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *position, void *client_data) { FlacDec *flacdec; - GstBuffer *inbuf = NULL; - gint insize; - guchar *indata; flacdec = GST_FLACDEC (client_data); - if (flacdec->data_left == NULL) { - inbuf = gst_pad_pull (flacdec->sinkpad); - insize = GST_BUFFER_SIZE (inbuf); - indata = GST_BUFFER_DATA (inbuf); - } - else { - inbuf = flacdec->data_left; - insize = GST_BUFFER_SIZE (inbuf) - flacdec->offset_left; - indata = GST_BUFFER_DATA (inbuf) + flacdec->offset_left; - } + *position = gst_bytestream_tell (flacdec->bs); + if (*position == -1) + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR; - if (*bytes < insize) { - /* we have more than we can handle */ - flacdec->data_left = inbuf; - flacdec->offset_left += *bytes; - inbuf = NULL; - } - else { - flacdec->data_left = NULL; - flacdec->offset_left = 0; - *bytes = insize; + GST_DEBUG (0, "tell %lld\n", *position); + + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; +} + +static FLAC__SeekableStreamDecoderLengthStatus +gst_flacdec_length (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *length, void *client_data) +{ + FlacDec *flacdec; + + flacdec = GST_FLACDEC (client_data); + + *length = gst_bytestream_length (flacdec->bs); + if (*length == -1) + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR; + + GST_DEBUG (0, "length %lld\n", *length); + + return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; +} + +static FLAC__bool +gst_flacdec_eof (const FLAC__SeekableStreamDecoder *decoder, void *client_data) +{ + FlacDec *flacdec; + + flacdec = GST_FLACDEC (client_data); + GST_DEBUG (0, "eof %d\n", flacdec->eos); + + return flacdec->eos; +} + +static FLAC__SeekableStreamDecoderReadStatus +gst_flacdec_read (const FLAC__SeekableStreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) +{ + FlacDec *flacdec; + gint insize = 0; + guint8 *indata; + + flacdec = GST_FLACDEC (client_data); + + //g_print ("read %u\n", *bytes); + + while (insize == 0) { + insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, *bytes); + if (insize < *bytes) { + GstEvent *event; + guint32 avail; + + gst_bytestream_get_status (flacdec->bs, &avail, &event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + GST_DEBUG (0, "eos"); + flacdec->eos = TRUE; + if (avail == 0) { + return 0; + } + break; + case GST_EVENT_DISCONTINUOUS: + GST_DEBUG (0, "discont"); + + /* we are not yet sending the discont, we'll do that in the next write operation */ + flacdec->need_discont = TRUE; + gst_event_free (event); + break; + default: + gst_pad_event_default (flacdec->sinkpad, event); + break; + } + if (avail > 0) + insize = gst_bytestream_peek_bytes (flacdec->bs, &indata, avail); + else + insize = 0; + } } - memcpy (buffer, indata, *bytes); - if (inbuf) - gst_buffer_unref (inbuf); + memcpy (buffer, indata, insize); + *bytes = insize; + gst_bytestream_flush_fast (flacdec->bs, insize); - return FLAC__STREAM_DECODER_READ_CONTINUE; + return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; } static FLAC__StreamDecoderWriteStatus -gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data) +gst_flacdec_write (const FLAC__SeekableStreamDecoder *decoder, const FLAC__Frame *frame, + const FLAC__int32 *buffer[], void *client_data) { FlacDec *flacdec; GstBuffer *outbuf; @@ -183,6 +312,32 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, flacdec = GST_FLACDEC (client_data); + if (flacdec->need_discont) { + gint64 time = 0, bytes = 0; + GstFormat format; + GstEvent *discont; + + flacdec->need_discont = FALSE; + + if (flacdec->seek_pending) { + flacdec->total_samples = flacdec->seek_value; + } + + GST_DEBUG (0, "send discont"); + + format = GST_FORMAT_TIME; + gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples, + &format, &time); + format = GST_FORMAT_BYTES; + gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples, + &format, &bytes); + discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, + GST_FORMAT_BYTES, bytes, + GST_FORMAT_UNITS, flacdec->total_samples, NULL); + + gst_pad_push (flacdec->srcpad, GST_BUFFER (discont)); + } + if (!GST_PAD_CAPS (flacdec->srcpad)) { gst_pad_try_set_caps (flacdec->srcpad, GST_CAPS_NEW ( @@ -197,11 +352,16 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, "rate", GST_PROPS_INT (frame->header.sample_rate), "channels", GST_PROPS_INT (channels) )); + + flacdec->depth = depth; + flacdec->channels = channels; + flacdec->frequency = frame->header.sample_rate; } outbuf = gst_buffer_new (); GST_BUFFER_SIZE (outbuf) = samples * channels * ((depth+7)>>3); GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf)); + GST_BUFFER_TIMESTAMP (outbuf) = flacdec->total_samples * GST_SECOND / frame->header.sample_rate; if (depth == 8) { guint8 *outbuffer = (guint8 *)GST_BUFFER_DATA (outbuf); @@ -226,6 +386,8 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, return FLAC__STREAM_DECODER_WRITE_ABORT; } + flacdec->total_samples += samples; + gst_pad_push (flacdec->srcpad, outbuf); return FLAC__STREAM_DECODER_WRITE_CONTINUE; @@ -235,13 +397,196 @@ static void gst_flacdec_loop (GstElement *element) { FlacDec *flacdec; + gboolean res; flacdec = GST_FLACDEC (element); - - if (FLAC__stream_decoder_get_state (flacdec->decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) { - FLAC__stream_decoder_process_metadata (flacdec->decoder); + + if (flacdec->init) { + FLAC__seekable_stream_decoder_init (flacdec->decoder); + FLAC__seekable_stream_decoder_process_metadata (flacdec->decoder); + flacdec->init = FALSE; + } + + if (flacdec->seek_pending) { + GST_DEBUG (GST_CAT_EVENT, "perform seek to sample %lld\n", flacdec->seek_value); + + if (FLAC__seekable_stream_decoder_seek_absolute(flacdec->decoder, flacdec->seek_value)) { + flacdec->total_samples = flacdec->seek_value; + GST_DEBUG (GST_CAT_EVENT, "seek done\n"); + } + else { + GST_DEBUG (GST_CAT_EVENT, "seek failed\n"); + } + flacdec->seek_pending = FALSE; + } + + res = FLAC__seekable_stream_decoder_process_one_frame (flacdec->decoder); + if (FLAC__seekable_stream_decoder_get_state (flacdec->decoder) == FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM) { + GstEvent *event; + + FLAC__seekable_stream_decoder_finish(flacdec->decoder); + flacdec->init = TRUE; + + event = gst_event_new(GST_EVENT_EOS); + gst_pad_push (flacdec->srcpad, GST_BUFFER (event)); + gst_element_set_eos (element); + } +} + +static gboolean +gst_flacdec_convert_src (GstPad *pad, GstFormat src_format, gint64 src_value, + GstFormat *dest_format, gint64 *dest_value) +{ + gboolean res = TRUE; + FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad)); + guint scale = 1; + gint bytes_per_sample; + + bytes_per_sample = flacdec->channels * ((flacdec->depth+7)>>3); + + switch (src_format) { + case GST_FORMAT_BYTES: + switch (*dest_format) { + case GST_FORMAT_UNITS: + if (bytes_per_sample == 0) + return FALSE; + *dest_value = src_value / bytes_per_sample; + break; + case GST_FORMAT_DEFAULT: + *dest_format = GST_FORMAT_TIME; + case GST_FORMAT_TIME: + { + gint byterate = bytes_per_sample * flacdec->frequency; + + if (byterate == 0) + return FALSE; + *dest_value = src_value * GST_SECOND / byterate; + break; + } + default: + res = FALSE; + } + break; + case GST_FORMAT_UNITS: + switch (*dest_format) { + case GST_FORMAT_BYTES: + *dest_value = src_value * bytes_per_sample; + break; + case GST_FORMAT_DEFAULT: + *dest_format = GST_FORMAT_TIME; + case GST_FORMAT_TIME: + if (flacdec->frequency == 0) + return FALSE; + *dest_value = src_value * GST_SECOND / flacdec->frequency; + break; + default: + res = FALSE; + } + break; + case GST_FORMAT_TIME: + switch (*dest_format) { + case GST_FORMAT_DEFAULT: + *dest_format = GST_FORMAT_BYTES; + case GST_FORMAT_BYTES: + scale = bytes_per_sample; + case GST_FORMAT_UNITS: + *dest_value = src_value * scale * flacdec->frequency / GST_SECOND; + break; + default: + res = FALSE; + } + break; + default: + res = FALSE; + } + return res; +} + +static gboolean +gst_flacdec_src_query (GstPad *pad, GstPadQueryType type, + GstFormat *format, gint64 *value) +{ + gboolean res = TRUE; + FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad)); + + switch (type) { + case GST_PAD_QUERY_TOTAL: + { + guint64 samples; + + if (flacdec->stream_samples == 0) + samples = flacdec->total_samples; + else + samples = flacdec->stream_samples; + + gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, samples, format, value); + break; + } + case GST_PAD_QUERY_POSITION: + gst_pad_convert (flacdec->srcpad, GST_FORMAT_UNITS, flacdec->total_samples, format, value); + break; + default: + res = FALSE; + break; + } + + return res; +} + +static gboolean +gst_flacdec_src_event (GstPad *pad, GstEvent *event) +{ + gboolean res = TRUE; + FlacDec *flacdec = GST_FLACDEC (gst_pad_get_parent (pad)); + GstFormat format; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + format = GST_FORMAT_UNITS; + + if (gst_pad_convert (flacdec->srcpad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event), + &format, &flacdec->seek_value)) + flacdec->seek_pending = TRUE; + else + res = FALSE; + break; + default: + res = FALSE; + break; + } + return res; +} + +static GstElementStateReturn +gst_flacdec_change_state (GstElement *element) +{ + FlacDec *flacdec = GST_FLACDEC (element); + + switch (GST_STATE_TRANSITION (element)) { + case GST_STATE_NULL_TO_READY: + case GST_STATE_READY_TO_PAUSED: + flacdec->init = TRUE; + flacdec->bs = gst_bytestream_new (flacdec->sinkpad); + flacdec->seek_pending = FALSE; + flacdec->total_samples = 0; + flacdec->init = TRUE; + flacdec->eos = FALSE; + break; + case GST_STATE_PAUSED_TO_PLAYING: + flacdec->eos = FALSE; + case GST_STATE_PLAYING_TO_PAUSED: + break; + case GST_STATE_PAUSED_TO_READY: + gst_bytestream_destroy (flacdec->bs); + break; + case GST_STATE_READY_TO_NULL: + default: + break; } - FLAC__stream_decoder_process_one_frame (flacdec->decoder); + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; } |