def __init__(self, camera, size=None, seconds=None, max_frames=None, bitrate=17000000, splitter_port=1): if size is None and seconds is None: raise PiCameraValueError( 'You must specify either size, or seconds') if size is not None and seconds is not None: raise PiCameraValueError( 'You cannot specify both size and seconds') if seconds is not None: size = bitrate * seconds // 8 super(PiCameraCircularIO, self).__init__(size) try: camera._encoders except AttributeError: raise PiCameraValueError('camera must be a valid PiCamera object') self.camera = camera self.max_frames = max_frames self.splitter_port = splitter_port self._data = PiCameraDequeHack(self) self._frames = PiCameraDequeFrames(self)
def __init__(self, camera, size=None, seconds=None, bitrate=17000000): if size is None and seconds is None: raise PiCameraValueError('You must specify either size, or seconds') if size is not None and seconds is not None: raise PiCameraValueError('You cannot specify both size and seconds') if seconds is not None: size = bitrate * seconds // 8 super(PiCameraCircularIO, self).__init__(size) self.camera = camera self._data = PiCameraDequeHack(camera)
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 copy_to( self, output, size=None, seconds=None, first_frame=PiVideoFrameType.sps_header): """ Copies content from the stream to *output*. By default, this method copies all complete frames from the circular stream to the filename or file-like object given by *output*. If *size* is specified then the copy will be limited to the whole number of frames that fit within the specified number of bytes. If *seconds* if specified, then the copy will be limited to that number of seconds worth of frames. Only one of *size* or *seconds* can be specified. If neither is specified, all frames are copied. If *first_frame* is specified, it defines the frame type of the first frame to be copied. By default this is :attr:`~PiVideoFrameType.sps_header` as this must usually be the first frame in an H264 stream. If *first_frame* is ``None``, not such limit will be applied. .. warning:: Note that if a frame of the specified type (e.g. SPS header) cannot be found within the specified number of seconds or bytes then this method will simply copy nothing (but no error will be raised). The stream's position is not affected by this method. """ if size is not None and seconds is not None: raise PiCameraValueError('You cannot specify both size and seconds') if isinstance(output, bytes): output = output.decode('utf-8') opened = isinstance(output, str) if opened: output = io.open(output, 'wb') try: with self.lock: save_pos = self.tell() try: if size is not None: pos = self._find_size(size, first_frame) elif seconds is not None: pos = self._find_seconds(seconds, first_frame) else: pos = self._find_all(first_frame) # Copy chunks efficiently from the position found if pos is not None: self.seek(pos) while True: buf = self.read1() if not buf: break output.write(buf) finally: self.seek(save_pos) finally: if opened: output.close()
def _set_rotation(self, value): try: value = ((int(value) % 360) // 90) * 90 except ValueError: raise PiCameraValueError("Invalid rotation angle: %s" % value) self._set_transform( self._get_transform(value, self._vflip, self._hflip)) self._rotation = value
def __init__(self, camera, splitter_port=1): super(PiCameraDequeHack, self).__init__() try: camera._encoders except AttributeError: raise PiCameraValueError('camera must be a valid PiCamera object') self.camera = camera self.splitter_port = splitter_port
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 _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 bytes_to_rgba(self, data, resolution): ''' Converts a bytes objects containing RGBA/BGRA data to a `numpy`_ array. i.e. this is the 4 byte per pixel version. It's here as a class method to keep things neat - the 3-byte-per-pixel version is a module function. i.e. picamera.array.bytes_to_rgb() ''' width, height = resolution fwidth, fheight = picamera.array.raw_resolution(resolution) # Workaround: output from the video splitter is rounded to 16x16 instead # of 32x16 (but only for RGB, and only when a resizer is not used) bpp = 4 if len(data) != (fwidth * fheight * bpp): fwidth, fheight = picamera.array.raw_resolution(resolution, splitter=True) if len(data) != (fwidth * fheight * bpp): raise PiCameraValueError( 'Incorrect buffer length for resolution %dx%d' % (width, height)) # Crop to the actual resolution return np.frombuffer(data, dtype=np.uint8).reshape( (fheight, fwidth, bpp))[:height, :width, :]
def copy_to_stream(self, size=None, seconds=None, first_frame=PiVideoFrameType.sps_header): if size is not None and seconds is not None: raise PiCameraValueError( 'You cannot specify both size and seconds') stream = BytesIO() with self.lock: save_pos = self.tell() try: if size is not None: pos = self._find_size(size, first_frame) elif seconds is not None: pos = self._find_seconds(seconds, first_frame) else: pos = self._find_all(first_frame) if pos is not None: self.seek(pos) while True: buf = self.read1() if not buf: break stream.write(buf) finally: self.seek(save_pos) stream.seek(0) return stream
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")
def copy_to(self, output, size=None, seconds=None, frames=None, first_frame=PiVideoFrameType.sps_header, match=False): """ copy_to(output, size=None, seconds=None, frames=None, first_frame=PiVideoFrameType.sps_header) Copies content from the stream to *output*. By default, this method copies all complete frames from the circular stream to the filename or file-like object given by *output*. If *size* is specified then the copy will be limited to the whole number of frames that fit within the specified number of bytes. If *seconds* if specified, then the copy will be limited to that number of seconds worth of frames. If *frames* is specified then the copy will be limited to that number of frames. Only one of *size*, *seconds*, or *frames* can be specified. If none is specified, all frames are copied. If *first_frame* is specified, it defines the frame type of the first frame to be copied. By default this is :attr:`~PiVideoFrameType.sps_header` as this must usually be the first frame in an H264 stream. If *first_frame* is ``None``, not such limit will be applied. If *match* is true and *size*, *seconds* or *frames* is specified then the copy will be limited to the whole number of frames that matches or exceeds the given criteria by less than one frame. .. warning:: Note that if a frame of the specified type (e.g. SPS header) cannot be found within the specified number of seconds, bytes, or frames, then this method will simply copy nothing (but no error will be raised). The stream's position is not affected by this method. """ if (size, seconds, frames).count(None) < 2: raise PiCameraValueError( 'You can only specify one of size, seconds, or frames') if isinstance(output, bytes): output = output.decode('utf-8') opened = isinstance(output, str) if opened: output = io.open(output, 'wb') try: with self.lock: if size is not None: first, last = self._find('video_size', size, first_frame, match) elif seconds is not None: seconds = int(seconds * 1000000) first, last = self._find('timestamp', seconds, first_frame, match) elif frames is not None: first, last = self._find('index', frames, first_frame, match) else: first, last = self._find_all(first_frame) # Copy chunk references into a holding buffer; this allows us # to release the lock on the stream quickly (in case recording # is on-going) chunks = [] if first is not None and last is not None: pos = 0 for buf, frame in self._data.iter_both(False): if pos > last.position + last.frame_size: break elif pos >= first.position: chunks.append(buf) pos += len(buf) # Perform the actual I/O, copying chunks to the output for buf in chunks: output.write(buf) return first, last finally: if opened: output.close()