1 /*****************************************************************************
2 * d3d9_filters.c: D3D9 filters module callbacks
3 *****************************************************************************
4 * Copyright © 2017 VLC authors, VideoLAN and VideoLabs
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 *****************************************************************************/
27 #include <stdatomic.h>
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_picture.h>
35 #include <vlc_codec.h>
41 #include "../../video_chroma/d3d9_fmt.h"
43 #include "d3d9_filters.h"
51 DXVA2_ValueRange Range
;
56 HINSTANCE hdecoder_dll
;
57 IDirectXVideoProcessor
*processor
;
58 IDirect3DSurface9
*hw_surface
;
60 struct filter_level Brightness
;
61 struct filter_level Contrast
;
62 struct filter_level Hue
;
63 struct filter_level Saturation
;
66 #define THRES_TEXT N_("Brightness threshold")
67 #define THRES_LONGTEXT N_("When this mode is enabled, pixels will be " \
68 "shown as black or white. The threshold value will be the brightness " \
70 #define CONT_TEXT N_("Image contrast (0-2)")
71 #define CONT_LONGTEXT N_("Set the image contrast, between 0 and 2. Defaults to 1.")
72 #define HUE_TEXT N_("Image hue (0-360)")
73 #define HUE_LONGTEXT N_("Set the image hue, between 0 and 360. Defaults to 0.")
74 #define SAT_TEXT N_("Image saturation (0-3)")
75 #define SAT_LONGTEXT N_("Set the image saturation, between 0 and 3. Defaults to 1.")
76 #define LUM_TEXT N_("Image brightness (0-2)")
77 #define LUM_LONGTEXT N_("Set the image brightness, between 0 and 2. Defaults to 1.")
78 #define GAMMA_TEXT N_("Image gamma (0-10)")
79 #define GAMMA_LONGTEXT N_("Set the image gamma, between 0.01 and 10. Defaults to 1.")
81 static const char *const ppsz_filter_options
[] = {
82 "contrast", "brightness", "hue", "saturation", "gamma",
83 "brightness-threshold", NULL
86 static void FillSample( DXVA2_VideoSample
*p_sample
,
90 picture_sys_d3d9_t
*p_sys_src
= ActiveD3D9PictureSys(p_pic
);
92 p_sample
->SrcSurface
= p_sys_src
->surface
;
93 p_sample
->SampleFormat
.SampleFormat
= DXVA2_SampleProgressiveFrame
;
96 p_sample
->SampleData
= 0;
97 p_sample
->DstRect
= p_sample
->SrcRect
= *p_area
;
98 p_sample
->PlanarAlpha
= DXVA2_Fixed32OpaqueAlpha();
101 static picture_t
*AllocPicture( filter_t
*p_filter
)
103 struct d3d9_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
104 if (unlikely(pic_ctx
== NULL
))
107 picture_t
*pic
= picture_NewFromFormat( &p_filter
->fmt_out
.video
);
108 if (unlikely(pic
== NULL
))
114 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueContext(p_filter
->vctx_out
);
115 d3d9_video_context_t
*vctx_sys
= GetD3D9ContextPrivate( p_filter
->vctx_out
);
117 HRESULT hr
= IDirect3DDevice9_CreateOffscreenPlainSurface(d3d9_decoder
->d3ddev
.dev
,
118 p_filter
->fmt_out
.video
.i_width
,
119 p_filter
->fmt_out
.video
.i_height
,
122 &pic_ctx
->picsys
.surface
,
127 picture_Release(pic
);
130 AcquireD3D9PictureSys( &pic_ctx
->picsys
);
131 IDirect3DSurface9_Release(pic_ctx
->picsys
.surface
);
132 pic_ctx
->s
= (picture_context_t
) {
133 d3d9_pic_context_destroy
, d3d9_pic_context_copy
,
134 vlc_video_context_Hold(p_filter
->vctx_out
),
136 pic
->context
= &pic_ctx
->s
;
140 static picture_t
*Filter(filter_t
*p_filter
, picture_t
*p_pic
)
142 filter_sys_t
*p_sys
= p_filter
->p_sys
;
144 picture_sys_d3d9_t
*p_src_sys
= ActiveD3D9PictureSys(p_pic
);
146 picture_t
*p_outpic
= AllocPicture( p_filter
);
150 picture_sys_d3d9_t
*p_out_sys
= ActiveD3D9PictureSys(p_outpic
);
151 if( !p_out_sys
|| !p_out_sys
->surface
)
154 picture_CopyProperties( p_outpic
, p_pic
);
157 D3DSURFACE_DESC srcDesc
, dstDesc
;
160 hr
= IDirect3DSurface9_GetDesc( p_src_sys
->surface
, &srcDesc
);
161 if (unlikely(FAILED(hr
)))
163 hr
= IDirect3DSurface9_GetDesc( p_sys
->hw_surface
, &dstDesc
);
164 if (unlikely(FAILED(hr
)))
167 area
.top
= area
.left
= 0;
168 area
.bottom
= __MIN(srcDesc
.Height
, dstDesc
.Height
);
169 area
.right
= __MIN(srcDesc
.Width
, dstDesc
.Width
);
171 DXVA2_VideoProcessBltParams params
= {0};
172 DXVA2_VideoSample sample
= {0};
173 FillSample( &sample
, p_pic
, &area
);
175 params
.ProcAmpValues
.Brightness
.ll
= atomic_load( &p_sys
->Brightness
.level
);
176 params
.ProcAmpValues
.Contrast
.ll
= atomic_load( &p_sys
->Contrast
.level
);
177 params
.ProcAmpValues
.Hue
.ll
= atomic_load( &p_sys
->Hue
.level
);
178 params
.ProcAmpValues
.Saturation
.ll
= atomic_load( &p_sys
->Saturation
.level
);
179 params
.TargetFrame
= 0;
180 params
.TargetRect
= area
;
182 params
.Alpha
= DXVA2_Fixed32OpaqueAlpha();
183 params
.DestFormat
.SampleFormat
= DXVA2_SampleProgressiveFrame
;
184 params
.BackgroundColor
.Alpha
= 0xFFFF;
186 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueContext(p_filter
->vctx_out
);
188 hr
= IDirectXVideoProcessor_VideoProcessBlt( p_sys
->processor
,
193 hr
= IDirect3DDevice9_StretchRect( d3d9_decoder
->d3ddev
.dev
,
194 p_sys
->hw_surface
, NULL
,
195 p_out_sys
->surface
, NULL
,
200 picture_Release( p_pic
);
203 picture_Release( p_pic
);
207 static LONG
StoreLevel(const struct filter_level
*range
, const DXVA2_ValueRange
*Range
, float val
)
210 if (val
> range
->default_val
)
212 level
= (Range
->MaxValue
.ll
- Range
->DefaultValue
.ll
) * (val
- range
->default_val
) /
213 (range
->max
- range
->default_val
);
215 else if (val
< range
->default_val
)
217 level
= (Range
->MinValue
.ll
- Range
->DefaultValue
.ll
) * (val
- range
->default_val
) /
218 (range
->min
- range
->default_val
);
223 return level
+ Range
->DefaultValue
.ll
;
226 static void SetLevel(struct filter_level
*range
, float val
)
228 atomic_store( &range
->level
, StoreLevel( range
, &range
->Range
, val
) );
231 static void InitLevel(filter_t
*filter
, struct filter_level
*range
, const char *p_name
, float def
)
233 module_config_t
*cfg
= config_FindConfig(p_name
);
234 range
->min
= cfg
->min
.f
;
235 range
->max
= cfg
->max
.f
;
236 range
->default_val
= def
;
238 float val
= var_CreateGetFloatCommand( filter
, p_name
);
240 atomic_init( &range
->level
, StoreLevel( range
, &range
->Range
, val
) );
243 static int AdjustCallback( vlc_object_t
*p_this
, char const *psz_var
,
244 vlc_value_t oldval
, vlc_value_t newval
,
247 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
248 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
250 if( !strcmp( psz_var
, "contrast" ) )
251 SetLevel( &p_sys
->Contrast
, newval
.f_float
);
252 else if( !strcmp( psz_var
, "brightness" ) )
253 SetLevel( &p_sys
->Brightness
, newval
.f_float
);
254 else if( !strcmp( psz_var
, "hue" ) )
255 SetLevel( &p_sys
->Hue
, newval
.f_float
);
256 else if( !strcmp( psz_var
, "saturation" ) )
257 SetLevel( &p_sys
->Saturation
, newval
.f_float
);
262 static void D3D9CloseAdjust(filter_t
*filter
)
264 filter_sys_t
*sys
= filter
->p_sys
;
266 IDirect3DSurface9_Release( sys
->hw_surface
);
267 IDirectXVideoProcessor_Release( sys
->processor
);
268 FreeLibrary( sys
->hdecoder_dll
);
269 vlc_video_context_Release(filter
->vctx_out
);
274 static const struct vlc_filter_operations filter_ops
= {
275 .filter_video
= Filter
, .close
= D3D9CloseAdjust
,
278 static int D3D9OpenAdjust(filter_t
*filter
)
280 filter_sys_t
*sys
= NULL
;
281 HINSTANCE hdecoder_dll
= NULL
;
283 GUID
*processorGUIDs
= NULL
;
284 GUID
*processorGUID
= NULL
;
285 IDirectXVideoProcessorService
*processor
= NULL
;
287 if (filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D9_OPAQUE
288 && filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D9_OPAQUE_10B
)
290 if ( GetD3D9ContextPrivate(filter
->vctx_in
) == NULL
)
292 if (!video_format_IsSimilar(&filter
->fmt_in
.video
, &filter
->fmt_out
.video
))
295 sys
= calloc(1, sizeof (*sys
));
296 if (unlikely(sys
== NULL
))
299 hdecoder_dll
= LoadLibrary(TEXT("DXVA2.DLL"));
303 d3d9_video_context_t
*vtcx_sys
= GetD3D9ContextPrivate( filter
->vctx_in
);
304 D3DFORMAT format
= vtcx_sys
->format
;
306 HRESULT (WINAPI
*CreateVideoService
)(IDirect3DDevice9
*,
310 (void *)GetProcAddress(hdecoder_dll
, "DXVA2CreateVideoService");
311 if (CreateVideoService
== NULL
)
313 msg_Err(filter
, "Can't create video service");
317 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueContext(filter
->vctx_in
);
318 hr
= CreateVideoService( d3d9_decoder
->d3ddev
.dev
, &IID_IDirectXVideoProcessorService
,
322 msg_Err(filter
, "Failed to create the video processor. (hr=0x%lX)", hr
);
327 ZeroMemory(&dsc
, sizeof(dsc
));
328 dsc
.SampleWidth
= filter
->fmt_in
.video
.i_width
;
329 dsc
.SampleHeight
= filter
->fmt_in
.video
.i_height
;
331 if (filter
->fmt_in
.video
.i_frame_rate
&& filter
->fmt_in
.video
.i_frame_rate_base
) {
332 dsc
.InputSampleFreq
.Numerator
= filter
->fmt_in
.video
.i_frame_rate
;
333 dsc
.InputSampleFreq
.Denominator
= filter
->fmt_in
.video
.i_frame_rate_base
;
335 dsc
.InputSampleFreq
.Numerator
= 0;
336 dsc
.InputSampleFreq
.Denominator
= 0;
338 dsc
.OutputFrameFreq
= dsc
.InputSampleFreq
;
340 DXVA2_ExtendedFormat
*pFormat
= &dsc
.SampleFormat
;
341 pFormat
->SampleFormat
= DXVA2_SampleProgressiveFrame
;
344 hr
= IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor
,
350 msg_Err(filter
, "Failed to get processor GUIDs. (hr=0x%lX)", hr
);
354 const UINT neededCaps
= DXVA2_ProcAmp_Brightness
|
355 DXVA2_ProcAmp_Contrast
|
357 DXVA2_ProcAmp_Saturation
;
358 DXVA2_VideoProcessorCaps caps
;
359 unsigned best_score
= 0;
360 for (UINT i
=0; i
<count
; ++i
) {
361 hr
= IDirectXVideoProcessorService_GetVideoProcessorCaps( processor
,
366 if ( FAILED(hr
) || !caps
.ProcAmpControlCaps
)
369 unsigned score
= (caps
.ProcAmpControlCaps
& neededCaps
) ? 10 : 1;
370 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_GetProcAmpRange( processor
, processorGUID
, &dsc
,
383 format
, DXVA2_ProcAmp_Brightness
,
384 &sys
->Brightness
.Range
);
387 msg_Err(filter
, "Failed to get the brightness range. (hr=0x%lX)", hr
);
391 hr
= IDirectXVideoProcessorService_GetProcAmpRange( processor
, processorGUID
, &dsc
,
392 format
, DXVA2_ProcAmp_Contrast
,
393 &sys
->Contrast
.Range
);
396 msg_Err(filter
, "Failed to get the contrast range. (hr=0x%lX)", hr
);
400 hr
= IDirectXVideoProcessorService_GetProcAmpRange( processor
, processorGUID
, &dsc
,
401 format
, DXVA2_ProcAmp_Hue
,
405 msg_Err(filter
, "Failed to get the hue range. (hr=0x%lX)", hr
);
409 hr
= IDirectXVideoProcessorService_GetProcAmpRange( processor
, processorGUID
, &dsc
,
410 format
, DXVA2_ProcAmp_Saturation
,
411 &sys
->Saturation
.Range
);
414 msg_Err(filter
, "Failed to get the saturation range. (hr=0x%lX)", hr
);
418 /* needed to get options passed in transcode using the
419 * adjust{name=value} syntax */
420 config_ChainParse( filter
, "", ppsz_filter_options
, filter
->p_cfg
);
422 InitLevel(filter
, &sys
->Contrast
, "contrast", 1.0 );
423 InitLevel(filter
, &sys
->Brightness
, "brightness", 1.0 );
424 InitLevel(filter
, &sys
->Hue
, "hue", 0.0 );
425 InitLevel(filter
, &sys
->Saturation
, "saturation", 1.0 );
427 var_AddCallback( filter
, "contrast", AdjustCallback
, sys
);
428 var_AddCallback( filter
, "brightness", AdjustCallback
, sys
);
429 var_AddCallback( filter
, "hue", AdjustCallback
, sys
);
430 var_AddCallback( filter
, "saturation", AdjustCallback
, sys
);
431 var_AddCallback( filter
, "gamma", AdjustCallback
, sys
);
432 var_AddCallback( filter
, "brightness-threshold",
433 AdjustCallback
, sys
);
435 hr
= IDirectXVideoProcessorService_CreateVideoProcessor( processor
,
443 msg_Err(filter
, "Failed to create the video processor. (hr=0x%lX)", hr
);
447 hr
= IDirectXVideoProcessorService_CreateSurface( processor
,
448 filter
->fmt_out
.video
.i_width
,
449 filter
->fmt_out
.video
.i_height
,
454 DXVA2_VideoProcessorRenderTarget
,
459 msg_Err(filter
, "Failed to create the hardware surface. (hr=0x%lX)", hr
);
463 CoTaskMemFree(processorGUIDs
);
464 IDirectXVideoProcessorService_Release(processor
);
466 sys
->hdecoder_dll
= hdecoder_dll
;
468 filter
->ops
= &filter_ops
;
470 filter
->vctx_out
= vlc_video_context_Hold(filter
->vctx_in
);
474 CoTaskMemFree(processorGUIDs
);
475 if (sys
&& sys
->processor
)
476 IDirectXVideoProcessor_Release( sys
->processor
);
478 IDirectXVideoProcessorService_Release(processor
);
480 FreeLibrary(hdecoder_dll
);
487 set_description(N_("Direct3D9 adjust filter"))
488 set_category(CAT_VIDEO
)
489 set_subcategory(SUBCAT_VIDEO_VFILTER
)
490 set_callback_video_filter(D3D9OpenAdjust
)
491 add_shortcut( "adjust" )
493 add_float_with_range( "contrast", 1.0, 0.0, 2.0,
494 CONT_TEXT
, CONT_LONGTEXT
, false )
496 add_float_with_range( "brightness", 1.0, 0.0, 2.0,
497 LUM_TEXT
, LUM_LONGTEXT
, false )
499 add_float_with_range( "hue", 0, -180., +180.,
500 HUE_TEXT
, HUE_LONGTEXT
, false )
502 add_float_with_range( "saturation", 1.0, 0.0, 3.0,
503 SAT_TEXT
, SAT_LONGTEXT
, false )
505 add_float_with_range( "gamma", 1.0, 0.01, 10.0,
506 GAMMA_TEXT
, GAMMA_LONGTEXT
, false )
508 add_bool( "brightness-threshold", false,
509 THRES_TEXT
, THRES_LONGTEXT
, false )
513 set_description(N_("Direct3D9 deinterlace filter"))
514 set_deinterlace_callback( D3D9OpenDeinterlace
)
517 set_callback_video_converter( D3D9OpenConverter
, 10 )
520 set_callback_video_converter( D3D9OpenCPUConverter
, 10 )
523 set_description(N_("Direct3D9"))
524 set_callback_dec_device( D3D9OpenDecoderDevice
, 10 )
525 add_shortcut ("dxva2")