ijkplayer系列10:流程分析-開始工作

開始工作
開始工作流程如下
在這裏插入圖片描述

前面幾個沒啥好看的,我們直接進入ijkmp_start_l():

static int ikjmp_chkst_start_l(int mp_state) {
    MPST_RET_IF_EQ(mp_state, MP_STATE_IDLE);
    MPST_RET_IF_EQ(mp_state, MP_STATE_INITIALIZED);
    MPST_RET_IF_EQ(mp_state, MP_STATE_ASYNC_PREPARING);
    // MPST_RET_IF_EQ(mp_state, MP_STATE_PREPARED);
    // MPST_RET_IF_EQ(mp_state, MP_STATE_STARTED);
    // MPST_RET_IF_EQ(mp_state, MP_STATE_PAUSED);
    // MPST_RET_IF_EQ(mp_state, MP_STATE_COMPLETED);
    MPST_RET_IF_EQ(mp_state, MP_STATE_STOPPED);
    MPST_RET_IF_EQ(mp_state, MP_STATE_ERROR);
    MPST_RET_IF_EQ(mp_state, MP_STATE_END);

    return 0;
}

static int ijkmp_start_l(IjkMediaPlayer *mp) {
    assert(mp);

    MP_RET_IF_FAILED(ikjmp_chkst_start_l(mp->mp_state));

    ffp_remove_msg(mp->ffplayer, FFP_REQ_START);
    ffp_remove_msg(mp->ffplayer, FFP_REQ_PAUSE);
    ffp_notify_msg1(mp->ffplayer, FFP_REQ_START);

    return 0;
}

可以看出,能夠切換到STARTED狀態的場景還是比較多的,總共有4個,分別爲PREPARED、STARTED、PAUSED、COMPLETED。ijkmp_start_l()不會直接將狀態變更爲STARTED,而是發出一個事件FFP_REQ_START,在收到這個事件後再變更狀態。
我們來看下處理事件的代碼,代碼位於ijkplayer.c的ijkmp_get_msg()方法中:

 /* need to call msg_free_res for freeing the resouce obtained in msg */
int ijkmp_get_msg(IjkMediaPlayer *mp, AVMessage *msg, int block) {
    assert(mp);
    while (1) {
        int continue_wait_next_msg = 0;
        int retval = msg_queue_get(&mp->ffplayer->msg_queue, msg, block);
        if (retval <= 0)
            return retval;

        switch (msg->what) {
        ...
        case FFP_REQ_START:
            MPTRACE("ijkmp_get_msg: FFP_REQ_START\n");
            continue_wait_next_msg = 1;
            pthread_mutex_lock(&mp->mutex);
            if (0 == ikjmp_chkst_start_l(mp->mp_state)) {
                // FIXME: 8 check seekable
                if (mp->restart) {
                    if (mp->restart_from_beginning) {
                        av_log(mp->ffplayer, AV_LOG_DEBUG, "ijkmp_get_msg: FFP_REQ_START: restart from beginning\n");
                        retval = ffp_start_from_l(mp->ffplayer, 0);
                        if (retval == 0)
                            ijkmp_change_state_l(mp, MP_STATE_STARTED);
                    } else {
                        av_log(mp->ffplayer, AV_LOG_DEBUG, "ijkmp_get_msg: FFP_REQ_START: restart from seek pos\n");
                        retval = ffp_start_l(mp->ffplayer);
                        if (retval == 0)
                            ijkmp_change_state_l(mp, MP_STATE_STARTED);
                    }
                    mp->restart = 0;
                    mp->restart_from_beginning = 0;
                } else {
                    av_log(mp->ffplayer, AV_LOG_DEBUG, "ijkmp_get_msg: FFP_REQ_START: start on fly\n");
                    retval = ffp_start_l(mp->ffplayer);
                    if (retval == 0)
                        ijkmp_change_state_l(mp, MP_STATE_STARTED);
                }
            }
            pthread_mutex_unlock(&mp->mutex);
            break;
            ...
        }
    }
}

這裏面做了三件事情:

  1. 狀態檢驗,如果當前狀態不能變更到STARTED,則忽視這條消息。
  2. 判斷是否重啓(restart),針對restart做區分處理。由於restart的場景一般適應於點播,所以對於直播來說不用去太關心這部分邏輯,可以認爲就是調用ffp_start_l()方法。
  3. 變更狀態爲STARTED並通知外部。
    接着,我們來看下ffp_start_l(),該方法最終會調用到stream_toggle_pause_l()方法:
/* pause or resume the video */
static void stream_toggle_pause_l(FFPlayer *ffp, int pause_on) {
    VideoState *is = ffp->is;
    if (is->paused && !pause_on) {
        is->frame_timer += av_gettime_relative() / 1000000.0 - is->vidclk.last_updated;

#ifdef FFP_MERGE
        if (is->read_pause_return != AVERROR(ENOSYS)) {
            is->vidclk.paused = 0;
        }
#endif
        set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
    } else {
    }
    set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
    is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = pause_on;
    
    SDL_AoutPauseAudio(ffp->aout, pause_on);
} 

不要被這個方法名稱欺騙了,這個方法可以暫停工作也可以恢復工作,在這裏,我們當然是要恢復工作。方法末尾調用了SDL_AoutPauseAudio()方法來恢復語音工作,這個方法我們不再深入,音頻API相關的我會另外開個專題講解,這裏只需要知道它的作用即可。
從以上的分析中,我們知道了start階段主要做了以下幾件事情:

  1. 變更狀態爲STARTED並通知外部。
  2. 處理一些時間相關的邏輯,時間相關的邏輯我也還沒理清,後續會搞個時間專題。
  3. 恢復語音工作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章