FFmpeg的AVcodecParser

文章目录

    • 结构体
    • 操作函数
    • 支持的AVCodecParser

这个模块是AVCodec中的子模块,专门用来提前解析码流的元数据,为后面的解码做准备,这一点对cuda-NVdec非常明显,英伟达解码器的元数据解析是放在CPU上的,所以就非常依赖这个解析模块。
FFmpeg中有很多现成的parser
在这里插入图片描述

结构体

下面是它的上下文


typedef struct AVCodecParserContext {void *priv_data;const struct AVCodecParser *parser;int64_t frame_offset; /* offset of the current frame */int64_t cur_offset; /* current offset(incremented by each av_parser_parse()) */int64_t next_frame_offset; /* offset of the next frame *//* video info */int pict_type; /* XXX: Put it back in AVCodecContext. *//*** This field is used for proper frame duration computation in lavf.* It signals, how much longer the frame duration of the current frame* is compared to normal frame duration.** frame_duration = (1 + repeat_pict) * time_base** It is used by codecs like H.264 to display telecined material.*/int repeat_pict; /* XXX: Put it back in AVCodecContext. */int64_t pts;     /* pts of the current frame */int64_t dts;     /* dts of the current frame *//* private data */int64_t last_pts;int64_t last_dts;int fetch_timestamp;#define AV_PARSER_PTS_NB 4int cur_frame_start_index;int64_t cur_frame_offset[AV_PARSER_PTS_NB];int64_t cur_frame_pts[AV_PARSER_PTS_NB];int64_t cur_frame_dts[AV_PARSER_PTS_NB];int flags;
#define PARSER_FLAG_COMPLETE_FRAMES           0x0001
#define PARSER_FLAG_ONCE                      0x0002
/// Set if the parser has a valid file offset
#define PARSER_FLAG_FETCHED_OFFSET            0x0004
#define PARSER_FLAG_USE_CODEC_TS              0x1000int64_t offset;      ///< byte offset from starting packet startint64_t cur_frame_end[AV_PARSER_PTS_NB];/*** Set by parser to 1 for key frames and 0 for non-key frames.* It is initialized to -1, so if the parser doesn't set this flag,* old-style fallback using AV_PICTURE_TYPE_I picture type as key frames* will be used.*/int key_frame;// Timestamp generation support:/*** Synchronization point for start of timestamp generation.** Set to >0 for sync point, 0 for no sync point and <0 for undefined* (default).** For example, this corresponds to presence of H.264 buffering period* SEI message.*/int dts_sync_point;/*** Offset of the current timestamp against last timestamp sync point in* units of AVCodecContext.time_base.** Set to INT_MIN when dts_sync_point unused. Otherwise, it must* contain a valid timestamp offset.** Note that the timestamp of sync point has usually a nonzero* dts_ref_dts_delta, which refers to the previous sync point. Offset of* the next frame after timestamp sync point will be usually 1.** For example, this corresponds to H.264 cpb_removal_delay.*/int dts_ref_dts_delta;/*** Presentation delay of current frame in units of AVCodecContext.time_base.** Set to INT_MIN when dts_sync_point unused. Otherwise, it must* contain valid non-negative timestamp delta (presentation time of a frame* must not lie in the past).** This delay represents the difference between decoding and presentation* time of the frame.** For example, this corresponds to H.264 dpb_output_delay.*/int pts_dts_delta;/*** Position of the packet in file.** Analogous to cur_frame_pts/dts*/int64_t cur_frame_pos[AV_PARSER_PTS_NB];/*** Byte position of currently parsed frame in stream.*/int64_t pos;/*** Previous frame byte position.*/int64_t last_pos;/*** Duration of the current frame.* For audio, this is in units of 1 / AVCodecContext.sample_rate.* For all other types, this is in units of AVCodecContext.time_base.*/int duration;enum AVFieldOrder field_order;/*** Indicate whether a picture is coded as a frame, top field or bottom field.** For example, H.264 field_pic_flag equal to 0 corresponds to* AV_PICTURE_STRUCTURE_FRAME. An H.264 picture with field_pic_flag* equal to 1 and bottom_field_flag equal to 0 corresponds to* AV_PICTURE_STRUCTURE_TOP_FIELD.*/enum AVPictureStructure picture_structure;/*** Picture number incremented in presentation or output order.* This field may be reinitialized at the first picture of a new sequence.** For example, this corresponds to H.264 PicOrderCnt.*/int output_picture_number;/*** Dimensions of the decoded video intended for presentation.*/int width;int height;/*** Dimensions of the coded video.*/int coded_width;int coded_height;/*** The format of the coded data, corresponds to enum AVPixelFormat for video* and for enum AVSampleFormat for audio.** Note that a decoder can have considerable freedom in how exactly it* decodes the data, so the format reported here might be different from the* one returned by a decoder.*/int format;
} AVCodecParserContext;

