#!/usr/bin/env python
# -*- Mode: Python -*-
# vi:si:et:sw=4:sts=4:ts=4

import sys

import pygtk
pygtk.require('2.0')

import gobject
gobject.threads_init()

import gtk
from gtk import gdk
gdk.threads_init()

import pygst
pygst.require('0.10')
import gst
import gst.interfaces

class GstTV:
    def __init__(self, videowidget):
        self.playing = False
        self.player = self.make_pipeline()
        self.player2 = None # self.make_aud_pipeline()
        self.videowidget = videowidget
        self.on_eos = False

        bus = self.player.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 make_pipeline(self):
        p = gst.Pipeline()
        self.vsrc = gst.element_factory_make('v4l2src')
        cf1 = gst.element_factory_make('capsfilter')
        f1 = gst.element_factory_make('ffmpegcolorspace')
        vq = gst.element_factory_make('identity')
        self.deint = gst.element_factory_make('deinterlace2')
        f2 = gst.element_factory_make('ffmpegcolorspace')
        vr = gst.element_factory_make('videorate')
        vb = gst.element_factory_make('videobox')
        vo = self.vsink = gst.parse_launch('xvimagesink pixel-aspect-ratio=33/48')

        p.add(self.vsrc, cf1, f1, vq, self.deint, f2, vr, vb, vo)
        gst.element_link_many(self.vsrc, cf1, f1, vq, self.deint, f2, vr, vb, vo)

        self.vsrc.set_property ("always-copy", False)
        self.vsrc.set_property ("device", "/dev/video0")

        cf1.set_property("caps", \
            gst.caps_from_string ("video/x-raw-yuv,framerate=(fraction)25/1")) # ,width=720"))

        self.deint.set_property ('method', 6)

        vb.set_property('left', 32)
        vb.set_property('right', 32)
        vb.set_property('top', 16)
        vb.set_property('bottom', 16)
        self.vsink.set_property ('sync', False)
        self.vsink.set_property ('force-aspect-ratio', True)

        return p

    def make_aud_pipeline(self):
        p = gst.Pipeline()

        self.asrc = gst.element_factory_make('pulsesrc')
        cf2 = gst.element_factory_make('capsfilter')
        aq = gst.element_factory_make('queue')
        arate = gst.element_factory_make('audiorate')
        ac = gst.element_factory_make('audioconvert')
        r = gst.element_factory_make('audioresample')
        ao = self.asink = gst.element_factory_make('pulsesink')

        p.add(self.asrc, cf2, aq, arate, ac, r, ao)
        gst.element_link_many(self.asrc, cf2, aq, arate, ac, r, ao)

        cf2.set_property("caps", \
            gst.caps_from_string ("audio/x-raw-int,rate=48000,width=16,depth=16"))
        self.asrc.set_property("device", \
            "alsa_input.pci_1131_7133_sound_card_0_alsa_capture_0")
        #self.asrc.set_property("buffer-time", 25000)
        #self.asrc.set_property("latency-time", 5000)

        cf2.set_property("caps", \
            gst.caps_from_string ("audio/x-raw-int,width=16,depth=16,rate=48000"))

        #self.asink.set_property("latency-time", 10000)
        #self.asink.set_property("buffer-time", 200000)
        self.asink.set_property("slave-method", 0)

        # self.asink.set_property("sync", False)
        return p

    def on_sync_message(self, bus, message):
        if message.structure is None:
            return
        if message.structure.get_name() == 'prepare-xwindow-id':
            # Sync with the X server before giving the X-id to the sink
            gtk.gdk.threads_enter()
            gtk.gdk.display_get_default().sync()
            gtk.gdk.threads_leave()
            self.videowidget.set_sink(message.src)
            # message.src.set_property('force-aspect-ratio', True)
            
    def on_message(self, bus, message):
        t = message.type
        if t == gst.MESSAGE_ERROR:
            err, debug = message.parse_error()
            print "Error: %s" % err, debug
            if self.on_eos:
                self.on_eos()
            self.playing = False
        elif t == gst.MESSAGE_EOS:
            if self.on_eos:
                self.on_eos()
            self.playing = False
        elif t == gst.MESSAGE_STATE_CHANGED:
            if message.src == self.vsrc:
                old, new, pending = message.parse_state_changed()
                if old == gst.STATE_NULL and new == gst.STATE_READY:
                    for c in self.vsrc.list_channels():
                        if c.label == 'Composite1':
                            self.vsrc.set_channel(c)
                    for c in self.vsrc.list_norms():
                        if c.label == 'PAL':
                            self.vsrc.set_norm(c)
        elif t == gst.MESSAGE_LATENCY:
            print "Latency message from", message.src

    def query_position(self):
        "Returns a (position, duration) tuple"
        try:
            position, format = self.player.query_position(gst.FORMAT_TIME)
        except:
            position = gst.CLOCK_TIME_NONE

        try:
            duration, format = self.player.query_duration(gst.FORMAT_TIME)
        except:
            duration = gst.CLOCK_TIME_NONE

        return (position, duration)

    def seek(self, location):
        """
        @param location: time to seek to, in nanoseconds
        """
        gst.debug("seeking to %r" % location)
        event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
            gst.SEEK_TYPE_SET, location,
            gst.SEEK_TYPE_NONE, 0)

        res = self.player.send_event(event)
        if res:
            gst.info("setting new stream time to 0")
            self.player.set_new_stream_time(0L)
        else:
            gst.error("seek to %r failed" % location)

    def pause(self):
        gst.info("pausing player")
        self.player.set_state(gst.STATE_PAUSED)
        if self.player2:
            self.player2.set_state(gst.STATE_PAUSED)
        self.playing = False

    def play(self):
        gst.info("playing player")
        self.player.set_state(gst.STATE_PLAYING)
        if self.player2:
            self.player2.set_state(gst.STATE_PLAYING)
        self.playing = True
        
    def stop(self):
        self.player.set_state(gst.STATE_NULL)
        if self.player2:
            self.player2.set_state(gst.STATE_NULL)
        gst.info("stopped player")

    def get_state(self, timeout=1):
        return self.player.get_state(timeout=timeout)

    def is_playing(self):
        return self.playing
    
