95

I am trying to capture an image during a live preview from the camera, by AVFoundation captureStillImageAsynchronouslyFromConnection. So far the program works as expected. However, how can I mute the shutter sound?

7
  • 6
    In Japan for example you can't do that. There the shutter sound always plays even if you muted the phone. (Some weird rules to prevent privacy infringement, aka upskirt photography are in place in Japan as far as i know) So I don't think there is a way to do that. Dec 16, 2010 at 15:28
  • 3
    Yeah, it might be that your iPhone doesn't allow for muted camera shots. I'm in the US and my iPhone doesn't make a sound when I take a picture with the phone muted; my girlfriend's, on the other hand, does make a sound (hers is from Korea).
    – donkim
    Dec 18, 2010 at 2:05
  • 5
    To be clear, this is a solution for when the phone is not muted / vibrate mode. In that mode, no sound is made when taking a picture. May 23, 2014 at 17:50
  • 32
    @NewAlexandria I heard in Japan shutter sounds in vibration mode too. This is law requirement.
    – k06a
    May 23, 2014 at 19:30
  • 3
    Btw AudioServicesPlaySystemSound will not play if device is muted. So Japanese devices will still make a sound when muted, but not when non-muted... May 23, 2014 at 23:27

9 Answers 9

1504
+100

I used this code once to capture iOS default shutter sound (here is list of sound file names https://github.com/TUNER88/iOSSystemSoundsLibrary):

NSString *path = @"/System/Library/Audio/UISounds/photoShutter.caf";
NSString *docs = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSData *data = [NSData dataWithContentsOfFile:path];
[data writeToFile:[docs stringByAppendingPathComponent:@"photoShutter.caf"] atomically:YES];

Then I used third-party app to extract photoShutter.caf from Documents directory (DiskAid for Mac). Next step I opened photoShutter.caf in Audacity audio editor and applied inversion effect, it looks like this on high zoom:

enter image description here

Then I saved this sound as photoShutter2.caf and tried to play this sound right before captureStillImageAsynchronouslyFromConnection:

static SystemSoundID soundID = 0;
if (soundID == 0) {
    NSString *path = [[NSBundle mainBundle] pathForResource:@"photoShutter2" ofType:@"caf"];
    NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)filePath, &soundID);
}
AudioServicesPlaySystemSound(soundID);

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:
...

And this really works! I runs test several times, every time I hear no shutter sound :)

You can get already inverted sound, captured on iPhone 5S iOS 7.1.1 from this link: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

33
  • 105
    Very cool. Only caveat is that when flash is turned on, this will result in a double shutter sound because the default system sound is not played when captureStillImageAsynchronouslyFromConnection: is called, but rather when image is actually captured (during flash sequence). Instead add a key-value observer on self.stillImageOutput for keyPath 'capturingStillImage' to detect when capture truly starts, then play inverse sound in callback method. May 23, 2014 at 23:20
  • 17
    This is how modern military aircraft defeat radar. This is why you don't see the "stealth shape" silhouette on new fighter designs: There is an electronics package that handles what is basically a radar-defeating phase shift that broadcasts an "inverted" (phase-shifted) duplicate signal.
    – L0j1k
    May 24, 2014 at 2:43
  • 5
    @L0j1k: It depends on what kind of stealth ship you are talking about. The F22 an ilk aren't interested in being undetected but in being unlockable. Problem with phase shift technology is you are incredibly obvious from every other position, since they won't see the same shift.
    – Guvante
    May 24, 2014 at 3:05
  • 14
    This is how noise-cancelling headphones work, except they capture the sound in realtime May 24, 2014 at 3:19
  • 4
    This was great for iOS7, but it no longer works on iOS8, any idea how to solve this on iOS8 ?
    – Ticko
    Oct 28, 2014 at 11:32
50

My Solution in Swift

When you call AVCapturePhotoOutput.capturePhoto method to capture an image like the below code.

photoOutput.capturePhoto(with: self.capturePhotoSettings, delegate: self)

