24
24
using namespace std ;
25
25
using namespace av ;
26
26
27
+ // Example that uses a complex filter to produce a good
28
+ // animated GIF from a video
29
+
27
30
int main (int argc, char **argv)
28
31
{
29
32
if (argc < 2 )
@@ -33,6 +36,7 @@ int main(int argc, char **argv)
33
36
av::setFFmpegLoggingLevel (AV_LOG_DEBUG);
34
37
35
38
string uri{argv[1 ]};
39
+ string out{argv[2 ]};
36
40
37
41
ssize_t videoStream = -1 ;
38
42
VideoDecoderContext vdec;
@@ -98,6 +102,45 @@ int main(int argc, char **argv)
98
102
}
99
103
}
100
104
105
+ //
106
+ // OUTPUT
107
+ //
108
+ OutputFormat ofrmt;
109
+ FormatContext octx;
110
+
111
+ ofrmt.setFormat (string (), out);
112
+ octx.setFormat (ofrmt);
113
+
114
+ Codec ocodec = findEncodingCodec (ofrmt);
115
+ cout << " Codec: " << ocodec.name () << endl;
116
+ VideoEncoderContext encoder {ocodec};
117
+
118
+ // Settings
119
+ encoder.setWidth (320 );
120
+ encoder.setHeight (200 );
121
+ encoder.setPixelFormat (AV_PIX_FMT_PAL8);
122
+ encoder.setTimeBase (Rational {1 , 1000 });
123
+ encoder.setBitRate (vdec.bitRate ());
124
+
125
+ encoder.open (Codec (), ec);
126
+ if (ec) {
127
+ cerr << " Can't opent encodec\n " ;
128
+ return 1 ;
129
+ }
130
+
131
+ Stream ost = octx.addStream (encoder);
132
+ ost.setFrameRate (vst.frameRate ());
133
+
134
+ octx.openOutput (out, ec);
135
+ if (ec) {
136
+ cerr << " Can't open output\n " ;
137
+ return 1 ;
138
+ }
139
+
140
+ octx.dump ();
141
+ octx.writeHeader ();
142
+ octx.flush ();
143
+
101
144
// Setup filter
102
145
Filter filter_buffer_src{" buffer" };
103
146
Filter filter_buffer_sink{" buffersink" };
@@ -122,9 +165,16 @@ int main(int argc, char **argv)
122
165
}
123
166
124
167
// Setup the filter chain
125
- filter_graph.parse (" scale=320x200" , src_ctx, sink_ctx, ec);
126
- if (ec)
127
- {
168
+ string filterChain;
169
+ if (argc > 3 ) {
170
+ filterChain = argv[3 ];
171
+ } else {
172
+ filterChain = " scale=320x200:-1:flags=lanczos,split [s0][s1]; "
173
+ " [s0] palettegen [p]; [s1][p] paletteuse[f]; " ;
174
+ }
175
+ cout << " Filter: " << filterChain << endl;
176
+ filter_graph.parse (filterChain, src_ctx, sink_ctx, ec);
177
+ if (ec) {
128
178
clog << " Error parsing filter chain: " << ec << " , " << ec.message () << endl;
129
179
return 1 ;
130
180
}
@@ -169,7 +219,13 @@ int main(int argc, char **argv)
169
219
continue ;
170
220
}
171
221
222
+ frame.setTimeBase (encoder.timeBase ());
223
+ frame.setStreamIndex (0 );
224
+ frame.setPictureType ();
225
+
172
226
// Push into the filter
227
+ cout << " Pushing frame into filter: " << frame.width () << " x" << frame.height () << " , size=" << frame.size ()
228
+ << " , ts=" << frame.pts () << " , tb: " << frame.timeBase () << endl;
173
229
buffer_src.addVideoFrame (frame, AV_BUFFERSRC_FLAG_KEEP_REF, ec);
174
230
if (ec)
175
231
{
@@ -181,55 +237,78 @@ int main(int argc, char **argv)
181
237
VideoFrame filtered;
182
238
while (buffer_sink.getVideoFrame (filtered, ec))
183
239
{
184
- filtered.setTimeBase (frame .timeBase ());
240
+ filtered.setTimeBase (encoder .timeBase ());
185
241
auto ts = filtered.pts ();
186
242
187
243
clog << " filtered: " << filtered.width () << " x" << filtered.height () << " , size=" << filtered.size ()
188
244
<< " , ts=" << ts << " , tm: " << ts.seconds () << " , tb: " << filtered.timeBase ()
189
245
<< " , ref=" << filtered.isReferenced () << " :" << filtered.refCount () << endl;
246
+
247
+ // Encode
248
+ Packet opkt = filtered ? encoder.encode (filtered, ec) : encoder.encode (ec);
249
+ if (ec) {
250
+ cerr << " Encoding error: " << ec << endl;
251
+ return 1 ;
252
+ }
253
+
254
+ if (opkt) {
255
+ // Only one output stream
256
+ opkt.setStreamIndex (0 );
257
+
258
+ clog << " Write packet: pts=" << opkt.pts () << " , dts=" << opkt.dts () << " / " << opkt.pts ().seconds () << " / " << opkt.timeBase () << " / st: " << opkt.streamIndex () << endl;
259
+
260
+ octx.writePacket (opkt, ec);
261
+ if (ec) {
262
+ cerr << " Error write packet: " << ec << " , " << ec.message () << endl;
263
+ return 1 ;
264
+ }
265
+ }
190
266
}
191
267
if (ec && ec.value () != -EAGAIN)
192
268
{
193
269
clog << " Filter pull error: " << ec << " , " << ec.message () << endl;
194
270
return 1 ;
195
271
}
272
+ cout << " No more frames available in filter" << endl;
196
273
}
197
274
198
- clog << " Flush frames;\n " ;
199
- while (true )
275
+ clog << " Flush frames\n " ;
276
+ // Push a null frame at then end for filters that
277
+ // need the full input before generating any output
278
+ // (animated GIF with a palette is one of them)
279
+ auto null_frame = VideoFrame::null ();
280
+ buffer_src.addVideoFrame (null_frame, 0 , ec);
281
+ // Pull until there are no more incoming frames
282
+ VideoFrame filtered;
283
+ while (buffer_sink.getVideoFrame (filtered, ec))
200
284
{
201
- VideoFrame frame = vdec.decode (Packet (), ec);
202
- if (ec)
203
- {
204
- cerr << " Error: " << ec << " , " << ec.message () << endl;
285
+ filtered.setTimeBase (encoder.timeBase ());
286
+ auto ts = filtered.pts ();
287
+ clog << " filtered: " << filtered.width () << " x" << filtered.height () << " , size=" << filtered.size ()
288
+ << " , ts=" << ts << " , tm: " << ts.seconds () << " , tb: " << filtered.timeBase ()
289
+ << " , ref=" << filtered.isReferenced () << " :" << filtered.refCount () << endl;
290
+
291
+ // Encode
292
+ Packet opkt = filtered ? encoder.encode (filtered, ec) : encoder.encode (ec);
293
+ if (ec) {
294
+ cerr << " Encoding error: " << ec << endl;
205
295
return 1 ;
206
296
}
207
- if (!frame)
208
- break ;
209
297
210
- // Push into the filter
211
- buffer_src.addVideoFrame (frame, ec);
212
- if (ec)
213
- {
214
- clog << " Filter push error: " << ec << " , " << ec.message () << endl;
215
- return 1 ;
216
- }
298
+ if (opkt) {
299
+ // Only one output stream
300
+ opkt.setStreamIndex (0 );
217
301
218
- // Pull until there are no more incoming frames
219
- VideoFrame filtered;
220
- while (buffer_sink.getVideoFrame (filtered, ec))
221
- {
222
- filtered.setTimeBase (frame.timeBase ());
223
- auto ts = filtered.pts ();
224
- clog << " filtered: " << filtered.width () << " x" << filtered.height () << " , size=" << filtered.size ()
225
- << " , ts=" << ts << " , tm: " << ts.seconds () << " , tb: " << filtered.timeBase ()
226
- << " , ref=" << filtered.isReferenced () << " :" << filtered.refCount () << endl;
227
- }
228
- if (ec && ec.value () != -EAGAIN)
229
- {
230
- clog << " Filter pull error: " << ec << " , " << ec.message () << endl;
231
- return 1 ;
302
+ clog << " Write packet: pts=" << opkt.pts () << " , dts=" << opkt.dts () << " / " << opkt.pts ().seconds () << " / " << opkt.timeBase () << " / st: " << opkt.streamIndex () << endl;
303
+
304
+ octx.writePacket (opkt, ec);
305
+ if (ec) {
306
+ cerr << " Error write packet: " << ec << " , " << ec.message () << endl;
307
+ return 1 ;
308
+ }
232
309
}
233
310
}
311
+ cout << " Filter flushed, frames: " << count << endl;
312
+ octx.writeTrailer ();
234
313
}
235
314
}
0 commit comments