在第四篇中可以看到,stream_open中涉及到两个线程read_thread和video_refresh_thread的创建,在第五篇中,我着重学习了read_thread中的核心函数stream_component_open,那么在这一篇中,则着重学习视频渲染相关的线程。
video_refresh_thread
video_refresh_thread.png
video_refresh
video_refresh_thread—>video_refresh—>video_display2—>video_image_display2
static void video_refresh(FFPlayer *opaque, double *remaining_time)
{
......
if (is->video_st) {
retry:
if (frame_queue_nb_remaining(&is->pictq) == 0) {
// nothing to do, no picture to display in the queue
} else {
double last_duration, duration, delay;
Frame *vp, *lastvp;
/* dequeue the picture */
//先获取最早的frame
lastvp = frame_queue_peek_last(&is->pictq);
vp = frame_queue_peek(&is->pictq);
/* compute nominal last_duration */
// 获取距离下一帧的时间(用来处理音视频同步的。根据同步的方式,获取修正过后的下一帧时间。)
last_duration = vp_duration(is, lastvp, vp);
delay = compute_target_delay(ffp, last_duration, is);
......
if (frame_queue_nb_remaining(&is->pictq) > 1) {
Frame *nextvp = frame_queue_peek_next(&is->pictq);
duration = vp_duration(is, vp, nextvp);
if(!is->step && (ffp->framedrop > 0 || (ffp->framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) && time > is->frame_timer + duration) {
// 确定要显示下一帧之后,才调用frame_queue_next把下一帧推到队列关键位,即索引rindex指定的位置。
frame_queue_next(&is->pictq);
goto retry;
}
}
}
display:
/* display picture */
if (!ffp->display_disable && is->force_refresh && is->show_mode == SHOW_MODE_VIDEO && is->pictq.rindex_shown)
// video_display2最终到了SDL_Vout的display_overlay函数。和前面一样,到了显示层,在这做了解耦处理,SDL_Vout对象是ffp->vout,也是在IJKFFMoviePlayerControllerinit里构建的
video_display2(ffp);
}
is->force_refresh = 0;
......
}
video_image_display2
static void video_image_display2(FFPlayer *ffp)
{
VideoState *is = ffp->is;
Frame *vp;
Frame *sp = NULL;
//调用frame_queue_peek_last从pictq中读取当前需要显示视频帧
vp = frame_queue_peek_last(&is->pictq);
......
// 调用SDL_VoutDisplayYUVOverlay进行绘制
SDL_VoutDisplayYUVOverlay(ffp->vout, vp->bmp);
......
}
SDL_VoutDisplayYUVOverlay
int SDL_VoutDisplayYUVOverlay(SDL_Vout *vout, SDL_VoutOverlay *overlay)
{
if (vout && overlay && vout->display_overlay)
return vout->display_overlay(vout, overlay);
return -1;
}
display_overlay函数指针在前面初始化流程有介绍过,它在SDL_Vout
SDL_VoutIos_CreateForGLES2()方法中被赋值为vout_display_overlay,该方法就是调用OpengGL绘制图像