Using USB API (MTP) with libghoto2 and Python bindings on MacOS, Raspberry Pi, Linux, ROS

For prototyping, can you use Vagrant or straight VirtualBox and run Linux inside the VM?

That way, you can test out your app concepts quickly instead of diving into compiling the library.

Maybe you can even deploy with Linux in a VM?

gphoto2 works well on linux and it seems to be installable on MacOs.
I haven’t a Mac but on Linux Raspbian I need to uninstall sort of automount to make it work



Additional tests with Automount

FWIW I have had the best luck with gphoto2. Older versions (2.5.4) will not set shutter speed but I am having luck with the latest (2.5.23).

List all shutter speeds:

gphoto2 --get-config=d00f

Set shutter speed iso and fstop:

gphoto2 --set-config-index=d00f=0 --set-config=500f=80 --set-config=5007=560

Thanks for the report. It’s surprising that there’s a difference between 2.5.4 and 2.5.23. This information should be helpful to other people. :slight_smile:

Here is some code using the gphoto2 python module…I have a UI that uses this to shoot HDRI exposures…an example in _unittest(). Not totally polished but maybe useful to others. Works with Z1:

USB api for added performance over http

Theta api reference:

Unable to get mtp or ptp to connect to the camera; After some pain was able to get gphoto2 working

import os
import time

import gphoto2 as gp

# Properties
F_NUMBER = '5007'

# milliseconds

def wait_for_event(camera, timeout=TIMEOUT, event_type=gp.GP_EVENT_TIMEOUT):
    Wait for event_type to to be triggered.
    :param camera:
    :param timeout:
    :param event_type:
    :return: event_data
    while True:
        _event_type, event_data = camera.wait_for_event(timeout)
        if _event_type == gp.GP_EVENT_TIMEOUT:
        if _event_type == event_type:
            return event_data

def set_config_by_index(config, index):
    """Set config using choice index"""
    value = config.get_choice(index)

    return config

# def list_files(camera, path='/'):
#     result = []
#     # get files
#     for name, value in camera.folder_list_files(path):
#         result.append(os.path.join(path, name))
#     # read folders
#     folders = []
#     for name, value in camera.folder_list_folders(path):
#         folders.append(name)
#     # recurse over subfolders
#     for name in folders:
#         result.extend(list_files(camera, os.path.join(path, name)))
#     return result
# def get_file_info(camera, path):
#     folder, name = os.path.split(path)
#     return camera.file_get_info(folder, name)

class CameraUsb(object):
    Define API for multiple exposure
    def __init__(self, verbose=False):
        self.verbose = verbose = gp.Camera()

        self.camera_config = None
        self.status_config = None
        self.other_config = None
        self.shutter_speed_config = None
        self.shutter_speed_options = []

    def init(self):
        Set manual exposure and other defaults
        :return: config
            self.camera_config =
        except gp.GPhoto2Error:
            raise RuntimeError("Unable to connect to Camera")

        self.other_config = self.camera_config.get_child_by_name('other')

        # Manual/f-stop/iso
        exposure_program_mode = self.other_config.get_child_by_name(EXPOSURE_PROGRAM_MODE)
        if not exposure_program_mode.get_value() == '1':
            print('Setting camera to Manual exposure program')

            # When switching exposure program, we need to refresh the configs
            self.camera_config =
            self.other_config = self.camera_config.get_child_by_name('other')

        self.status_config = self.camera_config.get_child_by_name('status')

        self.shutter_speed_config = self.other_config.get_child_by_name(SHUTTER_SPEED)
        self.shutter_speed_options = [str(x) for x in self.shutter_speed_config.get_choices()]
        if len(self.shutter_speed_options) != 61:
            raise RuntimeError('Unble to determine shutter speed options; restart app')

        fstop = self.other_config.get_child_by_name(F_NUMBER)

        iso = self.other_config.get_child_by_name(EXPOSURE_INDEX)

    def get_info(self):
        :return: Dict containing serialnumber, batterylevel, remainingpictures, etc
        if not self.camera_config:

        battery_level = self.status_config.get_child_by_name('batterylevel').get_value()
        # Convert '67%' to int
        battery_level = int(''.join([x for x in battery_level if x.isdigit()]))

        info = {'serialnumber': self.status_config.get_child_by_name('serialnumber').get_value(),
                'cameramodel': self.status_config.get_child_by_name('cameramodel').get_value(),
                'deviceversion': self.status_config.get_child_by_name('deviceversion').get_value(),
                'batterylevel': battery_level,
                'remainingpictures': int([0].freeimages)}
        return info

    def take_picture(self, shutter_speed_index=None, color_temperature=None, volume=None):
        Set camera options and take picture
        :param shutter_speed_index: int in range 0-60 (0 fastest shutter)
        :param color_temperature: in in range 2500-10000 by 100 increment
        :param volume: int in range 0-100
        :return: (jpg_path, dng_path)
        t1 = time.time()
        if not self.camera_config:

        if shutter_speed_index is not None:

        if color_temperature is not None:

        if volume is not None:
        # We need this even though no event is triggered

        gp_jpg_path =

        gp_dng_path = wait_for_event(, timeout=TIMEOUT_CAPTURE_DNG, event_type=gp.GP_EVENT_FILE_ADDED)
        if not gp_dng_path:
            raise RuntimeError('Unable to copy DNG')

        jpg_path = os.path.join(gp_jpg_path.folder,
        dng_path = os.path.join(gp_dng_path.folder,

        print('Capture took %0.03f sec' % (time.time() - t1, ))
        return jpg_path, dng_path

    def download_file(self, src_path, dst_path, delete=True):
        """Copy the file from the camera src_path to local dst_path"""
        t1 = time.time()

        src_folder, src_name = os.path.split(src_path)
        src_file =, src_name, gp.GP_FILE_TYPE_NORMAL)
        print('Download %s ->\n\t%s' % (src_path, dst_path))
        print('Download took %0.03f sec' % (time.time() - t1, ))

        if delete:
            t1 = time.time()
            print('Delete %s' % src_path)
  , src_name)
            print('Delete took %0.03f sec' % (time.time() - t1, ))

def _unittest():
    """test a short exposure sequence"""
    # temporary directory
    dst_template = '/tmp/theta/capture.%04d.%s'

    t1 = time.time()
    camera = CameraUsb()



    frame = 1
    jpg_path, dng_path = camera.take_picture(0)
    print(jpg_path, dng_path)
    camera.download_file(dng_path, dst_template % (frame, 'dng'))
    frame += 1

    jpg_path, dng_path = camera.take_picture(24)
    print(jpg_path, dng_path)
    camera.download_file(dng_path, dst_template % (frame, 'dng'))
    frame += 1

    jpg_path, dng_path = camera.take_picture(42)
    print(jpg_path, dng_path)
    camera.download_file(dng_path, dst_template % (frame, 'dng'))
    frame += 1
    print('Done in %0.03f sec' % (time.time() - t1, ))

if __name__ == "__main__":



Changelog in Gphoto2 indicates support of Theta ShutterSpeed in 2.5.10
But after some tests
Gphoto2 2.5.23 (fresh compilation) on raspbian + Theta V => shutterSpeed (on video mode, i.e. shutter is less than a second) is not working (no real change after a set-config)

Maybe it’s work on other cameras.For now, ptpcam still needed for shutterspeed configuration in USB

I found a way to change shutterspeed on the Theta V with gphoto2 (on linux raspbian but it shoud work on others platforms)
Since version 2.5.10, Ricoh Shutterspeed in implemented in libghoto2 but it was not working with the Theta V (at least for me).
After a few tries I found that the VendorID reported on the Theta V is 0x00000006, this number is MICROSOFT and not PENTAX (the ricoh shutterspeed is only for PENTAX VendorID)

I add this ligne in config.c (libgphoto2-2.5.23\camlibs\ptp2)

{ N_(“Shutter Speed”), “shutterspeed”, PTP_DPC_RICOH_ShutterSpeed, PTP_VENDOR_MICROSOFT, PTP_DTC_UINT64, _get_Ricoh_ShutterSpeed, _put_Ricoh_ShutterSpeed },

just after this one :

{ N_(“Shutter Speed”), “shutterspeed”, PTP_DPC_RICOH_ShutterSpeed, PTP_VENDOR_PENTAX, PTP_DTC_UINT64, _get_Ricoh_ShutterSpeed, _put_Ricoh_ShutterSpeed },

You need to compile libgphoto2 and now you can use
gphoto2 --get-config shutterspeed
gphoto2 --set-config-index shutterspeed=10

(with the property 0xD00F it’s not working)

If ExposureProgramMode=2 (Normal)
I get:

Label: Shutter Speed
Readonly: 0
Current: Auto
Choice: 0 Auto

if I switch to ExposureProgramMode=4 (Shutter priority program)
(gphoto2 --set-config=/main/other/500e=4 )
I get

Label: Shutter Speed
Readonly: 0
Current: 1/60
Choice: 0 1/25000
Choice: 1 1/20000
Choice: 2 1/16000
Choice: 3 1/12500
Choice: 4 1/10000
Choice: 5 1/8000
Choice: 6 1/6400
Choice: 7 1/5000
Choice: 8 1/4000
Choice: 9 1/3200
Choice: 10 1/2500
Choice: 11 1/2000
Choice: 12 1/1600
Choice: 13 1/1250
Choice: 14 1/1000
Choice: 15 1/800
Choice: 16 1/640
Choice: 17 1/500
Choice: 18 1/400
Choice: 19 1/320
Choice: 20 1/250
Choice: 21 1/200
Choice: 22 1/160
Choice: 23 1/125
Choice: 24 1/100
Choice: 25 1/80
Choice: 26 1/60
Choice: 27 1/50
Choice: 28 1/40
Choice: 29 1/30

Bye bye ptpcam (the main camera led was blinking during ptpcam shutterspeed modification - not very good - and sometimes the camera was disconnected from usb)


1 Like

FWIW I set ExposureProgramMode=1 (Manual) before setting shutter_speed

Mr. @mhenrie, is this code on Github?
If you know the Git URL when I’m helping you with this code, you can fork it or whatever, which will come in handy for contributing to the community and all that.
I’d appreciate it if you could let me know.

1 Like

Sorry I do not have this code on github or elsewhere but you are free to use what is posted here.


Copy that.
When I create a derived program, I’ll indicate your display name and the URL of this site.


If you put your code up on GitHub, please put the link here. I’m going to run tests with gphoto2 myself next week. Thanks.

Update Sept 9, 2020

I need advice on how to set the values of the camera with the Python API. If I change the camera to video mode, the value changes, but the camera does not actually shift into video mode.

The same command works from the command line. I think I need help with the basic syntax of using the gphoto2 Python module.

import gphoto2 as gp

MODE = '5013'
MOVIE =  str(int('0x8002', 0))

camera = gp.Camera()

mode = camera.get_single_config(MODE)
1 Like

It’s been a while, @mhenrie.
I finally tried your code after all sorts of messes, but I got an error.
Can you guess the cause?

Setting camera to Manual exposure program
Traceback (most recent call last):
File “/”, line 234, in
File “/”, line 210, in _unittest
File “/”, line 116, in init
raise RuntimeError(‘Unable to determine shutter speed options; restart app’)
RuntimeError: Unable to determine shutter speed options; restart app

Unfortunately, @craig’s code also gives me an error, although I don’t know why.
Maybe the path is wrong?

Traceback (most recent call last):
File “/”, line 10, in
mode = camera.get_single_config(MODE)
gphoto2.GPhoto2Error: [-6] Unsupported operation

Can you run the code in the examples directory of python-gphoto2 on GitHub?

In particular, does this work?

$ python 
WARNING: gphoto2: (gp_port_set_error [gphoto2-port.c:1190]) The supplied vendor or product id (0x0,0x0) is not valid.
Manufacturer: Ricoh Company, Ltd.
  Version: 1.50.1
  Serial Number: 10010104
Vendor Extension ID: 0x6 (1.10)

Hugues mentioned that a modification to this file might help.

I have not tried his fix yet.

Some of the commands seem to work.

$ python 
WARNING: gphoto2: (gp_port_set_error [gphoto2-port.c:1190]) The supplied vendor or product id (0x0,0x0) is not valid.
File list

Information on building libgphoto2 from source is here:

I was able to build it from source.

At the current time, I am using ptpcam, which seems to work okay for my uses.

My goodness, that’s a lot more sample programs than I checked!
I might be able to develop some of this myself.

Aside from that, I ran and it looked like below.

WARNING: gphoto2: (foreach_func [gphoto2-port-info-list.c:237]) Error during assembling of port list: 'Unspecified error' (-1).
WARNING: gphoto2: (gp_port_set_error [gphoto2-port.c:1186]) The supplied vendor or product id (0x0,0x0) is not valid.
Manufacturer: Ricoh Company, Ltd.
  Version: 3.40.1
  Serial Number: 00153573
Vendor Extension ID: 0x6 (1.10)

Capture Formats: 
Display Formats: Association/Directory, JPEG, MP4, Firmware
Supported MTP Object Properties:
	Association/Directory/3001: dc01/StorageID dc02/ObjectFormat dc04/ObjectSize dc0b/ParentObject dc41/PersistantUniqueObjectIdentifier dc44/Name
	JPEG/3801: dc01/StorageID dc02/ObjectFormat dc03/ProtectionStatus dc04/ObjectSize dc07/ObjectFileName dc08/DateCreated dc09/DateModified dc0b/ParentObject dc41/PersistantUniqueObjectIdentifier dc44/Name dc87/Width dc88/Height
	MP4/b982: dc01/StorageID dc02/ObjectFormat dc03/ProtectionStatus dc04/ObjectSize dc07/ObjectFileName dc08/DateCreated dc09/DateModified dc0b/ParentObject dc41/PersistantUniqueObjectIdentifier dc44/Name dc87/Width dc88/Height
	Firmware/b802: dc01/StorageID dc02/ObjectFormat dc04/ObjectSize dc0b/ParentObject dc41/PersistantUniqueObjectIdentifier dc44/Name

Device Capabilities:
	File Download, File Deletion, No File Upload
	Generic Image Capture, Open Capture, No vendor specific capture

Storage Devices Summary:
	StorageDescription: None
	VolumeLabel: None
	Storage Type: Builtin RAM
	Filesystemtype: Digital Camera Layout (DCIM)
	Access Capability: Read-Write
	Maximum Capability: 21088788480 (20111 MB)
	Free Space (Bytes): 19082989568 (18198 MB)
	Free Space (Images): 4549

Device Property Summary:
Battery Level(0x5001):(read only) (type=0x2) Range [0 - 100, step 1] value: 100% (100)
Functional Mode(0x5002):(read only) (type=0x4) Enumeration [0] value: 0
Image Size(0x5003):(readwrite) (type=0xffff) Enumeration [
	] value: '5376x2688'
