Author: dmeyer
Date: Fri Nov  3 21:26:35 2006
New Revision: 1975

Modified:
   trunk/popcorn/src/backends/base.py
   trunk/popcorn/src/backends/mplayer/config.py
   trunk/popcorn/src/backends/mplayer/player.py
   trunk/popcorn/src/backends/xine/config.py
   trunk/popcorn/src/backends/xine/player.py
   trunk/popcorn/src/generic.py
   trunk/popcorn/src/ptypes.py

Log:
o add STATE_OPEN between STATE_OPENING and STATE_PLAYING
o cleanup signal usage in backend player
o cleanup state handling
o move shared code to base
o rearange functions in backends to match base
o add header
o smaller cleanups
o broke gstreamer by doing the stuff above


Modified: trunk/popcorn/src/backends/base.py
==============================================================================
--- trunk/popcorn/src/backends/base.py  (original)
+++ trunk/popcorn/src/backends/base.py  Fri Nov  3 21:26:35 2006
@@ -28,6 +28,8 @@
 
 # python imports
 import sets
+import os
+import md5
 
 # kaa imports
 import kaa.notifier
@@ -41,32 +43,30 @@
     Base class for players
     """
 
+    _instance_count = 0
+
     def __init__(self):
         self.signals = {
-            "pause": kaa.notifier.Signal(),
-            "play": kaa.notifier.Signal(),
-            "pause_toggle": kaa.notifier.Signal(),
-            "seek": kaa.notifier.Signal(),
-            "start": kaa.notifier.Signal(),
-            "failed": kaa.notifier.Signal(),
-            # Stream ended (either stopped by user or finished)
-            "end": kaa.notifier.Signal(),
             "stream_changed": kaa.notifier.Signal(),
-            "frame": kaa.notifier.Signal(), # CAP_CANVAS
-            "osd_configure": kaa.notifier.Signal(),  # CAP_OSD
-
-            # Player released video and audio devices
-            "release": kaa.notifier.Signal(),
-
-            # Process died (shared memory will go away)
-            "shm_quit": kaa.notifier.Signal()
+            "frame": kaa.notifier.Signal(),
+            "osd_configure": kaa.notifier.Signal(),
         }
-
+        self._state_changed = kaa.notifier.Signal()
         self._state_object = STATE_IDLE
         self._window = None
         self._size = None
         self._config = None
+        self._instance_id = "popcorn-%d-%d" % (os.getpid(), 
self._instance_count)
+        MediaPlayer._instance_count += 1
 
+        # shared memory keys
+        key = md5.md5(self._instance_id + "osd").hexdigest()[:7]
+        self._osd_shmkey = int(key, 16)
+        self._osd_shmem = None
+        key = md5.md5(self._instance_id + "frame").hexdigest()[:7]
+        self._frame_shmkey = int(key, 16)
+        self._frame_shmem = None
+        
 
     def get_capabilities(self):
         """
@@ -120,24 +120,7 @@
         old_state = self._state_object
         self._state_object = state
 
-        if old_state == STATE_OPENING and state in (STATE_IDLE, 
STATE_NOT_RUNNING):
-            # From STATE_OPENING to not playing. This means something went
-            # wrong and the player failed to play.
-            self.signals["failed"].emit()
-
-        if old_state == STATE_OPENING and \
-               state in (STATE_PLAYING, STATE_PAUSED):
-            # From STATE_OPENING to playing. Signal playback start
-            self.signals["start"].emit()
-
-        if old_state in (STATE_PLAYING, STATE_PAUSED) and \
-               state in (STATE_IDLE, STATE_NOT_RUNNING):
-            # From playing to finished. Signal end.
-            self.signals["end"].emit()
-
-        if state == STATE_NOT_RUNNING == self._state_object:
-            self.signals["release"].emit()
-            self.signals["shm_quit"].emit()
+        self._state_changed.emit(old_state, state)
 
     # state property based on get_state and _set_state
     _state = property(get_state, _set_state, None, 'state of the player')
@@ -236,7 +219,7 @@
         """
         Release audio and video devices.
         """
-        self.die()
+        pass
 
 
     def seek_relative(self, offset):

Modified: trunk/popcorn/src/backends/mplayer/config.py
==============================================================================
--- trunk/popcorn/src/backends/mplayer/config.py        (original)
+++ trunk/popcorn/src/backends/mplayer/config.py        Fri Nov  3 21:26:35 2006
@@ -1,12 +1,45 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# mplayer/config.py - config for mplayer backend
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------------
+# kaa.popcorn - Generic Player API
+# Copyright (C) 2006 Jason Tackaberry, Dirk Meyer
+#
+# Please see the file AUTHORS for a complete list of authors.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------------
+
+# python imports
 import os
 import sys
 import gettext
+
+# kaa imports
 from kaa.config import Var, Group, Dict, List
 
+# i18 setup for this file
 path = os.path.normpath(
     os.path.join(os.path.dirname(__file__), '../../../../share/locale'))
 i18n = gettext.translation('kaa.popcorn', path, fallback=True).ugettext
 
+# config variable for mplayer
 config = Group(desc=i18n('mplayer configuration'), schema=[
     Var(name='activate', default=True, desc=i18n('activate backend'))
     ])

Modified: trunk/popcorn/src/backends/mplayer/player.py
==============================================================================
--- trunk/popcorn/src/backends/mplayer/player.py        (original)
+++ trunk/popcorn/src/backends/mplayer/player.py        Fri Nov  3 21:26:35 2006
@@ -1,15 +1,56 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# mplayer/player.py - mplayer backend
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------------
+# kaa.popcorn - Generic Player API
+# Copyright (C) 2006 Jason Tackaberry, Dirk Meyer
+#
+# Please see the file AUTHORS for a complete list of authors.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------------
+
+# python imports
 import logging
-import os, re, string, tempfile, time, stat, threading, md5, struct
-from kaa import notifier, display, shm
+import os
+import re
+import string
+import tempfile
+import stat
+import threading
+import md5
+import struct
+
+# kaa imports
 import kaa
+from kaa import shm
 import kaa.utils
+import kaa.notifier
+import kaa.display
+
+# kaa.popcorn base imports
 from kaa.popcorn.backends.base import MediaPlayer
-from kaa.popcorn.ptypes import *
 from kaa.popcorn.utils import parse_mrl
+from kaa.popcorn.ptypes import *
 
-# 0 = none, 1 = interesting lines, 2 = everything, 3 = everything + status,
-# 4 = everything + status + run through gdb
-DEBUG=1
+# start mplayer in gdb for debugging
+USE_GDB = False
 
 BUFFER_UNLOCKED = 0x10
 BUFFER_LOCKED = 0x20
@@ -49,7 +90,7 @@
         if callback:
             # We need to run MPlayer to get these values.  Create a signal, 
