输出格式转换
2024-12-20
7
0
输出格式转换使用函数render_convert_texture实现。
着色器
转换使用的着色器文件为format_conversion.effect,其支持以下几种基本类型:
- NV12
- I420
- I444
其对应的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;
}
最终输出。