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源码学习笔记三相关推荐

  1. 雷神FFMpeg源码学习笔记

    雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...

  2. jquery源码学习笔记三:jQuery工厂剖析

    jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...

  3. ffmpeg源码学习笔记五

    14.read_frame_internal static int read_frame_internal(AVFormatContext *s, AVPacket *pkt) {...//初始化pa ...

  4. Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)

    在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二) 我们通过运行npm run dev命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个 ...

  5. Java多线程之JUC包:Semaphore源码学习笔记

    若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...

  6. Vuex 4源码学习笔记 - 通过dispatch一步步来掌握Vuex整个数据流(五)

    在上一篇笔记中:Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) 我们通过查看Store 构造函数的源代码可以看到主要做了三件事情: 初始化一些内部变量以外 执行installMod ...

  7. RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?

    RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...

  8. 基于Qt5.14.2和mingw的Qt源码学习(三) — 元对象系统简介及moc工具是如何保存类属性和方法的

    基于Qt5.14.2和mingw的Qt源码学习(三) - 元对象系统简介及moc工具是如何保存类属性和方法的 一.什么是元对象系统 1.元对象系统目的 2.实现元对象系统的关键 3.元对象系统的其他一 ...

  9. Vuex 4源码学习笔记 - 通过Vuex源码学习E2E测试(十一)

    在上一篇笔记中:Vuex 4源码学习笔记 - 做好changelog更新日志很重要(十) 我们学到了通过conventional-changelog来生成项目的Changelog更新日志,通过更新日志 ...

最新文章

  1. [洛谷P1317]低洼地
  2. String、StringBuffer、StringBuilder源码解析
  3. 2009年出现的计算机术语,2009年计算机一级考试真题及答案
  4. hdu 1584蜘蛛牌(DFS)
  5. SQLite 语法(http://www.w3cschool.cc/sqlite/sqlite-syntax.html)
  6. python程序详细描述_如何逐行描述Python代码?
  7. HALCON示例程序sequence_diff.hdev通过两张连续图像进行车辆流量监控
  8. Q: 为什么如果我们把这两个组件安装在同一个COM+组件包中问题就不会出现(zz)...
  9. API 日调用量超 100 亿次!腾讯云首次披露云原生产品数据
  10. C语言 · 9-1九宫格
  11. 转!!URL和URI区别
  12. 洛谷—— P2251 质量检测
  13. 高新计算机考试1-8视频,最新版计算机高新考试 PS 第1单元1-8.doc
  14. Linux桌面没有minidwep,ubuntu安装minidwep-gtk
  15. 最新微信公众号怎么申请?
  16. bmp180气压传感器工作原理_40张动图揭示各种传感器工作原理
  17. 海康设备接入EasyCVR,出现告警信息缺失且不同步该如何解决?
  18. 使用@Aspect不起作用
  19. keepalived脑裂现象
  20. 138.深度学习分布式计算框架-1

热门文章

  1. 用按键精灵,写个简单的跳课脚本
  2. 广东虚拟服务器,广东虚拟主机云服务器
  3. 基于DirectShow和FFmpeg的USB摄像头监控软件-转
  4. 一,JavaScript基本语法
  5. Unity 可视化编辑工具 树节点 Tree Node Editor 四
  6. 易语言 图片插入超级列表框_利用PPT制作一个图片抽奖
  7. 【愚公系列】2022年03月 微信小程序-富文本编辑器
  8. 怎么设置永磁同步电机的电流控制角
  9. Flink菜鸟教程(一)——从入门到开发
  10. 将百分制成绩转化为5分制成绩。