Experimental VLC support

VLC now works, but it ain't perfect.
This commit is contained in:
Et0h 2013-01-17 20:43:44 +00:00
parent a778cfd60a
commit e03cfe12c3
4 changed files with 372 additions and 83 deletions

View File

@ -76,8 +76,7 @@ function detectchanges()
if newfilepath ~= oldfilepath then
oldfilepath = newfilepath
notificationbuffer = notificationbuffer .. "filepath-change"..msgseperator..tostring(newfilepath)..msgterminator
notificationbuffer = notificationbuffer .. "file-length-change"..msgseperator..get_var("length")..msgterminator
notificationbuffer = notificationbuffer .. "filepath-change"..notificationmarker..msgterminator
end
notificationbuffer = notificationbuffer .. "playstate"..msgseperator..tostring(get_play_state())..msgterminator
@ -90,7 +89,7 @@ function detectchanges()
if newinputstate ~= oldinputstate then
oldinputstate = newinputstate
notificationbuffer = "inputstate-change"..msgseperator..tostring(newinputstate)..msgterminator..notificationbuffer
notificationbuffer = notificationbuffer.."inputstate-change"..msgseperator..tostring(newinputstate)..msgterminator
end
return notificationbuffer
@ -166,7 +165,7 @@ function set_var(vartoset, varvalue)
return errormsg
end
h:listen( "localhost:"..port)
h:listen( "localhost:"..port)
-- h:listen( "*console" )
function get_play_state()
@ -194,6 +193,14 @@ function get_filepath ()
local item = vlc.input.item()
if item then
response = vlc.strings.decode_uri(item:uri())
if (string.sub(response, 1, 8) == "file:///") then
response = string.gsub(response, "file:///","")
response = string.gsub(response, "/","\\")
else
response = ""
errormsg = noinput
end
else
errormsg = noinput
end
@ -205,14 +212,34 @@ function get_filepath ()
end
function get_filename ()
local response
local index
local filename
filename = errormerge(get_filepath())
if(filename ~= nil) and (filename ~= "") and (filename ~= noinput) then
index = string.len(tostring(string.match(filename, ".*\\")))
if index then
response = string.sub(tostring(filename), index+1)
end
else
response = noinput
end
return response
end
function get_duration ()
local response
local errormsg
local item
local input = vlc.object.input()
if input then
local item = vlc.input.item()
if item then
response = item:name()
response = vlc.input.item():duration()
else
errormsg = noinput
end
@ -222,6 +249,7 @@ function get_filename ()
return response, errormsg
end
function display_osd ( argument )
local errormsg
@ -246,22 +274,21 @@ function do_command ( command, argument)
local command = tostring(command)
local argument = tostring(argument)
local errormsg = ""
local response = ""
local input = vlc.object.input()
local response = ""
if command == "get-interface-version" then response = "interface-version"..msgseperator..connectorversion..msgterminator
elseif command == "get-duration" then response = "duration"..msgseperator..errormerge(get_duration())..msgterminator
elseif command == "get-filepath" then response = "filepath"..msgseperator..errormerge(get_filepath())..msgterminator
elseif command == "get-filename" then response = "filename"..msgseperator..errormerge(get_filename())..msgterminator
elseif command == "set-position" then errormsg = set_var("time", tonumber(argument))
elseif command == "set-playstate" then errormsg = set_playstate(argument)
elseif command == "set-rate" then errormsg = set_var("rate", tonumber(argument))
elseif command == "display-osd" then errormsg = display_osd(argument)
elseif command == "close-vlc" then misc.quit()
elseif command == "close-vlc" then vlc.misc.quit()
else errormsg = unknowncommand
end
if (tostring(errormsg) ~= nil) and (errormsg ~= "") then
if (errormsg ~= nil) and (errormsg ~= "") then
response = command..errormarker..msgseperator..tostring(errormsg)..msgterminator
end
@ -269,6 +296,14 @@ function do_command ( command, argument)
end
function errormerge(argument, errormsg)
if (errormsg ~= nil) and (errormsg ~= "") then
do return errormsg end
end
return argument
end
function set_playstate(argument)
local errormsg
local input = vlc.object.input()
@ -302,9 +337,7 @@ while not vlc.misc.should_die() do
local responsebuffer
if not str then break end
local safestr = string.gsub(tostring(str), "\r", "")
if client.inputbuffer == nil then client.inputbuffer = "" end
client.inputbuffer = client.inputbuffer .. safestr

