Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步
Pygame是一個(gè)超好用的SDL綁定。自從有了Pygame,媽媽再也不用擔(dān)心我內(nèi)存泄漏了。
但是這里有一個(gè)問題,Pygame的Movie模塊已經(jīng)廢棄多年,這次做課題項(xiàng)目卻要在一個(gè)游戲中來段視頻播放。有點(diǎn)蒙圈。Ren'py提供的解決方案是使用libav,我嘗試了一早上也搞不明白pyav怎么用。后來干脆用手邊的OpenCV硬讀視頻吧。
這里說下第三方庫:
- pygame
- numpy
- opencv-python
其中,numpy是Anaconda自帶,我沒自己裝過不知道,但是其他兩個(gè)都是可以用pip直接安裝的。
其實(shí)Pygame使用opencv或者moviepy不少見,就是使用pygame的pygame.surfarray接口可以把numpy矩陣轉(zhuǎn)化為surface,而這些視頻讀取庫能把每幀轉(zhuǎn)為numpy矩陣。這部分在下面代碼中都會(huì)體現(xiàn)。
我測(cè)試的視頻是來自ドラッグオンドラグーン(龍背上的騎兵3)的一段音游部分。音樂就是用pygame的音頻模塊隨便播放下就好。但是注意,視頻幀率必須設(shè)置為跟你的視頻一致,我這里是25fps。建議稍微多設(shè)置一點(diǎn),比如25.5幀是對(duì)應(yīng)25幀最好的選擇。因?yàn)榱粲杏嗟亍?br />
平時(shí)你的游戲基本不可能莫名其妙的在邏輯中出現(xiàn)視頻播放,所以盡量單獨(dú)播放視頻,防止幀數(shù)掉出25.當(dāng)然現(xiàn)代電腦這種情況比較少。
上圖:

