2016-07-05 17:44:02 +00:00
/*
This file is part of Telegram Desktop ,
2018-01-03 10:23:14 +00:00
the official desktop application for the Telegram messaging service .
2016-07-05 17:44:02 +00:00
2018-01-03 10:23:14 +00:00
For license and copyright information please follow this link :
https : //github.com/telegramdesktop/tdesktop/blob/master/LEGAL
2016-07-05 17:44:02 +00:00
*/
2019-02-13 12:36:59 +00:00
# include "media/audio/media_audio_ffmpeg_loader.h"
2016-07-05 17:44:02 +00:00
2020-10-13 16:43:18 +00:00
# include "core/file_location.h"
# include "ffmpeg/ffmpeg_utility.h"
2018-03-27 12:16:00 +00:00
# include "base/bytes.h"
2019-02-28 21:03:25 +00:00
namespace Media {
2018-01-02 16:18:53 +00:00
uint64_t AbstractFFMpegLoader : : ComputeChannelLayout (
2019-02-28 21:03:25 +00:00
uint64_t channel_layout ,
int channels ) {
2018-01-02 16:18:53 +00:00
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 08:57:43 +00:00
}
2018-01-02 16:18:53 +00:00
int64 AbstractFFMpegLoader : : Mul ( int64 value , AVRational rational ) {
return value * rational . num / rational . den ;
}
2017-12-08 08:57:43 +00:00
2019-02-19 06:57:53 +00:00
bool AbstractFFMpegLoader : : open ( crl : : time positionMs ) {
2016-07-05 17:44:02 +00:00
if ( ! AudioPlayerLoader : : openFile ( ) ) {
return false ;
}
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
2020-10-13 16:43:18 +00:00
ioBuffer = ( uchar * ) av_malloc ( FFmpeg : : kAVBlockSize ) ;
2017-05-03 11:36:39 +00:00
if ( ! _data . isEmpty ( ) ) {
2020-10-13 16:43:18 +00:00
ioContext = avio_alloc_context ( ioBuffer , FFmpeg : : kAVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_data , 0 , & AbstractFFMpegLoader : : _seek_data ) ;
2017-05-03 11:36:39 +00:00
} else if ( ! _bytes . empty ( ) ) {
2020-10-13 16:43:18 +00:00
ioContext = avio_alloc_context ( ioBuffer , FFmpeg : : kAVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_bytes , 0 , & AbstractFFMpegLoader : : _seek_bytes ) ;
2017-05-03 11:36:39 +00:00
} else {
2020-10-13 16:43:18 +00:00
ioContext = avio_alloc_context ( ioBuffer , FFmpeg : : kAVBlockSize , 0 , reinterpret_cast < void * > ( this ) , & AbstractFFMpegLoader : : _read_file , 0 , & AbstractFFMpegLoader : : _seek_file ) ;
2016-07-05 17:44:02 +00:00
}
fmtContext = avformat_alloc_context ( ) ;
if ( ! fmtContext ) {
2017-05-03 11:36:39 +00: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 17:44:02 +00:00
return false ;
}
fmtContext - > pb = ioContext ;
if ( ( res = avformat_open_input ( & fmtContext , 0 , 0 , 0 ) ) < 0 ) {
ioBuffer = 0 ;
2017-05-03 11:36:39 +00: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 17:44:02 +00:00
return false ;
}
_opened = true ;
if ( ( res = avformat_find_stream_info ( fmtContext , 0 ) ) < 0 ) {
2017-05-03 11:36:39 +00: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 17:44:02 +00:00
return false ;
}
streamId = av_find_best_stream ( fmtContext , AVMEDIA_TYPE_AUDIO , - 1 , - 1 , & codec , 0 ) ;
if ( streamId < 0 ) {
2017-05-03 11:36:39 +00: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 17:44:02 +00:00
return false ;
}
2018-01-02 16:18:53 +00: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 17:44:02 +00:00
} else {
2018-01-02 16:18:53 +00:00
_samplesCount = Mul (
fmtContext - > duration * _samplesFrequency ,
{ 1 , AV_TIME_BASE } ) ;
2016-07-05 17:44:02 +00:00
}
return true ;
}
AbstractFFMpegLoader : : ~ AbstractFFMpegLoader ( ) {
if ( _opened ) {
avformat_close_input ( & fmtContext ) ;
2016-07-13 17:34:57 +00:00
}
if ( ioContext ) {
2017-01-09 13:12:53 +00:00
av_freep ( & ioContext - > buffer ) ;
av_freep ( & ioContext ) ;
2016-07-05 17:44:02 +00:00
} else if ( ioBuffer ) {
2017-01-09 13:12:53 +00:00
av_freep ( & ioBuffer ) ;
2016-07-05 17:44:02 +00:00
}
if ( fmtContext ) avformat_free_context ( fmtContext ) ;
}
int AbstractFFMpegLoader : : _read_data ( void * opaque , uint8_t * buf , int buf_size ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 17:44:02 +00:00
2017-05-03 11:36:39 +00:00
auto nbytes = qMin ( l - > _data . size ( ) - l - > _dataPos , int32 ( buf_size ) ) ;
2016-07-05 17:44:02 +00:00
if ( nbytes < = 0 ) {
return 0 ;
}
2017-05-03 11:36:39 +00:00
memcpy ( buf , l - > _data . constData ( ) + l - > _dataPos , nbytes ) ;
l - > _dataPos + = nbytes ;
2016-07-05 17:44:02 +00:00
return nbytes ;
}
int64_t AbstractFFMpegLoader : : _seek_data ( void * opaque , int64_t offset , int whence ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2017-05-03 11:36:39 +00:00
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 ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2017-05-03 11:36:39 +00:00
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 ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 17:44:02 +00:00
int32 newPos = - 1 ;
switch ( whence ) {
case SEEK_SET : newPos = offset ; break ;
2017-05-03 11:36:39 +00:00
case SEEK_CUR : newPos = l - > _dataPos + offset ; break ;
case SEEK_END : newPos = static_cast < int > ( l - > _bytes . size ( ) ) + offset ; break ;
2019-02-28 21:03:25 +00:00
case AVSEEK_SIZE :
{
2017-03-01 07:31:36 +00:00
// Special whence for determining filesize without any seek.
2017-05-03 11:36:39 +00:00
return l - > _bytes . size ( ) ;
2017-03-01 07:31:36 +00:00
} break ;
2016-07-05 17:44:02 +00:00
}
2017-05-03 11:36:39 +00:00
if ( newPos < 0 | | newPos > l - > _bytes . size ( ) ) {
2016-07-05 17:44:02 +00:00
return - 1 ;
}
2017-05-03 11:36:39 +00:00
l - > _dataPos = newPos ;
return l - > _dataPos ;
2016-07-05 17:44:02 +00:00
}
int AbstractFFMpegLoader : : _read_file ( void * opaque , uint8_t * buf , int buf_size ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
return int ( l - > _f . read ( ( char * ) ( buf ) , buf_size ) ) ;
2016-07-05 17:44:02 +00:00
}
int64_t AbstractFFMpegLoader : : _seek_file ( void * opaque , int64_t offset , int whence ) {
2019-02-28 21:03:25 +00:00
auto l = reinterpret_cast < AbstractFFMpegLoader * > ( opaque ) ;
2016-07-05 17:44:02 +00:00
switch ( whence ) {
2017-05-03 11:36:39 +00: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 ;
2019-02-28 21:03:25 +00:00
case AVSEEK_SIZE :
{
2017-03-01 07:31:36 +00:00
// Special whence for determining filesize without any seek.
2017-05-03 11:36:39 +00:00
return l - > _f . size ( ) ;
2017-03-01 07:31:36 +00:00
} break ;
2016-07-05 17:44:02 +00:00
}
return - 1 ;
}
2018-01-02 17:22:13 +00:00
AbstractAudioFFMpegLoader : : AbstractAudioFFMpegLoader (
2020-10-13 16:43:18 +00:00
const Core : : FileLocation & file ,
2018-01-02 16:18:53 +00:00
const QByteArray & data ,
2018-03-27 12:16:00 +00:00
bytes : : vector & & buffer )
2019-02-28 21:03:25 +00:00
: AbstractFFMpegLoader ( file , data , std : : move ( buffer ) )
2019-06-26 15:04:38 +00:00
, _frame ( FFmpeg : : MakeFramePointer ( ) ) {
2016-07-05 17:44:02 +00:00
}
2018-01-02 17:22:13 +00:00
bool AbstractAudioFFMpegLoader : : initUsingContext (
2019-06-26 15:04:38 +00:00
not_null < AVCodecContext * > context ,
int64 initialCount ,
int initialFrequency ) {
2018-01-02 16:18:53 +00:00
const auto layout = ComputeChannelLayout (
2018-01-02 17:22:13 +00:00
context - > channel_layout ,
context - > channels ) ;
2017-04-30 11:34:23 +00:00
if ( ! layout ) {
2018-01-02 16:18:53 +00:00
LOG ( ( " Audio Error: Unknown channel layout %1 for %2 channels. "
2018-01-02 17:22:13 +00:00
) . arg ( context - > channel_layout
) . arg ( context - > channels
2018-01-02 16:18:53 +00:00
) ) ;
return false ;
2017-04-30 11:34:23 +00:00
}
2018-01-02 16:18:53 +00:00
2018-01-02 17:22:13 +00:00
_swrSrcSampleFormat = context - > sample_fmt ;
2016-07-05 17:44:02 +00:00
switch ( layout ) {
case AV_CH_LAYOUT_MONO :
2018-01-02 17:22:13 +00:00
switch ( _swrSrcSampleFormat ) {
2016-07-05 17:44:02 +00:00
case AV_SAMPLE_FMT_U8 :
2018-01-02 16:18:53 +00:00
case AV_SAMPLE_FMT_U8P :
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat = _swrSrcSampleFormat ;
2018-01-02 16:18:53 +00:00
_swrDstChannelLayout = layout ;
2018-01-02 17:22:13 +00:00
_outputChannels = 1 ;
_outputSampleSize = 1 ;
_outputFormat = AL_FORMAT_MONO8 ;
2018-01-02 16:18:53 +00:00
break ;
2016-07-05 17:44:02 +00:00
case AV_SAMPLE_FMT_S16 :
2018-01-02 16:18:53 +00:00
case AV_SAMPLE_FMT_S16P :
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat = _swrSrcSampleFormat ;
2018-01-02 16:18:53 +00:00
_swrDstChannelLayout = layout ;
2018-01-02 17:22:13 +00:00
_outputChannels = 1 ;
_outputSampleSize = sizeof ( uint16 ) ;
_outputFormat = AL_FORMAT_MONO16 ;
2018-01-02 16:18:53 +00:00
break ;
2016-07-05 17:44:02 +00:00
}
break ;
2018-01-02 16:18:53 +00:00
case AV_CH_LAYOUT_STEREO :
2018-01-02 17:22:13 +00:00
switch ( _swrSrcSampleFormat ) {
2018-01-02 16:18:53 +00:00
case AV_SAMPLE_FMT_U8 :
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat = _swrSrcSampleFormat ;
2018-01-02 16:18:53 +00:00
_swrDstChannelLayout = layout ;
2018-01-02 17:22:13 +00:00
_outputChannels = 2 ;
_outputSampleSize = 2 ;
_outputFormat = AL_FORMAT_STEREO8 ;
2018-01-02 16:18:53 +00:00
break ;
case AV_SAMPLE_FMT_S16 :
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat = _swrSrcSampleFormat ;
2018-01-02 16:18:53 +00:00
_swrDstChannelLayout = layout ;
2018-01-02 17:22:13 +00:00
_outputChannels = 2 ;
_outputSampleSize = 2 * sizeof ( uint16 ) ;
_outputFormat = AL_FORMAT_STEREO16 ;
2018-01-02 16:18:53 +00:00
break ;
2016-07-05 17:44:02 +00:00
}
2018-01-02 16:18:53 +00:00
break ;
2017-12-02 08:58:50 +00:00
}
2018-01-02 17:22:13 +00:00
if ( _swrDstRate = = initialFrequency ) {
_outputSamplesCount = initialCount ;
2018-01-02 16:18:53 +00:00
} else {
2018-01-02 17:22:13 +00:00
_outputSamplesCount = av_rescale_rnd (
initialCount ,
2018-01-02 16:18:53 +00:00
_swrDstRate ,
2018-01-02 17:22:13 +00:00
initialFrequency ,
2018-01-02 16:18:53 +00:00
AV_ROUND_UP ) ;
2016-07-05 17:44:02 +00:00
}
return true ;
}
2019-02-17 11:08:29 +00:00
auto AbstractAudioFFMpegLoader : : replaceFrameAndRead (
2019-06-26 15:04:38 +00:00
FFmpeg : : FramePointer frame ,
2019-02-17 11:08:29 +00:00
QByteArray & result ,
int64 & samplesAdded )
- > ReadResult {
2019-02-28 21:03:25 +00:00
_frame = std : : move ( frame ) ;
2019-02-17 11:08:29 +00:00
return readFromReadyFrame ( result , samplesAdded ) ;
}
2018-01-02 17:22:13 +00:00
auto AbstractAudioFFMpegLoader : : readFromReadyContext (
2019-02-28 21:03:25 +00:00
not_null < AVCodecContext * > context ,
2018-01-02 17:22:13 +00:00
QByteArray & result ,
int64 & samplesAdded )
- > ReadResult {
2019-02-28 21:03:25 +00:00
const auto res = avcodec_receive_frame ( context , _frame . get ( ) ) ;
2016-07-22 15:01:24 +00: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 16:18:53 +00: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 15:01:24 +00:00
return ReadResult : : Error ;
}
2018-01-02 17:22:13 +00:00
return ReadResult : : Wait ;
2016-07-22 15:01:24 +00:00
}
2016-07-05 17:44:02 +00:00
2018-01-02 17:22:13 +00:00
bool AbstractAudioFFMpegLoader : : frameHasDesiredFormat ( ) const {
2018-01-02 16:18:53 +00:00
const auto frameChannelLayout = ComputeChannelLayout (
_frame - > channel_layout ,
_frame - > channels ) ;
return true
2018-01-02 17:22:13 +00:00
& & ( _frame - > format = = _swrDstSampleFormat )
2018-01-02 16:18:53 +00:00
& & ( frameChannelLayout = = _swrDstChannelLayout )
& & ( _frame - > sample_rate = = _swrDstRate ) ;
}
2016-07-22 15:01:24 +00:00
2018-01-02 17:22:13 +00:00
bool AbstractAudioFFMpegLoader : : initResampleForFrame ( ) {
2018-01-02 16:18:53 +00:00
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
2018-01-02 17:22:13 +00:00
& & ( _frame - > format = = _swrSrcSampleFormat )
2018-01-02 16:18:53 +00:00
& & ( frameChannelLayout = = _swrSrcChannelLayout )
& & ( _frame - > sample_rate = = _swrSrcRate ) ) {
return true ;
2016-07-05 17:44:02 +00:00
}
2018-01-02 16:18:53 +00:00
swr_close ( _swrContext ) ;
}
2017-12-02 08:58:50 +00:00
2018-01-02 17:22:13 +00:00
_swrSrcSampleFormat = static_cast < AVSampleFormat > ( _frame - > format ) ;
2018-01-02 16:18:53 +00:00
_swrSrcChannelLayout = frameChannelLayout ;
_swrSrcRate = _frame - > sample_rate ;
return initResampleUsingFormat ( ) ;
}
2017-12-02 08:58:50 +00:00
2018-01-02 17:22:13 +00:00
bool AbstractAudioFFMpegLoader : : initResampleUsingFormat ( ) {
2018-01-02 16:18:53 +00:00
int res = 0 ;
2017-12-08 08:57:43 +00:00
2018-01-02 16:18:53 +00:00
_swrContext = swr_alloc_set_opts (
_swrContext ,
_swrDstChannelLayout ,
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat ,
2018-01-02 16:18:53 +00:00
_swrDstRate ,
_swrSrcChannelLayout ,
2018-01-02 17:22:13 +00:00
_swrSrcSampleFormat ,
2018-01-02 16:18:53 +00:00
_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 08:57:43 +00:00
2018-01-02 17:22:13 +00:00
bool AbstractAudioFFMpegLoader : : ensureResampleSpaceAvailable ( int samples ) {
2018-01-02 16:18:53 +00:00
if ( _swrDstData ! = nullptr & & _swrDstDataCapacity > = samples ) {
return true ;
}
const auto allocate = std : : max ( samples , int ( av_rescale_rnd (
2020-10-13 16:43:18 +00:00
FFmpeg : : kAVBlockSize / _outputSampleSize ,
2018-01-02 16:18:53 +00:00
_swrDstRate ,
_swrSrcRate ,
AV_ROUND_UP ) ) ) ;
if ( _swrDstData ) {
av_freep ( & _swrDstData [ 0 ] ) ;
}
const auto res = _swrDstData
? av_samples_alloc (
_swrDstData ,
nullptr ,
2018-01-02 17:22:13 +00:00
_outputChannels ,
2018-01-02 16:18:53 +00:00
allocate ,
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat ,
2018-01-02 16:18:53 +00:00
0 )
: av_samples_alloc_array_and_samples (
& _swrDstData ,
nullptr ,
2018-01-02 17:22:13 +00:00
_outputChannels ,
2018-01-02 16:18:53 +00:00
allocate ,
2018-01-02 17:22:13 +00:00
_swrDstSampleFormat ,
2018-01-02 16:18:53 +00:00
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 ;
}
2018-01-02 17:22:13 +00:00
void AbstractAudioFFMpegLoader : : appendSamples (
2019-02-28 21:03:25 +00:00
QByteArray & result ,
int64 & samplesAdded ,
uint8_t * * data ,
int count ) const {
2018-01-02 17:22:13 +00:00
result . append (
2019-02-28 21:03:25 +00:00
reinterpret_cast < const char * > ( data [ 0 ] ) ,
2018-01-02 17:22:13 +00:00
count * _outputSampleSize ) ;
samplesAdded + = count ;
}
AudioPlayerLoader : : ReadResult AbstractAudioFFMpegLoader : : readFromReadyFrame (
2019-02-28 21:03:25 +00:00
QByteArray & result ,
int64 & samplesAdded ) {
2018-01-02 16:18:53 +00:00
if ( frameHasDesiredFormat ( ) ) {
2018-01-02 17:22:13 +00:00
appendSamples (
result ,
samplesAdded ,
_frame - > extended_data ,
_frame - > nb_samples ) ;
return ReadResult : : Ok ;
2018-01-02 16:18:53 +00:00
} else if ( ! initResampleForFrame ( ) ) {
return ReadResult : : Error ;
2016-07-05 17:44:02 +00:00
}
2018-01-02 16:18:53 +00: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 ,
2019-02-28 21:03:25 +00:00
( const uint8_t * * ) _frame - > extended_data ,
2018-01-02 16:18:53 +00:00
_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 ;
}
2018-01-02 17:22:13 +00:00
appendSamples (
result ,
samplesAdded ,
_swrDstData ,
samples ) ;
2016-07-05 17:44:02 +00:00
return ReadResult : : Ok ;
}
2018-01-02 17:22:13 +00:00
AbstractAudioFFMpegLoader : : ~ AbstractAudioFFMpegLoader ( ) {
2018-01-02 16:18:53 +00:00
if ( _swrContext ) {
swr_free ( & _swrContext ) ;
}
if ( _swrDstData ) {
if ( _swrDstData [ 0 ] ) {
av_freep ( & _swrDstData [ 0 ] ) ;
2016-07-05 17:44:02 +00:00
}
2018-01-02 16:18:53 +00:00
av_freep ( & _swrDstData ) ;
2016-07-05 17:44:02 +00:00
}
}
2018-01-02 17:22:13 +00:00
FFMpegLoader : : FFMpegLoader (
2020-10-13 16:43:18 +00:00
const Core : : FileLocation & file ,
2019-02-28 21:03:25 +00:00
const QByteArray & data ,
bytes : : vector & & buffer )
2018-03-27 12:16:00 +00:00
: AbstractAudioFFMpegLoader ( file , data , std : : move ( buffer ) ) {
2018-01-02 17:22:13 +00:00
}
2019-02-19 06:57:53 +00:00
bool FFMpegLoader : : open ( crl : : time positionMs ) {
2018-01-02 17:22:13 +00:00
if ( ! AbstractFFMpegLoader : : open ( positionMs ) ) {
return false ;
}
if ( ! openCodecContext ( ) ) {
return false ;
}
if ( ! initUsingContext ( _codecContext , _samplesCount , _samplesFrequency ) ) {
return false ;
}
return seekTo ( positionMs ) ;
}
bool FFMpegLoader : : openCodecContext ( ) {
int res = 0 ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
_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 ( )
) ) ;
return false ;
}
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 )
) ) ;
return false ;
}
2020-02-06 12:39:47 +00:00
_codecContext - > pkt_timebase = stream - > time_base ;
2018-01-02 17:22:13 +00:00
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 )
) ) ;
return false ;
}
return true ;
}
2019-02-19 06:57:53 +00:00
bool FFMpegLoader : : seekTo ( crl : : time positionMs ) {
2018-01-02 17:22:13 +00:00
if ( positionMs ) {
const auto stream = fmtContext - > streams [ streamId ] ;
const auto timeBase = stream - > time_base ;
const auto timeStamp = ( positionMs * timeBase . den )
/ ( 1000LL * timeBase . num ) ;
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 ) {
}
}
}
return true ;
}
AudioPlayerLoader : : ReadResult FFMpegLoader : : readMore (
2019-02-28 21:03:25 +00:00
QByteArray & result ,
int64 & samplesAdded ) {
2018-01-02 17:22:13 +00:00
const auto readResult = readFromReadyContext (
_codecContext ,
result ,
samplesAdded ) ;
if ( readResult ! = ReadResult : : Wait ) {
return readResult ;
}
auto res = 0 ;
if ( ( res = av_read_frame ( fmtContext , & _packet ) ) < 0 ) {
if ( res ! = AVERROR_EOF ) {
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
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 )
) ) ;
return ReadResult : : Error ;
}
avcodec_send_packet ( _codecContext , nullptr ) ; // drain
return ReadResult : : Ok ;
}
if ( _packet . stream_index = = streamId ) {
res = avcodec_send_packet ( _codecContext , & _packet ) ;
if ( res < 0 ) {
av_packet_unref ( & _packet ) ;
char err [ AV_ERROR_MAX_STRING_SIZE ] = { 0 } ;
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 )
) ) ;
// 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
//}
return ReadResult : : Error ;
}
}
av_packet_unref ( & _packet ) ;
return ReadResult : : Ok ;
}
FFMpegLoader : : ~ FFMpegLoader ( ) {
if ( _codecContext ) {
avcodec_free_context ( & _codecContext ) ;
}
}
2019-02-28 21:03:25 +00:00
} // namespace Media