2011-07-04 14 views
27

Estoy tratando de combinar varios videoclips en uno usando AVFoundation. puedo crear un solo vídeo utilizando AVMutableComposition con el siguiente códigoCómo combinar videoclips con diferente orientación con AVFoundation

AVMutableComposition *composition = [AVMutableComposition composition]; 

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

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

CMTime startTime = kCMTimeZero; 

/*videoClipPaths is a array of paths of the video clips recorded*/ 

//for loop to combine clips into a single video 
for (NSInteger i=0; i < [videoClipPaths count]; i++) { 

    NSString *path = (NSString*)[videoClipPaths objectAtIndex:i]; 

    NSURL *url = [[NSURL alloc] initFileURLWithPath:path]; 

    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; 
    [url release]; 

    AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 
    AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; 

    //set the orientation 
    if(i == 0) 
    { 
     [compositionVideoTrack setPreferredTransform:videoTrack.preferredTransform]; 
    } 

    ok = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:videoTrack atTime:startTime error:nil]; 
    ok = [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:audioTrack atTime:startTime error:nil]; 

    startTime = CMTimeAdd(startTime, [asset duration]); 
} 

//export the combined video 
NSString *combinedPath = /* path of the combined video*/; 

NSURL *url = [[NSURL alloc] initFileURLWithPath: combinedPath]; 

AVAssetExportSession *exporter = [[[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPreset640x480] autorelease]; 

exporter.outputURL = url; 
[url release]; 

exporter.outputFileType = [[exporter supportedFileTypes] objectAtIndex:0]; 

[exporter exportAsynchronouslyWithCompletionHandler:^(void){[self combineVideoFinished:exporter.outputURL status:exporter.status error:exporter.error];}]; 

El código anterior funciona bien si todos los clips de vídeo se registraron en la misma orientación (vertical u horizontal). Sin embargo, si tengo una mezcla de orientaciones en los clips, el video final tendrá una parte girada 90 grados hacia la derecha (o hacia la izquierda).

Me preguntaba si hay alguna manera de transformar todos los clips a la misma orientación (por ejemplo, la orientación del primer clip) mientras los está componiendo. Por lo que leo en el documento XCode AVMutableVideoCompositionLayerInstruction parece puede ser utilizado para transformar AVAsset, pero no estoy seguro de cómo crear y aplicar varias instrucciones capa diferente a los clips correspondientes y utilizar luego en la composición (AVMutableComposition*)

cualquier ayuda ¡ser apreciado!

Respuesta

25

Esto es lo que hago. Luego uso una AVAssetExportSession para crear el archivo real. pero te advierto, los CGAffineTransforms a veces se aplican tarde, por lo que verás uno o dos de los originales antes de que el video se transforme. No tengo idea de por qué sucede esto, una combinación diferente de videos arrojará el resultado esperado, pero a veces no funciona.

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

AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; videoComposition.frameDuration = CMTimeMake(1,30); videoComposition.renderScale = 1.0; 

AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; AVMutableVideoCompositionLayerInstruction *layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:compositionVideoTrack]; 

// Get only paths the user selected NSMutableArray *array = [NSMutableArray array]; for(NSString* string in videoPathArray){ 
if(![string isEqualToString:@""]){ 
    [array addObject:string]; 
} 

self.videoPathArray = array; 

float time = 0; 

for (int i = 0; i<self.videoPathArray.count; i++) { 

    AVURLAsset *sourceAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:[videoPathArray objectAtIndex:i]] options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey]]; 

    NSError *error = nil; 

    BOOL ok = NO; 
    AVAssetTrack *sourceVideoTrack = [[sourceAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; 

    CGSize temp = CGSizeApplyAffineTransform(sourceVideoTrack.naturalSize, sourceVideoTrack.preferredTransform); 
    CGSize size = CGSizeMake(fabsf(temp.width), fabsf(temp.height)); 
    CGAffineTransform transform = sourceVideoTrack.preferredTransform; 

    videoComposition.renderSize = sourceVideoTrack.naturalSize; 
    if (size.width > size.height) { 
     [layerInstruction setTransform:transform atTime:CMTimeMakeWithSeconds(time, 30)]; 
    } else { 

     float s = size.width/size.height; 

     CGAffineTransform new = CGAffineTransformConcat(transform, CGAffineTransformMakeScale(s,s)); 

     float x = (size.height - size.width*s)/2; 

     CGAffineTransform newer = CGAffineTransformConcat(new, CGAffineTransformMakeTranslation(x, 0)); 

     [layerInstruction setTransform:newer atTime:CMTimeMakeWithSeconds(time, 30)]; 
    } 

    ok = [compositionVideoTrack insertTimeRange:sourceVideoTrack.timeRange ofTrack:sourceVideoTrack atTime:[composition duration] error:&error]; 

    if (!ok) { 
     // Deal with the error. 
     NSLog(@"something went wrong"); 
    } 

    NSLog(@"\n source asset duration is %f \n source vid track timerange is %f %f \n composition duration is %f \n composition vid track time range is %f %f",CMTimeGetSeconds([sourceAsset duration]), CMTimeGetSeconds(sourceVideoTrack.timeRange.start),CMTimeGetSeconds(sourceVideoTrack.timeRange.duration),CMTimeGetSeconds([composition duration]), CMTimeGetSeconds(compositionVideoTrack.timeRange.start),CMTimeGetSeconds(compositionVideoTrack.timeRange.duration)); 

    time += CMTimeGetSeconds(sourceVideoTrack.timeRange.duration); 
    } 

instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction]; instruction.timeRange = compositionVideoTrack.timeRange; 

videoComposition.instructions = [NSArray arrayWithObject:instruction]; 

Esto es lo que hago. Luego uso una AVAssetExportSession para crear el archivo real. pero te advierto, los CGAffineTransforms a veces se aplican tarde, por lo que verás uno o dos de los originales antes de que el video se transforme. No tengo idea de por qué sucede esto, una combinación diferente de videos arrojará el resultado esperado, pero a veces no funciona.

+0

parece funcionar, sin embargo, no encontré las transformaciones que se aplican más tarde problema – Song

+2

Weird. No estoy intentando fusionar dos archivos, solo estoy tratando de obtener una AVAssetExportSession para mantener la orientación del video. Se supone que solo debes poder llamar '[compositionVideoTrack setPreferredTransform: transform]' pero no funciona. Usar tu método tampoco funcionó para mí. Pero usar * both * funcionó. Suena como un error de framework. También estoy usando la configuración de la sesión de exportación 'AVAssetExportPresetPassthrough'. – Dex

+0

También estoy teniendo problemas muy extraños con el tiempo también. La mayoría de las veces, el último 1 segundo más o menos del video se corta. – Dex

Cuestiones relacionadas