ImageVerifierCode 换一换
格式:DOCX , 页数:71 ,大小:53.07KB ,
资源ID:23859070      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/23859070.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(FFMPEG SDK 教程.docx)为本站会员(b****7)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

FFMPEG SDK 教程.docx

1、FFMPEG SDK 教程FFPLAY的原理概要电影文件有很多基本的组成部分。首先,文件本身被称为容器Container,容器的类型决定了信息被存放在文件中的位置。AVI和Quicktime就 是容器的例子。接着,你有一组流,例如,你经常有的是一个音频流和一个视频流。(一个流只是一种想像出来的词语,用来表示一连串的通过时间来串连的数据元 素)。在流中的数据元素被称为帧Frame。每个流是由不同的编码 器来编码生成的。编解码器 描 述了实际的数据是如何被编码Coded和解码DECoded的,因此它的名字叫做CODEC。Divx和 MP3就是编解码器的例子。接着从流中被读出来的叫做包Packets

2、。包是一段数据,它包含了一段可以被解码成方便我们最后在应用程序中操作的原始帧的 数据。根据我们的目的,每个包包含了完整的帧或者对于音频来说是许多格式的完整帧。基本上来说,处理视频和音频流是很容易的:10 从video.avi文件中打开视频流video_stream20 从视频流中读取包到帧中30 如果这个帧还不完整,跳到2040 对这个帧进行一些操作50 跳回到20在这个程序中使用ffmpeg来处理多种媒体是相当容易的,虽然很多程序可能在对帧进行操作的时候非常的复杂。因此在这篇指导中,我们将打开一个文件,读取里面的视频流,而且我们对帧的操作将是把这个帧写到一个PPM文件中。打开文件首先,来看一

3、下我们如何打开一个文件。通过ffmpeg,你必需先初始化这个库。(注意在某些系统中必需用和来替换)#include #include .int main(int argc, charg *argv) av_register_all();这里注册了所有的文件格式和编解码器的库,所以它们将被自动的使用在被打开的合适格式的文件上。注意你只需要调用 av_register_all()一次,因此我们在主函数main()中来调用它。如果你喜欢,也可以只注册特定的格式和编解码器,但是通常你没有必要 这样做。现在我们可以真正的打开文件:AVFormatContext *pFormatCtx;/ Open vi

4、deo fileif(av_open_input_file(&pFormatCtx, argv1, NULL, 0, NULL)!=0)return -1; / Couldnt open file我们通过第一个参数来获得文件名。这个函数读取文件的头部并且把信息保存到我们给的AVFormatContext结构体中。最后三个参数用来指定特殊的文件格式,缓冲大小和格式参数,但如果把它们设置为空NULL或者0,libavformat将自动检测这些参数。这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息:/ Retrieve stream informationif(av_find_s

5、tream_info(pFormatCtx)streams填充上正确的信息。我们引进一个手工调试的函数来看一下里面有什么:/ Dump information about file onto standard errordump_format(pFormatCtx, 0, argv1, 0);现在pFormatCtx-streams仅仅是一组大小为pFormatCtx-nb_streams的指针,所以让我们先跳过它直到我们找到一个视频流。int i;AVCodecContext *pCodecCtx;/ Find the first video streamvideoStream=-1;for

6、(i=0; inb_streams; i+)if(pFormatCtx-streamsi-codec-codec_type=CODEC_TYPE_VIDEO) videoStream=i;break;if(videoStream=-1)return -1; / Didnt find a video stream/ Get a pointer to the codec context for the video streampCodecCtx=pFormatCtx-streamsvideoStream-codec;流中关于编解码器的信息就是被我们叫做codec context(编解码器上下文)的

7、东西。这里面包含了流中所使用的关于编解码器的所有信息,现在我们有了一个指向他的指针。但是我们必需要找到真正的编解码器并且打开它:AVCodec *pCodec;/ Find the decoder for the video streampCodec=avcodec_find_decoder(pCodecCtx-codec_id);if(pCodec=NULL) fprintf(stderr, Unsupported codec!n);return -1; / Codec not found/ Open codecif(avcodec_open(pCodecCtx, pCodec)flags和

8、添加一个hack来粗糙的修正帧率。这两个修正已经不在存在于ffplay.c中。因此,我必需假设它们不再必 要。我们移除了那些代码后还有一个需要指出的不同点:pCodecCtx-time_base现在已经保存了帧率的信息。time_base是一 个结构体,它里面有一个分子和分母 (AVRational)。我们使用分数的方式来表示帧率是因为很多编解码器使用非整数的帧率(例如NTSC使用29.97fps)。保存数据现在我们需要找到一个地方来保存帧:AVFrame *pFrame;/ Allocate video framepFrame=avcodec_alloc_frame();因为我们准备输出保存

9、24位RGB色的PPM文件,我们必需把帧的格式从原来的转换为RGB。FFMPEG将为我们做这些转换。在大多数项目中(包括我们的这个)我们都想把原始的帧转换成一个特定的格式。让我们先为转换来申请一帧的内存。/ Allocate an AVFrame structurepFrameRGB=avcodec_alloc_frame();if(pFrameRGB=NULL)return -1;即使我们申请了一帧的内存,当转换的时候,我们仍然需要一个地方来放置原始的数据。我们使用avpicture_get_size来获得我们需要的大小,然后手工申请内存空间:uint8_t *buffer;int numB

10、ytes;/ Determine required buffer size and allocate buffernumBytes=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的问题所困扰。现在我们使用a

11、vpicture_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 AVPictureavpicture_fill(AVPicture *)pFrameRGB, buffer, PIX_FMT_RG

12、B24,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 frameavcodec_decode_v

13、ideo(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);/ Did we get a video frame?if(frameFinished) / Convert the image from its native format to RGBimg_convert(AVPicture *)pFrameRGB, PIX_FMT_RGB24,(AVPicture*)pFrame, pCodecCtx-pix_fmt,pCodecCtx-width, pCodecCtx-height);/ Save the frame to

14、 diskif(+iwidth,pCodecCtx-height, i);/ Free the packet that was allocated by av_read_frameav_free_packet(&packet);这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到AVPacket结构体中。注意我们仅仅申请了一个包的结构体 ffmpeg为我们申请了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过av_free_packet()来释 放。函数avcodec_decode_video()把包转换为帧。然而当解码一个包的时候,

15、我们可能没有得到我们需要的关于帧的信息。因此,当我们得 到下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。最后,我们使用 img_convert()函数来把帧从原始格式(pCodecCtx-pix_fmt)转换成为RGB格式。要记住,你可以把一个 AVFrame结构体的指针转换为AVPicture结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。关于包Packets的注释从技术上讲一个包可以包含部分或者其它的数据,但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧。

16、现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件中。我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) FILE *pFile;char szFilename32;int y;/ Open filesprintf(szFilename, frame%d.ppm, iFrame);pFile=fopen(szFilename, wb);if(pFile=NULL)return;/ Write headerfprintf(p

17、File, P6n%d %dn255n, width, height);/ Write pixel datafor(y=0; ydata0+y*pFrame-linesize0, 1, width*3, pFile);/ Close filefclose(pFile);我们做了一些标准的文件打开动作,然后写入RGB数据。我们一次向文件写入一行数据。PPM格式文件的是一种包含一长串的RGB数据的文件。如果你了解 HTML色彩表示的方式,那么它就类似于把每个像素的颜色头对头的展开,就像#ff0000#ff0000.就表示了了个红色的屏幕。(它被保存成 二进制方式并且没有分隔符,但是你自己是知道如何

