diff --git a/configure.ac b/configure.ac index 163d9ea..88dbf02 100644 --- a/configure.ac +++ b/configure.ac @@ -29,6 +29,7 @@ if test x$BOXTYPE = xgeneric; then # don't know which version is exactly needed here... PKG_CHECK_MODULES([AVUTIL], [libavutil]) PKG_CHECK_MODULES([SWSCALE], [libswscale]) + PKG_CHECK_MODULES([SWRESAMPLE], [libswresample]) fi AC_OUTPUT([ Makefile diff --git a/generic-pc/audio.cpp b/generic-pc/audio.cpp index 99effcd..fc1cd2f 100644 --- a/generic-pc/audio.cpp +++ b/generic-pc/audio.cpp @@ -16,7 +16,7 @@ * * cAudio implementation with decoder. * uses libao for output - * ffmpeg for demuxing / decoding + * ffmpeg for demuxing / decoding / format conversion */ #include @@ -33,6 +33,9 @@ extern "C" { #include +#include +#include +#include #include } /* ffmpeg buf 2k */ @@ -279,6 +282,13 @@ void cAudio::run() ao_info *ai; // ao_device *adevice; // ao_sample_format sformat; + /* resample */ + SwrContext *swr = NULL; + uint8_t *obuf = NULL; + int obuf_sz = 0; /* in samples */ + int obuf_sz_max = 0; + int o_ch, o_sr; /* output channels and sample rate */ + uint64_t o_layout; /* output channels layout */ curr_pts = 0; av_init_packet(&avpkt); @@ -324,13 +334,17 @@ void cAudio::run() lt_info("%s: avcodec_alloc_frame failed\n", __func__); goto out2; } - if (sformat.channels != c->channels || sformat.rate != c->sample_rate || + /* output sample rate, channels, layout could be set here if necessary */ + o_ch = c->channels; /* 2 */ + o_sr = c->sample_rate; /* 48000 */ + o_layout = c->channel_layout; /* AV_CH_LAYOUT_STEREO */ + if (sformat.channels != o_ch || sformat.rate != o_sr || sformat.byte_format != AO_FMT_NATIVE || sformat.bits != 16 || adevice == NULL) { driver = ao_default_driver_id(); sformat.bits = 16; - sformat.channels = c->channels; - sformat.rate = c->sample_rate; + sformat.channels = o_ch; + sformat.rate = o_sr; sformat.byte_format = AO_FMT_NATIVE; sformat.matrix = 0; if (adevice) @@ -338,7 +352,7 @@ void cAudio::run() adevice = ao_open_live(driver, &sformat, NULL); ai = ao_driver_info(driver); lt_info("%s: changed params ch %d srate %d bits %d adevice %p\n", - __func__, c->channels, c->sample_rate, 16, adevice);; + __func__, o_ch, o_sr, 16, adevice);; lt_info("libao driver: %d name '%s' short '%s' author '%s'\n", driver, ai->name, ai->short_name, ai->author); } @@ -350,19 +364,49 @@ void cAudio::run() #endif lt_info("codec params: sample_fmt %d sample_rate %d channels %d\n", c->sample_fmt, c->sample_rate, c->channels); + swr = swr_alloc_set_opts(swr, + o_layout, AV_SAMPLE_FMT_S16, o_sr, /* output */ + c->channel_layout, c->sample_fmt, c->sample_rate, /* input */ + 0, NULL); + if (! swr) { + lt_info("could not alloc resample context\n"); + goto out3; + } + swr_init(swr); while (thread_started) { int gotframe = 0; if (av_read_frame(avfc, &avpkt) < 0) break; avcodec_decode_audio4(c, frame, &gotframe, &avpkt); if (gotframe && thread_started) { + int out_linesize; + obuf_sz = av_rescale_rnd(swr_get_delay(swr, c->sample_rate) + + frame->nb_samples, o_sr, c->sample_rate, AV_ROUND_UP); + if (obuf_sz > obuf_sz_max) { + lt_info("obuf_sz: %d old: %d\n", obuf_sz, obuf_sz_max); + av_free(obuf); + if (av_samples_alloc(&obuf, &out_linesize, o_ch, + frame->nb_samples, AV_SAMPLE_FMT_S16, 1) < 0) { + lt_info("av_samples_alloc failed\n"); + av_free_packet(&avpkt); + break; /* while (thread_started) */ + } + obuf_sz_max = obuf_sz; + } + obuf_sz = swr_convert(swr, &obuf, obuf_sz, + (const uint8_t **)frame->extended_data, frame->nb_samples); curr_pts = av_frame_get_best_effort_timestamp(frame); lt_debug("%s: pts 0x%" PRIx64 " %3f\n", __func__, curr_pts, curr_pts/90000.0); - ao_play(adevice, (char*)frame->extended_data[0], frame->linesize[0]); + int o_buf_sz = av_samples_get_buffer_size(&out_linesize, o_ch, + obuf_sz, AV_SAMPLE_FMT_S16, 1); + ao_play(adevice, (char *)obuf, o_buf_sz); } av_free_packet(&avpkt); } // ao_close(adevice); /* can take long :-( */ + av_free(obuf); + swr_free(&swr); + out3: avcodec_free_frame(&frame); out2: avcodec_close(c);