class VideoWidget(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        self.imagesink = None
        self.add_events (gdk.BUTTON_RELEASE_MASK | gdk.BUTTON_PRESS_MASK |
            gdk.KEY_PRESS_MASK | gdk.KEY_RELEASE_MASK |
            gdk.POINTER_MOTION_MASK | gdk.POINTER_MOTION_HINT_MASK)
        self.set_flags(gtk.CAN_FOCUS)
        self.unset_flags(gtk.DOUBLE_BUFFERED)

    def do_expose_event(self, event):
        if self.imagesink:
            self.imagesink.expose()
            return False
        else:
            return True

    def set_sink(self, sink):
        assert self.window.xid
        self.imagesink = sink
        self.imagesink.set_xwindow_id(self.window.xid)

class PlayerWindow(gtk.Window):
    UPDATE_INTERVAL = 500
    def __init__(self):
        gtk.Window.__init__(self)
        self.set_default_size(730,586)
    
        self.is_fullscreen = False

        self.create_ui()

        self.player = GstTV(self.videowidget)

        def on_eos():
            gtk.main_quit()
        self.player.on_eos = lambda *x: on_eos()
        
        self.update_id = -1
        self.changed_id = -1
        self.seek_timeout_id = -1

        self.p_position = gst.CLOCK_TIME_NONE
        self.p_duration = gst.CLOCK_TIME_NONE

        def on_delete_event():
            self.player.stop()
            gtk.main_quit()
        self.connect('delete-event', lambda *x: on_delete_event())

    def create_ui(self):
        vbox = gtk.VBox()
        self.add(vbox)

        self.videowidget = VideoWidget()
        vbox.pack_start(self.videowidget)
        
        self.videowidget.connect_after('realize',
                                       lambda *x: self.on_play())
        self.videowidget.connect('key-release-event', self.keypress)

    def on_play(self):
        self.window.freeze_updates()
        self.fullscreen()
        self.is_fullscreen = True
        self.window.thaw_updates()
        self.player.play()

    def keypress(self, sender, event):
        key = gtk.gdk.keyval_name (event.keyval)
        if key == 'f':
            self.toggle_fullscreen ()
            return True
        elif key == 'q':
            self.player.stop()
            gtk.main_quit()
            return True
        return False

    def toggle_fullscreen (self):
        self.window.freeze_updates()
        if self.is_fullscreen:
            self.unfullscreen()
        else:
            self.fullscreen()
        self.window.thaw_updates()
        self.is_fullscreen = not self.is_fullscreen

def main(args):
    # Need to register our derived widget types for implicit event
    # handlers to get called.
    gobject.type_register(PlayerWindow)
    gobject.type_register(VideoWidget)

    w = PlayerWindow()

    w.show_all()

    gtk.main()

if __name__ == '__main__':
    sys.exit(main(sys.argv))
