ijkplayer里用户的操作都会转换为消息,放到消息队列里,然后消息线程会不断的从消息队列里读取消息进行相应的处理。
暂停后播放
消息状态为FFP_REQ_START, 函数调用如下
具体的调用过程,里面会省略一些不相关的代码
/* 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");
MPTRACE("ijkmp_get_msg: FFP_REQ_START, %lld\n", get_current_time());
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 {
// 暂停后播放走这里,会去改变状态为ffp_start_l
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;
case FFP_REQ_PAUSE:
...
case FFP_REQ_SEEK:
...
}
if (continue_wait_next_msg) {
msg_free_res(msg);
continue;
}
return retval;
}
return -1;
}
int ffp_start_l(FFPlayer *ffp)
{
assert(ffp);
VideoState *is = ffp->is;
if (!is)
return EIJK_NULL_IS_PTR;
toggle_pause(ffp, 0);
return 0;
}
static void toggle_pause(FFPlayer *ffp, int pause_on)
{
SDL_LockMutex(ffp->is->play_mutex);
toggle_pause_l(ffp, pause_on);
SDL_UnlockMutex(ffp->is->play_mutex);
}
static void toggle_pause_l(FFPlayer *ffp, int pause_on)
{
VideoState *is = ffp->is;
if (is->pause_req && !pause_on) {
set_clock(&is->vidclk, get_clock(&is->vidclk), is->vidclk.serial);
set_clock(&is->audclk, get_clock(&is->audclk), is->audclk.serial);
}
is->pause_req = pause_on;
ffp->auto_resume = !pause_on;
stream_update_pause_l(ffp);
is->step = 0;
}
static void stream_update_pause_l(FFPlayer *ffp)
{
VideoState *is = ffp->is;
if (!is->step && (is->pause_req || is->buffering_on)) {
stream_toggle_pause_l(ffp, 1);
} else {
stream_toggle_pause_l(ffp, 0);
}
}
/* 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);
set_clock(&is->audclk, get_clock(&is->audclk), is->audclk.serial);
} else {
}
set_clock(&is->extclk, get_clock(&is->extclk), is->extclk.serial);
// step模式并且(is->pause_req || is->buffering_on)
if (is->step && (is->pause_req || is->buffering_on)) {
is->paused = is->vidclk.paused = is->extclk.paused = pause_on;
} else {
is->paused = is->audclk.paused = is->vidclk.paused = is->extclk.paused = pause_on;
// 暂停或者开启音频播放
SDL_AoutPauseAudio(ffp->aout, pause_on);
}
}
如果是播放,则会开启音频的播放,否则关闭音频的播放。这样可以驱动整个生产消费场景的继续还是暂停。
seek
seek消息类型为FFP_REQ_SEEK,函数调用如下
具体的流程如下:
/* 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_SEEK:
// MPTRACE("ijkmp_get_msg: FFP_REQ_SEEK\n");
MPTRACE("ijkmp_get_msg: FFP_REQ_SEEK, %lld\n", get_current_time());
continue_wait_next_msg = 1;
pthread_mutex_lock(&mp->mutex);
if (0 == ikjmp_chkst_seek_l(mp->mp_state)) {
mp->restart_from_beginning = 0;
if (0 == ffp_seek_to_l(mp->ffplayer, msg->arg1)) {
av_log(mp->ffplayer, AV_LOG_DEBUG, "ijkmp_get_msg: FFP_REQ_SEEK: seek to %d\n", (int)msg->arg1);
}
}
pthread_mutex_unlock(&mp->mutex);
break;
}
if (continue_wait_next_msg) {
msg_free_res(msg);
continue;
}
return retval;
}
return -1;
}
int ffp_seek_to_l(FFPlayer *ffp, long msec)
{
assert(ffp);
VideoState *is = ffp->is;
int64_t start_time = 0;
int64_t seek_pos = milliseconds_to_fftime(msec);
int64_t duration = milliseconds_to_fftime(ffp_get_duration_l(ffp));
if (!is)
return EIJK_NULL_IS_PTR;
if (duration > 0 && seek_pos >= duration && ffp->enable_accurate_seek) {
toggle_pause(ffp, 1);
ffp_notify_msg1(ffp, FFP_MSG_COMPLETED);
return 0;
}
start_time = is->ic->start_time;
if (start_time > 0 && start_time != AV_NOPTS_VALUE)
seek_pos += start_time;
// FIXME: 9 seek by bytes
// FIXME: 9 seek out of range
// FIXME: 9 seekable
av_log(ffp, AV_LOG_DEBUG, "stream_seek %"PRId64"(%d) + %"PRId64", \n", seek_pos, (int)msec, start_time);
stream_seek(is, seek_pos, 0, 0);
return 0;
}
/* seek in the stream */
static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int seek_by_bytes)
{
// 设置seek数据
if (!is->seek_req) {
is->seek_pos = pos;
is->seek_rel = rel;
is->seek_flags &= ~AVSEEK_FLAG_BYTE;
if (seek_by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
SDL_CondSignal(is->continue_read_thread);
}
}
最终是设置了一下seek数据,seek的操作是在渲染之前进行逻辑处理的,要判断当前帧的pts和is->seek_pos是否靠近,这个在视频解码线程,将解码数据放到frame_q队列之前进行的判断,暂时不详细展开。
暂停
消息状态为FFP_REQ_PAUSE, 函数调用流程如下
具体的流程如下:
/* 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_MSG_PREPARED:
// MPTRACE("ijkmp_get_msg: FFP_MSG_PREPARED\n");
MPTRACE("ijkmp_get_msg: FFP_MSG_PREPARED, %lld\n", get_current_time());
pthread_mutex_lock(&mp->mutex);
if (mp->mp_state == MP_STATE_ASYNC_PREPARING) {
ijkmp_change_state_l(mp, MP_STATE_PREPARED);
} else {
// FIXME: 1: onError() ?
av_log(mp->ffplayer, AV_LOG_DEBUG, "FFP_MSG_PREPARED: expecting mp_state==MP_STATE_ASYNC_PREPARING\n");
}
if (!mp->ffplayer->start_on_prepared) {
//ready后这个暂停状态不通知外边了,那么现在外边获取状态时是playing状态,如果seek的话是seek状态
// ijkmp_change_state_l(mp, MP_STATE_PAUSED);
}
pthread_mutex_unlock(&mp->mutex);
break;
case FFP_MSG_COMPLETED:
// MPTRACE("ijkmp_get_msg: FFP_MSG_COMPLETED\n");
MPTRACE("ijkmp_get_msg: FFP_MSG_COMPLETED, %lld\n", get_current_time());
pthread_mutex_lock(&mp->mutex);
mp->restart = 1;
mp->restart_from_beginning = 1;
ijkmp_change_state_l(mp, MP_STATE_COMPLETED);
pthread_mutex_unlock(&mp->mutex);
break;
case FFP_MSG_SEEK_COMPLETE:
// MPTRACE("ijkmp_get_msg: FFP_MSG_SEEK_COMPLETE\n");
MPTRACE("ijkmp_get_msg: FFP_MSG_SEEK_COMPLETE, %lld\n", get_current_time());
pthread_mutex_lock(&mp->mutex);
mp->seek_req = 0;
mp->seek_msec = 0;
pthread_mutex_unlock(&mp->mutex);
break;
case FFP_REQ_START:
// MPTRACE("ijkmp_get_msg: FFP_REQ_START\n");
MPTRACE("ijkmp_get_msg: FFP_REQ_START, %lld\n", get_current_time());
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;
case FFP_REQ_PAUSE:
// MPTRACE("ijkmp_get_msg: FFP_REQ_PAUSE\n");
MPTRACE("ijkmp_get_msg: FFP_REQ_PAUSE, %lld\n", get_current_time());
continue_wait_next_msg = 1;
pthread_mutex_lock(&mp->mutex);
if (0 == ikjmp_chkst_pause_l(mp->mp_state)) {
int pause_ret = ffp_pause_l(mp->ffplayer);
if (pause_ret == 0)
ijkmp_change_state_l(mp, MP_STATE_PAUSED);
}
pthread_mutex_unlock(&mp->mutex);
break;
case FFP_REQ_SEEK:
// MPTRACE("ijkmp_get_msg: FFP_REQ_SEEK\n");
MPTRACE("ijkmp_get_msg: FFP_REQ_SEEK, %lld\n", get_current_time());
continue_wait_next_msg = 1;
pthread_mutex_lock(&mp->mutex);
if (0 == ikjmp_chkst_seek_l(mp->mp_state)) {
mp->restart_from_beginning = 0;
if (0 == ffp_seek_to_l(mp->ffplayer, msg->arg1)) {
av_log(mp->ffplayer, AV_LOG_DEBUG, "ijkmp_get_msg: FFP_REQ_SEEK: seek to %d\n", (int)msg->arg1);
}
}
pthread_mutex_unlock(&mp->mutex);
break;
}
if (continue_wait_next_msg) {
msg_free_res(msg);
continue;
}
return retval;
}
return -1;
}
可以对比pause和pause后进行播放
从toggle_pause后函数调用是一样的,最终一个是在函数stream_toggle_pause_l里播放音频,一个是暂停音频播放。