FFPLAY的原理(一)

概要

电影文件有很多基本的组成部分。首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置。AVI和Quicktime就是容器的例子。接着,你有一组流,例如,你经常有的是一个音频流和一个视频流。(一个流只是一种想像出来的词语,用来表示一连串的通过时间来串连的数据元素)。在流中的数据元素被称为帧Frame。每个流是由不同的编码 器来编码生成的。编解码器 描述了实际的数据是如何被编码Coded和解码DECoded的,因此它的名字叫做CODEC。Divx和 MP3就是编解码器的例子。接着从流中被读出来的叫做包Packets。包是一段数据,它包含了一段可以被解码成方便我们最后在应用程序中操作的原始帧的数据。根据我们的目的,每个包包含了完整的帧或者对于音频来说是许多格式的完整帧。

基本上来说,处理视频和音频流是很容易的:

10 从video.avi文件中打开视频流video_stream

20 从视频流中读取包到帧中

30 如果这个帧还不完整,跳到20

40 对这个帧进行一些操作

50 跳回到20

在这个程序中使用ffmpeg来处理多种媒体是相当容易的,虽然很多程序可能在对帧进行操作的时候非常的复杂。因此在这篇指导中,我们将打开一个文件,读取里面的视频流,而且我们对帧的操作将是把这个帧写到一个PPM文件中。

打开文件

首先,来看一下我们如何打开一个文件。通过ffmpeg,你必需先初始化这个库。(注意在某些系统中必需用<ffmpeg/avcodec.h>和<ffmpeg/avformat.h>来替换)

#include <avcodec.h>

#include <avformat.h>

...

int main(int argc, charg *argv[]) {

av_register_all();

这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用 av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要这样做。

现在我们可以真正的打开文件:

AVFormatContext *pFormatCtx;

// Open video file

if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)

return -1; // Couldn't open file

我们通过第一个参数来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。

这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息:

// Retrieve stream information

if(av_find_stream_info(pFormatCtx)<0)

return -1; // Couldn't find stream information

这个函数为pFormatCtx->streams填充上正确的信息。我们引进一个手工调试的函数来看一下里面有什么:

// Dump information about file onto standard error

dump_format(pFormatCtx, 0, argv[1], 0);

现在pFormatCtx->streams仅仅是一组大小为pFormatCtx->nb_streams的指针,所以让我们先跳过它直到我们找到一个视频流。

int i;

AVCodecContext *pCodecCtx;

// Find the first video stream

videoStream=-1;

for(i=0; i<pFormatCtx->nb_streams; i++)

if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) {

videoStream=i;

break;

}

if(videoStream==-1)

return -1; // Didn't find a video stream

// Get a pointer to the codec context for the video stream

pCodecCtx=pFormatCtx->streams[videoStream]->codec;

流中关于编解码器的信息就是被我们叫做"codec context"(编解码器上下文)的东西。这里面包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。但是我们必需要找到真正的编解码器并且打开它:

AVCodec *pCodec;

// Find the decoder for the video stream

pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

if(pCodec==NULL) {

fprintf(stderr, "Unsupported codec!/n");

return -1; // Codec not found

}

// Open codec

if(avcodec_open(pCodecCtx, pCodec)<0)

return -1; // Could not open codec

有些人可能会从旧的指导中记得有两个关于这些代码其它部分:添加CODEC_FLAG_TRUNCATED到 pCodecCtx->flags和添加一个hack来粗糙的修正帧率。这两个修正已经不在存在于ffplay.c中。因此,我必需假设它们不再必要。我们移除了那些代码后还有一个需要指出的不同点:pCodecCtx->time_base现在已经保存了帧率的信息。time_base是一个结构体,它里面有一个分子和分母 (AVRational)。我们使用分数的方式来表示帧率是因为很多编解码器使用非整数的帧率(例如NTSC使用29.97fps)。

保存数据

现在我们需要找到一个地方来保存帧:

AVFrame *pFrame;

// Allocate video frame

pFrame=avcodec_alloc_frame();

因为我们准备输出保存24位RGB色的PPM文件,我们必需把帧的格式从原来的转换为RGB。FFMPEG将为我们做这些转换。在大多数项目中(包括我们的这个)我们都想把原始的帧转换成一个特定的格式。让我们先为转换来申请一帧的内存。

// Allocate an AVFrame structure

pFrameRGB=avcodec_alloc_frame();

if(pFrameRGB==NULL)

return -1;

即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始的数据。我们使用avpicture_get_size来获得我们需要的大小,然后手工申请内存空间:

uint8_t *buffer;

int numBytes;

// Determine required buffer size and allocate buffer

numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,

pCodecCtx->height);

buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

