gphoto / libgphoto2

The libgphoto2 camera access and control library.
GNU Lesser General Public License v2.1
1.04k stars 324 forks source link

Fuji X-T2, USB ID 04cb:02cd #133

Closed jbreiden closed 3 years ago

jbreiden commented 7 years ago

This is the Fuji X-T2 camera. I'll look into USB communication details next.

$ lsusb | grep Fuji Bus 004 Device 004: ID 04cb:02cd Fuji Photo Film Co., Ltd

--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -1978,6 +1978,7 @@ static struct {
        {"Fuji:Fujifilm X-E2",                  0x04cb, 0x02b5, 0},
        /* Vladimir K <enewsletters@inbox.ru> */
        {"Fuji:Fujifilm X-T1",                  0x04cb, 0x02bf, 0},
+        {"Fuji:Fujifilm X-T2",                 0x04cb, 0x02cd, 0},
        /* https://github.com/gphoto/libgphoto2/issues/32 */
        {"Fuji:Fujifilm X-T10",                 0x04cb, 0x02c8, 0},
jbreiden commented 7 years ago
0.066445 print_debug_deviceinfo      (2): Device info:
0.066448 print_debug_deviceinfo      (2): Manufacturer: FUJIFILM
0.066451 print_debug_deviceinfo      (2):   Model: X-T2
0.066453 print_debug_deviceinfo      (2):   device version: 1.00
0.066455 print_debug_deviceinfo      (2):   serial number: '5935333130321611296B3020112361'
0.066457 print_debug_deviceinfo      (2): Vendor extension ID: 0x0000000e
0.066459 print_debug_deviceinfo      (2): Vendor extension version: 100
0.066462 print_debug_deviceinfo      (2): Vendor extension description: fujifilm.co.jp: 1.0; 
0.066464 print_debug_deviceinfo      (2): Functional Mode: 0x0000
0.066466 print_debug_deviceinfo      (2): PTP Standard Version: 100
0.066468 print_debug_deviceinfo      (2): Supported operations:
0.066473 print_debug_deviceinfo      (2):   0x1001 (Get device info)
0.066476 print_debug_deviceinfo      (2):   0x1002 (Open session)
0.066479 print_debug_deviceinfo      (2):   0x1003 (Close session)
0.066482 print_debug_deviceinfo      (2):   0x1004 (Get storage IDs)
0.066484 print_debug_deviceinfo      (2):   0x1005 (Get storage info)
0.066487 print_debug_deviceinfo      (2):   0x1006 (Get number of objects)
0.066489 print_debug_deviceinfo      (2):   0x1007 (Get object handles)
0.066492 print_debug_deviceinfo      (2):   0x1008 (Get object info)
0.066494 print_debug_deviceinfo      (2):   0x1009 (Get object)
0.066497 print_debug_deviceinfo      (2):   0x100a (Get thumbnail)
0.066500 print_debug_deviceinfo      (2):   0x100b (Delete object)
0.066502 print_debug_deviceinfo      (2):   0x100c (Send object info)
0.066505 print_debug_deviceinfo      (2):   0x100d (Send object)
0.066507 print_debug_deviceinfo      (2):   0x100f (Format storage)
0.066510 print_debug_deviceinfo      (2):   0x1014 (Get device property description)
0.066513 print_debug_deviceinfo      (2):   0x1015 (Get device property value)
0.066516 print_debug_deviceinfo      (2):   0x1016 (Set device property value)
0.066518 print_debug_deviceinfo      (2):   0x101b (Get partial object)
0.066521 print_debug_deviceinfo      (2):   0x900c (Unknown VendorExtensionID)
0.066524 print_debug_deviceinfo      (2):   0x900d (Unknown VendorExtensionID)
0.066526 print_debug_deviceinfo      (2):   0x901d (Unknown VendorExtensionID)
0.066529 print_debug_deviceinfo      (2):   0x9801 (Unknown VendorExtensionID)
0.066532 print_debug_deviceinfo      (2):   0x9802 (Unknown VendorExtensionID)
0.066534 print_debug_deviceinfo      (2):   0x9803 (Unknown VendorExtensionID)
0.066537 print_debug_deviceinfo      (2):   0x9805 (Unknown VendorExtensionID)
0.066539 print_debug_deviceinfo      (2): Events Supported:
0.066541 print_debug_deviceinfo      (2):   0x4002 
0.066542 print_debug_deviceinfo      (2):   0x4003
0.066544 print_debug_deviceinfo      (2):   0x4004
0.066546 print_debug_deviceinfo      (2):   0x4005
0.066547 print_debug_deviceinfo      (2):   0x4006
0.066549 print_debug_deviceinfo      (2):   0x4008
0.066551 print_debug_deviceinfo      (2):   0x4009
0.066552 print_debug_deviceinfo      (2): Device Properties Supported:
0.066554 print_debug_deviceinfo      (2):   0x5001
0.066556 print_debug_deviceinfo      (2):   0xd303
0.066558 print_debug_deviceinfo      (2):   0xd406
0.066559 print_debug_deviceinfo      (2):   0xd407
0x4002 ObjectAdded
0x4003 ObjectRemoved
0x4004 StoreAdded
0x4005 StoreRemoved
0x4006 DevicePropChanged
0x4007 ObjectInfoChanged
0x4008 DeviceInfoChanged
0x4009 RequestObjectTransfer 
0x5001 BatteryLevel 
msmeissn commented 7 years ago

So far there is no indication that it has capture opcodes. The only unknown opcodes are 900c, 900d, 901d (The 0x98xx opcodes are generic MTP commands.) There is no event for capturecomplete. The 0xd303 and 0xd4xx properties are the standard MTP properties.

While capture might be hiding behind the 0x90xx codes or be hidden from the deviceinfo altogether, this seems unlikely.

jbreiden commented 7 years ago

Tethering capability was added in firmware revision 1.1 released Nov 2016. I will update the camera today.

jbreiden commented 7 years ago

To my surprise, the newer firmware (which supports tethering) has identical opcodes. I'll will shortly have my hands on a Windows computer and will endeavor to monitor USB communication during tethering. If there is a preferred mechanism for USB sniffing on Windows, please let me know.

0.034602 print_debug_deviceinfo      (2): Device info:
0.034605 print_debug_deviceinfo      (2): Manufacturer: FUJIFILM
0.034607 print_debug_deviceinfo      (2):   Model: X-T2
0.034609 print_debug_deviceinfo      (2):   device version: 1.10
0.034611 print_debug_deviceinfo      (2):   serial number: '5935333130321611296B3020112361'
0.034613 print_debug_deviceinfo      (2): Vendor extension ID: 0x0000000e
0.034614 print_debug_deviceinfo      (2): Vendor extension version: 100
0.034617 print_debug_deviceinfo      (2): Vendor extension description: fujifilm.co.jp: 1.0; 
0.034619 print_debug_deviceinfo      (2): Functional Mode: 0x0000
0.034621 print_debug_deviceinfo      (2): PTP Standard Version: 100
0.034622 print_debug_deviceinfo      (2): Supported operations:
0.034627 print_debug_deviceinfo      (2):   0x1001 (Get device info)
0.034629 print_debug_deviceinfo      (2):   0x1002 (Open session)
0.034631 print_debug_deviceinfo      (2):   0x1003 (Close session)
0.034634 print_debug_deviceinfo      (2):   0x1004 (Get storage IDs)
0.034636 print_debug_deviceinfo      (2):   0x1005 (Get storage info)
0.034638 print_debug_deviceinfo      (2):   0x1006 (Get number of objects)
0.034640 print_debug_deviceinfo      (2):   0x1007 (Get object handles)
0.034643 print_debug_deviceinfo      (2):   0x1008 (Get object info)
0.034645 print_debug_deviceinfo      (2):   0x1009 (Get object)
0.034647 print_debug_deviceinfo      (2):   0x100a (Get thumbnail)
0.034649 print_debug_deviceinfo      (2):   0x100b (Delete object)
0.034651 print_debug_deviceinfo      (2):   0x100c (Send object info)
0.034654 print_debug_deviceinfo      (2):   0x100d (Send object)
0.034656 print_debug_deviceinfo      (2):   0x100f (Format storage)
0.034658 print_debug_deviceinfo      (2):   0x1014 (Get device property description)
0.034661 print_debug_deviceinfo      (2):   0x1015 (Get device property value)
0.034663 print_debug_deviceinfo      (2):   0x1016 (Set device property value)
0.034665 print_debug_deviceinfo      (2):   0x101b (Get partial object)
0.034668 print_debug_deviceinfo      (2):   0x900c (Unknown VendorExtensionID)
0.034670 print_debug_deviceinfo      (2):   0x900d (Unknown VendorExtensionID)
0.034672 print_debug_deviceinfo      (2):   0x901d (Unknown VendorExtensionID)
0.034674 print_debug_deviceinfo      (2):   0x9801 (Unknown VendorExtensionID)
0.034677 print_debug_deviceinfo      (2):   0x9802 (Unknown VendorExtensionID)
0.034679 print_debug_deviceinfo      (2):   0x9803 (Unknown VendorExtensionID)
0.034681 print_debug_deviceinfo      (2):   0x9805 (Unknown VendorExtensionID)
0.034683 print_debug_deviceinfo      (2): Events Supported:
0.034684 print_debug_deviceinfo      (2):   0x4002
0.034686 print_debug_deviceinfo      (2):   0x4003
0.034687 print_debug_deviceinfo      (2):   0x4004
0.034689 print_debug_deviceinfo      (2):   0x4005
0.034690 print_debug_deviceinfo      (2):   0x4006
0.034692 print_debug_deviceinfo      (2):   0x4008
0.034693 print_debug_deviceinfo      (2):   0x4009
0.034695 print_debug_deviceinfo      (2): Device Properties Supported:
0.034696 print_debug_deviceinfo      (2):   0x5001
0.034698 print_debug_deviceinfo      (2):   0xd303
0.034699 print_debug_deviceinfo      (2):   0xd406
0.034701 print_debug_deviceinfo      (2):   0xd407
msmeissn commented 7 years ago

In in earlier times used SnoopyPro. Not sure if it still lives..

Can you try

gphoto2 --capture-tethered

and press shutter button and see if something happens?

jbreiden commented 7 years ago

Nothing. I'll play with the camera and make sure that I'm in the right mode.

31.309643 camera_wait_for_event       (2): no events received.
31.309653 camera_wait_for_event       (2): waiting for events timeout 1000 ms
31.309661 gp_port_get_timeout         (2): Current port timeout is 20000 milliseconds.
31.309667 gp_port_set_timeout         (2): Setting port timeout to 150 milliseconds.
31.309674 gp_port_check_int           (3): Reading 24 = 0x18 bytes from interrupt endpoint...
31.459909 gp_port_check_int           (3): Reading 24 = 0x18 bytes from interrupt endpoint...
31.610150 gp_port_set_timeout         (2): Setting port timeout to 20000 milliseconds.
31.610200 ptp_usb_event [usb.c:530]   (0): Reading PTP event failed: Timeout reading from or writing to the port (-10)
jbreiden commented 7 years ago

Now with the camera in the correct mode (Connection Setting > PC Shoot Mode > USB Auto) we get a lot more. Still nothing from gphoto2 --capture-tethered

log.txt

jbreiden commented 7 years ago

Wait, maybe I am getting something. Need to charge battery.

jbreiden commented 7 years ago
$ gphoto2 --capture-tethered --camera="Fuji Fujifilm X-T2" --debug --debug-logfile=/tmp/debug.txt --debug-loglevel=debug
Waiting for events from camera. Press Ctrl-C to abort.                         
Saving file as DSCF0001.jpg                                                    
^C
Cancelling...

debug.txt

msmeissn commented 7 years ago

0.035898 print_debug_deviceinfo (2): 0x100e (Initiate capture)

gphoto2 --capture-image now should also work

jbreiden commented 7 years ago
$ gphoto2 --capture-image

*** Error ***              
PTP Device Busy
ERROR: Could not capture image.
ERROR: Could not capture.
*** 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 <gphoto-devel@lists.sourceforge.net>, please run
gphoto2 as follows:

    env LANG=C gphoto2 --debug --debug-logfile=my-logfile.txt --capture-image

Please make sure there is sufficient quoting around the arguments.

debug.txt

msmeissn commented 7 years ago

usually that should just work as is when called. hard to say why it reports busy :(

can you get output of:

gphoto2 --summary > summary.txt gphoto2 --list-all-config > allconfig.txt

jbreiden commented 7 years ago

Had to reschedule USB tracing, should have that by the end of Monday.

summary.txt allconfig.txt

jbreiden commented 7 years ago

The one of most interest is Single Shot

Data viewer http://www.totalphase.com/products/data-center/

Traces https://drive.google.com/drive/folders/0B1NxYWevtHcMdHJmcVBLVEdzalU?usp=sharing

jbreiden commented 7 years ago

Decoded USB traces added to same location. Note the double call to InitiateCapture.

jbreiden commented 7 years ago

I think the camera is unhappy and becomes "PTP Device Busy" because when we try to list the files on the camera. Here is what gphoto2 is doing.

$ gphoto2 --debug --capture-image |& grep Sending | cut -f2 -d:
 Sending PTP_OC 0x1002 (Open session) (0x1) request...
 Sending PTP_OC 0x1001 (Get device info) request...
 Sending PTP_OC 0x1007 (Get object handles) (0xffffffff,0x0,0xffffffff) request...
 Sending PTP_OC 0x1004 (Get storage IDs) request...
 Sending PTP_OC 0x1007 (Get object handles) (0x10000001,0x0,0xffffffff) request...
 Sending PTP_OC 0x1007 (Get object handles) (0x10000002,0x0,0xffffffff) request...
 Sending PTP_OC 0x100e (Initiate capture) (0x0,0x0) request...
 FAIL

Comparing to the working USB trace, there isn't any attempt to get object handles until after capture is complete. And when it does happen, it looks different to my eye.

$ grep COMMAND Single\ Shot.txt | cut -f4- -d: | less
...
PTP_OC_InitiateCapture         trans:831c payload:'\x00\x00\x00\x00\x00\x00\x00\x00'
...
PTP_OC_InitiateCapture         trans:835e payload:'\x00\x00\x00\x00\x00\x00\x00\x00'
...
PTP_OC_GetObjectHandles        trans:8365 payload:'\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00'
PTP_OC_GetObjectInfo           trans:8366 payload:'\x07\x00\x00\x00'
PTP_OC_GetObjectInfo           trans:8367 payload:'\x07\x00\x00\x00'
PTP_OC_GetObjectInfo           trans:8368 payload:'\x07\x00\x00\x00'
PTP_OC_MTP_GetObjPropList      trans:8369 payload:'\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
PTP_OC_GetObject               trans:836a payload:'\x07\x00\x00\x00'
PTP_OC_DeleteObject            trans:836b payload:'\x07\x00\x00\x00'
...
msmeissn commented 7 years ago

This is very helpful, thanks. It is setting various properties, that is probably more the reason.

(currently a bit busy witjh work and FOSDEM travel, hope to have some time next week to check it out more)

jbreiden commented 7 years ago

This appears to be the magical incantation to allow InitiateCapture() to work. Only works once per camera powercycle. This will make the camera beep, presumably indicating successful autofocus.

propval.u16 = 0x0002; C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
propval.u16 = 0x0200; C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

Add in this part, and we get a click sound after the beep.

usleep(1000*1000);
propval.u16 = 0x0304; C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));        

At this point the camera is blocked and unresponsive to further shot requests. The camera has in image in RAM and is very afraid to lose that data. There is a small LED on the camera body the flashes red and green. I have to unplug USB, turn off the power switch, then hit "okay" to actually powercycle and prepare for next shot.

Here's a debug log when I plumb this through --capture-image-and-download.

debuglog.txt

msmeissn commented 7 years ago

ignore last commit, it seems per-captuyre specific.

msmeissn commented 7 years ago

(thanks for your work so far ... still not yet found time to properly go over it :/ )

jbreiden commented 7 years ago

Looking at Fuji XT-2 again. This is what it takes to make a "beep" (presumably autofocus) then a "click" (presumably shutter release) using gphoto2 --trigger-capture. Contrary to my earlier reports above, the only property I'm messing with is 0xd208. And this is sufficient run multiple times without power cycling. Yay. I am going to try to rewrite this for real. (Also, we need to revert c998fcd which is causing harm)

happy.txt

--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -4577,7 +4577,15 @@ camera_trigger_capture (Camera *camera, GPContext *context)
                        _("Sorry, your camera does not support generic capture"));
                return GP_ERROR_NOT_SUPPORTED;
        }
-       C_PTP_REP (ptp_initiatecapture(params, 0x00000000, 0x00000000));
+        {
+          PTPPropertyValue     propval;
+          propval.u16 = 0x0200; C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP (ptp_initiatecapture(params, 0x00000000, 0x00000000));
+          usleep(1000*1000);
+          propval.u16 = 0x0304; C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));
+        }
+
        return GP_OK;
 }