call
             # ourself as a thread, and return the signal back to the caller.
-            thread = notifier.Thread(_get_mplayer_info, path, None, mtime)
+            thread = kaa.notifier.Thread(_get_mplayer_info, path, None, mtime)
             # Thread class ensures the callbacks get invoked in the main 
thread.
             thread.signals["completed"].connect(callback)
             thread.signals["exception"].connect(callback)
@@ -97,37 +138,22 @@
 
 
 
-class MPlayerError(Exception):
-    pass
-
-class MPlayerExitError(MPlayerError):
-    pass
-
 class MPlayer(MediaPlayer):
 
     PATH = None
-    _instance_count = 0
-
     RE_STATUS = re.compile("V:\s*([\d+\.]+)|A:\s*([\d+\.]+)\s\W")
 
     def __init__(self):
         super(MPlayer, self).__init__()
+        self._state = STATE_NOT_RUNNING
         self._mp_cmd = MPlayer.PATH
         if not self._mp_cmd:
             self._mp_cmd = kaa.utils.which("mplayer")
 
         if not self._mp_cmd:
-            raise MPlayerError, "No MPlayer executable found in PATH"
+            raise PlayerError, "No MPlayer executable found in PATH"
 
-        self._debug = DEBUG
-        # Used for vf_overlay and vf_outbuf
-        self._instance_id = "%d-%d" % (os.getpid(), MPlayer._instance_count)
-        MPlayer._instance_count += 1
-
-        self._process = None
-        self._state = STATE_NOT_RUNNING
-        self._overlay_shmem = None
-        self._outbuf_shmem = None
+        self._child_app = None
         self._file = None
         self._file_args = []
 
@@ -137,57 +163,16 @@
         self._filters_add = []
         self._last_line = None
 
-        self.signals.update({
-            "output": notifier.Signal(),
-        })
-
         self._mp_info = _get_mplayer_info(self._mp_cmd, self._handle_mp_info)
-        self._check_new_frame_timer = notifier.WeakTimer(self._check_new_frame)
+        self._check_new_frame_timer = 
kaa.notifier.WeakTimer(self._check_new_frame)
         self._cur_outbuf_mode = [True, False, None] # vo, shmem, size
 
 
     def __del__(self):
-        if self._outbuf_shmem:
-            self._outbuf_shmem.detach()
-        if self._overlay_shmem:
-            self._overlay_shmem.detach()
-
-
-    def _spawn(self, args, hook_notifier = True):
-        log.debug("Spawn: %s %s", self._mp_cmd, args)
-
-        if self._debug > 3:
-            # With debug > 3, run mplayer through gdb.
-            self._process = notifier.Process("gdb")
-            self._process.start(self._mp_cmd)
-            self._process.write("run %s\n" % args)
-        else:
-            self._process = notifier.Process(self._mp_cmd)
-            self._process.start(args)
-
-        if hook_notifier:
-            self._process.signals["stdout"].connect_weak(self._handle_line)
-            self._process.signals["stderr"].connect_weak(self._handle_line)
-            self._process.signals["completed"].connect_weak(self._exited)
-            
self._process.set_stop_command(notifier.WeakCallback(self._end_child))
-        return self._process
-
-
-    def _make_dummy_input_config(self):
-        """
-        There is no way to make MPlayer ignore keys from the X11 window.  So
-        this hack makes a temp input file that maps all keys to a dummy (and
-        non-existent) command which causes MPlayer not to react to any key
-        presses, allowing us to implement our own handlers.  The temp file is
-        deleted once MPlayer has read it.
-        """
-        keys = filter(lambda x: x not in string.whitespace, string.printable)
-        keys = list(keys) + self._mp_info["keylist"]
-        fp, filename = tempfile.mkstemp()
-        for key in keys:
-            os.write(fp, "%s noop\n" % key)
-        os.close(fp)
-        return filename
+        if self._frame_shmem:
+            self._frame_shmem.detach()
+        if self._osd_shmem:
+            self._osd_shmem.detach()
 
 
     def _handle_mp_info(self, info):
@@ -198,39 +183,44 @@
         self._mp_info = info
 
 
-    def _handle_line(self, line):
-        if self._debug:
-            if re.search("@@@|outbuf|overlay", line, re.I) and self._debug == 
1:
-                print line
-            elif line[:2] not in ("A:", "V:") and self._debug == 2:
-                print line
-            elif self._debug == 3:
-                print line
+
+    #
+    # child IO
+    #
+
+    def _child_stop(self):
+        self._child_write("quit")
+        # Could be paused, try sending again.
+        self._child_write("quit")
+
+
+    def _child_handle_line(self, line):
+        if re.search("@@@|outbuf|overlay", line, re.I):
+            log.info(line)
+        elif line[:2] not in ("A:", "V:"):
+            log.debug(line)
+        elif USE_GDB:
+            log.debug(line)
 
         if line.startswith("V:") or line.startswith("A:"):
             m = MPlayer.RE_STATUS.search(line)
             if m:
                 old_pos = self._position
                 self._position = float((m.group(1) or m.group(2)).replace(",", 
"."))
-                if self._position - old_pos < 0 or self._position - old_pos > 
1:
-                    self.signals["seek"].emit(self._position)
+#                 if self._position - old_pos < 0 or self._position - old_pos 
> 1:
+#                     self.signals["seek"].emit(self._position)
 
                 # XXX this logic won't work with seek-while-paused patch; state
                 # will be "playing" after a seek.
-                if self._state == STATE_PAUSED:
-                    self.signals["pause_toggle"].emit()
                 if self._state not in (STATE_PAUSED, STATE_PLAYING):
                     self.set_frame_output_mode()
                     self._state = STATE_PLAYING
                     self.signals["stream_changed"].emit()
                 elif self._state != STATE_PLAYING:
                     self._state = STATE_PLAYING
-                    self.signals["play"].emit()
 
         elif line.startswith("  =====  PAUSE"):
             self._state = STATE_PAUSED
-            self.signals["pause_toggle"].emit()
-            self.signals["pause"].emit()
 
         elif line.startswith("ID_") and line.find("=") != -1:
             attr, value = line.split("=")
@@ -267,24 +257,25 @@
             if m:
                 width, height = int(m.group(1)), int(m.group(2))
                 try:
-                    if self._overlay_shmem:
-                        self._overlay_shmem.detach()
+                    if self._osd_shmem:
+                        self._osd_shmem.detach()
                 except shm.error:
                     pass
-                self._overlay_shmem = 
shm.memory(shm.getshmid(self._get_overlay_shm_key()))
-                self._overlay_shmem.attach()
+                self._osd_shmem = shm.memory(\
+                    shm.getshmid(self._osd_shmkey))
+                self._osd_shmem.attach()
 
