Skip to content

Commit

Permalink
Merge branch 'master' of github.com:waveform80/picamera
Browse files Browse the repository at this point in the history
  • Loading branch information
waveform80 committed Dec 6, 2015
2 parents bc47155 + 1062688 commit 24e1a17
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Change log
==========


Release 1.10 (2014-03-31)
Release 1.10 (2015-03-31)
=========================

1.10 consists mostly of minor enhancements:
Expand Down
9 changes: 9 additions & 0 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
Frequently Asked Questions (FAQ)
================================


AttributeError: 'module' object has no attribute 'PiCamera'
===========================================================

You've named your script ``picamera.py`` (or you've named some other script
``picamera.py``. If you name a script after a system or third-party package you
will break imports for that system or third-party package. Delete or rename
that script (and any associated ``.pyc`` files), and try again.

Can I put the preview in a window?
==================================

Expand Down
44 changes: 40 additions & 4 deletions picamera/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ class PiCamera(object):
You should only need to specify this parameter if you are using a custom
DeviceTree blob (this is only typical on the `Compute Module`_ platform).
The *clock_mode* parameter can be used to change when the camera's frame
timestamps reset to zero (see
:attr:`~picamera.encoders.PiVideoFrame.timestamp` for more information).
No preview or recording is started automatically upon construction. Use
the :meth:`capture` method to capture images, the :meth:`start_recording`
method to begin recording video, or the :meth:`start_preview` method to
Expand Down Expand Up @@ -417,6 +421,11 @@ class PiCamera(object):
'top-bottom': mmal.MMAL_STEREOSCOPIC_MODE_BOTTOM,
}

CLOCK_MODES = {
'reset': mmal.MMAL_PARAM_TIMESTAMP_MODE_RESET_STC,
'raw': mmal.MMAL_PARAM_TIMESTAMP_MODE_RAW_STC,
}

_METER_MODES_R = {v: k for (k, v) in METER_MODES.items()}
_EXPOSURE_MODES_R = {v: k for (k, v) in EXPOSURE_MODES.items()}
_FLASH_MODES_R = {v: k for (k, v) in FLASH_MODES.items()}
Expand All @@ -428,7 +437,8 @@ class PiCamera(object):

def __init__(
self, camera_num=0, stereo_mode='none', stereo_decimate=False,
resolution=None, framerate=None, sensor_mode=0, led_pin=None):
resolution=None, framerate=None, sensor_mode=0, led_pin=None,
clock_mode='reset'):
bcm_host.bcm_host_init()
mimetypes.add_type('application/h264', '.h264', False)
mimetypes.add_type('application/mjpeg', '.mjpg', False)
Expand Down Expand Up @@ -482,10 +492,18 @@ def __init__(
if framerate is None:
framerate = fractions.Fraction(
self.DEFAULT_FRAME_RATE_NUM, self.DEFAULT_FRAME_RATE_DEN)
try:
stereo_mode = self.STEREO_MODES[stereo_mode]
except KeyError:
raise PiCameraValueError('Invalid stereo mode: %s' % stereo_mode)
try:
clock_mode = self.CLOCK_MODES[clock_mode]
except KeyError:
raise PiCameraValueError('Invalid clock mode: %s' % clock_mode)
try:
self._init_camera(
camera_num, sensor_mode, resolution, framerate,
self.STEREO_MODES[stereo_mode], stereo_decimate)
stereo_mode, stereo_decimate, clock_mode)
self._init_defaults()
self._init_preview()
self._init_splitter()
Expand All @@ -508,7 +526,7 @@ def _init_led(self):

