AVFoundation,如何在captureStillImageAsynchronouslyFromConnection时关闭快门声音?


89

我正在尝试通过AVFoundation captureStillImageAsynchronouslyFromConnection从相机进行实时预览期间捕获图像。到目前为止,该程序可以按预期工作。但是,如何使快门声静音?


5
例如,在日本,您无法做到这一点。即使您将手机静音,那里也会一直播放快门声音。(据我所知,日本有一些防止侵犯隐私的怪异规则,也就是超短裙摄影),所以我认为没有办法做到这一点。
Dunkelstern 2010年

3
是的,可能是您的iPhone不允许静音相机拍摄。我在美国,并且在手机静音的情况下拍照时,iPhone没有声音;另一方面,我女朋友的声音确实很大(她来自韩国)。
donkim 2010年

5
需要明确的是,这是当电话未处于静音/振动模式时的解决方案。在那种模式下,拍摄照片时不会发出声音。
2014年

31
@NewAlexandria我在日本也听说百叶窗在振动模式下也会发出声音。这是法律要求。
k06a 2014年

3
如果设备静音,则Btw AudioServicesPlaySystemSound将不会播放。因此,日语设备在静音时仍会发出声音,而在不静音时不会发出声音……
Filip Radelic 2014年

Answers:


1501

我曾经使用此代码捕获iOS默认的快门声音(这是声音文件名的列表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];

然后,我使用了第三方应用程序photoShutter.caf从“文档”目录(适用于Mac的DiskAid)中提取内容。下一步,我photoShutter.caf在Audacity音频编辑器中打开并应用了反转效果,在高缩放下看起来像这样:

在此处输入图片说明

然后我将声音另存为,photoShutter2.caf并尝试在播放声音之前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:
...

这确实有效!每当我没有听到快门声时,我都会进行几次测试:)

您可以从以下链接获取在iPhone 5S iOS 7.1.1上捕获的已经倒置的声音:https : //www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf


104
很酷。唯一需要注意的是,当打开闪光灯时,这将导致双快门声音,因为在调用captureStillImageAsynchronouslyFromConnection:时,而不是在实际捕获图像时(在闪光序列期间),不会播放默认系统声音。而是在self.stillImageOutput上为keyPath'capturingStillImage'添加一个键值观察器,以检测何时真正开始捕获,然后在回调方法中播放反向声音。
杰夫·马西娅

16
这就是现代军用飞机击败雷达的方式。这就是为什么您在新的战斗机设计中看不到“隐身形状”的轮廓的原因:有一个电子设备套件可以处理基本上是雷达破坏性的相移,该相移广播“反向”(相移)重复信号。
L0j1k 2014年

5
@ L0j1k:这取决于您在谈论哪种隐形船。F22之类的武器不感兴趣,不会被发现,而是可以解锁。相移技术的问题在于,从其他位置来看,您会非常明显,因为他们不会看到相同的相移。
2014年

13
这是怎么降噪耳机的工作,除非他们捕捉的实时声音
丹尼尔塞罗迪奥

4
这对iOS7来说很棒,但是在iOS8上不再起作用,知道如何在iOS8上解决这个问题吗?
Ticko 2014年

33

我在Swift中的解决方案

当您调用AVCapturePhotoOutput.capturePhoto方法捕获图像时,如以下代码所示。

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

AVCapturePhotoCaptureDelegate方法将被调用。并且系统尝试在willCapturePhotoFor调用后播放快门声音。因此,您可以在willCapturePhotoFor方法中处理系统声音。

在此处输入图片说明

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

也可以看看


3
这是一个很好的解决方案,并且效果很好。答案也不错。感谢@kyle
翘曲

1
这是完美的工作。如果需要将静默模式和正常模式分开,请反向使用AudioServicesPlaySytemSoundID(1108)。谢谢。
MJ Studio

1
有用!我想知道您使用这种方法上传到AppStore是否有困难?
Liew Jun Tung

2
@LiewJunTung此解决方案没有问题。我已经使用此解决方案上传了应用。有很多应用程序都使用这种解决方案。快乐的编码。
赢得

我发现了一个问题。我想知道您是否发现了这个问题。基本上,这是内存泄漏,仅在AudioServicesDisposeSystemSoundID(1108)调用时发生。您对此有什么解决方案吗?:)
Liew Jun Tung

21

方法1:不确定是否可以,但是在发送捕获事件之前尝试播放空白音频文件。

要播放剪辑,请添加Audio Toolbox框架,#include <AudioToolbox/AudioToolbox.h> 并在拍摄照片之前立即播放音频文件,如下所示:

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

如果需要,这是一个空白音频文件。 https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

方法2:如果此方法无效,则还有一种替代方法。只要您不需要高分辨率,就可以从视频流中抓取一帧,从而完全避免图像声音。

________________________________________________________________________________________________________________________________________

方法3:另一种方法是对应用程序进行“截屏”。这样做:

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

如果您希望使用视频流的预览来填充整个屏幕,从而使屏幕截图看起来不错:

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种声音。其他2种方法似乎可行!
Linuxmint 2010年

2
试一试。我不确定它是否会起作用,但这是希望!
sudo rm -rf 2010年

7

我可以通过在snapStillImage函数中使用此代码来使其正常工作,并且它在iOS 8.3 iPhone 5上对我而言非常理想。矿)

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];

请记住要保持友善,并在应用返回时取消静音!


5

我住在日本,因此出于安全考虑,我在拍照时无法使音频静音。在视频中,但是音频关闭。我不明白为什么。

我拍摄没有快门声的照片的唯一方法是使用AVCaptureVideoDataOutput或AVCaptureMovieFileOutput。对于分析静止图像,AVCaptureVideoDataOutput是唯一的方法。在AVFoundatation示例代码中,

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);

在我的3GS中,设置CMTimeMake(1,1)会很重;//每秒一帧。

在WWDC 2010示例代码FindMyiCone中,我发现以下代码,

[output setAlwaysDiscardsLateVideoFrames:YES];

使用此API时,不会授予时间,而是顺序调用API。我这是最好的解决方案。


3

您还可以从视频流中获取一帧以捕获(非全分辨率)图像。

它是用来在这里以短的时间间隔拍摄图像:

- (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;
}

使用AVCaptureMovieFileOutput时,您能否使AVCaptureVideoDataOutput正常工作?当我尝试同时使用它们时,不会调用AVCaptureVideoDataOutput的委托方法。
Paul Cezanne 2014年

抱歉,我没有尝试同时输出多个输出。
Rivera 2014年


0

我能想到的唯一可能的解决方法是,当他们按下“拍照”按钮时将iphone声音静音,然后过一秒钟取消静音。


如何以编程方式静音iPhone声音?谢谢!
ohho 2010年

即使您可以,Apple也不会批准

0

在这种情况下,一个常见的技巧是找出框架是否为此事件调用某个方法,然后临时覆盖该方法,从而使它的效果无效。

很抱歉,但是我还不能很好地告诉您在这种情况下是否可行。您可以在框架可执行文件上尝试“ nm”命令,以查看是否有一个具有合适名称的命名函数,或者将gdb与Simulator一起使用以跟踪其去向。

我相信,一旦知道要覆盖的内容,便可以使用那些低级的ObjC调度函数来重定向函数的查找。我想我曾经做过一次,但是记不清细节了。

希望您可以使用我的提示来搜索一些解决方案。祝好运。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.