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++
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截图。
可以看到在mp_media_init_internal函数里面创建了一个mp_media_thread_start线程来执行音视频的解码,对ffmpeg解封装流程比较熟悉,这里面的代码就很好理解了,这里便不再赘述。有时间单独做一个ffmpeg系列的专栏,详细说一下ffmpeg解封装,编解码的api调用逻辑。
解码后的视频帧通过函数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() 调用在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开源项目的理解,难免有错误的地方,如果有欢迎指出。
若有帮助幸甚。