18、分隔的)。文件的头部表示了图像的宽度和高度以及最大的RGB值的大小。现在,回顾我们的main()函数。一旦我们开始读取完视频流,我们必需清理一切:/ Free the RGB imageav_free(buffer);av_free(pFrameRGB);/ Free the YUV frameav_free(pFrame);/ Close the codecavcodec_close(pCodecCtx);/ Close the video fileav_close_input_file(pFormatCtx);return 0;你会注意到我们使用av_free来释放我们使用avcode_a

19、lloc_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文件。可以使用一些电影文件来进行测试。输出到屏幕SDL和视频为了在屏幕上显示,我们将

20、使用SDL.SDL是Simple Direct Layer的缩写。它是一个出色的多媒体库,适用于多平台,并且被用在许多工程中。你可以从它的官方网站的网址 http:/www.libsdl.org/ 上来得到这个库的源代码或者如果有可能的话你可以直接下载开发包到你的操作系统中。按照这个指导,你将需要编译这个库。(剩下的几个指导中也是一样)SDL库中有许多种方式来在屏幕上绘制图形,而且它有一个特殊的方式来在屏幕上显示图像这种方式叫做YUV覆盖。YUV(从技术上来讲并不叫 YUV而是叫做YCbCr)是一种类似于RGB方式的存储原始图像的格式。粗略的讲,Y是亮度分量,U和V是色度分量。(这种格式比RG

21、B复杂的多,因为 很多的颜色信息被丢弃了,而且你可以每2个Y有1个U和1个V)。SDL的YUV覆盖使用一组原始的YUV数据并且在屏幕上显示出他们。它可以允许4种不 同的 YUV格式,但是其中的YV12是最快的一种。还有一个叫做YUV420P的YUV格式,它和YV12是一样的,除了U和V分量的位置被调换了以外。 420意味着它以4:2:0的比例进行了二次抽样,基本上就意味着1个颜色分量对应着4个亮度分量。所以它的色度信息只有原来的1/4。这是一种节省带宽 的好方式,因为人眼感觉不到这种变化。在名称中的P表示这种格式是平面的简单的说就是Y,U和V分量分别在不同的数组中。FFMPEG可以把图像格式

22、转换为YUV420P,但是现在很多视频流的格式已经是YUV420P的了或者可以被很容易的转换成YUV420P格式。于是,我们现在计划把指导1中的SaveFrame()函数替换掉,让它直接输出我们的帧到屏幕上去。但一开始我们必需要先看一下如何使用SDL库。首先我们必需先包含SDL库的头文件并且初始化它。#include #include if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) fprintf(stderr, Could not initialize SDL - %sn, SDL_GetError();exit(1

