I am trying to write a tiny video editor in python. All works just fine, however, the function that establishes an end mark sometimes returns a time which is two or three tenths of a second earlier than the desired point. Note that earlier there. I would understand a delay due to human reaction time. It is a negative latency though. What makes it weirder is that the problem occurs only with the end marks and not with the start marks. Here is the 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):
def __init__(self, parent, title, video_path):
super().__init__(parent, title=title, size=(800, 600))
self.video_path = video_path
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()
if keycode == wx.WXK_SPACE:
self.toggle_play_pause()
elif keycode == wx.WXK_RETURN:
self.cut_clip()
elif keycode == ord('S'):
self.set_start_mark()
elif keycode == ord('E'):
self.set_end_mark()
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 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 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)