AVFoundation, jak wyłączyć dźwięk migawki podczas przechwytywaniaStillImageAsynchronouslyFromConnection?

90

Próbuję przechwycić obraz podczas podglądu na żywo z kamery, przez AVFoundation captureStillImageAsynchronouslyFromConnection . Na razie program działa zgodnie z oczekiwaniami. Jak jednak mogę wyciszyć dźwięk migawki?

ohho
źródło
5
Na przykład w Japonii nie możesz tego zrobić. Tam dźwięk migawki jest zawsze odtwarzany, nawet po wyciszeniu telefonu. (O ile wiem, w Japonii obowiązują dziwne zasady zapobiegania naruszaniu prywatności, zwane również fotografiami upskirt). Więc nie sądzę, aby można to zrobić.
Dunkelstern
3
Tak, może się zdarzyć, że Twój iPhone nie pozwala na wyciszone zdjęcia z aparatu. Jestem w Stanach Zjednoczonych i mój iPhone nie wydaje dźwięku, kiedy robię zdjęcie przy wyciszonym telefonie; z drugiej strony moja dziewczyna wydaje dźwięk (jej jest z Korei).
donkim
5
Dla jasności jest to rozwiązanie na wypadek, gdy telefon nie jest wyciszony / wibruje. W tym trybie podczas robienia zdjęcia nie słychać dźwięku.
Nowa Aleksandria
31
@NewAlexandria Słyszałem w Japonii dźwięk migawki również w trybie wibracji. To jest wymóg prawny.
k06a
3
Btw AudioServicesPlaySystemSound nie będzie odtwarzany, jeśli urządzenie jest wyciszone. Tak więc japońskie urządzenia nadal będą wydawać dźwięk, gdy są wyciszone, ale nie, gdy nie są wyciszone ...
Filip Radelic

Odpowiedzi:

1500

Użyłem tego kodu raz do przechwycenia domyślnego dźwięku migawki iOS (tutaj jest lista nazw plików dźwiękowych 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];

Następnie użyłem aplikacji innej firmy do wyodrębnienia photoShutter.cafz katalogu Dokumenty (DiskAid dla komputerów Mac). Następny krok otworzyłem photoShutter.cafw edytorze audio Audacity i zastosowałem efekt inwersji, wygląda to tak przy dużym powiększeniu:

wprowadź opis obrazu tutaj

Następnie zapisałem ten dźwięk jako photoShutter2.cafi próbowałem go odtworzyć tuż przed 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:
...

I to naprawdę działa! Kilkakrotnie przeprowadzam test, za każdym razem nie słyszę dźwięku migawki :)

Możesz uzyskać już odwrócony dźwięk, przechwycony na iPhone 5S iOS 7.1.1 z tego linku: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf

k06a
źródło
104
Bardzo fajny. Jedynym zastrzeżeniem jest to, że po włączeniu lampy błyskowej spowoduje to podwójny dźwięk migawki, ponieważ domyślny dźwięk systemowy nie jest odtwarzany po wywołaniu funkcji captureStillImageAsynchronouslyFromConnection:, ale raczej, gdy obraz jest faktycznie przechwytywany (podczas sekwencji flash). Zamiast tego dodaj obserwatora klucz-wartość na self.stillImageOutput dla keyPath 'capturingStillImage', aby wykryć, kiedy przechwytywanie naprawdę się rozpocznie, a następnie odtwórz odwrócony dźwięk w metodzie wywołania zwrotnego.
Jeff Mascia
16
W ten sposób współczesne samoloty wojskowe pokonują radar. To dlatego nie widać sylwetki "stealth shape" na nowych projektach myśliwców: istnieje pakiet elektroniki, który obsługuje to, co w zasadzie jest przesunięciem fazowym pokonującym radar, które nadaje „odwrócony” (przesunięty w fazie) zduplikowany sygnał.
L0j1k
5
@ L0j1k: To zależy od tego, o jakim typie statku stealth mówisz. F22 podobnie nie jest zainteresowany pozostaniem niewykrytym, ale możliwością odblokowania. Problem z technologią przesunięcia fazowego polega na tym, że jesteś niezwykle widoczny z każdej innej pozycji, ponieważ nie zobaczą tego samego przesunięcia.
Guvante
13
Tak działają słuchawki z redukcją szumów, z wyjątkiem tego, że rejestrują dźwięk w czasie rzeczywistym
Daniel Serodio,
4
To było świetne dla iOS7, ale już nie działa na iOS8, jakikolwiek pomysł, jak to rozwiązać na iOS8?
Ticko
36

Moje rozwiązanie w Swift

Kiedy wywołujesz AVCapturePhotoOutput.capturePhotometodę przechwytywania obrazu, takiego jak poniższy kod.

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

Zostaną wywołane metody AVCapturePhotoCaptureDelegate. System próbuje odtworzyć dźwięk migawki po willCapturePhotoForwywołaniu. Więc możesz pozbyć się dźwięku systemowego w willCapturePhotoForsposób.

wprowadź opis obrazu tutaj

extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {

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

Zobacz też

Wygrała
źródło
3
To piękne rozwiązanie i działa doskonale. Świetna odpowiedź też. Dzięki @kyle
Warpling
1
To doskonale działa. Jeśli chcesz oddzielić tryb cichy i tryb normalny, użyj odwrotnie AudioServicesPlaySytemSoundID (1108). Dziękuję Ci.
MJ Studio
1
To działa! Chcę wiedzieć, czy masz problemy z przesyłaniem do AppStore taką metodą?
Liew Jun Tung
2
@LiewJunTung Nie ma problemu z tym rozwiązaniem. Wrzuciłem już aplikacje z tym rozwiązaniem. Istnieje wiele aplikacji, które korzystały z tego rodzaju rozwiązania. Miłego kodowania.
Wygrał
Odkryłem problem. Zastanawiam się, czy odkryłeś ten problem. Zasadniczo jest to wyciek pamięci, który ma miejsce tylko wtedy, gdy AudioServicesDisposeSystemSoundID(1108)zostanie wywołany. Czy masz na to jakieś rozwiązanie? :)
Liew Jun Tung
21

