AVFoundation, ¿cómo apagar el sonido del obturador cuando captureStillImageAsynchronouslyFromConnection?

89

Estoy tratando de capturar una imagen durante una vista previa en vivo desde la cámara, por AVFoundation captureStillImageAsynchronouslyFromConnection . Hasta ahora el programa funciona como se esperaba. Sin embargo, ¿cómo puedo silenciar el sonido del obturador?

Oh ho
fuente
5
En Japón, por ejemplo, no puedes hacer eso. Allí, el sonido del obturador siempre se reproduce incluso si silencia el teléfono. (Hasta donde yo sé, existen algunas reglas extrañas para prevenir la violación de la privacidad, también conocidas como fotografía debajo de las faldas). Así que no creo que haya una manera de hacerlo.
Dunkelstern
3
Sí, puede ser que su iPhone no permita tomas de cámara silenciadas. Estoy en los EE. UU. Y mi iPhone no emite ningún sonido cuando tomo una foto con el teléfono en silencio; el de mi novia, por otro lado, hace un sonido (el de ella es de Corea).
donkim
5
Para ser claros, esta es una solución para cuando el teléfono no está en modo de silencio / vibración. En ese modo, no se emite ningún sonido al tomar una foto.
Nueva Alejandría
31
@NewAlexandria También escuché en Japón sonidos de obturadores en modo de vibración. Este es un requisito de la ley.
k06a
3
Por cierto AudioServicesPlaySystemSound no se reproducirá si el dispositivo está silenciado. Entonces, los dispositivos japoneses seguirán
emitiendo

Respuestas:

1500

Usé este código una vez para capturar el sonido del obturador predeterminado de iOS (aquí hay una lista de nombres de archivos de sonido 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];

Luego utilicé una aplicación de terceros para extraer photoShutter.cafdel directorio de documentos (DiskAid para Mac). El siguiente paso que abrí photoShutter.cafen el editor de audio Audacity y apliqué el efecto de inversión, se ve así en el zoom alto:

ingrese la descripción de la imagen aquí

Luego guardé este sonido photoShutter2.cafe intenté reproducir este sonido justo 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:
...

¡Y esto realmente funciona! Ejecuto la prueba varias veces, cada vez que no escucho el sonido del obturador :)

Puede obtener un sonido ya invertido, capturado en iPhone 5S iOS 7.1.1 desde este enlace: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

k06a
fuente
104
Muy genial. La única advertencia es que cuando el flash está encendido, esto dará como resultado un sonido de obturador doble porque el sonido del sistema predeterminado no se reproduce cuando se llama a captureStillImageAsynchronouslyFromConnection:, sino cuando se captura la imagen (durante la secuencia de flash). En su lugar, agregue un observador de valor clave en self.stillImageOutput para keyPath 'capturingStillImage' para detectar cuándo comienza realmente la captura, luego reproduzca el sonido inverso en el método de devolución de llamada.
Jeff Mascia
16
Así es como los aviones militares modernos derrotan al radar. Esta es la razón por la que no ves la silueta de la "forma sigilosa" en los nuevos diseños de caza: hay un paquete electrónico que maneja lo que es básicamente un cambio de fase que derrota al radar y que emite una señal duplicada "invertida" (con cambio de fase).
L0j1k
5
@ L0j1k: Depende de qué tipo de nave sigilosa estés hablando. El F22 no está interesado en pasar desapercibido, sino en ser desbloqueable. El problema con la tecnología de cambio de fase es que eres increíblemente obvio desde cualquier otra posición, ya que no verán el mismo cambio.
Guvante
13
Así es como funcionan los auriculares con cancelación de ruido, excepto que capturan el sonido en tiempo real
Daniel Serodio
4
Esto fue genial para iOS7, pero ya no funciona en iOS8, ¿alguna idea de cómo resolver esto en iOS8?
Ticko
33

Mi solución en Swift

Cuando llama al AVCapturePhotoOutput.capturePhotométodo para capturar una imagen como el siguiente código.

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

Se invocarán los métodos AVCapturePhotoCaptureDelegate. Y el sistema intenta reproducir el sonido del obturador después de willCapturePhotoForinvocado. Para que pueda deshacerse del sonido del sistema en willCapturePhotoFormethod.

ingrese la descripción de la imagen aquí

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Ver también

