kivyblocks/kivyblocks/vplayer.py
2020-08-08 16:05:01 +08:00

471 lines
12 KiB
Python

import os
import sys
import time
import threading
from appPublic.sockPackage import get_free_local_addr
from kivy.utils import platform
from traceback import print_exc
from kivy.core.window import Window
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.video import Video
from kivy.uix.slider import Slider
from kivy.uix.popup import Popup
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.filechooser import FileChooserListView
from kivy.uix.label import Label
from kivy.app import App
from kivy.clock import Clock
from kivy.logger import Logger
from kivy.properties import ObjectProperty, StringProperty, BooleanProperty, \
NumericProperty, DictProperty, OptionProperty
from pythonosc import dispatcher, osc_server
from ffpyplayer.tools import set_log_callback
from .utils import *
from .paging import PageLoader
from .baseWidget import PressableImage
from .swipebehavior import SwipeBehavior
desktopOSs=[
"win",
"linux",
"macosx"
]
logger_func = {'quiet': Logger.critical, 'panic': Logger.critical,
'fatal': Logger.critical, 'error': Logger.error,
'warning': Logger.warning, 'info': Logger.info,
'verbose': Logger.debug, 'debug': Logger.debug}
othersplatforms=['ios','android']
class BaseVPlayer(FloatLayout):
fullscreen = BooleanProperty(False)
def __init__(self,vfile=None,loop=None,mute=False):
FloatLayout.__init__(self)
self.loop = loop
self.muteFlg = mute
self.register_event_type('on_source_error')
self.register_event_type('on_next')
self.register_event_type('on_previous')
Window.allow_screensaver = False
self._video = Video(allow_stretch=True)
self.add_widget(self._video)
self.nextdir = None
self.playlist = []
self.curplay = 0
self.old_volume = 0
self._video.bind(state=self.on_state)
# self._video.bind(loaded=self.playVideo) no effect
self._video.bind(position=self.positionChanged)
if loop:
self.eos = 'loop'
# self.bind(on_swipe_down=self.previous)
# self.bind(on_swipe_up=self.next)
set_log_callback(self.ffplayerLog)
if hasattr(self._video._video, '_ffplayer'):
self.ffplayer = self._video._video._ffplayer
if vfile is not None:
self.setSource(vfile)
def on_size(self,*args):
self._video.size = self.size
print('****on_size()****',self.pos,self.size,self._video.pos,self._video.size)
def positionChanged(self,o,v):
if self.muteFlg:
self._video.volume = 0
def playVideo(self,o=None,v=None):
# print('-------------VPlayer():playVideo()')
self._video.state = 'play'
self.nextdir = None
def setSource(self,s):
self.stop()
self.curplay = 0
self.playlist = [s]
self._video.source = self.playlist[self.curplay]
Logger.info('kivyblocks: Vplayer().setSource,s=%s',s)
self.playVideo()
def on_source_error(self,o,v):
Logger.info('safecorner: {} error'.format(v))
def on_next(self,o=None, v=None):
pass
def on_previous(self, o=None, v=None):
pass
def ffplayerLog(self, msg, level):
msg = msg.strip()
if msg:
logger_func[level]('yffpyplayer: {}'.format(msg))
if level == 'error' and self._video.source in msg:
self.dispatch('on_source_error',self,self._video.source)
def play(self,o=None,v=None):
if self.curplay >= 0:
self._video.source = self.playlist[self.curplay]
self._video.state = 'play'
else:
Logger.info('VPlaer: self.curpay < 0')
def next(self,o=None,v=None):
self.nextdir = 'down'
self.stop()
self.dispatch('on_next',self)
def previous(self,o=None,v=None):
self.nextdir = 'up'
self.stop()
self.dispatch('on_previous',self)
def on_state(self,o=None,v=None):
if self._video.state == 'play':
Window.allow_screensaver = False
else:
Window.allow_screensaver = True
if self._video.state == 'stop':
Logger.info('VPlayer: state==stop,nextdir=%s',self.nextdir)
if self.nextdir is None:
self.dispatch('on_next',self)
self.nextdir = None
def on_fullscreen(self, instance, value):
window = self.get_parent_window()
if not window:
Logger.warning('VideoPlayer: Cannot switch to fullscreen, '
'window not found.')
if value:
self.fullscreen = False
return
if not self.parent:
Logger.warning('VideoPlayer: Cannot switch to fullscreen, '
'no parent.')
if value:
self.fullscreen = False
return
if value:
Window.fullscreen = True
self._fullscreen_state = state = {
'parent': self.parent,
'pos': self.pos,
'size': self.size,
'pos_hint': self.pos_hint,
'size_hint': self.size_hint,
'window_children': window.children[:]}
if platform in desktopOSs:
Window.maximize()
# remove all window children
for child in window.children[:]:
window.remove_widget(child)
# put the video in fullscreen
if state['parent'] is not window:
state['parent'].remove_widget(self)
window.add_widget(self)
# ensure the video widget is in 0, 0, and the size will be
# readjusted
self.pos = (0, 0)
self.size = (100, 100)
self.pos_hint = {}
self.size_hint = (1, 1)
else:
Window.fullscreen = False
state = self._fullscreen_state
window.remove_widget(self)
for child in state['window_children']:
window.add_widget(child)
self.pos_hint = state['pos_hint']
self.size_hint = state['size_hint']
self.pos = state['pos']
self.size = state['size']
if state['parent'] is not window:
state['parent'].add_widget(self)
if platform in desktopOSs:
Window.restore()
def endplay(self,btn=None):
self._video.seek(1.0,precise=True)
def replay(self,btn=None):
self._video.seek(0.0,precise=True)
def audioswitch(self,btn=None):
x = self._video._video._ffplayer.request_channel('audio')
def setVolume(self,obj,v):
self._video.volume = v
if v >= 0.01:
self.muteFlg = False
else:
self.muteFlg = True
def setPosition(self,obj,v):
self._video.seek(v)
def mute(self,btn=None):
if self._video.volume > 0.001:
self.old_volume = self._video.volume
self._video.volume = 0.0
else:
self._video.volume = self.old_volume
def stop(self):
self._video.unload()
def pause(self,t=None):
if self._video.state == 'play':
self._video.state = 'pause'
else:
self._video.state = 'play'
def __del__(self):
self.stop()
class Swipe_VPlayer(BaseVPlayer, SwipeBehavior):
def __init__(self,vfile=None, loop=False, mute=False):
BaseVPlayer.__init__(self,vfile=vfile, loop=loop, mute=mute)
SwipeBehavior.__init__(self)
self.bind(on_swipe_down=self.previous)
self.bind(on_swipe_up=self.next)
class OSC_VPlayer(BaseVPlayer):
def __init__(self,vfile=None, loop=False, mute=False):
self.dispatcher = dispatcher.Dispatcher()
self.ip,self.port = get_free_local_addr()
self.server = osc_server.ThreadingOSCUDPServer( (self.ip,self.port),
self.dispatcher)
BaseVPlayer.__init__(self,vfile=vfile, loop=loop, mute=mute)
self.map('/mute',self.mute)
self.map('/pause',self.pause)
self.map('/atrack',self.audioswitch)
self.map('/endplay',self.endplay)
self.map('/replay',self.replay)
self.map('/next',self.next)
self.map('/previous',self.previous)
t = threading.Thread(target=self.server.serve_forever)
t.daemon_threads = True
t.start()
def osc_server_quit(self):
self.server.shutdown()
self.server.server_close()
def __del__(self):
print("*******************VPlayer deleted**********")
self.quit()
def map(self,p,f):
self.dispatcher.map(p,f,None)
def get_osc_info(self):
return {
"host":self.ip,
"port":self.port
}
class VPlayer(Swipe_VPlayer):
def __init__(self,vfile=None, loop=False,mute=False, opbar=True):
self.opbar = opbar
self.menubar = None
self._popup = None
self.menu_status = False
self.manualMode = False
self.pb = None
super().__init__(vfile=vfile,loop=loop,mute=mute)
self._video.bind(on_touch_down=self.show_hide_menu)
def totime(self,dur):
h = dur / 3600
m = dur % 3600 / 60
s = dur % 60
return '%02d:%02d:%02d' % (h,m,s)
def createProgressBar(self):
if self.pb is None:
self.pb = BoxLayout(orientation='horizontal',
size_hint = (0.99,None),height=CSize(1.4))
self.curposition = Label(text='0',width=CSize(4),
size_hint_x=None)
self.curposition.align='right'
self.maxposition = Label(text=self.totime(self._video.duration),
width=CSize(4),size_hint_x=None)
self.maxposition.align = 'left'
self.slider = Slider(min=0,
max=self._video.duration,
value=0,
orientation='horizontal',
step=0.01)
self.slider.bind(on_touch_down=self.enterManualMode)
self.slider.bind(on_touch_up=self.endManualMode)
self.manual_mode=False
self.pb.add_widget(self.curposition)
self.pb.add_widget(self.slider)
self.pb.add_widget(self.maxposition)
self.menubar.add_widget(self.pb)
def enterManualMode(self,obj,touch):
if not self.slider.collide_point(*touch.pos):
return
self.manualMode = True
def endManualMode(self,obj,touch):
if not self.manualMode:
return
if self._video.duration < 0.0001:
return
self._video.seek(self.slider.value/self._video.duration)
self.manualMode = False
def positionChanged(self,o,v):
self.update_slider(None)
def update_slider(self,t):
if self._video.state != 'play':
return
if self.pb is None:
return
v = self._video.position
if v is None:
return
self.curposition.text = self.totime(v)
if not self.manualMode:
self.slider.value = v
self.slider.max = self._video.duration
self.maxposition.text = self.totime(self._video.duration)
def __del__(self):
self.stop()
def beforeDestroy(self):
print('beforeDestroy() called')
self.stop()
return True
def show_hide_menu(self,obj,touch):
if not self.collide_point(*touch.pos):
print('not inside the player',touch.pos,self.pos,self.size)
return
if touch.is_double_tap:
self.fullscreen = False if self.fullscreen else True
print('doube_tap')
return
if not self.opbar:
return
if not self.menubar:
self.buildMenu()
return
if self.menu_status:
self.remove_widget(self.menubar)
else:
self.add_widget(self.menubar)
self.menu_status = not self.menu_status
def buildMenu(self):
self.menubar = BoxLayout(orientation='horizontal',
size_hint_y=None,height=CSize(1.4))
self.btn_pause = PressableImage(source=blockImage('pause.png'),
size_hint=(None,None),
size=CSize(3,3)
)
if self._video.state == 'pause':
self.btn_pause.source = blockImage('play.png')
self.btn_pause.bind(on_press=self.pause)
self.menubar.add_widget(self.btn_pause)
"""
self.btn_mute = PressableImage(source=blockImage('mute.png'),
size_hint=(None,None),
size=CSize(3,3)
)
self.btn_mute.bind(on_press=self.mute)
self.menubar.add_widget(self.btn_mute)
"""
btn_cut = PressableImage(source=blockImage('next.png'),
size_hint=(None,None),
size=CSize(3,3)
)
btn_cut.bind(on_press=self.endplay)
self.menubar.add_widget(btn_cut)
btn_replay = PressableImage(source=blockImage('replay.png'),
size_hint=(None,None),
size=CSize(3,3)
)
btn_replay.bind(on_press=self.replay)
self.menubar.add_widget(btn_replay)
self.btn_audioswitch = PressableImage( \
source=blockImage('musictrack.png'),
size_hint=(None,None),
size=CSize(3,3)
)
self.btn_audioswitch.bind(on_press=self.audioswitch)
self.menubar.add_widget(self.btn_audioswitch)
slider = Slider(min=0,
max=1,
value=self._video.volume,
orientation='horizontal',
size_hint=(None,None),
height = CSize(2),
width = CSize(10),
step=0.07)
slider.bind(on_touch_up=self.setVolume)
self.menubar.add_widget(slider)
self.menubar.pos = CSize(0,0)
self.createProgressBar()
self.add_widget(self.menubar)
self.menu_status = True
def setVolume(self,obj,v=None):
BaseVPlayer.setVolume(self,obj,obj.value)
def mute(self,btn):
BaseVPlayer.mute(self,btn)
if self.menubar:
if self._video.volume < 0.001:
btn.source = blockImage('volume.png')
else:
btn.source = blockImage('mute.png')
def pause(self,t=None):
BaseVPlayer.pause(self,t)
if self.menubar:
if self._video.state == 'pause':
self.btn_pause.source = blockImage('play.png')
else:
self.btn_pause.source = blockImage('pause.png')
if __name__ == '__main__':
class MyApp(App):
def build(self):
vf = None
if len(sys.argv) > 1:
vf = sys.argv[1:]
self.player = VPlayer(vfile=vf,
loop=True,
can_openfile=True,
can_move = True,
can_cut=True,
can_replay=True,
can_changevolume = True
)
return self.player
MyApp().run()