Use libswresample to convert 32-bit and float audio into s16

Since libao+pulseaudio cannot play 32-bit or flt/fltp/dbl/dblp audio,
the following audio formats are passed through libswresample
to convert into AV_SAMPLE_FMT_S16, which is accepted by libao:

* AV_SAMPLE_FMT_S32
* AV_SAMPLE_FMT_S32P
* AV_SAMPLE_FMT_FLT
* AV_SAMPLE_FMT_FLTP
* AV_SAMPLE_FMT_DBL
* AV_SAMPLE_FMT_DBLP

This fixes issue #949 and issue #1014. Now FFmpeg+libao internal player
can play with pulseaudio backend enabled in /etc/libao.conf .

Signed-off-by: hrimfaxi <outmatch@gmail.com>
This commit is contained in:
hrimfaxi 2020-07-18 20:42:29 +08:00
parent ec40c1dcfd
commit 2d2db3b208
2 changed files with 53 additions and 63 deletions

View file

@ -19,6 +19,7 @@ extern "C" {
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include <libavutil/avutil.h> #include <libavutil/avutil.h>
#include "libswresample/swresample.h"
} }
#include <QString> #include <QString>
@ -100,6 +101,8 @@ struct DecoderContext
ao_device * aoDevice_; ao_device * aoDevice_;
bool avformatOpened_; bool avformatOpened_;
SwrContext *swr_;
DecoderContext( QByteArray const & audioData, QAtomicInt & isCancelled ); DecoderContext( QByteArray const & audioData, QAtomicInt & isCancelled );
~DecoderContext(); ~DecoderContext();
@ -122,7 +125,8 @@ DecoderContext::DecoderContext( QByteArray const & audioData, QAtomicInt & isCan
avioContext_( NULL ), avioContext_( NULL ),
audioStream_( NULL ), audioStream_( NULL ),
aoDevice_( NULL ), aoDevice_( NULL ),
avformatOpened_( false ) avformatOpened_( false ),
swr_( NULL )
{ {
} }
@ -243,11 +247,36 @@ bool DecoderContext::openCodec( QString & errorString )
av_log( NULL, AV_LOG_INFO, "Codec open: %s: channels: %d, rate: %d, format: %s\n", codec_->long_name, av_log( NULL, AV_LOG_INFO, "Codec open: %s: channels: %d, rate: %d, format: %s\n", codec_->long_name,
codecContext_->channels, codecContext_->sample_rate, av_get_sample_fmt_name( codecContext_->sample_fmt ) ); codecContext_->channels, codecContext_->sample_rate, av_get_sample_fmt_name( codecContext_->sample_fmt ) );
if ( codecContext_->sample_fmt == AV_SAMPLE_FMT_S32 ||
codecContext_->sample_fmt == AV_SAMPLE_FMT_S32P ||
codecContext_->sample_fmt == AV_SAMPLE_FMT_FLT ||
codecContext_->sample_fmt == AV_SAMPLE_FMT_FLTP ||
codecContext_->sample_fmt == AV_SAMPLE_FMT_DBL ||
codecContext_->sample_fmt == AV_SAMPLE_FMT_DBLP )
{
swr_ = swr_alloc_set_opts( NULL,
codecContext_->channel_layout,
AV_SAMPLE_FMT_S16,
codecContext_->sample_rate,
codecContext_->channel_layout,
codecContext_->sample_fmt,
codecContext_->sample_rate,
0,
NULL );
swr_init( swr_ );
}
return true; return true;
} }
void DecoderContext::closeCodec() void DecoderContext::closeCodec()
{ {
if ( swr_ )
{
swr_free( &swr_ );
}
if ( !formatContext_ ) if ( !formatContext_ )
{ {
if ( avioContext_ ) if ( avioContext_ )
@ -306,11 +335,12 @@ bool DecoderContext::openOutputDevice( QString & errorString )
} }
ao_sample_format aoSampleFormat; ao_sample_format aoSampleFormat;
memset (&aoSampleFormat, 0, sizeof(aoSampleFormat) );
aoSampleFormat.channels = codecContext_->channels; aoSampleFormat.channels = codecContext_->channels;
aoSampleFormat.rate = codecContext_->sample_rate; aoSampleFormat.rate = codecContext_->sample_rate;
aoSampleFormat.byte_format = AO_FMT_NATIVE; aoSampleFormat.byte_format = AO_FMT_NATIVE;
aoSampleFormat.matrix = 0; aoSampleFormat.matrix = 0;
aoSampleFormat.bits = qMin( 32, av_get_bytes_per_sample( codecContext_->sample_fmt ) << 3 ); aoSampleFormat.bits = qMin( 16, av_get_bytes_per_sample( codecContext_->sample_fmt ) << 3 );
if ( aoSampleFormat.bits == 0 ) if ( aoSampleFormat.bits == 0 )
{ {
@ -484,34 +514,11 @@ bool DecoderContext::normalizeAudio( AVFrame * frame, vector<char> & samples )
{ {
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16:
case AV_SAMPLE_FMT_S32:
{ {
samples.resize( dataSize ); samples.resize( dataSize );
memcpy( &samples.front(), frame->data[0], lineSize ); memcpy( &samples.front(), frame->data[0], lineSize );
} }
break; break;
case AV_SAMPLE_FMT_FLT:
{
samples.resize( dataSize );
int32_t * out = ( int32_t * )&samples.front();
for ( int i = 0; i < dataSize; i += sizeof( float ) )
{
*out++ = toInt32( *( float * )frame->data[i] );
}
}
break;
case AV_SAMPLE_FMT_DBL:
{
samples.resize( dataSize / 2 );
int32_t * out = ( int32_t * )&samples.front();
for ( int i = 0; i < dataSize; i += sizeof( double ) )
{
*out++ = toInt32( *( double * )frame->data[i] );
}
}
break;
// Planar // Planar
case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_U8P:
{ {
@ -541,48 +548,28 @@ bool DecoderContext::normalizeAudio( AVFrame * frame, vector<char> & samples )
} }
} }
break; break;
case AV_SAMPLE_FMT_S32:
/* Pass through */
case AV_SAMPLE_FMT_S32P: case AV_SAMPLE_FMT_S32P:
{ /* Pass through */
samples.resize( dataSize ); case AV_SAMPLE_FMT_FLT:
/* Pass through */
int32_t * out = ( int32_t * )&samples.front();
for ( int i = 0; i < frame->nb_samples; i++ )
{
for ( int ch = 0; ch < codecContext_->channels; ch++ )
{
*out++ = ( ( int32_t * )frame->extended_data[ch] )[i];
}
}
}
break;
case AV_SAMPLE_FMT_FLTP: case AV_SAMPLE_FMT_FLTP:
{ /* Pass through */
samples.resize( dataSize );
float ** data = ( float ** )frame->extended_data;
int32_t * out = ( int32_t * )&samples.front();
for ( int i = 0; i < frame->nb_samples; i++ )
{
for ( int ch = 0; ch < codecContext_->channels; ch++ )
{
*out++ = toInt32( data[ch][i] );
}
}
}
break;
case AV_SAMPLE_FMT_DBLP:
{ {
samples.resize( dataSize / 2 ); samples.resize( dataSize / 2 );
double ** data = ( double ** )frame->extended_data; uint8_t *out = ( uint8_t * )&samples.front();
int32_t * out = ( int32_t * )&samples.front(); swr_convert( swr_, &out, frame->nb_samples, (const uint8_t**)frame->extended_data, frame->nb_samples );
for ( int i = 0; i < frame->nb_samples; i++ )
{
for ( int ch = 0; ch < codecContext_->channels; ch++ )
{
*out++ = toInt32( data[ch][i] );
}
} }
break;
case AV_SAMPLE_FMT_DBL:
case AV_SAMPLE_FMT_DBLP:
{
samples.resize( dataSize / 4 );
uint8_t *out = ( uint8_t * )&samples.front();
swr_convert( swr_, &out, frame->nb_samples, (const uint8_t**)frame->extended_data, frame->nb_samples );
} }
break; break;
default: default:

View file

@ -108,6 +108,7 @@ win32 {
-logg -logg
!CONFIG( no_ffmpeg_player ) { !CONFIG( no_ffmpeg_player ) {
LIBS += -lao \ LIBS += -lao \
-lswresample-gd \
-lavutil-gd \ -lavutil-gd \
-lavformat-gd \ -lavformat-gd \
-lavcodec-gd -lavcodec-gd
@ -156,7 +157,8 @@ unix:!mac {
PKGCONFIG += ao \ PKGCONFIG += ao \
libavutil \ libavutil \
libavformat \ libavformat \
libavcodec libavcodec \
libswresample \
} }
arm { arm {
LIBS += -liconv LIBS += -liconv
@ -210,6 +212,7 @@ mac {
-llzo2 -llzo2
!CONFIG( no_ffmpeg_player ) { !CONFIG( no_ffmpeg_player ) {
LIBS += -lao \ LIBS += -lao \
-lswresample-gd \
-lavutil-gd \ -lavutil-gd \
-lavformat-gd \ -lavformat-gd \
-lavcodec-gd -lavcodec-gd