obs_source视频数据Frame生命周期
2025-05-22
9
0
视频数据来源主要来源于obs-ffmpeg
1.首先通过mp_media_thread线程中通过ffmpeg解析出视频数据到d->frame
AVFrame *f = m->v.frame
2.然后通过mp_media_next_video最终缓冲解出的视频数据
- mp_media_next_video
- m->v_cb = get_frame
- obs_source_output_video
- obs_source_output_video_internal
- obs_source_output_video
- m->v_cb = get_frame
obs_source_output_video_internal函数中首先通过cache_video缓存数据。这里的缓存其实是有一个内存管理的操作。
在obs_source中async_cache数组是开避的30多个循环队列,用于缓存长宽和格式一致的数据。所以新创建的视频数据最终调用copy_frame_data(new_frame, frame)将其复制到循环队列数组中,并返回其缓存数据的指到。
static void
obs_source_output_video_internal(obs_source_t *source,
const struct obs_source_frame *frame)
{
struct obs_source_frame *output = cache_video(source, frame);
da_push_back(source->async_frames, &output);
}
然后将该缓存队列中的frame即output加异步队列中.
obs_source_frame *
cache_video(struct obs_source *source, const struct obs_source_frame *frame)
{
//find unused list
for (size_t i = 0; i < source->async_cache.num; i++) {
struct async_frame *af = &source->async_cache.array[i];
if (!af->used) {
new_frame = af->frame;
new_frame->format = format;
af->used = true;
af->unused_count = 0;
break;
}
}
copy_frame_data(new_frame, frame);
}
copy_frame_data是一个深拷贝,其中获取的new_frame是一个循环队列,用于内存管理。
这样async_frames队列中第一个永远首先要判断是否需是一个未使用的(多次循环的flag效率大于对象在队列中的切换)。
这样就算mp_media_next_video结束.
if (m->has_video)
mp_media_next_video(m, false);
3.数据的获取
在obs_source中的obs_source_video_tick中调用
async_tick(source)
攻取当前最满足的时间帧
source->cur_async_frame = get_closest_frame(source, sys_time)
obs_source_frame就是获取【0】,并将其从队列中移除
static inline struct obs_source_frame *get_closest_frame(obs_source_t *source, uint64_t sys_time)
{
if (!source->async_frames.num)
return NULL;
//first frame start or get the closet time
if (!source->last_frame_ts || ready_async_frame(source, sys_time)) {
struct obs_source_frame *frame = source->async_frames.array[0];
da_erase(source->async_frames, 0);
if (!source->last_frame_ts)
source->last_frame_ts = frame->timestamp;
return frame;
}
return NULL;
}
async_tick中获取新的帧存于source->cur_async_frame之后,最后调用set_async_texture_size来创建3D11的texture
static void async_tick(obs_source_t *source)
{
source->cur_async_frame = get_closest_frame(source, sys_time);
if (source->cur_async_frame)
source->async_update_texture = set_async_texture_size(source, source->cur_async_frame);
}
另外,或以看到其remove_async_frame,这其实是在其渲染之后才回收至队列中的,其调用栈关系如下:
obs.dll!remove_async_frame(obs_source * source, obs_source_frame * frame) 行 3437 C
obs.dll!obs_source_release_frame(obs_source * source, obs_source_frame * frame) 行 3594
obs.dll!obs_source_update_async_video(obs_source * source) 行 2163 C
obs.dll!render_video(obs_source * source) 行 2295 C
obs.dll!obs_source_video_render(obs_source * source) 行 2330 C
obs.dll!render_item(obs_scene_item * item) 行 666 C
obs.dll!scene_video_render(void * data, gs_effect * effect) 行 763 C
obs.dll!obs_source_main_render(obs_source * source) 行 2254 C
obs.dll!render_video(obs_source * source) 行 2309 C
obs.dll!obs_source_video_render(obs_source * source) 行 2330 C
obs.dll!obs_view_render(obs_view * view) 行 146 C
obs.dll!render_main_texture(obs_core_video * video) 行 151 C
void remove_async_frame(obs_source_t *source, struct obs_source_frame *frame)
{
if (frame)
frame->prev_frame = false;
for (size_t i = 0; i < source->async_cache.num; i++) {
struct async_frame *f = &source->async_cache.array[i];
if (f->frame == frame) {
f->used = false;
break;
}
}
}