1 /*****************************************************************************
2 * dxva2_deinterlace.c: DxVA2 deinterlacing filter
3 *****************************************************************************
4 * Copyright (C) 2017 Videolabs SAS
6 * Authors: Steve Lhomme <robux4@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_filter.h>
33 #include <vlc_picture.h>
39 #include "../../video_chroma/d3d9_fmt.h"
40 #include "../../video_filter/deinterlace/common.h"
44 HINSTANCE hdecoder_dll
;
45 /* keep a reference in case the vout is released first */
47 IDirect3DDevice9
*d3ddev
;
48 IDirectXVideoProcessor
*processor
;
49 IDirect3DSurface9
*hw_surface
;
51 DXVA2_VideoProcessorCaps decoder_caps
;
53 struct deinterlace_ctx context
;
54 picture_t
* (*buffer_new
)( filter_t
* );
61 deinterlace_algo settings
;
63 static struct filter_mode_t filter_mode
[] = {
64 { "blend", DXVA2_DeinterlaceTech_BOBLineReplicate
,
65 { false, false, false, false } },
66 { "bob", DXVA2_DeinterlaceTech_BOBVerticalStretch
,
67 { true, false, false, false } },
68 { "x", DXVA2_DeinterlaceTech_BOBVerticalStretch4Tap
,
69 { true, true, false, false } },
70 { "ivtc", DXVA2_DeinterlaceTech_InverseTelecine
,
71 { false, true, true, false } },
72 { "yadif2x", DXVA2_DeinterlaceTech_PixelAdaptive
,
73 { true, true, false, false } },
76 static void Flush(filter_t
*filter
)
78 FlushDeinterlacing(&filter
->p_sys
->context
);
81 static void FillSample( DXVA2_VideoSample
*p_sample
,
82 const struct deinterlace_ctx
*p_context
,
84 const video_format_t
*p_fmt
,
88 picture_sys_t
*p_sys_src
= ActivePictureSys(p_pic
);
90 p_sample
->SrcSurface
= p_sys_src
->surface
;
91 p_sample
->SampleFormat
.SampleFormat
= p_pic
->b_top_field_first
?
92 DXVA2_SampleFieldInterleavedEvenFirst
:
93 DXVA2_SampleFieldInterleavedOddFirst
;
95 p_sample
->End
= GetFieldDuration(p_context
, p_fmt
, p_pic
) * 10;
96 p_sample
->SampleData
= DXVA2_SampleData_RFF_TFF_Present
;
98 p_sample
->SampleData
|= DXVA2_SampleData_TFF
;
100 p_sample
->SampleData
|= DXVA2_SampleData_RFF
;
101 p_sample
->DstRect
= p_sample
->SrcRect
= *p_area
;
102 p_sample
->PlanarAlpha
= DXVA2_Fixed32OpaqueAlpha();
105 static void FillBlitParams( DXVA2_VideoProcessBltParams
*params
, const RECT
*area
,
106 const DXVA2_VideoSample
*samples
, int order
)
108 memset(params
, 0, sizeof(*params
));
109 params
->TargetFrame
= (samples
->End
- samples
->Start
) * order
/ 2;
110 params
->TargetRect
= *area
;
111 params
->DestData
= 0;
112 params
->Alpha
= DXVA2_Fixed32OpaqueAlpha();
113 params
->DestFormat
.SampleFormat
= DXVA2_SampleProgressiveFrame
;
114 params
->BackgroundColor
.Alpha
= 0xFFFF;
115 params
->ConstrictionSize
.cx
= params
->TargetRect
.right
;
116 params
->ConstrictionSize
.cy
= params
->TargetRect
.bottom
;
119 static int RenderPic( filter_t
*filter
, picture_t
*p_outpic
, picture_t
*src
,
120 int order
, int i_field
)
122 filter_sys_t
*sys
= filter
->p_sys
;
123 const int i_samples
= sys
->decoder_caps
.NumBackwardRefSamples
+ 1 +
124 sys
->decoder_caps
.NumForwardRefSamples
;
126 DXVA2_VideoProcessBltParams params
;
127 DXVA2_VideoSample samples
[i_samples
];
128 picture_t
*pictures
[i_samples
];
129 D3DSURFACE_DESC srcDesc
, dstDesc
;
132 picture_t
*p_prev
= sys
->context
.pp_history
[0];
133 picture_t
*p_cur
= sys
->context
.pp_history
[1];
134 picture_t
*p_next
= sys
->context
.pp_history
[2];
136 picture_sys_t
*p_sys_src
= ActivePictureSys(src
);
138 hr
= IDirect3DSurface9_GetDesc( p_sys_src
->surface
, &srcDesc
);
139 if (unlikely(FAILED(hr
)))
141 hr
= IDirect3DSurface9_GetDesc( sys
->hw_surface
, &dstDesc
);
142 if (unlikely(FAILED(hr
)))
145 area
.top
= area
.left
= 0;
146 area
.bottom
= __MIN(srcDesc
.Height
, dstDesc
.Height
);
147 area
.right
= __MIN(srcDesc
.Width
, dstDesc
.Width
);
149 int idx
= i_samples
- 1;
152 pictures
[idx
--] = p_next
;
154 pictures
[idx
--] = p_cur
;
156 pictures
[idx
--] = p_prev
;
159 pictures
[idx
--] = src
;
161 pictures
[idx
--] = NULL
;
163 for (idx
= 0; idx
<= i_samples
-1; idx
++)
166 FillSample( &samples
[idx
], &sys
->context
, pictures
[idx
], &filter
->fmt_out
.video
, &area
, i_field
);
169 FillSample( &samples
[idx
], &sys
->context
, src
, &filter
->fmt_out
.video
, &area
, i_field
);
170 samples
[idx
].SampleFormat
.SampleFormat
= DXVA2_SampleUnknown
;
174 FillBlitParams( ¶ms
, &area
, samples
, order
);
176 hr
= IDirectXVideoProcessor_VideoProcessBlt( sys
->processor
,
184 hr
= IDirect3DDevice9_StretchRect( sys
->d3ddev
,
185 sys
->hw_surface
, NULL
,
186 p_outpic
->p_sys
->surface
, NULL
,
194 static int RenderSinglePic( filter_t
*p_filter
, picture_t
*p_outpic
, picture_t
*p_pic
)
196 return RenderPic( p_filter
, p_outpic
, p_pic
, 0, 0 );
199 static picture_t
*Deinterlace(filter_t
*p_filter
, picture_t
*p_pic
)
201 return DoDeinterlacing( p_filter
, &p_filter
->p_sys
->context
, p_pic
);
204 static const struct filter_mode_t
*GetFilterMode(const char *mode
)
206 if ( mode
== NULL
|| !strcmp( mode
, "auto" ) )
209 for (size_t i
=0; i
<ARRAY_SIZE(filter_mode
); i
++)
211 if( !strcmp( mode
, filter_mode
[i
].psz_mode
) )
212 return &filter_mode
[i
];
218 static void d3d9_pic_context_destroy(struct picture_context_t
*ctx
)
220 struct va_pic_context
*pic_ctx
= (struct va_pic_context
*)ctx
;
221 ReleasePictureSys(&pic_ctx
->picsys
);
225 static struct picture_context_t
*d3d9_pic_context_copy(struct picture_context_t
*ctx
)
227 struct va_pic_context
*src_ctx
= (struct va_pic_context
*)ctx
;
228 struct va_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
229 if (unlikely(pic_ctx
==NULL
))
231 pic_ctx
->s
.destroy
= d3d9_pic_context_destroy
;
232 pic_ctx
->s
.copy
= d3d9_pic_context_copy
;
233 pic_ctx
->picsys
= src_ctx
->picsys
;
234 AcquirePictureSys(&pic_ctx
->picsys
);
238 static picture_t
*NewOutputPicture( filter_t
*p_filter
)
240 picture_t
*pic
= p_filter
->p_sys
->buffer_new( p_filter
);
243 /* the picture might be duplicated for snapshots so it needs a context */
244 assert( pic
->p_sys
!= NULL
); /* this opaque picture is wrong */
245 struct va_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
246 if (likely(pic_ctx
!=NULL
))
248 pic_ctx
->s
.destroy
= d3d9_pic_context_destroy
;
249 pic_ctx
->s
.copy
= d3d9_pic_context_copy
;
250 pic_ctx
->picsys
= *pic
->p_sys
;
251 AcquirePictureSys( &pic_ctx
->picsys
);
252 pic
->context
= &pic_ctx
->s
;
258 static int Open(vlc_object_t
*obj
)
260 filter_t
*filter
= (filter_t
*)obj
;
261 filter_sys_t
*sys
= NULL
;
262 HINSTANCE hdecoder_dll
= NULL
;
263 HINSTANCE d3d9_dll
= NULL
;
265 picture_t
*dst
= NULL
;
266 GUID
*processorGUIDs
= NULL
;
267 GUID
*processorGUID
= NULL
;
268 IDirectXVideoProcessorService
*processor
= NULL
;
270 if (filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D9_OPAQUE
271 && filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D9_OPAQUE_10B
)
273 if (!video_format_IsSimilar(&filter
->fmt_in
.video
, &filter
->fmt_out
.video
))
276 d3d9_dll
= LoadLibrary(TEXT("D3D9.DLL"));
280 hdecoder_dll
= LoadLibrary(TEXT("DXVA2.DLL"));
284 dst
= filter_NewPicture(filter
);
290 msg_Dbg(filter
, "D3D9 opaque without a texture");
294 sys
= calloc(1, sizeof (*sys
));
295 if (unlikely(sys
== NULL
))
298 HRESULT (WINAPI
*CreateVideoService
)(IDirect3DDevice9
*,
302 (void *)GetProcAddress(hdecoder_dll
, "DXVA2CreateVideoService");
303 if (CreateVideoService
== NULL
)
306 hr
= IDirect3DSurface9_GetDevice( dst
->p_sys
->surface
, &sys
->d3ddev
);
310 D3DSURFACE_DESC dstDesc
;
311 hr
= IDirect3DSurface9_GetDesc( dst
->p_sys
->surface
, &dstDesc
);
312 if (unlikely(FAILED(hr
)))
315 hr
= CreateVideoService( sys
->d3ddev
, &IID_IDirectXVideoProcessorService
,
321 ZeroMemory(&dsc
, sizeof(dsc
));
322 dsc
.SampleWidth
= dstDesc
.Width
;
323 dsc
.SampleHeight
= dstDesc
.Height
;
324 dsc
.Format
= dstDesc
.Format
;
325 if (filter
->fmt_in
.video
.i_frame_rate
&& filter
->fmt_in
.video
.i_frame_rate_base
) {
326 dsc
.InputSampleFreq
.Numerator
= filter
->fmt_in
.video
.i_frame_rate
;
327 dsc
.InputSampleFreq
.Denominator
= filter
->fmt_in
.video
.i_frame_rate_base
;
329 dsc
.InputSampleFreq
.Numerator
= 0;
330 dsc
.InputSampleFreq
.Denominator
= 0;
332 dsc
.OutputFrameFreq
= dsc
.InputSampleFreq
;
334 DXVA2_ExtendedFormat
*pFormat
= &dsc
.SampleFormat
;
335 pFormat
->SampleFormat
= dst
->b_top_field_first
?
336 DXVA2_SampleFieldInterleavedEvenFirst
:
337 DXVA2_SampleFieldInterleavedOddFirst
;
340 hr
= IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor
,
347 char *psz_mode
= var_InheritString( filter
, "deinterlace-mode" );
348 const struct filter_mode_t
*p_mode
= GetFilterMode(psz_mode
);
351 msg_Dbg(filter
, "unknown mode %s, trying blend", psz_mode
);
352 p_mode
= GetFilterMode("blend");
354 if (strcmp(p_mode
->psz_mode
, psz_mode
))
355 msg_Dbg(filter
, "using %s deinterlacing mode", p_mode
->psz_mode
);
357 DXVA2_VideoProcessorCaps caps
, best_caps
;
358 unsigned best_score
= 0;
359 for (UINT i
=0; i
<count
; ++i
) {
360 hr
= IDirectXVideoProcessorService_GetVideoProcessorCaps( processor
,
365 if ( FAILED(hr
) || !caps
.DeinterlaceTechnology
)
368 unsigned score
= (caps
.DeinterlaceTechnology
& p_mode
->i_mode
) ? 10 : 1;
369 if (best_score
< score
) {
372 processorGUID
= processorGUIDs
+ i
;
376 if (processorGUID
== NULL
)
378 msg_Dbg(filter
, "Could not find a filter to output the required format");
382 hr
= IDirectXVideoProcessorService_CreateVideoProcessor( processor
,
391 hr
= IDirectXVideoProcessorService_CreateSurface( processor
,
398 DXVA2_VideoProcessorRenderTarget
,
404 sys
->hdecoder_dll
= hdecoder_dll
;
405 sys
->d3d9_dll
= d3d9_dll
;
406 sys
->decoder_caps
= best_caps
;
408 InitDeinterlacingContext( &sys
->context
);
410 sys
->context
.settings
= p_mode
->settings
;
411 sys
->context
.settings
.b_use_frame_history
= best_caps
.NumBackwardRefSamples
!= 0 ||
412 best_caps
.NumForwardRefSamples
!= 0;
413 if (sys
->context
.settings
.b_use_frame_history
!= p_mode
->settings
.b_use_frame_history
)
414 msg_Dbg( filter
, "deinterlacing not using frame history as requested");
415 if (sys
->context
.settings
.b_double_rate
)
416 sys
->context
.pf_render_ordered
= RenderPic
;
418 sys
->context
.pf_render_single_pic
= RenderSinglePic
;
420 video_format_t out_fmt
;
421 GetDeinterlacingOutput( &sys
->context
, &out_fmt
, &filter
->fmt_in
.video
);
422 if( !filter
->b_allow_fmt_out_change
&&
423 out_fmt
.i_height
!= filter
->fmt_in
.video
.i_height
)
428 CoTaskMemFree(processorGUIDs
);
429 IDirectXVideoProcessorService_Release(processor
);
430 picture_Release(dst
);
432 sys
->buffer_new
= filter
->owner
.video
.buffer_new
;
433 filter
->owner
.video
.buffer_new
= NewOutputPicture
;
434 filter
->fmt_out
.video
= out_fmt
;
435 filter
->pf_video_filter
= Deinterlace
;
436 filter
->pf_flush
= Flush
;
441 CoTaskMemFree(processorGUIDs
);
442 if (sys
&& sys
->processor
)
443 IDirectXVideoProcessor_Release( sys
->processor
);
445 IDirectXVideoProcessorService_Release(processor
);
446 if (sys
&& sys
->d3ddev
)
447 IDirect3DDevice9_Release( sys
->d3ddev
);
449 FreeLibrary(hdecoder_dll
);
451 FreeLibrary(d3d9_dll
);
453 picture_Release(dst
);
459 static void Close(vlc_object_t
*obj
)
461 filter_t
*filter
= (filter_t
*)obj
;
462 filter_sys_t
*sys
= filter
->p_sys
;
464 IDirect3DSurface9_Release( sys
->hw_surface
);
465 IDirectXVideoProcessor_Release( sys
->processor
);
466 IDirect3DDevice9_Release( sys
->d3ddev
);
467 FreeLibrary( sys
->hdecoder_dll
);
468 FreeLibrary( sys
->d3d9_dll
);
474 set_description(N_("Direct3D9 deinterlacing filter"))
475 set_capability("video filter", 0)
476 set_category(CAT_VIDEO
)
477 set_subcategory(SUBCAT_VIDEO_VFILTER
)
478 set_callbacks(Open
, Close
)
479 add_shortcut ("deinterlace")