7

I was wondering if it's possible to add multiple AVCaptureVideoDataOutput to AVCaptureSession with a single camera device input?

My experiments indicate that adding a second VideoDataOutput will cause canAddOutput return NO. But I couldn't find anywhere on Apple's documentation says multiple data output is disallow.

xuguo
  • 1,816
  • 1
  • 11
  • 15

1 Answers1

7

We can not use single AVCaptureSession for multiple AVCaptureVideoDataOutput objects.

What can you do is you can make multiple AVCaptureVideoDataOutput with multiple AVCaptureSession objects.

You can create two different setups of the AVCaptureVideoDataOutput and AVCaptureSession then you can use them one after another in the app and you will be able to achieve the goal.

In my case, I had to capture front and back image using the camera at a time.

I did create two different objects for AVCaptureVideoDataOutput and AVCaptureSession as given below.

/* Front camera settings */
@property bool isFrontRecording;
@property (strong, nonatomic) AVCaptureDeviceInput *videoInputBack;
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutputBack;
@property (strong, nonatomic) AVCaptureSession *sessionBack;

/* Back camera settings */
@property bool isBackRecording;
@property (strong, nonatomic) AVCaptureDeviceInput *videoInputFront;
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutputFront;
@property (strong, nonatomic) AVCaptureSession *sessionFront;

Now in view did load initially I did setup back camera and set up flags for session started recording or not for both sessions as followed.

- (void)viewDidLoad {
    [super viewDidLoad];

    [self setupBackAVCapture];

    self.isFrontRecording = NO;
    self.isBackRecording = NO;
}

- (void)setupBackAVCapture
{
    NSError *error = nil;

    self.sessionBack = [[AVCaptureSession alloc] init];
    self.sessionBack.sessionPreset = AVCaptureSessionPresetPhoto;

    AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    self.videoInputBack = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&error];
    [self.sessionBack addInput:self.videoInputBack];

    self.imageOutputBack = [[AVCaptureStillImageOutput alloc] init];
    [self.sessionBack addOutput:self.imageOutputBack];

}

Now, whenever the user starts capturing photo we will capture the front photo using below code.

- (IBAction)buttonCapture:(id)sender {
    [self takeBackPhoto];
}

- (void)takeBackPhoto
{
    [self.sessionBack startRunning];
    if (!self.isFrontRecording) {

        self.isFrontRecording = YES;

        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
        AVCaptureConnection *videoConnection = [self.imageOutputBack connectionWithMediaType:AVMediaTypeVideo];

        if (videoConnection == nil) {
            return;
        }


        [self.imageOutputBack
         captureStillImageAsynchronouslyFromConnection:videoConnection
         completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

             if (imageDataSampleBuffer == NULL) {
                 return;
             }

             NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

             UIImage *image = [[UIImage alloc] initWithData:imageData];

             UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);

             [self.imageView setImage:image];

             [self.sessionBack stopRunning];

             // Set up front camera setting and capture photo.
             [self setupFrontAVCapture];
             [self takeFrontPhoto];

         }];

        self.isFrontRecording = NO;

    }

}

As soon as the back image will be captured we will setup session to capture front image using setupFrontAVCapture method and then we will capture front image using takeFrontPhoto method as given below.

- (void)setupFrontAVCapture
{
    NSError *error = nil;

    self.sessionFront = [[AVCaptureSession alloc] init];
    self.sessionFront.sessionPreset = AVCaptureSessionPresetPhoto;

    AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    camera = [self cameraWithPosition:AVCaptureDevicePositionFront];

    self.videoInputFront = [[AVCaptureDeviceInput alloc] initWithDevice:camera error:&error];
    [self.sessionFront addInput:self.videoInputFront];

    self.imageOutputFront = [[AVCaptureStillImageOutput alloc] init];
    [self.sessionFront addOutput:self.imageOutputFront];
}

- (void)takeFrontPhoto
{
    [self.sessionFront startRunning];
    if (!self.isBackRecording) {

        self.isBackRecording = YES;

        AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
        AVCaptureConnection *videoConnection = [self.imageOutputFront connectionWithMediaType:AVMediaTypeVideo];

        if (videoConnection == nil) {
            return;
        }


        [self.imageOutputFront
         captureStillImageAsynchronouslyFromConnection:videoConnection
         completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

             if (imageDataSampleBuffer == NULL) {
                 return;
             }

             NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];

             UIImage *image = [[UIImage alloc] initWithData:imageData];

             UIImageWriteToSavedPhotosAlbum(image, self, nil, nil);
             [self.imageViewBack setImage:image];

             [self.sessionFront stopRunning];


         }];

        self.isBackRecording = NO;

    }

}

This way you can use two different set of AVCaptureSession and AVCaptureStillImageOutput objects and you can achieve your goal.

Please let me know if you have any confusion.

Jayeshkumar Sojitra
  • 2,501
  • 4
  • 31
  • 44
  • Thanks for the info Jayesh. Do you know if this method would work for a single input device? My goal is to use the same back facing camera, and take two outputs in different resolution. – xuguo Jun 20 '17 at 19:20
  • No, you can't use single input device at a time. In your case might be you can make another resolution's file from original output. That can be one option for you. Or you can use two different sessions and use one after another. But that will take one or two seconds in between. – Jayeshkumar Sojitra Jun 21 '17 at 03:45
  • Thanks! That's aligned with my experiments too. I'm accepting you solution as answer now. But do you know if there's anywhere that apple says that this is not allowed? – xuguo Jun 21 '17 at 16:51
  • Hi @xuguo I'm in the same situation as you. I've got an app that needs to record video and stream video in the same time. I need to record in 4k, but for the streaming, I can use a lower resolution. Right now I'm resizing the 4k image with CoreImage, but if you have a more efficient solution, I would be happy to hear it. Thanks – Arkader Apr 13 '23 at 22:20