Skip to content

Commit 132bb05

Browse files
authored
more generic filter example that also encodes the result (#151)
This improved example also encodes the result and handles correctly streams that require the full input before producing any output (palettegen in this case) It also adds the example to the makefiles
1 parent 6089ac4 commit 132bb05

File tree

3 files changed

+114
-33
lines changed

3 files changed

+114
-33
lines changed

example/api2-samples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(TARGETS
1919
api2-decode-audio
2020
api2-decode-rasample-audio
2121
api2-decode-encode-audio
22+
api2-decode-filter-encode
2223
api2-dict-basic
2324
api2-timestamp
2425
api2-demux-seek

example/api2-samples/api2-decode-filter.cpp renamed to example/api2-samples/api2-decode-filter-encode.cpp

Lines changed: 112 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
using namespace std;
2525
using namespace av;
2626

27+
// Example that uses a complex filter to produce a good
28+
// animated GIF from a video
29+
2730
int main(int argc, char **argv)
2831
{
2932
if (argc < 2)
@@ -33,6 +36,7 @@ int main(int argc, char **argv)
3336
av::setFFmpegLoggingLevel(AV_LOG_DEBUG);
3437

3538
string uri{argv[1]};
39+
string out{argv[2]};
3640

3741
ssize_t videoStream = -1;
3842
VideoDecoderContext vdec;
@@ -98,6 +102,45 @@ int main(int argc, char **argv)
98102
}
99103
}
100104

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+
101144
// Setup filter
102145
Filter filter_buffer_src{"buffer"};
103146
Filter filter_buffer_sink{"buffersink"};
@@ -122,9 +165,16 @@ int main(int argc, char **argv)
122165
}
123166

124167
// 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) {
128178
clog << "Error parsing filter chain: " << ec << ", " << ec.message() << endl;
129179
return 1;
130180
}
@@ -169,7 +219,13 @@ int main(int argc, char **argv)
169219
continue;
170220
}
171221

222+
frame.setTimeBase(encoder.timeBase());
223+
frame.setStreamIndex(0);
224+
frame.setPictureType();
225+
172226
// 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;
173229
buffer_src.addVideoFrame(frame, AV_BUFFERSRC_FLAG_KEEP_REF, ec);
174230
if (ec)
175231
{
@@ -181,55 +237,78 @@ int main(int argc, char **argv)
181237
VideoFrame filtered;
182238
while (buffer_sink.getVideoFrame(filtered, ec))
183239
{
184-
filtered.setTimeBase(frame.timeBase());
240+
filtered.setTimeBase(encoder.timeBase());
185241
auto ts = filtered.pts();
186242

187243
clog << " filtered: " << filtered.width() << "x" << filtered.height() << ", size=" << filtered.size()
188244
<< ", ts=" << ts << ", tm: " << ts.seconds() << ", tb: " << filtered.timeBase()
189245
<< ", 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+
}
190266
}
191267
if (ec && ec.value() != -EAGAIN)
192268
{
193269
clog << "Filter pull error: " << ec << ", " << ec.message() << endl;
194270
return 1;
195271
}
272+
cout << "No more frames available in filter" << endl;
196273
}
197274

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))
200284
{
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;
205295
return 1;
206296
}
207-
if (!frame)
208-
break;
209297

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

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+
}
232309
}
233310
}
311+
cout << "Filter flushed, frames: " << count << endl;
312+
octx.writeTrailer();
234313
}
235314
}

example/api2-samples/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ samples = [
1414
'api2-decode-audio',
1515
'api2-decode-rasample-audio',
1616
'api2-decode-encode-audio',
17+
'api2-decode-filter-encode',
1718
'api2-dict-basic',
1819
'api2-timestamp',
1920
'api2-demux-seek',

0 commit comments

Comments
 (0)