def video_encode(self, encoding, image, options): """ This method is used by make_data_packet to encode frames using video encoders. Video encoders only deal with fixed dimensions, so we must clean and reinitialize the encoder if the window dimensions has changed. Since this runs in the non-UI thread 'data_to_packet', we must use the '_lock' to prevent races. """ debug("video_encode%s", (encoding, image, options)) x, y, w, h = image.get_geometry()[:4] assert x==0 and y==0, "invalid position: %s,%s" % (x,y) src_format = image.get_pixel_format() try: self._lock.acquire() if not self.check_pipeline(encoding, w, h, src_format): raise Exception("failed to setup a video pipeline for %s encoding with source format %s" % (encoding, src_format)) #dw and dh are the edges we don't handle here width = w & self.width_mask height = h & self.height_mask debug("video_encode%s wxh=%s-%s, widthxheight=%sx%s", (encoding, image, options), w, h, width, height) csc_image, csc, enc_width, enc_height = self.csc_image(image, width, height) start = time.time() data, client_options = self._video_encoder.compress_image(csc_image, options) end = time.time() if csc_image is image: #no csc step, so the image comes from the UI server #and must be freed in the UI thread: self.idle_add(csc_image.free) else: #csc temporary images can be freed at will csc_image.free() del csc_image if data is None: log.error("video_encode: ouch, %s compression failed", encoding) return None, None, 0 if self.encoding_client_options: #tell the client which colour subsampling we used: #(note: see csc_equiv!) if self.uses_csc_atoms: client_options["csc"] = self.csc_equiv(csc) else: #ugly hack: expose internal ffmpeg/libav constant #for old versions without the "csc_atoms" feature: client_options["csc_pixel_format"] = get_avutil_enum_from_colorspace(csc) #tell the client about scaling (the size of the encoded picture): #(unless the video encoder has already done so): if self._csc_encoder and ("scaled_size" not in client_options) and (enc_width!=width or enc_height!=height): client_options["scaled_size"] = enc_width, enc_height debug("video_encode encoder: %s %sx%s result is %s bytes (%.1f MPixels/s), client options=%s", encoding, enc_width, enc_height, len(data), (enc_width*enc_height/(end-start+0.000001)/1024.0/1024.0), client_options) return self._video_encoder.get_encoding(), Compressed(encoding, data), client_options, width, height, 0, 24 finally: self._lock.release()
def __init__(self, *args): WindowSource.__init__(self, *args) #client uses uses_swscale (has extra limits on sizes) self.uses_swscale = self.encoding_options.get("uses_swscale", True) self.uses_csc_atoms = self.encoding_options.get("csc_atoms", False) self.video_scaling = self.encoding_options.get("video_scaling", False) self.video_reinit = self.encoding_options.get("video_reinit", False) if not self.encoding_client_options: #old clients can only use 420P: def_csc_modes = ("YUV420P") else: #default for newer clients that don't specify "csc_modes": def_csc_modes = ("YUV420P", "YUV422P", "YUV444P") #0.10 onwards should have specified csc_modes: self.csc_modes = self.encoding_options.get("csc_modes", def_csc_modes) self.video_encodings = ("vp8", "vp9", "h264") for x in self.video_encodings: if x in self.server_core_encodings: self._encoders[x] = self.video_encode #these constraints get updated with real values #when we construct the video pipeline: self.min_w = 1 self.min_h = 1 self.max_w = 16384 self.max_h = 16384 self.width_mask = 0xFFFF self.height_mask = 0xFFFF self.actual_scaling = (1, 1) self._csc_encoder = None self._video_encoder = None self._lock = Lock( ) #to ensure we serialize access to the encoder and its internals self.last_pipeline_params = None self.last_pipeline_scores = [] self.video_helper = getVideoHelper() if self.encoding_options.get("proxy.video", False): #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original: self.video_helper = getVideoHelper().clone(deep=True) #enabling video proxy: try: self.parse_proxy_video() except: log.error("failed to parse proxy video", exc_info=True)
def __init__(self, *args): WindowSource.__init__(self, *args) #client uses uses_swscale (has extra limits on sizes) self.uses_swscale = self.encoding_options.get("uses_swscale", True) self.uses_csc_atoms = self.encoding_options.get("csc_atoms", False) self.video_scaling = self.encoding_options.get("video_scaling", False) self.video_reinit = self.encoding_options.get("video_reinit", False) if not self.encoding_client_options: #old clients can only use 420P: def_csc_modes = ("YUV420P") else: #default for newer clients that don't specify "csc_modes": def_csc_modes = ("YUV420P", "YUV422P", "YUV444P") #0.10 onwards should have specified csc_modes: self.csc_modes = self.encoding_options.get("csc_modes", def_csc_modes) self.video_encodings = ("vp8", "vp9", "h264") for x in self.video_encodings: if x in self.server_core_encodings: self._encoders[x] = self.video_encode #these constraints get updated with real values #when we construct the video pipeline: self.min_w = 1 self.min_h = 1 self.max_w = 16384 self.max_h = 16384 self.width_mask = 0xFFFF self.height_mask = 0xFFFF self.actual_scaling = (1, 1) self._csc_encoder = None self._video_encoder = None self._lock = Lock() #to ensure we serialize access to the encoder and its internals self.last_pipeline_params = None self.last_pipeline_scores = [] self.video_helper = getVideoHelper() if self.encoding_options.get("proxy.video", False): #if we "proxy video", we will modify the video helper to add #new encoders, so we must make a deep copy to preserve the original: self.video_helper = getVideoHelper().clone(deep=True) #enabling video proxy: try: self.parse_proxy_video() except: log.error("failed to parse proxy video", exc_info=True)
def video_encode(self, encoding, image, options): """ This method is used by make_data_packet to encode frames using video encoders. Video encoders only deal with fixed dimensions, so we must clean and reinitialize the encoder if the window dimensions has changed. Since this runs in the non-UI thread 'data_to_packet', we must use the '_lock' to prevent races. """ debug("video_encode%s", (encoding, image, options)) x, y, w, h = image.get_geometry()[:4] assert x == 0 and y == 0, "invalid position: %s,%s" % (x, y) src_format = image.get_pixel_format() try: self._lock.acquire() if not self.check_pipeline(encoding, w, h, src_format): raise Exception( "failed to setup a video pipeline for %s encoding with source format %s" % (encoding, src_format)) #dw and dh are the edges we don't handle here width = w & self.width_mask height = h & self.height_mask debug("video_encode%s wxh=%s-%s, widthxheight=%sx%s", (encoding, image, options), w, h, width, height) csc_image, csc, enc_width, enc_height = self.csc_image( image, width, height) start = time.time() data, client_options = self._video_encoder.compress_image( csc_image, options) end = time.time() if csc_image is image: #no csc step, so the image comes from the UI server #and must be freed in the UI thread: self.idle_add(csc_image.free) else: #csc temporary images can be freed at will csc_image.free() del csc_image if data is None: log.error("video_encode: ouch, %s compression failed", encoding) return None, None, 0 if self.encoding_client_options: #tell the client which colour subsampling we used: #(note: see csc_equiv!) if self.uses_csc_atoms: client_options["csc"] = self.csc_equiv(csc) else: #ugly hack: expose internal ffmpeg/libav constant #for old versions without the "csc_atoms" feature: client_options[ "csc_pixel_format"] = get_avutil_enum_from_colorspace( csc) #tell the client about scaling (the size of the encoded picture): #(unless the video encoder has already done so): if self._csc_encoder and ("scaled_size" not in client_options ) and (enc_width != width or enc_height != height): client_options["scaled_size"] = enc_width, enc_height debug( "video_encode encoder: %s %sx%s result is %s bytes (%.1f MPixels/s), client options=%s", encoding, enc_width, enc_height, len(data), (enc_width * enc_height / (end - start + 0.000001) / 1024.0 / 1024.0), client_options) return self._video_encoder.get_type(), Compressed( encoding, data), client_options, width, height, 0, 24 finally: self._lock.release()