AVFoundation, как отключить звук затвора при captureStillImageAsynchronouslyFromConnection?

90

Я пытаюсь захватить изображение во время предварительного просмотра с камеры с помощью AVFoundation captureStillImageAsynchronouslyFromConnection . Пока программа работает как положено. Однако как я могу отключить звук срабатывания затвора?

охо
источник
5
Например, в Японии вы не можете этого сделать. Там всегда звучит звук срабатывания затвора, даже если вы отключили звук в телефоне. (Насколько я знаю, в Японии действуют некоторые странные правила для предотвращения нарушения конфиденциальности, также известные как фотография под юбкой) Так что я не думаю, что есть способ сделать это.
Dunkelstern
3
Да, возможно, ваш iPhone не позволяет делать снимки с камеры с отключенным звуком. Я нахожусь в США, и мой iPhone не издает звука, когда я делаю снимок с отключенным телефоном; с другой стороны, моя девушка издает звук (ее из Кореи).
donkim
5
Чтобы быть ясным, это решение, когда телефон не отключен / не вибрирует. В этом режиме при фотосъемке нет звука.
Новая Александрия
31
@NewAlexandria В Японии я слышал звуки затвора и в режиме вибрации. Это требование закона.
k06a
3
Btw AudioServicesPlaySystemSound не будет воспроизводиться, если устройство отключено. Таким образом, японские устройства по-прежнему будут издавать звук при отключении звука, но не при отключении звука ...
Филип Раделич

Ответы:

1500

Я однажды использовал этот код, чтобы записать звук затвора 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

k06a
источник
104
Очень круто. Единственное предостережение заключается в том, что при включении вспышки это приведет к звуку двойного затвора, потому что системный звук по умолчанию не воспроизводится при вызове captureStillImageAsynchronouslyFromConnection:, а при фактическом захвате изображения (во время последовательности вспышки). Вместо этого добавьте в self.stillImageOutput наблюдателя "ключ-значение" для keyPath 'capturingStillImage', чтобы определить, когда действительно начинается захват, а затем воспроизвести обратный звук в методе обратного вызова.
Джефф Машиа
16
Так современные военные самолеты побеждают радар. Вот почему вы не видите силуэт «невидимой формы» на новых конструкциях истребителей: есть пакет электроники, который обрабатывает то, что по сути является нарушающим радар сдвигом фазы, который передает «инвертированный» (сдвинутый по фазе) дублированный сигнал.
L0j1k
5
@ L0j1k: Это зависит от того, о каком корабле-невидимке вы говорите. F22 и иже с ним не заинтересованы в том, чтобы их не обнаружили, а в том, чтобы их можно было разблокировать. Проблема с технологией фазового сдвига в том, что вы невероятно заметны из любой другой позиции, поскольку они не увидят такой же сдвиг.
Guvante
13
Вот как работают наушники с шумоподавлением, за исключением того, что они улавливают звук в реальном времени
Даниэль Серодио,
4
Это было здорово для iOS7, но это больше не работает на iOS8, есть идеи, как решить эту проблему на iOS8?
Ticko
36

Мое решение в 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
Warpling
1
Это прекрасно работает. Если вам нужно разделить тихий режим и нормальный режим, используйте AudioServicesPlaySytemSoundID (1108) наоборот. Спасибо.
MJ Studio
1
Оно работает! Я хочу знать, есть ли у вас проблемы с загрузкой в ​​AppStore таким методом?
Liew Jun Tung
2
@LiewJunTung Нет проблем с этим решением. Я уже загрузил приложения с этим решением. Есть много приложений, использующих такое решение. Удачного кодирования.
Вон
Я обнаружил проблему. Мне интересно, обнаружили ли вы эту проблему. По сути, это утечка памяти, которая происходит только при AudioServicesDisposeSystemSoundID(1108)вызове. У вас есть какие-то решения для этого? :)
Лью Джун Тунг
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];
sudo rm -rf
источник
2
Я не думаю, что это сработает, он просто воспроизводит "пустой" звук, пока играет шум затвора. Вполне возможно играть 2 звука одновременно. Остальные 2 метода кажутся работоспособными!
Linuxmint,
2
Дать ему шанс. Я не уверен, что это сработает, но надеюсь!
sudo rm -rf
7

Мне удалось заставить это работать, используя этот код в функции snapStillImage, и он отлично работает для меня на iPhone 5 с iOS 8.3. Я также подтвердил, что 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];

Просто не забудьте быть вежливым и включить его, когда ваше приложение вернется!

frakman1
источник
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;
}
Ривера
источник
Можете ли вы заставить AVCaptureVideoDataOutput работать, пока используется AVCaptureMovieFileOutput? Когда я пытаюсь использовать их оба, методы делегата AVCaptureVideoDataOutput не вызываются.
Поль Сезанн
Извините, я не пробовал иметь более одного вывода одновременно.
Rivera
0

Единственный возможный обходной путь, о котором я могу думать, - это отключить звук iphone, когда они нажимают кнопку «сделать снимок», а затем включить его через секунду.

Linuxmint
источник
Как программно отключить звук iPhone? Благодарность!
ohho
даже если бы вы могли, Apple бы этого не одобрила
0

Распространенный прием в таких случаях - выяснить, вызывает ли фреймворк определенный метод для этого события, а затем временно перезаписать этот метод, аннулируя его эффект.

Мне очень жаль, но я недостаточно хорош, чтобы сразу сказать вам, работает ли это в данном случае. Вы можете попробовать команду «nm» на исполняемых файлах фреймворка, чтобы увидеть, есть ли именованная функция с подходящим именем, или использовать gdb с Simulator, чтобы отследить, куда она идет.

Я считаю, что когда вы знаете, что перезаписывать, появляются те низкоуровневые диспетчерские функции ObjC, которые вы можете использовать для перенаправления поиска функций. Я думаю, что делал это некоторое время назад, но не могу вспомнить детали.

Надеюсь, вы сможете воспользоваться моими подсказками, чтобы найти в Google несколько путей решения этой проблемы. Удачи.

Томас Темпельманн
источник