下面是插件接口的入口。
如果你要实现一个自己的parse,只要简单的重写下面四个函数就可以了。

typedef struct AVCodecParser {int codec_ids[7]; /* several codec IDs are permitted */int ;int (*parser_init)(AVCodecParserContext *s);/* This callback never returns an error, a negative value means that* the frame start was in a previous packet. */int (*parser_parse)(AVCodecParserContext *s,AVCodecContext *avctx,const uint8_t **poutbuf, int *poutbuf_size,const uint8_t *buf, int buf_size);void (*parser_close)(AVCodecParserContext *s);int (*split)(AVCodecContext *avctx, const uint8_t *buf, int buf_size);
} AVCodecParser;

特别注意上面的priv_data_size是AVCodecParserContext中的priv_data,这里面就是保存各种元信息。比如说对于h264来说,里面保存的就是:

static av_cold int init(AVCodecParserContext *s)
{H264ParseContext *p = s->priv_data;...return 0;
}

其实就是sps,pps,sei的值

typedef struct H264ParseContext {ParseContext pc;H264ParamSets ps;H264DSPContext h264dsp;H264POCContext poc;H264SEIContext sei;int is_avc;int nal_length_size;int got_first;int picture_structure;uint8_t parse_history[6];int parse_history_count;int parse_last_mb;int64_t reference_dts;int last_frame_num, last_picture_structure;
} H264ParseContext;

比如h264就是重写了下面三个函数接口。

const AVCodecParser ff_h264_parser = {.codec_ids      = { AV_CODEC_ID_H264 },.priv_data_size = sizeof(H264ParseContext),.parser_init    = init,.parser_parse   = h264_parse,.parser_close   = h264_close,
};

操作函数

下面是操作函数,第一个是列出所有的parser

/*** Iterate over all registered codec parsers.** @param opaque a pointer where libavcodec will store the iteration state. Must*               point to NULL to start the iteration.** @return the next registered codec parser or NULL when the iteration is*         finished*/
const AVCodecParser *av_parser_iterate(void **opaque);

第二个是初始化parser

AVCodecParserContext *av_parser_init(int codec_id);

第三个是最重要的,也就是解析pkt

/*** Parse a packet.** @param s             parser context.* @param avctx         codec context.* @param poutbuf       set to pointer to parsed buffer or NULL if not yet finished.* @param poutbuf_size  set to size of parsed buffer or zero if not yet finished.* @param buf           input buffer.* @param buf_size      buffer size in bytes without the padding. I.e. the full buffersize is assumed to be buf_size + AV_INPUT_BUFFER_PADDING_SIZE.To signal EOF, this should be 0 (so that the last framecan be output).* @param pts           input presentation timestamp.* @param dts           input decoding timestamp.* @param pos           input byte position in stream.* @return the number of bytes of the input bitstream used.** Example:* @code*   while(in_len){*       len = av_parser_parse2(myparser, AVCodecContext, &data, &size,*                                        in_data, in_len,*                                        pts, dts, pos);*       in_data += len;*       in_len  -= len;**       if(size)*          decode_frame(data, size);*   }* @endcode*/
int av_parser_parse2(AVCodecParserContext *s,AVCodecContext *avctx,uint8_t **poutbuf, int *poutbuf_size,const uint8_t *buf, int buf_size,int64_t pts, int64_t dts,int64_t pos);

