I have just made a simple app which uses QTKit to merge two videos together
Here is the problem:
I have just discovered that if I try to merge two videos with different width & height the output is a video whose height and width is the same of the big video but contains the small one with black and wide borders.
Question:
How can i merge two videos together and get an output video with a correct format (width/height) ?
Example to understand better what i need:
I have two videos:
Video 1 = 1920 x 1280 VIdeo 2 = 1280 x 960
What i want: A video in 1920 x 1280 (Need to up-scale and crop the small one)
Code:
-(void)voidMethodToCombine {
NSError *err = nil;
QTMovie *myCombinedMovie = [[QTMovie alloc] initToWritableData:[NSMutableData data] error:&err];
if (err)
{
NSRunAlertPanel(@"Errore nella creazione del video",[err localizedDescription], nil, @"Chiudi", nil, nil);
return;
return;
}
// Create the File Open Dialog class.
NSSavePanel *savePanel = [NSSavePanel savePanel]; //Dove salvo il video ?
if ([savePanel runModal])//Se premo ok
{
[indicatore setUsesThreadedAnimation:YES];
[indicatore startAnimation:self];
NSURL *saveURL = savePanel.URL;
NSString *savePath = [saveURL path];
//Prelevo i 2 percorsi e gli inserisco negli array
NSString *video1 = [URLVideo1 stringValue];
NSString *video2 = [URLVideo2 stringValue];
NSURL *theURLvideo1 = [NSURL fileURLWithPath:video1];
NSURL *theURLvideo2 = [NSURL fileURLWithPath:video2];
if((theURLvideo1) && (theURLvideo2)) {
NSArray *myMovieURLs = [NSArray arrayWithObjects:theURLvideo1,theURLvideo2, nil];
for (NSURL *url in myMovieURLs)
{
QTMovie *theMovie = [QTMovie movieWithURL:url error:&err];
if (err){
NSRunAlertPanel(@"Errore durante il caricamento di uno dei film",[err localizedDescription], nil, @"Chiudi", nil, nil);
return;
}
QTTimeRange timeRange = QTMakeTimeRange(QTZeroTime, [theMovie duration]);
QTTime insertionTime = [myCombinedMovie duration];
[myCombinedMovie insertSegmentOfMovie:theMovie timeRange:timeRange atTime:insertionTime];
}
NSDictionary *writeAttributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], QTMovieFlatten, nil];
//Pannello di path salvataggio
bool success = [myCombinedMovie writeToFile:savePath withAttributes:writeAttributes error:&err];
[indicatore stopAnimation:self];
NSBeep();
NSString *dove = [[NSString alloc] initWithFormat:@"Video esportato in:\n%@",savePath];
NSRunAlertPanel(@"Esportazione avvenuta con successo!", dove, @"Ok", nil, nil);
if (!success)
{
NSRunAlertPanel(@"Errore durante il salvataggio del video.",[err localizedDescription], nil, @"Chiudi", nil, nil);
return;
return;
}
}
//Se non esiste alcun percorso
else {
NSRunAlertPanel(@"Impossibile selezionare i file.", @"Controlla di aver specificato un percorso.", @"Chiudi", nil, nil);
}
To do this it's easiest to use AVFoundation frameworks instead of QTKit, as editing/manipulating the video is much easier (imo), though it seems to be a bit slower and the code may be less compact.
I'd suggest starting by reading the AVFoundation programming guide at https://developer.apple.com/library/mac/documentation/AudioVideo/Conceptual/AVFoundationPG/AVFoundationPG.pdf
But here's a basic example to get you started. Before starting, make sure you have linked against the AVFoundation and CoreMedia frameworks.
In your header besides any other methods or ivars that you may have add an AVAssetExportSession *exporter
and NSTimer *timer
, as well as a method - (void) monitorProgress;
.
Your implementation file would then include the following methods (assuming you're triggering the method with an IBAction called doIt). And don't forget to #import <AVFoundation/ AVFoundation.h>
and #import <CoreMedia/CoreMedia.h>
:
- (IBAction)doIt:(id)sender {
// Initial array of movie URLs
NSArray *myMovieURLs = [NSArray arrayWithObjects:[NSURL fileURLWithPath:@"/path/to/first.mov"], [NSURL fileURLWithPath:@"/path/to/second.mov"], nil];
// Create the composition & A/V tracks
AVMutableComposition *comp = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [comp addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *compositionAudioTrack = [comp addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
// A reference for insertion start time
CMTime startTime = kCMTimeZero;
for (int i=0; i< [myMovieURLs count]; i++){
// Get asset
NSURL *movieURL = [myMovieURLs objectAtIndex:i];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:movieURL options:nil];
// Get video and audio tracks (assuming video exists - test for audio as an empty track will crash the program!) and insert in composition tracks
AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
bool success = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:videoTrack atTime:startTime error:nil];
if ([[asset tracksWithMediaType:AVMediaTypeAudio]count]){
AVAssetTrack *audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
success = [compositionAudioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, [asset duration]) ofTrack:audioTrack atTime:startTime error:nil];
}
// increment the start time to the end of this first video
startTime = CMTimeAdd(startTime, [asset duration]);
}
//Set the output URL
NSURL *outputURL = [NSURL fileURLWithPath:@"/path/to/output.mov"];
/* Create the exporter.
Note the preset type is up to you to choose. If you wanted, you could check the asset's size (with [asset naturalSize]) or other values above and use that to base your preset on.
Use exportPresetsCompatibleWithAsset: to get a list of presets that are compatible with a specific asset.
*/
NSLog(@"Compat presets you could use: %@", [AVAssetExportSession exportPresetsCompatibleWithAsset:comp]);
exporter = [[AVAssetExportSession alloc] initWithAsset:comp presetName:AVAssetExportPreset640x480];
[exporter setOutputURL:outputURL];
[exporter setOutputFileType:AVFileTypeQuickTimeMovie];
[exporter exportAsynchronouslyWithCompletionHandler:^(void){
switch ([exporter status]) {
case AVAssetExportSessionStatusFailed:
NSLog(@"Export failed: %@", [[exporter error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"Export canceled");
break;
default:
break;
}
}];
// This is just a simple timer that will call a method to log the progress
timer=[NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:@selector(monitorProgress)
userInfo:nil
repeats:YES];
}
-(void)monitorProgress{
if ([exporter progress] == 1.0){
[timer invalidate];
}
NSLog(@"Progress: %f",[exporter progress]* 100);
}
来源:https://stackoverflow.com/questions/9213088/qtkit-merge-two-videos-with-different-width-and-height