2011-04-12 15 views
36

मुझे अपने आईफोन एप्लिकेशन से एक मूवी निर्यात करना है जिसमें एनएसएआरएआरई से यूआईएममेज शामिल है और कुछ ऑडियो फाइलें .caf प्रारूप में जोड़ती हैं जिन्हें पूर्व-निर्दिष्ट समय से शुरू करना होता है। अब मैं छवियों वाले वीडियो हिस्से को निर्यात करने के लिए AVAssetWriter (इस और अन्य साइटों पर कई प्रश्नों और उत्तरों के माध्यम से जाने के बाद) का उपयोग करने में सक्षम हूं लेकिन फिल्म को पूरा करने के लिए ऑडियो फ़ाइलों को जोड़ने का कोई तरीका नहीं दिख रहा है।AVFoundation + AssetWriter: छवियों और ऑडियो के साथ मूवी जेनरेट करें

यहाँ है कि मैं क्या 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"); 
} 

और अब कोड मिल गया है है

- (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; 
} 

तो तुम मुझे ऑडियो फ़ाइलें जोड़ने के बारे में के बारे में मदद कर सकते हैं और कैसे बफ़र्स बनाने के लिए उनके लिए और एडाप्टर और इनपुट सेटिंग्स इत्यादि

यदि यह दृष्टिकोण मुझे समस्या का कारण बन सकता है, तो AVMutableComposition का उपयोग करने के लिए छवि सरणी का उपयोग करने के तरीके के बारे में मुझे मार्गदर्शन करें वीडियो निर्यात

+0

ठीक है मैं AVAssetReaders और AVAssetWriterInputs का उपयोग कर ऑडियो फ़ाइलें जोड़ने के लिए हालांकि, सक्षम किया गया है जब मैं ऑडियो फाइलों को जोड़ता हूं, तो वे बिना किसी विराम के एक दूसरे के बाद एक शुरू करते हैं (एक खत्म होता है और एक शुरू होता है) पूर्व निर्धारित समय के बजाय, तो मैं AVAssetWriter को एक निश्चित समय पर इनपुट लेने के लिए कैसे कहूं। ऐसा इसलिए है क्योंकि जैसा कि मैं समझता हूं [startSessionAtSourceTime] स्रोत के समय को निर्धारित करने के लिए है, गंतव्य मूवी में समय नहीं, तो – MuTaTeD

+0

कोई संकेत देता है आप दूसरों के लिए ऐसे विस्तृत समाधान पोस्ट करने के लिए बहुत ही अच्छे हैं। – TigerCoding

+0

यह 1080 * 1920 छवियों के साथ भी काम कर रहा है? क्योंकि मैंने 720 * 1280 (720/16) के साथ एक ही कोड लागू किया है और यह अच्छी तरह से काम कर रहा है, लेकिन उन वीडियो चौड़ाई के साथ काम नहीं कर रहा है जिनके फेंकने के परिणामस्वरूप फ्लोटिंग वैल्यू (वीडियो चौड़ाई/16) कोई सुझाव है? –

उत्तर

17

मैंने ऊपर दिए गए कोड का उपयोग करके वीडियो को अलग से निर्यात करना समाप्त कर दिया और AVComposition & AVExportSession का उपयोग करके ऑडियो फ़ाइलों को अलग से जोड़ा। यहाँ कोड

-(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" लूप को एक "ऑडियोइन्फो" शब्दकोश के साथ प्रतिस्थापित कर सकते हैं जिसमें सभी मूल्यों को सेट करने की आवश्यकता है ताकि यह अधिक कॉपी-पेस्ट अनुकूल हो जाए? :) –

+0

@ चिंतन पटेल: मुझे जेनरेट की गई फिल्म में वीडियो के विभिन्न हिस्सों के खिलाफ अलग-अलग ऑडियो फ़ाइलों की परिवर्तनीय लंबाई जोड़नी पड़ी (मैं इसके बजाय पूरी ऑडियो फाइलों का उपयोग कर समाप्त करता हूं), इसलिए मैंने प्रत्येक ऑडियो फ़ाइल के लिए एक शब्दकोश बनाया शामिल और उन्हें सरणी (audioInfoArray) में जोड़ा गया। ऑडियोइन्फो शब्दकोश में निम्नलिखित कुंजी ** ऑडियोफाइलपाथ ** और ** ऑडियो अवधि **, _NSString_ और _float_ क्रमशः शामिल हैं। – MuTaTeD

+0

धन्यवाद, वह करेगा। –

4

आप एक ही "audioInfo" शब्दकोश जो सभी मूल्यों जो स्थापित किया जाना है, ताकि इसे और अधिक अनुकूल कॉपी-पेस्ट हो जाता है की जरूरत है साथ पाश "के लिए" के बदले कर सकता है? :)

आप सिर्फ एक ऑडियो फ़ाइल जोड़ना चाहते हैं, तो निम्न कोड को प्रतिस्थापित करना चाहिए पाश के लिए:

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];