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