Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/hw/d3d9/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 17 kB image not shown  

Quelle  dxa9.c   Sprache: C

 

/*****************************************************************************
 * dxa9.c : DXVA2 GPU surface conversion module for vlc
 *****************************************************************************
 * Copyright (C) 2015 VLC authors, VideoLAN and VideoLabs
 *
 * 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.
 *****************************************************************************/


/*****************************************************************************
 * Preamble
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <vlc_common.h>
#include <vlc_filter.h>
#include <vlc_picture.h>

#include "d3d9_filters.h"

#include "../../video_chroma/copy.h"

#include <windows.h>
#include <d3d9.h>
#include "../../video_chroma/d3d9_fmt.h"

typedef struct
{
    /* GPU to CPU */
    copy_cache_t      cache;

    /* CPU to GPU */
    filter_t          *filter;
    picture_t         *staging;
} filter_sys_t;

static bool GetLock(filter_t *p_filter, IDirect3DSurface9 *d3d,
                    D3DLOCKED_RECT *p_lock, D3DSURFACE_DESC *p_desc)
{
    if (unlikely(FAILED( IDirect3DSurface9_GetDesc(d3d, p_desc))))
        return false;

    /* */
    if (FAILED(IDirect3DSurface9_LockRect(d3d, p_lock, NULL, D3DLOCK_READONLY))) {
        msg_Err(p_filter, "Failed to lock surface");
        return false;
    }

    return true;
}

static void DXA9_YV12(filter_t *p_filter, picture_t *src, picture_t *dst)
{
    copy_cache_t *p_copy_cache = p_filter->p_sys;
    picture_sys_d3d9_t *p_sys = ActiveD3D9PictureSys(src);

    D3DSURFACE_DESC desc;
    D3DLOCKED_RECT lock;
    if (!GetLock(p_filter, p_sys->surface, &lock, &desc))
        return;

    if (desc.Format == MAKEFOURCC('Y','V','1','2') ||
        desc.Format == MAKEFOURCC('I','M','C','3')) {

        if (dst->format.i_chroma == VLC_CODEC_I420)
            picture_SwapUV( dst );

        bool imc3 = desc.Format == MAKEFOURCC('I','M','C','3');
        size_t chroma_pitch = imc3 ? lock.Pitch : (lock.Pitch / 2);

        const size_t pitch[3] = {
            lock.Pitch,
            chroma_pitch,
            chroma_pitch,
        };

        const uint8_t *plane[3] = {
            (uint8_t*)lock.pBits,
            (uint8_t*)lock.pBits + pitch[0] * desc.Height,
            (uint8_t*)lock.pBits + pitch[0] * desc.Height
                                 + pitch[1] * desc.Height / 2,
        };

        if (imc3) {
            const uint8_t *V = plane[1];
            plane[1] = plane[2];
            plane[2] = V;
        }
        Copy420_P_to_P(dst, plane, pitch, src->format.i_height, p_copy_cache);

        if (dst->format.i_chroma == VLC_CODEC_I420)
            picture_SwapUV( dst );
    } else if (desc.Format == MAKEFOURCC('N','V','1','2')
            || desc.Format == MAKEFOURCC('P','0','1','0')) {
        const uint8_t *plane[2] = {
            lock.pBits,
            (uint8_t*)lock.pBits + lock.Pitch * desc.Height
        };
        const size_t  pitch[2] = {
            lock.Pitch,
            lock.Pitch,
        };
        if (desc.Format == MAKEFOURCC('N','V','1','2'))
            Copy420_SP_to_P(dst, plane, pitch,
                            __MIN(desc.Height, src->format.i_y_offset + src->format.i_visible_height),
                            p_copy_cache);
        else
            Copy420_16_SP_to_P(dst, plane, pitch,
                              __MIN(desc.Height, src->format.i_y_offset + src->format.i_visible_height),
                              6, p_copy_cache);

        if (dst->format.i_chroma == VLC_CODEC_YV12)
            picture_SwapUV(dst);
    } else {
        msg_Err(p_filter, "Unsupported DXA9 conversion from 0x%08X to YV12", desc.Format);
    }

    /* */
    IDirect3DSurface9_UnlockRect(p_sys->surface);
}

static void DXA9_NV12(filter_t *p_filter, picture_t *src, picture_t *dst)
{
    copy_cache_t *p_copy_cache = p_filter->p_sys;
    picture_sys_d3d9_t *p_sys = ActiveD3D9PictureSys(src);

    D3DSURFACE_DESC desc;
    D3DLOCKED_RECT lock;
    if (!GetLock(p_filter, p_sys->surface, &lock, &desc))
        return;

    if (desc.Format == MAKEFOURCC('N','V','1','2')
     || desc.Format == MAKEFOURCC('P','0','1','0')) {
        const uint8_t *plane[2] = {
            lock.pBits,
            (uint8_t*)lock.pBits + lock.Pitch * desc.Height
        };
        size_t  pitch[2] = {
            lock.Pitch,
            lock.Pitch,
        };
        Copy420_SP_to_SP(dst, plane, pitch,
                         __MIN(desc.Height, src->format.i_y_offset + src->format.i_visible_height),
                         p_copy_cache);
    } else {
        msg_Err(p_filter, "Unsupported DXA9 conversion from 0x%08X to NV12", desc.Format);
    }

    /* */
    IDirect3DSurface9_UnlockRect(p_sys->surface);
}

