1 /*****************************************************************************
2 * d3d11_adjust.c: D3D11 adjust filter (no gamma)
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_configuration.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_picture.h>
35 #include <vlc_atomic.h>
41 #include "../../video_chroma/d3d11_fmt.h"
44 #define D3D11_VIDEO_PROCESSOR_FILTER_CAPS_BRIGHTNESS 0x1
45 #define D3D11_VIDEO_PROCESSOR_FILTER_CAPS_CONTRAST 0x2
46 #define D3D11_VIDEO_PROCESSOR_FILTER_CAPS_HUE 0x4
47 #define D3D11_VIDEO_PROCESSOR_FILTER_CAPS_SATURATION 0x8
50 #define PROCESSOR_SLICES 2
58 D3D11_VIDEO_PROCESSOR_FILTER_RANGE Range
;
64 bool b_brightness_threshold
;
66 struct filter_level Brightness
;
67 struct filter_level Contrast
;
68 struct filter_level Hue
;
69 struct filter_level Saturation
;
71 ID3D11VideoDevice
*d3dviddev
;
72 ID3D11VideoContext
*d3dvidctx
;
73 ID3D11VideoProcessor
*videoProcessor
;
74 ID3D11VideoProcessorEnumerator
*procEnumerator
;
78 ID3D11Texture2D
*outTexture
;
79 ID3D11Resource
*outResource
;
81 ID3D11VideoProcessorInputView
*procInput
[PROCESSOR_SLICES
];
82 ID3D11VideoProcessorOutputView
*procOutput
[PROCESSOR_SLICES
];
85 #define THRES_TEXT N_("Brightness threshold")
86 #define THRES_LONGTEXT N_("When this mode is enabled, pixels will be " \
87 "shown as black or white. The threshold value will be the brightness " \
89 #define CONT_TEXT N_("Image contrast (0-2)")
90 #define CONT_LONGTEXT N_("Set the image contrast, between 0 and 2. Defaults to 1.")
91 #define HUE_TEXT N_("Image hue (0-360)")
92 #define HUE_LONGTEXT N_("Set the image hue, between 0 and 360. Defaults to 0.")
93 #define SAT_TEXT N_("Image saturation (0-3)")
94 #define SAT_LONGTEXT N_("Set the image saturation, between 0 and 3. Defaults to 1.")
95 #define LUM_TEXT N_("Image brightness (0-2)")
96 #define LUM_LONGTEXT N_("Set the image brightness, between 0 and 2. Defaults to 1.")
97 #define GAMMA_TEXT N_("Image gamma (0-10)")
98 #define GAMMA_LONGTEXT N_("Set the image gamma, between 0.01 and 10. Defaults to 1.")
100 static const char *const ppsz_filter_options
[] = {
101 "contrast", "brightness", "hue", "saturation", "gamma",
102 "brightness-threshold", NULL
105 static int assert_ProcessorInput(filter_t
*p_filter
, picture_sys_t
*p_sys_src
)
107 filter_sys_t
*p_sys
= p_filter
->p_sys
;
108 if (!p_sys_src
->processorInput
)
110 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc
= {
112 .ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
,
113 .Texture2D
.MipSlice
= 0,
114 .Texture2D
.ArraySlice
= p_sys_src
->slice_index
,
118 hr
= ID3D11VideoDevice_CreateVideoProcessorInputView(p_sys
->d3dviddev
,
119 p_sys_src
->resource
[KNOWN_DXGI_INDEX
],
120 p_sys
->procEnumerator
,
122 &p_sys_src
->processorInput
);
126 msg_Dbg(p_filter
,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys_src
->slice_index
, hr
);
134 static bool ApplyFilter( filter_sys_t
*p_sys
,
135 D3D11_VIDEO_PROCESSOR_FILTER filter
,
136 const struct filter_level
*p_level
,
137 ID3D11VideoProcessorInputView
*input
,
138 ID3D11VideoProcessorOutputView
*output
)
142 int level
= atomic_load(&p_level
->level
);
143 if (level
== p_level
->Range
.Default
)
146 ID3D11VideoContext_VideoProcessorSetStreamFilter(p_sys
->d3dvidctx
,
147 p_sys
->videoProcessor
,
153 D3D11_VIDEO_PROCESSOR_STREAM stream
= {0};
154 stream
.Enable
= TRUE
;
155 stream
.pInputSurface
= input
;
157 hr
= ID3D11VideoContext_VideoProcessorBlt(p_sys
->d3dvidctx
,
158 p_sys
->videoProcessor
,
161 return SUCCEEDED(hr
);
164 static void SetLevel(struct filter_level
*range
, float val
)
167 if (val
> range
->default_val
)
168 level
= (range
->Range
.Maximum
- range
->Range
.Default
) * (val
- range
->default_val
) /
169 (range
->max
- range
->default_val
);
170 else if (val
< range
->default_val
)
171 level
= (range
->Range
.Minimum
- range
->Range
.Default
) * (val
- range
->default_val
) /
172 (range
->min
- range
->default_val
);
176 atomic_store( &range
->level
, range
->Range
.Default
+ level
);
179 static void InitLevel(filter_t
*filter
, struct filter_level
*range
, const char *p_name
, float def
)
183 module_config_t
*cfg
= config_FindConfig(p_name
);
184 if (unlikely(cfg
== NULL
))
188 range
->default_val
= 1.;
192 range
->min
= cfg
->min
.f
;
193 range
->max
= cfg
->max
.f
;
194 range
->default_val
= def
;
196 float val
= var_CreateGetFloatCommand( filter
, p_name
);
198 if (val
> range
->default_val
)
199 level
= (range
->Range
.Maximum
- range
->Range
.Default
) * (val
- range
->default_val
) /
200 (range
->max
- range
->default_val
);
201 else if (val
< range
->default_val
)
202 level
= (range
->Range
.Minimum
- range
->Range
.Default
) * (val
- range
->default_val
) /
203 (range
->min
- range
->default_val
);
206 atomic_init( &range
->level
, range
->Range
.Default
+ level
);
209 static picture_t
*Filter(filter_t
*p_filter
, picture_t
*p_pic
)
211 filter_sys_t
*p_sys
= p_filter
->p_sys
;
213 picture_sys_t
*p_src_sys
= ActivePictureSys(p_pic
);
214 if ( assert_ProcessorInput(p_filter
, ActivePictureSys(p_pic
) ) )
216 picture_Release( p_pic
);
220 picture_t
*p_outpic
= filter_NewPicture( p_filter
);
223 picture_Release( p_pic
);
227 picture_CopyProperties( p_outpic
, p_pic
);
229 if( p_sys
->context_mutex
!= INVALID_HANDLE_VALUE
)
230 WaitForSingleObjectEx( p_sys
->context_mutex
, INFINITE
, FALSE
);
232 ID3D11VideoProcessorInputView
*inputs
[4] = {
233 p_src_sys
->processorInput
,
239 ID3D11VideoProcessorOutputView
*outputs
[4] = {
240 p_sys
->procOutput
[0],
241 p_sys
->procOutput
[1],
242 p_sys
->procOutput
[0],
248 if ( ApplyFilter( p_sys
,
249 D3D11_VIDEO_PROCESSOR_FILTER_CONTRAST
, &p_sys
->Contrast
,
250 inputs
[idx
], outputs
[idx
] ) )
253 if ( ApplyFilter( p_sys
,
254 D3D11_VIDEO_PROCESSOR_FILTER_BRIGHTNESS
, &p_sys
->Brightness
,
255 inputs
[idx
], outputs
[idx
] ) )
258 if ( ApplyFilter( p_sys
,
259 D3D11_VIDEO_PROCESSOR_FILTER_HUE
, &p_sys
->Hue
,
260 inputs
[idx
], outputs
[idx
] ) )
263 if ( ApplyFilter( p_sys
,
264 D3D11_VIDEO_PROCESSOR_FILTER_SATURATION
, &p_sys
->Saturation
,
265 inputs
[idx
], outputs
[idx
] ) )
268 ID3D11DeviceContext_CopySubresourceRegion(p_outpic
->p_sys
->context
,
269 p_outpic
->p_sys
->resource
[KNOWN_DXGI_INDEX
],
270 p_outpic
->p_sys
->slice_index
,
273 outputs
[idx
] == p_sys
->procOutput
[0] ? 1 : 0,
276 if( p_sys
->context_mutex
!= INVALID_HANDLE_VALUE
)
277 ReleaseMutex( p_sys
->context_mutex
);
279 picture_Release( p_pic
);
283 static int AdjustCallback( vlc_object_t
*p_this
, char const *psz_var
,
284 vlc_value_t oldval
, vlc_value_t newval
,
287 VLC_UNUSED(p_this
); VLC_UNUSED(oldval
);
288 filter_sys_t
*p_sys
= (filter_sys_t
*)p_data
;
290 if( !strcmp( psz_var
, "contrast" ) )
291 SetLevel( &p_sys
->Contrast
, newval
.f_float
);
292 else if( !strcmp( psz_var
, "brightness" ) )
293 SetLevel( &p_sys
->Brightness
, newval
.f_float
);
294 else if( !strcmp( psz_var
, "hue" ) )
295 SetLevel( &p_sys
->Hue
, newval
.f_float
);
296 else if( !strcmp( psz_var
, "saturation" ) )
297 SetLevel( &p_sys
->Saturation
, newval
.f_float
);
302 static int Open(vlc_object_t
*obj
)
304 filter_t
*filter
= (filter_t
*)obj
;
306 ID3D11Device
*d3ddevice
= NULL
;
307 ID3D11VideoProcessorEnumerator
*processorEnumerator
= NULL
;
309 if (filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D11_OPAQUE
310 && filter
->fmt_in
.video
.i_chroma
!= VLC_CODEC_D3D11_OPAQUE_10B
)
312 if (!video_format_IsSimilar(&filter
->fmt_in
.video
, &filter
->fmt_out
.video
))
315 picture_t
*dst
= filter_NewPicture(filter
);
320 msg_Dbg(filter
, "D3D11 opaque without a texture");
321 picture_Release(dst
);
325 D3D11_TEXTURE2D_DESC dstDesc
;
326 ID3D11Texture2D_GetDesc(dst
->p_sys
->texture
[KNOWN_DXGI_INDEX
], &dstDesc
);
328 filter_sys_t
*sys
= malloc(sizeof (*sys
));
329 if (unlikely(sys
== NULL
))
331 memset(sys
, 0, sizeof (*sys
));
333 ID3D11DeviceContext_GetDevice(dst
->p_sys
->context
, &d3ddevice
);
335 hr
= ID3D11Device_QueryInterface(d3ddevice
, &IID_ID3D11VideoDevice
, (void **)&sys
->d3dviddev
);
337 msg_Err(filter
, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr
);
341 hr
= ID3D11DeviceContext_QueryInterface(dst
->p_sys
->context
, &IID_ID3D11VideoContext
, (void **)&sys
->d3dvidctx
);
343 msg_Err(filter
, "Could not Query ID3D11VideoContext Interface from the picture. (hr=0x%lX)", hr
);
347 HANDLE context_lock
= INVALID_HANDLE_VALUE
;
348 UINT dataSize
= sizeof(context_lock
);
349 hr
= ID3D11Device_GetPrivateData(d3ddevice
, &GUID_CONTEXT_MUTEX
, &dataSize
, &context_lock
);
351 msg_Warn(filter
, "No mutex found to lock the decoder");
352 sys
->context_mutex
= context_lock
;
354 const video_format_t
*fmt
= &dst
->format
;
356 D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc
= {
357 .InputFrameFormat
= D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE
,
359 .Numerator
= fmt
->i_frame_rate
,
360 .Denominator
= fmt
->i_frame_rate_base
,
362 .InputWidth
= fmt
->i_width
,
363 .InputHeight
= fmt
->i_height
,
364 .OutputWidth
= dst
->format
.i_width
,
365 .OutputHeight
= dst
->format
.i_height
,
367 .Numerator
= dst
->format
.i_frame_rate
,
368 .Denominator
= dst
->format
.i_frame_rate_base
,
370 .Usage
= D3D11_VIDEO_USAGE_PLAYBACK_NORMAL
,
372 hr
= ID3D11VideoDevice_CreateVideoProcessorEnumerator(sys
->d3dviddev
, &processorDesc
, &processorEnumerator
);
373 if ( processorEnumerator
== NULL
)
375 msg_Dbg(filter
, "Can't get a video processor for the video.");
381 for (int format
= 0; format
< 188; format
++) {
382 hr
= ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator
, format
, &flags
);
383 if (SUCCEEDED(hr
) && (flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
))
384 msg_Dbg(filter
, "processor format %s (%d) is supported for input", DxgiFormatToStr(format
),format
);
385 if (SUCCEEDED(hr
) && (flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
))
386 msg_Dbg(filter
, "processor format %s (%d) is supported for output", DxgiFormatToStr(format
),format
);
389 hr
= ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator
, dst
->p_sys
->formatTexture
, &flags
);
392 msg_Dbg(filter
, "can't read processor support for %s", DxgiFormatToStr(dst
->p_sys
->formatTexture
));
395 if ( !(flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT
) ||
396 !(flags
& D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT
) )
398 msg_Dbg(filter
, "input/output %s is not supported", DxgiFormatToStr(dst
->p_sys
->formatTexture
));
402 D3D11_VIDEO_PROCESSOR_CAPS processorCaps
;
403 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(processorEnumerator
, &processorCaps
);
407 const UINT neededCaps
= D3D11_VIDEO_PROCESSOR_FILTER_CAPS_BRIGHTNESS
|
408 D3D11_VIDEO_PROCESSOR_FILTER_CAPS_CONTRAST
|
409 D3D11_VIDEO_PROCESSOR_FILTER_CAPS_HUE
|
410 D3D11_VIDEO_PROCESSOR_FILTER_CAPS_SATURATION
;
411 if ((processorCaps
.FilterCaps
& neededCaps
) != neededCaps
)
413 msg_Dbg(filter
, "missing capabilities 0x%x", neededCaps
- (processorCaps
.FilterCaps
& neededCaps
));
417 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorFilterRange(processorEnumerator
,
418 D3D11_VIDEO_PROCESSOR_FILTER_BRIGHTNESS
,
419 &sys
->Brightness
.Range
);
423 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorFilterRange(processorEnumerator
,
424 D3D11_VIDEO_PROCESSOR_FILTER_CONTRAST
,
425 &sys
->Contrast
.Range
);
429 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorFilterRange(processorEnumerator
,
430 D3D11_VIDEO_PROCESSOR_FILTER_HUE
,
435 hr
= ID3D11VideoProcessorEnumerator_GetVideoProcessorFilterRange(processorEnumerator
,
436 D3D11_VIDEO_PROCESSOR_FILTER_SATURATION
,
437 &sys
->Saturation
.Range
);
441 /* needed to get options passed in transcode using the
442 * adjust{name=value} syntax */
443 config_ChainParse( filter
, "", ppsz_filter_options
, filter
->p_cfg
);
445 InitLevel(filter
, &sys
->Contrast
, "contrast", 1.0 );
446 InitLevel(filter
, &sys
->Brightness
, "brightness", 1.0 );
447 InitLevel(filter
, &sys
->Hue
, "hue", 0.0 );
448 InitLevel(filter
, &sys
->Saturation
, "saturation", 1.0 );
449 sys
->f_gamma
= var_CreateGetFloatCommand( filter
, "gamma" );
450 sys
->b_brightness_threshold
=
451 var_CreateGetBoolCommand( filter
, "brightness-threshold" );
453 var_AddCallback( filter
, "contrast", AdjustCallback
, sys
);
454 var_AddCallback( filter
, "brightness", AdjustCallback
, sys
);
455 var_AddCallback( filter
, "hue", AdjustCallback
, sys
);
456 var_AddCallback( filter
, "saturation", AdjustCallback
, sys
);
457 var_AddCallback( filter
, "gamma", AdjustCallback
, sys
);
458 var_AddCallback( filter
, "brightness-threshold",
459 AdjustCallback
, sys
);
461 hr
= ID3D11VideoDevice_CreateVideoProcessor(sys
->d3dviddev
,
462 processorEnumerator
, 0,
463 &sys
->videoProcessor
);
464 if (FAILED(hr
) || sys
->videoProcessor
== NULL
)
466 msg_Dbg(filter
, "failed to create the processor");
470 D3D11_TEXTURE2D_DESC texDesc
;
471 ZeroMemory(&texDesc
, sizeof(texDesc
));
472 texDesc
.MipLevels
= 1;
473 texDesc
.SampleDesc
.Count
= 1;
474 texDesc
.MiscFlags
= 0; //D3D11_RESOURCE_MISC_SHARED;
475 texDesc
.Usage
= D3D11_USAGE_DEFAULT
;
476 texDesc
.CPUAccessFlags
= 0;
477 texDesc
.Format
= dstDesc
.Format
;
478 texDesc
.BindFlags
= D3D11_BIND_RENDER_TARGET
;
479 texDesc
.Usage
= D3D11_USAGE_DEFAULT
;
480 texDesc
.CPUAccessFlags
= 0;
481 texDesc
.ArraySize
= PROCESSOR_SLICES
;
482 texDesc
.Height
= dstDesc
.Height
;
483 texDesc
.Width
= dstDesc
.Width
;
485 hr
= ID3D11Device_CreateTexture2D( d3ddevice
, &texDesc
, NULL
, &sys
->outTexture
);
487 msg_Err(filter
, "CreateTexture2D failed. (hr=0x%0lx)", hr
);
491 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc
= {
492 .ViewDimension
= D3D11_VPOV_DIMENSION_TEXTURE2DARRAY
,
493 .Texture2DArray
.MipSlice
= 0,
494 .Texture2DArray
.ArraySize
= 1,
497 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc
= {
499 .ViewDimension
= D3D11_VPIV_DIMENSION_TEXTURE2D
,
500 .Texture2D
.MipSlice
= 0,
503 for (int i
=0; i
<PROCESSOR_SLICES
; i
++)
505 outDesc
.Texture2DArray
.FirstArraySlice
= i
;
506 hr
= ID3D11VideoDevice_CreateVideoProcessorOutputView(sys
->d3dviddev
,
510 &sys
->procOutput
[i
]);
513 msg_Dbg(filter
,"Failed to create processor output. (hr=0x%lX)", hr
);
517 inDesc
.Texture2D
.ArraySlice
= i
;
518 hr
= ID3D11VideoDevice_CreateVideoProcessorInputView(sys
->d3dviddev
,
526 msg_Dbg(filter
,"Failed to create processor input. (hr=0x%lX)", hr
);
531 sys
->procEnumerator
= processorEnumerator
;
533 filter
->pf_video_filter
= Filter
;
536 ID3D11Device_Release(d3ddevice
);
537 picture_Release(dst
);
541 ID3D11Device_Release(d3ddevice
);
542 picture_Release(dst
);
544 for (int i
=0; i
<PROCESSOR_SLICES
; i
++)
546 if (sys
->procInput
[i
])
547 ID3D11VideoProcessorInputView_Release(sys
->procInput
[i
]);
548 if (sys
->procOutput
[i
])
549 ID3D11VideoProcessorOutputView_Release(sys
->procOutput
[i
]);
553 ID3D11Texture2D_Release(sys
->outTexture
);
554 if (sys
->videoProcessor
)
555 ID3D11VideoProcessor_Release(sys
->videoProcessor
);
556 if (processorEnumerator
)
557 ID3D11VideoProcessorEnumerator_Release(processorEnumerator
);
559 ID3D11VideoContext_Release(sys
->d3dvidctx
);
561 ID3D11VideoDevice_Release(sys
->d3dviddev
);
566 static void Close(vlc_object_t
*obj
)
568 filter_t
*filter
= (filter_t
*)obj
;
569 filter_sys_t
*sys
= filter
->p_sys
;
571 var_DelCallback( filter
, "contrast", AdjustCallback
, sys
);
572 var_DelCallback( filter
, "brightness", AdjustCallback
, sys
);
573 var_DelCallback( filter
, "hue", AdjustCallback
, sys
);
574 var_DelCallback( filter
, "saturation", AdjustCallback
, sys
);
575 var_DelCallback( filter
, "gamma", AdjustCallback
, sys
);
576 var_DelCallback( filter
, "brightness-threshold",
577 AdjustCallback
, sys
);
579 for (int i
=0; i
<PROCESSOR_SLICES
; i
++)
581 ID3D11VideoProcessorInputView_Release(sys
->procInput
[i
]);
582 ID3D11VideoProcessorOutputView_Release(sys
->procOutput
[i
]);
584 ID3D11Texture2D_Release(sys
->outTexture
);
585 ID3D11VideoProcessor_Release(sys
->videoProcessor
);
586 ID3D11VideoProcessorEnumerator_Release(sys
->procEnumerator
);
587 ID3D11VideoContext_Release(sys
->d3dvidctx
);
588 ID3D11VideoDevice_Release(sys
->d3dviddev
);
594 set_description(N_("Direct3D11 adjust filter"))
595 set_capability("video filter", 0)
596 set_category( CAT_VIDEO
)
597 set_subcategory( SUBCAT_VIDEO_VFILTER
)
598 set_callbacks(Open
, Close
)
599 add_shortcut( "adjust" )
601 add_float_with_range( "contrast", 1.0, 0.0, 2.0,
602 CONT_TEXT
, CONT_LONGTEXT
, false )
604 add_float_with_range( "brightness", 1.0, 0.0, 2.0,
605 LUM_TEXT
, LUM_LONGTEXT
, false )
607 add_float_with_range( "hue", 0, -180., +180.,
608 HUE_TEXT
, HUE_LONGTEXT
, false )
610 add_float_with_range( "saturation", 1.0, 0.0, 3.0,
611 SAT_TEXT
, SAT_LONGTEXT
, false )
613 add_float_with_range( "gamma", 1.0, 0.01, 10.0,
614 GAMMA_TEXT
, GAMMA_LONGTEXT
, false )
616 add_bool( "brightness-threshold", false,
617 THRES_TEXT
, THRES_LONGTEXT
, false )