AVFoundation, как отключить звук затвора, когда captureStillImageAsynchronouslyFromConnection?
Я пытаюсь захватить изображение во время предварительного просмотра с камеры, с помощью AVFoundation captureStillImageAsynchronouslyFromConnection. Пока что программа работает как положено. Тем не менее, как я могу отключить звук затвора?
8 ответов
Я использовал этот код один раз для захвата звука затвора 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
из каталога документов (DiskAid для Mac). Следующий шаг я открыл 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
Мое решение для Swift 4
Когда вы звоните 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)
}
}
Смотрите также
Способ 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);
Вот пустой аудио-файл, если вам это нужно. http://cl.ly/3cKi
________________________________________________________________________________________________________________________________________
Метод 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];
Я смог заставить это работать, используя этот код в функции snapStillImage, и он отлично работает для меня на iOS 8.3 iPhone 5. Я также подтвердил, что Apple не будет отклонять ваше приложение, если вы используете это (они не отклоняли мой)
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];
Просто помните, чтобы быть хорошим и включить его, когда ваше приложение вернется!
Я живу в Японии, поэтому не могу отключить звук, когда мы делаем фотографии из соображений безопасности. В видео, однако звук отключается. Я не понимаю почему.
Единственный способ сделать фотографию без звука затвора - использовать 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 вызывается последовательно. Я это лучшие решения.
См. Этот пост для другого вида ответа: захват изображения из буфера изображения. Снимок экрана во время предварительного просмотра видео не удается
Вы также можете взять кадр из видеопотока, чтобы сделать снимок (не с полным разрешением).
Он используется здесь для захвата изображений через короткие промежутки времени:
- (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;
}
Обычная хитрость в таких случаях - выяснить, вызывает ли фреймворк определенный метод для этого события, а затем временно перезаписать этот метод, тем самым аннулируя его эффект.
Извините, но я недостаточно хорош, чтобы сразу сказать вам, работает ли это в этом случае. Вы можете попробовать команду "nm" в исполняемых файлах фреймворка, чтобы увидеть, есть ли именованная функция с подходящим именем, или использовать gdb с Simulator, чтобы отследить, куда она идет.
Как только вы узнаете, что нужно перезаписать, есть те низкоуровневые диспетчерские функции ObjC, которые, я думаю, можно использовать для перенаправления поиска функций. Я думаю, что сделал это некоторое время назад, но не могу вспомнить детали.
Надеюсь, вы можете использовать мои советы, чтобы найти несколько путей решения этой проблемы. Удачи.
Единственный возможный обходной путь, о котором я могу подумать, - это отключить звук iphone, когда они нажимают кнопку "сделать снимок", а затем включить его через секунду.