d3d11_deinterlace.c 21.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/*****************************************************************************
 * 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_filter.h>
#include <vlc_picture.h>

#define COBJMACROS
#include <initguid.h>
#include <d3d11.h>

38
#include "d3d11_filters.h"
39
#include "../../video_chroma/d3d11_fmt.h"
40
#include "../../video_filter/deinterlace/common.h"
41 42

#ifdef __MINGW32__
43 44 45 46 47 48 49
typedef UINT D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS;
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND               0x1
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB                 0x2
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE            0x4
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION 0x8
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE               0x10
#define D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_FRAME_RATE_CONVERSION          0x20
50 51
#endif

52
typedef struct
53
{
54
    d3d11_handle_t                 hd3d;
55
    d3d11_device_t                 d3d_dev;
56 57 58 59 60 61 62 63 64 65 66
    ID3D11VideoDevice              *d3dviddev;
    ID3D11VideoContext             *d3dvidctx;
    ID3D11VideoProcessor           *videoProcessor;
    ID3D11VideoProcessorEnumerator *procEnumerator;

    HANDLE                         context_mutex;
    union {
        ID3D11Texture2D            *outTexture;
        ID3D11Resource             *outResource;
    };
    ID3D11VideoProcessorOutputView *processorOutput;
67 68

    struct deinterlace_ctx         context;
69
    picture_t *                    (*buffer_new)( filter_t * );
70
} filter_sys_t;
71

72
struct filter_mode_t
73
{
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
    const char                           *psz_mode;
    D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS  i_mode;
    deinterlace_algo                      settings;
};
static struct filter_mode_t filter_mode [] = {
    { "blend",   D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND,
                 { false, false, false, false } },
    { "bob",     D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB,
                 { true,  false, false, false } },
    { "x",       D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION,
                 { true,  true,  false, false } },
    { "ivtc",    D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE,
                 { false, true,  true, false } },
    { "yadif2x", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE,
                 { true,  true,  false, false } },
};
90

91 92 93 94
static int assert_ProcessorInput(filter_t *p_filter, picture_sys_t *p_sys_src)
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if (!p_sys_src->processorInput)
95 96 97 98 99
    {
        D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inDesc = {
            .FourCC = 0,
            .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
            .Texture2D.MipSlice = 0,
100
            .Texture2D.ArraySlice = p_sys_src->slice_index,
101
        };
102
        HRESULT hr;
103

104 105 106
        hr = ID3D11VideoDevice_CreateVideoProcessorInputView(p_sys->d3dviddev,
                                                             p_sys_src->resource[KNOWN_DXGI_INDEX],
                                                             p_sys->procEnumerator,
107
                                                             &inDesc,
108
                                                             &p_sys_src->processorInput);
109 110 111
        if (FAILED(hr))
        {
#ifndef NDEBUG
112
            msg_Dbg(p_filter,"Failed to create processor input for slice %d. (hr=0x%lX)", p_sys_src->slice_index, hr);
113
#endif
114
            return VLC_EGENERIC;
115 116
        }
    }
117 118
    return VLC_SUCCESS;
}
119

120 121
static void Flush(filter_t *filter)
{
122 123
    filter_sys_t *p_sys = filter->p_sys;
    FlushDeinterlacing(&p_sys->context);
124
}
125

126 127 128 129 130 131
static int RenderPic( filter_t *p_filter, picture_t *p_outpic, picture_t *p_pic,
                      int order, int i_field )
{
    VLC_UNUSED(order);
    HRESULT hr;
    filter_sys_t *p_sys = p_filter->p_sys;
132
    picture_sys_t *p_out_sys = p_outpic->p_sys;
133

134 135 136 137 138 139
    picture_t *p_prev = p_sys->context.pp_history[0];
    picture_t *p_cur  = p_sys->context.pp_history[1];
    picture_t *p_next = p_sys->context.pp_history[2];

    /* TODO adjust the format if it's the first or second field ? */
    D3D11_VIDEO_FRAME_FORMAT frameFormat = !i_field ?
