diff options
Diffstat (limited to 'src/combined/ffmpeg')
-rw-r--r-- | src/combined/ffmpeg/ff_audio_decoder.c | 212 |
1 files changed, 148 insertions, 64 deletions
diff --git a/src/combined/ffmpeg/ff_audio_decoder.c b/src/combined/ffmpeg/ff_audio_decoder.c index 7c293c100..8d7cc1574 100644 --- a/src/combined/ffmpeg/ff_audio_decoder.c +++ b/src/combined/ffmpeg/ff_audio_decoder.c @@ -75,6 +75,8 @@ typedef struct ff_audio_decoder_s { char *decode_buffer; int decoder_ok; + AVCodecParserContext *parser_context; + } ff_audio_decoder_t; @@ -164,6 +166,24 @@ static void ff_audio_init_codec(ff_audio_decoder_t *this, unsigned int codec_typ this->context->codec_id = this->codec->id; this->context->codec_type = this->codec->type; this->context->codec_tag = _x_stream_info_get(this->stream, XINE_STREAM_INFO_AUDIO_FOURCC); + + /* Use parser for AAC LATM and MPEG. + * Fixes: + * - DVB streams where multiple AAC LATM frames are packed to single PES + * - DVB streams where MPEG audio frames do not follow PES packet boundaries + */ + if (codec_type == BUF_AUDIO_AAC_LATM || + codec_type == BUF_AUDIO_MPEG) { + + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: using parser\n"); + + this->parser_context = av_parser_init(this->codec->id); + if (!this->parser_context) { + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + "ffmpeg_audio_dec: couldn't init parser\n"); + } + } } static int ff_audio_open_codec(ff_audio_decoder_t *this, unsigned int codec_type) { @@ -310,6 +330,83 @@ static void ff_handle_header_buffer(ff_audio_decoder_t *this, buf_element_t *buf this->size = 0; } +static void ff_audio_reset_parser(ff_audio_decoder_t *this) +{ + /* reset parser */ + if (this->parser_context) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: resetting parser\n"); + + pthread_mutex_lock (&ffmpeg_lock); + av_parser_close(this->parser_context); + this->parser_context = av_parser_init(this->codec->id); + pthread_mutex_unlock (&ffmpeg_lock); + } +} + +static int ff_audio_decode(xine_t *xine, + AVCodecContext *ctx, + AVCodecParserContext *parser_ctx, + int16_t *decode_buffer, int *decode_buffer_size, + uint8_t *buf, int size) +{ + int consumed; + int parser_consumed = 0; + + if (parser_ctx) { + uint8_t *outbuf; + int outsize; + + do { + int ret = av_parser_parse2(parser_ctx, ctx, + &outbuf, &outsize, + buf, size, + 0, 0, 0); + parser_consumed += ret; + buf += ret; + size -= ret; + } while (size > 0 && outsize <= 0); + + /* nothing to decode ? */ + if (outsize <= 0) { + *decode_buffer_size = 0; + xprintf (xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: not enough data to decode\n"); + return parser_consumed; + } + + /* decode next packet */ + buf = outbuf; + size = outsize; + } + +#if AVAUDIO > 2 + AVPacket avpkt; + av_init_packet (&avpkt); + avpkt.data = buf; + avpkt.size = size; + avpkt.flags = AV_PKT_FLAG_KEY; + consumed = avcodec_decode_audio3 (ctx, + decode_buffer, decode_buffer_size, + &avpkt); +#else + consumed = avcodec_decode_audio2 (ctx, + decode_buffer, decode_buffer_size, + buf, size); +#endif + + if (consumed < 0) { + xprintf (xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: error decompressing audio frame (%d)\n", consumed); + } else if (parser_consumed && consumed != size) { + + xprintf (xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_audio_dec: decoder didn't consume all data\n"); + } + + return parser_consumed ? parser_consumed : consumed; +} + static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) { ff_audio_decoder_t *this = (ff_audio_decoder_t *) this_gen; @@ -332,10 +429,6 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) } else { -#if AVAUDIO > 2 - AVPacket avpkt; -#endif - if( !this->decoder_ok ) { if (ff_audio_open_codec(this, codec_type) < 0) { return; @@ -349,49 +442,7 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) xine_fast_memcpy (&this->buf[this->size], buf->content, buf->size); this->size += buf->size; - if (!this->output_open) { - if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) { - int ret; - - decode_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; -#if AVAUDIO > 2 - av_init_packet (&avpkt); - avpkt.data = (uint8_t *)&this->buf[0]; - avpkt.size = this->size; - avpkt.flags = AV_PKT_FLAG_KEY; - ret = avcodec_decode_audio3 (this->context, - (int16_t *)this->decode_buffer, - &decode_buffer_size, &avpkt); -#else - ret = avcodec_decode_audio2 (this->context, - (int16_t *)this->decode_buffer, - &decode_buffer_size, - &this->buf[0], - this->size); -#endif - this->audio_bits = this->context->bits_per_sample; - this->audio_sample_rate = this->context->sample_rate; - this->audio_channels = this->context->channels; - if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) { - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_audio_dec: cannot read codec parameters from packet (error=%d)\n"), ret); - - /* We can't use this packet, so we must discard it - * and wait for another one. */ - this->size = 0; - return; - } - } - this->output_open = (this->stream->audio_out->open) (this->stream->audio_out, - this->stream, this->audio_bits, this->audio_sample_rate, - _x_ao_channels2mode(this->audio_channels)); - } - - /* if the audio still isn't open, bail */ - if (!this->output_open) - return; - - if (buf->decoder_flags & BUF_FLAG_FRAME_END) { /* time to decode a frame */ + if (this->parser_context || buf->decoder_flags & BUF_FLAG_FRAME_END) { /* time to decode a frame */ offset = 0; @@ -400,24 +451,14 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) while (this->size>0) { decode_buffer_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; -#if AVAUDIO > 2 - av_init_packet (&avpkt); - avpkt.data = (uint8_t *)&this->buf[offset]; - avpkt.size = this->size; - avpkt.flags = AV_PKT_FLAG_KEY; - bytes_consumed = avcodec_decode_audio3 (this->context, - (int16_t *)this->decode_buffer, - &decode_buffer_size, &avpkt); -#else - bytes_consumed = avcodec_decode_audio2 (this->context, - (int16_t *)this->decode_buffer, - &decode_buffer_size, - &this->buf[offset], - this->size); -#endif + + bytes_consumed = + ff_audio_decode(this->stream->xine, this->context, + this->parser_context, + (int16_t *)this->decode_buffer, &decode_buffer_size, + &this->buf[offset], this->size); + if (bytes_consumed<0) { - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_audio_dec: error decompressing audio frame\n"); this->size=0; return; } else if (bytes_consumed == 0 && decode_buffer_size == 0) { @@ -426,6 +467,33 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) return; } + if (!this->output_open) { + if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) { + this->audio_bits = this->context->bits_per_sample; + this->audio_sample_rate = this->context->sample_rate; + this->audio_channels = this->context->channels; + } + if (!this->audio_bits || !this->audio_sample_rate || !this->audio_channels) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + _("ffmpeg_audio_dec: cannot read codec parameters from packet\n")); + /* try to decode next packet. */ + /* there shouldn't be any output yet */ + decode_buffer_size = 0; + /* pts applies only to first audio packet */ + buf->pts = 0; + } else { + this->output_open = (this->stream->audio_out->open) (this->stream->audio_out, + this->stream, this->audio_bits, this->audio_sample_rate, + _x_ao_channels2mode(this->audio_channels)); + if (!this->output_open) { + xprintf(this->stream->xine, XINE_VERBOSITY_LOG, + "ffmpeg_audio_dec: error opening audio output\n"); + this->size = 0; + return; + } + } + } + /* dispatch the decoded audio */ out = 0; while (out < decode_buffer_size) { @@ -478,6 +546,7 @@ static void ff_audio_decode_data (audio_decoder_t *this_gen, buf_element_t *buf) audio_buffer->num_frames = bytes_to_send / 2 / this->audio_channels; audio_buffer->vpts = buf->pts; + buf->pts = 0; /* only first buffer gets the real pts */ this->stream->audio_out->put_buffer (this->stream->audio_out, audio_buffer, this->stream); @@ -506,15 +575,30 @@ static void ff_audio_reset (audio_decoder_t *this_gen) { this->decoder_ok = 0; pthread_mutex_unlock (&ffmpeg_lock); } + + ff_audio_reset_parser(this); } static void ff_audio_discontinuity (audio_decoder_t *this_gen) { + + ff_audio_decoder_t *this = (ff_audio_decoder_t *) this_gen; + + this->size = 0; + + ff_audio_reset_parser(this); } static void ff_audio_dispose (audio_decoder_t *this_gen) { ff_audio_decoder_t *this = (ff_audio_decoder_t *) this_gen; + if (this->parser_context) { + pthread_mutex_lock (&ffmpeg_lock); + av_parser_close(this->parser_context); + this->parser_context = NULL; + pthread_mutex_unlock (&ffmpeg_lock); + } + if( this->context && this->decoder_ok ) { pthread_mutex_lock (&ffmpeg_lock); avcodec_close (this->context); |