View File

@ -1,59 +1,60 @@
#You might want to change these
DEFAULT_PORT = 8999
OSD_DURATION = 3
OSD_WARNING_MESSAGE_DURATION = 15
MPC_OSD_POSITION = 2 #Right corner, 1 for left
MPLAYER_OSD_LEVEL = 1
UI_TIME_FORMAT = "[%X] "
DEFAULT_CONFIG_NAME = ".syncplay"
#Changing these might be ok
REWIND_THRESHOLD = 4
SEEK_THRESHOLD = 1
SLOWDOWN_RATE = 0.95
SLOWDOWN_KICKIN_THRESHOLD = 1.5
SLOWDOWN_RESET_THRESHOLD = 0.1
DIFFFERENT_DURATION_THRESHOLD = 1
PROTOCOL_TIMEOUT = 12.5
RECONNECT_RETRIES = 10
SERVER_STATE_INTERVAL = 1
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
#Usually there's no need to adjust these
COMMANDS_UNDO = ["u", "undo", "revert"]
COMMANDS_LIST = ["l", "list", "users"]
COMMANDS_PAUSE = ["p", "play", "pause"]
COMMANDS_ROOM = ["r", "room"]
COMMANDS_HELP = ['help', 'h', '?', '/?', '\?']
MPC_MIN_VER = "1.6.4"
MPC_PATHS = [
"C:\Program Files (x86)\MPC-HC\mpc-hc.exe",
"C:\Program Files\MPC-HC\mpc-hc.exe",
"C:\Program Files\MPC-HC\mpc-hc64.exe",
"C:\Program Files\Media Player Classic - Home Cinema\mpc-hc.exe",
"C:\Program Files\Media Player Classic - Home Cinema\mpc-hc64.exe",
"C:\Program Files (x86)\Media Player Classic - Home Cinema\mpc-hc.exe",
"C:\Program Files (x86)\K-Lite Codec Pack\Media Player Classic\mpc-hc.exe",
"C:\Program Files\K-Lite Codec Pack\Media Player Classic\mpc-hc.exe",
"C:\Program Files (x86)\Combined Community Codec Pack\MPC\mpc-hc.exe",
"C:\Program Files\MPC HomeCinema (x64)\mpc-hc64.exe",
]
MPLAYER_PATHS = ["mplayer2"]
MPV_PATHS = ["mpv", "/opt/mpv/mpv"]
#Changing these is usually not something you're looking for
PLAYER_ASK_DELAY = 0.1
PING_MOVING_AVERAGE_WEIGHT = 0.85
MPC_OPEN_MAX_WAIT_TIME = 10
MPC_LOCK_WAIT_TIME = 0.2
MPC_RETRY_WAIT_TIME = 0.01
MPC_MAX_RETRIES = 30
MPC_PAUSE_TOGGLE_DELAY = 0.05
#These are not changes you're looking for
MPLAYER_SLAVE_ARGS = [ '-slave', '-msglevel', 'all=1:global=4']
MPV_SLAVE_ARGS = [ '--slave-broken', '-msglevel', 'all=1:global=4']
MPLAYER_ANSWER_REGEX = "^ANS_([a-zA-Z_]+)=(.+)$"
UI_COMMAND_REGEX = r"^(?P<command>[^\ ]+)(?:\ (?P<parameter>.+))?"
UI_OFFSET_REGEX = r"^(?:o|offset)\ ?(?P<sign>[/+-])?(?P<time>\d{1,4}(?:[^\d\.](?:\d{1,6})){0,2}(?:\.(?:\d{1,3}))?)$"
UI_SEEK_REGEX = r"^(?:s|seek)?\ ?(?P<sign>[+-])?(?P<time>\d{1,4}(?:[^\d\.](?:\d{1,6})){0,2}(?:\.(?:\d{1,3}))?)$"
PARSE_TIME_REGEX = r'(:?(?:(?P<hours>\d+?)[^\d\.])?(?:(?P<minutes>\d+?))?[^\d\.])?(?P<seconds>\d+?)(?:\.(?P<miliseconds>\d+?))?$'
#You might want to change these
DEFAULT_PORT = 8999
OSD_DURATION = 3
OSD_WARNING_MESSAGE_DURATION = 15
MPC_OSD_POSITION = 2 #Right corner, 1 for left
MPLAYER_OSD_LEVEL = 1
UI_TIME_FORMAT = "[%X] "
DEFAULT_CONFIG_NAME = ".syncplay"
#Changing these might be ok
REWIND_THRESHOLD = 4
SEEK_THRESHOLD = 1
SLOWDOWN_RATE = 0.95
SLOWDOWN_KICKIN_THRESHOLD = 1.5
SLOWDOWN_RESET_THRESHOLD = 0.1
DIFFFERENT_DURATION_THRESHOLD = 1
PROTOCOL_TIMEOUT = 12.5
RECONNECT_RETRIES = 10
SERVER_STATE_INTERVAL = 1
WARNING_OSD_MESSAGES_LOOP_INTERVAL = 1
#Usually there's no need to adjust these
COMMANDS_UNDO = ["u", "undo", "revert"]
COMMANDS_LIST = ["l", "list", "users"]
COMMANDS_PAUSE = ["p", "play", "pause"]
COMMANDS_ROOM = ["r", "room"]
COMMANDS_HELP = ['help', 'h', '?', '/?', '\?']
MPC_MIN_VER = "1.6.4"
MPC_PATHS = [
"C:\Program Files (x86)\MPC-HC\mpc-hc.exe",
"C:\Program Files\MPC-HC\mpc-hc.exe",
"C:\Program Files\MPC-HC\mpc-hc64.exe",
"C:\Program Files\Media Player Classic - Home Cinema\mpc-hc.exe",
"C:\Program Files\Media Player Classic - Home Cinema\mpc-hc64.exe",
"C:\Program Files (x86)\Media Player Classic - Home Cinema\mpc-hc.exe",
"C:\Program Files (x86)\K-Lite Codec Pack\Media Player Classic\mpc-hc.exe",
"C:\Program Files\K-Lite Codec Pack\Media Player Classic\mpc-hc.exe",
"C:\Program Files (x86)\Combined Community Codec Pack\MPC\mpc-hc.exe",
"C:\Program Files\MPC HomeCinema (x64)\mpc-hc64.exe",
]
MPLAYER_PATHS = ["mplayer2"]
MPV_PATHS = ["mpv", "/opt/mpv/mpv"]
VLC_PATHS = ["C:\Program Files (x86)\VideoLAN\VLC\vlc.exe", "C:\Program Files\VideoLAN\VLC\vlc.exe"]
#Changing these is usually not something you're looking for
PLAYER_ASK_DELAY = 0.1
PING_MOVING_AVERAGE_WEIGHT = 0.85
MPC_OPEN_MAX_WAIT_TIME = 10
MPC_LOCK_WAIT_TIME = 0.2
MPC_RETRY_WAIT_TIME = 0.01
MPC_MAX_RETRIES = 30
MPC_PAUSE_TOGGLE_DELAY = 0.05
#These are not changes you're looking for
MPLAYER_SLAVE_ARGS = [ '-slave', '-msglevel', 'all=1:global=4']
MPV_SLAVE_ARGS = [ '--slave-broken', '-msglevel', 'all=1:global=4']
MPLAYER_ANSWER_REGEX = "^ANS_([a-zA-Z_]+)=(.+)$"
UI_COMMAND_REGEX = r"^(?P<command>[^\ ]+)(?:\ (?P<parameter>.+))?"
UI_OFFSET_REGEX = r"^(?:o|offset)\ ?(?P<sign>[/+-])?(?P<time>\d{1,4}(?:[^\d\.](?:\d{1,6})){0,2}(?:\.(?:\d{1,3}))?)$"
UI_SEEK_REGEX = r"^(?:s|seek)?\ ?(?P<sign>[+-])?(?P<time>\d{1,4}(?:[^\d\.](?:\d{1,6})){0,2}(?:\.(?:\d{1,3}))?)$"
PARSE_TIME_REGEX = r'(:?(?:(?P<hours>\d+?)[^\d\.])?(?:(?P<minutes>\d+?))?[^\d\.])?(?P<seconds>\d+?)(?:\.(?P<miliseconds>\d+?))?$'

