FFmpeg简易播放器的实现-音频播放

本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文件解码和调用SDL播放两大部分。本实验仅研究音频播放的实现方式,不考虑视频。在开始前,可先参考如下文章了解播放器的基本流程:“FFmpeg简易播放器的实现-最简版”1. 视频播放器基本原理下图...
摘要由CSDN通过智能技术生成

本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10068490.html

基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文件解码和调用SDL播放两大部分。
本实验仅研究音频播放的实现方式,不考虑视频。

FFmpeg简易播放器系列文章如下:
[1]. FFmpeg简易播放器的实现-最简版
[2]. FFmpeg简易播放器的实现-视频播放
[3]. FFmpeg简易播放器的实现-音频播放
[4]. FFmpeg简易播放器的实现-音视频播放
[5]. FFmpeg简易播放器的实现-音视频同步

1. 视频播放器基本原理

下图引用自“雷霄骅,视音频编解码技术零基础学习方法”,因原图太小,看不太清楚,故重新制作了一张图片。
播放器基本原理示意图
如下内容引用自“雷霄骅,视音频编解码技术零基础学习方法”:

解协议
将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。

解封装
将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。

解码
将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。

音视频同步
根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。

2. 简易播放器的实现-音频播放

2.1 实验平台

实验平台:openSUSE Leap 42.3
FFmpeg版本:4.1
SDL版本:2.0.9
FFmpeg开发环境搭建可参考“ffmpeg开发环境构建

2.2 源码流程分析

本实验仅播放视频文件中的声音,而不显示图像。源码流程参考如下:
FFmpeg简易播放器-音频播放流程图

2.3 源码清单

代码已经变得挺长了,不贴完整源码了,源码参考:
https://github.com/leichn/exercises/blob/master/source/ffmpeg/player_audio/ffplayer.c

源码清单中涉及的一些概念简述如下:
container:
对应数据结构AVFormatContext
封装器,将流数据封装为指定格式的文件,文件格式如AVI、MP4等。
FFmpeg可识别五种流类型:视频video(v)、音频audio(a)、attachment(t)、数据data(d)、字幕subtitle。

codec:
对应数据结构AVCodec
编解码器。编码器将未压缩的原始图像或音频数据编码为压缩数据。解码器与之相反。

codec context:
对应数据结构AVCodecContext
编解码器上下文。此为非常重要的一个数据结构,后文分析。各API大量使用AVCodecContext来引用编解码器。

codec par:
对应数据结构AVCodecParameters
编解码器参数。新版本增加的字段。新版本建议使用AVStream->codepar替代AVStream->codec。

packet:
对应数据结构AVPacket
经过编码的数据。通过av_read_frame()从媒体文件中获取得到的一个packet可能包含多个(整数个)音频帧或单个
视频帧,或者其他类型的流数据。

frame:
对应数据结构AVFrame
解码后的原始数据。解码器将packet解码后生成frame。

2.4 关键过程

几个关键函数的说明直接写在代码注释里:

2.4.1 开启音频处理子线程
// B2. 打开音频设备并创建音频处理线程
// B2.1 打开音频设备,获取SDL设备支持的音频参数actual_spec(期望的参数是wanted_spec,实际得到actual_spec)
// 1) SDL提供两种使音频设备取得音频数据方法:
//    a. push,SDL以特定的频率调用回调函数,在回调函数中取得音频数据
//    b. pull,用户程序以特定的频率调用SDL_QueueAudio(),向音频设备提供数据。此种情况wanted_spec.callback=NULL
// 2) 音频设备打开后播放静音,不启动回调,调用SDL_PauseAudio(0)后启动回调,开始正常播放音频
wanted_spec.freq = p_codec_ctx->sample_rate;    // 采样率
wanted_spec.format = AUDIO_S16SYS;              // S表带符号,16是采样深度,SYS表采用系统字节序
wanted_spec.channels = p_codec_ctx->channels;   // 声道数
wanted_spec.silence = 0;                        // 静音值
wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;    // SDL声音缓冲区尺寸,单位是单声道采样点尺寸x通道数
wanted_spec.callback = sdl_audio_callback;      // 回调函数,若为NULL,则应使用SDL_QueueAudio()机制
wanted_spec.userdata = p_codec_ctx;             // 提供给回调函数的参数
if (SDL_OpenAudio(&wanted_spec, &actual_spec) < 0)
{
   
    printf("SDL_OpenAudio() failed: %s\n", SDL_GetError());
    goto exit4;
}

// B2.2 根据SDL音频参数构建音频重采样参数
// wanted_spec是期望的参数,actual_spec是实际的参数,wanted_spec和auctual_spec都是SDL中的参数。
// 此处audio_param是FFmpeg中的参数,此参数应保证是SDL播放支持的参数,后面重采样要用到此参数
// 音频帧解码后得到的frame中的音频格式未必被SDL支持,比如frame可能是planar格式,但SDL2.0并不支持planar格式,
// 若将解码后的frame直接送入SDL音频缓冲区,声音将无法正常播放。所以需要先将frame重采样(转换格式)为SDL支持的模式,
// 然后送再写入SDL音频缓冲区
s_audio_param_tgt.fmt = AV_SAMPLE_FMT_S16;
s_audio_param_tgt.freq = actual_spec.freq;
s_audio_param_tgt.channel_layout = av_get_default_channel_layout(actual_spec.channels)
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值