]> git.sesse.net Git - vlc/blob - plugins/filter/deinterlace.c
* ./plugins/chroma/i420_rgb16.c: fix for skewed display in software RV32
[vlc] / plugins / filter / deinterlace.c
1 /*****************************************************************************
2  * deinterlace.c : deinterlacer plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: deinterlace.c,v 1.12 2002/05/28 22:49:25 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <videolan/vlc.h>
32
33 #include "video.h"
34 #include "video_output.h"
35
36 #include "filter_common.h"
37
38 #define DEINTERLACE_MODE_BOB     1
39 #define DEINTERLACE_MODE_BLEND   2
40
41 /*****************************************************************************
42  * Capabilities defined in the other files.
43  *****************************************************************************/
44 static void vout_getfunctions( function_list_t * p_function_list );
45
46 static void *memblend( void *, const void *, const void *, size_t );
47
48 /*****************************************************************************
49  * Build configuration tree.
50  *****************************************************************************/
51 MODULE_CONFIG_START
52 ADD_CATEGORY_HINT( N_("Miscellaneous"), NULL )
53 ADD_STRING  ( "deinterlace-mode", "bob", NULL, N_("Deinterlace mode"),
54               N_("one of 'bob' and 'blend'") )
55 MODULE_CONFIG_STOP
56
57 MODULE_INIT_START
58     SET_DESCRIPTION( _("deinterlacing module") )
59     /* Capability score set to 0 because we don't want to be spawned
60      * as a video output unless explicitly requested to */
61     ADD_CAPABILITY( VOUT, 0 )
62     ADD_SHORTCUT( "deinterlace" )
63 MODULE_INIT_STOP
64
65 MODULE_ACTIVATE_START
66     vout_getfunctions( &p_module->p_functions->vout );
67 MODULE_ACTIVATE_STOP
68
69 MODULE_DEACTIVATE_START
70 MODULE_DEACTIVATE_STOP
71
72 /*****************************************************************************
73  * vout_sys_t: Deinterlace video output method descriptor
74  *****************************************************************************
75  * This structure is part of the video output thread descriptor.
76  * It describes the Deinterlace specific properties of an output thread.
77  *****************************************************************************/
78 typedef struct vout_sys_s
79 {
80     int i_mode;
81     struct vout_thread_s *p_vout;
82     mtime_t last_date;
83
84 } vout_sys_t;
85
86 /*****************************************************************************
87  * Local prototypes
88  *****************************************************************************/
89 static int  vout_Create    ( struct vout_thread_s * );
90 static int  vout_Init      ( struct vout_thread_s * );
91 static void vout_End       ( struct vout_thread_s * );
92 static void vout_Destroy   ( struct vout_thread_s * );
93 static int  vout_Manage    ( struct vout_thread_s * );
94 static void vout_Render    ( struct vout_thread_s *, struct picture_s * );
95 static void vout_Display   ( struct vout_thread_s *, struct picture_s * );
96
97 /*****************************************************************************
98  * Functions exported as capabilities. They are declared as static so that
99  * we don't pollute the namespace too much.
100  *****************************************************************************/
101 static void vout_getfunctions( function_list_t * p_function_list )
102 {
103     p_function_list->functions.vout.pf_create     = vout_Create;
104     p_function_list->functions.vout.pf_init       = vout_Init;
105     p_function_list->functions.vout.pf_end        = vout_End;
106     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
107     p_function_list->functions.vout.pf_manage     = vout_Manage;
108     p_function_list->functions.vout.pf_render     = vout_Render;
109     p_function_list->functions.vout.pf_display    = vout_Display;
110 }
111
112 /*****************************************************************************
113  * vout_Create: allocates Deinterlace video thread output method
114  *****************************************************************************
115  * This function allocates and initializes a Deinterlace vout method.
116  *****************************************************************************/
117 static int vout_Create( vout_thread_t *p_vout )
118 {
119     char *psz_method;
120
121     /* Allocate structure */
122     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
123     if( p_vout->p_sys == NULL )
124     {
125         intf_ErrMsg("error: %s", strerror(ENOMEM) );
126         return( 1 );
127     }
128
129     /* Look what method was requested */
130     psz_method = config_GetPszVariable( "deinterlace-mode" );
131
132     if( psz_method == NULL )
133     {
134         intf_ErrMsg( "vout error: configuration variable %s empty",
135                      "deinterlace-mode" );
136         intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
137                      "using deinterlace:bob" );
138         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
139     }
140     else
141     {
142         if( !strcmp( psz_method, "bob" ) )
143         {
144             p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
145         }
146         else if( !strcmp( psz_method, "blend" ) )
147         {
148             p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
149         }
150         else
151         {
152             intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
153                          "using deinterlace:bob" );
154             p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
155         }
156     }
157
158     free( psz_method );
159
160     return( 0 );
161 }
162
163 /*****************************************************************************
164  * vout_Init: initialize Deinterlace video thread output method
165  *****************************************************************************/
166 static int vout_Init( vout_thread_t *p_vout )
167 {
168     int i_index;
169     char *psz_filter;
170     picture_t *p_pic;
171     
172     I_OUTPUTPICTURES = 0;
173
174     /* Initialize the output structure, full of directbuffers since we want
175      * the decoder to output directly to our structures. */
176     switch( p_vout->render.i_chroma )
177     {
178         case FOURCC_I420:
179         case FOURCC_IYUV:
180         case FOURCC_YV12:
181         case FOURCC_I422:
182             p_vout->output.i_chroma = p_vout->render.i_chroma;
183             p_vout->output.i_width  = p_vout->render.i_width;
184             p_vout->output.i_height = p_vout->render.i_height;
185             p_vout->output.i_aspect = p_vout->render.i_aspect;
186             break;
187
188         default:
189             return( 0 ); /* unknown chroma */
190             break;
191     }
192
193     /* Try to open the real video output, with half the height our images */
194     psz_filter = config_GetPszVariable( "filter" );
195     config_PutPszVariable( "filter", NULL );
196
197     intf_WarnMsg( 1, "filter: spawning the real video output" );
198
199     switch( p_vout->render.i_chroma )
200     {
201     case FOURCC_I420:
202     case FOURCC_IYUV:
203     case FOURCC_YV12:
204         switch( p_vout->p_sys->i_mode )
205         {
206         case DEINTERLACE_MODE_BOB:
207             p_vout->p_sys->p_vout =
208                 vout_CreateThread( NULL,
209                        p_vout->output.i_width, p_vout->output.i_height / 2,
210                        p_vout->output.i_chroma, p_vout->output.i_aspect );
211             break;
212
213         case DEINTERLACE_MODE_BLEND:
214             p_vout->p_sys->p_vout =
215                 vout_CreateThread( NULL,
216                        p_vout->output.i_width, p_vout->output.i_height,
217                        p_vout->output.i_chroma, p_vout->output.i_aspect );
218             break;
219         }
220         break;
221
222     case FOURCC_I422:
223         p_vout->p_sys->p_vout =
224             vout_CreateThread( NULL,
225                        p_vout->output.i_width, p_vout->output.i_height,
226                        FOURCC_I420, p_vout->output.i_aspect );
227         break;
228
229     default:
230         break;
231     }
232
233     config_PutPszVariable( "filter", psz_filter );
234     if( psz_filter ) free( psz_filter );
235
236     /* Everything failed */
237     if( p_vout->p_sys->p_vout == NULL )
238     {
239         intf_ErrMsg( "filter error: can't open vout, aborting" );
240
241         return( 0 );
242     }
243  
244     p_vout->p_sys->last_date = 0;
245
246     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
247
248     return( 0 );
249 }
250
251 /*****************************************************************************
252  * vout_End: terminate Deinterlace video thread output method
253  *****************************************************************************/
254 static void vout_End( vout_thread_t *p_vout )
255 {
256     int i_index;
257
258     /* Free the fake output buffers we allocated */
259     for( i_index = I_OUTPUTPICTURES ; i_index ; )
260     {
261         i_index--;
262         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
263     }
264 }
265
266 /*****************************************************************************
267  * vout_Destroy: destroy Deinterlace video thread output method
268  *****************************************************************************
269  * Terminate an output method created by DeinterlaceCreateOutputMethod
270  *****************************************************************************/
271 static void vout_Destroy( vout_thread_t *p_vout )
272 {
273     vout_DestroyThread( p_vout->p_sys->p_vout, NULL );
274
275     free( p_vout->p_sys );
276 }
277
278 /*****************************************************************************
279  * vout_Manage: handle Deinterlace events
280  *****************************************************************************
281  * This function should be called regularly by video output thread. It manages
282  * console events. It returns a non null value on error.
283  *****************************************************************************/
284 static int vout_Manage( vout_thread_t *p_vout )
285 {
286     return( 0 );
287 }
288
289 /*****************************************************************************
290  * vout_Render: displays previously rendered output
291  *****************************************************************************
292  * This function send the currently rendered image to Deinterlace image,
293  * waits until it is displayed and switch the two rendering buffers, preparing
294  * next frame.
295  *****************************************************************************/
296 static void vout_Render ( vout_thread_t *p_vout, picture_t *p_pic )
297 {
298     picture_t *p_outpic;
299     int i_plane, i_field;
300     /* 20ms is a bit arbitrary, but it's only for the first image we get */
301     mtime_t new_date = p_vout->p_sys->last_date
302                        ? ( 3 * p_pic->date - p_vout->p_sys->last_date ) / 2
303                        : p_pic->date + 20000;
304
305     p_vout->p_sys->last_date = p_pic->date;
306
307     for( i_field = 0 ; i_field < 2 ; i_field++ )
308     {
309         /* Get a structure from the video_output. */
310         while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
311                                                 0, 0, 0 ) )
312                   == NULL )
313         {
314             if( p_vout->b_die || p_vout->b_error )
315             {
316                 return;
317             }
318             msleep( VOUT_OUTMEM_SLEEP );
319         }   
320
321         vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
322                           p_pic->date + i_field ? new_date : p_pic->date );
323
324         /* Copy image and skip lines */
325         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
326         {
327             u8 *p_in, *p_out_end, *p_out;
328             int i_increment;
329
330             p_in = p_pic->p[i_plane].p_pixels
331                        + i_field * p_pic->p[i_plane].i_pitch;
332
333             p_out = p_outpic->p[i_plane].p_pixels;
334             p_out_end = p_out + p_outpic->p[i_plane].i_pitch
335                                  * p_outpic->p[i_plane].i_lines;
336
337             switch( p_vout->render.i_chroma )
338             {
339             case FOURCC_I420:
340             case FOURCC_IYUV:
341             case FOURCC_YV12:
342
343                 switch( p_vout->p_sys->i_mode )
344                 {
345                 case DEINTERLACE_MODE_BOB:
346                     for( ; p_out < p_out_end ; )
347                     {
348                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
349
350                         p_out += p_pic->p[i_plane].i_pitch;
351                         p_in += 2 * p_pic->p[i_plane].i_pitch;
352                     }
353                     break;
354
355                 case DEINTERLACE_MODE_BLEND:
356                     if( i_field == 0 )
357                     {
358                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
359                         p_in += 2 * p_pic->p[i_plane].i_pitch;
360                         p_out += p_pic->p[i_plane].i_pitch;
361                     }
362
363                     p_out_end -= p_outpic->p[i_plane].i_pitch;
364
365                     for( ; p_out < p_out_end ; )
366                     {
367                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
368
369                         p_out += p_pic->p[i_plane].i_pitch;
370
371                         memblend( p_out, p_in,
372                                   p_in + 2 * p_pic->p[i_plane].i_pitch,
373                                   p_pic->p[i_plane].i_pitch );
374
375                         p_in += 2 * p_pic->p[i_plane].i_pitch;
376                         p_out += p_pic->p[i_plane].i_pitch;
377                     }
378
379 #if 0
380                     if( i_field == 0 )
381                     {
382                         p_in -= 2 * p_pic->p[i_plane].i_pitch;
383                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
384                     }
385 #endif
386
387                     break;
388                 }
389                 break;
390
391             case FOURCC_I422:
392
393                 i_increment = 2 * p_pic->p[i_plane].i_pitch;
394
395                 if( i_plane == Y_PLANE )
396                 {
397                     for( ; p_out < p_out_end ; )
398                     {
399                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
400                         p_out += p_pic->p[i_plane].i_pitch;
401                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
402                         p_out += p_pic->p[i_plane].i_pitch;
403                         p_in += i_increment;
404                     }
405                 }
406                 else
407                 {
408                     for( ; p_out < p_out_end ; )
409                     {
410                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
411                         p_out += p_pic->p[i_plane].i_pitch;
412                         p_in += i_increment;
413                     }
414                 }
415                 break;
416
417             default:
418                 break;
419             }
420         }
421
422         vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
423     }
424 }
425
426 /*****************************************************************************
427  * vout_Display: displays previously rendered output
428  *****************************************************************************
429  * This function send the currently rendered image to Invert image, waits
430  * until it is displayed and switch the two rendering buffers, preparing next
431  * frame.
432  *****************************************************************************/
433 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
434 {
435     ;
436 }
437
438 static void *memblend( void *p_dest, const void *p_s1,
439                        const void *p_s2, size_t i_bytes )
440 {
441     u8* p_end = (u8*)p_dest + i_bytes - 8;
442
443     while( (u8*)p_dest < p_end )
444     {
445         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
446         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
447         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
448         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
449         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
450         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
451         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
452         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
453     }
454
455     p_end += 8;
456
457     while( (u8*)p_dest < p_end )
458     {
459         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
460     }
461
462     return p_dest;
463 }
464