mirror of
https://github.com/xiaoyifang/goldendict-ng.git
synced 2024-11-24 00:14:06 +00:00
feature: remove libao dependency and use QAudioSink(QAudioOutput) to play the pcm audio format
This commit is contained in:
parent
e3d904f8b8
commit
85aad0f80c
187
audiooutput.cpp
Normal file
187
audiooutput.cpp
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
#include "audiooutput.h"
|
||||||
|
|
||||||
|
#include <QAudioFormat>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QtConcurrent/qtconcurrentrun.h>
|
||||||
|
#include <QFuture>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QThreadPool>
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
#include <QAudioOutput>
|
||||||
|
#else
|
||||||
|
#include <QAudioSink>
|
||||||
|
#endif
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QBuffer>
|
||||||
|
|
||||||
|
static QAudioFormat format( int sampleRate, int channelCount )
|
||||||
|
{
|
||||||
|
QAudioFormat out;
|
||||||
|
|
||||||
|
out.setSampleRate( sampleRate );
|
||||||
|
out.setChannelCount( 2 );
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
out.setByteOrder( QAudioFormat::LittleEndian );
|
||||||
|
out.setCodec( QLatin1String( "audio/pcm" ) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
out.setSampleSize( 16 );
|
||||||
|
out.setSampleType( QAudioFormat::SignedInt );
|
||||||
|
#else
|
||||||
|
out.setSampleFormat( QAudioFormat::Int16 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioOutputPrivate: public QIODevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputPrivate()
|
||||||
|
{
|
||||||
|
open( QIODevice::ReadOnly );
|
||||||
|
threadPool.setMaxThreadCount( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture< void > audioPlayFuture;
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
using AudioOutput = QAudioOutput;
|
||||||
|
#else
|
||||||
|
using AudioOutput = QAudioSink;
|
||||||
|
#endif
|
||||||
|
AudioOutput * audioOutput = nullptr;
|
||||||
|
QByteArray buffer;
|
||||||
|
qint64 offset = 0;
|
||||||
|
bool quit = 0;
|
||||||
|
QMutex mutex;
|
||||||
|
QWaitCondition cond;
|
||||||
|
QThreadPool threadPool;
|
||||||
|
int sampleRate = 0;
|
||||||
|
int channels = 0;
|
||||||
|
|
||||||
|
void setAudioFormat( int _sampleRate, int _channels )
|
||||||
|
{
|
||||||
|
sampleRate = _sampleRate;
|
||||||
|
channels = _channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 readData( char * data, qint64 len ) override
|
||||||
|
{
|
||||||
|
if( !len )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QMutexLocker locker( &mutex );
|
||||||
|
qint64 bytesWritten = 0;
|
||||||
|
while( len && !quit )
|
||||||
|
{
|
||||||
|
if( buffer.isEmpty() )
|
||||||
|
{
|
||||||
|
// Wait for more frames
|
||||||
|
if( bytesWritten == 0 )
|
||||||
|
cond.wait( &mutex );
|
||||||
|
if( buffer.isEmpty() )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sampleData = buffer.data();
|
||||||
|
const int toWrite = qMin( (qint64) buffer.size(), len );
|
||||||
|
memcpy( &data[bytesWritten], sampleData, toWrite );
|
||||||
|
buffer.remove( 0, toWrite );
|
||||||
|
bytesWritten += toWrite;
|
||||||
|
// data += toWrite;
|
||||||
|
len -= toWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesWritten;
|
||||||
|
}
|
||||||
|
|
||||||
|
qint64 writeData( const char *, qint64 ) override { return 0; }
|
||||||
|
qint64 size() const override { return buffer.size(); }
|
||||||
|
qint64 bytesAvailable() const override { return buffer.size(); }
|
||||||
|
bool isSequential() const override { return true; }
|
||||||
|
bool atEnd() const override { return buffer.isEmpty(); }
|
||||||
|
|
||||||
|
void init( const QAudioFormat & fmt )
|
||||||
|
{
|
||||||
|
if( !audioOutput || ( fmt.isValid() && audioOutput->format() != fmt )
|
||||||
|
|| audioOutput->state() == QAudio::StoppedState )
|
||||||
|
{
|
||||||
|
if( audioOutput )
|
||||||
|
audioOutput->deleteLater();
|
||||||
|
audioOutput = new AudioOutput( fmt );
|
||||||
|
QObject::connect( audioOutput, &AudioOutput::stateChanged, audioOutput, [ & ]( QAudio::State state ) {
|
||||||
|
switch( state )
|
||||||
|
{
|
||||||
|
case QAudio::StoppedState:
|
||||||
|
if( audioOutput->error() != QAudio::NoError )
|
||||||
|
qWarning() << "QAudioOutput stopped:" << audioOutput->error();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
audioOutput->start( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
// audioOutput->setVolume(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void doPlayAudio()
|
||||||
|
{
|
||||||
|
while( !quit )
|
||||||
|
{
|
||||||
|
QMutexLocker locker( &mutex );
|
||||||
|
cond.wait( &mutex, 10 );
|
||||||
|
auto fmt = sampleRate == 0 ? QAudioFormat() : format( sampleRate, channels );
|
||||||
|
locker.unlock();
|
||||||
|
if( fmt.isValid() )
|
||||||
|
init( fmt );
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
if( audioOutput )
|
||||||
|
{
|
||||||
|
audioOutput->stop();
|
||||||
|
audioOutput->deleteLater();
|
||||||
|
}
|
||||||
|
audioOutput = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AudioOutput::AudioOutput( QObject * parent ): QObject( parent ), d_ptr( new AudioOutputPrivate )
|
||||||
|
{
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
|
||||||
|
d_ptr->audioPlayFuture = QtConcurrent::run( &d_ptr->threadPool, d_ptr.data(), &AudioOutputPrivate::doPlayAudio );
|
||||||
|
#else
|
||||||
|
d_ptr->audioPlayFuture = QtConcurrent::run( &d_ptr->threadPool, &AudioOutputPrivate::doPlayAudio, d_ptr.data() );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutput::setAudioFormat( int sampleRate, int channels ) { d_ptr->setAudioFormat( sampleRate, channels ); }
|
||||||
|
|
||||||
|
AudioOutput::~AudioOutput()
|
||||||
|
{
|
||||||
|
Q_D( AudioOutput );
|
||||||
|
d->quit = true;
|
||||||
|
d->cond.wakeAll();
|
||||||
|
d->audioPlayFuture.waitForFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioOutput::play( const uint8_t * data, qint64 len )
|
||||||
|
{
|
||||||
|
Q_D( AudioOutput );
|
||||||
|
if( d->quit )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
QMutexLocker locker( &d->mutex );
|
||||||
|
auto cuint = const_cast< uint8_t * >( data );
|
||||||
|
auto cptr = reinterpret_cast< char * >( cuint );
|
||||||
|
d->buffer.append( cptr, len );
|
||||||
|
d->cond.wakeAll();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
25
audiooutput.h
Normal file
25
audiooutput.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef AUDIOOUTPUT_H
|
||||||
|
#define AUDIOOUTPUT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QScopedPointer>
|
||||||
|
|
||||||
|
class AudioOutputPrivate;
|
||||||
|
class AudioOutput: public QObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutput( QObject * parent = nullptr );
|
||||||
|
~AudioOutput();
|
||||||
|
|
||||||
|
bool play( const uint8_t * data, qint64 len );
|
||||||
|
void setAudioFormat( int sampleRate, int channels );
|
||||||
|
protected:
|
||||||
|
QScopedPointer< AudioOutputPrivate > d_ptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY( AudioOutput )
|
||||||
|
Q_DECLARE_PRIVATE( AudioOutput )
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // AUDIOOUTPUT_H
|
|
@ -224,7 +224,7 @@ public:
|
||||||
private:
|
private:
|
||||||
#ifdef MAKE_FFMPEG_PLAYER
|
#ifdef MAKE_FFMPEG_PLAYER
|
||||||
static InternalPlayerBackend ffmpeg()
|
static InternalPlayerBackend ffmpeg()
|
||||||
{ return InternalPlayerBackend( "FFmpeg+libao" ); }
|
{ return InternalPlayerBackend( "FFmpeg" ); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef MAKE_QTMULTIMEDIA_PLAYER
|
#ifdef MAKE_QTMULTIMEDIA_PLAYER
|
||||||
|
|
195
ffmpegaudio.cc
195
ffmpegaudio.cc
|
@ -1,20 +1,11 @@
|
||||||
#ifdef MAKE_FFMPEG_PLAYER
|
#ifdef MAKE_FFMPEG_PLAYER
|
||||||
|
|
||||||
|
#include "audiooutput.h"
|
||||||
#include "ffmpegaudio.hh"
|
#include "ffmpegaudio.hh"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#ifndef INT64_C
|
|
||||||
#define INT64_C(c) (c ## LL)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef UINT64_C
|
|
||||||
#define UINT64_C(c) (c ## ULL)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <ao/ao.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
@ -27,7 +18,11 @@ extern "C" {
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#if( QT_VERSION >= QT_VERSION_CHECK( 6, 2, 0 ) )
|
||||||
|
#include <QMediaDevices>
|
||||||
|
|
||||||
|
#include <QAudioDevice>
|
||||||
|
#endif
|
||||||
#include "gddebug.hh"
|
#include "gddebug.hh"
|
||||||
#include "utils.hh"
|
#include "utils.hh"
|
||||||
|
|
||||||
|
@ -53,13 +48,13 @@ AudioService & AudioService::instance()
|
||||||
|
|
||||||
AudioService::AudioService()
|
AudioService::AudioService()
|
||||||
{
|
{
|
||||||
ao_initialize();
|
// ao_initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioService::~AudioService()
|
AudioService::~AudioService()
|
||||||
{
|
{
|
||||||
emit cancelPlaying( true );
|
emit cancelPlaying( true );
|
||||||
ao_shutdown();
|
// ao_shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioService::playMemory( const char * ptr, int size )
|
void AudioService::playMemory( const char * ptr, int size )
|
||||||
|
@ -100,7 +95,8 @@ struct DecoderContext
|
||||||
AVCodecContext * codecContext_;
|
AVCodecContext * codecContext_;
|
||||||
AVIOContext * avioContext_;
|
AVIOContext * avioContext_;
|
||||||
AVStream * audioStream_;
|
AVStream * audioStream_;
|
||||||
ao_device * aoDevice_;
|
// ao_device * aoDevice_;
|
||||||
|
AudioOutput * audioOutput;
|
||||||
bool avformatOpened_;
|
bool avformatOpened_;
|
||||||
|
|
||||||
SwrContext *swr_;
|
SwrContext *swr_;
|
||||||
|
@ -113,7 +109,7 @@ struct DecoderContext
|
||||||
bool openOutputDevice( QString & errorString );
|
bool openOutputDevice( QString & errorString );
|
||||||
void closeOutputDevice();
|
void closeOutputDevice();
|
||||||
bool play( QString & errorString );
|
bool play( QString & errorString );
|
||||||
bool normalizeAudio( AVFrame * frame, vector<char> & samples );
|
bool normalizeAudio( AVFrame * frame, vector<uint8_t> & samples );
|
||||||
void playFrame( AVFrame * frame );
|
void playFrame( AVFrame * frame );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +122,7 @@ DecoderContext::DecoderContext( QByteArray const & audioData, QAtomicInt & isCan
|
||||||
codecContext_( NULL ),
|
codecContext_( NULL ),
|
||||||
avioContext_( NULL ),
|
avioContext_( NULL ),
|
||||||
audioStream_( NULL ),
|
audioStream_( NULL ),
|
||||||
aoDevice_( NULL ),
|
audioOutput( new AudioOutput ),
|
||||||
avformatOpened_( false ),
|
avformatOpened_( false ),
|
||||||
swr_( NULL )
|
swr_( NULL )
|
||||||
{
|
{
|
||||||
|
@ -243,15 +239,15 @@ bool DecoderContext::openCodec( QString & errorString )
|
||||||
gdDebug( "Codec open: %s: channels: %d, rate: %d, format: %s\n", codec_->long_name,
|
gdDebug( "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 ||
|
// if ( codecContext_->sample_fmt == AV_SAMPLE_FMT_S32 ||
|
||||||
codecContext_->sample_fmt == AV_SAMPLE_FMT_S32P ||
|
// codecContext_->sample_fmt == AV_SAMPLE_FMT_S32P ||
|
||||||
codecContext_->sample_fmt == AV_SAMPLE_FMT_FLT ||
|
// codecContext_->sample_fmt == AV_SAMPLE_FMT_FLT ||
|
||||||
codecContext_->sample_fmt == AV_SAMPLE_FMT_FLTP ||
|
// codecContext_->sample_fmt == AV_SAMPLE_FMT_FLTP ||
|
||||||
codecContext_->sample_fmt == AV_SAMPLE_FMT_DBL ||
|
// codecContext_->sample_fmt == AV_SAMPLE_FMT_DBL ||
|
||||||
codecContext_->sample_fmt == AV_SAMPLE_FMT_DBLP )
|
// codecContext_->sample_fmt == AV_SAMPLE_FMT_DBLP )
|
||||||
{
|
{
|
||||||
swr_ = swr_alloc_set_opts( NULL,
|
swr_ = swr_alloc_set_opts( NULL,
|
||||||
codecContext_->channel_layout,
|
av_get_default_channel_layout(2),
|
||||||
AV_SAMPLE_FMT_S16,
|
AV_SAMPLE_FMT_S16,
|
||||||
codecContext_->sample_rate,
|
codecContext_->sample_rate,
|
||||||
codecContext_->channel_layout,
|
codecContext_->channel_layout,
|
||||||
|
@ -317,75 +313,25 @@ void DecoderContext::closeCodec()
|
||||||
|
|
||||||
bool DecoderContext::openOutputDevice( QString & errorString )
|
bool DecoderContext::openOutputDevice( QString & errorString )
|
||||||
{
|
{
|
||||||
// Prepare for audio output
|
// only check device when qt version is greater than 6.2
|
||||||
int aoDriverId = ao_default_driver_id();
|
#if (QT_VERSION >= QT_VERSION_CHECK(6,2,0))
|
||||||
ao_info * aoDrvInfo = ao_driver_info( aoDriverId );
|
QAudioDevice m_outputDevice = QMediaDevices::defaultAudioOutput();
|
||||||
|
if(m_outputDevice.isNull()){
|
||||||
if ( aoDriverId < 0 || !aoDrvInfo )
|
errorString += QObject::tr( "Can not found default audio output device" );
|
||||||
{
|
|
||||||
errorString = QObject::tr( "Cannot find usable audio output device." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ao_sample_format aoSampleFormat;
|
|
||||||
memset (&aoSampleFormat, 0, sizeof(aoSampleFormat) );
|
|
||||||
aoSampleFormat.channels = codecContext_->channels;
|
|
||||||
aoSampleFormat.rate = codecContext_->sample_rate;
|
|
||||||
aoSampleFormat.byte_format = AO_FMT_NATIVE;
|
|
||||||
aoSampleFormat.matrix = 0;
|
|
||||||
aoSampleFormat.bits = qMin( 16, av_get_bytes_per_sample( codecContext_->sample_fmt ) << 3 );
|
|
||||||
|
|
||||||
if ( aoSampleFormat.bits == 0 )
|
|
||||||
{
|
|
||||||
errorString = QObject::tr( "Unsupported sample format." );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gdDebug( "ao_open_live(): %s: channels: %d, rate: %d, bits: %d\n",
|
|
||||||
aoDrvInfo->name, aoSampleFormat.channels, aoSampleFormat.rate, aoSampleFormat.bits );
|
|
||||||
|
|
||||||
aoDevice_ = ao_open_live( aoDriverId, &aoSampleFormat, NULL );
|
|
||||||
if ( !aoDevice_ )
|
|
||||||
{
|
|
||||||
errorString = QObject::tr( "ao_open_live() failed: " );
|
|
||||||
|
|
||||||
switch ( errno )
|
|
||||||
{
|
|
||||||
case AO_ENODRIVER:
|
|
||||||
errorString += QObject::tr( "No driver." );
|
|
||||||
break;
|
|
||||||
case AO_ENOTLIVE:
|
|
||||||
errorString += QObject::tr( "This driver is not a live output device." );
|
|
||||||
break;
|
|
||||||
case AO_EBADOPTION:
|
|
||||||
errorString += QObject::tr( "A valid option key has an invalid value." );
|
|
||||||
break;
|
|
||||||
case AO_EOPENDEVICE:
|
|
||||||
errorString += QObject::tr( "Cannot open the device: %1, channels: %2, rate: %3, bits: %4." )
|
|
||||||
.arg( aoDrvInfo->short_name )
|
|
||||||
.arg( aoSampleFormat.channels )
|
|
||||||
.arg( aoSampleFormat.rate )
|
|
||||||
.arg( aoSampleFormat.bits );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
errorString += QObject::tr( "Unknown error." );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
audioOutput->setAudioFormat( codecContext_->sample_rate, codecContext_->channels );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecoderContext::closeOutputDevice()
|
void DecoderContext::closeOutputDevice()
|
||||||
{
|
{
|
||||||
// ao_close() is synchronous, it will wait until all audio streams flushed
|
// if(audioOutput){
|
||||||
if ( aoDevice_ )
|
// delete audioOutput;
|
||||||
{
|
// audioOutput = 0;
|
||||||
ao_close( aoDevice_ );
|
// }
|
||||||
aoDevice_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecoderContext::play( QString & errorString )
|
bool DecoderContext::play( QString & errorString )
|
||||||
|
@ -440,79 +386,17 @@ bool DecoderContext::play( QString & errorString )
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecoderContext::normalizeAudio( AVFrame * frame, vector<char> & samples )
|
bool DecoderContext::normalizeAudio( AVFrame * frame, vector<uint8_t > & samples )
|
||||||
{
|
{
|
||||||
int lineSize = 0;
|
int lineSize = 0;
|
||||||
int dataSize = av_samples_get_buffer_size( &lineSize, codecContext_->channels,
|
// int dataSize = av_samples_get_buffer_size( &lineSize, codecContext_->channels,
|
||||||
frame->nb_samples, codecContext_->sample_fmt, 1 );
|
// frame->nb_samples, codecContext_->sample_fmt, 1 );
|
||||||
|
int dataSize = frame->nb_samples * 2 * 2;
|
||||||
// Portions from: https://code.google.com/p/lavfilters/source/browse/decoder/LAVAudio/LAVAudio.cpp
|
|
||||||
// But this one use 8, 16, 32 bits integer, respectively.
|
|
||||||
switch ( codecContext_->sample_fmt )
|
|
||||||
{
|
|
||||||
case AV_SAMPLE_FMT_U8:
|
|
||||||
case AV_SAMPLE_FMT_S16:
|
|
||||||
{
|
|
||||||
samples.resize( dataSize );
|
|
||||||
memcpy( &samples.front(), frame->data[0], lineSize );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
// Planar
|
|
||||||
case AV_SAMPLE_FMT_U8P:
|
|
||||||
{
|
|
||||||
samples.resize( dataSize );
|
samples.resize( dataSize );
|
||||||
|
uint8_t *data[2] = { 0 };
|
||||||
|
data[0] = &samples.front(); //输出格式为AV_SAMPLE_FMT_S16(packet类型),所以转换后的LR两通道都存在data[0]中
|
||||||
|
|
||||||
uint8_t * out = ( uint8_t * )&samples.front();
|
swr_convert( swr_, data, frame->nb_samples, (const uint8_t**)frame->data, frame->nb_samples );
|
||||||
for ( int i = 0; i < frame->nb_samples; i++ )
|
|
||||||
{
|
|
||||||
for ( int ch = 0; ch < codecContext_->channels; ch++ )
|
|
||||||
{
|
|
||||||
*out++ = ( ( uint8_t * )frame->extended_data[ch] )[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AV_SAMPLE_FMT_S16P:
|
|
||||||
{
|
|
||||||
samples.resize( dataSize );
|
|
||||||
|
|
||||||
int16_t * out = ( int16_t * )&samples.front();
|
|
||||||
for ( int i = 0; i < frame->nb_samples; i++ )
|
|
||||||
{
|
|
||||||
for ( int ch = 0; ch < codecContext_->channels; ch++ )
|
|
||||||
{
|
|
||||||
*out++ = ( ( int16_t * )frame->extended_data[ch] )[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AV_SAMPLE_FMT_S32:
|
|
||||||
/* Pass through */
|
|
||||||
case AV_SAMPLE_FMT_S32P:
|
|
||||||
/* Pass through */
|
|
||||||
case AV_SAMPLE_FMT_FLT:
|
|
||||||
/* Pass through */
|
|
||||||
case AV_SAMPLE_FMT_FLTP:
|
|
||||||
/* Pass through */
|
|
||||||
{
|
|
||||||
samples.resize( dataSize / 2 );
|
|
||||||
|
|
||||||
uint8_t *out = ( uint8_t * )&samples.front();
|
|
||||||
swr_convert( swr_, &out, frame->nb_samples, (const uint8_t**)frame->extended_data, frame->nb_samples );
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -522,9 +406,12 @@ void DecoderContext::playFrame( AVFrame * frame )
|
||||||
if ( !frame )
|
if ( !frame )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
vector<char> samples;
|
vector<uint8_t> samples;
|
||||||
if ( normalizeAudio( frame, samples ) )
|
if ( normalizeAudio( frame, samples ) )
|
||||||
ao_play( aoDevice_, &samples.front(), samples.size() );
|
{
|
||||||
|
// ao_play( aoDevice_, &samples.front(), samples.size() );
|
||||||
|
audioOutput->play(&samples.front(), samples.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DecoderThread::DecoderThread( QByteArray const & audioData, QObject * parent ) :
|
DecoderThread::DecoderThread( QByteArray const & audioData, QObject * parent ) :
|
||||||
|
|
|
@ -263,6 +263,7 @@ HEADERS += folding.hh \
|
||||||
ankiconnector.h \
|
ankiconnector.h \
|
||||||
article_inspect.h \
|
article_inspect.h \
|
||||||
articlewebpage.h \
|
articlewebpage.h \
|
||||||
|
audiooutput.h \
|
||||||
base/globalregex.hh \
|
base/globalregex.hh \
|
||||||
globalbroadcaster.h \
|
globalbroadcaster.h \
|
||||||
headwordsmodel.h \
|
headwordsmodel.h \
|
||||||
|
@ -407,6 +408,7 @@ SOURCES += folding.cc \
|
||||||
ankiconnector.cpp \
|
ankiconnector.cpp \
|
||||||
article_inspect.cpp \
|
article_inspect.cpp \
|
||||||
articlewebpage.cpp \
|
articlewebpage.cpp \
|
||||||
|
audiooutput.cpp \
|
||||||
base/globalregex.cc \
|
base/globalregex.cc \
|
||||||
globalbroadcaster.cpp \
|
globalbroadcaster.cpp \
|
||||||
headwordsmodel.cpp \
|
headwordsmodel.cpp \
|
||||||
|
|
Loading…
Reference in a new issue