前言

obs支持添加各种容器格式的视频文件,音频文件(mp4、flv、mp3、ts),也支持添加网络流(rtmp,srt,rtsp,udp)。本质上obs是基于ffmpeg的libavformat库提供解码能力播放媒体视频。只要是obs的依赖ffmpeg库支持的协议都可以作为媒体源添加。
通过下面命令查看ffmpeg支持的协议

.\ffmpeg.exe -protocols		//打印ffmpeg支持的协议

媒体源的创建

媒体源的创建有多种方式,可以直接将视频文件直接拖拽进obs主窗口,可以在主窗口右键添加媒体源,也可以复制当前存在的媒体源。不管哪种方式创建媒体源最后的入口都是obs_source_create,这是libobs提供的创建源的api,如果进行obs二次开发添加源的入口也都是走这个api。下面贴一下拖拽方式创建媒体源的调用堆栈。

>	obs-ffmpeg.dll!ffmpeg_source_update(void * data, obs_data * settings)456	C
 	obs-ffmpeg.dll!ffmpeg_source_create(obs_data * settings, obs_source * source)613	C
 	obs.dll!obs_source_create_internal(const char * id, const char * name, obs_data * settings, obs_data * hotkey_data, bool private, unsigned int last_obs_ver)387	C
 	obs.dll!obs_source_create(const char * id, const char * name, obs_data * settings, obs_data * hotkey_data)416	C
 	obs64.exe!OBSBasic::AddDropSource(const char * data, OBSBasic::DropType image)176	C++
 	obs64.exe!OBSBasic::dropEvent(QDropEvent * event)280	C++
 	[外部代码]	
 	obs64.exe!run_program(std::basic_fstream<char,std::char_traits<char>> & logFile, int argc, char * * argv)2143	C++
 	obs64.exe!main(int argc, char * * argv)2839	C++
 	obs64.exe!WinMain(HINSTANCE__ * __formal, HINSTANCE__ * __formal, char * __formal, int __formal)97	C++

媒体源输出音频帧和视频帧

media-playback 封装了操作媒体源的api

obs对媒体源的操作都封装到了media-playback.lib,通过media.h里的函数的命名就可以看出。

//媒体源的初始化和销毁
extern bool mp_media_init(mp_media_t *media, const struct mp_media_info *info);
extern void mp_media_free(mp_media_t *media);

//媒体源的控制 播放 停止 暂停 获取当前播放时间 快进快退
extern void mp_media_play(mp_media_t *media, bool loop, bool reconnecting);
extern void mp_media_stop(mp_media_t *media);
extern void mp_media_play_pause(mp_media_t *media, bool pause);
extern int64_t mp_get_current_time(mp_media_t *m);
extern void mp_media_seek_to(mp_media_t *m, int64_t pos);

通过下面创建媒体源的调用堆栈可以看到,最终都调用到了media-playback的里面。

 	obs-ffmpeg.dll!mp_media_init_internal(mp_media * m, const mp_media_info * info)797	C
 	obs-ffmpeg.dll!mp_media_init(mp_media * media, const mp_media_info * info)834	C
 	obs-ffmpeg.dll!ffmpeg_source_open(ffmpeg_source * s)319	C
 	obs-ffmpeg.dll!ffmpeg_source_update(void * data, obs_data * settings)453	C
 	obs-ffmpeg.dll!ffmpeg_source_create(obs_data * settings, obs_source * source)613	C

一图抵千言,贴一下我准备的xmind截图。
media-playback
可以看到在mp_media_init_internal函数里面创建了一个mp_media_thread_start线程来执行音视频的解码,对ffmpeg解封装流程比较熟悉,这里面的代码就很好理解了,这里便不再赘述。有时间单独做一个ffmpeg系列的专栏,详细说一下ffmpeg解封装,编解码的api调用逻辑。

mp_media_next_video() 输出解码后的视频帧

解码后的视频帧通过函数mp_media_next_video调用在ffmpeg_source_open里面绑定回调函数get_audio,最终通过 obs_source_output_video() 插入到libobs的视频帧缓存队列,等待视频渲染线程取走做渲染。媒体源属于异步输出视频帧,output_flags包含OBS_SOURCE_ASYNC_VIDEO属性。

static void get_frame(void *opaque, struct obs_source_frame *f)
{
	struct ffmpeg_source *s = opaque;
	obs_source_output_video(s->source, f);
}

mp_media_next_audio() 输出解码后的音频帧

解码后的音频帧通过函数 mp_media_next_audio() 调用在ffmpeg_source_open里面绑定回调函数get_audio,最终通过obs_source_output_audio输出到libobs的音频帧缓冲队列,等待音频编码线程取走做编码发送。

static void get_audio(void *opaque, struct obs_source_audio *a)
{
	struct ffmpeg_source *s = opaque;
	obs_source_output_audio(s->source, a);

	if (!s->is_local_file && os_atomic_set_bool(&s->reconnecting, false))
		FF_BLOG(LOG_INFO, "Reconnected.");
}

媒体源的控制

控制媒体源的暂停,播放,还有seek操作都比较简单。阅读下面绑定的函数很容易理解。

struct obs_source_info ffmpeg_source = {
	.media_play_pause = ffmpeg_source_play_pause,	//控制暂停
	.media_restart = ffmpeg_source_restart,			//重新播放
	.media_stop = ffmpeg_source_stop,				//停止播放
	.media_get_duration = ffmpeg_source_get_duration,//获取播放媒体源的总时长
	.media_get_time = ffmpeg_source_get_time,		//获取当前播放时机
	.media_set_time = ffmpeg_source_set_time,		// seek操作
	.media_get_state = ffmpeg_source_get_state,		//获取当前播放状态
}

总结

obs的媒体源整个处理比较简单,底层基于ffmpeg的api做视频的解封装,只用了一个线程完成了音视频的解码输出,解码完成后通过libobs的api添加到音视频相应的队列,等待音视频各自的线程取走渲染播放。media-playback是一个非常好的可以学习ffmepg解封装api的一个小项目。

以上都是个人工作当中对obs-studio开源项目的理解,难免有错误的地方,如果有欢迎指出。

若有帮助幸甚。


技术参考

  1. 视频技术参考: https://ke.qq.com/course/3202131?flowToken=1040950
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