美文网首页
ijkplayer学习笔记(三)——初始化流程

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

作者: 程序媛的程 | 来源:发表于2021-09-17 12:20 被阅读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

    相关文章

      网友评论

          本文标题:ijkplayer学习笔记(三)——初始化流程

          本文链接:https://www.haomeiwen.com/subject/ivpjgltx.html