static void DeleteFilter( filter_t * p_filter )
{
    vlc_filter_UnloadModule( p_filter );

    es_format_Clean( &p_filter->fmt_in );
    es_format_Clean( &p_filter->fmt_out );

    vlc_object_delete(p_filter);
}

static picture_t *NewBuffer(filter_t *p_filter)
{
    filter_t *p_parent = p_filter->owner.sys;
    filter_sys_t *p_sys = p_parent->p_sys;
    return p_sys->staging;
}

static vlc_decoder_device * HoldD3D9DecoderDevice(vlc_object_t *o, void *sys)
{
    VLC_UNUSED(o);
    filter_t *p_this = sys;
    return filter_HoldDecoderDevice(p_this);
}

static filter_t *CreateFilter( filter_t *p_this, const es_format_t *p_fmt_in,
                               vlc_fourcc_t dst_chroma )
{
    filter_t *p_filter;

    p_filter = vlc_object_create( p_this, sizeof(filter_t) );
    if (unlikely(p_filter == NULL))
        return NULL;

    static const struct filter_video_callbacks cbs = { NewBuffer, HoldD3D9DecoderDevice };
    p_filter->b_allow_fmt_out_change = false;
    p_filter->owner.video = &cbs;
    p_filter->owner.sys = p_this;

    es_format_InitFromVideo( &p_filter->fmt_in,  &p_fmt_in->video );
    es_format_InitFromVideo( &p_filter->fmt_out, &p_fmt_in->video );
    p_filter->fmt_out.i_codec = p_filter->fmt_out.video.i_chroma = dst_chroma;
    p_filter->p_module = vlc_filter_LoadModule (p_filter, "video converter", NULL, false );

    if( !p_filter->p_module )
    {
        msg_Dbg( p_filter, "no video converter found" );
        DeleteFilter( p_filter );
        return NULL;
    }
    assert( p_filter->ops != NULL );

    return p_filter;
}

static void YV12_D3D9(filter_t *p_filter, picture_t *src, picture_t *dst)
{
    filter_sys_t *sys = p_filter->p_sys;
    picture_sys_d3d9_t *p_dst_sys = ActiveD3D9PictureSys(dst);
    if (sys->filter == NULL)
    {
        D3DLOCKED_RECT d3drect;
        HRESULT hr = IDirect3DSurface9_LockRect(p_dst_sys->surface, &d3drect, NULL, 0);
        if (FAILED(hr))
            return;

        picture_context_t *dst_pic_ctx = dst->context;
        dst->context = NULL; // some CPU filters won't like the mix of CPU/GPU

        picture_UpdatePlanes(dst, d3drect.pBits, d3drect.Pitch);

        /* The dx/d3d buffer is always allocated as YV12 */
        if (dst->format.i_chroma == VLC_CODEC_I420)
            picture_SwapUV( dst );

        picture_CopyPixels(dst, src);

        dst->context = dst_pic_ctx;

        IDirect3DSurface9_UnlockRect(p_dst_sys->surface);
    }
    else
    {
        picture_sys_d3d9_t *p_staging_sys = ActiveD3D9PictureSys(sys->staging);

        D3DLOCKED_RECT d3drect;
        HRESULT hr = IDirect3DSurface9_LockRect(p_staging_sys->surface, &d3drect, NULL, 0);
        if (FAILED(hr))
            return;

        picture_UpdatePlanes(sys->staging, d3drect.pBits, d3drect.Pitch);

        /* The dx/d3d buffer is always allocated as YV12 */
        if (sys->staging->format.i_chroma == VLC_CODEC_I420)
            picture_SwapUV( sys->staging );

        picture_context_t *staging_pic_ctx = sys->staging->context;
        sys->staging->context = NULL; // some CPU filters won't like the mix of CPU/GPU

        picture_Hold( src );

        sys->filter->ops->filter_video(sys->filter, src);

        sys->staging->context = staging_pic_ctx;

        IDirect3DSurface9_UnlockRect(p_staging_sys->surface);

        d3d9_decoder_device_t *d3d9_decoder = GetD3D9OpaqueContext(p_filter->vctx_out);

        RECT visibleSource = {
            .right = dst->format.i_width, .bottom = dst->format.i_height,
        };
        IDirect3DDevice9_StretchRect( d3d9_decoder->d3ddev.dev,
                                    p_staging_sys->surface, &visibleSource,
                                    p_dst_sys->surface, &visibleSource,
                                    D3DTEXF_NONE );
    }
    // stop pretending this is a CPU picture
    dst->format.i_chroma = p_filter->fmt_out.video.i_chroma;
    dst->i_planes = 0;
}

