@ -465,9 +465,8 @@ static av_cold int vmdvideo_decode_end(AVCodecContext *avctx)
# define BLOCK_TYPE_SILENCE 3
# define BLOCK_TYPE_SILENCE 3
typedef struct VmdAudioContext {
typedef struct VmdAudioContext {
AVCodecContext * avctx ;
int out_bps ;
int out_bps ;
int predictors [ 2 ] ;
int chunk_size ;
} VmdAudioContext ;
} VmdAudioContext ;
static const uint16_t vmdaudio_table [ 128 ] = {
static const uint16_t vmdaudio_table [ 128 ] = {
@ -490,13 +489,23 @@ static av_cold int vmdaudio_decode_init(AVCodecContext *avctx)
{
{
VmdAudioContext * s = avctx - > priv_data ;
VmdAudioContext * s = avctx - > priv_data ;
s - > avctx = avctx ;
if ( avctx - > channels < 1 | | avctx - > channels > 2 ) {
av_log ( avctx , AV_LOG_ERROR , " invalid number of channels \n " ) ;
return AVERROR ( EINVAL ) ;
}
if ( avctx - > block_align < 1 ) {
av_log ( avctx , AV_LOG_ERROR , " invalid block align \n " ) ;
return AVERROR ( EINVAL ) ;
}
if ( avctx - > bits_per_coded_sample = = 16 )
if ( avctx - > bits_per_coded_sample = = 16 )
avctx - > sample_fmt = AV_SAMPLE_FMT_S16 ;
avctx - > sample_fmt = AV_SAMPLE_FMT_S16 ;
else
else
avctx - > sample_fmt = AV_SAMPLE_FMT_U8 ;
avctx - > sample_fmt = AV_SAMPLE_FMT_U8 ;
s - > out_bps = av_get_bytes_per_sample ( avctx - > sample_fmt ) ;
s - > out_bps = av_get_bytes_per_sample ( avctx - > sample_fmt ) ;
s - > chunk_size = avctx - > block_align + avctx - > channels * ( s - > out_bps = = 2 ) ;
av_log ( avctx , AV_LOG_DEBUG , " %d channels, %d bits/sample, "
av_log ( avctx , AV_LOG_DEBUG , " %d channels, %d bits/sample, "
" block align = %d, sample rate = %d \n " ,
" block align = %d, sample rate = %d \n " ,
avctx - > channels , avctx - > bits_per_coded_sample , avctx - > block_align ,
avctx - > channels , avctx - > bits_per_coded_sample , avctx - > block_align ,
@ -505,52 +514,47 @@ static av_cold int vmdaudio_decode_init(AVCodecContext *avctx)
return 0 ;
return 0 ;
}
}
static void vmdaudio_decode_audio ( VmdAudioContext * s , unsigned char * data ,
static void decode_audio_s16 ( int16_t * out , const uint8_t * buf , int buf_size ,
const uint8_t * buf , int buf_size , int stereo )
int channels )
{
{
int i ;
int ch ;
int chan = 0 ;
const uint8_t * buf_end = buf + buf_size ;
int16_t * out = ( int16_t * ) data ;
int predictor [ 2 ] ;
int st = channels - 1 ;
for ( i = 0 ; i < buf_size ; i + + ) {
if ( buf [ i ] & 0x80 )
/* decode initial raw sample */
s - > predictors [ chan ] - = vmdaudio_table [ buf [ i ] & 0x7F ] ;
for ( ch = 0 ; ch < channels ; ch + + ) {
predictor [ ch ] = ( int16_t ) AV_RL16 ( buf ) ;
buf + = 2 ;
* out + + = predictor [ ch ] ;
}
/* decode DPCM samples */
ch = 0 ;
while ( buf < buf_end ) {
uint8_t b = * buf + + ;
if ( b & 0x80 )
predictor [ ch ] - = vmdaudio_table [ b & 0x7F ] ;
else
else
s - > predictors [ chan ] + = vmdaudio_table [ buf [ i ] ] ;
predictor [ ch ] + = vmdaudio_table [ b ] ;
s - > predictors [ chan ] = av_clip_int16 ( s - > predictors [ chan ] ) ;
predictor [ ch ] = av_clip_int16 ( predictor [ ch ] ) ;
out [ i ] = s - > predictors [ chan ] ;
* out + + = predictor [ ch ] ;
chan ^ = stereo ;
ch ^ = st ;
}
}
}
}
static int vmdaudio_loadsound ( VmdAudioContext * s , unsigned char * data ,
const uint8_t * buf , int silent_chunks , int data_size )
{
int silent_size = s - > avctx - > block_align * silent_chunks * s - > out_bps ;
if ( silent_chunks ) {
memset ( data , s - > out_bps = = 2 ? 0x00 : 0x80 , silent_size ) ;
data + = silent_size ;
}
if ( s - > avctx - > bits_per_coded_sample = = 16 )
vmdaudio_decode_audio ( s , data , buf , data_size , s - > avctx - > channels = = 2 ) ;
else {
/* just copy the data */
memcpy ( data , buf , data_size ) ;
}
return silent_size + data_size * s - > out_bps ;
}
static int vmdaudio_decode_frame ( AVCodecContext * avctx ,
static int vmdaudio_decode_frame ( AVCodecContext * avctx ,
void * data , int * data_size ,
void * data , int * data_size ,
AVPacket * avpkt )
AVPacket * avpkt )
{
{
const uint8_t * buf = avpkt - > data ;
const uint8_t * buf = avpkt - > data ;
const uint8_t * buf_end ;
int buf_size = avpkt - > size ;
int buf_size = avpkt - > size ;
VmdAudioContext * s = avctx - > priv_data ;
VmdAudioContext * s = avctx - > priv_data ;
int block_type , silent_chunks ;
int block_type , silent_chunks , audio_chunks ;
unsigned char * output_samples = ( unsigned char * ) data ;
int nb_samples , out_size ;
uint8_t * output_samples_u8 = data ;
int16_t * output_samples_s16 = data ;
if ( buf_size < 16 ) {
if ( buf_size < 16 ) {
av_log ( avctx , AV_LOG_WARNING , " skipping small junk packet \n " ) ;
av_log ( avctx , AV_LOG_WARNING , " skipping small junk packet \n " ) ;
@ -566,11 +570,14 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx,
buf + = 16 ;
buf + = 16 ;
buf_size - = 16 ;
buf_size - = 16 ;
/* get number of silent chunks */
silent_chunks = 0 ;
silent_chunks = 0 ;
if ( block_type = = BLOCK_TYPE_INITIAL ) {
if ( block_type = = BLOCK_TYPE_INITIAL ) {
uint32_t flags ;
uint32_t flags ;
if ( buf_size < 4 )
if ( buf_size < 4 ) {
return - 1 ;
av_log ( avctx , AV_LOG_ERROR , " packet is too small \n " ) ;
return AVERROR ( EINVAL ) ;
}
flags = AV_RB32 ( buf ) ;
flags = AV_RB32 ( buf ) ;
silent_chunks = av_popcount ( flags ) ;
silent_chunks = av_popcount ( flags ) ;
buf + = 4 ;
buf + = 4 ;
@ -581,11 +588,41 @@ static int vmdaudio_decode_frame(AVCodecContext *avctx,
}
}
/* ensure output buffer is large enough */
/* ensure output buffer is large enough */
if ( * data_size < ( avctx - > block_align * silent_chunks + buf_size ) * s - > out_bps )
audio_chunks = buf_size / s - > chunk_size ;
nb_samples = ( ( silent_chunks + audio_chunks ) * avctx - > block_align ) / avctx - > channels ;
out_size = nb_samples * avctx - > channels * s - > out_bps ;
if ( * data_size < out_size )
return - 1 ;
return - 1 ;
* data_size = vmdaudio_loadsound ( s , output_samples , buf , silent_chunks , buf_size ) ;
/* decode silent chunks */
if ( silent_chunks > 0 ) {
int silent_size = avctx - > block_align * silent_chunks ;
if ( s - > out_bps = = 2 ) {
memset ( output_samples_s16 , 0x00 , silent_size * 2 ) ;
output_samples_s16 + = silent_size ;
} else {
memset ( output_samples_u8 , 0x80 , silent_size ) ;
output_samples_u8 + = silent_size ;
}
}
/* decode audio chunks */
if ( audio_chunks > 0 ) {
buf_end = buf + buf_size ;
while ( buf < buf_end ) {
if ( s - > out_bps = = 2 ) {
decode_audio_s16 ( output_samples_s16 , buf , s - > chunk_size ,
avctx - > channels ) ;
output_samples_s16 + = avctx - > block_align ;
} else {
memcpy ( output_samples_u8 , buf , s - > chunk_size ) ;
output_samples_u8 + = avctx - > block_align ;
}
buf + = s - > chunk_size ;
}
}
* data_size = out_size ;
return avpkt - > size ;
return avpkt - > size ;
}
}