/src/vlc/src/misc/picture.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * picture.c : picture management functions |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2000-2010 VLC authors and VideoLAN |
5 | | * Copyright (C) 2009-2010 Laurent Aimar |
6 | | * |
7 | | * Authors: Vincent Seguin <seguin@via.ecp.fr> |
8 | | * Samuel Hocevar <sam@zoy.org> |
9 | | * Laurent Aimar <fenrir _AT_ videolan _DOT_ org> |
10 | | * |
11 | | * This program is free software; you can redistribute it and/or modify it |
12 | | * under the terms of the GNU Lesser General Public License as published by |
13 | | * the Free Software Foundation; either version 2.1 of the License, or |
14 | | * (at your option) any later version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, |
17 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | * GNU Lesser General Public License for more details. |
20 | | * |
21 | | * You should have received a copy of the GNU Lesser General Public License |
22 | | * along with this program; if not, write to the Free Software Foundation, |
23 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
24 | | *****************************************************************************/ |
25 | | |
26 | | /***************************************************************************** |
27 | | * Preamble |
28 | | *****************************************************************************/ |
29 | | |
30 | | #ifdef HAVE_CONFIG_H |
31 | | # include "config.h" |
32 | | #endif |
33 | | #include <assert.h> |
34 | | #include <limits.h> |
35 | | #include <stdckdint.h> |
36 | | |
37 | | #include <vlc_common.h> |
38 | | #include "picture.h" |
39 | | #include <vlc_image.h> |
40 | | #include <vlc_block.h> |
41 | | |
42 | | #include <vlc_ancillary.h> |
43 | | |
44 | | static void PictureDestroyContext( picture_t *p_picture ) |
45 | 787 | { |
46 | 787 | picture_context_t *ctx = p_picture->context; |
47 | 787 | if (ctx != NULL) |
48 | 0 | { |
49 | 0 | vlc_video_context *vctx = ctx->vctx; |
50 | 0 | ctx->destroy(ctx); |
51 | 0 | if (vctx) |
52 | 0 | vlc_video_context_Release(vctx); |
53 | 0 | p_picture->context = NULL; |
54 | 0 | } |
55 | 787 | } |
56 | | |
57 | | /** |
58 | | * Destroys a picture allocated by picture_NewFromResource() but without |
59 | | * a custom destruction callback. |
60 | | */ |
61 | | static void picture_DestroyDummy( picture_t *p_picture ) |
62 | 0 | { |
63 | 0 | (void) p_picture; |
64 | 0 | } |
65 | | |
66 | | /** |
67 | | * Destroys a picture allocated with picture_NewFromFormat(). |
68 | | */ |
69 | | static void picture_DestroyFromFormat(picture_t *pic) |
70 | 787 | { |
71 | 787 | picture_buffer_t *res = pic->p_sys; |
72 | | |
73 | 787 | if (res != NULL) |
74 | 787 | picture_Deallocate(res->fd, res->base, res->size); |
75 | 787 | } |
76 | | |
77 | | VLC_WEAK void *picture_Allocate(int *restrict fdp, size_t size) |
78 | 787 | { |
79 | 787 | assert((size % 64) == 0); |
80 | 787 | *fdp = -1; |
81 | 787 | return aligned_alloc(64, size); |
82 | 787 | } |
83 | | |
84 | | VLC_WEAK void picture_Deallocate(int fd, void *base, size_t size) |
85 | 787 | { |
86 | 787 | assert(fd == -1); |
87 | 787 | aligned_free(base); |
88 | 787 | assert((size % 64) == 0); |
89 | 787 | } |
90 | | |
91 | | /***************************************************************************** |
92 | | * |
93 | | *****************************************************************************/ |
94 | | void picture_Reset( picture_t *p_picture ) |
95 | 0 | { |
96 | | /* */ |
97 | 0 | p_picture->date = VLC_TICK_INVALID; |
98 | 0 | p_picture->b_force = false; |
99 | 0 | p_picture->b_still = false; |
100 | 0 | p_picture->b_progressive = false; |
101 | 0 | p_picture->i_nb_fields = 2; |
102 | 0 | p_picture->b_top_field_first = false; |
103 | 0 | PictureDestroyContext( p_picture ); |
104 | |
|
105 | 0 | picture_priv_t *priv = container_of(p_picture, picture_priv_t, picture); |
106 | 0 | vlc_ancillary_array_Clear(&priv->ancillaries); |
107 | 0 | } |
108 | | |
109 | | /***************************************************************************** |
110 | | * |
111 | | *****************************************************************************/ |
112 | | static int LCM( int a, int b ) |
113 | 14.2k | { |
114 | 14.2k | return a * b / GCD( a, b ); |
115 | 14.2k | } |
116 | | |
117 | | int picture_Setup( picture_t *p_picture, const video_format_t *restrict fmt ) |
118 | 3.55k | { |
119 | 3.55k | const vlc_chroma_description_t *p_dsc = |
120 | 3.55k | vlc_fourcc_GetChromaDescription( fmt->i_chroma ); |
121 | 3.55k | if( unlikely(!p_dsc) ) |
122 | 0 | return VLC_EGENERIC; |
123 | | |
124 | | /* Store default values */ |
125 | 3.55k | p_picture->i_planes = 0; |
126 | 21.3k | for( unsigned i = 0; i < ARRAY_SIZE(p_picture->p); i++ ) |
127 | 17.7k | { |
128 | 17.7k | plane_t *p = &p_picture->p[i]; |
129 | 17.7k | p->p_pixels = NULL; |
130 | 17.7k | p->i_pixel_pitch = 0; |
131 | 17.7k | } |
132 | | |
133 | 3.55k | p_picture->i_nb_fields = 2; |
134 | | |
135 | 3.55k | video_format_Setup( &p_picture->format, fmt->i_chroma, fmt->i_width, fmt->i_height, |
136 | 3.55k | fmt->i_visible_width, fmt->i_visible_height, |
137 | 3.55k | fmt->i_sar_num, fmt->i_sar_den ); |
138 | 3.55k | if( fmt->i_x_offset < fmt->i_width && |
139 | 3.55k | fmt->i_y_offset < fmt->i_height && |
140 | 3.55k | fmt->i_visible_width > 0 && fmt->i_x_offset + fmt->i_visible_width <= fmt->i_width && |
141 | 3.55k | fmt->i_visible_height > 0 && fmt->i_y_offset + fmt->i_visible_height <= fmt->i_height ) |
142 | 3.09k | video_format_CopyCrop( &p_picture->format, fmt ); |
143 | | |
144 | | /* We want V (width/height) to respect: |
145 | | (V * p_dsc->p[i].w.i_num) % p_dsc->p[i].w.i_den == 0 |
146 | | (V * p_dsc->p[i].w.i_num/p_dsc->p[i].w.i_den * p_dsc->i_pixel_size) % 16 == 0 |
147 | | Which is respected if you have |
148 | | V % lcm( p_dsc->p[0..planes].w.i_den * 16) == 0 |
149 | | */ |
150 | 3.55k | unsigned i_modulo_w = 1; |
151 | 3.55k | unsigned i_modulo_h = 1; |
152 | 3.55k | unsigned i_ratio_h = 1; |
153 | | |
154 | 8.88k | for( unsigned i = 0; i < p_dsc->plane_count; i++ ) |
155 | 5.32k | { |
156 | 5.32k | i_modulo_w = LCM( i_modulo_w, 64 * p_dsc->p[i].w.den ); |
157 | 5.32k | i_modulo_h = LCM( i_modulo_h, 16 * p_dsc->p[i].h.den ); |
158 | 5.32k | if( i_ratio_h < p_dsc->p[i].h.den ) |
159 | 925 | i_ratio_h = p_dsc->p[i].h.den; |
160 | 5.32k | } |
161 | 3.55k | i_modulo_h = LCM( i_modulo_h, 32 ); |
162 | | |
163 | 3.55k | unsigned width, height; |
164 | | |
165 | 3.55k | if (unlikely(ckd_add(&width, fmt->i_width, i_modulo_w - 1)) |
166 | 3.55k | || unlikely(ckd_add(&height, fmt->i_height, i_modulo_h - 1))) |
167 | 849 | return VLC_EGENERIC; |
168 | | |
169 | 2.70k | width = width / i_modulo_w * i_modulo_w; |
170 | 2.70k | height = height / i_modulo_h * i_modulo_h; |
171 | | |
172 | | /* plane_t uses 'int'. */ |
173 | 2.70k | if (unlikely(width > INT_MAX) || unlikely(height > INT_MAX)) |
174 | 598 | return VLC_EGENERIC; |
175 | | |
176 | 5.60k | for( unsigned i = 0; i < p_dsc->plane_count; i++ ) |
177 | 3.49k | { |
178 | 3.49k | plane_t *p = &p_picture->p[i]; |
179 | 3.49k | const vlc_rational_t *h = &p_dsc->p[i].h; |
180 | 3.49k | const vlc_rational_t *w = &p_dsc->p[i].w; |
181 | | |
182 | | /* A plane cannot be over-sampled. This could lead to overflow. */ |
183 | 3.49k | assert(h->den >= h->num); |
184 | 3.49k | assert(w->den >= w->num); |
185 | | |
186 | 3.49k | p->i_lines = height * h->num / h->den; |
187 | 3.49k | p->i_visible_lines = (fmt->i_visible_height + (h->den - 1)) / h->den * h->num; |
188 | | |
189 | 3.49k | p->i_pitch = width * w->num / w->den * p_dsc->pixel_size; |
190 | 3.49k | p->i_visible_pitch = (fmt->i_visible_width + (w->den - 1)) / w->den * w->num |
191 | 3.49k | * p_dsc->pixel_size; |
192 | 3.49k | p->i_pixel_pitch = p_dsc->pixel_size; |
193 | | |
194 | 3.49k | assert( (p->i_pitch % 64) == 0 ); |
195 | 3.49k | } |
196 | 2.10k | p_picture->i_planes = p_dsc->plane_count; |
197 | | |
198 | 2.10k | return VLC_SUCCESS; |
199 | 2.10k | } |
200 | | |
201 | | /***************************************************************************** |
202 | | * |
203 | | *****************************************************************************/ |
204 | | |
205 | | static bool picture_InitPrivate(const video_format_t *restrict p_fmt, |
206 | | picture_priv_t *priv, |
207 | | const picture_resource_t *p_resource) |
208 | 3.55k | { |
209 | 3.55k | picture_t *p_picture = &priv->picture; |
210 | | |
211 | 3.55k | memset( p_picture, 0, sizeof( *p_picture ) ); |
212 | 3.55k | p_picture->date = VLC_TICK_INVALID; |
213 | | |
214 | 3.55k | video_format_Copy(&p_picture->format, p_fmt); |
215 | | /* Make sure the real dimensions are a multiple of 16 */ |
216 | 3.55k | if( picture_Setup( p_picture, p_fmt ) ) |
217 | 1.44k | { |
218 | 1.44k | video_format_Clean(&p_picture->format); |
219 | 1.44k | return false; |
220 | 1.44k | } |
221 | | |
222 | 2.10k | vlc_atomic_rc_init(&p_picture->refs); |
223 | 2.10k | priv->gc.opaque = NULL; |
224 | | |
225 | 2.10k | p_picture->p_sys = p_resource->p_sys; |
226 | | |
227 | 2.10k | if( p_resource->pf_destroy != NULL ) |
228 | 2.10k | priv->gc.destroy = p_resource->pf_destroy; |
229 | 0 | else |
230 | 0 | priv->gc.destroy = picture_DestroyDummy; |
231 | | |
232 | 2.10k | priv->pool = NULL; |
233 | | |
234 | 2.10k | vlc_ancillary_array_Init(&priv->ancillaries); |
235 | | |
236 | 2.10k | return true; |
237 | 3.55k | } |
238 | | |
239 | | picture_t *picture_NewFromResource( const video_format_t *p_fmt, const picture_resource_t *p_resource ) |
240 | 0 | { |
241 | 0 | assert(p_resource != NULL); |
242 | | |
243 | 0 | picture_priv_t *priv = malloc(sizeof(*priv)); |
244 | 0 | if (unlikely(priv == NULL)) |
245 | 0 | return NULL; |
246 | | |
247 | 0 | if (!picture_InitPrivate(p_fmt, priv, p_resource)) |
248 | 0 | { |
249 | 0 | free(priv); |
250 | 0 | return NULL; |
251 | 0 | } |
252 | | |
253 | 0 | picture_t *p_picture = &priv->picture; |
254 | |
|
255 | 0 | for( int i = 0; i < p_picture->i_planes; i++ ) |
256 | 0 | { |
257 | 0 | p_picture->p[i].p_pixels = p_resource->p[i].p_pixels; |
258 | 0 | p_picture->p[i].i_lines = p_resource->p[i].i_lines; |
259 | 0 | p_picture->p[i].i_pitch = p_resource->p[i].i_pitch; |
260 | 0 | } |
261 | |
|
262 | 0 | return p_picture; |
263 | 0 | } |
264 | | |
265 | | #define PICTURE_SW_SIZE_MAX (UINT32_C(1) << 28) /* 256MB: 8K * 8K * 4*/ |
266 | | |
267 | | struct picture_priv_buffer_t { |
268 | | picture_priv_t priv; |
269 | | picture_buffer_t res; |
270 | | }; |
271 | | |
272 | | picture_t *picture_NewFromFormat(const video_format_t *restrict fmt) |
273 | 3.55k | { |
274 | 3.55k | static_assert(offsetof(struct picture_priv_buffer_t, priv)==0, |
275 | 3.55k | "misplaced picture_priv_t, destroy won't work"); |
276 | | |
277 | 3.55k | struct picture_priv_buffer_t *privbuf = malloc(sizeof(*privbuf)); |
278 | 3.55k | if (unlikely(privbuf == NULL)) |
279 | 0 | return NULL; |
280 | | |
281 | 3.55k | picture_buffer_t *res = &privbuf->res; |
282 | | |
283 | 3.55k | picture_resource_t pic_res = { |
284 | 3.55k | .p_sys = res, |
285 | 3.55k | .pf_destroy = picture_DestroyFromFormat, |
286 | 3.55k | }; |
287 | | |
288 | 3.55k | picture_priv_t *priv = &privbuf->priv; |
289 | 3.55k | if (!picture_InitPrivate(fmt, priv, &pic_res)) |
290 | 1.44k | goto error; |
291 | | |
292 | 2.10k | picture_t *pic = &priv->picture; |
293 | 2.10k | if (pic->i_planes == 0) { |
294 | 0 | pic->p_sys = NULL; // not compatible with picture_DestroyFromFormat |
295 | 0 | return pic; |
296 | 0 | } |
297 | | |
298 | | /* Calculate how big the new image should be */ |
299 | 2.10k | assert(pic->i_planes <= PICTURE_PLANE_MAX); |
300 | 2.10k | size_t plane_sizes[PICTURE_PLANE_MAX]; |
301 | 2.10k | size_t pic_size = 0; |
302 | | |
303 | 5.47k | for (int i = 0; i < pic->i_planes; i++) |
304 | 3.45k | { |
305 | 3.45k | const plane_t *p = &pic->p[i]; |
306 | | |
307 | 3.45k | if (unlikely(ckd_mul(&plane_sizes[i], p->i_pitch, p->i_lines)) |
308 | 3.45k | || unlikely(ckd_add(&pic_size, pic_size, plane_sizes[i]))) |
309 | 86 | goto error; |
310 | 3.45k | } |
311 | | |
312 | 2.02k | if (unlikely(pic_size >= PICTURE_SW_SIZE_MAX)) |
313 | 1.23k | goto error; |
314 | | |
315 | 787 | unsigned char *buf = picture_Allocate(&res->fd, pic_size); |
316 | 787 | if (unlikely(buf == NULL)) |
317 | 0 | goto error; |
318 | | |
319 | 787 | res->base = buf; |
320 | 787 | res->size = pic_size; |
321 | 787 | res->offset = 0; |
322 | | |
323 | | /* Fill the p_pixels field for each plane */ |
324 | 2.42k | for (int i = 0; i < pic->i_planes; i++) |
325 | 1.63k | { |
326 | 1.63k | pic->p[i].p_pixels = buf; |
327 | 1.63k | buf += plane_sizes[i]; |
328 | 1.63k | } |
329 | | |
330 | 787 | return pic; |
331 | 2.76k | error: |
332 | 2.76k | video_format_Clean(&priv->picture.format); |
333 | 2.76k | free(privbuf); |
334 | 2.76k | return NULL; |
335 | 787 | } |
336 | | |
337 | | picture_t *picture_New( vlc_fourcc_t i_chroma, int i_width, int i_height, int i_sar_num, int i_sar_den ) |
338 | 0 | { |
339 | 0 | video_format_t fmt; |
340 | |
|
341 | 0 | video_format_Init( &fmt, 0 ); |
342 | 0 | video_format_Setup( &fmt, i_chroma, i_width, i_height, |
343 | 0 | i_width, i_height, i_sar_num, i_sar_den ); |
344 | |
|
345 | 0 | return picture_NewFromFormat( &fmt ); |
346 | 0 | } |
347 | | |
348 | | /***************************************************************************** |
349 | | * |
350 | | *****************************************************************************/ |
351 | | |
352 | | void picture_Destroy(picture_t *picture) |
353 | 787 | { |
354 | 787 | assert(vlc_atomic_rc_get(&picture->refs) == 0); |
355 | | |
356 | 787 | PictureDestroyContext(picture); |
357 | | |
358 | 787 | picture_priv_t *priv = container_of(picture, picture_priv_t, picture); |
359 | 787 | assert(priv->gc.destroy != NULL); |
360 | 787 | priv->gc.destroy(picture); |
361 | 787 | vlc_ancillary_array_Clear(&priv->ancillaries); |
362 | 787 | video_format_Clean(&picture->format); |
363 | 787 | free(priv); |
364 | 787 | } |
365 | | |
366 | | /***************************************************************************** |
367 | | * |
368 | | *****************************************************************************/ |
369 | | void plane_CopyPixels( plane_t *p_dst, const plane_t *p_src ) |
370 | 0 | { |
371 | 0 | const unsigned i_width = __MIN( p_dst->i_visible_pitch, |
372 | 0 | p_src->i_visible_pitch ); |
373 | 0 | const unsigned i_height = __MIN( p_dst->i_visible_lines, |
374 | 0 | p_src->i_visible_lines ); |
375 | | |
376 | | /* The 2x visible pitch check does two things: |
377 | | 1) Makes field plane_t's work correctly (see the deinterlacer module) |
378 | | 2) Moves less data if the pitch and visible pitch differ much. |
379 | | */ |
380 | 0 | if( p_src->i_pitch == p_dst->i_pitch && |
381 | 0 | p_src->i_pitch < 2*p_src->i_visible_pitch ) |
382 | 0 | { |
383 | | /* There are margins, but with the same width : perfect ! */ |
384 | 0 | memcpy( p_dst->p_pixels, p_src->p_pixels, |
385 | 0 | p_src->i_pitch * i_height ); |
386 | 0 | } |
387 | 0 | else |
388 | 0 | { |
389 | | /* We need to proceed line by line */ |
390 | 0 | uint8_t *p_in = p_src->p_pixels; |
391 | 0 | uint8_t *p_out = p_dst->p_pixels; |
392 | |
|
393 | 0 | assert( p_in ); |
394 | 0 | assert( p_out ); |
395 | | |
396 | 0 | for( int i_line = i_height; i_line--; ) |
397 | 0 | { |
398 | 0 | memcpy( p_out, p_in, i_width ); |
399 | 0 | p_in += p_src->i_pitch; |
400 | 0 | p_out += p_dst->i_pitch; |
401 | 0 | } |
402 | 0 | } |
403 | 0 | } |
404 | | |
405 | | void picture_CopyProperties( picture_t *p_dst, const picture_t *p_src ) |
406 | 0 | { |
407 | 0 | p_dst->date = p_src->date; |
408 | 0 | p_dst->b_force = p_src->b_force; |
409 | 0 | p_dst->b_still = p_src->b_still; |
410 | |
|
411 | 0 | p_dst->b_progressive = p_src->b_progressive; |
412 | 0 | p_dst->i_nb_fields = p_src->i_nb_fields; |
413 | 0 | p_dst->b_top_field_first = p_src->b_top_field_first; |
414 | |
|
415 | 0 | const picture_priv_t *src_priv = container_of(p_src, picture_priv_t, picture); |
416 | 0 | picture_priv_t *dst_priv = container_of(p_dst, picture_priv_t, picture); |
417 | 0 | vlc_ancillary_array_Merge(&dst_priv->ancillaries, &src_priv->ancillaries); |
418 | 0 | } |
419 | | |
420 | | void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src ) |
421 | 0 | { |
422 | 0 | for( int i = 0; i < p_src->i_planes ; i++ ) |
423 | 0 | plane_CopyPixels( p_dst->p+i, p_src->p+i ); |
424 | |
|
425 | 0 | assert( p_dst->context == NULL ); |
426 | | |
427 | 0 | if( p_src->context != NULL ) |
428 | 0 | p_dst->context = p_src->context->copy( p_src->context ); |
429 | 0 | } |
430 | | |
431 | | void picture_Copy( picture_t *p_dst, const picture_t *p_src ) |
432 | 0 | { |
433 | 0 | picture_CopyPixels( p_dst, p_src ); |
434 | 0 | picture_CopyProperties( p_dst, p_src ); |
435 | 0 | } |
436 | | |
437 | | static void picture_DestroyClone(picture_t *clone) |
438 | 0 | { |
439 | 0 | picture_priv_t *clone_priv = container_of(clone, picture_priv_t, picture); |
440 | 0 | picture_t *picture = clone_priv->gc.opaque; |
441 | |
|
442 | 0 | picture_Release(picture); |
443 | 0 | } |
444 | | |
445 | | picture_t *picture_InternalClone(picture_t *picture, |
446 | | void (*pf_destroy)(picture_t *), void *opaque) |
447 | 0 | { |
448 | 0 | picture_resource_t res = { |
449 | 0 | .p_sys = picture->p_sys, |
450 | 0 | .pf_destroy = pf_destroy, |
451 | 0 | }; |
452 | |
|
453 | 0 | for (int i = 0; i < picture->i_planes; i++) { |
454 | 0 | res.p[i].p_pixels = picture->p[i].p_pixels; |
455 | 0 | res.p[i].i_lines = picture->p[i].i_lines; |
456 | 0 | res.p[i].i_pitch = picture->p[i].i_pitch; |
457 | 0 | } |
458 | |
|
459 | 0 | picture_t *clone = picture_NewFromResource(&picture->format, &res); |
460 | 0 | if (likely(clone != NULL)) { |
461 | 0 | picture_priv_t *clone_priv = container_of(clone, picture_priv_t, picture); |
462 | 0 | clone_priv->gc.opaque = opaque; |
463 | | |
464 | | /* The picture context is responsible for potentially holding the |
465 | | * video context attached to the picture if needed. */ |
466 | 0 | if (picture->context != NULL) |
467 | 0 | clone->context = picture->context->copy(picture->context); |
468 | |
|
469 | 0 | picture_Hold(picture); |
470 | 0 | } |
471 | |
|
472 | 0 | return clone; |
473 | 0 | } |
474 | | |
475 | | picture_t *picture_Clone(picture_t *picture) |
476 | 0 | { |
477 | 0 | picture_t *clone = picture_InternalClone(picture, picture_DestroyClone, picture); |
478 | 0 | if (clone == NULL) |
479 | 0 | return NULL; |
480 | | |
481 | 0 | const picture_priv_t *priv = container_of(picture, picture_priv_t, picture); |
482 | 0 | picture_priv_t *clone_priv = container_of(clone, picture_priv_t, picture); |
483 | 0 | vlc_ancillary_array_Merge(&clone_priv->ancillaries, &priv->ancillaries); |
484 | 0 | return clone; |
485 | 0 | } |
486 | | |
487 | | int |
488 | | picture_MergeAncillaries(picture_t *pic, const vlc_ancillary_array *src_array) |
489 | 0 | { |
490 | 0 | picture_priv_t *priv = container_of(pic, picture_priv_t, picture); |
491 | 0 | return vlc_ancillary_array_Merge(&priv->ancillaries, src_array); |
492 | 0 | } |
493 | | |
494 | | int |
495 | | picture_MergeAndClearAncillaries(picture_t *pic, vlc_ancillary_array *src_array) |
496 | 0 | { |
497 | 0 | picture_priv_t *priv = container_of(pic, picture_priv_t, picture); |
498 | 0 | return vlc_ancillary_array_MergeAndClear(&priv->ancillaries, src_array); |
499 | 0 | } |
500 | | |
501 | | int |
502 | | picture_AttachAncillary(picture_t *pic, struct vlc_ancillary *ancillary) |
503 | 0 | { |
504 | 0 | picture_priv_t *priv = container_of(pic, picture_priv_t, picture); |
505 | 0 | return vlc_ancillary_array_Insert(&priv->ancillaries, ancillary); |
506 | 0 | } |
507 | | |
508 | | void * |
509 | | picture_AttachNewAncillary(picture_t *pic, vlc_ancillary_id id, size_t size) |
510 | 0 | { |
511 | 0 | void *data = malloc(size); |
512 | 0 | if (!data) |
513 | 0 | return NULL; |
514 | | |
515 | 0 | struct vlc_ancillary *ancillary = vlc_ancillary_Create(data, id); |
516 | 0 | if (!ancillary) { |
517 | 0 | free(data); |
518 | 0 | return NULL; |
519 | 0 | } |
520 | | |
521 | 0 | if (picture_AttachAncillary(pic, ancillary) != 0) { |
522 | 0 | vlc_ancillary_Release(ancillary); |
523 | 0 | return NULL; |
524 | 0 | } |
525 | | |
526 | 0 | vlc_ancillary_Release(ancillary); |
527 | |
|
528 | 0 | return data; |
529 | 0 | } |
530 | | |
531 | | struct vlc_ancillary * |
532 | | picture_GetAncillary(const picture_t *pic, vlc_ancillary_id id) |
533 | 0 | { |
534 | 0 | picture_priv_t *priv = container_of(pic, picture_priv_t, picture); |
535 | 0 | return vlc_ancillary_array_Get(&priv->ancillaries, id); |
536 | 0 | } |
537 | | |
538 | | /***************************************************************************** |
539 | | * |
540 | | *****************************************************************************/ |
541 | | int picture_Export( vlc_object_t *p_obj, |
542 | | block_t **pp_image, |
543 | | video_format_t *p_fmt, |
544 | | picture_t *p_picture, |
545 | | vlc_fourcc_t i_codec, |
546 | | int i_override_width, int i_override_height, |
547 | | bool b_crop ) |
548 | 0 | { |
549 | | /* */ |
550 | 0 | video_format_t fmt_in = p_picture->format; |
551 | 0 | if( fmt_in.i_sar_num <= 0 || fmt_in.i_sar_den <= 0 ) |
552 | 0 | { |
553 | 0 | fmt_in.i_sar_num = |
554 | 0 | fmt_in.i_sar_den = 1; |
555 | 0 | } |
556 | | |
557 | | /* */ |
558 | 0 | video_format_t fmt_out; |
559 | 0 | memset( &fmt_out, 0, sizeof(fmt_out) ); |
560 | 0 | fmt_out.i_sar_num = |
561 | 0 | fmt_out.i_sar_den = 1; |
562 | | |
563 | | /* compute original width/height */ |
564 | 0 | unsigned int i_width, i_height, i_original_width, i_original_height; |
565 | 0 | if( fmt_in.i_visible_width > 0 && fmt_in.i_visible_height > 0 ) |
566 | 0 | { |
567 | 0 | i_width = fmt_in.i_visible_width; |
568 | 0 | i_height = fmt_in.i_visible_height; |
569 | 0 | } |
570 | 0 | else |
571 | 0 | { |
572 | 0 | i_width = fmt_in.i_width; |
573 | 0 | i_height = fmt_in.i_height; |
574 | 0 | } |
575 | 0 | if( fmt_in.i_sar_num >= fmt_in.i_sar_den ) |
576 | 0 | { |
577 | 0 | i_original_width = (int64_t)i_width * fmt_in.i_sar_num / fmt_in.i_sar_den; |
578 | 0 | i_original_height = i_height; |
579 | 0 | } |
580 | 0 | else |
581 | 0 | { |
582 | 0 | i_original_width = i_width; |
583 | 0 | i_original_height = i_height * fmt_in.i_sar_den / fmt_in.i_sar_num; |
584 | 0 | } |
585 | | |
586 | | /* */ |
587 | 0 | if( b_crop && i_override_width > 0 && i_override_height > 0 ) |
588 | 0 | { |
589 | 0 | float f_ar_dest = (float)i_override_width / i_override_height; |
590 | 0 | float f_ar_src = (float)i_width / i_height; |
591 | 0 | unsigned int i_crop_width, i_crop_height; |
592 | 0 | if ( f_ar_dest > f_ar_src ) |
593 | 0 | { |
594 | 0 | i_crop_width = i_width; |
595 | 0 | i_crop_height = (float)i_crop_width / f_ar_dest; |
596 | 0 | } |
597 | 0 | else |
598 | 0 | { |
599 | 0 | i_crop_height = i_height; |
600 | 0 | i_crop_width = (float)i_crop_height * f_ar_dest; |
601 | 0 | } |
602 | 0 | fmt_out.i_width = i_override_width; |
603 | 0 | fmt_out.i_height = i_override_height; |
604 | 0 | fmt_in.i_visible_width = i_crop_width; |
605 | 0 | fmt_in.i_visible_height = i_crop_height; |
606 | 0 | fmt_in.i_x_offset += (i_width - i_crop_width) / 2; |
607 | 0 | fmt_in.i_y_offset += (i_height - i_crop_height) / 2; |
608 | 0 | } |
609 | 0 | else |
610 | 0 | { |
611 | 0 | fmt_out.i_width = ( i_override_width < 0 ) ? |
612 | 0 | i_original_width : (unsigned)i_override_width; |
613 | 0 | fmt_out.i_height = ( i_override_height < 0 ) ? |
614 | 0 | i_original_height : (unsigned)i_override_height; |
615 | 0 | } |
616 | | |
617 | | /* scale if only one direction is provided */ |
618 | 0 | if( fmt_out.i_height == 0 && fmt_out.i_width > 0 ) |
619 | 0 | { |
620 | 0 | fmt_out.i_height = i_height * fmt_out.i_width |
621 | 0 | * fmt_in.i_sar_den / fmt_in.i_width / fmt_in.i_sar_num; |
622 | 0 | } |
623 | 0 | else if( fmt_out.i_width == 0 && fmt_out.i_height > 0 ) |
624 | 0 | { |
625 | 0 | fmt_out.i_width = i_width * fmt_out.i_height |
626 | 0 | * fmt_in.i_sar_num / fmt_in.i_height / fmt_in.i_sar_den; |
627 | 0 | } |
628 | 0 | fmt_out.i_visible_width = fmt_out.i_width; |
629 | 0 | fmt_out.i_visible_height = fmt_out.i_height; |
630 | |
|
631 | 0 | image_handler_t *p_image = image_HandlerCreate( p_obj ); |
632 | 0 | if( !p_image ) |
633 | 0 | return VLC_ENOMEM; |
634 | | |
635 | 0 | vlc_tick_t date = p_picture->date; |
636 | 0 | block_t *p_block = image_Write( p_image, p_picture, &fmt_in, i_codec, &fmt_out ); |
637 | |
|
638 | 0 | image_HandlerDelete( p_image ); |
639 | |
|
640 | 0 | if( !p_block ) |
641 | 0 | return VLC_EGENERIC; |
642 | | |
643 | 0 | p_block->i_pts = |
644 | 0 | p_block->i_dts = date; |
645 | |
|
646 | 0 | if( p_fmt ) |
647 | 0 | *p_fmt = fmt_out; |
648 | 0 | *pp_image = p_block; |
649 | |
|
650 | 0 | return VLC_SUCCESS; |
651 | 0 | } |