AVFoundation, come disattivare il suono dell'otturatore quando captureStillImageAsynchronouslyFromConnection?


89

Sto cercando di catturare un'immagine durante un'anteprima in tempo reale dalla telecamera, da AVFoundation captureStillImageAsynchronouslyFromConnection . Finora il programma funziona come previsto. Tuttavia, come posso disattivare il suono dell'otturatore?


5
In Giappone, ad esempio, non puoi farlo. Lì il suono dell'otturatore suona sempre anche se hai disattivato l'audio del telefono. (Alcune strane regole per prevenire la violazione della privacy, ovvero la fotografia upskirt, sono in vigore in Giappone per quanto ne so) Quindi non penso che ci sia un modo per farlo.
Dunkelstern

3
Sì, potrebbe essere che il tuo iPhone non consenta scatti con la fotocamera disattivata. Sono negli Stati Uniti e il mio iPhone non emette alcun suono quando scatto una foto con il telefono disattivato; quello della mia ragazza, d'altra parte, fa un suono (il suo è dalla Corea).
donkim

5
Per essere chiari, questa è una soluzione per quando il telefono non è in modalità muto / vibrazione. In questa modalità, non viene emesso alcun suono quando si scatta una foto.
New Alexandria

31
@NewAlexandria Ho sentito anche in Giappone i suoni dell'otturatore in modalità vibrazione. Questo è un requisito di legge.
k06a

3
Btw AudioServicesPlaySystemSound non verrà riprodotto se il dispositivo è disattivato. Quindi i dispositivi giapponesi continueranno a emettere un suono quando disattivati, ma non quando non disattivati ​​...
Filip Radelic

Risposte:


1500

Ho usato questo codice una volta per acquisire il suono dell'otturatore predefinito di iOS (ecco l'elenco dei nomi dei file audio 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];

Quindi ho usato un'app di terze parti per estrarre photoShutter.cafdalla directory Documenti (DiskAid per Mac). Il prossimo passo che ho aperto photoShutter.cafnell'editor audio di Audacity e applicato l'effetto di inversione, sembra questo con lo zoom elevato:

inserisci qui la descrizione dell'immagine

Quindi ho salvato questo suono come photoShutter2.cafe ho provato a riprodurlo subito prima 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 funziona davvero! Eseguo il test più volte, ogni volta che non sento il suono dell'otturatore :)

Puoi ottenere il suono già invertito, catturato su iPhone 5S iOS 7.1.1 da questo link: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf


104
Molto bello. L'unico avvertimento è che quando il flash è acceso, questo risulterà in un suono di doppio scatto perché il suono di sistema predefinito non viene riprodotto quando viene chiamato captureStillImageAsynchronouslyFromConnection:, ma piuttosto quando l'immagine viene effettivamente catturata (durante la sequenza di flash). Aggiungere invece un osservatore valore-chiave su self.stillImageOutput per keyPath 'capturingStillImage' per rilevare quando l'acquisizione inizia veramente, quindi riprodurre il suono inverso nel metodo di callback.
Jeff Mascia

16
È così che i moderni aerei militari sconfiggono il radar. Questo è il motivo per cui non vedi la sagoma della "forma invisibile" sui nuovi modelli di caccia: c'è un pacchetto di elettronica che gestisce quello che è fondamentalmente uno sfasamento che sconfigge il radar che trasmette un segnale duplicato "invertito" (sfasato).
L0j1k

5
@ L0j1k: Dipende dal tipo di nave stealth di cui parli. Gli F22 non sono interessati a non essere rilevati ma a essere sbloccabili. Il problema con la tecnologia di sfasamento è che sei incredibilmente ovvio da ogni altra posizione, dal momento che non vedranno lo stesso spostamento.
Guvante

13
Ecco come funzionano le cuffie con cancellazione del rumore, tranne che catturano il suono in tempo reale
Daniel Serodio,

4
Questo è stato ottimo per iOS7, ma non funziona più su iOS8, qualche idea su come risolverlo su iOS8?
Ticko

33

La mia soluzione in Swift

Quando chiami il AVCapturePhotoOutput.capturePhotometodo per acquisire un'immagine come il codice seguente.

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

Verranno richiamati i metodi AVCapturePhotoCaptureDelegate. E il sistema tenta di riprodurre il suono dell'otturatore dopo l' willCapturePhotoForinvocazione. Quindi puoi smaltire il suono del sistema nel willCapturePhotoFormetodo.

inserisci qui la descrizione dell'immagine

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Guarda anche


3
Questa è una bellissima soluzione e funziona perfettamente. Ottima risposta anche. Grazie @kyle
Warpling

1
Funziona perfettamente. Se è necessario separare la modalità silenziosa e la modalità normale, utilizzare al contrario AudioServicesPlaySytemSoundID (1108). Grazie.
MJ Studio

