Get Current Frame Number of Video Playing with Python-Vlc
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...
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)
1 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))