Проблема при создании видео из массива изображения

Я использовал следующий код для генерации видео из массива изображений и аудио (предварительно записанных).

- (void)viewDidLoad
{
    imagearray=[[NSMutableArray alloc]initWithObjects:@"Quiz pic1.jpg",@"Quiz pic2.jpg",@"Quiz pic3.jpg",@"Quiz pic6.jpg",@"Quiz pic7.jpg",nil];
    image1array=[[NSMutableArray alloc]init];
    for (int i=0; i<[imagearray count]; i++)
    {
        UIImage *aimage=[UIImage imageNamed:[imagearray objectAtIndex:i]];
        [image1array addObject:aimage];
    }
    NSLog(@"%@",image1array);
    ImageVideoPath=@"/Users/image/Library/Application Support/iPhone Simulator/4.3/Applications/6CC91208-5819-4BFF-B868-6605887861EB/Output";
    FinalVideoPath=@"/Users/image/Library/Application Support/iPhone Simulator/4.3/Applications/6CC91208-5819-4BFF-B868-6605887861EB/VideoOutput";
    CGSize size;
    UIImage *image=[UIImage imageNamed:[imagearray objectAtIndex:0]];
    size=image.size;
    NSString *audioFilePath;
    int duration=10;
    //[self pixelBufferFromCGImage:[[image1array objectAtIndex:0] CGImage]];
    [self writeImageAndAudioAsMovie:image andAudio:audioFilePath duration:duration];

    //[self pixelBufferFromCGImage:[image CGImage] andSize:size];
    [super viewDidLoad];
}

- (void)writeImageAndAudioAsMovie:(UIImage*)image andAudio:(NSString *)audioFilePath duration:(int)duration {
    NSLog(@"start make movie: length:%d",duration);
    NSError *error = nil;
    AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:ImageVideoPath] fileType:AVFileTypeQuickTimeMovie
                                                              error:&error];
    NSParameterAssert(videoWriter);
    if ([[NSFileManager defaultManager] fileExistsAtPath:ImageVideoPath]) 
        [[NSFileManager defaultManager] removeItemAtPath:ImageVideoPath error:nil];

    NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey,
                                   [NSNumber numberWithInt:image.size.width],AVVideoWidthKey,[NSNumber numberWithInt:image.size.height], AVVideoHeightKey,nil];
    AVAssetWriterInput* writerInput = [[AVAssetWriterInput
                                        assetWriterInputWithMediaType:AVMediaTypeVideo
                                        outputSettings:videoSettings] retain];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    writerInput.expectsMediaDataInRealTime = YES;
    [videoWriter setShouldOptimizeForNetworkUse:YES];
    [videoWriter addInput:writerInput];

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

    //Write samples:
    CVPixelBufferRef buffer = [self pixelBufferFromCGImage:image.CGImage];
    [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];

    //Finish the session:
    [videoWriter endSessionAtSourceTime:CMTimeMake(duration, 1)];
    [writerInput markAsFinished];
    [videoWriter finishWriting];

    CVPixelBufferPoolRelease(adaptor.pixelBufferPool);
    [videoWriter release];
    [writerInput release];
    audioFilePath=[[NSBundle mainBundle]pathForResource:@"Video" ofType:@"mp3"];
    NSLog(@"%@",audioFilePath);
    [self addAudioToFileAtPath:ImageVideoPath andAudioPath:audioFilePath];
}

-(CVPixelBufferRef)pixelBufferFromCGImage: (CGImageRef) image{
    float width = CGImageGetWidth(image);
    float height = CGImageGetHeight(image);

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    CVPixelBufferRef pxbuffer = NULL;
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width,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,width,height,8,4*width,rgbColorSpace,kCGImageAlphaNoneSkipFirst);

    NSParameterAssert(context);
    CGContextDrawImage(context, CGRectMake(0, 0,width, height), image);

    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

-(void) addAudioToFileAtPath:(NSString *)vidoPath andAudioPath:(NSString *)audioFilePath{
    AVMutableComposition* mixComposition = [AVMutableComposition composition];
    NSLog(@"%@ %@",ImageVideoPath,audioFilePath);
    NSURL* audio_inputFileUrl = [NSURL fileURLWithPath:audioFilePath];
    NSURL* video_inputFileUrl = [NSURL fileURLWithPath:ImageVideoPath];
    NSLog(@"%@",video_inputFileUrl);
    NSString *outputFilePath = FinalVideoPath;
    NSURL* outputFileUrl = [NSURL fileURLWithPath:outputFilePath];

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

   AVURLAsset* audioAsset = [[AVURLAsset alloc]initWithURL:audio_inputFileUrl options:nil];
AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:video_inputFileUrl options:nil];
NSLog(@"asset:%@",videoAsset);
NSArray *tracks1=[videoAsset tracksWithMediaType:AVMediaTypeVideo];
if ([tracks1 count]>0)
{
    //CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration);
    AVAssetTrack *videoAssetTrack=[tracks1 objectAtIndex:0];
    AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];

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

}

