oaubert / python-vlc

Python vlc bindings
GNU Lesser General Public License v2.1
381 stars 108 forks source link

The function get_time() fails to provide an acurrate time #269

Open alcor717 opened 8 months ago

alcor717 commented 8 months ago

Hello, I am trying to write a tiny video editor. It basically allows you to set start and end marks and press enter to cut clips. The problem is that the end marks (and it's odd because it doesn't happen with the start marks) are set a few tenths of a second before the desired moment. It's strange because I would understand a delay due to human reaction time, but instead, there is an advance. Here is the relevant code: from moviepy.video.io.VideoFileClip import VideoFileClip import vlc import wx import sys

def ms_to_hms(ms): """Convert milliseconds to h:mm:ss.xxx""" seconds, ms = divmod(ms, 1000) minutes, seconds = divmod(seconds, 60) hours, minutes = divmod(minutes, 60) return f"{hours}:{minutes:02d}:{seconds:02d}.{ms:03d}"

class VideoFrame(wx.Frame): TIME_UNITS = [10, 100, 1000, 5000, 60000, 300000] # Centiseconds, Deciseconds, Seconds, 5 Seconds, Minutes, 5 Minutes

def __init__(self, parent, title, video_path):
    super().__init__(parent, title=title, size=(800, 600))
    self.video_path = video_path
    self.time_unit_index = 4  # Minutes by default
    self.init_ui()
    self.Show(True)

def init_ui(self):
    self.panel = wx.Panel(self)
    self.Instance = vlc.Instance()
    self.player = self.Instance.media_list_player_new()
    Media = self.Instance.media_new(self.video_path)
    media_list = self.Instance.media_list_new([Media])
    self.player.set_media_list(media_list)
    self.player.get_media_player().set_hwnd(self.panel.GetHandle())
    self.start_mark = None
    self.end_mark = None
    self.panel.Bind(wx.EVT_CHAR_HOOK, self.on_key_press)

def on_key_press(self, event):
    keycode = event.GetKeyCode()
    control_down = event.ControlDown()
    shift_down = event.ShiftDown()

    if keycode == wx.WXK_SPACE:
        self.toggle_play_pause()
    elif keycode == wx.WXK_RETURN:
        self.cut_clip()
    elif keycode == ord('S'):
        if shift_down:
            self.clear_start_mark()
        else:
            self.set_start_mark()
    elif keycode == ord('E'):
        if shift_down:
            self.clear_end_mark()
        else:
            self.set_end_mark()
    elif keycode in [wx.WXK_RIGHT, wx.WXK_LEFT]:
        offset = self.TIME_UNITS[self.time_unit_index] * (1 if keycode == wx.WXK_RIGHT else -1)
        if shift_down:
            self.adjust_mark("start", offset)
        elif control_down:
            self.adjust_mark("end", offset)

def toggle_play_pause(self):
    if self.player.is_playing():
        self.player.pause()
    else:
        self.player.play()

def set_start_mark(self):
    self.start_mark = self.player.get_media_player().get_time()
    print(f"Start mark set at: {ms_to_hms(self.start_mark)}")

def clear_start_mark(self):
    self.start_mark = None
    print("Start mark cleared")

def set_end_mark(self):
    self.end_mark = self.player.get_media_player().get_time()
    print(f"End mark set at: {ms_to_hms(self.end_mark)}")

def clear_end_mark(self):
    self.end_mark = None
    print("End mark cleared")

def adjust_mark(self, mark_type, offset):
    new_mark = None
    if mark_type == "start" and self.start_mark is not None:
        new_mark = max(0, min(self.start_mark + offset, self.player.get_media_player().get_length()))
        self.start_mark = new_mark
    elif mark_type == "end" and self.end_mark is not None:
        new_mark = max(0, min(self.end_mark + offset, self.player.get_media_player().get_length()))
        self.end_mark = new_mark

    if new_mark is not None:
        print(f"{mark_type.capitalize()} mark adjusted to: {ms_to_hms(new_mark)}")

def cut_clip(self):
    if self.start_mark is not None and self.end_mark is not None:
        clip = VideoFileClip(self.video_path).subclip(self.start_mark/1000, self.end_mark/1000)
        output_path = "cut_clip.mp4"
        clip.write_videofile(output_path)
        print(f"Clip cut: Start - {ms_to_hms(self.start_mark)}, End - {ms_to_hms(self.end_mark)}")

def main(video_path): app = wx.App(False) frame = VideoFrame(None, "Video Player", video_path) app.MainLoop()

if name == "main": video_path = sys.argv[1] main(video_path)

mrJean1 commented 8 months ago

The results from methods player.get_time, player.get_position, player.get_length and media.get_duration are not reliable until the video has been playing for several seconds. Examples like cocoavlc.py are affected by that same issue.