AVFoundation, wie schalte ich den Shutter-Sound aus, wenn ich CaptureStillImageAsynchronouslyFromConnection aufnehme?

89

Ich versuche, ein Bild während einer Live-Vorschau von der Kamera mit AVFoundation captureStillImageAsynchronouslyFromConnection aufzunehmen . Bisher funktioniert das Programm wie erwartet. Wie kann ich jedoch den Auslöserton stumm schalten?

Oh ho
quelle
4
In Japan zum Beispiel kann man das nicht machen. Dort wird der Auslöserton immer abgespielt, auch wenn Sie das Telefon stummgeschaltet haben. (Soweit ich weiß, gibt es in Japan einige seltsame Regeln zur Verhinderung von Datenschutzverletzungen, auch bekannt als Upskirt-Fotografie.) Ich glaube also nicht, dass es einen Weg gibt, dies zu tun.
Dunkelstern
3
Ja, es kann sein, dass Ihr iPhone keine gedämpften Kameraaufnahmen zulässt. Ich bin in den USA und mein iPhone gibt keinen Ton aus, wenn ich mit stummgeschaltetem Telefon ein Bild aufnehme. meine Freundin dagegen macht ein Geräusch (ihre ist aus Korea).
Donkim
5
Dies ist eine Lösung, wenn das Telefon nicht stummgeschaltet / vibriert ist. In diesem Modus wird beim Aufnehmen eines Bildes kein Ton ausgegeben.
New Alexandria
31
@NewAlexandria Ich habe in Japan auch im Vibrationsmodus Verschlussgeräusche gehört. Dies ist gesetzlich vorgeschrieben.
k06a
3
Btw AudioServicesPlaySystemSound wird nicht abgespielt, wenn das Gerät stummgeschaltet ist. So machen japanische Geräte immer noch einen Ton, wenn sie stummgeschaltet sind, aber nicht, wenn sie nicht stummgeschaltet sind ...
Filip Radelic

Antworten:

1500

Ich habe diesen Code einmal verwendet, um den Standard-Shutter-Sound von iOS aufzunehmen (hier ist eine Liste der Sounddateinamen 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];

Dann habe ich eine Drittanbieter-App verwendet, um photoShutter.cafaus dem Dokumentenverzeichnis (DiskAid für Mac) zu extrahieren . Der nächste Schritt, den ich photoShutter.cafim Audacity-Audio-Editor geöffnet und den Inversionseffekt angewendet habe, sieht bei hohem Zoom folgendermaßen aus:

Geben Sie hier die Bildbeschreibung ein

Dann habe ich diesen Sound als gespeichert photoShutter2.cafund versucht, diesen Sound direkt vorher abzuspielen 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:
...

Und das funktioniert wirklich! Ich führe den Test mehrmals durch, jedes Mal, wenn ich keinen Auslöserton höre :)

Über diesen Link können Sie bereits invertierten Sound erhalten, der auf dem iPhone 5S iOS 7.1.1 aufgenommen wurde: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

k06a
quelle
104
Sehr cool. Die einzige Einschränkung ist, dass beim Einschalten des Blitzes ein Double-Shutter-Sound erzeugt wird, da der Standard-System-Sound nicht abgespielt wird, wenn captureStillImageAsynchronouslyFromConnection: aufgerufen wird, sondern wenn das Bild tatsächlich aufgenommen wird (während der Flash-Sequenz). Fügen Sie stattdessen einen Schlüsselwertbeobachter zu self.stillImageOutput hinzu, damit keyPath 'capturingStillImage' erkennt, wann die Erfassung wirklich beginnt, und spielen Sie dann den inversen Sound in der Rückrufmethode ab.
Jeff Mascia
16
So besiegen moderne Militärflugzeuge Radar. Aus diesem Grund sehen Sie bei neuen Kämpferdesigns nicht die Silhouette der "Stealth-Form": Es gibt ein Elektronikpaket, das eine im Grunde genommen radarschädigende Phasenverschiebung handhabt, die ein "invertiertes" (phasenverschobenes) Duplikatsignal sendet.
L0j1k
5
@ L0j1k: Es hängt davon ab, um welche Art von Stealth-Schiff es sich handelt. Die F22 und andere sind nicht daran interessiert, unentdeckt zu bleiben, sondern freischaltbar zu sein. Das Problem mit der Phasenverschiebungstechnologie ist, dass Sie von jeder anderen Position aus unglaublich offensichtlich sind, da sie nicht dieselbe Verschiebung sehen.
Guvante
13
So funktionieren Kopfhörer mit Geräuschunterdrückung, außer dass sie den Ton in Echtzeit erfassen
Daniel Serodio
4
Das war großartig für iOS7, aber es funktioniert nicht mehr unter iOS8. Haben Sie eine Idee, wie Sie dies unter iOS8 lösen können?
Ticko
32