av_malloc是ffmpeg的malloc,用来实现一个简单的malloc的包装,这样来保证内存地址是对齐的(4字节对齐或者2字节对齐)。它并不能保护你不被内存泄漏,重复释放或者其它malloc的问题所困扰。

现在我们使用avpicture_fill来把帧和我们新申请的内存来结合。关于AVPicture的结成:AVPicture结构体是AVFrame结构体的子集――AVFrame结构体的开始部分与AVPicture结构体是一样的。

// Assign appropriate parts of buffer to image planes in pFrameRGB

// Note that pFrameRGB is an AVFrame, but AVFrame is a superset

// of AVPicture

avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,

pCodecCtx->width, pCodecCtx->height);

最后,我们已经准备好来从流中读取数据了。

读取数据

我们将要做的是通过读取包来读取整个视频流,然后把它解码成帧,最好后转换格式并且保存。

int frameFinished;

AVPacket packet;

i=0;

while(av_read_frame(pFormatCtx, &packet)>=0) {

// Is this a packet from the video stream?

if(packet.stream_index==videoStream) {

// Decode video frame

avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,

packet.data, packet.size);

// Did we get a video frame?

if(frameFinished) {

// Convert the image from its native format to RGB

img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24,

(AVPicture*)pFrame, pCodecCtx->pix_fmt,

pCodecCtx->width, pCodecCtx->height);

// Save the frame to disk

if(++i<=5)

SaveFrame(pFrameRGB, pCodecCtx->width,

pCodecCtx->height, i);

}

}

// Free the packet that was allocated by av_read_frame

av_free_packet(&packet);

}

这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到AVPacket结构体中。注意我们仅仅申请了一个包的结构体 ――ffmpeg为我们申请了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过av_free_packet()来释放。函数avcodec_decode_video()把包转换为帧。然而当解码一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得到下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。最后,我们使用 img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转换成为RGB格式。要记住,你可以把一个 AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。

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

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

相关文章

安卓开发 新浪微博share接口实现发带本地图片的微博

1.微博share接口 在开始之前&#xff0c;我们先看一下要用到的这个接口&#xff1a; 我们这次是要上传本地图片&#xff0c;可以很明确的知道&#xff0c;除了要用POST方式提交请求&#xff0c;还要采用multipart/form-data编码方式。 那么这个multipart/form-data编码方式是什…

python编写装饰器_我也来写一下python装饰器

有借用&#xff0c;但原文出处已经找不到了&#xff0c;根据笔记分享一下解释器的基础。下面的代码表示&#xff0c;等待两秒钟&#xff0c;输出‘test is running。现在要求增加统计程序运行时间的功能。等待两秒钟&#xff0c;输出‘test is running,现要求增加统计程序运行时…

VirtualBox安装Centos6.8出现——E_INVALIDARG (0x80070057)

VirtualBox使用已有的虚拟硬盘出错&#xff1a; 问题描述&#xff1a;UUID已经存在 Cannot register the hard disk E:\system_iso\centos6.8.vdi {05f096aa-67fc-4191-983d-1ed00fc6cce9} because a hard disk E:\system_iso\centos68_02\centos6.8.vdi with UUID {05f096aa-6…

DFT 与 ATPG综 述

DFT 可测试性设计 工程会接触 DFT。需要了解 DFT 知识&#xff0c;但不需要深入。 三种基本的测试&#xff08;概念来自参考文档&#xff09;&#xff1a; 边界扫描测试&#xff1a;Boundary Scan Test: 测试目标是 IO-PAD&#xff0c;利用 JTAG 接口互连以方便 测试。&#x…

非线性动力学_非线性动力学特辑 低维到高维的联通者

序言&#xff1a; 本文将以维度为主线&#xff0c; 带量大家进入非线性动力学的世界。 文章数学部分不需要全部理解&#xff0c; 理解思维方法为主非线性动力学&#xff0c;是物理学的思维进入传统方法所不能解决的问题的一座丰碑。它可以帮助我们理解不同复杂度和时间空间尺度…

Go语言channel与select原理

本文会尝试解释 go runtime 中 channel 和 select 的具体实现&#xff0c;部分内容来自 gophercon2017。Go版本为1.8.3channel 第一部分讲述一下 channel 的用法。channel 可以看做一个队列&#xff0c;用于多个goroutine之间的通信&#xff0c;例如下面的例子&#xff0c;一个…

Xadmin添加用户小组件出错

环境&#xff1a; Python 3.5.6 Django 2.1 Xadmin 原因&#xff1a; render函数在django2.1上有变化 解决方案&#xff1a; 1.在Python终端输入命令help(xadmin) 查看xadmin安装位置 得到如下输出 FILE/root/anaconda3/envs/learndjango/lib/python3.5/site-packages/xad…

