obs格式输出
+ -

输出格式转换

2024-12-20 7 0

输出格式转换使用函数render_convert_texture实现。

着色器

转换使用的着色器文件为format_conversion.effect,其支持以下几种基本类型:

  • NV12
  • I420
  • I444

YUV

其对应的pass如下:

static inline void calc_gpu_conversion_sizes(const struct obs_video_info *ovi)
{
    struct obs_core_video *video = &obs->video;

    video->conversion_needed = false;
    video->conversion_techs[0] = NULL;
    video->conversion_techs[1] = NULL;
    video->conversion_techs[2] = NULL;
    video->conversion_width_i = 0.f;

    switch ((uint32_t)ovi->output_format) {
    case VIDEO_FORMAT_I420:
        video->conversion_needed = true;
        video->conversion_techs[0] = "Planar_Y";
        video->conversion_techs[1] = "Planar_U_Left";
        video->conversion_techs[2] = "Planar_V_Left";
        video->conversion_width_i = 1.f / (float)ovi->output_width;
        break;
    case VIDEO_FORMAT_NV12:
        video->conversion_needed = true;
        video->conversion_techs[0] = "NV12_Y";
        video->conversion_techs[1] = "NV12_UV";
        video->conversion_width_i = 1.f / (float)ovi->output_width;
        break;
    case VIDEO_FORMAT_I444:
        video->conversion_needed = true;
        video->conversion_techs[0] = "Planar_Y";
        video->conversion_techs[1] = "Planar_U";
        video->conversion_techs[2] = "Planar_V";
        break;
    }
}

输出纹理

输出纹理需要配哈渲染目标视图,其创建方法如下:

video->convert_textures[0] =gs_texture_create(ovi->output_width, ovi->output_height, GS_R8, 1, NULL, GS_RENDER_TARGET);

const struct video_output_info *info =video_output_get_info(video->video);
switch (info->format)
{
case VIDEO_FORMAT_I420:
    video->convert_textures[1] = gs_texture_create(ovi->output_width/2, ovi->output_height/2,GS_R8, 1, NULL, GS_RENDER_TARGET);
    video->convert_textures[2] = gs_texture_create(ovi->output_width/2, ovi->output_height/2,GS_R8, 1, NULL, GS_RENDER_TARGET);
    if (!video->convert_textures[2])
        return false;
    break;
case VIDEO_FORMAT_NV12:
    video->convert_textures[1] = gs_texture_create(ovi->output_width/2, ovi->output_height/2,GS_R8G8, 1, NULL, GS_RENDER_TARGET);
    break;
case VIDEO_FORMAT_I444:
    video->convert_textures[1] = gs_texture_create(ovi->output_width, ovi->output_height, GS_R8, 1,    NULL, GS_RENDER_TARGET);
    video->convert_textures[2] = gs_texture_create(ovi->output_width, ovi->output_height, GS_R8, 1,    NULL, GS_RENDER_TARGET);
    if (!video->convert_textures[2])
        return false;
    break;
default:
    break;
}

输出的数据为YUV,但是不能CPU读(无法MAP映射)。

数据格式转换(UNOR->UINT)

其创建的copy_surfaces也和convert_textures一致。

static bool obs_init_gpu_copy_surfaces(struct obs_video_info *ovi, size_t i)
{
    struct obs_core_video *video = &obs->video;

    video->copy_surfaces[i][0] = gs_stagesurface_create(ovi->output_width, ovi->output_height, GS_R8);
    if (!video->copy_surfaces[i][0])
        return false;

    const struct video_output_info *info =    video_output_get_info(video->video);
    switch (info->format)
    {
    case VIDEO_FORMAT_I420:
        video->copy_surfaces[i][1] = gs_stagesurface_create(ovi->output_width/2, ovi->output_height/2, GS_R8);
        if (!video->copy_surfaces[i][1])
            return false;

        video->copy_surfaces[i][2] = gs_stagesurface_create(ovi->output_width/2, ovi->output_height/2, GS_R8);
        if (!video->copy_surfaces[i][2])
            return false;
        break;
    case VIDEO_FORMAT_NV12:
        video->copy_surfaces[i][1] = gs_stagesurface_create(ovi->output_width/2, ovi->output_height/2, GS_R8G8);
        if (!video->copy_surfaces[i][1])
            return false;
        break;
    case VIDEO_FORMAT_I444:
        video->copy_surfaces[i][1] = gs_stagesurface_create(ovi->output_width, ovi->output_height, GS_R8);
        if (!video->copy_surfaces[i][1])
            return false;

        video->copy_surfaces[i][2] = gs_stagesurface_create(ovi->output_width, ovi->output_height, GS_R8);
        if (!video->copy_surfaces[i][2])
            return false;
        break;
    default:
        break;
    }

    return true;
}

