開始工作
開始工作流程如下
前面幾個沒啥好看的,我們直接進入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;
...
}
}
}
這裏面做了三件事情:
- 狀態檢驗,如果當前狀態不能變更到STARTED,則忽視這條消息。
- 判斷是否重啓(restart),針對restart做區分處理。由於restart的場景一般適應於點播,所以對於直播來說不用去太關心這部分邏輯,可以認爲就是調用ffp_start_l()方法。
- 變更狀態爲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階段主要做了以下幾件事情:
- 變更狀態爲STARTED並通知外部。
- 處理一些時間相關的邏輯,時間相關的邏輯我也還沒理清,後續會搞個時間專題。
- 恢復語音工作。