jbreiden commented 7 years ago

... And now I can't communicate with the camera under any circumstance. Same code, nothing obviously different from the logs. Powercycled camera including primary battery removal. Wasn't fooling around with camera settings.

sad.txt

0.163308 camera_trigger_capture      (2): camera_trigger_capture
0.163321 camera_trigger_capture      (2): Triggering capture to , autofocus=0
0.163329 camera_trigger_capture      (2): XXX Hey
0.163356 ptp_usb_sendreq             (2): Sending PTP_OC 0x1016 (Set device property value) (0xd208) request...
0.163428 ptp_usb_senddata            (2): Sending PTP_OC 0x1016 (Set device property value) data...
0.163499 ptp_usb_getresp             (2): Reading PTP_OC 0x1016 (Set device property value) response...
0.363983 ptp_usb_sendreq             (2): Sending PTP_OC 0x100e (Initiate capture) (0x0,0x0) request...
0.364127 ptp_usb_getresp             (2): Reading PTP_OC 0x100e (Initiate capture) response...
0.364739 ptp_usb_getresp [usb.c:466] (0): PTP_OC 0x100e receiving resp failed: PTP Device Busy (0x2019)
0.364752 camera_trigger_capture [library.c:4585](0): 'ptp_initiatecapture(params, 0x00000000, 0x00000000)' failed: 'PTP Device Busy' (0x2019)
0.364763 gp_context_error            (0): PTP Device Busy

*** Error ***              
PTP Device Busy
0.364778 gp_camera_trigger_capture [gphoto2-camera.c:1369](0): 'camera->functions->trigger_capture (camera, context)' failed: -110
ERROR: Could not trigger capture.
*** Error (-110: 'I/O in progress') ***       
jbreiden commented 7 years ago

Flashed camera firmware to from v1.10 to v2.00; no change. Camera does not want to talk to me anymore.

msmeissn commented 7 years ago

urks :( sorry to hear :(

and sorry i currently did not pursue this issue further .. quite busy and i focused a bit more on pentax and canon eos m, but wanted to get back here

msmeissn commented 7 years ago

check for possible causes of busy condit9ion ... sd card full? bttery empty? ;)

jbreiden commented 7 years ago

Talking to camera again.

The camera has two modes controlled by property 0xd207. The default value 0x0100 uses the camera physical shutter release button. Set it to 0x0200 and USB tethering controls the shutter release. Here is what a trigger-capture loop looks like.

for (i = 0; i < 15; i++) {
  // focus. camera will beep if autofocus is turned on
  propval.u16 = 0x0200;
  C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
  C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

  // poll camera until it is ready
 propval.u16 = 0x0001;
  while (propval.u16 == 0x0001) {
    ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
  }

  // shoot. camera will make a click sound
  propval.u16 = 0x0304;
  C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
  C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

  // poll until camera is ready again.
  propval.u16 = 0x0000;
  while (propval.u16 == 0x0000) {
    ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT16);
  }
}

Getting images off the camera is interesting. The camera itself will blink an LED on the body if there is an image sitting in RAM. In camera_capture() we get stuck because the camera was not sending a PTP_NO_CAPTURE_COMPLETE event. That's easily fixed.

