obs_source_frame
+ -

obs_source_output_video

2024-07-03 7 0

在OBS的test-random.c中。其使用线程函数video_thread中使用fill_texture循环生成随机视频图像,obs_source_frame,将使用函数obs_source_output_video将其存入队列中。

static inline void fill_texture(uint32_t *pixels)
{
    size_t x, y;

    for (y = 0; y < 20; y++) 
    {
        for (x = 0; x < 20; x++) 
        {
            uint32_t pixel = 0;
            pixel |= (rand() % 256);
            pixel |= (rand() % 256) << 8;
            pixel |= (rand() % 256) << 16;
            pixels[y * 20 + x] = pixel;
        }
    }
}

static void *video_thread(void *data)
{
    struct random_tex *rt = data;
    uint32_t pixels[20 * 20];
    uint64_t cur_time = os_gettime_ns();

    struct obs_source_frame frame =
    {
        .data = {[0] = (uint8_t *)pixels},
        .linesize = {[0] = 20 * 4},
        .width = 20,
        .height = 20,
        .format = VIDEO_FORMAT_BGRX,
    };

    while (os_event_try(rt->stop_signal) == EAGAIN)
    {
    //随机填充数据内容
        fill_texture(pixels);

        frame.timestamp = cur_time;
        obs_source_output_video(rt->source, &frame);

        os_sleepto_ns(cur_time += 250000000);
    }

    return NULL;
}

obs_source_output_video函数中会复制一个新的obs_source_frame结构体,然后根据数据格式是否为YUV而设置成员full_range。然后调用obs_source_output_video_internal函数。

void obs_source_output_video(obs_source_t *source,    const struct obs_source_frame *frame)
{
    //源正在销毁,就不要再进行了
    if (destroying(source))
    {
        return;
    }

    if (!frame)
    {
        obs_source_output_video_internal(source, NULL);
        return;
    }

    struct obs_source_frame new_frame = *frame;
    new_frame.full_range =    format_is_yuv(frame->format) ? new_frame.full_range : true;

    obs_source_output_video_internal(source, &new_frame);
}

full_range即RGB之类的格式。

obs_source_output_video_internal这个函数设计的比较特别。会使用空的结构frame指针触发该source清空所有缓存。

static void
obs_source_output_video_internal(obs_source_t *source,             const struct obs_source_frame *frame)
{
    if (!obs_source_valid(source, "obs_source_output_video"))
        return;

//使用空指针触发清空操作
    if (!frame) {
        pthread_mutex_lock(&source->async_mutex);
        source->async_active = false;
        source->last_frame_ts = 0;
        free_async_cache(source);
        pthread_mutex_unlock(&source->async_mutex);
        return;
    }

    struct obs_source_frame *output = cache_video(source, frame);

    /* ------------------------------------------- */
    pthread_mutex_lock(&source->async_mutex);
    if (output)
    {
        if (os_atomic_dec_long(&output->refs) == 0)
        {
            obs_source_frame_destroy(output);
            output = NULL;
        }
        else 
        {
            da_push_back(source->async_frames, &output);
            source->async_active = true;
        }
    }
    pthread_mutex_unlock(&source->async_mutex);
}

cache_video其实会malloc一个新的obs_source_frame,其变量为output,然后通过da_push_back加入队列中。

#define MAX_ASYNC_FRAMES 30
//if return value is not null then do (os_atomic_dec_long(&output->refs) == 0) && obs_source_frame_destroy(output)
static inline struct obs_source_frame *
cache_video(struct obs_source *source, const struct obs_source_frame *frame)
{
    struct obs_source_frame *new_frame = NULL;

    pthread_mutex_lock(&source->async_mutex);

//缓存在多,清空
    if (source->async_frames.num >= MAX_ASYNC_FRAMES) {
        free_async_cache(source);
        source->last_frame_ts = 0;
        pthread_mutex_unlock(&source->async_mutex);
        return NULL;
    }

//格式发生变化,比如类型,长宽等,清空以前所有的
    if (async_texture_changed(source, frame)) {
        free_async_cache(source);
        source->async_cache_width = frame->width;
        source->async_cache_height = frame->height;
    }

    const enum video_format format = frame->format;
    source->async_cache_format = format;
    source->async_cache_full_range = frame->full_range;
    source->async_cache_trc = frame->trc;


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


    clean_cache(source);

    if (!new_frame) 
    {
        struct async_frame new_af;

        new_frame = obs_source_frame_create(format, frame->width,frame->height);
        new_af.frame = new_frame;
        new_af.used = true;
        new_af.unused_count = 0;
        new_frame->refs = 1;

        da_push_back(source->async_cache, &new_af);
    }

    os_atomic_inc_long(&new_frame->refs);

    pthread_mutex_unlock(&source->async_mutex);

//数据复制
    copy_frame_data(new_frame, frame);

    return new_frame;
}