-                self.signals["osd_configure"].emit(width, height, 
self._overlay_shmem.addr + 16,
-                                                   width, height)
+                self.signals["osd_configure"].emit(\
+                    width, height, self._osd_shmem.addr + 16, width, height)
 
         elif line.startswith("outbuf:") and line.find("shmem key") != -1:
             try:
-                if self._outbuf_shmem:
-                    self._outbuf_shmem.detach()
+                if self._frame_shmem:
+                    self._frame_shmem.detach()
             except shm.error:
                 pass
-            self._outbuf_shmem = 
shm.memory(shm.getshmid(self._get_outbuf_shm_key()))
-            self._outbuf_shmem.attach()
+            self._frame_shmem = shm.memory(shm.getshmid(self._frame_shmkey))
+            self._frame_shmem.attach()
             self.set_frame_output_mode()  # Sync
 
         elif line.startswith("EOF code"):
@@ -296,53 +287,42 @@
             file = line[line.find("file")+5:]
             os.unlink(file)
 
-        #elif line.startswith("File not found"):
-        #    file = line[line.find(":")+2:]
-        #    raise IOError, (2, "No such file or directory: %s" % file)
-
         elif line.startswith("FATAL:"):
-            raise MPlayerError, line.strip()
+            log.error(line.strip())
 
-        elif self._debug > 3 and line.startswith("Program received signal 
SIGSEGV"):
+        elif USE_GDB and line.startswith("Program received signal SIGSEGV"):
             # Mplayer crashed, issue backtrace.
-            self._process.write("thread apply all bt\n")
+            self._child_app.write("thread apply all bt\n")
 
         if line.strip():
-            self.signals["output"].emit(line)
             self._last_line = line
 
 
-    def _get_overlay_shm_key(self):
-        name = "mplayer-overlay.%s" % self._instance_id
-        return int(md5.md5(name).hexdigest()[:7], 16)
-
-    def _get_outbuf_shm_key(self):
-        name = "mplayer-outbuf.%s" % self._instance_id
-        return int(md5.md5(name).hexdigest()[:7], 16)
-
-
-    def _slave_cmd(self, cmd):
-        if not self.is_alive():
+    def _child_write(self, cmd):
+        if not self._child_is_alive():
             return False
+        log.info('mplayer send %s', cmd)
+        self._child_app.write(cmd + "\n")
 
-        if self._debug >= 1:
-            print "SLAVE:", cmd
-        self._process.write(cmd + "\n")
 
-
-    def _exited(self, exitcode):
-        log.debug('mplayer exited')
+    def _child_exited(self, exitcode):
+        log.info('mplayer exited')
         self._state = STATE_NOT_RUNNING
-        # exit code is strange somehow
-        # if exitcode != 0:
-        #     raise MPlayerExitError, (exitcode, self._last_line)
 
 
-    def is_alive(self):
-        return self._process and self._process.is_alive()
+    def _child_is_alive(self):
+        return self._child_app and self._child_app.is_alive()
+
 
-    def open(self, mrl, user_args = None):
 
+    #
+    # Methods for MediaPlayer subclasses
+    #
+
+    def open(self, mrl):
+        """
+        Open mrl.
+        """
         if self.get_state() != STATE_NOT_RUNNING:
             print 'Error: not idle'
             return False
@@ -369,22 +349,31 @@
         else:
             self._file = mrl
 
-        if user_args:
-            self._file_args.append(user_args)
+        self._state = STATE_OPENING
+
+        # We have a problem at this point. The 'open' function is used to
+        # open the stream and provide information about it. After that, the
+        # caller can still change stuff before calling play. But the current
+        # code uses -identify in mplayer on startup. This will give us the
+        # information just before starting. So we change from STATE_OPENING
+        # to STATE_OPEN just now and do not have information at this point.
+
+        self._state = STATE_OPEN
 
 
     def play(self):
+        """
+        Start playback.
+        """
         log.debug('mplayer start playback')
-        
+
         # we know that self._mp_info has to be there or the object would
         # not be selected by the generic one. FIXME: verify that!
         assert(self._mp_info)
 
-        keyfile = self._make_dummy_input_config()
-
         filters = self._filters_pre[:]
         if 'outbuf' in self._mp_info['video_filters']:
-            filters += ["outbuf=%s:yv12" % self._get_outbuf_shm_key()]
+            filters += ["outbuf=%s:yv12" % self._frame_shmkey]
 
         filters += ["scale=%d:-2" % self._size[0], "expand=%d:%d" % self._size,
                     "dsize=%d:%d" % self._size ]
@@ -393,24 +382,37 @@
 
         filters += self._filters_add
         if 'overlay' in self._mp_info['video_filters']:
-            filters += ["overlay=%s" % self._get_overlay_shm_key()]
+            filters += ["overlay=%s" % self._osd_shmkey]
 
         args = [ "-v", "-slave", "-osdlevel", "0", "-nolirc", "-nojoystick", \
                  "-nomouseinput", "-nodouble", "-fixed-vo", "-identify", \
                  "-framedrop", "-vf", ",".join(filters) ]
 
-        if isinstance(self._window, display.X11Window):
+        if isinstance(self._window, kaa.display.X11Window):
             args.extend((
                 "-wid", hex(self._window.get_id()),
                 "-display", self._window.get_display().get_string(),
-                "-input", "conf=%s" % keyfile))
+                "-input"))
         else:
             # no window == no video out
             args.extend(('-vo', 'null'))
-            
+
         # FIXME, add more settings here
         args += [ '-ao', self._config.audio.driver ]
-        
+
+        # There is no way to make MPlayer ignore keys from the X11 window.  So
+        # this hack makes a temp input file that maps all keys to a dummy (and
+        # non-existent) command which causes MPlayer not to react to any key
+        # presses, allowing us to implement our own handlers.  The temp file is
+        # deleted once MPlayer has read it.
+        keys = filter(lambda x: x not in string.whitespace, string.printable)
+        keys = list(keys) + self._mp_info["keylist"]
+        fp, keyfile = tempfile.mkstemp()
+        for key in keys:
+            os.write(fp, "%s noop\n" % key)
+        os.close(fp)
+        args.append("conf=%s" % keyfile)
+
         if self._file_args:
             if isinstance(self._file_args, str):
                 args.extend(self._file_args.split(' '))
@@ -420,59 +422,123 @@
         if self._file:
             args.append(self._file)
 
-        self._spawn(args)
-        self._state = STATE_OPENING
+        log.info("spawn: %s %s", self._mp_cmd, args)
 
+        if USE_GDB:
+            self._child_app = kaa.notifier.Process("gdb")
+            self._child_app.start(self._mp_cmd)
+            self._child_app.write("run %s\n" % args)
+        else:
+            self._child_app = kaa.notifier.Process(self._mp_cmd)
+            self._child_app.start(args)
+
+        self._child_app.signals["stdout"].connect_weak(self._child_handle_line)
+        self._child_app.signals["stderr"].connect_weak(self._child_handle_line)
+        self._child_app.signals["completed"].connect_weak(self._child_exited)
+        
self._child_app.set_stop_command(kaa.notifier.WeakCallback(self._child_stop))
         return
 
 