NSArray *tracks = [audioAsset tracksWithMediaType:AVMediaTypeAudio];
if([tracks count]>0)
{
    AVAssetTrack * audioAssetTrack = [tracks objectAtIndex:0];
    AVMutableCompositionTrack *compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio 
                                                                                   preferredTrackID: kCMPersistentTrackID_Invalid];

    [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,audioAsset.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];  


    //nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
    [audioAsset release];audioAsset = nil;
}


    AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];   
    _assetExport.outputFileType = AVFileTypeMPEG4;
    _assetExport.outputURL = outputFileUrl;

    [_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;
         }
     }];    
}

Я обнаружил, что один из видеофайлов создан из метода writeImageAndAudioAsMovie но это не поддержка любого игрока на моей машине... Я не знаю, что я скучаю? Любые предложения плз....

2 ответа

Проблема заключается в двух местах:

1.Путь, который вы указываете, должен быть таким, чтобы вы могли писать как каталог документов. И это должно иметь определенное расширение. Как вы создаете HighestQualityVideo, так и должно быть.mov.

2. Вы должны предоставить правильный outputFileType должен быть в соответствии с расширением и presetType. так что в вашем случае это должно быть _assetExport.outputFileType = AVFileTypeQuickTimeMovie;,

Попробуйте с этими изменениями.

Обновление: чтобы удалить ошибку, вам нужно заменить код на AVAssetTrack со следующим кодом в addAudioToFileAtPath метод:

NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeAudio];
if([tracks count]>0)
{
AVAssetTrack * audioAssetTrack = [tracks objectAtIndex:0];
    AVMutableCompositionTrack *compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio 
                                                                                preferredTrackID: kCMPersistentTrackID_Invalid];

    [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAsset.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:nil];  


    //nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
    [audioAsset release];audioAsset = nil;
}

Что касается типов видео:

Facebook поддерживает QuickTimeVideo(mov/qt), см. http://www.facebook.com/help/?faq=218673814818907

Для поддержки других типов видео вам необходимо изменить presetName при создании AVAssetExportSession Объект и расширение выходного файла для этого, пожалуйста, просмотрите этот документ.

http://www.google.co.in/url?sa=t&rct=j&q=AVAssetExportSession++class&source=web&cd=1&ved=0CCYQFjAA&url=http%3A%2F%2Fdeveloper.apple.com%2Flibrary%2Fios%2FDOCUMENTATION%2FAVFoundation%2FReference%2FAVAssetExportSession_Class%2FReference%2FReference.html&ei=xXxPT5akDsG8rAeck5XUDQ&usg=AFQjCNH1HqxIiT1kYJom6kZ82NS-qjVSyQ&cad=rja

Обновление 1:

Здесь мы получаем доступ к каждому изображению и добавляем его в буфер после отображения некоторого времени (я разделил продолжительность.

for (int i=0; i<[image1array count]; i++)
    {
        int time = (int)i*(duration/[image1array count]);
        CVPixelBufferRef buffer = [self pixelBufferFromCGImage:[[image1array objectAtIndex:i] CGImage]];
        [adaptor appendPixelBuffer:buffer withPresentationTime:CMTimeMake(time, 1)];
    }

Обновление 2:

Вот код, в котором я внес несколько изменений, чтобы составить смешанный актив.

-(void) addAudioToFileAtPath:(NSString *)vidoPath andAudioPath:(NSString *)audioFilePath{

    NSURL* audio_inputFileUrl = [NSURL fileURLWithPath:audioFilePath];
    NSURL* video_inputFileUrl = [NSURL fileURLWithPath:ImageVideoPath];
     NSURL* outputFileUrl = [NSURL fileURLWithPath:FinalVideoPath];
    AVMutableComposition *composition = [AVMutableComposition composition];
    AVAsset * audioAsset = [AVURLAsset URLAssetWithURL:audio_inputFileUrl options:nil];;
    AVAsset * videoAsset = [AVURLAsset URLAssetWithURL:video_inputFileUrl options:nil];

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

    NSError *error = nil;
    BOOL ok = NO;

    CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero,videoAsset.duration);
    AVAssetTrack *sourceVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
    ok = [compositionVideoTrack insertTimeRange:video_timeRange ofTrack:sourceVideoTrack atTime:kCMTimeZero error:&error];
    if (!ok) {
        // Deal with the error.
        NSLog(@"Error : %@ : %d",error,videoAsset.duration.value);
    }
    CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
    AVAssetTrack *sourceAudioTrack = [[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    ok = [compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:sourceAudioTrack atTime:kCMTimeZero error:&error];
    if (!ok) {
        // Deal with the error.
        NSLog(@"Error : %@ : %d",error,audioAsset.duration.value);
    }

    AVAssetExportSession* _assetExport = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetHighestQuality];   
    _assetExport.outputFileType = AVFileTypeQuickTimeMovie;
    _assetExport.outputURL = outputFileUrl;

    [_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;
         }
         NSLog(@"Error : %@",_assetExport.error);
     }];    
}

Спасибо,

Одна вещь, которая привлекла мое внимание, это то, что ты звонишь CVPixelBufferPoolRelease(adaptor.pixelBufferPool); в вашем writeImageAndAudioAsMovie:andAudio:duration: метод, но так как вы не создали adaptor.pixelBufferPoolВы не владеете им, и поэтому не должны выпускать его, верно? Мне кажется подозрительным.

Другие вопросы по тегам