--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -2007,7 +2007,7 @@ static struct {
        /* https://github.com/gphoto/libgphoto2/issues/32 */
        {"Fuji:Fujifilm X-T10",                 0x04cb, 0x02c8, 0},
        /* with new updated firmware 1.1 */
-       {"Fuji:Fujifilm X-T2",                  0x04cb, 0x02cd, PTP_CAP},
+       {"Fuji:Fujifilm X-T2",                  0x04cb, 0x02cd, PTP_CAP|PTP_NO_CAPTURE_COMPLETE},

        {"Ricoh:Caplio R5 (PTP mode)",          0x05ca, 0x0110, 0},
        {"Ricoh:Caplio GX (PTP mode)",          0x05ca, 0x0325, 0},

Our next problem is gphoto is only able to locate and transfer the first shot for a camera powercycle. That's because it requires data from the interrupt endpoint. Here's success from our first shot.

0.581214 gp_port_check_int           (2): XXX Enter gp_port_check_int
0.581224 gp_port_check_int           (2): Reading 24 = 0x18 bytes from interrupt endpoint...
0.581421 _cb_irq                     (2): 0x1581818 with status 0
0.581449 _cb_irq                     (2): requeuing complete transfer 0x1581818
0.581475 gp_port_check_int           (2): XXX Read 16 bytes from interrupt endpoint...
0.581488 gp_port_check_int [gphoto2-port.c:479](0): XXX Exit gp_port_check_int
0.581501 ptp                         (2): event: nparams=0x1, code=0x4002, trans_id=0xFFFFFFFF, p1=0x1, p2=0x0, p3=0x0

But the second shot never generates that interrupt.

0.137997 gp_port_check_int           (2): XXX Enter gp_port_check_int
0.138005 gp_port_check_int           (2): Reading 24 = 0x18 bytes from interrupt endpoint...
20.157053 gp_port_check_int           (2): XXX Enter gp_port_check_int
20.157085 gp_port_check_int           (2): Reading 24 = 0x18 bytes from interrupt endpoint...
40.164617 ptp_usb_event [usb.c:541]   (0): Reading PTP event failed: Timeout reading from or writing to the port (-10)

If we look at the USB traces from Windows, there is never any data transferred the interrupt endpoint. It sure looks like the magical signal for an available image is the 0xd212 property. As soon as that thing turns non-zero we get 8 bytes of data back, and then we all ready go the next step of PTP_OC_GetObjectHandles(). I don't know how important those 8 bytes are or if they can change. In my trace with 5 shots, they were all the same..

OUT Txn  0:20.609.412 COMMAND : code:PTP_OC_InitiateCapture         trans:44f5 payload:'\x00\x00\x00\x00\x00\x00\x00\x00'
 IN Txn  0:20.609.516 RESPONSE: code:PTP_RC_OK                      trans:44f5 data:''
OUT Txn  0:20.620.600 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44f6 property:0xd212
 IN Txn  0:20.621.171 DATA    : code:PTP_OC_GetDevicePropValue      trans:44f6 data:'\x00\x00'
 IN Txn  0:20.622.800 RESPONSE: code:PTP_RC_OK                      trans:44f6 data:''
OUT Txn  0:20.702.103 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44f7 property:0xd212
 IN Txn  0:20.702.284 DATA    : code:PTP_OC_GetDevicePropValue      trans:44f7 data:'\x00\x00'
 IN Txn  0:20.703.667 RESPONSE: code:PTP_RC_OK                      trans:44f7 data:''
OUT Txn  0:20.783.484 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44f8 property:0xd212
 IN Txn  0:20.783.911 DATA    : code:PTP_OC_GetDevicePropValue      trans:44f8 data:'\x00\x00'
 IN Txn  0:20.785.499 RESPONSE: code:PTP_RC_OK                      trans:44f8 data:''
OUT Txn  0:20.865.601 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44f9 property:0xd212
 IN Txn  0:20.868.479 DATA    : code:PTP_OC_GetDevicePropValue      trans:44f9 data:'\x00\x00'
 IN Txn  0:20.877.611 RESPONSE: code:PTP_RC_OK                      trans:44f9 data:''
OUT Txn  0:20.957.221 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44fa property:0xd212
 IN Txn  0:20.957.602 DATA    : code:PTP_OC_GetDevicePropValue      trans:44fa data:'\x00\x00'
 IN Txn  0:20.959.304 RESPONSE: code:PTP_RC_OK                      trans:44fa data:''
OUT Txn  0:21.039.017 COMMAND : code:PTP_OC_GetDevicePropValue      trans:44fb property:0xd212
 IN Txn  0:21.039.542 DATA    : code:PTP_OC_GetDevicePropValue      trans:44fb data:'\x01\x00\x0e\xd2\x12\x00\x00\x00'
 IN Txn  0:21.040.870 RESPONSE: code:PTP_RC_OK                      trans:44fb data:''
OUT Txn  0:21.041.544 COMMAND : code:PTP_OC_GetObjectHandles        trans:44fc payload:'\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00'
 IN Txn  0:21.047.883 DATA    : code:PTP_OC_GetObjectHandles        trans:44fc data:'\x01\x00\x00\x00\x04\x00\x00\x00'

Now going back to gphoto2, I played around with manually synthesizing events. And it works. You have to match Param1 to the shot number.

            {
              GP_LOG_D ("XXX Begin object handle hunt");
              C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
              while (handles.n == 0) {
                C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
              }
              for (i=0 ; i < handles.n; i++) {
                  GP_LOG_D ("XXX Got object handle %d", handles.Handler[i]);
              }

              event.Code = PTP_EC_ObjectAdded;
              event.Param1 = handles.Handler[0];
              event.Nparam = 1;
            }
jbreiden commented 7 years ago

The hacked together code above is happy to run for a long time on USB2. When we get to USB3 we run for a few shots then crash.

4.217176 get_file_func               (2): Getting file 'DSCF0004.jpg'.
4.217189 ptp_usb_sendreq             (2): Sending PTP_OC 0x1009 (Get object) (0x4) request...
4.220294 ptp_usb_getdata             (2): Reading PTP_OC 0x1009 (Get object) data...
4.737728 gp_libusb1_read [libusb1.c:583](0): 'libusb_bulk_transfer (port->pl->dh, port->settings.usb.inep, (unsigned char*)bytes, size, &curread, port->timeout)' failed: Overflow (-8)
4.737748 gp_port_read [gphoto2-port.c:441](0): Reading 209408 = 0x33200 bytes from port failed: Error reading from the port (-34)
4.737828 ptp_usb_getdata [usb.c:431] (0): PTP_OC 0x1009 receiving data failed: PTP I/O Error (0x02ff)
4.737833 get_file_func [library.c:6698](0): 'ret' failed: 'PTP I/O Error' (0x02ff)
4.737839 gp_context_error            (0): PTP I/O Error
msmeissn commented 7 years ago

overflow is an error I have not seen yet ... But I also currently have no usb3 hosts or cameras :/

jbreiden commented 7 years ago

The crash was my fault. I had been screwing around with the USB max packet size as part of my explorations. Removing that local change made the crash go away.

jbreiden commented 7 years ago

This is what things look like right now. Obviously this can't be an official patch because it replaces the default capture mechanisms with Fuji X-T2 specific ones. But hey, it's working and it should get the general idea across.

diff --git a/camlibs/ptp2/config.c b/camlibs/ptp2/config.c
index e32138a65..9c36f846f 100644
--- a/camlibs/ptp2/config.c
+++ b/camlibs/ptp2/config.c
@@ -477,19 +477,6 @@ camera_prepare_capture (Camera *camera, GPContext *context)

    GP_LOG_D ("prepare_capture");
    switch (params->deviceinfo.VendorExtensionID) {
-   case PTP_VENDOR_FUJI:   {
-       PTPPropertyValue    propval;
-
-       if (!have_prop(camera, PTP_VENDOR_FUJI, 0xd207))
-           break;
-       if (!have_prop(camera, PTP_VENDOR_FUJI, 0xd208))
-           break;
-       propval.u16 = 0x0002;
-       C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
-       propval.u16 = 0x0200;
-       C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
-       break;
-   }
    case PTP_VENDOR_CANON:
        if (ptp_operation_issupported(params, PTP_OC_CANON_InitiateReleaseControl))
            return camera_prepare_canon_powershot_capture(camera,context);
diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c
index bc586525d..dacbc57c4 100644
--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -2007,7 +2007,7 @@ static struct {
    /* https://github.com/gphoto/libgphoto2/issues/32 */
    {"Fuji:Fujifilm X-T10",         0x04cb, 0x02c8, 0},
    /* with new updated firmware 1.1 */
-   {"Fuji:Fujifilm X-T2",          0x04cb, 0x02cd, PTP_CAP},
+   {"Fuji:Fujifilm X-T2",          0x04cb, 0x02cd, PTP_CAP|PTP_NO_CAPTURE_COMPLETE},

    {"Ricoh:Caplio R5 (PTP mode)",          0x05ca, 0x0110, 0},
    {"Ricoh:Caplio GX (PTP mode)",          0x05ca, 0x0325, 0},
@@ -4022,8 +4022,41 @@ fallback:
     * indicating that the capure has been completed may occur after
     * few seconds. moving down the code. (kil3r)
     */
-   C_PTP_REP (ptp_initiatecapture(params, 0x00000000, 0x00000000));
-   CR (gp_port_set_timeout (camera->port, capture_timeout));
+        {
+          PTPPropertyValue propval;
+
+          GP_LOG_D ("XXX Attempting Fuji X-T2 capture");
+
+          // ask camera to listen to tethering commands instead of button
+          propval.u16 = 0x0002;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
+
+          // focus
+          propval.u16 = 0x0200;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));
+
+          // poll camera until it is ready
+          propval.u16 = 0x0001;
+          while (propval.u16 == 0x0001) {
+            ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
+            GP_LOG_D ("XXX Ready to shoot? %X", propval.u16);
+          }
+
+          // shoot
+          propval.u16 = 0x0304;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));
+
+          // poll camera until it is ready
+          propval.u16 = 0x0000;
+          while (propval.u16 == 0x0000) {
+            ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64);
+            GP_LOG_D ("XXX Ready after shooting? %lx", propval.u64);
+          }
+        }
+
+        CR (gp_port_set_timeout (camera->port, capture_timeout));
    /* A word of comments is worth here.
     * After InitiateCapture camera should report with ObjectAdded event
     * all newly created objects. However there might be more than one
@@ -4105,6 +4138,7 @@ fallback:
    while (done != 3) {
        uint16_t ret;

+#if 0
        C_PTP_REP (ptp_wait_event (params));

        if (!ptp_get_one_event(params, &event)) {
@@ -4114,6 +4148,20 @@ fallback:
                    break;
            continue;
        }
+#endif
+
+                {
+                  PTPObjectHandles handles;
+
+                  GP_LOG_D ("XXX Begin Fuji X-T2 fake event creation.");
+                  C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+                  while (handles.n == 0) {
+                    C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+                  }
+                  event.Code = PTP_EC_ObjectAdded;
+                  event.Param1 = handles.Handler[0];
+                  event.Nparam = 1;
+                }
        GP_LOG_D ("event.Code is %04x / param %08x", event.Code, event.Param1);

        switch (event.Code) {
@@ -4577,7 +4625,39 @@ camera_trigger_capture (Camera *camera, GPContext *context)
                    _("Sorry, your camera does not support generic capture"));
        return GP_ERROR_NOT_SUPPORTED;
    }
-   C_PTP_REP (ptp_initiatecapture(params, 0x00000000, 0x00000000));
+        {
+          GP_LOG_D ("XXX Attempting Fuji X-T2 trigger-capture");
+          PTPPropertyValue propval;
+
+          // ask camera to listen to tethering commands instead of button
+          propval.u16 = 0x0002;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
+
+          // focus
+          propval.u16 = 0x0200;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));
+
+          // poll camera until it is ready
+          propval.u16 = 0x0001;
+          while (propval.u16 == 0x0001) {
+            ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
+            GP_LOG_D ("XXX Ready to shoot? %X", propval.u16);
+          }
+
+          // shoot
+          propval.u16 = 0x0304;
+          C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
+          C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));
+
+          // poll camera until it is ready
+          propval.u16 = 0x0000;
+          while (propval.u16 == 0x0) {
+              ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64);
+              GP_LOG_D ("XXX Ready after shooting? %lx", propval.u64);
+          }
+        }
+
    return GP_OK;
 }
jbreiden commented 7 years ago

I'm seeing a 10% or better performance increase from adjusting USB read sizes. I also notice camera_wait_for_event basically never works, so disable that. It seems get called in between every shot for some reason, haven't bother tracing. Between these two changes, I'm seeing somewhere around 73 shots per minute for 10MB JPEG.

diff --git a/camlibs/ptp2/usb.c b/camlibs/ptp2/usb.c
index 065c753f3..1fa6ad73e 100644
--- a/camlibs/ptp2/usb.c
+++ b/camlibs/ptp2/usb.c
@@ -256,7 +256,7 @@ retry:
        return PTP_ERROR_IO;
 }

-#define READLEN 512*1024 /* read blob size, mostly to avoid reading all of it at once. */
+#define READLEN 32*1024*1024 /* read blob size, mostly to avoid reading all of it at once. */

 uint16_t
 ptp_usb_getdata (PTPParams* params, PTPContainer* ptp, PTPDataHandler *handler)
diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c
index bc586525d..fab3cc5bc 100644
--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c

@@ -4604,6 +4684,8 @@ camera_wait_for_event (Camera *camera, int timeout,
        *eventtype = GP_EVENT_TIMEOUT;
        *eventdata = NULL;

+        return GP_OK;
+
        if (params->device_flags & DEVICE_FLAG_OLYMPUS_XML_WRAPPED) {
                GP_LOG_D ("olympus setcameracontrolmode 2\n");
                C_PTP_REP (ptp_olympus_setcameracontrolmode (params, 2));
msmeissn commented 7 years ago

camera_wait_for_event is called by gphoto2 itself. but it will wait only the assigned time.

Increasing the usb readsize from 512kb to 32MB is a bit much

can you check the debuglogfile for events? will be visible around lines with _cb_irq in them can you attach one debuglogfile perhaps?

msmeissn commented 7 years ago

I created a "fuji" branch in GIT where I integrated your code.

The change done is that I do it in camera_fuji_capture and I reuse the nikon brokencap code for object detection currently.

jbreiden commented 7 years ago

This is a debug log for the Fuji branch, as per request.

$ gphoto2 --capture-image-and-download  --debug-loglevel=debug --debug --force-overwrite  --debug-logfile=fuji-branch.txt -I 2 -F 3
Time-lapse mode enabled (interval: 2s).                                        
Capturing frame #1/3...
New file is in location /store_10000001/DSCF0001.jpg on the camera
Saving file as DSCF0001.jpg                                                    
Deleting file /store_10000001/DSCF0001.jpg on the camera
Waiting for next capture slot 1 seconds...

*** Error ***              
PTP Invalid Object Handle
Capturing frame #2/3...
New file is in location /store_10000001/DSCF0002.jpg on the camera
Saving file as DSCF0002.jpg                                                    
Deleting file /store_10000001/DSCF0002.jpg on the camera
Waiting for next capture slot 0 seconds...
Capturing frame #3/3...
New file is in location /store_10000001/DSCF0003.jpg on the camera
Saving file as DSCF0003.jpg                                                    
Deleting file /store_10000001/DSCF0003.jpg on the camera

fuji-branch.txt

msmeissn commented 7 years ago

there is event happening, but the data is not in the dump. can you look for lines with _cb_irq in a full dump with data and quote the data inside? likely is 16 bytes or so 0.547019 _cb_irq (2): 0x1b32718 with status 0 0.547037 _cb_irq (2): requeuing complete transfer 0x1b32718

jbreiden commented 7 years ago

Three images, but only one interrupt. gphoto2 --capture-image-and-download -I 5 -F 3 --debug --debug-logfile=full.txt This is on the unmodified Fuji branch. This interrupt only happens on the first image after camera powercycle. I saw the exact same behavior when listening to a Windows tethering program.

By the way, that reused Nikon code slows thing down by 2X. I'm working on a pull request for that.

0.618652 _cb_irq                     (2): 0x180b518 with status 0
0.618667 _cb_irq                     (3): interrupt (hexdump of 16 bytes)
0000  10 00 00 00 04 00 02 40-ff ff ff ff 01 00 00 00  .......@........

0.618681 _cb_irq                     (2): requeuing complete transfer 0x180b518
0.619260 gp_port_read                (3): Read    20 = 0x14 out of 1024 bytes from port: (hexdump of 20 bytes)
0000  14 00 00 00 02 00 15 10-22 01 00 00 01 00 0e d2  ........".......
0010  12 00 00 00
[...]
18.750844 _cb_irq                     (2): 0x180b518 with status 3
18.750864 _cb_irq                     (2): 0x180b6e8 with status 3
18.750874 _cb_irq                     (2): 0x1813898 with status 3
18.750883 _cb_irq                     (2): 0x1813a68 with status 3
18.750892 _cb_irq                     (2): 0x1813c38 with status 3
18.750918 _cb_irq                     (2): 0x1813e08 with status 3
18.750928 _cb_irq                     (2): 0x1814018 with status 3
18.750937 _cb_irq                     (2): 0x1814228 with status 3
18.750947 _cb_irq                     (2): 0x1814438 with status 3
18.750956 _cb_irq                     (2): 0x1814648 with status 3
jbreiden commented 7 years ago

Well, I still don't understand how to use git/github,. Here's a patch for the Fuji branch.

commit 999ddaa326202b033308d469bf16a399e4cba710 (HEAD -> fuji)
Author: Jeff Breidenbach <jbreiden@google.com>
Date:   Mon May 8 15:18:31 2017 -0700

    Speed up captures per minute from about 35 to about 70.

    WARNING: This is tested on a camera that does not have an
    SD card inserted. It is very likely that this code will
    do the wrong thing if an SD card is involved.

    Also some minor changes:
     * Remove some debug messages
     * Make one of the polling loops less tight
     * Move a camera initialization command to camera_prepare_capture.

diff --git a/camlibs/ptp2/config.c b/camlibs/ptp2/config.c
index 0b758fbbf..447830a49 100644
--- a/camlibs/ptp2/config.c
+++ b/camlibs/ptp2/config.c
@@ -492,6 +492,13 @@ camera_prepare_capture (Camera *camera, GPContext *context)
                        return camera_prepare_canon_eos_capture(camera,context);
                gp_context_error(context, _("Sorry, your Canon camera does not support Canon capture"));
                return GP_ERROR_NOT_SUPPORTED;
+        case PTP_VENDOR_FUJI:
+               {
+                       PTPPropertyValue propval;
+                       propval.u16 = 0x0002;
+                       return ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16);
+               }
+               break;
        default:
                /* generic capture does not need preparation */
                return GP_OK;
diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c
index 32d7c1423..b62b059f8 100644
--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -3923,18 +3923,10 @@ camera_fuji_capture (Camera *camera, CameraCaptureType type, CameraFilePath *pat
 {
        PTPParams               *params = &camera->pl->params;
        PTPPropertyValue        propval;
-       PTPObjectHandles        handles, beforehandles;
-       int                     tries;
-       uint32_t                newobject;
+       PTPObjectHandles        handles;

        GP_LOG_D ("camera_fuji_capture");

-       C_PTP (ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0x000000, 0x000000, &beforehandles));
-
-       /* ask camera to listen to tethering commands instead of button */
-       propval.u16 = 0x0002;
-       C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
-
        /* focus */
        propval.u16 = 0x0200;
        C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
@@ -3944,7 +3936,6 @@ camera_fuji_capture (Camera *camera, CameraCaptureType type, CameraFilePath *pat
        propval.u16 = 0x0001;
        while (propval.u16 == 0x0001) {
                ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
-               GP_LOG_D ("XXX Ready to shoot? %X", propval.u16);
        }

        /* shoot */
@@ -3952,77 +3943,23 @@ camera_fuji_capture (Camera *camera, CameraCaptureType type, CameraFilePath *pat
        C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
        C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

-       /* poll camera until it is ready */
-       propval.u16 = 0x0000;
-       while (propval.u16 == 0x0000) {
-               ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64);
-               GP_LOG_D ("XXX Ready after shooting? %lx", propval.u64);
-       }
-
-       /* duplicate the nikon broken capture, as we do not know how to get events yet */
-
-       tries = 5;
-       GP_LOG_D ("missing fuji objectadded events workaround");
-       while (tries--) {
-               unsigned int i;
-               uint16_t ret = ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0x000000, 0x000000, &handles);
-               if (ret != PTP_RC_OK)
-                       break;
-
-               /* if (handles.n == params->handles.n)
-                *      continue;
-                * While this is a potential optimization, lets skip it for now.
-                */
-               newobject = 0;
-               for (i=0;i<handles.n;i++) {
-                       unsigned int    j;
-                       PTPObject       *ob;
-
-                       /* look if we saw the objecthandle before capture */
-                       for (j=0;j<beforehandles.n;j++)
-                               if (beforehandles.Handler[j] == handles.Handler[i])
-                                       break;
-                       if (j != beforehandles.n)
-                               continue;
-
-                       ret = ptp_object_want (params, handles.Handler[i], PTPOBJECT_OBJECTINFO_LOADED, &ob);
-                       if (ret != PTP_RC_OK) {
-                               GP_LOG_E ("object added, but not found?");
-                               continue;
-                       }
-                       /* A directory was added, like initial DCIM/100NIKON or so. */
-                       if (ob->oi.ObjectFormat == PTP_OFC_Association)
-                               continue;
-                       newobject = handles.Handler[i];
-                       /* we found a new file */
-                       break;
-               }
-               free (handles.Handler);
-               if (newobject)
-                       break;
-               C_PTP_REP (ptp_check_event (params));
-               sleep(1);
+       /* poll until image is ready */
+       C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+       while (handles.n == 0) {
+               C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+               usleep(50 * 1000);
        }
-       free (beforehandles.Handler);
-       if (!newobject)
-               GP_LOG_D ("fuji object added no new file found after 5 seconds?!?");

-       /* clear path, so we get defined results even without object info */
+       /* deal with image */
        path->name[0]='\0';
        path->folder[0]='\0';
-
-       if (newobject != 0) {
-               PTPObject       *ob;
-
-               C_PTP_REP (ptp_object_want (params, newobject, PTPOBJECT_OBJECTINFO_LOADED, &ob));
-               strcpy  (path->name,  ob->oi.Filename);
-               sprintf (path->folder,"/"STORAGE_FOLDER_PREFIX"%08lx/",(unsigned long)ob->oi.StorageID);
-               get_folder_from_handle (camera, ob->oi.StorageID, ob->oi.ParentObject, path->folder);
-               /* delete last / or we get confused later. */
-               path->folder[ strlen(path->folder)-1 ] = '\0';
-               return gp_filesystem_append (camera->fs, path->folder, path->name, context);
-       }
-       return GP_ERROR;
+       PTPObject       *ob;
+       C_PTP_REP (ptp_object_want (params, handles.Handler[0], PTPOBJECT_OBJECTINFO_LOADED, &ob));
+       strcpy  (path->name,  ob->oi.Filename);
+       sprintf (path->folder,"/"STORAGE_FOLDER_PREFIX"%08lx/",(unsigned long)ob->oi.StorageID);
+       get_folder_from_handle (camera, ob->oi.StorageID, ob->oi.ParentObject, path->folder);
+       path->folder[ strlen(path->folder)-1 ] = '\0';
+       return gp_filesystem_append (camera->fs, path->folder, path->name, context);
 }

 static int
@@ -4682,10 +4619,6 @@ camera_trigger_capture (Camera *camera, GPContext *context)
        ) {
                PTPPropertyValue        propval;

-               /* ask camera to listen to tethering commands instead of button */
-               propval.u16 = 0x0002;
-               C_PTP_REP (ptp_setdevicepropvalue (params, 0xd207, &propval, PTP_DTC_UINT16));
-
                /* focus */
                propval.u16 = 0x0200;
                C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
@@ -4695,7 +4628,6 @@ camera_trigger_capture (Camera *camera, GPContext *context)
                propval.u16 = 0x0001;
                while (propval.u16 == 0x0001) {
                        ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
-                       GP_LOG_D ("XXX Ready to shoot? %X", propval.u16);
                }

                /* shoot */
@@ -4707,7 +4639,6 @@ camera_trigger_capture (Camera *camera, GPContext *context)
                propval.u16 = 0x0000;
                while (propval.u16 == 0x0000) {
                        ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64);
-                       GP_LOG_D ("XXX Ready after shooting? %lx", propval.u64);
                }
        }
