static int ThreadDisplayPicture(vout_thread_t *vout, mtime_t *deadline) { bool frame_by_frame = !deadline; bool paused = vout->p->pause.is_on; //vout->p->displayed.current代表当前正在播放的帧(类比ffplay video_refresh函数中的lastvp) bool first = !vout->p->displayed.current; //调用ThreadDisplayPreparePicture准备一帧 if (first) if (ThreadDisplayPreparePicture(vout, true , frame_by_frame)) /* FIXME not sure it is ok */ return VLC_EGENERIC; //vout->p->displayed.next代表要播放的下一帧(类比ffplay video_refresh函数中的vp) if (!paused || frame_by_frame) while (!vout->p->displayed.next && !ThreadDisplayPreparePicture(vout, false , frame_by_frame)) ; const mtime_t date = mdate(); //这里计算render_delay来决定要提前多久显示 //这里补充说明一点,Google在MediaCodec.java中建议提前两个VSYNC时间显示,原因可以自行百度Android的VSYNC显示机制 //所以这里最好给render_delay再加33.3ms const mtime_t render_delay = vout_chrono_GetHigh(&vout->p->render) + VOUT_MWAIT_TOLERANCE /* + INT64_C(33333)*/ ; //这个drop_next_frame的名字起的很不恰当,我被这个变量名误导了很久,这里drop_next_frame代表的是丢弃当前播放的帧,显示下一帧 //这个逻辑也就保证了正常情况下,视频会按照帧率稳定地刷新显示 //如果是frame_by_frame步进模式,drop_next_frame恒为true bool drop_next_frame = frame_by_frame; mtime_t date_next = VLC_TS_INVALID; if (!paused && vout->p->displayed.next) { //计算下一帧需要显示的系统时间,如果当前时间已经到达,就将drop_next_frame置为true,丢弃当前帧 date_next = vout->p->displayed.next->date - render_delay; if (date_next /* + 0 FIXME */ <= date) drop_next_frame = true ; } /* FIXME/XXX we must redisplay the last decoded picture (because * of potential vout updated, or filters update or SPU update) * For now a high update period is needed but it could be removed * if and only if: * - vout module emits events from theselves. * - *and* SPU is modified to emit an event or a deadline when needed. * * So it will be done later. */ bool refresh = false ; //这里是强制刷新的逻辑,避免某一帧显示时间过长,如果某一帧显示时间超过(VOUT_REDISPLAY_DELAY-render_delay),就强制刷新 mtime_t date_refresh = VLC_TS_INVALID; if (vout->p->displayed.date > VLC_TS_INVALID) { date_refresh = vout->p->displayed.date + VOUT_REDISPLAY_DELAY - render_delay; refresh = date_refresh <= date; } bool force_refresh = !drop_next_frame && refresh; if (!frame_by_frame) { if (date_refresh != VLC_TS_INVALID) *deadline = date_refresh; if (date_next != VLC_TS_INVALID && date_next < *deadline) *deadline = date_next; } if (!first && !refresh && !drop_next_frame) { return VLC_EGENERIC; } //结合这里就能看懂drop_next_frame的含义了,其实是将当前帧释放掉了。。。 if (drop_next_frame) { picture_Release(vout->p->displayed.current); vout->p->displayed.current = vout->p->displayed.next; vout->p->displayed.next = NULL; } if (!vout->p->displayed.current) return VLC_EGENERIC; //调用ThreadDisplayRenderPicture函数显示Picture /* display the picture immediately */ bool is_forced = frame_by_frame || force_refresh || vout->p->displayed.current->b_force; int ret = ThreadDisplayRenderPicture(vout, is_forced); return force_refresh ? VLC_EGENERIC : ret; } |
发表评论 取消回复