iOS AVCaptureVideoDataOutputSampleBufferDelegate 录制视频

应用场景:

使用AVFoundation提供的API, 我们可以从 AVCaptureVideoDataOutputSampleBufferDelegate 代理中

实时的获取摄像头的视频流,通过解析数据流,可以进行实时的业务处理;同时我们也可以利用这些数据,实时的写入本地保存成

mov到本地文件;

问题一:实时视频流输出及文件保存为什么不同时用 AVCaptureVideoDataOutput 和 AVCaptureMovieFileOutput?

答案是否定的,目前不支持 同时 AVCaptureVideoDataOutput和AVCaptureMovieFileOutput

以下引用stackoverflow上的解答

I have contacted an engineer at Apple's support and he told me that simultaneous AVCaptureVideoDataOutput + AVCaptureMovieFileOutput use is not supported. I don't know if they will support it in the future, but he used the word "not supported at this time".

解决方式:

既然不能同时使用 AVCaptureVideoDataOutput 和 AVCaptureMovieFileOutput,那我们只有使用 AVCaptureVideoDataOutput,在代理buffer里面,使用

AVAssetWriter 去做视频音频编码写入本地文件了;

怎么使用,这里要感谢万能的苹果文档了,下载苹果官方demo  RosyWriter

这个工程,详细的展示了如何对数据流处理,以及视频图像的渲染;

要是只是需要把视频流保存到本地文件,只需查看

MovieRecorder.h MovieRecorder.m 这两个核心文件即可;当然这两个是可以直接拿来用的;