140 141
                D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST :
                D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
142

143
    ID3D11VideoContext_VideoProcessorSetStreamFrameFormat(p_sys->d3dvidctx, p_sys->videoProcessor, 0, frameFormat);
144

145 146 147
    D3D11_VIDEO_PROCESSOR_STREAM stream = {0};
    stream.Enable = TRUE;
    stream.InputFrameOrField = i_field ? 1 : 0;
148

149
    if( p_cur && p_next )
150
    {
151
        picture_sys_t *picsys_next = ActivePictureSys(p_next);
152
        if ( unlikely(!picsys_next) || assert_ProcessorInput(p_filter, picsys_next) )
153 154 155
            return VLC_EGENERIC;

        picture_sys_t *picsys_cur = ActivePictureSys(p_cur);
156
        if ( unlikely(!picsys_cur) || assert_ProcessorInput(p_filter, picsys_cur) )
157 158 159 160 161
            return VLC_EGENERIC;

        if ( p_prev )
        {
            picture_sys_t *picsys_prev = ActivePictureSys(p_prev);
162
            if ( unlikely(!picsys_prev) || assert_ProcessorInput(p_filter, picsys_prev) )
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
                return VLC_EGENERIC;

            stream.pInputSurface    = picsys_cur->processorInput;
            stream.ppFutureSurfaces = &picsys_next->processorInput;
            stream.ppPastSurfaces   = &picsys_prev->processorInput;

            stream.PastFrames   = 1;
            stream.FutureFrames = 1;
        }
        else
        {
            /* p_next is the current, p_cur is the previous frame */
            stream.pInputSurface  = picsys_next->processorInput;
            stream.ppPastSurfaces = &picsys_cur->processorInput;
            stream.PastFrames = 1;
        }
    }
    else
    {
        picture_sys_t *p_sys_src = ActivePictureSys(p_pic);
183
        if ( unlikely(!p_sys_src) || assert_ProcessorInput(p_filter, p_sys_src) )
184 185 186 187
            return VLC_EGENERIC;

        /* first single frame */
        stream.pInputSurface = p_sys_src->processorInput;
188
    }
189

190
    RECT srcRect;
191 192 193 194
    srcRect.left   = p_pic->format.i_x_offset;
    srcRect.top    = p_pic->format.i_y_offset;
    srcRect.right  = srcRect.left + p_pic->format.i_visible_width;
    srcRect.bottom = srcRect.top  + p_pic->format.i_visible_height;
195 196 197 198 199
    ID3D11VideoContext_VideoProcessorSetStreamSourceRect(p_sys->d3dvidctx, p_sys->videoProcessor,
                                                         0, TRUE, &srcRect);
    ID3D11VideoContext_VideoProcessorSetStreamDestRect(p_sys->d3dvidctx, p_sys->videoProcessor,
                                                         0, TRUE, &srcRect);

200 201 202 203 204 205
    hr = ID3D11VideoContext_VideoProcessorBlt(p_sys->d3dvidctx, p_sys->videoProcessor,
                                              p_sys->processorOutput,
                                              0, 1, &stream);
    if (FAILED(hr))
        return VLC_EGENERIC;

206 207
    D3D11_BOX box = {
        .top = 0,
208
        .bottom = p_outpic->format.i_height,
209
        .left = 0,
210
        .right = p_outpic->format.i_width,
211 212 213
        .back = 1,
    };

214 215 216
    ID3D11DeviceContext_CopySubresourceRegion(p_out_sys->context,
                                              p_out_sys->resource[KNOWN_DXGI_INDEX],
                                              p_out_sys->slice_index,
217
                                              0, 0, 0,
218
                                              p_sys->outResource,
219
                                              0, &box);
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
    return VLC_SUCCESS;
}

static int RenderSinglePic( filter_t *p_filter, picture_t *p_outpic, picture_t *p_pic )
{
    return RenderPic( p_filter, p_outpic, p_pic, 0, 0 );
}

