Estou tentando capturar uma imagem durante uma visualização ao vivo da câmera, por AVFoundation captureStillImageAsynchronouslyFromConnection . Até agora, o programa funciona conforme o esperado. No entanto, como posso silenciar o som do obturador?
Estou tentando capturar uma imagem durante uma visualização ao vivo da câmera, por AVFoundation captureStillImageAsynchronouslyFromConnection . Até agora, o programa funciona conforme o esperado. No entanto, como posso silenciar o som do obturador?
Respostas:
Usei este código uma vez para capturar o som do obturador padrão do iOS (aqui está uma lista de nomes de arquivos de som 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];
Em seguida, usei um aplicativo de terceiros para extrair photoShutter.caf
do diretório Documentos (DiskAid para Mac). Próxima etapa eu abri photoShutter.caf
no editor de áudio Audacity e apliquei o efeito de inversão, fica assim em zoom alto:
Então salvei este som como photoShutter2.caf
e tentei reproduzi-lo um pouco antes 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 isso realmente funciona! Executo o teste várias vezes, sempre que não ouço nenhum som do obturador :)
Você pode obter o som já invertido, capturado no iPhone 5S iOS 7.1.1 neste link: https://www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf
Quando você chama o AVCapturePhotoOutput.capturePhoto
método para capturar uma imagem como o código abaixo.
photoOutput.capturePhoto(with: self.capturePhotoSettings, delegate: self)
Os métodos AVCapturePhotoCaptureDelegate serão chamados. E o sistema tenta reproduzir o som do obturador depois de willCapturePhotoFor
invocado. Assim, você pode descartar o som do sistema no willCapturePhotoFor
método.
extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
// dispose system shutter sound
AudioServicesDisposeSystemSoundID(1108)
}
}
AudioServicesDisposeSystemSoundID(1108)
é chamado. Você tem alguma solução para isso? :)
Método 1: não tenho certeza se isso funcionará, mas tente reproduzir um arquivo de áudio em branco antes de enviar o evento de captura.
Para reproduzir um clipe, adicione a Audio Toolbox
estrutura #include <AudioToolbox/AudioToolbox.h>
e reproduza o arquivo de áudio assim imediatamente antes de tirar a foto:
NSString *path = [[NSBundle mainBundle] pathForResource:@"blank" ofType:@"wav"];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
AudioServicesPlaySystemSound(soundID);
Aqui está um arquivo de áudio em branco, se necessário. https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav
________________________________________________________________________________________________________________________________________________
Método 2: também há uma alternativa se isso não funcionar. Contanto que você não precise ter uma boa resolução, você pode capturar um quadro do stream de vídeo , evitando assim o som da imagem.
________________________________________________________________________________________________________________________________________________
Método 3: Outra maneira de fazer isso seria fazer uma "captura de tela" do seu aplicativo. Faça desta forma:
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 você deseja que preencha toda a tela com uma visualização do stream de vídeo para que sua captura de tela tenha uma boa aparência:
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];
Consegui fazer isso funcionar usando este código na função snapStillImage e funciona perfeitamente para mim no iOS 8.3 iPhone 5. Também confirmei que a Apple não rejeitará seu aplicativo se você usar isso (eles não rejeitaram meu)
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];
Lembre-se de ser simpático e ativar o som quando o aplicativo retornar!
Eu moro no Japão, então não posso silenciar o áudio quando tiramos fotos por motivos de segurança. No vídeo, entretanto, o áudio é desativado. Não entendo por quê.
A única maneira de tirar uma foto sem som do obturador é usando AVCaptureVideoDataOutput ou AVCaptureMovieFileOutput. Para analisar imagens estáticas, AVCaptureVideoDataOutput é a única maneira. No código de amostra 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);
No meu 3GS é muito pesado quando eu defino CMTimeMake (1, 1); // Um quadro por segundo.
No código de amostra do WWDC 2010, FindMyiCone, encontrei o seguinte código,
[output setAlwaysDiscardsLateVideoFrames:YES];
Quando esta API é usada, o tempo não é concedido, mas a API é chamada sequencialmente. Eu este são as melhores soluções.
Você também pode obter um quadro de um fluxo de vídeo para capturar uma imagem (sem resolução total).
É usado aqui para capturar imagens em intervalos curtos:
- (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;
}
Veja esta postagem para um tipo diferente de resposta: Capture a imagem do buffer de imagem. A captura de tela durante a visualização do vídeo falha
Um truque comum nesses casos é descobrir se a estrutura invoca um determinado método para esse evento e, em seguida, sobrescrever esse método temporariamente, anulando assim seu efeito.
Sinto muito, mas não sou um hack bom o suficiente para lhe dizer imediatamente se isso funciona neste caso. Você poderia tentar o comando "nm" nos executáveis do framework para ver se há uma função nomeada que tenha um nome adequado ou usar gdb com o Simulador para rastrear para onde ela vai.
Depois de saber o que substituir, existem aquelas funções de despacho ObjC de baixo nível que você pode usar para redirecionar a pesquisa de funções, eu acredito. Acho que já fiz isso há algum tempo, mas não consigo me lembrar dos detalhes.
Felizmente, você pode usar minhas dicas para pesquisar alguns caminhos de soluções para isso. Boa sorte.