還是喜歡零姐?。。?!
下面是基本的實(shí)現(xiàn)代碼
# -*- coding:utf-8 -*-
import pygame
import sys
import cv2
import numpy as np
import os
if __name__ == '__main__':
pygame.init() # 初始化pygame
FPS = 25.5
#設(shè)置窗口位置
os.environ['SDL_VIDEO_WINDOW_POS']="%d,%d" % (50,70)
FPSClock = pygame.time.Clock()
size = width, height = 1280, 720 # 設(shè)置窗口大小
screen = pygame.display.set_mode(size) # 顯示窗口
pygame.display.set_caption(u'打字游戲:反應(yīng)練習(xí)')
color = (255, 255, 255) # 設(shè)置顏色
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讀一段視頻進(jìn)來
ret, frame = videoCapture.read()
#下面這句可以獲取圖像大小
# print('frame.shape:', frame.shape)
#
# rot90(m, k=1, axes=(0, 1)
# m是要旋轉(zhuǎn)的數(shù)組(矩陣),
# k是旋轉(zhuǎn)的次數(shù),默認(rèn)旋轉(zhuǎn)1次,
# 那是順時(shí)針還是逆時(shí)針呢?
# 正數(shù)表示逆時(shí)針,而k為負(fù)數(shù)時(shí)則是對(duì)數(shù)組進(jìn)行順時(shí)針方向的旋轉(zhuǎn)。
# 視頻結(jié)束時(shí)會(huì)丟出一個(gè)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: # 如果單擊關(guān)閉窗口,則退出
sys.exit()
pygame.display.flip() # 更新全部顯示
time_next= FPSClock.tick(FPS)
b=pygame.time.get_ticks()
pygame.quit() # 退出pygame
好了。這樣就實(shí)現(xiàn)了pygame播放視頻~~~~
才怪呢。
因?yàn)槭智罚蟿?dòng)了以下窗口,然后發(fā)現(xiàn),程序居然一直停留在拖動(dòng)前的最后一幀,但我的音頻還是在播放,當(dāng)我松開鼠標(biāo)的時(shí)候,視頻圖像才重新開始顯示,這樣造成視音頻的同步上的問題,使鼠標(biāo)松開后的視頻直接跟不上音頻。
尤其是這個(gè)部分是音游,一旦不同步,節(jié)奏對(duì)不上,感覺是要瘋。
這問題太難受了。
于是動(dòng)手解決吧……
也不難,就是時(shí)間方面的問題。掌控時(shí)間,就是掌握了全局。
于是方案如下:
方案1:通過時(shí)間和跳幀。通過計(jì)算時(shí)間【就是時(shí)間差大于某個(gè)閾值,比如100ms】發(fā)現(xiàn)曾經(jīng)發(fā)生過停止,然后進(jìn)行跳幀。因?yàn)槿祟愂炙儆邢?,所以完全可以閾值稍微大點(diǎn),防止由于cpu方面的原因造成誤判。
方案2:重置音頻播放,計(jì)算視頻播放時(shí)間,讓音頻回到之前的狀態(tài)。這個(gè)簡(jiǎn)單點(diǎn),但是這不會(huì)太精確。需要仔細(xì)考慮使用哪個(gè)方案。
理論上跳幀方案比較好,因?yàn)閹看螅`差小。并且音頻感官可以不缺失。
反復(fù)比較,選用方案一
算法如下:
設(shè)已有變量:
時(shí)間參量=下一幀-上一幀
跳幀計(jì)數(shù)器=0【在循環(huán)外】
閾值=200[時(shí)間參量大于此值說明出現(xiàn)故障]
跳幀系統(tǒng)檢測(cè)到閾值超標(biāo),使用以下算法跳幀:
使用 故障值/正確延時(shí) ,得到要跳的幀數(shù)量。然后使計(jì)數(shù)器增長(zhǎng)到這個(gè)數(shù)量,然后跳幀。
以下是帶有跳幀器的代碼
# -*- coding:utf-8 -*-
import pygame
import sys
import cv2
import numpy as np
import os
# 音頻同步跳幀實(shí)驗(yàn)
# 新增:跳幀系統(tǒng)
if __name__ == '__main__':
pygame.init() # 初始化pygame
FPS = 25.5
# 設(shè)置窗口位置
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (50, 70)
FPSClock = pygame.time.Clock()
size = width, height = 1280, 720 # 設(shè)置窗口大小
# screen = pygame.display.set_mode(size,pygame.FULLSCREEN) # 顯示窗口
screen = pygame.display.set_mode(size) # 顯示窗口
pygame.display.set_caption(u'打字游戲:反應(yīng)練習(xí)')
color = (255, 255, 255) # 設(shè)置顏色
ogg = pygame.mixer.Sound("game.ogg")
# pygame.mixer.music.load("")
videoCapture = cv2.VideoCapture("test.mp4")
ogg.play()
# 真正時(shí)間
realtime = int(1000 / FPS)
# 閾值-要稍微留下空間
jumpyu = 100
# 跳幀計(jì)數(shù)器
jumpnum = 0
while True:
a = pygame.time.get_ticks()
if videoCapture.isOpened():
#這里是跳幀工具
if jumpnum != 0:
#這個(gè)函數(shù)光讀東西,比read少一個(gè)解碼過程,所以能省點(diǎn)算點(diǎn)
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: # 如果單擊關(guān)閉窗口,則退出
sys.exit()
pygame.display.flip() # 更新全部顯示
time_next = FPSClock.tick(FPS)
b = pygame.time.get_ticks()
k = b - a
#此處檢測(cè)閾值,大于閾值做記錄準(zhǔn)備跳幀
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('跳小數(shù)幀')
else:
print('跳整數(shù)幀')
jumpnum+=int(temp)
print('跳'+str(jumpnum)+'幀')
pygame.quit() # 退出pygame
看看成果:為了截圖拖了幾下窗口,于是就已經(jīng)智能跳幀了多次了。

搞定收工,這次將手賤拖動(dòng)窗口造成的音畫同步問題降低到最小。至少這個(gè)視頻是沒怎么出現(xiàn)節(jié)奏問題了。當(dāng)然,最好的建議還是別亂拖動(dòng)窗口,畢竟再精確也無法做到幀級(jí)別的完全精確。就算是全屏與窗口切換也是會(huì)有幀損失的。emm…
到此這篇關(guān)于Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步的文章就介紹到這了,更多相關(guān)Pygame OpenCV播放視頻內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python在信息學(xué)競(jìng)賽中的運(yùn)用及Python的基本用法(詳解)
下面小編就為大家?guī)硪黄狿ython在信息學(xué)競(jìng)賽中的運(yùn)用及Python的基本用法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08
python實(shí)現(xiàn)某考試系統(tǒng)生成word試卷
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)某考試系統(tǒng)生成word試卷,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05
python使用gdal對(duì)shp讀取,新建和更新的實(shí)例
這篇文章主要介紹了python使用gdal對(duì)shp讀取,新建和更新的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-03-03
python topk()函數(shù)求最大和最小值實(shí)例
這篇文章主要介紹了python topk()函數(shù)求最大和最小值實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-04-04
淺談四種快速易用的Python數(shù)據(jù)可視化方法
這篇文章主要介紹了淺談四種快速易用的Python數(shù)據(jù)可視化方法,數(shù)據(jù)可視化,是指用圖形的方式來展現(xiàn)數(shù)據(jù),從而更加清晰有效地傳遞信息,主要方法包括圖表類型的選擇和圖表設(shè)計(jì)的準(zhǔn)則,需要的朋友可以參考下2023-04-04
Pipenv輕量級(jí)虛擬環(huán)境管理工具使用指南
這篇文章主要為大家介紹了Pipenv輕量級(jí)虛擬環(huán)境管理工具使用指南,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
Tensorflow實(shí)現(xiàn)酸奶銷量預(yù)測(cè)分析
這篇文章主要為大家詳細(xì)介紹了Tensorflow酸奶銷量預(yù)測(cè)分析,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07
python3中的logging記錄日志實(shí)現(xiàn)過程及封裝成類的操作
這篇文章主要介紹了python3中的logging記錄日志實(shí)現(xiàn)過程及封裝成類的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05

