VAR (Video Assistant Referee : 비디오 판독 시스템) 구현 파이선 코드 #1

반응형

제가 스포츠 중계를 자주 하다보니
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()
반응형