def wait(self, timeout=None): """ Waits for the encoder to finish (successfully or otherwise) """ result = self.event.wait(timeout) if result: if self.started_capture: self.started_capture = False mmal_check( mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_FALSE), prefix="Failed to stop capture") try: mmal_check( mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") except PiCameraMMALError as e: if e.status != mmal.MMAL_EINVAL: raise self._close_output() # Check whether the callback set an exception if self.exception: raise self.exception return result
def __init__( self, parent, layer=0, alpha=255, fullscreen=True, window=None, crop=None, rotation=0, vflip=False, hflip=False): # Create and enable the renderer component self._rotation = 0 self._vflip = False self._hflip = False self.parent = parent self.renderer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check( mmal.mmal_component_create( mmal.MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, self.renderer), prefix="Failed to create renderer component") try: if not self.renderer[0].input_num: raise PiCameraError("No input ports on renderer component") self.layer = layer self.alpha = alpha self.fullscreen = fullscreen if window is not None: self.window = window if crop is not None: self.crop = crop self.rotation = rotation self.vflip = vflip self.hflip = hflip mmal_check( mmal.mmal_component_enable(self.renderer), prefix="Renderer component couldn't be enabled") except: mmal.mmal_component_destroy(self.renderer) raise
def update(self, source): """ Update the overlay with a new source of data. The new *source* buffer must have the same size as the original buffer used to create the overlay. There is currently no method for changing the size of an existing overlay (remove and recreate the overlay if you require this). """ port = self.renderer[0].input[0] fmt = port[0].format bp = ct.c_uint8 * (fmt[0].es[0].video.width * fmt[0].es[0].video.height * 3) try: sp = bp.from_buffer(source) except TypeError: sp = bp.from_buffer_copy(source) buf = mmal.mmal_queue_get(self.pool[0].queue) if not buf: raise PiCameraRuntimeError( "Couldn't get a buffer from the overlay's pool") ct.memmove(buf[0].data, sp, buf[0].alloc_size) buf[0].length = buf[0].alloc_size mmal_check( mmal.mmal_port_send_buffer(port, buf), prefix="Unable to send a buffer to the overlay's port")
def stop(self): """ Stops the encoder, regardless of whether it's finished """ # The check on is_enabled below is not a race condition; we ignore the # EINVAL error in the case the port turns out to be disabled when we # disable below. The check exists purely to prevent stderr getting # spammed by our continued attempts to disable an already disabled port if self.encoder and self.output_port[0].is_enabled: if self.started_capture: self.started_capture = False mmal_check( mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_FALSE ), prefix="Failed to stop capture", ) try: mmal_check(mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") except PiCameraMMALError as e: if e.status != mmal.MMAL_EINVAL: raise self.stopped = True self.event.set() self._close_output()
def _set_fullscreen(self, value): mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_FULLSCREEN, fullscreen=bool(value), ) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set fullscreen")
def _create_encoder(self, **options): """ Creates and configures the encoder itself """ assert not self.encoder self.encoder = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check( mmal.mmal_component_create(self.encoder_type, self.encoder), prefix="Failed to create encoder component" ) if not self.encoder[0].input_num: raise PiCameraError("No input ports on encoder component") if not self.encoder[0].output_num: raise PiCameraError("No output ports on encoder component") # Ensure output format is the same as the input self.output_port = self.encoder[0].output[0] if self.resizer: mmal.mmal_format_copy(self.encoder[0].input[0][0].format, self.resizer[0].output[0][0].format) else: mmal.mmal_format_copy(self.encoder[0].input[0][0].format, self.input_port[0].format) mmal_check( mmal.mmal_port_format_commit(self.encoder[0].input[0]), prefix="Failed to set encoder input port format" ) mmal.mmal_format_copy(self.output_port[0].format, self.encoder[0].input[0][0].format) # Set buffer size and number to appropriate values self.output_port[0].buffer_size = max( self.output_port[0].buffer_size_recommended, self.output_port[0].buffer_size_min ) self.output_port[0].buffer_num = max( self.output_port[0].buffer_num_recommended, self.output_port[0].buffer_num_min )
def _create_resizer(self, width, height): super(PiRawEncoderMixin, self)._create_resizer(width, height) # Set the resizer's output encoding to the requested format. If a # non-alpha format is requested, use the alpha-inclusive format and set # a flag to get the callback to strip the alpha bytes encoding, bytes_per_pixel = { "yuv": (mmal.MMAL_ENCODING_I420, 1.5), "rgb": (mmal.MMAL_ENCODING_RGBA, 3), "rgba": (mmal.MMAL_ENCODING_RGBA, 4), "bgr": (mmal.MMAL_ENCODING_BGRA, 3), "bgra": (mmal.MMAL_ENCODING_BGRA, 4), }[self.format] self._strip_alpha = self.format in ("rgb", "bgr") port = self.resizer[0].output[0] port[0].format[0].encoding = encoding port[0].format[0].encoding_variant = encoding mmal_check(mmal.mmal_port_format_commit(port), prefix="Failed to set resizer output port format") # Calculate the expected image size, to be used by the callback to # decide when a frame ends. This is to work around a firmware bug that # causes the raw image to be returned twice when the maximum camera # resolution is requested self._expected_size = int( ((width + 31) // 32 * 32) * ((height + 15) // 16 * 16) # round up width to nearest multiple of 32 * bytes_per_pixel # round up height to nearest multiple of 16 )
def _set_transform(self, value): mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_TRANSFORM, transform=value, ) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set transform")
def _add_exif_tag(self, tag, value): # Format the tag and value into an appropriate bytes string, encoded # with the Exif encoding (ASCII) if isinstance(tag, str): tag = tag.encode(self.exif_encoding) if isinstance(value, str): value = value.encode(self.exif_encoding) elif isinstance(value, datetime.datetime): value = value.strftime('%Y:%m:%d %H:%M:%S').encode(self.exif_encoding) # MMAL_PARAMETER_EXIF_T is a variable sized structure, hence all the # mucking about with string buffers here... buf = ct.create_string_buffer( ct.sizeof(mmal.MMAL_PARAMETER_EXIF_T) + len(tag) + len(value) + 1) mp = ct.cast(buf, ct.POINTER(mmal.MMAL_PARAMETER_EXIF_T)) mp[0].hdr.id = mmal.MMAL_PARAMETER_EXIF mp[0].hdr.size = len(buf) if (b'=' in tag or b'\x00' in value): data = tag + value mp[0].keylen = len(tag) mp[0].value_offset = len(tag) mp[0].valuelen = len(value) else: data = tag + b'=' + value ct.memmove(mp[0].data, data, len(data)) mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp[0].hdr), prefix="Failed to set Exif tag %s" % tag)
def _callback_recycle(self, port, buf): """ Recycles the buffer on behalf of the encoder callback function """ new_buf = mmal.mmal_queue_get(self.pool[0].queue) if not new_buf: raise PiCameraError("Unable to get a buffer to return to the encoder port") mmal_check(mmal.mmal_port_send_buffer(port, new_buf), prefix="Unable to return a buffer to the encoder port")
def _create_resizer(self, width, height): super(PiRawEncoderMixin, self)._create_resizer(width, height) encoding = self.RAW_ENCODINGS[self.format][0] port = self.resizer[0].output[0] port[0].format[0].encoding = encoding port[0].format[0].encoding_variant = encoding mmal_check( mmal.mmal_port_format_commit(port), prefix="Failed to set resizer output port format")
def test_mmal_check(): mmal_check(mmal.MMAL_SUCCESS) with pytest.raises(PiCameraError): mmal_check(mmal.MMAL_ENOSYS) with pytest.raises(PiCameraError): mmal_check(mmal.MMAL_ENOMEM) with pytest.raises(PiCameraError): mmal_check(mmal.MMAL_ENOSPC) with pytest.raises(PiCameraError): mmal_check(mmal.MMAL_EINVAL)
def _get_fullscreen(self): mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T) )) mmal_check( mmal.mmal_port_parameter_get(self.renderer[0].input[0], mp.hdr), prefix="Failed to get fullscreen") return mp.fullscreen != mmal.MMAL_FALSE
def stop(self): """ Stops the encoder, regardless of whether it's finished """ if self.encoder and self.output_port[0].is_enabled: mmal_check( mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") self.stopped = True self.event.set() self._close_output()
def _set_crop(self, value): try: x, y, w, h = value except (TypeError, ValueError) as e: raise PiCameraValueError("Invalid crop rectangle (x, y, w, h) tuple: %s" % value) mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_SRC_RECT, src_rect=mmal.MMAL_RECT_T(x, y, w, h), ) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set crop")
def _set_layer(self, value): try: if not (0 <= value <= 255): raise PiCameraValueError("Invalid layer value: %d (valid range 0..255)" % value) except TypeError: raise PiCameraValueError("Invalid layer value: %s" % value) mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_LAYER, layer=value, ) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set layer")
def _create_encoder(self, quality=85, thumbnail=(64, 48, 35), bayer=False): super(PiImageEncoder, self)._create_encoder() try: self.output_port[0].format[0].encoding = { 'jpeg': mmal.MMAL_ENCODING_JPEG, 'png': mmal.MMAL_ENCODING_PNG, 'gif': mmal.MMAL_ENCODING_GIF, 'bmp': mmal.MMAL_ENCODING_BMP, }[self.format] except KeyError: raise PiCameraValueError("Unrecognized format %s" % self.format) mmal_check( mmal.mmal_port_format_commit(self.output_port), prefix="Unable to set format on encoder output port") if self.format == 'jpeg': mmal_check( mmal.mmal_port_parameter_set_uint32( self.output_port, mmal.MMAL_PARAMETER_JPEG_Q_FACTOR, quality), prefix="Failed to set JPEG quality") mmal_check( mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_ENABLE_RAW_CAPTURE, int(bool(bayer))), prefix="Failed to set raw capture") if thumbnail is None: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T) ), 0, 0, 0, 0) else: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T) ), 1, *thumbnail) mmal_check( mmal.mmal_port_parameter_set(self.encoder[0].control, mp.hdr), prefix="Failed to set thumbnail configuration") mmal_check( mmal.mmal_component_enable(self.encoder), prefix="Unable to enable encoder component")
def _get_crop(self): mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T))) mmal_check(mmal.mmal_port_parameter_get(self.renderer[0].input[0], mp.hdr), prefix="Failed to get crop") return ( mp.src_rect.x, mp.src_rect.y, mp.src_rect.width, mp.src_rect.height, )
def start(self, output): """ Starts the encoder object writing to the specified output """ self.event.clear() self.stopped = False self.exception = None self._open_output(output) self.output_port[0].userdata = ct.cast(ct.pointer(ct.py_object(self)), ct.c_void_p) mmal_check(mmal.mmal_port_enable(self.output_port, _encoder_callback), prefix="Failed to enable encoder output port") for q in range(mmal.mmal_queue_length(self.pool[0].queue)): buf = mmal.mmal_queue_get(self.pool[0].queue) if not buf: raise PiCameraRuntimeError( "Unable to get a required buffer from pool queue") mmal_check(mmal.mmal_port_send_buffer(self.output_port, buf), prefix="Unable to send a buffer to encoder output port") b = mmal.MMAL_BOOL_T() mmal_check(mmal.mmal_port_parameter_get_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, b), prefix="Failed to query capture status") self.started_capture = not bool(b) if self.started_capture: mmal_check(mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_TRUE), prefix="Failed to start capture")
def wait(self, timeout=None): """ Waits for the encoder to finish (successfully or otherwise) """ result = self.event.wait(timeout) if result: mmal_check( mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") self._close_output() # Check whether the callback set an exception if self.exception: raise self.exception return result
def start(self, output): """ Starts the encoder object writing to the specified output """ self.event.clear() self.stopped = False self.exception = None self._open_output(output) self.output_port[0].userdata = ct.cast(ct.pointer(ct.py_object(self)), ct.c_void_p) mmal_check( mmal.mmal_port_enable(self.output_port, _encoder_callback), prefix="Failed to enable encoder output port" ) for q in range(mmal.mmal_queue_length(self.pool[0].queue)): buf = mmal.mmal_queue_get(self.pool[0].queue) if not buf: raise PiCameraRuntimeError("Unable to get a required buffer from pool queue") mmal_check( mmal.mmal_port_send_buffer(self.output_port, buf), prefix="Unable to send a buffer to encoder output port", ) b = mmal.MMAL_BOOL_T() mmal_check( mmal.mmal_port_parameter_get_boolean(self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, b), prefix="Failed to query capture status", ) self.started_capture = not bool(b) if self.started_capture: mmal_check( mmal.mmal_port_parameter_set_boolean(self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_TRUE), prefix="Failed to start capture", )
def __init__(self, parent, source): self.parent = parent self.renderer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check( mmal.mmal_component_create(mmal.MMAL_COMPONENT_DEFAULT_NULL_SINK, self.renderer), prefix="Failed to create null sink component", ) try: if not self.renderer[0].input_num: raise PiCameraError("No input ports on null sink component") mmal_check(mmal.mmal_component_enable(self.renderer), prefix="Null sink component couldn't be enabled") except: mmal.mmal_component_destroy(self.renderer) raise self.connection = self.parent._connect_ports(source, self.renderer[0].input[0])
def _set_layer(self, value): try: if not (0 <= value <= 255): raise PiCameraValueError( "Invalid layer value: %d (valid range 0..255)" % value) except TypeError: raise PiCameraValueError("Invalid layer value: %s" % value) mp = mmal.MMAL_DISPLAYREGION_T(mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_LAYER, layer=value) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set layer")
def _set_crop(self, value): try: x, y, w, h = value except (TypeError, ValueError) as e: raise PiCameraValueError( "Invalid crop rectangle (x, y, w, h) tuple: %s" % value) mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T(mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T)), set=mmal.MMAL_DISPLAY_SET_SRC_RECT, src_rect=mmal.MMAL_RECT_T(x, y, w, h), ) mmal_check(mmal.mmal_port_parameter_set(self.renderer[0].input[0], mp.hdr), prefix="Failed to set crop")
def _get_crop(self): mp = mmal.MMAL_DISPLAYREGION_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_DISPLAYREGION, ct.sizeof(mmal.MMAL_DISPLAYREGION_T) )) mmal_check( mmal.mmal_port_parameter_get(self.renderer[0].input[0], mp.hdr), prefix="Failed to get crop") return ( mp.src_rect.x, mp.src_rect.y, mp.src_rect.width, mp.src_rect.height, )
def __init__(self, parent, source): self.parent = parent self.renderer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check(mmal.mmal_component_create( mmal.MMAL_COMPONENT_DEFAULT_NULL_SINK, self.renderer), prefix="Failed to create null sink component") try: if not self.renderer[0].input_num: raise PiCameraError("No input ports on null sink component") mmal_check(mmal.mmal_component_enable(self.renderer), prefix="Null sink component couldn't be enabled") except: mmal.mmal_component_destroy(self.renderer) raise self.connection = self.parent._connect_ports(source, self.renderer[0].input[0])
def _create_connection(self): """ Connects the camera to the encoder object """ assert not self.connection self.connection = ct.POINTER(mmal.MMAL_CONNECTION_T)() mmal_check( mmal.mmal_connection_create( self.connection, self.camera[0].output[self.port], self.input_port, mmal.MMAL_CONNECTION_FLAG_TUNNELLING | mmal.MMAL_CONNECTION_FLAG_ALLOCATION_ON_INPUT), prefix="Failed to connect camera to encoder") mmal_check( mmal.mmal_connection_enable(self.connection), prefix="Failed to enable encoder connection")
def _callback_write(self, buf): """ Performs output writing on behalf of the encoder callback function; return value determines whether writing has completed. """ if buf[0].length: mmal_check( mmal.mmal_buffer_header_mem_lock(buf), prefix="Unable to lock buffer header memory") try: with self.lock: if self.output and self.output.write( ct.string_at(buf[0].data, buf[0].length)) != buf[0].length: raise PiCameraError( "Unable to write buffer to file - aborting") finally: mmal.mmal_buffer_header_mem_unlock(buf) return False
def _callback_write(self, buf): """ Performs output writing on behalf of the encoder callback function; return value determines whether writing has completed. """ if buf[0].length: mmal_check(mmal.mmal_buffer_header_mem_lock(buf), prefix="Unable to lock buffer header memory") try: with self.lock: if self.output: written = self.output.write(ct.string_at(buf[0].data, buf[0].length)) # Ignore None return value; most Python 2 streams have # no return value for write() if (written is not None) and (written != buf[0].length): raise PiCameraError("Unable to write buffer to file - aborting") finally: mmal.mmal_buffer_header_mem_unlock(buf) return bool(buf[0].flags & mmal.MMAL_BUFFER_HEADER_FLAG_EOS)
def _create_encoder(self, quality=85, thumbnail=(64, 48, 35), bayer=False): super(PiImageEncoder, self)._create_encoder() try: self.output_port[0].format[0].encoding = { 'jpeg': mmal.MMAL_ENCODING_JPEG, 'png': mmal.MMAL_ENCODING_PNG, 'gif': mmal.MMAL_ENCODING_GIF, 'bmp': mmal.MMAL_ENCODING_BMP, }[self.format] except KeyError: raise PiCameraValueError("Unrecognized format %s" % self.format) mmal_check(mmal.mmal_port_format_commit(self.output_port), prefix="Unable to set format on encoder output port") if self.format == 'jpeg': mmal_check(mmal.mmal_port_parameter_set_uint32( self.output_port, mmal.MMAL_PARAMETER_JPEG_Q_FACTOR, quality), prefix="Failed to set JPEG quality") mmal_check(mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_ENABLE_RAW_CAPTURE, int(bool(bayer))), prefix="Failed to set raw capture") if thumbnail is None: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T)), 0, 0, 0, 0) else: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T)), 1, *thumbnail) mmal_check(mmal.mmal_port_parameter_set(self.encoder[0].control, mp.hdr), prefix="Failed to set thumbnail configuration") mmal_check(mmal.mmal_component_enable(self.encoder), prefix="Unable to enable encoder component")
def start(self, output): """ Starts the encoder object writing to the specified output """ self.event.clear() self.stopped = False self.exception = None self._open_output(output) self.output_port[0].userdata = ct.cast(ct.pointer(ct.py_object(self)), ct.c_void_p) mmal_check(mmal.mmal_port_enable(self.output_port, _encoder_callback), prefix="Failed to enable encoder output port") for q in range(mmal.mmal_queue_length(self.pool[0].queue)): buf = mmal.mmal_queue_get(self.pool[0].queue) if not buf: raise PiCameraRuntimeError( "Unable to get a required buffer from pool queue") mmal_check(mmal.mmal_port_send_buffer(self.output_port, buf), prefix="Unable to send a buffer to encoder output port")
def _callback_write(self, buf): # Overridden to strip alpha bytes when necessary, and manually # calculate the frame end if buf[0].length and self._image_size: mmal_check(mmal.mmal_buffer_header_mem_lock(buf), prefix="Unable to lock buffer header memory") try: s = ct.string_at(buf[0].data, buf[0].length) if self._strip_alpha: s = b"".join(s[i : i + 3] for i in range(0, len(s), 4)) with self.lock: if self.output: written = self.output.write(s) # Ignore None return value; most Python 2 streams have # no return value for write() if (written is not None) and (written != len(s)): raise PiCameraError("Unable to write buffer to file - aborting") self._image_size -= len(s) assert self._image_size >= 0 finally: mmal.mmal_buffer_header_mem_unlock(buf) return self._image_size <= 0
def _create_resizer(self, width, height): self.resizer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check(mmal.mmal_component_create( mmal.MMAL_COMPONENT_DEFAULT_RESIZER, self.resizer), prefix="Failed to create resizer component") if not self.resizer[0].input_num: raise PiCameraError("No input ports on resizer component") if not self.resizer[0].output_num: raise PiCameraError("No output ports on resizer component") # Copy the original input port's format to the resizer's input, # then the resizer's input format to the output, and configure it mmal.mmal_format_copy(self.resizer[0].input[0][0].format, self.input_port[0].format) mmal_check(mmal.mmal_port_format_commit(self.resizer[0].input[0]), prefix="Failed to set resizer input port format") mmal.mmal_format_copy(self.resizer[0].output[0][0].format, self.resizer[0].input[0][0].format) fmt = self.resizer[0].output[0][0].format fmt[0].es[0].video.width = width fmt[0].es[0].video.height = height fmt[0].es[0].video.crop.x = 0 fmt[0].es[0].video.crop.y = 0 fmt[0].es[0].video.crop.width = width fmt[0].es[0].video.crop.height = height mmal_check(mmal.mmal_port_format_commit(self.resizer[0].output[0]), prefix="Failed to set resizer output port format")
def _create_resizer(self, width, height): self.resizer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check( mmal.mmal_component_create(mmal.MMAL_COMPONENT_DEFAULT_RESIZER, self.resizer), prefix="Failed to create resizer component", ) if not self.resizer[0].input_num: raise PiCameraError("No input ports on resizer component") if not self.resizer[0].output_num: raise PiCameraError("No output ports on resizer component") # Copy the original input port's format to the resizer's input, # then the resizer's input format to the output, and configure it mmal.mmal_format_copy(self.resizer[0].input[0][0].format, self.input_port[0].format) mmal_check( mmal.mmal_port_format_commit(self.resizer[0].input[0]), prefix="Failed to set resizer input port format" ) mmal.mmal_format_copy(self.resizer[0].output[0][0].format, self.resizer[0].input[0][0].format) fmt = self.resizer[0].output[0][0].format fmt[0].es[0].video.width = width fmt[0].es[0].video.height = height fmt[0].es[0].video.crop.x = 0 fmt[0].es[0].video.crop.y = 0 fmt[0].es[0].video.crop.width = width fmt[0].es[0].video.crop.height = height mmal_check( mmal.mmal_port_format_commit(self.resizer[0].output[0]), prefix="Failed to set resizer output port format" )
def __init__(self, parent, layer=0, alpha=255, fullscreen=True, window=None, crop=None, rotation=0, vflip=False, hflip=False): # Create and enable the renderer component self._rotation = 0 self._vflip = False self._hflip = False self.parent = parent self.renderer = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check(mmal.mmal_component_create( mmal.MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, self.renderer), prefix="Failed to create renderer component") try: if not self.renderer[0].input_num: raise PiCameraError("No input ports on renderer component") self.layer = layer self.alpha = alpha self.fullscreen = fullscreen if window is not None: self.window = window if crop is not None: self.crop = crop self.rotation = rotation self.vflip = vflip self.hflip = hflip mmal_check(mmal.mmal_component_enable(self.renderer), prefix="Renderer component couldn't be enabled") except: mmal.mmal_component_destroy(self.renderer) raise
def _callback_write(self, buf): """ Performs output writing on behalf of the encoder callback function; return value determines whether writing has completed. """ if buf[0].length: mmal_check(mmal.mmal_buffer_header_mem_lock(buf), prefix="Unable to lock buffer header memory") try: with self.lock: if self.output: written = self.output.write( ct.string_at(buf[0].data, buf[0].length)) # Ignore None return value; most Python 2 streams have # no return value for write() if (written is not None) and (written != buf[0].length): raise PiCameraError( "Unable to write buffer to file - aborting") finally: mmal.mmal_buffer_header_mem_unlock(buf) return bool(buf[0].flags & mmal.MMAL_BUFFER_HEADER_FLAG_EOS)
def wait(self, timeout=None): """ Waits for the encoder to finish (successfully or otherwise) """ result = self.event.wait(timeout) if result: if self.started_capture: self.started_capture = False mmal_check(mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_FALSE), prefix="Failed to stop capture") try: mmal_check(mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") except PiCameraMMALError as e: if e.status != mmal.MMAL_EINVAL: raise # Check whether the callback set an exception if self.exception: raise self.exception return result
def start(self, output): """ Starts the encoder object writing to the specified output """ self.event.clear() self.stopped = False self.exception = None self._open_output(output) self.output_port[0].userdata = ct.cast( ct.pointer(ct.py_object(self)), ct.c_void_p) mmal_check( mmal.mmal_port_enable(self.output_port, _encoder_callback), prefix="Failed to enable encoder output port") for q in range(mmal.mmal_queue_length(self.pool[0].queue)): buf = mmal.mmal_queue_get(self.pool[0].queue) if not buf: raise PiCameraRuntimeError( "Unable to get a required buffer from pool queue") mmal_check( mmal.mmal_port_send_buffer(self.output_port, buf), prefix="Unable to send a buffer to encoder output port")
def _callback_write(self, buf): # Overridden to strip alpha bytes when necessary, and manually # calculate the frame end if buf[0].length and self._image_size: mmal_check(mmal.mmal_buffer_header_mem_lock(buf), prefix="Unable to lock buffer header memory") try: s = ct.string_at(buf[0].data, buf[0].length) if self._strip_alpha: s = b''.join(s[i:i + 3] for i in range(0, len(s), 4)) with self.lock: if self.output: written = self.output.write(s) # Ignore None return value; most Python 2 streams have # no return value for write() if (written is not None) and (written != len(s)): raise PiCameraError( "Unable to write buffer to file - aborting") self._image_size -= len(s) assert self._image_size >= 0 finally: mmal.mmal_buffer_header_mem_unlock(buf) return self._image_size <= 0
def _create_encoder(self, format, **options): """ Creates and configures the encoder itself """ assert not self.encoder self.encoder = ct.POINTER(mmal.MMAL_COMPONENT_T)() mmal_check(mmal.mmal_component_create(self.encoder_type, self.encoder), prefix="Failed to create encoder component") if not self.encoder[0].input_num: raise PiCameraError("No input ports on encoder component") if not self.encoder[0].output_num: raise PiCameraError("No output ports on encoder component") # Ensure output format is the same as the input self.output_port = self.encoder[0].output[0] self.input_port = self.encoder[0].input[0] mmal.mmal_format_copy(self.output_port[0].format, self.input_port[0].format) # Set buffer size and number to appropriate values self.output_port[0].buffer_size = max( self.output_port[0].buffer_size_recommended, self.output_port[0].buffer_size_min) self.output_port[0].buffer_num = max( self.output_port[0].buffer_num_recommended, self.output_port[0].buffer_num_min)
def stop(self): """ Stops the encoder, regardless of whether it's finished """ # The check on is_enabled below is not a race condition; we ignore the # EINVAL error in the case the port turns out to be disabled when we # disable below. The check exists purely to prevent stderr getting # spammed by our continued attempts to disable an already disabled port if self.encoder and self.output_port[0].is_enabled: if self.started_capture: self.started_capture = False mmal_check(mmal.mmal_port_parameter_set_boolean( self.camera_port, mmal.MMAL_PARAMETER_CAPTURE, mmal.MMAL_FALSE), prefix="Failed to stop capture") try: mmal_check(mmal.mmal_port_disable(self.output_port), prefix="Failed to disable encoder output port") except PiCameraMMALError as e: if e.status != mmal.MMAL_EINVAL: raise self.stopped = True self.event.set() self._close_output()
def _create_encoder(self, format, **options): super(PiImageEncoder, self)._create_encoder(format, **options) try: self.output_port[0].format[0].encoding = { 'jpeg': mmal.MMAL_ENCODING_JPEG, 'png': mmal.MMAL_ENCODING_PNG, 'gif': mmal.MMAL_ENCODING_GIF, 'bmp': mmal.MMAL_ENCODING_BMP, }[format] except KeyError: raise PiCameraValueError("Unrecognized format %s" % format) mmal_check( mmal.mmal_port_format_commit(self.output_port), prefix="Unable to set format on encoder output port") if format == 'jpeg': mmal_check( mmal.mmal_port_parameter_set_uint32( self.output_port, mmal.MMAL_PARAMETER_JPEG_Q_FACTOR, options.get('quality', 85)), prefix="Failed to set JPEG quality") thumbnail = options.get('thumbnail', (64, 48, 35)) if thumbnail is None: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T) ), 0, 0, 0, 0) else: mp = mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_THUMBNAIL_CONFIGURATION, ct.sizeof(mmal.MMAL_PARAMETER_THUMBNAIL_CONFIG_T) ), 1, *thumbnail) mmal_check( mmal.mmal_port_parameter_set(self.encoder[0].control, mp.hdr), prefix="Failed to set thumbnail configuration") mmal_check( mmal.mmal_component_enable(self.encoder), prefix="Unable to enable encoder component")
def __init__(self, parent, source, size=None, layer=0, alpha=255, fullscreen=True, window=None, crop=None, rotation=0, vflip=False, hflip=False): super(PiOverlayRenderer, self).__init__(parent, layer, alpha, fullscreen, window, crop, rotation, vflip, hflip) # Copy format from camera's preview port, then adjust the encoding to # RGB888 and optionally adjust the resolution and size port = self.renderer[0].input[0] fmt = port[0].format mmal.mmal_format_copy( fmt, parent._camera[0].output[parent.CAMERA_PREVIEW_PORT][0].format) fmt[0].encoding = mmal.MMAL_ENCODING_RGB24 fmt[0].encoding_variant = mmal.MMAL_ENCODING_RGB24 if size is not None: w, h = size fmt[0].es[0].video.width = mmal.VCOS_ALIGN_UP(w, 32) fmt[0].es[0].video.height = mmal.VCOS_ALIGN_UP(h, 16) fmt[0].es[0].video.crop.width = w fmt[0].es[0].video.crop.height = h mmal_check(mmal.mmal_port_format_commit(port), prefix="Overlay format couldn't be set") port[0].buffer_num = port[0].buffer_num_min port[0].buffer_size = port[0].buffer_size_recommended mmal_check(mmal.mmal_component_enable(self.renderer), prefix="Overlay couldn't be enabled") mmal_check(mmal.mmal_port_enable(port, _overlay_callback), prefix="Overlay input port couldn't be enabled") self.pool = mmal.mmal_port_pool_create(port, port[0].buffer_num, port[0].buffer_size) if not self.pool: raise PiCameraRuntimeError("Couldn't create pool for overlay") self.update(source)
def __init__( self, parent, source, size=None, layer=0, alpha=255, fullscreen=True, window=None, crop=None, rotation=0, vflip=False, hflip=False): super(PiOverlayRenderer, self).__init__( parent, layer, alpha, fullscreen, window, crop, rotation, vflip, hflip) # Copy format from camera's preview port, then adjust the encoding to # RGB888 and optionally adjust the resolution and size port = self.renderer[0].input[0] fmt = port[0].format mmal.mmal_format_copy( fmt, parent._camera[0].output[parent.CAMERA_PREVIEW_PORT][0].format) fmt[0].encoding = mmal.MMAL_ENCODING_RGB24 fmt[0].encoding_variant = mmal.MMAL_ENCODING_RGB24 if size is not None: w, h = size fmt[0].es[0].video.width = mmal.VCOS_ALIGN_UP(w, 32) fmt[0].es[0].video.height = mmal.VCOS_ALIGN_UP(h, 16) fmt[0].es[0].video.crop.width = w fmt[0].es[0].video.crop.height = h mmal_check( mmal.mmal_port_format_commit(port), prefix="Overlay format couldn't be set") port[0].buffer_num = port[0].buffer_num_min port[0].buffer_size = port[0].buffer_size_recommended mmal_check( mmal.mmal_component_enable(self.renderer), prefix="Overlay couldn't be enabled") mmal_check( mmal.mmal_port_enable(port, _overlay_callback), prefix="Overlay input port couldn't be enabled") self.pool = mmal.mmal_port_pool_create( port, port[0].buffer_num, port[0].buffer_size) if not self.pool: raise PiCameraRuntimeError("Couldn't create pool for overlay") self.update(source)
def _create_encoder( self, bitrate=17000000, intra_period=0, profile="high", quantization=0, inline_headers=True, **options ): super(PiVideoEncoder, self)._create_encoder(**options) try: self.output_port[0].format[0].encoding = { "h264": mmal.MMAL_ENCODING_H264, "mjpeg": mmal.MMAL_ENCODING_MJPEG, }[self.format] except KeyError: raise PiCameraValueError("Unrecognized format %s" % self.format) if not (0 <= bitrate <= 25000000): raise PiCameraValueError("bitrate must be between 0 (VBR) and 25Mbps") if quantization and bitrate: warnings.warn("Setting bitrate to 0 as quantization is non-zero", PiCameraWarning) bitrate = 0 self.output_port[0].format[0].bitrate = bitrate mmal_check(mmal.mmal_port_format_commit(self.output_port), prefix="Unable to set format on encoder output port") if self.format == "h264": mp = mmal.MMAL_PARAMETER_VIDEO_PROFILE_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_PROFILE, ct.sizeof(mmal.MMAL_PARAMETER_VIDEO_PROFILE_T) ) ) try: mp.profile[0].profile = { "baseline": mmal.MMAL_VIDEO_PROFILE_H264_BASELINE, "main": mmal.MMAL_VIDEO_PROFILE_H264_MAIN, "high": mmal.MMAL_VIDEO_PROFILE_H264_HIGH, "constrained": mmal.MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, }[profile] except KeyError: raise PiCameraValueError("Invalid H.264 profile %s" % profile) mp.profile[0].level = mmal.MMAL_VIDEO_LEVEL_H264_4 mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set encoder H.264 profile" ) mmal_check( mmal.mmal_port_parameter_set_boolean( self.output_port, mmal.MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, int(inline_headers) ), prefix="Unable to set inline_headers", ) if not (bitrate and inline_headers): # If inline_headers is disabled, or VBR encoding is configured, # disable the split function self._next_output = None if intra_period: mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_INTRAPERIOD, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T) ), intra_period, ) mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set encoder intra_period" ) if quantization: mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T) ), quantization, ) mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set initial quantization" ) mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T) ), quantization, ) mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set minimum quantization" ) mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T) ), quantization, ) mmal_check( mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set maximum quantization" ) mmal_check( mmal.mmal_port_parameter_set_boolean( self.encoder[0].input[0], mmal.MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, 1 ), prefix="Unable to set immutable flag on encoder input port", ) mmal_check(mmal.mmal_component_enable(self.encoder), prefix="Unable to enable video encoder component")
def _create_encoder(self, bitrate=17000000, intra_period=0, profile='high', quantization=0, inline_headers=True, sei=False): super(PiVideoEncoder, self)._create_encoder() try: self.output_port[0].format[0].encoding = { 'h264': mmal.MMAL_ENCODING_H264, 'mjpeg': mmal.MMAL_ENCODING_MJPEG, }[self.format] except KeyError: raise PiCameraValueError('Unrecognized format %s' % self.format) if not (0 <= bitrate <= 25000000): raise PiCameraValueError( 'bitrate must be between 0 (VBR) and 25Mbps') if quantization and bitrate: warnings.warn('Setting bitrate to 0 as quantization is non-zero', PiCameraWarning) bitrate = 0 self.output_port[0].format[0].bitrate = bitrate mmal_check(mmal.mmal_port_format_commit(self.output_port), prefix="Unable to set format on encoder output port") if self.format == 'h264': mp = mmal.MMAL_PARAMETER_VIDEO_PROFILE_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_PROFILE, ct.sizeof(mmal.MMAL_PARAMETER_VIDEO_PROFILE_T), ), ) try: mp.profile[0].profile = { 'baseline': mmal.MMAL_VIDEO_PROFILE_H264_BASELINE, 'main': mmal.MMAL_VIDEO_PROFILE_H264_MAIN, 'high': mmal.MMAL_VIDEO_PROFILE_H264_HIGH, 'constrained': mmal.MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE, }[profile] except KeyError: raise PiCameraValueError("Invalid H.264 profile %s" % profile) mp.profile[0].level = mmal.MMAL_VIDEO_LEVEL_H264_4 mmal_check(mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set encoder H.264 profile") mmal_check(mmal.mmal_port_parameter_set_boolean( self.output_port, mmal.MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, int(inline_headers)), prefix="Unable to set inline_headers") mmal_check(mmal.mmal_port_parameter_set_boolean( self.output_port, mmal.MMAL_PARAMETER_VIDEO_ENCODE_SEI_ENABLE, int(sei)), prefix="Enable to set SEI") if not (bitrate and inline_headers): # If inline_headers is disabled, or VBR encoding is configured, # disable the split function self._next_output = None if intra_period: mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_INTRAPERIOD, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T), ), intra_period) mmal_check(mmal.mmal_port_parameter_set( self.output_port, mp.hdr), prefix="Unable to set encoder intra_period") if quantization: mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T), ), quantization) mmal_check(mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set initial quantization") mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T), ), quantization, ) mmal_check(mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set minimum quantization") mp = mmal.MMAL_PARAMETER_UINT32_T( mmal.MMAL_PARAMETER_HEADER_T( mmal.MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ct.sizeof(mmal.MMAL_PARAMETER_UINT32_T), ), quantization, ) mmal_check(mmal.mmal_port_parameter_set(self.output_port, mp.hdr), prefix="Unable to set maximum quantization") mmal_check(mmal.mmal_port_parameter_set_boolean( self.encoder[0].input[0], mmal.MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, 1), prefix="Unable to set immutable flag on encoder input port") mmal_check(mmal.mmal_component_enable(self.encoder), prefix="Unable to enable video encoder component")