View File

@ -1,10 +1,11 @@
from syncplay.players.mplayer import MplayerPlayer
from syncplay.players.mpv import MpvPlayer
try:
from syncplay.players.mpc import MPCHCAPIPlayer
except ImportError:
from syncplay.players.basePlayer import DummyPlayer
MPCHCAPIPlayer = DummyPlayer
def getAvailablePlayers():
return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer]
from syncplay.players.mplayer import MplayerPlayer
from syncplay.players.mpv import MpvPlayer
from syncplay.players.vlc import VlcPlayer
try:
from syncplay.players.mpc import MPCHCAPIPlayer
except ImportError:
from syncplay.players.basePlayer import DummyPlayer
MPCHCAPIPlayer = DummyPlayer
def getAvailablePlayers():
return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer, VlcPlayer]

254
syncplay/players/vlc.py Normal file
View File

@ -0,0 +1,254 @@
import subprocess
import re
import threading
from syncplay.players.basePlayer import BasePlayer
from syncplay import constants
from syncplay.messages import getMessage
import os
import random
import socket
import asynchat, asyncore
class VlcPlayer(BasePlayer):
speedSupported = True
RE_ANSWER = re.compile(r"(?:^(?P<command>[a-zA-Z_]+)(?:\: )?(?P<argument>.*))")
VLC_MIN_PORT = 10000
VLC_MAX_PORT = 65000
SLAVE_ARGS = ['--extraintf=luaintf','--lua-intf=syncplay','-vvv']
random.seed()
vlcport = random.randrange(VLC_MIN_PORT, VLC_MAX_PORT)
SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(vlcport)))
def __init__(self, client, playerPath, filePath, args):
self._client = client
self._paused = None
self._duration = None
self._filename = None
self._filepath = None
self._readyforchange = True
self._updatenotification = False
try:
self._listener = self.__Listener(self, playerPath, filePath, args)
except ValueError:
self._client.ui.showMessage("Failed to load VLC")
self._client.stop(True)
return
self._listener.setDaemon(True)
self._listener.start()
self._durationAsk = threading.Event()
self._filenameAsk = threading.Event()
self._pathAsk = threading.Event()
self._positionAsk = threading.Event()
self._pausedAsk = threading.Event()
self._vlcready = threading.Event()
self._vlcready.wait()
self._preparePlayer()
def _fileUpdateClearEvents(self):
self._durationAsk.clear()
self._filenameAsk.clear()
self._pathAsk.clear()
def _fileUpdateWaitEvents(self):
self._durationAsk.wait()
self._filenameAsk.wait()
self._pathAsk.wait()
def _onFileUpdate(self):
self._fileUpdateClearEvents()
self._getFilename()
self._getFilepath()
self._getLength()
self._fileUpdateWaitEvents()
self._client.updateFile(self._filename, self._duration, self._filepath)
def _preparePlayer(self):
self.setPaused(self._client.getGlobalPaused())
self.setPosition(self._client.getGlobalPosition())
self._client.initPlayer(self)
self._readyforchange = True
def askForStatus(self):
if (self._updatenotification):
self._updatenotification = False
self._onFileUpdate()
self._positionAsk.clear()
self._pausedAsk.clear()
self._listener.sendLine(".")
self._positionAsk.wait()
self._pausedAsk.wait()
self._client.updatePlayerStatus(self._paused, self._position)
def displayMessage(self, message, duration = constants.OSD_DURATION):
self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message))
def setSpeed(self, value):
self._setProperty('speed', "{:.2f}".format(value))
self._listener.sendLine("set-rate: {:.2f}".format(value))
def setPosition(self, value):
self._position = value
self._listener.sendLine("set-position: {}".format(value))
def setPaused(self, value):
self._paused = value
self._listener.sendLine('set-playstate: {}'.format("paused" if value else "playing"))
def _getFilename(self):
self._listener.sendLine("get-filename")
def _getLength(self):
self._listener.sendLine("get-duration")
def _getFilepath(self):
self._listener.sendLine("get-filepath")
def _getPaused(self):
self._listener.sendLine(".")
def _getPosition(self):
self._listener.sendLine(".")
def lineReceived(self, line):
#print "received: {}".format(line)
if (line[:16] == "VLC media player"):
self._vlcready.set()
return
elif(line == "filepath-change-notification"):
if (self._readyforchange):
self._updatenotification = True
return
match = self.RE_ANSWER.match(line)
if not match:
return
name, value = match.group('command'), match.group('argument')
if (name == "filepath"):
if (value != "no-input"):
self._filepath = value
self._pathAsk.set()
elif(name == "duration"):
if (value != "no-input"):
self._duration = float(value)
print line
self._durationAsk.set() #
elif(name == "playstate"):
if(value == "no-input"):
self._paused = self._client.getGlobalPaused()
else:
self._paused = bool(value != 'playing')
self._pausedAsk.set()
elif(name == "position"):
if (value == "no-input"):
self._position = self._client.getGlobalPosition()
else:
self._position = float(value)
self._positionAsk.set()
elif(name == "get-interface-version"):
print name
elif(name == "play-error"):
print name
elif(name == "set-playstate-error"):
print name
elif(name == "set-rate-error"):
print name
elif(name == "display-osd-error"):
print name
elif(name == "filename"):
self._filename = value
self._filenameAsk.set()
@staticmethod
def run(client, playerPath, filePath, args):
vlc = VlcPlayer(client, VlcPlayer.getExpandedPath(playerPath), filePath, args)
return vlc
@staticmethod
def getDefaultPlayerPathsList():
l = []
for path in constants.VLC_PATHS:
p = VlcPlayer.getExpandedPath(path)
if p:
l.append(p)
return l
@staticmethod
def isValidPlayerPath(path):
if("vlc" in path and VlcPlayer.getExpandedPath(path)):
return True
return False
@staticmethod
def getExpandedPath(playerPath):
if os.access(playerPath, os.X_OK):
return playerPath
for path in os.environ['PATH'].split(':'):
path = os.path.join(os.path.realpath(path), playerPath)
if os.access(path, os.X_OK):
return path
def drop(self):
self._listener.sendLine('close-vlc')
self._durationAsk.set()
self._filenameAsk.set()
self._pathAsk.set()
self._positionAsk.set()
self._vlcready.set()
self._pausedAsk.set()
self._client.stop(False)
class __Listener(threading.Thread, asynchat.async_chat):
def __init__(self, playerController, playerPath, filePath, args):
self.__playerController = playerController
call = [playerPath]
if(filePath):
call.append(filePath)
call.extend(playerController.SLAVE_ARGS)
if(args):
call.extend(args)
self.__process = subprocess.Popen(call)
threading.Thread.__init__(self, name="VLC Listener")
asynchat.async_chat.__init__(self)
self.set_terminator("\n")
self._ibuffer = []
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(('localhost', self.__playerController.vlcport))
def run(self):
asyncore.loop()
def collect_incoming_data(self, data):
self._ibuffer.append(data)
def found_terminator(self):
self.__playerController.lineReceived("".join(self._ibuffer))
self._ibuffer = []
def sendLine(self, line):
self.__playerController._vlcready.wait()
#print "send: {}".format(line)
self.push(line + "\n")