34102
|
1 /*
|
|
2 * Copyright (C) 2011 Nicolas George <nicolas.george@normalesup.org>
|
|
3 *
|
|
4 * This file is part of MPlayer.
|
|
5 *
|
|
6 * MPlayer is free software; you can redistribute it and/or modify
|
|
7 * it under the terms of the GNU General Public License as published by
|
|
8 * the Free Software Foundation; either version 2 of the License, or
|
|
9 * (at your option) any later version.
|
|
10 *
|
|
11 * MPlayer 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
|
|
14 * GNU General Public License for more details.
|
|
15 *
|
|
16 * You should have received a copy of the GNU General Public License along
|
|
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
|
|
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
19 */
|
|
20
|
|
21 #include "vf.h"
|
|
22 #include "m_struct.h"
|
|
23 #include "fmt-conversion.h"
|
|
24 #include "libavfilter/avfilter.h"
|
|
25 #include "libavfilter/avfiltergraph.h"
|
|
26 #include "libavutil/pixdesc.h"
|
|
27
|
|
28 struct vf_priv_s {
|
|
29 AVFilterGraph *graph;
|
|
30 AVFilterContext *in;
|
|
31 AVFilterContext *out;
|
|
32 int in_w;
|
|
33 int in_h;
|
|
34 enum PixelFormat in_pixfmt;
|
|
35 int in_imgfmt;
|
|
36 AVRational in_sar;
|
|
37 int out_w;
|
|
38 int out_h;
|
|
39 enum PixelFormat out_pixfmt;
|
|
40 int out_imgfmt;
|
|
41 AVRational out_sar;
|
|
42 struct AVFilterBufferRef *in_buf;
|
|
43 mp_image_t *in_mpi;
|
|
44 };
|
|
45
|
|
46 static void buf_mpi_free(AVFilterBuffer *buf)
|
|
47 {
|
|
48 ((mp_image_t *)buf->priv)->usage_count--;
|
|
49 av_free(buf);
|
|
50 }
|
|
51
|
|
52 static AVFilterBufferRef *mpi_to_bufref(mp_image_t *mpi, enum PixelFormat fmt,
|
|
53 AVRational sar)
|
|
54 {
|
|
55 AVFilterBufferRef *buf;
|
|
56 int perms = AV_PERM_READ;
|
|
57
|
|
58 if ((mpi->flags & MP_IMGFLAG_ALLOCATED))
|
|
59 perms |= AV_PERM_REUSE2;
|
|
60 if (!(mpi->flags & MP_IMGFLAG_PRESERVE))
|
|
61 perms |= AV_PERM_WRITE;
|
|
62 buf = avfilter_get_video_buffer_ref_from_arrays(mpi->planes, mpi->stride,
|
|
63 perms,
|
|
64 mpi->w, mpi->h,
|
|
65 fmt);
|
|
66 buf->video->sample_aspect_ratio = sar;
|
|
67 buf->buf->priv = mpi;
|
|
68 buf->buf->free = buf_mpi_free;
|
|
69 return buf;
|
|
70 }
|
|
71
|
|
72 static void bufref_to_mpi(AVFilterBufferRef *ref, mp_image_t *mpi)
|
|
73 {
|
|
74 memcpy(mpi->planes, ref->data, sizeof(ref->data));
|
|
75 memcpy(mpi->stride, ref->linesize, sizeof(ref->linesize));
|
|
76 }
|
|
77
|
|
78 struct mpsink_priv {
|
|
79 struct vf_instance *vf;
|
|
80 };
|
|
81
|
|
82 static int mpsink_init(AVFilterContext *ctx,
|
|
83 av_unused const char *args, void *opaque)
|
|
84 {
|
|
85 struct mpsink_priv *c = ctx->priv;
|
|
86 c->vf = opaque;
|
|
87 return 0;
|
|
88 }
|
|
89
|
|
90 static int mpsink_query_formats(AVFilterContext *ctx)
|
|
91 {
|
|
92 struct mpsink_priv *c = ctx->priv;
|
|
93 struct vf_instance *vf = c->vf;
|
|
94 AVFilterFormats *all;
|
|
95 enum PixelFormat *sup;
|
|
96 unsigned i, nsup = 0;
|
|
97 int ifmt;
|
|
98
|
|
99 all = avfilter_all_formats(AVMEDIA_TYPE_VIDEO);
|
|
100 sup = av_mallocz(sizeof(*sup) * (all->format_count + 1));
|
|
101 if (!sup)
|
|
102 return AVERROR(errno);
|
|
103 for(i = 0; i < all->format_count; i++) {
|
|
104 ifmt = pixfmt2imgfmt(all->formats[i]);
|
|
105 if (vf->next->query_format(vf->next, ifmt) > 0)
|
|
106 sup[nsup++] = all->formats[i];
|
|
107 }
|
|
108 sup[nsup++] = PIX_FMT_NONE;
|
|
109 avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(sup));
|
|
110 av_free(sup);
|
|
111 return 0;
|
|
112 }
|
|
113
|
|
114 static AVFilterBufferRef *mpsink_get_video_buffer(AVFilterLink *link,
|
|
115 int perms, int w, int h)
|
|
116 {
|
|
117 struct mpsink_priv *c = link->dst->priv;
|
|
118 struct vf_instance *vf = c->vf;
|
|
119 mp_image_t *dmpi;
|
|
120 int type;
|
|
121 int flags = 0;
|
|
122
|
|
123 type = MP_IMGTYPE_NUMBERED | (-1 << 16);
|
|
124 if ((perms & AV_PERM_PRESERVE))
|
|
125 flags |= MP_IMGFLAG_PRESERVE;
|
|
126 if ((perms & AV_PERM_READ))
|
|
127 flags |= MP_IMGFLAG_READABLE;
|
|
128 if ((perms & AV_PERM_NEG_LINESIZES))
|
|
129 flags |= MP_IMGFLAG_ACCEPT_STRIDE;
|
|
130 if (vf->priv->in_mpi) {
|
|
131 type = vf->priv->in_mpi->type;
|
|
132 vf->priv->in_mpi = NULL;
|
|
133 }
|
|
134 dmpi = vf_get_image(vf->next, vf->priv->out_imgfmt, type, flags, w, h);
|
|
135 return mpi_to_bufref(dmpi, vf->priv->out_pixfmt, vf->priv->out_sar);
|
|
136 }
|
|
137
|
|
138 static void mpsink_end_frame(AVFilterLink *link)
|
|
139 {
|
|
140 struct mpsink_priv *c = link->dst->priv;
|
|
141 struct vf_instance *vf = c->vf;
|
|
142 AVFilterBufferRef *buf = link->cur_buf;
|
|
143 mp_image_t *mpi = buf->buf->priv;
|
|
144 double pts;
|
|
145
|
|
146 pts = buf->pts == (int64_t)AV_NOPTS_VALUE ? MP_NOPTS_VALUE :
|
|
147 buf->pts * av_q2d(link->time_base);
|
|
148 mpi->pict_type = buf->video->pict_type;
|
|
149 mpi->fields = (buf->video->interlaced ? MP_IMGFIELD_INTERLACED : 0) |
|
|
150 (buf->video->top_field_first ? MP_IMGFIELD_TOP_FIRST : 0);
|
|
151 vf_next_put_image(vf, mpi, pts);
|
|
152 avfilter_unref_buffer(link->cur_buf);
|
|
153 }
|
|
154
|
|
155 static AVFilter mpsink = {
|
|
156 .name = "mpsink",
|
|
157 .description = "Video sink for mplayer interaction",
|
|
158 .priv_size = sizeof(struct mpsink_priv),
|
|
159
|
|
160 .init = mpsink_init,
|
|
161 .query_formats = mpsink_query_formats,
|
|
162
|
|
163 .inputs = (AVFilterPad[]) {{ .name = "default",
|
|
164 .type = AVMEDIA_TYPE_VIDEO,
|
|
165 .end_frame = mpsink_end_frame,
|
|
166 .get_video_buffer = mpsink_get_video_buffer,
|
|
167 .min_perms = AV_PERM_READ, },
|
|
168 { .name = NULL }},
|
|
169 .outputs = (AVFilterPad[]) {{ .name = NULL }},
|
|
170 };
|
|
171
|
|
172 struct mpsrc_priv {
|
|
173 struct vf_instance *vf;
|
|
174 };
|
|
175
|
|
176 static int mpsrc_init(AVFilterContext *ctx,
|
|
177 av_unused const char *args, void *opaque)
|
|
178 {
|
|
179 struct mpsrc_priv *c = ctx->priv;
|
|
180 c->vf = opaque;
|
|
181 return 0;
|
|
182 }
|
|
183
|
|
184 static int mpsrc_query_formats(AVFilterContext *ctx)
|
|
185 {
|
|
186 struct mpsrc_priv *c = ctx->priv;
|
|
187 enum PixelFormat pix_fmts[] = { c->vf->priv->in_pixfmt, PIX_FMT_NONE };
|
|
188 avfilter_set_common_pixel_formats(ctx, avfilter_make_format_list(pix_fmts));
|
|
189 return 0;
|
|
190 }
|
|
191
|
|
192 static int mpsrc_config_props(AVFilterLink *link)
|
|
193 {
|
|
194 struct mpsrc_priv *c = link->src->priv;
|
|
195 struct vf_instance *vf = c->vf;
|
|
196 link->w = vf->priv->in_w;
|
|
197 link->h = vf->priv->in_h;
|
|
198 link->sample_aspect_ratio = vf->priv->in_sar;
|
|
199 link->time_base = AV_TIME_BASE_Q;
|
|
200 return 0;
|
|
201 }
|
|
202
|
|
203 static int mpsrc_request_frame(AVFilterLink *link)
|
|
204 {
|
|
205 struct mpsrc_priv *c = link->src->priv;
|
|
206 struct vf_instance *vf = c->vf;
|
|
207
|
|
208 if (!vf->priv->in_buf)
|
|
209 return AVERROR(EINVAL);
|
|
210 avfilter_start_frame(link, avfilter_ref_buffer(vf->priv->in_buf, ~0));
|
|
211 avfilter_draw_slice(link, 0, link->h, 1);
|
|
212 avfilter_end_frame(link);
|
|
213 vf->priv->in_buf = NULL;
|
|
214 return 0;
|
|
215 }
|
|
216
|
|
217 static int mpsrc_poll_frame(AVFilterLink *link)
|
|
218 {
|
|
219 struct mpsrc_priv *c = link->src->priv;
|
|
220 struct vf_instance *vf = c->vf;
|
|
221 return vf->priv->in_buf != NULL;
|
|
222 }
|
|
223
|
|
224 AVFilter mpsrc = {
|
|
225 .name = "mpsrc",
|
|
226 .description = "Video source for mplayer interaction",
|
|
227 .priv_size = sizeof(struct mpsrc_priv),
|
|
228 .query_formats = mpsrc_query_formats,
|
|
229
|
|
230 .init = mpsrc_init,
|
|
231
|
|
232 .inputs = (AVFilterPad[]) {{ .name = NULL }},
|
|
233 .outputs = (AVFilterPad[]) {{ .name = "default",
|
|
234 .type = AVMEDIA_TYPE_VIDEO,
|
|
235 .request_frame = mpsrc_request_frame,
|
|
236 .poll_frame = mpsrc_poll_frame,
|
|
237 .config_props = mpsrc_config_props, },
|
|
238 { .name = NULL }},
|
|
239 };
|
|
240
|
|
241 static int config(struct vf_instance *vf, int w, int h, int dw, int dh,
|
|
242 unsigned flags, unsigned fmt)
|
|
243 {
|
|
244 int ret;
|
|
245 AVFilterLink *out;
|
|
246 AVRational iar, dar;
|
|
247
|
|
248 av_reduce(&iar.num, &iar.den, w, h, INT_MAX);
|
|
249 av_reduce(&dar.num, &dar.den, dw, dh, INT_MAX);
|
|
250 vf->priv->in_pixfmt = imgfmt2pixfmt(fmt);
|
|
251 vf->priv->in_imgfmt = fmt;
|
|
252 vf->priv->in_w = w;
|
|
253 vf->priv->in_h = h;
|
|
254 vf->priv->in_sar = av_div_q(dar, iar);
|
|
255 ret = avfilter_graph_config(vf->priv->graph, NULL);
|
|
256 if (ret < 0)
|
|
257 return 0;
|
|
258 out = vf->priv->out->inputs[0];
|
|
259 vf->priv->out_w = out->w;
|
|
260 vf->priv->out_h = out->h;
|
|
261 vf->priv->out_pixfmt = out->format;
|
|
262 vf->priv->out_imgfmt = pixfmt2imgfmt(out->format);
|
|
263 vf->priv->out_sar = out->sample_aspect_ratio;
|
|
264 if (vf->priv->out_sar.num != vf->priv->in_sar.num ||
|
|
265 vf->priv->out_sar.den != vf->priv->in_sar.den ||
|
|
266 out->w != w || out->h != h) {
|
|
267 av_reduce(&iar.num, &iar.den, out->w, out->h, INT_MAX);
|
|
268 dar = av_mul_q(iar, out->sample_aspect_ratio);
|
|
269 if (av_cmp_q(dar, iar) >= 0) {
|
|
270 dh = out->h;
|
|
271 dw = av_rescale(dh, dar.num, dar.den);
|
|
272 } else {
|
|
273 dw = out->w;
|
|
274 dh = av_rescale(dw, dar.den, dar.num);
|
|
275 }
|
|
276 }
|
|
277 return vf_next_config(vf, out->w, out->h, dw, dh, flags, fmt);
|
|
278 }
|
|
279
|
|
280 static void get_image(struct vf_instance *vf, mp_image_t *mpi)
|
|
281 {
|
|
282 AVFilterBufferRef *buf;
|
|
283 unsigned perms = AV_PERM_WRITE | AV_PERM_REUSE2;
|
|
284
|
|
285 avfilter_unref_buffer(mpi->priv);
|
|
286 mpi->priv = NULL; /* for safety */
|
|
287 if (mpi->flags & MP_IMGFLAG_READABLE)
|
|
288 perms |= AV_PERM_READ;
|
|
289 if (mpi->flags & MP_IMGFLAG_PRESERVE)
|
|
290 perms |= AV_PERM_PRESERVE;
|
|
291 vf->priv->in_mpi = mpi;
|
|
292 buf = avfilter_get_video_buffer(vf->priv->in->outputs[0], perms,
|
|
293 mpi->w, mpi->h);
|
|
294 vf->priv->in_mpi = NULL;
|
|
295 bufref_to_mpi(buf, mpi);
|
|
296 mpi->flags |= MP_IMGFLAG_DIRECT;
|
|
297 mpi->flags &= ~MP_IMGFLAG_ALLOCATED;
|
|
298 mpi->priv = buf;
|
|
299 }
|
|
300
|
|
301 static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
|
|
302 {
|
|
303 AVFilterBufferRef *buf;
|
|
304 mp_image_t *cmpi = NULL;
|
|
305
|
|
306 if (!(mpi->flags & MP_IMGFLAG_DIRECT)) {
|
|
307 cmpi = vf_get_image(vf, mpi->imgfmt, MP_IMGTYPE_TEMP,
|
|
308 MP_IMGFLAG_PREFER_ALIGNED_STRIDE,
|
|
309 mpi->w, mpi->h);
|
|
310 copy_mpi(cmpi, mpi);
|
|
311 buf = cmpi->priv;
|
|
312 } else {
|
|
313 buf = mpi->priv;
|
|
314 }
|
|
315 buf->video->key_frame = mpi->pict_type == 1;
|
|
316 buf->video->pict_type = mpi->pict_type; /* seems to be the same code */
|
|
317 buf->video->interlaced = !!(mpi->fields & MP_IMGFIELD_INTERLACED);
|
|
318 buf->video->top_field_first = !!(mpi->fields & MP_IMGFIELD_TOP_FIRST);
|
|
319 vf->priv->in_buf = buf;
|
|
320 if (pts != MP_NOPTS_VALUE)
|
|
321 buf->pts = pts * AV_TIME_BASE;
|
|
322 while (avfilter_poll_frame(vf->priv->out->inputs[0])) {
|
|
323 if (avfilter_request_frame(vf->priv->out->inputs[0]))
|
|
324 break;
|
|
325 }
|
|
326 return 1;
|
|
327 }
|
|
328
|
|
329 static void uninit(struct vf_instance *vf)
|
|
330 {
|
|
331 unsigned i;
|
|
332
|
|
333 #define FREE_MPI_ARRAY(field) \
|
|
334 for (i = 0; i < FF_ARRAY_ELEMS(vf->imgctx.field); i++) \
|
|
335 if (vf->imgctx.field[i]) \
|
|
336 avfilter_unref_buffer(vf->imgctx.field[i]->priv);
|
|
337 FREE_MPI_ARRAY(static_images);
|
|
338 FREE_MPI_ARRAY(temp_images);
|
|
339 FREE_MPI_ARRAY(export_images);
|
|
340 FREE_MPI_ARRAY(numbered_images);
|
|
341 avfilter_graph_free(&vf->priv->graph);
|
|
342 av_free(vf->priv);
|
|
343 }
|
|
344
|
|
345 static int lavfi_open(struct vf_instance *vf, char *args)
|
|
346 {
|
|
347 AVFilterInOut *outputs;
|
|
348 AVFilterInOut *inputs;
|
|
349 int ret;
|
|
350
|
|
351 avfilter_register_all();
|
|
352 if (!args) {
|
|
353 mp_msg(MSGT_VFILTER, MSGL_ERR, "lavfi: filtergraph needed\n");
|
|
354 goto fail;
|
|
355 }
|
|
356 if (args[0] == '$') {
|
|
357 char *e = getenv(args + 1);
|
|
358 if (!e) {
|
|
359 mp_msg(MSGT_VFILTER, MSGL_ERR, "lavfi: %s not defined\n", args);
|
|
360 goto fail;
|
|
361 }
|
|
362 args = e;
|
|
363 }
|
|
364 vf->priv = av_mallocz(sizeof(struct vf_priv_s));
|
|
365 if (!vf->priv)
|
|
366 return 0;
|
|
367
|
|
368 vf->priv->graph = avfilter_graph_alloc();
|
|
369 if (!vf->priv->graph)
|
|
370 goto fail;
|
|
371 ret = avfilter_graph_create_filter(&vf->priv->in, &mpsrc, "in",
|
|
372 NULL, vf, vf->priv->graph);
|
|
373 if (ret < 0)
|
|
374 goto fail;
|
|
375 ret = avfilter_graph_create_filter(&vf->priv->out, &mpsink, "out",
|
|
376 NULL, vf, vf->priv->graph);
|
|
377 if (ret < 0)
|
|
378 return 0;
|
|
379 outputs = avfilter_inout_alloc();
|
|
380 inputs = avfilter_inout_alloc();
|
|
381 if (!outputs || !inputs)
|
|
382 goto fail;
|
|
383 outputs->name = av_strdup("in");
|
|
384 outputs->filter_ctx = vf->priv->in;
|
|
385 outputs->pad_idx = 0;
|
|
386 outputs->next = NULL;
|
|
387 inputs->name = av_strdup("out");
|
|
388 inputs->filter_ctx = vf->priv->out;
|
|
389 inputs->pad_idx = 0;
|
|
390 inputs->next = NULL;
|
|
391 ret = avfilter_graph_parse(vf->priv->graph, args, &inputs, &outputs, NULL);
|
|
392 if (ret < 0)
|
|
393 goto fail;
|
|
394
|
|
395 vf->config = config;
|
|
396 vf->uninit = uninit;
|
|
397 vf->put_image = put_image;
|
|
398 vf->get_image = get_image;
|
|
399 return 1;
|
|
400
|
|
401 fail:
|
|
402 avfilter_inout_free(&inputs);
|
|
403 avfilter_inout_free(&outputs);
|
|
404 avfilter_graph_free(&vf->priv->graph);
|
|
405 av_free(vf->priv);
|
|
406 return 0;
|
|
407 }
|
|
408
|
|
409 static const m_option_t vf_opts_fields[] = { { .name = NULL } };
|
|
410
|
|
411 const vf_info_t vf_info_lavfi = {
|
|
412 "libavfilter wrapper",
|
|
413 "lavfi",
|
|
414 "Nicolas George",
|
|
415 "",
|
|
416 lavfi_open,
|
|
417 NULL,
|
|
418 };
|