A lot of changes happen at the same time:
- Add a framequeue fifo to AVFilterLink.
- split AVFilterLink.status into status_in and status_out: requires
changes to the few filters and programs that use it directly
(f_interleave, split, filtfmts).
- Add a field ready to AVFilterContext, marking when the filter is ready
and its activation priority.
- Add flags to mark blocked links.
- Change ff_filter_frame() to enqueue the frame.
- Change all filtering functions to update the ready field and the
blocked flags.
- Update ff_filter_graph_run_once() to use the ready field.
- buffersrc: always push the frame immediately.
... | ... |
@@ -34,6 +34,9 @@ |
34 | 34 |
#include "libavutil/rational.h" |
35 | 35 |
#include "libavutil/samplefmt.h" |
36 | 36 |
|
37 |
+#define FF_INTERNAL_FIELDS 1 |
|
38 |
+#include "framequeue.h" |
|
39 |
+ |
|
37 | 40 |
#include "audio.h" |
38 | 41 |
#include "avfilter.h" |
39 | 42 |
#include "formats.h" |
... | ... |
@@ -135,6 +138,10 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, |
135 | 135 |
{ |
136 | 136 |
AVFilterLink *link; |
137 | 137 |
|
138 |
+ av_assert0(src->graph); |
|
139 |
+ av_assert0(dst->graph); |
|
140 |
+ av_assert0(src->graph == dst->graph); |
|
141 |
+ |
|
138 | 142 |
if (src->nb_outputs <= srcpad || dst->nb_inputs <= dstpad || |
139 | 143 |
src->outputs[srcpad] || dst->inputs[dstpad]) |
140 | 144 |
return AVERROR(EINVAL); |
... | ... |
@@ -160,6 +167,7 @@ int avfilter_link(AVFilterContext *src, unsigned srcpad, |
160 | 160 |
link->type = src->output_pads[srcpad].type; |
161 | 161 |
av_assert0(AV_PIX_FMT_NONE == -1 && AV_SAMPLE_FMT_NONE == -1); |
162 | 162 |
link->format = -1; |
163 |
+ ff_framequeue_init(&link->fifo, &src->graph->internal->frame_queues); |
|
163 | 164 |
|
164 | 165 |
return 0; |
165 | 166 |
} |
... | ... |
@@ -170,6 +178,7 @@ void avfilter_link_free(AVFilterLink **link) |
170 | 170 |
return; |
171 | 171 |
|
172 | 172 |
av_frame_free(&(*link)->partial_buf); |
173 |
+ ff_framequeue_free(&(*link)->fifo); |
|
173 | 174 |
ff_video_frame_pool_uninit((FFVideoFramePool**)&(*link)->video_frame_pool); |
174 | 175 |
|
175 | 176 |
av_freep(link); |
... | ... |
@@ -180,16 +189,46 @@ int avfilter_link_get_channels(AVFilterLink *link) |
180 | 180 |
return link->channels; |
181 | 181 |
} |
182 | 182 |
|
183 |
+static void ff_filter_set_ready(AVFilterContext *filter, unsigned priority) |
|
184 |
+{ |
|
185 |
+ filter->ready = FFMAX(filter->ready, priority); |
|
186 |
+} |
|
187 |
+ |
|
188 |
+/** |
|
189 |
+ * Clear frame_blocked_in on all outputs. |
|
190 |
+ * This is necessary whenever something changes on input. |
|
191 |
+ */ |
|
192 |
+static void filter_unblock(AVFilterContext *filter) |
|
193 |
+{ |
|
194 |
+ unsigned i; |
|
195 |
+ |
|
196 |
+ for (i = 0; i < filter->nb_outputs; i++) |
|
197 |
+ filter->outputs[i]->frame_blocked_in = 0; |
|
198 |
+} |
|
199 |
+ |
|
200 |
+ |
|
183 | 201 |
void ff_avfilter_link_set_in_status(AVFilterLink *link, int status, int64_t pts) |
184 | 202 |
{ |
185 |
- ff_avfilter_link_set_out_status(link, status, pts); |
|
203 |
+ if (link->status_in == status) |
|
204 |
+ return; |
|
205 |
+ av_assert0(!link->status_in); |
|
206 |
+ link->status_in = status; |
|
207 |
+ link->status_in_pts = pts; |
|
208 |
+ link->frame_wanted_out = 0; |
|
209 |
+ link->frame_blocked_in = 0; |
|
210 |
+ filter_unblock(link->dst); |
|
211 |
+ ff_filter_set_ready(link->dst, 200); |
|
186 | 212 |
} |
187 | 213 |
|
188 | 214 |
void ff_avfilter_link_set_out_status(AVFilterLink *link, int status, int64_t pts) |
189 | 215 |
{ |
190 |
- link->status = status; |
|
191 |
- link->frame_wanted_in = link->frame_wanted_out = 0; |
|
192 |
- ff_update_link_current_pts(link, pts); |
|
216 |
+ av_assert0(!link->frame_wanted_out); |
|
217 |
+ av_assert0(!link->status_out); |
|
218 |
+ link->status_out = status; |
|
219 |
+ if (pts != AV_NOPTS_VALUE) |
|
220 |
+ ff_update_link_current_pts(link, pts); |
|
221 |
+ filter_unblock(link->dst); |
|
222 |
+ ff_filter_set_ready(link->src, 200); |
|
193 | 223 |
} |
194 | 224 |
|
195 | 225 |
void avfilter_link_set_closed(AVFilterLink *link, int closed) |
... | ... |
@@ -370,10 +409,23 @@ int ff_request_frame(AVFilterLink *link) |
370 | 370 |
{ |
371 | 371 |
FF_TPRINTF_START(NULL, request_frame); ff_tlog_link(NULL, link, 1); |
372 | 372 |
|
373 |
- if (link->status) |
|
374 |
- return link->status; |
|
375 |
- link->frame_wanted_in = 1; |
|
373 |
+ if (link->status_out) |
|
374 |
+ return link->status_out; |
|
375 |
+ if (link->status_in) { |
|
376 |
+ if (ff_framequeue_queued_frames(&link->fifo)) { |
|
377 |
+ av_assert1(!link->frame_wanted_out); |
|
378 |
+ av_assert1(link->dst->ready >= 300); |
|
379 |
+ return 0; |
|
380 |
+ } else { |
|
381 |
+ /* Acknowledge status change. Filters using ff_request_frame() will |
|
382 |
+ handle the change automatically. Filters can also check the |
|
383 |
+ status directly but none do yet. */ |
|
384 |
+ ff_avfilter_link_set_out_status(link, link->status_in, link->status_in_pts); |
|
385 |
+ return link->status_out; |
|
386 |
+ } |
|
387 |
+ } |
|
376 | 388 |
link->frame_wanted_out = 1; |
389 |
+ ff_filter_set_ready(link->src, 100); |
|
377 | 390 |
return 0; |
378 | 391 |
} |
379 | 392 |
|
... | ... |
@@ -382,22 +434,17 @@ int ff_request_frame_to_filter(AVFilterLink *link) |
382 | 382 |
int ret = -1; |
383 | 383 |
|
384 | 384 |
FF_TPRINTF_START(NULL, request_frame_to_filter); ff_tlog_link(NULL, link, 1); |
385 |
- link->frame_wanted_in = 0; |
|
385 |
+ /* Assume the filter is blocked, let the method clear it if not */ |
|
386 |
+ link->frame_blocked_in = 1; |
|
386 | 387 |
if (link->srcpad->request_frame) |
387 | 388 |
ret = link->srcpad->request_frame(link); |
388 | 389 |
else if (link->src->inputs[0]) |
389 | 390 |
ret = ff_request_frame(link->src->inputs[0]); |
390 |
- if (ret == AVERROR_EOF && link->partial_buf) { |
|
391 |
- AVFrame *pbuf = link->partial_buf; |
|
392 |
- link->partial_buf = NULL; |
|
393 |
- ret = ff_filter_frame_framed(link, pbuf); |
|
394 |
- ff_avfilter_link_set_in_status(link, AVERROR_EOF, AV_NOPTS_VALUE); |
|
395 |
- link->frame_wanted_out = 0; |
|
396 |
- return ret; |
|
397 |
- } |
|
398 | 391 |
if (ret < 0) { |
399 |
- if (ret != AVERROR(EAGAIN) && ret != link->status) |
|
392 |
+ if (ret != AVERROR(EAGAIN) && ret != link->status_in) |
|
400 | 393 |
ff_avfilter_link_set_in_status(link, ret, AV_NOPTS_VALUE); |
394 |
+ if (ret == AVERROR_EOF) |
|
395 |
+ ret = 0; |
|
401 | 396 |
} |
402 | 397 |
return ret; |
403 | 398 |
} |
... | ... |
@@ -1056,11 +1103,6 @@ static int ff_filter_frame_framed(AVFilterLink *link, AVFrame *frame) |
1056 | 1056 |
AVFilterCommand *cmd= link->dst->command_queue; |
1057 | 1057 |
int64_t pts; |
1058 | 1058 |
|
1059 |
- if (link->status) { |
|
1060 |
- av_frame_free(&frame); |
|
1061 |
- return link->status; |
|
1062 |
- } |
|
1063 |
- |
|
1064 | 1059 |
if (!(filter_frame = dst->filter_frame)) |
1065 | 1060 |
filter_frame = default_filter_frame; |
1066 | 1061 |
|
... | ... |
@@ -1142,52 +1184,9 @@ fail: |
1142 | 1142 |
return ret; |
1143 | 1143 |
} |
1144 | 1144 |
|
1145 |
-static int ff_filter_frame_needs_framing(AVFilterLink *link, AVFrame *frame) |
|
1146 |
-{ |
|
1147 |
- int insamples = frame->nb_samples, inpos = 0, nb_samples; |
|
1148 |
- AVFrame *pbuf = link->partial_buf; |
|
1149 |
- int nb_channels = av_frame_get_channels(frame); |
|
1150 |
- int ret = 0; |
|
1151 |
- |
|
1152 |
- /* Handle framing (min_samples, max_samples) */ |
|
1153 |
- while (insamples) { |
|
1154 |
- if (!pbuf) { |
|
1155 |
- AVRational samples_tb = { 1, link->sample_rate }; |
|
1156 |
- pbuf = ff_get_audio_buffer(link, link->partial_buf_size); |
|
1157 |
- if (!pbuf) { |
|
1158 |
- av_log(link->dst, AV_LOG_WARNING, |
|
1159 |
- "Samples dropped due to memory allocation failure.\n"); |
|
1160 |
- return 0; |
|
1161 |
- } |
|
1162 |
- av_frame_copy_props(pbuf, frame); |
|
1163 |
- pbuf->pts = frame->pts; |
|
1164 |
- if (pbuf->pts != AV_NOPTS_VALUE) |
|
1165 |
- pbuf->pts += av_rescale_q(inpos, samples_tb, link->time_base); |
|
1166 |
- pbuf->nb_samples = 0; |
|
1167 |
- } |
|
1168 |
- nb_samples = FFMIN(insamples, |
|
1169 |
- link->partial_buf_size - pbuf->nb_samples); |
|
1170 |
- av_samples_copy(pbuf->extended_data, frame->extended_data, |
|
1171 |
- pbuf->nb_samples, inpos, |
|
1172 |
- nb_samples, nb_channels, link->format); |
|
1173 |
- inpos += nb_samples; |
|
1174 |
- insamples -= nb_samples; |
|
1175 |
- pbuf->nb_samples += nb_samples; |
|
1176 |
- if (pbuf->nb_samples >= link->min_samples) { |
|
1177 |
- ret = ff_filter_frame_framed(link, pbuf); |
|
1178 |
- pbuf = NULL; |
|
1179 |
- } else { |
|
1180 |
- if (link->frame_wanted_out) |
|
1181 |
- link->frame_wanted_in = 1; |
|
1182 |
- } |
|
1183 |
- } |
|
1184 |
- av_frame_free(&frame); |
|
1185 |
- link->partial_buf = pbuf; |
|
1186 |
- return ret; |
|
1187 |
-} |
|
1188 |
- |
|
1189 | 1145 |
int ff_filter_frame(AVFilterLink *link, AVFrame *frame) |
1190 | 1146 |
{ |
1147 |
+ int ret; |
|
1191 | 1148 |
FF_TPRINTF_START(NULL, filter_frame); ff_tlog_link(NULL, link, 1); ff_tlog(NULL, " "); ff_tlog_ref(NULL, frame, 1); |
1192 | 1149 |
|
1193 | 1150 |
/* Consistency checks */ |
... | ... |
@@ -1220,23 +1219,329 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame) |
1220 | 1220 |
} |
1221 | 1221 |
} |
1222 | 1222 |
|
1223 |
- link->frame_wanted_out = 0; |
|
1223 |
+ link->frame_blocked_in = link->frame_wanted_out = 0; |
|
1224 | 1224 |
link->frame_count_in++; |
1225 |
- /* Go directly to actual filtering if possible */ |
|
1226 |
- if (link->type == AVMEDIA_TYPE_AUDIO && |
|
1227 |
- link->min_samples && |
|
1228 |
- (link->partial_buf || |
|
1229 |
- frame->nb_samples < link->min_samples || |
|
1230 |
- frame->nb_samples > link->max_samples)) { |
|
1231 |
- return ff_filter_frame_needs_framing(link, frame); |
|
1232 |
- } else { |
|
1233 |
- return ff_filter_frame_framed(link, frame); |
|
1225 |
+ filter_unblock(link->dst); |
|
1226 |
+ ret = ff_framequeue_add(&link->fifo, frame); |
|
1227 |
+ if (ret < 0) { |
|
1228 |
+ av_frame_free(&frame); |
|
1229 |
+ return ret; |
|
1234 | 1230 |
} |
1231 |
+ ff_filter_set_ready(link->dst, 300); |
|
1232 |
+ return 0; |
|
1233 |
+ |
|
1235 | 1234 |
error: |
1236 | 1235 |
av_frame_free(&frame); |
1237 | 1236 |
return AVERROR_PATCHWELCOME; |
1238 | 1237 |
} |
1239 | 1238 |
|
1239 |
+static int samples_ready(AVFilterLink *link) |
|
1240 |
+{ |
|
1241 |
+ return ff_framequeue_queued_frames(&link->fifo) && |
|
1242 |
+ (ff_framequeue_queued_samples(&link->fifo) >= link->min_samples || |
|
1243 |
+ link->status_in); |
|
1244 |
+} |
|
1245 |
+ |
|
1246 |
+static int take_samples(AVFilterLink *link, unsigned min, unsigned max, |
|
1247 |
+ AVFrame **rframe) |
|
1248 |
+{ |
|
1249 |
+ AVFrame *frame0, *frame, *buf; |
|
1250 |
+ unsigned nb_samples, nb_frames, i, p; |
|
1251 |
+ int ret; |
|
1252 |
+ |
|
1253 |
+ /* Note: this function relies on no format changes and must only be |
|
1254 |
+ called with enough samples. */ |
|
1255 |
+ av_assert1(samples_ready(link)); |
|
1256 |
+ frame0 = frame = ff_framequeue_peek(&link->fifo, 0); |
|
1257 |
+ if (frame->nb_samples >= min && frame->nb_samples < max) { |
|
1258 |
+ *rframe = ff_framequeue_take(&link->fifo); |
|
1259 |
+ return 0; |
|
1260 |
+ } |
|
1261 |
+ nb_frames = 0; |
|
1262 |
+ nb_samples = 0; |
|
1263 |
+ while (1) { |
|
1264 |
+ if (nb_samples + frame->nb_samples > max) { |
|
1265 |
+ if (nb_samples < min) |
|
1266 |
+ nb_samples = max; |
|
1267 |
+ break; |
|
1268 |
+ } |
|
1269 |
+ nb_samples += frame->nb_samples; |
|
1270 |
+ nb_frames++; |
|
1271 |
+ if (nb_frames == ff_framequeue_queued_frames(&link->fifo)) |
|
1272 |
+ break; |
|
1273 |
+ frame = ff_framequeue_peek(&link->fifo, nb_frames); |
|
1274 |
+ } |
|
1275 |
+ |
|
1276 |
+ buf = ff_get_audio_buffer(link, nb_samples); |
|
1277 |
+ if (!buf) |
|
1278 |
+ return AVERROR(ENOMEM); |
|
1279 |
+ ret = av_frame_copy_props(buf, frame0); |
|
1280 |
+ if (ret < 0) { |
|
1281 |
+ av_frame_free(&buf); |
|
1282 |
+ return ret; |
|
1283 |
+ } |
|
1284 |
+ buf->pts = frame0->pts; |
|
1285 |
+ |
|
1286 |
+ p = 0; |
|
1287 |
+ for (i = 0; i < nb_frames; i++) { |
|
1288 |
+ frame = ff_framequeue_take(&link->fifo); |
|
1289 |
+ av_samples_copy(buf->extended_data, frame->extended_data, p, 0, |
|
1290 |
+ frame->nb_samples, link->channels, link->format); |
|
1291 |
+ p += frame->nb_samples; |
|
1292 |
+ } |
|
1293 |
+ if (p < nb_samples) { |
|
1294 |
+ unsigned n = nb_samples - p; |
|
1295 |
+ frame = ff_framequeue_peek(&link->fifo, 0); |
|
1296 |
+ av_samples_copy(buf->extended_data, frame->extended_data, p, 0, n, |
|
1297 |
+ link->channels, link->format); |
|
1298 |
+ frame->nb_samples -= n; |
|
1299 |
+ av_samples_copy(frame->extended_data, frame->extended_data, 0, n, |
|
1300 |
+ frame->nb_samples, link->channels, link->format); |
|
1301 |
+ if (frame->pts != AV_NOPTS_VALUE) |
|
1302 |
+ frame->pts += av_rescale_q(n, av_make_q(1, link->sample_rate), link->time_base); |
|
1303 |
+ ff_framequeue_update_peeked(&link->fifo, 0); |
|
1304 |
+ ff_framequeue_skip_samples(&link->fifo, n); |
|
1305 |
+ } |
|
1306 |
+ |
|
1307 |
+ *rframe = buf; |
|
1308 |
+ return 0; |
|
1309 |
+} |
|
1310 |
+ |
|
1311 |
+int ff_filter_frame_to_filter(AVFilterLink *link) |
|
1312 |
+{ |
|
1313 |
+ AVFrame *frame; |
|
1314 |
+ AVFilterContext *dst = link->dst; |
|
1315 |
+ int ret; |
|
1316 |
+ |
|
1317 |
+ av_assert1(ff_framequeue_queued_frames(&link->fifo)); |
|
1318 |
+ if (link->min_samples) { |
|
1319 |
+ int min = link->min_samples; |
|
1320 |
+ if (link->status_in) |
|
1321 |
+ min = FFMIN(min, ff_framequeue_queued_samples(&link->fifo)); |
|
1322 |
+ ret = take_samples(link, min, link->max_samples, &frame); |
|
1323 |
+ if (ret < 0) |
|
1324 |
+ return ret; |
|
1325 |
+ } else { |
|
1326 |
+ frame = ff_framequeue_take(&link->fifo); |
|
1327 |
+ } |
|
1328 |
+ /* The filter will soon have received a new frame, that may allow it to |
|
1329 |
+ produce one or more: unblock its outputs. */ |
|
1330 |
+ filter_unblock(dst); |
|
1331 |
+ ret = ff_filter_frame_framed(link, frame); |
|
1332 |
+ if (ret < 0 && ret != link->status_out) { |
|
1333 |
+ ff_avfilter_link_set_out_status(link, ret, AV_NOPTS_VALUE); |
|
1334 |
+ } else { |
|
1335 |
+ /* Run once again, to see if several frames were available, or if |
|
1336 |
+ the input status has also changed, or any other reason. */ |
|
1337 |
+ ff_filter_set_ready(dst, 300); |
|
1338 |
+ } |
|
1339 |
+ return ret; |
|
1340 |
+} |
|
1341 |
+ |
|
1342 |
+static int forward_status_change(AVFilterContext *filter, AVFilterLink *in) |
|
1343 |
+{ |
|
1344 |
+ unsigned out = 0, progress = 0; |
|
1345 |
+ int ret; |
|
1346 |
+ |
|
1347 |
+ av_assert0(!in->status_out); |
|
1348 |
+ if (!filter->nb_outputs) { |
|
1349 |
+ /* not necessary with the current API and sinks */ |
|
1350 |
+ return 0; |
|
1351 |
+ } |
|
1352 |
+ while (!in->status_out) { |
|
1353 |
+ if (!filter->outputs[out]->status_in) { |
|
1354 |
+ progress++; |
|
1355 |
+ ret = ff_request_frame_to_filter(filter->outputs[out]); |
|
1356 |
+ if (ret < 0) |
|
1357 |
+ return ret; |
|
1358 |
+ } |
|
1359 |
+ if (++out == filter->nb_outputs) { |
|
1360 |
+ if (!progress) { |
|
1361 |
+ /* Every output already closed: input no longer interesting |
|
1362 |
+ (example: overlay in shortest mode, other input closed). */ |
|
1363 |
+ ff_avfilter_link_set_out_status(in, in->status_in, in->status_in_pts); |
|
1364 |
+ return 0; |
|
1365 |
+ } |
|
1366 |
+ progress = 0; |
|
1367 |
+ out = 0; |
|
1368 |
+ } |
|
1369 |
+ } |
|
1370 |
+ ff_filter_set_ready(filter, 200); |
|
1371 |
+ return 0; |
|
1372 |
+} |
|
1373 |
+ |
|
1374 |
+#define FFERROR_NOT_READY FFERRTAG('N','R','D','Y') |
|
1375 |
+ |
|
1376 |
+static int ff_filter_activate_default(AVFilterContext *filter) |
|
1377 |
+{ |
|
1378 |
+ unsigned i; |
|
1379 |
+ |
|
1380 |
+ for (i = 0; i < filter->nb_inputs; i++) { |
|
1381 |
+ if (samples_ready(filter->inputs[i])) { |
|
1382 |
+ return ff_filter_frame_to_filter(filter->inputs[i]); |
|
1383 |
+ } |
|
1384 |
+ } |
|
1385 |
+ for (i = 0; i < filter->nb_inputs; i++) { |
|
1386 |
+ if (filter->inputs[i]->status_in && !filter->inputs[i]->status_out) { |
|
1387 |
+ av_assert1(!ff_framequeue_queued_frames(&filter->inputs[i]->fifo)); |
|
1388 |
+ return forward_status_change(filter, filter->inputs[i]); |
|
1389 |
+ } |
|
1390 |
+ } |
|
1391 |
+ for (i = 0; i < filter->nb_outputs; i++) { |
|
1392 |
+ if (filter->outputs[i]->frame_wanted_out && |
|
1393 |
+ !filter->outputs[i]->frame_blocked_in) { |
|
1394 |
+ return ff_request_frame_to_filter(filter->outputs[i]); |
|
1395 |
+ } |
|
1396 |
+ } |
|
1397 |
+ return FFERROR_NOT_READY; |
|
1398 |
+} |
|
1399 |
+ |
|
1400 |
+/* |
|
1401 |
+ Filter scheduling and activation |
|
1402 |
+ |
|
1403 |
+ When a filter is activated, it must: |
|
1404 |
+ - if possible, output a frame; |
|
1405 |
+ - else, if relevant, forward the input status change; |
|
1406 |
+ - else, check outputs for wanted frames and forward the requests. |
|
1407 |
+ |
|
1408 |
+ The following AVFilterLink fields are used for activation: |
|
1409 |
+ |
|
1410 |
+ - frame_wanted_out: |
|
1411 |
+ |
|
1412 |
+ This field indicates if a frame is needed on this input of the |
|
1413 |
+ destination filter. A positive value indicates that a frame is needed |
|
1414 |
+ to process queued frames or internal data or to satisfy the |
|
1415 |
+ application; a zero value indicates that a frame is not especially |
|
1416 |
+ needed but could be processed anyway; a negative value indicates that a |
|
1417 |
+ frame would just be queued. |
|
1418 |
+ |
|
1419 |
+ It is set by filters using ff_request_frame() or ff_request_no_frame(), |
|
1420 |
+ when requested by the application through a specific API or when it is |
|
1421 |
+ set on one of the outputs. |
|
1422 |
+ |
|
1423 |
+ It is cleared when a frame is sent from the source using |
|
1424 |
+ ff_filter_frame(). |
|
1425 |
+ |
|
1426 |
+ It is also cleared when a status change is sent from the source using |
|
1427 |
+ ff_avfilter_link_set_in_status(). |
|
1428 |
+ |
|
1429 |
+ - frame_blocked_in: |
|
1430 |
+ |
|
1431 |
+ This field means that the source filter can not generate a frame as is. |
|
1432 |
+ Its goal is to avoid repeatedly calling the request_frame() method on |
|
1433 |
+ the same link. |
|
1434 |
+ |
|
1435 |
+ It is set by the framework on all outputs of a filter before activating it. |
|
1436 |
+ |
|
1437 |
+ It is automatically cleared by ff_filter_frame(). |
|
1438 |
+ |
|
1439 |
+ It is also automatically cleared by ff_avfilter_link_set_in_status(). |
|
1440 |
+ |
|
1441 |
+ It is also cleared on all outputs (using filter_unblock()) when |
|
1442 |
+ something happens on an input: processing a frame or changing the |
|
1443 |
+ status. |
|
1444 |
+ |
|
1445 |
+ - fifo: |
|
1446 |
+ |
|
1447 |
+ Contains the frames queued on a filter input. If it contains frames and |
|
1448 |
+ frame_wanted_out is not set, then the filter can be activated. If that |
|
1449 |
+ result in the filter not able to use these frames, the filter must set |
|
1450 |
+ frame_wanted_out to ask for more frames. |
|
1451 |
+ |
|
1452 |
+ - status_in and status_in_pts: |
|
1453 |
+ |
|
1454 |
+ Status (EOF or error code) of the link and timestamp of the status |
|
1455 |
+ change (in link time base, same as frames) as seen from the input of |
|
1456 |
+ the link. The status change is considered happening after the frames |
|
1457 |
+ queued in fifo. |
|
1458 |
+ |
|
1459 |
+ It is set by the source filter using ff_avfilter_link_set_in_status(). |
|
1460 |
+ |
|
1461 |
+ - status_out: |
|
1462 |
+ |
|
1463 |
+ Status of the link as seen from the output of the link. The status |
|
1464 |
+ change is considered having already happened. |
|
1465 |
+ |
|
1466 |
+ It is set by the destination filter using |
|
1467 |
+ ff_avfilter_link_set_out_status(). |
|
1468 |
+ |
|
1469 |
+ Filters are activated according to the ready field, set using the |
|
1470 |
+ ff_filter_set_ready(). Eventually, a priority queue will be used. |
|
1471 |
+ ff_filter_set_ready() is called whenever anything could cause progress to |
|
1472 |
+ be possible. Marking a filter ready when it is not is not a problem, |
|
1473 |
+ except for the small overhead it causes. |
|
1474 |
+ |
|
1475 |
+ Conditions that cause a filter to be marked ready are: |
|
1476 |
+ |
|
1477 |
+ - frames added on an input link; |
|
1478 |
+ |
|
1479 |
+ - changes in the input or output status of an input link; |
|
1480 |
+ |
|
1481 |
+ - requests for a frame on an output link; |
|
1482 |
+ |
|
1483 |
+ - after any actual processing using the legacy methods (filter_frame(), |
|
1484 |
+ and request_frame() to acknowledge status changes), to run once more |
|
1485 |
+ and check if enough input was present for several frames. |
|
1486 |
+ |
|
1487 |
+ Exemples of scenarios to consider: |
|
1488 |
+ |
|
1489 |
+ - buffersrc: activate if frame_wanted_out to notify the application; |
|
1490 |
+ activate when the application adds a frame to push it immediately. |
|
1491 |
+ |
|
1492 |
+ - testsrc: activate only if frame_wanted_out to produce and push a frame. |
|
1493 |
+ |
|
1494 |
+ - concat (not at stitch points): can process a frame on any output. |
|
1495 |
+ Activate if frame_wanted_out on output to forward on the corresponding |
|
1496 |
+ input. Activate when a frame is present on input to process it |
|
1497 |
+ immediately. |
|
1498 |
+ |
|
1499 |
+ - framesync: needs at least one frame on each input; extra frames on the |
|
1500 |
+ wrong input will accumulate. When a frame is first added on one input, |
|
1501 |
+ set frame_wanted_out<0 on it to avoid getting more (would trigger |
|
1502 |
+ testsrc) and frame_wanted_out>0 on the other to allow processing it. |
|
1503 |
+ |
|
1504 |
+ Activation of old filters: |
|
1505 |
+ |
|
1506 |
+ In order to activate a filter implementing the legacy filter_frame() and |
|
1507 |
+ request_frame() methods, perform the first possible of the following |
|
1508 |
+ actions: |
|
1509 |
+ |
|
1510 |
+ - If an input has frames in fifo and frame_wanted_out == 0, dequeue a |
|
1511 |
+ frame and call filter_frame(). |
|
1512 |
+ |
|
1513 |
+ Ratinale: filter frames as soon as possible instead of leaving them |
|
1514 |
+ queued; frame_wanted_out < 0 is not possible since the old API does not |
|
1515 |
+ set it nor provides any similar feedback; frame_wanted_out > 0 happens |
|
1516 |
+ when min_samples > 0 and there are not enough samples queued. |
|
1517 |
+ |
|
1518 |
+ - If an input has status_in set but not status_out, try to call |
|
1519 |
+ request_frame() on one of the outputs in the hope that it will trigger |
|
1520 |
+ request_frame() on the input with status_in and acknowledge it. This is |
|
1521 |
+ awkward and fragile, filters with several inputs or outputs should be |
|
1522 |
+ updated to direct activation as soon as possible. |
|
1523 |
+ |
|
1524 |
+ - If an output has frame_wanted_out > 0 and not frame_blocked_in, call |
|
1525 |
+ request_frame(). |
|
1526 |
+ |
|
1527 |
+ Rationale: checking frame_blocked_in is necessary to avoid requesting |
|
1528 |
+ repeatedly on a blocked input if another is not blocked (example: |
|
1529 |
+ [buffersrc1][testsrc1][buffersrc2][testsrc2]concat=v=2). |
|
1530 |
+ |
|
1531 |
+ TODO: respect needs_fifo and remove auto-inserted fifos. |
|
1532 |
+ |
|
1533 |
+ */ |
|
1534 |
+ |
|
1535 |
+int ff_filter_activate(AVFilterContext *filter) |
|
1536 |
+{ |
|
1537 |
+ int ret; |
|
1538 |
+ |
|
1539 |
+ filter->ready = 0; |
|
1540 |
+ ret = ff_filter_activate_default(filter); |
|
1541 |
+ if (ret == FFERROR_NOT_READY) |
|
1542 |
+ ret = 0; |
|
1543 |
+ return ret; |
|
1544 |
+} |
|
1545 |
+ |
|
1240 | 1546 |
const AVClass *avfilter_get_class(void) |
1241 | 1547 |
{ |
1242 | 1548 |
return &avfilter_class; |
... | ... |
@@ -368,6 +368,13 @@ struct AVFilterContext { |
368 | 368 |
* Overrides global number of threads set per filter graph. |
369 | 369 |
*/ |
370 | 370 |
int nb_threads; |
371 |
+ |
|
372 |
+ /** |
|
373 |
+ * Ready status of the filter. |
|
374 |
+ * A non-0 value means that the filter needs activating; |
|
375 |
+ * a higher value suggests a more urgent activation. |
|
376 |
+ */ |
|
377 |
+ unsigned ready; |
|
371 | 378 |
}; |
372 | 379 |
|
373 | 380 |
/** |
... | ... |
@@ -509,18 +516,6 @@ struct AVFilterLink { |
509 | 509 |
int max_samples; |
510 | 510 |
|
511 | 511 |
/** |
512 |
- * Link status. |
|
513 |
- * If not zero, all attempts of filter_frame or request_frame |
|
514 |
- * will fail with the corresponding code, and if necessary the reference |
|
515 |
- * will be destroyed. |
|
516 |
- * If request_frame returns an error, the status is set on the |
|
517 |
- * corresponding link. |
|
518 |
- * It can be set also be set by either the source or the destination |
|
519 |
- * filter. |
|
520 |
- */ |
|
521 |
- int status; |
|
522 |
- |
|
523 |
- /** |
|
524 | 512 |
* Number of channels. |
525 | 513 |
*/ |
526 | 514 |
int channels; |
... | ... |
@@ -541,13 +536,6 @@ struct AVFilterLink { |
541 | 541 |
void *video_frame_pool; |
542 | 542 |
|
543 | 543 |
/** |
544 |
- * True if a frame is currently wanted on the input of this filter. |
|
545 |
- * Set when ff_request_frame() is called by the output, |
|
546 |
- * cleared when the request is handled or forwarded. |
|
547 |
- */ |
|
548 |
- int frame_wanted_in; |
|
549 |
- |
|
550 |
- /** |
|
551 | 544 |
* True if a frame is currently wanted on the output of this filter. |
552 | 545 |
* Set when ff_request_frame() is called by the output, |
553 | 546 |
* cleared when a frame is filtered. |
... | ... |
@@ -559,6 +547,51 @@ struct AVFilterLink { |
559 | 559 |
* AVHWFramesContext describing the frames. |
560 | 560 |
*/ |
561 | 561 |
AVBufferRef *hw_frames_ctx; |
562 |
+ |
|
563 |
+#ifndef FF_INTERNAL_FIELDS |
|
564 |
+ |
|
565 |
+ /** |
|
566 |
+ * Internal structure members. |
|
567 |
+ * The fields below this limit are internal for libavfilter's use |
|
568 |
+ * and must in no way be accessed by applications. |
|
569 |
+ */ |
|
570 |
+ char reserved[0xF000]; |
|
571 |
+ |
|
572 |
+#else /* FF_INTERNAL_FIELDS */ |
|
573 |
+ |
|
574 |
+ /** |
|
575 |
+ * Queue of frames waiting to be filtered. |
|
576 |
+ */ |
|
577 |
+ FFFrameQueue fifo; |
|
578 |
+ |
|
579 |
+ /** |
|
580 |
+ * If set, the source filter can not generate a frame as is. |
|
581 |
+ * The goal is to avoid repeatedly calling the request_frame() method on |
|
582 |
+ * the same link. |
|
583 |
+ */ |
|
584 |
+ int frame_blocked_in; |
|
585 |
+ |
|
586 |
+ /** |
|
587 |
+ * Link input status. |
|
588 |
+ * If not zero, all attempts of filter_frame will fail with the |
|
589 |
+ * corresponding code. |
|
590 |
+ */ |
|
591 |
+ int status_in; |
|
592 |
+ |
|
593 |
+ /** |
|
594 |
+ * Timestamp of the input status change. |
|
595 |
+ */ |
|
596 |
+ int64_t status_in_pts; |
|
597 |
+ |
|
598 |
+ /** |
|
599 |
+ * Link output status. |
|
600 |
+ * If not zero, all attempts of request_frame will fail with the |
|
601 |
+ * corresponding code. |
|
602 |
+ */ |
|
603 |
+ int status_out; |
|
604 |
+ |
|
605 |
+#endif /* FF_INTERNAL_FIELDS */ |
|
606 |
+ |
|
562 | 607 |
}; |
563 | 608 |
|
564 | 609 |
/** |
... | ... |
@@ -32,6 +32,9 @@ |
32 | 32 |
#include "libavutil/opt.h" |
33 | 33 |
#include "libavutil/pixdesc.h" |
34 | 34 |
|
35 |
+#define FF_INTERNAL_FIELDS 1 |
|
36 |
+#include "framequeue.h" |
|
37 |
+ |
|
35 | 38 |
#include "avfilter.h" |
36 | 39 |
#include "formats.h" |
37 | 40 |
#include "internal.h" |
... | ... |
@@ -87,6 +90,7 @@ AVFilterGraph *avfilter_graph_alloc(void) |
87 | 87 |
|
88 | 88 |
ret->av_class = &filtergraph_class; |
89 | 89 |
av_opt_set_defaults(ret); |
90 |
+ ff_framequeue_global_init(&ret->internal->frame_queues); |
|
90 | 91 |
|
91 | 92 |
return ret; |
92 | 93 |
} |
... | ... |
@@ -1377,10 +1381,10 @@ void ff_avfilter_graph_update_heap(AVFilterGraph *graph, AVFilterLink *link) |
1377 | 1377 |
heap_bubble_down(graph, link, link->age_index); |
1378 | 1378 |
} |
1379 | 1379 |
|
1380 |
- |
|
1381 | 1380 |
int avfilter_graph_request_oldest(AVFilterGraph *graph) |
1382 | 1381 |
{ |
1383 | 1382 |
AVFilterLink *oldest = graph->sink_links[0]; |
1383 |
+ int64_t frame_count; |
|
1384 | 1384 |
int r; |
1385 | 1385 |
|
1386 | 1386 |
while (graph->sink_links_count) { |
... | ... |
@@ -1400,7 +1404,8 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) |
1400 | 1400 |
if (!graph->sink_links_count) |
1401 | 1401 |
return AVERROR_EOF; |
1402 | 1402 |
av_assert1(oldest->age_index >= 0); |
1403 |
- while (oldest->frame_wanted_out) { |
|
1403 |
+ frame_count = oldest->frame_count_out; |
|
1404 |
+ while (frame_count == oldest->frame_count_out) { |
|
1404 | 1405 |
r = ff_filter_graph_run_once(graph); |
1405 | 1406 |
if (r < 0) |
1406 | 1407 |
return r; |
... | ... |
@@ -1408,41 +1413,17 @@ int avfilter_graph_request_oldest(AVFilterGraph *graph) |
1408 | 1408 |
return 0; |
1409 | 1409 |
} |
1410 | 1410 |
|
1411 |
-static AVFilterLink *graph_run_once_find_filter(AVFilterGraph *graph) |
|
1412 |
-{ |
|
1413 |
- unsigned i, j; |
|
1414 |
- AVFilterContext *f; |
|
1415 |
- |
|
1416 |
- /* TODO: replace scanning the graph with a priority list */ |
|
1417 |
- for (i = 0; i < graph->nb_filters; i++) { |
|
1418 |
- f = graph->filters[i]; |
|
1419 |
- for (j = 0; j < f->nb_outputs; j++) |
|
1420 |
- if (f->outputs[j]->frame_wanted_in) |
|
1421 |
- return f->outputs[j]; |
|
1422 |
- } |
|
1423 |
- for (i = 0; i < graph->nb_filters; i++) { |
|
1424 |
- f = graph->filters[i]; |
|
1425 |
- for (j = 0; j < f->nb_outputs; j++) |
|
1426 |
- if (f->outputs[j]->frame_wanted_out) |
|
1427 |
- return f->outputs[j]; |
|
1428 |
- } |
|
1429 |
- return NULL; |
|
1430 |
-} |
|
1431 |
- |
|
1432 | 1411 |
int ff_filter_graph_run_once(AVFilterGraph *graph) |
1433 | 1412 |
{ |
1434 |
- AVFilterLink *link; |
|
1435 |
- int ret; |
|
1436 |
- |
|
1437 |
- link = graph_run_once_find_filter(graph); |
|
1438 |
- if (!link) { |
|
1439 |
- av_log(NULL, AV_LOG_WARNING, "Useless run of a filter graph\n"); |
|
1413 |
+ AVFilterContext *filter; |
|
1414 |
+ unsigned i; |
|
1415 |
+ |
|
1416 |
+ av_assert0(graph->nb_filters); |
|
1417 |
+ filter = graph->filters[0]; |
|
1418 |
+ for (i = 1; i < graph->nb_filters; i++) |
|
1419 |
+ if (graph->filters[i]->ready > filter->ready) |
|
1420 |
+ filter = graph->filters[i]; |
|
1421 |
+ if (!filter->ready) |
|
1440 | 1422 |
return AVERROR(EAGAIN); |
1441 |
- } |
|
1442 |
- ret = ff_request_frame_to_filter(link); |
|
1443 |
- if (ret == AVERROR_EOF) |
|
1444 |
- /* local EOF will be forwarded through request_frame() / |
|
1445 |
- set_status() until it reaches the sink */ |
|
1446 |
- ret = 0; |
|
1447 |
- return ret < 0 ? ret : 1; |
|
1423 |
+ return ff_filter_activate(filter); |
|
1448 | 1424 |
} |
... | ... |
@@ -31,6 +31,9 @@ |
31 | 31 |
#include "libavutil/mathematics.h" |
32 | 32 |
#include "libavutil/opt.h" |
33 | 33 |
|
34 |
+#define FF_INTERNAL_FIELDS 1 |
|
35 |
+#include "framequeue.h" |
|
36 |
+ |
|
34 | 37 |
#include "audio.h" |
35 | 38 |
#include "avfilter.h" |
36 | 39 |
#include "buffersink.h" |
... | ... |
@@ -129,18 +132,26 @@ int attribute_align_arg av_buffersink_get_frame_flags(AVFilterContext *ctx, AVFr |
129 | 129 |
{ |
130 | 130 |
BufferSinkContext *buf = ctx->priv; |
131 | 131 |
AVFilterLink *inlink = ctx->inputs[0]; |
132 |
- int ret; |
|
132 |
+ int peek_in_framequeue = 0, ret; |
|
133 |
+ int64_t frame_count; |
|
133 | 134 |
AVFrame *cur_frame; |
134 | 135 |
|
135 | 136 |
/* no picref available, fetch it from the filterchain */ |
136 | 137 |
while (!av_fifo_size(buf->fifo)) { |
137 |
- if (inlink->status) |
|
138 |
- return inlink->status; |
|
139 |
- if (flags & AV_BUFFERSINK_FLAG_NO_REQUEST) |
|
138 |
+ /* if peek_in_framequeue is true later, then ff_request_frame() and |
|
139 |
+ the ff_filter_graph_run_once() loop will take a frame from it and |
|
140 |
+ move it to the internal fifo, ending the global loop */ |
|
141 |
+ av_assert0(!peek_in_framequeue); |
|
142 |
+ if (inlink->status_out) |
|
143 |
+ return inlink->status_out; |
|
144 |
+ peek_in_framequeue = ff_framequeue_queued_frames(&inlink->fifo) && |
|
145 |
+ ff_framequeue_queued_samples(&inlink->fifo) >= inlink->min_samples; |
|
146 |
+ if ((flags & AV_BUFFERSINK_FLAG_NO_REQUEST) && !peek_in_framequeue) |
|
140 | 147 |
return AVERROR(EAGAIN); |
141 | 148 |
if ((ret = ff_request_frame(inlink)) < 0) |
142 | 149 |
return ret; |
143 |
- while (inlink->frame_wanted_out) { |
|
150 |
+ frame_count = inlink->frame_count_out; |
|
151 |
+ while (frame_count == inlink->frame_count_out) { |
|
144 | 152 |
ret = ff_filter_graph_run_once(ctx->graph); |
145 | 153 |
if (ret < 0) |
146 | 154 |
return ret; |
... | ... |
@@ -184,6 +184,7 @@ static int av_buffersrc_add_frame_internal(AVFilterContext *ctx, |
184 | 184 |
|
185 | 185 |
if (!frame) { |
186 | 186 |
s->eof = 1; |
187 |
+ ff_avfilter_link_set_in_status(ctx->outputs[0], AVERROR_EOF, AV_NOPTS_VALUE); |
|
187 | 188 |
return 0; |
188 | 189 |
} else if (s->eof) |
189 | 190 |
return AVERROR(EINVAL); |
... | ... |
@@ -235,9 +236,8 @@ static int av_buffersrc_add_frame_internal(AVFilterContext *ctx, |
235 | 235 |
return ret; |
236 | 236 |
} |
237 | 237 |
|
238 |
- if ((flags & AV_BUFFERSRC_FLAG_PUSH)) |
|
239 |
- if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0) |
|
240 |
- return ret; |
|
238 |
+ if ((ret = ctx->output_pads[0].request_frame(ctx->outputs[0])) < 0) |
|
239 |
+ return ret; |
|
241 | 240 |
|
242 | 241 |
return 0; |
243 | 242 |
} |
... | ... |
@@ -26,6 +26,10 @@ |
26 | 26 |
#include "libavutil/avassert.h" |
27 | 27 |
#include "libavutil/avstring.h" |
28 | 28 |
#include "libavutil/opt.h" |
29 |
+ |
|
30 |
+#define FF_INTERNAL_FIELDS 1 |
|
31 |
+#include "framequeue.h" |
|
32 |
+ |
|
29 | 33 |
#include "avfilter.h" |
30 | 34 |
#include "bufferqueue.h" |
31 | 35 |
#include "formats.h" |
... | ... |
@@ -59,7 +63,7 @@ inline static int push_frame(AVFilterContext *ctx) |
59 | 59 |
for (i = 0; i < ctx->nb_inputs; i++) { |
60 | 60 |
struct FFBufQueue *q = &s->queues[i]; |
61 | 61 |
|
62 |
- if (!q->available && !ctx->inputs[i]->status) |
|
62 |
+ if (!q->available && !ctx->inputs[i]->status_out) |
|
63 | 63 |
return 0; |
64 | 64 |
if (q->available) { |
65 | 65 |
frame = ff_bufqueue_peek(q, 0); |
... | ... |
@@ -190,7 +194,7 @@ static int request_frame(AVFilterLink *outlink) |
190 | 190 |
int i, ret; |
191 | 191 |
|
192 | 192 |
for (i = 0; i < ctx->nb_inputs; i++) { |
193 |
- if (!s->queues[i].available && !ctx->inputs[i]->status) { |
|
193 |
+ if (!s->queues[i].available && !ctx->inputs[i]->status_out) { |
|
194 | 194 |
ret = ff_request_frame(ctx->inputs[i]); |
195 | 195 |
if (ret != AVERROR_EOF) |
196 | 196 |
return ret; |
... | ... |
@@ -29,6 +29,7 @@ |
29 | 29 |
#include "avfiltergraph.h" |
30 | 30 |
#include "formats.h" |
31 | 31 |
#include "framepool.h" |
32 |
+#include "framequeue.h" |
|
32 | 33 |
#include "thread.h" |
33 | 34 |
#include "version.h" |
34 | 35 |
#include "video.h" |
... | ... |
@@ -147,6 +148,7 @@ struct AVFilterPad { |
147 | 147 |
struct AVFilterGraphInternal { |
148 | 148 |
void *thread; |
149 | 149 |
avfilter_execute_func *thread_execute; |
150 |
+ FFFrameQueueGlobal frame_queues; |
|
150 | 151 |
}; |
151 | 152 |
|
152 | 153 |
struct AVFilterInternal { |
... | ... |
@@ -336,6 +338,8 @@ int ff_request_frame(AVFilterLink *link); |
336 | 336 |
|
337 | 337 |
int ff_request_frame_to_filter(AVFilterLink *link); |
338 | 338 |
|
339 |
+int ff_filter_frame_to_filter(AVFilterLink *link); |
|
340 |
+ |
|
339 | 341 |
#define AVFILTER_DEFINE_CLASS(fname) \ |
340 | 342 |
static const AVClass fname##_class = { \ |
341 | 343 |
.class_name = #fname, \ |
... | ... |
@@ -376,6 +380,8 @@ int ff_filter_frame(AVFilterLink *link, AVFrame *frame); |
376 | 376 |
*/ |
377 | 377 |
AVFilterContext *ff_filter_alloc(const AVFilter *filter, const char *inst_name); |
378 | 378 |
|
379 |
+int ff_filter_activate(AVFilterContext *filter); |
|
380 |
+ |
|
379 | 381 |
/** |
380 | 382 |
* Remove a filter from a graph; |
381 | 383 |
*/ |
... | ... |
@@ -30,6 +30,9 @@ |
30 | 30 |
#include "libavutil/mem.h" |
31 | 31 |
#include "libavutil/opt.h" |
32 | 32 |
|
33 |
+#define FF_INTERNAL_FIELDS 1 |
|
34 |
+#include "framequeue.h" |
|
35 |
+ |
|
33 | 36 |
#include "avfilter.h" |
34 | 37 |
#include "audio.h" |
35 | 38 |
#include "formats.h" |
... | ... |
@@ -78,7 +81,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
78 | 78 |
for (i = 0; i < ctx->nb_outputs; i++) { |
79 | 79 |
AVFrame *buf_out; |
80 | 80 |
|
81 |
- if (ctx->outputs[i]->status) |
|
81 |
+ if (ctx->outputs[i]->status_in) |
|
82 | 82 |
continue; |
83 | 83 |
buf_out = av_frame_clone(frame); |
84 | 84 |
if (!buf_out) { |
... | ... |
@@ -22,6 +22,10 @@ |
22 | 22 |
#include "libavutil/imgutils.h" |
23 | 23 |
#include "libavutil/opt.h" |
24 | 24 |
#include "libavutil/pixdesc.h" |
25 |
+ |
|
26 |
+#define FF_INTERNAL_FIELDS 1 |
|
27 |
+#include "libavfilter/framequeue.h" |
|
28 |
+ |
|
25 | 29 |
#include "avfilter.h" |
26 | 30 |
#include "drawutils.h" |
27 | 31 |
#include "internal.h" |
... | ... |
@@ -283,7 +287,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame) |
283 | 283 |
const int idx = s->map[i]; |
284 | 284 |
AVFrame *out; |
285 | 285 |
|
286 |
- if (outlink->status) |
|
286 |
+ if (outlink->status_in) |
|
287 | 287 |
continue; |
288 | 288 |
|
289 | 289 |
out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |