做试用的网站,杭州有哪些外资企业招聘,php网站开发学习,做球服的网站有哪些video_frame_init 讲解
/* messy code alarm
video_frame_init 函数用于初始化视频帧。它接受一个指向 struct video_frame 结构体的指针 frame#xff0c;
视频格式 format#xff0c;以及宽度 width 和高度 height。该函数根据视频格式的不同#xff0c;计算出每个视频帧…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;
}