Metoda 1: Nie jestem pewien, czy to zadziała, ale spróbuj odtworzyć pusty plik audio tuż przed wysłaniem zdarzenia przechwytywania.

Aby odtworzyć klip, dodaj Audio Toolboxramę #include <AudioToolbox/AudioToolbox.h> i odtwórz plik audio w ten sposób bezpośrednio przed zrobieniem zdjęcia:

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

Tutaj jest pusty plik audio, jeśli go potrzebujesz. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav

________________________________________________________________________________________________________________________________________

Metoda 2: Istnieje również alternatywa, jeśli to nie zadziała. Dopóki nie potrzebujesz dobrej rozdzielczości, możesz pobrać klatkę ze strumienia wideo , całkowicie unikając w ten sposób dźwięku obrazu.

________________________________________________________________________________________________________________________________________

Metoda 3: Innym sposobem na zrobienie tego byłoby zrobienie „zrzutu ekranu” aplikacji. Zrób to w ten sposób:

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

Jeśli chcesz, aby wypełnił cały ekran podglądem strumienia wideo, aby zrzut ekranu wyglądał dobrze:

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
źródło
2
Nie sądzę, żeby to zadziałało, po prostu odtwarzałby „pusty” dźwięk, podczas gdy słychać dźwięk migawki. Całkiem możliwe jest jednoczesne odtwarzanie 2 dźwięków. Pozostałe 2 metody wydają się działać!
Linuxmint
2
Spróbuj. Nie jestem pewien, czy to zadziała, ale mam nadzieję!
sudo rm -rf
7

Udało mi się to uruchomić, używając tego kodu w funkcji snapStillImage i działa idealnie dla mnie na iOS 8.3 iPhone 5. Potwierdziłem również, że Apple nie odrzuci twojej aplikacji, jeśli jej użyjesz (nie odrzucili kopalnia)

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

Pamiętaj tylko, aby być miłym i wyłączyć wyciszenie, gdy Twoja aplikacja powróci!

frakman1
źródło
5

Mieszkam w Japonii, więc ze względów bezpieczeństwa nie mogę wyciszyć dźwięku podczas robienia zdjęć. Jednak w wideo dźwięk jest wyłączony. Nie rozumiem dlaczego.

Jedynym sposobem na zrobienie zdjęcia bez dźwięku migawki jest użycie AVCaptureVideoDataOutput lub AVCaptureMovieFileOutput. Do analizy nieruchomego obrazu AVCaptureVideoDataOutput jest jedyną drogą. W przykładowym kodzie 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);

W moim 3GS jest bardzo ciężki, kiedy ustawiam CMTimeMake (1, 1); // Jedna klatka na sekundę.

W przykładowym kodzie WWDC 2010 FindMyiCone znalazłem następujący kod,

[output setAlwaysDiscardsLateVideoFrames:YES];

Gdy używany jest ten interfejs API, czas nie jest przyznawany, ale interfejs API jest wywoływany sekwencyjnie. I to są najlepsze rozwiązania.

Ben
źródło
3

Możesz także pobrać klatkę ze strumienia wideo, aby przechwycić obraz (nie w pełnej rozdzielczości).

Służy tutaj do robienia zdjęć w krótkich odstępach czasu:

- (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
źródło
Czy możesz sprawić, aby AVCaptureVideoDataOutput działał, gdy używany jest AVCaptureMovieFileOutput? Kiedy próbuję użyć obu z nich, metody delegata AVCaptureVideoDataOutput nie są wywoływane.
Paul Cezanne
Przepraszam, nie próbowałem mieć więcej niż jednego wyjścia w tym samym czasie.
Rivera
0

Jedynym możliwym rozwiązaniem, jakie przychodzi mi do głowy, jest wyciszenie dźwięku iPhone'a po naciśnięciu przycisku „zrób zdjęcie”, a następnie wyłączenie wyciszenia sekundę później.

Linuxmint
źródło
Jak programowo wyciszyć dźwięk iPhone'a? Dzięki!
ohho
nawet gdybyś mógł, Apple by tego nie zaakceptował
0

Typową sztuczką w takich przypadkach jest sprawdzenie, czy framework wywołuje określoną metodę dla tego zdarzenia, a następnie tymczasowe nadpisanie tej metody, unieważniając w ten sposób jej efekt.

Przepraszam, ale nie jestem na tyle dobrym hackem, aby od razu powiedzieć, czy to zadziała w tym przypadku. Możesz wypróbować polecenie „nm” w plikach wykonywalnych frameworka, aby sprawdzić, czy istnieje nazwana funkcja, która ma odpowiednią nazwę, lub użyć gdb z symulatorem, aby śledzić, dokąd zmierza.

Kiedy już wiesz, co nadpisać, są te funkcje wysyłające ObjC niskiego poziomu, których możesz użyć do przekierowania wyszukiwania funkcji, jak sądzę. Myślę, że zrobiłem to jakiś czas temu, ale nie mogę sobie przypomnieć szczegółów.

Mam nadzieję, że możesz skorzystać z moich wskazówek, aby wygooglować kilka ścieżek do tego. Powodzenia.

Thomas Tempelmann
źródło