1、初始化入口
打开ijkplayer-ios/ios/IJKMediaDemo里面的工程文件,查看IJKFFMoviePlayerController的声明实现文件,实际上它并不继承自UIViewController,不是iOS传统意义上的VC控制器,而是iOS层与底层进行通信的控制层,是播放器类。
- (id)initWithContentURLString:(NSString *)aUrlString
withOptions:(IJKFFOptions *)options
{
if (aUrlString == nil)
return nil;
self = [super init];
if (self) {
//主要是ffmpeg的初始化工作, ijkplayer是对ffmpeg的封装
ijkmp_global_init();
//注册ijkplayer事件相关的回调函数
ijkmp_global_set_inject_callback(ijkff_inject_callback);
//检查ijkplayer版本号是否匹配
[IJKFFMoviePlayerController checkIfFFmpegVersionMatch:NO];
// 见https://www.jianshu.com/p/97f4f0fab3b6
if (options == nil)
options = [IJKFFOptions optionsByDefault];
// 监控类,可以提供fps,码率,网络状况,视频尺寸等信息
_monitor = [[IJKFFMonitor alloc] init];
// 源地址
_urlString = aUrlString;
//关键api,后面会深入展开
_mediaPlayer = ijkmp_ios_create(media_player_msg_loop);
// 向FFmpeg注册IJKFFMoviePlayerController
ijkmp_set_weak_thiz(_mediaPlayer, (__bridge_retained void *) self);
ijkmp_set_inject_opaque(_mediaPlayer, (__bridge_retained void *) weakHolder);
ijkmp_set_ijkio_inject_opaque(_mediaPlayer, (__bridge_retained void *)weakHolder);
ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "start-on-prepared", _shouldAutoplay ? 1 : 0);
// 渲染视图
_glView = [[IJKSDLGLView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_glView.isThirdGLView = NO;
_view = _glView;
//ffplayer与glView想关联
ijkmp_ios_set_glview(_mediaPlayer, _glView);
ijkmp_set_option(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");
......
}
return self;
}
2、初始化关键Api的深入学习
// 播放器创建
_mediaPlayer = ijkmp_ios_create(media_player_msg_loop);
IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*))
{
IjkMediaPlayer *mp = ijkmp_create(msg_loop);
if (!mp)
goto fail;
mp->ffplayer->vout = SDL_VoutIos_CreateForGLES2();//创建图像渲染对象SDL_Vout
if (!mp->ffplayer->vout)
goto fail;
mp->ffplayer->pipeline = ffpipeline_create_from_ios(mp->ffplayer);//创建平台相关的IJKFF_Pipeline对象
if (!mp->ffplayer->pipeline)
goto fail;
return mp;
fail:
ijkmp_dec_ref_p(&mp);
return NULL;
}
在该方法中主要完成了三个动作:
2.1创建IJKMediaPlayer对象:延伸到ffp_create()方法
2.2创建图像渲染对象SDL_Vout:调用OpenGL绘制图像
2.3创建平台相关的IJKFF_Pipeline对象,包括视频解码以及音频输出部分 : 输出管线需单独开篇幅学习了
说明:
ijkmp_ios_create:ios平台下对ijkplayer.c的封装
ijkmp_create:底层播放控制的C代码 和 上层的ios平台上操作控制的OC代码的中间层,是消息循环函数运行在这一层
IjkMediaPlayer结构体如下:
struct IjkMediaPlayer {
volatile int ref_count;
pthread_mutex_t mutex;
FFPlayer *ffplayer;
int (*msg_loop)(void*);
SDL_Thread *msg_thread;
SDL_Thread _msg_thread;
int mp_state;
char *data_source;
void *weak_thiz;
int restart;
int restart_from_beginning;
int seek_req;
long seek_msec;
};
IjkMediaPlayer结构体对象在创建完成以后将msg_loop消息循环函数指针赋值保存到msg_loop成员变量中。
3、消息循环初探
在播放器初始化方法中,可以看到media_player_msg_loop函数地址作为参数传入了ijkmp_ios_create
_mediaPlayer = ijkmp_ios_create(media_player_msg_loop);
int media_player_msg_loop(void* arg)
{
@autoreleasepool {
IjkMediaPlayer *mp = (IjkMediaPlayer*)arg;
__weak IJKFFMoviePlayerController *ffpController = ffplayerRetain(ijkmp_set_weak_thiz(mp, NULL));
while (ffpController) {
@autoreleasepool {
// 由底层通过msg_loop传出IJKFFMoviePlayerMessage对象,以便上层进行业务相关处理
IJKFFMoviePlayerMessage *msg = [ffpController obtainMessage];
if (!msg)
break;
int retval = ijkmp_get_msg(mp, &msg->_msg, 1);
if (retval < 0)
break;
// block-get should never return 0
assert(retval > 0);
[ffpController performSelectorOnMainThread:@selector(postEvent:) withObject:msg waitUntilDone:NO];
}
}
// retained in prepare_async, before SDL_CreateThreadEx
ijkmp_dec_ref_p(&mp);
return 0;
}
}
在播放过程中,某些行为的完成或者变化,如prepare完成,开始渲染等,需要以事件形式通知到外部,以便上层(这里是iOS层)作出具体的业务处理。ijkplayer支持的事件比较多,具体定义在ijkplayer/ijkmedia/ijkplayer/ff_ffmsg.h中。
而最终,该函数地址被赋值给了IjkMediaPlayer中的msg_loop函数指针
IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
{
......
mp->msg_loop = msg_loop;
......
}
当开始播放时,会开启一个消息线程
static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
{
......
mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");
......
}
其中ijkmp_msg_loop函数就是mp->msg_loop,至此已经完成了播放消息发送的准备工作。
参考:https://blog.csdn.net/xipiaoyouzi/article/details/74280170
金山视频云:https://www.jianshu.com/u/b2227c3472fd
网友评论