开始工作
开始工作流程如下
前面几个没啥好看的,我们直接进入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并通知外部。
- 处理一些时间相关的逻辑,时间相关的逻辑我也还没理清,后续会搞个时间专题。
- 恢复语音工作。