msmeissn commented 7 years ago

The camera sends a ObjectAdded event with objectid 1, which is what we need.

so i think we do not need the ptp_get_object_handles() poll loop

I commited the d207 move from your patch to fuji branch, but changed the camera_fuji_capture to just wait for the ObjectAdded event now. (rest is in #if 0)

jbreiden commented 7 years ago

But we only get one ObjectAdded event per camera power cycle.

msmeissn commented 7 years ago

hmm. this object ... is it in SDRAM of the camera? perhaps it needs to be deleted before the next capture?

jbreiden commented 7 years ago

Yes, this is all SDRAM, no SD card installed. The camera will take a second shot, but I can't get it off because there is no second ObjectAdded event. I know there was a second shot because the camera clicked, and the status LED flashes.

On the other hand, polling works fine. Using polling I took 5000 consecutive shots this morning before the battery drained.

msmeissn commented 7 years ago

hmm.

will it always start with a empty object handles array like in your change above as you are just checking for any object?

this will probably also not work with SD Card inserted

jbreiden commented 7 years ago

Here's a polling approach that works quickly and safely.

diff --git a/camlibs/ptp2/library.c b/camlibs/ptp2/library.c
index be3eddd19..b06f0da79 100644
--- a/camlibs/ptp2/library.c
+++ b/camlibs/ptp2/library.c
@@ -3926,31 +3926,22 @@ camera_fuji_capture (Camera *camera, CameraCaptureType type, CameraFilePath *pat
 {
        PTPParams               *params = &camera->pl->params;
        PTPPropertyValue        propval;
-#if 0
        PTPObjectHandles        handles, beforehandles;
-       int                     tries;
-#endif
-       PTPContainer            event;
-       uint32_t                newobject;
-       struct timeval          event_start;
-       int                     back_off_wait = 0;

        GP_LOG_D ("camera_fuji_capture");

-#if 0
+       /* see what images are already around */
        C_PTP (ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0x000000, 0x000000, &beforehandles));
-#endif

        /* focus */
        propval.u16 = 0x0200;
        C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
        C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

-       /* poll camera until it is ready */
+       /* poll until camera ready */
        propval.u16 = 0x0001;
        while (propval.u16 == 0x0001) {
                ptp_getdevicepropvalue (params, 0xd209, &propval, PTP_DTC_UINT16);
-               GP_LOG_D ("XXX Ready to shoot? %X", propval.u16);
        }

        /* shoot */
@@ -3958,100 +3949,23 @@ camera_fuji_capture (Camera *camera, CameraCaptureType type, CameraFilePath *pat
        C_PTP_REP (ptp_setdevicepropvalue (params, 0xd208, &propval, PTP_DTC_UINT16));
        C_PTP_REP(ptp_initiatecapture(params, 0x00000000, 0x00000000));

-       /* poll camera until it is ready */
-       propval.u16 = 0x0000;
-       while (propval.u16 == 0x0000) {
-               C_PTP_REP (ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64));
-               GP_LOG_D ("XXX Ready after shooting? %lx", propval.u64);
-               C_PTP_REP (ptp_check_event (params));
-       }
-
-       /* there is a ObjectAdded event being sent */
-       do {
-               C_PTP_REP (ptp_check_event (params));
-
-               while (ptp_get_one_event(params, &event)) {
-                       switch (event.Code) {
-                       case PTP_EC_ObjectAdded:
-                               newobject = event.Param1;
-                               goto downloadfile;
-                       default:
-                               GP_LOG_D ("unexpected unhandled event Code %04x, Param 1 %08x", event.Code, event.Param1);
-                               break;
-                       }
-               }
-       }  while (waiting_for_timeout (&back_off_wait, event_start, 2000)); /* wait for 2 more seconds after busy is no longer signaled */
-
-       return GP_ERROR;
-
-
-#if 0
-       /* If we got no event in 2 seconds duplicate the nikon broken capture, as we do not know how to get events yet */
-
-       tries = 5;
-       GP_LOG_D ("XXXX missing fuji objectadded events workaround");
-       while (tries--) {
-               unsigned int i;
-               uint16_t ret = ptp_getobjecthandles (params, PTP_HANDLER_SPECIAL, 0x000000, 0x000000, &handles);
-               if (ret != PTP_RC_OK)
-                       break;
-
-               /* if (handles.n == params->handles.n)
-                *      continue;
-                * While this is a potential optimization, lets skip it for now.
-                */
-               newobject = 0;
-               for (i=0;i<handles.n;i++) {
-                       unsigned int    j;
-                       PTPObject       *ob;
-
-                       /* look if we saw the objecthandle before capture */
-                       for (j=0;j<beforehandles.n;j++)
-                               if (beforehandles.Handler[j] == handles.Handler[i])
-                                       break;
-                       if (j != beforehandles.n)
-                               continue;
-
-                       ret = ptp_object_want (params, handles.Handler[i], PTPOBJECT_OBJECTINFO_LOADED, &ob);
-                       if (ret != PTP_RC_OK) {
-                               GP_LOG_E ("object added, but not found?");
-                               continue;
-                       }
-                       /* A directory was added, like initial DCIM/100NIKON or so. */
-                       if (ob->oi.ObjectFormat == PTP_OFC_Association)
-                               continue;
-                       newobject = handles.Handler[i];
-                       /* we found a new file */
-                       break;
-               }
-               free (handles.Handler);
-               if (newobject)
-                       break;
-               C_PTP_REP (ptp_check_event (params));
-               sleep(1);
+       /* poll until image is ready */
+       C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+       while (handles.n == beforehandles.n) {
+               C_PTP_REP (ptp_getobjecthandles (params, 0xffffffff, 0x000000, 0x000000, &handles));
+               usleep(50 * 1000);
        }
-       free (beforehandles.Handler);
-       if (!newobject)
-               GP_LOG_D ("fuji object added no new file found after 5 seconds?!?");
-#endif

-downloadfile:
-       /* clear path, so we get defined results even without object info */
+       /* deal with image */
        path->name[0]='\0';
        path->folder[0]='\0';
-
-       if (newobject != 0) {
-               PTPObject       *ob;
-
-               C_PTP_REP (ptp_object_want (params, newobject, PTPOBJECT_OBJECTINFO_LOADED, &ob));
-               strcpy  (path->name,  ob->oi.Filename);
-               sprintf (path->folder,"/"STORAGE_FOLDER_PREFIX"%08lx/",(unsigned long)ob->oi.StorageID);
-               get_folder_from_handle (camera, ob->oi.StorageID, ob->oi.ParentObject, path->folder);
-               /* delete last / or we get confused later. */
-               path->folder[ strlen(path->folder)-1 ] = '\0';
-               return gp_filesystem_append (camera->fs, path->folder, path->name, context);
-       }
-       return GP_ERROR;
+       PTPObject       *ob;
+       C_PTP_REP (ptp_object_want (params, handles.Handler[handles.n - 1], PTPOBJECT_OBJECTINFO_LOADED, &ob));
+       strcpy  (path->name,  ob->oi.Filename);
+       sprintf (path->folder,"/"STORAGE_FOLDER_PREFIX"%08lx/",(unsigned long)ob->oi.StorageID);
+       get_folder_from_handle (camera, ob->oi.StorageID, ob->oi.ParentObject, path->folder);
+       path->folder[ strlen(path->folder)-1 ] = '\0';
+       return gp_filesystem_append (camera->fs, path->folder, path->name, context);
 }

 static int
@@ -4730,7 +4644,7 @@ camera_trigger_capture (Camera *camera, GPContext *context)
                /* poll camera until it is ready */
                propval.u16 = 0x0000;
                while (propval.u16 == 0x0000) {
-                       C_PTP_REP (ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT64));
+                       C_PTP_REP (ptp_getdevicepropvalue (params, 0xd212, &propval, PTP_DTC_UINT16));
                }
        }