只不过创建的纹理为:

gs_stage_surface::gs_stage_surface(gs_device_t *device, uint32_t width,
                   uint32_t height, gs_color_format colorFormat)
    : gs_obj(device, gs_type::gs_stage_surface),
      width(width),
      height(height),
      format(colorFormat),
      dxgiFormat(ConvertGSTextureFormatView(colorFormat))
{
    HRESULT hr;

    memset(&td, 0, sizeof(td));
    td.Width = width;
    td.Height = height;
    td.MipLevels = 1;
    td.ArraySize = 1;
    td.Format = dxgiFormat;
    td.SampleDesc.Count = 1;
    td.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    td.Usage = D3D11_USAGE_STAGING;

    hr = device->device->CreateTexture2D(&td, NULL, texture.Assign());
    if (FAILED(hr))
        throw HRError("Failed to create staging surface", hr);
}

使用copy_surfaces实现。实现代码是stage_output_texture函数。

        for (int i = 0; i < NUM_CHANNELS; i++)
        {
            gs_stagesurf_t *copy =    video->copy_surfaces[cur_texture][i];
            if (copy)
                gs_stage_texture(copy,video->convert_textures[i]);//dst src
        }

gs_stage_texturea其实是:

void device_stage_texture(gs_device_t *device, gs_stagesurf_t *dst, gs_texture_t *src)
{
        gs_texture_2d *src2d = static_cast<gs_texture_2d *>(src);
        device->CopyTex(dst->texture, 0, 0, src, 0, 0, 0, 0);
}

实现:

inline void gs_device::CopyTex(ID3D11Texture2D *dst, uint32_t dst_x,
                   uint32_t dst_y, gs_texture_t *src,
                   uint32_t src_x, uint32_t src_y, uint32_t src_w,
                   uint32_t src_h)
{
    gs_texture_2d *tex2d = static_cast<gs_texture_2d *>(src);
    if (dst_x == 0 && dst_y == 0 && 
        src_x == 0 && src_y == 0 &&
        src_w == 0 && src_h == 0) {
        context->CopyResource(dst, tex2d->texture);
    }
}

download_frame

static inline bool download_frame(struct obs_core_video *video,
                  int prev_texture, struct video_data *frame)
{
    if (!video->textures_copied[prev_texture])
        return false;

    for (int channel = 0; channel < NUM_CHANNELS; ++channel)
    {
        gs_stagesurf_t *surface = video->copy_surfaces[prev_texture][channel];
        if (surface)
        {
            if (!gs_stagesurface_map(surface, &frame->data[channel],&frame->linesize[channel]))
                return false;

            video->mapped_surfaces[channel] = surface;
        }
    }
    return true;
}

即对copy_surfaces的纹理进行MAP,复制数据:

bool gs_stagesurface_map(gs_stagesurf_t *stagesurf, uint8_t **data,
             uint32_t *linesize)
{
    D3D11_MAPPED_SUBRESOURCE map;
    if (FAILED(stagesurf->device->context->Map(stagesurf->texture, 0,
                           D3D11_MAP_READ, 0, &map)))
        return false;

    *data = (uint8_t *)map.pData;
    *linesize = map.RowPitch;
    return true;
}

最终输出。

0 篇笔记 写笔记

作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!