ffmpeg 学习笔记1——读写文件

来源:互联网 发布:淘宝开企业店铺流程 编辑:程序博客网 时间:2024/05/22 17:42

http://www.guoyb.com/Tech/17.html

ffmpeg 学习笔记1——读写文件

作者:Usher 发布于:2012-4-12 14:38 Thursday 分类:技术杂记

    参考:http://dranger.com/ffmpeg/tutorial01.html

    0.基本概念

    Container:视频文件本身;

    Stream:数据流序列,一个文件中会有许多stream,例如一般都至少会有一个audio stream,一个video stream;

    Frame:Stream中的数据单元叫做Frame;

    Codec:每一个stream都被一种codec编码,codec是由code和decode两个单词合成,表示一个stream应该如何编码和解码;

    Packet:从Stream中读出的数据单元是packet,我们可以从packet解码出frame,在ffmpeg中,一个packet可以包含完整的frame,也可以包含多个frame(对于audio stream来说)。

    1.基本流程

10 OPEN video_stream FROM video.avi20 READ packet FROM video_stream INTO frame30 IF frame NOT COMPLETE GOTO 2040 DO SOMETHING WITH frame50 GOTO 20

    2.打开文件

    首先,应该包含相应的头文件,例如:   

1<span style="font-size:14px;">#include <libavcodec/avcodec.h>
2#include <libavformat/avformat.h>
3#include <libswscale/swscale.h></span>
    并且先运行
1<span style="font-size:14px;"> av_register_all();</span>

    来初始化ffmpeg库。

    之后就可以打开视频文件了:

1<span style="font-size:14px;">AVFormatContext *pFormatCtx;
2if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
3    return -1; // Couldn't open file</span>

    这样pFormatCtx就和相应的视频文件联系到了一起,存储了和视频文件格式相关的信息。pFormatCtx将是整个文件读写过程中很重要的一个结构。

    这个函数只检查了视频文件的头部信息,所以接下来需要填充stream相关的结构:

1<span style="font-size:14px;">// Retrieve stream information
2  if(av_find_stream_info(pFormatCtx)<0)
3    return -1; // Couldn't find stream information</span>

    这样,pFormatCtx->streams就被初始化好了,接下来就要寻找video stream了:

01<span style="font-size:14px;">  videoStream=-1;
02  for(i=0; i<pFormatCtx->nb_streams; i++)
03    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
04      videoStream=i;
05      break;
06    }
07  if(videoStream==-1)
08    return -1; // Didn't find a video stream
09   
10  // Get a pointer to the codec context for the video stream
11  pCodecCtx=pFormatCtx->streams[videoStream]->codec;</span>

    在pCodecCtx中存储了该stream中所有有关Codec的相关信息,但是我们接下来仍然需要找到实际使用的decoder来解码:

1<span style="font-size:14px;">  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
2  if(pCodec==NULL) {
3    fprintf(stderr, "Unsupported codec!\n");
4    return -1; // Codec not found
5  }
6  // Open codec
7  if(avcodec_open(pCodecCtx, pCodec)<0)
8    return -1; // Could not open codec</span>

    3.为视频数据分配存储空间

    接下来我们要为视频帧数据分配存储空间,包括解码得到的视频帧、格式转换得到的视频帧:

01<span style="font-size:14px;">  // Allocate video frame
02  pFrame=avcodec_alloc_frame();
03   
04  // Allocate an AVFrame structure
05  pFrameRGB=avcodec_alloc_frame();
06  if(pFrameRGB==NULL)
07    return -1;
08   
09  // Determine required buffer size and allocate buffer
10  numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
11                  pCodecCtx->height);
12  buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
13   
14  // Assign appropriate parts of buffer to image planes in pFrameRGB
15  // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
16  // of AVPicture
17  avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
18         pCodecCtx->width, pCodecCtx->height);</span>

    其中avpicture_fill是将pFrameRGB和buffer这一内存空间关联起来。而pFrame的实际存储空间将由后面的视频解码函数分配。

    4.读取数据

    首先来看代码:

01<span style="font-size:14px;">  // Read frames and save first five frames to disk
02  i=0;
03  while(av_read_frame(pFormatCtx, &packet)>=0) {
04    // Is this a packet from the video stream?
05    if(packet.stream_index==videoStream) {
06      // Decode video frame
07      avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
08               &packet);
09       
10      // Did we get a video frame?
11      if(frameFinished) {
12    // Convert the image from its native format to RGB
13    static struct SwsContext *img_convert_ctx;
14    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
15    sws_scale(img_convert_ctx, (const uint8_t* const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
16     
17    // Save the frame to disk
18    if(++i<=5)
19      SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
20            i);
21      }
22    }
23     
24    // Free the packet that was allocated by av_read_frame
25    av_free_packet(&packet);
26  }</span>
1<span style="font-size:14px;">    </span>

      原来的例程中格式转换使用的是img_convert,在新版本的ffmpeg中已经被sws_scale替换掉了,如上述程序中所示。

      另外,每个packet不一定可以解出完整的帧,所以需要设置一个frameFinished标志,ffmpeg会自己确保下一个packet与之前的packet联系起来。

        接下来我们只需要完成SaveFrame函数,将转换得到的Frame存储成PPM的格式即可:

01<span style="font-size:14px;">void SaveFrame(AVFrame *pFrame, int width, int height, intiFrame) {
02  FILE *pFile;
03  char szFilename[32];
04  int  y;
05   
06  // Open file
07  sprintf(szFilename, "frame%d.ppm", iFrame);
08  pFile=fopen(szFilename, "wb");
09  if(pFile==NULL)
10    return;
11   
12  // Write header
13  fprintf(pFile, "P6\n%d %d\n255\n", width, height);
14   
15  // Write pixel data
16  for(y=0; y<height; y++)
17    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
18   
19  // Close file
20  fclose(pFile);
21}</span>
    最后,释放之前申请空间:
01<span style="font-size:14px;">  // Free the RGB image
02  av_free(buffer);
03  av_free(pFrameRGB);
04   
05  // Free the YUV frame
06  av_free(pFrame);
07   
08  // Close the codec
09  avcodec_close(pCodecCtx);
10   
11  // Close the video file
12  av_close_input_file(pFormatCtx);</span>
    完整的程序如下所示,其中与例程有一些区别,例如前面提到的sws_scale函数:
001<span style="font-size:14px;">// tutorial01.c
002// Code based on a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
003// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
004 
005// A small sample program that shows how to use libavformat and libavcodec to
006// read video from a file.
007//
008// Use
009//
010// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lz
011//
012// to build (assuming libavformat and libavcodec are correctly installed
013// your system).
014//
015// Run using
016//
017// tutorial01 myvideofile.mpg
018//
019// to write the first five frames from "myvideofile.mpg" to disk in PPM
020// format.
021 
022#include <libavcodec/avcodec.h>
023#include <libavformat/avformat.h>
024#include <libswscale/swscale.h>
025 
026#include <stdio.h>
027 
028void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
029  FILE *pFile;
030  char szFilename[32];
031  int  y;
032   
033  // Open file
034  sprintf(szFilename, "frame%d.ppm", iFrame);
035  pFile=fopen(szFilename, "wb");
036  if(pFile==NULL)
037    return;
038   
039  // Write header
040  fprintf(pFile, "P6\n%d %d\n255\n", width, height);
041   
042  // Write pixel data
043  for(y=0; y<height; y++)
044    fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
045   
046  // Close file
047  fclose(pFile);
048}
049 
050int main(int argc, char *argv[]) {
051  AVFormatContext *pFormatCtx;
052  int             i, videoStream;
053  AVCodecContext  *pCodecCtx;
054  AVCodec         *pCodec;
055  AVFrame         *pFrame;
056  AVFrame         *pFrameRGB;
057  AVPacket        packet;
058  int             frameFinished;
059  int             numBytes;
060  uint8_t         *buffer;
061   
062  if(argc < 2) {
063    printf("Please provide a movie file\n");
064    return -1;
065  }
066  // Register all formats and codecs
067  av_register_all();
068   
069  // Open video file
070  if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)
071    return -1; // Couldn't open file
072   
073  // Retrieve stream information
074  if(av_find_stream_info(pFormatCtx)<0)
075    return -1; // Couldn't find stream information
076   
077  // Dump information about file onto standard error
078  dump_format(pFormatCtx, 0, argv[1], 0);
079   
080  // Find the first video stream
081  videoStream=-1;
082  for(i=0; i<pFormatCtx->nb_streams; i++)
083    if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO) {
084      videoStream=i;
085      break;
086    }
087  if(videoStream==-1)
088    return -1; // Didn't find a video stream
089   
090  // Get a pointer to the codec context for the video stream
091  pCodecCtx=pFormatCtx->streams[videoStream]->codec;
092   
093  // Find the decoder for the video stream
094  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
095  if(pCodec==NULL) {
096    fprintf(stderr, "Unsupported codec!\n");
097    return -1; // Codec not found
098  }
099  // Open codec
100  if(avcodec_open(pCodecCtx, pCodec)<0)
101    return -1; // Could not open codec
102   
103  // Allocate video frame
104  pFrame=avcodec_alloc_frame();
105   
106  // Allocate an AVFrame structure
107  pFrameRGB=avcodec_alloc_frame();
108  if(pFrameRGB==NULL)
109    return -1;
110   
111  // Determine required buffer size and allocate buffer
112  numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
113                  pCodecCtx->height);
114  buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
115   
116  // Assign appropriate parts of buffer to image planes in pFrameRGB
117  // Note that pFrameRGB is an AVFrame, but AVFrame is a superset
118  // of AVPicture
119  avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
120         pCodecCtx->width, pCodecCtx->height);
121   
122  // Read frames and save first five frames to disk
123  i=0;
124  while(av_read_frame(pFormatCtx, &packet)>=0) {
125    // Is this a packet from the video stream?
126    if(packet.stream_index==videoStream) {
127      // Decode video frame
128      avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished,
129               &packet);
130       
131      // Did we get a video frame?
132      if(frameFinished) {
133    // Convert the image from its native format to RGB
134    static struct SwsContext *img_convert_ctx;
135    img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
136    sws_scale(img_convert_ctx, (const uint8_t* const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
137     
138    // Save the frame to disk
139    if(++i<=5)
140      SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height,
141            i);
142      }
143    }
144     
145    // Free the packet that was allocated by av_read_frame
146    av_free_packet(&packet);
147  }
148   
149  // Free the RGB image
150  av_free(buffer);
151  av_free(pFrameRGB);
152   
153  // Free the YUV frame
154  av_free(pFrame);
155   
156  // Close the codec
157  avcodec_close(pCodecCtx);
158   
159  // Close the video file
160  av_close_input_file(pFormatCtx);
161   
162  return 0;
163}</span>

欢迎转载,转载请注明出处:http://ushertechblog.sinaapp.com/post-17.html

 P.S.抛弃EMLOG,拥抱Github+hexo+markdown,博客再次搬家,这里(http:\\blog.guoyb.com)是新博客的域名。

0 0
原创粉丝点击