23、);SDL_Init()函数告诉了SDL库,哪些特性我们将要用到。当然SDL_GetError()是一个用来手工除错的函数。创建一个显示现在我们需要在屏幕上的一个地方放上一些东西。在SDL中显示图像的基本区域叫做面surface。SDL_Surface *screen;screen = SDL_SetVideoMode(pCodecCtx-width, pCodecCtx-height, 0, 0);if(!screen) fprintf(stderr, SDL: could not set video mode - exitingn);exit(1);这就创建了一个给定高度和宽度的屏幕。下一

24、个选项是屏幕的颜色深度0表示使用和当前一样的深度。(这个在OS X系统上不能正常工作,原因请看源代码)现在我们在屏幕上来创建一个YUV覆盖以便于我们输入视频上去:SDL_Overlay *bmp;bmp = SDL_CreateYUVOverlay(pCodecCtx-width, pCodecCtx-height,SDL_YV12_OVERLAY, screen);正如前面我们所说的,我们使用YV12来显示图像。显示图像前面那些都是很简单的。现在我们需要来显示图像。让我们看一下是如何来处理完成后的帧的。我们将原来对RGB处理的方式,并且替换 SaveFrame() 为显示到屏幕上的代码。为了

25、显示到屏幕上,我们将先建立一个AVPicture结构体并且设置其数据指针和行尺寸来为我们的YUV覆盖服务:if(frameFinished) SDL_LockYUVOverlay(bmp);AVPicture pict;pict.data0 = bmp-pixels0;pict.data1 = bmp-pixels2;pict.data2 = bmp-pixels1;pict.linesize0 = bmp-pitches0;pict.linesize1 = bmp-pitches2;pict.linesize2 = bmp-pitches1;/ Convert the image into

26、YUV format that SDL usesimg_convert(&pict, PIX_FMT_YUV420P,(AVPicture *)pFrame, pCodecCtx-pix_fmt,pCodecCtx-width, pCodecCtx-height);SDL_UnlockYUVOverlay(bmp);首先,我们锁定这个覆盖,因为我们将要去改写它。这是一个避免以后发生问题的好习惯。正如前面所示的,这个AVPicture结构体有一个数据指针指向一 个有4个元素的指针数据。由于我们处理的是YUV420P,所以我们只需要3个通道即只要三组数据。其它的格式可能需要第四个指针来表示alph

27、a通道或 者其它参数。行尺寸正如它的名字表示的意义一样。在YUV覆盖中相同功能的结构体是像素pixel和程度pitch。(程度pitch是在SDL里用来表 示指定行数据宽度的值)。所以我们现在做的是让我们的覆盖中的pict.data中的三个指针有一个指向必要的空间的地址。类似的,我们可以直接从覆盖中 得到行尺寸信息。像前面一样我们使用img_convert来把格式转换成PIX_FMT_YUV420P。绘制图像但我们仍然需要告诉SDL如何来实际显示我们给的数据。我们也会传递一个表明电影位置、宽度、高度和缩放大小的矩形参数给SDL的函数。这样,SDL为我们做缩放并且它可以通过显卡的帮忙来进行快速缩

28、放。SDL_Rect rect;if(frameFinished) / Convert the image into YUV format that SDL usesimg_convert(&pict, PIX_FMT_YUV420P,(AVPicture *)pFrame, pCodecCtx-pix_fmt,pCodecCtx-width, pCodecCtx-height);SDL_UnlockYUVOverlay(bmp);rect.x = 0;rect.y = 0;rect.w = pCodecCtx-width;rect.h = pCodecCtx-height;SDL_Displ

29、ayYUVOverlay(bmp, &rect);让我们再花一点时间来看一下SDL的特性:它的事件驱动系统。SDL被设置成当你在SDL中点击或者移动鼠标或者向它发送一个信号它都将产生一个事件的驱 动方式。如果你的程序想要处理用户输入的话,它就会检测这些事件。你的程序也可以产生事件并且传递给SDL事件系统。当使用SDL进行多线程编程的时候, 这相当有用,这方面代码我们可以在指导4中看到。在这个程序中,我们将在处理完包以后就立即轮询事件。现在而言,我们将处理SDL_QUIT事件以便于我 们退出:SDL_Event event;av_free_packet(&packet);SDL_PollEven

30、t(&event);switch(event.type) case SDL_QUIT:SDL_Quit();exit(0);break;default:break;让我们去掉旧的冗余代码,开始编译。如果你使用的是Linux或者其变体,使用SDL库进行编译的最好方式为:gcc -o tutorial02 tutorial02.c -lavutil -lavformat -lavcodec -lz -lm sdl-config -cflags -libs这里的sdl-config命令会打印出用于gcc编译的包含正确SDL库的适当参数。为了进行编译,在你自己的平台你可能需要做的有点不同:请查阅一下SDL文档中关于你的系统的那部分。一旦可以编译,就马上运行它。当运行这个程序的时候会

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1