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?
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?
Odpowiedzi:
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.caf
z katalogu Dokumenty (DiskAid dla komputerów Mac). Następny krok otworzyłem photoShutter.caf
w edytorze audio Audacity i zastosowałem efekt inwersji, wygląda to tak przy dużym powiększeniu:
Następnie zapisałem ten dźwięk jako photoShutter2.caf
i 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
Kiedy wywołujesz AVCapturePhotoOutput.capturePhoto
metodę 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 willCapturePhotoFor
wywołaniu. Więc możesz pozbyć się dźwięku systemowego w willCapturePhotoFor
sposób.
extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
// dispose system shutter sound
AudioServicesDisposeSystemSoundID(1108)
}
}
AudioServicesDisposeSystemSoundID(1108)
zostanie wywołany. Czy masz na to jakieś rozwiązanie? :)
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 Toolbox
ramę #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];
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!
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.
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;
}
Zobacz ten post, aby uzyskać inną odpowiedź: przechwyć obraz z bufora obrazu. Przechwytywanie ekranu podczas podglądu wideo kończy się niepowodzeniem
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.
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.