1 /*
2  * This file is part of mpv.
3  *
4  * mpv is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * mpv is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with mpv.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <inttypes.h>
22 #include <math.h>
23 
24 #include <libavutil/rational.h>
25 
26 #include "common/msg.h"
27 #include "common/common.h"
28 #include "filters/f_autoconvert.h"
29 #include "filters/filter.h"
30 #include "filters/filter_internal.h"
31 #include "filters/user_filters.h"
32 #include "video/img_format.h"
33 #include "video/mp_image.h"
34 
35 #include "options/m_option.h"
36 
37 struct priv {
38     struct vf_format_opts *opts;
39     struct mp_autoconvert *conv;
40 };
41 
42 struct vf_format_opts {
43     int fmt;
44     int colormatrix;
45     int colorlevels;
46     int primaries;
47     int gamma;
48     float sig_peak;
49     int light;
50     int chroma_location;
51     int stereo_in;
52     int rotate;
53     int alpha;
54     int w, h;
55     int dw, dh;
56     double dar;
57     int convert;
58     int force_scaler;
59 };
60 
set_params(struct vf_format_opts * p,struct mp_image_params * out,bool set_size)61 static void set_params(struct vf_format_opts *p, struct mp_image_params *out,
62                        bool set_size)
63 {
64     if (p->colormatrix)
65         out->color.space = p->colormatrix;
66     if (p->colorlevels)
67         out->color.levels = p->colorlevels;
68     if (p->primaries)
69         out->color.primaries = p->primaries;
70     if (p->gamma) {
71         enum mp_csp_trc in_gamma = p->gamma;
72         out->color.gamma = p->gamma;
73         if (in_gamma != out->color.gamma) {
74             // When changing the gamma function explicitly, also reset stuff
75             // related to the gamma function since that information will almost
76             // surely be false now and have to be re-inferred
77             out->color.sig_peak = 0.0;
78             out->color.light = MP_CSP_LIGHT_AUTO;
79         }
80     }
81     if (p->sig_peak)
82         out->color.sig_peak = p->sig_peak;
83     if (p->light)
84         out->color.light = p->light;
85     if (p->chroma_location)
86         out->chroma_location = p->chroma_location;
87     if (p->stereo_in)
88         out->stereo3d = p->stereo_in;
89     if (p->rotate >= 0)
90         out->rotate = p->rotate;
91     if (p->alpha)
92         out->alpha = p->alpha;
93 
94     if (p->w > 0 && set_size)
95         out->w = p->w;
96     if (p->h > 0 && set_size)
97         out->h = p->h;
98     AVRational dsize;
99     mp_image_params_get_dsize(out, &dsize.num, &dsize.den);
100     if (p->dw > 0)
101         dsize.num = p->dw;
102     if (p->dh > 0)
103         dsize.den = p->dh;
104     if (p->dar > 0)
105         dsize = av_d2q(p->dar, INT_MAX);
106     mp_image_params_set_dsize(out, dsize.num, dsize.den);
107 }
108 
vf_format_process(struct mp_filter * f)109 static void vf_format_process(struct mp_filter *f)
110 {
111     struct priv *priv = f->priv;
112 
113     if (mp_pin_can_transfer_data(priv->conv->f->pins[0], f->ppins[0])) {
114         struct mp_frame frame = mp_pin_out_read(f->ppins[0]);
115 
116         if (priv->opts->convert && frame.type == MP_FRAME_VIDEO) {
117             struct mp_image *img = frame.data;
118             struct mp_image_params par = img->params;
119             int outfmt = priv->opts->fmt;
120 
121             // If we convert from RGB to YUV, default to limited range.
122             if (mp_imgfmt_get_forced_csp(img->imgfmt) == MP_CSP_RGB &&
123                 outfmt && mp_imgfmt_get_forced_csp(outfmt) == MP_CSP_AUTO)
124             {
125                 par.color.levels = MP_CSP_LEVELS_TV;
126             }
127 
128             set_params(priv->opts, &par, true);
129 
130             if (outfmt && par.imgfmt != outfmt) {
131                 par.imgfmt = outfmt;
132                 par.hw_subfmt = 0;
133             }
134             mp_image_params_guess_csp(&par);
135 
136             mp_autoconvert_set_target_image_params(priv->conv, &par);
137         }
138 
139         mp_pin_in_write(priv->conv->f->pins[0], frame);
140     }
141 
142     if (mp_pin_can_transfer_data(f->ppins[1], priv->conv->f->pins[1])) {
143         struct mp_frame frame = mp_pin_out_read(priv->conv->f->pins[1]);
144 
145         if (!priv->opts->convert && frame.type == MP_FRAME_VIDEO) {
146             struct mp_image *img = frame.data;
147 
148             set_params(priv->opts, &img->params, false);
149             mp_image_params_guess_csp(&img->params);
150         }
151 
152         mp_pin_in_write(f->ppins[1], frame);
153     }
154 }
155 
156 static const struct mp_filter_info vf_format_filter = {
157     .name = "format",
158     .process = vf_format_process,
159     .priv_size = sizeof(struct priv),
160 };
161 
vf_format_create(struct mp_filter * parent,void * options)162 static struct mp_filter *vf_format_create(struct mp_filter *parent, void *options)
163 {
164     struct mp_filter *f = mp_filter_create(parent, &vf_format_filter);
165     if (!f) {
166         talloc_free(options);
167         return NULL;
168     }
169 
170     struct priv *priv = f->priv;
171     priv->opts = talloc_steal(priv, options);
172 
173     mp_filter_add_pin(f, MP_PIN_IN, "in");
174     mp_filter_add_pin(f, MP_PIN_OUT, "out");
175 
176     priv->conv = mp_autoconvert_create(f);
177     if (!priv->conv) {
178         talloc_free(f);
179         return NULL;
180     }
181 
182     priv->conv->force_scaler = priv->opts->force_scaler;
183 
184     if (priv->opts->fmt)
185         mp_autoconvert_add_imgfmt(priv->conv, priv->opts->fmt, 0);
186 
187     return f;
188 }
189 
190 #define OPT_BASE_STRUCT struct vf_format_opts
191 static const m_option_t vf_opts_fields[] = {
192     {"fmt", OPT_IMAGEFORMAT(fmt)},
193     {"colormatrix", OPT_CHOICE_C(colormatrix, mp_csp_names)},
194     {"colorlevels", OPT_CHOICE_C(colorlevels, mp_csp_levels_names)},
195     {"primaries", OPT_CHOICE_C(primaries, mp_csp_prim_names)},
196     {"gamma", OPT_CHOICE_C(gamma, mp_csp_trc_names)},
197     {"sig-peak", OPT_FLOAT(sig_peak)},
198     {"light", OPT_CHOICE_C(light, mp_csp_light_names)},
199     {"chroma-location", OPT_CHOICE_C(chroma_location, mp_chroma_names)},
200     {"stereo-in", OPT_CHOICE_C(stereo_in, mp_stereo3d_names)},
201     {"rotate", OPT_INT(rotate), M_RANGE(-1, 359)},
202     {"alpha", OPT_CHOICE_C(alpha, mp_alpha_names)},
203     {"w", OPT_INT(w)},
204     {"h", OPT_INT(h)},
205     {"dw", OPT_INT(dw)},
206     {"dh", OPT_INT(dh)},
207     {"dar", OPT_DOUBLE(dar)},
208     {"convert", OPT_FLAG(convert)},
209     {"force-scaler", OPT_CHOICE(force_scaler,
210                                 {"auto", MP_SWS_AUTO},
211                                 {"sws", MP_SWS_SWS},
212                                 {"zimg", MP_SWS_ZIMG})},
213     {"outputlevels", OPT_REMOVED("use the --video-output-levels global option")},
214     {"peak", OPT_REMOVED("use sig-peak instead (changed value scale!)")},
215     {0}
216 };
217 
218 const struct mp_user_filter_entry vf_format = {
219     .desc = {
220         .description = "force output format",
221         .name = "format",
222         .priv_size = sizeof(OPT_BASE_STRUCT),
223         .priv_defaults = &(const OPT_BASE_STRUCT){
224             .rotate = -1,
225         },
226         .options = vf_opts_fields,
227     },
228     .create = vf_format_create,
229 };
230