AVCapturePhotoCaptureDelegate methods will be invoked. And the system tries to play shutter sound after willCapturePhotoFor invoked. So you can dispose of system sound in willCapturePhotoFor method.

enter image description here

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

    func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
        // dispose system shutter sound
        AudioServicesDisposeSystemSoundID(1108)
    }
}

See also

8
  • 5
    This is a beautiful solution and it works perfectly. Great answer too. Thanks @kyle
    – Warpling
    Mar 21, 2019 at 22:57
  • 1
    This is perfectly work. If you need to separate silent mode and normal mode, Use AudioServicesPlaySytemSoundID(1108) reversely. Thank you.
    – MJ Studio
    Apr 10, 2019 at 5:05
  • 1
    It works! I want to know if you have any trouble uploading to AppStore with such a method? Nov 22, 2019 at 10:03
  • 3
    @LiewJunTung No trouble with this solution. I already uploaded apps with this solution. There are a lot of apps that used this kind of solution. Happy coding.
    – Won
    Nov 23, 2019 at 4:25
  • 2
    This answer usually works, but it creates a race condition dependent on system load. Sometimes, iOS doesn't get around to calling photoOutput until after part of the shutter sound has been played. Oct 1, 2020 at 17:20
21
+100

Method 1: Not sure if this will work, but try playing a blank audio file right before you send the capture event.

To play a clip, add the Audio Toolbox framework, #include <AudioToolbox/AudioToolbox.h> and play the audio file like this immediately before you take the picture:

 NSString *path = [[NSBundle mainBundle] pathForResource:@"blank" ofType:@"wav"];
 SystemSoundID soundID;
 NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
 AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
 AudioServicesPlaySystemSound(soundID);

Here is a blank audio file if you need it. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

Method 2: There's also an alternative if this doesn't work. As long as you don't need to have a good resolution, you can grab a frame from the video stream, thus avoiding the picture sound altogether.

________________________________________________________________________________________________________________________________________

Method 3: Another way to do this would be to take a "screenshot" of your application. Do it this way:

UIGraphicsBeginImageContext(self.window.bounds.size);
[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
[data writeToFile:@"foo.png" atomically:YES];

If you're wanting this to fill the whole screen with a preview of the video stream so that your screenshot looks good:

AVCaptureSession *captureSession = yourcapturesession;
AVCaptureVideoPreviewLayer *previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
UIView *aView = theViewYouWantTheLayerIn;
previewLayer.frame = aView.bounds; // Assume you want the preview layer to fill the view.
[aView.layer addSublayer:previewLayer];
2
  • 2
    I don't think that would work, it would just play a "blank" sound while the shutter noise plays. Its quite possible to play 2 sounds at the same time. The other 2 methods seem workable!
    – Linuxmint
    Dec 13, 2010 at 22:46
  • 2
    Give it a shot. I'm not confident it will work, but here's to hoping! Dec 14, 2010 at 5:27
7

I was able to get this to work by using this code in the snapStillImage function and it works perfectly for me on iOS 8.3 iPhone 5. I have also confirmed that Apple won't reject your app if you use this (they didn't reject mine)

MPVolumeView* volumeView = [[MPVolumeView alloc] init];
//find the volumeSlider
UISlider* volumeViewSlider = nil;
for (UIView *view in [volumeView subviews]){
    if ([view.class.description isEqualToString:@"MPVolumeSlider"]){
        volumeViewSlider = (UISlider*)view;
        break;
    }
}
// mute it here:
[volumeViewSlider setValue:0.0f animated:YES];
[volumeViewSlider sendActionsForControlEvents:UIControlEventTouchUpInside];

Just remember to be nice and unmute it when your app returns!

0
5

I live in in Japan, so I can not mute the audio when we take photos for security reason. In video, however audio turns off. I don't understand why.

The only way I take a photo without shutter sound is using AVCaptureVideoDataOutput or AVCaptureMovieFileOutput. For analyze still image AVCaptureVideoDataOutput is only way. In AVFoundatation sample code,

AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
// If you wish to cap the frame rate to a known value, such as 15 fps, set 
// minFrameDuration.
output.minFrameDuration = CMTimeMake(1, 15);

In my 3GS it is very heavy when I set CMTimeMake(1, 1); // One frame per second.

In WWDC 2010 Sample code, FindMyiCone, I found following code,

[output setAlwaysDiscardsLateVideoFrames:YES];

When this API is used, the timing is not granted, but API is called sequentially. I this it is best solutions.

4

See this post for a different kind of answer: Capture the image from the image buffer. Screen capture during video preview fails

3

You can also take a frame from a video stream to capture a (not full resolution) image.

It is used here to capture images at short intervals:

- (IBAction)startStopPictureSequence:(id)sender
{
    if (!_capturingSequence)
    {
        if (!_captureVideoDataOutput)
        {
            _captureVideoDataOutput = [AVCaptureVideoDataOutput new];
            _captureVideoDataOutput.videoSettings = @{(NSString *)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)};
            [_captureVideoDataOutput setSampleBufferDelegate:self
                                                       queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)];
            if (_sequenceCaptureInterval == 0)
            {
                _sequenceCaptureInterval = 0.25;
            }
        }

        if ([_captureSession canAddOutput:_captureVideoDataOutput])
        {
            [_captureSession addOutput:_captureVideoDataOutput];
            _lastSequenceCaptureDate = [NSDate date]; // Skip the first image which looks to dark for some reason
            _sequenceCaptureOrientation = (_currentDevice.position == AVCaptureDevicePositionFront ? // Set the output orientation only once per sequence
                                           UIImageOrientationLeftMirrored :
                                           UIImageOrientationRight);
            _capturingSequence = YES;
        }
        else
        {
            NBULogError(@"Can't capture picture sequences here!");
            return;
        }
    }
    else
    {
        [_captureSession removeOutput:_captureVideoDataOutput];
        _capturingSequence = NO;
    }
}

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
{
    // Skip capture?
    if ([[NSDate date] timeIntervalSinceDate:_lastSequenceCaptureDate] < _sequenceCaptureInterval)
        return;

    _lastSequenceCaptureDate = [NSDate date];

    UIImage * image = [self imageFromSampleBuffer:sampleBuffer];
    NBULogInfo(@"Captured image: %@ of size: %@ orientation: %@",
               image, NSStringFromCGSize(image.size), @(image.imageOrientation));

    // Execute capture block
    dispatch_async(dispatch_get_main_queue(), ^
                   {
                       if (_captureResultBlock) _captureResultBlock(image, nil);
                   });
}

- (BOOL)isRecording
{
    return _captureMovieOutput.recording;
}
2
  • Are you able to get AVCaptureVideoDataOutput to work while AVCaptureMovieFileOutput is being used? When I try to use both of them the AVCaptureVideoDataOutput's delegate methods are not called. Aug 28, 2014 at 16:56
  • Sorry I haven't tried to have more than one output at the same time.
    – E. Rivera
    Sep 1, 2014 at 0:54
0

The only possible work-around I can think of would be to mute the iphone sound when they press the "take picture" button, and then un-mute it a second later.

2
  • How to mute the iPhone sound programmatically? Thanks!
    – ohho
    Dec 10, 2010 at 0:55
  • even if you could, Apple wouldn't approve that
    – user207616
    Dec 19, 2010 at 11:59
0

A common trick in such cases is to find out if the framework invokes a certain method for this event, and then to overwrite that method temporarily, thereby voiding its effect.

I'm sorry but I am not a good enough hack to tell you right away if that works in this case. You could try the "nm" command on the framework executables to see if there's a named function that has a suitatable name, or use gdb with the Simulator to trace where it goes.

Once you know what to overwrite, there are those low level ObjC dispatching functions that you can use to redirect the lookup for functions, I believe. I think I've done that once a while ago, but can't remember the details.

Hopefully, you can use my hints to google a few solutions paths to this. Good luck.

Not the answer you're looking for? Browse other questions tagged or ask your own question.