@ -26,13 +26,21 @@
# include "decoder.h"
# include <assert.h>
# include <ffmpeg/avcodec.h>
# include <ffmpeg/avformat.h>
# include <ffmpeg/avio.h>
extern " C " {
# include <libavcodec/avcodec.h>
# include <libavformat/avformat.h>
# include <libavformat/avio.h>
}
# include "ffmpeg_decoder.h"
# include <iostream>
# if LIBAVCODEC_VERSION_MAJOR < 58
# define CODECPAR codec
# else
# define CODECPAR codecpar
# endif
// FFMPEG callbacks
extern " C " {
static int akode_read ( void * opaque , unsigned char * buf , int size )
@ -45,7 +53,7 @@ extern "C" {
aKode : : File * file = ( aKode : : File * ) opaque ;
return file - > write ( ( char * ) buf , size ) ;
}
static off set _t akode_seek ( void * opaque , off set _t pos , int whence )
static off _t akode_seek ( void * opaque , off _t pos , int whence )
{
aKode : : File * file = ( aKode : : File * ) opaque ;
return file - > seek ( pos , whence ) ;
@ -76,7 +84,7 @@ struct FFMPEGDecoder::private_data
AVFormatContext * ic ;
AVCodec * codec ;
AVInputFormat * fmt ;
ByteIOContext stream ;
AVIOContext* stream ;
int audioStream ;
int videoStream ;
@ -94,14 +102,13 @@ struct FFMPEGDecoder::private_data
bool initialized ;
int retries ;
unsigned char file_buffer [ FILE_BUFFER_SIZE ] ;
char buffer [ AVCODEC_MAX_AUDIO_FRAME_SIZE ] ;
unsigned char * file_buffer ;
unsigned char * * buffer ;
int buffer_size ;
} ;
FFMPEGDecoder : : FFMPEGDecoder ( File * src ) {
d = new private_data ;
av_register_all ( ) ;
d - > src = src ;
}
@ -121,10 +128,12 @@ static bool setAudioConfiguration(AudioConfiguration *config, AVCodecContext *co
config - > channel_config = MonoStereo ;
// avcodec.h says sample_fmt is not used. I guess it means it is always S16
switch ( codec_context - > sample_fmt ) {
case SAMPLE_FMT_U8 :
case AV_SAMPLE_FMT_U8 :
case AV_SAMPLE_FMT_U8P :
config - > sample_width = 8 ; // beware unsigned!
break ;
case SAMPLE_FMT_S16 :
case AV_SAMPLE_FMT_S16 :
case AV_SAMPLE_FMT_S16P :
config - > sample_width = 16 ;
break ;
/* disabled because I don't know byte ordering
@ -132,10 +141,12 @@ static bool setAudioConfiguration(AudioConfiguration *config, AVCodecContext *co
config - > sample_width = 24 ;
break ;
*/
case SAMPLE_FMT_S32 :
case AV_SAMPLE_FMT_S32 :
case AV_SAMPLE_FMT_S32P :
config - > sample_width = 32 ;
break ;
case SAMPLE_FMT_FLT :
case AV_SAMPLE_FMT_FLT :
case AV_SAMPLE_FMT_FLTP :
config - > sample_width = - 32 ;
break ;
default :
@ -152,58 +163,35 @@ bool FFMPEGDecoder::openFile() {
// The following duplicates what av_open_input_file would normally do
// url_fdopen
init_put_byte ( & d - > stream , d - > file_buffer , FILE_BUFFER_SIZE , 0 , d - > src , akode_read , akode_write , akode_seek ) ;
d - > stream . is_streamed = ! d - > src - > seekable ( ) ;
d - > stream . max_packet_size = FILE_BUFFER_SIZE ;
{
// 2048 is PROBE_BUF_SIZE from libavformat/utils.c
AVProbeData pd ;
uint8_t buf [ 2048 ] ;
pd . filename = d - > src - > filename ;
pd . buf = buf ;
pd . buf_size = 0 ;
pd . buf_size = get_buffer ( & d - > stream , buf , 2048 ) ;
d - > fmt = av_probe_input_format ( & pd , 1 ) ;
// Seek back to 0
// copied from url_fseek
long offset1 = 0 - ( d - > stream . pos - ( d - > stream . buf_end - d - > stream . buffer ) ) ;
if ( offset1 > = 0 & & offset1 < = ( d - > stream . buf_end - d - > stream . buffer ) ) {
/* can do the seek inside the buffer */
d - > stream . buf_ptr = d - > stream . buffer + offset1 ;
} else {
if ( ! d - > src - > seek ( 0 ) ) {
d - > src - > close ( ) ;
d - > file_buffer = ( unsigned char * ) av_malloc ( FILE_BUFFER_SIZE ) ;
d - > stream = avio_alloc_context ( d - > file_buffer , FILE_BUFFER_SIZE , 0 , d - > src , akode_read , akode_write , akode_seek ) ;
if ( ! d - > stream ) {
return false ;
} else {
d - > stream . pos = 0 ;
d - > stream . buf_ptr = d - > file_buffer ;
d - > stream . buf_end = d - > file_buffer ;
}
}
}
if ( ! d - > fmt ) {
std : : cerr < < " akode: FFMPEG: Format not found \n " ;
closeFile ( ) ;
d - > stream - > seekable = d - > src - > seekable ( ) ;
d - > stream - > max_packet_size = FILE_BUFFER_SIZE ;
d - > ic = avformat_alloc_context ( ) ;
if ( ! d - > ic ) {
return false ;
}
d - > ic - > pb = d - > stream ;
if ( av _open_input_stream ( & d - > ic , & d - > stream , d - > src - > filename , d - > fmt , 0 ) ! = 0 )
if ( avformat_open_input ( & d - > ic , d - > src - > filename , NULL , NULL ) ! = 0 )
{
closeFile ( ) ;
return false ;
}
av _find_stream_info( d - > ic ) ;
avformat_find_stream_info ( d - > ic , NULL ) ;
// Find the first a/v streams
d - > audioStream = - 1 ;
d - > videoStream = - 1 ;
for ( int i = 0 ; i < d - > ic - > nb_streams ; i + + ) {
if ( d - > ic - > streams [ i ] - > codec- > codec_type = = CODEC _TYPE_AUDIO)
if ( d - > ic - > streams [ i ] - > CODECPAR- > codec_type = = AVMEDIA _TYPE_AUDIO)
d - > audioStream = i ;
else
if ( d - > ic - > streams [ i ] - > codec- > codec_type = = CODEC _TYPE_VIDEO)
if ( d - > ic - > streams [ i ] - > CODECPAR- > codec_type = = AVMEDIA _TYPE_VIDEO)
d - > videoStream = i ;
}
if ( d - > audioStream = = - 1 )
@ -214,6 +202,8 @@ bool FFMPEGDecoder::openFile() {
return false ;
}
av_dump_format ( d - > ic , d - > audioStream , d - > src - > filename , 0 ) ;
// Set config
if ( ! setAudioConfiguration ( & d - > config , d - > ic - > streams [ d - > audioStream ] - > codec ) )
{
@ -221,13 +211,13 @@ bool FFMPEGDecoder::openFile() {
return false ;
}
d - > codec = avcodec_find_decoder ( d - > ic - > streams [ d - > audioStream ] - > codec - > codec_id ) ;
d - > codec = avcodec_find_decoder ( d - > ic - > streams [ d - > audioStream ] - > CODECPAR - > codec_id ) ;
if ( ! d - > codec ) {
std : : cerr < < " akode: FFMPEG: Codec not found \n " ;
closeFile ( ) ;
return false ;
}
avcodec_open ( d - > ic - > streams [ d - > audioStream ] - > codec , d - > codec ) ;
avcodec_open 2 ( d - > ic - > streams [ d - > audioStream ] - > codec , d - > codec , NULL ) ;
double ffpos = ( double ) d - > ic - > streams [ d - > audioStream ] - > start_time / ( double ) AV_TIME_BASE ;
d - > position = ( long ) ( ffpos * d - > config . sample_rate ) ;
@ -236,8 +226,17 @@ bool FFMPEGDecoder::openFile() {
}
void FFMPEGDecoder : : closeFile ( ) {
if ( d - > stream ) {
if ( d - > stream - > buffer )
av_free ( d - > stream - > buffer ) ;
# if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 80, 100)
av_free ( d - > stream ) ;
# else
avio_context_free ( & d - > stream ) ;
# endif
}
if ( d - > packetSize > 0 ) {
av_free_packet ( & d - > packet ) ;
av_ packet_unref ( & d - > packet ) ;
d - > packetSize = 0 ;
}
@ -248,7 +247,7 @@ void FFMPEGDecoder::closeFile() {
if ( d - > ic ) {
// make sure av_close_input_file doesn't actually close the file
d - > ic - > iformat - > flags = d - > ic - > iformat - > flags | AVFMT_NOFILE ;
av _close_input_file ( d - > ic ) ;
av format _close_input( & d - > ic ) ;
d - > ic = 0 ;
}
if ( d - > src ) {
@ -260,7 +259,7 @@ bool FFMPEGDecoder::readPacket() {
do {
av_init_packet ( & d - > packet ) ;
if ( av_read_frame ( d - > ic , & d - > packet ) < 0 ) {
av_ free_ packet( & d - > packet ) ;
av_ packet_unref ( & d - > packet ) ;
d - > packetSize = 0 ;
d - > packetData = 0 ;
return false ;
@ -270,7 +269,7 @@ bool FFMPEGDecoder::readPacket() {
d - > packetData = d - > packet . data ;
return true ;
}
av_ free_ packet( & d - > packet ) ;
av_ packet_unref ( & d - > packet ) ;
} while ( true ) ;
return false ;
@ -286,11 +285,11 @@ static long demux(FFMPEGDecoder::private_data* d, AudioFrame* frame) {
if ( frame - > sample_width = = 8 ) offset = - 128 ; // convert unsigned to signed
// Demux into frame
T * buffer = ( T * ) d - > buffer ;
T * * buffer = ( T * * ) d - > buffer ;
T * * data = ( T * * ) frame - > data ;
for ( int i = 0 ; i < length ; i + + )
for ( int j = 0 ; j < channels ; j + + )
data [ j ] [ i ] = buffer [ i* channels + j ] + offset ;
data [ j ] [ i ] = buffer [ j] [ i ] + offset ;
return length ;
}
@ -314,9 +313,25 @@ bool FFMPEGDecoder::readFrame(AudioFrame* frame)
assert ( d - > packet . stream_index = = d - > audioStream ) ;
retry :
int len = avcodec_decode_audio ( d - > ic - > streams [ d - > audioStream ] - > codec ,
( short * ) d - > buffer , & d - > buffer_size ,
d - > packetData , d - > packetSize ) ;
AVFrame * decodeFrame = av_frame_alloc ( ) ;
if ( ! decodeFrame ) {
return false ;
}
int decoded ;
int len = avcodec_decode_audio4 ( d - > ic - > streams [ d - > audioStream ] - > codec ,
decodeFrame , & decoded ,
& d - > packet ) ;
# if !defined(FFMPEG_AVFRAME_HAVE_PKT_SIZE)
d - > packetSize = d - > packet . size ;
# else
d - > packetSize = decodeFrame - > pkt_size ;
# endif
d - > buffer = decodeFrame - > data ;
# if !defined(FFMPEG_AVFRAME_HAVE_CHANNELS)
d - > buffer_size = decodeFrame - > nb_samples * d - > ic - > streams [ d - > audioStream ] - > codec - > channels * av_get_bytes_per_sample ( d - > ic - > streams [ d - > audioStream ] - > codec - > sample_fmt ) ;
# else
d - > buffer_size = decodeFrame - > nb_samples * decodeFrame - > channels * av_get_bytes_per_sample ( d - > ic - > streams [ d - > audioStream ] - > codec - > sample_fmt ) ;
# endif
if ( len < = 0 ) {
d - > retries + + ;
@ -348,11 +363,12 @@ retry:
default :
assert ( false ) ;
}
av_frame_free ( & decodeFrame ) ;
if ( length = = 0 ) return readFrame ( frame ) ;
std : : cout < < " akode: FFMPEG: Frame length: " < < length < < " \n " ;
// std::cout << "akode: FFMPEG: Frame length: " << length << "\n";
if ( d - > packetSize < = 0 )
av_ free_ packet( & d - > packet ) ;
av_ packet_unref ( & d - > packet ) ;
frame - > pos = ( d - > position * 1000 ) / d - > config . sample_rate ;
d - > position + = length ;