Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/olpcgames/video.py
blob: 032aa130ce295e3e888c363020af09d93d5a2bb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
"""Video widget for displaying a gstreamer pipe

Note: currently this module is not all that elegant or useful,
we need a better recipe for using and working with Video 
under OLPCGames.
"""
import logging
log = logging.getLogger( 'olpcgames.video' )
#log.setLevel( logging.INFO )
import os
import signal
import pygame
import weakref
import olpcgames
from olpcgames import _gtkmain

import pygtk
pygtk.require('2.0')
import gtk
import gst

class VideoWidget(gtk.DrawingArea):
    """Widget to render GStreamer video over our Pygame Canvas
    
    The VideoWidget is a simple GTK window which is 
    held by the PygameCanvas, just as is the Pygame 
    window we normally use.  As such this approach 
    *cannot* work without the GTK wrapper.
    
    It *should* be possible to use raw X11 operations 
    to create a child window of the Pygame/SDL window 
    and use that for the same purpose, but that would 
    require some pretty low-level ctypes hacking.
    
    Attributes of Note:
    
        rect -- Pygame rectangle which tells us where to 
            display ourselves, setting the rect changes the 
            position and size of the window.
    """
    _imagesink = None
    _renderedRect = None
    def __init__(self, rect=None, force_aspect_ratio=True):
        super(VideoWidget, self).__init__()
        self.unset_flags(gtk.DOUBLE_BUFFERED)
        if rect is None:
            rect = pygame.Rect( (0,0), (160,120))
        self.rect = rect
        self.force_aspect_ratio = force_aspect_ratio
        self.set_size_request(rect.width,rect.height)
        olpcgames.WIDGET.put( self, rect.left,rect.top)
        self._renderedRect = rect
        self.show()
    
    def set_rect( self, rect ):
        """Set our rectangle (area of the screen)"""
        log.debug( 'Set rectangle: %s', rect )
        self.set_size_request(rect.width,rect.height)
        olpcgames.WIDGET.move( self, rect.left,rect.top)
        self.rect = rect

    def do_expose_event(self, event):
        """Handle exposure event (trigger redraw by gst)"""
        if self._imagesink:
            self._imagesink.expose()
            return False
        else:
            return True

    def set_sink(self, sink):
        """Set our window-sink for output"""
        assert self.window.xid
        self._imagesink = sink
        self._imagesink.set_xwindow_id(self.window.xid)
        self._imagesink.set_property('force-aspect-ratio', self.force_aspect_ratio)

class PygameWidget( object ):
    """Render "full-screen" video to the entire Pygame screen 
    
    Not particularly useful unless this happens to be exactly what you need.
    """
    def __init__( self ):
        try:
            window_id = pygame.display.get_wm_info()['window']
        except KeyError, err: # pygame-ctypes...
            window_id = int(os.environ['SDL_WINDOWID'])
        self.window_id = window_id
        self._imagesink = None 
        #self._holder = _gtkmain.Holder()
    def set_sink( self, sink ):
        """Set up our gst sink"""
        log.info( 'Setting sink: %s', sink )
        self._imagesink = sink 
        sink.set_xwindow_id( self.window_id )
        
#pipe_desc = 'v4l2src ! video/x-raw-yuv,width=160,height=120 ! ffmpegcolorspace ! xvimagesink'
class Player(object):
    pipe_desc = 'v4l2src ! ffmpegcolorspace ! video/x-raw-yuv ! xvimagesink'
    test_pipe_desc = 'videotestsrc ! ffmpegcolorspace ! video/x-raw-yuv ! xvimagesink'
    _synchronized = False
    def __init__(self, videowidget, pipe_desc=pipe_desc):
        self._playing = False
        self._videowidget = videowidget

        self._pipeline = gst.parse_launch(pipe_desc)

        bus = self._pipeline.get_bus()
        bus.enable_sync_message_emission()
        bus.add_signal_watch()
        bus.connect('sync-message::element', self.on_sync_message)
        bus.connect('message', self.on_message)

    def play(self):
        log.info( 'Play' )
        if self._playing == False:
            self._pipeline.set_state(gst.STATE_PLAYING)
            self._playing = True

    def pause(self):
        log.info( 'Pause' )
        if self._playing == True:
            if self._synchronized:
                log.debug( '  pause already sync\'d' )
                self._pipeline.set_state(gst.STATE_PAUSED)
            self._playing = False
    def stop( self ):
        """Stop all playback"""
        self._pipeline.set_state( gst.STATE_NULL )

    def on_sync_message(self, bus, message):
        log.info( 'Sync: %s', message )
        if message.structure is None:
            return
        if message.structure.get_name() == 'prepare-xwindow-id':
            self._synchronized = True
            self._videowidget.set_sink(message.src)

    def on_message(self, bus, message):
        log.info( 'Message: %s', message )
        t = message.type
        if t == gst.MESSAGE_ERROR:
            err, debug = message.parse_error()
            log.warn("Video error: (%s) %s" ,err, debug)
            self._playing = False
    
if __name__ == "__main__":
    # Simple testing code...
    logging.basicConfig()
    log.setLevel( logging.DEBUG )
    from pygame import image,display, event
    import pygame
    def main():
        display.init()
        maxX,maxY = display.list_modes()[0] 
        screen = display.set_mode( (maxX/3, maxY/3 ) )
        
        display.flip()
        
        pgw = PygameWidget( )
        p = Player( pgw, pipe_desc=Player.test_pipe_desc )
        p.play()
        
        clock = pygame.time.Clock()
        
        running = True
        while running:
            clock.tick( 60 )
            for evt in [pygame.event.wait()] + pygame.event.get():
                if evt.type == pygame.KEYDOWN:
                    if p._playing:
                        p.pause()
                    else:
                        p.play()
                elif evt.type == pygame.QUIT:
                    p.stop()
                    running = False
            #display.flip()
    main()