]> git.sesse.net Git - vlc/blob - plugins/filter/deinterlace.c
* ./plugins/chroma/i420_rgb8.c: fixed a warning.
[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.8 2002/03/19 00:30:44 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 MODULE_CONFIG_STOP
53
54 MODULE_INIT_START
55     SET_DESCRIPTION( "deinterlacing module" )
56     /* Capability score set to 0 because we don't want to be spawned
57      * as a video output unless explicitly requested to */
58     ADD_CAPABILITY( VOUT, 0 )
59     ADD_SHORTCUT( "deinterlace" )
60 MODULE_INIT_STOP
61
62 MODULE_ACTIVATE_START
63     vout_getfunctions( &p_module->p_functions->vout );
64 MODULE_ACTIVATE_STOP
65
66 MODULE_DEACTIVATE_START
67 MODULE_DEACTIVATE_STOP
68
69 /*****************************************************************************
70  * vout_sys_t: Deinterlace video output method descriptor
71  *****************************************************************************
72  * This structure is part of the video output thread descriptor.
73  * It describes the Deinterlace specific properties of an output thread.
74  *****************************************************************************/
75 typedef struct vout_sys_s
76 {
77     int i_mode;
78     struct vout_thread_s *p_vout;
79     mtime_t last_date;
80
81 } vout_sys_t;
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static int  vout_Create    ( struct vout_thread_s * );
87 static int  vout_Init      ( struct vout_thread_s * );
88 static void vout_End       ( struct vout_thread_s * );
89 static void vout_Destroy   ( struct vout_thread_s * );
90 static int  vout_Manage    ( struct vout_thread_s * );
91 static void vout_Render    ( struct vout_thread_s *, struct picture_s * );
92 static void vout_Display   ( struct vout_thread_s *, struct picture_s * );
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, *psz_method_tmp;
117
118     /* Allocate structure */
119     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
120     if( p_vout->p_sys == NULL )
121     {
122         intf_ErrMsg("error: %s", strerror(ENOMEM) );
123         return( 1 );
124     }
125
126     /* Look what method was requested */
127     if( !(psz_method = psz_method_tmp
128           = config_GetPszVariable( "filter" )) )
129     {
130         intf_ErrMsg( "vout error: configuration variable %s empty",
131                      "filter" );
132         return( 1 );
133     }
134
135     while( *psz_method && *psz_method != ':' )
136     {
137         psz_method++;
138     }
139
140     if( !strcmp( psz_method, ":bob" ) )
141     {
142         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
143     }
144     else if( !strcmp( psz_method, ":blend" ) )
145     {
146         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BLEND;
147     }
148     else
149     {
150         intf_ErrMsg( "filter error: no valid deinterlace mode provided, "
151                      "using deinterlace:bob" );
152         p_vout->p_sys->i_mode = DEINTERLACE_MODE_BOB;
153     }
154
155     free( psz_method_tmp );
156
157     return( 0 );
158 }
159
160 /*****************************************************************************
161  * vout_Init: initialize Deinterlace video thread output method
162  *****************************************************************************/
163 static int vout_Init( vout_thread_t *p_vout )
164 {
165     int i_index;
166     char *psz_filter;
167     picture_t *p_pic;
168     
169     I_OUTPUTPICTURES = 0;
170
171     /* Initialize the output structure, full of directbuffers since we want
172      * the decoder to output directly to our structures. */
173     switch( p_vout->render.i_chroma )
174     {
175         case FOURCC_I420:
176         case FOURCC_IYUV:
177         case FOURCC_YV12:
178         case FOURCC_I422:
179             p_vout->output.i_chroma = p_vout->render.i_chroma;
180             p_vout->output.i_width  = p_vout->render.i_width;
181             p_vout->output.i_height = p_vout->render.i_height;
182             p_vout->output.i_aspect = p_vout->render.i_aspect;
183             break;
184
185         default:
186             return( 0 ); /* unknown chroma */
187             break;
188     }
189
190     /* Try to open the real video output, with half the height our images */
191     psz_filter = config_GetPszVariable( "filter" );
192     config_PutPszVariable( "filter", NULL );
193
194     intf_WarnMsg( 1, "filter: spawning the real video output" );
195
196     switch( p_vout->render.i_chroma )
197     {
198     case FOURCC_I420:
199     case FOURCC_IYUV:
200     case FOURCC_YV12:
201         switch( p_vout->p_sys->i_mode )
202         {
203         case DEINTERLACE_MODE_BOB:
204             p_vout->p_sys->p_vout =
205                 vout_CreateThread( NULL,
206                        p_vout->output.i_width, p_vout->output.i_height / 2,
207                        p_vout->output.i_chroma, p_vout->output.i_aspect );
208             break;
209
210         case DEINTERLACE_MODE_BLEND:
211             p_vout->p_sys->p_vout =
212                 vout_CreateThread( NULL,
213                        p_vout->output.i_width, p_vout->output.i_height,
214                        p_vout->output.i_chroma, p_vout->output.i_aspect );
215             break;
216         }
217         break;
218
219     case FOURCC_I422:
220         p_vout->p_sys->p_vout =
221             vout_CreateThread( NULL,
222                        p_vout->output.i_width, p_vout->output.i_height,
223                        FOURCC_I420, p_vout->output.i_aspect );
224         break;
225
226     default:
227         break;
228     }
229
230     config_PutPszVariable( "filter", psz_filter );
231     if( psz_filter ) free( psz_filter );
232
233     /* Everything failed */
234     if( p_vout->p_sys->p_vout == NULL )
235     {
236         intf_ErrMsg( "filter error: can't open vout, aborting" );
237
238         return( 0 );
239     }
240  
241     p_vout->p_sys->last_date = 0;
242
243     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
244
245     return( 0 );
246 }
247
248 /*****************************************************************************
249  * vout_End: terminate Deinterlace video thread output method
250  *****************************************************************************/
251 static void vout_End( vout_thread_t *p_vout )
252 {
253     int i_index;
254
255     /* Free the fake output buffers we allocated */
256     for( i_index = I_OUTPUTPICTURES ; i_index ; )
257     {
258         i_index--;
259         free( PP_OUTPUTPICTURE[ i_index ]->p_data );
260     }
261 }
262
263 /*****************************************************************************
264  * vout_Destroy: destroy Deinterlace video thread output method
265  *****************************************************************************
266  * Terminate an output method created by DeinterlaceCreateOutputMethod
267  *****************************************************************************/
268 static void vout_Destroy( vout_thread_t *p_vout )
269 {
270     vout_DestroyThread( p_vout->p_sys->p_vout, NULL );
271
272     free( p_vout->p_sys );
273 }
274
275 /*****************************************************************************
276  * vout_Manage: handle Deinterlace events
277  *****************************************************************************
278  * This function should be called regularly by video output thread. It manages
279  * console events. It returns a non null value on error.
280  *****************************************************************************/
281 static int vout_Manage( vout_thread_t *p_vout )
282 {
283     return( 0 );
284 }
285
286 /*****************************************************************************
287  * vout_Render: displays previously rendered output
288  *****************************************************************************
289  * This function send the currently rendered image to Deinterlace image,
290  * waits until it is displayed and switch the two rendering buffers, preparing
291  * next frame.
292  *****************************************************************************/
293 static void vout_Render ( vout_thread_t *p_vout, picture_t *p_pic )
294 {
295     picture_t *p_outpic;
296     int i_plane, i_field;
297     /* 20ms is a bit arbitrary, but it's only for the first image we get */
298     mtime_t new_date = p_vout->p_sys->last_date
299                        ? ( 3 * p_pic->date - p_vout->p_sys->last_date ) / 2
300                        : p_pic->date + 20000;
301
302     p_vout->p_sys->last_date = p_pic->date;
303
304     for( i_field = 0 ; i_field < 2 ; i_field++ )
305     {
306         /* Get a structure from the video_output. */
307         while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout,
308                                                 0, 0, 0 ) )
309                   == NULL )
310         {
311             if( p_vout->b_die || p_vout->b_error )
312             {
313                 return;
314             }
315             msleep( VOUT_OUTMEM_SLEEP );
316         }   
317
318         vout_DatePicture( p_vout->p_sys->p_vout, p_outpic,
319                           p_pic->date + i_field ? new_date : p_pic->date );
320
321         /* Copy image and skip lines */
322         for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
323         {
324             u8 *p_in, *p_out_end, *p_out;
325             int i_increment;
326
327             p_in = p_pic->p[i_plane].p_pixels
328                        + i_field * p_pic->p[i_plane].i_pitch;
329
330             p_out = p_outpic->p[i_plane].p_pixels;
331             p_out_end = p_out + p_outpic->p[i_plane].i_pitch
332                                  * p_outpic->p[i_plane].i_lines;
333
334             switch( p_vout->render.i_chroma )
335             {
336             case FOURCC_I420:
337             case FOURCC_IYUV:
338             case FOURCC_YV12:
339
340                 switch( p_vout->p_sys->i_mode )
341                 {
342                 case DEINTERLACE_MODE_BOB:
343                     for( ; p_out < p_out_end ; )
344                     {
345                         FAST_MEMCPY( p_out, p_in, 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                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
356                         p_in += 2 * p_pic->p[i_plane].i_pitch;
357                         p_out += p_pic->p[i_plane].i_pitch;
358                     }
359
360                     p_out_end -= p_outpic->p[i_plane].i_pitch;
361
362                     for( ; p_out < p_out_end ; )
363                     {
364                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
365
366                         p_out += p_pic->p[i_plane].i_pitch;
367
368                         memblend( p_out, p_in,
369                                   p_in + 2 * p_pic->p[i_plane].i_pitch,
370                                   p_pic->p[i_plane].i_pitch );
371
372                         p_in += 2 * p_pic->p[i_plane].i_pitch;
373                         p_out += p_pic->p[i_plane].i_pitch;
374                     }
375
376 #if 0
377                     if( i_field == 0 )
378                     {
379                         p_in -= 2 * p_pic->p[i_plane].i_pitch;
380                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
381                     }
382 #endif
383
384                     break;
385                 }
386                 break;
387
388             case FOURCC_I422:
389
390                 i_increment = 2 * p_pic->p[i_plane].i_pitch;
391
392                 if( i_plane == Y_PLANE )
393                 {
394                     for( ; p_out < p_out_end ; )
395                     {
396                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
397                         p_out += p_pic->p[i_plane].i_pitch;
398                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
399                         p_out += p_pic->p[i_plane].i_pitch;
400                         p_in += i_increment;
401                     }
402                 }
403                 else
404                 {
405                     for( ; p_out < p_out_end ; )
406                     {
407                         FAST_MEMCPY( p_out, p_in, p_pic->p[i_plane].i_pitch );
408                         p_out += p_pic->p[i_plane].i_pitch;
409                         p_in += i_increment;
410                     }
411                 }
412                 break;
413
414             default:
415                 break;
416             }
417         }
418
419         vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
420     }
421 }
422
423 /*****************************************************************************
424  * vout_Display: displays previously rendered output
425  *****************************************************************************
426  * This function send the currently rendered image to Invert image, waits
427  * until it is displayed and switch the two rendering buffers, preparing next
428  * frame.
429  *****************************************************************************/
430 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
431 {
432     ;
433 }
434
435 static void *memblend( void *p_dest, const void *p_s1,
436                        const void *p_s2, size_t i_bytes )
437 {
438     u8* p_end = (u8*)p_dest + i_bytes - 8;
439
440     while( (u8*)p_dest < p_end )
441     {
442         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
443         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
444         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
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     }
451
452     p_end += 8;
453
454     while( (u8*)p_dest < p_end )
455     {
456         *(u8*)p_dest++ = ( (u16)(*(u8*)p_s1++) + (u16)(*(u8*)p_s2++) ) >> 1;
457     }
458
459     return p_dest;
460 }
461