ffmpeg 源代码简单分析 av read frame

本文详细分析了FFmpeg库中的av_read_frame()函数,讲解了其在解码过程中的作用,如何从码流中读取音频帧和视频帧。通过调用av_read_packet()和parse_packet(),确保每次读取的数据包含完整帧,并介绍了FLV封装格式中Tag的数据结构。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

=====================================================

FFmpeg的库函数源代码分析文章列表:

【架构图】

FFmpeg源代码结构图 - 解码

FFmpeg源代码结构图 - 编码

【通用】

FFmpeg 源代码简单分析:av_register_all()

FFmpeg 源代码简单分析:avcodec_register_all()

FFmpeg 源代码简单分析:内存的分配和释放(av_malloc()、av_free()等)

FFmpeg 源代码简单分析:常见结构体的初始化和销毁(AVFormatContext,AVFrame等)

FFmpeg 源代码简单分析:avio_open2()

FFmpeg 源代码简单分析:av_find_decoder()和av_find_encoder()

FFmpeg 源代码简单分析:avcodec_open2()

FFmpeg 源代码简单分析:avcodec_close()

【解码】

图解FFMPEG打开媒体的函数avformat_open_input

FFmpeg 源代码简单分析:avformat_open_input()

FFmpeg 源代码简单分析:avformat_find_stream_info()

FFmpeg 源代码简单分析:av_read_frame()

FFmpeg 源代码简单分析:avcodec_decode_video2()

FFmpeg 源代码简单分析:avformat_close_input()

【编码】

FFmpeg 源代码简单分析:avformat_alloc_output_context2()

FFmpeg 源代码简单分析:avformat_write_header()

FFmpeg 源代码简单分析:avcodec_encode_video()

FFmpeg 源代码简单分析:av_write_frame()

FFmpeg 源代码简单分析:av_write_trailer()

【其它】

FFmpeg源代码简单分析:日志输出系统(av_log()等)

FFmpeg源代码简单分析:结构体成员管理系统-AVClass

FFmpeg源代码简单分析:结构体成员管理系统-AVOption

FFmpeg源代码简单分析:libswscale的sws_getContext()

FFmpeg源代码简单分析:libswscale的sws_scale()

FFmpeg源代码简单分析:libavdevice的avdevice_register_all()

FFmpeg源代码简单分析:libavdevice的gdigrab

【脚本】

FFmpeg源代码简单分析:makefile

FFmpeg源代码简单分析:configure

【H.264】

FFmpeg的H.264解码器源代码简单分析:概述

=====================================================


ffmpeg中的av_read_frame()的作用是读取码流中的音频若干帧或者视频一帧。例如,解码视频的时候,每解码一个视频帧,需要先调用 av_read_frame()获得一帧视频的压缩数据,然后才能对该数据进行解码(例如H.264中一帧压缩数据通常对应一个NAL)。

对该函数源代码的分析是很久之前做的了,现在翻出来,用博客记录一下。


上代码之前,先参考了其他人对av_read_frame()的解释,在此做一个参考:

通过av_read_packet(***),读取一个包,需要说明的是此函数必须是包含整数帧的,不存在半帧的情况,以ts流为例,是读取一个完整的PES包(一个完整pes包包含若干视频或音频es包),读取完毕后,通过av_parser_parse2(***)分析出视频一帧(或音频若干帧),返回,下次进入循环的时候,如果上次的数据没有完全取完,则st = s->cur_st;不会是NULL,即再此进入av_parser_parse2(***)流程,而不是下面的av_read_packet(**)流程,这样就保证了,如果读取一次包含了N帧视频数据(以视频为例),则调用av_read_frame(***)N次都不会去读数据,而是返回第一次读取的数据,直到全部解析完毕。

av_read_frame()的声明位于libavformat\avformat.h,如下所示。

/** * Return the next frame of a stream. * This function returns what is stored in the file, and does not validate * that what is there are valid frames for the decoder. It will split what is * stored in the file into frames and return one for each call. It will not * omit invalid data between valid frames so as to give the decoder the maximum * information possible for decoding. * * If pkt->buf is NULL, then the packet is valid until the next * av_read_frame() or until avformat_close_input(). Otherwise the packet * is valid indefinitely. In both cases the packet must be freed with * av_free_packet when it is no longer needed. For video, the packet contains * exactly one frame. For audio, it contains an integer number of frames if each * frame has a known fixed size (e.g. PCM or ADPCM data). If the audio frames * have a variable size (e.g. MPEG audio), then it contains one frame. * * pkt->pts, pkt->dts and pkt->duration are always set to correct * values in AVStream.time_base units (and guessed if the format cannot * provide them). pkt->pts can be AV_NOPTS_VALUE if the video format * has B-frames, so it is better to rely on pkt->dts if you do not * decompress the payload. * * @return 0 if OK, < 0 on error or end of file */int av_read_frame(AVFormatContext *s, AVPacket *pkt);

av_read_frame()使用方法在注释中写得很详细,用中文简单描述一下它的两个参数:

s:输入的AVFormatContext

pkt:输出的AVPacket

如果返回0则说明读取正常。

函数调用结构图

函数调用结构图如下所示。


av_read_frame()

av_read_frame()的定义位于libavformat\utils.c,如下所示:

//获取一个AVPacket/* * av_read_frame - 新版本的ffmpeg用的是av_read_frame,而老版本的是av_read_packet * 。区别是av_read_packet读出的是包,它可能是半帧或多帧,不保证帧的完整性。av_read_frame对 * av_read_packet进行了封装,使读出的数据总是完整的帧 */int av_read_frame(AVFormatContext *s, AVPacket *pkt){    const int genpts = s->flags & AVFMT_FLAG_GENPTS;    int          eof = 0;    if (!genpts)     /**      * This buffer is only needed when packets were already buffered but      * not decoded, for example to get the codec parameters in MPEG      * streams.      * 一般情况下会调用read_frame_internal(s, pkt)      * 直接返回      */        return s->packet_buffer ? read_from_packet_buffer(s, pkt) :                                  read_frame_internal(s, pkt);    for (;;) {        int ret;        AVPacketList *pktl = s->packet_buffer;        if (pktl) {            AVPacket *next_pkt = &pktl->pkt;            if (next_pkt->dts != AV_NOPTS_VALUE) {                int wrap_bits = s->streams[next_pkt->stream_index]->pts_wrap_bits;                while (pktl && next_pkt->pts == AV_NOPTS_VALUE) {                    if (pktl->pkt.stream_index == next_pkt->stream_index &&                        (av_compare_mod(next_pkt->dts, pktl->pkt.dts, 2LL << (wrap_bits - 1)) < 0) &&                         av_compare_mod(pktl->pkt.pts, pktl->pkt.dts, 2LL << (wrap_bits - 1))) { //not b frame                        next_pkt->pts = pktl->pkt.dts;                    }                    pktl = pktl->next;                }                pktl = s->packet_buffer;            }            /* read packet from packet buffer, if there is data */            if (!(next_pkt->pts == AV_NOPTS_VALUE &&                  next_pkt->dts != AV_NOPTS_VALUE && 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值