python - 使用 OpenCV 和 ffpyplayer 进行音视频同步
问题描述
我目前正在开发一个“游戏”,它允许用户在观看视频时移动圆圈的位置。视频显示了两个人,每个人轮流发言。用户的任务是改变出现在当前演讲者面前的圆圈的位置。虽然发生这种情况,但在某些时候,我计划在“游戏”和圆圈继续显示时在用户不注意的情况下更改视频。
为了达到这个目的,我写了下面的代码。该代码从用户那里获取输入,并将所有数据发送到 TCP 服务器并将信息打印到记录器文件中。但我遇到了一个问题。首先,音频和视频不同步,即使使用最低的值waitkey(1)
,音频也比视频快
任何有关如何解决此问题的帮助将不胜感激。提前致谢。
PS-我正在使用 Visual Studio 代码,我的 python 版本是 3.9.6 64 位。
import cv2 as cv #import the OpenCV library
import numpy as np #Import Numpy Library
import socket # socket creation for Telnet
from datetime import datetime
from telnetlib import Telnet #telnet client
from ffpyplayer.player import MediaPlayer #ffpyplayer for playing audio
current_date_time = datetime.now().strftime('%Y-%m-%d-%H:%M:%S')
HOST = '127.0.0.1' # The remote host
PORT = 4212 # The same port as used by the server
TCP_PORT = 9999 # port used to connect with the server file
def send_data(message, s, output):
s.sendall(message.encode())
data = s.recv(1024)
output.write('\n'+ current_date_time+' '+message+ '\n')
return data
def circle(frame, left):
if left:
cv.circle(frame,(450,250),20,(255,255,255),50)
if not left:
cv.circle(frame,(1400,250),20,(255,255,255),50)
def video():
cap1 = cv.VideoCapture('P1.mp4') # the video that we want
player = MediaPlayer('P1.mp4')
circle_is_left = True
if (cap1.isOpened()== False):
print("Error opening video 1")
while (cap1.isOpened()):
ret,frame = cap1.read() #capture frame-by-frame video
audio_frame,val=player.get_frame() # capture frame-by-frame audio
if ret== True:
key_pressed = cv.waitKey(1)
if key_pressed == ord(' '): #pressing space bar ends the video
with open('out.txt', 'a') as output:
send_data('video 1 is changed',s,output)
break
elif key_pressed == 2: #left key pressed changes circle to lett
circle_is_left = True
with open('out.txt', 'a') as output:
send_data('left',s,output)
elif key_pressed == 3: # right key pressed changes circle to right
circle_is_left = False
with open('out.txt', 'a') as output:
send_data('Right ',s,output)
circle(frame, circle_is_left) #display the circle at all times
cv.imshow('cap1',frame) #display resulting frame
if val != 'eof' and audio_frame is not None:
img,t = audio_frame
cap1.release()
cv.destroyAllWindows()
cap2 = cv.VideoCapture('P2.mov') # the video that we want
player2 = MediaPlayer('P2.mov')
circle_is_left = True
if (cap2.isOpened()== False):
print("Error opening video 2")
while (cap2.isOpened()):
ret,frame = cap2.read() #capture frame-by-frame video
audio_frame,val=player2.get_frame() # capture frame-by-frame audio
if ret== True:
key_pressed = cv.waitKey(1)
if key_pressed == ord(' '): #pressing space bar ends the video
with open('out.txt', 'a') as output:
send_data('video 1 is changed',s,output)
break
elif key_pressed == 2: #left key pressed changes circle to lett
circle_is_left = True
with open('out.txt', 'a') as output:
send_data('left',s,output)
elif key_pressed == 3: # right key pressed changes circle to right
circle_is_left = False
with open('out.txt', 'a') as output:
send_data('Right ',s,output)
circle(frame, circle_is_left) #display the circle at all times
cv.imshow('cap2',frame) #display resulting frame
if val != 'eof' and audio_frame is not None:
img,t = audio_frame
cap2.release()
cv.destroyAllWindows()
def main():
print("Game1.py is connected to TCP server")
video()
if __name__=='__main__':
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, TCP_PORT))
main()
解决方案
您可以使用cv2.CAP_PROP_POS_MSEC
以下方式同步视频和音频:
import cv2
import time
from ffpyplayer.player import MediaPlayer
def video(file):
cap = cv2.VideoCapture(file)
player = MediaPlayer(file)
start_time = time.time()
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
_, val = player.get_frame(show=False)
if val == 'eof':
break
cv2.imshow(file, frame)
elapsed = (time.time() - start_time) * 1000 # msec
play_time = int(cap.get(cv2.CAP_PROP_POS_MSEC))
sleep = max(1, int(play_time - elapsed))
if cv2.waitKey(sleep) & 0xFF == ord("q"):
break
player.close_player()
cap.release()
cv2.destroyAllWindows()
推荐阅读
- sql - SQL Partition by with 条件
- java - JAX-RS LoggingFilter - 记录请求实体
- php - Infyom laravel 生成器不显示数据表数据
- regex - 正则表达式 - 如何在此正则表达式中包含特殊字符
- swift - 我得到了缺少目的字符串,但我在 info.plist 中添加了缺少的目的字符串是“NSNearbyInteractionAllowOnceUsageDescription”
- python - 运行“pyw”文件时如何隐藏.exe文件的控制台?
- javascript - 使用 jquery 更改图像的行
- powershell - 无法使用其他 AD 帐户启动进程?
- git - 我可以从 git pull -f 恢复未提交的代码吗?
- amazon-web-services - Elastic beanstalk 上的部署因 cloudformation 语法错误而失败