+    def stop(self):
+        """
+        Stop playback.
+        """
+        if self._child_app:
+            self._child_app.stop()
+            self._state = STATE_SHUTDOWN
+
+
     def pause(self):
-        if self.get_state() == STATE_PLAYING:
-            self._slave_cmd("pause")
+        """
+        Pause playback.
+        """
+        self._child_write("pause")
 
 
     def resume(self):
-        if self.get_state() == STATE_PAUSED:
-            self._slave_cmd("pause")
+        """
+        Resume playback.
+        """
+        self._child_write("pause")
+
 
     def seek(self, value, type):
+        """
+        Seek. Possible types are SEEK_RELATIVE, SEEK_ABSOLUTE and 
SEEK_PERCENTAGE.
+        """
         s = [SEEK_RELATIVE, SEEK_PERCENTAGE, SEEK_ABSOLUTE]
-        self._slave_cmd("seek %f %s" % (value, s.index(type)))
-
-    def stop(self):
-        self.die()
-        self._state = STATE_SHUTDOWN
-
-    def _end_child(self):
-        self._slave_cmd("quit")
-        # Could be paused, try sending again.
-        self._slave_cmd("quit")
+        self._child_write("seek %f %s" % (value, s.index(type)))
 
-    def die(self):
-        if self._process:
-            self._process.stop()
 
     def get_position(self):
+        """
+        Get current playing position.
+        """
         return self._position
 
+
     def get_info(self):
+        """
+        Get information about the stream.
+        """
         return self._file_info
 
+
+    #
+    # Methods for filter handling (not yet in generic and base
+    #
+
     def prepend_filter(self, filter):
+        """
+        Add filter to the prepend list.
+        """
         self._filters_pre.append(filter)
 
+
     def append_filter(self, filter):
+        """
+        Add filter to the normal filter list.
+        """
         self._filters_add.append(filter)
 
+
     def get_filters(self):
+        """
+        Return all filter set.
+        """
         return self._filters_pre + self._filters_add
 
+
     def remove_filter(self, filter):
+        """
+        Remove filter for filter list.
+        """
         for l in (self._filters_pre, self._filters_add):
             if filter in l:
                 l.remove(filter)
 
+
+    #
+    # Methods and helper for MediaPlayer subclasses for CAP_OSD
+    #
+
+    def osd_can_update(self):
+        """
+        Returns True if it is safe to write to the player's shared memory
+        buffer used for OSD, and False otherwise.  If this buffer is written
+        to even though this function returns False, the OSD may exhibit
+        corrupt output or tearing during animations.
+        See generic.osd_can_update for details.
+        """
+        if not self._osd_shmem:
+            return False
+
+
     def osd_update(self, alpha = None, visible = None, invalid_regions = None):
+        """
+        Updates the OSD of the player based on the given argments:
+        See generic.osd_update for details.
+        """
         cmd = []
         if alpha != None:
             cmd.append("alpha=%d" % alpha)
@@ -481,59 +547,40 @@
         if invalid_regions:
             for (x, y, w, h) in invalid_regions:
                 cmd.append("invalidate=%d:%d:%d:%d" % (x, y, w, h))
-        self._slave_cmd("overlay %s" % ",".join(cmd))
+        self._child_write("overlay %s" % ",".join(cmd))
         self._overlay_set_lock(BUFFER_LOCKED)
 
-
-    def osd_can_update(self):
-        if not self._overlay_shmem:
-            return False
-
         try:
-            if ord(self._overlay_shmem.read(1)) == BUFFER_UNLOCKED:
+            if ord(self._osd_shmem.read(1)) == BUFFER_UNLOCKED:
                 return True
         except shm.error:
-            self._overlay_shmem.detach()
-            self._overlay_shmem = None
+            self._osd_shmem.detach()
+            self._osd_shmem = None
 
         return False
 
 
     def _overlay_set_lock(self, byte):
         try:
-            if self._overlay_shmem and self._overlay_shmem.attached:
-                self._overlay_shmem.write(chr(byte))
+            if self._osd_shmem and self._osd_shmem.attached:
+                self._osd_shmem.write(chr(byte))
         except shm.error:
-            self._overlay_shmem.detach()
-            self._overlay_shmem = None
+            self._osd_shmem.detach()
+            self._osd_shmem = None
 
 
-    def _check_new_frame(self):
-        if not self._outbuf_shmem:
-            return
 
-        try:
-            lock, width, height, aspect = struct.unpack("hhhd", 
self._outbuf_shmem.read(16))
-        except shm.error:
-            self._outbuf_shmem.detach()
-            self._outbuf_shmem = None
-            return
-
-        if lock & BUFFER_UNLOCKED:
-            return
-
-        if width > 0 and height > 0 and aspect > 0:
-            self.signals["frame"].emit(width, height, aspect, 
self._outbuf_shmem.addr + 16, "yv12")
-
-    def unlock_frame_buffer(self):
-        try:
-            self._outbuf_shmem.write(chr(BUFFER_UNLOCKED))
-        except shm.error:
-            self._outbuf_shmem.detach()
-            self._outbuf_shmem = None
+    #
+    # Methods and helper for MediaPlayer subclasses for CAP_CANVAS
+    #
 
 
     def set_frame_output_mode(self, vo = None, notify = None, size = None):
+        """
+        Controls if and how frames are delivered via the 'frame' signal, and
+        whether or not frames are drawn to the vo driver's video window.
+        See generic.set_frame_output_mode for details.
+        """
         if vo != None:
             self._cur_outbuf_mode[0] = vo
         if notify != None:
@@ -545,7 +592,7 @@
         if size != None:
             self._cur_outbuf_mode[2] = size
 
