AVFoundationで動画をリサイズ + 反転する
前回の続きで、動画を反転させる処理を加えました。
というのも、iPhoneやiPadって、撮影時のデバイスの向きによっては
書きだした際に反転してしまったりするのです。
撮影 => 書出し まではよくても、AVAssetExportSessionを使って変換した際に
たぶんVideoOrientationの値を判定して書出し、みたいなことはやってくれないので、
自分で反転、回転を行う必要があります。
以下コード。
// 書き出した動画をリサイズ + 回転 +(void)exportVideoFile:(AVAsset *)_asset toPath:(NSString *)filePath { AVAsset* asset = _asset; // ----------------------------------------------------------------- // タイムラインを用意する // ----------------------------------------------------------------- // 映像と音声を編集するための入れ物を用意する(ビデオ編集ソフトでいうタイムライン?) AVMutableVideoComposition* videoComposition = [AVMutableVideoComposition videoComposition]; videoComposition.renderSize = CGSizeMake(VIDEO_SIZE, VIDEO_SIZE); // 動画のサイズを決める videoComposition.frameDuration = CMTimeMake(1, VIDEO_FRAME_RATE); // 動画のフレームレートを決める // 動画に変換などの処理を加えるための構造体 AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(VIDEO_SECONDS, VIDEO_FRAME_RATE) ); // ----------------------------------------------------------------- // 縮小、回転などの変換を行う // ----------------------------------------------------------------- // 渡されたAssetの中からビデオトラックを抜き出す AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; // ビデオトラックを変換するためのLayerInstruction を抜き出す AVMutableVideoCompositionLayerInstruction* transformer = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; CGAffineTransform finalTransform; float shortMember = MIN(videoTrack.naturalSize.height, videoTrack.naturalSize.width); float longMember = MAX(videoTrack.naturalSize.height, videoTrack.naturalSize.width); float scaleRate = VIDEO_SIZE / shortMember; float surplus = (longMember*scaleRate - VIDEO_SIZE)/2; CGAffineTransform shrinkTransform = CGAffineTransformMakeScale(scaleRate, -scaleRate); CGAffineTransform translateToCenter = CGAffineTransformMakeTranslation(-surplus , VIDEO_SIZE); finalTransform = CGAffineTransformConcat( shrinkTransform, translateToCenter); [transformer setTransform:finalTransform atTime:kCMTimeZero]; // ----------------------------------------------------------------- // 変換を適用する // ----------------------------------------------------------------- instruction.layerInstructions = [NSArray arrayWithObject:transformer]; videoComposition.instructions = [NSArray arrayWithObject:instruction]; // ----------------------------------------------------------------- // 動画に書き出す // ----------------------------------------------------------------- AVAssetExportSession *exportSession; exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetMediumQuality]; exportSession.videoComposition = videoComposition; [[NSFileManager defaultManager] removeItemAtPath:filePath error:nil]; exportSession.outputURL = [NSURL fileURLWithPath:filePath]; exportSession.outputFileType = AVFileTypeMPEG4; [exportSession exportAsynchronouslyWithCompletionHandler:^ { switch ([exportSession status]) { case AVAssetExportSessionStatusFailed: { NSString *msg = [NSString stringWithFormat:@"動画を保存するのに十分な空き容量があるか確認して下さい。 %@", [exportSession.error localizedDescription]]; [CSCommon showAlert:@"動画の書き出しに失敗しました" withMessage:msg]; [[NSNotificationCenter defaultCenter] postNotificationName:CSVideoFileExporterDidFailExport object:nil]; break; } case AVAssetExportSessionStatusCancelled: { NSString *msg = [NSString stringWithFormat:@"動画を保存するのに十分な空き容量があるか確認して下さい。 %@", [exportSession.error localizedDescription]]; [CSCommon showAlert:@"動画の書き出しがキャンセルされました" withMessage:msg]; [[NSNotificationCenter defaultCenter] postNotificationName:CSVideoFileExporterDidFailExport object:nil]; break; } case AVAssetExportSessionStatusCompleted : { [[NSNotificationCenter defaultCenter] postNotificationName:CSVideoFileExporterDidFinishExport object:nil]; break; } } }]; }