static picture_t *Deinterlace(filter_t *p_filter, picture_t *p_pic)
{
    filter_sys_t *p_sys = p_filter->p_sys;

    if( p_sys->context_mutex != INVALID_HANDLE_VALUE )
        WaitForSingleObjectEx( p_sys->context_mutex, INFINITE, FALSE );

    picture_t *res = DoDeinterlacing( p_filter, &p_sys->context, p_pic );

    if( p_sys->context_mutex  != INVALID_HANDLE_VALUE )
        ReleaseMutex( p_sys->context_mutex );

    return res;
}

static const struct filter_mode_t *GetFilterMode(const char *mode)
{
    if ( mode == NULL || !strcmp( mode, "auto" ) )
        mode = "x";

    for (size_t i=0; i<ARRAY_SIZE(filter_mode); i++)
    {
        if( !strcmp( mode, filter_mode[i].psz_mode ) )
            return &filter_mode[i];
    }
    return NULL;
254 255
}

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
static void d3d11_pic_context_destroy(struct picture_context_t *opaque)
{
    struct va_pic_context *pic_ctx = (struct va_pic_context*)opaque;
    ReleasePictureSys(&pic_ctx->picsys);
    free(pic_ctx);
}

static struct picture_context_t *d3d11_pic_context_copy(struct picture_context_t *ctx)
{
    struct va_pic_context *src_ctx = (struct va_pic_context*)ctx;
    struct va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
    if (unlikely(pic_ctx==NULL))
        return NULL;
    pic_ctx->s.destroy = d3d11_pic_context_destroy;
    pic_ctx->s.copy    = d3d11_pic_context_copy;
    pic_ctx->picsys = src_ctx->picsys;
    AcquirePictureSys(&pic_ctx->picsys);
    return &pic_ctx->s;
}

static picture_t *NewOutputPicture( filter_t *p_filter )
{
278 279
    filter_sys_t *p_sys = p_filter->p_sys;
    picture_t *pic = p_sys->buffer_new( p_filter );
280
    picture_sys_t *pic_sys = pic->p_sys;
281 282
    if ( !pic->context )
    {
283 284
        bool b_local_texture = false;

285
        if ( !pic_sys )
286
        {
287 288
            pic_sys = calloc(1, sizeof(*pic_sys));
            if (unlikely(pic_sys == NULL))
289
                return NULL;
290
            pic->p_sys = pic_sys;
291 292

            D3D11_TEXTURE2D_DESC dstDesc;
293
            ID3D11Texture2D_GetDesc(p_sys->outTexture, &dstDesc);
294 295 296 297 298 299 300 301 302 303 304 305

            const d3d_format_t *cfg = NULL;
            for (const d3d_format_t *output_format = GetRenderFormatList();
                 output_format->name != NULL; ++output_format)
            {
                if (output_format->formatTexture == dstDesc.Format &&
                    is_d3d11_opaque(output_format->fourcc))
                {
                    cfg = output_format;
                    break;
                }
            }
306 307
            if (unlikely(cfg == NULL))
            {
308
                free(pic_sys);
309 310
                return NULL;
            }
311 312 313 314 315

            /* create the texture that's missing */
            video_format_t fmt = p_filter->fmt_out.video;
            fmt.i_width  = dstDesc.Width;
            fmt.i_height = dstDesc.Height;
316
            if (AllocateTextures(VLC_OBJECT(p_filter), &p_sys->d3d_dev, cfg,
317
                                 &fmt, 1, pic_sys->texture) != VLC_SUCCESS)
318
            {
319
                free(pic_sys);
320 321 322 323
                return NULL;
            }
            b_local_texture = true;

324
            pic_sys->context = p_sys->d3d_dev.d3dcontext;
325
            pic_sys->formatTexture = dstDesc.Format;
326 327

        }
328 329 330 331 332 333
        /* the picture might be duplicated for snapshots so it needs a context */
        struct va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
        if (likely(pic_ctx!=NULL))
        {
            pic_ctx->s.destroy = d3d11_pic_context_destroy;
            pic_ctx->s.copy    = d3d11_pic_context_copy;
334
            pic_ctx->picsys = *pic_sys;
335 336 337
            AcquirePictureSys( &pic_ctx->picsys );
            pic->context = &pic_ctx->s;
        }
338 339
        if (b_local_texture) {
            for (int i=0; i<D3D11_MAX_SHADER_VIEW; i++) {
340 341
                if (pic_sys->texture[i])
                    ID3D11Texture2D_Release(pic_sys->texture[i]);
342 343
            }
        }
344 345 346 347
    }
    return pic;
}

