obs_source_output_video
2024-07-03
30
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);
}
}