[SimplePlayer] 1. 从视频文件中提取图像

在开始之前,我们需要了解视频文件的格式。视频文件的格式众多,无法三言两语就能详细分析其结构,尽管如此,ffmpeg却很好地提取了各类视频文件的共同特性,并对其进行了抽象描述。

image

视频文件格式,统称为container。它包含一个描述视频信息的头部,以及内含实际的音视频编码数据的packets。当然,这里的头部以及packet部分只是个抽象描述,实际的视频格式的描述信息可能不是存放在视频文件的起始位置,可能是由分散于视频文件的各个位置的多个部分组成;数据包有可能是由头部以及尾部进行分割的传统数据包形式,也有可能是一大块数据区域,由索引进行各个数据包的分割。

视频文件中的packets最主要的就是视频以及音频packets,demux的过程就是解析container的header来获取视频信息,所得到的视频信息能帮助我们区分packet是音频或者视频。同样属性的packets会被称为stream。

packet中存储的数据就是音视频编码后的数据,通过解码器进行decode后就能得到视频图像或者音频帧。其中需要注意的一点是,一个packet不一定对应一帧,packet的顺序也不一定是实际的播放顺序,而通过ffmpeg解码出来的frame的顺序就是实际的播放顺序。

 

Demux

首先需要一个用于存储视频文件信息的结构体。

pFormatCtx = avformat_alloc_context();

 

读取视频文件,并对该文件进行demux,所得到的视频信息存储于刚刚所构建的结构体当中

    if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0){fprintf(stderr, "open input failed\n");return -1;}

如果pFormatCtx=NULL,那么avformat_open_input也能自动为pFormatCtx分配存储空间。

 

对于有些视频格式,单单通过demux并不能获得所有的视频信息,为了获得这些信息,还需要读取并尝试解码该视频几个最前端packets(通常会解码每个stream第一个packet)。所读取的这几个packets会被缓存以供后续处理。

if(avformat_find_stream_info(pFormatCtx, NULL)<0){fprintf(stderr, "find stream info failed\n");return -1;}

 

从所获得的信息当中得到video stream序号,后续可以通过stream序号来对packet进行筛选。

videoStream = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

 

 

Decode

创建一个用于存储以及维护解码信息结构体。

pCodecCtx = avcodec_alloc_context3(NULL);

 

把demux时所获得的视频相关信息传递到解码结构体中。

if(avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar)<0){fprintf(stderr, "copy param from format context to codec context failed\n");return -1;}

 

根据解码器id来寻找对应的解码器

pCodec = avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){fprintf(stderr, "Unsupported codec,codec id %d\n", pCodecCtx->codec_id);return -1;}else{fprintf(stdout, "codec id is %d\n", pCodecCtx->codec_id);}

 

打开该解码器,主要目的是对解码器进行初始化

    if(avcodec_open2(pCodecCtx, pCodec, NULL)<0){fprintf(stderr, "open codec failed\n");return -1;}

 

创建一个用于维护所读取的packet的结构体,一个用于维护解码所得的frame的结构体

    pPacket = av_packet_alloc();pFrame = av_frame_alloc();if(pFrame == NULL||pPacket == NULL){fprintf(stderr, "cannot get buffer of frame or packet\n");return -1;}

 