348
int D3D11OpenDeinterlace(vlc_object_t *obj)
349 350 351
{
    filter_t *filter = (filter_t *)obj;
    HRESULT hr;
352
    ID3D11VideoProcessorEnumerator *processorEnumerator = NULL;
353

354
    if (!is_d3d11_opaque(filter->fmt_in.video.i_chroma))
355 356 357 358
        return VLC_EGENERIC;
    if (!video_format_IsSimilar(&filter->fmt_in.video, &filter->fmt_out.video))
        return VLC_EGENERIC;

359 360 361 362 363
    filter_sys_t *sys = malloc(sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
    memset(sys, 0, sizeof (*sys));

364 365 366
    D3D11_TEXTURE2D_DESC dstDesc;
    D3D11_FilterHoldInstance(filter, &sys->d3d_dev, &dstDesc);
    if (unlikely(sys->d3d_dev.d3dcontext==NULL))
367
    {
368 369 370
        msg_Dbg(filter, "Filter without a context");
        free(sys);
        return VLC_ENOOBJ;
371
    }
372

373 374 375
    if (D3D11_Create(filter, &sys->hd3d) != VLC_SUCCESS)
        goto error;

376
    hr = ID3D11Device_QueryInterface(sys->d3d_dev.d3ddevice, &IID_ID3D11VideoDevice, (void **)&sys->d3dviddev);
377 378 379 380 381
    if (FAILED(hr)) {
       msg_Err(filter, "Could not Query ID3D11VideoDevice Interface. (hr=0x%lX)", hr);
       goto error;
    }

382
    hr = ID3D11DeviceContext_QueryInterface(sys->d3d_dev.d3dcontext, &IID_ID3D11VideoContext, (void **)&sys->d3dvidctx);
383
    if (FAILED(hr)) {
Steve Lhomme's avatar
Steve Lhomme committed
384
       msg_Err(filter, "Could not Query ID3D11VideoContext Interface from the picture. (hr=0x%lX)", hr);
385 386 387 388 389
       goto error;
    }

    HANDLE context_lock = INVALID_HANDLE_VALUE;
    UINT dataSize = sizeof(context_lock);
390
    hr = ID3D11Device_GetPrivateData(sys->d3d_dev.d3ddevice, &GUID_CONTEXT_MUTEX, &dataSize, &context_lock);
391 392 393 394
    if (FAILED(hr))
        msg_Warn(filter, "No mutex found to lock the decoder");
    sys->context_mutex = context_lock;

Steve Lhomme's avatar
Steve Lhomme committed
395
    const video_format_t *fmt = &filter->fmt_out.video;
396 397 398 399 400 401 402 403 404

    D3D11_VIDEO_PROCESSOR_CONTENT_DESC processorDesc = {
        .InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST,
        .InputFrameRate = {
            .Numerator   = fmt->i_frame_rate,
            .Denominator = fmt->i_frame_rate_base,
        },
        .InputWidth   = fmt->i_width,
        .InputHeight  = fmt->i_height,
Steve Lhomme's avatar
Steve Lhomme committed
405 406
        .OutputWidth  = dstDesc.Width,
        .OutputHeight = dstDesc.Height,
407
        .OutputFrameRate = {
Steve Lhomme's avatar
Steve Lhomme committed
408 409
            .Numerator   = fmt->i_frame_rate,
            .Denominator = fmt->i_frame_rate_base,
410 411 412 413 414 415 416 417 418 419 420 421
        },
        .Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL,
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator(sys->d3dviddev, &processorDesc, &processorEnumerator);
    if ( processorEnumerator == NULL )
    {
        msg_Dbg(filter, "Can't get a video processor for the video.");
        goto error;
    }

    UINT flags;
#ifndef NDEBUG
422
    D3D11_LogProcessorSupport(filter, processorEnumerator);
423
#endif
Steve Lhomme's avatar
Steve Lhomme committed
424
    hr = ID3D11VideoProcessorEnumerator_CheckVideoProcessorFormat(processorEnumerator, dstDesc.Format, &flags);
425 426
    if (!SUCCEEDED(hr))
    {
Steve Lhomme's avatar
Steve Lhomme committed
427
        msg_Dbg(filter, "can't read processor support for %s", DxgiFormatToStr(dstDesc.Format));
428 429 430 431 432
        goto error;
    }
    if ( !(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_INPUT) ||
         !(flags & D3D11_VIDEO_PROCESSOR_FORMAT_SUPPORT_OUTPUT) )
    {
Steve Lhomme's avatar
Steve Lhomme committed
433
        msg_Dbg(filter, "deinterlacing %s is not supported", DxgiFormatToStr(dstDesc.Format));
434 435 436 437 438 439 440 441
        goto error;
    }

    D3D11_VIDEO_PROCESSOR_CAPS processorCaps;
    hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(processorEnumerator, &processorCaps);
    if (FAILED(hr))
        goto error;

442 443 444 445 446 447 448
    char *psz_mode = var_InheritString( filter, "deinterlace-mode" );
    const struct filter_mode_t *p_mode = GetFilterMode(psz_mode);
    if (p_mode == NULL)
    {
        msg_Dbg(filter, "unknown mode %s, trying blend", psz_mode);
        p_mode = GetFilterMode("blend");
    }
449
    if (strcmp(p_mode->psz_mode, psz_mode))
450
        msg_Dbg(filter, "using %s deinterlacing mode", p_mode->psz_mode);
451

452
    D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rateCaps;
453 454 455
    for (UINT type = 0; type < processorCaps.RateConversionCapsCount; ++type)
    {
        ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps(processorEnumerator, type, &rateCaps);
456
        if (!(rateCaps.ProcessorCaps & p_mode->i_mode))
457 458 459 460 461 462 463 464
            continue;

        hr = ID3D11VideoDevice_CreateVideoProcessor(sys->d3dviddev,
                                                    processorEnumerator, type, &sys->videoProcessor);
        if (SUCCEEDED(hr))
            break;
        sys->videoProcessor = NULL;
    }
465 466 467 468
    if ( sys->videoProcessor==NULL &&
         p_mode->i_mode != D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB )
    {
        msg_Dbg(filter, "mode %s not available, trying bob", psz_mode);
469
        p_mode = GetFilterMode("bob");
470 471 472 473 474 475 476 477 478 479 480 481 482
        for (UINT type = 0; type < processorCaps.RateConversionCapsCount; ++type)
        {
            ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps(processorEnumerator, type, &rateCaps);
            if (!(rateCaps.ProcessorCaps & D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB))
                continue;

            hr = ID3D11VideoDevice_CreateVideoProcessor(sys->d3dviddev,
                                                        processorEnumerator, type, &sys->videoProcessor);
            if (SUCCEEDED(hr))
                break;
            sys->videoProcessor = NULL;
        }
    }
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503

    if (sys->videoProcessor == NULL)
    {
        msg_Dbg(filter, "couldn't find a deinterlacing filter");
        goto error;
    }

    D3D11_TEXTURE2D_DESC texDesc;
    ZeroMemory(&texDesc, sizeof(texDesc));
    texDesc.MipLevels = 1;
    texDesc.SampleDesc.Count = 1;
    texDesc.MiscFlags = 0; //D3D11_RESOURCE_MISC_SHARED;
    texDesc.Usage = D3D11_USAGE_DEFAULT;
    texDesc.CPUAccessFlags = 0;
    texDesc.Format = dstDesc.Format;
    texDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
    texDesc.Usage = D3D11_USAGE_DEFAULT;
    texDesc.ArraySize = 1;
    texDesc.Height = dstDesc.Height;
    texDesc.Width = dstDesc.Width;

504
    hr = ID3D11Device_CreateTexture2D( sys->d3d_dev.d3ddevice, &texDesc, NULL, &sys->outTexture );
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
    if (FAILED(hr)) {
        msg_Err(filter, "CreateTexture2D failed. (hr=0x%0lx)", hr);
        goto error;
    }

    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outDesc = {
        .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
    };

    hr = ID3D11VideoDevice_CreateVideoProcessorOutputView(sys->d3dviddev,
                                                         sys->outResource,
                                                         processorEnumerator,
                                                         &outDesc,
                                                         &sys->processorOutput);
    if (FAILED(hr))
    {
        msg_Dbg(filter,"Failed to create processor output. (hr=0x%lX)", hr);
        goto error;
    }

    sys->procEnumerator  = processorEnumerator;

527 528
    InitDeinterlacingContext( &sys->context );

529
    sys->context.settings = p_mode->settings;
530 531 532 533
    sys->context.settings.b_use_frame_history = rateCaps.PastFrames != 0 ||
        rateCaps.FutureFrames != 0;
    if (sys->context.settings.b_use_frame_history != p_mode->settings.b_use_frame_history)
        msg_Dbg(filter, "deinterlacing not using frame history as requested");
534 535 536 537 538 539 540 541 542 543 544 545 546
    if (sys->context.settings.b_double_rate)
        sys->context.pf_render_ordered = RenderPic;
    else
        sys->context.pf_render_single_pic = RenderSinglePic;

    video_format_t out_fmt;
    GetDeinterlacingOutput( &sys->context, &out_fmt, &filter->fmt_in.video );
    if( !filter->b_allow_fmt_out_change &&
         out_fmt.i_height != filter->fmt_in.video.i_height )
    {
       goto error;
    }

547 548
    sys->buffer_new = filter->owner.video.buffer_new;
    filter->owner.video.buffer_new = NewOutputPicture;
549
    filter->fmt_out.video   = out_fmt;
550
    filter->pf_video_filter = Deinterlace;
551
    filter->pf_flush        = Flush;
552 553 554 555
    filter->p_sys = sys;

    return VLC_SUCCESS;
error:
556 557
    if (sys->processorOutput)
        ID3D11VideoProcessorOutputView_Release(sys->processorOutput);
558 559 560 561 562 563 564 565 566 567
    if (sys->outTexture)
        ID3D11Texture2D_Release(sys->outTexture);
    if (sys->videoProcessor)
        ID3D11VideoProcessor_Release(sys->videoProcessor);
    if (processorEnumerator)
        ID3D11VideoProcessorEnumerator_Release(processorEnumerator);
    if (sys->d3dvidctx)
        ID3D11VideoContext_Release(sys->d3dvidctx);
    if (sys->d3dviddev)
        ID3D11VideoDevice_Release(sys->d3dviddev);
568
    if (sys->d3d_dev.d3dcontext)
569
        D3D11_FilterReleaseInstance(&sys->d3d_dev);
570
    D3D11_Destroy(&sys->hd3d);
571
    free(sys);
572 573 574 575

    return VLC_EGENERIC;
}

576
void D3D11CloseDeinterlace(vlc_object_t *obj)
577 578 579 580
{
    filter_t *filter = (filter_t *)obj;
    filter_sys_t *sys = filter->p_sys;

581 582
    if (likely(sys->processorOutput))
        ID3D11VideoProcessorOutputView_Release(sys->processorOutput);
583 584 585 586 587
    ID3D11Texture2D_Release(sys->outTexture);
    ID3D11VideoProcessor_Release(sys->videoProcessor);
    ID3D11VideoProcessorEnumerator_Release(sys->procEnumerator);
    ID3D11VideoContext_Release(sys->d3dvidctx);
    ID3D11VideoDevice_Release(sys->d3dviddev);
588
    D3D11_FilterReleaseInstance(&sys->d3d_dev);
589
    D3D11_Destroy(&sys->hd3d);
590 591 592 593

    free(sys);
}