http://blog.youkuaiyun.com/linzhiji/article/details/5393656
指导1:制作屏幕录像
源代码: tutorial01.c概要
电影文件有很多基本的组成部分。首先,文件本身被称为 容器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(); |
现在我们可以真正的打开文件:
AVFormatContext *pFormatCtx; // Open video file if(av_open_input_file(&pFormatCtx, argv[1],NULL, 0, NULL)!=0) return -1; // Couldn't open file |
这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息:
// Retrieve stream information if(av_find_stream_info(pFormatCtx)<0) return -1; // Couldn't find streaminformation |
// Dump information about file onto standard error dump_format(pFormatCtx, 0, argv[1], 0); |
int i; AVCodecContext *pCodecCtx; // Find the first video stream videoStream=-1; for(i=0; i<pFormatCtx->nb_streams;i++) if(pFormatCtx->streams->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; |
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(); |
// Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame(); if(pFrameRGB==NULL) return -1; |
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)); |
现在我们使用avpicture_fill来把帧和我们新申请的内存来结合。关于AVPicture的结成:AVPicture结构体是AVFrame结构体的子集――AVFrame结构体的开始部分与AVPicture结构体是一样的。
// Assign appropriate parts of buffer to image planes inpFrameRGB // Note that pFrameRGB is an AVFrame, but AVFrame is asuperset // 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 videostream? if(packet.stream_index==videoStream) { // Decodevideo frame avcodec_decode_video(pCodecCtx, pFrame,&frameFinished, packet.data,packet.size); // Did weget 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 byav_read_frame av_free_packet(&packet); } |
关于包Packets的注释 从技术上讲一个包可以包含部分或者其它的数据,但是ffmpeg的解释器保证了我们得到的包Packets包含的要么是完整的要么是多种完整的帧。 |
void SaveFrame(AVFrame *pFrame, int width, int height, intiFrame) { 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); } |
现在,回顾我们的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; |
上面的就是代码!下面,我们将使用Linux或者其它类似的平台,你将运行:
gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz-lavutil -lm |
gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz-lm |
大多数的图像处理函数可以打开PPM文件。可以使用一些电影文件来进行测试。
来源:http://www.zixundao.com/thread-1519-1-1.html