The camera device cannot be found using ICDeviceBrowser

43 views Asked by At

When I use ICDeviceBrowser to detect the camera linked through the cable, I can detect the camera device for the first time, but when I exit the page and enter the page again, I cannot detect the linked camera device. Here is the code.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    [self addImageCaptureCore];
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self checkCameraConnection];
    });
}

- (void)checkCameraConnection {
    if (@available(iOS 13.0, *)) {
        NSArray<ICDevice *> *connectedDevices = self.browser.devices;
        
        if (connectedDevices.count > 0) {
            NSLog(@"Camera is connected");
        } else {
            NSLog(@"Camera is not connected");
        }
    } 
    else {
        // Fallback on earlier versions
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    if (@available(iOS 13.0, *)) {
        if (self.cameraDevice) {
            if (self.cameraDevice.hasOpenSession) {
                [self.cameraDevice requestCloseSession];
                
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.browser stop];
                    self.browser.delegate = nil;
                    self.browser = nil;
                });
            }
            else {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.browser stop];
                    self.browser.delegate = nil;
                    self.browser = nil;
                });
            }
        }
    } else {
        // Fallback on earlier versions
    }
}

- (void)addImageCaptureCore {
    if (@available(iOS 13.0, *)) {
        ICDeviceBrowser *browser = [[ICDeviceBrowser alloc] init];
        browser.delegate = self;
        [browser start];
        self.browser = browser;
    } 
    else {
        
    }
}

#pragma mark - ICDeviceBrowserDelegate
    
- (void)deviceBrowser:(ICDeviceBrowser*)browser didAddDevice:(ICDevice*)device moreComing:(BOOL) moreComing  API_AVAILABLE(ios(13.0)){
    NSLog(@"Device name = %@",device.name);
    
    if ([device isKindOfClass:[ICCameraDevice class]]) {
        if ([device.capabilities containsObject:ICCameraDeviceCanAcceptPTPCommands]) {
            ICCameraDevice *cameraDevice = (ICCameraDevice *)device;
            cameraDevice.delegate = self;
            [cameraDevice requestOpenSession];
            self.cameraDevice = cameraDevice;
        }
    }
    
}

- (void)deviceBrowser:(ICDeviceBrowser*)browser didRemoveDevice:(ICDevice*)device moreGoing:(BOOL) moreGoing  API_AVAILABLE(ios(13.0)){
    if (self.cameraDevice) {
        if (self.cameraDevice.hasOpenSession) {
            [self.cameraDevice requestCloseSession];
            self.cameraDevice.delegate = nil;
            self.cameraDevice = nil;
        }
        else {
            self.cameraDevice.delegate = nil;
            self.cameraDevice = nil;
        }
    }
}

#pragma mark - ICCameraDeviceDelegate

- (void)cameraDevice:(ICCameraDevice*)camera didAddItems:(NSArray<ICCameraItem*>*) items  API_AVAILABLE(ios(13.0)){
    if (items.count > 0) {
        ICCameraItem *latestItem = items.lastObject;
        NSLog(@"name = %@",latestItem.name);
    }
    
}

#pragma mark - ICDeviceDelegate

- (void)device:(ICDevice*)device didOpenSessionWithError:(NSError* _Nullable) error  API_AVAILABLE(ios(13.0)){
    if (error) {
        NSLog(@"Failed to open session %@",error.localizedDescription);
    }
    else {
        NSLog(@"open session success");
    }
}

- (void)device:(ICDevice*)device didCloseSessionWithError:(NSError* _Nullable)error  API_AVAILABLE(ios(13.0)){
    if (error) {
        NSLog(@"close session error = %@",error.localizedDescription);
    }
    else {
        NSLog(@"didCloseSession");
    }
}

- (void)didRemoveDevice:(ICDevice*)device {
    
}

How can I ensure that the camera device is detected when I exit the page and re-enter the page

1

There are 1 answers

1
o15a3d4l11s2 On

UPDATE I can better summarise the whole answer in the following way: I think the device browser is stopped in viewWillDisappear, and the second time you visit the screen, viewWillAppear has no device browser running. It really depends on how you display/dismiss this view and its controller.

Here is a (dirty) trick to check this theory:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    if (@available(iOS 13.0, *) && self.browser == nil) {
        [self addImageCaptureCore];
    }
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self checkCameraConnection];
    });
}

If it starts working, then it means the browser is really destroyed while the controller isn't and you should check for strong references in your controller.

ORIGINAL ANSWER

Here are few recommendation that come to my mind:

  1. Verify if the controller is initialised the second time - might be that it has a reference not freed and therefore does not deinitialise. You can check if viewDidLoad is called the second time too.
  2. Related to point 1, you could explicitly stop the device browser like
- (void)dealloc {
    [self.deviceBrowser stop];
}

and check your logic in viewWillDisappear to ensure if the session and the device browser are stopped properly.

  1. When doing the device listing you count on the devices list to have devices, but that could not be true every time. Why? Because the .devices array is empty before the first invocation of the delegate method deviceBrowser(_:didAdd:moreComing:). So it could be a race-condition that you try to list devices before they are actually detected the second time (might be again related to the first two points. My suggestion would be to do a slight change of the logic in one of many possible ways:
    1. do the init and the detection starting after the view has appeared
    2. still init the browser in viewDidLoad, but wait for deviceBrowser(_:didAdd:moreComing:) to be fired before listing devices
    3. define a property @property (assign, nonatomic) BOOL hasAddedAllDevice; and set it to true once didAddDevice returns moreComing NO
    4. do some other approach that ensures that the deviceBrowser initialises first, then wait for the devices discovering to finish and only then list/use the devices