diff --git a/ext/flac/gstflac.c b/ext/flac/gstflac.c index c7bd381452..6faa035739 100644 --- a/ext/flac/gstflac.c +++ b/ext/flac/gstflac.c @@ -84,6 +84,12 @@ plugin_init (GModule *module, GstPlugin *plugin) GstTypeFactory *type; GstCaps *raw_caps, *flac_caps; + /* this filter needs the bytestream package */ + if (!gst_library_load ("gstbytestream")) { + gst_info ("vorbis:: could not load support library: 'gstbytestream'\n"); + return FALSE; + } + gst_plugin_set_longname (plugin, "The FLAC Lossless compressor Codec"); /* create an elementfactory for the flacenc element */ diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c index a6a07e40cb..d8c4184245 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 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 void gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, +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__stream_decoder_new (); - flacdec->offset_left = 0; - flacdec->data_left = NULL; + flacdec->decoder = FLAC__seekable_stream_decoder_new (); + flacdec->total_samples = 0; + flacdec->init = TRUE; + flacdec->eos = FALSE; + flacdec->seek_pending = FALSE; - 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); - - FLAC__stream_decoder_init (flacdec->decoder); + 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); } static void -gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data) -{ -} - -static void -gst_flacdec_error_callback (const FLAC__StreamDecoder *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) +gst_flacdec_metadata_callback (const FLAC__SeekableStreamDecoder *decoder, + const FLAC__StreamMetaData *metadata, 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; + flacdec->stream_samples = metadata->data.stream_info.total_samples; +} + +static void +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; } - 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; - } - memcpy (buffer, indata, *bytes); + GST_DEBUG (0, error); + + gst_element_error (GST_ELEMENT (flacdec), error); +} - if (inbuf) - gst_buffer_unref (inbuf); +static FLAC__SeekableStreamDecoderSeekStatus +gst_flacdec_seek (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 position, void *client_data) +{ + FlacDec *flacdec; - return FLAC__STREAM_DECODER_READ_CONTINUE; + 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__SeekableStreamDecoderTellStatus +gst_flacdec_tell (const FLAC__SeekableStreamDecoder *decoder, FLAC__uint64 *position, void *client_data) +{ + FlacDec *flacdec; + + flacdec = GST_FLACDEC (client_data); + + *position = gst_bytestream_tell (flacdec->bs); + if (*position == -1) + return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_ERROR; + + 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, insize); + *bytes = insize; + gst_bytestream_flush_fast (flacdec->bs, insize); + + 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; } - FLAC__stream_decoder_process_one_frame (flacdec->decoder); + 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; + } + + if (GST_ELEMENT_CLASS (parent_class)->change_state) + return GST_ELEMENT_CLASS (parent_class)->change_state (element); + + return GST_STATE_SUCCESS; } diff --git a/ext/flac/gstflacdec.h b/ext/flac/gstflacdec.h index 6faf1717a9..6ba447fd6f 100644 --- a/ext/flac/gstflacdec.h +++ b/ext/flac/gstflacdec.h @@ -24,6 +24,7 @@ #include #include +#include #include @@ -42,17 +43,25 @@ typedef struct _FlacDec FlacDec; typedef struct _FlacDecClass FlacDecClass; struct _FlacDec { - GstElement element; + GstElement element; - GstPad *sinkpad,*srcpad; + GstPad *sinkpad,*srcpad; + GstByteStream *bs; - FLAC__StreamDecoder *decoder; + FLAC__SeekableStreamDecoder *decoder; + gint channels; + gint depth; + gint frequency; - gint offset_left; - GstBuffer *data_left; + gboolean need_discont; + gboolean seek_pending; + gint64 seek_value; - gboolean eos; - guint state; + gboolean init; + guint64 total_samples; + guint64 stream_samples; + + gboolean eos; }; struct _FlacDecClass { diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c index 0119f95a72..a9b9af9c2a 100644 --- a/ext/flac/gstflacenc.c +++ b/ext/flac/gstflacenc.c @@ -132,10 +132,15 @@ gst_flacenc_init (FlacEnc *flacenc) flacenc->srcpad = gst_pad_new_from_template (enc_src_template, "src"); gst_element_add_pad(GST_ELEMENT(flacenc),flacenc->srcpad); + flacenc->first = TRUE; + flacenc->first_buf = NULL; flacenc->encoder = FLAC__stream_encoder_new(); + FLAC__stream_encoder_set_write_callback (flacenc->encoder, gst_flacenc_write_callback); FLAC__stream_encoder_set_metadata_callback (flacenc->encoder, gst_flacenc_metadata_callback); FLAC__stream_encoder_set_client_data (flacenc->encoder, flacenc); + + GST_FLAG_SET (flacenc, GST_ELEMENT_EVENT_AWARE); } static FLAC__StreamEncoderWriteStatus @@ -153,6 +158,12 @@ gst_flacenc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte memcpy (GST_BUFFER_DATA (outbuf), buffer, bytes); + if (flacenc->first) { + flacenc->first_buf = outbuf; + gst_buffer_ref (outbuf); + flacenc->first = FALSE; + } + gst_pad_push (flacenc->srcpad, outbuf); return FLAC__STREAM_ENCODER_WRITE_OK; @@ -161,10 +172,49 @@ gst_flacenc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte static void gst_flacenc_metadata_callback (const FLAC__StreamEncoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data) { + GstEvent *event; + FlacEnc *flacenc; + + flacenc = GST_FLACENC (client_data); + + event = gst_event_new_discontinuous (FALSE, GST_FORMAT_BYTES, 0, NULL); + gst_pad_push (flacenc->srcpad, GST_BUFFER (event)); + + if (flacenc->first_buf) { + const FLAC__uint64 samples = metadata->data.stream_info.total_samples; + const unsigned min_framesize = metadata->data.stream_info.min_framesize; + const unsigned max_framesize = metadata->data.stream_info.max_framesize; + + guint8 *data = GST_BUFFER_DATA (flacenc->first_buf); + GstBuffer *outbuf = flacenc->first_buf; + + /* this looks evil but is actually how one is supposed to write + * the stream stats according to the FLAC examples */ + + memcpy (&data[26], metadata->data.stream_info.md5sum, 16); + + data[21] = (data[21] & 0xf0) | + (FLAC__byte)((samples >> 32) & 0x0f); + data[22] = (FLAC__byte)((samples >> 24) & 0xff); + data[23] = (FLAC__byte)((samples >> 16) & 0xff); + data[24] = (FLAC__byte)((samples >> 8 ) & 0xff); + data[25] = (FLAC__byte)((samples ) & 0xff); + + data[12] = (FLAC__byte)((min_framesize >> 16) & 0xFF); + data[13] = (FLAC__byte)((min_framesize >> 8 ) & 0xFF); + data[14] = (FLAC__byte)((min_framesize ) & 0xFF); + + data[15] = (FLAC__byte)((max_framesize >> 16) & 0xFF); + data[16] = (FLAC__byte)((max_framesize >> 8 ) & 0xFF); + data[17] = (FLAC__byte)((max_framesize ) & 0xFF); + + flacenc->first_buf = NULL; + gst_pad_push (flacenc->srcpad, outbuf); + } } static void -gst_flacenc_chain (GstPad *pad,GstBuffer *buf) +gst_flacenc_chain (GstPad *pad, GstBuffer *buf) { FlacEnc *flacenc; gint32 *data[FLAC__MAX_CHANNELS]; @@ -179,6 +229,19 @@ gst_flacenc_chain (GstPad *pad,GstBuffer *buf) flacenc = GST_FLACENC (gst_pad_get_parent (pad)); + if (GST_IS_EVENT (buf)) { + GstEvent *event = GST_EVENT (buf); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + FLAC__stream_encoder_finish(flacenc->encoder); + default: + gst_pad_event_default (pad, event); + break; + } + return; + } + channels = flacenc->channels; depth = flacenc->depth; diff --git a/ext/flac/gstflacenc.h b/ext/flac/gstflacenc.h index 1fe5e9001f..bca59c87ff 100644 --- a/ext/flac/gstflacenc.h +++ b/ext/flac/gstflacenc.h @@ -41,14 +41,16 @@ typedef struct _FlacEnc FlacEnc; typedef struct _FlacEncClass FlacEncClass; struct _FlacEnc { - GstElement element; + GstElement element; GstPad *sinkpad,*srcpad; - gboolean eos; - gint channels; - gint depth; - gint sample_rate; + gboolean first; + GstBuffer *first_buf; + gboolean eos; + gint channels; + gint depth; + gint sample_rate; FLAC__StreamEncoder *encoder; };