从视频文件中读取packet,如果所读取的packet是video,则进行解码,解码所得的帧由pFrame进行维护。当然,并不是每次调用avcodec_decode_video2都会返回一帧,因为也可能会有需要多个packet才能解码出一帧的情况,因此只有当指示一帧是否解码完成的frameFinished为1才能对这一帧进行后续处理。

    while(av_read_frame(pFormatCtx, pPacket)>=0){//Only deal with the video stream of the type "videoStream"if(pPacket->stream_index==videoStream){//Decode video frameavcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, pPacket);//fprintf(stdout, "Frame : %d ,pts=%lld, timebase=%lf\n", i, pFrame->pts, av_q2d(pFormatCtx->streams[videoStream]->time_base));if(frameFinished){if(i>=START_FRAME && i<=END_FRAME){SaveFrame2YUV(pFrame, pCodecCtx->width, pCodecCtx->height, i);i++;}else{i++;continue;}}}av_packet_unref(pPacket);}

当一个packet被解码后就可以调用av_packet_unref来释放该packet所占用的空间了。

 

 

Store

视频文件解码出来后通常都是YUV格式,Y、U、V三路分量分别存储在AVFrame的data[0]、data[1]、data[2]所指向的内存区域。linesize[0]、linesize[1]、linesize[2]分别指示了Y、U、V一行所占用的字节数。下面把解码所得的帧保存为YUV Planar格式。

void SaveFrame2YUV(AVFrame *pFrame, int width, int height, int iFrame){static FILE *pFile;char szFilename[32];int y;//Open fileif(iFrame==START_FRAME){sprintf(szFilename, "Video.yuv");pFile = fopen(szFilename, "wb");if(pFile==NULL)return;}//Write YUV Data, Only support YUV420//Yfor(y=0; y<height; y++){fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, pFrame->linesize[0], pFile);}//Ufor(y=0; y<(height+1)/2; y++){fwrite(pFrame->data[1]+y*pFrame->linesize[1], 1, pFrame->linesize[1], pFile);}//Vfor(y=0; y<(height+1)/2; y++){fwrite(pFrame->data[2]+y*pFrame->linesize[2], 1, pFrame->linesize[2], pFile);}//Close FIleif(iFrame==END_FRAME){fclose(pFile);}
}

 

 

最后就是释放内存,关闭decoder,关闭demuxer

    av_free(pPacket);av_free(pFrame);avcodec_close(pCodecCtx);avformat_close_input(&pFormatCtx);

转载于:https://www.cnblogs.com/TaigaCon/p/9603854.html

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

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

相关文章

android glide 版本,Android Studio 第六十七期 - Android Glide3.7.0和3.8.0用法

一、前言&#xff1a;再优秀的开源库都有坑要填手上的项目使用的图片加载框架是&#xff1a;Universal-Image-Loader业务需要定制化的一些代码。Universal-Image-Loader 这个框架是一个非常经典好用的框架&#xff0c;唯一的问题是是作者很久之前就不再更新了。所以综合考虑下&…

实例21:python

#猴子吃桃问题&#xff1a;猴子第一天摘下若干个桃子&#xff0c; #当即吃了一半&#xff0c;还不瘾&#xff0c;又多吃了一个第二天早上又将剩下的桃子吃掉一半&#xff0c; #又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。 #到第10天早上想再吃时&#xff0c;见只…

thincmf 分页无法搜索_thinkcmf5 后台搜索分页保持分页条件

本文最后更新于2019-05-16&#xff0c;已超过 1年没有更新&#xff0c;如果文章内容、图片或者下载资源失效&#xff0c;请留言反馈&#xff0c;我会及时处理&#xff0c;谢谢&#xff01;温馨提示&#xff1a;本文共568个字&#xff0c;读完预计2分钟。$where array();$keywo…

Python List insert()方法

Python 列表 描述 insert() 函数用于将指定对象插入列表的指定位置。 语法 insert()方法语法&#xff1a; list.insert(index, obj) 参数 • index – 对象 obj 需要插入的索引位置。 • obj – 要插入列表中的对象。 返回值 该方法没有返回值&#xff0c;但会在列表指定位置插…

android5.1 显示方向,Android5.1 Settings.apk定制显示选项

在Android5.0后&#xff0c;系统应用的目录结构发生了一些变化&#xff0c;以往/system/app/下直接是APK文件&#xff0c;目前是/system/app/应用名目录/应用apk类似这种目录结构。同时在Android5.1上反编译Settings.apk需要使用最新apktool_2.0.3来反编译&#xff0c;否则无法…

db2locate函数_DB2常用函数详解

VALUE函数语法&#xff1a;VALUE(EXPRESSION1,EXPRESSION2)VALUE函数是用返回一个非空的值&#xff0c;当其第一个参数非空&#xff0c;直接返回该参数的值&#xff0c;如果第一个参数为空&#xff0c;则返回第一个参数的值。eg:--表示如果T1.ID为空&#xff0c;则返回空串&…

实例22:python

#题目&#xff1a;两个乒乓球队进行比赛&#xff0c;各出三人。甲队为a,b,c三人&#xff0c; #乙队为x,y,z三人。已抽签决定比赛名单。 #有人向队员打听比赛的名单。a说他不和x比&#xff0c;c说他不和x,z比&#xff0c;请编程序找出三队赛手的名单。 #!/usr/bin/python -- c…

CF468B Two Sets

原题链接 DOWNLOAD AS PDF 题目大意 给出\(n\)个各不相同的数字&#xff0c;将它们分别放入\(A\)和\(B\)两个集合中&#xff0c;使它们满足&#xff1a; 若数字\(x\)在集合\(A\)中&#xff0c;那么数字\(a-x\)也在集合\(A\)中&#xff1b;若数字\(x\)在集合\(B\)中&#xff0c;…

windows c语言 redis,windows上使用VS2012 C++语言调用Redis的解决方案

最近我在VS2012上进行Redis开发遇到一些困扰. 编译Redis的lib库没问题。但是使用这些lib库就不太顺利了. 在网上查来查去也没解决。不过其实Redis的客户端项目RedisCli.vcxproj,就是一个最好的使用例子. 我把几个需要注意的地方贴出来注意事项1. 链接器->输入->忽略所有默…

plantuml 方法图_PlantUML 语法之时序图

plantUML 下载(含《plantUML语法指南手册》)&#xff1a;http://plantuml.com/downloadVisual Studio Code 安装 plantUML 插件&#xff0c;在插件应用商店中搜索&#xff1a;PlantUML&#xff0c;点击安装即可。快捷键&#xff1a;Alt D 即可快速预览&#xff0c;Ctrl Shift…

运维实施面试题

记得不是很清楚了&#xff0c;大概回忆一下 1集线器&#xff0c;路由器。交换机的区别 术语解释   路由器&#xff1a;&#xff08;Router&#xff09;是连接因特网中各局域网、广域网的设备。在路由器中记录着路由表&#xff0c;它会根据信道的情况自动选择和设定路由&#…

CCF 201312-3 最大的矩形[比较简单]

问题描述 试题编号&#xff1a;201312-3试题名称&#xff1a;最大的矩形时间限制&#xff1a;1.0s内存限制&#xff1a;256.0MB问题描述&#xff1a; 问题描述在横轴上放了n个相邻的矩形&#xff0c;每个矩形的宽度是1&#xff0c;而第i&#xff08;1 ≤ i ≤ n&#xff09;个矩…

鸿蒙系统太烂,一加终于觉醒!变相承认系统太烂,魅族的吐槽一针见血

刘作虎宣布&#xff0c;一加9系列出厂预装ColorOS 11&#xff0c;终于证实了外界的猜想&#xff0c;一加终于也要放弃自己的系统&#xff0c;这已经证明了之前的氢系统已经在国内市场上彻底失败。想要收服用户&#xff0c;还是需要本土定制化的应用。不过这也显示出一加手机的决…

安卓开发toolbar设置logo_Android之ToolBar的使用

Toolbar是在 Android 5.0 开始推出的一个 Material Design 风格的导航控件 &#xff0c;Google 非常推荐大家使用 Toolbar 来作为Android客户端的导航栏&#xff0c;以此来取代之前的 Actionbar 。与 Actionbar 相比&#xff0c; Toolbar 明显要灵活的多。它不像 Actionbar 一样…

实例23:python

#题目&#xff1a;打印出如下图案&#xff08;菱形&#xff09;: * *** ***** #******* ***** *** * #先把图形分成两部分来看待&#xff0c;前四行一个规律&#xff0c;后三行一个规律&#xff0c;利用双重for循环&#xff0c;第一层控制行&#xff0c;第二层控制列。…

babel 用法及其 .babelrc 的配置详解,想做前端架构,拒绝一知半解...

Babel 官方介绍&#xff1a;将 ECMAScript 2015 及其版本以后的 javascript 代码转为旧版本浏览器或者是环境中向后兼容版本的 javascript 代码。 简而言之&#xff0c;就是把不兼容的 JavaScript 代码转为可兼容&#xff0c;可以执行的 JavaScript 代码。 功能&#xff1a; 语…

android 滚动尺画到控件中间,android 刻度尺控件实现

主要实现刻度尺的效果&#xff0c;能够快速滑动刻度&#xff0c;设置刻度间距&#xff0c;刻度值&#xff0c;滑动回调。简单易用效果图textureView控件的选择总结来说:1.view的绘制在主线程里面&#xff0c;频繁绘制会导致主线程阻塞2.我们知道一个surfaceview是异步绘制的&am…

实例24:python

#题目&#xff1a;有一分数序列&#xff1a;2/1&#xff0c;3/2&#xff0c;5/3&#xff0c;8/5&#xff0c;13/8&#xff0c;21/13…求出这个数列的前20项之和。 #!/usr/bin/python -- coding: UTF-8 -- a 2.0 b 1.0 s 0 for n in range(1,21): s a / b t a a a b b…

适合新手入门的8个python项目_推荐:一个适合于Python新手的入门练手项目

随着人工智能的兴起&#xff0c;国内掀起了一股Python学习热潮&#xff0c;入门级编程语言&#xff0c;大多选择Python&#xff0c;有经验的程序员&#xff0c;也开始学习Python&#xff0c;正所谓是人生苦短&#xff0c;我用Python有个Python入门练手项目&#xff0c;一直没有…

Luogu 4514 上帝造题的七分钟

二维差分树状数组。 定义差分数组$d_{i, j} a_{i, j} a_{i - 1, j - 1} - a_{i, j - 1} - a_{i - 1, j}$&#xff0c;有$a_{i, j} \sum_{x 1}^{i}\sum_{y 1}^{j}d_{i, j}$。 我们要求$sum(n, m) \sum_{i 1}^{n}\sum_{j 1}^{m}a_{i, j} $&#xff0c; 代入$a_{i, j}$&am…