-        if not self.is_alive():
+        if not self._child_is_alive():
             return
 
         mode = { (False, False): 0, (True, False): 1,
@@ -553,10 +600,38 @@
 
         size = self._cur_outbuf_mode[2]
         if size == None:
-            self._slave_cmd("outbuf %d" % mode)
+            self._child_write("outbuf %d" % mode)
         else:
-            self._slave_cmd("outbuf %d %d %d" % (mode, size[0], size[1]))
+            self._child_write("outbuf %d %d %d" % (mode, size[0], size[1]))
 
-    def nav_command(self, input):
-        # MPlayer has no dvdnav support (yet).
-        return False
+
+    def unlock_frame_buffer(self):
+        """
+        Unlocks the frame buffer provided by the last 'frame' signal
+        See generic.unlock_frame_buffer for details.
+        """
+        try:
+            self._frame_shmem.write(chr(BUFFER_UNLOCKED))
+        except shm.error:
+            self._frame_shmem.detach()
+            self._frame_shmem = None
+
+
+    def _check_new_frame(self):
+        if not self._frame_shmem:
+            return
+
+        try:
+            lock, width, height, aspect = \
+                  struct.unpack("hhhd", self._frame_shmem.read(16))
+        except shm.error:
+            self._frame_shmem.detach()
+            self._frame_shmem = None
+            return
+
+        if lock & BUFFER_UNLOCKED:
+            return
+
+        if width > 0 and height > 0 and aspect > 0:
+            self.signals["frame"].emit(\
+                width, height, aspect, self._frame_shmem.addr + 16, "yv12")

Modified: trunk/popcorn/src/backends/xine/config.py
==============================================================================
--- trunk/popcorn/src/backends/xine/config.py   (original)
+++ trunk/popcorn/src/backends/xine/config.py   Fri Nov  3 21:26:35 2006
@@ -1,12 +1,45 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# xine/config.py - config for xine backend
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------------
+# kaa.popcorn - Generic Player API
+# Copyright (C) 2006 Jason Tackaberry, Dirk Meyer
+#
+# Please see the file AUTHORS for a complete list of authors.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------------
+
+# python imports
 import os
 import sys
 import gettext
+
+# kaa imports
 from kaa.config import Var, Group, Dict, List
 
+# i18 setup for this file
 path = os.path.normpath(
     os.path.join(os.path.dirname(__file__), '../../../../share/locale'))
 i18n = gettext.translation('kaa.popcorn', path, fallback=True).ugettext
 
+# config variable for xine
 config = Group(desc=i18n('xine configuration'), schema=[
     Var(name='activate', default=True, desc=i18n('activate backend'))
     ])

Modified: trunk/popcorn/src/backends/xine/player.py
==============================================================================
--- trunk/popcorn/src/backends/xine/player.py   (original)
+++ trunk/popcorn/src/backends/xine/player.py   Fri Nov  3 21:26:35 2006
@@ -1,17 +1,48 @@
+# -*- coding: iso-8859-1 -*-
+# -----------------------------------------------------------------------------
+# xine/player.py - xine backend
+# -----------------------------------------------------------------------------
+# $Id$
+#
+# -----------------------------------------------------------------------------
+# kaa.popcorn - Generic Player API
+# Copyright (C) 2006 Jason Tackaberry, Dirk Meyer
+#
+# Please see the file AUTHORS for a complete list of authors.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
+# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# -----------------------------------------------------------------------------
+
+# python imports
 import sys
 import os
 import md5
 import struct
 import logging
 
+# kaa imports
 import kaa
 import kaa.notifier
 import kaa.shm
 import kaa.xine as xine
 
+# kaa.popcorn imports
 from kaa.popcorn.backends.base import MediaPlayer
-from kaa.popcorn.ptypes import *
 from kaa.popcorn.utils import ChildProcess, parse_mrl
+from kaa.popcorn.ptypes import *
 
 # get logging object
 log = logging.getLogger('popcorn.xine')
@@ -21,29 +52,34 @@
 
 class Xine(MediaPlayer):
 
-    _instance_count = 0
-
     def __init__(self):
         super(Xine, self).__init__()
-        self._instance_id = "kaaxine-%d-%d" % (os.getpid(), 
Xine._instance_count)
-        Xine._instance_count += 1
-
-        self._osd_shmkey = int(md5.md5(self._instance_id + 
"osd").hexdigest()[:7], 16)
-        self._frame_shmkey = int(md5.md5(self._instance_id + 
"frame").hexdigest()[:7], 16)
-        self._osd_shmem = self._frame_shmem = None
-        #kaa.signals["shutdown"].connect_weak(self._remove_shmem)
         self._check_new_frame_timer = 
kaa.notifier.WeakTimer(self._check_new_frame)
-
         self._is_in_menu = False
-        self._state_data = None
         self._stream_info = {}
         self._position = 0.0
         self._cur_frame_output_mode = [True, False, None] # vo, shmem, size
+        self._child_spawn()
+
+
+    #
+    # child handling
+    #
+
+    def _child_spawn(self):
+        # Launch self (-u is unbuffered stdout)
+        script = os.path.join(os.path.dirname(__file__), 'main.py')
+        self._xine = ChildProcess(self, script, str(self._instance_id))
+        self._xine.signals["completed"].connect_weak(self._child_exited)
+        self._xine.set_stop_command(kaa.notifier.WeakCallback(self._xine.die))
+        self._xine.start()
 
-        self._spawn()
 
+    def _child_exited(self, exitcode):
+        log.debug('xine child dead')
+        self._xine = None
 
-    def _remove_shmem(self):
+        # remove shared memory
         if self._osd_shmem:
             try:
                 self._osd_shmem.detach()
@@ -60,33 +96,12 @@
             except kaa.shm.error:
                 pass
             self._frame_shmem = None
-
-
-    def _spawn(self):
-        # Launch self (-u is unbuffered stdout)
-        script = os.path.join(os.path.dirname(__file__), 'main.py')
-        self._xine = ChildProcess(self, script, str(self._instance_id))
-        self._xine.signals["completed"].connect_weak(self._exited)
-        self._xine.set_stop_command(kaa.notifier.WeakCallback(self._end_child))
-        self._xine.start()
-
-
-
-    def _end_child(self):
-        self._state = STATE_SHUTDOWN
-        self._xine.die()
-
-
-    def _exited(self, exitcode):
-        log.debug('xine child dead')
-        self._xine = None
-        self._remove_shmem()
         self._state = STATE_NOT_RUNNING
 
 
-    # 
#############################################################################
+    #
     # Commands from child
-    # 
#############################################################################
+    #
 
     def _child_set_status(self, pos, time, length, status, speed):
         old_pos = self._position
@@ -96,20 +111,14 @@
         if status == 2:
             if self.get_state() not in (STATE_PAUSED, STATE_PLAYING):
                 self._state = STATE_PLAYING
-                self.signals["play"].emit()
             if speed == xine.SPEED_PAUSE and self.get_state() != STATE_PAUSED:
                 self._state = STATE_PAUSED
-                self.signals["pause_toggle"].emit()
-                self.signals["pause"].emit()
             elif speed > xine.SPEED_PAUSE and self.get_state() != 
STATE_PLAYING:
                 prev_state = self.get_state()
                 self._state = STATE_PLAYING
-                if prev_state == STATE_PAUSED:
-                    self.signals["pause_toggle"].emit()
-                self.signals["play"].emit()
-
-            if self._position - old_pos < 0 or self._position - old_pos > 1:
-                self.signals["seek"].emit(self._position)
+            # TODO:
+            # if self._position - old_pos < 0 or self._position - old_pos > 1:
+            # self.signals["seek"].emit(self._position)
         elif status in (0, 1):
             if self.get_state() in (STATE_PAUSED, STATE_PLAYING):
                 # Stream ended.
@@ -131,8 +140,8 @@
 
         # TODO: remember these values and emit them to new connections to
         # this signal after this point.
-        self.signals["osd_configure"].emit(width, height, self._osd_shmem.addr 
+ 16,
-                                           width, height)
+        self.signals["osd_configure"].emit(\
+            width, height, self._osd_shmem.addr + 16, width, height)
 
 
     def _child_resize(self, size):
@@ -150,8 +159,7 @@
         self._stream_info = info
 
         if self._state == STATE_OPENING:
-            self._xine.play()
-            self.set_frame_output_mode()
+            self._state = STATE_OPEN
 
         if changed:
             self.signals["stream_changed"].emit()
@@ -167,10 +175,9 @@
         self._state = STATE_IDLE
 
 
-    # 
#############################################################################
+    #
     # Window handling
-    # 
#############################################################################
-
+    #
 
     def _window_visibility_event(self):
         self._xine.window_changed(self._window.get_id(), 
self._window.get_size(),
@@ -184,24 +191,45 @@
         self._xine.window_changed(self._window.get_id(), size,
                                    self._window.get_visible(), [])
 
+    def set_window(self, window):
+        """
+        Set a window for the player (override from MediaPlayer)
+        """
+        old_window = self._window
+        super(Xine, self).set_window(window)
 
+        if old_window and old_window != self._window:
+            # Disconnect signals from existing window.
+            
old_window.signals["configure_event"].disconnect(self._window_configure_event)
+            
old_window.signals["map_event"].disconnect(self._window_visibility_event)
+            
old_window.signals["unmap_event"].disconnect(self._window_visibility_event)
+            
old_window.signals["expose_event"].disconnect(self._window_expose_event)
+
+        if window and window.signals and old_window != self._window:
+            
window.signals["configure_event"].connect_weak(self._window_configure_event)
+            
window.signals["map_event"].connect_weak(self._window_visibility_event)
+            
window.signals["unmap_event"].connect_weak(self._window_visibility_event)
+            
window.signals["expose_event"].connect_weak(self._window_expose_event)
+
+        # Sends a window_changed command to slave.
+        if window:
+            self._window_visibility_event()
 
-    # 
#############################################################################
-    # API exposed to generic player
-    # 
#############################################################################
 
+    #
+    # Methods for MediaPlayer subclasses
+    #
 
     def open(self, mrl):
+        """
+        Open mrl.
+        """
         scheme, path = parse_mrl(mrl)
         if scheme not in self.get_supported_schemes():
             raise ValueError, "Unsupported mrl scheme '%s'" % scheme
-
         self._mrl = "%s:%s" % (scheme, path)
-
-
-    def play(self):
         if not self._xine:
-            self._spawn()
+            self._child_spawn()
 
         wid = None
         if self._window:
@@ -209,43 +237,126 @@
         self._xine.setup(wid=wid)
 
         self._position = 0.0
-        log.debug('xine play')
+        log.debug('xine open')
         self._xine.open(self._mrl)
         self._state = STATE_OPENING
 
 
+    def play(self):
+        """
+        Start playback.
+        """
+        log.debug('play')
+        self._xine.play()
+        self.set_frame_output_mode()
+
+
+    def stop(self):
+        """
+        Stop playback.
+        """
+        log.debug('xine stop')
+        self._xine.stop()
+
+
     def pause(self):
+        """
+        Pause playback.
+        """
         self._xine.pause()
 
 
     def resume(self):
+        """
+        Resume playback.
+        """
         self._xine.resume()
 
 
-    def stop(self):
-        log.debug('xine stop')
-        self._xine.stop()
-
-
-    def die(self):
+    def release(self):
+        """
+        Release audio and video devices.
+        """
         if self._xine:
             self._state = STATE_SHUTDOWN
             self._xine.die()
 
 
     def seek(self, value, type):
+        """
+        Seek. Possible types are SEEK_RELATIVE, SEEK_ABSOLUTE and 
SEEK_PERCENTAGE.
+        """
         self._xine.seek(value, type)
 
 
+    def get_position(self):
+        """
+        Get current playing position.
+        """
+        return self._position
+
+
     def get_info(self):
+        """
+        Get information about the stream.
+        """
         return self._stream_info
 
 
-    def osd_update(self, alpha = None, visible = None, invalid_regions = None):
-        self._xine.osd_update(alpha, visible, invalid_regions)
+    def nav_command(self, input):
+        """
+        Issue the navigation command to the player.
+        """
+        map = {
+            "up": xine.EVENT_INPUT_UP,
+            "down": xine.EVENT_INPUT_DOWN,
+            "left": xine.EVENT_INPUT_LEFT,
+            "right": xine.EVENT_INPUT_RIGHT,
+            "select": xine.EVENT_INPUT_SELECT,
+            "prev": xine.EVENT_INPUT_PREVIOUS,
+            "next": xine.EVENT_INPUT_NEXT,
+            "angle_prev": xine.EVENT_INPUT_ANGLE_PREVIOUS,
+            "angle_next": xine.EVENT_INPUT_ANGLE_NEXT,
+            "menu1": xine.EVENT_INPUT_MENU1,
+            "menu2": xine.EVENT_INPUT_MENU2,
+            "menu3": xine.EVENT_INPUT_MENU3,
+            "menu4": xine.EVENT_INPUT_MENU4,
+            "0": xine.EVENT_INPUT_NUMBER_0,
+            "1": xine.EVENT_INPUT_NUMBER_1,
+            "2": xine.EVENT_INPUT_NUMBER_2,
+            "3": xine.EVENT_INPUT_NUMBER_3,
+            "4": xine.EVENT_INPUT_NUMBER_4,
+            "5": xine.EVENT_INPUT_NUMBER_5,
+            "6": xine.EVENT_INPUT_NUMBER_6,
+            "7": xine.EVENT_INPUT_NUMBER_7,
+            "8": xine.EVENT_INPUT_NUMBER_8,
+            "9": xine.EVENT_INPUT_NUMBER_9
+        }
+        if input in map:
+            self._xine.input(map[input])
+            return True
+        return False
+
+
+    def is_in_menu(self):
+        """
+        Return True if the player is in a navigation menu.
+        """
+        return self._is_in_menu
 
 
+    #
+    # Methods and helper for MediaPlayer subclasses for CAP_OSD
+    #
+
     def osd_can_update(self):
+        """
+        Returns True if it is safe to write to the player's shared memory
+        buffer used for OSD, and False otherwise.  If this buffer is written
+        to even though this function returns False, the OSD may exhibit
+        corrupt output or tearing during animations.
+        See generic.osd_can_update for details.
+        """
         if not self._osd_shmem:
             return False
 
@@ -258,7 +369,26 @@
 
         return False
 
+
+    def osd_update(self, alpha = None, visible = None, invalid_regions = None):
+        """
+        Updates the OSD of the player based on the given argments:
+        See generic.osd_update for details.
+        """
+        self._xine.osd_update(alpha, visible, invalid_regions)
+
+
+
+    #
+    # Methods and helper for MediaPlayer subclasses for CAP_CANVAS
+    #
+
     def set_frame_output_mode(self, vo = None, notify = None, size = None):
+        """
+        Controls if and how frames are delivered via the 'frame' signal, and
+        whether or not frames are drawn to the vo driver's video window.
+        See generic.set_frame_output_mode for details.
+        """
         if vo != None:
             self._cur_frame_output_mode[0] = vo
         if notify != None:
@@ -279,37 +409,25 @@
         self._xine.frame_output(vo, notify, size)
 
 
-    def set_window(self, window):
-        old_window = self._window
-        super(Xine, self).set_window(window)
-
-        if old_window and old_window != self._window:
-            # Disconnect signals from existing window.
-            
old_window.signals["configure_event"].disconnect(self._window_configure_event)
-            
old_window.signals["map_event"].disconnect(self._window_visibility_event)
-            
old_window.signals["unmap_event"].disconnect(self._window_visibility_event)
-            
old_window.signals["expose_event"].disconnect(self._window_expose_event)
-
-        if window and window.signals and old_window != self._window:
-            
window.signals["configure_event"].connect_weak(self._window_configure_event)
-            
window.signals["map_event"].connect_weak(self._window_visibility_event)
-            
window.signals["unmap_event"].connect_weak(self._window_visibility_event)
-            
window.signals["expose_event"].connect_weak(self._window_expose_event)
-
-        # Sends a window_changed command to slave.
-        if window:
-            self._window_visibility_event()
+    def unlock_frame_buffer(self):
+        """
+        Unlocks the frame buffer provided by the last 'frame' signal
+        See generic.unlock_frame_buffer for details.
+        """
+        try:
+            self._frame_shmem.write(chr(BUFFER_UNLOCKED))
+        except kaa.shm.error:
+            self._frame_shmem.detach()
+            self._frame_shmem = None
 
 
-    def get_position(self):
-        return self._position
-
     def _check_new_frame(self):
         if not self._frame_shmem:
             return
 
         try:
-            lock, width, height, aspect = struct.unpack("hhhd", 
self._frame_shmem.read(16))
+            lock, width, height, aspect = struct.unpack(\
+                "hhhd", self._frame_shmem.read(16))
         except kaa.shm.error:
             self._frame_shmem.detach()
             self._frame_shmem = None
@@ -319,48 +437,5 @@
             return
 
         if width > 0 and height > 0 and aspect > 0:
-            self.signals["frame"].emit(width, height, aspect, 
self._frame_shmem.addr + 16, "bgr32")
-
-
-    def unlock_frame_buffer(self):
-        try:
-            self._frame_shmem.write(chr(BUFFER_UNLOCKED))
-        except kaa.shm.error:
-            self._frame_shmem.detach()
-            self._frame_shmem = None
-
-
-    def nav_command(self, input):
-        map = {
-            "up": xine.EVENT_INPUT_UP,
-            "down": xine.EVENT_INPUT_DOWN,
-            "left": xine.EVENT_INPUT_LEFT,
-            "right": xine.EVENT_INPUT_RIGHT,
-            "select": xine.EVENT_INPUT_SELECT,
-            "prev": xine.EVENT_INPUT_PREVIOUS,
-            "next": xine.EVENT_INPUT_NEXT,
-            "angle_prev": xine.EVENT_INPUT_ANGLE_PREVIOUS,
-            "angle_next": xine.EVENT_INPUT_ANGLE_NEXT,
-            "menu1": xine.EVENT_INPUT_MENU1,
-            "menu2": xine.EVENT_INPUT_MENU2,
-            "menu3": xine.EVENT_INPUT_MENU3,
-            "menu4": xine.EVENT_INPUT_MENU4,
-            "0": xine.EVENT_INPUT_NUMBER_0,
-            "1": xine.EVENT_INPUT_NUMBER_1,
-            "2": xine.EVENT_INPUT_NUMBER_2,
-            "3": xine.EVENT_INPUT_NUMBER_3,
-            "4": xine.EVENT_INPUT_NUMBER_4,
-            "5": xine.EVENT_INPUT_NUMBER_5,
-            "6": xine.EVENT_INPUT_NUMBER_6,
-            "7": xine.EVENT_INPUT_NUMBER_7,
-            "8": xine.EVENT_INPUT_NUMBER_8,
-            "9": xine.EVENT_INPUT_NUMBER_9
-        }
-        if input in map:
-            self._xine.input(map[input])
-            return True
-        return False
-
-
-    def is_in_menu(self):
-        return self._is_in_menu
+            a = self._frame_shmem.addr + 16
+            self.signals["frame"].emit(width, height, aspect, a, "bgr32")

Modified: trunk/popcorn/src/generic.py
==============================================================================
--- trunk/popcorn/src/generic.py        (original)
+++ trunk/popcorn/src/generic.py        Fri Nov  3 21:26:35 2006
@@ -89,14 +89,17 @@
             # signals created by this class
             "open": kaa.notifier.Signal(),
             "start": kaa.notifier.Signal(),
-            "failed": kaa.notifier.Signal(),
+            "play": kaa.notifier.Signal(),
             "end": kaa.notifier.Signal(),
-
-            # pass thru signals from player
+            "failed": kaa.notifier.Signal(),
             "pause": kaa.notifier.Signal(),
-            "play": kaa.notifier.Signal(),
             "pause_toggle": kaa.notifier.Signal(),
-            "seek": kaa.notifier.Signal(),
+
+            # "seek": kaa.notifier.Signal(),
+            # Process died (shared memory will go away)
+            # "shm_quit": kaa.notifier.Signal()
+
+            # pass thru signals from player
             "stream_changed": kaa.notifier.Signal(),
             # Emitted when a new frame is availabnle.  See 
             # set_frame_output_mode() doc for more info.
@@ -104,8 +107,6 @@
             # Emitted when OSD buffer has changed.  See osd_update() doc
             # for more info.
             "osd_configure": kaa.notifier.Signal(),  # CAP_OSD
-            # Process died (shared memory will go away)
-            "shm_quit": kaa.notifier.Signal()
         }
 
         # pending commands
@@ -130,20 +131,21 @@
             self._open_mrl, self._open_caps, player, exclude)
 
         
