instrumentation: add next-share/
[cs-p2p-next.git] / instrumentation / next-share / BaseLib / Player / EmbeddedPlayer4Frame.py
1 # Written by Fabian van der Werf and Arno Bakker\r
2 # see LICENSE.txt for license information\r
3 #\r
4 # EmbeddedPlayerPanel is the panel used in Tribler 5.0\r
5 # EmbeddedPlayer4FramePanel is the panel used in the SwarmPlayer / 4.5\r
6\r
7 \r
8 import wx\r
9 import sys\r
10 \r
11 import os, shutil\r
12 import time\r
13 import random\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
19 \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
25 \r
26 DEBUG = False\r
27 \r
28 class EmbeddedPlayer4FramePanel(wx.Panel):\r
29     """\r
30     The Embedded Player consists of a VLCLogoWindow and the media controls such \r
31     as Play/Pause buttons and Volume Control.\r
32     """\r
33 \r
34     def __init__(self, parent, utility, vlcwrap, logopath):\r
35         wx.Panel.__init__(self, parent, -1)\r
36         self.utility = utility\r
37 \r
38         self.estduration = None\r
39 \r
40         #self.SetBackgroundColour(wx.WHITE)\r
41         self.SetBackgroundColour(wx.BLACK)\r
42         mainbox = wx.BoxSizer(wx.VERTICAL)\r
43 \r
44 \r
45         if vlcwrap is None:\r
46             size = (320,64)\r
47         else:\r
48             size = (320,240) \r
49         \r
50         self.vlcwin = VLCLogoWindow(self,size,vlcwrap,logopath, animate = False)\r
51         self.vlcwrap = vlcwrap\r
52 \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
56 \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
64             \r
65                             \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
68     \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
76     \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
79     \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
83             \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
89         \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
95         \r
96         self.playtimer = None\r
97         self.update = False\r
98         self.timer = None\r
99         \r
100     def Load(self,url,streaminfo = None):\r
101         if DEBUG:\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
107         #else:\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
112 \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
116              \r
117         self.vlcwrap.load(url,streaminfo=streaminfo)\r
118         \r
119         # Enable update of progress slider\r
120         self.update = True\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
126         \r
127     def StartPlay(self):\r
128         """ Start playing the new item after VLC has stopped playing the old\r
129         one\r
130         """\r
131         if DEBUG:\r
132             print >>sys.stderr,"embedplay: PlayWhenStopped"\r
133         self.playtimer = DelayTimer(self)\r
134 \r
135     def Play(self, evt=None):\r
136         if DEBUG:\r
137             print >>sys.stderr,"embedplay: Play pressed"\r
138 \r
139         if self.GetState() != MEDIASTATE_PLAYING:\r
140             self.ppbtn.setToggled(False)\r
141             self.vlcwrap.start()\r
142 \r
143     def Pause(self, evt=None):\r
144         """ Toggle between playing and pausing of current item """\r
145         if DEBUG:\r
146             print >>sys.stderr,"embedplay: Pause pressed"\r
147         \r
148         if self.GetState() == MEDIASTATE_PLAYING:\r
149             self.ppbtn.setToggled(True)\r
150             self.vlcwrap.pause()\r
151 \r
152 \r
153     def PlayPause(self, evt=None):\r
154         """ Toggle between playing and pausing of current item """\r
155         if DEBUG:\r
156             print >>sys.stderr,"embedplay: PlayPause pressed"\r
157         \r
158         if self.GetState() == MEDIASTATE_PLAYING:\r
159             self.ppbtn.setToggled(True)\r
160             self.vlcwrap.pause()\r
161 \r
162         else:\r
163             self.ppbtn.setToggled(False)\r
164             self.vlcwrap.resume()\r
165 \r
166 \r
167     def Seek(self, evt=None):\r
168         if DEBUG:\r
169             print >>sys.stderr,"embedplay: Seek"\r
170         \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
175         \r
176         try:\r
177             if self.GetState() == MEDIASTATE_STOPPED:\r
178                 self.vlcwrap.start(pos)\r
179             else:\r
180                 self.vlcwrap.set_media_position(pos)\r
181         except:\r
182             print_exc()\r
183             if DEBUG:\r
184                 print >> sys.stderr, 'embedplay: could not seek'\r
185             self.slider.SetValue(oldsliderpos)\r
186         self.update = True\r
187         \r
188 \r
189     def FullScreen(self,evt=None):\r
190         self.vlcwrap.set_fullscreen(True)\r
191 \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
197         else:\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
201         \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
206             \r
207     \r
208     def SetVolume(self, evt = None):\r
209         if DEBUG:\r
210             print >> sys.stderr, "embedplay: SetVolume:",self.volume.GetValue()\r
211         self.vlcwrap.sound_set_volume(float(self.volume.GetValue()) / 100)\r
212         # reset mute\r
213         if self.volumeicon.isToggled():\r
214             self.volumeicon.setToggled(False)\r
215 \r
216     def Stop(self):\r
217         if DEBUG:\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
223             self.timer.Stop()\r
224 \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
228         if DEBUG:\r
229             print >>sys.stderr,"embedplay: GetState"\r
230             \r
231         status = self.vlcwrap.get_stream_information_status()\r
232         \r
233         import vlc\r
234         if status == vlc.PlayingStatus:\r
235             return MEDIASTATE_PLAYING\r
236         elif status == vlc.PauseStatus:\r
237             return MEDIASTATE_PAUSED\r
238         else:\r
239             return MEDIASTATE_STOPPED\r
240 \r
241 \r
242     def EnableSaveButton(self, b, callback):\r
243         self.save_button.setToggled(b)\r
244         if b:\r
245             self.save_callback = callback\r
246         else:\r
247             self.save_callback = lambda:None\r
248 \r
249     def Reset(self):\r
250         self.DisableInput()\r
251         self.Stop()\r
252         self.UpdateProgressSlider([False])\r
253 \r
254     #\r
255     # Control on-screen information\r
256     #\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
261     \r
262     def SetPlayerStatus(self,s):\r
263         self.statuslabel.SetLabel(s)\r
264 \r
265     def SetContentName(self,s):\r
266         self.vlcwin.set_content_name(s)\r
267 \r
268     def SetContentImage(self,wximg):\r
269         self.vlcwin.set_content_image(wximg)\r
270 \r
271 \r
272     #\r
273     # Internal methods\r
274     #\r
275     def EnableInput(self):\r
276         self.ppbtn.Enable(True)\r
277         self.slider.Enable(True)\r
278         self.fsbtn.Enable(True)\r
279 \r
280     def UpdateProgressSlider(self, pieces_complete):\r
281         self.slider.setBufferFromPieces(pieces_complete)\r
282         self.slider.Refresh()\r
283         \r
284     def DisableInput(self):\r
285         return # Not currently used\r
286         \r
287         self.ppbtn.Disable()\r
288         self.slider.Disable()\r
289         self.fsbtn.Disable()\r
290 \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
294 \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
299                     return\r
300                 else:\r
301                     len = int(self.estduration)\r
302             else:\r
303                 len /= 1000\r
304 \r
305             cur = self.vlcwrap.get_media_position() / 1000\r
306 \r
307             self.slider.SetRange(0, len)\r
308             self.slider.SetValue(cur)\r
309             self.slider.SetTimePosition(float(cur), len)\r
310 \r
311     def StopSliderUpdate(self, evt):\r
312         self.update = False\r
313 \r
314 \r
315     def TellLVCWrapWindow4Playback(self):\r
316         if self.vlcwrap is not None:\r
317             self.vlcwin.tell_vclwrap_window_for_playback()\r
318 \r
319     def ShowLoading(self):\r
320         pass\r
321     \r
322     \r
323 \r
324 \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
329     """\r
330     \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
334     \r
335         self.SetMinSize(size)\r
336         self.SetBackgroundColour(bg)\r
337         self.bg = bg\r
338         self.vlcwrap = vlcwrap\r
339         self.animation_running = False\r
340        \r
341         self.Bind(wx.EVT_KEY_UP, self.keyDown)\r
342 \r
343         print >>sys.stderr,"VLCLogoWindow: logopath is",logopath\r
344 \r
345         if logopath is not None and not animate:\r
346             self.logo = wx.BitmapFromImage(wx.Image(logopath),-1)\r
347         else:\r
348             self.logo = None\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
356         if animate:\r
357             if sys.platform == 'darwin':\r
358                 self.agVideo = wx.animate.GIFAnimationCtrl(self, 1, logopath)\r
359             else:\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
365         else:\r
366             self.agVideo = None\r
367 \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
373         else:\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
377         else:\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
380 \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
386             self.Layout()\r
387             self.Refresh()\r
388         if self.vlcwrap is not None:\r
389             wx.CallAfter(self.tell_vclwrap_window_for_playback)\r
390         \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
395         """\r
396         self.vlcwrap.set_window(self)\r
397 \r
398     def get_vlcwrap(self):\r
399         return self.vlcwrap\r
400 \r
401     def set_content_name(self,s):\r
402         if DEBUG:\r
403             print >>sys.stderr,"VLCWin: set_content_name"\r
404         self.contentname = s\r
405         self.Refresh()\r
406     \r
407     def set_content_image(self,wximg):\r
408         if DEBUG:\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
412         else:\r
413             self.contentbm = None\r
414 \r
415     def is_animation_running(self):\r
416         return self.animation_running\r
417 \r
418     def setloadingtext(self, text):\r
419         self.loadingtext.SetLabel(text)\r
420         self.Refresh()\r
421 \r
422     def show_loading(self):\r
423         if self.agVideo:\r
424             self.agVideo.Show()\r
425             self.agVideo.Play()\r
426             self.animation_running = True\r
427             self.Refresh()\r
428 \r
429 \r
430         \r
431         \r
432     def stop_animation(self):\r
433         if self.agVideo:\r
434             self.agVideo.Stop()\r
435             self.agVideo.Hide()\r
436             self.animation_running = False\r
437             self.Refresh()\r
438 \r
439     def OnPaint(self,evt):\r
440         dc = wx.PaintDC(self)\r
441         dc.Clear()\r
442         dc.BeginDrawing()        \r
443 \r
444         x,y,maxw,maxh = self.GetClientRect()\r
445         halfx = (maxw-x)/2\r
446         halfy = (maxh-y)/2\r
447         if self.logo is None:\r
448             halfx = 10\r
449             halfy = 10\r
450             lheight = 20\r
451         else:\r
452             halfx -= self.logo.GetWidth()/2\r
453             halfy -= self.logo.GetHeight()/2\r
454             lheight = self.logo.GetHeight()\r
455 \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
464 \r
465         dc.SetTextForeground(wx.WHITE)\r
466         dc.SetTextBackground(wx.BLACK)\r
467         \r
468         lineoffset = 120\r
469         txty = halfy+lheight+lineoffset\r
470         if txty > maxh:\r
471             txty = 0\r
472         if self.contentname is not None:\r
473             txt = self.contentname\r
474             dc.DrawText(txt,30,txty)\r
475             lineoffset += 30\r
476 \r
477         #txt = self.getStatus()\r
478         #dc.DrawText(txt,30,halfy+self.logo.GetHeight()+lineoffset)\r
479         \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
483         \r
484         dc.EndDrawing()\r
485         if evt is not None:\r
486             evt.Skip(True)\r
487 \r
488 \r
489     def keyDown(self, event):\r
490         Level = event.StopPropagation()\r
491         event.ResumePropagation(10)\r
492 \r
493         event.Skip()\r
494 \r