Extract Images From a Video Using FFmpeg

- - | Comments

利用FFmpeg的 libavformat, libavcodec, libswscale 可以把video file的每個video frame給dump出來.

在google上可以找到這樣的source code: http://dranger.com/ffmpeg/tutorial01.c

在我的Ubuntu server上, 這支程式需要稍做修改, 避開compile error, 才能build出執行檔. 我把它改成可以dump出每個video frame, 而不是原本的只有dump出頭5個frame.

ffmpeg_ppm_dumper.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// tutorial01.c
// Code based on a tutorial by Martin Bohme (boehme@inb.uni-luebeckREMOVETHIS.de)
// Tested on Gentoo, CVS version 5/01/07 compiled with GCC 4.1.1
// With updates from https://github.com/chelyaev/ffmpeg-tutorial
// Updates tested on:
// LAVC 54.59.100, LAVF 54.29.104, LSWS 2.1.101 
// on GCC 4.7.2 in Debian February 2015

// A small sample program that shows how to use libavformat and libavcodec to
// read video from a file.
//
// Use
//
// gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lswscale -lz
//
// to build (assuming libavformat and libavcodec are correctly installed
// your system).
//
// Run using
//
// tutorial01 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>

// compatibility with newer API
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55,28,1)
#define av_frame_alloc avcodec_alloc_frame
#define av_frame_free avcodec_free_frame
#endif

void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
  FILE *pFile;
  char szFilename[32];
  int  y;

  // Open file
  sprintf(szFilename, "frame%08d.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);
}

int main(int argc, char *argv[]) {
  // Initalizing these to NULL prevents segfaults!
  AVFormatContext   *pFormatCtx = NULL;
  int               i, videoStream;
  AVCodecContext    *pCodecCtxOrig = NULL;
  AVCodecContext    *pCodecCtx = NULL;
  AVCodec           *pCodec = NULL;
  AVFrame           *pFrame = NULL;
  AVFrame           *pFrameRGB = NULL;
  AVPacket          packet;
  int               frameFinished;
  int               numBytes;
  uint8_t           *buffer = NULL;
  struct SwsContext *sws_ctx = NULL;

  if(argc < 2) {
    printf("Please provide a movie file\n");
    return -1;
  }
  // Register all formats and codecs
  av_register_all();

  // Open video file
  if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL)!=0)
    return -1; // Couldn't open file

  // Retrieve stream information
  if(avformat_find_stream_info(pFormatCtx, NULL)<0)
    return -1; // Couldn't find stream information

  // Dump information about file onto standard error
  av_dump_format(pFormatCtx, 0, argv[1], 0);

  // Find the first video stream
  videoStream=-1;
  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
  pCodecCtxOrig=pFormatCtx->streams[videoStream]->codec;
  // Find the decoder for the video stream
  pCodec=avcodec_find_decoder(pCodecCtxOrig->codec_id);
  if(pCodec==NULL) {
    fprintf(stderr, "Unsupported codec!\n");
    return -1; // Codec not found
  }
  // Copy context
  pCodecCtx = avcodec_alloc_context3(pCodec);
  if(avcodec_copy_context(pCodecCtx, pCodecCtxOrig) != 0) {
    fprintf(stderr, "Couldn't copy codec context");
    return -1; // Error copying codec context
  }

  // Open codec
  if(avcodec_open2(pCodecCtx, pCodec, NULL)<0)
    return -1; // Could not open codec

  // Allocate video frame
  pFrame=av_frame_alloc();

  // Allocate an AVFrame structure
  pFrameRGB=av_frame_alloc();
  if(pFrameRGB==NULL)
    return -1;

  // 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);

  // initialize SWS context for software scaling
  sws_ctx = sws_getContext(pCodecCtx->width,
             pCodecCtx->height,
             pCodecCtx->pix_fmt,
             pCodecCtx->width,
             pCodecCtx->height,
             PIX_FMT_RGB24,
             SWS_BILINEAR,
             NULL,
             NULL,
             NULL
             );

  // 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_video2(pCodecCtx, pFrame, &frameFinished, &packet);

      // Did we get a video frame?
      if(frameFinished) {
  // Convert the image from its native format to RGB
  sws_scale(sws_ctx, (uint8_t const * 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);
          i++ ;
      }
    }

    // Free the packet that was allocated by av_read_frame
    av_free_packet(&packet);
  }

  sws_freeContext(sws_ctx);

  // Free the RGB image
  av_free(buffer);
  //av_frame_free(&pFrameRGB);

  // Free the YUV frame
  //av_frame_free(&pFrame);

  // Close the codecs
  avcodec_close(pCodecCtx);
  avcodec_close(pCodecCtxOrig);

  // Close the video file
  avformat_close_input(&pFormatCtx);

  return 0;
}

