]> git.sesse.net Git - ffmpeg/blob - ffmpeg_filter.c
Merge commit '722ec3eb35bc152ce91d0a4502eca0df1c0086d0'
[ffmpeg] / ffmpeg_filter.c
1 /*
2  * ffmpeg filter configuration
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include <stdint.h>
22
23 #include "ffmpeg.h"
24
25 #include "libavfilter/avfilter.h"
26 #include "libavfilter/buffersink.h"
27 #include "libavfilter/buffersrc.h"
28
29 #include "libavresample/avresample.h"
30
31 #include "libavutil/avassert.h"
32 #include "libavutil/avstring.h"
33 #include "libavutil/bprint.h"
34 #include "libavutil/channel_layout.h"
35 #include "libavutil/display.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/pixdesc.h"
38 #include "libavutil/pixfmt.h"
39 #include "libavutil/imgutils.h"
40 #include "libavutil/samplefmt.h"
41
42 static const enum AVPixelFormat *get_compliance_unofficial_pix_fmts(enum AVCodecID codec_id, const enum AVPixelFormat default_formats[])
43 {
44     static const enum AVPixelFormat mjpeg_formats[] =
45         { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P,
46           AV_PIX_FMT_YUV420P,  AV_PIX_FMT_YUV422P,  AV_PIX_FMT_YUV444P,
47           AV_PIX_FMT_NONE };
48     static const enum AVPixelFormat ljpeg_formats[] =
49         { AV_PIX_FMT_BGR24   , AV_PIX_FMT_BGRA    , AV_PIX_FMT_BGR0,
50           AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUVJ422P,
51           AV_PIX_FMT_YUV420P , AV_PIX_FMT_YUV444P , AV_PIX_FMT_YUV422P,
52           AV_PIX_FMT_NONE};
53
54     if (codec_id == AV_CODEC_ID_MJPEG) {
55         return mjpeg_formats;
56     } else if (codec_id == AV_CODEC_ID_LJPEG) {
57         return ljpeg_formats;
58     } else {
59         return default_formats;
60     }
61 }
62
63 enum AVPixelFormat choose_pixel_fmt(AVStream *st, AVCodecContext *enc_ctx, AVCodec *codec, enum AVPixelFormat target)
64 {
65     if (codec && codec->pix_fmts) {
66         const enum AVPixelFormat *p = codec->pix_fmts;
67         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(target);
68         int has_alpha = desc ? desc->nb_components % 2 == 0 : 0;
69         enum AVPixelFormat best= AV_PIX_FMT_NONE;
70
71         if (enc_ctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
72             p = get_compliance_unofficial_pix_fmts(enc_ctx->codec_id, p);
73         }
74         for (; *p != AV_PIX_FMT_NONE; p++) {
75             best= avcodec_find_best_pix_fmt_of_2(best, *p, target, has_alpha, NULL);
76             if (*p == target)
77                 break;
78         }
79         if (*p == AV_PIX_FMT_NONE) {
80             if (target != AV_PIX_FMT_NONE)
81                 av_log(NULL, AV_LOG_WARNING,
82                        "Incompatible pixel format '%s' for codec '%s', auto-selecting format '%s'\n",
83                        av_get_pix_fmt_name(target),
84                        codec->name,
85                        av_get_pix_fmt_name(best));
86             return best;
87         }
88     }
89     return target;
90 }
91
92 void choose_sample_fmt(AVStream *st, AVCodec *codec)
93 {
94     if (codec && codec->sample_fmts) {
95         const enum AVSampleFormat *p = codec->sample_fmts;
96         for (; *p != -1; p++) {
97             if (*p == st->codecpar->format)
98                 break;
99         }
100         if (*p == -1) {
101             if((codec->capabilities & AV_CODEC_CAP_LOSSLESS) && av_get_sample_fmt_name(st->codecpar->format) > av_get_sample_fmt_name(codec->sample_fmts[0]))
102                 av_log(NULL, AV_LOG_ERROR, "Conversion will not be lossless.\n");
103             if(av_get_sample_fmt_name(st->codecpar->format))
104             av_log(NULL, AV_LOG_WARNING,
105                    "Incompatible sample format '%s' for codec '%s', auto-selecting format '%s'\n",
106                    av_get_sample_fmt_name(st->codecpar->format),
107                    codec->name,
108                    av_get_sample_fmt_name(codec->sample_fmts[0]));
109             st->codecpar->format = codec->sample_fmts[0];
110         }
111     }
112 }
113
114 static char *choose_pix_fmts(OutputStream *ost)
115 {
116     AVDictionaryEntry *strict_dict = av_dict_get(ost->encoder_opts, "strict", NULL, 0);
117     if (strict_dict)
118         // used by choose_pixel_fmt() and below
119         av_opt_set(ost->enc_ctx, "strict", strict_dict->value, 0);
120
121      if (ost->keep_pix_fmt) {
122         if (ost->filter)
123             avfilter_graph_set_auto_convert(ost->filter->graph->graph,
124                                             AVFILTER_AUTO_CONVERT_NONE);
125         if (ost->enc_ctx->pix_fmt == AV_PIX_FMT_NONE)
126             return NULL;
127         return av_strdup(av_get_pix_fmt_name(ost->enc_ctx->pix_fmt));
128     }
129     if (ost->enc_ctx->pix_fmt != AV_PIX_FMT_NONE) {
130         return av_strdup(av_get_pix_fmt_name(choose_pixel_fmt(ost->st, ost->enc_ctx, ost->enc, ost->enc_ctx->pix_fmt)));
131     } else if (ost->enc && ost->enc->pix_fmts) {
132         const enum AVPixelFormat *p;
133         AVIOContext *s = NULL;
134         uint8_t *ret;
135         int len;
136
137         if (avio_open_dyn_buf(&s) < 0)
138             exit_program(1);
139
140         p = ost->enc->pix_fmts;
141         if (ost->enc_ctx->strict_std_compliance <= FF_COMPLIANCE_UNOFFICIAL) {
142             p = get_compliance_unofficial_pix_fmts(ost->enc_ctx->codec_id, p);
143         }
144
145         for (; *p != AV_PIX_FMT_NONE; p++) {
146             const char *name = av_get_pix_fmt_name(*p);
147             avio_printf(s, "%s|", name);
148         }
149         len = avio_close_dyn_buf(s, &ret);
150         ret[len - 1] = 0;
151         return ret;
152     } else
153         return NULL;
154 }
155
156 /* Define a function for building a string containing a list of
157  * allowed formats. */
158 #define DEF_CHOOSE_FORMAT(type, var, supported_list, none, get_name)           \
159 static char *choose_ ## var ## s(OutputStream *ost)                            \
160 {                                                                              \
161     if (ost->enc_ctx->var != none) {                                           \
162         get_name(ost->enc_ctx->var);                                           \
163         return av_strdup(name);                                                \
164     } else if (ost->enc && ost->enc->supported_list) {                         \
165         const type *p;                                                         \
166         AVIOContext *s = NULL;                                                 \
167         uint8_t *ret;                                                          \
168         int len;                                                               \
169                                                                                \
170         if (avio_open_dyn_buf(&s) < 0)                                         \
171             exit_program(1);                                                           \
172                                                                                \
173         for (p = ost->enc->supported_list; *p != none; p++) {                  \
174             get_name(*p);                                                      \
175             avio_printf(s, "%s|", name);                                       \
176         }                                                                      \
177         len = avio_close_dyn_buf(s, &ret);                                     \
178         ret[len - 1] = 0;                                                      \
179         return ret;                                                            \
180     } else                                                                     \
181         return NULL;                                                           \
182 }
183
184 // DEF_CHOOSE_FORMAT(enum AVPixelFormat, pix_fmt, pix_fmts, AV_PIX_FMT_NONE,
185 //                   GET_PIX_FMT_NAME)
186
187 DEF_CHOOSE_FORMAT(enum AVSampleFormat, sample_fmt, sample_fmts,
188                   AV_SAMPLE_FMT_NONE, GET_SAMPLE_FMT_NAME)
189
190 DEF_CHOOSE_FORMAT(int, sample_rate, supported_samplerates, 0,
191                   GET_SAMPLE_RATE_NAME)
192
193 DEF_CHOOSE_FORMAT(uint64_t, channel_layout, channel_layouts, 0,
194                   GET_CH_LAYOUT_NAME)
195
196 int init_simple_filtergraph(InputStream *ist, OutputStream *ost)
197 {
198     FilterGraph *fg = av_mallocz(sizeof(*fg));
199
200     if (!fg)
201         exit_program(1);
202     fg->index = nb_filtergraphs;
203
204     GROW_ARRAY(fg->outputs, fg->nb_outputs);
205     if (!(fg->outputs[0] = av_mallocz(sizeof(*fg->outputs[0]))))
206         exit_program(1);
207     fg->outputs[0]->ost   = ost;
208     fg->outputs[0]->graph = fg;
209
210     ost->filter = fg->outputs[0];
211
212     GROW_ARRAY(fg->inputs, fg->nb_inputs);
213     if (!(fg->inputs[0] = av_mallocz(sizeof(*fg->inputs[0]))))
214         exit_program(1);
215     fg->inputs[0]->ist   = ist;
216     fg->inputs[0]->graph = fg;
217     fg->inputs[0]->format = -1;
218
219     GROW_ARRAY(ist->filters, ist->nb_filters);
220     ist->filters[ist->nb_filters - 1] = fg->inputs[0];
221
222     GROW_ARRAY(filtergraphs, nb_filtergraphs);
223     filtergraphs[nb_filtergraphs - 1] = fg;
224
225     return 0;
226 }
227
228 static void init_input_filter(FilterGraph *fg, AVFilterInOut *in)
229 {
230     InputStream *ist = NULL;
231     enum AVMediaType type = avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx);
232     int i;
233
234     // TODO: support other filter types
235     if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO) {
236         av_log(NULL, AV_LOG_FATAL, "Only video and audio filters supported "
237                "currently.\n");
238         exit_program(1);
239     }
240
241     if (in->name) {
242         AVFormatContext *s;
243         AVStream       *st = NULL;
244         char *p;
245         int file_idx = strtol(in->name, &p, 0);
246
247         if (file_idx < 0 || file_idx >= nb_input_files) {
248             av_log(NULL, AV_LOG_FATAL, "Invalid file index %d in filtergraph description %s.\n",
249                    file_idx, fg->graph_desc);
250             exit_program(1);
251         }
252         s = input_files[file_idx]->ctx;
253
254         for (i = 0; i < s->nb_streams; i++) {
255             enum AVMediaType stream_type = s->streams[i]->codecpar->codec_type;
256             if (stream_type != type &&
257                 !(stream_type == AVMEDIA_TYPE_SUBTITLE &&
258                   type == AVMEDIA_TYPE_VIDEO /* sub2video hack */))
259                 continue;
260             if (check_stream_specifier(s, s->streams[i], *p == ':' ? p + 1 : p) == 1) {
261                 st = s->streams[i];
262                 break;
263             }
264         }
265         if (!st) {
266             av_log(NULL, AV_LOG_FATAL, "Stream specifier '%s' in filtergraph description %s "
267                    "matches no streams.\n", p, fg->graph_desc);
268             exit_program(1);
269         }
270         ist = input_streams[input_files[file_idx]->ist_index + st->index];
271     } else {
272         /* find the first unused stream of corresponding type */
273         for (i = 0; i < nb_input_streams; i++) {
274             ist = input_streams[i];
275             if (ist->dec_ctx->codec_type == type && ist->discard)
276                 break;
277         }
278         if (i == nb_input_streams) {
279             av_log(NULL, AV_LOG_FATAL, "Cannot find a matching stream for "
280                    "unlabeled input pad %d on filter %s\n", in->pad_idx,
281                    in->filter_ctx->name);
282             exit_program(1);
283         }
284     }
285     av_assert0(ist);
286
287     ist->discard         = 0;
288     ist->decoding_needed |= DECODING_FOR_FILTER;
289     ist->st->discard = AVDISCARD_NONE;
290
291     GROW_ARRAY(fg->inputs, fg->nb_inputs);
292     if (!(fg->inputs[fg->nb_inputs - 1] = av_mallocz(sizeof(*fg->inputs[0]))))
293         exit_program(1);
294     fg->inputs[fg->nb_inputs - 1]->ist   = ist;
295     fg->inputs[fg->nb_inputs - 1]->graph = fg;
296     fg->inputs[fg->nb_inputs - 1]->format = -1;
297
298     GROW_ARRAY(ist->filters, ist->nb_filters);
299     ist->filters[ist->nb_filters - 1] = fg->inputs[fg->nb_inputs - 1];
300 }
301
302 int init_complex_filtergraph(FilterGraph *fg)
303 {
304     AVFilterInOut *inputs, *outputs, *cur;
305     AVFilterGraph *graph;
306     int ret = 0;
307
308     /* this graph is only used for determining the kinds of inputs
309      * and outputs we have, and is discarded on exit from this function */
310     graph = avfilter_graph_alloc();
311     if (!graph)
312         return AVERROR(ENOMEM);
313
314     ret = avfilter_graph_parse2(graph, fg->graph_desc, &inputs, &outputs);
315     if (ret < 0)
316         goto fail;
317
318     for (cur = inputs; cur; cur = cur->next)
319         init_input_filter(fg, cur);
320
321     for (cur = outputs; cur;) {
322         GROW_ARRAY(fg->outputs, fg->nb_outputs);
323         fg->outputs[fg->nb_outputs - 1] = av_mallocz(sizeof(*fg->outputs[0]));
324         if (!fg->outputs[fg->nb_outputs - 1])
325             exit_program(1);
326
327         fg->outputs[fg->nb_outputs - 1]->graph   = fg;
328         fg->outputs[fg->nb_outputs - 1]->out_tmp = cur;
329         fg->outputs[fg->nb_outputs - 1]->type    = avfilter_pad_get_type(cur->filter_ctx->output_pads,
330                                                                          cur->pad_idx);
331         cur = cur->next;
332         fg->outputs[fg->nb_outputs - 1]->out_tmp->next = NULL;
333     }
334
335 fail:
336     avfilter_inout_free(&inputs);
337     avfilter_graph_free(&graph);
338     return ret;
339 }
340
341 static int insert_trim(int64_t start_time, int64_t duration,
342                        AVFilterContext **last_filter, int *pad_idx,
343                        const char *filter_name)
344 {
345     AVFilterGraph *graph = (*last_filter)->graph;
346     AVFilterContext *ctx;
347     const AVFilter *trim;
348     enum AVMediaType type = avfilter_pad_get_type((*last_filter)->output_pads, *pad_idx);
349     const char *name = (type == AVMEDIA_TYPE_VIDEO) ? "trim" : "atrim";
350     int ret = 0;
351
352     if (duration == INT64_MAX && start_time == AV_NOPTS_VALUE)
353         return 0;
354
355     trim = avfilter_get_by_name(name);
356     if (!trim) {
357         av_log(NULL, AV_LOG_ERROR, "%s filter not present, cannot limit "
358                "recording time.\n", name);
359         return AVERROR_FILTER_NOT_FOUND;
360     }
361
362     ctx = avfilter_graph_alloc_filter(graph, trim, filter_name);
363     if (!ctx)
364         return AVERROR(ENOMEM);
365
366     if (duration != INT64_MAX) {
367         ret = av_opt_set_int(ctx, "durationi", duration,
368                                 AV_OPT_SEARCH_CHILDREN);
369     }
370     if (ret >= 0 && start_time != AV_NOPTS_VALUE) {
371         ret = av_opt_set_int(ctx, "starti", start_time,
372                                 AV_OPT_SEARCH_CHILDREN);
373     }
374     if (ret < 0) {
375         av_log(ctx, AV_LOG_ERROR, "Error configuring the %s filter", name);
376         return ret;
377     }
378
379     ret = avfilter_init_str(ctx, NULL);
380     if (ret < 0)
381         return ret;
382
383     ret = avfilter_link(*last_filter, *pad_idx, ctx, 0);
384     if (ret < 0)
385         return ret;
386
387     *last_filter = ctx;
388     *pad_idx     = 0;
389     return 0;
390 }
391
392 static int insert_filter(AVFilterContext **last_filter, int *pad_idx,
393                          const char *filter_name, const char *args)
394 {
395     AVFilterGraph *graph = (*last_filter)->graph;
396     AVFilterContext *ctx;
397     int ret;
398
399     ret = avfilter_graph_create_filter(&ctx,
400                                        avfilter_get_by_name(filter_name),
401                                        filter_name, args, NULL, graph);
402     if (ret < 0)
403         return ret;
404
405     ret = avfilter_link(*last_filter, *pad_idx, ctx, 0);
406     if (ret < 0)
407         return ret;
408
409     *last_filter = ctx;
410     *pad_idx     = 0;
411     return 0;
412 }
413
414 static int configure_output_video_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
415 {
416     char *pix_fmts;
417     OutputStream *ost = ofilter->ost;
418     OutputFile    *of = output_files[ost->file_index];
419     AVCodecContext *codec = ost->enc_ctx;
420     AVFilterContext *last_filter = out->filter_ctx;
421     int pad_idx = out->pad_idx;
422     int ret;
423     char name[255];
424
425     snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index);
426     ret = avfilter_graph_create_filter(&ofilter->filter,
427                                        avfilter_get_by_name("buffersink"),
428                                        name, NULL, NULL, fg->graph);
429
430     if (ret < 0)
431         return ret;
432
433     if (!hw_device_ctx && (codec->width || codec->height)) {
434         char args[255];
435         AVFilterContext *filter;
436         AVDictionaryEntry *e = NULL;
437
438         snprintf(args, sizeof(args), "%d:%d",
439                  codec->width,
440                  codec->height);
441
442         while ((e = av_dict_get(ost->sws_dict, "", e,
443                                 AV_DICT_IGNORE_SUFFIX))) {
444             av_strlcatf(args, sizeof(args), ":%s=%s", e->key, e->value);
445         }
446
447         snprintf(name, sizeof(name), "scaler for output stream %d:%d",
448                  ost->file_index, ost->index);
449         if ((ret = avfilter_graph_create_filter(&filter, avfilter_get_by_name("scale"),
450                                                 name, args, NULL, fg->graph)) < 0)
451             return ret;
452         if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)
453             return ret;
454
455         last_filter = filter;
456         pad_idx = 0;
457     }
458
459     if ((pix_fmts = choose_pix_fmts(ost))) {
460         AVFilterContext *filter;
461         snprintf(name, sizeof(name), "pixel format for output stream %d:%d",
462                  ost->file_index, ost->index);
463         ret = avfilter_graph_create_filter(&filter,
464                                            avfilter_get_by_name("format"),
465                                            "format", pix_fmts, NULL, fg->graph);
466         av_freep(&pix_fmts);
467         if (ret < 0)
468             return ret;
469         if ((ret = avfilter_link(last_filter, pad_idx, filter, 0)) < 0)
470             return ret;
471
472         last_filter = filter;
473         pad_idx     = 0;
474     }
475
476     if (ost->frame_rate.num && 0) {
477         AVFilterContext *fps;
478         char args[255];
479
480         snprintf(args, sizeof(args), "fps=%d/%d", ost->frame_rate.num,
481                  ost->frame_rate.den);
482         snprintf(name, sizeof(name), "fps for output stream %d:%d",
483                  ost->file_index, ost->index);
484         ret = avfilter_graph_create_filter(&fps, avfilter_get_by_name("fps"),
485                                            name, args, NULL, fg->graph);
486         if (ret < 0)
487             return ret;
488
489         ret = avfilter_link(last_filter, pad_idx, fps, 0);
490         if (ret < 0)
491             return ret;
492         last_filter = fps;
493         pad_idx = 0;
494     }
495
496     snprintf(name, sizeof(name), "trim for output stream %d:%d",
497              ost->file_index, ost->index);
498     ret = insert_trim(of->start_time, of->recording_time,
499                       &last_filter, &pad_idx, name);
500     if (ret < 0)
501         return ret;
502
503
504     if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
505         return ret;
506
507     return 0;
508 }
509
510 static int configure_output_audio_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
511 {
512     OutputStream *ost = ofilter->ost;
513     OutputFile    *of = output_files[ost->file_index];
514     AVCodecContext *codec  = ost->enc_ctx;
515     AVFilterContext *last_filter = out->filter_ctx;
516     int pad_idx = out->pad_idx;
517     char *sample_fmts, *sample_rates, *channel_layouts;
518     char name[255];
519     int ret;
520
521     snprintf(name, sizeof(name), "output stream %d:%d", ost->file_index, ost->index);
522     ret = avfilter_graph_create_filter(&ofilter->filter,
523                                        avfilter_get_by_name("abuffersink"),
524                                        name, NULL, NULL, fg->graph);
525     if (ret < 0)
526         return ret;
527     if ((ret = av_opt_set_int(ofilter->filter, "all_channel_counts", 1, AV_OPT_SEARCH_CHILDREN)) < 0)
528         return ret;
529
530 #define AUTO_INSERT_FILTER(opt_name, filter_name, arg) do {                 \
531     AVFilterContext *filt_ctx;                                              \
532                                                                             \
533     av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi "            \
534            "similarly to -af " filter_name "=%s.\n", arg);                  \
535                                                                             \
536     ret = avfilter_graph_create_filter(&filt_ctx,                           \
537                                        avfilter_get_by_name(filter_name),   \
538                                        filter_name, arg, NULL, fg->graph);  \
539     if (ret < 0)                                                            \
540         return ret;                                                         \
541                                                                             \
542     ret = avfilter_link(last_filter, pad_idx, filt_ctx, 0);                 \
543     if (ret < 0)                                                            \
544         return ret;                                                         \
545                                                                             \
546     last_filter = filt_ctx;                                                 \
547     pad_idx = 0;                                                            \
548 } while (0)
549     if (ost->audio_channels_mapped) {
550         int i;
551         AVBPrint pan_buf;
552         av_bprint_init(&pan_buf, 256, 8192);
553         av_bprintf(&pan_buf, "0x%"PRIx64,
554                    av_get_default_channel_layout(ost->audio_channels_mapped));
555         for (i = 0; i < ost->audio_channels_mapped; i++)
556             if (ost->audio_channels_map[i] != -1)
557                 av_bprintf(&pan_buf, "|c%d=c%d", i, ost->audio_channels_map[i]);
558
559         AUTO_INSERT_FILTER("-map_channel", "pan", pan_buf.str);
560         av_bprint_finalize(&pan_buf, NULL);
561     }
562
563     if (codec->channels && !codec->channel_layout)
564         codec->channel_layout = av_get_default_channel_layout(codec->channels);
565
566     sample_fmts     = choose_sample_fmts(ost);
567     sample_rates    = choose_sample_rates(ost);
568     channel_layouts = choose_channel_layouts(ost);
569     if (sample_fmts || sample_rates || channel_layouts) {
570         AVFilterContext *format;
571         char args[256];
572         args[0] = 0;
573
574         if (sample_fmts)
575             av_strlcatf(args, sizeof(args), "sample_fmts=%s:",
576                             sample_fmts);
577         if (sample_rates)
578             av_strlcatf(args, sizeof(args), "sample_rates=%s:",
579                             sample_rates);
580         if (channel_layouts)
581             av_strlcatf(args, sizeof(args), "channel_layouts=%s:",
582                             channel_layouts);
583
584         av_freep(&sample_fmts);
585         av_freep(&sample_rates);
586         av_freep(&channel_layouts);
587
588         snprintf(name, sizeof(name), "audio format for output stream %d:%d",
589                  ost->file_index, ost->index);
590         ret = avfilter_graph_create_filter(&format,
591                                            avfilter_get_by_name("aformat"),
592                                            name, args, NULL, fg->graph);
593         if (ret < 0)
594             return ret;
595
596         ret = avfilter_link(last_filter, pad_idx, format, 0);
597         if (ret < 0)
598             return ret;
599
600         last_filter = format;
601         pad_idx = 0;
602     }
603
604     if (audio_volume != 256 && 0) {
605         char args[256];
606
607         snprintf(args, sizeof(args), "%f", audio_volume / 256.);
608         AUTO_INSERT_FILTER("-vol", "volume", args);
609     }
610
611     if (ost->apad && of->shortest) {
612         char args[256];
613         int i;
614
615         for (i=0; i<of->ctx->nb_streams; i++)
616             if (of->ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
617                 break;
618
619         if (i<of->ctx->nb_streams) {
620             snprintf(args, sizeof(args), "%s", ost->apad);
621             AUTO_INSERT_FILTER("-apad", "apad", args);
622         }
623     }
624
625     snprintf(name, sizeof(name), "trim for output stream %d:%d",
626              ost->file_index, ost->index);
627     ret = insert_trim(of->start_time, of->recording_time,
628                       &last_filter, &pad_idx, name);
629     if (ret < 0)
630         return ret;
631
632     if ((ret = avfilter_link(last_filter, pad_idx, ofilter->filter, 0)) < 0)
633         return ret;
634
635     return 0;
636 }
637
638 #define DESCRIBE_FILTER_LINK(f, inout, in)                         \
639 {                                                                  \
640     AVFilterContext *ctx = inout->filter_ctx;                      \
641     AVFilterPad *pads = in ? ctx->input_pads  : ctx->output_pads;  \
642     int       nb_pads = in ? ctx->nb_inputs   : ctx->nb_outputs;   \
643     AVIOContext *pb;                                               \
644                                                                    \
645     if (avio_open_dyn_buf(&pb) < 0)                                \
646         exit_program(1);                                           \
647                                                                    \
648     avio_printf(pb, "%s", ctx->filter->name);                      \
649     if (nb_pads > 1)                                               \
650         avio_printf(pb, ":%s", avfilter_pad_get_name(pads, inout->pad_idx));\
651     avio_w8(pb, 0);                                                \
652     avio_close_dyn_buf(pb, &f->name);                              \
653 }
654
655 int configure_output_filter(FilterGraph *fg, OutputFilter *ofilter, AVFilterInOut *out)
656 {
657     av_freep(&ofilter->name);
658     DESCRIBE_FILTER_LINK(ofilter, out, 0);
659
660     if (!ofilter->ost) {
661         av_log(NULL, AV_LOG_FATAL, "Filter %s has an unconnected output\n", ofilter->name);
662         exit_program(1);
663     }
664
665     switch (avfilter_pad_get_type(out->filter_ctx->output_pads, out->pad_idx)) {
666     case AVMEDIA_TYPE_VIDEO: return configure_output_video_filter(fg, ofilter, out);
667     case AVMEDIA_TYPE_AUDIO: return configure_output_audio_filter(fg, ofilter, out);
668     default: av_assert0(0);
669     }
670 }
671
672 static int sub2video_prepare(InputStream *ist, InputFilter *ifilter)
673 {
674     AVFormatContext *avf = input_files[ist->file_index]->ctx;
675     int i, w, h;
676
677     /* Compute the size of the canvas for the subtitles stream.
678        If the subtitles codecpar has set a size, use it. Otherwise use the
679        maximum dimensions of the video streams in the same file. */
680     w = ifilter->width;
681     h = ifilter->height;
682     if (!(w && h)) {
683         for (i = 0; i < avf->nb_streams; i++) {
684             if (avf->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
685                 w = FFMAX(w, avf->streams[i]->codecpar->width);
686                 h = FFMAX(h, avf->streams[i]->codecpar->height);
687             }
688         }
689         if (!(w && h)) {
690             w = FFMAX(w, 720);
691             h = FFMAX(h, 576);
692         }
693         av_log(avf, AV_LOG_INFO, "sub2video: using %dx%d canvas\n", w, h);
694     }
695     ist->sub2video.w = ist->resample_width  = w;
696     ist->sub2video.h = ist->resample_height = h;
697
698     /* rectangles are AV_PIX_FMT_PAL8, but we have no guarantee that the
699        palettes for all rectangles are identical or compatible */
700     ist->resample_pix_fmt = ifilter->format = AV_PIX_FMT_RGB32;
701
702     ist->sub2video.frame = av_frame_alloc();
703     if (!ist->sub2video.frame)
704         return AVERROR(ENOMEM);
705     ist->sub2video.last_pts = INT64_MIN;
706     return 0;
707 }
708
709 static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter,
710                                         AVFilterInOut *in)
711 {
712     AVFilterContext *last_filter;
713     const AVFilter *buffer_filt = avfilter_get_by_name("buffer");
714     InputStream *ist = ifilter->ist;
715     InputFile     *f = input_files[ist->file_index];
716     AVRational tb = ist->framerate.num ? av_inv_q(ist->framerate) :
717                                          ist->st->time_base;
718     AVRational fr = ist->framerate;
719     AVRational sar;
720     AVBPrint args;
721     char name[255];
722     int ret, pad_idx = 0;
723     int64_t tsoffset = 0;
724     AVBufferSrcParameters *par = av_buffersrc_parameters_alloc();
725
726     if (!par)
727         return AVERROR(ENOMEM);
728     memset(par, 0, sizeof(*par));
729     par->format = AV_PIX_FMT_NONE;
730
731     if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_AUDIO) {
732         av_log(NULL, AV_LOG_ERROR, "Cannot connect video filter to audio input\n");
733         ret = AVERROR(EINVAL);
734         goto fail;
735     }
736
737     if (!fr.num)
738         fr = av_guess_frame_rate(input_files[ist->file_index]->ctx, ist->st, NULL);
739
740     if (ist->dec_ctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
741         ret = sub2video_prepare(ist, ifilter);
742         if (ret < 0)
743             goto fail;
744     }
745
746     sar = ifilter->sample_aspect_ratio;
747     if(!sar.den)
748         sar = (AVRational){0,1};
749     av_bprint_init(&args, 0, 1);
750     av_bprintf(&args,
751              "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:"
752              "pixel_aspect=%d/%d:sws_param=flags=%d",
753              ifilter->width, ifilter->height, ifilter->format,
754              tb.num, tb.den, sar.num, sar.den,
755              SWS_BILINEAR + ((ist->dec_ctx->flags&AV_CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0));
756     if (fr.num && fr.den)
757         av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
758     snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
759              ist->file_index, ist->st->index);
760
761
762     if ((ret = avfilter_graph_create_filter(&ifilter->filter, buffer_filt, name,
763                                             args.str, NULL, fg->graph)) < 0)
764         goto fail;
765     par->hw_frames_ctx = ifilter->hw_frames_ctx;
766     ret = av_buffersrc_parameters_set(ifilter->filter, par);
767     if (ret < 0)
768         goto fail;
769     av_freep(&par);
770     last_filter = ifilter->filter;
771
772     if (ist->autorotate) {
773         double theta = get_rotation(ist->st);
774
775         if (fabs(theta - 90) < 1.0) {
776             ret = insert_filter(&last_filter, &pad_idx, "transpose", "clock");
777         } else if (fabs(theta - 180) < 1.0) {
778             ret = insert_filter(&last_filter, &pad_idx, "hflip", NULL);
779             if (ret < 0)
780                 return ret;
781             ret = insert_filter(&last_filter, &pad_idx, "vflip", NULL);
782         } else if (fabs(theta - 270) < 1.0) {
783             ret = insert_filter(&last_filter, &pad_idx, "transpose", "cclock");
784         } else if (fabs(theta) > 1.0) {
785             char rotate_buf[64];
786             snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
787             ret = insert_filter(&last_filter, &pad_idx, "rotate", rotate_buf);
788         }
789         if (ret < 0)
790             return ret;
791     }
792
793     if (ist->framerate.num) {
794         AVFilterContext *setpts;
795
796         snprintf(name, sizeof(name), "force CFR for input from stream %d:%d",
797                  ist->file_index, ist->st->index);
798         if ((ret = avfilter_graph_create_filter(&setpts,
799                                                 avfilter_get_by_name("setpts"),
800                                                 name, "N", NULL,
801                                                 fg->graph)) < 0)
802             return ret;
803
804         if ((ret = avfilter_link(last_filter, 0, setpts, 0)) < 0)
805             return ret;
806
807         last_filter = setpts;
808     }
809
810     if (do_deinterlace) {
811         AVFilterContext *yadif;
812
813         snprintf(name, sizeof(name), "deinterlace input from stream %d:%d",
814                  ist->file_index, ist->st->index);
815         if ((ret = avfilter_graph_create_filter(&yadif,
816                                                 avfilter_get_by_name("yadif"),
817                                                 name, "", NULL,
818                                                 fg->graph)) < 0)
819             return ret;
820
821         if ((ret = avfilter_link(last_filter, 0, yadif, 0)) < 0)
822             return ret;
823
824         last_filter = yadif;
825     }
826
827     snprintf(name, sizeof(name), "trim for input stream %d:%d",
828              ist->file_index, ist->st->index);
829     if (copy_ts) {
830         tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
831         if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
832             tsoffset += f->ctx->start_time;
833     }
834     ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
835                       AV_NOPTS_VALUE : tsoffset, f->recording_time,
836                       &last_filter, &pad_idx, name);
837     if (ret < 0)
838         return ret;
839
840     if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
841         return ret;
842     return 0;
843 fail:
844     av_freep(&par);
845
846     return ret;
847 }
848
849 static int configure_input_audio_filter(FilterGraph *fg, InputFilter *ifilter,
850                                         AVFilterInOut *in)
851 {
852     AVFilterContext *last_filter;
853     const AVFilter *abuffer_filt = avfilter_get_by_name("abuffer");
854     InputStream *ist = ifilter->ist;
855     InputFile     *f = input_files[ist->file_index];
856     AVBPrint args;
857     char name[255];
858     int ret, pad_idx = 0;
859     int64_t tsoffset = 0;
860
861     if (ist->dec_ctx->codec_type != AVMEDIA_TYPE_AUDIO) {
862         av_log(NULL, AV_LOG_ERROR, "Cannot connect audio filter to non audio input\n");
863         return AVERROR(EINVAL);
864     }
865
866     av_bprint_init(&args, 0, AV_BPRINT_SIZE_AUTOMATIC);
867     av_bprintf(&args, "time_base=%d/%d:sample_rate=%d:sample_fmt=%s",
868              1, ifilter->sample_rate,
869              ifilter->sample_rate,
870              av_get_sample_fmt_name(ifilter->format));
871     if (ifilter->channel_layout)
872         av_bprintf(&args, ":channel_layout=0x%"PRIx64,
873                    ifilter->channel_layout);
874     else
875         av_bprintf(&args, ":channels=%d", ifilter->channels);
876     snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
877              ist->file_index, ist->st->index);
878
879     if ((ret = avfilter_graph_create_filter(&ifilter->filter, abuffer_filt,
880                                             name, args.str, NULL,
881                                             fg->graph)) < 0)
882         return ret;
883     last_filter = ifilter->filter;
884
885 #define AUTO_INSERT_FILTER_INPUT(opt_name, filter_name, arg) do {                 \
886     AVFilterContext *filt_ctx;                                              \
887                                                                             \
888     av_log(NULL, AV_LOG_INFO, opt_name " is forwarded to lavfi "            \
889            "similarly to -af " filter_name "=%s.\n", arg);                  \
890                                                                             \
891     snprintf(name, sizeof(name), "graph %d %s for input stream %d:%d",      \
892                 fg->index, filter_name, ist->file_index, ist->st->index);   \
893     ret = avfilter_graph_create_filter(&filt_ctx,                           \
894                                        avfilter_get_by_name(filter_name),   \
895                                        name, arg, NULL, fg->graph);         \
896     if (ret < 0)                                                            \
897         return ret;                                                         \
898                                                                             \
899     ret = avfilter_link(last_filter, 0, filt_ctx, 0);                       \
900     if (ret < 0)                                                            \
901         return ret;                                                         \
902                                                                             \
903     last_filter = filt_ctx;                                                 \
904 } while (0)
905
906     if (audio_sync_method > 0) {
907         char args[256] = {0};
908
909         av_strlcatf(args, sizeof(args), "async=%d", audio_sync_method);
910         if (audio_drift_threshold != 0.1)
911             av_strlcatf(args, sizeof(args), ":min_hard_comp=%f", audio_drift_threshold);
912         if (!fg->reconfiguration)
913             av_strlcatf(args, sizeof(args), ":first_pts=0");
914         AUTO_INSERT_FILTER_INPUT("-async", "aresample", args);
915     }
916
917 //     if (ost->audio_channels_mapped) {
918 //         int i;
919 //         AVBPrint pan_buf;
920 //         av_bprint_init(&pan_buf, 256, 8192);
921 //         av_bprintf(&pan_buf, "0x%"PRIx64,
922 //                    av_get_default_channel_layout(ost->audio_channels_mapped));
923 //         for (i = 0; i < ost->audio_channels_mapped; i++)
924 //             if (ost->audio_channels_map[i] != -1)
925 //                 av_bprintf(&pan_buf, ":c%d=c%d", i, ost->audio_channels_map[i]);
926 //         AUTO_INSERT_FILTER_INPUT("-map_channel", "pan", pan_buf.str);
927 //         av_bprint_finalize(&pan_buf, NULL);
928 //     }
929
930     if (audio_volume != 256) {
931         char args[256];
932
933         av_log(NULL, AV_LOG_WARNING, "-vol has been deprecated. Use the volume "
934                "audio filter instead.\n");
935
936         snprintf(args, sizeof(args), "%f", audio_volume / 256.);
937         AUTO_INSERT_FILTER_INPUT("-vol", "volume", args);
938     }
939
940     snprintf(name, sizeof(name), "trim for input stream %d:%d",
941              ist->file_index, ist->st->index);
942     if (copy_ts) {
943         tsoffset = f->start_time == AV_NOPTS_VALUE ? 0 : f->start_time;
944         if (!start_at_zero && f->ctx->start_time != AV_NOPTS_VALUE)
945             tsoffset += f->ctx->start_time;
946     }
947     ret = insert_trim(((f->start_time == AV_NOPTS_VALUE) || !f->accurate_seek) ?
948                       AV_NOPTS_VALUE : tsoffset, f->recording_time,
949                       &last_filter, &pad_idx, name);
950     if (ret < 0)
951         return ret;
952
953     if ((ret = avfilter_link(last_filter, 0, in->filter_ctx, in->pad_idx)) < 0)
954         return ret;
955
956     return 0;
957 }
958
959 static int configure_input_filter(FilterGraph *fg, InputFilter *ifilter,
960                                   AVFilterInOut *in)
961 {
962     av_freep(&ifilter->name);
963     DESCRIBE_FILTER_LINK(ifilter, in, 1);
964
965     if (!ifilter->ist->dec) {
966         av_log(NULL, AV_LOG_ERROR,
967                "No decoder for stream #%d:%d, filtering impossible\n",
968                ifilter->ist->file_index, ifilter->ist->st->index);
969         return AVERROR_DECODER_NOT_FOUND;
970     }
971     switch (avfilter_pad_get_type(in->filter_ctx->input_pads, in->pad_idx)) {
972     case AVMEDIA_TYPE_VIDEO: return configure_input_video_filter(fg, ifilter, in);
973     case AVMEDIA_TYPE_AUDIO: return configure_input_audio_filter(fg, ifilter, in);
974     default: av_assert0(0);
975     }
976 }
977
978 int configure_filtergraph(FilterGraph *fg)
979 {
980     AVFilterInOut *inputs, *outputs, *cur;
981     int ret, i, simple = filtergraph_is_simple(fg);
982     const char *graph_desc = simple ? fg->outputs[0]->ost->avfilter :
983                                       fg->graph_desc;
984
985     avfilter_graph_free(&fg->graph);
986     if (!(fg->graph = avfilter_graph_alloc()))
987         return AVERROR(ENOMEM);
988
989     if (simple) {
990         OutputStream *ost = fg->outputs[0]->ost;
991         char args[512];
992         AVDictionaryEntry *e = NULL;
993
994         args[0] = 0;
995         while ((e = av_dict_get(ost->sws_dict, "", e,
996                                 AV_DICT_IGNORE_SUFFIX))) {
997             av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);
998         }
999         if (strlen(args))
1000             args[strlen(args)-1] = 0;
1001         fg->graph->scale_sws_opts = av_strdup(args);
1002
1003         args[0] = 0;
1004         while ((e = av_dict_get(ost->swr_opts, "", e,
1005                                 AV_DICT_IGNORE_SUFFIX))) {
1006             av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);
1007         }
1008         if (strlen(args))
1009             args[strlen(args)-1] = 0;
1010         av_opt_set(fg->graph, "aresample_swr_opts", args, 0);
1011
1012         args[0] = '\0';
1013         while ((e = av_dict_get(fg->outputs[0]->ost->resample_opts, "", e,
1014                                 AV_DICT_IGNORE_SUFFIX))) {
1015             av_strlcatf(args, sizeof(args), "%s=%s:", e->key, e->value);
1016         }
1017         if (strlen(args))
1018             args[strlen(args) - 1] = '\0';
1019         fg->graph->resample_lavr_opts = av_strdup(args);
1020
1021         e = av_dict_get(ost->encoder_opts, "threads", NULL, 0);
1022         if (e)
1023             av_opt_set(fg->graph, "threads", e->value, 0);
1024     }
1025
1026     if ((ret = avfilter_graph_parse2(fg->graph, graph_desc, &inputs, &outputs)) < 0)
1027         return ret;
1028
1029     if (hw_device_ctx) {
1030         for (i = 0; i < fg->graph->nb_filters; i++) {
1031             fg->graph->filters[i]->hw_device_ctx = av_buffer_ref(hw_device_ctx);
1032         }
1033     }
1034
1035     if (simple && (!inputs || inputs->next || !outputs || outputs->next)) {
1036         const char *num_inputs;
1037         const char *num_outputs;
1038         if (!outputs) {
1039             num_outputs = "0";
1040         } else if (outputs->next) {
1041             num_outputs = ">1";
1042         } else {
1043             num_outputs = "1";
1044         }
1045         if (!inputs) {
1046             num_inputs = "0";
1047         } else if (inputs->next) {
1048             num_inputs = ">1";
1049         } else {
1050             num_inputs = "1";
1051         }
1052         av_log(NULL, AV_LOG_ERROR, "Simple filtergraph '%s' was expected "
1053                "to have exactly 1 input and 1 output."
1054                " However, it had %s input(s) and %s output(s)."
1055                " Please adjust, or use a complex filtergraph (-filter_complex) instead.\n",
1056                graph_desc, num_inputs, num_outputs);
1057         return AVERROR(EINVAL);
1058     }
1059
1060     for (cur = inputs, i = 0; cur; cur = cur->next, i++)
1061         if ((ret = configure_input_filter(fg, fg->inputs[i], cur)) < 0) {
1062             avfilter_inout_free(&inputs);
1063             avfilter_inout_free(&outputs);
1064             return ret;
1065         }
1066     avfilter_inout_free(&inputs);
1067
1068     for (cur = outputs, i = 0; cur; cur = cur->next, i++)
1069         configure_output_filter(fg, fg->outputs[i], cur);
1070     avfilter_inout_free(&outputs);
1071
1072     if ((ret = avfilter_graph_config(fg->graph, NULL)) < 0)
1073         return ret;
1074
1075     fg->reconfiguration = 1;
1076
1077     for (i = 0; i < fg->nb_outputs; i++) {
1078         OutputStream *ost = fg->outputs[i]->ost;
1079         if (!ost->enc) {
1080             /* identical to the same check in ffmpeg.c, needed because
1081                complex filter graphs are initialized earlier */
1082             av_log(NULL, AV_LOG_ERROR, "Encoder (codec %s) not found for output stream #%d:%d\n",
1083                      avcodec_get_name(ost->st->codecpar->codec_id), ost->file_index, ost->index);
1084             return AVERROR(EINVAL);
1085         }
1086         if (ost->enc->type == AVMEDIA_TYPE_AUDIO &&
1087             !(ost->enc->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE))
1088             av_buffersink_set_frame_size(ost->filter->filter,
1089                                          ost->enc_ctx->frame_size);
1090     }
1091
1092     return 0;
1093 }
1094
1095 int ifilter_parameters_from_frame(InputFilter *ifilter, const AVFrame *frame)
1096 {
1097     av_buffer_unref(&ifilter->hw_frames_ctx);
1098
1099     ifilter->format = frame->format;
1100
1101     ifilter->width               = frame->width;
1102     ifilter->height              = frame->height;
1103     ifilter->sample_aspect_ratio = frame->sample_aspect_ratio;
1104
1105     ifilter->sample_rate         = frame->sample_rate;
1106     ifilter->channels            = av_frame_get_channels(frame);
1107     ifilter->channel_layout      = frame->channel_layout;
1108
1109     if (frame->hw_frames_ctx) {
1110         ifilter->hw_frames_ctx = av_buffer_ref(frame->hw_frames_ctx);
1111         if (!ifilter->hw_frames_ctx)
1112             return AVERROR(ENOMEM);
1113     }
1114
1115     return 0;
1116 }
1117
1118 int ifilter_parameters_from_decoder(InputFilter *ifilter, const AVCodecContext *avctx)
1119 {
1120     av_buffer_unref(&ifilter->hw_frames_ctx);
1121
1122     if (avctx->codec_type == AVMEDIA_TYPE_VIDEO)
1123         ifilter->format = avctx->pix_fmt;
1124     else
1125         ifilter->format = avctx->sample_fmt;
1126
1127     ifilter->width               = avctx->width;
1128     ifilter->height              = avctx->height;
1129     if (ifilter->ist && ifilter->ist->st && ifilter->ist->st->sample_aspect_ratio.num)
1130         ifilter->sample_aspect_ratio = ifilter->ist->st->sample_aspect_ratio;
1131     else
1132         ifilter->sample_aspect_ratio = avctx->sample_aspect_ratio;
1133
1134     ifilter->sample_rate         = avctx->sample_rate;
1135     ifilter->channels            = avctx->channels;
1136     ifilter->channel_layout      = avctx->channel_layout;
1137
1138     if (avctx->hw_frames_ctx) {
1139         ifilter->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
1140         if (!ifilter->hw_frames_ctx)
1141             return AVERROR(ENOMEM);
1142     }
1143
1144     return 0;
1145 }
1146
1147 int ist_in_filtergraph(FilterGraph *fg, InputStream *ist)
1148 {
1149     int i;
1150     for (i = 0; i < fg->nb_inputs; i++)
1151         if (fg->inputs[i]->ist == ist)
1152             return 1;
1153     return 0;
1154 }
1155
1156 int filtergraph_is_simple(FilterGraph *fg)
1157 {
1158     return !fg->graph_desc;
1159 }