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