static picture_t *AllocateCPUtoGPUTexture(filter_t *p_filter)
{
    IDirect3DSurface9 *texture = NULL;
    video_format_t fmt_staging;
    d3d9_decoder_device_t *devsys = GetD3D9OpaqueContext(p_filter->vctx_out);

    static const D3DFORMAT outputFormats8[] = {
        MAKEFOURCC('I','4','2','0'),
        MAKEFOURCC('Y','V','1','2'),
        MAKEFOURCC('N','V','1','2'),
        D3DFMT_UNKNOWN
    };
    static const D3DFORMAT outputFormats10[] = {
        MAKEFOURCC('P','0','1','0'),
        MAKEFOURCC('I','4','2','0'),
        MAKEFOURCC('Y','V','1','2'),
        MAKEFOURCC('N','V','1','2'),
        D3DFMT_UNKNOWN
    };

    D3DFORMAT format = D3DFMT_UNKNOWN;
    const D3DFORMAT *list;
    switch( p_filter->fmt_in.video.i_chroma ) {
    case VLC_CODEC_I420:
    case VLC_CODEC_YV12:
        list = outputFormats8;
        break;
    case VLC_CODEC_I420_10L:
    case VLC_CODEC_P010:
        list = outputFormats10;
        break;
    default:
        vlc_assert_unreachable();
    }
    while (*list != D3DFMT_UNKNOWN)
    {
        HRESULT hr = IDirect3DDevice9_CreateOffscreenPlainSurface(devsys->d3ddev.dev,
                                                          p_filter->fmt_out.video.i_width,
                                                          p_filter->fmt_out.video.i_height,
                                                          *list,
                                                          D3DPOOL_DEFAULT,
                                                          &texture,
                                                          NULL);
        if (SUCCEEDED(hr)) {
            format = *list;
            msg_Dbg(p_filter, "using pixel format %4.4s", (char*)&format);
            break;
        }
        list++;
    }
    if (format == D3DFMT_UNKNOWN)
    {
        msg_Err(p_filter, "Failed to find a usable pixel format");
        goto done;
    }

    struct d3d9_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
    if (unlikely(pic_ctx == NULL))
        goto done;

    pic_ctx->s = (picture_context_t) {
        d3d9_pic_context_destroy, d3d9_pic_context_copy,
        vlc_video_context_Hold(p_filter->vctx_out),
    };

    video_format_Copy(&fmt_staging, &p_filter->fmt_out.video);
    fmt_staging.i_chroma = format;

    picture_resource_t dummy_res = { .p_sys = NULL };
    picture_t *p_dst = picture_NewFromResource(&fmt_staging, &dummy_res);
    if (p_dst == NULL) {
        msg_Err(p_filter, "Failed to map create the temporary picture.");
        goto done;
    }
    pic_ctx->picsys.surface = texture;
    p_dst->context = &pic_ctx->s;
    return p_dst;

done:
    return NULL;
}

static void D3D9CloseConverter( filter_t *p_filter )
{
    filter_sys_t *p_sys = p_filter->p_sys;
    CopyCleanCache( &p_sys->cache );
    free( p_sys );
    p_filter->p_sys = NULL;
}

VIDEO_FILTER_WRAPPER_CLOSE(DXA9_YV12, D3D9CloseConverter)
VIDEO_FILTER_WRAPPER_CLOSE(DXA9_NV12, D3D9CloseConverter)

static picture_t *YV12_D3D9_Filter( filter_t *p_filter, picture_t *p_pic )
{
    picture_t *p_outpic = AllocateCPUtoGPUTexture( p_filter );
    if( p_outpic )
    {
        YV12_D3D9( p_filter, p_pic, p_outpic );
        picture_CopyProperties( p_outpic, p_pic );
    }
    picture_Release( p_pic );
    return p_outpic;
}

