AVFoundation, como desligar o som do obturador quando captureStillImageAsynchronouslyFromConnection?

89

Estou tentando capturar uma imagem durante uma visualização ao vivo da câmera, por AVFoundation captureStillImageAsynchronouslyFromConnection . Até agora, o programa funciona conforme o esperado. No entanto, como posso silenciar o som do obturador?

Oh Ho
fonte
5
No Japão, por exemplo, você não pode fazer isso. Lá, o som do obturador sempre é reproduzido, mesmo se você silenciar o telefone. (Algumas regras estranhas para evitar violação de privacidade, também conhecidas como fotografia upskirt, estão em vigor no Japão, pelo que eu sei). Portanto, não acho que haja uma maneira de fazer isso.
Dunkelstern
3
Sim, pode ser que o seu iPhone não permita fotos sem som. Estou nos EUA e meu iPhone não emite nenhum som quando tiro uma foto com o telefone sem som; a da minha namorada, por outro lado, faz barulho (a dela é da Coréia).
donkim
5
Para ser claro, esta é uma solução para quando o telefone não está no modo silencioso / vibrar. Nesse modo, nenhum som é emitido ao tirar uma foto.
Nova Alexandria
31
@NewAlexandria Eu ouvi sons do obturador no Japão no modo de vibração também. Este é um requisito da lei.
k06a
3
Btw AudioServicesPlaySystemSound não vai jogar se o dispositivo estiver mudo. Portanto, os dispositivos japoneses ainda farão um som quando silenciados, mas não quando não
estiverem

Respostas:

1500

Usei este código uma vez para capturar o som do obturador padrão do iOS (aqui está uma lista de nomes de arquivos de som 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];

Em seguida, usei um aplicativo de terceiros para extrair photoShutter.cafdo diretório Documentos (DiskAid para Mac). Próxima etapa eu abri photoShutter.cafno editor de áudio Audacity e apliquei o efeito de inversão, fica assim em zoom alto:

insira a descrição da imagem aqui

Então salvei este som como photoShutter2.cafe tentei reproduzi-lo um pouco antes 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:
...

E isso realmente funciona! Executo o teste várias vezes, sempre que não ouço nenhum som do obturador :)

Você pode obter o som já invertido, capturado no iPhone 5S iOS 7.1.1 neste link: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

k06a
fonte
104
Muito legal. A única ressalva é que, quando o flash é ativado, isso resultará em um som de obturador duplo porque o som do sistema padrão não é reproduzido quando captureStillImageAsynchronouslyFromConnection: é chamado, mas sim quando a imagem é realmente capturada (durante a sequência do flash). Em vez disso, adicione um observador de valor-chave em self.stillImageOutput para keyPath 'capturingStillImage' para detectar quando a captura realmente começa e, em seguida, reproduza o som inverso no método de retorno de chamada.
Jeff Mascia
16
É assim que as aeronaves militares modernas derrotam o radar. É por isso que você não vê a silhueta de "forma furtiva" nos projetos de novos caças: há um pacote eletrônico que lida com o que é basicamente uma mudança de fase contra o radar que transmite um sinal duplicado "invertido" (com mudança de fase).
L0j1k
5
@ L0j1k: Depende de que tipo de nave furtiva você está falando. O F22 e uma laia não estão interessados ​​em não serem detectados, mas sim desbloqueáveis. O problema com a tecnologia de mudança de fase é que você fica incrivelmente óbvio em todas as outras posições, pois eles não verão a mesma mudança.
Guvante
13
É assim que os fones de ouvido com cancelamento de ruído funcionam, exceto que capturam o som em tempo real
Daniel Serodio
4
Isso era ótimo para iOS7, mas não funciona mais no iOS8, alguma ideia de como resolver isso no iOS8?
Ticko
33

Minha solução em Swift

Quando você chama o AVCapturePhotoOutput.capturePhotométodo para capturar uma imagem como o código abaixo.

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

Os métodos AVCapturePhotoCaptureDelegate serão chamados. E o sistema tenta reproduzir o som do obturador depois de willCapturePhotoForinvocado. Assim, você pode descartar o som do sistema no willCapturePhotoFormétodo.

insira a descrição da imagem aqui

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Veja também

Ganhou
fonte
3
Esta é uma solução bonita e funciona perfeitamente. Ótima resposta também. Obrigado @kyle
Warpling
1
Isso funciona perfeitamente. Se você precisar separar o modo silencioso do modo normal, use AudioServicesPlaySytemSoundID (1108) inversamente. Obrigado.
MJ Studio
1
Funciona! Quero saber se você tem problemas para fazer upload para a AppStore com esse método?
Liew Jun Tung
2
@LiewJunTung Nenhum problema com esta solução. Já carreguei aplicativos com esta solução. Existem muitos aplicativos que usam esse tipo de solução. Boa codificação.
Ganhou em
Eu descobri um problema. Estou me perguntando se você descobriu esse problema. É basicamente um vazamento de memória que só acontece quando AudioServicesDisposeSystemSoundID(1108)é chamado. Você tem alguma solução para isso? :)
Liew Jun Tung
21

