예제 #1
0
파일: vf_expand.c 프로젝트: ihling/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
    int e_x = vf->priv->exp_x, e_y = vf->priv->exp_y;
    int e_w = vf->priv->exp_w, e_h = vf->priv->exp_h;

    if (e_x == 0 && e_y == 0 && e_w == mpi->w && e_h == mpi->h)
        return mpi;

    struct mp_image *dmpi = vf_alloc_out_image(vf);
    mp_image_copy_attributes(dmpi, mpi);

    struct mp_image cropped = *dmpi;
    mp_image_crop(&cropped, e_x, e_y, e_x + mpi->w, e_y + mpi->h);
    mp_image_copy(&cropped, mpi);

    int e_x2 = e_x + MP_ALIGN_DOWN(mpi->w, mpi->fmt.align_x);
    int e_y2 = e_y + MP_ALIGN_DOWN(mpi->h, mpi->fmt.align_y);

    // top border (over the full width)
    mp_image_clear(dmpi, 0, 0, e_w, e_y);
    // bottom border (over the full width)
    mp_image_clear(dmpi, 0, e_y2, e_w, e_h);
    // left
    mp_image_clear(dmpi, 0, e_y, e_x, e_y2);
    // right
    mp_image_clear(dmpi, e_x2, e_y, e_w, e_y2);

    talloc_free(mpi);
    return dmpi;
}
예제 #2
0
파일: vf_pp.c 프로젝트: CrimsonVoid/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
    // pass-through if pp disabled
    if (!vf->priv->pp)
        return mpi;

    bool non_local = vf->priv->pp & 0xFFFF;

    struct mp_image *dmpi = mpi;
    if (!mp_image_is_writeable(mpi) || non_local) {
        dmpi = vf_alloc_out_image(vf);
        mp_image_copy_attributes(dmpi, mpi);
    }

    // apparently this is required
    assert(mpi->stride[0] >= ((mpi->w+7)&(~7)));

	// do the postprocessing! (or copy if no DR)
	pp_postprocess((const uint8_t **)mpi->planes, mpi->stride,
		    dmpi->planes,dmpi->stride,
		    (mpi->w+7)&(~7),mpi->h,
		    mpi->qscale, mpi->qstride,
		    vf->priv->ppMode[ vf->priv->pp ], vf->priv->context,
#ifdef PP_PICT_TYPE_QP2
		    mpi->pict_type | (mpi->qscale_type ? PP_PICT_TYPE_QP2 : 0));
#else
		    mpi->pict_type);
#endif

    if (dmpi != mpi)
        talloc_free(mpi);
    return dmpi;
}
예제 #3
0
파일: image_writer.c 프로젝트: AddictXQ/mpv
struct mp_image *convert_image(struct mp_image *image, int destfmt,
                               struct mp_log *log)
{
    int d_w, d_h;
    mp_image_params_get_dsize(&image->params, &d_w, &d_h);
    bool is_anamorphic = image->w != d_w || image->h != d_h;

    // Caveat: no colorspace/levels conversion done if pixel formats equal
    //         it's unclear what colorspace/levels the target wants
    if (image->imgfmt == destfmt && !is_anamorphic)
        return mp_image_new_ref(image);

    struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
    if (!dst) {
        mp_err(log, "Out of memory.\n");
        return NULL;
    }
    mp_image_copy_attributes(dst, image);

    if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
        mp_err(log, "Error when converting image.\n");
        talloc_free(dst);
        return NULL;
    }

    return dst;
}
예제 #4
0
파일: vf_mirror.c 프로젝트: Aseeker/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
    mp_image_t *dmpi = vf_alloc_out_image(vf);
    mp_image_copy_attributes(dmpi, mpi);

    for (int p = 0; p < mpi->num_planes; p++) {
        for (int y = 0; y < mpi->plane_h[p]; y++) {
            void *p_src = mpi->planes[p] + mpi->stride[p] * y;
            void *p_dst = dmpi->planes[p] + dmpi->stride[p] * y;
            int w = dmpi->plane_w[p];
            if (mpi->imgfmt == IMGFMT_YUYV) {
                mirror_4_m(p_dst, p_src, w / 2, 2, 1, 0, 3);
            } else if (mpi->imgfmt == IMGFMT_UYVY) {
                mirror_4_m(p_dst, p_src, w / 2, 0, 3, 2, 1);
            } else {
                // make the compiler unroll the memcpy in mirror()
                switch (mpi->fmt.bytes[p]) {
                case 1: mirror(p_dst, p_src, 1, w); break;
                case 2: mirror(p_dst, p_src, 2, w); break;
                case 3: mirror(p_dst, p_src, 3, w); break;
                case 4: mirror(p_dst, p_src, 4, w); break;
                default:
                    mirror(p_dst, p_src, mpi->fmt.bytes[p], w);
                }
            }
        }
    }

    talloc_free(mpi);
    return dmpi;
}
예제 #5
0
파일: image_writer.c 프로젝트: maletor/mpv
int write_image(struct mp_image *image, const struct image_writer_opts *opts,
                const char *filename)
{
    struct mp_image *allocated_image = NULL;
    struct image_writer_opts defs = image_writer_opts_defaults;
    int d_w = image->display_w ? image->display_w : image->w;
    int d_h = image->display_h ? image->display_h : image->h;
    bool is_anamorphic = image->w != d_w || image->h != d_h;

    if (!opts)
        opts = &defs;

    const struct img_writer *writer = get_writer(opts);
    struct image_writer_ctx ctx = { opts, writer };
    int destfmt = IMGFMT_RGB24;

    if (writer->pixfmts) {
        destfmt = writer->pixfmts[0];   // default to first pixel format
        for (int *fmt = writer->pixfmts; *fmt; fmt++) {
            if (*fmt == image->imgfmt) {
                destfmt = *fmt;
                break;
            }
        }
    }

    // Caveat: no colorspace/levels conversion done if pixel formats equal
    //         it's unclear what colorspace/levels the target wants
    if (image->imgfmt != destfmt || is_anamorphic) {
        struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
        mp_image_copy_attributes(dst, image);

        int flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP |
                    SWS_ACCURATE_RND | SWS_BITEXACT;

        mp_image_swscale(dst, image, flags);

        allocated_image = dst;
        image = dst;
    }

    FILE *fp = fopen(filename, "wb");
    int success = 0;
    if (fp == NULL) {
        mp_msg(MSGT_CPLAYER, MSGL_ERR,
               "Error opening '%s' for writing!\n", filename);
    } else {
        success = writer->write(&ctx, image, fp);
        success = !fclose(fp) && success;
        if (!success)
            mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error writing file '%s'!\n",
                   filename);
    }

    talloc_free(allocated_image);

    return success;
}
예제 #6
0
파일: d3d11va.c 프로젝트: DZW314/mpv
static struct mp_image *d3d11va_retrieve_image(struct lavc_ctx *s,
                                               struct mp_image *img)
{
    HRESULT hr;
    struct priv *p = s->hwdec_priv;
    ID3D11Texture2D              *staging = p->decoder->staging;

