ffmpeg源码学习笔记五
14.read_frame_internal
static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
{...//初始化packetav_init_packet(pkt);while (!got_packet && !s->parse_queue) {.../* read next packet *//*这个会调用format的read packet.例如mkv的,稍后再对这个API进行深入剖析,这个API每次读取一个block data<==>AVPacketAVInputFormat ff_matroska_demuxer = {....read_packet = matroska_read_packet,...};*/ret = ff_read_packet(s, &cur_pkt);if (ret < 0) {if (ret == AVERROR(EAGAIN))return ret;/* flush the parsers */for (i = 0; i < s->nb_streams; i++) {st = s->streams[i];if (st->parser && st->need_parsing)parse_packet(s, NULL, st->index);}/* all remaining packets are now in parse_queue =>* really terminate parsing */break;}ret = 0;st = s->streams[cur_pkt.stream_index];...
}
15.matroska_read_packet
static int matroska_read_packet(AVFormatContext *s, AVPacket *pkt)
{MatroskaDemuxContext *matroska = s->priv_data;/*从之前读取好的avpacket中,将packet[0] copy给pktstatic int matroska_deliver_packet(MatroskaDemuxContext *matroska,AVPacket *pkt){//第一次读,matroska->num_packets 为0,返回-1if (matroska->num_packets > 0) {//如果num_packets >0 说明有parse好的AVPacket,直接copymemcpy(pkt, matroska->packets[0], sizeof(AVPacket));av_free(matroska->packets[0]);if (matroska->num_packets > 1) {void *newpackets;//如果有多个,把指针数组往前移.memmove(&matroska->packets[0], &matroska->packets[1],(matroska->num_packets - 1) * sizeof(AVPacket *));//把指针数组进行调整,调整前数组大小是num_packets,调整之后,//数组大小为num_packets -1 newpackets = av_realloc(matroska->packets,(matroska->num_packets - 1) *sizeof(AVPacket *));if (newpackets)matroska->packets = newpackets;} else {//如果只有一个,会直接释放数组av_freep(&matroska->packets);matroska->prev_pkt = NULL;}//num减1matroska->num_packets--;return 0;}return -1;}*/while (matroska_deliver_packet(matroska, pkt)) {//第一次读,while条件是-1,需要重新parse cluster,读取avpacket,//下面介绍 matroska_parse_clusterint64_t pos = avio_tell(matroska->ctx->pb);if (matroska->done)return AVERROR_EOF;if (matroska_parse_cluster(matroska) < 0)matroska_resync(matroska, pos);}return 0;
}static int matroska_parse_cluster(MatroskaDemuxContext *matroska)
{.../*static EbmlSyntax matroska_cluster[] = {{ MATROSKA_ID_CLUSTERTIMECODE, EBML_UINT, 0, offsetof(MatroskaCluster, timecode) },{ MATROSKA_ID_BLOCKGROUP, EBML_NEST, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },{ MATROSKA_ID_SIMPLEBLOCK, EBML_PASS, sizeof(MatroskaBlock), offsetof(MatroskaCluster, blocks), { .n = matroska_blockgroup } },{ MATROSKA_ID_CLUSTERPOSITION, EBML_NONE },{ MATROSKA_ID_CLUSTERPREVSIZE, EBML_NONE },{ 0 }};static EbmlSyntax matroska_clusters[] = {{ MATROSKA_ID_CLUSTER, EBML_NEST, 0, 0, { .n = matroska_cluster } },{ MATROSKA_ID_INFO, EBML_NONE },{ MATROSKA_ID_CUES, EBML_NONE },{ MATROSKA_ID_TAGS, EBML_NONE },{ MATROSKA_ID_SEEKHEAD, EBML_NONE },{ 0 }};只需要parse matroska_cluster 的ebml item以及其子ebml item.一个cluster通常包含多个 block group或者 多个simple block.ebml_parse这个API前面有介绍过,现在跳过.*/res = ebml_parse(matroska, matroska_clusters, &cluster);blocks_list = &cluster.blocks;blocks = blocks_list->elem;//对每一个block parse,下面介绍 matroska_parse_blockfor (i = 0; i < blocks_list->nb_elem; i++)if (blocks[i].bin.size > 0 && blocks[i].bin.data) {int is_keyframe = blocks[i].non_simple ? !blocks[i].reference : -1;res = matroska_parse_block(matroska, blocks[i].bin.data,blocks[i].bin.size, blocks[i].bin.pos,cluster.timecode, blocks[i].duration,is_keyframe, NULL, 0, 0, pos,blocks[i].discard_padding);}ebml_free(matroska_cluster, &cluster);return res;
}static int matroska_parse_block(MatroskaDemuxContext *matroska, uint8_t *data,int size, int64_t pos, uint64_t cluster_time,uint64_t block_duration, int is_keyframe,uint8_t *additional, uint64_t additional_id, int additional_size,int64_t cluster_pos, int64_t discard_padding)
{...//找到这个block 中num 对应的track,matroska中,一个block只能对应一个track.//前面在parse track时候,有将track信息保存在matroska中track = matroska_find_track_by_num(matroska, num);if (!track || !track->stream) {av_log(matroska->ctx, AV_LOG_INFO,"Invalid stream %"PRIu64" or size %u\n", num, size);return AVERROR_INVALIDDATA;} else if (size <= 3)return 0;st = track->stream;if (st->discard >= AVDISCARD_ALL)return res; av_assert1(block_duration != AV_NOPTS_VALUE);block_time = sign_extend(AV_RB16(data), 16);//读取block_time 的偏移量//跳过已经parse过的内容data += 2;flags = *data++;size -= 3;if (is_keyframe == -1)is_keyframe = flags & 0x80 ? AV_PKT_FLAG_KEY : 0;//根据cluster time和 block_time(偏移量) 计算当前block的 timecode.if (cluster_time != (uint64_t) -1 &&(block_time >= 0 || cluster_time >= -block_time)) {timecode = cluster_time + block_time - track->codec_delay;if (track->type == MATROSKA_TRACK_TYPE_SUBTITLE &&timecode < track->end_timecode)is_keyframe = 0; /* overlapping subtitles are not key frame */if (is_keyframe)av_add_index_entry(st, cluster_pos, timecode, 0, 0,AVINDEX_KEYFRAME);}...//parse lace,带蕾丝是ebml里的一种比较常见的数据储存.通常将多个frame,放在一个block中//每个frame 由一个蕾丝组成,这个API就是找到每个蕾丝的边界//有3中蕾丝类型.具体参考matroska官方文档. 如果不带lace,//可以将这个block当着一个lace.res = matroska_parse_laces(matroska, &data, &size, (flags & 0x06) >> 1,&lace_size, &laces);...//对每个lace parsefor (n = 0; n < laces; n++) {//计算每个lace的durationint64_t lace_duration = block_duration*(n+1) / laces - block_duration*n / laces;if (lace_size[n] > size) {av_log(matroska->ctx, AV_LOG_ERROR, "Invalid packet size\n");break;}if ((st->codec->codec_id == AV_CODEC_ID_RA_288 ||st->codec->codec_id == AV_CODEC_ID_COOK ||st->codec->codec_id == AV_CODEC_ID_SIPR ||st->codec->codec_id == AV_CODEC_ID_ATRAC3) &&st->codec->block_align && track->audio.sub_packet_size) {res = matroska_parse_rm_audio(matroska, track, st, data,lace_size[n],timecode, pos);if (res)goto end;} else if (st->codec->codec_id == AV_CODEC_ID_WEBVTT) {res = matroska_parse_webvtt(matroska, track, st,data, lace_size[n],timecode, lace_duration,pos);if (res)goto end;} else {//接下来介绍 matroska_parse_frameres = matroska_parse_frame(matroska, track, st, data, lace_size[n],timecode, lace_duration, pos,!n ? is_keyframe : 0,additional, additional_id, additional_size,discard_padding);if (res)goto end;}...}...
}static int matroska_parse_frame(MatroskaDemuxContext *matroska,MatroskaTrack *track, AVStream *st,uint8_t *data, int pkt_size,uint64_t timecode, uint64_t lace_duration,int64_t pos, int is_keyframe,uint8_t *additional, uint64_t additional_id, int additional_size,int64_t discard_padding)
{MatroskaTrackEncoding *encodings = track->encodings.elem;uint8_t *pkt_data = data;int offset = 0, res;AVPacket *pkt;//有些block data数据是压缩的,在送到decoder之前,需要解压缩,需要预处理//是否压缩,以及压缩类型,在read_header 中都能获取if (encodings && !encodings->type && encodings->scope & 1) {res = matroska_decode_buffer(&pkt_data, &pkt_size, track);if (res < 0)return res;}...//特定的codec类型处理...//分配AVPacketpkt = av_mallocz(sizeof(AVPacket));/* XXX: prevent data copy... */if (av_new_packet(pkt, pkt_size + offset) < 0) {av_free(pkt);res = AVERROR(ENOMEM);goto fail;}if (st->codec->codec_id == AV_CODEC_ID_PRORES && offset == 8) {uint8_t *buf = pkt->data;bytestream_put_be32(&buf, pkt_size);bytestream_put_be32(&buf, MKBETAG('i', 'c', 'p', 'f'));}memcpy(pkt->data + offset, pkt_data, pkt_size);...//一坨子操作就是填充AVPacket...//将pkt添加到matroska->packets,同时matroska->num_packets 加1//经过以上操作,才得到matroska->num_packets > 0,这样最开始的那个while循环,才能拿到一个//AVPacket,退出循环dynarray_add(&matroska->packets, &matroska->num_packets, pkt);matroska->prev_pkt = pkt;return 0;...
}
ffmpeg源码学习笔记五相关推荐
- 雷神FFMpeg源码学习笔记
雷神FFMpeg源码学习笔记 文章目录 雷神FFMpeg源码学习笔记 读取编码并依据编码初始化内容结构 每一帧的视频解码处理 读取编码并依据编码初始化内容结构 在开始编解码视频的时候首先第一步需要注册 ...
- ffmpeg源码学习笔记三
9. 关于如何parse mkv 前面为了不把战线拉太长,把如何parse mkv container 内容直接跳过了 接下来还是从read_header 开始讲解 static int matros ...
- Vuex 4源码学习笔记 - 通过dispatch一步步来掌握Vuex整个数据流(五)
在上一篇笔记中:Vuex 4源码学习笔记 - Store 构造函数都干了什么(四) 我们通过查看Store 构造函数的源代码可以看到主要做了三件事情: 初始化一些内部变量以外 执行installMod ...
- Java多线程之JUC包:Semaphore源码学习笔记
若有不正之处请多多谅解,并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/go2sea/p/5625536.html Semaphore是JUC ...
- jquery源码学习笔记三:jQuery工厂剖析
jquery源码学习笔记二:jQuery工厂 jquery源码学习笔记一:总体结构 上两篇说过,query的核心是一个jQuery工厂.其代码如下 function( window, noGlobal ...
- RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的?
RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 文章目录 RocketMQ 源码学习笔记 Producer 是怎么将消息发送至 Broker 的? 前言 项目 ...
- Vuex 4源码学习笔记 - 通过Vuex源码学习E2E测试(十一)
在上一篇笔记中:Vuex 4源码学习笔记 - 做好changelog更新日志很重要(十) 我们学到了通过conventional-changelog来生成项目的Changelog更新日志,通过更新日志 ...
- Vuex 4源码学习笔记 - Vuex是怎么与Vue结合?(三)
在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二) 我们通过运行npm run dev命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个 ...
- Apache log4j-1.2.17源码学习笔记
(1)Apache log4j-1.2.17源码学习笔记 http://blog.csdn.net/zilong_zilong/article/details/78715500 (2)Apache l ...
最新文章
- [android] 练习使用ListView(一)
- 【编译原理】递归下降的预测分析(真の能看懂~!)
- Java中的注解以及应用 @Deprecated @SupressWarning @Override
- 关于Netty的一些理解、实践与陷阱
- BIRT:基于 Eclipse 的报表
- 《配置管理最佳实践》——1.2 从哪里开始
- 力软敏捷开发框架真正源码_敏捷真正使谁受益?
- 【java笔记】java语言的跨平台性和运行环境
- 资源管理器和计算机的功能基本相同吗,“资源管理器”和“计算机”的功能基本相同...
- Logs Viewer
- GridView commandname
- PHP第一季视频教程.李炎恢.学习笔记(五)(第3章 操作符与控制结构(1)(2))
- 通过写《费用明细表》发现写sql的乐趣
- 爆款养成思路,教你利用砍价做出刷屏活动!
- 关闭“Chromium 未正确关闭”提示
- 首个“中国籍”曲妥珠单抗于欧盟获批上市
- liferay的控制条docbar消失解决方法
- DNS劫持,HTTP劫持、HTTPS劫持【流量劫持】
- HackerEarth, The Grass Type (dsu on tree)
- 使用xlsx.utils.js前端导出excel