ffmpeg源码学习笔记三
9. 关于如何parse mkv
前面为了不把战线拉太长,把如何parse mkv container 内容直接跳过了
接下来还是从read_header 开始讲解
static int matroska_read_header(AVFormatContext *s)
{...//mkv整个文件是以EBML形式存储的,如果对matroska不熟悉,可以去官网下载文档阅读//multimedia文件的存放,从segment开始,前面EBML可以略过/* The next thing is a segment. *//*首先看一下EbmlSyntax 结构体,是一个链表结构typedef const struct EbmlSyntax {//ID,EBML特殊标志符, 例如//#define MATROSKA_ID_TRACKS 0x1654AE6B 表示接下来存储的是track信息//这是matroska官方定义的标准uint32_t id;//type这是FFMPEG自己定义的,表示ebml 类型,例如 EBML_STOP //表示ebml_parse函数parse到这种类型,就结束parse动作//对应于 res = ebml_parse(matroska, matroska_segments, matroska);返回1//EBML_NEST表示有嵌套.EbmlType type;//当前ebml 的size. ebml是树状结构,表示当前节点的sizeint list_elem_size;//距离文件头偏移量int data_offset;union {int64_t i;uint64_t u;double f;const char *s;const struct EbmlSyntax *n;} def;//当前节点的值,如果} EbmlSyntax; 例如:static const EbmlSyntax matroska_seekhead[] = {{ MATROSKA_ID_SEEKENTRY, EBML_NEST, sizeof(MatroskaSeekhead), offsetof(MatroskaDemuxContext, seekhead), { .n = matroska_seekhead_entry } },{ 0 }};static const EbmlSyntax matroska_segment[] = {{ MATROSKA_ID_INFO, EBML_LEVEL1, 0, 0, { .n = matroska_info } },{ MATROSKA_ID_TRACKS, EBML_LEVEL1, 0, 0, { .n = matroska_tracks } },{ MATROSKA_ID_ATTACHMENTS, EBML_LEVEL1, 0, 0, { .n = matroska_attachments } },{ MATROSKA_ID_CHAPTERS, EBML_LEVEL1, 0, 0, { .n = matroska_chapters } },{ MATROSKA_ID_CUES, EBML_LEVEL1, 0, 0, { .n = matroska_index } },{ MATROSKA_ID_TAGS, EBML_LEVEL1, 0, 0, { .n = matroska_tags } },{ MATROSKA_ID_SEEKHEAD, EBML_LEVEL1, 0, 0, { .n = matroska_seekhead } },{ MATROSKA_ID_CLUSTER, EBML_STOP },{ 0 }};static const EbmlSyntax matroska_segments[] = {{ MATROSKA_ID_SEGMENT, EBML_NEST, 0, 0, { .n = matroska_segment } },{ 0 }};*///获取当前读的位置,因为前面有读/parse EBML段信息,pos应该就是从segement开始位置pos = avio_tell(matroska->ctx->pb);//parse 顶层segment信息res = ebml_parse(matroska, matroska_segments, matroska);//下面循环parse segment子节点信息,也就是matroska_segment 中的信息,parse到cluster结束.//cluster中保存的是media data信息,是播放时候parse的.//ebml_parse 过程,就是填写 MatroskaDemuxContext 过程// try resyncing until we find a EBML_STOP type element.while (res != 1) {res = matroska_resync(matroska, pos);if (res < 0)return res;pos = avio_tell(matroska->ctx->pb);//如果遇到EBML_STOP,返回1,跳出循环.res = ebml_parse(matroska, matroska_segment, matroska);}//读取seekhead信息,seekhead里面保存level_1层head的索引/*原因是matroska 并没有要求所有的头信息都在文件最开始,有些重要的信息可能在cluster后面,之前parse 到cluster就结束了,导致无法拿到全部的level_1层信息,所以就需要通过seekhead seek 到相应位置(cluster之后)去parse 一些信息.static void matroska_execute_seekhead(MatroskaDemuxContext *matroska){EbmlList *seekhead_list = &matroska->seekhead;int i;// we should not do any seeking in the streaming caseif (!matroska->ctx->pb->seekable)return;for (i = 0; i < seekhead_list->nb_elem; i++) {MatroskaSeekhead *seekheads = seekhead_list->elem;uint32_t id = seekheads[i].id;uint64_t pos = seekheads[i].pos;//从seekheaders中判断是否是lvl1层的elem,如果不是,或者parse过了,就直接跳过MatroskaLevel1Element *elem = matroska_find_level1_elem(matroska, id);if (!elem || elem->parsed)continue;elem->pos = pos;// defer cues parsing until we actually need cue data.//暂且不需要parse cue data(保存key frame data,这块数据量比较大)if (id == MATROSKA_ID_CUES)continue;//这个API就是直接跳到seekheads[i].pos位置去parse相应的信息,信息还是保存在//matroska中if (matroska_parse_seekhead_entry(matroska, pos) < 0) {// mark index as brokenmatroska->cues_parsing_deferred = -1;break;}elem->parsed = 1;}}*/matroska_execute_seekhead(matroska);if (!matroska->time_scale)matroska->time_scale = 1000000;if (matroska->duration)//计算durationmatroska->ctx->duration = matroska->duration * matroska->time_scale *1000 / AV_TIME_BASE;//s->metadata为NULL,在av_dict_set分配memory.av_dict_set(&s->metadata, "title", matroska->title, 0);av_dict_set(&s->metadata, "encoder", matroska->muxingapp, 0);...//parse track 信息,因为前面只是件ebml 信息保存在matroska->track中,现在需要parse出//每个栏位的具体信息/*处理一些track信息,这里信息也蛮多的,接下来介绍*/res = matroska_parse_tracks(s);if (res < 0)return res;//后面再parse attachments和chapter, 大部分文件都没有attachments和chapter 信息...}
10. matroska_parse_tracks 函数
static int matroska_parse_tracks(AVFormatContext *s)
{...for (i = 0; i < matroska->tracks.nb_elem; i++) {.../* Apply some sanity checks. */if (track->type != MATROSKA_TRACK_TYPE_VIDEO &&track->type != MATROSKA_TRACK_TYPE_AUDIO &&track->type != MATROSKA_TRACK_TYPE_SUBTITLE &&track->type != MATROSKA_TRACK_TYPE_METADATA) {av_log(matroska->ctx, AV_LOG_INFO,"Unknown or unsupported track type %"PRIu64"\n",track->type);continue;}//如果为NULL,skipif (!track->codec_id)continue;...if (track->type == MATROSKA_TRACK_TYPE_VIDEO) {//如果default_duration是0,计算default_durationif (!track->default_duration && track->video.frame_rate > 0) {double default_duration = 1000000000 / track->video.frame_rate;if (default_duration > UINT64_MAX || default_duration < 0) {av_log(matroska->ctx, AV_LOG_WARNING,"Invalid frame rate %e. Cannot calculate default duration.\n",track->video.frame_rate);} else {track->default_duration = default_duration;}}//填宽高if (track->video.display_width == -1)track->video.display_width = track->video.pixel_width;if (track->video.display_height == -1)track->video.display_height = track->video.pixel_height;if (track->video.color_space.size == 4)fourcc = AV_RL32(track->video.color_space.data);} else if (track->type == MATROSKA_TRACK_TYPE_AUDIO) {if (!track->audio.out_samplerate)track->audio.out_samplerate = track->audio.samplerate;}...//处理一些头解压,一般很少会将头压缩...//之前保存在track->codec_id 是char*, 转化为ffmpeg内部的codec类型//char* -->enumfor (j = 0; ff_mkv_codec_tags[j].id != AV_CODEC_ID_NONE; j++) {if (!strncmp(ff_mkv_codec_tags[j].str, track->codec_id,strlen(ff_mkv_codec_tags[j].str))) {codec_id = ff_mkv_codec_tags[j].id;break;}}//alloc AVStream,之前有介绍st = track->stream = avformat_new_stream(s, NULL);if (!st) {av_free(key_id_base64);return AVERROR(ENOMEM);}//从codec_private信息中,parse一些信息,//codec_private信息存放一些decoder需要的信息,这些信息比较多,有些还搞不清楚,等以后//有时间再研究....
}
ffmpeg源码学习笔记三相关推荐
- 雷神FFMpeg源码学习笔记
雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...
- jquery源码学习笔记三:jQuery工厂剖析
jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...
- ffmpeg源码学习笔记五
14.read_frame_internal static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {...//初始化pa ...
- Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)
在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二) 我们通过运行npm run dev命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个 ...
- Java多线程之JUC包:Semaphore源码学习笔记
若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...
- Vuex 4源码学习笔记 - 通过dispatch一步步来掌握Vuex整个数据流(五)
在上一篇笔记中:Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) 我们通过查看Store 构造函数的源代码可以看到主要做了三件事情: 初始化一些内部变量以外 执行installMod ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...
- 基于Qt5.14.2和mingw的Qt源码学习(三) — 元对象系统简介及moc工具是如何保存类属性和方法的
基于Qt5.14.2和mingw的Qt源码学习(三) - 元对象系统简介及moc工具是如何保存类属性和方法的 一.什么是元对象系统 1.元对象系统目的 2.实现元对象系统的关键 3.元对象系统的其他一 ...
- Vuex 4源码学习笔记 - 通过Vuex源码学习E2E测试(十一)
在上一篇笔记中:Vuex 4源码学习笔记 - 做好changelog更新日志很重要(十) 我们学到了通过conventional-changelog来生成项目的Changelog更新日志,通过更新日志 ...
最新文章
- [洛谷P1317]低洼地
- String、StringBuffer、StringBuilder源码解析
- 2009年出现的计算机术语,2009年计算机一级考试真题及答案
- hdu 1584蜘蛛牌(DFS)
- SQLite 语法(http://www.w3cschool.cc/sqlite/sqlite-syntax.html)
- python程序详细描述_如何逐行描述Python代码?
- HALCON示例程序sequence_diff.hdev通过两张连续图像进行车辆流量监控
- Q: 为什么如果我们把这两个组件安装在同一个COM+组件包中问题就不会出现(zz)...
- API 日调用量超 100 亿次!腾讯云首次披露云原生产品数据
- C语言 · 9-1九宫格
- 转!!URL和URI区别
- 洛谷—— P2251 质量检测
- 高新计算机考试1-8视频,最新版计算机高新考试 PS 第1单元1-8.doc
- Linux桌面没有minidwep,ubuntu安装minidwep-gtk
- 最新微信公众号怎么申请?
- bmp180气压传感器工作原理_40张动图揭示各种传感器工作原理
- 海康设备接入EasyCVR,出现告警信息缺失且不同步该如何解决?
- 使用@Aspect不起作用
- keepalived脑裂现象
- 138.深度学习分布式计算框架-1