def paint_with_video_decoder(self, decoder_module, coding, img_data, x, y,
                                 width, height, options, callbacks):
        assert x == 0 and y == 0
        assert hasattr(
            decoder_module, "Decoder"
        ), "decoder module %s does not have 'Decoder' factory function!" % decoder_module
        assert hasattr(
            decoder_module, "get_colorspaces"
        ), "decoder module %s does not have 'get_colorspaces' function!" % decoder_module
        factory = getattr(decoder_module, "Decoder")
        get_colorspaces = getattr(decoder_module, "get_colorspaces")
        try:
            self._decoder_lock.acquire()
            if self._backing is None:
                log("window %s is already gone!", self.wid)
                fire_paint_callbacks(callbacks, False)
                return False
            enc_width, enc_height = options.get("scaled_size", (width, height))
            input_colorspace = options.get("csc")
            if not input_colorspace:
                # Backwards compatibility with pre 0.10.x clients
                # We used to specify the colorspace as an avutil PixelFormat constant
                old_csc_fmt = options.get("csc_pixel_format")
                input_colorspace = get_colorspace_from_avutil_enum(old_csc_fmt)
                assert input_colorspace is not None, "csc was not specified and we cannot find a colorspace from csc_pixel_format=%s" % old_csc_fmt

            #do we need a prep step for decoders that cannot handle the input_colorspace directly?
            decoder_colorspaces = get_colorspaces()
            decoder_colorspace = input_colorspace
            #if input_colorspace not in decoder_colorspaces:
            if input_colorspace not in decoder_colorspaces:
                log("colorspace not supported by %s directly", decoder_module)
                assert input_colorspace in (
                    "BGRA", "BGRX"
                ), "colorspace %s cannot be handled directly or via a csc preparation step!" % input_colorspace
                decoder_colorspace = "YUV444P"
                if self._csc_prep:
                    if self._csc_prep.get_src_format() != input_colorspace:
                        #this should not happen!
                        log.warn("input colorspace has changed from %s to %s",
                                 self._csc_prep.get_src_format(),
                                 input_colorspace)
                        self.do_clean_csc_prep()
                    elif self._csc_prep.get_dst_format(
                    ) not in decoder_colorspaces:
                        #this should not happen!
                        log.warn("csc prep colorspace %s is now invalid!?",
                                 self._csc_prep.get_dst_format())
                        self.do_clean_csc_prep()
                    elif self._csc_prep.get_src_width(
                    ) != enc_width or self._csc_prep.get_src_height(
                    ) != enc_height:
                        log("csc prep dimensions have changed from %s to %s",
                            (self._csc_prep.get_src_width(),
                             self._csc_prep.get_src_height()),
                            (enc_width, enc_height))
                        self.do_clean_csc_prep()
                if self._csc_prep is None:
                    from xpra.codecs.csc_swscale.colorspace_converter import ColorspaceConverter  #@UnresolvedImport
                    self._csc_prep = ColorspaceConverter()
                    csc_speed = 0  #always best quality
                    self._csc_prep.init_context(enc_width, enc_height,
                                                input_colorspace, width,
                                                height, decoder_colorspace,
                                                csc_speed)
                    log("csc preparation step: %s", self._csc_prep)
            elif self._csc_prep:
                #no longer needed?
                self.do_clean_csc_prep()

            if self._video_decoder:
                if self._video_decoder.get_type() != coding:
                    if DRAW_DEBUG:
                        log.info(
                            "paint_with_video_decoder: encoding changed from %s to %s",
                            self._video_decoder.get_type(), coding)
                    self.do_clean_video_decoder()
                elif self._video_decoder.get_width(
                ) != enc_width or self._video_decoder.get_height(
                ) != enc_height:
                    if DRAW_DEBUG:
                        log.info(
                            "paint_with_video_decoder: window dimensions have changed from %s to %s",
                            (self._video_decoder.get_width(),
                             self._video_decoder.get_height()),
                            (enc_width, enc_height))
                    self.do_clean_video_decoder()
                elif self._video_decoder.get_colorspace(
                ) != decoder_colorspace:
                    if DRAW_DEBUG:
                        log.info(
                            "paint_with_video_decoder: colorspace changed from %s to %s",
                            self._video_decoder.get_colorspace(),
                            decoder_colorspace)
                    self.do_clean_video_decoder()
                elif options.get("frame") == 0:
                    if DRAW_DEBUG:
                        log.info(
                            "paint_with_video_decoder: first frame of new stream"
                        )
                    self.do_clean_video_decoder()
            if self._video_decoder is None:
                if DRAW_DEBUG:
                    log.info("paint_with_video_decoder: new %s(%s,%s,%s)",
                             factory, width, height, decoder_colorspace)
                self._video_decoder = factory()
                self._video_decoder.init_context(enc_width, enc_height,
                                                 decoder_colorspace)
                if DRAW_DEBUG:
                    log.info("paint_with_video_decoder: info=%s",
                             self._video_decoder.get_info())

            img = self._video_decoder.decompress_image(img_data, options)
            if not img:
                raise Exception(
                    "paint_with_video_decoder: wid=%s, %s decompression error on %s bytes of picture data for %sx%s pixels, options=%s"
                    %
                    (self.wid, coding, len(img_data), width, height, options))
            self.do_video_paint(img, x, y, enc_width, enc_height, width,
                                height, options, callbacks)
        finally:
            self._decoder_lock.release()
            if self._backing is None:
                self.close_decoder(True)
        return False
    def do_video_paint(self, img, x, y, enc_width, enc_height, width, height,
                       options, callbacks):
        rgb_format = "RGB"  #we may want to be able to change this (RGBA, BGR, ..)
        #as some video formats like vpx can forward transparency
        #also we could skip the csc step in some cases:
        pixel_format = img.get_pixel_format()
        #to handle this, we would need the decoder to handle buffers allocation properly:
        assert pixel_format != rgb_format, "no csc needed! but we don't handle this scenario yet!"
        if self._csc_decoder is not None:
            if self._csc_decoder.get_src_format() != pixel_format:
                if DRAW_DEBUG:
                    log.info(
                        "do_video_paint csc: switching src format from %s to %s",
                        self._csc_decoder.get_src_format(), pixel_format)
                self.do_clean_csc_decoder()
            elif self._csc_decoder.get_dst_format() != rgb_format:
                if DRAW_DEBUG:
                    log.info(
                        "do_video_paint csc: switching dst format from %s to %s",
                        self._csc_decoder.get_dst_format(), rgb_format)
                self.do_clean_csc_decoder()
            elif self._csc_decoder.get_src_width(
            ) != enc_width or self._csc_decoder.get_src_height() != enc_height:
                if DRAW_DEBUG:
                    log.info(
                        "do_video_paint csc: switching src size from %sx%s to %sx%s",
                        enc_width, enc_height,
                        self._csc_decoder.get_src_width(),
                        self._csc_decoder.get_src_height())
                self.do_clean_csc_decoder()
            elif self._csc_decoder.get_dst_width(
            ) != width or self._csc_decoder.get_dst_height() != height:
                if DRAW_DEBUG:
                    log.info(
                        "do_video_paint csc: switching src size from %sx%s to %sx%s",
                        width, height, self._csc_decoder.get_dst_width(),
                        self._csc_decoder.get_dst_height())
                self.do_clean_csc_decoder()
        if self._csc_decoder is None:
            from xpra.codecs.csc_swscale.colorspace_converter import ColorspaceConverter  #@UnresolvedImport
            self._csc_decoder = ColorspaceConverter()
            #use higher quality csc to compensate for lower quality source
            #(which generally means that we downscaled via YUV422P or lower)
            #or when upscaling the video:
            q = options.get("quality", 50)
            csc_speed = int(
                min(100, 100 - q,
                    100.0 * (enc_width * enc_height) / (width * height)))
            self._csc_decoder.init_context(enc_width, enc_height, pixel_format,
                                           width, height, rgb_format,
                                           csc_speed)
            if DRAW_DEBUG:
                log.info("do_video_paint new csc decoder: %s",
                         self._csc_decoder)
        rgb = self._csc_decoder.convert_image(img)
        if DRAW_DEBUG:
            log.info("do_video_paint rgb(%s)=%s", img, rgb)
        img.free()
        assert rgb.get_planes() == 0, "invalid number of planes for %s: %s" % (
            rgb_format, rgb.get_planes())

        #this will also take care of firing callbacks (from the UI thread):
        def paint():
            data = rgb.get_pixels()
            rowstride = rgb.get_rowstride()
            self.do_paint_rgb24(data, x, y, width, height, rowstride, options,
                                callbacks)
            rgb.free()

        self.idle_add(paint)
