qt: playlist: use item title if available
[vlc.git] / modules / hw / d3d9 / d3d9_filters.c
blobcb8bf173e24375d3f9388e1be291383c898f39e1
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 <stdatomic.h>
28 #include <stdlib.h>
29 #include <assert.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>
37 #define COBJMACROS
38 #include <initguid.h>
39 #include <d3d9.h>
40 #include <dxva2api.h>
41 #include "../../video_chroma/d3d9_fmt.h"
43 #include "d3d9_filters.h"
45 struct filter_level
47 atomic_long level;
48 float default_val;
49 float min;
50 float max;
51 DXVA2_ValueRange Range;
54 typedef struct
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;
64 } filter_sys_t;
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 " \
69 "defined below." )
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,
87 picture_t *p_pic,
88 const RECT *p_area )
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;
94 p_sample->Start = 0;
95 p_sample->End =0;
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))
105 return NULL;
107 picture_t *pic = picture_NewFromFormat( &p_filter->fmt_out.video );
108 if (unlikely(pic == NULL))
110 free(pic_ctx);
111 return 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,
120 vctx_sys->format,
121 D3DPOOL_DEFAULT,
122 &pic_ctx->picsys.surface,
123 NULL);
124 if (FAILED(hr))
126 free(pic_ctx);
127 picture_Release(pic);
128 return NULL;
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;
137 return pic;
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 );
147 if( !p_outpic )
148 goto failed;
150 picture_sys_d3d9_t *p_out_sys = ActiveD3D9PictureSys(p_outpic);
151 if( !p_out_sys || !p_out_sys->surface )
152 goto failed;
154 picture_CopyProperties( p_outpic, p_pic );
156 RECT area;
157 D3DSURFACE_DESC srcDesc, dstDesc;
158 HRESULT hr;
160 hr = IDirect3DSurface9_GetDesc( p_src_sys->surface, &srcDesc );
161 if (unlikely(FAILED(hr)))
162 goto failed;
163 hr = IDirect3DSurface9_GetDesc( p_sys->hw_surface, &dstDesc );
164 if (unlikely(FAILED(hr)))
165 goto failed;
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;
181 params.DestData = 0;
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,
189 p_sys->hw_surface,
190 &params,
191 &sample,
192 1, NULL );
193 hr = IDirect3DDevice9_StretchRect( d3d9_decoder->d3ddev.dev,
194 p_sys->hw_surface, NULL,
195 p_out_sys->surface, NULL,
196 D3DTEXF_NONE);
197 if (FAILED(hr))
198 goto failed;
200 picture_Release( p_pic );
201 return p_outpic;
202 failed:
203 picture_Release( p_pic );
204 return NULL;
207 static LONG StoreLevel(const struct filter_level *range, const DXVA2_ValueRange *Range, float val)
209 LONG level;
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);
220 else
221 level = 0;
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,
245 void *p_data )
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 );
259 return VLC_SUCCESS;
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);
271 free(sys);
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;
282 HRESULT hr;
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)
289 return VLC_EGENERIC;
290 if ( GetD3D9ContextPrivate(filter->vctx_in) == NULL )
291 return VLC_EGENERIC;
292 if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
293 return VLC_EGENERIC;
295 sys = calloc(1, sizeof (*sys));
296 if (unlikely(sys == NULL))
297 return VLC_ENOMEM;
299 hdecoder_dll = LoadLibrary(TEXT("DXVA2.DLL"));
300 if (!hdecoder_dll)
301 goto error;
303 d3d9_video_context_t *vtcx_sys = GetD3D9ContextPrivate( filter->vctx_in );
304 D3DFORMAT format = vtcx_sys->format;
306 HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
307 REFIID riid,
308 void **ppService);
309 CreateVideoService =
310 (void *)GetProcAddress(hdecoder_dll, "DXVA2CreateVideoService");
311 if (CreateVideoService == NULL)
313 msg_Err(filter, "Can't create video service");
314 goto error;
317 d3d9_decoder_device_t *d3d9_decoder = GetD3D9OpaqueContext(filter->vctx_in);
318 hr = CreateVideoService( d3d9_decoder->d3ddev.dev, &IID_IDirectXVideoProcessorService,
319 (void**)&processor);
320 if (FAILED(hr))
322 msg_Err(filter, "Failed to create the video processor. (hr=0x%lX)", hr);
323 goto error;
326 DXVA2_VideoDesc dsc;
327 ZeroMemory(&dsc, sizeof(dsc));
328 dsc.SampleWidth = filter->fmt_in.video.i_width;
329 dsc.SampleHeight = filter->fmt_in.video.i_height;
330 dsc.Format = format;
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;
334 } else {
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;
343 UINT count = 0;
344 hr = IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor,
345 &dsc,
346 &count,
347 &processorGUIDs);
348 if (FAILED(hr))
350 msg_Err(filter, "Failed to get processor GUIDs. (hr=0x%lX)", hr);
351 goto error;
354 const UINT neededCaps = DXVA2_ProcAmp_Brightness |
355 DXVA2_ProcAmp_Contrast |
356 DXVA2_ProcAmp_Hue |
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,
362 processorGUIDs+i,
363 &dsc,
364 dsc.Format,
365 &caps);
366 if ( FAILED(hr) || !caps.ProcAmpControlCaps )
367 continue;
369 unsigned score = (caps.ProcAmpControlCaps & neededCaps) ? 10 : 1;
370 if (best_score < score) {
371 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");
379 goto error;
382 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
383 format, DXVA2_ProcAmp_Brightness,
384 &sys->Brightness.Range );
385 if (FAILED(hr))
387 msg_Err(filter, "Failed to get the brightness range. (hr=0x%lX)", hr);
388 goto error;
391 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
392 format, DXVA2_ProcAmp_Contrast,
393 &sys->Contrast.Range );
394 if (FAILED(hr))
396 msg_Err(filter, "Failed to get the contrast range. (hr=0x%lX)", hr);
397 goto error;
400 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
401 format, DXVA2_ProcAmp_Hue,
402 &sys->Hue.Range );
403 if (FAILED(hr))
405 msg_Err(filter, "Failed to get the hue range. (hr=0x%lX)", hr);
406 goto error;
409 hr = IDirectXVideoProcessorService_GetProcAmpRange( processor, processorGUID, &dsc,
410 format, DXVA2_ProcAmp_Saturation,
411 &sys->Saturation.Range );
412 if (FAILED(hr))
414 msg_Err(filter, "Failed to get the saturation range. (hr=0x%lX)", hr);
415 goto error;
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,
436 processorGUID,
437 &dsc,
438 dsc.Format,
440 &sys->processor );
441 if (FAILED(hr))
443 msg_Err(filter, "Failed to create the video processor. (hr=0x%lX)", hr);
444 goto error;
447 hr = IDirectXVideoProcessorService_CreateSurface( processor,
448 filter->fmt_out.video.i_width,
449 filter->fmt_out.video.i_height,
451 format,
452 D3DPOOL_DEFAULT,
454 DXVA2_VideoProcessorRenderTarget,
455 &sys->hw_surface,
456 NULL);
457 if (FAILED(hr))
459 msg_Err(filter, "Failed to create the hardware surface. (hr=0x%lX)", hr);
460 goto error;
463 CoTaskMemFree(processorGUIDs);
464 IDirectXVideoProcessorService_Release(processor);
466 sys->hdecoder_dll = hdecoder_dll;
468 filter->ops = &filter_ops;
469 filter->p_sys = sys;
470 filter->vctx_out = vlc_video_context_Hold(filter->vctx_in);
472 return VLC_SUCCESS;
473 error:
474 CoTaskMemFree(processorGUIDs);
475 if (sys && sys->processor)
476 IDirectXVideoProcessor_Release( sys->processor );
477 if (processor)
478 IDirectXVideoProcessorService_Release(processor);
479 if (hdecoder_dll)
480 FreeLibrary(hdecoder_dll);
481 free(sys);
483 return VLC_EGENERIC;
486 vlc_module_begin()
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 )
495 change_safe()
496 add_float_with_range( "brightness", 1.0, 0.0, 2.0,
497 LUM_TEXT, LUM_LONGTEXT, false )
498 change_safe()
499 add_float_with_range( "hue", 0, -180., +180.,
500 HUE_TEXT, HUE_LONGTEXT, false )
501 change_safe()
502 add_float_with_range( "saturation", 1.0, 0.0, 3.0,
503 SAT_TEXT, SAT_LONGTEXT, false )
504 change_safe()
505 add_float_with_range( "gamma", 1.0, 0.01, 10.0,
506 GAMMA_TEXT, GAMMA_LONGTEXT, false )
507 change_safe()
508 add_bool( "brightness-threshold", false,
509 THRES_TEXT, THRES_LONGTEXT, false )
510 change_safe()
512 add_submodule()
513 set_description(N_("Direct3D9 deinterlace filter"))
514 set_deinterlace_callback( D3D9OpenDeinterlace )
516 add_submodule()
517 set_callback_video_converter( D3D9OpenConverter, 10 )
519 add_submodule()
520 set_callback_video_converter( D3D9OpenCPUConverter, 10 )
522 add_submodule()
523 set_description(N_("Direct3D9"))
524 set_callback_dec_device( D3D9OpenDecoderDevice, 10 )
525 add_shortcut ("dxva2")
526 vlc_module_end()