]> git.sesse.net Git - mlt/blob - src/modules/avformat/filter_avdeinterlace.c
Move rotoscoping filter into plusgpl module.
[mlt] / src / modules / avformat / filter_avdeinterlace.c
1 /*
2  * filter_avdeinterlace.c -- deinterlace filter
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <framework/mlt_filter.h>
22 #include <framework/mlt_frame.h>
23
24 #include <string.h>
25 #include <stdlib.h>
26
27 // ffmpeg Header files
28 #include <libavformat/avformat.h>
29
30 #ifdef USE_MMX
31 #include "mmx.h"
32 #else
33 #define MAX_NEG_CROP 1024
34 static uint8_t ff_cropTbl[256 + 2 * MAX_NEG_CROP] = {0,};
35 #endif
36
37 #ifdef USE_MMX
38 #define DEINT_INPLACE_LINE_LUM \
39                     movd_m2r(lum_m4[0],mm0);\
40                     movd_m2r(lum_m3[0],mm1);\
41                     movd_m2r(lum_m2[0],mm2);\
42                     movd_m2r(lum_m1[0],mm3);\
43                     movd_m2r(lum[0],mm4);\
44                     punpcklbw_r2r(mm7,mm0);\
45                     movd_r2m(mm2,lum_m4[0]);\
46                     punpcklbw_r2r(mm7,mm1);\
47                     punpcklbw_r2r(mm7,mm2);\
48                     punpcklbw_r2r(mm7,mm3);\
49                     punpcklbw_r2r(mm7,mm4);\
50                     paddw_r2r(mm3,mm1);\
51                     psllw_i2r(1,mm2);\
52                     paddw_r2r(mm4,mm0);\
53                     psllw_i2r(2,mm1);\
54                     paddw_r2r(mm6,mm2);\
55                     paddw_r2r(mm2,mm1);\
56                     psubusw_r2r(mm0,mm1);\
57                     psrlw_i2r(3,mm1);\
58                     packuswb_r2r(mm7,mm1);\
59                     movd_r2m(mm1,lum_m2[0]);
60
61 #define DEINT_LINE_LUM \
62                     movd_m2r(lum_m4[0],mm0);\
63                     movd_m2r(lum_m3[0],mm1);\
64                     movd_m2r(lum_m2[0],mm2);\
65                     movd_m2r(lum_m1[0],mm3);\
66                     movd_m2r(lum[0],mm4);\
67                     punpcklbw_r2r(mm7,mm0);\
68                     punpcklbw_r2r(mm7,mm1);\
69                     punpcklbw_r2r(mm7,mm2);\
70                     punpcklbw_r2r(mm7,mm3);\
71                     punpcklbw_r2r(mm7,mm4);\
72                     paddw_r2r(mm3,mm1);\
73                     psllw_i2r(1,mm2);\
74                     paddw_r2r(mm4,mm0);\
75                     psllw_i2r(2,mm1);\
76                     paddw_r2r(mm6,mm2);\
77                     paddw_r2r(mm2,mm1);\
78                     psubusw_r2r(mm0,mm1);\
79                     psrlw_i2r(3,mm1);\
80                     packuswb_r2r(mm7,mm1);\
81                     movd_r2m(mm1,dst[0]);
82 #endif
83
84 /* filter parameters: [-1 4 2 4 -1] // 8 */
85 static inline void deinterlace_line(uint8_t *dst, 
86                              const uint8_t *lum_m4, const uint8_t *lum_m3, 
87                              const uint8_t *lum_m2, const uint8_t *lum_m1, 
88                              const uint8_t *lum,
89                              int size)
90 {
91 #ifndef USE_MMX
92     uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
93     int sum;
94
95     for(;size > 0;size--) {
96         sum = -lum_m4[0];
97         sum += lum_m3[0] << 2;
98         sum += lum_m2[0] << 1;
99         sum += lum_m1[0] << 2;
100         sum += -lum[0];
101         dst[0] = cm[(sum + 4) >> 3];
102         lum_m4++;
103         lum_m3++;
104         lum_m2++;
105         lum_m1++;
106         lum++;
107         dst++;
108     }
109 #else
110
111     {
112         mmx_t rounder;
113         rounder.uw[0]=4;
114         rounder.uw[1]=4;
115         rounder.uw[2]=4;
116         rounder.uw[3]=4;
117         pxor_r2r(mm7,mm7);
118         movq_m2r(rounder,mm6);
119     }
120     for (;size > 3; size-=4) {
121         DEINT_LINE_LUM
122         lum_m4+=4;
123         lum_m3+=4;
124         lum_m2+=4;
125         lum_m1+=4;
126         lum+=4;
127         dst+=4;
128     }
129 #endif
130 }
131 static inline void deinterlace_line_inplace(uint8_t *lum_m4, uint8_t *lum_m3, uint8_t *lum_m2, uint8_t *lum_m1, uint8_t *lum,
132                              int size)
133 {
134 #ifndef USE_MMX
135     uint8_t *cm = ff_cropTbl + MAX_NEG_CROP;
136     int sum;
137
138     for(;size > 0;size--) {
139         sum = -lum_m4[0];
140         sum += lum_m3[0] << 2;
141         sum += lum_m2[0] << 1;
142         lum_m4[0]=lum_m2[0];
143         sum += lum_m1[0] << 2;
144         sum += -lum[0];
145         lum_m2[0] = cm[(sum + 4) >> 3];
146         lum_m4++;
147         lum_m3++;
148         lum_m2++;
149         lum_m1++;
150         lum++;
151     }
152 #else
153
154     {
155         mmx_t rounder;
156         rounder.uw[0]=4;
157         rounder.uw[1]=4;
158         rounder.uw[2]=4;
159         rounder.uw[3]=4;
160         pxor_r2r(mm7,mm7);
161         movq_m2r(rounder,mm6);
162     }
163     for (;size > 3; size-=4) {
164         DEINT_INPLACE_LINE_LUM
165         lum_m4+=4;
166         lum_m3+=4;
167         lum_m2+=4;
168         lum_m1+=4;
169         lum+=4;
170     }
171 #endif
172 }
173
174 /* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The
175    top field is copied as is, but the bottom field is deinterlaced
176    against the top field. */
177 static inline void deinterlace_bottom_field(uint8_t *dst, int dst_wrap,
178                                     const uint8_t *src1, int src_wrap,
179                                     int width, int height)
180 {
181     const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2;
182     int y;
183
184     src_m2 = src1;
185     src_m1 = src1;
186     src_0=&src_m1[src_wrap];
187     src_p1=&src_0[src_wrap];
188     src_p2=&src_p1[src_wrap];
189     for(y=0;y<(height-2);y+=2) {
190         memcpy(dst,src_m1,width);
191         dst += dst_wrap;
192         deinterlace_line(dst,src_m2,src_m1,src_0,src_p1,src_p2,width);
193         src_m2 = src_0;
194         src_m1 = src_p1;
195         src_0 = src_p2;
196         src_p1 += 2*src_wrap;
197         src_p2 += 2*src_wrap;
198         dst += dst_wrap;
199     }
200     memcpy(dst,src_m1,width);
201     dst += dst_wrap;
202     /* do last line */
203     deinterlace_line(dst,src_m2,src_m1,src_0,src_0,src_0,width);
204 }
205
206 static inline void deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap,
207                                              int width, int height)
208 {
209     uint8_t *src_m1, *src_0, *src_p1, *src_p2;
210     int y;
211     uint8_t *buf;
212     buf = (uint8_t*)av_malloc(width);
213
214     src_m1 = src1;
215     memcpy(buf,src_m1,width);
216     src_0=&src_m1[src_wrap];
217     src_p1=&src_0[src_wrap];
218     src_p2=&src_p1[src_wrap];
219     for(y=0;y<(height-2);y+=2) {
220         deinterlace_line_inplace(buf,src_m1,src_0,src_p1,src_p2,width);
221         src_m1 = src_p1;
222         src_0 = src_p2;
223         src_p1 += 2*src_wrap;
224         src_p2 += 2*src_wrap;
225     }
226     /* do last line */
227     deinterlace_line_inplace(buf,src_m1,src_0,src_0,src_0,width);
228     av_free(buf);
229 }
230
231
232 /* deinterlace - if not supported return -1 */
233 static int mlt_avpicture_deinterlace(AVPicture *dst, const AVPicture *src,
234                           int pix_fmt, int width, int height)
235 {
236     int i;
237
238     if (pix_fmt != PIX_FMT_YUV420P &&
239         pix_fmt != PIX_FMT_YUV422P &&
240         pix_fmt != PIX_FMT_YUYV422 &&
241         pix_fmt != PIX_FMT_YUV444P &&
242         pix_fmt != PIX_FMT_YUV411P)
243         return -1;
244     if ((width & 3) != 0 || (height & 3) != 0)
245         return -1;
246
247         if ( pix_fmt != PIX_FMT_YUYV422 )
248         {
249       for(i=0;i<3;i++) {
250           if (i == 1) {
251               switch(pix_fmt) {
252               case PIX_FMT_YUV420P:
253                   width >>= 1;
254                   height >>= 1;
255                   break;
256               case PIX_FMT_YUV422P:
257                   width >>= 1;
258                   break;
259               case PIX_FMT_YUV411P:
260                   width >>= 2;
261                   break;
262               default:
263                   break;
264               }
265           }
266           if (src == dst) {
267               deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i],
268                                    width, height);
269           } else {
270               deinterlace_bottom_field(dst->data[i],dst->linesize[i],
271                                           src->data[i], src->linesize[i],
272                                           width, height);
273           }
274           }
275     }
276         else {
277       if (src == dst) {
278           deinterlace_bottom_field_inplace(dst->data[0], dst->linesize[0],
279                                width<<1, height);
280       } else {
281           deinterlace_bottom_field(dst->data[0],dst->linesize[0],
282                                       src->data[0], src->linesize[0],
283                                       width<<1, height);
284       }
285         }
286
287 #ifdef USE_MMX
288     emms();
289 #endif
290     return 0;
291 }
292
293 /** Do it :-).
294 */
295
296 static int filter_get_image( mlt_frame frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
297 {
298         int error = 0;
299         int deinterlace = mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "consumer_deinterlace" );
300
301         // Determine if we need a writable version or not
302         if ( deinterlace && !writable )
303                  writable = !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" );
304
305         // Get the input image
306         *format = mlt_image_yuv422;
307         error = mlt_frame_get_image( frame, image, format, width, height, 1 );
308
309         // Check that we want progressive and we aren't already progressive
310         if ( deinterlace && *format == mlt_image_yuv422 && *image != NULL && !mlt_properties_get_int( MLT_FRAME_PROPERTIES( frame ), "progressive" ) )
311         {
312                 // Create a picture
313                 AVPicture *output = mlt_pool_alloc( sizeof( AVPicture ) );
314
315                 // Fill the picture
316                 avpicture_fill( output, *image, PIX_FMT_YUYV422, *width, *height );
317                 mlt_avpicture_deinterlace( output, output, PIX_FMT_YUYV422, *width, *height );
318
319                 // Free the picture
320                 mlt_pool_release( output );
321
322                 // Make sure that others know the frame is deinterlaced
323                 mlt_properties_set_int( MLT_FRAME_PROPERTIES( frame ), "progressive", 1 );
324         }
325
326         return error;
327 }
328
329 /** Deinterlace filter processing - this should be lazy evaluation here...
330 */
331
332 static mlt_frame deinterlace_process( mlt_filter filter, mlt_frame frame )
333 {
334         // Push the get_image method on to the stack
335         mlt_frame_push_get_image( frame, filter_get_image );
336         
337         return frame;
338 }
339
340 /** Constructor for the filter.
341 */
342
343 mlt_filter filter_avdeinterlace_init( void *arg )
344 {
345 #ifndef USE_MMX
346         if ( ff_cropTbl[MAX_NEG_CROP + 1] == 0 )
347         {
348                 int i;
349                 for(i=0;i<256;i++) ff_cropTbl[i + MAX_NEG_CROP] = i;
350                 for(i=0;i<MAX_NEG_CROP;i++) {
351                         ff_cropTbl[i] = 0;
352                         ff_cropTbl[i + MAX_NEG_CROP + 256] = 255;
353                 }
354         }
355 #endif
356         mlt_filter filter = mlt_filter_new( );
357         if ( filter != NULL )
358                 filter->process = deinterlace_process;
359         return filter;
360 }
361