最后一个是close parser

void av_parser_close(AVCodecParserContext *s);

下面是一个用例


int main(int argc, char **argv)
{const char *filename, *outfilename;const AVCodec *codec;AVCodecParserContext *parser;AVCodecContext *c= NULL;FILE *f;AVFrame *frame;uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];uint8_t *data;size_t   data_size;int ret;AVPacket *pkt;if (argc <= 2) {fprintf(stderr, "Usage: %s <input file> <output file>\n""And check your input file is encoded by mpeg1video please.\n", argv[0]);exit(0);}filename    = argv[1];outfilename = argv[2];pkt = av_packet_alloc();if (!pkt)exit(1);/* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);/* find the MPEG-1 video decoder */codec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);if (!codec) {fprintf(stderr, "Codec not found\n");exit(1);}parser = av_parser_init(codec->id);if (!parser) {fprintf(stderr, "parser not found\n");exit(1);}c = avcodec_alloc_context3(codec);if (!c) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}/* For some codecs, such as msmpeg4 and mpeg4, width and heightMUST be initialized there because this information is notavailable in the bitstream. *//* open it */if (avcodec_open2(c, codec, NULL) < 0) {fprintf(stderr, "Could not open codec\n");exit(1);}f = fopen(filename, "rb");if (!f) {fprintf(stderr, "Could not open %s\n", filename);exit(1);}frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate video frame\n");exit(1);}while (!feof(f)) {/* read raw data from the input file */data_size = fread(inbuf, 1, INBUF_SIZE, f);if (!data_size)break;/* use the parser to split the data into frames */data = inbuf;while (data_size > 0) {ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size,data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);if (ret < 0) {fprintf(stderr, "Error while parsing\n");exit(1);}data      += ret;data_size -= ret;if (pkt->size)decode(c, frame, pkt, outfilename);}}/* flush the decoder */decode(c, frame, NULL, outfilename);fclose(f);av_parser_close(parser);avcodec_free_context(&c);av_frame_free(&frame);av_packet_free(&pkt);return 0;
}

上面的例子展示了一个从文件中读取码流,然后通过av_parser_parse2将码流解析为一个个分离的NALU单元,然后送去解码,我们可以看到,第二个参数是c,也就是说将来要把sps,pps,sei这些信息保存在c中的extradata中。

下面第一个函数是av_parser_init,看到除了分配空间外,就是对parser->parser_init(s)的封装。


AVCodecParserContext *av_parser_init(int codec_id)
{AVCodecParserContext *s = NULL;const AVCodecParser *parser;void *i = 0;int ret;if (codec_id == AV_CODEC_ID_NONE)return NULL;while ((parser = av_parser_iterate(&i))) {if (parser->codec_ids[0] == codec_id ||parser->codec_ids[1] == codec_id ||parser->codec_ids[2] == codec_id ||parser->codec_ids[3] == codec_id ||parser->codec_ids[4] == codec_id ||parser->codec_ids[5] == codec_id ||parser->codec_ids[6] == codec_id)goto found;}return NULL;found:s = av_mallocz(sizeof(AVCodecParserContext));if (!s)goto err_out;s->parser = parser;s->priv_data = av_mallocz(parser->priv_data_size);if (!s->priv_data)goto err_out;s->fetch_timestamp=1;s->pict_type = AV_PICTURE_TYPE_I;if (parser->parser_init) {ret = parser->parser_init(s);if (ret != 0)goto err_out;}s->key_frame            = -1;s->dts_sync_point       = INT_MIN;s->dts_ref_dts_delta    = INT_MIN;s->pts_dts_delta        = INT_MIN;s->format               = -1;return s;err_out:if (s)av_freep(&s->priv_data);av_free(s);return NULL;
}

