マイクのオーディオとともにUIImageViewのコンテンツを録画し、リアルタイムでビデオに録画するアプリを作ろうとしています。 (Talking Tom Catアプリのようなもの)iPhone - AVAssetWriterInputPixelBufferAdaptorを使用してマイクからオーディオをキャプチャしてビデオを録画する
私はAVAssetWriterInputPixelBufferAdaptorを使用してUIImageViewの内容を問題なく記録していますが、これにオーディオを組み込む方法はわかりません。私はこのウェブサイト、Google、iPhone開発者用のforumnsなどを組み合わせて1週間以上の間、この特定の問題に取り組んできました。これはちょうど私のお茶の杯ではありません。
これに最も近い参照である: How do I export UIImage array as a movie?
私は別にビデオとオーディオをキャプチャして、(私は本当にこの問題を解決するために努力してきた)事実の後にそれらを一緒にエンコードしますが、このアプリを取得することができますリアルタイムで記録することは本当に理想的です。ここで
は、私がこれまで持っている記録を実証し、いくつかのフレームワークコードです:ここで
は.hファイルである
//
// RecordingTestProjectViewController.h
// RecordingTestProject
//
// Created by Sean Luck on 7/26/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreMedia/CoreMedia.h>
#import <CoreVideo/CoreVideo.h>
#import <QuartzCore/QuartzCore.h>
@interface RecordingTestProjectViewController : UIViewController
{
UIImageView *testView;
NSTimer *theTimer;
NSTimer *assetWriterTimer;
AVMutableComposition *mutableComposition;
AVAssetWriter *assetWriter;
AVAssetWriterInput *assetWriterInput;
AVAssetWriterInput *_audioWriterInput;
AVCaptureDeviceInput *audioInput;
AVCaptureSession *_capSession;
AVAssetWriterInputPixelBufferAdaptor *assetWriterPixelBufferAdaptor;
CFAbsoluteTime firstFrameWallClockTime;
int count;
}
-(void) writeSample: (NSTimer*) _timer;
-(void) startRecording;
-(void) pauseRecording;
-(void) stopRecording;
-(NSString*) pathToDocumentsDirectory;
@end
と.mファイル
//
// RecordingTestProjectViewController.m
// RecordingTestProject
//
// Created by Sean Luck on 7/26/11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "RecordingTestProjectViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <CoreMedia/CoreMedia.h>
#import <CoreVideo/CoreVideo.h>
#import <QuartzCore/QuartzCore.h>
#define OUTPUT_FILE_NAME @"screen.mov"
#define TIME_SCALE 600
@implementation RecordingTestProjectViewController
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
testView = [[UIImageView alloc] initWithImage:nil];
testView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
testView.userInteractionEnabled=NO;
testView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:testView];
[testView release];
[super viewDidLoad];
[self startRecording];
}
-(void) writeSample: (NSTimer*) _timer
{
if ([assetWriterInput isReadyForMoreMediaData])
{
NSLog(@"count=%.i",count);
count=count+10;
UIGraphicsBeginImageContext(testView.frame.size);
[testView.image drawInRect:CGRectMake(0, 0, testView.frame.size.width, testView.frame.size.height)];
CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), 0,1,1,1);
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), 5);
CGContextBeginPath(UIGraphicsGetCurrentContext());
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 10+count/2, 10+count);
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), 20+count*2, 20+count);
CGContextStrokePath(UIGraphicsGetCurrentContext());
testView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CVReturn cvErr = kCVReturnSuccess;
CGImageRef image = (CGImageRef) [testView.image CGImage];
// prepare the pixel buffer
CVPixelBufferRef pixelBuffer = NULL;
CFDataRef imageData= CGDataProviderCopyData(CGImageGetDataProvider(image));
cvErr = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,testView.frame.size.width,testView.frame.size.height,kCVPixelFormatType_32BGRA,(void*)CFDataGetBytePtr(imageData),CGImageGetBytesPerRow(image),NULL,NULL,NULL,&pixelBuffer);
// calculate the time
CFAbsoluteTime thisFrameWallClockTime = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsedTime = thisFrameWallClockTime - firstFrameWallClockTime;
CMTime presentationTime = CMTimeMake (elapsedTime * TIME_SCALE, TIME_SCALE);
if (!cvErr)
{
// write the sample
BOOL appended = [assetWriterPixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:presentationTime];
if (appended)
{
}
else
{
NSLog (@"failed to append");
[self stopRecording];
}
}
CVPixelBufferRelease(pixelBuffer);
CFRelease(imageData);
}
if (count>1000)
{
[self stopRecording];
}
}
-(void) startRecording
{
// Doesn't record audio at all. Needs to be implemented.
// create the AVAssetWriter
NSString *moviePath = [[self pathToDocumentsDirectory] stringByAppendingPathComponent:OUTPUT_FILE_NAME];
if ([[NSFileManager defaultManager] fileExistsAtPath:moviePath])
{
[[NSFileManager defaultManager] removeItemAtPath:moviePath error:nil];
}
NSURL *movieURL = [NSURL fileURLWithPath:moviePath];
NSError *movieError = nil;
[assetWriter release];
assetWriter = [[AVAssetWriter alloc] initWithURL:movieURL fileType: AVFileTypeQuickTimeMovie error: &movieError];
NSDictionary *assetWriterInputSettings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey,[NSNumber numberWithInt:testView.frame.size.width], AVVideoWidthKey,[NSNumber numberWithInt:testView.frame.size.height], AVVideoHeightKey,nil];
[assetWriterInput release];
assetWriterInput =[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:assetWriterInputSettings];
assetWriterInput.expectsMediaDataInRealTime = YES;
[assetWriter addInput:assetWriterInput];
[assetWriterPixelBufferAdaptor release];
assetWriterPixelBufferAdaptor = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:nil];
[assetWriter startWriting];
firstFrameWallClockTime = CFAbsoluteTimeGetCurrent();
[assetWriter startSessionAtSourceTime: CMTimeMake(0, TIME_SCALE)];
// start writing samples to it
[assetWriterTimer release];
assetWriterTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector (writeSample:) userInfo:nil repeats:YES];
[movieURL release];
[assetWriterInputSettings release];
}
-(void) stopRecording
{
if (assetWriterTimer!=nil)
{
[assetWriterTimer invalidate];
assetWriterTimer = nil;
[assetWriter finishWriting];
NSString *moviePath = [[self pathToDocumentsDirectory] stringByAppendingPathComponent:OUTPUT_FILE_NAME];
UISaveVideoAtPathToSavedPhotosAlbum (moviePath, nil, nil, nil);
}
}
-(void) pauseRecording
{
// needs to be implemented
}
-(NSString*) pathToDocumentsDirectory
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return documentsDirectory;
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
@end
だから、ということです。誰かがマイクのオーディオを録音するためにこのコードを変更できるなら、私はそれに感謝する多くの人がいると確信しています。オーディオが追加されると、これは開発者が自分のアプリをデモンストレーションするためのアプリのスクリーンキャストで行うストレートなフレームワークになります。
FYI:私のコードは、オープンソースで、主にVTMScreenRecorderTestプロジェクトの後にモデル化されています(http://www.subfurther.com/blog/2011/04/12/voices-that-matter-iphone-spring-2011/)。私は小規模な修正を行い、メモリーリークをきれいにしました。
アセットライターでオーディオキャプチャのソースとオーディオ出力を設定しようとしましたか? WWDC 2010ソースパッケージのAVCamDemoコードを確認してください。 MOVファイルにオーディオをキャプチャして書き込むコードがあります。 –
ええ、私はこのコードでは本当に無能です。私は正直にもメモリリークを修正することができたことに驚いています。提案してくれてありがとう、私はそれを見て、私はそれを実装することができます参照してください! -Sean – Sean