#include <libavutil/channel_layout.h>
#include <gst/gst.h>
+#include <gst/base/gstbytewriter.h>
#include "gstav.h"
#include "gstavcodecmap.h"
*/
static gboolean
gst_ffmpegauddec_audio_frame (GstFFMpegAudDec * ffmpegdec,
- AVCodec * in_plugin, GstBuffer ** outbuf, GstFlowReturn * ret)
+ AVCodec * in_plugin, GstBuffer ** outbuf, GstFlowReturn * ret,
+ gboolean * need_more_data)
{
gboolean got_frame = FALSE;
gint res;
GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_CORRUPTED);
} else if (res == AVERROR (EAGAIN)) {
*outbuf = NULL;
+ *need_more_data = TRUE;
} else if (res == AVERROR_EOF) {
*ret = GST_FLOW_EOS;
GST_DEBUG_OBJECT (ffmpegdec, "Context was entirely flushed");
* Returns: whether a frame was decoded
*/
static gboolean
-gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, GstFlowReturn * ret)
+gst_ffmpegauddec_frame (GstFFMpegAudDec * ffmpegdec, GstFlowReturn * ret,
+ gboolean * need_more_data)
{
GstFFMpegAudDecClass *oclass;
GstBuffer *outbuf = NULL;
oclass = (GstFFMpegAudDecClass *) (G_OBJECT_GET_CLASS (ffmpegdec));
got_frame =
- gst_ffmpegauddec_audio_frame (ffmpegdec, oclass->in_plugin, &outbuf, ret);
+ gst_ffmpegauddec_audio_frame (ffmpegdec, oclass->in_plugin, &outbuf, ret,
+ need_more_data);
if (outbuf) {
GST_LOG_OBJECT (ffmpegdec, "Decoded data, buffer %" GST_PTR_FORMAT, outbuf);
{
GstFlowReturn ret = GST_FLOW_OK;
gboolean got_any_frames = FALSE;
+ gboolean need_more_data = FALSE;
gboolean got_frame;
if (avcodec_send_packet (ffmpegdec->context, NULL))
goto send_packet_failed;
do {
- got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
+ got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret, &need_more_data);
if (got_frame)
got_any_frames = TRUE;
- } while (got_frame);
+ } while (got_frame && !need_more_data);
avcodec_flush_buffers (ffmpegdec->context);
/* FFMpeg will return AVERROR_EOF if it's internal was fully drained
GstFlowReturn ret = GST_FLOW_OK;
gboolean is_header;
AVPacket packet;
+ GstAudioClippingMeta *clipping_meta = NULL;
+ guint32 num_clipped_samples = 0;
+ gboolean fully_clipped = FALSE;
+ gboolean need_more_data = FALSE;
ffmpegdec = (GstFFMpegAudDec *) decoder;
inbuf = gst_buffer_make_writable (inbuf);
}
+ /* mpegaudioparse is setting buffer flags for the Xing/LAME header. This
+ * should not be passed to the decoder as it results in unnecessary silence
+ * samples to be output */
+ if (oclass->in_plugin->id == AV_CODEC_ID_MP3 &&
+ GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DECODE_ONLY) &&
+ GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DROPPABLE)) {
+ gst_buffer_unref (inbuf);
+ return gst_audio_decoder_finish_frame (decoder, NULL, 1);
+ }
+
+ clipping_meta = gst_buffer_get_audio_clipping_meta (inbuf);
+
gst_buffer_map (inbuf, &map, GST_MAP_READ);
data = map.data;
if (!packet.size)
goto unmap;
+ if (clipping_meta != NULL) {
+ if (clipping_meta->format == GST_FORMAT_DEFAULT) {
+ uint8_t *p = av_packet_new_side_data (&packet, AV_PKT_DATA_SKIP_SAMPLES,
+ 10);
+ if (p != NULL) {
+ GstByteWriter writer;
+ guint32 start = clipping_meta->start;
+ guint32 end = clipping_meta->end;
+
+ num_clipped_samples = start + end;
+
+ gst_byte_writer_init_with_data (&writer, p, 10, FALSE);
+ gst_byte_writer_put_uint32_le (&writer, start);
+ gst_byte_writer_put_uint32_le (&writer, end);
+ GST_LOG_OBJECT (ffmpegdec, "buffer has clipping metadata; added skip "
+ "side data to avpacket with start %u and end %u", start, end);
+ }
+ } else {
+ GST_WARNING_OBJECT (ffmpegdec,
+ "buffer has clipping metadata in unsupported format %s",
+ gst_format_get_name (clipping_meta->format));
+ }
+ }
+
if (avcodec_send_packet (ffmpegdec->context, &packet) < 0) {
goto send_packet_failed;
}
do {
/* decode a frame of audio now */
- got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret);
+ got_frame = gst_ffmpegauddec_frame (ffmpegdec, &ret, &need_more_data);
if (got_frame)
got_any_frames = TRUE;
/* bad flow return, make sure we discard all data and exit */
break;
}
- } while (got_frame);
-
- if (is_header || got_any_frames) {
+ } while (got_frame && !need_more_data);
+
+ /* The frame was fully clipped if we have samples to be clipped and
+ * it's either more than the known fixed frame size, or the decoder returned
+ * that it needs more data (EAGAIN) and we didn't decode any frames at all.
+ */
+ fully_clipped = (clipping_meta != NULL && num_clipped_samples > 0)
+ && ((ffmpegdec->context->frame_size != 0
+ && num_clipped_samples >= ffmpegdec->context->frame_size)
+ || (need_more_data && !got_any_frames));
+
+ if (is_header || got_any_frames || fully_clipped) {
/* Even if previous return wasn't GST_FLOW_OK, we need to call
* _finish_frame() since baseclass is expecting that _finish_frame()
* is followed by _finish_subframe()