diff options
Diffstat (limited to 'source/blender/src/hddaudio.c')
-rw-r--r-- | source/blender/src/hddaudio.c | 360 |
1 files changed, 272 insertions, 88 deletions
diff --git a/source/blender/src/hddaudio.c b/source/blender/src/hddaudio.c index a06ff0c6901..4221bd440b5 100644 --- a/source/blender/src/hddaudio.c +++ b/source/blender/src/hddaudio.c @@ -20,7 +20,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - * Contributor(s): Peter Schlaile <peter@schlaile.de> 2005 + * Contributor(s): Peter Schlaile <peter [at] schlaile [dot] de> 2005 * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ @@ -38,6 +38,7 @@ #ifdef WITH_FFMPEG #include <ffmpeg/avformat.h> #include <ffmpeg/avcodec.h> +#include <ffmpeg/rational.h> #if LIBAVFORMAT_VERSION_INT < (49 << 16) #define FFMPEG_OLD_FRAME_RATE 1 #else @@ -65,8 +66,12 @@ struct hdaudio { AVCodecContext *pCodecCtx; int frame_position; int frame_duration; + int frame_alloc_duration; + int decode_pos; int frame_size; short * decode_cache; + short * decode_cache_zero; + short * resample_cache; int decode_cache_size; int target_channels; int target_rate; @@ -77,6 +82,20 @@ struct hdaudio { #endif }; +#ifdef WITH_FFMPEG +#ifdef FFMPEG_CODEC_IS_POINTER +static AVCodecContext* get_codec_from_stream(AVStream* stream) +{ + return stream->codec; +} +#else +static AVCodecContext* get_codec_from_stream(AVStream* stream) +{ + return &stream->codec; +} +#endif +#endif + struct hdaudio * sound_open_hdaudio(char * filename) { #ifdef WITH_FFMPEG @@ -104,11 +123,8 @@ struct hdaudio * sound_open_hdaudio(char * filename) /* Find the first audio stream */ audioStream=-1; for(i=0; i<pFormatCtx->nb_streams; i++) -#ifdef FFMPEG_CODEC_IS_POINTER - if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO) -#else - if(pFormatCtx->streams[i]->codec.codec_type==CODEC_TYPE_AUDIO) -#endif + if(get_codec_from_stream(pFormatCtx->streams[i]) + ->codec_type == CODEC_TYPE_AUDIO) { audioStream=i; break; @@ -119,11 +135,7 @@ struct hdaudio * sound_open_hdaudio(char * filename) return 0; } -#ifdef FFMPEG_CODEC_IS_POINTER - pCodecCtx=pFormatCtx->streams[audioStream]->codec; -#else - pCodecCtx=&pFormatCtx->streams[audioStream]->codec; -#endif + pCodecCtx = get_codec_from_stream(pFormatCtx->streams[audioStream]); /* Find the decoder for the audio stream */ pCodec = avcodec_find_decoder(pCodecCtx->codec_id); @@ -133,11 +145,6 @@ struct hdaudio * sound_open_hdaudio(char * filename) return 0; } -#if 0 - if(pCodec->capabilities & CODEC_CAP_TRUNCATED) - pCodecCtx->flags|=CODEC_FLAG_TRUNCATED; -#endif - if(avcodec_open(pCodecCtx, pCodec)<0) { avcodec_close(pCodecCtx); av_close_input_file(pFormatCtx); @@ -155,22 +162,23 @@ struct hdaudio * sound_open_hdaudio(char * filename) rval->pCodecCtx = pCodecCtx; rval->pCodec = pCodec; rval->audioStream = audioStream; - rval->frame_position = -1; + rval->frame_position = -10; - /* FIXME: This only works with integer frame rates ... */ - rval->frame_duration = AV_TIME_BASE; + rval->frame_duration = AV_TIME_BASE / 10; + rval->frame_alloc_duration = AV_TIME_BASE; rval->decode_cache_size = (long long) rval->sample_rate * rval->channels - * rval->frame_duration / AV_TIME_BASE + * rval->frame_alloc_duration / AV_TIME_BASE * 2; rval->decode_cache = (short*) MEM_mallocN( rval->decode_cache_size * sizeof(short), "hdaudio decode cache"); - + rval->decode_pos = 0; rval->target_channels = -1; rval->target_rate = -1; rval->resampler = 0; + rval->resample_cache = 0; return rval; #else return 0; @@ -195,21 +203,27 @@ long sound_hdaudio_get_duration(struct hdaudio * hdaudio, int frame_rate) #endif } -void sound_hdaudio_extract(struct hdaudio * hdaudio, - short * target_buffer, - int sample_position /* units of target_rate */, - int target_rate, - int target_channels, - int nb_samples /* in target */) -{ #ifdef WITH_FFMPEG +static void sound_hdaudio_extract_small_block( + struct hdaudio * hdaudio, + short * target_buffer, + int sample_position /* units of target_rate */, + int target_rate, + int target_channels, + int nb_samples /* in target */) +{ AVPacket packet; int frame_position; int frame_size = (long long) target_rate * hdaudio->frame_duration / AV_TIME_BASE; + int in_frame_size = (long long) hdaudio->sample_rate + * hdaudio->frame_duration / AV_TIME_BASE; int rate_conversion = (target_rate != hdaudio->sample_rate) || (target_channels != hdaudio->channels); + int sample_ofs = target_channels * (sample_position % frame_size); + + frame_position = sample_position / frame_size; if (hdaudio == 0) return; @@ -226,94 +240,261 @@ void sound_hdaudio_extract(struct hdaudio * hdaudio, target_rate, hdaudio->sample_rate); hdaudio->target_rate = target_rate; hdaudio->target_channels = target_channels; + if (hdaudio->resample_cache) { + MEM_freeN(hdaudio->resample_cache); + } + + + hdaudio->resample_cache = (short*) MEM_mallocN( + (long long) + hdaudio->target_channels + * frame_size * 2 + * sizeof(short), + "hdaudio resample cache"); + + if (frame_position == hdaudio->frame_position) { + audio_resample(hdaudio->resampler, + hdaudio->resample_cache, + hdaudio->decode_cache_zero, + in_frame_size * 7 / 4); + } } } - frame_position = sample_position / frame_size; + if (frame_position == hdaudio->frame_position + 1 + && in_frame_size * hdaudio->channels <= hdaudio->decode_pos) { + int bl_size = in_frame_size * hdaudio->channels; + int decode_pos = hdaudio->decode_pos; + + hdaudio->frame_position = frame_position; + + memcpy(hdaudio->decode_cache, + hdaudio->decode_cache + bl_size, + (decode_pos - bl_size) * sizeof(short)); + + decode_pos -= bl_size; + + while(av_read_frame(hdaudio->pFormatCtx, &packet) >= 0) { + int data_size; + int len; + uint8_t *audio_pkt_data; + int audio_pkt_size; + + if(packet.stream_index != hdaudio->audioStream) { + av_free_packet(&packet); + continue; + } + + audio_pkt_data = packet.data; + audio_pkt_size = packet.size; + + while (audio_pkt_size > 0) { + len = avcodec_decode_audio( + hdaudio->pCodecCtx, + hdaudio->decode_cache + + decode_pos, + &data_size, + audio_pkt_data, + audio_pkt_size); + if (data_size <= 0) { + continue; + } + if (len < 0) { + audio_pkt_size = 0; + break; + } + + audio_pkt_size -= len; + audio_pkt_data += len; + + decode_pos += data_size / sizeof(short); + if (decode_pos + data_size + / sizeof(short) + > hdaudio->decode_cache_size) { + break; + } + } + av_free_packet(&packet); + + if (decode_pos + data_size / sizeof(short) + > hdaudio->decode_cache_size) { + break; + } + } + + if (rate_conversion) { + audio_resample(hdaudio->resampler, + hdaudio->resample_cache, + hdaudio->decode_cache_zero, + in_frame_size * 7 / 4); + } + + hdaudio->decode_pos = decode_pos; + } if (frame_position != hdaudio->frame_position) { long decode_pos = 0; + long long st_time = hdaudio->pFormatCtx + ->streams[hdaudio->audioStream]->start_time; + double time_base = + av_q2d(hdaudio->pFormatCtx + ->streams[hdaudio->audioStream]->time_base); + long long pos = frame_position * AV_TIME_BASE + * hdaudio->frame_duration / AV_TIME_BASE; hdaudio->frame_position = frame_position; + if (st_time == AV_NOPTS_VALUE) { + st_time = 0; + } + + pos += st_time * AV_TIME_BASE * time_base; + av_seek_frame(hdaudio->pFormatCtx, -1, - (long long) frame_position * AV_TIME_BASE, + pos, AVSEEK_FLAG_ANY | AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(hdaudio->pCodecCtx); + + hdaudio->decode_cache_zero = 0; while(av_read_frame(hdaudio->pFormatCtx, &packet) >= 0) { - if(packet.stream_index == hdaudio->audioStream) { - int data_size; - int len; - uint8_t *audio_pkt_data; - int audio_pkt_size; - - audio_pkt_data = packet.data; - audio_pkt_size = packet.size; - - while (audio_pkt_size > 0) { - len = avcodec_decode_audio( - hdaudio->pCodecCtx, - hdaudio->decode_cache - + decode_pos, - &data_size, - audio_pkt_data, - audio_pkt_size); - if (data_size <= 0) { - continue; - } - if (len < 0) { - audio_pkt_size = 0; - break; + int data_size; + int len; + uint8_t *audio_pkt_data; + int audio_pkt_size; + + if(packet.stream_index != hdaudio->audioStream) { + av_free_packet(&packet); + continue; + } + + audio_pkt_data = packet.data; + audio_pkt_size = packet.size; + + if (!hdaudio->decode_cache_zero + && audio_pkt_size > 0) { + long long diff; + + if (packet.pts == AV_NOPTS_VALUE) { + fprintf(stderr, + "hdaudio: audio " + "pts=NULL audio " + "distortion!\n"); + diff = 0; + } else { + long long pts = packet.pts; + long long spts = (long long) ( + pos / time_base / AV_TIME_BASE + + 0.5); + diff = spts - pts; + if (diff < 0) { + fprintf(stderr, + "hdaudio: " + "negative seek: " + "%lld < %lld " + "audio distortion!!\n", + spts, pts); + diff = 0; } + } - audio_pkt_size -= len; - audio_pkt_data += len; - decode_pos += - data_size / sizeof(short); - if (decode_pos + data_size - / sizeof(short) - > hdaudio->decode_cache_size) { - av_free_packet(&packet); - break; - } + diff *= hdaudio->sample_rate * time_base; + diff *= hdaudio->channels; + + if (diff > hdaudio->decode_cache_size / 2) { + fprintf(stderr, + "hdaudio: audio " + "diff too large!!\n"); + diff = 0; + } + + hdaudio->decode_cache_zero + = hdaudio->decode_cache + diff; + } + + while (audio_pkt_size > 0) { + len = avcodec_decode_audio( + hdaudio->pCodecCtx, + hdaudio->decode_cache + + decode_pos, + &data_size, + audio_pkt_data, + audio_pkt_size); + if (data_size <= 0) { + continue; } - if (decode_pos + data_size / sizeof(short) + if (len < 0) { + audio_pkt_size = 0; + break; + } + + audio_pkt_size -= len; + audio_pkt_data += len; + + decode_pos += data_size / sizeof(short); + if (decode_pos + data_size + / sizeof(short) > hdaudio->decode_cache_size) { break; } } + av_free_packet(&packet); + + if (decode_pos + data_size / sizeof(short) + > hdaudio->decode_cache_size) { + break; + } + } + if (rate_conversion) { + audio_resample(hdaudio->resampler, + hdaudio->resample_cache, + hdaudio->decode_cache_zero, + in_frame_size * 7 / 4); } + hdaudio->decode_pos = decode_pos; } - if (!rate_conversion) { - int ofs = target_channels * (sample_position % frame_size); - memcpy(target_buffer, - hdaudio->decode_cache + ofs, - nb_samples * target_channels * sizeof(short)); - } else { - double ratio = (double) hdaudio->sample_rate / target_rate; - long in_samples = (long) ((nb_samples + 16) * ratio); - short temp_buffer[target_channels * (nb_samples + 64)]; - - int s = audio_resample(hdaudio->resampler, - temp_buffer, - hdaudio->decode_cache - + target_channels * - (long) - (ratio*(sample_position % frame_size)), - in_samples); - if (s < nb_samples || s > nb_samples + 63) { - fprintf(stderr, "resample ouch: %d != %d\n", - s, nb_samples); - } - memcpy(target_buffer, temp_buffer, - nb_samples * target_channels * sizeof(short)); + memcpy(target_buffer, (rate_conversion + ? hdaudio->resample_cache + : hdaudio->decode_cache_zero) + sample_ofs, + nb_samples * target_channels * sizeof(short)); +} +#endif + + +void sound_hdaudio_extract(struct hdaudio * hdaudio, + short * target_buffer, + int sample_position /* units of target_rate */, + int target_rate, + int target_channels, + int nb_samples /* in target */) +{ +#ifdef WITH_FFMPEG + long long max_samples = (long long) target_rate + * hdaudio->frame_duration / AV_TIME_BASE / 4; + + while (nb_samples > max_samples) { + sound_hdaudio_extract_small_block(hdaudio, target_buffer, + sample_position, + target_rate, + target_channels, + max_samples); + target_buffer += max_samples * target_channels; + sample_position += max_samples; + nb_samples -= max_samples; + } + if (nb_samples > 0) { + sound_hdaudio_extract_small_block(hdaudio, target_buffer, + sample_position, + target_rate, + target_channels, + nb_samples); } #else -#endif +#endif } void sound_close_hdaudio(struct hdaudio * hdaudio) @@ -324,6 +505,9 @@ void sound_close_hdaudio(struct hdaudio * hdaudio) avcodec_close(hdaudio->pCodecCtx); av_close_input_file(hdaudio->pFormatCtx); MEM_freeN (hdaudio->decode_cache); + if (hdaudio->resample_cache) { + MEM_freeN(hdaudio->resample_cache); + } free(hdaudio->filename); MEM_freeN (hdaudio); } |