def _init_camera(
self, num, sensor_mode, resolution, framerate,
stereo_mode, stereo_decimate):
stereo_mode, stereo_decimate, clock_mode):
self._camera = ct.POINTER(mmal.MMAL_COMPONENT_T)()
self._camera_config = mmal.MMAL_PARAMETER_CAMERA_CONFIG_T(
mmal.MMAL_PARAMETER_HEADER_T(
Expand Down Expand Up @@ -570,7 +588,7 @@ def _init_camera(
cc.num_preview_video_frames = 3
cc.stills_capture_circular_buffer_height = 0
cc.fast_preview_resume = 0
cc.use_stc_timestamp = mmal.MMAL_PARAM_TIMESTAMP_MODE_RESET_STC
cc.use_stc_timestamp = clock_mode
mmal_check(
mmal.mmal_port_parameter_set(self._camera[0].control, cc.hdr),
prefix="Camera control port couldn't be configured")
Expand Down Expand Up @@ -2052,6 +2070,24 @@ def _set_raw_format(self, value):
various capture methods instead.
""")

def _get_timestamp(self):
stc = ct.c_uint64()
mmal_check(
mmal.mmal_port_parameter_get_uint64(self._camera[0].control,
mmal.MMAL_PARAMETER_SYSTEM_TIME, stc),
prefix="Failed to retrieve camera time")
return stc.value
timestamp = property(_get_timestamp, doc="""
Retrieves the system time according to the camera firmware.
The camera's timestamp is a 64-bit integer representing the number of
microseconds since the last system boot. When the camera's clock mode
is ``'raw'`` (see ``clock_mode`` in the :class:`PiCamera` documentation)
the values returned by this attribute are comparable to those from the
:attr:`frame` :attr:`~picamera.encoders.PiVideoFrame.timestamp`
attribute.
""")

def _get_frame(self):
for e in self._encoders.values():
try:
Expand Down
24 changes: 14 additions & 10 deletions picamera/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,12 @@ class PiVideoFrame(namedtuple('PiVideoFrame', (
.. attribute:: timestamp
Returns the presentation timestamp (PTS) of the current frame as
reported by the encoder. This is represented by the number of
microseconds (millionths of a second) since video recording started. As
the frame attribute is only updated when the encoder outputs the end of
a frame, this value may lag behind the actual time since
:meth:`~picamera.camera.PiCamera.start_recording` was called.
reported by the encoder. When the camera's clock mode is ``'reset'``
(the default), this is the number of microseconds (millionths of a
second) since video recording started. When the camera's clock mode is
``'raw'``, this is the number of microseconds since the last system
reboot. See :attr:`~picamera.camera.PiCamera.timestamp` for more
information.
.. warning::
Expand Down Expand Up @@ -865,9 +866,12 @@ def stop(self):
# The check 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
with self.parent._encoders_lock:
if self.active:
# continued attempts to disable an already disabled port. Lock
# acquisition must occur after the check to avoid re-acquiring a
# non-re-entrant lock in certain conditions (e.g. encoder destruction
# from __init__, when the lock is held by the same thread)
if self.active:
with self.parent._encoders_lock:
self.parent._stop_capture(self.camera_port)
try:
mmal_check(
Expand Down Expand Up @@ -1063,7 +1067,7 @@ def _create_encoder(
'mjpeg': mmal.MMAL_ENCODING_MJPEG,
}[self.format]
except KeyError:
raise PiCameraValueError('Unrecognized format %s' % self.format)
raise PiCameraValueError('Unsupported format %s' % self.format)

if not (0 <= bitrate <= 25000000):
raise PiCameraValueError('bitrate must be between 0 and 25Mbps')
Expand Down Expand Up @@ -1387,7 +1391,7 @@ def _create_encoder(self, quality=85, thumbnail=(64, 48, 35), bayer=False):
'bmp': mmal.MMAL_ENCODING_BMP,
}[self.format]
except KeyError:
raise PiCameraValueError("Unrecognized format %s" % self.format)
raise PiCameraValueError("Unsupported format %s" % self.format)
mmal_check(
mmal.mmal_port_format_commit(self.output_port),
prefix="Unable to set format on encoder output port")
Expand Down
10 changes: 10 additions & 0 deletions tests/test_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,13 @@ def test_exif_binary(camera, mode):
assert exif[33432] == b'Photographer copyright (c) 2000 Foo\x00Editor copyright (c) 2002 Bar\x00'
assert exif[37510] == b'UNICODE\x00\xff\xfeF\x00o\x00o\x00'

def test_capture_bad_format(camera):
with pytest.raises(picamera.PiCameraValueError):
camera.capture('test.foo')
with pytest.raises(picamera.PiCameraValueError):
camera.capture('test.jpg', format='foo')
with pytest.raises(picamera.PiCameraValueError):
camera.capture('test.tiff')
with pytest.raises(picamera.PiCameraValueError):
camera.capture('test.jpg', format='tiff')

10 changes: 10 additions & 0 deletions tests/test_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,13 @@ def test_motion_record(camera, mode):
finally:
camera.stop_recording()

def test_record_bad_format(camera):
with pytest.raises(picamera.PiCameraValueError):
camera.start_recording('test.foo')
with pytest.raises(picamera.PiCameraValueError):
camera.start_recording('test.h264', format='foo')
with pytest.raises(picamera.PiCameraValueError):
camera.start_recording('test.mp4')
with pytest.raises(picamera.PiCameraValueError):
camera.start_recording('test.h264', format='mp4')

0 comments on commit 24e1a17

Please sign in to comment.