2016-07-05 20:44:02 +03:00
/*
This file is part of Telegram Desktop ,
the official desktop version of Telegram messaging app , see https : //telegram.org
Telegram Desktop is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
It is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2017-01-11 22:31:31 +04:00
Copyright ( c ) 2014 - 2017 John Preston , https : //desktop.telegram.org
2016-07-05 20:44:02 +03:00
*/
# include "media/media_audio_ffmpeg_loader.h"
2017-12-08 12:57:43 +04:00
namespace {
2016-07-05 20:44:02 +03:00
constexpr AVSampleFormat AudioToFormat = AV_SAMPLE_FMT_S16 ;
constexpr int64_t AudioToChannelLayout = AV_CH_LAYOUT_STEREO ;
constexpr int32 AudioToChannels = 2 ;
2018-01-02 19:18:53 +03:00
} // namespace
uint64_t AbstractFFMpegLoader : : ComputeChannelLayout (
uint64_t channel_layout ,
int channels ) {
if ( channel_layout ) {
if ( av_get_channel_layout_nb_channels ( channel_layout ) = = channels ) {
return channel_layout ;
}
}
return av_get_default_channel_layout ( channels ) ;
2017-12-08 12:57:43 +04:00
}
2018-01-02 19:18:53 +03:00
int64 AbstractFFMpegLoader : : Mul ( int64 value , AVRational rational ) {
return value * rational . num / rational . den ;
}
2017-12-08 12:57:43 +04:00
2017-12-10 12:52:38 +04:00
bool AbstractFFMpegLoader : : open ( TimeMs positionMs ) {
2016-07-05 20:44:02 +03:00
if ( ! AudioPlayerLoader : : openFile ( ) ) {
return false ;
}
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
ioBuffer = ( uchar * ) av_malloc ( AVBlockSize ) ;
2017-05-03 14:36:39 +03:00
if ( ! _data . isEmpty ( ) ) {
2016-07-05 20:44:02 +03:00
ioContext = avio_alloc_context ( ioBuffer , AVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_data , 0 , & AbstractFFMpegLoader : : _seek_data ) ;
2017-05-03 14:36:39 +03:00
} else if ( ! _bytes . empty ( ) ) {
ioContext = avio_alloc_context ( ioBuffer , AVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_bytes , 0 , & AbstractFFMpegLoader : : _seek_bytes ) ;
} else {
ioContext = avio_alloc_context ( ioBuffer , AVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_file , 0 , & AbstractFFMpegLoader : : _seek_file ) ;
2016-07-05 20:44:02 +03:00
}
fmtContext = avformat_alloc_context ( ) ;
if ( ! fmtContext ) {
2017-05-03 14:36:39 +03:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_alloc_context for file '%1', data size '%2' " ) . arg ( _file . name ( ) ) . arg ( _data . size ( ) ) ) ;
2016-07-05 20:44:02 +03:00
return false ;
}
fmtContext - > pb = ioContext ;
if ( ( res = avformat_open_input ( & fmtContext , 0 , 0 , 0 ) ) < 0 ) {
ioBuffer = 0 ;
2017-05-03 14:36:39 +03:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_open_input for file '%1', data size '%2', error %3, %4 " ) . arg ( _file . name ( ) ) . arg ( _data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2016-07-05 20:44:02 +03:00
return false ;
}
_opened = true ;
if ( ( res = avformat_find_stream_info ( fmtContext , 0 ) ) < 0 ) {
2017-05-03 14:36:39 +03:00
DEBUG_LOG ( ( " Audio Read Error: Unable to avformat_find_stream_info for file '%1', data size '%2', error %3, %4 " ) . arg ( _file . name ( ) ) . arg ( _data . size ( ) ) . arg ( res ) . arg ( av_make_error_string ( err , sizeof ( err ) , res ) ) ) ;
2016-07-05 20:44:02 +03:00
return false ;
}
streamId = av_find_best_stream ( fmtContext , AVMEDIA_TYPE_AUDIO , - 1 , - 1 , & codec , 0 ) ;
if ( streamId < 0 ) {
2017-05-03 14:36:39 +03:00
LOG ( ( " Audio Error: Unable to av_find_best_stream for file '%1', data size '%2', error %3, %4 " ) . arg ( _file . name ( ) ) . arg ( _data . size ( ) ) . arg ( streamId ) . arg ( av_make_error_string ( err , sizeof ( err ) , streamId ) ) ) ;
2016-07-05 20:44:02 +03:00
return false ;
}
2018-01-02 19:18:53 +03:00
const auto stream = fmtContext - > streams [ streamId ] ;
const auto params = stream - > codecpar ;
_samplesFrequency = params - > sample_rate ;
if ( stream - > duration ! = AV_NOPTS_VALUE ) {
_samplesCount = Mul (
stream - > duration * _samplesFrequency ,
stream - > time_base ) ;
2016-07-05 20:44:02 +03:00
} else {
2018-01-02 19:18:53 +03:00
_samplesCount = Mul (
fmtContext - > duration * _samplesFrequency ,
{ 1 , AV_TIME_BASE } ) ;
2016-07-05 20:44:02 +03:00
}
return true ;
}
AbstractFFMpegLoader : : ~ AbstractFFMpegLoader ( ) {
if ( _opened ) {
avformat_close_input ( & fmtContext ) ;
2016-07-13 20:34:57 +03:00
}
if ( ioContext ) {
2017-01-09 17:12:53 +04:00
av_freep ( & ioContext - > buffer ) ;
av_freep ( & ioContext ) ;
2016-07-05 20:44:02 +03:00
} else if ( ioBuffer ) {
2017-01-09 17:12:53 +04:00
av_freep ( & ioBuffer ) ;
2016-07-05 20:44:02 +03:00
}
if ( fmtContext ) avformat_free_context ( fmtContext ) ;
}
int AbstractFFMpegLoader : : _read_data ( void * opaque , uint8_t * buf , int buf_size ) {
2017-05-03 14:36:39 +03:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 20:44:02 +03:00
2017-05-03 14:36:39 +03:00
auto nbytes = qMin ( l - > _data . size ( ) - l - > _dataPos , int32 ( buf_size ) ) ;
2016-07-05 20:44:02 +03:00
if ( nbytes < = 0 ) {
return 0 ;
}
2017-05-03 14:36:39 +03:00
memcpy ( buf , l - > _data . constData ( ) + l - > _dataPos , nbytes ) ;
l - > _dataPos + = nbytes ;
2016-07-05 20:44:02 +03:00
return nbytes ;
}
int64_t AbstractFFMpegLoader : : _seek_data ( void * opaque , int64_t offset , int whence ) {
2017-05-03 14:36:39 +03:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
int32 newPos = - 1 ;
switch ( whence ) {
case SEEK_SET : newPos = offset ; break ;
case SEEK_CUR : newPos = l - > _dataPos + offset ; break ;
case SEEK_END : newPos = l - > _data . size ( ) + offset ; break ;
case AVSEEK_SIZE : {
// Special whence for determining filesize without any seek.
return l - > _data . size ( ) ;
} break ;
}
if ( newPos < 0 | | newPos > l - > _data . size ( ) ) {
return - 1 ;
}
l - > _dataPos = newPos ;
return l - > _dataPos ;
}
int AbstractFFMpegLoader : : _read_bytes ( void * opaque , uint8_t * buf , int buf_size ) {
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
auto nbytes = qMin ( static_cast < int > ( l - > _bytes . size ( ) ) - l - > _dataPos , buf_size ) ;
if ( nbytes < = 0 ) {
return 0 ;
}
memcpy ( buf , l - > _bytes . data ( ) + l - > _dataPos , nbytes ) ;
l - > _dataPos + = nbytes ;
return nbytes ;
}
int64_t AbstractFFMpegLoader : : _seek_bytes ( void * opaque , int64_t offset , int whence ) {
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 20:44:02 +03:00
int32 newPos = - 1 ;
switch ( whence ) {
case SEEK_SET : newPos = offset ; break ;
2017-05-03 14:36:39 +03:00
case SEEK_CUR : newPos = l - > _dataPos + offset ; break ;
case SEEK_END : newPos = static_cast < int > ( l - > _bytes . size ( ) ) + offset ; break ;
2017-03-01 10:31:36 +03:00
case AVSEEK_SIZE : {
// Special whence for determining filesize without any seek.
2017-05-03 14:36:39 +03:00
return l - > _bytes . size ( ) ;
2017-03-01 10:31:36 +03:00
} break ;
2016-07-05 20:44:02 +03:00
}
2017-05-03 14:36:39 +03:00
if ( newPos < 0 | | newPos > l - > _bytes . size ( ) ) {
2016-07-05 20:44:02 +03:00
return - 1 ;
}
2017-05-03 14:36:39 +03:00
l - > _dataPos = newPos ;
return l - > _dataPos ;
2016-07-05 20:44:02 +03:00
}
int AbstractFFMpegLoader : : _read_file ( void * opaque , uint8_t * buf , int buf_size ) {
2017-05-03 14:36:39 +03:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
return int ( l - > _f . read ( ( char * ) ( buf ) , buf_size ) ) ;
2016-07-05 20:44:02 +03:00
}
int64_t AbstractFFMpegLoader : : _seek_file ( void * opaque , int64_t offset , int whence ) {
2017-05-03 14:36:39 +03:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 20:44:02 +03:00
switch ( whence ) {
2017-05-03 14:36:39 +03:00
case SEEK_SET : return l - > _f . seek ( offset ) ? l - > _f . pos ( ) : - 1 ;
case SEEK_CUR : return l - > _f . seek ( l - > _f . pos ( ) + offset ) ? l - > _f . pos ( ) : - 1 ;
case SEEK_END : return l - > _f . seek ( l - > _f . size ( ) + offset ) ? l - > _f . pos ( ) : - 1 ;
2017-03-01 10:31:36 +03:00
case AVSEEK_SIZE : {
// Special whence for determining filesize without any seek.
2017-05-03 14:36:39 +03:00
return l - > _f . size ( ) ;
2017-03-01 10:31:36 +03:00
} break ;
2016-07-05 20:44:02 +03:00
}
return - 1 ;
}
2018-01-02 19:18:53 +03:00
FFMpegLoader : : FFMpegLoader (
const FileLocation & file ,
const QByteArray & data ,
base : : byte_vector & & bytes )
: AbstractFFMpegLoader ( file , data , std : : move ( bytes ) ) {
_frame = av_frame_alloc ( ) ;
2016-07-05 20:44:02 +03:00
}
2017-12-10 12:52:38 +04:00
bool FFMpegLoader : : open ( TimeMs positionMs ) {
if ( ! AbstractFFMpegLoader : : open ( positionMs ) ) {
2016-07-05 20:44:02 +03:00
return false ;
}
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2018-01-02 19:18:53 +03:00
_codecContext = avcodec_alloc_context3 ( nullptr ) ;
if ( ! _codecContext ) {
LOG ( ( " Audio Error: "
" Unable to avcodec_alloc_context3 for file '%1', data size '%2' "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) ) ;
2016-07-22 18:01:24 +03:00
return false ;
}
2018-01-02 19:18:53 +03:00
const auto stream = fmtContext - > streams [ streamId ] ;
if ( ( res = avcodec_parameters_to_context (
_codecContext ,
stream - > codecpar ) ) < 0 ) {
LOG ( ( " Audio Error: "
" Unable to avcodec_parameters_to_context for file '%1', "
" data size '%2', error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
2016-07-22 18:01:24 +03:00
return false ;
}
2018-01-02 19:18:53 +03:00
av_codec_set_pkt_timebase ( _codecContext , stream - > time_base ) ;
av_opt_set_int ( _codecContext , " refcounted_frames " , 1 , 0 ) ;
if ( ( res = avcodec_open2 ( _codecContext , codec , 0 ) ) < 0 ) {
LOG ( ( " Audio Error: "
" Unable to avcodec_open2 for file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
2016-07-05 20:44:02 +03:00
return false ;
}
2018-01-02 19:18:53 +03:00
const auto layout = ComputeChannelLayout (
_codecContext - > channel_layout ,
_codecContext - > channels ) ;
2017-04-30 14:34:23 +03:00
if ( ! layout ) {
2018-01-02 19:18:53 +03:00
LOG ( ( " Audio Error: Unknown channel layout %1 for %2 channels. "
) . arg ( _codecContext - > channel_layout
) . arg ( _codecContext - > channels
) ) ;
return false ;
2017-04-30 14:34:23 +03:00
}
2018-01-02 19:18:53 +03:00
_swrSrcFormat = _codecContext - > sample_fmt ;
2016-07-05 20:44:02 +03:00
switch ( layout ) {
case AV_CH_LAYOUT_MONO :
2018-01-02 19:18:53 +03:00
switch ( _swrSrcFormat ) {
2016-07-05 20:44:02 +03:00
case AV_SAMPLE_FMT_U8 :
2018-01-02 19:18:53 +03:00
case AV_SAMPLE_FMT_U8P :
_swrDstFormat = _swrSrcFormat ;
_swrDstChannelLayout = layout ;
_swrDstChannels = 1 ;
_format = AL_FORMAT_MONO8 ;
sampleSize = 1 ;
break ;
2016-07-05 20:44:02 +03:00
case AV_SAMPLE_FMT_S16 :
2018-01-02 19:18:53 +03:00
case AV_SAMPLE_FMT_S16P :
_swrDstFormat = _swrSrcFormat ;
_swrDstChannelLayout = layout ;
_swrDstChannels = 1 ;
_format = AL_FORMAT_MONO16 ;
sampleSize = sizeof ( uint16 ) ;
break ;
2016-07-05 20:44:02 +03:00
}
break ;
2018-01-02 19:18:53 +03:00
case AV_CH_LAYOUT_STEREO :
switch ( _swrSrcFormat ) {
case AV_SAMPLE_FMT_U8 :
_swrDstFormat = _swrSrcFormat ;
_swrDstChannelLayout = layout ;
_swrDstChannels = 2 ;
_format = AL_FORMAT_STEREO8 ;
sampleSize = 2 ;
break ;
case AV_SAMPLE_FMT_S16 :
_swrDstFormat = _swrSrcFormat ;
_swrDstChannelLayout = layout ;
_swrDstChannels = 2 ;
_format = AL_FORMAT_STEREO16 ;
sampleSize = 2 * sizeof ( uint16 ) ;
break ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
break ;
2017-12-02 12:58:50 +04:00
}
2018-01-02 19:18:53 +03:00
if ( _swrDstRate = = _samplesFrequency ) {
_swrDstSamplesCount = _samplesCount ;
} else {
_swrDstSamplesCount = av_rescale_rnd (
_samplesCount ,
_swrDstRate ,
_samplesFrequency ,
AV_ROUND_UP ) ;
2016-07-05 20:44:02 +03:00
}
2017-12-10 12:52:38 +04:00
if ( positionMs ) {
2018-01-02 19:18:53 +03:00
const auto timeBase = stream - > time_base ;
2017-12-10 12:52:38 +04:00
const auto timeStamp = ( positionMs * timeBase . den )
/ ( 1000LL * timeBase . num ) ;
2018-01-02 19:18:53 +03:00
const auto flags1 = AVSEEK_FLAG_ANY ;
if ( av_seek_frame ( fmtContext , streamId , timeStamp , flags1 ) < 0 ) {
const auto flags2 = 0 ;
if ( av_seek_frame ( fmtContext , streamId , timeStamp , flags2 ) < 0 ) {
2016-07-05 20:44:02 +03:00
}
}
}
return true ;
}
2018-01-02 19:18:53 +03:00
AudioPlayerLoader : : ReadResult FFMpegLoader : : readMore (
QByteArray & result ,
int64 & samplesAdded ) {
2016-07-05 20:44:02 +03:00
int res ;
2016-07-22 18:01:24 +03:00
2018-01-02 19:18:53 +03:00
av_frame_unref ( _frame ) ;
res = avcodec_receive_frame ( _codecContext , _frame ) ;
2016-07-22 18:01:24 +03:00
if ( res > = 0 ) {
return readFromReadyFrame ( result , samplesAdded ) ;
}
if ( res = = AVERROR_EOF ) {
return ReadResult : : EndOfFile ;
} else if ( res ! = AVERROR ( EAGAIN ) ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2018-01-02 19:18:53 +03:00
LOG ( ( " Audio Error: "
" Unable to avcodec_receive_frame() file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
2016-07-22 18:01:24 +03:00
return ReadResult : : Error ;
}
2018-01-02 19:18:53 +03:00
if ( ( res = av_read_frame ( fmtContext , & _packet ) ) < 0 ) {
2016-07-05 20:44:02 +03:00
if ( res ! = AVERROR_EOF ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2018-01-02 19:18:53 +03:00
LOG ( ( " Audio Error: "
" Unable to av_read_frame() file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
2016-07-05 20:44:22 +03:00
return ReadResult : : Error ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
avcodec_send_packet ( _codecContext , nullptr ) ; // drain
2016-07-22 18:01:24 +03:00
return ReadResult : : Ok ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
if ( _packet . stream_index = = streamId ) {
res = avcodec_send_packet ( _codecContext , & _packet ) ;
2016-07-22 18:01:24 +03:00
if ( res < 0 ) {
2018-01-02 19:18:53 +03:00
av_packet_unref ( & _packet ) ;
2016-07-22 18:01:24 +03:00
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2018-01-02 19:18:53 +03:00
LOG ( ( " Audio Error: "
" Unable to avcodec_send_packet() file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
2016-08-14 21:57:23 +03:00
// There is a sample voice message where skipping such packet
// results in a crash (read_access to nullptr) in swr_convert().
//if (res == AVERROR_INVALIDDATA) {
// return ReadResult::NotYet; // try to skip bad packet
//}
2016-07-05 20:44:02 +03:00
return ReadResult : : Error ;
}
2016-07-22 18:01:24 +03:00
}
2018-01-02 19:18:53 +03:00
av_packet_unref ( & _packet ) ;
2016-07-22 18:01:24 +03:00
return ReadResult : : Ok ;
}
2016-07-05 20:44:02 +03:00
2018-01-02 19:18:53 +03:00
bool FFMpegLoader : : frameHasDesiredFormat ( ) const {
const auto frameChannelLayout = ComputeChannelLayout (
_frame - > channel_layout ,
_frame - > channels ) ;
return true
& & ( _frame - > format = = _swrDstFormat )
& & ( frameChannelLayout = = _swrDstChannelLayout )
& & ( _frame - > sample_rate = = _swrDstRate ) ;
}
2016-07-22 18:01:24 +03:00
2018-01-02 19:18:53 +03:00
bool FFMpegLoader : : initResampleForFrame ( ) {
const auto frameChannelLayout = ComputeChannelLayout (
_frame - > channel_layout ,
_frame - > channels ) ;
if ( ! frameChannelLayout ) {
LOG ( ( " Audio Error: "
" Unable to compute channel layout for frame in file '%1', "
" data size '%2', channel_layout %3, channels %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( _frame - > channel_layout
) . arg ( _frame - > channels
) ) ;
return false ;
} else if ( _frame - > format = = - 1 ) {
LOG ( ( " Audio Error: "
" Unknown frame format in file '%1', data size '%2' "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) ) ;
return false ;
} else if ( _swrContext ) {
if ( true
& & ( _frame - > format = = _swrSrcFormat )
& & ( frameChannelLayout = = _swrSrcChannelLayout )
& & ( _frame - > sample_rate = = _swrSrcRate ) ) {
return true ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
swr_close ( _swrContext ) ;
}
2017-12-02 12:58:50 +04:00
2018-01-02 19:18:53 +03:00
_swrSrcFormat = static_cast < AVSampleFormat > ( _frame - > format ) ;
_swrSrcChannelLayout = frameChannelLayout ;
_swrSrcRate = _frame - > sample_rate ;
return initResampleUsingFormat ( ) ;
}
2017-12-02 12:58:50 +04:00
2018-01-02 19:18:53 +03:00
bool FFMpegLoader : : initResampleUsingFormat ( ) {
int res = 0 ;
2017-12-08 12:57:43 +04:00
2018-01-02 19:18:53 +03:00
_swrContext = swr_alloc_set_opts (
_swrContext ,
_swrDstChannelLayout ,
_swrDstFormat ,
_swrDstRate ,
_swrSrcChannelLayout ,
_swrSrcFormat ,
_swrSrcRate ,
0 ,
nullptr ) ;
if ( ! _swrContext ) {
LOG ( ( " Audio Error: "
" Unable to swr_alloc for file '%1', data size '%2' "
) . arg ( _file . name ( )
) . arg ( _data . size ( ) ) ) ;
return false ;
} else if ( ( res = swr_init ( _swrContext ) ) < 0 ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
LOG ( ( " Audio Error: "
" Unable to swr_init for file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
return false ;
}
if ( _swrDstData ) {
av_freep ( & _swrDstData [ 0 ] ) ;
_swrDstDataCapacity = - 1 ;
}
return true ;
}
2017-12-08 12:57:43 +04:00
2018-01-02 19:18:53 +03:00
bool FFMpegLoader : : ensureResampleSpaceAvailable ( int samples ) {
if ( _swrDstData ! = nullptr & & _swrDstDataCapacity > = samples ) {
return true ;
}
const auto allocate = std : : max ( samples , int ( av_rescale_rnd (
AVBlockSize / sampleSize ,
_swrDstRate ,
_swrSrcRate ,
AV_ROUND_UP ) ) ) ;
if ( _swrDstData ) {
av_freep ( & _swrDstData [ 0 ] ) ;
}
const auto res = _swrDstData
? av_samples_alloc (
_swrDstData ,
nullptr ,
_swrDstChannels ,
allocate ,
_swrDstFormat ,
0 )
: av_samples_alloc_array_and_samples (
& _swrDstData ,
nullptr ,
_swrDstChannels ,
allocate ,
_swrDstFormat ,
0 ) ;
if ( res < 0 ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
LOG ( ( " Audio Error: "
" Unable to av_samples_alloc for file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( res
) . arg ( av_make_error_string ( err , sizeof ( err ) , res )
) ) ;
return false ;
}
_swrDstDataCapacity = allocate ;
return true ;
}
AudioPlayerLoader : : ReadResult FFMpegLoader : : readFromReadyFrame (
QByteArray & result ,
int64 & samplesAdded ) {
if ( frameHasDesiredFormat ( ) ) {
result . append (
reinterpret_cast < const char * > ( _frame - > extended_data [ 0 ] ) ,
_frame - > nb_samples * sampleSize ) ;
samplesAdded + = _frame - > nb_samples ;
} else if ( ! initResampleForFrame ( ) ) {
return ReadResult : : Error ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
const auto maxSamples = av_rescale_rnd (
swr_get_delay ( _swrContext , _swrSrcRate ) + _frame - > nb_samples ,
_swrDstRate ,
_swrSrcRate ,
AV_ROUND_UP ) ;
if ( ! ensureResampleSpaceAvailable ( maxSamples ) ) {
return ReadResult : : Error ;
}
const auto samples = swr_convert (
_swrContext ,
_swrDstData ,
maxSamples ,
( const uint8_t * * ) _frame - > extended_data ,
_frame - > nb_samples ) ;
if ( samples < 0 ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
LOG ( ( " Audio Error: "
" Unable to swr_convert for file '%1', data size '%2', "
" error %3, %4 "
) . arg ( _file . name ( )
) . arg ( _data . size ( )
) . arg ( samples
) . arg ( av_make_error_string ( err , sizeof ( err ) , samples )
) ) ;
return ReadResult : : Error ;
}
const auto bytesCount = av_samples_get_buffer_size (
nullptr ,
_swrDstChannels ,
samples ,
_swrDstFormat ,
1 ) ;
result . append (
reinterpret_cast < const char * > ( _swrDstData [ 0 ] ) ,
bytesCount ) ;
samplesAdded + = bytesCount / sampleSize ;
2016-07-05 20:44:02 +03:00
return ReadResult : : Ok ;
}
FFMpegLoader : : ~ FFMpegLoader ( ) {
2018-01-02 19:18:53 +03:00
if ( _codecContext ) {
avcodec_free_context ( & _codecContext ) ;
}
if ( _swrContext ) {
swr_free ( & _swrContext ) ;
}
if ( _swrDstData ) {
if ( _swrDstData [ 0 ] ) {
av_freep ( & _swrDstData [ 0 ] ) ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
av_freep ( & _swrDstData ) ;
2016-07-05 20:44:02 +03:00
}
2018-01-02 19:18:53 +03:00
av_frame_free ( & _frame ) ;
2016-07-05 20:44:02 +03:00
}