Meine Lösung in Swift

Wenn Sie die AVCapturePhotoOutput.capturePhotoMethode aufrufen , um ein Bild wie den folgenden Code aufzunehmen.

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

AVCapturePhotoCaptureDelegate-Methoden werden aufgerufen. Und das System versucht, den Shutter-Sound nach dem willCapturePhotoForAufrufen wiederzugeben. So können Sie den Systemklang in der willCapturePhotoForMethode entsorgen .

Geben Sie hier die Bildbeschreibung ein

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Siehe auch

Gewonnen
quelle
3
Dies ist eine schöne Lösung und es funktioniert perfekt. Tolle Antwort auch. Danke @kyle
Warpling
1
Das ist perfekt Arbeit. Wenn Sie den Silent-Modus vom Normal-Modus trennen müssen, verwenden Sie AudioServicesPlaySytemSoundID (1108) umgekehrt. Danke dir.
MJ Studio
1
Es klappt! Ich möchte wissen, ob Sie Probleme beim Hochladen in den AppStore mit einer solchen Methode haben.
Liew Jun Tung
1
@LiewJunTung Keine Probleme mit dieser Lösung. Ich habe bereits Apps mit dieser Lösung hochgeladen. Es gibt viele Apps, die diese Art von Lösung verwenden. Viel Spaß beim Codieren.
Gewonnen
Ich habe ein Problem entdeckt. Ich frage mich, ob Sie dieses Problem entdeckt haben. Es ist im Grunde ein Speicherverlust, der nur auftritt, wenn er AudioServicesDisposeSystemSoundID(1108)aufgerufen wird. Haben Sie Lösungen dafür? :)
Liew Jun Tung
21

Methode 1: nicht sicher, ob dies funktioniert. Versuchen Sie jedoch, eine leere Audiodatei abzuspielen, bevor Sie das Capture-Ereignis senden.

Um einen Clip abzuspielen, fügen Sie das Audio ToolboxFramework hinzu #include <AudioToolbox/AudioToolbox.h> und spielen Sie die Audiodatei unmittelbar vor der Aufnahme wie folgt ab:

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

Hier ist eine leere Audiodatei, wenn Sie sie brauchen. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________________________

Methode 2: Es gibt auch eine Alternative, wenn dies nicht funktioniert. Solange Sie keine gute Auflösung benötigen, können Sie ein Bild aus dem Videostream abrufen und so den Bildton ganz vermeiden.

________________________________________________________________________________________________________________________________________________________

Methode 3: Eine andere Möglichkeit, dies zu tun, besteht darin, einen "Screenshot" Ihrer Anwendung zu erstellen. Mach es so:

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

Wenn Sie möchten, dass dies den gesamten Bildschirm mit einer Vorschau des Videostreams ausfüllt, damit Ihr Screenshot gut aussieht:

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
quelle
2
Ich denke nicht, dass das funktionieren würde, es würde nur einen "leeren" Ton wiedergeben, während das Verschlussgeräusch abgespielt wird. Es ist durchaus möglich, 2 Sounds gleichzeitig abzuspielen. Die anderen 2 Methoden scheinen praktikabel!
Linuxmint
2
Versuch es einmal. Ich bin nicht sicher, ob es funktionieren wird, aber hier ist zu hoffen!
Sudo rm -rf
7