copy_frame_data数据这里就比较重要的了。因为格式是多种多样的,所以要支持所有格式:


static void copy_frame_data(struct obs_source_frame *dst,
                const struct obs_source_frame *src)
{
    dst->flip = src->flip;
    dst->flags = src->flags;
    dst->trc = src->trc;
    dst->full_range = src->full_range;
    dst->max_luminance = src->max_luminance;
    dst->timestamp = src->timestamp;
    memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
    if (!dst->full_range) {
        size_t const size = sizeof(float) * 3;
        memcpy(dst->color_range_min, src->color_range_min, size);
        memcpy(dst->color_range_max, src->color_range_max, size);
    }

    switch (src->format) {
    case VIDEO_FORMAT_I420:
    case VIDEO_FORMAT_I010: {
        const uint32_t height = dst->height;
        const uint32_t half_height = (height + 1) / 2;
        copy_frame_data_plane(dst, src, 0, height);
        copy_frame_data_plane(dst, src, 1, half_height);
        copy_frame_data_plane(dst, src, 2, half_height);
        break;
    }

    case VIDEO_FORMAT_NV12:
    case VIDEO_FORMAT_P010: {
        const uint32_t height = dst->height;
        const uint32_t half_height = (height + 1) / 2;
        copy_frame_data_plane(dst, src, 0, height);
        copy_frame_data_plane(dst, src, 1, half_height);
        break;
    }

    case VIDEO_FORMAT_I444:
    case VIDEO_FORMAT_I422:
    case VIDEO_FORMAT_I210:
    case VIDEO_FORMAT_I412:
        copy_frame_data_plane(dst, src, 0, dst->height);
        copy_frame_data_plane(dst, src, 1, dst->height);
        copy_frame_data_plane(dst, src, 2, dst->height);
        break;

    case VIDEO_FORMAT_YVYU:
    case VIDEO_FORMAT_YUY2:
    case VIDEO_FORMAT_UYVY:
    case VIDEO_FORMAT_NONE:
    case VIDEO_FORMAT_RGBA:
    case VIDEO_FORMAT_BGRA:
    case VIDEO_FORMAT_BGRX:
    case VIDEO_FORMAT_Y800:
    case VIDEO_FORMAT_BGR3:
    case VIDEO_FORMAT_AYUV:
    case VIDEO_FORMAT_V210:
    case VIDEO_FORMAT_R10L:
        copy_frame_data_plane(dst, src, 0, dst->height);
        break;

    case VIDEO_FORMAT_I40A: {
        const uint32_t height = dst->height;
        const uint32_t half_height = (height + 1) / 2;
        copy_frame_data_plane(dst, src, 0, height);
        copy_frame_data_plane(dst, src, 1, half_height);
        copy_frame_data_plane(dst, src, 2, half_height);
        copy_frame_data_plane(dst, src, 3, height);
        break;
    }

    case VIDEO_FORMAT_I42A:
    case VIDEO_FORMAT_YUVA:
    case VIDEO_FORMAT_YA2L:
        copy_frame_data_plane(dst, src, 0, dst->height);
        copy_frame_data_plane(dst, src, 1, dst->height);
        copy_frame_data_plane(dst, src, 2, dst->height);
        copy_frame_data_plane(dst, src, 3, dst->height);
        break;

    case VIDEO_FORMAT_P216:
    case VIDEO_FORMAT_P416:
        /* Unimplemented */
        break;
    }
}

关于数据的复制copy_frame_data_plane

static inline void copy_frame_data_line(struct obs_source_frame *dst,
                    const struct obs_source_frame *src,
                    uint32_t plane, uint32_t y)
{
    uint32_t pos_src = y * src->linesize[plane];//src起始位置
    uint32_t pos_dst = y * dst->linesize[plane];//dest起始位置
    uint32_t minbytes = dst->linesize[plane] < src->linesize[plane] //最小复制字节数
                 ? dst->linesize[plane]
                 : src->linesize[plane];

    memcpy(dst->data[plane] + pos_dst, src->data[plane] + pos_src, minbytes);
}

static inline void copy_frame_data_plane(struct obs_source_frame *dst,
                     const struct obs_source_frame *src,
                     uint32_t plane, uint32_t lines)
{
    //不等时,部分复制
    if (dst->linesize[plane] != src->linesize[plane])
    {
        for (uint32_t y = 0; y < lines; y++)
            copy_frame_data_line(dst, src, plane, y);
    } 
    else
    {
    //相等时直接整体复制
        memcpy(dst->data[plane], src->data[plane],(size_t)dst->linesize[plane] * (size_t)lines);
    }
}

0 篇笔记 写笔记

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

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

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