1 //
2 //
3 // Port to avidemux2 by mean
4 // Original filter by M Niedermayer
5 // See below
6 /*
7 Copyright (C) 2006 Michael Niedermayer <michaelni@gmx.at>
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "ADM_default.h"
25 #include "ADM_coreVideoFilterInternal.h"
26 #include "mcdeint.h"
27 #include "mcdeint_desc.cpp"
28 #include "DIA_factory.h"
29
30 extern "C"
31 {
32 #include "libavcodec/avcodec.h"
33 }
34
35
36 struct vf_priv_s {
37 int mode;
38 int qp;
39 int parity;
40 #if 0
41 int temp_stride[3];
42 uint8_t *src[3];
43 int16_t *temp[3];
44 #endif
45 int outbuf_size;
46 uint8_t *outbuf;
47 AVCodecContext *avctx_enc;
48 AVFrame *frame;
49 AVFrame *frame_dec;
50 };
51
52
53 /**
54 \class horizontalFlipFilter
55 */
56 class AVDMVideoMCDeint : public ADM_coreVideoFilterCached
57 {
58 protected:
59 mcdeint _param;
60 uint8_t init();
61 uint8_t cleanup();
62 vf_priv_s priv;
63 public:
64 AVDMVideoMCDeint(ADM_coreVideoFilter *previous,CONFcouple *conf);
65 ~AVDMVideoMCDeint();
66
67 virtual const char *getConfiguration(void); /// Return current configuration as a human readable string
68 virtual bool getNextFrame(uint32_t *fn,ADMImage *image); /// Return the next image
69 // virtual FilterInfo *getInfo(void); /// Return picture parameters after this filter
70 virtual bool getCoupledConf(CONFcouple **couples) ; /// Return the current filter configuration
71 virtual void setCoupledConf(CONFcouple *couples);
72 virtual bool configure(void); /// Start graphical user interface
73 };
74
75 // Add the hook to make it valid plugin
76 DECLARE_VIDEO_FILTER( AVDMVideoMCDeint, // Class
77 1,0,0, // Version
78 ADM_UI_ALL, // UI
79 VF_INTERLACING, // Category
80 "mcdeinterlace", // internal name (must be uniq!)
81 QT_TRANSLATE_NOOP("mcdeint", "MCDeint"), // Display name
82 QT_TRANSLATE_NOOP("mcdeint", "Motion compensation deinterlacer. Ported from MPlayer.") // Description
83 );
84
85 //********** Register chunk ************
86
87 static void filter(struct vf_priv_s *p, uint8_t *dst[3], uint8_t *src[3], int dst_stride[3], int src_stride[3], int width, int height);
88
89
90 /**
91 \fn getCoupledConf
92 \brief Return our current configuration as couple name=value
93 */
getCoupledConf(CONFcouple ** couples)94 bool AVDMVideoMCDeint::getCoupledConf(CONFcouple **couples)
95 {
96 return ADM_paramSave(couples, mcdeint_param,&_param);
97 }
98
setCoupledConf(CONFcouple * couples)99 void AVDMVideoMCDeint::setCoupledConf(CONFcouple *couples)
100 {
101 ADM_paramLoad(couples, mcdeint_param, &_param);
102 }
103
104 /**
105 \fn getConfiguration
106 \brief Return current setting as a string
107 */
getConfiguration(void)108 const char *AVDMVideoMCDeint::getConfiguration(void)
109 {
110 static char conf[256];
111 snprintf(conf,255,"MC deinterlacer : Mode %d, qp %d, parity %d ",_param.mode,_param.qp,_param.initial_parity);
112 return conf;
113 }
114 /**
115 \fn configure
116 */
configure()117 bool AVDMVideoMCDeint::configure()
118 {
119
120 diaMenuEntry menuMode[4]={{0,QT_TRANSLATE_NOOP("mcdeint","Fast"),NULL},
121 {1,QT_TRANSLATE_NOOP("mcdeint","Medium"),NULL},
122 {2,QT_TRANSLATE_NOOP("mcdeint","Slow iterative motion search"),NULL},
123 {3,QT_TRANSLATE_NOOP("mcdeint","Extra slow (same as 3+multiple reference frames)"),NULL}
124 };
125
126 diaElemMenu menu1(&(_param.mode),QT_TRANSLATE_NOOP("mcdeint","_Mode:"), 4,menuMode);
127 diaElemToggle menu2(&(_param.initial_parity),QT_TRANSLATE_NOOP("mcdeint","Bottom :"));
128 diaElemUInteger qp(&(_param.qp),QT_TRANSLATE_NOOP("mcdeint","_Qp:"),1,60);
129
130 diaElem *elems[3]={&menu1,&menu2,&qp};
131
132 return diaFactoryRun(QT_TRANSLATE_NOOP("mcdeint","mcDeinterlace"),3,elems);
133
134 }
135 /**
136 \fn ctor
137 */
AVDMVideoMCDeint(ADM_coreVideoFilter * in,CONFcouple * setup)138 AVDMVideoMCDeint::AVDMVideoMCDeint( ADM_coreVideoFilter *in,CONFcouple *setup)
139 : ADM_coreVideoFilterCached(4,in,setup)
140 {
141 if(!setup || !ADM_paramLoad(setup,mcdeint_param,&_param))
142 {
143 // Default value
144 _param.mode=0;
145 _param.qp=1;
146 _param.initial_parity=0;
147
148 }
149 init();
150
151 }
152 /**
153 \fn dtor
154 */
~AVDMVideoMCDeint()155 AVDMVideoMCDeint::~AVDMVideoMCDeint()
156 {
157 cleanup();
158 }
159
160
161 /**
162 \fn getNextFrame
163 */
getNextFrame(uint32_t * fn,ADMImage * image)164 bool AVDMVideoMCDeint::getNextFrame(uint32_t *fn,ADMImage *image)
165 {
166
167 ADMImage *curImage;
168 char txt[256];
169 uint32_t frame=nextFrame++;
170 *fn=frame;
171 curImage=vidCache->getImage(frame);
172 if(!curImage)
173 {
174 printf("MCDeint : error getting frame\n");
175 return 0;
176 }
177
178 // Prepare to call filter...
179 uint8_t *dplanes[3],*splanes[3];
180 int dstride[3],sstride[3];
181 for(int i=0;i<3;i++)
182 {
183 ADM_PLANE plane=(ADM_PLANE)i;
184
185 splanes[i]=curImage->GetReadPtr(plane);
186 sstride[i]=curImage->GetPitch(plane);
187
188 dplanes[i]=image->GetWritePtr(plane);
189 dstride[i]=image->GetPitch(plane);
190
191 }
192
193 filter(&priv, dplanes, splanes, dstride, sstride, info.width, info.height);
194 image->copyInfo(curImage);
195 vidCache->unlockAll();
196 return 1;
197 }
198 /**
199 \fn init
200 */
init(void)201 uint8_t AVDMVideoMCDeint::init( void )
202 {
203 memset(&priv,0,sizeof(priv));
204 int i;
205
206 AVCodec *enc= avcodec_find_encoder(CODEC_ID_SNOW);
207 ADM_assert(enc);
208
209 for(i=0; i<3; i++)
210 {
211 AVCodecContext *avctx_enc;
212 avctx_enc= priv.avctx_enc= avcodec_alloc_context();
213 avctx_enc->width = info.width;
214 avctx_enc->height = info.height;
215 avctx_enc->time_base= (AVRational){1,25}; // meaningless
216 avctx_enc->gop_size = 300;
217 avctx_enc->max_b_frames= 0;
218 avctx_enc->pix_fmt = PIX_FMT_YUV420P;
219 avctx_enc->flags = CODEC_FLAG_QSCALE | CODEC_FLAG_LOW_DELAY;
220 avctx_enc->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;
221 avctx_enc->global_quality= 1;
222 avctx_enc->flags2= CODEC_FLAG2_MEMC_ONLY;
223 avctx_enc->me_cmp=
224 avctx_enc->me_sub_cmp= FF_CMP_SAD; //SSE;
225 avctx_enc->mb_cmp= FF_CMP_SSE;
226
227 switch(_param.mode)
228 {
229 case 3:
230 avctx_enc->refs= 3;
231 case 2:
232 avctx_enc->me_method= ME_ITER;
233 case 1:
234 avctx_enc->flags |= CODEC_FLAG_4MV;
235 avctx_enc->dia_size=2;
236 // avctx_enc->mb_decision = MB_DECISSION_RD;
237 case 0:
238 avctx_enc->flags |= CODEC_FLAG_QPEL;
239 }
240
241 avcodec_open(avctx_enc, enc);
242
243 }
244 priv.frame= av_frame_alloc();
245
246 priv.outbuf_size= info.width*info.height*10;
247 priv.outbuf= (uint8_t *)ADM_alloc(priv.outbuf_size);
248 priv.parity=_param.initial_parity;
249 return 1;
250 }
251 /**
252 \fn cleanup
253 */
cleanup(void)254 uint8_t AVDMVideoMCDeint::cleanup( void )
255 {
256 //
257 avcodec_close(priv.avctx_enc);
258 av_free(priv.avctx_enc);
259 ADM_dezalloc(priv.outbuf);
260 memset(&priv,0,sizeof(priv));
261 return 1;
262 }
263
264
265 /*
266 Known Issues:
267 * The motion estimation is somewhat at the mercy of the input, if the input
268 frames are created purely based on spatial interpolation then for example
269 a thin black line or another random and not interpolateable pattern
270 will cause problems
271 Note: completly ignoring the "unavailable" lines during motion estimation
272 didnt look any better, so the most obvious solution would be to improve
273 tfields or penalize problematic motion vectors ...
274
275 * If non iterative ME is used then snow currently ignores the OBMC window
276 and as a result sometimes creates artifacts
277
278 * only past frames are used, we should ideally use future frames too, something
279 like filtering the whole movie in forward and then backward direction seems
280 like a interresting idea but the current filter framework is FAR from
281 supporting such things
282
283 * combining the motion compensated image with the input image also isnt
284 as trivial as it seems, simple blindly taking even lines from one and
285 odd ones from the other doesnt work at all as ME/MC sometimes simple
286 has nothing in the previous frames which matches the current, the current
287 algo has been found by trial and error and almost certainly can be
288 improved ...
289 */
290
291
292
293 #define MIN(a,b) ((a) > (b) ? (b) : (a))
294 #define MAX(a,b) ((a) < (b) ? (b) : (a))
295 #define ABS(a) ((a) > 0 ? (a) : (-(a)))
296
297 //===========================================================================//
298
299
filter(struct vf_priv_s * p,uint8_t * dst[3],uint8_t * src[3],int dst_stride[3],int src_stride[3],int width,int height)300 static void filter(struct vf_priv_s *p, uint8_t *dst[3], uint8_t *src[3], int dst_stride[3], int src_stride[3], int width, int height){
301 int x, y, i;
302 int out_size;
303
304 for(i=0; i<3; i++){
305 p->frame->data[i]= src[i];
306 p->frame->linesize[i]= src_stride[i];
307 }
308
309 p->avctx_enc->me_cmp=
310 p->avctx_enc->me_sub_cmp= FF_CMP_SAD /*| (p->parity ? FF_CMP_ODD : FF_CMP_EVEN)*/;
311 p->frame->quality= p->qp*FF_QP2LAMBDA;
312 out_size = avcodec_encode_video(p->avctx_enc, p->outbuf, p->outbuf_size, p->frame);
313 p->frame_dec = p->avctx_enc->coded_frame;
314
315 for(i=0; i<3; i++){
316 int is_chroma= !!i;
317 int w= width >>is_chroma;
318 int h= height>>is_chroma;
319 int fils= p->frame_dec->linesize[i];
320 int srcs= src_stride[i];
321
322 for(y=0; y<h; y++){
323 if((y ^ p->parity) & 1){
324 for(x=0; x<w; x++){
325 if((x-2)+(y-1)*w>=0 && (x+2)+(y+1)*w<w*h){ //FIXME either alloc larger images or optimize this
326 uint8_t *filp= &p->frame_dec->data[i][x + y*fils];
327 uint8_t *srcp= &src[i][x + y*srcs];
328 int diff0= filp[-fils] - srcp[-srcs];
329 int diff1= filp[+fils] - srcp[+srcs];
330 int spatial_score= ABS(srcp[-srcs-1] - srcp[+srcs-1])
331 +ABS(srcp[-srcs ] - srcp[+srcs ])
332 +ABS(srcp[-srcs+1] - srcp[+srcs+1]) - 1;
333 int temp= filp[0];
334
335 #define CHECK(j)\
336 { int score= ABS(srcp[-srcs-1+j] - srcp[+srcs-1-j])\
337 + ABS(srcp[-srcs +j] - srcp[+srcs -j])\
338 + ABS(srcp[-srcs+1+j] - srcp[+srcs+1-j]);\
339 if(score < spatial_score){\
340 spatial_score= score;\
341 diff0= filp[-fils+j] - srcp[-srcs+j];\
342 diff1= filp[+fils-j] - srcp[+srcs-j];
343
344 CHECK(-1) CHECK(-2) }} }}
345 CHECK( 1) CHECK( 2) }} }}
346 #if 0
347 if((diff0 ^ diff1) > 0){
348 int mindiff= ABS(diff0) > ABS(diff1) ? diff1 : diff0;
349 temp-= mindiff;
350 }
351 #elif 1
352 if(diff0 + diff1 > 0)
353 temp-= (diff0 + diff1 - ABS( ABS(diff0) - ABS(diff1) )/2)/2;
354 else
355 temp-= (diff0 + diff1 + ABS( ABS(diff0) - ABS(diff1) )/2)/2;
356 #else
357 temp-= (diff0 + diff1)/2;
358 #endif
359 #if 1
360 filp[0]=
361 dst[i][x + y*dst_stride[i]]= temp > 255U ? ~(temp>>31) : temp;
362 #else
363 dst[i][x + y*dst_stride[i]]= filp[0];
364 filp[0]= temp > 255U ? ~(temp>>31) : temp;
365 #endif
366 }else
367 dst[i][x + y*dst_stride[i]]= p->frame_dec->data[i][x + y*fils];
368 }
369 }
370 }
371 for(y=0; y<h; y++){
372 if(!((y ^ p->parity) & 1)){
373 for(x=0; x<w; x++){
374 #if 1
375 p->frame_dec->data[i][x + y*fils]=
376 dst[i][x + y*dst_stride[i]]= src[i][x + y*srcs];
377 #else
378 dst[i][x + y*dst_stride[i]]= p->frame_dec->data[i][x + y*fils];
379 p->frame_dec->data[i][x + y*fils]= src[i][x + y*srcs];
380 #endif
381 }
382 }
383 }
384 }
385 p->parity ^= 1;
386
387 }
388
389 //EOF
390