[vlc-devel] [PATCH] [RFC] always output an opaque D3D9 surface from DXVA

Rémi Denis-Courmont remi at remlab.net
Thu Apr 23 17:51:05 CEST 2015


Le jeudi 23 avril 2015, 17:11:19 Steve Lhomme a écrit :
> This is a test code to show how DXVA direct rendering could be done.
> 
> When avcodec asks for an AVFrame, the vout and its decoder pool is already
> created. So we use the D3D9 device from the picture_t coming from that pool
> to recreate the DXVA2 decoder surface.
> 
> This is working fine for normal playback, seeking, opening files of
> same/different dimensions.
> 
> The decoder of D3D9 surface is created in the vout wrapper (like all the
> others) but with a dirty hack for now.
> 
> This code also works with direct rendering to D3D9, not included in this
> patch.
> 
> The changes to the other va->Setup callback are not done and it's a
> temporary test code. ---
>  include/vlc_fourcc.h            |   3 +
>  modules/codec/avcodec/dxva2.c   | 173 ++++++++++++++++++++++------
>  modules/codec/avcodec/va.h      |   6 +-
>  modules/codec/avcodec/video.c   |  19 +--
>  modules/video_chroma/copy.c     |  40 +++++--
>  src/Makefile.am                 |   2 +
>  src/misc/fourcc.c               |   3 +
>  src/video_output/vout_wrapper.c |  17 ++-
>  src/win32/direct3d9_pool.c      | 248
> ++++++++++++++++++++++++++++++++++++++++ src/win32/direct3d9_pool.h      | 
> 39 +++++++
>  10 files changed, 491 insertions(+), 59 deletions(-)
>  create mode 100644 src/win32/direct3d9_pool.c
>  create mode 100644 src/win32/direct3d9_pool.h
> 
> diff --git a/include/vlc_fourcc.h b/include/vlc_fourcc.h
> index 5d30ece..df82f6f 100644
> --- a/include/vlc_fourcc.h
> +++ b/include/vlc_fourcc.h
> @@ -332,6 +332,9 @@
>  /* Broadcom MMAL opaque buffer type */
>  #define VLC_CODEC_MMAL_OPAQUE     VLC_FOURCC('M','M','A','L')
> 
> +/* DXVA2 opaque video surface for use with D3D9 */
> +#define VLC_CODEC_D3D9_OPAQUE     VLC_FOURCC('D','X','A','9')
> +
>  /* Image codec (video) */
>  #define VLC_CODEC_PNG             VLC_FOURCC('p','n','g',' ')
>  #define VLC_CODEC_PPM             VLC_FOURCC('p','p','m',' ')
> diff --git a/modules/codec/avcodec/dxva2.c b/modules/codec/avcodec/dxva2.c
> index db9820f..4382e24 100644
> --- a/modules/codec/avcodec/dxva2.c
> +++ b/modules/codec/avcodec/dxva2.c
> @@ -42,6 +42,7 @@
>  #include <vlc_cpu.h>
>  #include <vlc_plugin.h>
>  #include <vlc_codecs.h>
> +#include <vlc_filter.h>
> 
>  #include <libavcodec/avcodec.h>
>  #    define DXVA2API_USE_BITFIELDS
> @@ -55,12 +56,19 @@
>  static int Open(vlc_va_t *, AVCodecContext *, const es_format_t *);
>  static void Close(vlc_va_t *, AVCodecContext *);
> 
> +static int  OpenFilter( vlc_object_t * );
> +static void CloseFilter( vlc_object_t * );
> +
>  vlc_module_begin()
>      set_description(N_("DirectX Video Acceleration (DXVA) 2.0"))
>      set_capability("hw decoder", 0)
>      set_category(CAT_INPUT)
>      set_subcategory(SUBCAT_INPUT_VCODEC)
>      set_callbacks(Open, Close)
> +    add_submodule()
> +        set_description( N_("DXVA2 surface to planes") )
> +        set_capability( "video filter2", 10 )
> +        set_callbacks( OpenFilter, CloseFilter )
>  vlc_module_end()
> 
>  #include <windows.h>
> @@ -348,6 +356,11 @@ struct vlc_va_sys_t
>      LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
>  };
> 
> +struct picture_sys_t
> +{
> +    LPDIRECT3DSURFACE9 surface;
> +};

You don't need a structure to store a single pointer (assuming you won't be 
adding more stuff).

> +
>  /* */
>  static int D3dCreateDevice(vlc_va_t *);
>  static void D3dDestroyDevice(vlc_va_sys_t *);
> @@ -369,9 +382,49 @@ static void DxCreateVideoConversion(vlc_va_sys_t *);
>  static void DxDestroyVideoConversion(vlc_va_sys_t *);
> 
>  /* */
> -static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t *chroma)
> +static int Setup(vlc_va_t *va, AVCodecContext *avctx, vlc_fourcc_t
> *chroma, picture_t *p_test_output) {
>      vlc_va_sys_t *sys = va->sys;
> +    LPDIRECT3DDEVICE9 picd3ddev = NULL;
> +    if (p_test_output!=NULL && p_test_output->p_sys)
> +        IDirect3DSurface9_GetDevice(p_test_output->p_sys->surface,
> &picd3ddev);
> +
> +    if ( picd3ddev != NULL && picd3ddev != sys->d3ddev )
> +    {
> +        /* restart the dxva decoder with the appropriate D3D9 device */
> +        DxDestroyVideoDecoder(sys);
> +        DxDestroyVideoService(sys);
> +        D3dDestroyDeviceManager(sys);
> +        D3dDestroyDevice(sys);
> +
> +        if (FAILED(IDirect3DDevice9_GetDirect3D(picd3ddev, &sys->d3dobj)))
> { +            msg_Err(va, "Can't retrieve the D3D from the device"); +    
>        goto error;
> +        }
> +        IDirect3D9_AddRef(sys->d3dobj);
> +        IDirect3DDevice9_AddRef(picd3ddev);
> +        sys->d3ddev = picd3ddev;
> +        msg_Dbg(va, "D3dCreateDevice succeed");
> +
> +        if (D3dCreateDeviceManager(va)) {
> +            msg_Err(va, "D3dCreateDeviceManager failed");
> +            goto error;
> +        }
> +
> +        if (DxCreateVideoService(va)) {
> +            msg_Err(va, "DxCreateVideoService failed");
> +            goto error;
> +        }
> +
> +        /* */
> +        es_format_t fake_fmt;
> +        memset(&fake_fmt, 0, sizeof(fake_fmt));
> +        fake_fmt.i_profile = avctx->profile;
> +        if (DxFindVideoServiceConversion(va, &sys->input, &sys->render,
> &fake_fmt)) { +            msg_Err(va, "DxFindVideoServiceConversion
> failed");
> +            goto error;
> +        }
> +    }
> 
>      if (sys->width == avctx->coded_width && sys->height ==
> avctx->coded_height && sys->decoder != NULL)
> @@ -406,33 +459,40 @@ static int Setup(vlc_va_t *va, AVCodecContext *avctx,
> vlc_fourcc_t *chroma) /* */
>  ok:
>      avctx->hwaccel_context = &sys->hw;
> -    const d3d_format_t *output = D3dFindFormat(sys->output);
> -    *chroma = output->codec;
> +    *chroma = VLC_CODEC_D3D9_OPAQUE;
> 
>      return VLC_SUCCESS;
> +
> +error:
> +    DxDestroyVideoDecoder(sys);
> +    DxDestroyVideoService(sys);
> +    D3dDestroyDeviceManager(sys);
> +    D3dDestroyDevice(sys);
> +    return VLC_EGENERIC;
>  }
> 
> -static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
> +static void DXA9_I420 (filter_t *p_filter, picture_t *src, picture_t *dst)
>  {
> -    vlc_va_sys_t *sys = va->sys;
> -    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
> +    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
> 
>      if (!sys->surface_cache.buffer)
>          return VLC_EGENERIC;
> 
> -    /* */
> -    assert(sys->output == MAKEFOURCC('Y','V','1','2'));
> +    LPDIRECT3DSURFACE9 d3d = src->p_sys->surface;
> +    D3DSURFACE_DESC desc;
> +    if (FAILED( IDirect3DSurface9_GetDesc(d3d, &desc) ))
> +        return;
> 
>      /* */
>      D3DLOCKED_RECT lock;
>      if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL,
> D3DLOCK_READONLY))) { -        msg_Err(va, "Failed to lock surface");
> -        return VLC_EGENERIC;
> +        msg_Err(p_filter, "Failed to lock surface");
> +        return;
>      }
> 
> -    if (sys->render == MAKEFOURCC('Y','V','1','2') ||
> -        sys->render == MAKEFOURCC('I','M','C','3')) {
> -        bool imc3 = sys->render == MAKEFOURCC('I','M','C','3');
> +    if (desc.Format == MAKEFOURCC('Y','V','1','2') ||
> +        desc.Format == MAKEFOURCC('I','M','C','3')) {
> +        bool imc3 = desc.Format == MAKEFOURCC('I','M','C','3');
>          size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);
> 
>          size_t pitch[3] = {
> @@ -443,9 +503,9 @@ static int Extract(vlc_va_t *va, picture_t *picture,
> uint8_t *data)
> 
>          uint8_t *plane[3] = {
>              (uint8_t*)lock.pBits,
> -            (uint8_t*)lock.pBits + pitch[0] * sys->surface_height,
> -            (uint8_t*)lock.pBits + pitch[0] * sys->surface_height
> -                                 + pitch[1] * sys->surface_height / 2,
> +            (uint8_t*)lock.pBits + pitch[0] * src->format.i_height,
> +            (uint8_t*)lock.pBits + pitch[0] * src->format.i_height
> +                                 + pitch[1] * src->format.i_height / 2,
>          };
> 
>          if (imc3) {
> @@ -453,24 +513,49 @@ static int Extract(vlc_va_t *va, picture_t *picture,
> uint8_t *data) plane[1] = plane[2];
>              plane[2] = V;
>          }
> -        CopyFromYv12(picture, plane, pitch, sys->width, sys->height,
> -                     &sys->surface_cache);
> -    } else {
> -        assert(sys->render == MAKEFOURCC('N','V','1','2'));
> +        CopyFromYv12(dst, plane, pitch, src->format.i_width,
> src->format.i_visible_height, +                     p_copy_cache);
> +    } else if (desc.Format == MAKEFOURCC('N','V','1','2')) {
>          uint8_t *plane[2] = {
>              lock.pBits,
> -            (uint8_t*)lock.pBits + lock.Pitch * sys->surface_height
> +            (uint8_t*)lock.pBits + lock.Pitch * src->format.i_height
>          };
>          size_t  pitch[2] = {
>              lock.Pitch,
>              lock.Pitch,
>          };
> -        CopyFromNv12(picture, plane, pitch, sys->width, sys->height,
> -                     &sys->surface_cache);
> +        CopyFromNv12(dst, plane, pitch, src->format.i_width,
> src->format.i_visible_height, +                     p_copy_cache);
> +    } else {
> +        msg_Err(p_filter, "Unsupported DXA9 conversion to 0x%08X",
> desc.Format); }
> 
>      /* */
>      IDirect3DSurface9_UnlockRect(d3d);
> +}
> +
> +static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
> +{
> +    vlc_va_sys_t *sys = va->sys;
> +    LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)data;
> +    picture_sys_t *p_sys = picture->p_sys;
> +    LPDIRECT3DSURFACE9 output = p_sys->surface;
> +
> +    assert(d3d != output);
> +#ifndef NDEBUG
> +    LPDIRECT3DDEVICE9 srcDevice, dstDevice;
> +    IDirect3DSurface9_GetDevice(d3d, &srcDevice);
> +    IDirect3DSurface9_GetDevice(output, &dstDevice);
> +    assert(srcDevice == dstDevice);
> +#endif
> +
> +    HRESULT hr;
> +    hr = IDirect3DDevice9_StretchRect( sys->d3ddev, d3d, NULL, output,
> NULL, D3DTEXF_NONE);
> +    if (FAILED(hr)) {
> +        msg_Err(va, "Failed to copy the hw surface to the decoder surface
> (hr=0x%0lx)", hr );
> +        return VLC_EGENERIC;
> +    }
> +

OK but I would expect Extract to be unnecessary when doing direct rendering 
(the one in VDPAU is pretty much doing nothing).

>      return VLC_SUCCESS;
>  }
> 
> @@ -1078,21 +1163,37 @@ static int DxResetVideoDecoder(vlc_va_t *va)
>      return VLC_EGENERIC;
>  }
> 
> -static void DxCreateVideoConversion(vlc_va_sys_t *va)
> +VIDEO_FILTER_WRAPPER (DXA9_I420)
> +
> +static int OpenFilter( vlc_object_t *obj )
>  {
> -    switch (va->render) {
> -    case MAKEFOURCC('N','V','1','2'):
> -    case MAKEFOURCC('I','M','C','3'):
> -        va->output = MAKEFOURCC('Y','V','1','2');
> -        break;
> -    default:
> -        va->output = va->render;
> -        break;
> -    }
> -    CopyInitCache(&va->surface_cache, va->surface_width);
> +    filter_t *p_filter = (filter_t *)obj;
> +    if ( p_filter->fmt_in.video.i_height !=
> p_filter->fmt_out.video.i_height +         ||
> p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width ) +      
>  return VLC_EGENERIC;
> +
> +    if ( p_filter->fmt_out.video.i_chroma != VLC_CODEC_I420
> +         && p_filter->fmt_out.video.i_chroma != VLC_CODEC_YV12
> +         && p_filter->fmt_out.video.i_chroma != VLC_CODEC_NV12 )
> +        return VLC_EGENERIC;
> +
> +    if ( p_filter->fmt_in.video.i_chroma == VLC_CODEC_D3D9_OPAQUE )
> +        p_filter->pf_video_filter = DXA9_I420_Filter;
> +    else
> +        return VLC_EGENERIC;
> +
> +    copy_cache_t *p_copy_cache = calloc(1, sizeof(*p_copy_cache));
> +    CopyInitCache(p_copy_cache, p_filter->fmt_in.video.i_width );
> +    p_filter->p_sys = (filter_sys_t*) p_copy_cache;
> +
> +    return VLC_SUCCESS;
>  }
> 
> -static void DxDestroyVideoConversion(vlc_va_sys_t *va)
> +static void CloseFilter( vlc_object_t *obj )
>  {
> -    CopyCleanCache(&va->surface_cache);
> +    filter_t *p_filter = (filter_t *)obj;
> +    copy_cache_t *p_copy_cache = (copy_cache_t*) p_filter->p_sys;
> +    CopyCleanCache(p_copy_cache);
> +    free( p_copy_cache );
> +    p_filter->p_sys = NULL;
>  }
> diff --git a/modules/codec/avcodec/va.h b/modules/codec/avcodec/va.h
> index a5ffeae..cbf43e9 100644
> --- a/modules/codec/avcodec/va.h
> +++ b/modules/codec/avcodec/va.h
> @@ -37,7 +37,7 @@ struct vlc_va_t {
>      const char *description;
>      int pix_fmt;
> 
> -    int  (*setup)(vlc_va_t *, AVCodecContext *, vlc_fourcc_t *output);
> +    int  (*setup)(vlc_va_t *, AVCodecContext *, vlc_fourcc_t *output,
> picture_t *p_test_output);
> int  (*get)(vlc_va_t *, picture_t *pic, uint8_t
> **data);
>      void (*release)(void *pic, uint8_t *surface);
>      int  (*extract)(vlc_va_t *, picture_t *pic, uint8_t *data);
> @@ -58,9 +58,9 @@ vlc_va_t *vlc_va_New(vlc_object_t *obj, AVCodecContext *,
> const es_format_t *fmt * @return VLC_SUCCESS on success, otherwise an error
> code.
>   */
>  static inline int vlc_va_Setup(vlc_va_t *va, AVCodecContext *avctx,
> -                               vlc_fourcc_t *output)
> +                               vlc_fourcc_t *output, picture_t
> *p_test_output) {
> -    return va->setup(va, avctx, output);
> +    return va->setup(va, avctx, output, p_test_output);
>  }
> 
>  /**
> diff --git a/modules/codec/avcodec/video.c b/modules/codec/avcodec/video.c
> index 71f8fbf..30fd185 100644
> --- a/modules/codec/avcodec/video.c
> +++ b/modules/codec/avcodec/video.c
> @@ -1063,10 +1063,19 @@ static int lavc_GetFrame(struct AVCodecContext *ctx,
> AVFrame *frame, int flags) frame->opaque = NULL;
> 
>      wait_mt(sys);
> +    /* The semaphore protects updates to fmt_out */
> +    pic = ffmpeg_NewPictBuf(dec, ctx);

You need to set i_chroma first.

> +    if (pic == NULL)
> +    {
> +        post_mt(sys);
> +        return -1;
> +    }
> +
>      if (sys->p_va != NULL)
>      {   /* TODO: Move this to get_format(). We are screwed if it fails
> here. */
> -        if (vlc_va_Setup(sys->p_va, ctx,
> &dec->fmt_out.video.i_chroma))
> +        if (vlc_va_Setup(sys->p_va, ctx,
> &dec->fmt_out.video.i_chroma, pic)) {

And you can't change i_chroma after you've allocated a picture.

> +            picture_Release(pic);
>              post_mt(sys);
>              msg_Err(dec, "hardware acceleration setup failed");
>              return -1;
> @@ -1074,15 +1083,11 @@ static int lavc_GetFrame(struct AVCodecContext *ctx,
> AVFrame *frame, int flags) }
>      else if (!sys->b_direct_rendering)
>      {
> +        picture_Release(pic);
>          post_mt(sys);
>          return avcodec_default_get_buffer2(ctx, frame, flags);
>      }
> -
> -    /* The semaphore protects updates to fmt_out */
> -    pic = ffmpeg_NewPictBuf(dec, ctx);
>      post_mt(sys);
> -    if (pic == NULL)
> -        return -1;
> 
>      if (sys->p_va != NULL)
>          return lavc_va_GetFrame(ctx, frame, pic);
> @@ -1142,7 +1147,7 @@ static enum PixelFormat ffmpeg_GetFormat(
> AVCodecContext *p_context, /* We try to call vlc_va_Setup when possible to
> detect errors when * possible (later is too late) */
>          if( p_context->width > 0 && p_context->height > 0
> -         && vlc_va_Setup( p_va, p_context, &p_dec->fmt_out.video.i_chroma )
> ) +         && vlc_va_Setup( p_va, p_context,
> &p_dec->fmt_out.video.i_chroma, NULL ) ) {
>              msg_Err( p_dec, "acceleration setup failure" );
>              break;
> diff --git a/modules/video_chroma/copy.c b/modules/video_chroma/copy.c
> index cc98c92..2ddf9ef 100644
> --- a/modules/video_chroma/copy.c
> +++ b/modules/video_chroma/copy.c
> @@ -326,11 +326,23 @@ static void SSE_CopyFromNv12(picture_t *dst,
>                    src[0], src_pitch[0],
>                    cache->buffer, cache->size,
>                    width, height, cpu);
> -    SSE_SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> -                    dst->p[1].p_pixels, dst->p[1].i_pitch,
> -                    src[1], src_pitch[1],
> -                    cache->buffer, cache->size,
> -                    (width+1)/2, (height+1)/2, cpu);
> +    if( dst->format.i_chroma == VLC_CODEC_YV12 )
> +        SSE_SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> +                        dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                        src[1], src_pitch[1],
> +                        cache->buffer, cache->size,
> +                        (width+1)/2, (height+1)/2, cpu);
> +    else if( dst->format.i_chroma == VLC_CODEC_I420 )
> +        SSE_SplitPlanes(dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                        dst->p[2].p_pixels, dst->p[2].i_pitch,
> +                        src[1], src_pitch[1],
> +                        cache->buffer, cache->size,
> +                        (width+1)/2, (height+1)/2, cpu);
> +    else if( dst->format.i_chroma == VLC_CODEC_NV12 )
> +        SSE_CopyPlane(dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                      src[1], src_pitch[1],
> +                      cache->buffer, cache->size,
> +                      width, height/2, cpu);
>      asm volatile ("emms");
>  }
> 
> @@ -394,10 +406,20 @@ void CopyFromNv12(picture_t *dst, uint8_t *src[2],
> size_t src_pitch[2], CopyPlane(dst->p[0].p_pixels, dst->p[0].i_pitch,
>                src[0], src_pitch[0],
>                width, height);
> -    SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> -                dst->p[1].p_pixels, dst->p[1].i_pitch,
> -                src[1], src_pitch[1],
> -                width/2, height/2);
> +    if( dst->format.i_chroma == VLC_CODEC_YV12 )
> +        SplitPlanes(dst->p[2].p_pixels, dst->p[2].i_pitch,
> +                    dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                    src[1], src_pitch[1],
> +                    width/2, height/2);
> +    else if( dst->format.i_chroma = VLC_CODEC_I420 )
> +        SplitPlanes(dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                    dst->p[2].p_pixels, dst->p[2].i_pitch,
> +                    src[1], src_pitch[1],
> +                    width/2, height/2);
> +    else if( dst->format.i_chroma == VLC_CODEC_NV12 )
> +        CopyPlane(dst->p[1].p_pixels, dst->p[1].i_pitch,
> +                      src[1], src_pitch[1],
> +                      width, height/2);
>  }
> 
>  void CopyFromYv12(picture_t *dst, uint8_t *src[3], size_t src_pitch[3],
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 1f02494..a2489ab 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -290,6 +290,8 @@ SOURCES_libvlc_linux = \
>  	$(NULL)
> 
>  SOURCES_libvlc_win32 = \
> +	win32/direct3d9_pool.c \
> +	win32/direct3d9_pool.h \
>  	win32/dirs.c \
>  	win32/error.c \
>  	win32/filesystem.c \
> diff --git a/src/misc/fourcc.c b/src/misc/fourcc.c
> index 3b5c9b3..032a12c 100644
> --- a/src/misc/fourcc.c
> +++ b/src/misc/fourcc.c
> @@ -2193,6 +2193,9 @@ static const struct
>      { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, },
>                                                 FAKE_FMT() },
> 
> +    { { VLC_CODEC_D3D9_OPAQUE, },
> +                                               FAKE_FMT() },
> +
>      { { 0 },                                   FAKE_FMT() }
>  };
> 
> diff --git a/src/video_output/vout_wrapper.c
> b/src/video_output/vout_wrapper.c index d172bcc..3442bd0 100644
> --- a/src/video_output/vout_wrapper.c
> +++ b/src/video_output/vout_wrapper.c
> @@ -35,6 +35,9 @@
>  #include <assert.h>
>  #include "vout_internal.h"
>  #include "display.h"
> +#if defined(_WIN32)
> +#include "../win32/direct3d9_pool.h"
> +#endif
> 
>  /**************************************************************************
> *** * Local prototypes
> @@ -139,10 +142,16 @@ int vout_InitWrapper(vout_thread_t *vout)
>          sys->decoder_pool = display_pool;
>          sys->display_pool = display_pool;
>      } else if (!sys->decoder_pool) {
> -        sys->decoder_pool =
> -            picture_pool_NewFromFormat(&source,
> -                                       __MAX(VOUT_MAX_PICTURES,
> -                                             reserved_picture +
> decoder_picture - DISPLAY_PICTURE_COUNT)); +        const unsigned
> decoder_pool_size = __MAX(VOUT_MAX_PICTURES, +                             
>                    reserved_picture + decoder_picture -
> DISPLAY_PICTURE_COUNT); +#if defined(_WIN32)
> +        if (source.i_chroma == VLC_CODEC_D3D9_OPAQUE)
> +            /* FIXME dirty hack for now */
> +            sys->decoder_pool = AllocPoolD3D9( VLC_OBJECT(vout), &source,
> decoder_pool_size ); +        else
> +#endif
> +            sys->decoder_pool = picture_pool_NewFromFormat( &source,
> decoder_pool_size ); +
>          if (!sys->decoder_pool)
>              return VLC_EGENERIC;
>          if (allow_dr) {
> diff --git a/src/win32/direct3d9_pool.c b/src/win32/direct3d9_pool.c
> new file mode 100644
> index 0000000..fd40a0a
> --- /dev/null
> +++ b/src/win32/direct3d9_pool.c
> @@ -0,0 +1,248 @@
> +/**************************************************************************
> *** + * direct3d9_pool.c: Windows Direct3D9 picture pool creation
> +
> ***************************************************************************
> ** + * Copyright (C) 2015 VLC authors and VideoLAN
> + *$Id$
> + *
> + * Authors: Steve Lhomme <robux4 at gmail.com>,
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 of the License, or + *
> (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> ***************************************************************************
> **/ +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include "direct3d9_pool.h"
> +
> +static void DestroyPicture(picture_t *picture);
> +
> +struct picture_sys_t {
> +    LPDIRECT3DSURFACE9 surface;
> +    HINSTANCE          hd3d9_dll;
> +};
> +
> +picture_pool_t *AllocPoolD3D9( vlc_object_t *va, const video_format_t *fmt,
> unsigned pool_size ) +{
> +    HINSTANCE         hd3d9_dll = NULL;
> +    LPDIRECT3D9       d3dobj = NULL;
> +    LPDIRECT3DDEVICE9 d3ddev = NULL;
> +
> +    if (fmt->i_chroma != VLC_CODEC_D3D9_OPAQUE)
> +        return picture_pool_NewFromFormat(fmt, pool_size);
> +
> +    OSVERSIONINFO winVer;
> +    winVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
> +    if(GetVersionEx(&winVer) && winVer.dwMajorVersion < 6) {
> +        msg_Warn(va, "windows version not compatible with D3D9");
> +        goto error;
> +    }
> +
> +    hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
> +    if (!hd3d9_dll) {
> +        msg_Warn(va, "cannot load d3d9.dll, aborting");
> +        goto error;
> +    }
> +
> +    LPDIRECT3D9 (WINAPI *OurDirect3DCreate9)(UINT SDKVersion);
> +    OurDirect3DCreate9 = (void *)GetProcAddress(hd3d9_dll,
> "Direct3DCreate9"); +    if (!OurDirect3DCreate9) {
> +        msg_Err(va, "Cannot locate reference to Direct3DCreate9 ABI in
> DLL"); +        goto error;
> +    }
> +
> +    /* Create the D3D object. */
> +    d3dobj = OurDirect3DCreate9(D3D_SDK_VERSION);
> +    if (!d3dobj) {
> +       msg_Err(va, "Could not create Direct3D9 instance.");
> +       goto error;
> +    }
> +
> +    /*
> +    ** Get device capabilities
> +    */
> +    D3DCAPS9    d3dcaps;
> +    ZeroMemory(&d3dcaps, sizeof(d3dcaps));
> +    HRESULT hr = IDirect3D9_GetDeviceCaps(d3dobj, D3DADAPTER_DEFAULT,
> D3DDEVTYPE_HAL, &d3dcaps); +    if (FAILED(hr)) {
> +       msg_Err(va, "Could not read adapter capabilities. (hr=0x%0lx)", hr);
> +       goto error;
> +    }
> +
> +    /* TODO: need to test device capabilities and select the right render
> function */ +    if (!(d3dcaps.DevCaps2 &
> D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES) || +       
> !(d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MAGFLINEAR)) || +       
> !(d3dcaps.TextureFilterCaps & (D3DPTFILTERCAPS_MINFLINEAR))) { +       
> msg_Err(va, "Device does not support stretching from textures."); +       
> goto error;
> +    }
> +
> +    // Create the D3DDevice
> +    UINT AdapterToUse = D3DADAPTER_DEFAULT;
> +    D3DDEVTYPE DeviceType = D3DDEVTYPE_HAL;
> +
> +#ifndef NDEBUG
> +    // Look for 'NVIDIA PerfHUD' adapter
> +    // If it is present, override default settings
> +    for (UINT Adapter=0; Adapter< IDirect3D9_GetAdapterCount(d3dobj);
> ++Adapter) { +        D3DADAPTER_IDENTIFIER9 Identifier;
> +        HRESULT Res =
> IDirect3D9_GetAdapterIdentifier(d3dobj,Adapter,0,&Identifier); +        if
> (SUCCEEDED(Res) && strstr(Identifier.Description,"PerfHUD") != 0) { +      
>      AdapterToUse = Adapter;
> +            DeviceType = D3DDEVTYPE_REF;
> +            break;
> +        }
> +    }
> +#endif
> +
> +    /* */
> +    D3DADAPTER_IDENTIFIER9 d3dai;
> +    if (FAILED(IDirect3D9_GetAdapterIdentifier(d3dobj,AdapterToUse,0,
> &d3dai))) { +        msg_Warn(va, "IDirect3D9_GetAdapterIdentifier
> failed");
> +    } else {
> +        msg_Dbg(va, "Direct3d9 Device: %s %lu %lu %lu", d3dai.Description,
> +                d3dai.VendorId, d3dai.DeviceId, d3dai.Revision );
> +    }
> +
> +    /*
> +    ** Get the current desktop display mode, so we can set up a back
> +    ** buffer of the same format
> +    */
> +    D3DDISPLAYMODE d3ddm;
> +    hr = IDirect3D9_GetAdapterDisplayMode(d3dobj, D3DADAPTER_DEFAULT,
> &d3ddm); +    if (FAILED(hr)) {
> +       msg_Err(va, "Could not read adapter display mode. (hr=0x%0lx)", hr);
> +       goto error;
> +    }
> +
> +    D3DPRESENT_PARAMETERS d3dpp;
> +    ZeroMemory(&d3dpp, sizeof(d3dpp));
> +    d3dpp.Flags                  = D3DPRESENTFLAG_VIDEO;
> +    d3dpp.Windowed               = TRUE;
> +    d3dpp.hDeviceWindow          = NULL;
> +    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
> +    d3dpp.MultiSampleType        = D3DMULTISAMPLE_NONE;
> +    d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
> +    d3dpp.BackBufferCount        = 0; /* FIXME may not be right */
> +    d3dpp.BackBufferFormat       = d3ddm.Format;
> +    d3dpp.BackBufferWidth        = __MAX((unsigned
> int)GetSystemMetrics(SM_CXVIRTUALSCREEN), +                                
>          d3ddm.Width);
> +    d3dpp.BackBufferHeight       = __MAX((unsigned
> int)GetSystemMetrics(SM_CYVIRTUALSCREEN), +                                
>          d3ddm.Height);
> +    d3dpp.EnableAutoDepthStencil = FALSE;
> +
> +    hr = IDirect3D9_CreateDevice(d3dobj, AdapterToUse,
> +                                         DeviceType, GetDesktopWindow(),
> +                                        
> D3DCREATE_SOFTWARE_VERTEXPROCESSING| +                                     
>    D3DCREATE_MULTITHREADED, +                                        
> &d3dpp, &d3ddev);
> +    if (FAILED(hr)) {
> +       msg_Err(va, "Could not create the D3D9 device! (hr=0x%0lx)", hr);
> +       goto error;
> +    }
> +
> +    picture_pool_t *result = AllocPoolD3D9Ex(va, d3ddev, fmt, pool_size);
> +    if (!result)
> +        goto error;
> +
> +    FreeLibrary(hd3d9_dll);
> +    return result;
> +
> +error:
> +    if (d3ddev)
> +        IDirect3DDevice9_Release(d3ddev);
> +    if (d3dobj)
> +        IDirect3D9_Release(d3dobj);
> +    if (hd3d9_dll)
> +        FreeLibrary(hd3d9_dll);
> +    return NULL;
> +}
> +
> +picture_pool_t *AllocPoolD3D9Ex(vlc_object_t *va, LPDIRECT3DDEVICE9 d3ddev,
> +                                const video_format_t *fmt, unsigned
> pool_size) +{
> +    picture_t**       pictures = NULL;
> +    unsigned          picture_count = 0;
> +
> +    pictures = calloc(pool_size, sizeof(*pictures));
> +    if (!pictures)
> +        goto error;
> +    for (picture_count = 0; picture_count < pool_size; ++picture_count)
> +    {
> +        picture_sys_t *picsys = malloc(sizeof(*picsys));
> +        if (unlikely(picsys == NULL))
> +            goto error;
> +
> +        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(d3ddev,
> +                                                          fmt->i_width,
> +                                                          fmt->i_height,
> +                                                         
> MAKEFOURCC('N','V','1','2'), /* FIXME d3ddm.Format, */ +                   
>                                       D3DPOOL_DEFAULT, +                   
>                                       &picsys->surface, +                  
>                                        NULL);
> +        if (FAILED(hr)) {
> +           msg_Err(va, "Failed to allocate surface %d (hr=0x%0lx)",
> picture_count, hr); +           goto error;
> +        }
> +
> +        picture_resource_t resource = {
> +            .p_sys = picsys,
> +            .pf_destroy = DestroyPicture,
> +        };
> +
> +        picture_t *picture = picture_NewFromResource(fmt, &resource);
> +        if (unlikely(picture == NULL)) {
> +            free(picsys);
> +            goto error;
> +        }
> +
> +        pictures[picture_count] = picture;
> +        /* each picture_t holds a ref to the device and release it on
> Destroy */ +        IDirect3DDevice9_AddRef(d3ddev);
> +        /* each picture_t holds a ref to the DLL */
> +        picsys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
> +    }
> +
> +    /* release the system resources, they will be free'd with the pool */
> +    IDirect3DDevice9_Release(d3ddev);
> +
> +    picture_pool_configuration_t pool_cfg;
> +    memset(&pool_cfg, 0, sizeof(pool_cfg));
> +    pool_cfg.picture_count = pool_size;
> +    pool_cfg.picture       = pictures;
> +
> +    return picture_pool_NewExtended( &pool_cfg );
> +
> +error:
> +    if (pictures) {
> +        for (unsigned i=0;i<picture_count; ++i)
> +            DestroyPicture(pictures[i]);
> +        free(pictures);
> +    }
> +    return NULL;
> +}
> +
> +static void DestroyPicture(picture_t *picture)
> +{
> +    LPDIRECT3DDEVICE9 d3ddev;
> +    if (!FAILED(IDirect3DSurface9_GetDevice(picture->p_sys->surface,
> &d3ddev))) +        IDirect3DDevice9_Release(d3ddev);
> +
> +    IDirect3DSurface9_Release(picture->p_sys->surface);
> +
> +    FreeLibrary(picture->p_sys->hd3d9_dll);
> +
> +    free(picture->p_sys);
> +}
> diff --git a/src/win32/direct3d9_pool.h b/src/win32/direct3d9_pool.h
> new file mode 100644
> index 0000000..b1b9fe2
> --- /dev/null
> +++ b/src/win32/direct3d9_pool.h
> @@ -0,0 +1,39 @@
> +/**************************************************************************
> *** + * direct3d9_pool.c: Windows Direct3D9 picture pool creation
> +
> ***************************************************************************
> ** + * Copyright (C) 2015 VLC authors and VideoLAN
> + *$Id$
> + *
> + * Authors: Steve Lhomme <robux4 at gmail.com>,
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License as published by
> + * the Free Software Foundation; either version 2.1 of the License, or + *
> (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program; if not, write to the Free Software Foundation,
> + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +
> ***************************************************************************
> **/ +
> +#ifndef WIN32_DIRECT3D9_POOL_H_
> +#define WIN32_DIRECT3D9_POOL_H_
> +
> +#include <windows.h>
> +#include <d3d9.h>
> +
> +#include <vlc_common.h>
> +#include <vlc_picture_pool.h>
> +
> +#define VA_DXVA2_MAX_SURFACE_COUNT (64)
> +
> +picture_pool_t *AllocPoolD3D9( vlc_object_t *obj, const video_format_t
> *p_fmt, unsigned pool_size ); +picture_pool_t *AllocPoolD3D9Ex(vlc_object_t
> *obj, LPDIRECT3DDEVICE9 d3ddev, +                                const
> video_format_t *fmt, unsigned pool_size); +
> +#endif /* WIN32_DIRECT3D9_POOL_H_ */

-- 
Rémi Denis-Courmont
http://www.remlab.net/




More information about the vlc-devel mailing list