7

My iOS camera app written in Objective C freezes its preview layer when coming back from the lock screen/when unlocking the phone.

All the camera configurations settings are being called in the viewWillAppear. I have been successful so far, except for the only problem, which is the camera preview layer freezes or stuck when coming back from the lock screen. The camera section of my code is given below.

Any help is much appreciated. Thank you. ps: Please feel free to point out any mistake in my code as i am just a newbie.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    dispatch_async(dispatch_get_main_queue(), ^{
    [self setGUIBasedOnMode];
    });
}


-(void) setGUIBasedOnMode
{
if (![self isStreamStarted]) {
if (shutterActionMode == SnapCamSelectionModeLiveStream)
{
    _flashButton.hidden = true;
    _cameraButton.hidden = true;
    _liveSteamSession = [[VCSimpleSession alloc] initWithVideoSize:[[UIScreen mainScreen]bounds].size frameRate:30 bitrate:1000000 useInterfaceOrientation:YES];
    [_liveSteamSession.previewView removeFromSuperview];
    AVCaptureVideoPreviewLayer  *ptr;
    [_liveSteamSession getCameraPreviewLayer:(&ptr)];
    _liveSteamSession.previewView.frame = self.view.bounds;
    _liveSteamSession.delegate = self;
}
else{
    [_liveSteamSession.previewView removeFromSuperview];
    _liveSteamSession.delegate = nil;
    _cameraButton.hidden = false;
    if(flashFlag == 0){
        _flashButton.hidden = false;
    }
    else if(flashFlag == 1){
        _flashButton.hidden = true;
    }
    self.session = [[AVCaptureSession alloc] init];
    self.previewView.hidden = false;
    self.previewView.session = self.session;

    [self configureCameraSettings]; //All The Camera Configuration Settings.

    dispatch_async( self.sessionQueue, ^{
        switch ( self.setupResult )
        {
            case AVCamSetupResultSuccess:
            {
                [self addObservers];

                [self.session startRunning];

                self.sessionRunning = self.session.isRunning;
                if(loadingCameraFlag == false){
                    [self hidingView];
                }
                break;
            }
            case AVCamSetupResultCameraNotAuthorized:
            {
                dispatch_async( dispatch_get_main_queue(), ^{
                    NSString *message = NSLocalizedString( @"MyApp doesn't have permission to use the camera, please change privacy settings", @"Alert message when the user has denied access to the camera");
                    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AVCam" message:message preferredStyle:UIAlertControllerStyleAlert];
                    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"OK", @"Alert OK button" ) style:UIAlertActionStyleCancel handler:nil];
                    [alertController addAction:cancelAction];

                    UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"Settings", @"Alert button to open Settings" ) style:UIAlertActionStyleDefault handler:^( UIAlertAction *action ) {
                        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
                    }];
                    [alertController addAction:settingsAction];
                    [self presentViewController:alertController animated:YES completion:nil];
                } );
                break;
            }
            case AVCamSetupResultSessionConfigurationFailed:
            {
                dispatch_async( dispatch_get_main_queue(), ^{
                    NSString *message = NSLocalizedString( @"Unable to capture media", @"Alert message when something goes wrong during capture session configuration" );
                    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"MyApp" message:message preferredStyle:UIAlertControllerStyleAlert];
                    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString( @"OK", @"Alert OK button" ) style:UIAlertActionStyleCancel handler:nil];
                    [alertController addAction:cancelAction];
                    [self presentViewController:alertController animated:YES completion:nil];
                } );
                break;
            }
        }
    });
}
}

-(void)configureCameraSettings
{
self.sessionQueue = dispatch_queue_create( "session queue",      DISPATCH_QUEUE_SERIAL );
self.setupResult = AVCamSetupResultSuccess;
switch ( [AVCaptureDevice     authorizationStatusForMediaType:AVMediaTypeVideo] )
{
    case AVAuthorizationStatusAuthorized:
    {
        break;
    }
    case AVAuthorizationStatusNotDetermined:
    {
        dispatch_suspend( self.sessionQueue);
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^( BOOL granted ) {
            if ( ! granted ) {
                self.setupResult = AVCamSetupResultCameraNotAuthorized;
            }
            dispatch_resume( self.sessionQueue );
        }];
        break;
    }
    default:
    {
        self.setupResult = AVCamSetupResultCameraNotAuthorized;
        break;
    }
}