在这里插入图片描述
第二个函数也很简单av_parser_parse2,内部其实也是对parser_parse的封装。

int av_parser_parse2(AVCodecParserContext *s, AVCodecContext *avctx,uint8_t **poutbuf, int *poutbuf_size,const uint8_t *buf, int buf_size,int64_t pts, int64_t dts, int64_t pos){...index = s->parser->parser_parse(s, avctx, (const uint8_t **) poutbuf,poutbuf_size, buf, buf_size);...}

第三个函数av_parser_close就更加简单了

void av_parser_close(AVCodecParserContext *s)
{if (s) {if (s->parser->parser_close)s->parser->parser_close(s);av_freep(&s->priv_data);av_free(s);}
}

上面全部是架构代码,如果想要查看详细的解析过程,可以单独看每个插件的内容,也就是那三个函数。

支持的AVCodecParser


extern const AVCodecParser ff_aac_parser;
extern const AVCodecParser ff_aac_latm_parser;
extern const AVCodecParser ff_ac3_parser;
extern const AVCodecParser ff_adx_parser;
extern const AVCodecParser ff_amr_parser;
extern const AVCodecParser ff_av1_parser;
extern const AVCodecParser ff_avs2_parser;
extern const AVCodecParser ff_avs3_parser;
extern const AVCodecParser ff_bmp_parser;
extern const AVCodecParser ff_cavsvideo_parser;
extern const AVCodecParser ff_cook_parser;
extern const AVCodecParser ff_cri_parser;
extern const AVCodecParser ff_dca_parser;
extern const AVCodecParser ff_dirac_parser;
extern const AVCodecParser ff_dnxhd_parser;
extern const AVCodecParser ff_dolby_e_parser;
extern const AVCodecParser ff_dpx_parser;
extern const AVCodecParser ff_dvaudio_parser;
extern const AVCodecParser ff_dvbsub_parser;
extern const AVCodecParser ff_dvdsub_parser;
extern const AVCodecParser ff_dvd_nav_parser;
extern const AVCodecParser ff_flac_parser;
extern const AVCodecParser ff_g723_1_parser;
extern const AVCodecParser ff_g729_parser;
extern const AVCodecParser ff_gif_parser;
extern const AVCodecParser ff_gsm_parser;
extern const AVCodecParser ff_h261_parser;
extern const AVCodecParser ff_h263_parser;
extern const AVCodecParser ff_h264_parser;
extern const AVCodecParser ff_hevc_parser;
extern const AVCodecParser ff_ipu_parser;
extern const AVCodecParser ff_jpeg2000_parser;
extern const AVCodecParser ff_mjpeg_parser;
extern const AVCodecParser ff_mlp_parser;
extern const AVCodecParser ff_mpeg4video_parser;
extern const AVCodecParser ff_mpegaudio_parser;
extern const AVCodecParser ff_mpegvideo_parser;
extern const AVCodecParser ff_opus_parser;
extern const AVCodecParser ff_png_parser;
extern const AVCodecParser ff_pnm_parser;
extern const AVCodecParser ff_rv30_parser;
extern const AVCodecParser ff_rv40_parser;
extern const AVCodecParser ff_sbc_parser;
extern const AVCodecParser ff_sipr_parser;
extern const AVCodecParser ff_tak_parser;
extern const AVCodecParser ff_vc1_parser;
extern const AVCodecParser ff_vorbis_parser;
extern const AVCodecParser ff_vp3_parser;
extern const AVCodecParser ff_vp8_parser;
extern const AVCodecParser ff_vp9_parser;
extern const AVCodecParser ff_webp_parser;
extern const AVCodecParser ff_xbm_parser;
extern const AVCodecParser ff_xma_parser;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/265846.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git提交代码报错Git: husky > pre-commit