White Balance(0x5005):(readwrite) (type=0x4) Enumeration [2,4,32769,32770,6,32800,32771,32772,32773,32774,32775,32776] value: Automatic (2)
Exposure Program Mode(0x500e):(readwrite) (type=0x4) Enumeration [1,2,4,32771] value: P (2)
Exposure Index (film speed ISO)(0x500f):(readwrite) (type=0x4) Enumeration [65535] value: ISO 65535 (65535)
Exposure Bias Compensation(0x5010):(readwrite) (type=0x3) Enumeration [2000,1700,1300,1000,700,300,0,-300,-700,-1000,-1300,-1700,-2000] value: 0.0 stops (0)
Date & Time(0x5011):(readwrite) (type=0xffff) '20200925T020641+0900'
Pre-Capture Delay(0x5012):(readwrite) (type=0x6) Range [0 - 10000, step 1000] value: 0.0s (0)
Still Capture Mode(0x5013):(readwrite) (type=0x4) Enumeration [1,3,32770,32772,32773,32774,32775] value: Single Shot (1)
Timelapse Number(0x501a):(readwrite) (type=0x4) Range [0 - 9999, step 1] value: 0
Timelapse Interval(0x501b):(readwrite) (type=0x6) Range [4000 - 3600000, step 1000] value: 4000
Audio Volume(0x502c):(readwrite) (type=0x6) Range [0 - 100, step 1] value: 100
Property 0xd006:(read only) (type=0x6) 0
Property 0xd00f:(readwrite) (type=0x8) Enumeration [0] value: 0
Perceived Device Type(0xd407):(read only) (type=0x6) 1
Property 0xd801:(readwrite) (type=0xffff) '(null)'
Property 0xd803:(readwrite) (type=0x4) Range [0 - 65534, step 1] value: 180
Property 0xd805:(readwrite) (type=0xffff) 'THETAYL00153573.OSC'
Property 0xd806:(readwrite) (type=0xffff) '00153573'
Property 0xd808:(read only) (type=0x2) Enumeration [0,1,2,3,4] value: 0
Property 0xd809:(read only) (type=0x4) Range [0 - 1499, step 1] value: 0
Property 0xd80a:(read only) (type=0x4) Range [0 - 1500, step 1] value: 0
Property 0xd80b:(readwrite) (type=0x2) Enumeration [0,1,2,3,4] value: 0
Property 0xd80c:(read only) (type=0x2) Enumeration [0,1,2] value: 1
Property 0xd80d:(read only) (type=0x4) 8098
Property 0xd80e:(readwrite) (type=0x2) Enumeration [0,1] value: 0
Property 0xd812:(readwrite) (type=0x0) Undefined
Property 0xd813:(readwrite) (type=0x4) Range [2500 - 10000, step 100] value: 5000
Property 0xd814:(readwrite) (type=0x2) Enumeration [0,1] value: 0
Property 0xd815:(readwrite) (type=0xffff) 'THETAYL00153573'
Property 0xd816:(readwrite) (type=0xffff) '00153573'
Property 0xd817:(readwrite) (type=0x4) Enumeration [1] value: 1
Property 0xd818:(readwrite) (type=0x4) Enumeration [1,2] value: 1
Property 0xd81a:(readwrite) (type=0x2) Enumeration [0,1] value: 0
Property 0xd81b:(readwrite) (type=0x6) Range [0 - 2592000, step 60] value: 64980
Property 0xd81c:(readwrite) (type=0x2) Enumeration [0,1,2] value: 0
Property 0xd81d:(readwrite) (type=0x2) Enumeration [0,1] value: 0
Property 0xd81f:(readwrite) (type=0x2) Enumeration [0,1,2] value: 0
Property 0xd820:(readwrite) (type=0x2) Enumeration [0,1,2,3,4,5,6,7,8] value: 0
Property 0xd821:(readwrite) (type=0x2) Enumeration [0] value: 0
Property 0xd822:(read only) (type=0x4) 0
Property 0xd823:(readwrite) (type=0x4) Enumeration [300,1500] value: 1500
Property 0xd825:(readwrite) (type=0x2) Enumeration [0,1,2,3,4] value: 0
Property 0xd826:(readwrite) (type=0x4) Enumeration [200,250,320,400,500,640,800,1000,1250,1600,2000,2500,3200] value: 1600
Property 0xd829:(readwrite) (type=0x4) Enumeration [0] value: 0
Property 0xd82a:(readwrite) (type=0x2) Enumeration [0,1] value: 1
Property 0xd82c:(readwrite) (type=0x2) Enumeration [1,2,3] value: 2
Property 0xd831:(read only) (type=0xffff) 'ProgressRate:000'