-    def _state_change(self, signal):
+    def _state_change(self, old_state, state):
         """
-        The used player changed its state and emited a signal. This function
-        will emit the same signal to the application, handles some internal
-        changes and will call the pending calls based on the new state.
         """
-        log.debug('player signal: %s', signal)
-        
-        if signal == 'failed':
-            # The playing has failed. This means that the player we wanted to
-            # use was unable to play this file. In that case we add the player
-            # the list of of failed_player and try to find a better one.
-            # TODO: What happens if the user tries to open a new file while
-            # we are trying to find a good player for the old mrl?
+        log.debug('player %s -> %s', old_state, state)
+
+        if old_state in (STATE_OPENING, STATE_OPEN) and \
+               state in (STATE_IDLE, STATE_NOT_RUNNING, STATE_SHUTDOWN):
+            # From STATE_OPEN(ING) to not playing. This means something
+            # went wrong and the player failed to play.  This means
+            # that the player we wanted to use was unable to play this
+            # file. In that case we add the player the list of of
+            # failed_player and try to find a better one.
+            # TODO: What happens if the user tries to open a new file
+            # while we are trying to find a good player for the old
+            # mrl?
             self._failed_player.append(self.get_player_id())
             self._pending = []
             self._player.release()
@@ -153,23 +155,44 @@
                 self._create_player(cls)
                 self._open(self._open_mrl)
                 self.play()