Ich konnte dies mithilfe dieses Codes in der Funktion snapStillImage zum Laufen bringen und es funktioniert perfekt für mich unter iOS 8.3 iPhone 5. Ich habe auch bestätigt, dass Apple Ihre App nicht ablehnt, wenn Sie dies verwenden (sie haben es nicht abgelehnt) Bergwerk)

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

Denken Sie daran, nett zu sein und die Stummschaltung aufzuheben, wenn Ihre App zurückkehrt!

frakman1
quelle
5

Ich lebe in Japan, daher kann ich das Audio nicht stumm schalten, wenn wir aus Sicherheitsgründen Fotos machen. In Video wird Audio jedoch ausgeschaltet. Ich verstehe nicht warum.

Die einzige Möglichkeit, ein Foto ohne Auslöserton aufzunehmen, ist die Verwendung von AVCaptureVideoDataOutput oder AVCaptureMovieFileOutput. Für die Analyse von Standbildern ist AVCaptureVideoDataOutput nur möglich. Im AVFoundatation-Beispielcode

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

In meinem 3GS ist es sehr schwer, wenn ich CMTimeMake (1, 1) einstelle; // Ein Bild pro Sekunde.

Im WWDC 2010-Beispielcode FindMyiCone habe ich folgenden Code gefunden:

[output setAlwaysDiscardsLateVideoFrames:YES];

Wenn diese API verwendet wird, wird das Timing nicht gewährt, aber die API wird nacheinander aufgerufen. Ich das ist es beste Lösungen.

Ben
quelle
3

Sie können auch ein Bild aus einem Videostream aufnehmen, um ein Bild (nicht in voller Auflösung) aufzunehmen.

Es wird hier verwendet , um Bilder in kurzen Intervallen aufzunehmen:

- (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
quelle
Können Sie AVCaptureVideoDataOutput zum Laufen bringen, während AVCaptureMovieFileOutput verwendet wird? Wenn ich versuche, beide zu verwenden, werden die Delegatmethoden von AVCaptureVideoDataOutput nicht aufgerufen.
Paul Cezanne
Entschuldigung, ich habe nicht versucht, mehr als eine Ausgabe gleichzeitig zu haben.
Rivera
0

Die einzig mögliche Lösung, die ich mir vorstellen kann, besteht darin, den iPhone-Ton stummzuschalten, wenn sie auf die Schaltfläche "Bild aufnehmen" klicken, und ihn eine Sekunde später wieder stummzuschalten.

Linuxmint
quelle
Wie kann ich den iPhone-Sound programmgesteuert stummschalten? Vielen Dank!
Ohho
Selbst wenn Sie könnten, würde Apple das nicht genehmigen
0

Ein häufiger Trick in solchen Fällen besteht darin, herauszufinden, ob das Framework eine bestimmte Methode für dieses Ereignis aufruft, und diese Methode dann vorübergehend zu überschreiben, wodurch ihre Wirkung aufgehoben wird.

Es tut mir leid, aber ich bin nicht gut genug, um Ihnen sofort zu sagen, ob das in diesem Fall funktioniert. Sie können den Befehl "nm" für die ausführbaren Framework-Dateien verwenden, um festzustellen, ob eine benannte Funktion einen geeigneten Namen hat, oder gdb mit dem Simulator verwenden, um zu verfolgen, wohin sie führt.

Sobald Sie wissen, was Sie überschreiben müssen, gibt es diese einfachen ObjC-Dispatching-Funktionen, mit denen Sie die Suche nach Funktionen umleiten können, glaube ich. Ich glaube, ich habe das schon vor einiger Zeit gemacht, kann mich aber nicht an die Details erinnern.

Hoffentlich können Sie meine Hinweise verwenden, um ein paar Lösungspfade dazu zu googeln. Viel Glück.

Thomas Tempelmann
quelle