dispatch_async( self.sessionQueue, ^{
if ( self.setupResult != AVCamSetupResultSuccess ) {
    return;
}
self.backgroundRecordingID = UIBackgroundTaskInvalid;
NSError *error = nil;

AVCaptureDevice *videoDevice = [IPhoneCameraViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];

[self.session beginConfiguration];

if ( [self.session canAddInput:videoDeviceInput] ) {
    [self.session addInput:videoDeviceInput];
    self.videoDeviceInput = videoDeviceInput;

    dispatch_async( dispatch_get_main_queue(), ^{
        UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
        AVCaptureVideoOrientation initialVideoOrientation = AVCaptureVideoOrientationPortrait;
        if ( statusBarOrientation != UIInterfaceOrientationUnknown ) {
            initialVideoOrientation = (AVCaptureVideoOrientation)statusBarOrientation;
        }
        AVCaptureVideoPreviewLayer *previewLayer = (AVCaptureVideoPreviewLayer *)self.previewView.layer;
        if (shutterActionMode == SnapCamSelectionModeVideo)
        {
            [previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
            if([self.session canSetSessionPreset:AVCaptureSessionPresetMedium]){
                [self.session setSessionPreset:AVCaptureSessionPresetMedium];
            }
        }
        previewLayer.connection.videoOrientation = initialVideoOrientation;
    } );
}
else {
    self.setupResult = AVCamSetupResultSessionConfigurationFailed;
}

AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];

if ( ! audioDeviceInput ) {
}

if ( [self.session canAddInput:audioDeviceInput] ) {
    [self.session addInput:audioDeviceInput];
}
else {
}

AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];

Float64 TotalSeconds = 10*60;
int32_t preferredTimeScale = 30;
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);           movieFileOutput.maxRecordedDuration = maxDuration;
movieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024 * 100;

if ( [self.session canAddOutput:movieFileOutput] ) {
    [self.session addOutput:movieFileOutput];
    AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
    if ( connection.isVideoStabilizationSupported ) {
        connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
    }
    self.movieFileOutput = movieFileOutput;
}
else {
    self.setupResult = AVCamSetupResultSessionConfigurationFailed;
}

AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
if ( [self.session canAddOutput:stillImageOutput] ) {
    stillImageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
    [self.session addOutput:stillImageOutput];
    self.stillImageOutput = stillImageOutput;
}
else {
    self.setupResult = AVCamSetupResultSessionConfigurationFailed;
}
[self.session commitConfiguration];
});
}
vaibhav
  • 4,038
  • 1
  • 21
  • 51
sreejesh
  • 718
  • 5
  • 15

2 Answers2

1

try to observe UIApplicationDidEnterBackgroundNotification/UIApplicationWillEnterForegroundNotification,UIApplicationWillResignActiveNotification/UIApplicationDidBecomeActiveNotification notifications to stop/start your capture session correspondingly

Frog Tan
  • 79
  • 4
0

There is a lot difference between the execution of ViewDidLoad ViewWillAppear and ViewDidAppear method app life cycle.

Creation of UIViews or executing some heavy task is fairly expensive causes freeze, and you should avoid as much as possible doing that on the ViewWillAppear method

have a look:

  1. ViewDidLoad: Whenever I'm adding controls to a view that should appear together with the view, right away, I put it in the ViewDidLoad method. Basically this method is called whenever the view was loaded into memory. So for example, if my view is a form with 3 labels, I would add the labels here; the view will never exist without those forms.
  2. ViewWillAppear: ViewWillAppear usually just to update the data on the form. So, for the example above, I would use this to actually load the data from my domain into the form. Creation of UIViews is fairly expensive, and you should avoid as much as possible doing that on the ViewWillAppear method, becuase when this gets called, it means that the iPhone is already ready to show the UIView to the user, and anything heavy you do here will impact performance in a very visible manner (like animations being delayed, etc).
  3. ViewDidAppear: ViewDidAppear to start off new threads to things that would take a long time to execute, like for example doing a webservice call to get extra data for the form above.The good thing is that because the view already exists and is being displayed to the user, you can show a nice "Waiting" message to the user while you get the data.
vaibhav
  • 4,038
  • 1
  • 21
  • 51