/*
File: MovieRecorder.h
Abstract: Real-time movie recorder which is totally non-blocking
Version: 2.1 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software. In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. Copyright (C) 2014 Apple Inc. All Rights Reserved. */ #import <Foundation/Foundation.h> #import <CoreMedia/CMFormatDescription.h>
#import <CoreMedia/CMSampleBuffer.h> @protocol MovieRecorderDelegate; @interface MovieRecorder : NSObject - (instancetype)initWithURL:(NSURL *)URL; // Only one audio and video track each are allowed.
- (void)addVideoTrackWithSourceFormatDescription:(CMFormatDescriptionRef)formatDescription transform:(CGAffineTransform)transform settings:(NSDictionary *)videoSettings; // see AVVideoSettings.h for settings keys/values
- (void)addAudioTrackWithSourceFormatDescription:(CMFormatDescriptionRef)formatDescription settings:(NSDictionary *)audioSettings; // see AVAudioSettings.h for settings keys/values - (void)setDelegate:(id<MovieRecorderDelegate>)delegate callbackQueue:(dispatch_queue_t)delegateCallbackQueue; // delegate is weak referenced - (void)prepareToRecord; // Asynchronous, might take several hundred milliseconds. When finished the delegate's recorderDidFinishPreparing: or recorder:didFailWithError: method will be called. - (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer;
- (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime;
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer; - (void)finishRecording; // Asynchronous, might take several hundred milliseconds. When finished the delegate's recorderDidFinishRecording: or recorder:didFailWithError: method will be called. @end @protocol MovieRecorderDelegate <NSObject>
@required
- (void)movieRecorderDidFinishPreparing:(MovieRecorder *)recorder;
- (void)movieRecorder:(MovieRecorder *)recorder didFailWithError:(NSError *)error;
- (void)movieRecorderDidFinishRecording:(MovieRecorder *)recorder;
@end
/*
File: MovieRecorder.m
Abstract: Real-time movie recorder which is totally non-blocking
Version: 2.1 Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple
Inc. ("Apple") in consideration of your agreement to the following
terms, and your use, installation, modification or redistribution of
this Apple software constitutes acceptance of these terms. If you do
not agree with these terms, please do not use, install, modify or
redistribute this Apple software. In consideration of your agreement to abide by the following terms, and
subject to these terms, Apple grants you a personal, non-exclusive
license, under Apple's copyrights in this original Apple software (the
"Apple Software"), to use, reproduce, modify and redistribute the Apple
Software, with or without modifications, in source and/or binary forms;
provided that if you redistribute the Apple Software in its entirety and
without modifications, you must retain this notice and the following
text and disclaimers in all such redistributions of the Apple Software.
Neither the name, trademarks, service marks or logos of Apple Inc. may
be used to endorse or promote products derived from the Apple Software
without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or
implied, are granted by Apple herein, including but not limited to any
patent rights that may be infringed by your derivative works or by other
works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. Copyright (C) 2014 Apple Inc. All Rights Reserved. */ #import "MovieRecorder.h" #import <AVFoundation/AVAssetWriter.h>
#import <AVFoundation/AVAssetWriterInput.h> #import <AVFoundation/AVMediaFormat.h>
#import <AVFoundation/AVVideoSettings.h>
#import <AVFoundation/AVAudioSettings.h> #include <objc/runtime.h> // for objc_loadWeak() and objc_storeWeak() #define LOG_STATUS_TRANSITIONS 0 typedef NS_ENUM( NSInteger, MovieRecorderStatus ) {
MovieRecorderStatusIdle = ,
MovieRecorderStatusPreparingToRecord,
MovieRecorderStatusRecording,
MovieRecorderStatusFinishingRecordingPart1, // waiting for inflight buffers to be appended
MovieRecorderStatusFinishingRecordingPart2, // calling finish writing on the asset writer
MovieRecorderStatusFinished, // terminal state
MovieRecorderStatusFailed // terminal state
}; // internal state machine @interface MovieRecorder ()
{
MovieRecorderStatus _status; __weak id <MovieRecorderDelegate> _delegate; // __weak doesn't actually do anything under non-ARC
dispatch_queue_t _delegateCallbackQueue; dispatch_queue_t _writingQueue; NSURL *_URL; AVAssetWriter *_assetWriter;
BOOL _haveStartedSession; CMFormatDescriptionRef _audioTrackSourceFormatDescription;
NSDictionary *_audioTrackSettings;
AVAssetWriterInput *_audioInput; CMFormatDescriptionRef _videoTrackSourceFormatDescription;
CGAffineTransform _videoTrackTransform;
NSDictionary *_videoTrackSettings;
AVAssetWriterInput *_videoInput;
}
@end @implementation MovieRecorder #pragma mark -
#pragma mark API - (instancetype)initWithURL:(NSURL *)URL
{
if ( ! URL ) {
[self release];
return nil;
} self = [super init];
if ( self ) {
_writingQueue = dispatch_queue_create( "com.apple.sample.movierecorder.writing", DISPATCH_QUEUE_SERIAL );
_videoTrackTransform = CGAffineTransformIdentity;
_URL = [URL retain];
}
return self;
} - (void)addVideoTrackWithSourceFormatDescription:(CMFormatDescriptionRef)formatDescription transform:(CGAffineTransform)transform settings:(NSDictionary *)videoSettings
{
if ( formatDescription == NULL ) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"NULL format description" userInfo:nil];
return;
} @synchronized( self )
{
if ( _status != MovieRecorderStatusIdle ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Cannot add tracks while not idle" userInfo:nil];
return;
} if ( _videoTrackSourceFormatDescription ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Cannot add more than one video track" userInfo:nil];
return;
} _videoTrackSourceFormatDescription = (CMFormatDescriptionRef)CFRetain( formatDescription );
_videoTrackTransform = transform;
_videoTrackSettings = [videoSettings copy];
}
} - (void)addAudioTrackWithSourceFormatDescription:(CMFormatDescriptionRef)formatDescription settings:(NSDictionary *)audioSettings
{
if ( formatDescription == NULL ) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"NULL format description" userInfo:nil];
return;
} @synchronized( self )
{
if ( _status != MovieRecorderStatusIdle ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Cannot add tracks while not idle" userInfo:nil];
return;
} if ( _audioTrackSourceFormatDescription ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Cannot add more than one audio track" userInfo:nil];
return;
} _audioTrackSourceFormatDescription = (CMFormatDescriptionRef)CFRetain( formatDescription );
_audioTrackSettings = [audioSettings copy];
}
} - (id<MovieRecorderDelegate>)delegate
{
id <MovieRecorderDelegate> delegate = nil;
@synchronized( self ) {
delegate = objc_loadWeak( &_delegate ); // unnecessary under ARC, just assign to delegate directly
}
return delegate;
} - (void)setDelegate:(id<MovieRecorderDelegate>)delegate callbackQueue:(dispatch_queue_t)delegateCallbackQueue; // delegate is weak referenced
{
if ( delegate && ( delegateCallbackQueue == NULL ) ) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Caller must provide a delegateCallbackQueue" userInfo:nil];
} @synchronized( self )
{
objc_storeWeak( &_delegate, delegate ); // unnecessary under ARC, just assign to _delegate directly
if ( delegateCallbackQueue != _delegateCallbackQueue ) {
[_delegateCallbackQueue release];
_delegateCallbackQueue = [delegateCallbackQueue retain];
}
}
} - (void)prepareToRecord
{
@synchronized( self )
{
if ( _status != MovieRecorderStatusIdle ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Already prepared, cannot prepare again" userInfo:nil];
return;
} [self transitionToStatus:MovieRecorderStatusPreparingToRecord error:nil];
} dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, ), ^{ @autoreleasepool
{
NSError *error = nil;
// AVAssetWriter will not write over an existing file.
[[NSFileManager defaultManager] removeItemAtURL:_URL error:NULL]; _assetWriter = [[AVAssetWriter alloc] initWithURL:_URL fileType:AVFileTypeQuickTimeMovie error:&error]; // Create and add inputs
if ( ! error && _videoTrackSourceFormatDescription ) {
[self setupAssetWriterVideoInputWithSourceFormatDescription:_videoTrackSourceFormatDescription transform:_videoTrackTransform settings:_videoTrackSettings error:&error];
} if ( ! error && _audioTrackSourceFormatDescription ) {
[self setupAssetWriterAudioInputWithSourceFormatDescription:_audioTrackSourceFormatDescription settings:_audioTrackSettings error:&error];
} if ( ! error ) {
BOOL success = [_assetWriter startWriting];
if ( ! success ) {
error = _assetWriter.error;
}
} @synchronized( self )
{
if ( error ) {
[self transitionToStatus:MovieRecorderStatusFailed error:error];
}
else {
[self transitionToStatus:MovieRecorderStatusRecording error:nil];
}
}
}
} );
} - (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];
} - (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime
{
CMSampleBufferRef sampleBuffer = NULL; CMSampleTimingInfo timingInfo = {,};
timingInfo.duration = kCMTimeInvalid;
timingInfo.decodeTimeStamp = kCMTimeInvalid;
timingInfo.presentationTimeStamp = presentationTime; OSStatus err = CMSampleBufferCreateForImageBuffer( kCFAllocatorDefault, pixelBuffer, true, NULL, NULL, _videoTrackSourceFormatDescription, &timingInfo, &sampleBuffer );
if ( sampleBuffer ) {
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];
CFRelease( sampleBuffer );
}
else {
NSString *exceptionReason = [NSString stringWithFormat:@"sample buffer create failed (%i)", (int)err];
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:exceptionReason userInfo:nil];
return;
}
} - (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio];
} - (void)finishRecording
{
@synchronized( self )
{
BOOL shouldFinishRecording = NO;
switch ( _status )
{
case MovieRecorderStatusIdle:
case MovieRecorderStatusPreparingToRecord:
case MovieRecorderStatusFinishingRecordingPart1:
case MovieRecorderStatusFinishingRecordingPart2:
case MovieRecorderStatusFinished:
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Not recording" userInfo:nil];
break;
case MovieRecorderStatusFailed:
// From the client's perspective the movie recorder can asynchronously transition to an error state as the result of an append.
// Because of this we are lenient when finishRecording is called and we are in an error state.
NSLog( @"Recording has failed, nothing to do" );
break;
case MovieRecorderStatusRecording:
shouldFinishRecording = YES;
break;
} if ( shouldFinishRecording ) {
[self transitionToStatus:MovieRecorderStatusFinishingRecordingPart1 error:nil];
}
else {
return;
}
} dispatch_async( _writingQueue, ^{ @autoreleasepool
{
@synchronized( self )
{
// We may have transitioned to an error state as we appended inflight buffers. In that case there is nothing to do now.
if ( _status != MovieRecorderStatusFinishingRecordingPart1 ) {
return;
} // It is not safe to call -[AVAssetWriter finishWriting*] concurrently with -[AVAssetWriterInput appendSampleBuffer:]
// We transition to MovieRecorderStatusFinishingRecordingPart2 while on _writingQueue, which guarantees that no more buffers will be appended.
[self transitionToStatus:MovieRecorderStatusFinishingRecordingPart2 error:nil];
} [_assetWriter finishWritingWithCompletionHandler:^{
@synchronized( self )
{
NSError *error = _assetWriter.error;
if ( error ) {
[self transitionToStatus:MovieRecorderStatusFailed error:error];
}
else {
[self transitionToStatus:MovieRecorderStatusFinished error:nil];
}
}
}];
}
} );
} - (void)dealloc
{
objc_storeWeak( &_delegate, nil ); // unregister _delegate as a weak reference [_delegateCallbackQueue release]; [_writingQueue release]; [self teardownAssetWriterAndInputs]; if ( _audioTrackSourceFormatDescription ) {
CFRelease( _audioTrackSourceFormatDescription );
}
[_audioTrackSettings release]; if ( _videoTrackSourceFormatDescription ) {
CFRelease( _videoTrackSourceFormatDescription );
}
[_videoTrackSettings release]; [_URL release]; [super dealloc];
} #pragma mark -
#pragma mark Internal - (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer ofMediaType:(NSString *)mediaType
{
if ( sampleBuffer == NULL ) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"NULL sample buffer" userInfo:nil];
return;
} @synchronized( self ) {
if ( _status < MovieRecorderStatusRecording ) {
@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"Not ready to record yet" userInfo:nil];
return;
}
} CFRetain( sampleBuffer );
dispatch_async( _writingQueue, ^{ @autoreleasepool
{
@synchronized( self )
{
// From the client's perspective the movie recorder can asynchronously transition to an error state as the result of an append.
// Because of this we are lenient when samples are appended and we are no longer recording.
// Instead of throwing an exception we just release the sample buffers and return.
if ( _status > MovieRecorderStatusFinishingRecordingPart1 ) {
CFRelease( sampleBuffer );
return;
}
} if ( ! _haveStartedSession ) {
[_assetWriter startSessionAtSourceTime:CMSampleBufferGetPresentationTimeStamp(sampleBuffer)];
_haveStartedSession = YES;
} AVAssetWriterInput *input = ( mediaType == AVMediaTypeVideo ) ? _videoInput : _audioInput; if ( input.readyForMoreMediaData )
{
BOOL success = [input appendSampleBuffer:sampleBuffer];
if ( ! success ) {
NSError *error = _assetWriter.error;
@synchronized( self ) {
[self transitionToStatus:MovieRecorderStatusFailed error:error];
}
}
}
else
{
NSLog( @"%@ input not ready for more media data, dropping buffer", mediaType );
}
CFRelease( sampleBuffer );
}
} );
} // call under @synchonized( self )
- (void)transitionToStatus:(MovieRecorderStatus)newStatus error:(NSError *)error
{
BOOL shouldNotifyDelegate = NO; #if LOG_STATUS_TRANSITIONS
NSLog( @"MovieRecorder state transition: %@->%@", [self stringForStatus:_status], [self stringForStatus:newStatus] );
#endif if ( newStatus != _status )
{
// terminal states
if ( ( newStatus == MovieRecorderStatusFinished ) || ( newStatus == MovieRecorderStatusFailed ) )
{
shouldNotifyDelegate = YES;
// make sure there are no more sample buffers in flight before we tear down the asset writer and inputs dispatch_async( _writingQueue, ^{
[self teardownAssetWriterAndInputs];
if ( newStatus == MovieRecorderStatusFailed ) {
[[NSFileManager defaultManager] removeItemAtURL:_URL error:NULL];
}
} ); #if LOG_STATUS_TRANSITIONS
if ( error ) {
NSLog( @"MovieRecorder error: %@, code: %i", error, (int)error.code );
}
#endif
}
else if ( newStatus == MovieRecorderStatusRecording )
{
shouldNotifyDelegate = YES;
} _status = newStatus;
} if ( shouldNotifyDelegate && self.delegate )
{
dispatch_async( _delegateCallbackQueue, ^{ @autoreleasepool
{
switch ( newStatus )
{
case MovieRecorderStatusRecording:
[self.delegate movieRecorderDidFinishPreparing:self];
break;
case MovieRecorderStatusFinished:
[self.delegate movieRecorderDidFinishRecording:self];
break;
case MovieRecorderStatusFailed:
[self.delegate movieRecorder:self didFailWithError:error];
break;
default:
break;
}
}
} );
}
} #if LOG_STATUS_TRANSITIONS - (NSString *)stringForStatus:(MovieRecorderStatus)status
{
NSString *statusString = nil; switch ( status )
{
case MovieRecorderStatusIdle:
statusString = @"Idle";
break;
case MovieRecorderStatusPreparingToRecord:
statusString = @"PreparingToRecord";
break;
case MovieRecorderStatusRecording:
statusString = @"Recording";
break;
case MovieRecorderStatusFinishingRecordingPart1:
statusString = @"FinishingRecordingPart1";
break;
case MovieRecorderStatusFinishingRecordingPart2:
statusString = @"FinishingRecordingPart2";
break;
case MovieRecorderStatusFinished:
statusString = @"Finished";
break;
case MovieRecorderStatusFailed:
statusString = @"Failed";
break;
default:
statusString = @"Unknown";
break;
}
return statusString; } #endif // LOG_STATUS_TRANSITIONS - (BOOL)setupAssetWriterAudioInputWithSourceFormatDescription:(CMFormatDescriptionRef)audioFormatDescription settings:(NSDictionary *)audioSettings error:(NSError **)errorOut
{
if ( ! audioSettings ) {
NSLog( @"No audio settings provided, using default settings" );
audioSettings = @{ AVFormatIDKey : @(kAudioFormatMPEG4AAC) };
} if ( [_assetWriter canApplyOutputSettings:audioSettings forMediaType:AVMediaTypeAudio] )
{
_audioInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:audioSettings sourceFormatHint:audioFormatDescription];
_audioInput.expectsMediaDataInRealTime = YES; if ( [_assetWriter canAddInput:_audioInput] )
{
[_assetWriter addInput:_audioInput];
}
else
{
if ( errorOut ) {
*errorOut = [[self class] cannotSetupInputError];
}
return NO;
}
}
else
{
if ( errorOut ) {
*errorOut = [[self class] cannotSetupInputError];
}
return NO;
} return YES;
} - (BOOL)setupAssetWriterVideoInputWithSourceFormatDescription:(CMFormatDescriptionRef)videoFormatDescription transform:(CGAffineTransform)transform settings:(NSDictionary *)videoSettings error:(NSError **)errorOut
{
if ( ! videoSettings )
{
float bitsPerPixel;
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions( videoFormatDescription );
int numPixels = dimensions.width * dimensions.height;
int bitsPerSecond; NSLog( @"No video settings provided, using default settings" ); // Assume that lower-than-SD resolutions are intended for streaming, and use a lower bitrate
if ( numPixels < ( * ) ) {
bitsPerPixel = 4.05; // This bitrate approximately matches the quality produced by AVCaptureSessionPresetMedium or Low.
}
else {
bitsPerPixel = 10.1; // This bitrate approximately matches the quality produced by AVCaptureSessionPresetHigh.
} bitsPerSecond = numPixels * bitsPerPixel; NSDictionary *compressionProperties = @{ AVVideoAverageBitRateKey : @(bitsPerSecond),
AVVideoExpectedSourceFrameRateKey : @(),
AVVideoMaxKeyFrameIntervalKey : @() }; videoSettings = @{ AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : @(dimensions.width),
AVVideoHeightKey : @(dimensions.height),
AVVideoCompressionPropertiesKey : compressionProperties };
} if ( [_assetWriter canApplyOutputSettings:videoSettings forMediaType:AVMediaTypeVideo] )
{
_videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoSettings sourceFormatHint:videoFormatDescription];
_videoInput.expectsMediaDataInRealTime = YES;
_videoInput.transform = transform; if ( [_assetWriter canAddInput:_videoInput] )
{
[_assetWriter addInput:_videoInput];
}
else
{
if ( errorOut ) {
*errorOut = [[self class] cannotSetupInputError];
}
return NO;
}
}
else
{
if ( errorOut ) {
*errorOut = [[self class] cannotSetupInputError];
}
return NO;
} return YES;
} + (NSError *)cannotSetupInputError
{
NSString *localizedDescription = NSLocalizedString( @"Recording cannot be started", nil );
NSString *localizedFailureReason = NSLocalizedString( @"Cannot setup asset writer input.", nil );
NSDictionary *errorDict = @{ NSLocalizedDescriptionKey : localizedDescription,
NSLocalizedFailureReasonErrorKey : localizedFailureReason };
return [NSError errorWithDomain:@"com.apple.dts.samplecode" code: userInfo:errorDict];
} - (void)teardownAssetWriterAndInputs
{
[_videoInput release];
_videoInput = nil;
[_audioInput release];
_audioInput = nil;
[_assetWriter release];
_assetWriter = nil;
} @end

最后貼上一个我使用 MovieRecorder 的 DEMO 工程;

https://github.com/cocoajin/TDDDemo/tree/master/VideoCAMPTest

iOS AVCaptureVideoDataOutputSampleBufferDelegate 录制视频的更多相关文章

  1. iOS 三种录制视频方式

    随着每一代 iPhone 处理能力和相机硬件配置的提高,使用它来捕获视频也变得更加有意思.它们小巧,轻便,低调,而且与专业摄像机之间的差距已经变得非常小,小到在某些情况下,iPhone 可以真正替代它 ...

  2. 根据分析查看相关知识点分析iOS 三种录制视频方式

    这篇文章讨论了关于如何配置视频捕获管线 (pipeline) 和最大限度地利用硬件性能的一些不同选择. 这里有个使用了不同管线的样例 app,可以在 GitHub 查看. 第一种:UIImagePic ...

  3. iOS录制视频

    随着每一代 iPhone 处理能力和相机硬件配置的提高,使用它来捕获视频也变得更加有意思.它们小巧,轻便,低调,而且与专业摄像机之间的差距已经变得非常小,小到在某些情况下,iPhone 可以真正替代它 ...

  4. iOS 录制视频MOV格式转MP4

    使用UIImagePickerController系统控制器录制视频时,默认生成的格式是MOV,如果要转成MP4格式的,我们需要使用AVAssetExportSession; 支持转换的视频质量:低, ...

  5. iOS音频与视频的开发(二)- 使用AVAudioRecorder进行录制音频

    1.使用AVAudioRecorder录制视频 AVAudioRecorder与AVAudioPlayer类似,它们都属于AVFoundation的类.AVAudioRecorder的功能类似于一个录 ...

  6. iOS音频AAC视频H264编码 推流最佳方案

    iOS音频AAC视频H264编码 推流最佳方案 项目都是个人的调研与实验,可能很多不好或者不对的地方请多包涵. 1    功能概况 *  实现音视频的数据的采集 *  实现音视频数据的编码,视频编码成 ...

  7. 【Android】 Android实现录音、播音、录制视频功能

    智能手机操作系统IOS与Android平分天下(PS:WP与其他的直接无视了),而Android的免费招来了一大堆厂商分分向Android示好,故Android可能会有“较好”的前景. Android ...

  8. IOS 上架要求视频及屏幕截屏

    客户提供上架的资料 1.IOS 上架要求视频演示,录制一段视频,上传到优酷,需要url连接. 2.手机截屏,每个尺寸5张.5s/6/6p *5=15张.截屏图片分辨率. iPhone4s手机 3.5I ...

  9. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

随机推荐

  1. linux 内核手动编译

    手动编译内核 编译时后应安装的支持yum install perlyum install bcyum insatll gcc-c++ .uname -r 先查看内核版本 .yum groupinsta ...

  2. eclipse 智能提示

    eclipse 智能提示 1.显示行号 2.android 的xml提示 文本框的内容为: <=:.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTU ...

  3. centos6.6下编译安装mysql5.6之后启动失败:Starting MySQL... ERROR! The server quit without updating PID file (/var/lib/mysql/localhost.localdomain.pid).

    今天在编译安装mysql5.6时候出现Starting MySQL... ERROR! The server quit without updating PID file (/var/lib/mysq ...

  4. java导出cvs文件

    package testcvs; import java.io.BufferedWriter;import java.io.File;import java.io.FileOutputStream;i ...

  5. BZOJ 2566 xmastree(树分治+multiset)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2566 题意:一棵有边权的树.结点有颜色.每次修改一个点的颜色.求每次修改后所有同色 ...

  6. BZOJ 1042 硬币购物(完全背包+DP)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1042 题意:给出四种面值的硬币c1,c2,c3,c4.n个询问.每次询问用d1.d2.d ...

  7. git push 403

    1. 在github上新建一个空项目. 2. git clone 到本地仓库. 3. git add [一些文件]. 4. git commit -m "first commit" ...

  8. SQL函数学习(十九):CAST()函数和CONVERT()函数

    19.CAST()函数和CONVERT()函数 CAST()函数可以将某种数据类型的表达式转化为另一种数据类型 CONVERT()函数 也 可以将指定的数据类型转换为另一种数据类型 19.1 CAST ...

  9. Java编译环境的搭建(eclipse)

    每用一种语言开发,要搭建其编译和开发环境,我们废话不说,立刻来看看Java开发环境的搭建. 1.安装JDK和JRE Windows环境下: a.去Oracle官网下载对应版本的JDK安装包,http: ...

  10. SLAM reference

    Technical website: OpenSlam: http://openslam.org/ MRPT: http://www.mrpt.org/ Monocular SLAM: https:/ ...