OBS源码分析之render_video处理流程

来源:互联网 发布:淘宝网开店卖什么好 编辑:程序博客网 时间:2024/06/05 19:15

最近在看OBS源码,发现其复杂度真不是一般的高,看到render_video时脑子混乱了,经过单步调试跟踪才终于明朗了,下面先贴上源码:

static inline voidrender_video(obs_source_t *source)

{

if(source->info.type != OBS_SOURCE_TYPE_FILTER &&

    (source->info.output_flags &OBS_SOURCE_VIDEO) == 0)

return;

 

if(source->info.type == OBS_SOURCE_TYPE_INPUT &&

    (source->info.output_flags &OBS_SOURCE_ASYNC) != 0 &&

    !source->rendering_filter) {

if(deinterlacing_enabled(source))

deinterlace_update_async_video(source);

obs_source_update_async_video(source);

}

 

if(!source->context.data || !source->enabled) {

if(source->filter_parent)

obs_source_skip_video_filter(source);

return;

}

 

if(source->filters.num && !source->rendering_filter)

obs_source_render_filters(source);

 

elseif (source->info.video_render)

obs_source_main_render(source);

 

elseif (source->filter_target)

obs_source_video_render(source->filter_target);

 

elseif (deinterlacing_enabled(source))

deinterlace_render(source);

 

else

obs_source_render_async_video(source);

}

 

如果source不是OBS_SOURCE_TYPE_FILTER且不是OBS_SOURCE_VIDEO直接退出;

如果source为OBS_SOURCE_TYPE_INPUT且是OBS_SOURCE_ASYNC,并且!source->rendering_filter(对source进行滤镜处理时会将此变量值true

if(deinterlacing_enabled(source))

deinterlace_update_async_video(source);

obs_source_update_async_video(source);

黄色部分先不管,看看

staticvoid obs_source_update_async_video(obs_source_t *source)

{

if(!source->async_rendered) {

structobs_source_frame *frame = obs_source_get_frame(source);

 

if(frame)

frame= filter_async_video(source, frame);

 

source->async_rendered= true;

if(frame) {

source->timing_adjust=

os_gettime_ns()- frame->timestamp;

source->timing_set= true;

 

if(set_async_texture_size(source, frame)) {

update_async_texture(source,frame,

source->async_texture,

source->async_texrender);

}

 

obs_source_release_frame(source,frame);

}

}

}

 

首先

if(!source->async_rendered) {

structobs_source_frame *frame = obs_source_get_frame(source);

source->async_rendered这个变量在obs_source_video_tick得到最近的一帧视频后值false,(obs_source_video_tick?姑且认为是采集视频源输出帧数据)表示没有进行异步渲染,因此这里if的意思就是对还没有进行异步渲染的帧进行render;

接下来source->async_rendered= true;与obs_source_video_tick对应;

下面做了些时间戳的处理,先不细说;继续往下遇到一个重点:

update_async_texture(source,frame,

source->async_texture,

source->async_texrender);

       这个函数进行了帧色彩空间转换,采用PBO技术将frame数据传到source->async_texture,然后渲染到文理source->async_texrender->target;一大堆图像渲染引擎的东东,可以先略过。

接着回到render_video如果这个source只是一个简单的source,(怎样算简单?直接走到最后一个else的就是简单的了这样的source只是一个INPUT,且没有任何过滤器,也没有自己的video_render方法)这样就直接到了

static inline voidobs_source_render_async_video(obs_source_t *source)

{

if(source->async_texture && source->async_active)

obs_source_draw_async_texture(source);

}

 

这个函数很简单就一个IF也是同步用的,

if(source->async_texture && source->async_active)

obs_source_draw_async_texture(source);

source->async_texture 上面已经提到,source->async_active在obs_source_output_video中进行赋值,如果视频源输出了新的帧会将此变量值true,也就是说视频源输出帧之后才进行obs_source_draw_async_texture;继续看这个函数的具体实现:

static voidobs_source_draw_async_texture(struct obs_source *source)

{

gs_effect_t    *effect        = gs_get_effect();

bool           yuv           =format_is_yuv(source->async_format);

bool           limited_range = yuv &&!source->async_full_range;

constchar     *type         = yuv ? "DrawMatrix" :"Draw";

bool           def_draw      = (!effect);

gs_technique_t*tech          = NULL;

 

if(def_draw) {

effect= obs_get_base_effect(OBS_EFFECT_DEFAULT);

tech= gs_effect_get_technique(effect, type);

gs_technique_begin(tech);

gs_technique_begin_pass(tech,0);

}

 

obs_source_draw_texture(source,effect,

yuv? source->async_color_matrix : NULL,

limited_range? source->async_color_range_min : NULL,

limited_range? source->async_color_range_max : NULL);

 

if(def_draw) {

gs_technique_end_pass(tech);

gs_technique_end(tech);

}

}

 

这个函数实现了将source->async_texrender->target作为文理,再次进行一遍渲染,当然两次渲染是用的shader不同,细心的人会发现这次渲染没有设置渲染到哪里,那最终输出到哪了?既然没有设置渲染到哪里,那就自然是渲染到之前设置的目标上了,最终目的就是我们的屏幕;

最简单的source就说完了,那就继续复杂点的:

拿带filler的来说吧,source是可以带filler的,而且还可以带多个,大体的处理流程是:

1、update_async_texture,将原始帧数据渲染到FBO,

2、然后走到下面这个分支:

if(source->filters.num && !source->rendering_filter)

obs_source_render_filters(source);

static inline voidobs_source_render_filters(obs_source_t *source)

{

source->rendering_filter= true;

obs_source_video_render(source->filters.array[0]);

source->rendering_filter= false;

}

voidobs_source_video_render(obs_source_t *source)

{

if(!obs_source_valid(source, "obs_source_video_render"))

return;

 

obs_source_addref(source);

render_video(source);

obs_source_release(source);

}

代码很简单,对第一个filler执行render_video,(又到了render_video)

继续往下走可能走到两个分支:

(1)else if (source->info.video_render)

obs_source_main_render(source);

 

(2)else if (source->filter_target)

obs_source_video_render(source->filter_target);

先说分支(1)吧,大家都知道filler是有次序的,在OBS中FILLER的执行时递归实现的画个图说一下吧:


FILLER1是source的第一个FILLER,obs_source_main_render,的输入是FILLER1,但是FILLER1要先执行FILLER2,FILLER2要先执行FILLER3,FILLER3对frame处理之后,返回到FILLER2;然后FILLER1;然后SOURCE,

每个FILLER的执行都是首先filter_texrender->target设置与FBO绑定,然后渲染其前面的FILLER,最后渲染自己。

再说分支(2),对于没有video_render方法的FILLER,直接渲染其前面的FILLER;

1 0
原创粉丝点击