Terminal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
bramante@matrix:~/ffmpeg_ppm_dumper$ ll
total 3568
drwxr-xr-x 2 bramante bramante   12288 Jul 28 00:28 ./
drwxr-xr-x 5 bramante bramante    4096 Jul 28 00:15 ../
-rwxrw-rw- 1 bramante bramante 3625204 Jul 21 20:04 demo.ts*
-rwxrw-rw- 1 bramante bramante    5528 Jul 28 00:12 ffmpeg_ppm_dumper.c*
bramante@matrix:~/ffmpeg_ppm_dumper$ gcc -g -Wall -o ffmpeg_ppm_dumper ffmpeg_ppm_dumper.c -lavformat -lavcodec -lswscale
bramante@matrix:~/ffmpeg_ppm_dumper$ ll
total 3616
drwxr-xr-x 2 bramante bramante   12288 Jul 28 00:28 ./
drwxr-xr-x 5 bramante bramante    4096 Jul 28 00:15 ../
-rwxrw-rw- 1 bramante bramante 3625204 Jul 21 20:04 demo.ts*
-rwxrwxr-x 1 bramante bramante   47251 Jul 28 00:28 ffmpeg_ppm_dumper*
-rwxrw-rw- 1 bramante bramante    5528 Jul 28 00:12 ffmpeg_ppm_dumper.c*
bramante@matrix:~/ffmpeg_ppm_dumper$ ./ffmpeg_ppm_dumper ./demo.ts
[mpegts @ 0x1c49040] max_analyze_duration reached
Input #0, mpegts, from './demo.ts':
  Duration: 00:04:15.18, start: 1.400000, bitrate: 113 kb/s
  Program 1
    Metadata:
      service_name    : Service01
      service_provider: Libav
    Stream #0.0[0x100]: Video: mpeg4 (Simple Profile), yuv420p, 640x480 [PAR 1:1 DAR 4:3], 1 fps, 1 tbr, 90k tbn, 1 tbc
    Stream #0.1[0x101]: Audio: aac, 44100 Hz, stereo, s16, 61 kb/s
bramante@matrix:~/ffmpeg_ppm_dumper$ ll
total 235040
drwxr-xr-x 2 bramante bramante   12288 Jul 28 00:29 ./
drwxr-xr-x 5 bramante bramante    4096 Jul 28 00:15 ../
-rwxrw-rw- 1 bramante bramante 3625204 Jul 21 20:04 demo.ts*
-rwxrwxr-x 1 bramante bramante   47251 Jul 28 00:28 ffmpeg_ppm_dumper*
-rwxrw-rw- 1 bramante bramante    5528 Jul 28 00:12 ffmpeg_ppm_dumper.c*
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:28 frame00000000.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:28 frame00000001.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:28 frame00000002.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:28 frame00000003.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:28 frame00000004.ppm
....
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:29 frame00000252.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:29 frame00000253.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:29 frame00000254.ppm
-rw-rw-r-- 1 bramante bramante  921615 Jul 28 00:29 frame00000255.ppm
bramante@matrix:~/ffmpeg_ppm_dumper$

Dump出的*.ppm檔可以用ACDSee開檔來看.

Comments