2011-07-04 13 views
27

Próbuję połączyć kilka klipów wideo w jeden za pomocą AVFoundation. mogę utworzyć jeden film za pomocą AVMutableComposition przy użyciu kodu poniżejJak łączyć klipy wideo z inną orientacją za pomocą 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];}]; 

Powyższy kod działa poprawnie, jeśli wszystkie klipy wideo zostały nagrane w tej samej orientacji (pionowej lub poziomej). Jeśli jednak w klipach mam mieszankę orientacji, końcowe wideo będzie miało część obróconą o 90 stopni w prawo (lub w lewo).

Zastanawiam się, czy istnieje sposób przekształcania wszystkich klipów do tej samej orientacji (na przykład orientacji pierwszego klipu) podczas ich komponowania. Z tego co czytałem z dokumentu XCode AVMutableVideoCompositionLayerInstruction wydaje może być używany do przekształcania AVAsset, ale nie jestem pewien, w jaki sposób utworzyć i zastosować kilka różnych instrukcji warstwy do odpowiednich zacisków i używać wtedy w składzie (AVMutableComposition*)

Każda pomoc będzie być docenionym!

Odpowiedz

25

Oto, co robię. Następnie używam AVAssetExportSession do utworzenia rzeczywistego pliku. ale ostrzegam, CGAffineTransformy są czasami stosowane późno, więc zobaczysz jeden lub dwa oryginały przed transformacją wideo. Nie mam pojęcia, dlaczego tak się dzieje, inna kombinacja filmów przyniesie oczekiwany rezultat, ale czasami się wyłącza.

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

Oto, co robię. Następnie używam AVAssetExportSession do utworzenia rzeczywistego pliku. ale ostrzegam, CGAffineTransformy są czasami stosowane późno, więc zobaczysz jeden lub dwa oryginały przed transformacją wideo. Nie mam pojęcia, dlaczego tak się dzieje, inna kombinacja filmów przyniesie oczekiwany rezultat, ale czasami się wyłącza.

+0

wydaje się działać, jednak nie napotkałem transformacji, które zostały zastosowane później problem – Song

+2

Dziwne. Nie próbuję scalić dwóch plików, tylko próbuję uzyskać AVAssetExportSession, aby zachować orientację wideo. Powinieneś po prostu zadzwonić do '[compositionVideoTrack setPreferredTransform: transform]', ale to nie działa. Używanie twojej metody też nie działa. Ale użycie * obu * działało. Brzmi jak błąd ramowy. Używam również ustawienia sesji eksportu "AVAssetExportPresetPassthrough". – Dex

+0

Mam również bardzo dziwne problemy z czasem. Częściej niż nie, ostatnia 1 sekunda filmu zostaje odcięta. – Dex

Powiązane problemy