美文网首页
2018-12-05

2018-12-05

作者: Hope_加贝 | 来源:发表于2018-12-05 11:05 被阅读2次

ffmpeg教程

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

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

* 从video.avi文件中打开视频流video_stream
* 从视频流中读取包到帧中
* 如果这个真不完整跳到第二步
* 对这个帧进行一些操作
* 调回到第二步

这篇指导中,我们将打开一个文件,读取里面的视频流,而且我们对帧的操作是把这个帧写到一个PPM文件中
打开文件
首先,通过ffmpeg,必须先初始化这个库。

#include <avcodec.h>
#include <avformat.h>
...
int main(int argc,char *argv[]){
av_register_all();
}

这里注册了所有的文件格式和编解码的库,所以它们将被自动的使用在被打开的合适的格式文件上。
现在我们可以真正的打开文件:

AVFormatContext *pFormatCtx;
if(av_open_input_file(&pFormatCtx,argv[1],NULL,0,NULL)!=0)
  return -1;

我们通过第一个参数来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,设为NULL的话,libavfort将自动检测这些参数。
这个函数只是检测了文件的头部,接着我们需要检查在文件中的流的信息:

//Retrieve stream information
if(av_find_stream_info(pFormatCtx)<0)
return -1; //Couldn't fine stream information

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

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

现在pFormatCtx->streams仅仅是一组大小为pFormat->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

保存数据
现在我们需要一个地方来保存帧:

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);

相关文章

网友评论

      本文标题:2018-12-05

      本文链接:https://www.haomeiwen.com/subject/daxicqtx.html