1 /*****************************************************************************
2 * video filter: video filter doing chroma conversion and resizing
3 * using the ffmpeg library
4 *****************************************************************************
5 * Copyright (C) 1999-2001 the VideoLAN team
8 * Authors: Gildas Bazin <gbazin@videolan.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
29 #include <vlc_codec.h>
31 #include <vlc_filter.h>
34 #ifdef HAVE_FFMPEG_AVCODEC_H
35 # include <ffmpeg/avcodec.h>
42 #if !defined(HAVE_FFMPEG_SWSCALE_H) && !defined(HAVE_LIBSWSCALE_TREE)
43 void E_(InitLibavcodec) ( vlc_object_t *p_object );
44 static int CheckInit( filter_t *p_filter );
45 static picture_t *Process( filter_t *p_filter, picture_t *p_pic );
47 /*****************************************************************************
48 * filter_sys_t : filter descriptor
49 *****************************************************************************/
54 vlc_bool_t b_resize_first;
55 vlc_bool_t b_enable_croppadd;
58 int i_src_ffmpeg_chroma;
60 int i_dst_ffmpeg_chroma;
64 ImgReSampleContext *p_rsc;
67 /*****************************************************************************
68 * OpenFilterEx: common code to OpenFilter and OpenCropPadd
69 *****************************************************************************/
70 static int OpenFilterEx( vlc_object_t *p_this, vlc_bool_t b_enable_croppadd )
72 filter_t *p_filter = (filter_t*)p_this;
74 vlc_bool_t b_convert, b_resize;
76 /* Check if we can handle that formats */
77 if( E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ) < 0 ||
78 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma ) < 0 )
84 ( p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) ||
85 ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height );
87 if ( b_enable_croppadd )
89 b_resize = b_resize ||
90 ( p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ) ||
91 ( p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ) ||
92 ( p_filter->fmt_in.video.i_x_offset != 0 ) ||
93 ( p_filter->fmt_in.video.i_y_offset != 0 ) ||
94 ( p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ) ||
95 ( p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ) ||
96 ( p_filter->fmt_out.video.i_x_offset != 0 ) ||
97 ( p_filter->fmt_out.video.i_y_offset != 0 );
101 ( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma );
103 if( !b_resize && !b_convert )
109 /* Allocate the memory needed to store the decoder's structure */
110 if( ( p_filter->p_sys = p_sys =
111 (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
113 msg_Err( p_filter, "out of memory" );
119 p_sys->b_enable_croppadd = b_enable_croppadd;
120 p_sys->i_src_ffmpeg_chroma =
121 E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma );
122 p_sys->i_dst_ffmpeg_chroma =
123 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma );
124 p_filter->pf_video_filter = Process;
125 es_format_Init( &p_sys->fmt_in, 0, 0 );
126 es_format_Init( &p_sys->fmt_out, 0, 0 );
128 /* Dummy alloc, will be reallocated in CheckInit */
129 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
130 p_filter->fmt_out.video.i_width,
131 p_filter->fmt_out.video.i_height );
133 if( CheckInit( p_filter ) != VLC_SUCCESS )
139 msg_Dbg( p_filter, "input: %ix%i %4.4s -> %ix%i %4.4s",
140 p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
141 (char *)&p_filter->fmt_in.video.i_chroma,
142 p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
143 (char *)&p_filter->fmt_out.video.i_chroma );
145 /* libavcodec needs to be initialized for some chroma conversions */
146 E_(InitLibavcodec)(p_this);
151 /*****************************************************************************
152 * OpenFilter: probe the filter and return score
153 *****************************************************************************/
154 int E_(OpenFilter)( vlc_object_t *p_this )
156 return OpenFilterEx( p_this, VLC_FALSE );
159 /*****************************************************************************
160 * OpenCropPadd: probe the filter and return score
161 *****************************************************************************/
162 int E_(OpenCropPadd)( vlc_object_t *p_this )
164 return OpenFilterEx( p_this, VLC_TRUE );
168 /*****************************************************************************
169 * CloseFilter: clean up the filter
170 *****************************************************************************/
171 void E_(CloseFilter)( vlc_object_t *p_this )
173 filter_t *p_filter = (filter_t*)p_this;
174 filter_sys_t *p_sys = p_filter->p_sys;
176 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
177 avpicture_free( &p_sys->tmp_pic );
182 /*****************************************************************************
183 * CheckInit: Initialise filter when necessary
184 *****************************************************************************/
185 static int CheckInit( filter_t *p_filter )
187 filter_sys_t *p_sys = p_filter->p_sys;
198 b_change = ( p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width ) ||
199 ( p_filter->fmt_in.video.i_height != p_sys->fmt_in.video.i_height ) ||
200 ( p_filter->fmt_out.video.i_width != p_sys->fmt_out.video.i_width ) ||
201 ( p_filter->fmt_out.video.i_height != p_sys->fmt_out.video.i_height );
203 if ( p_sys->b_enable_croppadd )
205 b_change = b_change ||
206 ( p_filter->fmt_in.video.i_y_offset != p_sys->fmt_in.video.i_y_offset ) ||
207 ( p_filter->fmt_in.video.i_x_offset != p_sys->fmt_in.video.i_x_offset ) ||
208 ( p_filter->fmt_in.video.i_visible_width != p_sys->fmt_in.video.i_visible_width ) ||
209 ( p_filter->fmt_in.video.i_visible_height != p_sys->fmt_in.video.i_visible_height ) ||
210 ( p_filter->fmt_out.video.i_y_offset != p_sys->fmt_out.video.i_y_offset ) ||
211 ( p_filter->fmt_out.video.i_x_offset != p_sys->fmt_out.video.i_x_offset ) ||
212 ( p_filter->fmt_out.video.i_visible_width != p_sys->fmt_out.video.i_visible_width ) ||
213 ( p_filter->fmt_out.video.i_visible_height != p_sys->fmt_out.video.i_visible_height );
218 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
222 ( p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma );
225 ( p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) ||
226 ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height );
228 p_sys->b_resize_first =
229 ( p_filter->fmt_in.video.i_width * p_filter->fmt_in.video.i_height ) >
230 ( p_filter->fmt_out.video.i_width * p_filter->fmt_out.video.i_height );
232 if( p_sys->b_resize &&
233 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
234 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P ) &&
235 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
236 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
238 msg_Err( p_filter, "img_resample_init only deals with I420" );
241 else if( ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
242 ( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
244 p_sys->b_resize_first = VLC_FALSE;
246 else if( ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P ) &&
247 ( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P ) )
249 p_sys->b_resize_first = VLC_TRUE;
252 if ( p_sys->b_enable_croppadd )
254 p_sys->b_resize = p_sys->b_resize ||
255 ( p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ) ||
256 ( p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ) ||
257 ( p_filter->fmt_in.video.i_x_offset != 0 ) ||
258 ( p_filter->fmt_in.video.i_y_offset != 0 ) ||
259 ( p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ) ||
260 ( p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ) ||
261 ( p_filter->fmt_out.video.i_x_offset != 0 ) ||
262 ( p_filter->fmt_out.video.i_y_offset != 0 );
265 if( p_sys->b_resize )
267 if ( p_sys->b_enable_croppadd )
269 i_croptop = p_filter->fmt_in.video.i_y_offset;
270 i_cropbottom = p_filter->fmt_in.video.i_height
271 - p_filter->fmt_in.video.i_visible_height
272 - p_filter->fmt_in.video.i_y_offset;
273 i_cropleft = p_filter->fmt_in.video.i_x_offset;
274 i_cropright = p_filter->fmt_in.video.i_width
275 - p_filter->fmt_in.video.i_visible_width
276 - p_filter->fmt_in.video.i_x_offset;
279 i_paddtop = p_filter->fmt_out.video.i_y_offset;
280 i_paddbottom = p_filter->fmt_out.video.i_height
281 - p_filter->fmt_out.video.i_visible_height
282 - p_filter->fmt_out.video.i_y_offset;
283 i_paddleft = p_filter->fmt_out.video.i_x_offset;
284 i_paddright = p_filter->fmt_out.video.i_width
285 - p_filter->fmt_out.video.i_visible_width
286 - p_filter->fmt_out.video.i_x_offset;
289 p_sys->p_rsc = img_resample_full_init(
290 p_filter->fmt_out.video.i_width,
291 p_filter->fmt_out.video.i_height,
292 p_filter->fmt_in.video.i_width,
293 p_filter->fmt_in.video.i_height,
294 i_croptop,i_cropbottom,
295 i_cropleft,i_cropright,
296 i_paddtop,i_paddbottom,
297 i_paddleft,i_paddright );
301 msg_Err( p_filter, "img_resample_init failed" );
305 msg_Dbg( p_filter, "input: %ix%i -> %ix%i",
306 p_filter->fmt_out.video.i_width,
307 p_filter->fmt_out.video.i_height,
308 p_filter->fmt_in.video.i_width,
309 p_filter->fmt_in.video.i_height);
313 avpicture_free( &p_sys->tmp_pic );
315 if( p_sys->b_resize_first )
317 /* Resizing then conversion */
318 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
319 p_filter->fmt_out.video.i_width,
320 p_filter->fmt_out.video.i_height );
324 /* Conversion then resizing */
325 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_dst_ffmpeg_chroma,
326 p_filter->fmt_in.video.i_width,
327 p_filter->fmt_in.video.i_height );
330 p_sys->fmt_in = p_filter->fmt_in;
331 p_sys->fmt_out = p_filter->fmt_out;
337 /* fill padd code from ffmpeg */
338 static int padcolor[3] = { 16, 128, 128 };
340 /* Expects img to be yuv420 */
341 static void fill_pad_region( AVPicture* img, int height, int width,
342 int padtop, int padbottom, int padleft, int padright, int *color )
347 for ( i = 0; i < 3; i++ )
349 shift = ( i == 0 ) ? 0 : 1;
351 if ( padtop || padleft )
353 memset( img->data[i], color[i], ( ( ( img->linesize[i] * padtop ) +
354 padleft ) >> shift) );
357 if ( padleft || padright )
359 optr = img->data[i] + ( img->linesize[i] * ( padtop >> shift ) ) +
360 ( img->linesize[i] - ( padright >> shift ) );
362 for ( y = 0; y < ( ( height - ( padtop + padbottom ) ) >> shift ); y++ )
364 memset( optr, color[i], ( padleft + padright ) >> shift );
365 optr += img->linesize[i];
371 optr = img->data[i] + ( img->linesize[i] * ( ( height - padbottom ) >> shift ) );
372 memset( optr, color[i], ( ( img->linesize[i] * padbottom ) >> shift ) );
377 /* Workaround, because old libavcodec doesnt know how to padd */
378 static void img_resample_padd( ImgReSampleContext *s, AVPicture *output,
379 const AVPicture *input, int padtop, int padleft )
381 AVPicture nopadd_pic = *output;
383 /* shift out top and left padding for old ffmpeg */
384 nopadd_pic.data[0] += ( nopadd_pic.linesize[0] * padtop + padleft );
385 nopadd_pic.data[1] += ( nopadd_pic.linesize[1] * padtop + padleft ) >> 1;
386 nopadd_pic.data[2] += ( nopadd_pic.linesize[2] * padtop + padleft ) >> 1;
387 img_resample( s, &nopadd_pic, input );
391 /*****************************************************************************
392 * Do the processing here
393 *****************************************************************************/
394 static picture_t *Process( filter_t *p_filter, picture_t *p_pic )
396 filter_sys_t *p_sys = p_filter->p_sys;
397 AVPicture src_pic, dest_pic;
398 AVPicture *p_src, *p_dst;
399 picture_t *p_pic_dst;
402 /* Check if format properties changed */
403 if( CheckInit( p_filter ) != VLC_SUCCESS ) return 0;
405 /* Request output picture */
406 p_pic_dst = p_filter->pf_vout_buffer_new( p_filter );
409 msg_Warn( p_filter, "can't get output picture" );
410 p_pic->pf_release( p_pic );
414 /* Prepare the AVPictures for the conversion */
415 for( i = 0; i < p_pic->i_planes; i++ )
417 src_pic.data[i] = p_pic->p[i].p_pixels;
418 src_pic.linesize[i] = p_pic->p[i].i_pitch;
420 for( i = 0; i < p_pic_dst->i_planes; i++ )
422 dest_pic.data[i] = p_pic_dst->p[i].p_pixels;
423 dest_pic.linesize[i] = p_pic_dst->p[i].i_pitch;
427 if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
428 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
431 src_pic.data[1] = p_pic->p[2].p_pixels;
432 src_pic.data[2] = p_pic->p[1].p_pixels;
434 if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
435 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
438 dest_pic.data[1] = p_pic_dst->p[2].p_pixels;
439 dest_pic.data[2] = p_pic_dst->p[1].p_pixels;
441 if( p_sys->i_src_ffmpeg_chroma == PIX_FMT_RGB24 )
442 if( p_filter->fmt_in.video.i_bmask == 0x00ff0000 )
443 p_sys->i_src_ffmpeg_chroma = PIX_FMT_BGR24;
447 if( p_sys->b_resize && p_sys->p_rsc )
450 if( p_sys->b_resize_first )
452 if( p_sys->b_convert ) p_dst = &p_sys->tmp_pic;
454 img_resample( p_sys->p_rsc, p_dst, p_src );
456 if (p_sys->b_enable_croppadd)
458 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
459 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
460 p_filter->fmt_out.video.i_x_offset != 0 ||
461 p_filter->fmt_out.video.i_y_offset != 0)
463 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
464 p_filter->fmt_out.video.i_width,
465 p_filter->fmt_out.video.i_y_offset,
466 p_filter->fmt_out.video.i_height
467 - p_filter->fmt_out.video.i_visible_height
468 - p_filter->fmt_out.video.i_y_offset,
469 p_filter->fmt_out.video.i_x_offset,
470 p_filter->fmt_out.video.i_width
471 - p_filter->fmt_out.video.i_visible_width
472 - p_filter->fmt_out.video.i_x_offset,
481 if( p_sys->b_convert )
483 video_format_t *p_fmt = &p_filter->fmt_out.video;
485 if( p_sys->b_resize && !p_sys->b_resize_first )
487 p_dst = &p_sys->tmp_pic;
488 p_fmt = &p_filter->fmt_in.video;
491 img_convert( p_dst, p_sys->i_dst_ffmpeg_chroma,
492 p_src, p_sys->i_src_ffmpeg_chroma,
493 p_fmt->i_width, p_fmt->i_height );
498 if( p_sys->b_resize && !p_sys->b_resize_first && p_sys->p_rsc )
502 img_resample( p_sys->p_rsc, p_dst, p_src );
504 if (p_sys->b_enable_croppadd)
506 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
507 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
508 p_filter->fmt_out.video.i_x_offset != 0 ||
509 p_filter->fmt_out.video.i_y_offset != 0)
511 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
512 p_filter->fmt_out.video.i_width,
513 p_filter->fmt_out.video.i_y_offset,
514 p_filter->fmt_out.video.i_height
515 - p_filter->fmt_out.video.i_visible_height
516 - p_filter->fmt_out.video.i_y_offset,
517 p_filter->fmt_out.video.i_x_offset,
518 p_filter->fmt_out.video.i_width
519 - p_filter->fmt_out.video.i_visible_width
520 - p_filter->fmt_out.video.i_x_offset,
526 /* Special case for RV32 -> YUVA */
527 if( !p_sys->b_resize &&
528 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('R','V','3','2') &&
529 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
531 uint8_t *p_src = p_pic->p[0].p_pixels;
532 int i_src_pitch = p_pic->p[0].i_pitch;
533 uint8_t *p_dst = p_pic_dst->p[3].p_pixels;
534 int i_dst_pitch = p_pic_dst->p[3].i_pitch;
537 for( l = 0; l < p_filter->fmt_out.video.i_height; l++ )
539 for( j = 0; j < p_filter->fmt_out.video.i_width; j++ )
541 p_dst[j] = p_src[j*4+3];
543 p_src += i_src_pitch;
544 p_dst += i_dst_pitch;
547 else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
549 /* Special case for YUVA */
550 memset( p_pic_dst->p[3].p_pixels, 0xFF,
551 p_pic_dst->p[3].i_pitch * p_pic_dst->p[3].i_lines );
554 p_pic_dst->date = p_pic->date;
555 p_pic_dst->b_force = p_pic->b_force;
556 p_pic_dst->i_nb_fields = p_pic->i_nb_fields;
557 p_pic_dst->b_progressive = p_pic->b_progressive;
558 p_pic_dst->b_top_field_first = p_pic->b_top_field_first;
560 p_pic->pf_release( p_pic );
563 #endif /* ( (defined(HAVE_FFMPEG_SWSCALE_H) || defined(HAVE_LIBSWSCALE_TREE)) */