    if (img->imgfmt != IMGFMT_D3D11VA)
        return img;

    ID3D11Texture2D *texture = (void *)img->planes[1];
    int subindex = (intptr_t)img->planes[2];

    if (!texture) {
        MP_ERR(p, "Failed to get Direct3D texture and surface from mp_image\n");
        return img;
    }

    D3D11_TEXTURE2D_DESC texture_desc;
    ID3D11Texture2D_GetDesc(texture, &texture_desc);
    if (texture_desc.Width < img->w || texture_desc.Height < img->h) {
        MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n");
        return img;
    }

    // copy to the staging texture
    ID3D11DeviceContext_CopySubresourceRegion(
        p->device_ctx,
        (ID3D11Resource *)staging, 0, 0, 0, 0,
        (ID3D11Resource *)texture, subindex, NULL);

    struct mp_image *sw_img = mp_image_pool_get(p->sw_pool,
                                                p->decoder->mpfmt_decoded,
                                                texture_desc.Width,
                                                texture_desc.Height);
    if (!sw_img) {
        MP_ERR(p, "Failed to get %s surface from CPU pool\n",
               mp_imgfmt_to_name(p->decoder->mpfmt_decoded));
        return img;
    }

    // copy staging texture to the cpu mp_image
    D3D11_MAPPED_SUBRESOURCE lock;
    hr = ID3D11DeviceContext_Map(p->device_ctx, (ID3D11Resource *)staging,
                                 0, D3D11_MAP_READ, 0, &lock);
    if (FAILED(hr)) {
        MP_ERR(p, "Failed to map D3D11 surface: %s\n", mp_HRESULT_to_str(hr));
        talloc_free(sw_img);
        return img;
    }
    copy_nv12(sw_img, lock.pData, lock.RowPitch, texture_desc.Height);
    ID3D11DeviceContext_Unmap(p->device_ctx, (ID3D11Resource *)staging, 0);

    mp_image_set_size(sw_img, img->w, img->h);
    mp_image_copy_attributes(sw_img, img);
    talloc_free(img);
    return sw_img;
}
예제 #7
0
파일: d3d11va.c 프로젝트: CarlOlson/mpv
static struct mp_image *d3d11va_retrieve_image(struct lavc_ctx *s,
                                               struct mp_image *img)
{
    HRESULT hr;
    struct priv *p = s->hwdec_priv;
    ID3D11Texture2D              *staging = p->decoder->staging;
    ID3D11Texture2D              *texture = d3d11_texture_in_mp_image(img);
    ID3D11VideoDecoderOutputView *surface = d3d11_surface_in_mp_image(img);

    if (!texture || !surface) {
        MP_ERR(p, "Failed to get Direct3D texture and surface from mp_image\n");
        return img;
    }

    D3D11_TEXTURE2D_DESC texture_desc;
    ID3D11Texture2D_GetDesc(texture, &texture_desc);
    if (texture_desc.Width < img->w || texture_desc.Height < img->h) {
        MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n");
        return img;
    }

    // copy to the staging texture
    D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC surface_desc;
    ID3D11VideoDecoderOutputView_GetDesc(surface, &surface_desc);
    ID3D11DeviceContext_CopySubresourceRegion(
        p->device_ctx,
        (ID3D11Resource *)staging, 0, 0, 0, 0,
        (ID3D11Resource *)texture, surface_desc.Texture2D.ArraySlice, NULL);

    struct mp_image *sw_img = mp_image_pool_get(p->sw_pool,
                                                p->decoder->mpfmt_decoded,
                                                texture_desc.Width,
                                                texture_desc.Height);
    if (!sw_img) {
        MP_ERR(p, "Failed to get %s surface from CPU pool\n",
               mp_imgfmt_to_name(p->decoder->mpfmt_decoded));
        return img;
    }

    // copy staging texture to the cpu mp_image
    D3D11_MAPPED_SUBRESOURCE lock;
    hr = ID3D11DeviceContext_Map(p->device_ctx, (ID3D11Resource *)staging,
                                 0, D3D11_MAP_READ, 0, &lock);
    if (FAILED(hr)) {
        MP_ERR(p, "Failed to map D3D11 surface: %s\n", mp_HRESULT_to_str(hr));
        talloc_free(sw_img);
        return img;
    }
    copy_nv12(sw_img, lock.pData, lock.RowPitch, texture_desc.Height);
    ID3D11DeviceContext_Unmap(p->device_ctx, (ID3D11Resource *)staging, 0);

    mp_image_set_size(sw_img, img->w, img->h);
    mp_image_copy_attributes(sw_img, img);
    talloc_free(img);
    return sw_img;
}
예제 #8
0
파일: vf_scale.c 프로젝트: amosonn/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
    struct mp_image *dmpi = vf_alloc_out_image(vf);
    mp_image_copy_attributes(dmpi, mpi);

    mp_sws_scale(vf->priv->sws, dmpi, mpi);

    talloc_free(mpi);
    return dmpi;
}
예제 #9
0
mp_image *HwAccVda::getImage(mp_image *mpi) {
	auto buffer = (CVPixelBufferRef)mpi->planes[3];
	auto release = [] (void *arg) {
		CVPixelBufferRef buffer = (CVPixelBufferRef)arg;
		CVPixelBufferRelease(buffer);
	};
	CVPixelBufferRetain(buffer);
	auto img = null_mp_image(IMGFMT_VDA, size().width(), size().height(), buffer, release);
	mp_image_copy_attributes(img, mpi);
	img->planes[3] = mpi->planes[3];
	return img;
}
예제 #10
0
파일: image_writer.c 프로젝트: Nikoli/mpv
int write_image(struct mp_image *image, const struct image_writer_opts *opts,
                const char *filename, struct mp_log *log)
{
    struct mp_image *allocated_image = NULL;
    struct image_writer_opts defs = image_writer_opts_defaults;
    int d_w = image->params.d_w;
    int d_h = image->params.d_h;
    bool is_anamorphic = image->w != d_w || image->h != d_h;

    if (!opts)
        opts = &defs;

    const struct img_writer *writer = get_writer(opts);
    struct image_writer_ctx ctx = { log, opts, writer };
    int destfmt = IMGFMT_RGB24;

    if (writer->pixfmts) {
        destfmt = writer->pixfmts[0];   // default to first pixel format
        for (int *fmt = writer->pixfmts; *fmt; fmt++) {
            if (*fmt == image->imgfmt) {
                destfmt = *fmt;
                break;
            }
        }
    }

    // Caveat: no colorspace/levels conversion done if pixel formats equal
    //         it's unclear what colorspace/levels the target wants
    if (image->imgfmt != destfmt || is_anamorphic) {
        struct mp_image *dst = mp_image_alloc(destfmt, d_w, d_h);
        mp_image_copy_attributes(dst, image);

        mp_image_swscale(dst, image, mp_sws_hq_flags);

        allocated_image = dst;
        image = dst;
    }

    FILE *fp = fopen(filename, "wb");
    int success = 0;
    if (fp == NULL) {
        mp_err(log, "Error opening '%s' for writing!\n", filename);
    } else {
        success = writer->write(&ctx, image, fp);
        success = !fclose(fp) && success;
        if (!success)
            mp_err(log, "Error writing file '%s'!\n", filename);
    }

    talloc_free(allocated_image);

    return success;
}
예제 #11
0
	mp_image *render(const VideoFrame &frame, int flags) {
		if (m_rebuild)
			update();
		auto in = VaApiSurfacePool::getSurface(frame.mpi());
		if (!in)
			return nullptr;
		m_pool.create(5, frame.width(), frame.height(), in->format());
		auto ret = m_pool.getMpImage();
		if (!ret)
			return nullptr;
		auto out = VaApiSurfacePool::getSurface(ret);
		if (!out)
			return nullptr;
		VAProcPipelineParameterBuffer *param = nullptr;
		VABufferID buffer = BufferMan::create(m_context, VAProcPipelineParameterBufferType, param, 1);
		if (buffer == VA_INVALID_ID)
			return nullptr;
		enum {Begun = 1, Rendered = 2};
		int state = 0;
		auto pass = [this, out, &ret, &buffer, &state, &frame] () -> mp_image* {
			if (state & Begun)
				vaEndPicture(VaApi::glx(), m_context);
			if (state & Rendered) {
				mp_image_copy_attributes(ret, frame.mpi());
			} else
				mp_image_unrefp(&ret);
			vaDestroyBuffer(VaApi::glx(), buffer);
			vaSyncSurface(VaApi::glx(), out->id());
			return ret;
		};
		if (!isSuccess(vaBeginPicture(VaApi::glx(), m_context, out->id())))
			return pass();
		state |= Begun;
		if (!BufferMan::map(buffer, param))
			return pass();
		memset(param, 0, sizeof(*param));
		param->surface = in->id();
		param->filter_flags = flags;
		param->filters = &m_buffers.first();
		param->num_filters = m_buffers.size();
		param->forward_references = m_forward_refs.data();
		param->backward_references = m_backward_refs.data();
		param->num_forward_references = m_caps.num_forward_references;
		param->num_backward_references = m_caps.num_backward_references;
		BufferMan::unmap(buffer);
		if (!isSuccess(vaRenderPicture(VaApi::glx(), m_context, &buffer, 1)))
			return pass();
		state |= Rendered;
		return pass();
	}