-                return
+                return True
+            # everything failed
+            self.signals["failed"].emit()
+
+        if old_state == STATE_OPENING and state == STATE_OPEN:
+            # stream open now for playing
+            print self._player.get_info()
+            self.signals["open"].emit(self._player.get_info())
             
-        if signal in self.signals:
-            # signal the change
-            self.signals[signal].emit()
-
-        if signal in ('end', 'failed') and self.get_state() == STATE_IDLE \
-               and not self._pending:
-            # no new mrl to play, release player
-            log.info('release player')
-            return self._player.release()
-
-        if signal == 'release':
+        if old_state == STATE_OPEN and \
+               state in (STATE_PLAYING, STATE_PAUSED):
+            # From STATE_OPENING to playing. Signal playback start
+            self.signals["start"].emit()
+            self.signals["play"].emit()
+
+        if old_state in (STATE_PLAYING, STATE_PAUSED) and \
+               state in (STATE_IDLE, STATE_NOT_RUNNING):
+            # From playing to finished. Signal end.
+            self.signals["end"].emit()
+
+        if old_state == STATE_PLAYING and state == STATE_PAUSED:
+            self.signals["pause"].emit()
+            self.signals["pause_toggle"].emit()
+            
+        if old_state == STATE_PAUSED and state == STATE_PLAYING:
+            self.signals["play"].emit()
+            self.signals["pause_toggle"].emit()
+            
+        if state == STATE_NOT_RUNNING == self._player.get_state():
             # Player released the video and audio device. Right now we set
             # self._player to None to simulate STATE_NOT_RUNNING.
             # This needs to be fixed.
             self._player = None
