I'm trying to make a python program that plays a video in sync with a light show done via Arduino. To achieve this I need to know what frame of the video is currently playing to send out data to the Arduino.
I modified the cocoavlc.py example of python-vlc to play a video, pause, resume and rewind it, go forward and backwards by 10 seconds.
To keep track of the frame number tried the method get_time of python-vlc but it only updates a few times a second. I had the idea to interpolate its data with a timer but it's not perfect.
Here's the code (without the timer):
import vlc from pycocoa import __name__ as _pycocoa_ from pycocoa import App, Item, ItemSeparator, MediaWindow, Menu, OpenPanel from os.path import isfile from threading import Thread from time import sleep import time _Movies = '.m4v', '.mov', '.mp4' _Select = 'select a video file from the panel' class AppVLC(App): player = None sized = None Toggle = None video = None window = None def __init__(self, video = None, title = 'AppVLC'): super(AppVLC, self).__init__(title = title) self.media = None self.Toggle = Item('Play', self.menuToggle_, key='p') self.video = video self.player = vlc.MediaPlayer() def appLaunched_(self, app): super(AppVLC, self).appLaunched_(app) self.window = MediaWindow(title=self.video or self.title) if self.player and self.video and isfile(self.video): # the VLC player on macOS needs an ObjC NSView self.media = self.player.set_mrl(self.video) self.player.set_nsobject(self.window.NSview) menu = Menu('VLC') menu.append( Item('Open...', key='o'), ItemSeparator(), self.Toggle, Item('Rewind', key='r'), Item('Forward', key='f'), ItemSeparator(), Item('Back', key='b')) ItemSeparator(), self.append(menu) self.menuPlay_(None) self.window.front() def menuOpen_(self, item): self.menuPause_(item) self.badge.label = 'O' v = OpenPanel(_Select).pick(_Movies) if v: self.window.title = self.video = v self.player.set_mrl(v) self.sized = None def menuPause_(self, item, pause = False): if pause or self.player.is_playing(): self.player.pause() self.badge.label = 'S' self.Toggle.title = 'Play' def menuPlay_(self, item_or_None): self.player.play() self._resizer() self.badge.label = 'P' self.Toggle.title = 'Pause' def menuRewind_(self, item): self.player.set_position(0.0) self.badge.label = 'R' self.sized = None def menuForward_(self, item): time = self.player.get_time() lenght = self.player.get_length() time = time + 10000 if time < lenght: self.player.set_time(time) self.sized = None def menuBack_(self, item): time = self.player.get_time() lenght = self.player.get_length() time = time - 10000 if time > 0: self.player.set_time(time) self.sized = None def menuToggle_(self, item): if self.player.is_playing(): self.menuPause_(item, pause=True) else: self.menuPlay_(item) def windowClose_(self, window): if window is self.window: self.terminate() super(AppVLC, self).windowClose_(window) def windowResize_(self, window): if window is self.window: self._resizer() super(AppVLC, self).windowResize_(window) def _resizer(self): if self.sized: self.window.ratio = self.sized else: Thread(target=self._sizer).start() def _sizer(self, secs=0.1): while True: w, h = self.player.video_get_size(0) if h > 0 and w > 0: self.window.ratio = self.sized = w, h break elif secs > 0.001: sleep(secs) else: break def get_frame(self): #? def trackframes(): frame = 0 while True: if frame != app.get_frame(): frame = app.get_frame() send(frame) #send data to the Arduino if __name__ == '__main__': _video = OpenPanel('Select a video file').pick(_Movies) app = AppVLC(video = _video) Thread(target = trackframes).start() app.run(timeout = None) 11 Answer
The only sensible way I have discovered to approximate the frame number in python vlc, is to take the frames per second player.get_fps() or 25, if not available and calculate the frame number based on the current playing time.
sec = player.get_time() / 1000 frame_no = int(round(sec * fps)) 5