Pygame與OpenCV聯合播放視頻并保證音畫同步
Pygame是一個超好用的SDL綁定。自從有了Pygame,媽媽再也不用擔心我內存泄漏了。
但是這里有一個問題,Pygame的Movie模塊已經廢棄多年,這次做課題項目卻要在一個游戲中來段視頻播放。有點蒙圈。Ren'py提供的解決方案是使用libav,我嘗試了一早上也搞不明白pyav怎么用。后來干脆用手邊的OpenCV硬讀視頻吧。
這里說下第三方庫:
- pygame
- numpy
- opencv-python
其中,numpy是Anaconda自帶,我沒自己裝過不知道,但是其他兩個都是可以用pip直接安裝的。
其實Pygame使用opencv或者moviepy不少見,就是使用pygame的pygame.surfarray接口可以把numpy矩陣轉化為surface,而這些視頻讀取庫能把每幀轉為numpy矩陣。這部分在下面代碼中都會體現。
我測試的視頻是來自ドラッグオンドラグーン(龍背上的騎兵3)的一段音游部分。音樂就是用pygame的音頻模塊隨便播放下就好。但是注意,視頻幀率必須設置為跟你的視頻一致,我這里是25fps。建議稍微多設置一點,比如25.5幀是對應25幀最好的選擇。因為留有余地。
平時你的游戲基本不可能莫名其妙的在邏輯中出現視頻播放,所以盡量單獨播放視頻,防止幀數掉出25.當然現代電腦這種情況比較少。
上圖:
還是喜歡零姐?。。。?/p>
下面是基本的實現代碼
# -*- coding:utf-8 -*- import pygame import sys import cv2 import numpy as np import os if __name__ == '__main__': pygame.init() # 初始化pygame FPS = 25.5 #設置窗口位置 os.environ['SDL_VIDEO_WINDOW_POS']="%d,%d" % (50,70) FPSClock = pygame.time.Clock() size = width, height = 1280, 720 # 設置窗口大小 screen = pygame.display.set_mode(size) # 顯示窗口 pygame.display.set_caption(u'打字游戲:反應練習') color = (255, 255, 255) # 設置顏色 ogg=pygame.mixer.Sound("game.ogg") # pygame.mixer.music.load("") videoCapture = cv2.VideoCapture("test.mp4") ogg.play() while True: a=pygame.time.get_ticks() if videoCapture.isOpened(): #從opncv讀一段視頻進來 ret, frame = videoCapture.read() #下面這句可以獲取圖像大小 # print('frame.shape:', frame.shape) # # rot90(m, k=1, axes=(0, 1) # m是要旋轉的數組(矩陣), # k是旋轉的次數,默認旋轉1次, # 那是順時針還是逆時針呢? # 正數表示逆時針,而k為負數時則是對數組進行順時針方向的旋轉。 # 視頻結束時會丟出一個ValueError異常 try: frame = np.rot90(frame,k=-1) except: continue frame = pygame.surfarray.make_surface(frame) frame=pygame.transform.flip(frame,False,True) # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) screen.blit(frame, (0,0)) # print() for event in pygame.event.get(): # 遍歷所有事件 if event.type == pygame.QUIT: # 如果單擊關閉窗口,則退出 sys.exit() pygame.display.flip() # 更新全部顯示 time_next= FPSClock.tick(FPS) b=pygame.time.get_ticks() pygame.quit() # 退出pygame
好了。這樣就實現了pygame播放視頻~~~~
才怪呢。
因為手欠,拖動了以下窗口,然后發(fā)現,程序居然一直停留在拖動前的最后一幀,但我的音頻還是在播放,當我松開鼠標的時候,視頻圖像才重新開始顯示,這樣造成視音頻的同步上的問題,使鼠標松開后的視頻直接跟不上音頻。
尤其是這個部分是音游,一旦不同步,節(jié)奏對不上,感覺是要瘋。
這問題太難受了。
于是動手解決吧……
也不難,就是時間方面的問題。掌控時間,就是掌握了全局。
于是方案如下:
方案1:通過時間和跳幀。通過計算時間【就是時間差大于某個閾值,比如100ms】發(fā)現曾經發(fā)生過停止,然后進行跳幀。因為人類手速有限,所以完全可以閾值稍微大點,防止由于cpu方面的原因造成誤判。
方案2:重置音頻播放,計算視頻播放時間,讓音頻回到之前的狀態(tài)。這個簡單點,但是這不會太精確。需要仔細考慮使用哪個方案。
理論上跳幀方案比較好,因為幀量大,誤差小。并且音頻感官可以不缺失。
反復比較,選用方案一
算法如下:
設已有變量:
時間參量=下一幀-上一幀
跳幀計數器=0【在循環(huán)外】
閾值=200[時間參量大于此值說明出現故障]
跳幀系統(tǒng)檢測到閾值超標,使用以下算法跳幀:
使用 故障值/正確延時 ,得到要跳的幀數量。然后使計數器增長到這個數量,然后跳幀。
以下是帶有跳幀器的代碼
# -*- coding:utf-8 -*- import pygame import sys import cv2 import numpy as np import os # 音頻同步跳幀實驗 # 新增:跳幀系統(tǒng) if __name__ == '__main__': pygame.init() # 初始化pygame FPS = 25.5 # 設置窗口位置 os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (50, 70) FPSClock = pygame.time.Clock() size = width, height = 1280, 720 # 設置窗口大小 # screen = pygame.display.set_mode(size,pygame.FULLSCREEN) # 顯示窗口 screen = pygame.display.set_mode(size) # 顯示窗口 pygame.display.set_caption(u'打字游戲:反應練習') color = (255, 255, 255) # 設置顏色 ogg = pygame.mixer.Sound("game.ogg") # pygame.mixer.music.load("") videoCapture = cv2.VideoCapture("test.mp4") ogg.play() # 真正時間 realtime = int(1000 / FPS) # 閾值-要稍微留下空間 jumpyu = 100 # 跳幀計數器 jumpnum = 0 while True: a = pygame.time.get_ticks() if videoCapture.isOpened(): #這里是跳幀工具 if jumpnum != 0: #這個函數光讀東西,比read少一個解碼過程,所以能省點算點 videoCapture.grab() jumpnum -= 1 continue ret, frame = videoCapture.read() try: frame = np.rot90(frame, k=-1) except: continue frame = pygame.surfarray.make_surface(frame) frame = pygame.transform.flip(frame, False, True) # frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) screen.blit(frame, (0, 0)) # print() for event in pygame.event.get(): # 遍歷所有事件 if event.type == pygame.QUIT: # 如果單擊關閉窗口,則退出 sys.exit() pygame.display.flip() # 更新全部顯示 time_next = FPSClock.tick(FPS) b = pygame.time.get_ticks() k = b - a #此處檢測閾值,大于閾值做記錄準備跳幀 if k > jumpyu: print('超過閾值,跳幀') temp = (k - realtime)/realtime tempstr=str(temp) tempstr=tempstr[tempstr.find('.')+1:] if len(tempstr)>1 or tempstr[0]!='0': jumpnum+=(int(temp)+1) print('跳小數幀') else: print('跳整數幀') jumpnum+=int(temp) print('跳'+str(jumpnum)+'幀') pygame.quit() # 退出pygame
看看成果:為了截圖拖了幾下窗口,于是就已經智能跳幀了多次了。
搞定收工,這次將手賤拖動窗口造成的音畫同步問題降低到最小。至少這個視頻是沒怎么出現節(jié)奏問題了。當然,最好的建議還是別亂拖動窗口,畢竟再精確也無法做到幀級別的完全精確。就算是全屏與窗口切換也是會有幀損失的。emm…
到此這篇關于Pygame與OpenCV聯合播放視頻并保證音畫同步的文章就介紹到這了,更多相關Pygame OpenCV播放視頻內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Python在信息學競賽中的運用及Python的基本用法(詳解)
下面小編就為大家?guī)硪黄狿ython在信息學競賽中的運用及Python的基本用法(詳解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08python3中的logging記錄日志實現過程及封裝成類的操作
這篇文章主要介紹了python3中的logging記錄日志實現過程及封裝成類的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05