[vlc-devel] [RFC 1/6] libvlc: add rendering callbacks for D3D11 and D3D9
Steve Lhomme
robux4 at ycbcr.xyz
Thu May 2 10:04:42 CEST 2019
On 4/30/2019 6:41 PM, Rémi Denis-Courmont wrote:
> Le mardi 30 avril 2019, 12:34:33 EEST Steve Lhomme a écrit :
>> ---
>> include/vlc/libvlc_media_player.h | 143 ++++++++++++++++++++++++++++++
>> lib/libvlc.sym | 1 +
>> lib/media_player.c | 39 ++++++++
>> 3 files changed, 183 insertions(+)
>>
>> diff --git a/include/vlc/libvlc_media_player.h
>> b/include/vlc/libvlc_media_player.h index ca72a550d3..51dd964600 100644
>> --- a/include/vlc/libvlc_media_player.h
>> +++ b/include/vlc/libvlc_media_player.h
>> @@ -557,6 +557,149 @@ int libvlc_video_set_output_callbacks(
>> libvlc_media_player_t *mp, libvlc_video_getProcAddress_cb
>> getProcAddress_cb, void* opaque );
>>
>> +
>> +/**
>> + * Enumeration of the Video engine to be used on output.
>> + * can be passed to @a libvlc_video_set_rendering_callbacks
>> + */
>> +typedef enum libvlc_video_rendering_t {
>> + /** Direct3D11 rendering engine */
>> + libvlc_video_rendering_direct3d11,
>> + /** Direct3D9 rendering engine */
>> + libvlc_video_rendering_direct3d9,
>> +} libvlc_video_rendering_t;
>> +
>> +typedef struct
>> +{
>> + unsigned width; /** rendering video width in pixel */
>> + unsigned height; /** rendering video height in pixel */
>> +} libvlc_video_callback_setup_t;
>> +
>> +/**
>> + * List of control passed to the @a libvlc_video_control_cb callback
>> + */
>> +typedef enum libvlc_video_callback_control_t {
>> + /** Setup the rendering environment.
>> + *
>> + * \ref input ignored
>> + * \ref output pointer depending on the type of the engine
>> + *
>> + * For \ref libvlc_video_rendering_direct3d9 the output must be a
>> IDirect3DDevice9*. + * A reference to this object is held until the
>> \ref LIBVLC_VIDEO_DEVICE_CLEANUP is called. + * the device must be
>> created with D3DPRESENT_PARAMETERS.hDeviceWindow set to 0. + *
>> + * For \ref libvlc_video_rendering_direct3d11 the output must be a
>> ID3D11DeviceContext*. + * A reference to this object is held until the
>> \ref LIBVLC_VIDEO_DEVICE_CLEANUP is called. + * The ID3D11Device used
>> to create ID3D11DeviceContext must have multithreading enabled. + */
>> + LIBVLC_VIDEO_DEVICE_SETUP,
>> +
>> + /** Cleanup the rendering environment initialized during \ref
>> LIBVLC_VIDEO_DEVICE_SETUP. + *
>> + * \ref input ignored
>> + * \ref output ignored
>> + */
>> + LIBVLC_VIDEO_DEVICE_CLEANUP,
>> +
>> + /** Update the rendering output setup.
>> + *
>> + * \ref input const libvlc_video_callback_setup_t* with the new setup
>> + * \ref output ignored
>> + */
>> + LIBVLC_VIDEO_UPDATE_OUTPUT,
>> +
>> + /** Callback prototype called after performing drawing calls.
>> + *
>> + * \ref input ignored
>> + * \ref output ignored
>> + */
>> + LIBVLC_VIDEO_SWAP,
>> +
>> + /** Tell the host the rendering is about to start.
>> + *
>> + * \ref input ignored
>> + * \ref output ignored
>> + *
>> + * Between \ref LIBVLC_VIDEO_START_RENDERING and \ref
>> LIBVLC_VIDEO_FINISHED_RENDERING + * of Direct3D9 the following may
>> change on the provided IDirect3DDevice9*: + * - D3DSAMP_ADDRESSU
>> + * - D3DSAMP_ADDRESSV
>> + * - D3DSAMP_MINFILTER
>> + * - D3DSAMP_MAGFILTER
>> + * - D3DRS_AMBIENT
>> + * - D3DRS_CULLMODE
>> + * - D3DRS_ZENABLE
>> + * - D3DRS_LIGHTING
>> + * - D3DRS_DITHERENABLE
>> + * - D3DRS_STENCILENABLE
>> + * - D3DRS_ALPHABLENDENABLE
>> + * - D3DRS_SRCBLEND,D3DBLEND_SRCALPHA
>> + * - D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA
>> + * - D3DPCMPCAPS_GREATER
>> + * - D3DRS_ALPHATESTENABLE
>> + * - D3DRS_ALPHAREF
>> + * - D3DRS_ALPHAFUNC
>> + * - D3DTSS_COLOROP
>> + * - D3DTSS_COLORARG1
>> + * - D3DTSS_ALPHAOP
>> + * - D3DTSS_ALPHAARG1
>> + * - D3DTSS_ALPHAARG2
>> + *
>> + * Between \ref LIBVLC_VIDEO_START_RENDERING and \ref
>> LIBVLC_VIDEO_FINISHED_RENDERING + * of Direct3D11 the following may
>> change on the provided ID3D11DeviceContext*: + * -
>> IASetPrimitiveTopology()
>> + * - IASetInputLayout()
>> + * - IASetVertexBuffers()
>> + * - IASetIndexBuffer()
>> + * - VSSetConstantBuffers()
>> + * - VSSetShader()
>> + * - PSSetSamplers()
>> + * - PSSetConstantBuffers()
>> + * - PSSetShaderResources()
>> + * - PSSetShader()
>> + * - RSSetViewports()
>> + * - DrawIndexed()
>> + */
>> + LIBVLC_VIDEO_START_RENDERING,
>> +
>> + /** Tell the host the rendering has ended.
>> + *
>> + * \ref input ignored
>> + * \ref output ignored
>> + */
>> + LIBVLC_VIDEO_FINISHED_RENDERING,
>> +} libvlc_video_callback_control_t;
>> +
>> +/**
>> + * Callback prototype for the @a libvlc_video_set_surface_callbacks
>> callback. + *
>> + * \param opaque private pointer passed to the @a
>> libvlc_video_set_surface_callbacks() [IN] + * \version LibVLC 4.0.0 or
>> later
>> + */
>> +typedef int (*libvlc_video_control_cb)(void* opaque,
>> + libvlc_video_callback_control_t
>> control, + const void *input,
>> + void **output);
> I really don't think that a type-unsafe API is a good idea for LibVLC. It's
> already hard enough to get right.
It's a balance between simplicity and safety. Having to handle a
message/control means you need to understand what is in "input" and in
"output". It's exactly the same has having to add a callback with the
proper parameters, you also need to understand them.
For the record in a newer version I replaced the "void **output" by a
"void *output". *That* would be a source of problems for a lot of people.
>
>> +
>> +/**
>> + * Set callbacks and data to render decoded video to a custom Direct3D
>> output + *
>> + * \warning VLC will perform video rendering in its own thread and at its
>> own rate, + * You need to provide your own synchronisation mechanism.
>> + *
>> + * \param mp the media player
>> + * \param engine the GPU engine to use
>> + * \param control_cb callback that receives all the \ref
>> libvlc_video_callback_control_t + * \param opaque private pointer passed to
>> the control callback
>> + * \libvlc_return_bool
>> + * \version LibVLC 4.0.0 or later
>> + */
>> +LIBVLC_API
>> +int libvlc_video_set_rendering_callbacks( libvlc_media_player_t *mp,
>> + libvlc_video_rendering_t engine,
>> + libvlc_video_control_cb
>> control_cb, + void* opaque );
>> +
>> /**
>> * Set the NSView handler where the media player should render its video
>> output. *
>> diff --git a/lib/libvlc.sym b/lib/libvlc.sym
>> index ef132c37e4..f212088b30 100644
>> --- a/lib/libvlc.sym
>> +++ b/lib/libvlc.sym
>> @@ -244,6 +244,7 @@ libvlc_video_set_deinterlace
>> libvlc_video_set_format
>> libvlc_video_set_format_callbacks
>> libvlc_video_set_output_callbacks
>> +libvlc_video_set_rendering_callbacks
>> libvlc_video_set_key_input
>> libvlc_video_set_logo_int
>> libvlc_video_set_logo_string
>> diff --git a/lib/media_player.c b/lib/media_player.c
>> index 106e7d26b0..e04f1dab19 100644
>> --- a/lib/media_player.c
>> +++ b/lib/media_player.c
>> @@ -646,6 +646,7 @@ libvlc_media_player_new( libvlc_instance_t *instance )
>> var_Create( mp, "vout-cb-swap", VLC_VAR_ADDRESS );
>> var_Create( mp, "vout-cb-get-proc-address", VLC_VAR_ADDRESS );
>> var_Create( mp, "vout-cb-make-current", VLC_VAR_ADDRESS );
>> + var_Create( mp, "vout-cb-control", VLC_VAR_ADDRESS );
>>
>> var_Create (mp, "avcodec-hw", VLC_VAR_STRING);
>> var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
>> @@ -1202,6 +1203,31 @@ int libvlc_video_set_output_callbacks(
>> libvlc_media_player_t *mp, }
>>
>>
>> +int libvlc_video_set_rendering_callbacks( libvlc_media_player_t *mp,
>> + libvlc_video_rendering_t engine,
>> + libvlc_video_control_cb control_cb,
>> + void* opaque )
>> +{
>> + var_SetString( mp, "window", "wdummy");
>> +
>> + if ( engine == libvlc_video_rendering_direct3d11 )
>> + {
>> + var_SetString ( mp, "vout", "direct3d11" );
>> + var_SetString ( mp, "avcodec-hw", "d3d11va");
>> + }
>> + else if ( engine == libvlc_video_rendering_direct3d9 )
>> + {
>> + var_SetString ( mp, "vout", "direct3d9" );
>> + var_SetString ( mp, "avcodec-hw", "dxva2");
>> + }
>> + else
>> + return 0;
>> +
>> + var_SetAddress( mp, "vout-cb-opaque", opaque );
>> + var_SetAddress( mp, "vout-cb-control", control_cb );
>> + return 1;
>> +}
>> +
>> /**************************************************************************
>> * set_nsobject
>>
>> **************************************************************************/
>> @@ -2049,3 +2075,16 @@ int
>> libvlc_media_player_get_role(libvlc_media_player_t *mp) free(str);
>> return ret;
>> }
>> +
>> +#include <vlc_vout_display.h>
>> +
>> +static_assert(VLC_VIDEO_SURFACE_DEVICE_SETUP ==
>> LIBVLC_VIDEO_DEVICE_SETUP && +
>> VLC_VIDEO_SURFACE_DEVICE_CLEANUP == LIBVLC_VIDEO_DEVICE_CLEANUP && +
>> VLC_VIDEO_SURFACE_UPDATE_OUTPUT ==
>> LIBVLC_VIDEO_UPDATE_OUTPUT && + VLC_VIDEO_SURFACE_SWAP
>> == LIBVLC_VIDEO_SWAP && +
>> VLC_VIDEO_SURFACE_START_RENDERING == LIBVLC_VIDEO_START_RENDERING && +
>> VLC_VIDEO_SURFACE_FINISHED_RENDERING ==
>> LIBVLC_VIDEO_FINISHED_RENDERING && +
>> sizeof(vlc_video_surface_cfg_t) ==
>> sizeof(libvlc_video_callback_setup_t) && +
>> offsetof(vlc_video_surface_cfg_t, width) ==
>> offsetof(libvlc_video_callback_setup_t, width) && +
>> offsetof(vlc_video_surface_cfg_t, height) ==
>> offsetof(libvlc_video_callback_setup_t, height) + , "video
>> surface mismatch");
> What's the point of this static assertion? Structures of different types cannot
> alias even if they have the same layout and representation, AFAIK.
Because the libvlc API mimicks the vlc_vout_display API I did for rendering:
https://code.videolan.org/robUx4/vlc/blob/libvlc/callbacks/59/include/vlc_vout_display.h#L458
In the future it may not be the case and we'll have to write wrappers,
copy each parameters one by one, etc. I think it's easier/faster to have
static asserts while we don't have to do all this.
More information about the vlc-devel
mailing list