ijkplayer学习笔记(三)——初始化流程

2021-09-17  本文已影响0人  程序媛的程

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

上一篇 下一篇

猜你喜欢

热点阅读