1 # Written by Fabian van der Werf and Arno Bakker
\r
2 # see LICENSE.txt for license information
\r
4 # EmbeddedPlayerPanel is the panel used in Tribler 5.0
\r
5 # EmbeddedPlayer4FramePanel is the panel used in the SwarmPlayer / 4.5
\r
14 from time import sleep
\r
15 from tempfile import mkstemp
\r
16 from threading import currentThread,Event, Thread
\r
17 from traceback import print_stack,print_exc
\r
18 from textwrap import wrap
\r
20 from BaseLib.__init__ import LIBRARYNAME
\r
21 from BaseLib.Video.defs import *
\r
22 from BaseLib.Video.Progress import ProgressSlider, VolumeSlider
\r
23 from BaseLib.Video.Buttons import PlayerSwitchButton, PlayerButton
\r
24 from BaseLib.Video.VideoFrame import DelayTimer
\r
28 class EmbeddedPlayer4FramePanel(wx.Panel):
\r
30 The Embedded Player consists of a VLCLogoWindow and the media controls such
\r
31 as Play/Pause buttons and Volume Control.
\r
34 def __init__(self, parent, utility, vlcwrap, logopath):
\r
35 wx.Panel.__init__(self, parent, -1)
\r
36 self.utility = utility
\r
38 self.estduration = None
\r
40 #self.SetBackgroundColour(wx.WHITE)
\r
41 self.SetBackgroundColour(wx.BLACK)
\r
42 mainbox = wx.BoxSizer(wx.VERTICAL)
\r
50 self.vlcwin = VLCLogoWindow(self,size,vlcwrap,logopath, animate = False)
\r
51 self.vlcwrap = vlcwrap
\r
53 # Arno: until we figure out how to show in-playback prebuffering info
\r
54 self.statuslabel = wx.StaticText(self, -1, 'Loading player...' )
\r
55 self.statuslabel.SetForegroundColour(wx.WHITE)
\r
57 if vlcwrap is not None:
\r
58 ctrlsizer = wx.BoxSizer(wx.HORIZONTAL)
\r
59 #self.slider = wx.Slider(self, -1)
\r
60 self.slider = ProgressSlider(self, self.utility, imgprefix='4frame')
\r
61 self.slider.SetRange(0,1)
\r
62 self.slider.SetValue(0)
\r
63 self.oldvolume = None
\r
66 self.ppbtn = PlayerSwitchButton(self, os.path.join(self.utility.getPath(), LIBRARYNAME, 'Images'), 'pause', 'play')
\r
67 self.ppbtn.Bind(wx.EVT_LEFT_UP, self.PlayPause)
\r
69 self.volumebox = wx.BoxSizer(wx.HORIZONTAL)
\r
70 self.volumeicon = PlayerSwitchButton(self, os.path.join(self.utility.getPath(), LIBRARYNAME, 'Images'), 'volume', 'mute')
\r
71 self.volumeicon.Bind(wx.EVT_LEFT_UP, self.Mute)
\r
72 self.volume = VolumeSlider(self, self.utility, imgprefix='4frame')
\r
73 self.volume.SetRange(0, 100)
\r
74 self.volumebox.Add(self.volumeicon, 0, wx.ALIGN_CENTER_VERTICAL)
\r
75 self.volumebox.Add(self.volume, 0, wx.ALIGN_CENTER_VERTICAL, 0)
\r
77 self.fsbtn = PlayerButton(self, os.path.join(self.utility.getPath(), LIBRARYNAME, 'Images'), 'fullScreen')
\r
78 self.fsbtn.Bind(wx.EVT_LEFT_UP, self.FullScreen)
\r
80 self.save_button = PlayerSwitchButton(self, os.path.join(self.utility.getPath(), LIBRARYNAME, 'Images'), 'saveDisabled', 'save')
\r
81 self.save_button.Bind(wx.EVT_LEFT_UP, self.Save)
\r
82 self.save_callback = lambda:None
\r
84 ctrlsizer.Add(self.ppbtn, 0, wx.ALIGN_CENTER_VERTICAL)
\r
85 ctrlsizer.Add(self.slider, 1, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
\r
86 ctrlsizer.Add(self.volumebox, 0, wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
\r
87 ctrlsizer.Add(self.fsbtn, 0, wx.ALIGN_CENTER_VERTICAL)
\r
88 ctrlsizer.Add(self.save_button, 0, wx.ALIGN_CENTER_VERTICAL)
\r
90 mainbox.Add(self.vlcwin, 1, wx.EXPAND, 1)
\r
91 mainbox.Add(self.statuslabel, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 30)
\r
92 if vlcwrap is not None:
\r
93 mainbox.Add(ctrlsizer, 0, wx.ALIGN_BOTTOM|wx.EXPAND, 1)
\r
94 self.SetSizerAndFit(mainbox)
\r
96 self.playtimer = None
\r
100 def Load(self,url,streaminfo = None):
\r
102 print >>sys.stderr,"embedplay: Load:",url,streaminfo,currentThread().getName()
\r
103 # Arno: hack: disable dragging when not playing from file.
\r
104 #if url is None or url.startswith('http:'):
\r
105 #if url is not None and url.startswith('http:'):
\r
106 # self.slider.DisableDragging()
\r
108 self.slider.EnableDragging()
\r
109 self.SetPlayerStatus('')
\r
110 if streaminfo is not None:
\r
111 self.estduration = streaminfo.get('estduration',None)
\r
113 # Arno, 2008-10-17: If we don't do this VLC gets the wrong playlist somehow
\r
114 self.vlcwrap.stop()
\r
115 self.vlcwrap.playlist_clear()
\r
117 self.vlcwrap.load(url,streaminfo=streaminfo)
\r
119 # Enable update of progress slider
\r
121 wx.CallAfter(self.slider.SetValue,0)
\r
122 if self.timer is None:
\r
123 self.timer = wx.Timer(self)
\r
124 self.Bind(wx.EVT_TIMER, self.UpdateSlider)
\r
125 self.timer.Start(200)
\r
127 def StartPlay(self):
\r
128 """ Start playing the new item after VLC has stopped playing the old
\r
132 print >>sys.stderr,"embedplay: PlayWhenStopped"
\r
133 self.playtimer = DelayTimer(self)
\r
135 def Play(self, evt=None):
\r
137 print >>sys.stderr,"embedplay: Play pressed"
\r
139 if self.GetState() != MEDIASTATE_PLAYING:
\r
140 self.ppbtn.setToggled(False)
\r
141 self.vlcwrap.start()
\r
143 def Pause(self, evt=None):
\r
144 """ Toggle between playing and pausing of current item """
\r
146 print >>sys.stderr,"embedplay: Pause pressed"
\r
148 if self.GetState() == MEDIASTATE_PLAYING:
\r
149 self.ppbtn.setToggled(True)
\r
150 self.vlcwrap.pause()
\r
153 def PlayPause(self, evt=None):
\r
154 """ Toggle between playing and pausing of current item """
\r
156 print >>sys.stderr,"embedplay: PlayPause pressed"
\r
158 if self.GetState() == MEDIASTATE_PLAYING:
\r
159 self.ppbtn.setToggled(True)
\r
160 self.vlcwrap.pause()
\r
163 self.ppbtn.setToggled(False)
\r
164 self.vlcwrap.resume()
\r
167 def Seek(self, evt=None):
\r
169 print >>sys.stderr,"embedplay: Seek"
\r
171 oldsliderpos = self.slider.GetValue()
\r
172 print >>sys.stderr, 'embedplay: Seek: GetValue returned,',oldsliderpos
\r
173 pos = int(oldsliderpos * 1000.0)
\r
174 print >>sys.stderr, 'embedplay: Seek: newpos',pos
\r
177 if self.GetState() == MEDIASTATE_STOPPED:
\r
178 self.vlcwrap.start(pos)
\r
180 self.vlcwrap.set_media_position(pos)
\r
184 print >> sys.stderr, 'embedplay: could not seek'
\r
185 self.slider.SetValue(oldsliderpos)
\r
189 def FullScreen(self,evt=None):
\r
190 self.vlcwrap.set_fullscreen(True)
\r
192 def Mute(self, evt = None):
\r
193 if self.volumeicon.isToggled():
\r
194 if self.oldvolume is not None:
\r
195 self.vlcwrap.sound_set_volume(self.oldvolume)
\r
196 self.volumeicon.setToggled(False)
\r
198 self.oldvolume = self.vlcwrap.sound_get_volume()
\r
199 self.vlcwrap.sound_set_volume(0.0) # mute sound
\r
200 self.volumeicon.setToggled(True)
\r
202 def Save(self, evt = None):
\r
203 # save media content in different directory
\r
204 if self.save_button.isToggled():
\r
205 self.save_callback()
\r
208 def SetVolume(self, evt = None):
\r
210 print >> sys.stderr, "embedplay: SetVolume:",self.volume.GetValue()
\r
211 self.vlcwrap.sound_set_volume(float(self.volume.GetValue()) / 100)
\r
213 if self.volumeicon.isToggled():
\r
214 self.volumeicon.setToggled(False)
\r
218 print >> sys.stderr, "embedplay: Stop"
\r
219 self.vlcwrap.stop()
\r
220 self.ppbtn.SetLabel(self.utility.lang.get('playprompt'))
\r
221 self.slider.SetValue(0)
\r
222 if self.timer is not None:
\r
225 def GetState(self):
\r
226 """ Returns the state of VLC as summarized by Fabian:
\r
227 MEDIASTATE_PLAYING, MEDIASTATE_PAUSED, MEDIASTATE_STOPPED """
\r
229 print >>sys.stderr,"embedplay: GetState"
\r
231 status = self.vlcwrap.get_stream_information_status()
\r
234 if status == vlc.PlayingStatus:
\r
235 return MEDIASTATE_PLAYING
\r
236 elif status == vlc.PauseStatus:
\r
237 return MEDIASTATE_PAUSED
\r
239 return MEDIASTATE_STOPPED
\r
242 def EnableSaveButton(self, b, callback):
\r
243 self.save_button.setToggled(b)
\r
245 self.save_callback = callback
\r
247 self.save_callback = lambda:None
\r
250 self.DisableInput()
\r
252 self.UpdateProgressSlider([False])
\r
255 # Control on-screen information
\r
257 def UpdateStatus(self,playerstatus,pieces_complete):
\r
258 self.SetPlayerStatus(playerstatus)
\r
259 if self.vlcwrap is not None:
\r
260 self.UpdateProgressSlider(pieces_complete)
\r
262 def SetPlayerStatus(self,s):
\r
263 self.statuslabel.SetLabel(s)
\r
265 def SetContentName(self,s):
\r
266 self.vlcwin.set_content_name(s)
\r
268 def SetContentImage(self,wximg):
\r
269 self.vlcwin.set_content_image(wximg)
\r
275 def EnableInput(self):
\r
276 self.ppbtn.Enable(True)
\r
277 self.slider.Enable(True)
\r
278 self.fsbtn.Enable(True)
\r
280 def UpdateProgressSlider(self, pieces_complete):
\r
281 self.slider.setBufferFromPieces(pieces_complete)
\r
282 self.slider.Refresh()
\r
284 def DisableInput(self):
\r
285 return # Not currently used
\r
287 self.ppbtn.Disable()
\r
288 self.slider.Disable()
\r
289 self.fsbtn.Disable()
\r
291 def UpdateSlider(self, evt):
\r
292 if not self.volumeicon.isToggled():
\r
293 self.volume.SetValue(int(self.vlcwrap.sound_get_volume() * 100))
\r
295 if self.update and self.GetState() != MEDIASTATE_STOPPED:
\r
296 len = self.vlcwrap.get_stream_information_length()
\r
297 if len == -1 or len == 0:
\r
298 if self.estduration is None:
\r
301 len = int(self.estduration)
\r
305 cur = self.vlcwrap.get_media_position() / 1000
\r
307 self.slider.SetRange(0, len)
\r
308 self.slider.SetValue(cur)
\r
309 self.slider.SetTimePosition(float(cur), len)
\r
311 def StopSliderUpdate(self, evt):
\r
312 self.update = False
\r
315 def TellLVCWrapWindow4Playback(self):
\r
316 if self.vlcwrap is not None:
\r
317 self.vlcwin.tell_vclwrap_window_for_playback()
\r
319 def ShowLoading(self):
\r
325 class VLCLogoWindow(wx.Panel):
\r
326 """ A wx.Window to be passed to the vlc.MediaControl to draw the video
\r
327 in (normally). In addition, the class can display a logo, a thumbnail and a
\r
328 "Loading: bla.video" message when VLC is not playing.
\r
331 def __init__(self, parent, size, vlcwrap, logopath, fg=wx.WHITE, bg=wx.BLACK, animate = False, position = (300,300)):
\r
332 wx.Panel.__init__(self, parent, -1, size=size)
\r
333 self.parent = parent ##
\r
335 self.SetMinSize(size)
\r
336 self.SetBackgroundColour(bg)
\r
338 self.vlcwrap = vlcwrap
\r
339 self.animation_running = False
\r
341 self.Bind(wx.EVT_KEY_UP, self.keyDown)
\r
343 print >>sys.stderr,"VLCLogoWindow: logopath is",logopath
\r
345 if logopath is not None and not animate:
\r
346 self.logo = wx.BitmapFromImage(wx.Image(logopath),-1)
\r
349 self.contentname = None
\r
350 self.contentbm = None
\r
351 self.Bind(wx.EVT_PAINT, self.OnPaint)
\r
352 if sys.platform == 'darwin':
\r
353 self.hsizermain = wx.BoxSizer(wx.HORIZONTAL)
\r
354 self.vsizer = wx.BoxSizer(wx.VERTICAL)
\r
355 self.vsizer.Add((0,70),0,0,0)
\r
357 if sys.platform == 'darwin':
\r
358 self.agVideo = wx.animate.GIFAnimationCtrl(self, 1, logopath)
\r
360 self.agVideo = wx.animate.GIFAnimationCtrl(self, 1, logopath, pos = (110,70))
\r
361 self.agVideo.Hide()
\r
362 if sys.platform == 'darwin':
\r
363 self.vsizer.Add(self.agVideo,0,wx.ALIGN_CENTRE_HORIZONTAL,0)
\r
364 self.vsizer.Add((0,10),0,0,0)
\r
366 self.agVideo = None
\r
368 #self.playbackText = wx.StaticText(self,-1,"Leave Tribler running\n for faster playback",wx.Point(30,140))
\r
369 #self.playbackText.SetFont(wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "UTF-8"))
\r
370 #self.playbackText.SetForegroundColour(wx.Colour(255,51,00))
\r
371 if sys.platform == 'darwin':
\r
372 self.loadingtext = wx.StaticText(self,-1,'')
\r
374 self.loadingtext = wx.StaticText(self,-1,'',wx.Point(0,200),wx.Size(320,30),style=wx.ALIGN_CENTRE)
\r
375 if sys.platform == 'darwin':
\r
376 self.loadingtext.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "UTF-8"))
\r
378 self.loadingtext.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, 0, "UTF-8"))
\r
379 self.loadingtext.SetForegroundColour(wx.WHITE)
\r
381 if sys.platform == 'darwin':
\r
382 self.vsizer.Add(self.loadingtext,1,wx.ALIGN_CENTRE_HORIZONTAL,0)
\r
383 self.hsizermain.Add(self.vsizer,1,wx.ALIGN_CENTRE_HORIZONTAL,0)
\r
384 self.SetSizer(self.hsizermain)
\r
385 self.SetAutoLayout(1)
\r
388 if self.vlcwrap is not None:
\r
389 wx.CallAfter(self.tell_vclwrap_window_for_playback)
\r
391 def tell_vclwrap_window_for_playback(self):
\r
392 """ This method must be called after the VLCLogoWindow has been
\r
393 realized, otherwise the self.GetHandle() call that vlcwrap.set_window()
\r
394 does, doesn't return a correct XID.
\r
396 self.vlcwrap.set_window(self)
\r
398 def get_vlcwrap(self):
\r
399 return self.vlcwrap
\r
401 def set_content_name(self,s):
\r
403 print >>sys.stderr,"VLCWin: set_content_name"
\r
404 self.contentname = s
\r
407 def set_content_image(self,wximg):
\r
409 print >>sys.stderr,"VLCWin: set_content_image"
\r
410 if wximg is not None:
\r
411 self.contentbm = wx.BitmapFromImage(wximg,-1)
\r
413 self.contentbm = None
\r
415 def is_animation_running(self):
\r
416 return self.animation_running
\r
418 def setloadingtext(self, text):
\r
419 self.loadingtext.SetLabel(text)
\r
422 def show_loading(self):
\r
424 self.agVideo.Show()
\r
425 self.agVideo.Play()
\r
426 self.animation_running = True
\r
432 def stop_animation(self):
\r
434 self.agVideo.Stop()
\r
435 self.agVideo.Hide()
\r
436 self.animation_running = False
\r
439 def OnPaint(self,evt):
\r
440 dc = wx.PaintDC(self)
\r
444 x,y,maxw,maxh = self.GetClientRect()
\r
447 if self.logo is None:
\r
452 halfx -= self.logo.GetWidth()/2
\r
453 halfy -= self.logo.GetHeight()/2
\r
454 lheight = self.logo.GetHeight()
\r
456 dc.SetPen(wx.Pen(self.bg,0))
\r
457 dc.SetBrush(wx.Brush(self.bg))
\r
458 if sys.platform == 'linux2':
\r
459 dc.DrawRectangle(x,y,maxw,maxh)
\r
460 if self.logo is not None:
\r
461 dc.DrawBitmap(self.logo,halfx,halfy,True)
\r
462 #logox = max(0,maxw-self.logo.GetWidth()-30)
\r
463 #dc.DrawBitmap(self.logo,logox,20,True)
\r
465 dc.SetTextForeground(wx.WHITE)
\r
466 dc.SetTextBackground(wx.BLACK)
\r
469 txty = halfy+lheight+lineoffset
\r
472 if self.contentname is not None:
\r
473 txt = self.contentname
\r
474 dc.DrawText(txt,30,txty)
\r
477 #txt = self.getStatus()
\r
478 #dc.DrawText(txt,30,halfy+self.logo.GetHeight()+lineoffset)
\r
480 if self.contentbm is not None:
\r
481 bmy = max(20,txty-20-self.contentbm.GetHeight())
\r
482 dc.DrawBitmap(self.contentbm,30,bmy,True)
\r
485 if evt is not None:
\r
489 def keyDown(self, event):
\r
490 Level = event.StopPropagation()
\r
491 event.ResumePropagation(10)
\r