예제 #12
0
파일: vf_rotate.c 프로젝트: ArcherSeven/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
    if (vf->priv->direction & 4)
        return mpi;

    struct mp_image *dmpi = vf_alloc_out_image(vf);
    mp_image_copy_attributes(dmpi, mpi);

    for (int p = 0; p < mpi->num_planes; p++) {
        rotate(dmpi->planes[p],mpi->planes[p], dmpi->stride[p],mpi->stride[p],
               dmpi->plane_w[p], dmpi->plane_h[p], mpi->fmt.bytes[p],
               vf->priv->direction);
    }

    talloc_free(mpi);
    return dmpi;
}
예제 #13
0
파일: hw_dxva2.c 프로젝트: wang-bin/mpv
static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
                                             struct mp_image *img)
{
    HRESULT hr;
    struct priv *p = s->hwdec_priv;
    IDirect3DSurface9 *surface = img->imgfmt == IMGFMT_DXVA2 ?
        (IDirect3DSurface9 *)img->planes[3] : NULL;

    if (!surface) {
        MP_ERR(p, "Failed to get Direct3D surface from mp_image\n");
        return img;
    }

    D3DSURFACE_DESC surface_desc;
    IDirect3DSurface9_GetDesc(surface, &surface_desc);
    if (surface_desc.Width < img->w || surface_desc.Height < img->h) {
        MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n");
        return img;
    }

    struct mp_image *sw_img = mp_image_pool_get(p->sw_pool,
                                                p->mpfmt_decoded,
                                                surface_desc.Width,
                                                surface_desc.Height);
    if (!sw_img) {
        MP_ERR(p, "Failed to get %s surface from CPU pool\n",
               mp_imgfmt_to_name(p->mpfmt_decoded));
        return img;
    }

    D3DLOCKED_RECT lock;
    hr = IDirect3DSurface9_LockRect(surface, &lock, NULL, D3DLOCK_READONLY);
    if (FAILED(hr)) {
        MP_ERR(p, "Unable to lock DXVA2 surface: %s\n",
               mp_HRESULT_to_str(hr));
        talloc_free(sw_img);
        return img;
    }
    copy_nv12(sw_img, lock.pBits, lock.Pitch, surface_desc.Height);
    IDirect3DSurface9_UnlockRect(surface);

    mp_image_set_size(sw_img, img->w, img->h);
    mp_image_copy_attributes(sw_img, img);
    talloc_free(img);
    return sw_img;
}
예제 #14
0
파일: vf_vdpaurb.c 프로젝트: EDEYUAN/mpv
static int filter_ext(struct vf_instance *vf, struct mp_image *mpi)
{
    VdpStatus vdp_st;
    struct vf_priv_s *p = vf->priv;
    struct mp_vdpau_ctx *ctx = p->ctx;
    struct vdp_functions *vdp = &ctx->vdp;

    if (!mpi) {
        return 0;
    }

    // Pass-through anything that's not been decoded by VDPAU
    if (mpi->imgfmt != IMGFMT_VDPAU) {
        vf_add_output_frame(vf, mpi);
        return 0;
    }

    if (mp_vdpau_mixed_frame_get(mpi)) {
        MP_ERR(vf, "Can't apply vdpaurb filter after vdpaupp filter.\n");
        mp_image_unrefp(&mpi);
        return -1;
    }

    struct mp_image *out = vf_alloc_out_image(vf);
    if (!out) {
        mp_image_unrefp(&mpi);
        return -1;
    }
    mp_image_copy_attributes(out, mpi);

    VdpVideoSurface surface = (uintptr_t)mpi->planes[3];
    assert(surface > 0);

    vdp_st = vdp->video_surface_get_bits_y_cb_cr(surface,
                                                 VDP_YCBCR_FORMAT_NV12,
                                                 (void * const *)out->planes,
                                                 out->stride);
    CHECK_VDP_WARNING(vf, "Error when calling vdp_output_surface_get_bits_y_cb_cr");

    vf_add_output_frame(vf, out);
    mp_image_unrefp(&mpi);
    return 0;
}
예제 #15
0
파일: dxva2.c 프로젝트: qq361168780/mpv
static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s,
                                             struct mp_image *img)
{
    DXVA2Context       *ctx = s->hwdec_priv;
    LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)img->planes[3];
    D3DSURFACE_DESC    surfaceDesc;
    D3DLOCKED_RECT     LockedRect;
    HRESULT            hr;

    IDirect3DSurface9_GetDesc(surface, &surfaceDesc);

    if (surfaceDesc.Width < img->w || surfaceDesc.Height < img->h)
        return img;

    struct mp_image *sw_img =
        mp_image_pool_get(ctx->sw_pool, IMGFMT_NV12, surfaceDesc.Width, surfaceDesc.Height);

    if (!sw_img)
        return img;

    hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, D3DLOCK_READONLY);
    if (FAILED(hr)) {
        MP_ERR(ctx, "Unable to lock DXVA2 surface\n");
        talloc_free(sw_img);
        return img;
    }

    copy_nv12(sw_img, LockedRect.pBits, LockedRect.Pitch, surfaceDesc.Height);
    mp_image_set_size(sw_img, img->w, img->h);
    mp_image_copy_attributes(sw_img, img);

    IDirect3DSurface9_UnlockRect(surface);

    talloc_free(img);
    return sw_img;
}
예제 #16
0
파일: image_writer.c 프로젝트: jmglogow/mpv
struct mp_image *convert_image(struct mp_image *image, int destfmt,
                               struct mp_log *log)
{
    int d_w, d_h;
    mp_image_params_get_dsize(&image->params, &d_w, &d_h);

