渲染transition或scene:不管是否需要transition,scene的渲染都是通过transition封装调用的。
transition相关的变量:
struct obs_source {uint64_t transition_start_time;uint64_t transition_duration;pthread_mutex_t transition_tex_mutex;gs_texrender_t *transition_texrender[2]; // 两个scene的画布pthread_mutex_t transition_mutex;obs_source_t *transition_sources[2]; // 两个scene对象bool transitioning_video; // video当前是否正在做transitionbool transitioning_audio;bool transition_source_active[2];uint32_t transition_alignment;uint32_t transition_actual_cx;uint32_t transition_actual_cy;uint32_t transition_cx;uint32_t transition_cy;uint32_t transition_fixed_duration;bool transition_use_fixed_duration;enum obs_transition_mode transition_mode;enum obs_transition_scale_type transition_scale_type;struct matrix4 transition_matrices[2];
其中transition_sources[0]始终存储了当前active的scene,当切换scene完成后,也会更新为新的scene。transition_sources[1]保存transition时需要切换的目标场景,当开始transition时会对其赋值。transition结束时更新[0]并且置空[1]的函数如下:
static void obs_transition_stop(obs_source_t *transition)
{obs_source_t *old_child = transition->transition_sources[0];// deactive之前的场景源if (old_child && transition->transition_source_active[0])obs_source_remove_active_child(transition, old_child);obs_source_release(old_child);transition->transition_source_active[0] = true;transition->transition_source_active[1] = false;transition->transition_sources[0] = transition->transition_sources[1];transition->transition_sources[1] = NULL;
}
transition插件的渲染函数中会调用obs_transition_video_render,并传递一个回调函数,比如:
void slide_video_render(void *data, gs_effect_t *effect)
{struct slide_info *slide = data;obs_transition_video_render(slide->source, slide_callback);
}
如果当前不需要做video transition(即transitioning_video==false)obs_transition_video_render中直接渲染当前激活的scene source:
gs_matrix_push();
gs_matrix_mul(&matrices[0]);
obs_source_video_render(state.s[0]); // render current scene source
gs_matrix_pop();
如果需要做video transition,则把transition_sources的两个scene source,分别画到画布数组transition_texrender中,然后调用transition插件传递过来的回调函数,由具体的transition插件完成两个画布中的纹理的合并:
if (state.transitioning_video && locked && callback){gs_texture_t *tex[2] = {NULL, NULL};for (size_t i = 0; i < 2; i++){if (state.s[i]){render_child(transition, state.s[i], i);tex[i] = get_texture(transition, i); // get transition_texrenderif (!tex[i])tex[i] = obs->video.transparent_texture;}else{tex[i] = obs->video.transparent_texture;}}uint32_t cx = get_cx(transition);uint32_t cy = get_cy(transition);if (cx && cy){gs_blend_state_push();gs_blend_function(GS_BLEND_ONE, GS_BLEND_INVSRCALPHA);callback(transition->context.data, tex[0], tex[1], t, cx, cy);gs_blend_state_pop();}}
1. 渲染没有filter的text source的调用堆栈(从transition source开始执行render函数):
2. 当input source有filter时,会在void render_video(obs_source_t *source)中先渲染filter:
// 如果当前Source有filter,则递归渲染filterSource,递归前后,inputsource的rendering_filter会被赋值为true和false
// 此处的source是input source
if (source->filters.num && !source->rendering_filter)obs_source_render_filters(source);
渲染filter的顺序时,先渲染索引为0的filter(即最后添加的filter,因为每次新添加的filter都是放到索引0的位置),然后会调用到filterSource的video_render函数,在该函数中会先调用obs_source_process_filter_begin,其中又会获取filter链路中的上一个source::filter_target , 会对filter_target调用obs_source_video_render(filter_target),在自己的画布上渲染上一个filter或source,获取的输出纹理作为当前filter的source texture。然后再调用obs_source_process_filter_begin,在主画布上使用当前filter的shader渲染获取的source texture.
添加filter时,设置filter_target的逻辑(可以把filter_target理解为sourceTexture, 即当前filter要处理的源纹理):
filter->filter_parent = source; // 关联的input source
if (source->filters.num > 0)
{// 设为上一个filterSourcefilter->filter_target = source->filters.array[0];
}
else
{filter->filter_target = source;
}
da_insert(source->filters, 0, &filter); // 新的filter放到0的位置
对一个source 先添加Color Key,再添加Scroll,其渲染的递归调用顺序如下:
filterPlugin的video_render函数,实现的伪代码通常是:
obs_source_process_filter_begin()// 设置shader参数
gs_effect_set_vec2(filter->param_add, &filter->offset);
gs_effect_set_vec2(filter->param_mul, &mul_val);
gs_effect_set_next_sampler(filter->param_image, filter->sampler);obs_source_process_filter_end(filter->filterSource, filter->effect, cx, cy);
obs_source_process_filter_begin代码备注
if (!filter->filter_texrender) // 创建一个纹理作为临时画布filter->filter_texrender = gs_texrender_create(format, GS_ZS_NONE);gs_blend_state_push(); // 存储之前的状态gs_blend_function(GS_BLEND_ONE, GS_BLEND_ZERO);// 将自己的纹理设为画布,保存之前的画布和深度模板if (gs_texrender_begin(filter->filter_texrender, cx, cy)) {bool custom_draw = (parent_flags & OBS_SOURCE_CUSTOM_DRAW) != 0;bool async = (parent_flags & OBS_SOURCE_ASYNC) != 0;struct vec4 clear_color;vec4_zero(&clear_color);gs_clear(GS_CLEAR_COLOR, &clear_color, 0.0f, 0);gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -100.0f, 100.0f);if (target == parent && !custom_draw && !async){obs_source_default_render(target);}else{
// 将filter链路的上一个source(可能是filterSource或inputSource)的输出纹理,
// 画到自己的临时画布上obs_source_video_render(target);}// 恢复之前的矩阵和画布gs_texrender_end(filter->filter_texrender); }gs_blend_state_pop(); // 恢复之前的状态return true;
obs_source_process_filter_end代码备注
void obs_source_process_filter_end(obs_source_t *filter, gs_effect_t *effect,uint32_t width, uint32_t height)
{obs_source_t *target, *parent;gs_texture_t *texture;uint32_t parent_flags;if (!obs_ptr_valid(filter, "obs_source_process_filter_end"))return;target = obs_filter_get_target(filter);parent = obs_filter_get_parent(filter);parent_flags = parent->info.output_flags;if (can_bypass(target, parent, parent_flags, filter->allow_direct)){render_filter_bypass(target, effect, "Draw");}else{// 获取自己临时画布的纹理(inputSource的纹理已经画到临时画布了)texture = gs_texrender_get_texture(filter->filter_texrender); if (texture){// 将临时画布的纹理,使用filterShader画到目标画布上render_filter_tex(texture, effect, width, height, "Draw");}}
}