
"Upload GStreamer video data as textures."

import gst, gobject

import OpenGL.GL as gl

from glitch.texture import ApplyTexture, Texture, TexturedSquare

class VideoTexture(Texture, gobject.GObject):
    "Abstract base class for GStreamer-based video textures."

    __gsignals__ = {
        'new-buffer': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
            (gobject.TYPE_PYOBJECT,))
    }

    internal_format = gl.GL_RGB
    format = gl.GL_RGB
    y_flip = True

    def __init__(self):
        gobject.GObject.__init__(self)
        Texture.__init__(self, None, None, None)
        self.bin = self.make_bin()
        self.appsink = self.bin.get_by_name('appsink')
        self.appsink.props.emit_signals = True
        self.new_buffer_handler = \
            self.appsink.connect('new-buffer', self.pull_buffer)
        self.buffer = None

    def make_bin(self):
        """
        Called to create the GStreamer bin.

        The bin should have an appsink called "appsink".
        """

        raise NotImplementedError

    def pull_buffer(self, appsink):
        if self.width is None:
            pad = appsink.get_pad('sink')
            caps = pad.get_negotiated_caps()
            self.width = caps[0]['width']
            self.height = caps[0]['height']

        self.buffer = appsink.emit('pull-buffer')
        self.version += 1
        self.data = self.buffer.data
        self.emit('new-buffer', self.buffer)

    def stop(self):
        self.appsink.disconnect(self.new_buffer_handler)

    def upload(self, ctx):
        if self.buffer is not None:
            Texture.upload(self, ctx)

class VideoFileTexture(VideoTexture):
    "Load a video from a file."

    def __init__(self, path):
        self.path = path
        VideoTexture.__init__(self)

    def make_bin(self):
        bin = gst.parse_bin_from_description('''
            filesrc name=filesrc !
            decodebin name=decodebin !
            videoscale !
            ffmpegcolorspace !
            video/x-raw-rgb,bpp=24 !
            appsink name=appsink
            ''', False)
        filesrc = bin.get_by_name('filesrc')
        filesrc.props.location = self.path
        return bin

class Video(ApplyTexture):
    "Utility to show a video in a square."

    def __init__(self, texture, **kw):
        assert 'children' not in kw
        ApplyTexture.__init__(self, texture, **kw)
        self.texture = texture
        # XXX: Perhaps this should be TexturedRectangle.
        self.children = [TexturedSquare()]

    def _no_video(self):
        # XXX: Perhaps it should be possible to disable this.

        gl.glBegin(gl.GL_LINE_STRIP)
        gl.glVertex2f(1, 1)
        gl.glVertex2f(1, 0)
        gl.glVertex2f(0, 0)
        gl.glVertex2f(0, 1)
        gl.glVertex2f(1, 1)
        gl.glEnd()

        gl.glBegin(gl.GL_LINES)
        gl.glVertex2f(1, 1)
        gl.glVertex2f(0, 0)
        gl.glVertex2f(1, 0)
        gl.glVertex2f(0, 1)
        gl.glEnd()

    def render(self, ctx):
        if self.texture.buffer is None:
            self._no_video()
        else:
            ApplyTexture.render(self, ctx)