Método 1: não tenho certeza se isso funcionará, mas tente reproduzir um arquivo de áudio em branco antes de enviar o evento de captura.

Para reproduzir um clipe, adicione a Audio Toolboxestrutura #include <AudioToolbox/AudioToolbox.h> e reproduza o arquivo de áudio assim imediatamente antes de tirar a foto:

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

Aqui está um arquivo de áudio em branco, se necessário. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________________

Método 2: também há uma alternativa se isso não funcionar. Contanto que você não precise ter uma boa resolução, você pode capturar um quadro do stream de vídeo , evitando assim o som da imagem.

________________________________________________________________________________________________________________________________________________

Método 3: Outra maneira de fazer isso seria fazer uma "captura de tela" do seu aplicativo. Faça desta forma:

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

Se você deseja que preencha toda a tela com uma visualização do stream de vídeo para que sua captura de tela tenha uma boa aparência:

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
fonte
2
Eu não acho que isso funcionaria, ele apenas tocaria um som "em branco" enquanto o ruído do obturador é reproduzido. É perfeitamente possível reproduzir 2 sons ao mesmo tempo. Os outros 2 métodos parecem viáveis!
Linuxmint
2
Dê uma chance. Não tenho certeza de que funcionará, mas espero que seja!
sudo rm -rf
7

Consegui fazer isso funcionar usando este código na função snapStillImage e funciona perfeitamente para mim no iOS 8.3 iPhone 5. Também confirmei que a Apple não rejeitará seu aplicativo se você usar isso (eles não rejeitaram meu)

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

Lembre-se de ser simpático e ativar o som quando o aplicativo retornar!

frakman1
fonte
5

Eu moro no Japão, então não posso silenciar o áudio quando tiramos fotos por motivos de segurança. No vídeo, entretanto, o áudio é desativado. Não entendo por quê.

A única maneira de tirar uma foto sem som do obturador é usando AVCaptureVideoDataOutput ou AVCaptureMovieFileOutput. Para analisar imagens estáticas, AVCaptureVideoDataOutput é a única maneira. No código de amostra 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);

No meu 3GS é muito pesado quando eu defino CMTimeMake (1, 1); // Um ​​quadro por segundo.

No código de amostra do WWDC 2010, FindMyiCone, encontrei o seguinte código,

[output setAlwaysDiscardsLateVideoFrames:YES];

Quando esta API é usada, o tempo não é concedido, mas a API é chamada sequencialmente. Eu este são as melhores soluções.

Ben
fonte
3

Você também pode obter um quadro de um fluxo de vídeo para capturar uma imagem (sem resolução total).

É usado aqui para capturar imagens em intervalos curtos:

- (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;
}
Rivera
fonte
Você consegue fazer o AVCaptureVideoDataOutput funcionar enquanto o AVCaptureMovieFileOutput está sendo usado? Quando tento usar os dois, os métodos de delegado de AVCaptureVideoDataOutput não são chamados.
Paul Cezanne
Desculpe, não tentei ter mais de uma saída ao mesmo tempo.
Rivera
0

A única solução possível em que consigo pensar seria desativar o som do iphone quando eles pressionam o botão "tirar foto" e reativá-lo um segundo depois.

Linuxmint
fonte
Como silenciar o som do iPhone de maneira programática? Obrigado!
ohho
mesmo se você pudesse, a Apple não aprovaria isso
0

Um truque comum nesses casos é descobrir se a estrutura invoca um determinado método para esse evento e, em seguida, sobrescrever esse método temporariamente, anulando assim seu efeito.

Sinto muito, mas não sou um hack bom o suficiente para lhe dizer imediatamente se isso funciona neste caso. Você poderia tentar o comando "nm" nos executáveis ​​do framework para ver se há uma função nomeada que tenha um nome adequado ou usar gdb com o Simulador para rastrear para onde ela vai.

Depois de saber o que substituir, existem aquelas funções de despacho ObjC de baixo nível que você pode usar para redirecionar a pesquisa de funções, eu acredito. Acho que já fiz isso há algum tempo, mas não consigo me lembrar dos detalhes.

Felizmente, você pode usar minhas dicas para pesquisar alguns caminhos de soluções para isso. Boa sorte.

Thomas Tempelmann
fonte