<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Content-Style-Type" content="text/css" />
  <meta name="generator" content="pandoc" />
  <title></title>
  <style type="text/css">code{white-space: pre;}</style>
</head>
<body>
<p>Hi Steve,</p>
<p>This reply got written in-between three different conversations going besides my computer (which I took part it). I apologize if these momentarily breaks (in terms of my brain focusing on replying) has made the email hard to read/understand.</p>
<p>On 2017-04-11 16:50, Steve Lhomme wrote:</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> ---
  configure.ac                                  |   3 +
  modules/video_output/Makefile.am              |   5 +-
  modules/video_output/win32/d3d9_deinterlace.c | 292 ++++++++++++++++++++++++++
  3 files changed, 299 insertions(+), 1 deletion(-)
  create mode 100644 modules/video_output/win32/d3d9_deinterlace.c

 diff --git a/configure.ac b/configure.ac
 index dfbb3f4919..6130c4a437 100644
 --- a/configure.ac
 +++ b/configure.ac
 @@ -3276,6 +3276,9 @@ AS_IF([test "${enable_directx}" != "no"], [
    dnl Direct3D9
    AC_CHECK_HEADERS(d3d9.h, [
      VLC_ADD_PLUGIN([direct3d9])
 +    AC_CHECK_HEADERS(dxva2api.h, [
 +      VLC_ADD_PLUGIN([direct3d9_deinterlace])
 +    ])
    ])

  ])
 diff --git a/modules/video_output/Makefile.am b/modules/video_output/Makefile.am
 index 3b9fb053f6..d5c1137646 100644
 --- a/modules/video_output/Makefile.am
 +++ b/modules/video_output/Makefile.am
 @@ -214,9 +214,12 @@ libdirect3d9_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) \
      -DMODULE_NAME_IS_direct3d9
  libdirect3d9_plugin_la_LIBADD = -lgdi32 $(LIBCOM) -luuid
  libdirect3d9_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(voutdir)'
 +libdirect3d9_deinterlace_plugin_la_SOURCES = video_output/win32/d3d9_deinterlace.c
 +libdirect3d9_deinterlace_plugin_la_LIBADD = $(LIBCOM)
  if HAVE_WIN32_DESKTOP
  vout_LTLIBRARIES += $(LTLIBdirect3d9)
 -EXTRA_LTLIBRARIES += libdirect3d9_plugin.la
 +vout_LTLIBRARIES += $(LTLIBdirect3d9_deinterlace)
 +EXTRA_LTLIBRARIES += libdirect3d9_plugin.la libdirect3d9_deinterlace_plugin.la
  endif

  libdirect3d11_plugin_la_SOURCES = video_output/win32/direct3d11.c \
 diff --git a/modules/video_output/win32/d3d9_deinterlace.c b/modules/video_output/win32/d3d9_deinterlace.c
 new file mode 100644
 index 0000000000..88cc767ebf
 --- /dev/null
 +++ b/modules/video_output/win32/d3d9_deinterlace.c
 @@ -0,0 +1,292 @@
 +/*****************************************************************************
 + * d3d11_deinterlace.c: D3D11 deinterlacing filter
 + *****************************************************************************
 + * Copyright (C) 2017 Videolabs SAS
 + *
 + * Authors: Steve Lhomme <robux4@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 <stdlib.h>
 +#include <assert.h>
 +
 +#include <vlc_common.h>
 +#include <vlc_plugin.h>
 +#include <vlc_filter.h>
 +#include <vlc_picture.h>
 +
 +#define COBJMACROS
 +#include <initguid.h>
 +#include <d3d9.h>
 +#include <dxva2api.h>
 +
 +struct picture_sys_t
 +{
 +    LPDIRECT3DSURFACE9 surface;
 +};
 +
 +struct filter_sys_t
 +{
 +    HINSTANCE                      hdecoder_dll;
 +    /* keep a reference in case the vout is released first */
 +    HINSTANCE                      d3d9_dll;
 +    IDirect3DDevice9               *d3ddev;
 +    IDirectXVideoProcessor         *processor;
 +    IDirect3DSurface9              *hw_surface;
 +};
 +
 +static picture_t *Deinterlace(filter_t *filter, picture_t *src)
 +{
 +    filter_sys_t *sys = filter->p_sys;
 +    HRESULT hr;
 +    DXVA2_VideoProcessBltParams params = {0};
 +    DXVA2_VideoSample sample = {0};
 +    D3DSURFACE_DESC dstDesc;
 +
 +    hr = IDirect3DSurface9_GetDesc( src->p_sys->surface, &dstDesc );
 +    if (FAILED(hr))
 +        return src; /* cannot deinterlace without copying fields */
 +
 +    picture_t *dst = filter_NewPicture(filter);
 +    if (dst == NULL)
 +        return src; /* cannot deinterlace without copying fields */
 +
 +    sample.SrcSurface = src->p_sys->surface;
 +    sample.SampleFormat.SampleFormat = src->b_top_field_first ?
 +                DXVA2_SampleFieldInterleavedEvenFirst :
 +                DXVA2_SampleFieldInterleavedOddFirst;
 +    sample.Start = 0;
 +    sample.End = sample.Start + 333666;
 +    sample.SampleData = DXVA2_SampleData_RFF_TFF_Present;
 +    if (src->b_top_field_first)
 +        sample.SampleData |= DXVA2_SampleData_TFF;
 +    sample.SrcRect.bottom = dstDesc.Height;
 +    sample.SrcRect.right  = dstDesc.Width;
 +    sample.DstRect        = sample.SrcRect;
 +    sample.PlanarAlpha    = DXVA2_Fixed32OpaqueAlpha();
 +
 +    params.TargetFrame = sample.Start;
 +    params.TargetRect  = sample.DstRect;
 +    params.DestData    = sample.SampleData;
 +    params.Alpha       = DXVA2_Fixed32OpaqueAlpha();
 +    params.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;
 +    params.BackgroundColor.Cr    = 0x8000;
 +    params.BackgroundColor.Cb    = 0x8000;
 +    params.BackgroundColor.Y     = 0x1000;
 +    params.BackgroundColor.Alpha = 0xFFFF;
 +
 +    hr = IDirectXVideoProcessor_VideoProcessBlt( sys->processor,
 +                                                 sys->hw_surface,
 +                                                 &params,
 +                                                 &sample,
 +                                                 1, NULL );
 +    if (FAILED(hr))
 +        goto error;
 +
 +    hr = IDirect3DDevice9_StretchRect( sys->d3ddev,
 +                                       sys->hw_surface, NULL,
 +                                       dst->p_sys->surface, NULL,
 +                                       D3DTEXF_NONE);
 +    if (FAILED(hr))
 +        goto error;
 +
 +    picture_CopyProperties(dst, src);
 +    picture_Release(src);
 +    dst->b_progressive = true;
 +    dst->i_nb_fields = 1;
 +    return dst;
 +error:
 +    picture_Release(dst);
 +    return src;
 +}
 +
 +static int Open(vlc_object_t *obj)
 +{
 +    filter_t *filter = (filter_t *)obj;
 +    HINSTANCE hdecoder_dll = NULL;
 +    HINSTANCE d3d9_dll = NULL;
 +    HRESULT hr;
 +    picture_t *dst = NULL;
 +    GUID *processorGUIDs = NULL;
 +    GUID *processorGUID = NULL;
 +    IDirectXVideoProcessorService *processor = NULL;
 +
 +    if (filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE
 +     && filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B)
 +        return VLC_EGENERIC;
 +    if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
 +        return VLC_EGENERIC;
 +
 +    d3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
 +    if (!d3d9_dll)
 +        return VLC_EGENERIC;
 +
 +    hdecoder_dll = LoadLibrary(TEXT("DXVA2.DLL"));
 +    if (!hdecoder_dll)
 +        return VLC_EGENERIC;</code></pre>
</blockquote>
<p>Leak of <code>d3d9_dll</code>; did you mean to use <code>goto error</code>?</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    dst = filter_NewPicture(filter);
 +    if (dst == NULL)
 +        return VLC_EGENERIC;</code></pre>
</blockquote>
<p>Leak of <code>d3d9_dll</code> and <code>hdecoder_dll</code>; did you mean to use <code>goto error</code>?</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    filter_sys_t *sys = malloc(sizeof (*sys));
 +    if (unlikely(sys == NULL))
 +        goto error;
 +    memset(sys, 0, sizeof (*sys));</code></pre>
</blockquote>
<p>Strictly speaking, <code>memset</code> with <code>0</code> is not guaranteed to have pointers in the destination object be equivalent to having assigned <code>NULL</code> to them directly.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +    filter->p_sys = sys;
 +
 +    HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
 +                                         REFIID riid,
 +                                         void **ppService);
 +    CreateVideoService =
 +      (void *)GetProcAddress(hdecoder_dll, "DXVA2CreateVideoService");</code></pre>