Beispiel #3
0
def test_push():
    import time
    from PIL import Image, ImageDraw
    from xpra.codecs.v4l2.pusher import Pusher  #@UnresolvedImport
    p = Pusher()
    W = 480
    H = 640
    p.init_context(W, H, W, "YUV420P", "/dev/video1")
    print("actual dimensions: %s - requested=%s" %
          ((p.get_width(), p.get_height()), (W, H)))
    from xpra.codecs.csc_swscale.colorspace_converter import ColorspaceConverter  #@UnresolvedImport
    csc = ColorspaceConverter()
    csc.init_context(W, H, "BGRX", W, H, "YUV420P")
    print("csc=%s" % csc)
    from xpra.codecs.image_wrapper import ImageWrapper

    def h(v):
        return ("0" + ("%#x" % v)[2:])[-2:].upper()

    i = 0
    while True:
        for r, g, b in (
            (0, 255, 0),
            (0, 0, 255),
            (255, 255, 255),
            (255, 0, 0),
            (128, 128, 128),
        ):
            name = "%s%s%s" % (h(r), h(g), h(b))
            print("testing with RGB: %s" % name)
            rgbx = (chr(r) + chr(g) + chr(b) + chr(255)) * (W * H)
            image = Image.frombytes("RGBA", (W, H), rgbx, "raw", "RGBA", W * 4,
                                    1)
            draw = ImageDraw.Draw(image)
            if i % 3 == 0:
                draw.polygon([(W // 2, H // 4), (W // 4, H * 3 // 4),
                              (W * 3 // 4, H * 3 // 4)], (0, 0, 0, 255))
            elif i % 3 == 1:
                draw.rectangle([W // 8, H // 8, W * 7 // 8, H * 7 // 8],
                               fill=(b, g, r, 255),
                               outline=(128, 255, 128, 128))
                draw.rectangle([W // 4, H // 4, W * 3 // 4, H * 3 // 4],
                               fill=(g, r, b, 255),
                               outline=(255, 128, 128, 128))
                draw.rectangle(
                    [W * 3 // 8, H * 3 // 8, W * 5 // 8, H * 5 // 8],
                    fill=(r, g, b, 255),
                    outline=(128, 128, 255, 128))
            else:
                c = [255, 0, 0] * 2
                for j in range(3):
                    ci = (i + j) % 3
                    fc = tuple(c[(ci):(ci + 3)] + [255])
                    draw.rectangle([W * j // 3, 0, W * (j + 1) // 3, H],
                                   fill=fc,
                                   outline=(128, 255, 128, 128))
            image.save("./%s.png" % name, "png")
            bgrx = image.tobytes('raw', "BGRA")
            import binascii
            #print("%s=%s" % (name, binascii.hexlify(bgrx)))
            with open("./latest.hex", "wb") as f:
                f.write(binascii.hexlify(bgrx))
            bgrx_image = ImageWrapper(0,
                                      0,
                                      W,
                                      H,
                                      bgrx,
                                      "BGRX",
                                      32,
                                      W * 4,
                                      planes=ImageWrapper.PACKED)
            image = csc.convert_image(bgrx_image)
            for _ in range(100):
                #print(".")
                p.push_image(image)
                time.sleep(0.05)
            i += 1