mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
FLAC updates, implement sample accurate seeking, add some convert/query functions, make the encoder write a header
Original commit message from CVS: FLAC updates, implement sample accurate seeking, add some convert/query functions, make the encoder write a header
This commit is contained in:
parent
e805a46dca
commit
51aa4cdbd7
5 changed files with 499 additions and 74 deletions
|
@ -84,6 +84,12 @@ plugin_init (GModule *module, GstPlugin *plugin)
|
||||||
GstTypeFactory *type;
|
GstTypeFactory *type;
|
||||||
GstCaps *raw_caps, *flac_caps;
|
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");
|
gst_plugin_set_longname (plugin, "The FLAC Lossless compressor Codec");
|
||||||
|
|
||||||
/* create an elementfactory for the flacenc element */
|
/* create an elementfactory for the flacenc element */
|
||||||
|
|
|
@ -50,20 +50,39 @@ static void gst_flacdec_class_init (FlacDecClass *klass);
|
||||||
static void gst_flacdec_init (FlacDec *flacdec);
|
static void gst_flacdec_init (FlacDec *flacdec);
|
||||||
|
|
||||||
static void gst_flacdec_loop (GstElement *element);
|
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,
|
const FLAC__StreamMetaData *metadata,
|
||||||
void *client_data);
|
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,
|
FLAC__StreamDecoderErrorStatus status,
|
||||||
void *client_data);
|
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 GstElementClass *parent_class = NULL;
|
||||||
/*static guint gst_flacdec_signals[LAST_SIGNAL] = { 0 }; */
|
/*static guint gst_flacdec_signals[LAST_SIGNAL] = { 0 }; */
|
||||||
|
|
||||||
|
@ -96,6 +115,8 @@ gst_flacdec_class_init (FlacDecClass *klass)
|
||||||
gstelement_class = (GstElementClass*)klass;
|
gstelement_class = (GstElementClass*)klass;
|
||||||
|
|
||||||
parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
|
parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
|
||||||
|
|
||||||
|
gstelement_class->change_state = gst_flacdec_change_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -103,76 +124,184 @@ gst_flacdec_init (FlacDec *flacdec)
|
||||||
{
|
{
|
||||||
flacdec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink");
|
flacdec->sinkpad = gst_pad_new_from_template (dec_sink_template, "sink");
|
||||||
gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->sinkpad);
|
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);
|
gst_element_set_loop_function (GST_ELEMENT (flacdec), gst_flacdec_loop);
|
||||||
flacdec->srcpad = gst_pad_new_from_template (dec_src_template, "src");
|
flacdec->srcpad = gst_pad_new_from_template (dec_src_template, "src");
|
||||||
gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->srcpad);
|
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->decoder = FLAC__seekable_stream_decoder_new ();
|
||||||
flacdec->offset_left = 0;
|
flacdec->total_samples = 0;
|
||||||
flacdec->data_left = NULL;
|
flacdec->init = TRUE;
|
||||||
|
flacdec->eos = FALSE;
|
||||||
|
flacdec->seek_pending = FALSE;
|
||||||
|
|
||||||
FLAC__stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read);
|
FLAC__seekable_stream_decoder_set_read_callback (flacdec->decoder, gst_flacdec_read);
|
||||||
FLAC__stream_decoder_set_write_callback (flacdec->decoder, gst_flacdec_write);
|
FLAC__seekable_stream_decoder_set_seek_callback (flacdec->decoder, gst_flacdec_seek);
|
||||||
FLAC__stream_decoder_set_metadata_callback (flacdec->decoder, gst_flacdec_metadata_callback);
|
FLAC__seekable_stream_decoder_set_tell_callback (flacdec->decoder, gst_flacdec_tell);
|
||||||
FLAC__stream_decoder_set_error_callback (flacdec->decoder, gst_flacdec_error_callback);
|
FLAC__seekable_stream_decoder_set_length_callback (flacdec->decoder, gst_flacdec_length);
|
||||||
FLAC__stream_decoder_set_client_data (flacdec->decoder, flacdec);
|
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__stream_decoder_init (flacdec->decoder);
|
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
|
static void
|
||||||
gst_flacdec_metadata_callback (const FLAC__StreamDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
|
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, FLAC__StreamDecoderErrorStatus status, void *client_data)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static FLAC__StreamDecoderReadStatus
|
|
||||||
gst_flacdec_read (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data)
|
|
||||||
{
|
{
|
||||||
FlacDec *flacdec;
|
FlacDec *flacdec;
|
||||||
GstBuffer *inbuf = NULL;
|
|
||||||
gint insize;
|
|
||||||
guchar *indata;
|
|
||||||
|
|
||||||
flacdec = GST_FLACDEC (client_data);
|
flacdec = GST_FLACDEC (client_data);
|
||||||
|
|
||||||
if (flacdec->data_left == NULL) {
|
flacdec->stream_samples = metadata->data.stream_info.total_samples;
|
||||||
inbuf = gst_pad_pull (flacdec->sinkpad);
|
}
|
||||||
insize = GST_BUFFER_SIZE (inbuf);
|
|
||||||
indata = GST_BUFFER_DATA (inbuf);
|
static void
|
||||||
}
|
gst_flacdec_error_callback (const FLAC__SeekableStreamDecoder *decoder,
|
||||||
else {
|
FLAC__StreamDecoderErrorStatus status, void *client_data)
|
||||||
inbuf = flacdec->data_left;
|
{
|
||||||
insize = GST_BUFFER_SIZE (inbuf) - flacdec->offset_left;
|
FlacDec *flacdec;
|
||||||
indata = GST_BUFFER_DATA (inbuf) + flacdec->offset_left;
|
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) {
|
GST_DEBUG (0, error);
|
||||||
/* we have more than we can handle */
|
|
||||||
flacdec->data_left = inbuf;
|
gst_element_error (GST_ELEMENT (flacdec), error);
|
||||||
flacdec->offset_left += *bytes;
|
}
|
||||||
inbuf = NULL;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
flacdec->data_left = NULL;
|
|
||||||
flacdec->offset_left = 0;
|
|
||||||
*bytes = insize;
|
|
||||||
}
|
|
||||||
memcpy (buffer, indata, *bytes);
|
|
||||||
|
|
||||||
if (inbuf)
|
static FLAC__SeekableStreamDecoderSeekStatus
|
||||||
gst_buffer_unref (inbuf);
|
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
|
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;
|
FlacDec *flacdec;
|
||||||
GstBuffer *outbuf;
|
GstBuffer *outbuf;
|
||||||
|
@ -183,6 +312,32 @@ gst_flacdec_write (const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
|
||||||
|
|
||||||
flacdec = GST_FLACDEC (client_data);
|
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)) {
|
if (!GST_PAD_CAPS (flacdec->srcpad)) {
|
||||||
gst_pad_try_set_caps (flacdec->srcpad,
|
gst_pad_try_set_caps (flacdec->srcpad,
|
||||||
GST_CAPS_NEW (
|
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),
|
"rate", GST_PROPS_INT (frame->header.sample_rate),
|
||||||
"channels", GST_PROPS_INT (channels)
|
"channels", GST_PROPS_INT (channels)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
flacdec->depth = depth;
|
||||||
|
flacdec->channels = channels;
|
||||||
|
flacdec->frequency = frame->header.sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
outbuf = gst_buffer_new ();
|
outbuf = gst_buffer_new ();
|
||||||
GST_BUFFER_SIZE (outbuf) = samples * channels * ((depth+7)>>3);
|
GST_BUFFER_SIZE (outbuf) = samples * channels * ((depth+7)>>3);
|
||||||
GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
|
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) {
|
if (depth == 8) {
|
||||||
guint8 *outbuffer = (guint8 *)GST_BUFFER_DATA (outbuf);
|
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;
|
return FLAC__STREAM_DECODER_WRITE_ABORT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flacdec->total_samples += samples;
|
||||||
|
|
||||||
gst_pad_push (flacdec->srcpad, outbuf);
|
gst_pad_push (flacdec->srcpad, outbuf);
|
||||||
|
|
||||||
return FLAC__STREAM_DECODER_WRITE_CONTINUE;
|
return FLAC__STREAM_DECODER_WRITE_CONTINUE;
|
||||||
|
@ -235,13 +397,196 @@ static void
|
||||||
gst_flacdec_loop (GstElement *element)
|
gst_flacdec_loop (GstElement *element)
|
||||||
{
|
{
|
||||||
FlacDec *flacdec;
|
FlacDec *flacdec;
|
||||||
|
gboolean res;
|
||||||
|
|
||||||
flacdec = GST_FLACDEC (element);
|
flacdec = GST_FLACDEC (element);
|
||||||
|
|
||||||
if (FLAC__stream_decoder_get_state (flacdec->decoder) == FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) {
|
if (flacdec->init) {
|
||||||
FLAC__stream_decoder_process_metadata (flacdec->decoder);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/bytestream/bytestream.h>
|
||||||
|
|
||||||
#include <FLAC/all.h>
|
#include <FLAC/all.h>
|
||||||
|
|
||||||
|
@ -42,17 +43,25 @@ typedef struct _FlacDec FlacDec;
|
||||||
typedef struct _FlacDecClass FlacDecClass;
|
typedef struct _FlacDecClass FlacDecClass;
|
||||||
|
|
||||||
struct _FlacDec {
|
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;
|
gboolean need_discont;
|
||||||
GstBuffer *data_left;
|
gboolean seek_pending;
|
||||||
|
gint64 seek_value;
|
||||||
|
|
||||||
gboolean eos;
|
gboolean init;
|
||||||
guint state;
|
guint64 total_samples;
|
||||||
|
guint64 stream_samples;
|
||||||
|
|
||||||
|
gboolean eos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _FlacDecClass {
|
struct _FlacDecClass {
|
||||||
|
|
|
@ -132,10 +132,15 @@ gst_flacenc_init (FlacEnc *flacenc)
|
||||||
flacenc->srcpad = gst_pad_new_from_template (enc_src_template, "src");
|
flacenc->srcpad = gst_pad_new_from_template (enc_src_template, "src");
|
||||||
gst_element_add_pad(GST_ELEMENT(flacenc),flacenc->srcpad);
|
gst_element_add_pad(GST_ELEMENT(flacenc),flacenc->srcpad);
|
||||||
|
|
||||||
|
flacenc->first = TRUE;
|
||||||
|
flacenc->first_buf = NULL;
|
||||||
flacenc->encoder = FLAC__stream_encoder_new();
|
flacenc->encoder = FLAC__stream_encoder_new();
|
||||||
|
|
||||||
FLAC__stream_encoder_set_write_callback (flacenc->encoder, gst_flacenc_write_callback);
|
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_metadata_callback (flacenc->encoder, gst_flacenc_metadata_callback);
|
||||||
FLAC__stream_encoder_set_client_data (flacenc->encoder, flacenc);
|
FLAC__stream_encoder_set_client_data (flacenc->encoder, flacenc);
|
||||||
|
|
||||||
|
GST_FLAG_SET (flacenc, GST_ELEMENT_EVENT_AWARE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static FLAC__StreamEncoderWriteStatus
|
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);
|
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);
|
gst_pad_push (flacenc->srcpad, outbuf);
|
||||||
|
|
||||||
return FLAC__STREAM_ENCODER_WRITE_OK;
|
return FLAC__STREAM_ENCODER_WRITE_OK;
|
||||||
|
@ -161,10 +172,49 @@ gst_flacenc_write_callback (const FLAC__StreamEncoder *encoder, const FLAC__byte
|
||||||
static void
|
static void
|
||||||
gst_flacenc_metadata_callback (const FLAC__StreamEncoder *encoder, const FLAC__StreamMetaData *metadata, void *client_data)
|
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
|
static void
|
||||||
gst_flacenc_chain (GstPad *pad,GstBuffer *buf)
|
gst_flacenc_chain (GstPad *pad, GstBuffer *buf)
|
||||||
{
|
{
|
||||||
FlacEnc *flacenc;
|
FlacEnc *flacenc;
|
||||||
gint32 *data[FLAC__MAX_CHANNELS];
|
gint32 *data[FLAC__MAX_CHANNELS];
|
||||||
|
@ -179,6 +229,19 @@ gst_flacenc_chain (GstPad *pad,GstBuffer *buf)
|
||||||
|
|
||||||
flacenc = GST_FLACENC (gst_pad_get_parent (pad));
|
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;
|
channels = flacenc->channels;
|
||||||
depth = flacenc->depth;
|
depth = flacenc->depth;
|
||||||
|
|
||||||
|
|
|
@ -41,14 +41,16 @@ typedef struct _FlacEnc FlacEnc;
|
||||||
typedef struct _FlacEncClass FlacEncClass;
|
typedef struct _FlacEncClass FlacEncClass;
|
||||||
|
|
||||||
struct _FlacEnc {
|
struct _FlacEnc {
|
||||||
GstElement element;
|
GstElement element;
|
||||||
|
|
||||||
GstPad *sinkpad,*srcpad;
|
GstPad *sinkpad,*srcpad;
|
||||||
|
|
||||||
gboolean eos;
|
gboolean first;
|
||||||
gint channels;
|
GstBuffer *first_buf;
|
||||||
gint depth;
|
gboolean eos;
|
||||||
gint sample_rate;
|
gint channels;
|
||||||
|
gint depth;
|
||||||
|
gint sample_rate;
|
||||||
|
|
||||||
FLAC__StreamEncoder *encoder;
|
FLAC__StreamEncoder *encoder;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue