MAC/iOS利用FFmpeg解析音视频数据流

大神,请教一下,MAC/iOS利用FFmpeg解析音视频数据流
最新回答
初逝的格调シ

2024-06-30 03:35:52

利用FFmpeg解析音视频流,音视频流可以来自一个标准的RTMP的URL或者是一个文件. 通过解析得到音视频流,进一步就可以解码, 然后视频渲染在屏幕上,音频通过扬声器输出.

利用FFmpeg框架中libavformat模块可以通过函数 av_read_frame 解析出音视频流的音视频数据,如果直接使用FFmpeg硬解,仅需要解析到AVPacket即可传给解码模块使用,如果使用VideoToolbox中的硬解, 对于视频数据,还需要获取其NALU Header中的(vps)sps, pps以便后续使用.

使用流程

FFmpeg parse流程

下面的链接中包含搭建iOS需要的FFmpeg环境的详细步骤,需要的可以提前阅读.

iOS手动编译并搭建FFmpeg

导入FFmpeg框架后,首先需要将用到FFmpeg的文件改名为.mm, 因为涉及C,C++混编,所以需要更改文件名

然后在头文件中导入FFmpeg头文件.

注意: FFmpeg是一个广为流传的框架,其结构复杂,一般导入都按照如上格式,以文件夹名为根目录进行导入,具体设置,请参考上文链接.

2.1. 注册FFmpeg

一般在程序中的main函数或是主程序启动的代理方法 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 中初始化FFmpeg,执行一次即可.

2.2. 利用视频文件生成格式上下文对象

C++音视频开发学习资料 :点击领取 音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

2.3. 获取Audio / Video流的索引值.

通过遍历format context对象可以从 nb_streams 数组中找到音频或视频流索引,以便后续使用

2.4. 是否支持音视频流

目前视频仅支持H264, H265编码的格式.实际过程中,解码得到视频的旋转角度可能是不同的,以及不同机型可以支持的解码文件格式也是不同的,所以可以用这个方法手动过滤一些不支持的情况.具体请下载代码观看,这里仅列出实战中测试出支持的列表.

音频本例中仅支持AAC格式.其他格式可根据需求自行更改.

使用AVPacket这个结构体来存储压缩数据.对于视频而言, 它通常包含一个压缩帧,对音频而言,可能包含多个压缩帧,该结构体类型通过 av_malloc() 函数分配内存,通过 av_packet_ref() 函数拷贝,通过 av_packet_unref(). 函数释放内存.

解析数据

int av_read_frame(AVFormatContext *s, AVPacket *pkt); : 此函数返回存储在文件中的内容,并且不验证解码器的有效帧是什么。它会将存储在文件中的内容分成帧,并为每次调用返回一个。它不会在有效帧之间省略无效数据,以便为解码器提供解码时可能的最大信息。

获取sps, pps等NALU Header信息

通过调用av_bitstream_filter_filter可以从码流中过滤得到sps, pps等NALU Header信息.

av_bitstream_filter_init: 通过给定的比特流过滤器名词创建并初始化一个比特流过滤器上下文.

av_bitstream_filter_filter: 此函数通过过滤buf参数中的数据,将过滤后的数据放在poutbuf参数中.输出的buffer必须被调用者释放.

此函数使用buf_size大小过滤缓冲区buf,并将过滤后的缓冲区放在poutbuf指向的缓冲区中。

注意: 下面使用new_packet是为了解决av_bitstream_filter_filter会产生内存泄漏的问题.每次使用完后将用new_packet释放即可.

可以根据自己的需求自定义时间戳生成规则.这里使用当前系统时间戳加上数据包中的自带的pts/dts生成了时间戳.

本例将获取到的数据放在自定义的结构体中,然后通过block回调传给方法的调用者,调用者可以在回调函数中处理parse到的视频数据.

获取parse到的音频数据

因为我们已经将packet中的关键数据拷贝到自定义的结构体中,所以使用完后需要释放packet.

parse完成后释放相关资源

C++音视频开发学习资料 :点击领取 音视频开发(资料文档+视频教程+面试题)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

注意: 如果使用FFmpeg硬解,则仅仅需要获取到AVPacket数据结构即可.不需要再将数据封装到自定义的结构体中