[Libav-user] Missing a channel in Dolby 5:1

Gonzalo Garramuno ggarra13 at gmail.com
Wed May 29 16:54:02 CEST 2013


I am using ffmpeg and libswresample to play back movie trailers. For 
stereo playback everything is good, but for Dolby 5.1, I get correct 
playback of all ambient noises but the audio track for voices is missing 
or too low of a sound, like a missing channel.  My only weird 
implementation if that the audio tracks are split into frames before 
playback.
I wonder if someone can suggest what it is I am doing wrong or missing.
Here is some sample code:

#if defined(_WIN32) || defined(_WIN64)
mrv::AudioEngine::AudioFormat kIntSampleFormat = mrv::AudioEngine::kS16LSB;
AVSampleFormat kInternalSampleFormat = AV_SAMPLE_FMT_S16;
unsigned kFormatSize = (unsigned) sizeof(int16_t);
#else
mrv::AudioEngine::AudioFormat kIntSampleFormat = 
mrv::AudioEngine::kFloatLSB;
AVSampleFormat kInternalSampleFormat = AV_SAMPLE_FMT_FLT;
unsigned kFormatSize = (unsigned) sizeof(float);
#endif

...



int CMedia::decode_audio3(AVCodecContext *avctx, int16_t *samples,
               int *frame_size_ptr,
               AVPacket *avpkt)
{
    AVFrame frame = { { 0 } };
    int ret, got_frame = 0;


     ret = avcodec_decode_audio4(avctx, &frame, &got_frame, avpkt);

     if (ret >= 0 && got_frame) {
        int plane_size;
        int planar    = av_sample_fmt_is_planar(avctx->sample_fmt);
        int data_size = av_samples_get_buffer_size(&plane_size, 
avctx->channels,
                           frame.nb_samples,
                           avctx->sample_fmt, 1);
         if (*frame_size_ptr < data_size) {
        IMG_ERROR( "decode_audio3 - Output buffer size is too small for "
               "the current frame ("
               << *frame_size_ptr << " < " << data_size << ")" );
        return AVERROR(EINVAL);
         }


     if ( avctx->sample_fmt != kInternalSampleFormat )
     {
        if (!forw_ctx)
        {
           char buf[256];

           uint64_t  in_ch_layout =
get_valid_channel_layout(avctx->channel_layout, avctx->channels);

           if ( in_ch_layout == 0 ) in_ch_layout = AV_CH_LAYOUT_STEREO;

           av_get_channel_layout_string( buf, 256, avctx->channels,
                         in_ch_layout );

           IMG_INFO("Create audio conversion from " << buf
                << ", channels " << avctx->channels << ", " );
           IMG_INFO( "format "
                << av_get_sample_fmt_name( avctx->sample_fmt )
             << ", sample rate " << avctx->sample_rate << " to" );

           uint64_t out_ch_layout = in_ch_layout;

           av_get_channel_layout_string( buf, 256, avctx->channels,
                         out_ch_layout );
           AVSampleFormat  out_sample_fmt = kInternalSampleFormat;
           AVSampleFormat  in_sample_fmt = avctx->sample_fmt;
           int in_sample_rate = avctx->sample_rate;
           int out_sample_rate = in_sample_rate;
           IMG_INFO( buf << ", channels " << avctx->channels << ", format "
             << av_get_sample_fmt_name( out_sample_fmt )
             << ", sample rate "
             << out_sample_rate);


           forw_ctx  = swr_alloc_set_opts(NULL, out_ch_layout,
                          out_sample_fmt,  out_sample_rate,
                          in_ch_layout,  in_sample_fmt,
                          in_sample_rate,
                          0, NULL);
           if(!forw_ctx) {
          LOG_ERROR("Failed to alloc swresample library");
          return 0;
           }
           if(swr_init(forw_ctx) < 0)
           {
          char buf[256];
          av_get_channel_layout_string(buf, 256, -1, in_ch_layout);
          LOG_ERROR( "Failed to init swresample library with "
                 << buf << " "
                 << av_get_sample_fmt_name(in_sample_fmt)
                 << " frequency: " << in_sample_rate );
          return 0;
           }
        }


        swr_convert(forw_ctx, (uint8_t**)&samples,
                data_size / sizeof(int16_t),
                (const uint8_t **)frame.extended_data,
                frame.nb_samples );

     // If S16, data is half the size after conversion
        if ( kInternalSampleFormat == AV_SAMPLE_FMT_S16 )
           data_size /= 2;

     }
     else
     {
        memcpy(samples, frame.extended_data[0], data_size);

        if (planar && avctx->channels > 1) {
           uint8_t *out = ((uint8_t *)samples) + plane_size;
           for (int ch = 1; ch < avctx->channels; ch++) {
          memcpy(out, frame.extended_data[ch], plane_size);
          out += plane_size;
           }
        }
     }

         *frame_size_ptr = data_size;
     } else {
         *frame_size_ptr = 0;
     }
     return ret;
}

