import gobject
gobject.threads_init()
import gst

class ChangeStride(gst.BaseTransform):
    sink_template = gst.PadTemplate('sink',
            gst.PAD_SINK, gst.PAD_ALWAYS,
            gst.Caps('video/x-raw-yuv;'
                    'video/x-raw-rgb'))

    src_template = gst.PadTemplate('src',
            gst.PAD_SRC, gst.PAD_ALWAYS,
            gst.Caps('video/x-raw-yuv, stride_changed=(boolean) true;'
                    'video/x-raw-rgb, stride_changed=(boolean) true'))

    __gstdetails__ = ('DShow stride hack',
            'Filter/Converter/Video',
            'Changes the stride of YV12 output by direct show '
                    'to the one expected by gstreamer',
            'Alessandro Decina <alessandro@fluendo.com')

    __gsttemplates__ = (sink_template, src_template)

    def __init__(self):
        gst.BaseTransform.__init__(self)
        self.set_in_place(False)
        self.set_passthrough(False)

    def _get_dshow_strides_and_size(self, width, height):
        y_stride = (width + 3) & ~3
        y_height = (height + 1) & ~1
        y_size = y_stride * y_height

        chr_stride = ((width + 3) & ~3) / 2
        chr_height = ((height + 1) & ~1) / 2
        chr_size = chr_stride * chr_height

        size = y_size + (2 * chr_size)
        return y_stride, chr_stride, size

    def _get_gst_strides_and_size(self, width, height):
        y_stride = (width + 3) & ~3
        y_height = (height + 1) & ~1
        y_size = y_stride * y_height

        chr_stride = ((width + 7) & ~7) / 2
        chr_height = ((height + 1) & ~1) / 2
        chr_size = chr_stride * chr_height

        size = y_size + (2 * chr_size)
        return y_stride, chr_stride, size

    def do_transform_size(self, direction, caps, size, othercaps):
        width = caps[0]['width']
        height = caps[0]['height']

        size = self._get_gst_strides_and_size(width, height)[2]
        return size

    def do_set_caps(self, incaps, outcaps):
        structure = incaps[0]
        passthrough = True
        if structure.has_field('dshowvdec_stride') and \
                structure.get_name() == 'video/x-raw-yuv' and \
                structure['format'].fourcc == 'YV12':
            width = incaps[0]['width']
            height = incaps[0]['height']

            ds_y_stride, ds_chr_stride, ds_size = \
                    self._get_dshow_strides_and_size(width, height)

            gst_y_stride, gst_chr_stride, gst_size = \
                    self._get_gst_strides_and_size(width, height)

            y_pad = gst_y_stride - ds_y_stride
            chr_pad = gst_chr_stride - ds_chr_stride
            if y_pad > 0 or chr_pad > 0:
                passthrough = False

        self.set_passthrough(passthrough)

        return True

    def do_transform(self, inbuf, outbuf):
        caps = self.get_pad('sink').props.caps
        width = caps[0]['width']
        height = caps[0]['height']

        ds_y_stride, ds_chr_stride, ds_size = \
                self._get_dshow_strides_and_size(width, height)

        gst_y_stride, gst_chr_stride, gst_size = \
                self._get_gst_strides_and_size(width, height)

        src_ptr = 0
        dst_ptr = 0

        y_pad = gst_y_stride - ds_y_stride
        for i in xrange(height):
            if y_pad:
                outbuf[dst_ptr:dst_ptr + gst_y_stride] = \
                        inbuf[src_ptr:src_ptr + ds_y_stride] + '\x00' * y_pad
            else:
                outbuf[dst_ptr:dst_ptr + gst_y_stride] = \
                        inbuf[src_ptr:src_ptr + ds_y_stride]

            src_ptr += ds_y_stride
            dst_ptr += gst_y_stride

        chr_pad = gst_chr_stride - ds_chr_stride
        for i in xrange(height):
            if chr_pad:
                outbuf[dst_ptr:dst_ptr + gst_chr_stride] = \
                        inbuf[src_ptr:src_ptr + ds_chr_stride] + '\x00' * chr_pad
            else:
                outbuf[dst_ptr:dst_ptr + gst_chr_stride] = \
                        inbuf[src_ptr:src_ptr + ds_chr_stride]

            src_ptr += ds_chr_stride
            dst_ptr += gst_chr_stride

        return gst.FLOW_OK

gobject.type_register(ChangeStride)
gst.element_register(ChangeStride, 'changestride', gst.RANK_MARGINAL)

class Player(object):
    def run(self):
        self.loop = gobject.MainLoop()
#        pipeline = gst.parse_launch('videotestsrc ! '
#                'video/x-raw-yuv,width=(int)321,height=(int)240 ! '
#                'changestride ! ffmpegcolorspace ! ximagesink')

        import sys
        pipeline = gst.parse_launch('filesrc location=%s ! '
                'decodebin ! changestride ! ffmpegcolorspace ! '
                'directdrawsink' % sys.argv[1])

        bus = pipeline.get_bus()
        bus.add_signal_watch()
        bus.connect('message::eos', self._bus_message_eos)
        bus.connect('message::error', self._bus_message_error)

        pipeline.set_state(gst.STATE_PLAYING)
        self.loop.run()
        pipeline.set_state(gst.STATE_NULL)

    def _bus_message_eos(self, bus, message):
        print 'EOS'
        self.loop.quit()

    def _bus_message_error(self, bus, message):
        print 'ERROR', message
        self.loop.quit()

def main():
    Player().run()

if __name__ == '__main__':
    main()