1
Funziona! Voglio sapere se hai problemi a caricare su AppStore con un metodo del genere?
Liew Jun Tung,

2
@LiewJunTung Nessun problema con questa soluzione. Ho già caricato app con questa soluzione. Ci sono molte app che hanno utilizzato questo tipo di soluzione. Buona codifica.
Ha vinto il

Ho scoperto un problema. Mi chiedo se hai scoperto questo problema. È fondamentalmente una perdita di memoria che si verifica solo quando AudioServicesDisposeSystemSoundID(1108)viene chiamata. Hai delle soluzioni per questo? :)
Liew Jun Tung

21

Metodo 1: non sono sicuro che funzioni, ma prova a riprodurre un file audio vuoto subito prima di inviare l'evento di acquisizione.

Per riprodurre una clip, aggiungi il Audio Toolboxframework #include <AudioToolbox/AudioToolbox.h> e riproduci il file audio in questo modo immediatamente prima di scattare 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);

Ecco un file audio vuoto se ne hai bisogno. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

Metodo 2: c'è anche un'alternativa se questo non funziona. Finché non è necessario disporre di una buona risoluzione, è possibile acquisire un fotogramma dal flusso video , evitando così del tutto il suono dell'immagine.

________________________________________________________________________________________________________________________________________

Metodo 3: Un altro modo per farlo sarebbe quello di fare uno "screenshot" della tua applicazione. Fallo in questo modo:

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 vuoi che questo riempia l'intero schermo con un'anteprima del flusso video in modo che il tuo screenshot abbia un bell'aspetto:

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
Non penso che funzionerebbe, riprodurrebbe solo un suono "vuoto" mentre suona il rumore dell'otturatore. È del tutto possibile riprodurre 2 suoni contemporaneamente. Gli altri 2 metodi sembrano fattibili!
Linuxmint

2
Dagli Un colpo. Non sono sicuro che funzionerà, ma spero!
sudo rm -rf

7

Sono riuscito a farlo funzionare utilizzando questo codice nella funzione snapStillImage e funziona perfettamente per me su iOS 8.3 iPhone 5. Ho anche confermato che Apple non rifiuterà la tua app se la usi (non hanno rifiutato il mio)

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

Ricorda solo di essere gentile e di riattivarlo quando la tua app torna!


5

Vivo in Giappone, quindi non posso disattivare l'audio quando scattiamo foto per motivi di sicurezza. Nel video, tuttavia, l'audio si spegne. Non capisco perché.

L'unico modo per scattare una foto senza il suono dell'otturatore è utilizzare AVCaptureVideoDataOutput o AVCaptureMovieFileOutput. Per analizzare le immagini fisse, AVCaptureVideoDataOutput è l'unico modo. Nel codice di esempio di 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);

Nel mio 3GS è molto pesante quando imposto CMTimeMake (1, 1); // Un fotogramma al secondo.

Nel codice di esempio WWDC 2010, FindMyiCone, ho trovato il seguente codice,

[output setAlwaysDiscardsLateVideoFrames:YES];

Quando viene utilizzata questa API, la tempistica non è concessa, ma l'API viene chiamata in sequenza. Questa è la soluzione migliore.


3

Puoi anche prendere un fotogramma da un flusso video per catturare un'immagine (non a piena risoluzione).

Viene utilizzato qui per acquisire immagini a brevi intervalli:

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

Sei in grado di far funzionare AVCaptureVideoDataOutput mentre viene utilizzato AVCaptureMovieFileOutput? Quando provo a usarli entrambi, i metodi delegati di AVCaptureVideoDataOutput non vengono chiamati.
Paul Cezanne

Mi dispiace non ho provato ad avere più di un'uscita allo stesso tempo.
Rivera


0

L'unica soluzione possibile a cui riesco a pensare è disattivare l'audio dell'iPhone quando premono il pulsante "scatta foto", quindi riattivarlo un secondo dopo.


Come disattivare l'audio dell'iPhone a livello di programmazione? Grazie!
ohho

anche se potessi, Apple non lo approverebbe

0

Un trucco comune in questi casi è scoprire se il framework richiama un certo metodo per questo evento e quindi sovrascriverlo temporaneamente, annullando così il suo effetto.

Mi dispiace ma non sono un hack abbastanza bravo da dirti subito se funziona in questo caso. Potresti provare il comando "nm" sugli eseguibili del framework per vedere se c'è una funzione denominata che ha un nome adatto, o usare gdb con il simulatore per tracciare dove va.

Una volta che sai cosa sovrascrivere, ci sono quelle funzioni di invio ObjC di basso livello che puoi usare per reindirizzare la ricerca delle funzioni, credo. Penso di averlo fatto qualche tempo fa, ma non ricordo i dettagli.

Si spera che tu possa usare i miei suggerimenti per cercare su Google alcuni percorsi di soluzioni per questo. In bocca al lupo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.