老调重弹之ffmpeg解码音频

老调重弹之ffmpeg解码音频

接着之前的视频解码,在之前的基础上加上音频解码,还是使用SDL。

  1. 首先找到音频流 与找视频流时一样,在avformat_find_stream_info之后,遍历一下AVFormatContext中的streams,找到codecpar->codec_typeAVMEDIA_TYPE_AUDIO的索引。
else if(pformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audioStream = i;
}
  1. 打开解码器 与视频流时一样,创建一个AVCodecContext
paudiocodecContext = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(paudiocodecContext, pformatContext->streams[audioStream]->codecpar);
paudiocodec = avcodec_find_decoder(paudiocodecContext->codec_id);
avcodec_open2(paudiocodecContext, paudiocodec, &opts);
  1. SDL打开音频 SDL打开音频设备时,需要提供一些关于音频的参数,如采样率、采样格式、通道数等。 但SDL实际使用时的参数可能与指定的不一样,因此需要把打开音频设备时返回的实际使用的参数保存下来,当解码出来的音频参数与设备使用的参数不一致时,需要进行转换。 另外,还需要提供一下回调函数,但SDL需要音频数据时,就会调用这个回调函数来获取数据。只是为了解码音频,所以简单地使用一个缓冲区直接顺序保存解码得到的音频数据,当SDL通过回调函数来取时,直接从这个缓冲区中读取。

  2. 解码音频数据 与视频时一样,通过av_read_frame从文件中读取packet后,通过packet中的stream_index可知道是音频还是视频。 当时音频时,通过函数avcodec_send_packet把packet发到解码器,通过avcodec_receive_frame读取解码的frame。对于音频,一个packet可能包含多个frame,因此需要多次调用avcodec_receive_frame

  3. 音频数据格式转换 音频流中的格式可能与需要的不一致,就要进行转换。 通过swr_alloc_set_opts分配一个转换时需要的上下文,然后swr_init进行初始化,再使用swr_convert进行转换。 尽可能简单,把转换后的数据直接放入缓冲区,等SDL通过回调来读取。

  4. 播放音频数据 SDL通过回调函数获得音频数据。直接从音频解码后放入的缓冲区中读取数据给SDL。

paudiocodecContext = avcodec_alloc_context3(nullptr);
avcodec_parameters_to_context(paudiocodecContext, pformatContext->streams[audioStream]->codecpar);
paudiocodec = avcodec_find_decoder(paudiocodecContext->codec_id);
avcodec_open2(paudiocodecContext, paudiocodec, &opts);

SDL_AudioSpec wanted_spec, got_spec;
wanted_spec.freq = paudiocodecContext->sample_rate;
wanted_spec.format = AUDIO_S16SYS;
wanted_spec.channels = paudiocodecContext->channels;
wanted_spec.silence = 0;
wanted_spec.samples = 1024;
wanted_spec.callback = audio_callback;
wanted_spec.userdata = nullptr;
if(SDL_OpenAudio(&wanted_spec, &got_spec) < 0)
{
    cerr << "SDL_OpenAudio fail:" << SDL_GetError() << endl;
    exit(1);
}
AVFrame audio_wanted_frame;
audio_wanted_frame.format = AV_SAMPLE_FMT_S16;
audio_wanted_frame.sample_rate = got_spec.freq;
audio_wanted_frame.channel_layout = av_get_default_channel_layout(got_spec.channels);
audio_wanted_frame.channels = got_spec.channels;

SwrContext *pswrcontext = nullptr;

uint8_t audio_buff[(192000 * 3) / 2];
uint8_t *paudio_buff = audio_buff;
int audio_buff_size = 0;

//解码后的数据直接简单地放入这个buf中
g_audiobuf.cond = SDL_CreateCond();
g_audiobuf.mutex = SDL_CreateMutex();
g_audiobuf.buf.AllocateBuffer(SIZE_16M);

SDL_PauseAudio(0);


    //读取到packet后,
else if(pkt.stream_index == audioStream)
{
    iret = avcodec_send_packet(paudiocodecContext, &pkt);
    for(;;)
    {
        iret = avcodec_receive_frame(paudiocodecContext, pframe);
        if(iret == 0)
        {
            pswrcontext = swr_alloc_set_opts(pswrcontext, 
                audio_wanted_frame.channel_layout, (AVSampleFormat)audio_wanted_frame.format, audio_wanted_frame.sample_rate,
                pframe->channel_layout, (AVSampleFormat)pframe->format, pframe->sample_rate,
                0, nullptr);
            if (!pswrcontext || swr_init(pswrcontext) < 0)
            {
                cerr << "swr_alloc_set_opts | swr_init fail." << endl;
                continue;
            }
            int dst_nb_samples = (int)av_rescale_rnd(swr_get_delay(pswrcontext, pframe->sample_rate) + pframe->nb_samples,
                audio_wanted_frame.sample_rate, audio_wanted_frame.format, AVRounding(1));
            int len2 = swr_convert(pswrcontext, &paudio_buff, dst_nb_samples, (const uint8_t**)pframe->data, pframe->nb_samples);
            audio_buff_size = audio_wanted_frame.channels * len2 * av_get_bytes_per_sample((AVSampleFormat)audio_wanted_frame.format);
            
            //放及取数据的这部分烂死
            for(;;)
            {
                SDL_LockMutex(g_audiobuf.mutex);
                uInt32 ifree = g_audiobuf.buf.GetFree();
                if(ifree < audio_buff_size)
                {
                    SDL_CondSignal(g_audiobuf.cond);
                    SDL_UnlockMutex(g_audiobuf.mutex);
                    
                    continue;
                }
                g_audiobuf.buf.Write(paudio_buff, audio_buff_size);
                SDL_UnlockMutex(g_audiobuf.mutex);
                break;
            }
        }
        else
        {
            break;
        }
    }
}
//SDL音频回调函数
static void audio_callback(void *userdata, Uint8* stream, int len)
{
    memset(stream, 0, len);
    if(bquit)
        return;
    //放及取数据的这部分烂死
    SDL_LockMutex(g_audiobuf.mutex);
    while(!bquit)
    {
        uInt32 idata = g_audiobuf.buf.GetSize();
        if(idata < len)
        {
            SDL_CondWait(g_audiobuf.cond, g_audiobuf.mutex);
        }
        else
        {
            break;
        }
    }
    g_audiobuf.buf.Read(stream, len);
    SDL_UnlockMutex(g_audiobuf.mutex);
}

转载于:https://my.oschina.net/u/2286208/blog/752948

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值