我们接下来的讨论,会基於读者已经先读过我 day5 文章 的架构下去进行程序设计
如果还不清楚我程序设计的逻辑 (UI.py、controller.py、start.py 分别在干麻)
建议先阅读 day5 文章後再来阅读此文。
https://www.wongwonggoods.com/python/pyqt5-5/
主要就是新增滑条的部分,新元素的名称:
pyuic5 -x day26.ui -o UI.py
一样,这程序只有介面 (视觉上的呈现),没有任何互动功能
python UI.py
我们已经在 【PyQt5】Day 14 – 使用 QSlider 制作可拖曳的滑条 有详细的教学 QSlider 该如何使用了,
这边我们就直接使用吧!
def init_video_info(self):
self.ui.slider_videoframe.setRange(0, self.video_total_frame_count-1)
self.ui.slider_videoframe.valueChanged.connect(self.getslidervalue)
def __get_frame_from_frame_no(self, frame_no):
self.setslidervalue(frame_no)
def getslidervalue(self):
self.current_frame_no = self.ui.slider_videoframe.value()
def setslidervalue(self, value):
self.ui.slider_videoframe.setValue(self.current_frame_no)
我们在 init_video_info() 新增了关於滑条初始化的功能,
我们设定好这个滑条的 range 为 (0, 全部 frame 数 -1),
并且将这个滑条连结於 getslidervalue() 的功能上,
只要我们移动滑条,就会启动这个函数。
我们制作了一个函数 setslidervalue(),当我们更改 frame 的时候,
我们可以直接也更改滑条的值。
而呼叫这个 setslidervalue() 的 function 位於取得 frame from frame number 的时候,
也同步呼叫这个函数,就可以完成「随着 frame 变化更改滑条的值
」。
昨天我们提到我们程序执行的时候会有 lag 的问题,
那时我是直接给个优化的方向,是我们可以考虑提程序的 decode 加入「multiprocessing」的平行运算功能。
不过今天处理的过程中,我稍微替我的程序加了几个计时器,
後来意外发现卡住的 function 其实只有一个,这样就好处理了!
以这支程序来说,很直觉的我会认为会慢都是牵扯到 decode 那一段的速度,
因为处理图片基本上就是最花时间的地方...
因此我加了一些 timer 在昨天的 code
def __get_frame_from_frame_no(self, frame_no):
time_start = time.time()
self.vc.set(1, frame_no)
ret, frame = self.vc.read()
time_end = time.time()
print(time_end - time_start)
我们来计时一下,这段处理到底花了多少时间。
结果发现了一个很有趣的现象:
这就很奇怪了!!!
照理来说处理一个图片,应该也不会到有那麽大的误差。
而且是平均时间,还不是几张图片或许资讯比较丰富所以处理比较久。
这表示我们设计的机制一定有什麽可优化的问题。
最後我们发现一件有趣的事情:
原本我以为是处理处片的时间很久,结果只花了 0.001 秒
ret, frame = self.vc.read()
然而却是以下这行,设定人在哪个 frame 的函数,可能会造成约 0.05 秒左右的延迟。
self.vc.set(1, frame_no)
但是,我之前做过的专案经验告诉我,正常来说的解码不会那麽久,
所以一定是我不够正确的使用这一行。
所以我决定修改机制。
照官方文件的定义,即使没有 vc.set(),只需要一直 vc.read() 也能够一直往下取 frame,
我猜可能这就是原因了,因为 OpenCV (或说是他使用的 ffmpeg library) 在 decode 的时候,
针对连续的 frame 有做优化的演化法,
「所以如果我每次都重新设定第几个 frame,会导致这个优化演算法失效
」
可以想像是,因为我们的影片都是连续的,
所以搞不好可以透过计算向量差的方式,更快的算出下一张图片。(而这机制被我的设计弄到失效)
於是我们更改一下原本的逻辑,「只要必须要设定 frame 时,才使用 vc.set()」
我们把 self.vc.set(1, frame_no) 这个会造成 bottleneck 的 funciton 独立出来。
def set_current_frame_no(self, frame_no):
self.vc.set(1, frame_no) # bottleneck
def __get_next_frame(self):
ret, frame = self.vc.read()
self.ui.label_framecnt.setText(f"frame number: {self.current_frame_no}/{self.video_total_frame_count}")
self.setslidervalue(self.current_frame_no)
return frame
def timer_timeout_job(self):
if (self.videoplayer_state == "play"):
if self.current_frame_no >= self.video_total_frame_count-1:
#self.videoplayer_state = "pause"
self.current_frame_no = 0 # auto replay
self.set_current_frame_no(self.current_frame_no)
else:
self.current_frame_no += 1
if (self.videoplayer_state == "stop"):
self.current_frame_no = 0
self.set_current_frame_no(self.current_frame_no)
if (self.videoplayer_state == "pause"):
self.current_frame_no = self.current_frame_no
self.set_current_frame_no(self.current_frame_no)
frame = self.__get_next_frame()
self.__update_label_frame(frame)
原本会更新画面的函数位置不变,而我们在 pause、stop、与影片播放完毕後,
都启用 set_current_frame_no() 这个函数,才会去启动 vc.set() 修改 frame index。
def getslidervalue(self):
self.current_frame_no = self.ui.slider_videoframe.value()
self.set_current_frame_no(self.current_frame_no)
另外一个也会影响到 frame index 的就是滑条,
我们也是在滑条「被移动」的时候,才会去呼叫 set_current_frame_no() 启动 vc.set()
我的影片播放器终於顺畅了!!! 耶!!!
此外昨天保留的计算 fps 机制就可以拿回来用了。
如果不想要让影片已超快的 1ms 更新,可以改回上面 timer 的做法。
self.timer.start(1000//self.video_fps) # start Timer, here we set '1000ms//Nfps' while timeout one time
self.timer.start(1) # but if CPU can not decode as fast as fps, we set 1 (need decode time)
★ 本文也同步发於我的个人网站(会有内容目录与显示各个小节,阅读起来更流畅):【PyQt5】Day 26 project / 替我们影片播放器增加一个显示进度的滑条 video player add slider (与昨日 bottleneck 处理细节)
>>: 追求JS小姊姊系列 Day26 -- 不是被已读,而是JS回覆你却没看到:`console`
不怎麽重要的前言 上一篇介绍了两个小题目,稍微带过解题的思路,以及多重回圈(巢状回圈)的概念。 现在...
今天开始将进行Python基本语法练习,因大部分语法跟很多程序语言相似,故这个部分将主要以笔记方式注...
今天大概会聊到的范围 @Composable compose compiler & run...
上一篇在 TwMarketTradingInfoManager 完成了拿取大盘成交量的 API,接下...
前言 昨天已经将TableView给建立完毕了,今天来跟大家聊聊TableViewCell的建立方法...