...
CMedia::DecodeStatus
CMedia::decode_audio_packet( boost::int64_t& ptsframe,
                  const boost::int64_t frame,
                  const AVPacket& pkt )
{

   AVStream* stream = get_audio_stream();
   if ( !stream ) return kDecodeNoStream;

   // Get the audio codec context
   AVCodecContext* ctx = stream->codec;


   assert( !_audio_packets.is_seek( pkt ) );
   assert( !_audio_packets.is_flush( pkt ) );
   assert( !_audio_packets.is_preroll( pkt ) );
   assert( !_audio_packets.is_loop_end( pkt ) );
   assert( !_audio_packets.is_loop_start( pkt ) );

   ptsframe = get_frame( stream, pkt );

   // Make sure audio frames are continous during playback to
   // accomodate weird sample rates not evenly divisable by frame rate
   if ( _audio_buf_used != 0 && (!_audio.empty()) )
     {
        ptsframe = _audio_last_frame + 1;
       // assert( ptsframe <= last_frame() );
     }



#ifdef DEBUG
   if ( _audio_buf_used + pkt.size >= _audio_max )
     {
       IMG_ERROR( _("Too much audio used:") << _audio_buf_used  );
     }
#endif

   AVPacket pkt_temp;
   av_init_packet(&pkt_temp);
   pkt_temp.data = pkt.data;
   pkt_temp.size = pkt.size;



   assert( _audio_buf != NULL );
   assert( pkt.size + _audio_buf_used < _audio_max );

   int audio_size = AVCODEC_MAX_AUDIO_FRAME_SIZE;
   assert( pkt_temp.size <= audio_size );

   while ( pkt_temp.size > 0 || pkt_temp.data == NULL )
     {
        // Decode the audio into the buffer
        assert( _audio_buf_used + pkt_temp.size <= _audio_max );
        assert( audio_size > 0 );
        int ret = decode_audio3( ctx,
                                ( int16_t * )( (char*)_audio_buf +
_audio_buf_used ),
                                &audio_size, &pkt_temp );

       // If no samples are returned, then break now
       if ( ret <= 0 )
     {
        pkt_temp.size = 0;
        IMG_ERROR( _("Audio missed for frame: ") << ptsframe
               << _(" ret: ") << ret
               << _(" audio max: ")  << _audio_max
               << _(" audio used: ") << _audio_buf_used
                );

       return kDecodeMissingSamples;
     }


       assert( audio_size > 0 );
       assert( audio_size + _audio_buf_used <= _audio_max );

       // Decrement the length by the number of bytes parsed
       pkt_temp.data += ret;
       pkt_temp.size -= ret;

       if ( audio_size <= 0 ) break;

       _audio_buf_used += audio_size;
     }

   if ( pkt_temp.size == 0 ) return kDecodeOK;

   return kDecodeMissingSamples;
}



bool CMedia::open_audio( const short channels,
              const unsigned nSamplesPerSec )
{
   close_audio();

   _samples_per_sec = nSamplesPerSec;
   return _audio_engine->open( channels, nSamplesPerSec,
                   kIntSampleFormat, kFormatSize*8);
}



I can show the implementation of _audio_engine and how I split the audio 
into frames if needed, too.


More information about the Libav-user mailing list