diff options
Diffstat (limited to 'libhb/declpcm.c')
-rw-r--r-- | libhb/declpcm.c | 155 |
1 files changed, 129 insertions, 26 deletions
diff --git a/libhb/declpcm.c b/libhb/declpcm.c index 5c6a0e4a1..86dd6e823 100644 --- a/libhb/declpcm.c +++ b/libhb/declpcm.c @@ -8,14 +8,14 @@ */ #include "hb.h" -#include "downmix.h" +#include "audio_remap.h" struct hb_work_private_s { hb_job_t *job; uint32_t size; /* frame size in bytes */ - uint32_t chunks; /* number of samples pairs if paired */ - uint32_t samples; /* frame size in samples */ + uint32_t nchunks; /* number of samples pairs if paired */ + uint32_t nsamples; /* frame size in samples */ uint32_t pos; /* buffer offset for next input data */ int64_t next_pts; /* pts for next output frame */ @@ -29,6 +29,15 @@ struct hb_work_private_s uint8_t sample_size; /* bits per sample */ uint8_t frame[HB_DVD_READ_BUFFER_SIZE*2]; + uint8_t * data; + uint32_t alloc_size; + + AVAudioResampleContext *avresample; + int resample; + int out_channels; + int stereo_downmix_mode; + uint64_t out_channel_layout; + uint64_t resample_channel_layout; }; static hb_buffer_t * Decode( hb_work_object_t * w ); @@ -53,12 +62,85 @@ static const int hdr2samplerate[] = { 48000, 96000, 44100, 32000 }; static const int hdr2samplesize[] = { 16, 20, 24, 16 }; static const uint64_t hdr2layout[] = { - AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, - AV_CH_LAYOUT_2_1, AV_CH_LAYOUT_2_2, - AV_CH_LAYOUT_5POINT0, AV_CH_LAYOUT_2_2|AV_CH_FRONT_LEFT_OF_CENTER|AV_CH_FRONT_RIGHT_OF_CENTER, - AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_MONO, AV_CH_LAYOUT_STEREO, + AV_CH_LAYOUT_2_1, AV_CH_LAYOUT_QUAD, + AV_CH_LAYOUT_5POINT0_BACK, AV_CH_LAYOUT_6POINT0_FRONT, + AV_CH_LAYOUT_STEREO, AV_CH_LAYOUT_STEREO, }; +static hb_buffer_t * downmixAudio(hb_work_private_t *pv, + hb_audio_t *audio) +{ + int64_t in_layout; + int resample_changed; + + in_layout = hdr2layout[pv->nchannels - 1]; + pv->resample = pv->resample || (pv->out_channel_layout != in_layout); + resample_changed = pv->resample && (pv->resample_channel_layout != in_layout); + + if (resample_changed || (pv->resample && pv->avresample == NULL)) + { + if (pv->avresample == NULL) + { + pv->avresample = avresample_alloc_context(); + if (pv->avresample == NULL) + { + hb_error("Failed to initialize avresample"); + return NULL; + } + // some settings only need to be set once + av_opt_set_int(pv->avresample, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0); + av_opt_set_int(pv->avresample, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); + av_opt_set_int(pv->avresample, "out_channel_layout", pv->out_channel_layout, 0); + av_opt_set_int(pv->avresample, "matrix_encoding", pv->stereo_downmix_mode, 0); + } + else if (resample_changed) + { + avresample_close(pv->avresample); + } + + av_opt_set_int(pv->avresample, "in_channel_layout", in_layout, 0); + + if (avresample_open(pv->avresample) < 0) + { + hb_error("Failed to open libavresample"); + return NULL; + } + + pv->resample_channel_layout = in_layout; + } + + hb_buffer_t *buf; + int out_size, out_linesize, sample_size; + sample_size = av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT); + out_size = av_samples_get_buffer_size(&out_linesize, pv->out_channels, + pv->nsamples, AV_SAMPLE_FMT_FLT, !pv->resample); + buf = hb_buffer_init(out_size); + + if (pv->resample) + { + int in_linesize, out_samples; + in_linesize = pv->nsamples * pv->nchannels * sample_size; + out_samples = avresample_convert(pv->avresample, + (void**)&buf->data, out_linesize, pv->nsamples, + (void**)&pv->data, in_linesize, pv->nsamples); + + if (out_samples < 0) + { + hb_error("avresample_convert() failed"); + return NULL; + } + buf->size = out_samples * sample_size * pv->out_channels; + } + else + { + memcpy(buf->data, pv->data, out_size); + buf->size = pv->nsamples * sample_size * pv->out_channels; + } + + return buf; +} + static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) { hb_work_private_t * pv = w->private_data; @@ -99,8 +181,8 @@ static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) hb_log( "declpcm: illegal frame offset %d", pv->offset ); pv->offset = 2; /*XXX*/ } - pv->samplerate = hdr2samplerate[ ( in->data[4] >> 4 ) & 0x3 ]; - pv->nchannels = ( in->data[4] & 7 ) + 1; + pv->nchannels = ( in->data[4] & 7 ) + 1; + pv->samplerate = hdr2samplerate[ ( in->data[4] >> 4 ) & 0x3 ]; pv->sample_size = hdr2samplesize[in->data[4] >> 6]; // 20 and 24 bit lpcm is always encoded in sample pairs. So take this @@ -142,10 +224,10 @@ static void lpcmInfo( hb_work_object_t *w, hb_buffer_t *in ) 149 ) / 150; pv->duration = frames * 150; - pv->chunks = ( pv->duration * pv->nchannels * pv->samplerate + + pv->nchunks = ( pv->duration * pv->nchannels * pv->samplerate + samples_per_chunk - 1 ) / ( 90000 * samples_per_chunk ); - pv->samples = ( pv->duration * pv->nchannels * pv->samplerate ) / 90000; - pv->size = pv->chunks * chunk_size; + pv->nsamples = ( pv->duration * pv->samplerate ) / 90000; + pv->size = pv->nchunks * chunk_size; pv->next_pts = in->s.start; } @@ -154,7 +236,13 @@ static int declpcmInit( hb_work_object_t * w, hb_job_t * job ) { hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) ); w->private_data = pv; - pv->job = job; + pv->job = job; + + // initialize output settings for avresample + pv->out_channels = hb_mixdown_get_discrete_channel_count(w->audio->config.out.mixdown); + pv->out_channel_layout = hb_ff_mixdown_xlat(w->audio->config.out.mixdown, + &pv->stereo_downmix_mode); + return 0; } @@ -215,20 +303,19 @@ static int declpcmWork( hb_work_object_t * w, hb_buffer_t ** buf_in, static hb_buffer_t *Decode( hb_work_object_t *w ) { hb_work_private_t *pv = w->private_data; - hb_buffer_t *out; - if (pv->samples == 0) + if (pv->nsamples == 0) return NULL; - out = hb_buffer_init( pv->samples * sizeof( float ) ); - - out->s.start = pv->next_pts; - out->s.duration = pv->duration; - pv->next_pts += pv->duration; - out->s.stop = pv->next_pts; + int size = pv->nsamples * pv->nchannels * sizeof( float ); + if (pv->alloc_size != size) + { + pv->data = realloc( pv->data, size ); + pv->alloc_size = size; + } - float *odat = (float *)out->data; - int count = pv->chunks / pv->nchannels; + float *odat = (float *)pv->data; + int count = pv->nchunks / pv->nchannels; switch( pv->sample_size ) { @@ -312,14 +399,30 @@ static hb_buffer_t *Decode( hb_work_object_t *w ) } } break; } + + hb_buffer_t *out; + out = downmixAudio( pv, w->audio ); + + out->s.start = pv->next_pts; + out->s.duration = pv->duration; + pv->next_pts += pv->duration; + out->s.stop = pv->next_pts; + return out; } static void declpcmClose( hb_work_object_t * w ) { - if ( w->private_data ) + hb_work_private_t * pv = w->private_data; + + if ( pv ) { - free( w->private_data ); + if ( pv->avresample ) + { + avresample_free( &pv->avresample ); + } + free( pv->data ); + free( pv ); w->private_data = 0; } } @@ -342,7 +445,7 @@ static int declpcmBSInfo( hb_work_object_t *w, const hb_buffer_t *b, info->bitrate = bitrate; info->flags = ( b->data[3] << 16 ) | ( b->data[4] << 8 ) | b->data[5]; info->channel_layout = hdr2layout[nchannels - 1]; - info->channel_map = &hb_qt_chan_map; + info->channel_map = &hb_libav_chan_map; info->samples_per_frame = ( duration * rate ) / 90000; return 1; |