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/decoder.h>
30 #include "vlc_filter.h"
33 #ifdef HAVE_FFMPEG_AVCODEC_H
34 # 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 );
46 static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic );
48 /*****************************************************************************
49 * filter_sys_t : filter descriptor
50 *****************************************************************************/
55 vlc_bool_t b_resize_first;
56 vlc_bool_t b_enable_croppadd;
59 int i_src_ffmpeg_chroma;
61 int i_dst_ffmpeg_chroma;
64 ImgReSampleContext *p_rsc;
68 /*****************************************************************************
69 * OpenFilterEx: common code to OpenFilter and OpenCropPadd
70 *****************************************************************************/
71 static int OpenFilterEx( vlc_object_t *p_this, vlc_bool_t b_enable_croppadd )
73 filter_t *p_filter = (filter_t*)p_this;
75 vlc_bool_t b_convert, b_resize;
77 /* Check if we can handle that formats */
78 if( E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ) < 0 ||
79 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma ) < 0 )
85 p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
86 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
88 if ( b_enable_croppadd )
90 b_resize = b_resize ||
91 p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ||
92 p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ||
93 p_filter->fmt_in.video.i_x_offset != 0 ||
94 p_filter->fmt_in.video.i_y_offset != 0 ||
95 p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
96 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
97 p_filter->fmt_out.video.i_x_offset != 0 ||
98 p_filter->fmt_out.video.i_y_offset != 0;
102 p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
104 if( !b_resize && !b_convert )
110 /* Allocate the memory needed to store the decoder's structure */
111 if( ( p_filter->p_sys = p_sys =
112 (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
114 msg_Err( p_filter, "out of memory" );
120 p_sys->b_enable_croppadd = b_enable_croppadd;
121 p_sys->i_src_ffmpeg_chroma =
122 E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma );
123 p_sys->i_dst_ffmpeg_chroma =
124 E_(GetFfmpegChroma)( p_filter->fmt_out.video.i_chroma );
125 p_filter->pf_video_filter = Process;
126 es_format_Init( &p_sys->fmt_in, 0, 0 );
127 es_format_Init( &p_sys->fmt_out, 0, 0 );
129 /* Dummy alloc, will be reallocated in CheckInit */
130 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
131 p_filter->fmt_out.video.i_width,
132 p_filter->fmt_out.video.i_height );
134 if( CheckInit( p_filter ) != VLC_SUCCESS )
140 msg_Dbg( p_filter, "input: %ix%i %4.4s -> %ix%i %4.4s",
141 p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
142 (char *)&p_filter->fmt_in.video.i_chroma,
143 p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
144 (char *)&p_filter->fmt_out.video.i_chroma );
146 /* libavcodec needs to be initialized for some chroma conversions */
147 E_(InitLibavcodec)(p_this);
152 /*****************************************************************************
153 * OpenFilter: probe the filter and return score
154 *****************************************************************************/
155 int E_(OpenFilter)( vlc_object_t *p_this )
157 return OpenFilterEx( p_this, VLC_FALSE );
160 /*****************************************************************************
161 * OpenCropPadd: probe the filter and return score
162 *****************************************************************************/
163 int E_(OpenCropPadd)( vlc_object_t *p_this )
165 return OpenFilterEx( p_this, VLC_TRUE );
169 /*****************************************************************************
170 * CloseFilter: clean up the filter
171 *****************************************************************************/
172 void E_(CloseFilter)( vlc_object_t *p_this )
174 filter_t *p_filter = (filter_t*)p_this;
175 filter_sys_t *p_sys = p_filter->p_sys;
177 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
179 avpicture_free( &p_sys->tmp_pic );
184 /*****************************************************************************
185 * CheckInit: Initialise filter when necessary
186 *****************************************************************************/
187 static int CheckInit( filter_t *p_filter )
189 filter_sys_t *p_sys = p_filter->p_sys;
201 b_change= p_filter->fmt_in.video.i_width != p_sys->fmt_in.video.i_width ||
202 p_filter->fmt_in.video.i_height != p_sys->fmt_in.video.i_height ||
203 p_filter->fmt_out.video.i_width != p_sys->fmt_out.video.i_width ||
204 p_filter->fmt_out.video.i_height != p_sys->fmt_out.video.i_height;
206 if ( p_sys->b_enable_croppadd )
208 b_change = b_change ||
209 p_filter->fmt_in.video.i_y_offset != p_sys->fmt_in.video.i_y_offset ||
210 p_filter->fmt_in.video.i_x_offset != p_sys->fmt_in.video.i_x_offset ||
211 p_filter->fmt_in.video.i_visible_width != p_sys->fmt_in.video.i_visible_width ||
212 p_filter->fmt_in.video.i_visible_height != p_sys->fmt_in.video.i_visible_height ||
213 p_filter->fmt_out.video.i_y_offset != p_sys->fmt_out.video.i_y_offset ||
214 p_filter->fmt_out.video.i_x_offset != p_sys->fmt_out.video.i_x_offset ||
215 p_filter->fmt_out.video.i_visible_width != p_sys->fmt_out.video.i_visible_width ||
216 p_filter->fmt_out.video.i_visible_height != p_sys->fmt_out.video.i_visible_height;
221 if( p_sys->p_rsc ) img_resample_close( p_sys->p_rsc );
225 p_filter->fmt_in.video.i_chroma != p_filter->fmt_out.video.i_chroma;
228 p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ||
229 p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height;
231 p_sys->b_resize_first =
232 p_filter->fmt_in.video.i_width * p_filter->fmt_in.video.i_height >
233 p_filter->fmt_out.video.i_width * p_filter->fmt_out.video.i_height;
235 if( p_sys->b_resize &&
236 p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P &&
237 p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P &&
238 p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P &&
239 p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P )
241 msg_Err( p_filter, "img_resample_init only deals with I420" );
244 else if( p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUV420P &&
245 p_sys->i_src_ffmpeg_chroma != PIX_FMT_YUVJ420P )
247 p_sys->b_resize_first = VLC_FALSE;
249 else if( p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUV420P &&
250 p_sys->i_dst_ffmpeg_chroma != PIX_FMT_YUVJ420P )
252 p_sys->b_resize_first = VLC_TRUE;
255 if ( p_sys->b_enable_croppadd )
257 p_sys->b_resize = p_sys->b_resize ||
258 p_filter->fmt_in.video.i_visible_width != p_filter->fmt_in.video.i_width ||
259 p_filter->fmt_in.video.i_visible_height != p_filter->fmt_in.video.i_height ||
260 p_filter->fmt_in.video.i_x_offset != 0 ||
261 p_filter->fmt_in.video.i_y_offset != 0 ||
262 p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
263 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
264 p_filter->fmt_out.video.i_x_offset != 0 ||
265 p_filter->fmt_out.video.i_y_offset != 0;
268 if( p_sys->b_resize )
270 if ( p_sys->b_enable_croppadd )
272 i_croptop=p_filter->fmt_in.video.i_y_offset;
273 i_cropbottom=p_filter->fmt_in.video.i_height
274 - p_filter->fmt_in.video.i_visible_height
275 - p_filter->fmt_in.video.i_y_offset;
276 i_cropleft=p_filter->fmt_in.video.i_x_offset;
277 i_cropright=p_filter->fmt_in.video.i_width
278 - p_filter->fmt_in.video.i_visible_width
279 - p_filter->fmt_in.video.i_x_offset;
282 i_paddtop=p_filter->fmt_out.video.i_y_offset;
283 i_paddbottom=p_filter->fmt_out.video.i_height
284 - p_filter->fmt_out.video.i_visible_height
285 - p_filter->fmt_out.video.i_y_offset;
286 i_paddleft=p_filter->fmt_out.video.i_x_offset;
287 i_paddright=p_filter->fmt_out.video.i_width
288 - p_filter->fmt_out.video.i_visible_width
289 - p_filter->fmt_out.video.i_x_offset;
292 p_sys->p_rsc = img_resample_full_init(
293 p_filter->fmt_out.video.i_width,
294 p_filter->fmt_out.video.i_height,
295 p_filter->fmt_in.video.i_width,
296 p_filter->fmt_in.video.i_height,
297 i_croptop,i_cropbottom,
298 i_cropleft,i_cropright,
299 i_paddtop,i_paddbottom,
300 i_paddleft,i_paddright );
302 msg_Dbg( p_filter, "input: %ix%i -> %ix%i",
303 p_filter->fmt_out.video.i_width,
304 p_filter->fmt_out.video.i_height,
305 p_filter->fmt_in.video.i_width,
306 p_filter->fmt_in.video.i_height);
310 msg_Err( p_filter, "img_resample_init failed" );
315 avpicture_free( &p_sys->tmp_pic );
317 if( p_sys->b_resize_first )
319 /* Resizing then conversion */
320 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_src_ffmpeg_chroma,
321 p_filter->fmt_out.video.i_width,
322 p_filter->fmt_out.video.i_height );
326 /* Conversion then resizing */
327 avpicture_alloc( &p_sys->tmp_pic, p_sys->i_dst_ffmpeg_chroma,
328 p_filter->fmt_in.video.i_width,
329 p_filter->fmt_in.video.i_height );
332 p_sys->fmt_in = p_filter->fmt_in;
333 p_sys->fmt_out = p_filter->fmt_out;
339 /* fill padd code from ffmpeg */
340 static int padcolor[3] = { 16, 128, 128 };
342 /* Expects img to be yuv420 */
343 static void fill_pad_region( AVPicture* img, int height, int width,
344 int padtop, int padbottom, int padleft, int padright, int *color )
349 for ( i = 0; i < 3; i++ )
351 shift = ( i == 0 ) ? 0 : 1;
353 if ( padtop || padleft )
355 memset( img->data[i], color[i], ( ( ( img->linesize[i] * padtop ) +
356 padleft ) >> shift) );
359 if ( padleft || padright )
361 optr = img->data[i] + ( img->linesize[i] * ( padtop >> shift ) ) +
362 ( img->linesize[i] - ( padright >> shift ) );
364 for ( y = 0; y < ( ( height - ( padtop + padbottom ) ) >> shift ); y++ )
366 memset( optr, color[i], ( padleft + padright ) >> shift );
367 optr += img->linesize[i];
373 optr = img->data[i] + ( img->linesize[i] * ( ( height - padbottom ) >> shift ) );
374 memset( optr, color[i], ( ( img->linesize[i] * padbottom ) >> shift ) );
379 /* Workaround, because old libavcodec doesnt know how to padd */
380 static void img_resample_padd( ImgReSampleContext *s, AVPicture *output,
381 const AVPicture *input, int padtop, int padleft )
383 AVPicture nopadd_pic = *output;
385 /* shift out top and left padding for old ffmpeg */
386 nopadd_pic.data[0] += ( nopadd_pic.linesize[0] * padtop + padleft );
387 nopadd_pic.data[1] += ( nopadd_pic.linesize[1] * padtop + padleft ) >> 1;
388 nopadd_pic.data[2] += ( nopadd_pic.linesize[2] * padtop + padleft ) >> 1;
389 img_resample( s, &nopadd_pic, input );
393 /*****************************************************************************
394 * Do the processing here
395 *****************************************************************************/
396 static picture_t *Process( filter_t *p_filter, picture_t *p_pic )
398 filter_sys_t *p_sys = p_filter->p_sys;
399 AVPicture src_pic, dest_pic;
400 AVPicture *p_src, *p_dst;
401 picture_t *p_pic_dst;
404 /* Check if format properties changed */
405 if( CheckInit( p_filter ) != VLC_SUCCESS ) return 0;
407 /* Request output picture */
408 p_pic_dst = p_filter->pf_vout_buffer_new( p_filter );
411 msg_Warn( p_filter, "can't get output picture" );
412 p_pic->pf_release( p_pic );
416 /* Prepare the AVPictures for the conversion */
417 for( i = 0; i < p_pic->i_planes; i++ )
419 src_pic.data[i] = p_pic->p[i].p_pixels;
420 src_pic.linesize[i] = p_pic->p[i].i_pitch;
422 for( i = 0; i < p_pic_dst->i_planes; i++ )
424 dest_pic.data[i] = p_pic_dst->p[i].p_pixels;
425 dest_pic.linesize[i] = p_pic_dst->p[i].i_pitch;
429 if( p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
430 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
433 src_pic.data[1] = p_pic->p[2].p_pixels;
434 src_pic.data[2] = p_pic->p[1].p_pixels;
436 if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','1','2') ||
437 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','V','U','9') )
440 dest_pic.data[1] = p_pic_dst->p[2].p_pixels;
441 dest_pic.data[2] = p_pic_dst->p[1].p_pixels;
443 if( p_sys->i_src_ffmpeg_chroma == PIX_FMT_RGB24 )
444 if( p_filter->fmt_in.video.i_bmask == 0x00ff0000 )
445 p_sys->i_src_ffmpeg_chroma = PIX_FMT_BGR24;
449 if( p_sys->b_resize && p_sys->p_rsc )
452 if( p_sys->b_resize_first )
454 if( p_sys->b_convert ) p_dst = &p_sys->tmp_pic;
456 img_resample( p_sys->p_rsc, p_dst, p_src );
458 if (p_sys->b_enable_croppadd)
460 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
461 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
462 p_filter->fmt_out.video.i_x_offset != 0 ||
463 p_filter->fmt_out.video.i_y_offset != 0)
465 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
466 p_filter->fmt_out.video.i_width,
467 p_filter->fmt_out.video.i_y_offset,
468 p_filter->fmt_out.video.i_height
469 - p_filter->fmt_out.video.i_visible_height
470 - p_filter->fmt_out.video.i_y_offset,
471 p_filter->fmt_out.video.i_x_offset,
472 p_filter->fmt_out.video.i_width
473 - p_filter->fmt_out.video.i_visible_width
474 - p_filter->fmt_out.video.i_x_offset,
483 if( p_sys->b_convert )
485 video_format_t *p_fmt = &p_filter->fmt_out.video;
487 if( p_sys->b_resize && !p_sys->b_resize_first )
489 p_dst = &p_sys->tmp_pic;
490 p_fmt = &p_filter->fmt_in.video;
493 img_convert( p_dst, p_sys->i_dst_ffmpeg_chroma,
494 p_src, p_sys->i_src_ffmpeg_chroma,
495 p_fmt->i_width, p_fmt->i_height );
500 if( p_sys->b_resize && !p_sys->b_resize_first && p_sys->p_rsc )
504 img_resample( p_sys->p_rsc, p_dst, p_src );
506 if (p_sys->b_enable_croppadd)
508 if (p_filter->fmt_out.video.i_visible_width != p_filter->fmt_out.video.i_width ||
509 p_filter->fmt_out.video.i_visible_height != p_filter->fmt_out.video.i_height ||
510 p_filter->fmt_out.video.i_x_offset != 0 ||
511 p_filter->fmt_out.video.i_y_offset != 0)
513 fill_pad_region(p_dst, p_filter->fmt_out.video.i_height,
514 p_filter->fmt_out.video.i_width,
515 p_filter->fmt_out.video.i_y_offset,
516 p_filter->fmt_out.video.i_height
517 - p_filter->fmt_out.video.i_visible_height
518 - p_filter->fmt_out.video.i_y_offset,
519 p_filter->fmt_out.video.i_x_offset,
520 p_filter->fmt_out.video.i_width
521 - p_filter->fmt_out.video.i_visible_width
522 - p_filter->fmt_out.video.i_x_offset,
528 /* Special case for RV32 -> YUVA */
529 if( !p_sys->b_resize &&
530 p_filter->fmt_in.video.i_chroma == VLC_FOURCC('R','V','3','2') &&
531 p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
533 uint8_t *p_src = p_pic->p[0].p_pixels;
534 int i_src_pitch = p_pic->p[0].i_pitch;
535 uint8_t *p_dst = p_pic_dst->p[3].p_pixels;
536 int i_dst_pitch = p_pic_dst->p[3].i_pitch;
539 for( l = 0; l < p_filter->fmt_out.video.i_height; l++ )
541 for( j = 0; j < p_filter->fmt_out.video.i_width; j++ )
543 p_dst[j] = p_src[j*4+3];
545 p_src += i_src_pitch;
546 p_dst += i_dst_pitch;
549 else if( p_filter->fmt_out.video.i_chroma == VLC_FOURCC('Y','U','V','A') )
551 /* Special case for YUVA */
552 memset( p_pic_dst->p[3].p_pixels, 0xFF,
553 p_pic_dst->p[3].i_pitch * p_pic_dst->p[3].i_lines );
556 p_pic_dst->date = p_pic->date;
557 p_pic_dst->b_force = p_pic->b_force;
558 p_pic_dst->i_nb_fields = p_pic->i_nb_fields;
559 p_pic_dst->b_progressive = p_pic->b_progressive;
560 p_pic_dst->b_top_field_first = p_pic->b_top_field_first;
562 p_pic->pf_release( p_pic );
566 /*****************************************************************************
567 * OpenDeinterlace: probe the filter and return score
568 *****************************************************************************/
569 int E_(OpenDeinterlace)( vlc_object_t *p_this )
571 filter_t *p_filter = (filter_t*)p_this;
574 /* Check if we can handle that formats */
575 if( E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma ) < 0 )
580 /* Allocate the memory needed to store the decoder's structure */
581 if( ( p_filter->p_sys = p_sys =
582 (filter_sys_t *)malloc(sizeof(filter_sys_t)) ) == NULL )
584 msg_Err( p_filter, "out of memory" );
589 p_sys->i_src_ffmpeg_chroma =
590 E_(GetFfmpegChroma)( p_filter->fmt_in.video.i_chroma );
591 p_filter->pf_video_filter = Deinterlace;
593 msg_Dbg( p_filter, "deinterlacing" );
595 /* libavcodec needs to be initialized for some chroma conversions */
596 E_(InitLibavcodec)(p_this);
601 /*****************************************************************************
602 * CloseDeinterlace: clean up the filter
603 *****************************************************************************/
604 void E_(CloseDeinterlace)( vlc_object_t *p_this )
606 filter_t *p_filter = (filter_t*)p_this;
607 filter_sys_t *p_sys = p_filter->p_sys;
612 /*****************************************************************************
613 * Do the processing here
614 *****************************************************************************/
615 static picture_t *Deinterlace( filter_t *p_filter, picture_t *p_pic )
617 filter_sys_t *p_sys = p_filter->p_sys;
618 AVPicture src_pic, dest_pic;
619 picture_t *p_pic_dst;
622 /* Request output picture */
623 p_pic_dst = p_filter->pf_vout_buffer_new( p_filter );
626 msg_Warn( p_filter, "can't get output picture" );
630 /* Prepare the AVPictures for the conversion */
631 for( i = 0; i < p_pic->i_planes; i++ )
633 src_pic.data[i] = p_pic->p[i].p_pixels;
634 src_pic.linesize[i] = p_pic->p[i].i_pitch;
636 for( i = 0; i < p_pic_dst->i_planes; i++ )
638 dest_pic.data[i] = p_pic_dst->p[i].p_pixels;
639 dest_pic.linesize[i] = p_pic_dst->p[i].i_pitch;
642 avpicture_deinterlace( &dest_pic, &src_pic, p_sys->i_src_ffmpeg_chroma,
643 p_filter->fmt_in.video.i_width,
644 p_filter->fmt_in.video.i_height );
646 p_pic_dst->date = p_pic->date;
647 p_pic_dst->b_force = p_pic->b_force;
648 p_pic_dst->i_nb_fields = p_pic->i_nb_fields;
649 p_pic_dst->b_progressive = VLC_TRUE;
650 p_pic_dst->b_top_field_first = p_pic->b_top_field_first;
652 p_pic->pf_release( p_pic );
656 #endif /* ( (defined(HAVE_FFMPEG_SWSCALE_H) || defined(HAVE_LIBSWSCALE_TREE)) */