1 #include "audio/aframe.h"
2 #include "audio/out/ao.h"
3 #include "common/global.h"
4 #include "options/m_config.h"
5 #include "options/m_option.h"
6 #include "video/out/vo.h"
7 
8 #include "filter_internal.h"
9 
10 #include "f_autoconvert.h"
11 #include "f_auto_filters.h"
12 #include "f_lavfi.h"
13 #include "f_output_chain.h"
14 #include "f_utils.h"
15 #include "user_filters.h"
16 
17 struct chain {
18     struct mp_filter *f;
19     struct mp_log *log;
20 
21     enum mp_output_chain_type type;
22 
23     // Expected media type.
24     enum mp_frame_type frame_type;
25 
26     struct mp_stream_info stream_info;
27 
28     struct mp_user_filter **pre_filters;
29     int num_pre_filters;
30     struct mp_user_filter **post_filters;
31     int num_post_filters;
32 
33     struct mp_user_filter **user_filters;
34     int num_user_filters;
35 
36     // Concatenated list of pre+user+post filters.
37     struct mp_user_filter **all_filters;
38     int num_all_filters;
39     // First input/last output of all_filters[].
40     struct mp_pin *filters_in, *filters_out;
41 
42     struct mp_user_filter *input, *output, *convert_wrapper;
43     struct mp_autoconvert *convert;
44 
45     struct vo *vo;
46     struct ao *ao;
47 
48     struct mp_output_chain public;
49 };
50 
51 // This wraps each individual "actual" filter for:
52 //  - isolating against its failure (logging it and disabling the filter)
53 //  - tracking its output format (mostly for logging)
54 //  - store extra per-filter information like the filter label
55 struct mp_user_filter {
56     struct chain *p;
57 
58     struct mp_filter *wrapper; // parent filter for f
59     struct mp_filter *f; // the actual user filter
60     struct m_obj_settings *args; // NULL, or list of 1 item with creation args
61     char *label;
62     bool generated_label;
63     char *name;
64 
65     struct mp_image_params last_in_vformat;
66     struct mp_aframe *last_in_aformat;
67 
68     bool last_is_active;
69 
70     int64_t last_in_pts, last_out_pts;
71 
72     bool failed;
73     bool error_eof_sent;
74 };
75 
update_output_caps(struct chain * p)76 static void update_output_caps(struct chain *p)
77 {
78     if (p->type != MP_OUTPUT_CHAIN_VIDEO)
79         return;
80 
81     mp_autoconvert_clear(p->convert);
82 
83     if (p->vo) {
84         uint8_t allowed_output_formats[IMGFMT_END - IMGFMT_START] = {0};
85         vo_query_formats(p->vo, allowed_output_formats);
86 
87         for (int n = 0; n < MP_ARRAY_SIZE(allowed_output_formats); n++) {
88             if (allowed_output_formats[n])
89                 mp_autoconvert_add_imgfmt(p->convert, IMGFMT_START + n, 0);
90         }
91     }
92 }
93 
check_in_format_change(struct mp_user_filter * u,struct mp_frame frame)94 static void check_in_format_change(struct mp_user_filter *u,
95                                    struct mp_frame frame)
96 {
97     struct chain *p = u->p;
98 
99     if (frame.type == MP_FRAME_VIDEO) {
100         struct mp_image *img = frame.data;
101 
102         if (!mp_image_params_equal(&img->params, &u->last_in_vformat)) {
103             MP_VERBOSE(p, "[%s] %s\n", u->name,
104                        mp_image_params_to_str(&img->params));
105             u->last_in_vformat = img->params;
106 
107             if (u == p->input) {
108                 p->public.input_params = img->params;
109 
110                 // Unfortunately there's no good place to update these.
111                 // But a common case is enabling HW decoding, which
112                 // might init some support of them in the VO, and update
113                 // the VO's format list.
114                 update_output_caps(p);
115             } else if (u == p->output) {
116                 p->public.output_params = img->params;
117             }
118 
119             p->public.reconfig_happened = true;
120         }
121     }
122 
123     if (frame.type == MP_FRAME_AUDIO) {
124         struct mp_aframe *aframe = frame.data;
125 
126         if (!mp_aframe_config_equals(aframe, u->last_in_aformat)) {
127             MP_VERBOSE(p, "[%s] %s\n", u->name,
128                        mp_aframe_format_str(aframe));
129             mp_aframe_config_copy(u->last_in_aformat, aframe);
130 
131             if (u == p->input) {
132                 mp_aframe_config_copy(p->public.input_aformat, aframe);
133             } else if (u == p->output) {
134                 mp_aframe_config_copy(p->public.output_aformat, aframe);
135             }
136 
137             p->public.reconfig_happened = true;
138         }
139     }
140 }
141 
process_user(struct mp_filter * f)142 static void process_user(struct mp_filter *f)
143 {
144     struct mp_user_filter *u = f->priv;
145     struct chain *p = u->p;
146 
147     mp_filter_set_error_handler(u->f, f);
148     const char *name = u->label ? u->label : u->name;
149     assert(u->name);
150 
151     if (!u->failed && mp_filter_has_failed(u->f)) {
152         if (u == p->convert_wrapper) {
153             // This is a fuckup we can't ignore.
154             MP_FATAL(p, "Cannot convert decoder/filter output to any format "
155                      "supported by the output.\n");
156             p->public.failed_output_conversion = true;
157             mp_filter_wakeup(p->f);
158         } else {
159             MP_ERR(p, "Disabling filter %s because it has failed.\n", name);
160             mp_filter_reset(u->f); // clear out staled buffered data
161         }
162         u->failed = true;
163     }
164 
165     if (u->failed) {
166         if (u == p->convert_wrapper) {
167             if (mp_pin_in_needs_data(f->ppins[1])) {
168                 if (!u->error_eof_sent)
169                     mp_pin_in_write(f->ppins[1], MP_EOF_FRAME);
170                 u->error_eof_sent = true;
171             }
172             return;
173         }
174 
175         mp_pin_transfer_data(f->ppins[1], f->ppins[0]);
176         return;
177     }
178 
179     if (mp_pin_can_transfer_data(u->f->pins[0], f->ppins[0])) {
180         struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
181 
182         check_in_format_change(u, frame);
183 
184         double pts = mp_frame_get_pts(frame);
185         if (pts != MP_NOPTS_VALUE)
186             u->last_in_pts = pts;
187 
188         mp_pin_in_write(u->f->pins[0], frame);
189     }
190 
191     if (mp_pin_can_transfer_data(f->ppins[1], u->f->pins[1])) {
192         struct mp_frame frame = mp_pin_out_read(u->f->pins[1]);
193 
194         double pts = mp_frame_get_pts(frame);
195         if (pts != MP_NOPTS_VALUE)
196             u->last_out_pts = pts;
197 
198         mp_pin_in_write(f->ppins[1], frame);
199 
200         struct mp_filter_command cmd = {.type = MP_FILTER_COMMAND_IS_ACTIVE};
201         if (mp_filter_command(u->f, &cmd) && u->last_is_active != cmd.is_active) {
202             u->last_is_active = cmd.is_active;
203             MP_VERBOSE(p, "[%s] (%sabled)\n", u->name,
204                        u->last_is_active ? "en" : "dis");
205         }
206     }
207 }
208 
reset_user(struct mp_filter * f)209 static void reset_user(struct mp_filter *f)
210 {
211     struct mp_user_filter *u = f->priv;
212 
213     u->error_eof_sent = false;
214     u->last_in_pts = u->last_out_pts = MP_NOPTS_VALUE;
215 }
216 
destroy_user(struct mp_filter * f)217 static void destroy_user(struct mp_filter *f)
218 {
219     struct mp_user_filter *u = f->priv;
220 
221     struct m_option dummy = {.type = &m_option_type_obj_settings_list};
222     m_option_free(&dummy, &u->args);
223 
224     mp_filter_free_children(f);
225 }
226 
227 static const struct mp_filter_info user_wrapper_filter = {
228     .name = "user_filter_wrapper",
229     .priv_size = sizeof(struct mp_user_filter),
230     .process = process_user,
231     .reset = reset_user,
232     .destroy = destroy_user,
233 };
234 
create_wrapper_filter(struct chain * p)235 static struct mp_user_filter *create_wrapper_filter(struct chain *p)
236 {
237     struct mp_filter *f = mp_filter_create(p->f, &user_wrapper_filter);
238     if (!f)
239         abort();
240     struct mp_user_filter *wrapper = f->priv;
241     wrapper->wrapper = f;
242     wrapper->p = p;
243     wrapper->last_in_aformat = talloc_steal(wrapper, mp_aframe_create());
244     wrapper->last_is_active = true;
245     mp_filter_add_pin(f, MP_PIN_IN, "in");
246     mp_filter_add_pin(f, MP_PIN_OUT, "out");
247     return wrapper;
248 }
249 
250 // Rebuild p->all_filters and relink the filters. Non-destructive if no change.
relink_filter_list(struct chain * p)251 static void relink_filter_list(struct chain *p)
252 {
253     struct mp_user_filter **all_filters[3] =
254         {p->pre_filters, p->user_filters, p->post_filters};
255     int all_filters_num[3] =
256         {p->num_pre_filters, p->num_user_filters, p->num_post_filters};
257     p->num_all_filters = 0;
258     for (int n = 0; n < 3; n++) {
259         struct mp_user_filter **filters = all_filters[n];
260         int filters_num = all_filters_num[n];
261         for (int i = 0; i < filters_num; i++)
262             MP_TARRAY_APPEND(p, p->all_filters, p->num_all_filters, filters[i]);
263     }
264 
265     assert(p->num_all_filters > 0);
266 
267     p->filters_in = NULL;
268     p->filters_out = NULL;
269     for (int n = 0; n < p->num_all_filters; n++) {
270         struct mp_filter *f = p->all_filters[n]->wrapper;
271         if (n == 0)
272             p->filters_in = f->pins[0];
273         if (p->filters_out)
274             mp_pin_connect(f->pins[0], p->filters_out);
275         p->filters_out = f->pins[1];
276     }
277 }
278 
process(struct mp_filter * f)279 static void process(struct mp_filter *f)
280 {
281     struct chain *p = f->priv;
282 
283     if (mp_pin_can_transfer_data(p->filters_in, f->ppins[0])) {
284         struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
285 
286         if (frame.type == MP_FRAME_EOF)
287             MP_VERBOSE(p, "filter input EOF\n");
288 
289         if (frame.type == MP_FRAME_VIDEO && p->public.update_subtitles) {
290             p->public.update_subtitles(p->public.update_subtitles_ctx,
291                                        mp_frame_get_pts(frame));
292         }
293 
294         mp_pin_in_write(p->filters_in, frame);
295     }
296 
297     if (mp_pin_can_transfer_data(f->ppins[1], p->filters_out)) {
298         struct mp_frame frame = mp_pin_out_read(p->filters_out);
299 
300         p->public.got_output_eof = frame.type == MP_FRAME_EOF;
301         if (p->public.got_output_eof)
302             MP_VERBOSE(p, "filter output EOF\n");
303 
304         mp_pin_in_write(f->ppins[1], frame);
305     }
306 }
307 
reset(struct mp_filter * f)308 static void reset(struct mp_filter *f)
309 {
310     struct chain *p = f->priv;
311 
312     p->public.ao_needs_update = false;
313 
314     p->public.got_output_eof = false;
315 }
316 
mp_output_chain_reset_harder(struct mp_output_chain * c)317 void mp_output_chain_reset_harder(struct mp_output_chain *c)
318 {
319     struct chain *p = c->f->priv;
320 
321     mp_filter_reset(p->f);
322 
323     p->public.failed_output_conversion = false;
324     for (int n = 0; n < p->num_all_filters; n++) {
325         struct mp_user_filter *u = p->all_filters[n];
326 
327         u->failed = false;
328         u->last_in_vformat = (struct mp_image_params){0};
329         mp_aframe_reset(u->last_in_aformat);
330     }
331 
332     if (p->type == MP_OUTPUT_CHAIN_AUDIO) {
333         p->ao = NULL;
334         mp_autoconvert_clear(p->convert);
335     }
336 }
337 
destroy(struct mp_filter * f)338 static void destroy(struct mp_filter *f)
339 {
340     reset(f);
341 }
342 
343 static const struct mp_filter_info output_chain_filter = {
344     .name = "output_chain",
345     .priv_size = sizeof(struct chain),
346     .process = process,
347     .reset = reset,
348     .destroy = destroy,
349 };
350 
get_display_fps(struct mp_stream_info * i)351 static double get_display_fps(struct mp_stream_info *i)
352 {
353     struct chain *p = i->priv;
354     double res = 0;
355     if (p->vo)
356         vo_control(p->vo, VOCTRL_GET_DISPLAY_FPS, &res);
357     return res;
358 }
359 
mp_output_chain_set_vo(struct mp_output_chain * c,struct vo * vo)360 void mp_output_chain_set_vo(struct mp_output_chain *c, struct vo *vo)
361 {
362     struct chain *p = c->f->priv;
363 
364     p->stream_info.hwdec_devs = vo ? vo->hwdec_devs : NULL;
365     p->stream_info.osd = vo ? vo->osd : NULL;
366     p->stream_info.rotate90 = vo ? vo->driver->caps & VO_CAP_ROTATE90 : false;
367     p->stream_info.dr_vo = vo;
368     p->vo = vo;
369     update_output_caps(p);
370 }
371 
mp_output_chain_set_ao(struct mp_output_chain * c,struct ao * ao)372 void mp_output_chain_set_ao(struct mp_output_chain *c, struct ao *ao)
373 {
374     struct chain *p = c->f->priv;
375 
376     assert(p->public.ao_needs_update); // can't just call it any time
377     assert(!p->ao);
378 
379     p->public.ao_needs_update = false;
380 
381     p->ao = ao;
382 
383     int out_format = 0;
384     int out_rate = 0;
385     struct mp_chmap out_channels = {0};
386     ao_get_format(p->ao, &out_rate, &out_format, &out_channels);
387 
388     mp_autoconvert_clear(p->convert);
389     mp_autoconvert_add_afmt(p->convert, out_format);
390     mp_autoconvert_add_srate(p->convert, out_rate);
391     mp_autoconvert_add_chmap(p->convert, &out_channels);
392 
393     mp_autoconvert_format_change_continue(p->convert);
394 
395     // Just to get the format change logged again.
396     mp_aframe_reset(p->public.output_aformat);
397 }
398 
on_audio_format_change(void * opaque)399 static void on_audio_format_change(void *opaque)
400 {
401     struct chain *p = opaque;
402 
403     // Let the f_output_chain user know what format to use. (Not quite proper,
404     // since we overwrite what some other code normally automatically sets.
405     // The main issue is that this callback is called before output_aformat can
406     // be set, because we "block" the converter until the AO is reconfigured,
407     // and mp_autoconvert_format_change_continue() is called.)
408     mp_aframe_config_copy(p->public.output_aformat,
409                           p->convert_wrapper->last_in_aformat);
410 
411     // Ask for calling mp_output_chain_set_ao().
412     p->public.ao_needs_update = true;
413     p->ao = NULL;
414 
415     // Do something silly to notify the f_output_chain user. (Not quite proper,
416     // it's merely that this will cause the core to run again, and check the
417     // flag set above.)
418     mp_filter_wakeup(p->f);
419 }
420 
find_by_label(struct chain * p,const char * label)421 static struct mp_user_filter *find_by_label(struct chain *p, const char *label)
422 {
423     for (int n = 0; n < p->num_user_filters; n++) {
424         struct mp_user_filter *u = p->user_filters[n];
425         if (label && u->label && strcmp(label, u->label) == 0)
426             return u;
427     }
428     return NULL;
429 }
430 
mp_output_chain_command(struct mp_output_chain * c,const char * target,struct mp_filter_command * cmd)431 bool mp_output_chain_command(struct mp_output_chain *c, const char *target,
432                              struct mp_filter_command *cmd)
433 {
434     struct chain *p = c->f->priv;
435 
436     if (!target || !target[0])
437         return false;
438 
439     if (strcmp(target, "all") == 0 && cmd->type == MP_FILTER_COMMAND_TEXT) {
440         // (Following old semantics.)
441         for (int n = 0; n < p->num_user_filters; n++)
442             mp_filter_command(p->user_filters[n]->f, cmd);
443         return true;
444     }
445 
446     struct mp_user_filter *f = find_by_label(p, target);
447     if (!f)
448         return false;
449 
450     return mp_filter_command(f->f, cmd);
451 }
452 
453 // Set the speed on the last filter in the chain that supports it. If a filter
454 // supports it, reset *speed, then keep setting the speed on the other filters.
455 // The purpose of this is to make sure only 1 filter changes speed.
set_speed_any(struct mp_user_filter ** filters,int num_filters,int command,double * speed)456 static void set_speed_any(struct mp_user_filter **filters, int num_filters,
457                           int command, double *speed)
458 {
459     for (int n = num_filters - 1; n >= 0; n--) {
460         assert(*speed);
461         struct mp_filter_command cmd = {
462             .type = command,
463             .speed = *speed,
464         };
465         if (mp_filter_command(filters[n]->f, &cmd))
466             *speed = 1.0;
467     }
468 }
469 
mp_output_chain_set_audio_speed(struct mp_output_chain * c,double speed,double resample,double drop)470 void mp_output_chain_set_audio_speed(struct mp_output_chain *c,
471                                      double speed, double resample, double drop)
472 {
473     struct chain *p = c->f->priv;
474 
475     // We always resample with the final libavresample instance.
476     set_speed_any(p->post_filters, p->num_post_filters,
477                   MP_FILTER_COMMAND_SET_SPEED_RESAMPLE, &resample);
478 
479     // If users have filters like "scaletempo" insert anywhere, use that,
480     // otherwise use the builtin ones.
481     set_speed_any(p->user_filters, p->num_user_filters,
482                   MP_FILTER_COMMAND_SET_SPEED, &speed);
483     set_speed_any(p->post_filters, p->num_post_filters,
484                   MP_FILTER_COMMAND_SET_SPEED, &speed);
485     set_speed_any(p->user_filters, p->num_user_filters,
486                   MP_FILTER_COMMAND_SET_SPEED_DROP, &drop);
487     set_speed_any(p->post_filters, p->num_post_filters,
488                   MP_FILTER_COMMAND_SET_SPEED_DROP, &drop);
489 }
490 
mp_output_get_measured_total_delay(struct mp_output_chain * c)491 double mp_output_get_measured_total_delay(struct mp_output_chain *c)
492 {
493     struct chain *p = c->f->priv;
494 
495     double delay = 0;
496 
497     for (int n = 0; n < p->num_all_filters; n++) {
498         struct mp_user_filter *u = p->all_filters[n];
499 
500         if (u->last_in_pts != MP_NOPTS_VALUE &&
501             u->last_out_pts != MP_NOPTS_VALUE)
502         {
503             delay += u->last_in_pts - u->last_out_pts;
504         }
505     }
506 
507     return delay;
508 }
509 
mp_output_chain_update_filters(struct mp_output_chain * c,struct m_obj_settings * list)510 bool mp_output_chain_update_filters(struct mp_output_chain *c,
511                                     struct m_obj_settings *list)
512 {
513     struct chain *p = c->f->priv;
514 
515     struct mp_user_filter **add = NULL;      // new filters
516     int num_add = 0;
517     struct mp_user_filter **res = NULL;      // new final list
518     int num_res = 0;
519     bool *used = talloc_zero_array(NULL, bool, p->num_user_filters);
520 
521     for (int n = 0; list && list[n].name; n++) {
522         struct m_obj_settings *entry = &list[n];
523 
524         if (!entry->enabled)
525             continue;
526 
527         struct mp_user_filter *u = NULL;
528 
529         for (int i = 0; i < p->num_user_filters; i++) {
530             if (!used[i] && m_obj_settings_equal(entry, p->user_filters[i]->args))
531             {
532                 u = p->user_filters[i];
533                 used[i] = true;
534                 break;
535             }
536         }
537 
538         if (!u) {
539             u = create_wrapper_filter(p);
540             u->name = talloc_strdup(u, entry->name);
541             u->label = talloc_strdup(u, entry->label);
542             u->f = mp_create_user_filter(u->wrapper, p->type, entry->name,
543                                          entry->attribs);
544             if (!u->f) {
545                 talloc_free(u->wrapper);
546                 goto error;
547             }
548 
549             struct m_obj_settings *args = (struct m_obj_settings[2]){*entry, {0}};
550 
551             struct m_option dummy = {.type = &m_option_type_obj_settings_list};
552             m_option_copy(&dummy, &u->args, &args);
553 
554             MP_TARRAY_APPEND(NULL, add, num_add, u);
555         }
556 
557         MP_TARRAY_APPEND(p, res, num_res, u);
558     }
559 
560     // At this point we definitely know we'll use the new list, so clean up.
561 
562     for (int n = 0; n < p->num_user_filters; n++) {
563         if (!used[n])
564             talloc_free(p->user_filters[n]->wrapper);
565     }
566 
567     talloc_free(p->user_filters);
568     p->user_filters = res;
569     p->num_user_filters = num_res;
570 
571     relink_filter_list(p);
572 
573     for (int n = 0; n < p->num_user_filters; n++) {
574         struct mp_user_filter *u = p->user_filters[n];
575         if (u->generated_label)
576             TA_FREEP(&u->label);
577         if (!u->label) {
578             for (int i = 0; i < 100; i++) {
579                 char *label = mp_tprintf(80, "%s.%02d", u->name, i);
580                 if (!find_by_label(p, label)) {
581                     u->label = talloc_strdup(u, label);
582                     u->generated_label = true;
583                     break;
584                 }
585             }
586         }
587     }
588 
589     MP_VERBOSE(p, "User filter list:\n");
590     for (int n = 0; n < p->num_user_filters; n++) {
591         struct mp_user_filter *u = p->user_filters[n];
592         MP_VERBOSE(p, "  %s (%s)\n", u->name, u->label ? u->label : "-");
593     }
594     if (!p->num_user_filters)
595         MP_VERBOSE(p, "  (empty)\n");
596 
597     // Filters can load hwdec interops, which might add new formats.
598     update_output_caps(p);
599 
600     mp_filter_wakeup(p->f);
601 
602     talloc_free(add);
603     talloc_free(used);
604     return true;
605 
606 error:
607     for (int n = 0; n < num_add; n++)
608         talloc_free(add[n]);
609     talloc_free(add);
610     talloc_free(used);
611     return false;
612 }
613 
create_video_things(struct chain * p)614 static void create_video_things(struct chain *p)
615 {
616     p->frame_type = MP_FRAME_VIDEO;
617 
618     p->stream_info.priv = p;
619     p->stream_info.get_display_fps = get_display_fps;
620 
621     p->f->stream_info = &p->stream_info;
622 
623     struct mp_user_filter *f = create_wrapper_filter(p);
624     f->name = "userdeint";
625     f->f = mp_deint_create(f->wrapper);
626     if (!f->f)
627         abort();
628     MP_TARRAY_APPEND(p, p->pre_filters, p->num_pre_filters, f);
629 
630     f = create_wrapper_filter(p);
631     f->name = "autorotate";
632     f->f = mp_autorotate_create(f->wrapper);
633     if (!f->f)
634         abort();
635     MP_TARRAY_APPEND(p, p->post_filters, p->num_post_filters, f);
636 }
637 
create_audio_things(struct chain * p)638 static void create_audio_things(struct chain *p)
639 {
640     p->frame_type = MP_FRAME_AUDIO;
641 
642     struct mp_user_filter *f = create_wrapper_filter(p);
643     f->name = "userspeed";
644     f->f = mp_autoaspeed_create(f->wrapper);
645     if (!f->f)
646         abort();
647     MP_TARRAY_APPEND(p, p->post_filters, p->num_post_filters, f);
648 }
649 
mp_output_chain_create(struct mp_filter * parent,enum mp_output_chain_type type)650 struct mp_output_chain *mp_output_chain_create(struct mp_filter *parent,
651                                                enum mp_output_chain_type type)
652 {
653     struct mp_filter *f = mp_filter_create(parent, &output_chain_filter);
654     if (!f)
655         return NULL;
656 
657     mp_filter_add_pin(f, MP_PIN_IN, "in");
658     mp_filter_add_pin(f, MP_PIN_OUT, "out");
659 
660     const char *log_name = NULL;
661     switch (type) {
662     case MP_OUTPUT_CHAIN_VIDEO: log_name = "!vf"; break;
663     case MP_OUTPUT_CHAIN_AUDIO: log_name = "!af"; break;
664     }
665     if (log_name)
666         f->log = mp_log_new(f, parent->global->log, log_name);
667 
668     struct chain *p = f->priv;
669     p->f = f;
670     p->log = f->log;
671     p->type = type;
672 
673     struct mp_output_chain *c = &p->public;
674     c->f = f;
675     c->input_aformat = talloc_steal(p, mp_aframe_create());
676     c->output_aformat = talloc_steal(p, mp_aframe_create());
677 
678     // Dummy filter for reporting and logging the input format.
679     p->input = create_wrapper_filter(p);
680     p->input->f = mp_bidir_nop_filter_create(p->input->wrapper);
681     if (!p->input->f)
682         abort();
683     p->input->name = "in";
684     MP_TARRAY_APPEND(p, p->pre_filters, p->num_pre_filters, p->input);
685 
686     switch (type) {
687     case MP_OUTPUT_CHAIN_VIDEO: create_video_things(p); break;
688     case MP_OUTPUT_CHAIN_AUDIO: create_audio_things(p); break;
689     }
690 
691     p->convert_wrapper = create_wrapper_filter(p);
692     p->convert = mp_autoconvert_create(p->convert_wrapper->wrapper);
693     if (!p->convert)
694         abort();
695     p->convert_wrapper->name = "convert";
696     p->convert_wrapper->f = p->convert->f;
697     MP_TARRAY_APPEND(p, p->post_filters, p->num_post_filters, p->convert_wrapper);
698 
699     if (type == MP_OUTPUT_CHAIN_AUDIO) {
700         p->convert->on_audio_format_change = on_audio_format_change;
701         p->convert->on_audio_format_change_opaque = p;
702     }
703 
704     // Dummy filter for reporting and logging the output format.
705     p->output = create_wrapper_filter(p);
706     p->output->f = mp_bidir_nop_filter_create(p->output->wrapper);
707     if (!p->output->f)
708         abort();
709     p->output->name = "out";
710     MP_TARRAY_APPEND(p, p->post_filters, p->num_post_filters, p->output);
711 
712     relink_filter_list(p);
713 
714     reset(f);
715 
716     return c;
717 }
718