</blockquote>
<p>Missing error-check: If <code>GetProcAddress</code> fails you will have a <em>null-pointer</em> dereference at the place where the <em>function-pointer</em> is dereferenced (called).</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    hr = IDirect3DSurface9_GetDevice( dst->p_sys->surface, &sys->d3ddev );
 +    if (FAILED(hr))
 +        goto error;</code></pre>
</blockquote>
<p>See next comment.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    D3DSURFACE_DESC dstDesc;
 +    hr = IDirect3DSurface9_GetDesc( dst->p_sys->surface, &dstDesc );
 +    if (FAILED(hr))
 +        goto error;</code></pre>
</blockquote>
<p>See next comment.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    hr = CreateVideoService( sys->d3ddev, &IID_IDirectXVideoProcessorService,
 +                            (void**)&processor);
 +    if (FAILED(hr))
 +        goto error;</code></pre>
</blockquote>
<p>Is <code>DXVA2CreateVideoService</code> really guaranteed not to touch the contents of <code>processor</code> in case of error? If it is not, then the potential garbage will make the clean-up under the <em>error-label</em> problematic.</p>
<p>Mostly asking out of curiosity, but in either case; if it is not guaranteed something should be done about it.</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +
 +    DXVA2_VideoDesc dsc;
 +    ZeroMemory(&dsc, sizeof(dsc));
 +    dsc.SampleWidth     = dstDesc.Width;
 +    dsc.SampleHeight    = dstDesc.Height;
 +    dsc.Format          = dstDesc.Format;
 +    if (es_format_HasValidFrameRate( &filter->fmt_in )) {
 +        dsc.InputSampleFreq.Numerator   = filter->fmt_in.video.frame_rate.num;
 +        dsc.InputSampleFreq.Denominator = filter->fmt_in.video.frame_rate.den;
 +    } else {
 +        dsc.InputSampleFreq.Numerator   = 0;
 +        dsc.InputSampleFreq.Denominator = 0;
 +    }
 +    dsc.OutputFrameFreq = dsc.InputSampleFreq;
 +
 +    DXVA2_ExtendedFormat *pFormat = &dsc.SampleFormat;
 +    pFormat->SampleFormat = dst->b_top_field_first ?
 +                DXVA2_SampleFieldInterleavedEvenFirst :
 +                DXVA2_SampleFieldInterleavedOddFirst;
 +
 +    UINT count = 0;
 +    hr = IDirectXVideoProcessorService_GetVideoProcessorDeviceGuids( processor,
 +                                                                &dsc,
 +                                                                &count,
 +                                                                &processorGUIDs);
 +    if (FAILED(hr))
 +        goto error;
 +
 +    DXVA2_VideoProcessorCaps caps;
 +    for (UINT i=0; i<count && processorGUID==NULL; ++i) {
 +        hr = IDirectXVideoProcessorService_GetVideoProcessorCaps( processor,
 +                                                                  processorGUIDs+i,
 +                                                                  &dsc,
 +                                                                  dsc.Format,
 +                                                                  &caps);
 +        if ( SUCCEEDED(hr) && caps.DeinterlaceTechnology &&
 +             !caps.NumForwardRefSamples && !caps.NumBackwardRefSamples )
 +            processorGUID = processorGUIDs + i;
 +    }
 +
 +    if (processorGUID == NULL)
 +    {
 +        msg_Dbg(filter, "Could not find a filter to output the required format");
 +        goto error;
 +    }
 +
 +    hr = IDirectXVideoProcessorService_CreateVideoProcessor( processor,
 +                                                             processorGUID,
 +                                                             &dsc,
 +                                                             dsc.Format,
 +                                                             1,
 +                                                             &sys->processor );
 +    if (FAILED(hr))
 +        goto error;
 +
 +    hr = IDirectXVideoProcessorService_CreateSurface( processor,
 +                                                      dstDesc.Width,
 +                                                      dstDesc.Height,
 +                                                      0,
 +                                                      dstDesc.Format,
 +                                                      D3DPOOL_DEFAULT,
 +                                                      0,
 +                                                      DXVA2_VideoProcessorRenderTarget,
 +                                                      &sys->hw_surface,
 +                                                      NULL);
 +    if (FAILED(hr))
 +        goto error;
 +
 +    CoTaskMemFree(processorGUIDs);
 +    picture_Release(dst);
 +    IDirectXVideoProcessorService_Release(processor);
 +
 +    sys->hdecoder_dll = hdecoder_dll;
 +    sys->d3d9_dll     = d3d9_dll;
 +    filter->pf_video_filter = Deinterlace;
 +    filter->p_sys = sys;
 +
 +    return VLC_SUCCESS;
 +error:
 +    CoTaskMemFree(processorGUIDs);
 +    if (sys && sys->processor)
 +        IDirectXVideoProcessor_Release( sys->processor );
 +    if (processor)
 +        IDirectXVideoProcessorService_Release(processor);
 +    if (sys && sys->d3ddev)
 +        IDirect3DDevice9_Release( sys->d3ddev );
 +    if (hdecoder_dll)
 +        FreeLibrary(hdecoder_dll);
 +    if (d3d9_dll)
 +        FreeLibrary(d3d9_dll);</code></pre>