Won
fuente
3
Esta es una hermosa solución y funciona perfectamente. Gran respuesta también. Gracias @kyle
Warpling
1
Esto funciona perfectamente. Si necesita separar el modo silencioso del modo normal, use AudioServicesPlaySytemSoundID (1108) a la inversa. Gracias.
MJ Studio
1
¡Funciona! Quiero saber si tiene algún problema para cargar en AppStore con este método.
Liew Jun Tung
2
@LiewJunTung No hay problema con esta solución. Ya cargué aplicaciones con esta solución. Hay muchas aplicaciones que utilizan este tipo de solución. Codificación feliz.
Ganó el
He descubierto un problema. Me pregunto si ha descubierto este problema. Básicamente es una pérdida de memoria que solo ocurre cuando AudioServicesDisposeSystemSoundID(1108)se llama. ¿Tiene alguna solución para esto? :)
Liew Jun Tung
21

Método 1: No estoy seguro de si esto funcionará, pero intente reproducir un archivo de audio en blanco justo antes de enviar el evento de captura.

Para reproducir un clip, agregue el Audio Toolboxmarco #include <AudioToolbox/AudioToolbox.h> y reproduzca el archivo de audio de esta manera inmediatamente antes de tomar la foto:

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

Aquí hay un archivo de audio en blanco si lo necesita. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

Método 2: también hay una alternativa si esto no funciona. Siempre que no necesite tener una buena resolución, puede tomar un fotograma de la transmisión de video , evitando así el sonido de la imagen por completo.

________________________________________________________________________________________________________________________________________

Método 3: Otra forma de hacer esto sería tomar una "captura de pantalla" de su aplicación. Hacerlo de esta 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];

Si desea que esto llene toda la pantalla con una vista previa de la transmisión de video para que su captura de pantalla se vea bien:

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
fuente
2
No creo que eso funcione, simplemente reproduciría un sonido "en blanco" mientras se reproduce el ruido del obturador. Es muy posible reproducir 2 sonidos al mismo tiempo. ¡Los otros 2 métodos parecen viables!
Linuxmint
2
Dale un tiro. No estoy seguro de que funcione, ¡pero tenemos esperanza!
sudo rm -rf
7

Pude hacer que esto funcionara usando este código en la función snapStillImage y funciona perfectamente para mí en iOS 8.3 iPhone 5. También he confirmado que Apple no rechazará tu aplicación si usas esto (no rechazaron mía)

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

¡Solo recuerde ser amable y activarlo cuando vuelva su aplicación!

frakman1
fuente
5

Vivo en Japón, así que no puedo silenciar el audio cuando tomamos fotos por motivos de seguridad. En video, sin embargo, el audio se apaga. No entiendo por qué.

La única forma en que tomo una foto sin el sonido del obturador es usando AVCaptureVideoDataOutput o AVCaptureMovieFileOutput. Para analizar imágenes fijas, AVCaptureVideoDataOutput es la única forma. En el código de muestra de 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);

En mi 3GS es muy pesado cuando configuro CMTimeMake (1, 1); // Un fotograma por segundo.

En el código de muestra de WWDC 2010, FindMyiCone, encontré el siguiente código,

[output setAlwaysDiscardsLateVideoFrames:YES];

Cuando se utiliza esta API, no se concede el tiempo, pero la API se llama secuencialmente. Yo esta es la mejor solución.

Ben
fuente
3

También puede tomar un fotograma de una transmisión de video para capturar una imagen (no con resolución completa).

Se utiliza aquí para capturar imágenes a intervalos cortos:

- (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
fuente
¿Puede hacer que AVCaptureVideoDataOutput funcione mientras se utiliza AVCaptureMovieFileOutput? Cuando trato de usar ambos, los métodos delegados de AVCaptureVideoDataOutput no son llamados.
Paul Cezanne
Lo siento, no he intentado tener más de una salida al mismo tiempo.
Rivera
0

La única solución alternativa que se me ocurre es silenciar el sonido del iPhone cuando presionan el botón "tomar foto" y luego desactivarlo un segundo más tarde.

Linuxmint
fuente
¿Cómo silenciar el sonido del iPhone mediante programación? ¡Gracias!
ohho
incluso si pudieras, Apple no aprobaría eso
0

Un truco común en tales casos es averiguar si el marco invoca un método determinado para este evento y luego sobrescribir ese método temporalmente, anulando así su efecto.

Lo siento, pero no soy lo suficientemente bueno como para decirte de inmediato si eso funciona en este caso. Puede probar el comando "nm" en los ejecutables del framework para ver si hay una función nombrada que tenga un nombre adecuado, o usar gdb con el Simulador para rastrear a dónde va.

Una vez que sepa qué sobrescribir, existen esas funciones de despacho de ObjC de bajo nivel que puede usar para redirigir la búsqueda de funciones, creo. Creo que lo hice hace un tiempo, pero no recuerdo los detalles.

Con suerte, puede usar mis sugerencias para buscar en Google algunas soluciones para esto. Buena suerte.

Thomas Tempelmann
fuente