#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include <stdio.h>
void SaveFrame(AVFrame *pFrame,int width,int height,int iFrame);
int main(int argc,char * argv[])
{
av_register_all();
AVFormatContext *pFormatCtx;
pFormatCtx=avformat_alloc_context();
//Open video file
#ifdef _FFMPEG_0_6_
if(av_open_input_file(&pFormatCtx,argv[1],NULL,0,NULL))
#else
if (avformat_open_input(&pFormatCtx,argv[1],NULL,NULL)!=0)
#endif
return -1;//Couldn't open file
/**
* Open an input stream and read the header. The codecs are not opened.
* The stream must be closed with avformat_close_input().
*
* @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context).
* May be a pointer to NULL, in which case an AVFormatContext is allocated by this
* function and written into ps.
* Note that a user-supplied AVFormatContext will be freed on failure.
* @param filename Name of the stream to open.
* @param fmt If non-NULL, this parameter forces a specific input format.
* Otherwise the format is autodetected.
* @param options A dictionary filled with AVFormatContext and demuxer-private options.
* On return this parameter will be destroyed and replaced with a dict containing
* options that were not found. May be NULL.
*
* @return 0 on success, a negative AVERROR on failure.
*
* @note If you want to use custom IO, preallocate the format context and set its pb field.
*/
int avformat_open_input(AVFormatContext **ps, const char *filename, AVInputFormat *fmt, AVDictionary **options);
//Retrieve stream information
#ifdef _FFMPEG_0_6_
if (av_find_stream_info(pFormatCtx)<0)
#else
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
#endif
return -1;//Couldn't find stream information
//Dump information about file onto standard error
av_dump_format(pFormatCtx,0,argv[1],0);
int i;
AVCodecContext *pCodecCtx;
//Find the first video stream
int videoStream=-1;
printf("pFormatCtx->nb_streams=%d\n",pFormatCtx->nb_streams);
for (i = 0; i < pFormatCtx->nb_streams; ++i)
{
if (pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_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;
printf("videoStream=%d\n", videoStream);
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
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
#ifdef _FFMPEG_0_6_
if(avcodec_open(pCodecCtx,pCodec)<0)
#else
if(avcodec_open2(pCodecCtx,pCodec,NULL)<0)
#endif
return -1;//Could not open codec
AVFrame *pFrame;
//Allocate video frame
pFrame=avcodec_alloc_frame();
//Allocate an AVFrame structure
AVFrame *pFrameRGB;
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));
//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);
/**
* Setup the picture fields based on the specified image parameters
* and the provided image data buffer.
*
* The picture fields are filled in by using the image data buffer
* pointed to by ptr.
*
* If ptr is NULL, the function will fill only the picture linesize
* array and return the required size for the image buffer.
*
* To allocate an image buffer and fill the picture data in one call,
* use avpicture_alloc().
*
* @param picture the picture to be filled in
* @param ptr buffer where the image data is stored, or NULL
* @param pix_fmt the pixel format of the image
* @param width the width of the image in pixels
* @param height the height of the image in pixels
* @return the size in bytes required for src, a negative error code
* in case of failure
*
* @see av_image_fill_arrays()
*/
int avpicture_fill(AVPicture *picture, const uint8_t *ptr, enum AVPixelFormat pix_fmt, int width, int height);
int frameFinished;
AVPacket packet;
i=0;
av_init_packet(&packet);//////////////
while(av_read_frame(pFormatCtx,&packet)>=0)
{
//printf("packet.stream_index=%d, packet.size=%d\n", packet.stream_index,packet.size);
//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);
avcodec_decode_video2(pCodecCtx,pFrame,&frameFinished,&packet);
printf("frameFinished=%d\n",frameFinished );
//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);
static struct SwsContext *img_convert_ctx;
img_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,
PIX_FMT_RGB24,SWS_BICUBIC,NULL,NULL,NULL);
if(img_convert_ctx==NULL)
{
fprintf(stderr,"Can not initialize the conversion context!\n");
exit(1);
}
sws_scale(img_convert_ctx,(const uint8_t *const)pFrame->data,pFrame->linesize,0,pCodecCtx->height,
pFrameRGB->data,pFrameRGB->linesize);
//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);
}
/**
* Scale the image slice in srcSlice and put the resulting scaled
* slice in the image in dst. A slice is a sequence of consecutive
* rows in an image.
*
* Slices have to be provided in sequential order, either in
* top-bottom or bottom-top order. If slices are provided in
* non-sequential order the behavior of the function is undefined.
*
* @param c the scaling context previously created with
* sws_getContext()
* @param srcSlice the array containing the pointers to the planes of
* the source slice
* @param srcStride the array containing the strides for each plane of
* the source image
* @param srcSliceY the position in the source image of the slice to
* process, that is the number (counted starting from
* zero) in the image of the first row of the slice
* @param srcSliceH the height of the source slice, that is the number
* of rows in the slice
* @param dst the array containing the pointers to the planes of
* the destination image
* @param dstStride the array containing the strides for each plane of
* the destination image
* @return the height of the output slice
*/
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]);
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);
}
圖像數據的保存格式可以用ASCII碼,也可用二進制,下面只說說一種ppm格式中比較簡單的一種:24位彩色、二進制保存的圖像。
文件頭+rgb數據:
P6\n
width height\n
255\n
rgbrgb...
其中P6表示ppm的這種格式;\n表示換行符;width和height表示圖像的寬高,用空格隔開;255表示每個顏色分量的最大值;rgb數據從上到下,從左到右排放
1)第一行爲“P6",表示文件類型
2)第二行爲圖像的寬度和高度
3)第三行爲最大的象素值
接下來是圖像數據塊。按行順序存儲。每個象素佔3個字節,依次爲紅綠藍通道,每個通道爲1字節整
數。左上角爲座標原點。
//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
#ifdef _FFMPEG_0_6_
av_close_input_file(pFormatCtx);
#else
avformat_close_input(&pFormatCtx);
#endif
avformat_free_context(pFormatCtx);
return 0;
在我Linux系統下編譯的命令:
源代碼:見這裏的github。