从output入手,梳理一下obs output的结构。这里需要仔细过一遍,因为接下来需要把视频写入Unreal的Rendertarget对象,来渲染成材质。
音频也需要单独接入到Unreal引擎中。梳理的过程中,非核心的逻辑和标记我会去掉,只保留主干。
//碧麟备注版 struct obs_output { // obs上下文 struct obs_context_data context; // 输出结构信息 struct obs_output_info info; /* indicates ownership of the info.id buffer */ bool owns_info_id; int64_t video_offset; int64_t audio_offsets[MAX_OUTPUT_AUDIO_ENCODERS]; int64_t highest_audio_ts; int64_t highest_video_ts; pthread_t end_data_capture_thread; int total_frames; //视频信息指针 video_t *video; //音频信息指针 audio_t *audio; //视频编码器 obs_encoder_t *video_encoder; //音频编码器,因为支持多路音频合成,所以这里用的是数组 obs_encoder_t *audio_encoders[MAX_OUTPUT_AUDIO_ENCODERS]; struct circlebuf audio_buffer[MAX_AUDIO_MIXES][MAX_AV_PLANES]; uint64_t audio_start_ts; uint64_t video_start_ts; size_t audio_size; size_t planes; size_t sample_rate; size_t total_audio_frames; uint32_t scaled_width; uint32_t scaled_height; struct video_scale_info video_conversion; struct audio_convert_info audio_conversion; struct circlebuf caption_data; float audio_data[MAX_AUDIO_CHANNELS][AUDIO_OUTPUT_FRAMES]; };
看完上面的output结构,我们实际调试一下,点击“屏幕录制”,会进入obs_output_start这个函数,这个函数是个马甲,简单带过
//碧麟精简标注版
bool obs_output_start(obs_output_t *output)
{
bool encoded;
bool has_service;
encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0;
if (encoded && output->delay_sec) {
//延迟运行
return obs_output_delay_start(output);
} else {
//实际运行
if (obs_output_actual_start(output)) {
//发送starting signal
do_output_signal(output, "starting");
return true;
}
return false;
}
}
接下来是重点
//碧麟精简批注版
bool obs_output_actual_start(obs_output_t *output)
{
bool success = false;
// 第一步,调用outp->info.start,参数是output->context.data
if (output->context.data)
success = output->info.start(output->context.data);
if (success && output->video) {
output->starting_frame_count =
video_output_get_total_frames(output->video);
output->starting_drawn_count = obs->video.total_frames;
output->starting_lagged_count = obs->video.lagged_frames;
}
if (os_atomic_load_long(&output->delay_restart_refs))
os_atomic_dec_long(&output->delay_restart_refs);
output->caption_timestamp = 0;
circlebuf_free(&output->caption_data);
circlebuf_init(&output->caption_data);
return success;
}
第一步是,调用outp->info.start,参数是output->context.data
output->info.start应该是一个函数指针,定义如下
// 碧麟精简批注版
// output_info结构 ,主要存储函数指针
struct obs_output_info {
/* required */
const char *id;
uint32_t flags;
const char *(*get_name)(void *type_data);
//创建
void *(*create)(obs_data_t *settings, obs_output_t *output);
//销毁
void (*destroy)(void *data);
//开始
bool (*start)(void *data);
//停止
void (*stop)(void *data, uint64_t ts);
void (*raw_video)(void *data, struct video_data *frame);
void (*raw_audio)(void *data, struct audio_data *frames);
void (*encoded_packet)(void *data, struct encoder_packet *p