</blockquote>
<p>If the previous remarks regarding potentially missing <code>goto error</code> is not correct, the above checks in terms of <code>hdecoder_dll</code> and <code>d3d9_dll</code> are redundant since both are guaranteed to evaluate to <code>true</code> (at the points where <code>goto error</code> is used).</p>
<blockquote style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;color:#500050">
<pre><code> +    if (dst)
 +        picture_Release(dst);
 +    free(sys);
 +
 +    return VLC_EGENERIC;
 +}
 +
 +static void Close(vlc_object_t *obj)
 +{
 +    filter_t *filter = (filter_t *)obj;
 +    filter_sys_t *sys = filter->p_sys;
 +
 +    IDirect3DSurface9_Release( sys->hw_surface );
 +    IDirectXVideoProcessor_Release( sys->processor );
 +    IDirect3DDevice9_Release( sys->d3ddev );
 +    FreeLibrary( sys->hdecoder_dll );
 +    FreeLibrary( sys->d3d9_dll );
 +
 +    free(sys);
 +}
 +
 +vlc_module_begin()
 +    set_description(N_("Direct3D9 deinterlacing filter"))
 +    set_capability("video filter", 0)
 +    set_category(CAT_VIDEO)
 +    set_subcategory(SUBCAT_VIDEO_VFILTER)
 +    set_callbacks(Open, Close)
 +    add_shortcut ("deinterlace")
 +vlc_module_end()
 -- </code></pre>
</blockquote>
<p>Best Regards, Filip</p>
</body>
</html>