OBS源
+ -

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_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;
        }
    }
}

0 篇笔记 写笔记

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

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

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