"rate = (int) [ 4000, 96000 ], " "channels = (int) [ 1, 6 ]")
);
-GST_BOILERPLATE (GstA52Dec, gst_a52dec, GstAudioDecoder,
- GST_TYPE_AUDIO_DECODER);
+#define gst_a52dec_parent_class parent_class
- G_DEFINE_TYPE (GstA52Dec, gst_a52dec, GST_TYPE_ELEMENT);
++G_DEFINE_TYPE (GstA52Dec, gst_a52dec, GST_TYPE_AUDIO_DECODER);
+
+ static gboolean gst_a52dec_start (GstAudioDecoder * dec);
+ static gboolean gst_a52dec_stop (GstAudioDecoder * dec);
+ static gboolean gst_a52dec_set_format (GstAudioDecoder * bdec, GstCaps * caps);
+ static gboolean gst_a52dec_parse (GstAudioDecoder * dec, GstAdapter * adapter,
+ gint * offset, gint * length);
+ static GstFlowReturn gst_a52dec_handle_frame (GstAudioDecoder * dec,
+ GstBuffer * buffer);
+ static GstFlowReturn gst_a52dec_pre_push (GstAudioDecoder * bdec,
+ GstBuffer ** buffer);
-static GstFlowReturn gst_a52dec_chain (GstPad * pad, GstBuffer * buffer);
-
+static GstFlowReturn gst_a52dec_chain (GstPad * pad, GstObject * parent,
+ GstBuffer * buffer);
- static GstFlowReturn gst_a52dec_chain_raw (GstPad * pad, GstObject * parent,
- GstBuffer * buf);
- static gboolean gst_a52dec_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
- static GstStateChangeReturn gst_a52dec_change_state (GstElement * element,
- GstStateChange transition);
-
static void gst_a52dec_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_a52dec_get_property (GObject * object, guint prop_id,
gst_a52dec_class_init (GstA52DecClass * klass)
{
GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstAudioDecoderClass *gstbase_class;
guint cpuflags;
gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ gstbase_class = (GstAudioDecoderClass *) klass;
gobject_class->set_property = gst_a52dec_set_property;
gobject_class->get_property = gst_a52dec_get_property;
}
static void
-gst_a52dec_init (GstA52Dec * a52dec, GstA52DecClass * g_class)
+gst_a52dec_init (GstA52Dec * a52dec)
{
- /* create the sink and src pads */
- a52dec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
- gst_pad_set_chain_function (a52dec->sinkpad,
+ a52dec->request_channels = A52_CHANNEL;
+ a52dec->dynamic_range_compression = FALSE;
+
+ a52dec->state = NULL;
+ a52dec->samples = NULL;
+
+ /* retrieve and intercept base class chain.
+ * Quite HACKish, but that's dvd specs/caps for you,
+ * since one buffer needs to be split into 2 frames */
+ a52dec->base_chain = GST_PAD_CHAINFUNC (GST_AUDIO_DECODER_SINK_PAD (a52dec));
+ gst_pad_set_chain_function (GST_AUDIO_DECODER_SINK_PAD (a52dec),
GST_DEBUG_FUNCPTR (gst_a52dec_chain));
- gst_pad_set_event_function (a52dec->sinkpad,
- GST_DEBUG_FUNCPTR (gst_a52dec_sink_event));
- gst_element_add_pad (GST_ELEMENT (a52dec), a52dec->sinkpad);
+ }
- a52dec->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
- gst_element_add_pad (GST_ELEMENT (a52dec), a52dec->srcpad);
+ static gboolean
+ gst_a52dec_start (GstAudioDecoder * dec)
+ {
+ GstA52Dec *a52dec = GST_A52DEC (dec);
+ GstA52DecClass *klass;
- a52dec->request_channels = A52_CHANNEL;
- a52dec->dynamic_range_compression = FALSE;
+ GST_DEBUG_OBJECT (dec, "start");
+
+ klass = GST_A52DEC_CLASS (G_OBJECT_GET_CLASS (a52dec));
+ a52dec->state = a52_init (klass->a52_cpuflags);
+
+ if (!a52dec->state) {
+ GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), LIBRARY, INIT, (NULL),
+ ("failed to initialize a52 state"));
+ return FALSE;
+ }
+
+ a52dec->samples = a52_samples (a52dec->state);
+ a52dec->bit_rate = -1;
+ a52dec->sample_rate = -1;
+ a52dec->stream_channels = A52_CHANNEL;
+ a52dec->using_channels = A52_CHANNEL;
+ a52dec->level = 1;
+ a52dec->bias = 0;
+ a52dec->flag_update = TRUE;
- gst_segment_init (&a52dec->segment, GST_FORMAT_UNDEFINED);
+ /* call upon legacy upstream byte support (e.g. seeking) */
+ gst_audio_decoder_set_byte_time (dec, TRUE);
+
+ return TRUE;
+ }
+
+ static gboolean
+ gst_a52dec_stop (GstAudioDecoder * dec)
+ {
+ GstA52Dec *a52dec = GST_A52DEC (dec);
+
+ GST_DEBUG_OBJECT (dec, "stop");
+
+ a52dec->samples = NULL;
+ if (a52dec->state) {
+ a52_free (a52dec->state);
+ a52dec->state = NULL;
+ }
+ if (a52dec->pending_tags) {
+ gst_tag_list_free (a52dec->pending_tags);
+ a52dec->pending_tags = NULL;
+ }
+
+ return TRUE;
+ }
+
+ static GstFlowReturn
+ gst_a52dec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
+ gint * _offset, gint * len)
+ {
+ GstA52Dec *a52dec;
- guint8 *data;
++ const guint8 *data;
+ gint av, size;
+ gint length = 0, flags, sample_rate, bit_rate;
- GstFlowReturn result = GST_FLOW_UNEXPECTED;
++ GstFlowReturn result = GST_FLOW_EOS;
+
+ a52dec = GST_A52DEC (bdec);
+
+ size = av = gst_adapter_available (adapter);
- data = (guint8 *) gst_adapter_peek (adapter, av);
++ data = (const guint8 *) gst_adapter_map (adapter, av);
+
+ /* find and read header */
+ bit_rate = a52dec->bit_rate;
+ sample_rate = a52dec->sample_rate;
+ flags = 0;
+ while (av >= 7) {
- length = a52_syncinfo (data, &flags, &sample_rate, &bit_rate);
++ length = a52_syncinfo ((guint8 *) data, &flags, &sample_rate, &bit_rate);
+
+ if (length == 0) {
+ /* shift window to re-find sync */
+ data++;
+ size--;
+ } else if (length <= size) {
+ GST_LOG_OBJECT (a52dec, "Sync: frame size %d", length);
+ result = GST_FLOW_OK;
+ break;
+ } else {
+ GST_LOG_OBJECT (a52dec, "Not enough data available (needed %d had %d)",
+ length, size);
+ break;
+ }
+ }
++ gst_adapter_unmap (adapter);
+
+ *_offset = av - size;
+ *len = length;
+
+ return result;
}
static gint
return chans;
}
- static void
- clear_queued (GstA52Dec * dec)
- {
- g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL);
- g_list_free (dec->queued);
- dec->queued = NULL;
- }
-
- static GstFlowReturn
- flush_queued (GstA52Dec * dec)
- {
- GstFlowReturn ret = GST_FLOW_OK;
-
- while (dec->queued) {
- GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data);
-
- GST_LOG_OBJECT (dec, "pushing buffer %p, timestamp %"
- GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT, buf,
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
- /* iterate ouput queue an push downstream */
- ret = gst_pad_push (dec->srcpad, buf);
-
- dec->queued = g_list_delete_link (dec->queued, dec->queued);
- }
- return ret;
- }
-
- static GstFlowReturn
- gst_a52dec_drain (GstA52Dec * dec)
- {
- GstFlowReturn ret = GST_FLOW_OK;
-
- if (dec->segment.rate < 0.0) {
- /* if we have some queued frames for reverse playback, flush
- * them now */
- ret = flush_queued (dec);
- }
- return ret;
- }
-
- static GstFlowReturn
- gst_a52dec_push (GstA52Dec * a52dec,
- GstPad * srcpad, int flags, sample_t * samples, GstClockTime timestamp)
- {
- GstBuffer *buf;
- int chans, n, c;
- GstFlowReturn result;
- sample_t *data;
-
- flags &= (A52_CHANNEL_MASK | A52_LFE);
- if (!(chans = gst_a52dec_channels (flags, NULL)))
- goto no_channels;
-
- buf = gst_buffer_new_allocate (NULL, 256 * chans * (SAMPLE_WIDTH / 8), 0);
-
- data = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
- for (n = 0; n < 256; n++) {
- for (c = 0; c < chans; c++) {
- data[n * chans + c] = samples[c * 256 + n];
- }
- }
- gst_audio_reorder_channels (data, 256 * chans * (SAMPLE_WIDTH / 8),
- (SAMPLE_WIDTH == 64) ? GST_AUDIO_FORMAT_F64 : GST_AUDIO_FORMAT_F32, chans,
- a52dec->from, a52dec->to);
- gst_buffer_unmap (buf, data, -1);
-
- GST_BUFFER_TIMESTAMP (buf) = timestamp;
- GST_BUFFER_DURATION (buf) = 256 * GST_SECOND / a52dec->sample_rate;
-
- result = GST_FLOW_OK;
- if ((buf = gst_audio_buffer_clip (buf, &a52dec->segment,
- a52dec->sample_rate, (SAMPLE_WIDTH / 8) * chans))) {
- /* set discont when needed */
- if (a52dec->discont) {
- GST_LOG_OBJECT (a52dec, "marking DISCONT");
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
- a52dec->discont = FALSE;
- }
-
- if (a52dec->segment.rate > 0.0) {
- GST_DEBUG_OBJECT (a52dec,
- "Pushing buffer with ts %" GST_TIME_FORMAT " duration %"
- GST_TIME_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
-
- result = gst_pad_push (srcpad, buf);
- } else {
- /* reverse playback, queue frame till later when we get a discont. */
- GST_DEBUG_OBJECT (a52dec, "queued frame");
- a52dec->queued = g_list_prepend (a52dec->queued, buf);
- }
- }
- return result;
-
- /* ERRORS */
- no_channels:
- {
- GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL),
- ("invalid channel flags: %d", flags));
- return GST_FLOW_ERROR;
- }
- }
-
static gboolean
--gst_a52dec_reneg (GstA52Dec * a52dec, GstPad * pad)
++gst_a52dec_reneg (GstA52Dec * a52dec)
{
- GstAudioChannelPosition *pos;
- gint channels = gst_a52dec_channels (a52dec->using_channels, &pos);
+ gint channels;
GstCaps *caps = NULL;
gboolean result = FALSE;
- channels = gst_a52dec_channels (a52dec->using_channels, a52dec->from);
++ GstAudioChannelPosition from[6], to[6];
+
++ channels = gst_a52dec_channels (a52dec->using_channels, from);
if (!channels)
goto done;
GST_INFO_OBJECT (a52dec, "reneg channels:%d rate:%d",
channels, a52dec->sample_rate);
- memcpy (a52dec->to, a52dec->from, sizeof (a52dec->from));
- gst_audio_channel_positions_to_valid_order (a52dec->to, channels);
- caps = gst_caps_new_simple ("audio/x-raw-float",
- "endianness", G_TYPE_INT, G_BYTE_ORDER,
- "width", G_TYPE_INT, SAMPLE_WIDTH,
++ memcpy (to, from, sizeof (GstAudioChannelPosition) * channels);
++ gst_audio_channel_positions_to_valid_order (to, channels);
++ gst_audio_get_channel_reorder_map (channels, from, to,
++ a52dec->channel_reorder_map);
+
+ caps = gst_caps_new_simple ("audio/x-raw",
+ "format", G_TYPE_STRING, SAMPLE_FORMAT,
+ "layout", G_TYPE_STRING, "interleaved",
"channels", G_TYPE_INT, channels,
"rate", G_TYPE_INT, a52dec->sample_rate, NULL);
- gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos);
- g_free (pos);
- if (!gst_pad_set_caps (pad, caps))
+ if (channels > 1) {
+ guint64 channel_mask = 0;
- gint i;
+
- for (i = 0; i < channels; i++)
- channel_mask |= G_GUINT64_CONSTANT (1) << a52dec->to[i];
++ gst_audio_channel_positions_to_mask (to, channels, &channel_mask);
+ gst_caps_set_simple (caps, "channel-mask", GST_TYPE_BITMASK, channel_mask,
+ NULL);
+ }
+
- if (!gst_pad_set_caps (pad, caps))
++ if (!gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (a52dec), caps))
goto done;
result = TRUE;
return result;
}
- static gboolean
- gst_a52dec_sink_setcaps (GstA52Dec * a52dec, GstCaps * caps)
- {
- GstStructure *structure;
-
- structure = gst_caps_get_structure (caps, 0);
-
- if (structure && gst_structure_has_name (structure, "audio/x-private1-ac3"))
- a52dec->dvdmode = TRUE;
- else
- a52dec->dvdmode = FALSE;
-
- return TRUE;
- }
-
- static gboolean
- gst_a52dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
+ static void
+ gst_a52dec_update_streaminfo (GstA52Dec * a52dec)
{
- GstA52Dec *a52dec = GST_A52DEC (parent);
- gboolean ret = FALSE;
-
- GST_LOG ("Handling %s event", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_CAPS:
- {
- GstCaps *caps;
-
- gst_event_parse_caps (event, &caps);
+ GstTagList *taglist;
- ret = gst_a52dec_sink_setcaps (a52dec, caps);
- gst_event_unref (event);
- break;
- }
- case GST_EVENT_SEGMENT:
- {
- GstSegment seg;
-
- gst_event_copy_segment (event, &seg);
-
- /* drain queued buffers before activating the segment so that we can clip
- * against the old segment first */
- gst_a52dec_drain (a52dec);
-
- if (seg.format != GST_FORMAT_TIME || !GST_CLOCK_TIME_IS_VALID (seg.start)) {
- GST_WARNING ("No time in newsegment event %p (format is %s)",
- event, gst_format_get_name (seg.format));
- gst_event_unref (event);
- a52dec->sent_segment = FALSE;
- /* set some dummy values, FIXME: do proper conversion */
- a52dec->time = seg.start = seg.position = 0;
- seg.format = GST_FORMAT_TIME;
- seg.stop = -1;
- } else {
- a52dec->time = seg.start;
- a52dec->sent_segment = TRUE;
- GST_DEBUG_OBJECT (a52dec, "Pushing segment %" GST_SEGMENT_FORMAT, &seg);
- taglist = gst_tag_list_new ();
++ taglist = gst_tag_list_new_empty ();
+ gst_tag_list_add (taglist, GST_TAG_MERGE_APPEND, GST_TAG_BITRATE,
+ (guint) a52dec->bit_rate, NULL);
- ret = gst_pad_push_event (a52dec->srcpad, event);
- }
- a52dec->segment = seg;
- break;
- }
- case GST_EVENT_TAG:
- ret = gst_pad_push_event (a52dec->srcpad, event);
- break;
- case GST_EVENT_EOS:
- gst_a52dec_drain (a52dec);
- ret = gst_pad_push_event (a52dec->srcpad, event);
- break;
- case GST_EVENT_FLUSH_START:
- ret = gst_pad_push_event (a52dec->srcpad, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- if (a52dec->cache) {
- gst_buffer_unref (a52dec->cache);
- a52dec->cache = NULL;
- }
- clear_queued (a52dec);
- gst_segment_init (&a52dec->segment, GST_FORMAT_UNDEFINED);
- ret = gst_pad_push_event (a52dec->srcpad, event);
- break;
- default:
- ret = gst_pad_push_event (a52dec->srcpad, event);
- break;
+ if (a52dec->pending_tags) {
+ gst_tag_list_free (a52dec->pending_tags);
+ a52dec->pending_tags = NULL;
}
- return ret;
+ a52dec->pending_tags = taglist;
}
- static void
- gst_a52dec_update_streaminfo (GstA52Dec * a52dec)
+ static GstFlowReturn
+ gst_a52dec_pre_push (GstAudioDecoder * bdec, GstBuffer ** buffer)
{
- GstTagList *taglist;
+ GstA52Dec *a52dec = GST_A52DEC (bdec);
- taglist = gst_tag_list_new (GST_TAG_AUDIO_CODEC, "Dolby Digital (AC-3)",
- GST_TAG_BITRATE, (guint) a52dec->bit_rate, NULL);
+ if (G_UNLIKELY (a52dec->pending_tags)) {
- gst_element_found_tags_for_pad (GST_ELEMENT (a52dec),
- GST_AUDIO_DECODER_SRC_PAD (a52dec), a52dec->pending_tags);
++ gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (a52dec),
++ gst_event_new_tag (a52dec->pending_tags));
+ a52dec->pending_tags = NULL;
+ }
- gst_pad_push_event (GST_PAD (a52dec->srcpad), gst_event_new_tag (taglist));
+ return GST_FLOW_OK;
}
static GstFlowReturn
- gst_a52dec_handle_frame (GstA52Dec * a52dec, guint8 * data,
- guint length, gint flags, gint sample_rate, gint bit_rate)
+ gst_a52dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer)
{
+ GstA52Dec *a52dec;
gint channels, i;
gboolean need_reneg = FALSE;
- gint size, chans;
++ gint chans;
+ gint length = 0, flags, sample_rate, bit_rate;
+ guint8 *data;
++ gsize size;
+ GstFlowReturn result = GST_FLOW_OK;
+ GstBuffer *outbuf;
+ const gint num_blocks = 6;
+
+ a52dec = GST_A52DEC (bdec);
+
+ /* no fancy draining */
+ if (G_UNLIKELY (!buffer))
+ return GST_FLOW_OK;
+
+ /* parsed stuff already, so this should work out fine */
- data = GST_BUFFER_DATA (buffer);
- size = GST_BUFFER_SIZE (buffer);
++ data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
+ g_assert (size >= 7);
+
+ /* re-obtain some sync header info,
+ * should be same as during _parse and could also be cached there,
+ * but anyway ... */
+ bit_rate = a52dec->bit_rate;
+ sample_rate = a52dec->sample_rate;
+ flags = 0;
+ length = a52_syncinfo (data, &flags, &sample_rate, &bit_rate);
+ g_assert (length == size);
/* update stream information, renegotiate or re-streaminfo if needed */
need_reneg = FALSE;
flags |= A52_ADJUST_LEVEL;
a52dec->level = 1;
if (a52_frame (a52dec->state, data, &flags, &a52dec->level, a52dec->bias)) {
- GST_WARNING ("a52_frame error");
- a52dec->discont = TRUE;
- return GST_FLOW_OK;
++ gst_buffer_unmap (buffer, data, size);
+ GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL),
+ ("a52_frame error"), result);
+ goto exit;
}
++ gst_buffer_unmap (buffer, data, size);
+
channels = flags & (A52_CHANNEL_MASK | A52_LFE);
if (a52dec->using_channels != channels) {
need_reneg = TRUE;
/* negotiate if required */
if (need_reneg) {
- GST_DEBUG ("a52dec reneg: sample_rate:%d stream_chans:%d using_chans:%d",
+ GST_DEBUG_OBJECT (a52dec,
+ "a52dec reneg: sample_rate:%d stream_chans:%d using_chans:%d",
a52dec->sample_rate, a52dec->stream_channels, a52dec->using_channels);
- if (!gst_a52dec_reneg (a52dec, a52dec->srcpad)) {
- GST_ELEMENT_ERROR (a52dec, CORE, NEGOTIATION, (NULL), (NULL));
- return GST_FLOW_ERROR;
- }
- if (!gst_a52dec_reneg (a52dec, GST_AUDIO_DECODER_SRC_PAD (a52dec)))
++ if (!gst_a52dec_reneg (a52dec))
+ goto failed_negotiation;
}
if (a52dec->dynamic_range_compression == FALSE) {
a52_dynrng (a52dec->state, NULL, NULL);
}
- /* each frame consists of 6 blocks */
- for (i = 0; i < 6; i++) {
- if (a52_block (a52dec->state)) {
- /* ignore errors but mark a discont */
- GST_WARNING ("a52_block error %d", i);
- a52dec->discont = TRUE;
- } else {
- GstFlowReturn ret;
+ flags &= (A52_CHANNEL_MASK | A52_LFE);
+ chans = gst_a52dec_channels (flags, NULL);
+ if (!chans)
+ goto invalid_flags;
- /* push on */
- ret = gst_a52dec_push (a52dec, a52dec->srcpad, a52dec->using_channels,
- a52dec->samples, a52dec->time);
- if (ret != GST_FLOW_OK)
- return ret;
+ /* handle decoded data;
+ * each frame has 6 blocks, one block is 256 samples, ea */
- result =
- gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (a52dec), 0,
- 256 * chans * (SAMPLE_WIDTH / 8) * num_blocks,
- GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (a52dec)), &outbuf);
- if (result != GST_FLOW_OK)
- goto exit;
-
- data = GST_BUFFER_DATA (outbuf);
- for (i = 0; i < num_blocks; i++) {
- if (a52_block (a52dec->state)) {
- /* also marks discont */
- GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL),
- ("error decoding block %d", i), result);
- if (result != GST_FLOW_OK)
- goto exit;
- } else {
- gint n, c;
++ outbuf =
++ gst_buffer_new_and_alloc (256 * chans * (SAMPLE_WIDTH / 8) * num_blocks);
+
- for (n = 0; n < 256; n++) {
- for (c = 0; c < chans; c++) {
- ((sample_t *) data)[n * chans + c] = a52dec->samples[c * 256 + n];
++ data = gst_buffer_map (buffer, &size, NULL, GST_MAP_WRITE);
++ {
++ guint8 *ptr = data;
++ for (i = 0; i < num_blocks; i++) {
++ if (a52_block (a52dec->state)) {
++ /* also marks discont */
++ GST_AUDIO_DECODER_ERROR (a52dec, 1, STREAM, DECODE, (NULL),
++ ("error decoding block %d", i), result);
++ if (result != GST_FLOW_OK) {
++ gst_buffer_unmap (outbuf, data, size);
++ goto exit;
++ }
++ } else {
++ gint n, c;
++ gint *reorder_map = a52dec->channel_reorder_map;
++
++ for (n = 0; n < 256; n++) {
++ for (c = 0; c < chans; c++) {
++ ((sample_t *) ptr)[reorder_map[n] * chans + c] =
++ a52dec->samples[c * 256 + n];
++ }
+ }
+ }
++ ptr += 256 * chans * (SAMPLE_WIDTH / 8);
}
- a52dec->time += 256 * GST_SECOND / a52dec->sample_rate;
- data += 256 * chans * (SAMPLE_WIDTH / 8);
}
++ gst_buffer_unmap (outbuf, data, size);
- return GST_FLOW_OK;
+ result = gst_audio_decoder_finish_frame (bdec, outbuf, 1);
+
+ exit:
+ return result;
+
+ /* ERRORS */
+ failed_negotiation:
+ {
+ GST_ELEMENT_ERROR (a52dec, CORE, NEGOTIATION, (NULL), (NULL));
+ return GST_FLOW_ERROR;
+ }
+ invalid_flags:
+ {
+ GST_ELEMENT_ERROR (GST_ELEMENT (a52dec), STREAM, DECODE, (NULL),
+ ("Invalid channel flags: %d", flags));
+ return GST_FLOW_ERROR;
+ }
+ }
+
+ static gboolean
+ gst_a52dec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
+ {
+ GstA52Dec *a52dec = GST_A52DEC (bdec);
+ GstStructure *structure;
+
+ structure = gst_caps_get_structure (caps, 0);
+
+ if (structure && gst_structure_has_name (structure, "audio/x-private1-ac3"))
+ a52dec->dvdmode = TRUE;
+ else
+ a52dec->dvdmode = FALSE;
+
+ return TRUE;
}
static GstFlowReturn
-gst_a52dec_chain (GstPad * pad, GstBuffer * buf)
+gst_a52dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
- GstA52Dec *a52dec = GST_A52DEC (GST_PAD_PARENT (pad));
+ GstA52Dec *a52dec = GST_A52DEC (parent);
- GstFlowReturn ret;
+ GstFlowReturn ret = GST_FLOW_OK;
gint first_access;
- if (GST_BUFFER_IS_DISCONT (buf)) {
- GST_LOG_OBJECT (a52dec, "received DISCONT");
- gst_a52dec_drain (a52dec);
- /* clear cache on discont and mark a discont in the element */
- if (a52dec->cache) {
- gst_buffer_unref (a52dec->cache);
- a52dec->cache = NULL;
- }
- a52dec->discont = TRUE;
- }
-
if (a52dec->dvdmode) {
- gint size = GST_BUFFER_SIZE (buf);
- guchar *data = GST_BUFFER_DATA (buf);
+ gsize size;
+ guint8 data[2];
gint offset;
gint len;
GstBuffer *subbuf;
if (len <= 0 || offset + len > size)
goto bad_first_access_parameter;
- subbuf = gst_buffer_create_sub (buf, offset, len);
- gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
+ subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len);
GST_BUFFER_TIMESTAMP (subbuf) = GST_CLOCK_TIME_NONE;
- ret = gst_a52dec_chain_raw (pad, parent, subbuf);
- if (ret != GST_FLOW_OK)
- ret = a52dec->base_chain (pad, subbuf);
++ ret = a52dec->base_chain (pad, parent, subbuf);
+ if (ret != GST_FLOW_OK) {
+ gst_buffer_unref (buf);
goto done;
+ }
offset += len;
len = size - offset;
if (len > 0) {
- subbuf = gst_buffer_create_sub (buf, offset, len);
- gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
+ subbuf = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, len);
GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
- ret = gst_a52dec_chain_raw (pad, parent, subbuf);
- ret = a52dec->base_chain (pad, subbuf);
++ ret = a52dec->base_chain (pad, parent, subbuf);
}
+ gst_buffer_unref (buf);
} else {
/* first_access = 0 or 1, so if there's a timestamp it applies to the first byte */
- subbuf = gst_buffer_create_sub (buf, offset, size - offset);
- gst_buffer_copy_metadata (subbuf, buf, GST_BUFFER_COPY_ALL);
- ret = a52dec->base_chain (pad, subbuf);
+ subbuf =
+ gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset,
+ size - offset);
+ GST_BUFFER_TIMESTAMP (subbuf) = GST_BUFFER_TIMESTAMP (buf);
- ret = gst_a52dec_chain_raw (pad, parent, subbuf);
++ ret = a52dec->base_chain (pad, parent, subbuf);
}
} else {
- ret = a52dec->base_chain (pad, buf);
+ gst_buffer_ref (buf);
- ret = gst_a52dec_chain_raw (pad, parent, buf);
++ ret = a52dec->base_chain (pad, parent, buf);
}
done:
}
}
- data = gst_adapter_peek (lame->adapter, 4);
+ /* **** credits go to mpegaudioparse **** */
+
+ 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,},
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}
+ },
+ {
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}
+ },
+ };
+
+ static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000},
+ {22050, 24000, 16000},
+ {11025, 12000, 8000}
+ };
+
+ static inline guint
+ mp3_type_frame_length_from_header (GstLameMP3Enc * lame, guint32 header,
+ guint * put_version, guint * put_layer, guint * put_channels,
+ guint * put_bitrate, guint * put_samplerate, guint * put_mode,
+ guint * put_crc)
+ {
+ guint length;
+ gulong mode, samplerate, bitrate, layer, channels, padding, crc;
+ gulong version;
+ gint lsf, mpg25;
+
+ if (header & (1 << 20)) {
+ lsf = (header & (1 << 19)) ? 0 : 1;
+ mpg25 = 0;
+ } else {
+ lsf = 1;
+ mpg25 = 1;
+ }
+
+ version = 1 + lsf + mpg25;
+
+ layer = 4 - ((header >> 17) & 0x3);
+
+ crc = (header >> 16) & 0x1;
+
+ bitrate = (header >> 12) & 0xF;
+ bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000;
+ /* The caller has ensured we have a valid header, so bitrate can't be
+ zero here. */
+ g_assert (bitrate != 0);
+
+ samplerate = (header >> 10) & 0x3;
+ samplerate = mp3types_freqs[lsf + mpg25][samplerate];
+
+ padding = (header >> 9) & 0x1;
+
+ mode = (header >> 6) & 0x3;
+ channels = (mode == 3) ? 1 : 2;
+
+ switch (layer) {
+ case 1:
+ length = 4 * ((bitrate * 12) / samplerate + padding);
+ break;
+ case 2:
+ length = (bitrate * 144) / samplerate + padding;
+ break;
+ default:
+ case 3:
+ length = (bitrate * 144) / (samplerate << lsf) + padding;
+ break;
+ }
+
+ GST_DEBUG_OBJECT (lame, "Calculated mp3 frame length of %u bytes", length);
+ GST_DEBUG_OBJECT (lame, "samplerate = %lu, bitrate = %lu, version = %lu, "
+ "layer = %lu, channels = %lu", samplerate, bitrate, version,
+ layer, channels);
+
+ if (put_version)
+ *put_version = version;
+ if (put_layer)
+ *put_layer = layer;
+ if (put_channels)
+ *put_channels = channels;
+ if (put_bitrate)
+ *put_bitrate = bitrate;
+ if (put_samplerate)
+ *put_samplerate = samplerate;
+ if (put_mode)
+ *put_mode = mode;
+ if (put_crc)
+ *put_crc = crc;
+
+ return length;
+ }
+
+ static gboolean
+ mp3_sync_check (GstLameMP3Enc * lame, unsigned long head)
+ {
+ GST_DEBUG_OBJECT (lame, "checking mp3 header 0x%08lx", head);
+ /* if it's not a valid sync */
+ if ((head & 0xffe00000) != 0xffe00000) {
+ GST_WARNING_OBJECT (lame, "invalid sync");
+ return FALSE;
+ }
+ /* if it's an invalid MPEG version */
+ if (((head >> 19) & 3) == 0x1) {
+ GST_WARNING_OBJECT (lame, "invalid MPEG version: 0x%lx", (head >> 19) & 3);
+ return FALSE;
+ }
+ /* if it's an invalid layer */
+ if (!((head >> 17) & 3)) {
+ GST_WARNING_OBJECT (lame, "invalid layer: 0x%lx", (head >> 17) & 3);
+ return FALSE;
+ }
+ /* if it's an invalid bitrate */
+ if (((head >> 12) & 0xf) == 0x0) {
+ GST_WARNING_OBJECT (lame, "invalid bitrate: 0x%lx."
+ "Free format files are not supported yet", (head >> 12) & 0xf);
+ return FALSE;
+ }
+ if (((head >> 12) & 0xf) == 0xf) {
+ GST_WARNING_OBJECT (lame, "invalid bitrate: 0x%lx", (head >> 12) & 0xf);
+ return FALSE;
+ }
+ /* if it's an invalid samplerate */
+ if (((head >> 10) & 0x3) == 0x3) {
+ GST_WARNING_OBJECT (lame, "invalid samplerate: 0x%lx", (head >> 10) & 0x3);
+ return FALSE;
+ }
+
+ if ((head & 0x3) == 0x2) {
+ /* Ignore this as there are some files with emphasis 0x2 that can
+ * be played fine. See BGO #537235 */
+ GST_WARNING_OBJECT (lame, "invalid emphasis: 0x%lx", head & 0x3);
+ }
+
+ return TRUE;
+ }
+
+ /* **** end mpegaudioparse **** */
+
+ static GstFlowReturn
+ gst_lamemp3enc_finish_frames (GstLameMP3Enc * lame)
+ {
+ gint av;
+ guint header;
+ GstFlowReturn result = GST_FLOW_OK;
+
+ /* limited parsing, we don't expect to lose sync here */
+ while ((result == GST_FLOW_OK) &&
+ ((av = gst_adapter_available (lame->adapter)) > 4)) {
+ guint rate, version, layer, size;
+ GstBuffer *mp3_buf;
+ const guint8 *data;
+
++ data = gst_adapter_map (lame->adapter, 4);
+ header = GST_READ_UINT32_BE (data);
++ gst_adapter_unmap (lame->adapter);
++
+ if (!mp3_sync_check (lame, header))
+ goto invalid_header;
+
+ size = mp3_type_frame_length_from_header (lame, header, &version, &layer,
+ NULL, NULL, &rate, NULL, NULL);
+
+ if (G_UNLIKELY (layer != 3 || rate != lame->out_samplerate)) {
+ GST_DEBUG_OBJECT (lame,
+ "unexpected mp3 header with rate %u, version %u, layer %u",
+ rate, version, layer);
+ goto invalid_header;
+ }
+
+ if (size > av) {
+ /* pretty likely to occur when lame is holding back on us */
+ GST_LOG_OBJECT (lame, "frame size %u (> %d)", size, av);
+ break;
+ }
+
+ /* should be ok now */
+ mp3_buf = gst_adapter_take_buffer (lame->adapter, size);
+ /* number of samples for MPEG-1, layer 3 */
+ result = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (lame),
+ mp3_buf, version == 1 ? 1152 : 576);
+ }
+
+ exit:
+ return result;
+
+ /* ERRORS */
+ invalid_header:
+ {
+ GST_ELEMENT_ERROR (lame, STREAM, ENCODE,
+ ("invalid lame mp3 sync header %08X", header), (NULL));
+ result = GST_FLOW_ERROR;
+ goto exit;
+ }
+ }
+
static GstFlowReturn
gst_lamemp3enc_flush_full (GstLameMP3Enc * lame, gboolean push)
{
GstBuffer *buf;
gint size;
+ guint8 *data;
GstFlowReturn result = GST_FLOW_OK;
+ gint av;
if (!lame->lgf)
return GST_FLOW_OK;
buf = gst_buffer_new_and_alloc (7200);
- size = lame_encode_flush (lame->lgf, GST_BUFFER_DATA (buf), 7200);
+ data = gst_buffer_map (buf, NULL, NULL, GST_MAP_WRITE);
+ size = lame_encode_flush (lame->lgf, data, 7200);
if (size > 0) {
- GST_BUFFER_SIZE (buf) = size;
+ gst_buffer_unmap (buf, data, size);
- if (push) {
- GST_DEBUG_OBJECT (lame, "pushing final packet of %u bytes", size);
- result =
- gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (lame), buf, -1);
- }
+ GST_DEBUG_OBJECT (lame, "collecting final %d bytes", size);
+ gst_adapter_push (lame->adapter, buf);
} else {
+ gst_buffer_unmap (buf, data, 0);
GST_DEBUG_OBJECT (lame, "no final packet (size=%d, push=%d)", size, push);
gst_buffer_unref (buf);
result = GST_FLOW_OK;
(short int *) data,
num_samples / lame->num_channels, mp3_data, mp3_buffer_size);
}
+ gst_buffer_unmap (in_buf, data, size);
- GST_LOG_OBJECT (lame, "encoded %d bytes of audio to %d bytes of mp3",
- size, mp3_size);
+ GST_LOG_OBJECT (lame, "encoded %" G_GSIZE_FORMAT " bytes of audio "
+ "to %d bytes of mp3", size, mp3_size);
if (G_LIKELY (mp3_size > 0)) {
- GST_BUFFER_SIZE (mp3_buf) = mp3_size;
+ /* unfortunately lame does not provide frame delineated output,
+ * so collect output and parse into frames ... */
+ gst_buffer_unmap (mp3_buf, mp3_data, mp3_size);
- result = gst_audio_encoder_finish_frame (enc, mp3_buf, -1);
+ gst_adapter_push (lame->adapter, mp3_buf);
+ result = gst_lamemp3enc_finish_frames (lame);
} else {
+ gst_buffer_unmap (mp3_buf, mp3_data, 0);
if (mp3_size < 0) {
/* eat error ? */
g_warning ("error %d", mp3_size);