avplay: use libavresample for sample format conversion and channel mixing
[ffmpeg.git] / avplay.c
index 0d72f46..e18b2e4 100644 (file)
--- a/avplay.c
+++ b/avplay.c
@@ -34,7 +34,7 @@
 #include "libavformat/avformat.h"
 #include "libavdevice/avdevice.h"
 #include "libswscale/swscale.h"
-#include "libavcodec/audioconvert.h"
+#include "libavresample/avresample.h"
 #include "libavutil/opt.h"
 #include "libavcodec/avfft.h"
 
@@ -159,8 +159,12 @@ typedef struct VideoState {
     int audio_buf_index; /* in bytes */
     AVPacket audio_pkt_temp;
     AVPacket audio_pkt;
-    enum AVSampleFormat audio_src_fmt;
-    AVAudioConvert *reformat_ctx;
+    enum AVSampleFormat sdl_sample_fmt;
+    uint64_t sdl_channel_layout;
+    int sdl_channels;
+    enum AVSampleFormat resample_sample_fmt;
+    uint64_t resample_channel_layout;
+    AVAudioResampleContext *avr;
     AVFrame *frame;
 
     int show_audio; /* if true, display audio samples */
@@ -743,7 +747,7 @@ static void video_audio_display(VideoState *s)
     nb_freq = 1 << (rdft_bits - 1);
 
     /* compute display index : center on currently output samples */
-    channels = s->audio_st->codec->channels;
+    channels = s->sdl_channels;
     nb_display_channels = channels;
     if (!s->paused) {
         int data_used = s->show_audio == 1 ? s->width : (2 * nb_freq);
@@ -957,8 +961,8 @@ static double get_audio_clock(VideoState *is)
     hw_buf_size = audio_write_get_buf_size(is);
     bytes_per_sec = 0;
     if (is->audio_st) {
-        bytes_per_sec = is->audio_st->codec->sample_rate *
-                        2 * is->audio_st->codec->channels;
+        bytes_per_sec = is->audio_st->codec->sample_rate * is->sdl_channels *
+                        av_get_bytes_per_sample(is->sdl_sample_fmt);
     }
     if (bytes_per_sec)
         pts -= (double)hw_buf_size / bytes_per_sec;
@@ -1937,7 +1941,7 @@ static int synchronize_audio(VideoState *is, short *samples,
     int n, samples_size;
     double ref_clock;
 
-    n = 2 * is->audio_st->codec->channels;
+    n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt);
     samples_size = samples_size1;
 
     /* if not master, then we try to remove or add samples to correct the clock */
@@ -2018,6 +2022,8 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
     for (;;) {
         /* NOTE: the audio packet can contain several frames */
         while (pkt_temp->size > 0 || (!pkt_temp->data && new_packet)) {
+            int resample_changed, audio_resample;
+
             if (!is->frame) {
                 if (!(is->frame = avcodec_alloc_frame()))
                     return AVERROR(ENOMEM);
@@ -2047,39 +2053,67 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
                                                    is->frame->nb_samples,
                                                    dec->sample_fmt, 1);
 
-            if (dec->sample_fmt != is->audio_src_fmt) {
-                if (is->reformat_ctx)
-                    av_audio_convert_free(is->reformat_ctx);
-                is->reformat_ctx= av_audio_convert_alloc(AV_SAMPLE_FMT_S16, 1,
-                                                         dec->sample_fmt, 1, NULL, 0);
-                if (!is->reformat_ctx) {
-                    fprintf(stderr, "Cannot convert %s sample format to %s sample format\n",
-                        av_get_sample_fmt_name(dec->sample_fmt),
-                        av_get_sample_fmt_name(AV_SAMPLE_FMT_S16));
+            audio_resample = dec->sample_fmt     != is->sdl_sample_fmt ||
+                             dec->channel_layout != is->sdl_channel_layout;
+
+            resample_changed = dec->sample_fmt     != is->resample_sample_fmt ||
+                               dec->channel_layout != is->resample_channel_layout;
+
+            if ((!is->avr && audio_resample) || resample_changed) {
+                if (is->avr)
+                    avresample_close(is->avr);
+                else if (audio_resample) {
+                    int ret;
+                    is->avr = avresample_alloc_context();
+                    if (!is->avr) {
+                        fprintf(stderr, "error allocating AVAudioResampleContext\n");
                         break;
+                    }
+                    av_opt_set_int(is->avr, "in_channel_layout",  dec->channel_layout,    0);
+                    av_opt_set_int(is->avr, "in_sample_fmt",      dec->sample_fmt,        0);
+                    av_opt_set_int(is->avr, "in_sample_rate",     dec->sample_rate,       0);
+                    av_opt_set_int(is->avr, "out_channel_layout", is->sdl_channel_layout, 0);
+                    av_opt_set_int(is->avr, "out_sample_fmt",     is->sdl_sample_fmt,     0);
+                    av_opt_set_int(is->avr, "out_sample_rate",    dec->sample_rate,       0);
+                    if (av_get_bytes_per_sample(dec->sample_fmt) <= 2)
+                        av_opt_set_int(is->avr, "internal_sample_fmt", AV_SAMPLE_FMT_S16P, 0);
+
+                    if ((ret = avresample_open(is->avr)) < 0) {
+                        fprintf(stderr, "error initializing libavresample\n");
+                        break;
+                    }
                 }
-                is->audio_src_fmt= dec->sample_fmt;
+                is->resample_sample_fmt     = dec->sample_fmt;
+                is->resample_channel_layout = dec->channel_layout;
             }
 
-            if (is->reformat_ctx) {
-                const void *ibuf[6] = { is->frame->data[0] };
-                void *obuf[6];
-                int istride[6] = { av_get_bytes_per_sample(dec->sample_fmt) };
-                int ostride[6] = { 2 };
-                int len= data_size/istride[0];
-                obuf[0] = av_realloc(is->audio_buf1, FFALIGN(len * ostride[0], 32));
-                if (!obuf[0]) {
+            if (audio_resample) {
+                void *tmp_out;
+                int out_samples, out_size, out_linesize;
+                int osize      = av_get_bytes_per_sample(is->sdl_sample_fmt);
+                int nb_samples = is->frame->nb_samples;
+
+                out_size = av_samples_get_buffer_size(&out_linesize,
+                                                      is->sdl_channels,
+                                                      nb_samples,
+                                                      is->sdl_sample_fmt, 0);
+                tmp_out = av_realloc(is->audio_buf1, out_size);
+                if (!tmp_out)
                     return AVERROR(ENOMEM);
-                }
-                is->audio_buf1 = obuf[0];
-                if (av_audio_convert(is->reformat_ctx, obuf, ostride, ibuf, istride, len) < 0) {
-                    printf("av_audio_convert() failed\n");
+                is->audio_buf1 = tmp_out;
+
+                out_samples = avresample_convert(is->avr,
+                                                 (void **)&is->audio_buf1,
+                                                 out_linesize, nb_samples,
+                                                 (void **)is->frame->data,
+                                                 is->frame->linesize[0],
+                                                 is->frame->nb_samples);
+                if (out_samples < 0) {
+                    fprintf(stderr, "avresample_convert() failed\n");
                     break;
                 }
                 is->audio_buf = is->audio_buf1;
-                /* FIXME: existing code assume that data_size equals framesize*channels*2
-                          remove this legacy cruft */
-                data_size = len * 2;
+                data_size = out_samples * osize * is->sdl_channels;
             } else {
                 is->audio_buf = is->frame->data[0];
             }
@@ -2087,7 +2121,7 @@ static int audio_decode_frame(VideoState *is, double *pts_ptr)
             /* if no pts, then compute it */
             pts = is->audio_clock;
             *pts_ptr = pts;
-            n = 2 * dec->channels;
+            n = is->sdl_channels * av_get_bytes_per_sample(is->sdl_sample_fmt);
             is->audio_clock += (double)data_size /
                 (double)(n * dec->sample_rate);
 #ifdef DEBUG
@@ -2206,7 +2240,20 @@ static int stream_component_open(VideoState *is, int stream_index)
     if (avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
         wanted_spec.freq = avctx->sample_rate;
         wanted_spec.format = AUDIO_S16SYS;
-        wanted_spec.channels = avctx->channels;
+
+        if (!avctx->channel_layout)
+            avctx->channel_layout = av_get_default_channel_layout(avctx->channels);
+        if (!avctx->channel_layout) {
+            fprintf(stderr, "unable to guess channel layout\n");
+            return -1;
+        }
+        if (avctx->channels == 1)
+            is->sdl_channel_layout = AV_CH_LAYOUT_MONO;
+        else
+            is->sdl_channel_layout = AV_CH_LAYOUT_STEREO;
+        is->sdl_channels = av_get_channel_layout_nb_channels(is->sdl_channel_layout);
+
+        wanted_spec.channels = is->sdl_channels;
         wanted_spec.silence = 0;
         wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
         wanted_spec.callback = sdl_audio_callback;
@@ -2216,7 +2263,9 @@ static int stream_component_open(VideoState *is, int stream_index)
             return -1;
         }
         is->audio_hw_buf_size = spec.size;
-        is->audio_src_fmt = AV_SAMPLE_FMT_S16;
+        is->sdl_sample_fmt          = AV_SAMPLE_FMT_S16;
+        is->resample_sample_fmt     = is->sdl_sample_fmt;
+        is->resample_channel_layout = is->sdl_channel_layout;
     }
 
     ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
@@ -2275,9 +2324,8 @@ static void stream_component_close(VideoState *is, int stream_index)
 
         packet_queue_end(&is->audioq);
         av_free_packet(&is->audio_pkt);
-        if (is->reformat_ctx)
-            av_audio_convert_free(is->reformat_ctx);
-        is->reformat_ctx = NULL;
+        if (is->avr)
+            avresample_free(&is->avr);
         av_freep(&is->audio_buf1);
         is->audio_buf = NULL;
         av_freep(&is->frame);