int D3D9OpenConverter( filter_t *p_filter )
{
    if ( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE &&
         p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
        return VLC_EGENERIC;
    if ( GetD3D9ContextPrivate(p_filter->vctx_in) == NULL )
        return VLC_EGENERIC;

    if ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height
         || p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width )
        return VLC_EGENERIC;

    uint8_t pixel_bytes = 1;
    switch( p_filter->fmt_out.video.i_chroma ) {
    case VLC_CODEC_I420:
    case VLC_CODEC_YV12:
        if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE )
            return VLC_EGENERIC;
        p_filter->ops = &DXA9_YV12_ops;
        break;
    case VLC_CODEC_I420_10L:
        if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
            return VLC_EGENERIC;
        p_filter->ops = &DXA9_YV12_ops;
        pixel_bytes = 2;
        break;
    case VLC_CODEC_NV12:
        if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE )
            return VLC_EGENERIC;
        p_filter->ops = &DXA9_NV12_ops;
        break;
    case VLC_CODEC_P010:
        if( p_filter->fmt_in.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
            return VLC_EGENERIC;
        p_filter->ops = &DXA9_NV12_ops;
        pixel_bytes = 2;
        break;
    default:
        return VLC_EGENERIC;
    }

    filter_sys_t *p_sys = calloc(1, sizeof(filter_sys_t));
    if (!p_sys)
         return VLC_ENOMEM;

    if (CopyInitCache(&p_sys->cache, p_filter->fmt_in.video.i_width * pixel_bytes))
    {
        free(p_sys);
        return VLC_ENOMEM;
    }

    p_filter->p_sys = p_sys;
    return VLC_SUCCESS;
}

static void D3D9CloseCPUConverter( filter_t *p_filter )
{
    filter_sys_t *p_sys = p_filter->p_sys;
    if (p_sys->filter != NULL)
        DeleteFilter(p_sys->filter);
    if (p_sys->staging)
        picture_Release(p_sys->staging);
    vlc_video_context_Release(p_filter->vctx_out);
    free( p_sys );
    p_filter->p_sys = NULL;
}

static const struct vlc_filter_operations YV12_D3D9_ops = {
    .filter_video = YV12_D3D9_Filter, .close = D3D9CloseCPUConverter,
};

int D3D9OpenCPUConverter( filter_t *p_filter )
{
    int err = VLC_EGENERIC;
    picture_t *p_dst = NULL;

    if ( p_filter->fmt_out.video.i_chroma != VLC_CODEC_D3D9_OPAQUE
      && p_filter->fmt_out.video.i_chroma != VLC_CODEC_D3D9_OPAQUE_10B )
        return VLC_EGENERIC;

    if ( p_filter->fmt_in.video.i_height != p_filter->fmt_out.video.i_height
         || p_filter->fmt_in.video.i_width != p_filter->fmt_out.video.i_width )
        return VLC_EGENERIC;

    switch( p_filter->fmt_in.video.i_chroma ) {
    case VLC_CODEC_I420:
    case VLC_CODEC_YV12:
    case VLC_CODEC_I420_10L:
    case VLC_CODEC_P010:
        p_filter->ops = &YV12_D3D9_ops;
        break;
    default:
        return VLC_EGENERIC;
    }

    vlc_decoder_device *dec_device = filter_HoldDecoderDeviceType( p_filter, VLC_DECODER_DEVICE_DXVA2 );
    if (dec_device == NULL)
    {
        msg_Err(p_filter, "Missing decoder device");
        return VLC_EGENERIC;
    }
    d3d9_decoder_device_t *devsys = GetD3D9OpaqueDevice(dec_device);
    if (devsys == NULL)
    {
        msg_Err(p_filter, "Incompatible decoder device %d", dec_device->type);
        vlc_decoder_device_Release(dec_device);
        return VLC_EGENERIC;
    }

    filter_sys_t *p_sys = calloc(1, sizeof(filter_sys_t));
    if (!p_sys)
    {
        vlc_decoder_device_Release(dec_device);
        return VLC_ENOMEM;
    }

    p_filter->vctx_out = vlc_video_context_Create( dec_device, VLC_VIDEO_CONTEXT_DXVA2, sizeof(d3d9_video_context_t),
                                                   &d3d9_vctx_ops );
    vlc_decoder_device_Release(dec_device);

    if ( p_filter->vctx_out == NULL )
    {
        msg_Err(p_filter, "Failed to create the video context");
        goto done;
    }

    p_dst = AllocateCPUtoGPUTexture(p_filter);
    if (p_dst == NULL)
        goto done;
    d3d9_video_context_t *vctx_sys = GetD3D9ContextPrivate( p_filter->vctx_out );
    vctx_sys->format = p_dst->format.i_chroma;

    if ( !video_format_IsSameChroma( &p_filter->fmt_in.video, &p_dst->format ) )
    {
        p_sys->filter = CreateFilter(p_filter, &p_filter->fmt_in, p_dst->format.i_chroma);
        if (!p_sys->filter)
            goto done;
        p_sys->staging = p_dst;
    }
    else
    {
        picture_Release(p_dst);
    }

    p_filter->p_sys = p_sys;
    err = VLC_SUCCESS;

done:
    if (err != VLC_SUCCESS)
    {
        vlc_video_context_Release(p_filter->vctx_out);
        free(p_sys);
    }
    return err;
}

Messung
CHZG
839610093

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.