jbreiden commented 7 years ago

Looks like we get ourselves in trouble at shot ten thousand. Not sure if this is a problem with the camera or with gphoto2.

Capturing frame #9999...
New file is in location /store_10000001/DSCF9999.jpg on the camera
Saving file as DSCF9999.jpg                                                    
Deleting file /store_10000001/DSCF9999.jpg on the camera
not sleeping (0 seconds behind schedule)
Capturing frame #10000...
New file is in location /store_10000001/DSCF.jpg on the camera
Saving file as DSCF.jpg                                                        
Deleting file /store_10000001/DSCF.jpg on the camera
not sleeping (0 seconds behind schedule)
Capturing frame #10001...
New file is in location /store_10000001/DSCF.jpg on the camera
File DSCF.jpg exists. Overwrite? [y|n]          
msmeissn commented 7 years ago

On Mon, May 15, 2017 at 08:56:34AM -0700, jbreiden wrote:

Looks like we get ourselves in trouble at shot ten thousand. Not sure if this is a probably with the camera or with gphoto2.

Does the camera create a new folder on this occasion? That would cause trouble on name decoding.

jbreiden commented 7 years ago

Not sure what's going on, doing another run right now with --force-overwrite. It's a little hard to inspect things after running gphoto2, because we lock out physical camera controls in camera_prepare_capture(). Is there an appropriate place in code to for me restore the physical camera controls?

msmeissn commented 7 years ago

There is config.c:camera_unprepare_capture for this purpose :)

msmeissn commented 7 years ago

btw, i merged the fuji branch back into master branch

jbreiden commented 7 years ago

Filenames get weird starting at shot number 10000, but we are still able to takes them and get them off the camera. Unfortunately everything grinds to a halt on shot 35634.

Capturing frame #35633...
New file is in location /store_10000001/DSCF.jpg on the camera
Saving file as latest.jpg                                                      
Deleting file /store_10000001/DSCF.jpg on the camera
not sleeping (0 seconds behind schedule)
Capturing frame #35634...
[ ... hang ...]
jbreiden commented 7 years ago

I can't afford this half second delay waiting for an interrupt.

https://github.com/gphoto/libgphoto2/blob/master/camlibs/ptp2/library.c#L3983

Could it be removed, perhaps like this patch?

https://github.com/gphoto/libgphoto2/issues/133#issuecomment-300251622