文档章节

老调重弹之ffmpeg解码音频

b
 b-q
发布于 2016/09/28 22:31
字数 819
阅读 355
收藏 2

老调重弹之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);
}

© 著作权归作者所有

b

b-q

粉丝 0
博文 3
码字总数 3170
作品 0
闵行
私信 提问
加载中

评论(1)

ostmail
ostmail
大神,g_audiobuf 怎么定义的?
FFmpeg深入分析之零-基础

FFmpeg是相当强大的多媒体编解码框架,在深入分析其源代码之前必须要有基本的多媒体基础知识,否则其源代码会非常晦涩难懂。本文将从介绍一些基本的多媒体只是,主要是为研读ffmpeg源代码做准...

天下杰论
2015/04/22
242
0
最简单的基于FFMPEG+SDL的音频播放器:拆分-解码器和播放器

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/leixiaohua1020/article/details/46890259 ===================================================== 最简单的...

雷霄骅
2015/07/17
0
0
FFmpeg深入分析之零-基础

FFmpeg是相当强大的多媒体编解码框架,在深入分析其源代码之前必须要有基本的多媒 体基础知识,否则其源代码会非常晦涩难懂。本文将从介绍一些基本的多媒体只是,主要是为研读ffmpeg源代码做...

李荣刚
2015/03/16
226
0
Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)

关于 Android使用FFmpeg(一)--编译ffmpeg Android使用FFmpeg(二)--Android Studio配置ffmpeg Android使用FFmpeg(三)--ffmpeg实现视频播放 Android使用FFmpeg(四)--ffmpeg实现音频播放(使用A...

天王盖地虎626
01/14
28
0
Android使用FFmpeg(六)--ffmpeg实现音视频同步播放

关于 Android使用FFmpeg(一)--编译ffmpeg Android使用FFmpeg(二)--Android Studio配置ffmpeg Android使用FFmpeg(三)--ffmpeg实现视频播放 Android使用FFmpeg(四)--ffmpeg实现音频播放(使用A...

天王盖地虎626
01/14
34
0

没有更多内容

加载失败,请刷新页面

加载更多

总结:TCP/IP协议

一、介绍 TCP协议属于OSI七层模型中的传输层协议,提供处于网络连接中的两台计算机之间的数据 传输。   在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议...

浮躁的码农
32分钟前
3
0
一言不合就删库跑路?万名贡献者和阿里巴巴开源的二三事

9 月 27 日云栖大会,阿里巴巴宣布贾扬清担任开源技术委员会负责人。 有人问:开源是为了什么? 从个人视角看,可以证明自己的专业能力,获得行业认可; 从企业视角看,可以建立技术影响力,...

大涛学弟
43分钟前
4
0
JAVA编程注意事项(性能篇)

1. 尽量在合适的场合使用单例 使用单例可以缩短加载的时间,提高加载的效率,单例主要适用于以下三个方面: 第一,控制资源的使用,通过线程同步来控制资源的并发访问; 第二,控制实例的产生...

你好夜故事
44分钟前
5
0
List 前端 AngularJS JS 对IP排序

数据格式 $scope.dataList=[ {"ip":"192.168.10.10", "port":"8080",...}, { "ip":"192.168.10.12", "port":"8080",... } ,.....] 调用 $scope.ipSortForward($scope.dataList,"ip") 核心代码......

最菜最菜之小菜鸟
45分钟前
4
0
浅析Cassandra LeveledCompactionStrategy

前言 Cassandra是基于LSM架构的分布式数据库。LSM中有一个很重要的过程,就是压缩(Compaction)。默认的压缩策略是SizeTieredCompactionStrategy,今天主要说一下另一种压缩策略LeveledComp...

阿里云官方博客
49分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部