    struct mp_image_params p = {
        .imgfmt = destfmt,
        .w = d_w,
        .h = d_h,
        .p_w = 1,
        .p_h = 1,
    };
    mp_image_params_guess_csp(&p);

    // If RGB, just assume everything is correct.
    if (p.color.space != MP_CSP_RGB) {
        // Currently, assume what FFmpeg's jpg encoder needs.
        // Of course this works only for non-HDR (no HDR support in libswscale).
        p.color.levels = MP_CSP_LEVELS_PC;
        p.color.space = MP_CSP_BT_601;
        p.chroma_location = MP_CHROMA_CENTER;
        mp_image_params_guess_csp(&p);
    }

    if (mp_image_params_equal(&p, &image->params))
        return mp_image_new_ref(image);

    struct mp_image *dst = mp_image_alloc(p.imgfmt, p.w, p.h);
    if (!dst) {
        mp_err(log, "Out of memory.\n");
        return NULL;
    }
    mp_image_copy_attributes(dst, image);

    dst->params = p;

    if (mp_image_swscale(dst, image, mp_sws_hq_flags) < 0) {
        mp_err(log, "Error when converting image.\n");
        talloc_free(dst);
        return NULL;
    }

    return dst;
}

bool write_image(struct mp_image *image, const struct image_writer_opts *opts,
                const char *filename, struct mp_log *log)
{
    struct image_writer_opts defs = image_writer_opts_defaults;
    if (!opts)
        opts = &defs;

    struct image_writer_ctx ctx = { log, opts, image->fmt };
    bool (*write)(struct image_writer_ctx *, mp_image_t *, FILE *) = write_lavc;
    int destfmt = 0;

#if HAVE_JPEG
    if (opts->format == AV_CODEC_ID_MJPEG) {
        write = write_jpeg;
        destfmt = IMGFMT_RGB24;
    }
#endif

    if (!destfmt)
        destfmt = get_target_format(&ctx);

    struct mp_image *dst = convert_image(image, destfmt, log);
    if (!dst)
        return false;

    FILE *fp = fopen(filename, "wb");
    bool success = false;
    if (fp == NULL) {
        mp_err(log, "Error opening '%s' for writing!\n", filename);
    } else {
        success = write(&ctx, dst, fp);
        success = !fclose(fp) && success;
        if (!success)
            mp_err(log, "Error writing file '%s'!\n", filename);
    }

    talloc_free(dst);
    return success;
}

