老调重弹之ffmpeg解码音频
老调重弹之ffmpeg解码音频
接着之前的视频解码,在之前的基础上加上音频解码,还是使用SDL。
- 首先找到音频流 与找视频流时一样,在
avformat_find_stream_info
之后,遍历一下AVFormatContext
中的streams
,找到codecpar->codec_type
为AVMEDIA_TYPE_AUDIO
的索引。
else if(pformatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
audioStream = i;
}
- 打开解码器 与视频流时一样,创建一个
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);
SDL打开音频 SDL打开音频设备时,需要提供一些关于音频的参数,如采样率、采样格式、通道数等。 但SDL实际使用时的参数可能与指定的不一样,因此需要把打开音频设备时返回的实际使用的参数保存下来,当解码出来的音频参数与设备使用的参数不一致时,需要进行转换。 另外,还需要提供一下回调函数,但SDL需要音频数据时,就会调用这个回调函数来获取数据。只是为了解码音频,所以简单地使用一个缓冲区直接顺序保存解码得到的音频数据,当SDL通过回调函数来取时,直接从这个缓冲区中读取。
解码音频数据 与视频时一样,通过
av_read_frame
从文件中读取packet后,通过packet中的stream_index可知道是音频还是视频。 当时音频时,通过函数avcodec_send_packet
把packet发到解码器,通过avcodec_receive_frame
读取解码的frame。对于音频,一个packet可能包含多个frame,因此需要多次调用avcodec_receive_frame
。音频数据格式转换 音频流中的格式可能与需要的不一致,就要进行转换。 通过
swr_alloc_set_opts
分配一个转换时需要的上下文,然后swr_init
进行初始化,再使用swr_convert
进行转换。 尽可能简单,把转换后的数据直接放入缓冲区,等SDL通过回调来读取。播放音频数据 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
老调重弹之ffmpeg解码音频相关推荐
- ffmpeg 解码音频(aac、mp3)输出pcm文件
ffmpeg 解码音频(aac.mp3)输出pcm文件 播放pcm可以参考: ffplay -ar 48000 -ac 2 -f f32le out.pcm main.c #include <s ...
- 使用ffmpeg解码音频sdl(push)播放
自定义播放器系列 第一章 视频渲染 第二章 音频(push)播放(本章) 第三章 音频(pull)播放 第四章 实现时钟同步 第五章 实现通用时钟同步 第六章 实现播放器 文章目录 自定义播放器系列 ...
- FFmpeg开发笔记(七):ffmpeg解码音频保存为PCM并使用软件播放
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108799279 各位读者,知识无穷而人力有穷 ...
- FFmpeg解码音频代码
工程请访问DecodeTest.用c++写的,使用FFmpeg-4.1.0.比较完善的是OfficalDecodeAudio.cpp,它是我研究了一下ffplay.c的源码得来的.该代码按照统一的44 ...
- FFmpeg进行音频的解码和播放
音频编码 音频数字化主要有压缩与非压缩(pcm)两种方式. 非压缩编码(PCM)PCM音频编码 PCM通过抽样.量化.编码三个步骤将连续变化的模拟信号转换为数字编码. 当采样频率fs.max大于信号中 ...
- iOS 音视频开发:Audio Unit播放FFmpeg解码的音频
本文档描述了iOS播放经FFmpeg解码的音频数据的编程步骤,具体基于Audio Toolbox框架的Audio Session和Audio Unit框架提供的接口实现.在iOS 7及以上平台Audi ...
- ffmpeg 找不到bin_FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://blog.csdn.net/qq21497936/article/details/109603499 各位读者,知识无穷而人力有穷 ...
- FFMPEG视音频编解码学习(1)
###开篇 看过了FFMPEG视音频编解码的基础知识,接着还有许多比如制作一个基于FFMPEG的视频播放器等,但是好像比较抽象,而且在公司的mac上很多软件也没有(重点是看的一头雾水), 还是老老实实 ...
- [总结]FFMPEG视音频编解码零基础学习方法--转
ffmpeg编解码学习 目录(?)[-] ffmpeg程序的使用ffmpegexeffplayexeffprobeexe 1 ffmpegexe 2 ffplayexe 3 ffprobeexe ff ...
最新文章
- win7 python3环境变量设置_如何配置python3的环境变量
- 【CV冬季划】终极进阶,超30个项目实战+3本书+3年知识星球
- 設備(IE01/IE02/IE03)客製欄位及BAPI處理
- java spring mvc 上传_Java Spring MVC 上传下载文件配置及controller方法详解
- 【IT笔试面试题整理】堆栈和队列
- 【转】3.2SharePoint服务器端对象模型 之 访问文件和文件夹(Part 2)
- AC_Dream 1211 Reactor Cooling
- 帝国CMS7.2阿里大于云短信插件(UTF-8)
- 记一次由PCI BAR配置不正确引发的硬盘IO调度io_schedule阻塞的经历
- 网页ui设计模板素材|液体流动效果!
- 奥特曼在银行里下象棋的梗
- bootdo 重命名 版本2.0 (适合所有springboot项目)
- 初学python------写一个心理测试
- 作为iOS开发者,你不可错过的资源
- 分析亚洲手机游戏市场现状--中国篇、韩国篇以及日本篇
- Redhat最小化安装后安装图形界面步骤
- Mapguide配置心得
- HBase读数据和写数据流程
- 泛目录程序-泛目录站群程序-免费泛目录站群管理工具下载
- Windows phone 8 JumpStart--Async