hw:d3d9: add a file to control the D3D9 device common to all filters
[vlc.git] / modules / hw / d3d9 / d3d9_filters.c
blob9153b931b6166caffa5f4c8da92fd7ce8ff05ae0
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 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <stdlib.h>
28 #include <assert.h>
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_filter.h>
33 #include <vlc_picture.h>
34 #include <vlc_atomic.h>
36 #define COBJMACROS
37 #include <initguid.h>
38 #include <d3d9.h>
39 #include <dxva2api.h>
40 #include "../../video_chroma/d3d9_fmt.h"
42 #include "d3d9_filters.h"
44 struct filter_level
46 atomic_int level;
47 float default_val;
48 float min;
49 float max;
50 DXVA2_ValueRange Range;
53 struct filter_sys_t
55 HINSTANCE hdecoder_dll;
56 /* keep a reference in case the vout is released first */
57 HINSTANCE d3d9_dll;
58 IDirect3DDevice9 *d3ddev;
59 IDirectXVideoProcessor *processor;
60 IDirect3DSurface9 *hw_surface;
62 struct filter_level Brightness;
63 struct filter_level Contrast;
64 struct filter_level Hue;
65 struct filter_level Saturation;
68 #define THRES_TEXT N_("Brightness threshold")
69 #define THRES_LONGTEXT N_("When this mode is enabled, pixels will be " \
70 "shown as black or white. The threshold value will be the brightness " \
71 "defined below." )
72 #define CONT_TEXT N_("Image contrast (0-2)")
73 #define CONT_LONGTEXT N_("Set the image contrast, between 0 and 2. Defaults to 1.")
74 #define HUE_TEXT N_("Image hue (0-360)")
75 #define HUE_LONGTEXT N_("Set the image hue, between 0 and 360. Defaults to 0.")
76 #define SAT_TEXT N_("Image saturation (0-3)")
77 #define SAT_LONGTEXT N_("Set the image saturation, between 0 and 3. Defaults to 1.")
78 #define LUM_TEXT N_("Image brightness (0-2)")
79 #define LUM_LONGTEXT N_("Set the image brightness, between 0 and 2. Defaults to 1.")
80 #define GAMMA_TEXT N_("Image gamma (0-10)")
81 #define GAMMA_LONGTEXT N_("Set the image gamma, between 0.01 and 10. Defaults to 1.")
83 static const char *const ppsz_filter_options[] = {
84 "contrast", "brightness", "hue", "saturation", "gamma",
85 "brightness-threshold", NULL
88 static void FillSample( DXVA2_VideoSample *p_sample,
89 picture_t *p_pic,
90 const RECT *p_area )
92 picture_sys_t *p_sys_src = ActivePictureSys(p_pic);
94 p_sample->SrcSurface = p_sys_src->surface;
95 p_sample->SampleFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
96 p_sample->Start = 0;
97 p_sample->End =0;
98 p_sample->SampleData = 0;
99 p_sample->DstRect = p_sample->SrcRect = *p_area;
100 p_sample->PlanarAlpha = DXVA2_Fixed32OpaqueAlpha();
103 static picture_t *Filter(filter_t *p_filter, picture_t *p_pic)
105 filter_sys_t *p_sys = p_filter->p_sys;
107 picture_sys_t *p_src_sys = ActivePictureSys(p_pic);
109 picture_t *p_outpic = filter_NewPicture( p_filter );
110 if( !p_outpic )
111 goto failed;
113 picture_CopyProperties( p_outpic, p_pic );
115 RECT area;
116 D3DSURFACE_DESC srcDesc, dstDesc;
117 HRESULT hr;
119 hr = IDirect3DSurface9_GetDesc( p_src_sys->surface, &srcDesc );
120 if (unlikely(FAILED(hr)))
121 goto failed;
122 hr = IDirect3DSurface9_GetDesc( p_sys->hw_surface, &dstDesc );
123 if (unlikely(FAILED(hr)))
124 goto failed;
126 area.top = area.left = 0;
127 area.bottom = __MIN(srcDesc.Height, dstDesc.Height);
128 area.right = __MIN(srcDesc.Width, dstDesc.Width);
130 DXVA2_VideoProcessBltParams params = {0};
131 DXVA2_VideoSample sample = {0};
132 FillSample( &sample, p_pic, &area );
134 params.ProcAmpValues.Brightness.Value = atomic_load( &p_sys->Brightness.level );
135 params.ProcAmpValues.Contrast.Value = atomic_load( &p_sys->Contrast.level );
136 params.ProcAmpValues.Hue.Value = atomic_load( &p_sys->Hue.level );
137 params.ProcAmpValues.Saturation.Value = atomic_load( &p_sys->Saturation.level );
138 params.TargetFrame = 0;
139 params.TargetRect = area;
140 params.DestData = 0;
141 params.Alpha = DXVA2_Fixed32OpaqueAlpha();
142 params.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
143 params.BackgroundColor.Alpha = 0xFFFF;
145 hr = IDirectXVideoProcessor_VideoProcessBlt( p_sys->processor,
146 p_sys->hw_surface,
147 &params,
148 &sample,
149 1, NULL );
150 hr = IDirect3DDevice9_StretchRect( p_sys->d3ddev,
151 p_sys->hw_surface, NULL,
152 p_outpic->p_sys->surface, NULL,
153 D3DTEXF_NONE);
154 if (FAILED(hr))
155 goto failed;
157 picture_Release( p_pic );
158 return p_outpic;
159 failed:
160 picture_Release( p_pic );
161 return NULL;
164 static void SetLevel(struct filter_level *range, float val)
166 int level;
167 if (val > range->default_val)
168 level = (range->Range.MaxValue.Value - range->Range.DefaultValue.Value) * (val - range->default_val) /
169 (range->max - range->default_val);
170 else if (val < range->default_val)
171 level = (range->Range.MinValue.Value - range->Range.DefaultValue.Value) * (val - range->default_val) /
172 (range->min - range->default_val);
173 else
174 level = 0;
176 atomic_store( &range->level, range->Range.DefaultValue.Value + level );
179 static void InitLevel(filter_t *filter, struct filter_level *range, const char *p_name, float def)
181 int level;
183 module_config_t *cfg = config_FindConfig(p_name);
184 range->min = cfg->min.f;
185 range->max = cfg->max.f;
186 range->default_val = def;
188 float val = var_CreateGetFloatCommand( filter, p_name );
190 if (val > range->default_val)
191 level = (range->Range.MaxValue.Value - range->Range.DefaultValue.Value) * (val - range->default_val) /
192 (range->max - range->default_val);
193 else if (val < range->default_val)
194 level = (range->Range.MinValue.Value - range->Range.DefaultValue.Value) * (val - range->default_val) /
195 (range->min - range->default_val);
196 else
197 level = 0;
199 atomic_init( &range->level, range->Range.DefaultValue.Value + level );
202 static int AdjustCallback( vlc_object_t *p_this, char const *psz_var,
203 vlc_value_t oldval, vlc_value_t newval,
204 void *p_data )
206 VLC_UNUSED(p_this); VLC_UNUSED(oldval);
207 filter_sys_t *p_sys = (filter_sys_t *)p_data;
209 if( !strcmp( psz_var, "contrast" ) )
210 SetLevel( &p_sys->Contrast, newval.f_float );
211 else if( !strcmp( psz_var, "brightness" ) )
212 SetLevel( &p_sys->Brightness, newval.f_float );
213 else if( !strcmp( psz_var, "hue" ) )
214 SetLevel( &p_sys->Hue, newval.f_float );
215 else if( !strcmp( psz_var, "saturation" ) )
216 SetLevel( &p_sys->Saturation, newval.f_float );
218 return VLC_SUCCESS;
221 static int D3D9OpenAdjust(vlc_object_t *obj)
223 filter_t *filter = (filter_t *)obj;
224 filter_sys_t *sys = NULL;
225 HINSTANCE hdecoder_dll = NULL;
226 HINSTANCE d3d9_dll = NULL;
227 HRESULT hr;
228 picture_t *dst = NULL;
229 GUID *processorGUIDs = NULL;
230 GUID *processorGUID = NULL;
231 IDirectXVideoProcessorService *processor = NULL;
233 if (filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE
234 && filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B)
235 return VLC_EGENERIC;
236 if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
237 return VLC_EGENERIC;
239 d3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
240 if (!d3d9_dll)
241 goto error;
243 hdecoder_dll = LoadLibrary(TEXT("DXVA2.DLL"));
244 if (!hdecoder_dll)
245 goto error;
247 dst = filter_NewPicture(filter);
248 if (dst == NULL)
249 goto error;
251 if (!dst->p_sys)
253 msg_Dbg(filter, "D3D9 opaque without a texture");
254 goto error;
257 sys = calloc(1, sizeof (*sys));
258 if (unlikely(sys == NULL))
259 goto error;
261 HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
262 REFIID riid,
263 void **ppService);
264 CreateVideoService =
265 (void *)GetProcAddress(hdecoder_dll, "DXVA2CreateVideoService");
266 if (CreateVideoService == NULL)
267 goto error;
269 hr = IDirect3DSurface9_GetDevice( dst->p_sys->surface, &sys->d3ddev );
270 if (FAILED(hr))
271 goto error;
273 D3DSURFACE_DESC dstDesc;
274 hr = IDirect3DSurface9_GetDesc( dst->p_sys->surface, &dstDesc );
275 if (unlikely(FAILED(hr)))
276 goto error;
278 hr = CreateVideoService( sys->d3ddev, &IID_IDirectXVideoProcessorService,
279 (void**)&processor);
280 if (FAILED(hr))
281 goto error;
283 DXVA2_VideoDesc dsc;
284 ZeroMemory(&dsc, sizeof(dsc));
285 dsc.SampleWidth = dstDesc.Width;
286 dsc.SampleHeight = dstDesc.Height;
287 dsc.Format = dstDesc.Format;
288 if (filter->fmt_in.video.i_frame_rate && filter->fmt_in.video.i_frame_rate_base) {
289 dsc.InputSampleFreq.Numerator = filter->fmt_in.video.i_frame_rate;
290 dsc.InputSampleFreq.Denominator = filter->fmt_in.video.i_frame_rate_base;
291 } else {
292 dsc.InputSampleFreq.Numerator = 0;
293 dsc.InputSampleFreq.Denominator = 0;
295 dsc.OutputFrameFreq = dsc.InputSampleFreq;
297 DXVA2_ExtendedFormat *pFormat = &dsc.SampleFormat;
298 pFormat->SampleFormat = DXVA2_SampleProgressiveFrame;
300 UINT count = 0;
301 hr = IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor,
302 &dsc,
303 &count,
304 &processorGUIDs);
305 if (FAILED(hr))
306 goto error;
308 const UINT neededCaps = DXVA2_ProcAmp_Brightness |
309 DXVA2_ProcAmp_Contrast |
310 DXVA2_ProcAmp_Hue |
311 DXVA2_ProcAmp_Saturation;
312 DXVA2_VideoProcessorCaps caps;
313 unsigned best_score = 0;
314 for (UINT i=0; i<count; ++i) {
315 hr = IDirectXVideoProcessorService_GetVideoProcessorCaps( processor,
316 processorGUIDs+i,
317 &dsc,
318 dsc.Format,
319 &caps);
320 if ( FAILED(hr) || !caps.ProcAmpControlCaps )
321 continue;
323 unsigned score = (caps.ProcAmpControlCaps & neededCaps) ? 10 : 1;
324 if (best_score < score) {
325 best_score = score;
326 processorGUID = processorGUIDs + i;
330 if (processorGUID == NULL)
332 msg_Dbg(filter, "Could not find a filter to output the required format");
333 goto error;
336 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
337 dstDesc.Format, DXVA2_ProcAmp_Brightness,
338 &sys->Brightness.Range );
339 if (FAILED(hr))
340 goto error;
342 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
343 dstDesc.Format, DXVA2_ProcAmp_Contrast,
344 &sys->Contrast.Range );
345 if (FAILED(hr))
346 goto error;
348 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
349 dstDesc.Format, DXVA2_ProcAmp_Hue,
350 &sys->Hue.Range );
351 if (FAILED(hr))
352 goto error;
354 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
355 dstDesc.Format, DXVA2_ProcAmp_Saturation,
356 &sys->Saturation.Range );
357 if (FAILED(hr))
358 goto error;
360 /* needed to get options passed in transcode using the
361 * adjust{name=value} syntax */
362 config_ChainParse( filter, "", ppsz_filter_options, filter->p_cfg );
364 InitLevel(filter, &sys->Contrast, "contrast", 1.0 );
365 InitLevel(filter, &sys->Brightness, "brightness", 1.0 );
366 InitLevel(filter, &sys->Hue, "hue", 0.0 );
367 InitLevel(filter, &sys->Saturation, "saturation", 1.0 );
369 var_AddCallback( filter, "contrast", AdjustCallback, sys );
370 var_AddCallback( filter, "brightness", AdjustCallback, sys );
371 var_AddCallback( filter, "hue", AdjustCallback, sys );
372 var_AddCallback( filter, "saturation", AdjustCallback, sys );
373 var_AddCallback( filter, "gamma", AdjustCallback, sys );
374 var_AddCallback( filter, "brightness-threshold",
375 AdjustCallback, sys );
377 hr = IDirectXVideoProcessorService_CreateVideoProcessor( processor,
378 processorGUID,
379 &dsc,
380 dsc.Format,
382 &sys->processor );
383 if (FAILED(hr))
384 goto error;
386 hr = IDirectXVideoProcessorService_CreateSurface( processor,
387 dstDesc.Width,
388 dstDesc.Height,
390 dstDesc.Format,
391 D3DPOOL_DEFAULT,
393 DXVA2_VideoProcessorRenderTarget,
394 &sys->hw_surface,
395 NULL);
396 if (FAILED(hr))
397 goto error;
399 CoTaskMemFree(processorGUIDs);
400 picture_Release(dst);
401 IDirectXVideoProcessorService_Release(processor);
403 sys->hdecoder_dll = hdecoder_dll;
404 sys->d3d9_dll = d3d9_dll;
406 filter->pf_video_filter = Filter;
407 filter->p_sys = sys;
409 return VLC_SUCCESS;
410 error:
411 CoTaskMemFree(processorGUIDs);
412 if (sys && sys->processor)
413 IDirectXVideoProcessor_Release( sys->processor );
414 if (processor)
415 IDirectXVideoProcessorService_Release(processor);
416 if (sys && sys->d3ddev)
417 IDirect3DDevice9_Release( sys->d3ddev );
418 if (hdecoder_dll)
419 FreeLibrary(hdecoder_dll);
420 if (d3d9_dll)
421 FreeLibrary(d3d9_dll);
422 if (dst)
423 picture_Release(dst);
424 free(sys);
426 return VLC_EGENERIC;
429 static void D3D9CloseAdjust(vlc_object_t *obj)
431 filter_t *filter = (filter_t *)obj;
432 filter_sys_t *sys = filter->p_sys;
434 IDirect3DSurface9_Release( sys->hw_surface );
435 IDirectXVideoProcessor_Release( sys->processor );
436 IDirect3DDevice9_Release( sys->d3ddev );
437 FreeLibrary( sys->hdecoder_dll );
438 FreeLibrary( sys->d3d9_dll );
440 free(sys);
443 vlc_module_begin()
444 set_description(N_("Direct3D9 adjust filter"))
445 set_capability("video filter", 0)
446 set_category(CAT_VIDEO)
447 set_subcategory(SUBCAT_VIDEO_VFILTER)
448 set_callbacks(D3D9OpenAdjust, D3D9CloseAdjust)
449 add_shortcut( "adjust" )
451 add_float_with_range( "contrast", 1.0, 0.0, 2.0,
452 CONT_TEXT, CONT_LONGTEXT, false )
453 change_safe()
454 add_float_with_range( "brightness", 1.0, 0.0, 2.0,
455 LUM_TEXT, LUM_LONGTEXT, false )
456 change_safe()
457 add_float_with_range( "hue", 0, -180., +180.,
458 HUE_TEXT, HUE_LONGTEXT, false )
459 change_safe()
460 add_float_with_range( "saturation", 1.0, 0.0, 3.0,
461 SAT_TEXT, SAT_LONGTEXT, false )
462 change_safe()
463 add_float_with_range( "gamma", 1.0, 0.01, 10.0,
464 GAMMA_TEXT, GAMMA_LONGTEXT, false )
465 change_safe()
466 add_bool( "brightness-threshold", false,
467 THRES_TEXT, THRES_LONGTEXT, false )
468 change_safe()
470 add_submodule()
471 set_callbacks(D3D9OpenDeinterlace, D3D9CloseDeinterlace)
472 add_shortcut ("deinterlace")
474 add_submodule()
475 set_capability( "video converter", 10 )
476 set_callbacks( D3D9OpenConverter, D3D9CloseConverter )
478 add_submodule()
479 set_callbacks( D3D9OpenCPUConverter, D3D9CloseCPUConverter )
480 set_capability( "video converter", 10 )
481 vlc_module_end()