AVFoundation, comment désactiver le son de l'obturateur lors de la captureStillImageAsynchronouslyFromConnection?

89

J'essaie de capturer une image lors d'un aperçu en direct de la caméra, par AVFoundation captureStillImageAsynchronouslyFromConnection . Jusqu'à présent, le programme fonctionne comme prévu. Cependant, comment puis-je couper le son de l'obturateur?

ohho
la source
5
Au Japon, par exemple, vous ne pouvez pas faire cela. Là, le son de l'obturateur est toujours émis même si vous avez désactivé le téléphone. (Certaines règles étranges pour empêcher la violation de la vie privée, alias la photographie sous la jupe sont en place au Japon pour autant que je sache) Donc je ne pense pas qu'il y ait un moyen de le faire.
Dunkelstern
3
Ouais, il se peut que votre iPhone ne permette pas les prises de vue en sourdine. Je suis aux États-Unis et mon iPhone n'émet aucun son lorsque je prends une photo avec le téléphone en sourdine; ma copine, d'autre part, fait un bruit (la sienne est de Corée).
donkim
5
Pour être clair, il s'agit d'une solution lorsque le téléphone n'est pas en mode silencieux / vibreur. Dans ce mode, aucun son n'est émis lors de la prise de vue.
New Alexandria
31
@NewAlexandria J'ai aussi entendu au Japon des sons d'obturateur en mode vibration. C'est une exigence de la loi.
k06a
3
Btw AudioServicesPlaySystemSound ne sera pas lu si le périphérique est mis en sourdine. Ainsi, les appareils japonais émettent toujours un son lorsqu'ils sont mis en sourdine, mais pas lorsqu'ils ne le sont pas ...
Filip Radelic

Réponses:

1500

J'ai utilisé ce code une fois pour capturer le son de l'obturateur par défaut iOS (voici la liste des noms de fichiers 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];

Ensuite, j'ai utilisé une application tierce pour extraire photoShutter.cafdu répertoire Documents (DiskAid pour Mac). Étape suivante, j'ai ouvert photoShutter.cafl'éditeur audio Audacity et appliqué l'effet d'inversion, cela ressemble à ceci avec un zoom élevé:

entrez la description de l'image ici

Ensuite, j'ai enregistré ce son photoShutter2.cafet essayé de jouer ce son juste avant 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:
...

Et cela fonctionne vraiment! Je fais des tests plusieurs fois, à chaque fois que je n'entends aucun son d'obturateur :)

Vous pouvez obtenir un son déjà inversé, capturé sur iPhone 5S iOS 7.1.1 à partir de ce lien: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

k06a
la source
104
Très cool. La seule mise en garde est que lorsque le flash est activé, cela entraînera un son de double obturateur car le son système par défaut n'est pas lu lorsque captureStillImageAsynchronouslyFromConnection: est appelé, mais plutôt lorsque l'image est réellement capturée (pendant la séquence de flash). À la place, ajoutez un observateur clé-valeur sur self.stillImageOutput pour que keyPath 'capturingStillImage' détecte le moment où la capture démarre vraiment, puis lancez le son inverse dans la méthode de rappel.
Jeff Mascia
16
C'est ainsi que les avions militaires modernes vainquent le radar. C'est pourquoi vous ne voyez pas la silhouette de la «forme furtive» sur les nouveaux modèles de chasseurs: il existe un ensemble électronique qui gère ce qui est essentiellement un déphasage anti-radar qui diffuse un signal dupliqué «inversé» (déphasé).
L0j1k
5
@ L0j1k: Cela dépend du type de vaisseau furtif dont vous parlez. Le F22 et un autre ne sont pas intéressés à ne pas être détectés mais à être déverrouillables. Le problème avec la technologie de décalage de phase est que vous êtes incroyablement évident de toutes les autres positions, car ils ne verront pas le même décalage.
Guvante
13
C'est ainsi que fonctionnent les écouteurs à
réduction de
4
C'était génial pour iOS7, mais cela ne fonctionne plus sur iOS8, une idée de comment résoudre ce problème sur iOS8?
Ticko
33

Ma solution dans Swift

Lorsque vous appelez la AVCapturePhotoOutput.capturePhotométhode pour capturer une image comme le code ci-dessous.

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

Les méthodes AVCapturePhotoCaptureDelegate seront appelées. Et le système essaie de jouer le son de l'obturateur après son willCapturePhotoForappel. Ainsi, vous pouvez disposer du son système par willCapturePhotoForméthode.

entrez la description de l'image ici

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Voir également

A gagné
la source
3
C'est une belle solution et cela fonctionne parfaitement. Excellente réponse aussi. Merci @kyle
Warpling
1
Cela fonctionne parfaitement. Si vous devez séparer le mode silencieux et le mode normal, utilisez AudioServicesPlaySytemSoundID (1108) à l'envers. Je vous remercie.
MJ Studio
1
Ça marche! Je veux savoir si vous rencontrez des problèmes pour télécharger sur l'AppStore avec une telle méthode?
Liew Jun Tung
2
@LiewJunTung Aucun problème avec cette solution. J'ai déjà téléchargé des applications avec cette solution. Il existe de nombreuses applications qui utilisent ce type de solution. Bon codage.
Gagné
J'ai découvert un problème. Je me demande si vous avez découvert ce problème. C'est essentiellement une fuite de mémoire qui ne se produit que lorsqu'elle AudioServicesDisposeSystemSoundID(1108)est appelée. Avez-vous des solutions pour cela? :)
Liew Jun Tung
21

