2011-07-27 14 views
4

マイクのオーディオとともに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/)。私は小規模な修正を行い、メモリーリークをきれいにしました。

+1

アセットライターでオーディオキャプチャのソースとオーディオ出力を設定しようとしましたか? WWDC 2010ソースパッケージのAVCamDemoコードを確認してください。 MOVファイルにオーディオをキャプチャして書き込むコードがあります。 –

+0

ええ、私はこのコードでは本当に無能です。私は正直にもメモリリークを修正することができたことに驚いています。提案してくれてありがとう、私はそれを見て、私はそれを実装することができます参照してください! -Sean – Sean

答えて

1

録音したビデオと一緒にマイクを使用する場合は、CMTimestampsが同期している必要があることがわかりました。私はCMSampleBufferSetOutputPresentationTimeStampを使用してオーディオのサンプル時間を変更することによってそれらを同期しようとしました...しかし、私は何かが不足している、または動作しません。あなたのマイクが始まる時刻をトラップしてビデオにオフセットとして追加すると、ここのStackOverflowのAVAssetWriterの各サンプルは正常に動作しているようです -

ここで私はビデオを同期させておくマイクロフォン:

Img = Image 
Snd = Sound 
Avmedia = AVAssetWriter 
etc. 
#define Cmtm__Pref_Timescale_uk 10000 
      CMTime Duration_cmtm = CMTimeMakeWithSeconds(Avmedia_v->Img_Duration__Sec_df, Cmtm__Pref_Timescale_uk); 
      Avmedia_v->Img_cmtm = CMTimeAdd(Duration_cmtm, Avmedia_v->Exprt__Begin_cmtm); 

      if([ Avmedia_v->Clrbuf__Adaptor_v appendPixelBuffer: Clrbuf_v withPresentationTime: 
       Avmedia_v->Img_cmtm ] is no) 
      { 
      //If the operation was unsuccessful, 
      //invoke the AVAssetWriter object’s finishWriting method in order to save a partially completed asset. 
      [ Avmedia_v->Avwriter_v finishWriting ]; 

      CLog(Lv_Minor, "Movie Img exprt failed"); 
      } 
+0

あなたのコードは本当に読みにくいです! –

関連する問題