and ran

WARNING: gphoto2: (foreach_func [gphoto2-port-info-list.c:237]) Error during assembling of port list: 'Unspecified error' (-1).
WARNING: gphoto2: (gp_port_set_error [gphoto2-port.c:1186]) The supplied vendor or product id (0x0,0x0) is not valid.
File list
File info
image dimensions: 1920 960
image type: video/mp4
file mtime: 2020-09-02 19:55:11

Without any modifications, “gphoto2 --set-config-index shutterspeed=10” could not be executed correctly.

*** Error (-110: 'I/O in progress') ***                                        

For debugging messages, please use the --debug option.
Debugging messages may help finding a solution to your problem.
If you intend to send any error or debug messages to the gphoto
developer mailing list <>, please run
gphoto2 as follows:

    env LANG=C gphoto2 --debug --debug-logfile=my-logfile.txt --get-config shutterspeed

Please make sure there is sufficient quoting around the arguments.

Is this something that can be fixed by modifying as @Hugues pointed out, or did @Hugues get the same error?
Also, have you already put up an issue in Git with a fix for this issue? If you haven’t already, I can get it up. While doing the quote.
Or shall I fork it and make a pull request?



I had the same error with gphoto2 installed from apt. The fix is working but you need to modify and recompile libgphoto2.
It will be nice if you make a pull request. Before that if someone could check the VendorID on others Theta cameras in order to make the correction for all the theta models.



Thank you for all your work and insight.

  1. I changed the title of the topic to reflect the actual discussion which evolved beyond MacOS
  2. I added a note to the very top post from the “moderator” to let people know that the topic changed

I’ll also try and help test this. There’s been a new surge in interest in the USB API, possibly driven by the increased demand for virtual surveillance.

BTW, if anyone with a good business opportunity is looking to partner with a company with experience in the USB API and streaming, please check out the article on FOX Sewer Rover and consider contacting Hugues with a great business opportunity. :slight_smile: Note the followup posts covering his modifications.

@Hugues is the VendorID, the same as the VendorExtensionID?

From the MTP Specification.

If so, I can run it against the cameras I have. I believe the extension ID is 0x00000006 on all of them, but I can check. In the libptp source code, this ID is assigned to Microsoft.


Camera information
  manufacturer: Ricoh Company, Ltd.
  serial number: '10010104'
  device version: 1.50.1
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e


Camera information
  manufacturer: Ricoh Company, Ltd.
  serial number: '00105377'
  device version: 3.40.1
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e

SC2 for business

This is the same as the SC2. The firmware number is different, but everything else should be the same for our purposes.

Camera information
  manufacturer: Ricoh Company, Ltd.
  serial number: '40100146'
  device version: 06.12
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e

@NaokiSato102, I have not looked into the GitHub pull request. Please proceed with your testing and I can try and support your efforts. To do a pull request, you’ll need to fork the project first and do the modification, then test the modifications, then do the pull request.

My status:

  • I have compiled libghoto2 from source with no problems on Ubuntu 20.04 on x86
  • I have not installed or tested it as I think I need to first uninstall my existing libghoto2 and I am afraid my system will stop working
  • I have not applied the modifications

My plan:

  • I’ll attempt to compile it from source on a Raspberry Pi. If it’s possible, I’ll experiment with the modifications
  • if it takes too long due to the size of the project, I will try a different computer or a VM.
  • I will report back here

I couldn’t get it to work with the lib I installed with apt, either. (summarized)

Thank you @Hugues for checking the behavior.
I’m going to try the following on Raspberry Pi 3B+ before I try it in the production development environment.

  1. Try it with a lib you got with apt (it’s expected to not work properly. Continue with the test in 3)
  2. Try with a modified lib in a clean environment. (It is expected to work properly.)
  3. Uninstall the apt lib introduced in step 1, and try the modified lib. (Same as above.)

And if 2 and 3 go well, I’ll try it out in the production development environment.
I’ll make the transition between 1 and 2 by backing up the disk image.