ffmpeg tutorial01--------提取视频图像

概要

电影文件有很多基本的组成部分。首先,文件本身被称为 容器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 <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.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函数。


关于包Packets的注释

从技术上讲一个包可以包含部分或者其它的数据,但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧。

现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。


void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {

FILE *pFile;

char szFilename[32];

int y;

// Open file

sprintf(szFilename, "frame%d.ppm", iFrame);

pFile=fopen(szFilename, "wb");

if(pFile==NULL)

return;

// Write header

fprintf(pFile, "P6\n%d %d\n255\n", width, height);

// Write pixel data

for(y=0; y<height; y++)

fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);

// Close file

fclose(pFile);

}

我们做了一些标准的文件打开动作,然后写入RGB数据。我们一次向文件写入一行数据。PPM格式文件的是一种包含一长串的RGB数据的文件。如果你 了解 HTML色彩表示的方式,那么它就类似于把每个像素的颜色头对头的展开,就像#ff0000#ff0000....就表示了了个红色的屏幕。(它被保存成 二进制方式并且没有分隔符,但是你自己是知道如何分隔的)。文件的头部表示了图像的宽度和高度以及最大的RGB值的大小。

现在,回顾我们的main()函数。一旦我们开始读取完视频流,我们必需清理一切:


// Free the RGB image

av_free(buffer);

av_free(pFrameRGB);

// Free the YUV frame

av_free(pFrame);

// Close the codec

avcodec_close(pCodecCtx);

// Close the video file

av_close_input_file(pFormatCtx);

return 0;

你会注意到我们使用av_free来释放我们使用avcode_alloc_fram和av_malloc来分配的内存。

上面的就是代码!下面,我们将使用Linux或者其它类似的平台,你将运行:

gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lavutil -lm

如果你使用的是老版本的ffmpeg,你可以去掉-lavutil参数:

gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lm

大多数的图像处理函数可以打开PPM文件。可以使用一些电影文件来进行测试。

时间: 2024-09-18 11:34:20

ffmpeg tutorial01--------提取视频图像的相关文章

视频-qt通过显示ffmpeg解码出的图像

问题描述 qt通过显示ffmpeg解码出的图像 已经实现ffmpeg解码http视频流为一帧一帧是图像. 想通过OpenGL 或者 QML 或者 还有其他什么办法可以把这些帧图像显示出(视频帧率:30fps,大小:1280x720) 解决方案 http://www.oschina.net/question/202950_88107?sort=timehttp://www.oschina.net/code/snippet_1466652_44272

人数统计,提取前景图像像素面积

问题描述 人数统计,提取前景图像像素面积 视频人数统计,利用前景像素跟人数呈线性关系估计人数,现在已经通过背景差分法做了前景检测,提取出前景图像,怎么提取前景图像的像素面积呢?求高手指教. 解决方案 统计前景像素点数量,乘以一个常数就是面积. 解决方案二: 这个是要循环计算的,你也可以在差分的过程中把前景像素点数量计算出来,这样就不需要在开循环额外计算了

视频关键帧提取-python或者C++有提取视频关键帧的库或者函数吗

问题描述 python或者C++有提取视频关键帧的库或者函数吗 问题是这样的:我有一段视频,我想提取出这个视频中的关键帧并保存为图片.有C++或者python相关方面的库或者函数可以实现这方面的功能吗? 哪位朋友能指导一下,十分感谢 解决方案 http://stackoverflow.com/questions/9064962/key-frame-extraction-from-videohttp://superuser.com/questions/669716/how-to-extract-a

ubuntu-ffmpeg提取视频中的帧保存为图片的序号如何从0开始

问题描述 ffmpeg提取视频中的帧保存为图片的序号如何从0开始 问题是这样的:我在unbuntu环境下,安装好了ffmpeg,现在希望通过ffmpeg的命令行提取出视频中的帧并保存为图片,这个功能用以下已经实现: ffmpeg -i yourVideo.mp4 -vf select='eq(pict_type,I)' -vsync 2 -s 160x90 -f image2 %d.jpeg 以上命令虽然能截取视频中的帧并保存为图片,并且通过命令参数: -f image2 test%d.jpeg

OpenCV提取视频关键帧的算法

问题描述 OpenCV提取视频关键帧的算法 如题,好像OpenCV里没有封装好的函数,有没有做过的朋友知道的?或者提取关键帧 的算法? 解决方案 提取关键帧要看你设定的提取条件吧,最蠢的办法是弄个帧缓存池和一个帧指针(也就是一个计数器),然后用提取每一帧的时候进行条件判断,满足的就存入缓存池中,用帧指定提取指定的帧 解决方案二: 视频关键帧提取最新版ffmpeg 提取视频关键帧最新版ffmpeg 提取视频关键帧 解决方案三: 你可以参考<opencv 计算机视觉编程攻略>

用FFMPEG SDK进行视频转码压缩时解决音视频不同步问题的方法

用FFMPEG SDK进行视频转码压缩的时候,转码成功后去看视频的内容,发现音视频是不同步的.这个的确是一个恼火的事情.我在用FFMPEG SDK做h264格式的FLV文件编码Filter的时候就碰到了这个问题.         经过研究发现,FFMPEG SDK写入视频的时候有两个地方用来控制写入的时间戳,一个是AvPacket, 一个是AvFrame. 在调用avcodec_encode_video的时候需要传入AvFrame的对象指针,也就是传入一帧未压缩的视频进行压缩处理,AvFrame

如何提取视频中的音乐

  有时我们在网上经常会听到很好听的音乐的MV视频,可是又不知道背景音乐叫什么名字,有时候还想单曲循环一下,而不是一直看MV,所以会考虑到将背景音乐提取出来.那么,如何提取视频中的音乐呢?其实可以应用到一款叫转换软件<超级转 换秀>,利用它进行音乐提取. 一.添加待提取音乐的视频 <超级转 换秀>的转换功能涵盖各种音.视频的相互转换.要进行如何提取视频中的音乐,在如下的"音频转换通"功能选项卡进行操作即可.如图所示,点击左上角的"添加待转换音频&quo

PHP+FFMPEG实现将视频自动转码成H264标准Mp4文件_php实例

配置php.ini文件 复制代码 代码如下: file_uploads = on ;//是否允许通过HTTP上传文件的开关.默认为ON即是开 upload_tmp_dir ;//文件上传至服务器上存储临时文件的地方,如果没指定就会用系统默认的临时文件夹 upload_max_filesize = 1024m ;//望文生意,即允许上传文件大小的最大值.默认为2M,我们设置为1G post_max_size = 1024m ;//指通过表单POST给PHP的所能接收的最大值,我们也设置为1G ma

directShow如何提取视频中的音频,并将视频和音频同步?directShow如何提取音频

问题描述 directShow如何提取视频中的音频,并将视频和音频同步?directShow如何提取音频 directShow如何提取视频中的音频,并将视频和音频同步?directShow如何提取音频,OpenAL可以实现吗? 解决方案 参考:http://download.csdn.net/download/wangqiulin123456/5172050 解决方案二: 从视频中提取音频Android中从视频中提取音频