成本预算的四个步骤_全网推广步骤有哪些?

全网推广的步骤是什么&#xff1f;一般来说&#xff0c;搜索引擎优化是大多数中小企业常用的推广方法。主要是通过对一些搜索引擎的排名来提高网站的曝光率&#xff0c;从而更好的提高自己网站的流量&#xff0c;从而更好的实现互联网层面的销售。接下来&#xff0c;让我们学习…

undefined reference to `std::cout'等错误

&#xff08;1&#xff09;gcc和g都是GNU(组织)的一个编译器。 &#xff08;2&#xff09;后缀名为.c的程序和.cpp的程序g都会当成是c的源程序来处理。而gcc不然&#xff0c;gcc会把.c的程序处理成c程序。 &#xff08;3&#xff09;对于.cpp的程序&#xff0c;编译可以用gcc/g…

FFPLAY的原理(二)

关于包Packets的注释从技术上讲一个包可以包含部分或者其它的数据&#xff0c;但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧。现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。我们将生成一个简单的PPM格式文件…

python生成requirements.txt的两种方法

python项目如何在另一个环境上重新构建项目所需要的运行环境依赖包&#xff1f; 使用的时候边记载是个很麻烦的事情&#xff0c;总会出现遗漏的包的问题&#xff0c;这个时候手动安装也很麻烦&#xff0c;不能确定代码报错的需要安装的包是什么版本。这些问题&#xff0c;requi…

node.js 安装使用http-server

node.js npm全局安装了http-server后我该怎么使用它&#xff1f;我在它的安装目录下创建了inde.html&#xff0c;浏览器localhost:8080可以访问&#xff0c;那我的项目需要放在它的安装目录下&#xff1f;还是需要在我的项目下配置什么或者使用什么指令启动它&#xff1f;我在我…

D - 卿学姐与魔法

卿学姐与魔法 Time Limit: 1200/800MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit Status“你的膜法也救不了你 在去拯救公主的道路上&#xff0c;卿学姐披荆斩棘&#xff0c;刀刃早已锈迹斑斑。 一日卿学姐正在为武器的问题发愁&#xff0c;碰到了正…

python对excel表统计视频教程_Python实现对excel文件列表值进行统计的方法

本文实例讲述了Python实现对excel文件列表值进行统计的方法。分享给大家供大家参考。具体如下&#xff1a;#!/usr/bin/env python#codinggbk#此PY用来统计一个execl文件中的特定一列的值的分类import win32com.clientfilenameraw_input("请输入要统计文件的详细地址&#…

mooc后台管理系统设计

摘 要 本设计采用Python中的Django框架实现Mooc后台管理界面设计,django是一个完整的开源web开源框架,使用起来能够快速的搭建你想要的网站,由于django自带后台管理系统,本设计中后台管理模板采用功能更加强大的Xadmin实现。数据库部分采用mysql5.7,由于django中有自带封装的数…

DirectShow系统初级指南

流媒体的处理&#xff0c;以其复杂性和技术性&#xff0c;一向广受工业界的关注。特别伴随着因特网的普及&#xff0c;流媒体在网络上的广泛应用&#xff0c;怎样使流媒体的处理变得简单而富有成效逐渐成为了焦点问题。选择一种合适的应用方案&#xff0c;事半功倍。此时&#…

正则正整数含0

^0?$|^([1-9][0-9]*)?$

MySQL 数据库导出导入操作

有时需要将 MySQL 数据库中的数据导入到其它的数据库中&#xff0c;这里以从 Ubuntu 系统的 MySQL 数据库导出 zabbix 这个数据库到 Windows 系统中的MySQL 为例。 导出数据库 导出数据其实非常方便&#xff0c;比如将 MySQL 中的 zabbix 这个数据库导出到当前文件夹&#xff…

您的apple id 暂时不符合使用此应用程序_Mac相机不工作时该怎么办

苹果公司的许多台式机和笔记本电脑都包含一个内置网络摄像头&#xff0c;该公司愉快地将其称为FaceTime相机。但是&#xff0c;如果您的Mac网络摄像头无法正常工作&#xff0c;并且在尝试访问它时显示为断开连接或不可用&#xff0c;则您可能不会感到高兴。您可以尝试以下操作来…

基于DirectShow的流媒体解码和回放

一、 前言  流媒体的定义很广泛&#xff0c;大多数时候指的是把连续的影像和声音信息经过压缩处理后放上网站服务器&#xff0c;让用户一边下载一边观看、收听&#xff0c;而不需要等整个压缩文件下载到自己机器就可以观看的视频/音频传输、压缩技术。流媒体也指代由这种技术…