iOS объединяет три видео - вращайте центр видео
У меня есть три видео. Первый от задней камеры. Второй от передней камеры, а третий снова от задней камеры. Видеоролики всегда снимаются в ландшафтном режиме с кнопкой "Домой" справа.
Видеоролики, расположенные сзади, имеют правильную ориентацию Центральное видео, снятое с помощью фронтальной камеры, поворачивается на 180 градусов (вверх ногами). Я безуспешно исследовал и попробовал многочисленные методы для трансформации центрального видео. Я получаю одинаковые результаты каждый раз.
Я очень разочарован всем этим процессом. Все, что я читаю онлайн и комментарии / предложения от рецензента, должно работать, но это не работает. Видео одно и то же, что бы я ни старался для трансформаций. Это постоянно действует так, как будто я не применял никаких преобразований. Ничего такого. Я не понимаю, почему преобразования игнорируются на этом. Я потратил недели на это, и я в конце - это просто не работает.
Вот текущая итерация моего кода:
- (void)mergeVideos2:(NSMutableArray *)assets withCompletion:(void (^)(NSString *))completion {
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
__block NSMutableArray *instructions = [[NSMutableArray alloc] init];
__block CGSize size = CGSizeZero;
__block CMTime time = kCMTimeZero;
__block AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];
__block CGAffineTransform transformflip = CGAffineTransformMakeScale(1, -1);
// __block CGAffineTransform transformflip = CGAffineTransformMakeRotation(M_PI);
__block int32_t commontimescale = 600;
[assets enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *assetUrl = (NSURL *)obj;
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
CMTime cliptime = CMTimeConvertScale(asset.duration, commontimescale, kCMTimeRoundingMethod_QuickTime);
NSLog(@"%s: Number of tracks: %lu", __PRETTY_FUNCTION__, (unsigned long)[[asset tracks] count]);
AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
NSError *error;
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, cliptime)
ofTrack:assetTrack
atTime:time
error:&error];
if (error) {
NSLog(@"%s: Error - %@", __PRETTY_FUNCTION__, error.debugDescription);
}
AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
CGAffineTransform transform = assetTrack.preferredTransform;
[videoLayerInstruction setTransform:CGAffineTransformConcat(transform, transformflip) atTime:time];
// the main instruction set - this is wrapping the time
AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionInstruction.timeRange = CMTimeRangeMake(time, assetTrack.timeRange.duration);
if (videoLayerInstruction != nil)
videoCompositionInstruction.layerInstructions = @[videoLayerInstruction];
[instructions addObject:videoCompositionInstruction];
// time increment variables
time = CMTimeAdd(time, cliptime);
if (CGSizeEqualToSize(size, CGSizeZero)) {
size = assetTrack.naturalSize;;
}
}];
mutableVideoComposition.instructions = instructions;
// set the frame rate to 9fps
mutableVideoComposition.frameDuration = CMTimeMake(1, 12);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
int number = arc4random_uniform(10000);
self.outputFile = [documentsDirectory stringByAppendingFormat:@"/export_%i.mov",number];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition
presetName:AVAssetExportPreset1280x720];
exporter.outputURL = [NSURL fileURLWithPath:self.outputFile];
//Set the output file type
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// get the size of the file
unsigned long long size= ([[[NSFileManager defaultManager] attributesOfItemAtPath:self.outputFile error:nil] fileSize]);
NSString *filesize = [NSByteCountFormatter stringFromByteCount:size countStyle:NSByteCountFormatterCountStyleFile];
NSString *thereturn = [NSString stringWithFormat:@"%@: %@", self.outputFile, filesize];
NSLog(@"Export File (Final) - %@", self.outputFile);
completion(thereturn);
});
}
Есть идеи или предложения?
1 ответ
Каждый AVAssetTrack имеет preferredTransform
имущество. Он содержит информацию о том, как повернуть и перевести видео, чтобы правильно его отобразить, чтобы вам не приходилось догадываться. Используйте предпочтительный преобразование каждого видео в каждой инструкции слоя.
Не устанавливайте "videoCompositionTrack.preferredTransform = ..."
Удалить рампу преобразования "[videoLayerInstruction setTransformRampFromStartTransform:..."
В этом перечислении просто используйте:
CGAffineTransform transform = assetTrack.preferredTransform;
[videoLayerInstruction setTransform:transform atTime:time];
Я предполагаю, что ваши видео снимаются с теми же размерами, что и ваш вывод, с серединой видео, имеющей обратную ширину и высоту. Если это не так, вам нужно добавить соответствующее масштабирование:
float scaleFactor = ...// i.e. (outputWidth / videoWidth)
CGAffineTransform scale = CGAffineTransformMakeScale(scaleFactor,scaleFactor)
transform = CGAffineTransformConcat(transform, scale);
[videoLayerInstruction setTransform:transform atTime:time];
РЕДАКТИРОВАТЬ: Похоже, что исходные видео, которые появились в композиции с ног на голову, были с ног на голову, но имели идентичность CGAffineTransform. Этот код работал, чтобы показать их в правильной ориентации:
- (void)mergeVideos2:(NSMutableArray *)assets withCompletion:(void (^)(NSString *))completion {
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
__block NSMutableArray *instructions = [[NSMutableArray alloc] init];
__block CMTime time = kCMTimeZero;
__block AVMutableVideoComposition *mutableVideoComposition = [AVMutableVideoComposition videoComposition];
__block int32_t commontimescale = 600;
// Create one layer instruction. We have one video track, and there should be one layer instruction per video track.
AVMutableVideoCompositionLayerInstruction *videoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
[assets enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *assetUrl = (NSURL *)obj;
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
CMTime cliptime = CMTimeConvertScale(asset.duration, commontimescale, kCMTimeRoundingMethod_QuickTime);
NSLog(@"%s: Number of tracks: %lu", __PRETTY_FUNCTION__, (unsigned long)[[asset tracks] count]);
AVAssetTrack *assetTrack = [asset tracksWithMediaType:AVMediaTypeVideo].firstObject;
CGSize naturalSize = assetTrack.naturalSize;
NSError *error;
//insert the video from the assetTrack into the composition track
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, cliptime)
ofTrack:assetTrack
atTime:time
error:&error];
if (error) {
NSLog(@"%s: Error - %@", __PRETTY_FUNCTION__, error.debugDescription);
}
CGAffineTransform transform = assetTrack.preferredTransform;
//set the layer to have this videos transform at the time that this video starts
if (<* the video is an intermediate video - has the wrong orientation*>) {
//these videos have the identity transform, yet they are upside down.
//we need to rotate them by M_PI radians (180 degrees) and shift the video back into place
CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(M_PI);
CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(naturalSize.width, naturalSize.height);
[videoLayerInstruction setTransform:CGAffineTransformConcat(rotateTransform, translateTransform) atTime:time];
} else {
[videoLayerInstruction setTransform:transform atTime:time];
}
// time increment variables
time = CMTimeAdd(time, cliptime);
}];
// the main instruction set - this is wrapping the time
AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,mutableComposition.duration); //make the instruction last for the entire composition
videoCompositionInstruction.layerInstructions = @[videoLayerInstruction];
[instructions addObject:videoCompositionInstruction];
mutableVideoComposition.instructions = instructions;
// set the frame rate to 9fps
mutableVideoComposition.frameDuration = CMTimeMake(1, 12);
//set the rendersize for the video we're about to write
mutableVideoComposition.renderSize = CGSizeMake(1280,720);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
int number = arc4random_uniform(10000);
self.outputFile = [documentsDirectory stringByAppendingFormat:@"/export_%i.mov",number];
//let the rendersize of the video composition dictate size. use quality preset here
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mutableComposition
presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL = [NSURL fileURLWithPath:self.outputFile];
//Set the output file type
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mutableVideoComposition;
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// get the size of the file
unsigned long long size= ([[[NSFileManager defaultManager] attributesOfItemAtPath:self.outputFile error:nil] fileSize]);
NSString *filesize = [NSByteCountFormatter stringFromByteCount:size countStyle:NSByteCountFormatterCountStyleFile];
NSString *thereturn = [NSString stringWithFormat:@"%@: %@", self.outputFile, filesize];
NSLog(@"Export File (Final) - %@", self.outputFile);
completion(thereturn);
});
}