반응형
제가 스포츠 중계를 자주 하다보니
VAR 시스템이 필요하지만, 상용시스템을 구축하는 것은 방송국이나 대규모 스포츠 대회가 아니면 개인이 할 수 있는 수준이 아니죠.
제가 최근에 참가하고 있는 인라인하키대회의 연맹으로 부터
사실 프로야구도 그렇지만, VAR 은 중계방송과는 무관하게 심판들로 하여금 진행되고 있습니다.
따라서 유튜브 중계와는 별도로 분리된 VAR 시스템이 필요하며
WireCast 를 통하여, 전채널 ISO 녹화후
필요시 녹화중인 ISO 녹화를 중단/저장하여 지금까지 녹화중인 파일들에 EOF (End of File) 을 생성해야
로컬에서 플레이 가능하다.
아래 프로그램은 파이선으로 구축한 간단한 VAR 코드이며
가장 마지막에 생성된 파일 1개를 자동로드하여
간단한 키 스트록으로 원하는 장면을 찾아가는 기능이다.
프로그램을 더 간단하게 코딩하기 위해, VLC 플로그인을 활용하였습니다.
import vlc
import tkinter as tk
import os
import glob
class VideoPlayer:
def __init__(self, root):
self.root = root
self.root.title("VLC 전체화면 비디오 플레이어")
self.root.attributes('-fullscreen', True)
self.instance = vlc.Instance()
self.player = self.instance.media_player_new()
self.canvas = tk.Canvas(root, bg='black')
self.canvas.pack(fill=tk.BOTH, expand=True)
self.media = None
self.is_paused = False
self.speed_multiplier = 1.0
# 키 바인딩
self.root.bind("<space>", self.space_key)
self.root.bind("<Escape>", self.exit_fullscreen)
self.root.bind("<Left>", self.frame_backward)
self.root.bind("<Right>", self.frame_forward)
self.root.bind("l", self.speed_up) # 오른쪽 배속
self.root.bind("L", self.speed_up)
self.root.bind("k", self.slow_down) # 왼쪽 배속
self.root.bind("K", self.slow_down)
self.root.bind("b", self.seek_2min_before_end) # EOF - 2분
self.load_latest_mp4()
def load_latest_mp4(self):
video_dir = os.path.join(os.path.expanduser("~"), "Movies")
mp4_files = sorted(
glob.glob(os.path.join(video_dir, "*.mp4")),
key=os.path.getmtime,
reverse=True
)
for file in mp4_files:
if os.path.getsize(file) > 1024 * 1024: # 1MB 이상만
try:
with open(file, "rb") as f:
f.seek(-128, os.SEEK_END)
if f.read():
self.open_file(file)
return
except:
continue
print("MP4 파일을 찾을 수 없거나 완료되지 않은 파일입니다.")
def open_file(self, filepath):
self.media = self.instance.media_new(filepath)
self.player.set_media(self.media)
if hasattr(self.player, 'set_nsobject'):
self.player.set_nsobject(self.canvas.winfo_id())
self.play()
def play(self):
self.player.play()
self.is_paused = False
self.speed_multiplier = 1.0
self.player.set_rate(self.speed_multiplier)
def play_pause(self):
if self.player.is_playing():
self.player.pause()
self.is_paused = True
else:
self.player.play()
self.is_paused = False
self.player.set_rate(self.speed_multiplier)
def space_key(self, event=None):
self.play_pause()
def exit_fullscreen(self, event=None):
self.root.attributes('-fullscreen', False)
def frame_forward(self, event=None):
self.player.next_frame()
def frame_backward(self, event=None):
current_time = self.player.get_time()
self.player.set_time(max(current_time - 40, 0))
def speed_up(self, event=None):
if self.speed_multiplier < 32:
self.speed_multiplier *= 2
self.player.set_rate(self.speed_multiplier)
print(f"▶ 전진 배속: {self.speed_multiplier}x")
def slow_down(self, event=None):
if self.speed_multiplier < 32:
self.speed_multiplier *= 2
self.player.set_rate(-self.speed_multiplier)
print(f"◀ 후진 배속: {-self.speed_multiplier}x")
def seek_2min_before_end(self, event=None):
duration = self.player.get_length()
if duration > 0 and duration > 120000:
target_time = duration - 120000
self.player.set_time(target_time)
print(f"📍 2분 전으로 이동: {target_time // 1000}초")
else:
print("🎥 영상 길이가 너무 짧아 이동 불가")
if __name__ == "__main__":
root = tk.Tk()
player = VideoPlayer(root)
root.mainloop()
반응형