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

视频文件格式,统称为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 frame
avcodec_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 file
if(iFrame==START_FRAME){
sprintf(szFilename, "Video.yuv");
pFile = fopen(szFilename, "wb");
if(pFile==NULL)
return;
}
//Write YUV Data, Only support YUV420
//Y
for(y=0; y<height; y++){
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, pFrame->linesize[0], pFile);
}
//U
for(y=0; y<(height+1)/2; y++){
fwrite(pFrame->data[1]+y*pFrame->linesize[1], 1, pFrame->linesize[1], pFile);
}
//V
for(y=0; y<(height+1)/2; y++){
fwrite(pFrame->data[2]+y*pFrame->linesize[2], 1, pFrame->linesize[2], pFile);
}
//Close FIle
if(iFrame==END_FRAME){
fclose(pFile);
}
}
最后就是释放内存,关闭decoder,关闭demuxer
av_free(pPacket);
av_free(pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
[SimplePlayer] 1. 从视频文件中提取图像的更多相关文章
- [SimplePlayer] 4. 从视频文件中提取音频
提取音频,具体点来说就是提取音频帧.提取方法与从视频文件中提取图像的方法基本一样,这里仅列出其中的不同点: 1. 由于目的提取音频,因此在demux的时候需要指定的是提取audio stream Au ...
- 从ROS bag文件中提取图像
从ROS bag文件中提取图像 创建launch文件,如下: export.launch <launch> <node pkg="rosbag" type=&qu ...
- (转载)[FFmpeg]使用ffmpeg从各种视频文件中直接截取视频图片
你曾想过从一个视频文件中提取图片吗?在Linux下就可以,在这个教程中我将使用ffmpeg来从视频中获取图片. 什么是ffmpeg?What is ffmpeg? ffmpeg是一个非常有用的命令行程 ...
- 从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中
//从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中 //作者:sandy //时间:2015-10-10 #inclu ...
- 利用ROS工具从bag文件中提取图片
bag文件是ROS常用的数据存储格式,因此要从bag文件中提取数据就需要了解一点ROS的背景知识. 1. 什么是ROS及其优势 ROS全称Robot Operating System,是BSD-lic ...
- [转]【流媒體】H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流
[流媒體]H264—MP4格式及在MP4文件中提取H264的SPS.PPS及码流 SkySeraph Apr 1st 2012 Email:skyseraph00@163.com 一.MP4格式基本 ...
- 使用Python从PDF文件中提取数据
前言 数据是数据科学中任何分析的关键,大多数分析中最常用的数据集类型是存储在逗号分隔值(csv)表中的干净数据.然而,由于可移植文档格式(pdf)文件是最常用的文件格式之一,因此每个数据科学家都应该了 ...
- [数据科学] 从csv, xls文件中提取数据
在python语言中,用丰富的函数库来从文件中提取数据,这篇博客讲解怎么从csv, xls文件中得到想要的数据. 点击下载数据文件http://seanlahman.com/files/databas ...
- JSFinder:一个在js文件中提取URL和子域名的脚本
JSFinder介绍 JSFinder是一款用作快速在网站的js文件中提取URL,子域名的脚本工具. 支持用法 简单爬取 深度爬取 批量指定URL/指定JS 其他参数 以往我们子域名多数使用爆破或DN ...
随机推荐
- 43.Odoo产品分析 (四) – 工具板块(11) – 网站即时聊天(1)
查看Odoo产品分析系列--目录 在线聊天可以实现与顾客的在线实时交流,比如在"商店"功能中实现顾客对客服的商品咨询等类似的操作. 安装"网站即时聊天"模块: ...
- 36.Odoo产品分析 (四) – 工具板块(6) – 午餐管理(2)
查看Odoo产品分析系列--目录 接上一篇Odoo产品分析 (四) – 工具板块(6) – 午餐管理(1) 4 查看订单 点击"之前的订单",可以看到刚才的订单信息: 点击右边的 ...
- JavaScript中的闭包和作用域链
这部分几乎是JavaScript中最难的部分,也是面试官最爱问的地方. 下面的内容是我以前写的<JavaScript学习手册>中被客户删除的部分,理由听起来有点诡异:太难.
- 执行C#动态代码
执行C#动态代码 using System; using System.Data; using System.Configuration; using System.Text; using Syste ...
- Left Jion等价SQL猜想验证
猜想:以下两条SQL等价 select * from A left join B on A.ID=B.BID and B.BName=N'小明' select * from A left join ( ...
- 浅谈TCP IP协议栈(二)IP地址
上一节大致了解TCP/IP协议栈是个啥东西,依旧是雾里看花的状态,有很多时候学一门新知识时,开头总是很急躁,无从下手,刚学会一点儿,却发现连点皮毛都不算,成就感太低,所以任何时候学习最重要的是要在合适 ...
- Linux学习历程——Centos 7 grep命令
一.命令简介 grep 命令用于在文本中执行关键词搜索,并显示匹配的结果. 由于grep命令参数很多,这里只列出一些常用的参数. 参数 作用 -b 将可执行文件当作文本文件来搜索 -c 仅显示找到的行 ...
- Zookeeper与Kafka基础概念和原理
1.zookeeper概念介绍 在介绍ZooKeeper之前,先来介绍一下分布式协调技术,所谓分布式协调技术主要是用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种共享资源,防止造成 ...
- LeetCode算法题-Minimum Index Sum of Two Lists(Java实现)
这是悦乐书的第272次更新,第286篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第139题(顺位题号是599).假设Andy和Doris想要选择一家餐馆吃晚餐,他们都有 ...
- LeetCode算法题-Perfect Number(Java实现)
这是悦乐书的第249次更新,第262篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第116题(顺位题号是507).我们定义Perfect Number是一个正整数,它等于 ...