obs video-io.c

video_frame_init 讲解

/* messy code alarm
video_frame_init 函数用于初始化视频帧。它接受一个指向 struct video_frame 结构体的指针 frame,
视频格式 format,以及宽度 width 和高度 height。该函数根据视频格式的不同,计算出每个视频帧的大小,
并在堆上为帧数据分配内存空间。然后,根据视频格式的不同,设置帧数据的偏移量和行大小,并将这些信息填充到 frame 结构体中。
*/void video_frame_init(struct video_frame *frame, enum video_format format,uint32_t width, uint32_t height)
{size_t size;size_t offsets[MAX_AV_PLANES];int alignment = base_get_alignment();if (!frame)return;memset(frame, 0, sizeof(struct video_frame));memset(offsets, 0, sizeof(offsets));switch (format) {case VIDEO_FORMAT_NONE:return;case VIDEO_FORMAT_I420: {/* 计算 Y 分量的大小,基于帧的宽度和高度 */size = width * height;/* 根据指定的对齐方式对大小进行对齐 */ALIGN_SIZE(size, alignment);/* 设置 Y 分量的偏移量 */offsets[0] = size;/* 计算 U 和 V 分量的宽度和高度 */const uint32_t half_width = (width + 1) / 2;const uint32_t half_height = (height + 1) / 2;/* 计算 U 和 V 分量的区域大小 */const uint32_t quarter_area = half_width * half_height;/* 将 U 分量的大小添加到总大小中 */size += quarter_area;/* 根据指定的对齐方式对大小进行对齐 */ALIGN_SIZE(size, alignment);/* 设置 U 分量的偏移量 */offsets[1] = size;/* 将 V 分量的大小添加到总大小中 */size += quarter_area;/* 根据指定的对齐方式对大小进行对齐 */ALIGN_SIZE(size, alignment);/* 为 YUV 数据分配内存 */frame->data[0] = bmalloc(size);/* 设置 U 和 V 分量的指针 */frame->data[1] = (uint8_t *)frame->data[0] + offsets[0];frame->data[2] = (uint8_t *)frame->data[0] + offsets[1];/* 设置 YUV 数据的行大小 */frame->linesize[0] = width;frame->linesize[1] = half_width;frame->linesize[2] = half_width;break;}}

init_cache

/*
这段代码是用于初始化视频输出缓存的函数。函数名为 init_cache,它接受一个指向 struct video_output 结构体的指针作为参数。

函数首先检查 video->info.cache_size 是否大于最大缓存大小 MAX_CACHE_SIZE,如果是,则将 video->info.cache_size 设置为
MAX_CACHE_SIZE。接下来,通过一个 for 循环,对 video->info.cache_size 次迭代,分别初始化缓存中的每个视频帧。在每次循环中,
函数会创建一个 video_frame 结构体指针 frame,该指针指向 video->cache[i],并使用 video_frame_init 函数初始化该帧。
初始化时,会将视频格式、宽度和高度信息填入帧中。最后,函数将 video->available_frames 设置为 video->info.cache_size,
表示缓存中有多少帧可用。
*/

static inline void init_cache(struct video_output *video)
{if (video->info.cache_size > MAX_CACHE_SIZE)video->info.cache_size = MAX_CACHE_SIZE;for (size_t i = 0; i < video->info.cache_size; i++) {struct video_frame *frame;frame = (struct video_frame *)&video->cache[i];video_frame_init(frame, video->info.format, video->info.width,video->info.height);}video->available_frames = video->info.cache_size;
}

video_output_open

/*
这段代码是一个视频输出组件的初始化函数 video_output_open。它接受一个指向 video_t 指针的指针(用于返回视频输出对象的地址)和一个 video_output_info 结构体指针作为参数。

首先,函数会检查传入的 info 参数是否有效,如果无效则返回 VIDEO_OUTPUT_INVALIDPARAM,表示初始化失败。

接着,函数会动态分配内存以创建一个 video_output 结构体对象,并将 info 中的信息复制到这个对象中。如果内存分配失败,则函数会返回 VIDEO_OUTPUT_FAIL。

然后,函数会根据 info 中的帧率信息计算每一帧的时间间隔,并将结果存储在 frame_time 中。

接下来,函数会初始化两个递归互斥锁 data_mutex 和 input_mutex 以及一个信号量 update_semaphore。如果初始化失败,则函数会释放之前分配的资源,并返回 VIDEO_OUTPUT_FAIL。

紧接着,函数会创建一个线程 thread,并将其入口函数设置为 video_thread。如果线程创建失败,则函数会释放之前分配的资源,并返回 VIDEO_OUTPUT_FAIL。

最后,函数会调用 init_cache 函数对 out 进行初始化,并将 out 赋值给 *video,以便将视频输出对象的地址返回给调用者。最后,函数返回 VIDEO_OUTPUT_SUCCESS 表示初始化成功。

总的来说,video_output_open 函数的作用是根据传入的 video_output_info 初始化一个视频输出对象,并将其地址存储在 *video 中,同时返回初始化的结果。

*/

int video_output_open(video_t **video, struct video_output_info *info)
{struct video_output *out;if (!valid_video_params(info))return VIDEO_OUTPUT_INVALIDPARAM;out = bzalloc(sizeof(struct video_output));if (!out)goto fail0;memcpy(&out->info, info, sizeof(struct video_output_info));out->frame_time =util_mul_div64(1000000000ULL, info->fps_den, info->fps_num);if (pthread_mutex_init_recursive(&out->data_mutex) != 0)goto fail0;if (pthread_mutex_init_recursive(&out->input_mutex) != 0)goto fail1;if (os_sem_init(&out->update_semaphore, 0) != 0)goto fail2;if (pthread_create(&out->thread, NULL, video_thread, out) != 0)goto fail3;init_cache(out);*video = out;return VIDEO_OUTPUT_SUCCESS;fail3:os_sem_destroy(out->update_semaphore);
fail2:pthread_mutex_destroy(&out->input_mutex);
fail1:pthread_mutex_destroy(&out->data_mutex);
fail0:bfree(out);return VIDEO_OUTPUT_FAIL;
}

video_thread

/** 视频线程函数,用于处理视频输出** param: 指向视频输出结构体的指针*/
static void *video_thread(void *param)
{/* 将参数转换为视频输出结构体 */struct video_output *video = param;/* 设置线程名称为 "video-io: video thread" */os_set_thread_name("video-io: video thread");/* 获取视频线程的名称 */const char *video_thread_name =profile_store_name(obs_get_profiler_name_store(),"video_thread(%s)", video->info.name);/* 在视频信号量上等待 */while (os_sem_wait(video->update_semaphore) == 0) {/* 如果视频已停止,则退出循环 */if (video->stop)break;/* 启动性能分析 */profile_start(video_thread_name);/* 在当前帧输出之前不断增加总帧数,直到当前帧输出成功 */while (!video->stop && !video_output_cur_frame(video)) {os_atomic_inc_long(&video->total_frames);}/* 当前帧输出成功后增加总帧数 */os_atomic_inc_long(&video->total_frames);/* 结束性能分析 */profile_end(video_thread_name);/* 重新启用线程性能分析 */profile_reenable_thread();}return NULL;
}

video_output_cur_frame

// 动态数组结构体,用于存储动态数组的信息
struct darray {void *array;    // 指向数组数据的指针size_t num;     // 数组中当前元素的数量size_t capacity; // 数组当前的容量
};/** 用于存储有关缓存视频帧的信息的结构。*/
struct cached_frame_info {struct video_data frame; // 视频帧数据int skipped;             // 被跳过的帧数int count;               // 总帧数计数
};/** 视频输入结构体,用于描述视频输入设备的信息。*/
struct video_input {struct video_scale_info conversion;  // 视频转换信息video_scaler_t *scaler;              // 视频缩放器struct video_frame frame[MAX_CONVERT_BUFFERS]; // 视频帧缓冲区int cur_frame;                        // 当前帧索引// 允许以主合成 FPS 的分数输出,例如,60 FPS 的 frame_rate_divisor = 1 变为 30 FPS//// 使用单独的计数器而不是使用余数计算,// 以便允许同时启动的“inputs”在相同的帧上启动,// 而使用余数计算则会使帧对齐取决于编码器启动时的总帧数uint32_t frame_rate_divisor;          // 帧率除数uint32_t frame_rate_divisor_counter;  // 帧率除数计数器void (*callback)(void *param, struct video_data *frame); // 回调函数指针void *param;                           // 参数
};/** 检查当前视频输出是否包含完* 整的帧并处理当前帧** video: 视频输出结构体指针** 返回值:如果当前帧完整则返回true,否则返回false*/
static inline bool video_output_cur_frame(struct video_output *video)
{struct cached_frame_info *frame_info;bool complete;bool skipped;/* 获取视频数据前先锁定数据互斥锁 */pthread_mutex_lock(&video->data_mutex);/* 获取当前视频输出中第一个添加的帧信息 */frame_info = &video->cache[video->first_added];/* 解锁数据互斥锁 */pthread_mutex_unlock(&video->data_mutex);/* 锁定输入互斥锁以处理视频输入 */pthread_mutex_lock(&video->input_mutex);/* 遍历视频输入并处理帧数据 */for (size_t i = 0; i < video->inputs.num; i++) {struct video_input *input = video->inputs.array + i;struct video_data frame = frame_info->frame;// 使用显式计数器而不是求余来允许在相同时间启动的多个编码器// 在同一帧上启动uint32_t skip = input->frame_rate_divisor_counter++;if (input->frame_rate_divisor_counter == input->frame_rate_divisor)input->frame_rate_divisor_counter = 0;if (skip)continue;if (scale_video_output(input, &frame))input->callback(input->param, &frame);}/* 解锁输入互斥锁 */pthread_mutex_unlock(&video->input_mutex);/* 处理当前帧信息 */pthread_mutex_lock(&video->data_mutex);/* 更新当前帧的时间戳并检查当前帧是否已经完整 */frame_info->frame.timestamp += video->frame_time;complete = --frame_info->count == 0;skipped = frame_info->skipped > 0;/* 如果当前帧完整则更新视频输出的相关信息 */if (complete) {if (++video->first_added == video->info.cache_size)video->first_added = 0;if (++video->available_frames == video->info.cache_size)video->last_added = video->first_added;}/* 如果当前帧被跳过,则更新相关信息并增加跳过帧的计数 */else if (skipped) {--frame_info->skipped;os_atomic_inc_long(&video->skipped_frames);}/* 解锁数据互斥锁 */pthread_mutex_unlock(&video->data_mutex);/* 返回当前帧是否完整的标志 */return complete;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/570284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何解决SVN 清理失败

解决方法&#xff1a; 下载 sqlite3.exe 在你的清理失败的路径下查看.svn目录下是否存在一个wc.db文件&#xff0c;把解压好的sqlite3.exe 放在wc.db文件的同一路径下 注意&#xff1a;主要是用sqlite3.exe清理掉wc.db中的相关信息。 通过cmd命令行进入你清理失败的路径&am…

10-Linux与windows文件互传-pscp坑---- 'pscp' 不是内部或外部命令,也不是可运行的程序或批处理文件...

1.下载pscp工具http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html2.拷贝到C:\Windows\System32 如果考到其他文件夹&#xff0c;运行提示 pscp 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件。 那么考到这个文件下吧&#xff01;&#xff0…

MongoDB最简单的入门教程之三 使用Java代码往MongoDB里插入数据

前两篇教程我们介绍了如何搭建MongoDB的本地环境&#xff1a; MongoDB最简单的入门教程之一 环境搭建 以及如何用nodejs读取MongoDB里的记录&#xff1a; MongoDB最简单的入门教程之二 使用nodejs访问MongoDB 这篇教程我们会介绍如何使用Java代码来连接MongoDB。 如果您是基于M…

oracle创建用户及授权等相关操作

1、创建用户 create user test identified by test; 这样就创建了一个用户名密码都为test的用户 但这个时候test还是不能登陆成功的&#xff0c;我们需要赋予相应的权限 2、赋予create session 的权限 grant create session to test; 这样test用户就能成功登录进去赋予用户创…

Warning: Received `false` for a non-boolean attribute `xxx`.

React对boolean类型的attribute的识别方式问题&#xff0c;可以采用以下方法解决&#xff1a; xxx{value ? 1 : 0} 改成数字的写法&#xff0c;不用布尔值。 具体可以参考&#xff1a;https://github.com/styled-components/styled-components/issues/1198 转载于:https://www…

row_number() over(partition by order by )使用说明书

作用&#xff1a;数据库去除重复记录&#xff0c;进行排序。 语法&#xff1a;ROW_NUMBER() OVER (PARTITION BY COL1 ORDER BY COL2) 功能&#xff1a;表示根据COL1分组&#xff0c;在分组内部根据 COL2排序&#xff0c;而这个值就表示每组内部排序后的顺序编号&#xff08;组…

C点滴成海------Dev C++怎么修改成简体中文

第一步&#xff1a;选择菜单中的Tools 第二步&#xff1a;选择Tools中的“Envirnoment Options”&#xff0c;即第二个选项 第三步&#xff1a;选择中文并保存 将"1"的语言改成中文就行了 转载于:https://www.cnblogs.com/hahayixiao/p/9824080.html

PLSQL快捷键使用,让你高速开发

不需要选中整行&#xff0c;执行光标所在行&#xff0c;按F8就可直接执行&#xff1b; 工具—>首选项—>SQL窗口—>自动选择&#xff0c;勾选自动选择即可。 这样光标放在此行&#xff0c;按F8就可以执行&#xff0c;需要注意的是sql语句必须以分号结束。下期更新

eclipse的优化操作

取消“updating maven dependencies” 如果没有用到maven&#xff0c;可以取消这个操作。eclipse下可以选上这个选项 Window -> Preferences -> Maven -> enable Offline option MyEclipse的话&#xff0c;取消了Maven4MyEclipse的话&#xff0c;update maven index…

case when then else end

sql case when then else end 查询生成统计列表 case具有两种格式。简单case函数和case搜索函数。 –简单case函数 case sexwhen 1 then 男when 2 then 女else 其他 end--case搜索函数case when sex 1 then 男when sex 2 then 女else 其他 end这两种方式&#xff0c;可以实…

XML报错,是启动不了服务的。

XML报错&#xff0c;是启动不了服务的。

sql优化——模糊查询

like 和 instr的查询效率 select name from user where instr(id, 99)> 0; 等价于select name from user where id like %99%; LIKE查询一次&#xff0c;就走一次全表扫描&#xff0c;效率非常慢 同样的效果&#xff0c;现在来换做INSTR函数来执行&#xff0c;时间上…

Oracle创建序列,删除序列

1、创建 -- Create sequence create sequence SEQ_USERID minvalue 1 maxvalue 999999999999 start with 1 increment by 1 nocache cycle; 2、查询 select SEQ_USERID.nextval from dual; 3、删除 drop sequence SEQ_USERID&#xff1b;

Oracle创建、删除、备份表

1、创建 create table USER (ID NUMBER(20) not null,constraint PK_USER primary key (ID) //主键 );//添加注释 comment on table USER is 人员信息表; comment on column USER.ID is 人员ID; 2、备份表 create table USER_temp as select * from USER; --只取表结构…

MacOS下安装BeautifulSoup库及使用

BeautifulSoup简介 BeautifulSoup库是一个强大的python第三方库&#xff0c;它可以解析html进行解析&#xff0c;并提取信息。 安装BeautifulSoup 打开终端&#xff0c;输入命令&#xff1a;pip3 install beautifulsoup4 BeautifulSoup库小测 小测用到的html页面地址&#xff1…

Java中当前的时间的各种写法

1、系统时间&#xff08;电脑上显示的时间&#xff09; //导入的包&#xff1a; import java.text.SimpleDateFormat; import java.util.Date;SimpleDateFormat df new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 String dqsj df.format(new Date()…

第十章 深入理解Session与Cookie

理解Cookie 理解Session Cookie安全问题 分布式Session框架 Cookie压缩 表单重复提交问题 多终端Session统一转载于:https://www.cnblogs.com/hzzjj/p/9825639.html

sql 修改字段 删除字段操作

1、修改字段 --修改字段名 alter table [表名] rename column oldCname to newCName; --修改数据类型 alter table [表名] modify (columnName 数据类型); 2、删除字段 alter table [表名] drop column [字段名]

oracle创建、删除索引等操作

1、创建索引 create index 索引名 on 表名(列名); 2、删除索引 drop index 索引名; 3、创建组合索引 create index 索引名 on 表名(列名1,,列名2); 4、查询索引 --根据索引名&#xff0c;查询表索引字段 select * from user_ind_columns where index_name索引名; --根据…

判断radio单选框是否选中

1、利用获取选中值判断选中 /*------判断radio是否有选中&#xff0c;获取选中的值--------*/var val$(input:radio[name"sex"]:checked).val();if(valnull){alert("什么也没选中!");return false;}else{alert(val);} 2、使用checked属性判断选中 /*---…