void dump_png(struct mp_image *image, const char *filename, struct mp_log *log)
{
    struct image_writer_opts opts = image_writer_opts_defaults;
    opts.format = AV_CODEC_ID_PNG;
    write_image(image, &opts, filename, log);
}
예제 #17
0
static int recreate_video_proc(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;
    HRESULT hr;

    destroy_video_proc(vf);

    D3D11_VIDEO_PROCESSOR_CONTENT_DESC vpdesc = {
        .InputFrameFormat = p->d3d_frame_format,
        .InputWidth = p->c_w,
        .InputHeight = p->c_h,
        .OutputWidth = p->params.w,
        .OutputHeight = p->params.h,
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator(p->video_dev, &vpdesc,
                                                          &p->vp_enum);
    if (FAILED(hr))
        goto fail;

    D3D11_VIDEO_PROCESSOR_CAPS caps;
    hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(p->vp_enum, &caps);
    if (FAILED(hr))
        goto fail;

    MP_VERBOSE(vf, "Found %d rate conversion caps. Looking for caps=0x%x.\n",
               (int)caps.RateConversionCapsCount, p->mode);

    int rindex = -1;
    for (int n = 0; n < caps.RateConversionCapsCount; n++) {
        D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rcaps;
        hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps
                (p->vp_enum, n, &rcaps);
        if (FAILED(hr))
            goto fail;
        MP_VERBOSE(vf, "  - %d: 0x%08x\n", n, (unsigned)rcaps.ProcessorCaps);
        if (rcaps.ProcessorCaps & p->mode) {
            MP_VERBOSE(vf, "       (matching)\n");
            if (rindex < 0)
                rindex = n;
        }
    }

    if (rindex < 0) {
        MP_WARN(vf, "No fitting video processor found, picking #0.\n");
        rindex = 0;
    }

    // TOOD: so, how do we select which rate conversion mode the processor uses?

    hr = ID3D11VideoDevice_CreateVideoProcessor(p->video_dev, p->vp_enum, rindex,
                                                &p->video_proc);
    if (FAILED(hr)) {
        MP_ERR(vf, "Failed to create D3D11 video processor.\n");
        goto fail;
    }

    // Note: libavcodec does not support cropping left/top with hwaccel.
    RECT src_rc = {
        .right = p->params.w,
        .bottom = p->params.h,
    };
    ID3D11VideoContext_VideoProcessorSetStreamSourceRect(p->video_ctx,
                                                         p->video_proc,
                                                         0, TRUE, &src_rc);

    // This is supposed to stop drivers from f*****g up the video quality.
    ID3D11VideoContext_VideoProcessorSetStreamAutoProcessingMode(p->video_ctx,
                                                                 p->video_proc,
                                                                 0, FALSE);

    ID3D11VideoContext_VideoProcessorSetStreamOutputRate(p->video_ctx,
                                                         p->video_proc,
                                                         0,
                                                         D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL,
                                                         FALSE, 0);

    D3D11_VIDEO_PROCESSOR_COLOR_SPACE csp = {
        .YCbCr_Matrix = p->params.color.space != MP_CSP_BT_601,
        .Nominal_Range = p->params.color.levels == MP_CSP_LEVELS_TV ? 1 : 2,
    };
    ID3D11VideoContext_VideoProcessorSetStreamColorSpace(p->video_ctx,
                                                         p->video_proc,
                                                         0, &csp);
    if (p->out_rgb) {
        if (p->params.color.space != MP_CSP_BT_601 &&
            p->params.color.space != MP_CSP_BT_709)
        {
            MP_WARN(vf, "Unsupported video colorspace (%s/%s). Consider "
                    "disabling hardware decoding, or using "
                    "--hwdec=d3d11va-copy to get correct output.\n",
                    m_opt_choice_str(mp_csp_names, p->params.color.space),
                    m_opt_choice_str(mp_csp_levels_names, p->params.color.levels));
        }
    } else {
        ID3D11VideoContext_VideoProcessorSetOutputColorSpace(p->video_ctx,
                                                             p->video_proc,
                                                             &csp);
    }

    return 0;
fail:
    destroy_video_proc(vf);
    return -1;
}

static int render(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;
    int res = -1;
    HRESULT hr;
    ID3D11VideoProcessorInputView *in_view = NULL;
    ID3D11VideoProcessorOutputView *out_view = NULL;
    struct mp_image *in = NULL, *out = NULL;
    out = mp_image_pool_get(p->pool, p->out_params.imgfmt, p->params.w, p->params.h);
    if (!out)
        goto cleanup;

    ID3D11Texture2D *d3d_out_tex = (void *)out->planes[1];

    in = mp_refqueue_get(p->queue, 0);
    if (!in)
        goto cleanup;
    ID3D11Texture2D *d3d_tex = (void *)in->planes[1];
    int d3d_subindex = (intptr_t)in->planes[2];

    mp_image_copy_attributes(out, in);

    D3D11_VIDEO_FRAME_FORMAT d3d_frame_format;
    if (!mp_refqueue_should_deint(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    } else if (mp_refqueue_top_field_first(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
    } else {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
    }

    D3D11_TEXTURE2D_DESC texdesc;
    ID3D11Texture2D_GetDesc(d3d_tex, &texdesc);
    if (!p->video_proc || p->c_w != texdesc.Width || p->c_h != texdesc.Height ||
        p->d3d_frame_format != d3d_frame_format)
    {
        p->c_w = texdesc.Width;
        p->c_h = texdesc.Height;
        p->d3d_frame_format = d3d_frame_format;
        if (recreate_video_proc(vf) < 0)
            goto cleanup;
    }

    if (!mp_refqueue_should_deint(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    } else if (mp_refqueue_is_top_field(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
    } else {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
    }

    ID3D11VideoContext_VideoProcessorSetStreamFrameFormat(p->video_ctx,
                                                          p->video_proc,
                                                          0, d3d_frame_format);

    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC indesc = {
        .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
        .Texture2D = {
            .ArraySlice = d3d_subindex,
        },
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorInputView(p->video_dev,
                                                         (ID3D11Resource *)d3d_tex,
                                                         p->vp_enum, &indesc,
                                                         &in_view);
    if (FAILED(hr)) {
        MP_ERR(vf, "Could not create ID3D11VideoProcessorInputView\n");
        goto cleanup;
    }

    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outdesc = {
        .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorOutputView(p->video_dev,
                                                          (ID3D11Resource *)d3d_out_tex,
                                                          p->vp_enum, &outdesc,
                                                          &out_view);
    if (FAILED(hr))
        goto cleanup;

    D3D11_VIDEO_PROCESSOR_STREAM stream = {
        .Enable = TRUE,
        .pInputSurface = in_view,
    };
    int frame = mp_refqueue_is_second_field(p->queue);
    hr = ID3D11VideoContext_VideoProcessorBlt(p->video_ctx, p->video_proc,
                                              out_view, frame, 1, &stream);
    if (FAILED(hr)) {
        MP_ERR(vf, "VideoProcessorBlt failed.\n");
        goto cleanup;
    }

    res = 0;
cleanup:
    if (in_view)
        ID3D11VideoProcessorInputView_Release(in_view);
    if (out_view)
        ID3D11VideoProcessorOutputView_Release(out_view);
    if (res >= 0) {
        vf_add_output_frame(vf, out);
    } else {
        talloc_free(out);
    }
    mp_refqueue_next_field(p->queue);
    return res;
}

static int filter_out(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;

    if (!mp_refqueue_has_output(p->queue))
        return 0;

    // no filtering
    if (!mp_refqueue_should_deint(p->queue) && !p->require_filtering) {
        struct mp_image *in = mp_image_new_ref(mp_refqueue_get(p->queue, 0));
        if (!in)
            return -1;
        mp_image_set_params(in, &p->out_params);
        vf_add_output_frame(vf, in);
        mp_refqueue_next(p->queue);
        return 0;
    }

    return render(vf);
}

static int reconfig(struct vf_instance *vf, struct mp_image_params *in,
                    struct mp_image_params *out)
{
    struct vf_priv_s *p = vf->priv;

    flush_frames(vf);
    talloc_free(p->pool);
    p->pool = NULL;

    destroy_video_proc(vf);

    *out = *in;

    if (vf_next_query_format(vf, IMGFMT_D3D11VA) ||
        vf_next_query_format(vf, IMGFMT_D3D11NV12))
    {
        out->imgfmt = vf_next_query_format(vf, IMGFMT_D3D11VA)
                    ? IMGFMT_D3D11VA : IMGFMT_D3D11NV12;
        out->hw_subfmt = IMGFMT_NV12;
        p->out_format = DXGI_FORMAT_NV12;
        p->out_shared = false;
        p->out_rgb = false;
    } else {
        out->imgfmt = IMGFMT_D3D11RGB;
        out->hw_subfmt = IMGFMT_RGB0;
        p->out_format = DXGI_FORMAT_B8G8R8A8_UNORM;
        p->out_shared = true;
        p->out_rgb = true;
    }

    p->require_filtering = in->hw_subfmt != out->hw_subfmt;

    p->params = *in;
    p->out_params = *out;

    p->pool = mp_image_pool_new(20);
    mp_image_pool_set_allocator(p->pool, alloc_pool, vf);
    mp_image_pool_set_lru(p->pool);

    return 0;
}

static void uninit(struct vf_instance *vf)
{
    struct vf_priv_s *p = vf->priv;

    destroy_video_proc(vf);

    flush_frames(vf);
    mp_refqueue_free(p->queue);
    talloc_free(p->pool);

    if (p->video_ctx)
        ID3D11VideoContext_Release(p->video_ctx);

    if (p->video_dev)
        ID3D11VideoDevice_Release(p->video_dev);

    if (p->device_ctx)
        ID3D11DeviceContext_Release(p->device_ctx);

    if (p->vo_dev)
        ID3D11Device_Release(p->vo_dev);
}

static int query_format(struct vf_instance *vf, unsigned int imgfmt)
{
    if (imgfmt == IMGFMT_D3D11VA ||
        imgfmt == IMGFMT_D3D11NV12 ||
        imgfmt == IMGFMT_D3D11RGB)
    {
        return vf_next_query_format(vf, IMGFMT_D3D11VA) ||
               vf_next_query_format(vf, IMGFMT_D3D11NV12) ||
               vf_next_query_format(vf, IMGFMT_D3D11RGB);
    }
    return 0;
}

static bool test_conversion(int in, int out)
{
    return (in == IMGFMT_D3D11VA ||
            in == IMGFMT_D3D11NV12 ||
            in == IMGFMT_D3D11RGB) &&
           (out == IMGFMT_D3D11VA ||
            out == IMGFMT_D3D11NV12 ||
            out == IMGFMT_D3D11RGB);
}

static int control(struct vf_instance *vf, int request, void* data)
{
    struct vf_priv_s *p = vf->priv;
    switch (request){
    case VFCTRL_GET_DEINTERLACE:
        *(int*)data = !!p->deint_enabled;
        return true;
    case VFCTRL_SET_DEINTERLACE:
        p->deint_enabled = !!*(int*)data;
        return true;
    case VFCTRL_SEEK_RESET:
        flush_frames(vf);
        return true;
    default:
        return CONTROL_UNKNOWN;
    }
}

static int vf_open(vf_instance_t *vf)
{
    struct vf_priv_s *p = vf->priv;

    vf->reconfig = reconfig;
    vf->filter_ext = filter_ext;
    vf->filter_out = filter_out;
    vf->query_format = query_format;
    vf->uninit = uninit;
    vf->control = control;

    p->queue = mp_refqueue_alloc();

    p->vo_dev = hwdec_devices_load(vf->hwdec_devs, HWDEC_D3D11VA);
    if (!p->vo_dev)
        return 0;

    ID3D11Device_AddRef(p->vo_dev);

    HRESULT hr;

    hr = ID3D11Device_QueryInterface(p->vo_dev, &IID_ID3D11VideoDevice,
                                     (void **)&p->video_dev);
    if (FAILED(hr))
        goto fail;

    ID3D11Device_GetImmediateContext(p->vo_dev, &p->device_ctx);
    if (!p->device_ctx)
        goto fail;
    hr = ID3D11DeviceContext_QueryInterface(p->device_ctx, &IID_ID3D11VideoContext,
                                            (void **)&p->video_ctx);
    if (FAILED(hr))
        goto fail;

    return 1;

fail:
    uninit(vf);
    return 0;
}

#define OPT_BASE_STRUCT struct vf_priv_s
static const m_option_t vf_opts_fields[] = {
    OPT_FLAG("deint", deint_enabled, 0),
    OPT_FLAG("interlaced-only", interlaced_only, 0),
    OPT_CHOICE("mode", mode, 0,
        ({"blend", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND},
         {"bob", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB},
         {"adaptive", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE},
         {"mocomp", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION},
예제 #18
0
파일: vf_pullup.c 프로젝트: ArcherSeven/mpv
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi)
{
	struct pullup_context *c = vf->priv->ctx;
	struct pullup_buffer *b;
	struct pullup_frame *f;
	int p;
	int i;
        double pts = mpi->pts;
        struct mp_image *dmpi = NULL;

	if (!vf->priv->init) init_pullup(vf, mpi);

	if (1) {
		b = pullup_get_buffer(c, 2);
		if (!b) {
			mp_msg(MSGT_VFILTER,MSGL_ERR,"Could not get buffer from pullup!\n");
			f = pullup_get_frame(c);
			pullup_release_frame(f);
			goto skip;
		}
		memcpy_pic(b->planes[0], mpi->planes[0], mpi->w, mpi->h,
			c->stride[0], mpi->stride[0]);
			memcpy_pic(b->planes[1], mpi->planes[1],
				mpi->chroma_width, mpi->chroma_height,
				c->stride[1], mpi->stride[1]);
			memcpy_pic(b->planes[2], mpi->planes[2],
				mpi->chroma_width, mpi->chroma_height,
				c->stride[2], mpi->stride[2]);
	}
	if (mpi->qscale) {
		memcpy(b->planes[3], mpi->qscale, c->w[3]);
		memcpy(b->planes[3]+c->w[3], mpi->qscale, c->w[3]);
	}

	p = mpi->fields & MP_IMGFIELD_TOP_FIRST ? 0 :
		(mpi->fields & MP_IMGFIELD_ORDERED ? 1 : 0);

	if (pts == MP_NOPTS_VALUE) {
		pullup_submit_field(c, b, p, MP_NOPTS_VALUE);
		pullup_submit_field(c, b, p^1, MP_NOPTS_VALUE);
		if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST)
			pullup_submit_field(c, b, p, MP_NOPTS_VALUE);
	} else {
		double delta;
		if (vf->priv->lastpts == MP_NOPTS_VALUE)
			delta = 1001.0/60000.0; // delta = field time distance
		else
			delta = (pts - vf->priv->lastpts) / 2;
		if (delta <= 0.0 || delta >= 0.5)
			delta = 0.0;
		vf->priv->lastpts = pts;
		if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) {
			pullup_submit_field(c, b, p, pts - delta);
			pullup_submit_field(c, b, p^1, pts);
			pullup_submit_field(c, b, p, pts + delta);
		} else {
			pullup_submit_field(c, b, p, pts - delta * 0.5);
			pullup_submit_field(c, b, p^1, pts + delta * 0.5);
		}
	}

	pullup_release_buffer(b, 2);

	f = pullup_get_frame(c);

	/* Fake yes for first few frames (buffer depth) to keep from
	 * breaking A/V sync with G1's bad architecture... */
	//if (!f) return vf->priv->fakecount ? (--vf->priv->fakecount,1) : 0;
	if (!f)
            goto skip;

	if (f->length < 2) {
		pullup_release_frame(f);
		f = pullup_get_frame(c);
		if (!f) goto skip;
		if (f->length < 2) {
			pullup_release_frame(f);
			if (!(mpi->fields & MP_IMGFIELD_REPEAT_FIRST))
				goto skip;
			f = pullup_get_frame(c);
			if (!f) goto skip;
			if (f->length < 2) {
				pullup_release_frame(f);
				goto skip;
			}
		}
	}

#if 0
	/* Average qscale tables from both frames. */
	if (mpi->qscale) {
		for (i=0; i<c->w[3]; i++) {
			vf->priv->qbuf[i] = (f->ofields[0]->planes[3][i]
				+ f->ofields[1]->planes[3][i+c->w[3]])>>1;
		}
	}
#else
	/* Take worst of qscale tables from both frames. */
	if (mpi->qscale) {
		for (i=0; i<c->w[3]; i++) {
			vf->priv->qbuf[i] = MAX(f->ofields[0]->planes[3][i], f->ofields[1]->planes[3][i+c->w[3]]);
		}
	}
#endif

        /* If the frame isn't already exportable... */
        if (!f->buffer)
            pullup_pack_frame(c, f);

        // NOTE: the copy could probably be avoided by changing or using the
        //       pullup internal buffer management. But right now just do the
        //       safe thing and always copy. Code outside the filter might
        //       hold a buffer reference even if the filter chain is destroyed.
        dmpi = vf_alloc_out_image(vf);
        mp_image_copy_attributes(dmpi, mpi);

        struct mp_image data = *dmpi;

	data.planes[0] = f->buffer->planes[0];
	data.planes[1] = f->buffer->planes[1];
	data.planes[2] = f->buffer->planes[2];

	data.stride[0] = c->stride[0];
	data.stride[1] = c->stride[1];
	data.stride[2] = c->stride[2];

        mp_image_copy(dmpi, &data);

        dmpi->pts = f->pts;

        // Warning: entirely bogus memory management of qscale
	if (mpi->qscale) {
		dmpi->qscale = vf->priv->qbuf;
		dmpi->qstride = mpi->qstride;
		dmpi->qscale_type = mpi->qscale_type;
	}
	pullup_release_frame(f);

skip:
        talloc_free(mpi);
	return dmpi;
}
예제 #19
0
파일: vf_d3d11vpp.c 프로젝트: Akemi/mpv
static int recreate_video_proc(struct mp_filter *vf)
{
    struct priv *p = vf->priv;
    HRESULT hr;

    destroy_video_proc(vf);

    D3D11_VIDEO_PROCESSOR_CONTENT_DESC vpdesc = {
        .InputFrameFormat = p->d3d_frame_format,
        .InputWidth = p->c_w,
        .InputHeight = p->c_h,
        .OutputWidth = p->params.w,
        .OutputHeight = p->params.h,
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorEnumerator(p->video_dev, &vpdesc,
                                                          &p->vp_enum);
    if (FAILED(hr))
        goto fail;

    D3D11_VIDEO_PROCESSOR_CAPS caps;
    hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorCaps(p->vp_enum, &caps);
    if (FAILED(hr))
        goto fail;

    MP_VERBOSE(vf, "Found %d rate conversion caps. Looking for caps=0x%x.\n",
               (int)caps.RateConversionCapsCount, p->opts->mode);

    int rindex = -1;
    for (int n = 0; n < caps.RateConversionCapsCount; n++) {
        D3D11_VIDEO_PROCESSOR_RATE_CONVERSION_CAPS rcaps;
        hr = ID3D11VideoProcessorEnumerator_GetVideoProcessorRateConversionCaps
                (p->vp_enum, n, &rcaps);
        if (FAILED(hr))
            goto fail;
        MP_VERBOSE(vf, "  - %d: 0x%08x\n", n, (unsigned)rcaps.ProcessorCaps);
        if (rcaps.ProcessorCaps & p->opts->mode) {
            MP_VERBOSE(vf, "       (matching)\n");
            if (rindex < 0)
                rindex = n;
        }
    }

    if (rindex < 0) {
        MP_WARN(vf, "No fitting video processor found, picking #0.\n");
        rindex = 0;
    }

    // TOOD: so, how do we select which rate conversion mode the processor uses?

    hr = ID3D11VideoDevice_CreateVideoProcessor(p->video_dev, p->vp_enum, rindex,
                                                &p->video_proc);
    if (FAILED(hr)) {
        MP_ERR(vf, "Failed to create D3D11 video processor.\n");
        goto fail;
    }

    // Note: libavcodec does not support cropping left/top with hwaccel.
    RECT src_rc = {
        .right = p->params.w,
        .bottom = p->params.h,
    };
    ID3D11VideoContext_VideoProcessorSetStreamSourceRect(p->video_ctx,
                                                         p->video_proc,
                                                         0, TRUE, &src_rc);

    // This is supposed to stop drivers from f*****g up the video quality.
    ID3D11VideoContext_VideoProcessorSetStreamAutoProcessingMode(p->video_ctx,
                                                                 p->video_proc,
                                                                 0, FALSE);

    ID3D11VideoContext_VideoProcessorSetStreamOutputRate(p->video_ctx,
                                                         p->video_proc,
                                                         0,
                                                         D3D11_VIDEO_PROCESSOR_OUTPUT_RATE_NORMAL,
                                                         FALSE, 0);

    D3D11_VIDEO_PROCESSOR_COLOR_SPACE csp = {
        .YCbCr_Matrix = p->params.color.space != MP_CSP_BT_601,
        .Nominal_Range = p->params.color.levels == MP_CSP_LEVELS_TV ? 1 : 2,
    };
    ID3D11VideoContext_VideoProcessorSetStreamColorSpace(p->video_ctx,
                                                         p->video_proc,
                                                         0, &csp);
    if (p->out_rgb) {
        if (p->params.color.space != MP_CSP_BT_601 &&
            p->params.color.space != MP_CSP_BT_709)
        {
            MP_WARN(vf, "Unsupported video colorspace (%s/%s). Consider "
                    "disabling hardware decoding, or using "
                    "--hwdec=d3d11va-copy to get correct output.\n",
                    m_opt_choice_str(mp_csp_names, p->params.color.space),
                    m_opt_choice_str(mp_csp_levels_names, p->params.color.levels));
        }
    } else {
        ID3D11VideoContext_VideoProcessorSetOutputColorSpace(p->video_ctx,
                                                             p->video_proc,
                                                             &csp);
    }

    return 0;
fail:
    destroy_video_proc(vf);
    return -1;
}

static struct mp_image *render(struct mp_filter *vf)
{
    struct priv *p = vf->priv;
    int res = -1;
    HRESULT hr;
    ID3D11VideoProcessorInputView *in_view = NULL;
    ID3D11VideoProcessorOutputView *out_view = NULL;
    struct mp_image *in = NULL, *out = NULL;
    out = mp_image_pool_get(p->pool, IMGFMT_D3D11, p->params.w, p->params.h);
    if (!out) {
        MP_WARN(vf, "failed to allocate frame\n");
        goto cleanup;
    }

    ID3D11Texture2D *d3d_out_tex = (void *)out->planes[0];

    in = mp_refqueue_get(p->queue, 0);
    if (!in)
        goto cleanup;
    ID3D11Texture2D *d3d_tex = (void *)in->planes[0];
    int d3d_subindex = (intptr_t)in->planes[1];

    mp_image_copy_attributes(out, in);

    D3D11_VIDEO_FRAME_FORMAT d3d_frame_format;
    if (!mp_refqueue_should_deint(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    } else if (mp_refqueue_top_field_first(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
    } else {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
    }

    D3D11_TEXTURE2D_DESC texdesc;
    ID3D11Texture2D_GetDesc(d3d_tex, &texdesc);
    if (!p->video_proc || p->c_w != texdesc.Width || p->c_h != texdesc.Height ||
        p->d3d_frame_format != d3d_frame_format)
    {
        p->c_w = texdesc.Width;
        p->c_h = texdesc.Height;
        p->d3d_frame_format = d3d_frame_format;
        if (recreate_video_proc(vf) < 0)
            goto cleanup;
    }

    if (!mp_refqueue_should_deint(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
    } else if (mp_refqueue_is_top_field(p->queue)) {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_TOP_FIELD_FIRST;
    } else {
        d3d_frame_format = D3D11_VIDEO_FRAME_FORMAT_INTERLACED_BOTTOM_FIELD_FIRST;
    }

    ID3D11VideoContext_VideoProcessorSetStreamFrameFormat(p->video_ctx,
                                                          p->video_proc,
                                                          0, d3d_frame_format);

    D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC indesc = {
        .ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D,
        .Texture2D = {
            .ArraySlice = d3d_subindex,
        },
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorInputView(p->video_dev,
                                                         (ID3D11Resource *)d3d_tex,
                                                         p->vp_enum, &indesc,
                                                         &in_view);
    if (FAILED(hr)) {
        MP_ERR(vf, "Could not create ID3D11VideoProcessorInputView\n");
        goto cleanup;
    }

    D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outdesc = {
        .ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D,
    };
    hr = ID3D11VideoDevice_CreateVideoProcessorOutputView(p->video_dev,
                                                          (ID3D11Resource *)d3d_out_tex,
                                                          p->vp_enum, &outdesc,
                                                          &out_view);
    if (FAILED(hr)) {
        MP_ERR(vf, "Could not create ID3D11VideoProcessorOutputView\n");
        goto cleanup;
    }

    D3D11_VIDEO_PROCESSOR_STREAM stream = {
        .Enable = TRUE,
        .pInputSurface = in_view,
    };
    int frame = mp_refqueue_is_second_field(p->queue);
    hr = ID3D11VideoContext_VideoProcessorBlt(p->video_ctx, p->video_proc,
                                              out_view, frame, 1, &stream);
    if (FAILED(hr)) {
        MP_ERR(vf, "VideoProcessorBlt failed.\n");
        goto cleanup;
    }

    res = 0;
cleanup:
    if (in_view)
        ID3D11VideoProcessorInputView_Release(in_view);
    if (out_view)
        ID3D11VideoProcessorOutputView_Release(out_view);
    if (res < 0)
        TA_FREEP(&out);
    return out;
}

static bool vo_supports(struct priv *p, int subfmt)
{
    for (int n = 0; p->vo_formats && p->vo_formats[n]; n++) {
        if (p->vo_formats[n] == subfmt)
            return true;
    }
    return false;
}

static void vf_d3d11vpp_process(struct mp_filter *vf)
{
    struct priv *p = vf->priv;

    struct mp_image *in_fmt = mp_refqueue_execute_reinit(p->queue);
    if (in_fmt) {
        mp_image_pool_clear(p->pool);

        destroy_video_proc(vf);

        p->params = in_fmt->params;
        p->out_params = p->params;

        if (vo_supports(p, IMGFMT_NV12)) {
            p->out_params.hw_subfmt = IMGFMT_NV12;
            p->out_format = DXGI_FORMAT_NV12;
            p->out_shared = false;
            p->out_rgb = false;
        } else {
            p->out_params.hw_subfmt = IMGFMT_RGB0;
            p->out_format = DXGI_FORMAT_B8G8R8A8_UNORM;
            p->out_shared = true;
            p->out_rgb = true;
        }
        p->out_params.hw_flags = 0;

        p->require_filtering = p->params.hw_subfmt != p->out_params.hw_subfmt;
    }

    if (!mp_refqueue_can_output(p->queue))
        return;

    if (!mp_refqueue_should_deint(p->queue) && !p->require_filtering) {
        // no filtering
        struct mp_image *in = mp_image_new_ref(mp_refqueue_get(p->queue, 0));
        if (!in) {
            mp_filter_internal_mark_failed(vf);
            return;
        }
        mp_refqueue_write_out_pin(p->queue, in);
    } else {
        mp_refqueue_write_out_pin(p->queue, render(vf));
    }
}

static void uninit(struct mp_filter *vf)
{
    struct priv *p = vf->priv;

    destroy_video_proc(vf);

    flush_frames(vf);
    talloc_free(p->queue);
    talloc_free(p->pool);

    if (p->video_ctx)
        ID3D11VideoContext_Release(p->video_ctx);

    if (p->video_dev)
        ID3D11VideoDevice_Release(p->video_dev);

    if (p->device_ctx)
        ID3D11DeviceContext_Release(p->device_ctx);

    if (p->vo_dev)
        ID3D11Device_Release(p->vo_dev);
}

static const struct mp_filter_info vf_d3d11vpp_filter = {
    .name = "d3d11vpp",
    .process = vf_d3d11vpp_process,
    .reset = flush_frames,
    .destroy = uninit,
    .priv_size = sizeof(struct priv),
};

static struct mp_filter *vf_d3d11vpp_create(struct mp_filter *parent,
                                            void *options)
{
    struct mp_filter *f = mp_filter_create(parent, &vf_d3d11vpp_filter);
    if (!f) {
        talloc_free(options);
        return NULL;
    }

    mp_filter_add_pin(f, MP_PIN_IN, "in");
    mp_filter_add_pin(f, MP_PIN_OUT, "out");

    struct priv *p = f->priv;
    p->opts = talloc_steal(p, options);

    // Special path for vf_d3d11_create_outconv(): disable all processing except
    // possibly surface format conversions.
    if (!p->opts) {
        static const struct opts opts = {0};
        p->opts = (struct opts *)&opts;
    }

    p->queue = mp_refqueue_alloc(f);

    struct mp_stream_info *info = mp_filter_find_stream_info(f);
    if (!info || !info->hwdec_devs)
        goto fail;

    hwdec_devices_request_all(info->hwdec_devs);

    struct mp_hwdec_ctx *hwctx =
        hwdec_devices_get_by_lavc(info->hwdec_devs, AV_HWDEVICE_TYPE_D3D11VA);
    if (!hwctx || !hwctx->av_device_ref)
        goto fail;
    AVHWDeviceContext *avhwctx = (void *)hwctx->av_device_ref->data;
    AVD3D11VADeviceContext *d3dctx = avhwctx->hwctx;

    p->vo_dev = d3dctx->device;
    ID3D11Device_AddRef(p->vo_dev);

    p->vo_formats = hwctx->supported_formats;

    HRESULT hr;

    hr = ID3D11Device_QueryInterface(p->vo_dev, &IID_ID3D11VideoDevice,
                                     (void **)&p->video_dev);
    if (FAILED(hr))
        goto fail;

    ID3D11Device_GetImmediateContext(p->vo_dev, &p->device_ctx);
    if (!p->device_ctx)
        goto fail;
    hr = ID3D11DeviceContext_QueryInterface(p->device_ctx, &IID_ID3D11VideoContext,
                                            (void **)&p->video_ctx);
    if (FAILED(hr))
        goto fail;

    p->pool = mp_image_pool_new(f);
    mp_image_pool_set_allocator(p->pool, alloc_pool, f);
    mp_image_pool_set_lru(p->pool);

    mp_refqueue_add_in_format(p->queue, IMGFMT_D3D11, 0);

    mp_refqueue_set_refs(p->queue, 0, 0);
    mp_refqueue_set_mode(p->queue,
        (p->opts->deint_enabled ? MP_MODE_DEINT : 0) |
        MP_MODE_OUTPUT_FIELDS |
        (p->opts->interlaced_only ? MP_MODE_INTERLACED_ONLY : 0));

    return f;

fail:
    talloc_free(f);
    return NULL;
}

#define OPT_BASE_STRUCT struct opts
static const m_option_t vf_opts_fields[] = {
    OPT_FLAG("deint", deint_enabled, 0),
    OPT_FLAG("interlaced-only", interlaced_only, 0),
    OPT_CHOICE("mode", mode, 0,
        ({"blend", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BLEND},
         {"bob", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_BOB},
         {"adaptive", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_ADAPTIVE},
         {"mocomp", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_DEINTERLACE_MOTION_COMPENSATION},
         {"ivctc", D3D11_VIDEO_PROCESSOR_PROCESSOR_CAPS_INVERSE_TELECINE},