Created
February 17, 2011 16:37
-
-
Save lkraider/832062 to your computer and use it in GitHub Desktop.
ffmpeg thumbnailer - extract frame, scale and save it to disk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// avcodec_sample.0.5.0.c | |
// Original source: | |
// http://web.me.com/dhoerl/Home/Tech_Blog/Entries/2009/1/22_Revised_avcodec_sample.c.html | |
// | |
// A small sample program that shows how to use libavformat and libavcodec to | |
// read video from a file. | |
// | |
// This version is for the 0.4.9+ release of ffmpeg. This release adds the | |
// av_read_frame() API call, which simplifies the reading of video frames | |
// considerably. | |
// | |
// Use | |
// | |
// gcc -o avcodec_sample.0.5.0 avcodec_sample.0.5.0.c -lavformat -lavcodec -lavutil -lswscale -lz -lbz2 | |
// | |
// to build (assuming libavformat, libavcodec, libavutil, and swscale are correctly installed on | |
// your system). | |
// | |
// Run using | |
// | |
// avcodec_sample.0.5.0 myvideofile.mpg | |
// | |
// to write the first five frames from "myvideofile.mpg" to disk in PPM | |
// format. | |
#include <libavcodec/avcodec.h> | |
#include <libavformat/avformat.h> | |
#include <libswscale/swscale.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
static void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame); | |
int main (int argc, const char * argv[]) | |
{ | |
AVFormatContext *pFormatCtx; | |
int i, videoStream; | |
AVCodecContext *pCodecCtx; | |
AVCodec *pCodec; | |
AVFrame *pFrame; | |
AVFrame *pFrameRGB; | |
AVPacket packet; | |
int frameFinished; | |
int numBytes; | |
uint8_t *buffer; | |
// Register all formats and codecs | |
av_register_all(); | |
// 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 stream information | |
// Dump information about file onto standard error | |
dump_format(pFormatCtx, 0, argv[1], false); | |
// 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; | |
// Find the decoder for the video stream | |
pCodec=avcodec_find_decoder(pCodecCtx->codec_id); | |
if(pCodec==NULL) | |
return -1; // Codec not found | |
// Open codec | |
if(avcodec_open(pCodecCtx, pCodec)<0) | |
return -1; // Could not open codec | |
// Hack to correct wrong frame rates that seem to be generated by some codecs | |
if(pCodecCtx->time_base.num>1000 && pCodecCtx->time_base.den==1) | |
pCodecCtx->time_base.den=1000; | |
// Allocate video frame | |
pFrame=avcodec_alloc_frame(); | |
// Allocate an AVFrame structure | |
pFrameRGB=avcodec_alloc_frame(); | |
if(pFrameRGB==NULL) | |
return -1; | |
// Determine required buffer size and allocate buffer | |
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, | |
pCodecCtx->height); | |
buffer=malloc(numBytes); | |
// Assign appropriate parts of buffer to image planes in pFrameRGB | |
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, | |
pCodecCtx->width, pCodecCtx->height); | |
// Read frames and save first five frames to disk | |
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) | |
{ | |
static struct SwsContext *img_convert_ctx; | |
#if 0 | |
// Older removed code | |
// Convert the image from its native format to RGB swscale | |
img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, | |
(AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, | |
pCodecCtx->height); | |
// function template, for reference | |
int sws_scale(struct SwsContext *context, uint8_t* src[], int srcStride[], int srcSliceY, | |
int srcSliceH, uint8_t* dst[], int dstStride[]); | |
#endif | |
// Convert the image into YUV format that SDL uses | |
if(img_convert_ctx == NULL) { | |
int w = pCodecCtx->width; | |
int h = pCodecCtx->height; | |
img_convert_ctx = sws_getContext(w, h, | |
pCodecCtx->pix_fmt, | |
w, h, PIX_FMT_RGB24, SWS_BICUBIC, | |
NULL, NULL, NULL); | |
if(img_convert_ctx == NULL) { | |
fprintf(stderr, "Cannot initialize the conversion context!\n"); | |
exit(1); | |
} | |
} | |
int ret = sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, | |
pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize); | |
#if 0 // this use to be true, as of 1/2009, but apparently it is no longer true in 3/2009 | |
if(ret) { | |
fprintf(stderr, "SWS_Scale failed [%d]!\n", ret); | |
exit(-1); | |
} | |
#endif | |
// 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); | |
} | |
// Free the RGB image | |
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; | |
} | |
static 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); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment