2011-04-12 2 views
36

NSArray의 UIImage가 포함 된 iPhone 응용 프로그램에서 동영상을 내보내고 미리 지정된 시간에 시작해야하는 .caf 형식의 오디오 파일을 추가해야합니다. 이제 이미지가 포함 된 비디오 부분을 내보내려면 AVAssetWriter를 사용할 수 있었지만 오디오 파일을 추가하여 동영상을 완성하는 방법을 찾지 못하는 것 같습니다. 여기 AVFoundation + AssetWriter : 이미지 및 오디오로 동영상 생성

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image andSize:(CGSize) size 
{ 
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
         [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 
         [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, 
         nil]; 
    CVPixelBufferRef pxbuffer = NULL; 

    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, 
             size.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, 
             &pxbuffer); 
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); 

    CVPixelBufferLockBaseAddress(pxbuffer, 0); 
    void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); 
    NSParameterAssert(pxdata != NULL); 

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 
    CGContextRef context = CGBitmapContextCreate(pxdata, size.width, 
              size.height, 8, 4*size.width, rgbColorSpace, 
              kCGImageAlphaNoneSkipFirst); 
    NSParameterAssert(context); 
    CGContextConcatCTM(context, CGAffineTransformMakeRotation(0)); 
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), 
              CGImageGetHeight(image)), image); 
    CGColorSpaceRelease(rgbColorSpace); 
    CGContextRelease(context); 

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

    return pxbuffer; 
} 

그래서 당신은 오디오 파일을 추가하는 방법에 대해 저를 도울 수와 버퍼를 만드는 방법을 내가 pixelBufferFromCGImage

을 위해 지금까지 지금

-(void) writeImagesToMovieAtPath:(NSString *) path withSize:(CGSize) size 
{ 
    NSLog(@"Write Started"); 

    NSError *error = nil; 

    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL: 
           [NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie 
                  error:&error];  
    NSParameterAssert(videoWriter); 

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
           AVVideoCodecH264, AVVideoCodecKey, 
           [NSNumber numberWithInt:size.width], AVVideoWidthKey, 
           [NSNumber numberWithInt:size.height], AVVideoHeightKey, 
           nil]; 

    AVAssetWriterInput* videoWriterInput = [[AVAssetWriterInput 
            assetWriterInputWithMediaType:AVMediaTypeVideo 
            outputSettings:videoSettings] retain]; 


    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor 
              assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput 
               sourcePixelBufferAttributes:nil]; 

    NSParameterAssert(videoWriterInput); 
    NSParameterAssert([videoWriter canAddInput:videoWriterInput]); 
    videoWriterInput.expectsMediaDataInRealTime = YES; 
    [videoWriter addInput:videoWriterInput]; 

    //Start a session: 
    [videoWriter startWriting]; 
    [videoWriter startSessionAtSourceTime:kCMTimeZero]; 

    CVPixelBufferRef buffer = NULL; 

    //convert uiimage to CGImage. 

    int frameCount = 0; 

    for(UIImage * img in imageArray) 
    { 
      buffer = [self pixelBufferFromCGImage:[img CGImage] andSize:size]; 

      BOOL append_ok = NO; 
      int j = 0; 
      while (!append_ok && j < 30) 
      { 
       if (adaptor.assetWriterInput.readyForMoreMediaData) 
       { 
        printf("appending %d attemp %d\n", frameCount, j); 

        CMTime frameTime = CMTimeMake(frameCount,(int32_t) kRecordingFPS); 
        append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime]; 

        if(buffer) 
         CVBufferRelease(buffer); 
        [NSThread sleepForTimeInterval:0.05]; 
       } 
       else 
       { 
        printf("adaptor not ready %d, %d\n", frameCount, j); 
        [NSThread sleepForTimeInterval:0.1]; 
       } 
       j++; 
      } 
      if (!append_ok) { 
       printf("error appending image %d times %d\n", frameCount, j); 
      } 
      frameCount++; 
     } 
    } 

    //Finish the session: 
    [videoWriterInput markAsFinished]; 
    [videoWriter finishWriting]; 
    NSLog(@"Write Ended"); 
} 

그리고 코드를 얻었을 것입니다 그들과 어댑터 및 입력 설정 등을 위해

이 방법으로 인해 문제가 발생할 수있는 경우 AVMutableComposition을 사용하여 이미지 배열을 사용하는 방법에 대한 안내 비디오 내보내기

+0

나는 그러나, AVAssetReaders 및 AVAssetWriterInputs를 사용하여 오디오 파일을 추가 할 수 있었다 확인 오디오 파일을 추가 할 때 미리 정해진 시간에 시작하는 대신 일시 중지 (하나가 끝나고 하나가 시작되도록 설정)없이 연속적으로 시작하므로 특정 시간에 AVAssetWriter에 입력을 알리는 방법은 무엇입니까? 왜냐하면 나는 [startSessionAtSourceTime]이 대상 영화의 시간이 아닌 소스의 시간을 결정하기위한 것이라는 것을 알고 있기 때문에 어떤 힌트 일지 모른다. – MuTaTeD

+0

다른 사람들을 위해 그런 상세한 솔루션을 게시하는 것은 대단하다. – TigerCoding

+0

도 1080 * 1920 이미지로 작업하고 있습니까? 내가 동일한 코드를 구현하고 720 * 1280 (720/16)과 잘 작동하지만 floting value (video width/16)의 제안을 던지는 비디오 폭으로 작업하지 않기 때문에? –

답변

17

위 코드를 사용하여 비디오를 개별적으로 내보내고 AVComposition & AVExportSession을 사용하여 오디오 파일을 별도로 추가했습니다. 다음은 좀 더 쉬운 복사 붙여 넣기가되도록 설정해야하는 모든 값을 가지고 하나의 "audioInfo"사전과 "에 대한"루프를 교체하십시오 수 코드

-(void) addAudioToFileAtPath:(NSString *) filePath toPath:(NSString *)outFilePath 
{ 
    NSError * error = nil; 

    AVMutableComposition * composition = [AVMutableComposition composition]; 


    AVURLAsset * videoAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:filePath] options:nil]; 

    AVAssetTrack * videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 

    AVMutableCompositionTrack *compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo 
                       preferredTrackID: kCMPersistentTrackID_Invalid]; 

    [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero 
            error:&error];  

    CMTime audioStartTime = kCMTimeZero; 
    for (NSDictionary * audioInfo in audioInfoArray) 
    { 
     NSString * pathString = [audioInfo objectForKey:audioFilePath]; 
     AVURLAsset * urlAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:pathString] options:nil]; 

     AVAssetTrack * audioAssetTrack = [[urlAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 
     AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio 
                        preferredTrackID: kCMPersistentTrackID_Invalid]; 

     [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,urlAsset.duration) ofTrack:audioAssetTrack atTime:audioStartTime error:&error];  

     audioStartTime = CMTimeAdd(audioStartTime, CMTimeMake((int) (([[audioInfo objectForKey:audioDuration] floatValue] * kRecordingFPS) + 0.5), kRecordingFPS)); 
    } 
    AVAssetExportSession* assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality]; 
    assetExport.videoComposition = mutableVideoComposition; 

    assetExport.outputFileType =AVFileTypeQuickTimeMovie;// @"com.apple.quicktime-movie"; 
    assetExport.outputURL = [NSURL fileURLWithPath:outFilePath]; 

    [assetExport exportAsynchronouslyWithCompletionHandler: 
    ^(void) { 
     switch (assetExport.status) 
     { 
      case AVAssetExportSessionStatusCompleted: 
//    export complete 
       NSLog(@"Export Complete"); 
       break; 
      case AVAssetExportSessionStatusFailed: 
       NSLog(@"Export Failed"); 
       NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); 
//    export error (see exportSession.error) 
       break; 
      case AVAssetExportSessionStatusCancelled: 
       NSLog(@"Export Failed"); 
       NSLog(@"ExportSessionError: %@", [assetExport.error localizedDescription]); 
//    export cancelled 
       break; 
     } 
    }];  
} 
+0

"for"루프를 더 많은 복사 - 붙여 넣기가 가능하도록 설정해야하는 모든 값을 가진 단일 "audioInfo"사전으로 바꿀 수 있습니까? :) –

+0

@Chintan Patel : 나는 생성 된 동영상에서 비디오의 다른 부분에 대해 다양한 오디오 파일의 가변 길이를 추가해야했습니다 (대신 완전한 오디오 파일을 사용하게됩니다). 그래서 각 오디오 파일에 대한 사전을 만들었습니다. 포함하고 배열 (audioInfoArray)에 모두 추가했습니다. audioInfo 사전에는 ** audioFilePath ** 및 ** audioDuration **, _NSString_ 및 _float_ 키가 있습니다. – MuTaTeD

+0

감사합니다. –

4

입니까? :)

당신은 단지 하나의 오디오 파일을 추가하려면 다음 코드를 교체해야 for 루프 :

NSString * pathString = [self getAudioFilePath]; 
AVURLAsset * urlAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:pathString] options:nil]; 

AVAssetTrack * audioAssetTrack = [[urlAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 
AVMutableCompositionTrack *compositionAudioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio 
                preferredTrackID: kCMPersistentTrackID_Invalid]; 

[compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,urlAsset.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:&error];  
관련 문제