AVFoundation captureStillImageAsynchronouslyFromConnectionを使用して、ライブプレビュー中にカメラから画像をキャプチャしようとしています。これまでのところ、プログラムは期待どおりに機能しています。しかし、どうすればシャッター音をミュートできますか?
AVFoundation captureStillImageAsynchronouslyFromConnectionを使用して、ライブプレビュー中にカメラから画像をキャプチャしようとしています。これまでのところ、プログラムは期待どおりに機能しています。しかし、どうすればシャッター音をミュートできますか?
回答:
このコードを1回使用して、iOSのデフォルトのシャッターサウンドをキャプチャしました(サウンドファイル名のリストは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];
次に、サードパーティのアプリを使用photoShutter.caf
してDocumentsディレクトリ(DiskAid for Mac)から抽出しました。photoShutter.caf
Audacityオーディオエディターで次のステップを開き、反転効果を適用しました。高ズームでは次のようになります。
次に、このサウンドをとして保存し、photoShutter2.caf
直前にこのサウンドを再生してみました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:
...
そして、これは本当にうまくいきます!シャッター音が聞こえないたびに、テストを数回実行します:)
次のリンクから、iPhone 5S iOS 7.1.1でキャプチャしたすでに反転したサウンドを取得できます:https : //www.dropbox.com/s/1echsi6ivbb85bv/photoShutter2.caf
AVCapturePhotoOutput.capturePhoto
次のコードのような画像をキャプチャするためにメソッドを呼び出すとき。
photoOutput.capturePhoto(with: self.capturePhotoSettings, delegate: self)
AVCapturePhotoCaptureDelegateメソッドが呼び出されます。そして、システムはwillCapturePhotoFor
呼び出された後にシャッター音を再生しようとします。そのため、willCapturePhotoFor
メソッドでシステムサウンドを破棄できます。
extension PhotoCaptureService: AVCapturePhotoCaptureDelegate {
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) {
// dispose system shutter sound
AudioServicesDisposeSystemSoundID(1108)
}
}
AudioServicesDisposeSystemSoundID(1108)
が呼び出されたときにのみ発生するメモリリークです。これに対する解決策はありますか?:)
方法1:これが機能するかどうかは不明ですが、キャプチャイベントを送信する直前に空のオーディオファイルを再生してみてください。
クリップを再生するには、Audio Toolbox
フレームワークを追加し、写真を撮る直前に次の#include <AudioToolbox/AudioToolbox.h>
ようなオーディオファイルを再生します。
NSString *path = [[NSBundle mainBundle] pathForResource:@"blank" ofType:@"wav"];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
AudioServicesPlaySystemSound(soundID);
必要な場合は、空のオーディオファイルを次に示します。 https://d1sz9tkli0lfjq.cloudfront.net/items/0Y3Z0A1j1H2r1c0z3n3t/blank.wav
________________________________________________________________________________________________________________________________________
方法2:これが機能しない場合の代替策もあります。解像度を高くする必要がない限り、ビデオストリームからフレームを取得できるため、画像のサウンドを完全に回避できます。
________________________________________________________________________________________________________________________________________
方法3:これを行う別の方法は、アプリケーションの「スクリーンショット」を撮ることです。このようにしてください:
UIGraphicsBeginImageContext(self.window.bounds.size);
[self.window.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData * data = UIImagePNGRepresentation(image);
[data writeToFile:@"foo.png" atomically:YES];
これで画面全体にビデオストリームのプレビューを表示して、スクリーンショットがきれいになるようにしたい場合:
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];
このコードをsnapStillImage関数で使用することでこれを機能させることができました。iOS8.3 iPhone 5では完全に機能します。これを使用してもAppleがアプリを拒否しないことを確認しました(拒否しませんでした)私の)
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];
アプリが戻ってきたら、いいことを忘れずにミュートを解除してください!
私は日本に住んでいるので、安全のため写真を撮るときは音声をミュートできません。ただし、ビデオでは、オーディオはオフになります。理由がわかりません。
シャッター音なしで写真を撮る唯一の方法は、AVCaptureVideoDataOutputまたはAVCaptureMovieFileOutputを使用することです。静止画像を分析するには、AVCaptureVideoDataOutputが唯一の方法です。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);
私の3GSでは、CMTimeMake(1、1)を設定すると非常に重くなります。// 1秒あたり1フレーム。
WWDC 2010のサンプルコードFindMyiConeで、次のコードを見つけました、
[output setAlwaysDiscardsLateVideoFrames:YES];
このAPIを使用すると、タイミングは許可されませんが、APIは順次呼び出されます。私はこれが最善の解決策です。
ビデオストリームからフレームを取得して、(フル解像度ではない)画像をキャプチャすることもできます。
ここでは、短い間隔で画像をキャプチャするために使用されます。
- (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;
}
別の種類の回答については、この投稿を参照してください。画像バッファーから画像をキャプチャします。 ビデオプレビュー中の画面キャプチャが失敗する
このような場合の一般的なトリックは、フレームワークがこのイベントの特定のメソッドを呼び出すかどうかを確認し、そのメソッドを一時的に上書きして、その効果を無効にすることです。
申し訳ありませんが、このケースで機能するかどうかをすぐに説明するのに十分なハックではありません。フレームワーク実行可能ファイルで「nm」コマンドを試して、適切な名前の名前付き関数があるかどうかを確認するか、シミュレーターでgdbを使用して、どこにあるかを追跡できます。
何を上書きするかがわかったら、関数の検索をリダイレクトするために使用できる低レベルのObjCディスパッチ関数があると思います。少し前にやったと思いますが、詳細は思い出せません。
うまくいけば、私のヒントを使用して、これへのいくつかのソリューションパスをググることができます。幸運を。