目录 git提交代码报错原因解决方法&#xff08;三种&#xff09;1、第一种2、第二种3、第三种 git提交代码报错原因 这个问题是因为当你在终端输入git commit -m “XXX”,提交代码的时候,pre-commit(客户端)钩子&#xff0c;它会在Git键入提交信息前运行做代码风格检查。如果代…

Linux下安装MySQL 5.6

1、下载二进制安装文件 使用wget下载MySQL 5.6.35二进制安装文件并存放在/root目录下。 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.6.35-linux-glibc2.5-x86_64.tar.gz ll mysql-5.6.35-linux-glibc2.5-x86_64.tar.gz 2、创建mysql用户 先创建mysql…

微信社群机器人开发

简要描述&#xff1a; 删除朋友圈 请求URL&#xff1a; http://域名地址/deleteSns 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId…

集合的几个遍历方法

1. 集合的遍历 1.0 创建集合代码 List<String> strList new ArrayList<>(); strList.add("huawei"); strList.add("xiaomi"); strList.add("tencent"); strList.add("google"); strList.add("baidu");1.1 fo…

初识人工智能,一文读懂贝叶斯优化的知识文集(6)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

【C++】异常 -- 详解

一、C 语言传统的处理错误的方式 传统的错误处理机制&#xff1a; 终止程序&#xff0c;如 assert&#xff0c;缺陷&#xff1a;用户难以接受。如发生内存错误&#xff0c;除 0 错误时就会终止程序。 返回错误码&#xff0c;缺陷&#xff1a;需要程序员自己去查找对应的错误。…

leetcode 每日一题

https://leetcode.cn/problems/invert-binary-tree/submissions/ 这个题目我们的思路其实很简单&#xff0c;遇到空就是得返回空指针&#xff0c;因为要进行交换&#xff0c;但是这里有个小细节&#xff0c;就是我们的把他的左右节点进行保存&#xff0c;我们这里函数接口是有返…

zabbix监控山石防火墙

背景&#xff1a;已经community的情况下&#xff0c;在页面添加好防火墙的监控&#xff0c;发现报错提示timeout&#xff0c;显示不通 在zabbix服务端执行snmpwalk&#xff1a; [rootlocalhost ~]# snmpwalk -v 2c -c hillstone 10.6.0.53 Timeout: No Response from 10.6.…

k8s 如何部署Mysql(史上最权威教程)?

Kuboard K8s 部署Mysql5.7-8.x版本 部署Mysql5.7 在 Kuboard 界面进入名称空间 &#xff08;自己的命令空间&#xff09;&#xff0c;点击 创建工作负载 按钮&#xff0c;并填写表单&#xff0c;如下图所示&#xff1a; 字段名称填写内容工作负载类型有状态副本集&#xff0…

Redis分布式缓存超详细总结!

文章目录 前言一、Redis持久化解决数据丢失问题1.RDB&#xff08;Redis Database Backup file&#xff09;持久化&#xff08;1&#xff09;执行RDB&#xff08;2&#xff09;RDB方式bgsave的基本流程&#xff08;3&#xff09;RDB会在什么时候执行&#xff1f;save 60 1000代表…

JAVA8新特性之函数式编程详解

JAVA8新特性之函数式编程详解 前言一、初步了解函数式接口二、 Lambda表达式2.1 概述2.2 lambda省略规则2.3 lambda省略常见实例2.4 lambda表达式与函数式接口 三、 Stream流3.1 stream流的定义3.2 Stream流的特点3.3 Stream流的三个步骤3.4 Stream 和 Collection 集合的区别&a…

go 编译apk

首先进行安装go 安装 wget https://studygolang.com/dl/golang/go1.21.5.linux-amd64.tar.gz tar zxvf go1.21.5.linux-amd64.tar.gz mv go /usr/local/ vim /etc/profile # 进行配置环境变量&#xff1a; export GOROOT/usr/local/go export PATH$PATH:$GOROOT/bin # 保存退…