Méthode 1: Vous ne savez pas si cela fonctionnera, mais essayez de lire un fichier audio vierge juste avant d'envoyer l'événement de capture.

Pour lire un clip, ajoutez le Audio Toolboxcadre #include <AudioToolbox/AudioToolbox.h> et lisez le fichier audio comme celui-ci immédiatement avant de prendre la photo:

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

Voici un fichier audio vierge si vous en avez besoin. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

Méthode 2: Il existe également une alternative si cela ne fonctionne pas. Tant que vous n'avez pas besoin d'une bonne résolution, vous pouvez saisir une image du flux vidéo , évitant ainsi complètement le son de l'image.

________________________________________________________________________________________________________________________________________

Méthode 3: Une autre façon de faire serait de prendre une «capture d'écran» de votre application. Faites-le de cette façon:

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 vous souhaitez que cela remplisse tout l'écran avec un aperçu du flux vidéo afin que votre capture d'écran soit bonne:

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
la source
2
Je ne pense pas que cela fonctionnerait, cela jouerait juste un son "vide" pendant que le bruit de l'obturateur joue. Il est tout à fait possible de jouer 2 sons en même temps. Les 2 autres méthodes semblent réalisables!
Linuxmint
2
Donner un coup de feu. Je ne suis pas convaincu que cela fonctionnera, mais voici l'espoir!
sudo rm -rf
7

J'ai pu faire fonctionner cela en utilisant ce code dans la fonction snapStillImage et cela fonctionne parfaitement pour moi sur iOS 8.3 iPhone 5. J'ai également confirmé qu'Apple ne rejettera pas votre application si vous l'utilisez (ils n'ont pas rejeté mien)

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

N'oubliez pas d'être gentil et de réactiver le son lorsque votre application revient!

frakman1
la source
5

Je vis au Japon, donc je ne peux pas couper le son lorsque nous prenons des photos pour des raisons de sécurité. En vidéo, cependant, l'audio est désactivé. Je ne comprends pas pourquoi.

La seule façon de prendre une photo sans le son de l'obturateur est d'utiliser AVCaptureVideoDataOutput ou AVCaptureMovieFileOutput. Pour analyser l'image fixe AVCaptureVideoDataOutput est le seul moyen. Dans l'exemple de code 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);

Dans mon 3GS, il est très lourd lorsque je règle CMTimeMake (1, 1); // Une image par seconde.

Dans l'exemple de code WWDC 2010, FindMyiCone, j'ai trouvé le code suivant,

[output setAlwaysDiscardsLateVideoFrames:YES];

Lorsque cette API est utilisée, le timing n'est pas accordé, mais l'API est appelée séquentiellement. Je ce que ce sont les meilleures solutions.

Ben
la source
3

Vous pouvez également prendre une image d'un flux vidéo pour capturer une image (pas en pleine résolution).

Il est utilisé ici pour capturer des images à de courts intervalles:

- (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
la source
Pouvez-vous faire fonctionner AVCaptureVideoDataOutput pendant que AVCaptureMovieFileOutput est utilisé? Lorsque j'essaye de les utiliser tous les deux, les méthodes déléguées d'AVCaptureVideoDataOutput ne sont pas appelées.
Paul Cezanne
Désolé, je n'ai pas essayé d'avoir plus d'une sortie en même temps.
Rivera
0

La seule solution possible à laquelle je peux penser serait de couper le son de l'iPhone quand ils appuient sur le bouton "Prendre une photo", puis de le réactiver une seconde plus tard.

Linuxmint
la source
Comment couper le son de l'iPhone par programmation? Merci!
ohho
même si vous le pouviez, Apple n'approuverait pas cela
0

Une astuce courante dans de tels cas est de savoir si le framework appelle une certaine méthode pour cet événement, puis d'écraser temporairement cette méthode, annulant ainsi son effet.

Je suis désolé mais je ne suis pas assez bon pour vous dire tout de suite si cela fonctionne dans ce cas. Vous pouvez essayer la commande "nm" sur les exécutables du framework pour voir s'il existe une fonction nommée qui a un nom convenable, ou utiliser gdb avec le simulateur pour tracer où elle va.

Une fois que vous savez quoi écraser, il y a ces fonctions de distribution ObjC de bas niveau que vous pouvez utiliser pour rediriger la recherche de fonctions, je crois. Je pense que je l'ai fait il y a quelque temps, mais je ne me souviens pas des détails.

J'espère que vous pourrez utiliser mes conseils pour rechercher sur Google quelques solutions à ce problème. Bonne chance.

Thomas Tempelmann
la source