@interface WKMovieRecorder : NSObject + (WKMovieRecorder*) sharedRecorder; - (instancetype)initWithMaxDuration:(NSTimeInterval)duration; @end
/** * 录制结束 * * @param info 回调信息 * @param isCancle YES:取消 NO:正常结束 */ typedef void(^FinishRecordingBlock)(NSDictionary *info, WKRecorderFinishedReason finishReason); /** * 焦点改变 */ typedef void(^FocusAreaDidChanged)(); /** * 权限验证 * * @param success 是否成功 */ typedef void(^AuthorizationResult)(BOOL success); @interface WKMovieRecorder : NSObject //回调 @property (nonatomic, copy) FinishRecordingBlock finishBlock;//录制结束回调 @property (nonatomic, copy) FocusAreaDidChanged focusAreaDidChangedBlock; @property (nonatomic, copy) AuthorizationResult authorizationResultBlock; @end
@interface WKMovieRecorder ()
<
AVCaptureVideoDataOutputSampleBufferDelegate,
AVCaptureAudioDataOutputSampleBufferDelegate,
WKMovieWriterDelegate
>
{
AVCaptureSession* _session;
AVCaptureVideoPreviewLayer* _preview;
WKMovieWriter* _writer;
//暂停录制
BOOL _isCapturing;
BOOL _isPaused;
BOOL _discont;
int _currentFile;
CMTime _timeOffset;
CMTime _lastVideo;
CMTime _lastAudio;
NSTimeInterval _maxDuration;
}
// Session management.
@property (nonatomic, strong) dispatch_queue_t sessionQueue;
@property (nonatomic, strong) dispatch_queue_t videoDataOutputQueue;
@property (nonatomic, strong) AVCaptureSession *session;
@property (nonatomic, strong) AVCaptureDevice *captureDevice;
@property (nonatomic, strong) AVCaptureDeviceInput *videoDeviceInput;
@property (nonatomic, strong) AVCaptureStillImageOutput *stillImageOutput;
@property (nonatomic, strong) AVCaptureConnection *videoConnection;
@property (nonatomic, strong) AVCaptureConnection *audioConnection;
@property (nonatomic, strong) NSDictionary *videoCompressionSettings;
@property (nonatomic, strong) NSDictionary *audioCompressionSettings;
@property (nonatomic, strong) AVAssetWriterInputPixelBufferAdaptor *adaptor;
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;
//Utilities
@property (nonatomic, strong) NSMutableArray *frames;//存储录制帧
@property (nonatomic, assign) CaptureAVSetupResult result;
@property (atomic, readwrite) BOOL isCapturing;
@property (atomic, readwrite) BOOL isPaused;
@property (nonatomic, strong) NSTimer *durationTimer;
@property (nonatomic, assign) WKRecorderFinishedReason finishReason;
@end
+ (WKMovieRecorder *)sharedRecorder
{
static WKMovieRecorder *recorder;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
recorder = [[WKMovieRecorder alloc] initWithMaxDuration:CGFLOAT_MAX];
});
return recorder;
}
- (instancetype)initWithMaxDuration:(NSTimeInterval)duration
{
if(self = [self init]){
_maxDuration = duration;
_duration = 0.f;
}
return self;
}
- (instancetype)init
{
self = [super init];
if (self) {
_maxDuration = CGFLOAT_MAX;
_duration = 0.f;
_sessionQueue = dispatch_queue_create("wukong.movieRecorder.queue", DISPATCH_QUEUE_SERIAL );
_videoDataOutputQueue = dispatch_queue_create( "wukong.movieRecorder.video", DISPATCH_QUEUE_SERIAL );
dispatch_set_target_queue( _videoDataOutputQueue, dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0 ) );
}
return self;
}
//权限检查
switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]) {
case AVAuthorizationStatusNotDetermined: {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
if (granted) {
self.result = CaptureAVSetupResultSuccess;
}
}];
break;
}
case AVAuthorizationStatusAuthorized: {
break;
}
default:{
self.result = CaptureAVSetupResultCameraNotAuthorized;
}
}
if ( self.result != CaptureAVSetupResultSuccess) {
if (self.authorizationResultBlock) {
self.authorizationResultBlock(NO);
}
return;
}
AVCaptureDevice *captureDevice = [[self class] deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
_captureDevice = captureDevice;
NSError *error = nil;
_videoDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
if (!_videoDeviceInput) {
NSLog(@"未找到设备");
}
int frameRate;
if ( [NSProcessInfo processInfo].processorCount == 1 )
{
if ([self.session canSetSessionPreset:AVCaptureSessionPresetLow]) {
[self.session setSessionPreset:AVCaptureSessionPresetLow];
}
frameRate = 10;
}else{
if ([self.session canSetSessionPreset:AVCaptureSessionPreset640x480]) {
[self.session setSessionPreset:AVCaptureSessionPreset640x480];
}
frameRate = 30;
}
CMTime frameDuration = CMTimeMake( 1, frameRate );
if ( [_captureDevice lockForConfiguration:&error] ) {
_captureDevice.activeVideoMaxFrameDuration = frameDuration;
_captureDevice.activeVideoMinFrameDuration = frameDuration;
[_captureDevice unlockForConfiguration];
}
else {
NSLog( @"videoDevice lockForConfiguration returned error %@", error );
}
//Video
if ([self.session canAddInput:_videoDeviceInput]) {
[self.session addInput:_videoDeviceInput];
self.videoDeviceInput = _videoDeviceInput;
[self.session removeOutput:_videoDataOutput];
AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
_videoDataOutput = videoOutput;
videoOutput.videoSettings = @{ (id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_32BGRA) };
[videoOutput setSampleBufferDelegate:self queue:_videoDataOutputQueue];
videoOutput.alwaysDiscardsLateVideoFrames = NO;
if ( [_session canAddOutput:videoOutput] ) {
[_session addOutput:videoOutput];
[_captureDevice addObserver:self forKeyPath:@"adjustingFocus" options:NSKeyValueObservingOptionNew context:FocusAreaChangedContext];
_videoConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
if(_videoConnection.isVideoStabilizationSupported){
_videoConnection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
}
UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
AVCaptureVideoOrientation initialVideoOrientation = AVCaptureVideoOrientationPortrait;
if ( statusBarOrientation != UIInterfaceOrientationUnknown ) {
initialVideoOrientation = (AVCaptureVideoOrientation)statusBarOrientation;
}
_videoConnection.videoOrientation = initialVideoOrientation;
}
}
else{
NSLog(@"无法添加视频输入到会话");
}
//audio
AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
if ( ! audioDeviceInput ) {
NSLog( @"Could not create audio device input: %@", error );
}
if ( [self.session canAddInput:audioDeviceInput] ) {
[self.session addInput:audioDeviceInput];
}
else {
NSLog( @"Could not add audio device input to the session" );
}
AVCaptureAudioDataOutput *audioOut = [[AVCaptureAudioDataOutput alloc] init];
// Put audio on its own queue to ensure that our video processing doesn't cause us to drop audio
dispatch_queue_t audioCaptureQueue = dispatch_queue_create( "wukong.movieRecorder.audio", DISPATCH_QUEUE_SERIAL );
[audioOut setSampleBufferDelegate:self queue:audioCaptureQueue];
if ( [self.session canAddOutput:audioOut] ) {
[self.session addOutput:audioOut];
}
_audioConnection = [audioOut connectionWithMediaType:AVMediaTypeAudio];
NSDictionary *videoSettings;
if (_cropSize.height == 0 || _cropSize.width == 0) {
_cropSize = [UIScreen mainScreen].bounds.size;
}
videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:_cropSize.width], AVVideoWidthKey,
[NSNumber numberWithInt:_cropSize.height], AVVideoHeightKey,
AVVideoScalingModeResizeAspectFill,AVVideoScalingModeKey,
nil];
static void makeAnimatedGif(NSArray *images, NSURL *gifURL, NSTimeInterval duration) {
NSTimeInterval perSecond = duration /images.count;
NSDictionary *fileProperties = @{
(__bridge id)kCGImagePropertyGIFDictionary: @{
(__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever
}
};
NSDictionary *frameProperties = @{
(__bridge id)kCGImagePropertyGIFDictionary: @{
(__bridge id)kCGImagePropertyGIFDelayTime: @(perSecond), // a float (not double!) in seconds, rounded to centiseconds in the GIF data
}
};
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)gifURL, kUTTypeGIF, images.count, NULL);
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
for (UIImage *image in images) {
@autoreleasepool {
CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
}
}
if (!CGImageDestinationFinalize(destination)) {
NSLog(@"failed to finalize image destination");
}else{
}
CFRelease(destination);
}
//转成UIImage
- (void)convertVideoUIImagesWithURL:(NSURL *)url finishBlock:(void (^)(id images, NSTimeInterval duration))finishBlock
{
AVAsset *asset = [AVAsset assetWithURL:url];
NSError *error = nil;
self.reader = [[AVAssetReader alloc] initWithAsset:asset error:&error];
NSTimeInterval duration = CMTimeGetSeconds(asset.duration);
__weak typeof(self)weakSelf = self;
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(backgroundQueue, ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"");
if (error) {
NSLog(@"%@", [error localizedDescription]);
}
NSArray *videoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack =[videoTracks firstObject];
if (!videoTrack) {
return ;
}
int m_pixelFormatType;
// 视频播放时,
m_pixelFormatType = kCVPixelFormatType_32BGRA;
// 其他用途,如视频压缩
// m_pixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange;
NSMutableDictionary *options = [NSMutableDictionary dictionary];
[options setObject:@(m_pixelFormatType) forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:options];
if ([strongSelf.reader canAddOutput:videoReaderOutput]) {
[strongSelf.reader addOutput:videoReaderOutput];
}
[strongSelf.reader startReading];
NSMutableArray *images = [NSMutableArray array];
// 要确保nominalFrameRate>0,之前出现过android拍的0帧视频
while ([strongSelf.reader status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate > 0) {
@autoreleasepool {
// 读取 video sample
CMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];
if (!videoBuffer) {
break;
}
[images addObject:[WKVideoConverter convertSampleBufferRefToUIImage:videoBuffer]];
CFRelease(videoBuffer);
}
}
if (finishBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
finishBlock(images, duration);
});
}
});
}
@autoreleasepool {
// 读取 video sample
CMSampleBufferRef videoBuffer = [videoReaderOutput copyNextSampleBuffer];
if (!videoBuffer) {
break;
}
[images addObject:[WKVideoConverter convertSampleBufferRefToUIImage:videoBuffer]];
CFRelease(videoBuffer); }
机械节能产品生产企业官网模板...
大气智能家居家具装修装饰类企业通用网站模板...
礼品公司网站模板
宽屏简约大气婚纱摄影影楼模板...
蓝白WAP手机综合医院类整站源码(独立后台)...苏ICP备2024110244号-2 苏公网安备32050702011978号 增值电信业务经营许可证编号:苏B2-20251499 | Copyright 2018 - 2025 源码网商城 (www.ymwmall.com) 版权所有