2012-02-07 63 views
3

Im使用this tutorial製作一個視頻合併來自一個圖像(僅有一幀)和幾秒鐘音頻的視頻文件。當長度不同時合併音頻和視頻

在iPhone設備中,視頻持續時間相當於音頻持續時間,我沿着所有視頻看到圖像。

但是,當我分享到android設備(通過whatsapp),我按播放播放時間是從圖像持續時間(一幀)的電影。我做了一個測試,如果我從一個圖像重複一百次(10fps,十秒)創建一個電影文件,在Android設備playblack的持續時間是十秒鐘。

我認爲,Android設備只能播放視頻最短軌道,但如果我修改TIMERANGE視頻的addMutableTrackWithMediaType到音頻持續時間沒有任何反應。

有什麼建議嗎?

感謝您的支持

我把所有的代碼在這裏:

-(void) writeImagesToMovieAtPath:(NSString *)path withSize:(CGSize) size { 

    NSMutableArray *m_PictArray = [NSMutableArray arrayWithCapacity:1]; 
    [m_PictArray addObject:[UIImage imageNamed:@"prueba.jpg"]]; 

    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; 
    NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath error:nil]; 
    for (NSString *tString in dirContents) { 
     if ([tString isEqualToString:@"essai.mp4"]) 
     { 
      [[NSFileManager defaultManager]removeItemAtPath:[NSString stringWithFormat:@"%@/%@",documentsDirectoryPath,tString] error:nil]; 

     } 
    } 

    NSLog(@"Write Started"); 

    NSError *error = nil; 

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

    NSDictionary *codecSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            [NSNumber numberWithInt:128000], AVVideoAverageBitRateKey, 
            [NSNumber numberWithInt:15],AVVideoMaxKeyFrameIntervalKey, 
            AVVideoProfileLevelH264Main30, AVVideoProfileLevelKey, 
            nil];  

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
            AVVideoCodecH264, AVVideoCodecKey, 
            codecSettings,AVVideoCompressionPropertiesKey, 
            [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]; 


    //Video encoding 

    CVPixelBufferRef buffer = NULL; 

    //convert uiimage to CGImage. 

    int frameCount = 0; 

    for(int i = 0; i<[m_PictArray count]; i++) 
    { 
     buffer = [self newPixelBufferFromCGImage:[[m_PictArray objectAtIndex:i] 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) 10); 
       /* 
       Float64 seconds = 1; 
       int32_t preferredTimeScale = 10; 
       CMTime frameTime = CMTimeMakeWithSeconds(seconds, preferredTimeScale); 
       */ 
       append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime]; 
       CVPixelBufferPoolRef bufferPool = adaptor.pixelBufferPool; 
       NSParameterAssert(bufferPool != NULL); 

       [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++; 
     CVBufferRelease(buffer); 
    } 

    [videoWriterInput markAsFinished]; 
    [videoWriter finishWriting]; 

    [videoWriterInput release]; 
    [videoWriter release]; 

    [m_PictArray removeAllObjects]; 

    NSLog(@"Write Ended"); 

    [self saveVideoToAlbum:path]; 
} 


-(void)CompileFilesToMakeMovie { 

    NSLog(@"CompileFilesToMakeMovie"); 

    AVMutableComposition* mixComposition = [AVMutableComposition composition]; 

    NSString *documentsDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];  

    //Audio file in AAC 
    NSString* audio_inputFileName = @"zApY4o8QY.m4a"; 

    NSString* audio_inputFilePath = [NSString stringWithFormat:@"%@/%@",[[NSBundle mainBundle] resourcePath],audio_inputFileName]; 
    NSURL* audio_inputFileUrl = [NSURL fileURLWithPath:audio_inputFilePath]; 

    NSString* video_inputFileName = @"essai.mp4"; 
    NSString* video_inputFilePath = [NSString stringWithFormat:@"%@/%@",documentsDirectoryPath,video_inputFileName]; 
    NSURL* video_inputFileUrl = [NSURL fileURLWithPath:video_inputFilePath]; 

    NSString* outputFileName = @"outputFile.mov"; 
    NSString* outputFilePath = [NSString stringWithFormat:@"%@/%@",documentsDirectoryPath,outputFileName]; 

    NSURL* outputFileUrl = [NSURL fileURLWithPath:outputFilePath]; 

    if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath]) 
     [[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil]; 


    CMTime nextClipStartTime = kCMTimeZero; 

    AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:video_inputFileUrl options:nil]; 
    AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audio_inputFileUrl options:nil]; 


    //CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration); 
    CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration); 
    AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 
    [a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil]; 

    CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration); 
    AVMutableCompositionTrack *b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid]; 
    [b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:nextClipStartTime error:nil]; 



    AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetLowQuality]; 
    _assetExport.shouldOptimizeForNetworkUse = YES; 
    _assetExport.outputFileType = @"com.apple.quicktime-movie"; 
    _assetExport.outputURL = outputFileUrl; 
    _assetExport.timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration); 

    [_assetExport exportAsynchronouslyWithCompletionHandler: 
    ^(void) { 
     [self saveVideoToAlbum:outputFilePath]; 
    }  
    ]; 

    NSLog(@"CompileFilesToMakeMovie Finish"); 
} 

- (void) saveVideoToAlbum:(NSString*)path { 

    NSLog(@"saveVideoToAlbum"); 

    if(UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)){ 
     UISaveVideoAtPathToSavedPhotosAlbum (path, self, @selector(video:didFinishSavingWithError: contextInfo:), nil); 
    } 
} 

-(void) video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { 
    if(error) 
     NSLog(@"Exportado con error: %@", error); 
    else 
     NSLog(@"Exportado OK"); 
} 

- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef)image andSize:(CGSize)frameSize { 

    CGAffineTransform frameTransform = CGAffineTransformMake(0, 0, 0, 0, 0, 0); 

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
          [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 
          [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, 
          nil]; 
    CVPixelBufferRef pxbuffer = NULL; 

    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameSize.width, 
              frameSize.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, frameSize.width, 
               frameSize.height, 8, 4*frameSize.width, rgbColorSpace, 
               kCGImageAlphaNoneSkipFirst); 
    NSParameterAssert(context); 
    //CGContextConcatCTM(context, frameTransform); 
    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), 
              CGImageGetHeight(image)), image); 
    CGColorSpaceRelease(rgbColorSpace); 
    CGContextRelease(context); 

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

    return (CVPixelBufferRef)pxbuffer; 
} 

回答

0

就修好了​​!

我創作的電影文件重複圖像X次,然後在組合過程我縮放到audioAsset.duration的大小

CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration); 
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; 
[a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:kCMTimeZero error:nil]; 
[a_compositionVideoTrack scaleTimeRange:video_timeRange toDuration:audioAsset.duration]; 

您需要重複一下圖片讓道是縮放,但如果電影只有2幀,在Android只播放八秒鐘,所以我做了一個視頻與圖像重複10次,讓我超過45 secons的限制在視頻分享whatsapp

0

Inside CompileFilesToMakeMovie方法,在需要的地方使用video_timeRange而不是audio_timeRange ...