12

Apple's demo source code for the AVCam demo app found here: https://developer.apple.com/library/content/samplecode/AVCam/Introduction/Intro.html crashes when attempting to take a picture (regardless of whether you build the Objective-C or Swift versions) on the line in the AVCamCameraViewController/CameraViewController(Swift) that captures the photo:

[self.photoOutput capturePhotoWithSettings:photoSettings delegate:photoCaptureDelegate];

or (Swift)

self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate)

The error message when it crashes is:

2016-11-21 17:44:31.590070 AVCam[2178:2303627] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[AVCapturePhotoOutput capturePhotoWithSettings:delegate:] flashMode must be set to a value present in the supportedFlashModes array'

And when I examine the flash modes array, I get this:

(lldb) po [self.photoOutput supportedFlashModes] <__NSSingleObjectArrayI 0x170007c50>( 0 )

So in order to add the flash mode, the docs say you have to specify what modes you want to support in the AVCapturePhotoSettings object. I've done that with this line of code:

photoSettings.flashMode = AVCaptureFlashModeAuto;

or (Swift)

photoSettings.flashMode = .auto

So my hunch is that this is a bug specifically related to the 12.9" iPad Pro and I probably need to submit a radar, but thought I would ask here in case someone's seen it before. Any ideas?

Update

I've been able to duplicate this other iPads as well, so it doesn't appear to be only the 12.9" iPad Pro only.

Matt Long
  • 24,438
  • 4
  • 73
  • 99
  • Guess we need to submit a radar. Same code base is working on all iPhones and iPad air without any crashes. I don't have iPad Pro to test it :( – Bluewings Nov 24 '16 at 07:19
  • What I've done is to determine if the current device is .front or .back and I set the flashMode to .off for .front or .unspecified and .auto otherwise. I hope this helps. – psparago Dec 23 '16 at 18:05
  • Was this ever resolved? Having the same problem. But couldn't confirm wich iPad version the user has. :( – hadez30 Jan 10 '17 at 11:52
  • I've been able to overcome the problem with simply restarting my computer and then Xcode and then doing a clean build and run on device. – Matt Long Jan 10 '17 at 17:06
  • Hmm. Interesting. For my case, it was an issue in a production build. Not sure if it helps to restart my computer and generate a build. – hadez30 Jan 11 '17 at 03:56
  • @hadez30 I have yet to push a production build, so that may not help in your case. I'll let you know what happens when I do push to production. – Matt Long Jan 11 '17 at 15:31
  • Thanks, @MattLong. Would really appreciate that. :) – hadez30 Jan 12 '17 at 05:20
  • My app got denied because of this exact issue... any luck? – Walker Jan 17 '17 at 01:52

8 Answers8

11

Add this ...

let position = self.videoDeviceInput.device.position
photoSettings.flashMode = position == .front || position == .unspecified ? .off : .auto

right before this ...

self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate)

That should fix the problem.

slfan
  • 8,950
  • 115
  • 65
  • 78
Soja
  • 514
  • 6
  • 13
  • @WDyson I just edited the question to improve the formatting. Soja gave the answer – slfan Feb 01 '17 at 15:57
  • @soja Could you add more detail about why this solution fixes the problem? – W Dyson Feb 01 '17 at 18:40
  • 1
    @WDyson it checks for the position of camera in usage. If the position is front or unspecified, then flash mode is off. Otherwise its on .auto. I believe there is a bug with .auto... something Apple need to fix in their code. – Soja Feb 01 '17 at 22:44
  • @Soja why is it accepted as a correct answer? You just forbid to use the flash for front camera while it possible on some devices! – Vyachaslav Gerchicov Aug 23 '19 at 09:54
  • This doesn't seem to work. Checking `hasFlash` and setting to `.auto` if true is the correct solution – jshapy8 Nov 27 '19 at 17:57
3

// Also for someone that might be having this problem while trying to use the Apple supplied AVCam sample code in XCode on MacOS, using the Objective C supplied version of the code, just add this (in Objective C):

AVCaptureDevicePosition position = self.videoDeviceInput.device.position;
if (position == AVCaptureDevicePositionFront)
{
   photoSettings.flashMode = AVCaptureFlashModeOff;
}
else
{
   photoSettings.flashMode = AVCaptureFlashModeAuto;
}


//right before:

[self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate]
FreedomOfSpeech
  • 395
  • 2
  • 14
3

You must check if the device supports "Flash" mode with:

[self.photoOutput.supportedFlashModes containsObject:[NSNumber numberWithInt:AVCaptureFlashModeOn]])

and then use:

@property (nonatomic) AVCapturePhotoOutput *photoOutput;


AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings];


  AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        if ([device hasTorch] && [device hasFlash]){
            [device lockForConfiguration:nil];
            if([self.photoOutput.supportedFlashModes containsObject:[NSNumber numberWithInt:AVCaptureFlashModeOn]]){
                [device setFlashMode:AVCaptureFlashModeOn];
                photoSettings.flashMode = AVCaptureFlashModeAuto;
            }
            else{
                photoSettings.flashMode = AVCaptureFlashModeOff;
            }
            [device unlockForConfiguration];
        }
        else{
            photoSettings.flashMode = AVCaptureFlashModeOff;
            [device unlockForConfiguration];
        }

then

[self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate]
Ofir Malachi
  • 1,145
  • 14
  • 20
2

In swift it has to be checked whether photo output supports flash mode at all.

 let settings = AVCapturePhotoSettings()
 if photoOutput.supportedFlashModes.contains(.on) {
     settings.flashMode = self.flashMode
 } else {
     settings.flashMode = .off
 }

 self.photoOutput?.capturePhoto(with: settings, delegate: self)
JPetric
  • 3,838
  • 28
  • 26
1

To check if a camera has a flash, use the AVCaptureDevice.hasFlash property on that camera.

It doesn't really make sense to use the back/front position to guess this, when there's already something that just tells you whether or not it has a flash.

Andrew Koster
  • 1,550
  • 1
  • 21
  • 31
0

No need to check the camera position, all what we need is to check if the camera has flash of not and according to that we set the flash mode.

photoSettings.flashMode = AVCaptureDevice.default(for: AVMediaType.video)!.hasFlash ? .on : .off
Omar HossamEldin
  • 3,033
  • 1
  • 25
  • 51
-1

So building on Soja's answer, I decided to interrogate the supportedFlashModes array provided by the photoOutput variable which is an AVCapturePhotoOutput.

self.inProgressPhotoCaptureDelegates[photoCaptureDelegate.requestedPhotoSettings.uniqueID] = photoCaptureDelegate

if let _ = self.photoOutput.supportedFlashModes.filter({ (mode) -> Bool in
    return mode.intValue == AVCaptureFlashMode.auto.rawValue
}).last {
    let position = self.videoDeviceInput.device.position
    photoSettings.flashMode = position == .front || position == .unspecified ? .off : .auto
} else {
    photoSettings.flashMode = .off
}

self.photoOutput.capturePhoto(with: photoSettings, delegate: photoCaptureDelegate)

There are probably some more comprehensive ways to do it, but this ensures that the .auto mode is supported before attempting to use it. The difference and issue here seems to relate to the type of device, iPhone vs iPad due to the fact that iPads don't have a flash on the back like iPhones do. In other words, it works fine on my phone.

Matt Long
  • 24,438
  • 4
  • 73
  • 99
-2

@RazvanR solution works for that Apple AVCamera code, but just before it the code

// NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

doesn't, and needs to use AVCapturePhotoOutput

The Swift version is

Swift: photoData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer)

... but no idea what the Objective-C version is.

Harry McGovern
  • 517
  • 5
  • 19