+        
+        if self.get_state() == STATE_IDLE and not self._pending:
+            # no new mrl to play, release player
+            log.info('release player')
+            return self._player.release()
 
         # Handle pending calls based on the new state. The variable blocked is
         # used to avoid calling this function recursive.
@@ -187,13 +210,10 @@
         Create a player based on cls.
         """
         self._player = cls()
-        for sig in self.signals:
-            if sig in self._player.signals and \
-                   not sig in ('start', 'failed', 'end'):
-                self._player.signals[sig].connect_weak(self.signals[sig].emit)
-        for sig in ('start', 'end', 'release', 'failed'):
-            self._player.signals[sig].connect_weak(self._state_change, sig)
-
+        self._player._state_changed.connect_weak(self._state_change)
+        for signal in self._player.signals:
+            
self._player.signals[signal].connect_weak(self.signals[signal].emit)
+            
     
     @required_states(STATE_NOT_RUNNING, STATE_IDLE)
     def _open(self, mrl):
@@ -227,7 +247,7 @@
         if not self._player:
             self._create_player(cls)
         else:
-            if not self._player.get_state() in (STATE_IDLE, STATE_NOT_RUNNING):
+            if not self.get_state() in (STATE_IDLE, STATE_NOT_RUNNING, 
STATE_SHUTDOWN):
                 self._player.stop()
             if not isinstance(self._player, cls):
                 self._player.release()
@@ -235,7 +255,7 @@
         self._open(mrl)
 
 
-    @required_states(STATE_IDLE, STATE_PLAYING, STATE_PAUSED)
+    @required_states(STATE_OPEN, STATE_PLAYING, STATE_PAUSED)
     def play(self):
         if self.get_state() in (STATE_PLAYING, STATE_PAUSED):
             # called to toggle play/pause
@@ -261,7 +281,8 @@
         """
         Pause playback.
         """
-        self._player.pause()
+        if self.get_state() == STATE_PLAYING:
+            self._player.pause()
 
 
     @required_states(STATE_PLAYING, STATE_PAUSED)
@@ -269,7 +290,8 @@
         """
         Resume playback.
         """
-        self._player.resume()
+        if self.get_state() == STATE_PAUSED:
+            self._player.resume()
 
 
     @required_states(STATE_PLAYING, STATE_PAUSED)
@@ -277,21 +299,19 @@
         """
         Toggle play / pause.
         """
-        state = self.get_state()
-        if state == STATE_PLAYING:
+        if self.get_state() == STATE_PLAYING:
             self._player.pause()
-        if state == STATE_PAUSED:
+        else:
             self._player.resume()
 
 
-    @required_states(STATE_IDLE, STATE_PLAYING, STATE_PAUSED)
+    @required_states(STATE_OPEN, STATE_PLAYING, STATE_PAUSED)
     def seek(self, value, type=SEEK_RELATIVE):
         """
         Seek. Possible types are SEEK_RELATIVE, SEEK_ABSOLUTE and 
SEEK_PERCENTAGE.
         """
-        if self.get_state() == STATE_IDLE:
-            # FIXME: make it possible to seek between open() and play() and
-            # add STATE_IDLE to required_states.
+        if self.get_state() == STATE_OPEN:
+            # FIXME: make it possible to seek between open() and play()
             return False
         self._player.seek(value, type)
 

Modified: trunk/popcorn/src/ptypes.py
==============================================================================
--- trunk/popcorn/src/ptypes.py (original)
+++ trunk/popcorn/src/ptypes.py Fri Nov  3 21:26:35 2006
@@ -41,6 +41,7 @@
 STATE_NOT_RUNNING = 'STATE_NOT_RUNNING'
 STATE_IDLE = 'STATE_IDLE'
 STATE_OPENING = 'STATE_OPENING'
+STATE_OPEN = 'STATE_OPEN'
 STATE_PLAYING = 'STATE_PLAYING'
 STATE_PAUSED = 'STATE_PAUSED'
 STATE_SHUTDOWN = 'STATE_SHUTDOWN'

-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Freevo-cvslog mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/freevo-cvslog

Reply via email to