Video freezes on camera switch with AVFoundation
Asked Answered
T

2

6

I made an app with a feature to capture and save video. I have used AVFoundation for that and Apple's AVCam has been my guide.

I hope I can make it clear:
Everything works fine until I release the videoViewController that handles the AVCamCaptureManager for the first time (In AVCam that would be AVCamViewController). After that when I create it again, the video freezes right after camera switch. Even re-run will not help, nor will clean, or device reset. (Sometimes one of the things help, but it is not a rule).

I release the videoViewController when not needed to save memory.

Code for switching the camera is basically the same as in AVCam:

NSError *error;
AVCaptureDeviceInput *newVideoInput;
AVCaptureDevicePosition position = currentVideoInput.device.position;

if (position == AVCaptureDevicePositionBack)
    newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:frontFacingCamera error:&error];
else if (position == AVCaptureDevicePositionFront)
    newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:backFacingCamera error:&error];

if (newVideoInput != nil) {
    [session beginConfiguration];
    [session removeInput:currentVideoInput];
    if ([session canAddInput:newVideoInput]) {
        [session addInput:newVideoInput];
        [self setVideoInput:newVideoInput];
} else {
    [session addInput:currentVideoInput];
}
    [session commitConfiguration];
    [newVideoInput release];
} else if (error) {
    NSLog(@"%@",[error localizedDescription]);
}

Code with which I dismiss videoView

[self.videoViewController.view removeFromSuperview];
self.videoViewController = nil;

My current "workaround" is to simply leave it be, even if I do not need it.

Can someone explain why is this happening and how to solve it.

EDIT: Solved
As W Dyson pointed out in his response I should have stopped the session before releasing my videoViewController like so:

[[[self.videoViewController captureManager] session] stopRunning];
[self.videoViewController.view removeFromSuperview];
self.videoViewController = nil;

Thanks W Dyson.

Tineid answered 18/4, 2011 at 14:33 Comment(0)
D
3

Are you getting error logs? If not, you need to fix the code above and see what they say. What version of AVCam are you using? They've recently updated the project to version 1.2, which is much more efficient and uses blocks.

From my experience, you shouldn't be creating and recreating a session, you can just leave it on. Maybe you need to structure your app a little differently. What exactly is your app about? Maybe we can help you. If your app is centred around the camera, then it's easier to leave the session on, if your just taking video modally, then maybe using AVCam is overkill.

Your actual problem, to me, sounds like it's with AVCaptureDeviceInput. Download the original AVCam package and see if you've changed any retain counts, or safety if statements. If there's any other code, please post.

UPDATE: Can you change

} else if (error) {
    NSLog(@"%@",[error localizedDescription]);
}

to

} if (error) {
    NSLog(@"%@",[error localizedDescription]);
}

and tell me if there's an error?

Also, before you release the view controller that owns the session, make sure you stop the session and set the capture manager to nil.

UPDATE 2: Try this toggle code. It's what I have been using. AVCamMirringMode is a struct as follows:

enum {
    AVCamMirroringOff   = 1,
    AVCamMirroringOn    = 2,
    AVCamMirroringAuto  = 3
};
typedef NSInteger AVCamMirroringMode;


- (BOOL) toggleCamera
{
    BOOL success = NO;

    if ([self cameraCount] > 1) {
        NSError *error;
        AVCaptureDeviceInput *newVideoInput;
        AVCaptureDevicePosition position = [[videoInput device] position];

        BOOL mirror;
        if (position == AVCaptureDevicePositionBack){
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self frontFacingCamera] error:&error];
            switch ([self mirroringMode]) {
                case AVCamMirroringOff:
                    mirror = NO;
                    break;
                case AVCamMirroringOn:
                    mirror = YES;
                    break;
                case AVCamMirroringAuto:
                default:
                    mirror = YES;
                    break;
            }
        }
        else if (position == AVCaptureDevicePositionFront){
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:&error];
            switch ([self mirroringMode]) {
                case AVCamMirroringOff:
                    mirror = NO;
                    break;
                case AVCamMirroringOn:
                    mirror = YES;
                    break;
                case AVCamMirroringAuto:
                default:
                    mirror = NO;
                    break;
            }
        }
        else
            goto bail;

        if (newVideoInput != nil) {
            [[self session] beginConfiguration];
            [[self session] removeInput:[self videoInput]];
            if ([[self session] canAddInput:newVideoInput]) {
                [[self session] addInput:newVideoInput];
                AVCaptureConnection *connection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];
                if ([connection isVideoMirroringSupported]) {
                    [connection setVideoMirrored:mirror];
                }
                [self setVideoInput:newVideoInput];
            } else {
                [[self session] addInput:[self videoInput]];
            }
            [[self session] commitConfiguration];
            success = YES;
            [newVideoInput release];

        } else if (error) {
            if ([[self delegate] respondsToSelector:@selector(captureManager:didFailWithError:)]) {
                [[self delegate] captureManager:self didFailWithError:error];
            }
        }
    }

bail:
    return success;
}
Dendritic answered 18/4, 2011 at 14:46 Comment(5)
No, no error logs. What do you suggest? I've looked into AVCam (yes, I used 1.2) and implemented its -captureManager:didFailWithError:, but still no error logs. The app is not centered around the camera. The camera is a side thing and might not be used at all by some. I've checked the AVCam again. the only difference is, In AVCam the camera is always present, I want to dismiss it, for reasons I've mentioned above.Tineid
The thing is, without seeing your project and the changes you've made to the AVCam project, this is tricky. Also, run the analyzer and see if it catches anything.Dendritic
Ok, I've stripped down all my custom code and turned back to the original AVCam the problem remains. I've changed the error bit as you advised and still get no errors. But I get an error if I try to record after the camera freeze. It says "The operation couldn't be completed. No such file or directory". The strange thing is, it records normally before the camera toggle. I hope that helps.Tineid
Thanks for your code snippet in UPDATE 2, but that didn't help... What did was the other thing you said, but I missed it before. I had to stop the session before releasing the parent ViewController. It is logical now, but I didn't think of it before as the video froze only after camera toggle. Thank you very much for your time and effort.Tineid
No problem. The UPDATE 2 code that I posted is from version 1.0 of AVCam, I use a meld of 1.0 and 1.2 is my apps. Feel free to use it if u wish. Glad I could help.Dendritic
S
9

Try this—it worked like a champ for me:

BOOL isUsingFrontFacingCamera;

- (BOOL) swapCameras
{
    if ([self cameraCount] > 1) {
        AVCaptureDevicePosition desiredPosition;
        if (isUsingFrontFacingCamera) {
            desiredPosition = AVCaptureDevicePositionBack;
        } else {
            desiredPosition = AVCaptureDevicePositionFront;
        }

        for (AVCaptureDevice *d in [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo]) {
            if ([d position] == desiredPosition) {
                [[self session] beginConfiguration];
                AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:d error:nil];
                for (AVCaptureInput *oldInput in [[self session] inputs]) {
                    [[self session] removeInput:oldInput];
                }
                [[self session] addInput:input];
                [[self session] commitConfiguration];
                break;
            }
        }
        isUsingFrontFacingCamera = !isUsingFrontFacingCamera;
        return YES;
    }
    return NO;
}

You should be able to drop this code in to replace the original swapCameras in VideoRecorderCaptureManager.m

Origin: Apple's SquareCam sample code.

Salangi answered 17/1, 2013 at 14:8 Comment(2)
This is the only piece of code that worked for me. I guess the trick is to remove all the input devices from the session. Thanks.Colewort
@Salangi this is working fine for me i can record video with front and back camera but while recording front camera the final video is MUTE (without audio) where for back camera its working fine plz help me Check this #31457802Coprolite
D
3

Are you getting error logs? If not, you need to fix the code above and see what they say. What version of AVCam are you using? They've recently updated the project to version 1.2, which is much more efficient and uses blocks.

From my experience, you shouldn't be creating and recreating a session, you can just leave it on. Maybe you need to structure your app a little differently. What exactly is your app about? Maybe we can help you. If your app is centred around the camera, then it's easier to leave the session on, if your just taking video modally, then maybe using AVCam is overkill.

Your actual problem, to me, sounds like it's with AVCaptureDeviceInput. Download the original AVCam package and see if you've changed any retain counts, or safety if statements. If there's any other code, please post.

UPDATE: Can you change

} else if (error) {
    NSLog(@"%@",[error localizedDescription]);
}

to

} if (error) {
    NSLog(@"%@",[error localizedDescription]);
}

and tell me if there's an error?

Also, before you release the view controller that owns the session, make sure you stop the session and set the capture manager to nil.

UPDATE 2: Try this toggle code. It's what I have been using. AVCamMirringMode is a struct as follows:

enum {
    AVCamMirroringOff   = 1,
    AVCamMirroringOn    = 2,
    AVCamMirroringAuto  = 3
};
typedef NSInteger AVCamMirroringMode;


- (BOOL) toggleCamera
{
    BOOL success = NO;

    if ([self cameraCount] > 1) {
        NSError *error;
        AVCaptureDeviceInput *newVideoInput;
        AVCaptureDevicePosition position = [[videoInput device] position];

        BOOL mirror;
        if (position == AVCaptureDevicePositionBack){
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self frontFacingCamera] error:&error];
            switch ([self mirroringMode]) {
                case AVCamMirroringOff:
                    mirror = NO;
                    break;
                case AVCamMirroringOn:
                    mirror = YES;
                    break;
                case AVCamMirroringAuto:
                default:
                    mirror = YES;
                    break;
            }
        }
        else if (position == AVCaptureDevicePositionFront){
            newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:&error];
            switch ([self mirroringMode]) {
                case AVCamMirroringOff:
                    mirror = NO;
                    break;
                case AVCamMirroringOn:
                    mirror = YES;
                    break;
                case AVCamMirroringAuto:
                default:
                    mirror = NO;
                    break;
            }
        }
        else
            goto bail;

        if (newVideoInput != nil) {
            [[self session] beginConfiguration];
            [[self session] removeInput:[self videoInput]];
            if ([[self session] canAddInput:newVideoInput]) {
                [[self session] addInput:newVideoInput];
                AVCaptureConnection *connection = [AVCamUtilities connectionWithMediaType:AVMediaTypeVideo fromConnections:[[self stillImageOutput] connections]];
                if ([connection isVideoMirroringSupported]) {
                    [connection setVideoMirrored:mirror];
                }
                [self setVideoInput:newVideoInput];
            } else {
                [[self session] addInput:[self videoInput]];
            }
            [[self session] commitConfiguration];
            success = YES;
            [newVideoInput release];

        } else if (error) {
            if ([[self delegate] respondsToSelector:@selector(captureManager:didFailWithError:)]) {
                [[self delegate] captureManager:self didFailWithError:error];
            }
        }
    }

bail:
    return success;
}
Dendritic answered 18/4, 2011 at 14:46 Comment(5)
No, no error logs. What do you suggest? I've looked into AVCam (yes, I used 1.2) and implemented its -captureManager:didFailWithError:, but still no error logs. The app is not centered around the camera. The camera is a side thing and might not be used at all by some. I've checked the AVCam again. the only difference is, In AVCam the camera is always present, I want to dismiss it, for reasons I've mentioned above.Tineid
The thing is, without seeing your project and the changes you've made to the AVCam project, this is tricky. Also, run the analyzer and see if it catches anything.Dendritic
Ok, I've stripped down all my custom code and turned back to the original AVCam the problem remains. I've changed the error bit as you advised and still get no errors. But I get an error if I try to record after the camera freeze. It says "The operation couldn't be completed. No such file or directory". The strange thing is, it records normally before the camera toggle. I hope that helps.Tineid
Thanks for your code snippet in UPDATE 2, but that didn't help... What did was the other thing you said, but I missed it before. I had to stop the session before releasing the parent ViewController. It is logical now, but I didn't think of it before as the video froze only after camera toggle. Thank you very much for your time and effort.Tineid
No problem. The UPDATE 2 code that I posted is from version 1.0 of AVCam, I use a meld of 1.0 and 1.2 is my apps. Feel free to use it if u wish. Glad I could help.Dendritic

© 2022 - 2024 — McMap. All rights reserved.