• 【ijkplayer】整体结构总结(一)


    ijkplayer

    【ijkplayer】整体结构总结(一)

    【ijkplayer】read_thread 流程梳理(二)

    【ijkplayer】解码流程梳理(三)

    【ijkplayer】渲染流程梳理(四)


    【ijkplayer】整体结构总结(一)

    Java层—JNI层—C层,作为一个播放器的整体流程,整体结构体如下。
    在这里插入图片描述

    主要结构体

    在初始化native_setUp的时候,创建了IjkMediaPlayerFFPlayerIJKFF_Pipeline,在prepare阶段通过stream_open创建了VideoState,这几个结构体基本贯穿了整个流程,非常重要

    IjkMediaPlayer

    表示native层的Player,与Java层一对一绑定。作为Java到c的入口封装。

    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;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    FFPlayer

    具体的播放器,internal player,包含编码输出等;代码进入到ff_ffplay.c后,就都使用的是FFPlayer。持有VideoState

    typedef struct FFPlayer {
        VideoState *is;/* extra fields */
        SDL_Aout *aout;
        SDL_Vout *vout;
        struct IJKFF_Pipeline *pipeline;
        struct IJKFF_Pipenode *node_vdec;
    ​
        MessageQueue msg_queue;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    VideoState

    VideoState,在stream_open中被创建,表示播放过程中的所有状态

    typedef struct VideoState {
        Clock audclk;
        Clock vidclk;
        Clock extclk;
    ​
        FrameQueue pictq;
        FrameQueue subpq;
        FrameQueue sampq;
    ​
        Decoder auddec;
        Decoder viddec;
        Decoder subdec;
    ​
        PacketQueue audioq;
        PacketQueue subtitleq;
        PacketQueue videoq;int seek_req;
        double frame_timer;
        int abort_request;
        int force_refresh;
        int paused;}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    IJKFF_Pipeline

    ffpipeline封装了视频解码器和音频解码器/输出实现通过函数指针

    // ffpipeline_android.c中
    typedef struct IJKFF_Pipeline_Opaque {
        FFPlayer      *ffp;
        SDL_mutex     *surface_mutex;
        jobject        jsurface;
        volatile bool  is_surface_need_reconfigure;
    
        bool         (*mediacodec_select_callback)(void *opaque, ijkmp_mediacodecinfo_context *mcc);
        void          *mediacodec_select_callback_opaque;
    
        SDL_Vout      *weak_vout;
    
        float          left_volume;
        float          right_volume;
    } IJKFF_Pipeline_Opaque;
    
    // 在ff_ffpipeline.h
    typedef struct IJKFF_Pipeline_Opaque IJKFF_Pipeline_Opaque;
    typedef struct IJKFF_Pipeline IJKFF_Pipeline;
    struct IJKFF_Pipeline {
        SDL_Class             *opaque_class;
        IJKFF_Pipeline_Opaque *opaque;
    
        void            (*func_destroy)             (IJKFF_Pipeline *pipeline);
        IJKFF_Pipenode *(*func_open_video_decoder)  (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
        SDL_Aout       *(*func_open_audio_output)   (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
        IJKFF_Pipenode *(*func_init_video_decoder)  (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
        int           (*func_config_video_decoder)  (IJKFF_Pipeline *pipeline, FFPlayer *ffp);
    };
    
    IJKFF_Pipeline *ffpipeline_alloc(SDL_Class *opaque_class, size_t opaque_size);
    void ffpipeline_free(IJKFF_Pipeline *pipeline);
    void ffpipeline_free_p(IJKFF_Pipeline **pipeline);
    
    // 打开视频解码器
    IJKFF_Pipenode *ffpipeline_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp);
    // 打开音频解码器
    SDL_Aout       *ffpipeline_open_audio_output(IJKFF_Pipeline *pipeline, FFPlayer *ffp);
    
    // 异步初始化IJKFF_Pipenode
    IJKFF_Pipenode* ffpipeline_init_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp); 
    
    // 异步初始化视频解码器
    int ffpipeline_config_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    初始化流程

    native_setup

    static void IjkMediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
    {
        MPTRACE("%s\n", __func__);
        IjkMediaPlayer *mp = ijkmp_android_create(message_loop);
        JNI_CHECK_GOTO(mp, env, "java/lang/OutOfMemoryError", "mpjni: native_setup: ijkmp_create() failed", LABEL_RETURN);
    
        jni_set_media_player(env, thiz, mp);
        ijkmp_set_weak_thiz(mp, (*env)->NewGlobalRef(env, weak_this));
        ijkmp_set_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_set_ijkio_inject_opaque(mp, ijkmp_get_weak_thiz(mp));
        ijkmp_android_set_mediacodec_select_callback(mp, mediacodec_select_callback, ijkmp_get_weak_thiz(mp));
    
    LABEL_RETURN:
        ijkmp_dec_ref_p(&mp);
    }
    //ijkmp_android_create
    IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
    {
        //初始化 IjkMediaPlayer结构体
        IjkMediaPlayer *mp = ijkmp_create(msg_loop);
        if (!mp)
            goto fail;
        //  创建SDL_Vout
        mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
        if (!mp->ffplayer->vout)
            goto fail;
        // 创建IJKFF_Pipeline
        mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
        if (!mp->ffplayer->pipeline)
            goto fail;
        // 互相绑定s
        ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
    
        return mp;
    
    fail:
        ijkmp_dec_ref_p(&mp);
        return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    IjkMediaPlayer_setDataSource

    int ijkmp_set_data_source(IjkMediaPlayer *mp, const char *url)
    {
        assert(mp);
        assert(url);
        MPTRACE("ijkmp_set_data_source(url=\"%s\")\n", url);
        pthread_mutex_lock(&mp->mutex);
        int retval = ijkmp_set_data_source_l(mp, url);
        pthread_mutex_unlock(&mp->mutex);
        MPTRACE("ijkmp_set_data_source(url=\"%s\")=%d\n", url, retval);
        return retval;
    }
    static int ijkmp_set_data_source_l(IjkMediaPlayer *mp, const char *url)
    {
        assert(mp);
        assert(url);
    
        // MPST_RET_IF_EQ(mp->mp_state, MP_STATE_IDLE);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_INITIALIZED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ASYNC_PREPARING);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PREPARED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STARTED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PAUSED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_COMPLETED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STOPPED);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ERROR);
        MPST_RET_IF_EQ(mp->mp_state, MP_STATE_END);
    
        freep((void**)&mp->data_source);
        mp->data_source = strdup(url);
        if (!mp->data_source)
            return EIJK_OUT_OF_MEMORY;
    
        ijkmp_change_state_l(mp, MP_STATE_INITIALIZED);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    IjkMediaPlayer_prepareAsync

    调用流程 IjkMediaPlayer_prepareAsync->ijkmp_prepare_async_l->ffp_prepare_async_l->stream_open

    stream_open

    封装了所有流程中需要的参数

    static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputFormat *iformat)
    {
        assert(!ffp->is);
        VideoState *is;
    
        is = av_mallocz(sizeof(VideoState));
        if (!is)
            return NULL;
        is->filename = av_strdup(filename);
        if (!is->filename)
            goto fail;
        is->iformat = iformat;
        is->ytop    = 0;
        is->xleft   = 0;
    #if defined(__ANDROID__)
        if (ffp->soundtouch_enable) {
            is->handle = ijk_soundtouch_create();
        }
    #endif
    
        /* start video display */
        if (frame_queue_init(&is->pictq, &is->videoq, ffp->pictq_size, 1) < 0)
            goto fail;
        if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
            goto fail;
        if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
            goto fail;
    
        if (packet_queue_init(&is->videoq) < 0 ||
            packet_queue_init(&is->audioq) < 0 ||
            packet_queue_init(&is->subtitleq) < 0)
            goto fail;
    
        if (!(is->continue_read_thread = SDL_CreateCond())) {
            av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
            goto fail;
        }
    
        if (!(is->video_accurate_seek_cond = SDL_CreateCond())) {
            av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
            ffp->enable_accurate_seek = 0;
        }
    
        if (!(is->audio_accurate_seek_cond = SDL_CreateCond())) {
            av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
            ffp->enable_accurate_seek = 0;
        }
    
        init_clock(&is->vidclk, &is->videoq.serial);
        init_clock(&is->audclk, &is->audioq.serial);
        init_clock(&is->extclk, &is->extclk.serial);
        is->audio_clock_serial = -1;
        if (ffp->startup_volume < 0)
            av_log(NULL, AV_LOG_WARNING, "-volume=%d < 0, setting to 0\n", ffp->startup_volume);
        if (ffp->startup_volume > 100)
            av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", ffp->startup_volume);
        ffp->startup_volume = av_clip(ffp->startup_volume, 0, 100);
        ffp->startup_volume = av_clip(SDL_MIX_MAXVOLUME * ffp->startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
        is->audio_volume = ffp->startup_volume;
        is->muted = 0;
        is->av_sync_type = ffp->av_sync_type;
    
        is->play_mutex = SDL_CreateMutex();
        is->accurate_seek_mutex = SDL_CreateMutex();
        ffp->is = is;
        is->pause_req = !ffp->start_on_prepared;
    
        is->video_refresh_tid = SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, "ff_vout");
        if (!is->video_refresh_tid) {
            av_freep(&ffp->is);
            return NULL;
        }
    
        is->initialized_decoder = 0;
        is->read_tid = SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, "ff_read");
        if (!is->read_tid) {
            av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
            goto fail;
        }
    
        if (ffp->async_init_decoder && !ffp->video_disable && ffp->video_mime_type && strlen(ffp->video_mime_type) > 0
                        && ffp->mediacodec_default_name && strlen(ffp->mediacodec_default_name) > 0) {
            if (ffp->mediacodec_all_videos || ffp->mediacodec_avc || ffp->mediacodec_hevc || ffp->mediacodec_mpeg2) {
                decoder_init(&is->viddec, NULL, &is->videoq, is->continue_read_thread);
                ffp->node_vdec = ffpipeline_init_video_decoder(ffp->pipeline, ffp);
            }
        }
        is->initialized_decoder = 1;
    
        return is;
    fail:
        is->initialized_decoder = 1;
        is->abort_request = true;
        if (is->video_refresh_tid)
            SDL_WaitThread(is->video_refresh_tid, NULL);
        stream_close(ffp);
        return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    video_refresh_thread,主要负责音视频同步,以及音视频渲染显示流程。

    read_thread ,负责打开流,创建音视频解码线程,读取packet等流程

    message_loop

    消息循环队列,IjkMediaPlayer创建的时候赋值,prepare中创建线程,启动loop,实现机制类似android消息队列锁机制的Looper那一套。

  • 相关阅读:
    Slax Linux 强化了会话管理和引导参数选项
    Oracle 12c自动化管理特性的新进展:自动备份、自动恢复和自动维护功能的优势|oracle 12c相对oralce 11g的新特性(3)
    对标特斯拉的通用人形机器人Figure 01,终于迈出第一步
    若依系统分离版去除redis数据库
    WangEditor在Vue前端的应用
    leetcode 第 361 场周赛
    6、堆(新生区,永久区,堆内存调优(jvm调优))
    1027 Colors in Mars
    聊聊基于Alink库的特征工程方法
    SQLITE3【1】-SQLite向arm-imx6ull板子的移植
  • 原文地址:https://blog.csdn.net/weixin_43874301/article/details/126481256