基于iOS Objective-C从MV-HEVC格式3D视频提取左右立体图像
iOS下用Objective-C提取MV-HEVC 3D视频的左右立体图像
核心逻辑
MV-HEVC格式的3D视频一般包含左右眼两个独立的视频轨道,或者单轨道内包含拼接的左右帧。针对多轨道场景,需要先定位到左右眼对应的视频轨道,再分别生成对应时间点的图像;单轨道拼接场景则需提取帧后手动裁剪拆分左右视图。
完整实现代码
多轨道场景处理
NSString *inputVideoPath = @"你的MV-HEVC视频本地路径"; NSURL *inputVideoURL = [NSURL fileURLWithPath:inputVideoPath]; AVURLAsset *inputAsset = [AVURLAsset URLAssetWithURL:inputVideoURL options:nil]; // 获取所有视频轨道 NSArray<AVAssetTrack *> *videoTracks = [inputAsset tracksWithMediaType:AVMediaTypeVideo]; if (videoTracks.count < 2) { NSLog(@"未检测到双视图轨道,切换至单轨道拼接处理逻辑"); goto SingleTrackProcessing; } // 优先通过轨道元数据识别左右眼(示例:用extendedLanguageTag判断) AVAssetTrack *leftEyeTrack = nil; AVAssetTrack *rightEyeTrack = nil; for (AVAssetTrack *track in videoTracks) { NSString *langTag = track.extendedLanguageTag; if ([langTag containsString:@"left"]) { leftEyeTrack = track; } else if ([langTag containsString:@"right"]) { rightEyeTrack = track; } } // 若元数据无标识,默认取前两个轨道作为左右眼 if (!leftEyeTrack || !rightEyeTrack) { leftEyeTrack = videoTracks[0]; rightEyeTrack = videoTracks[1]; } CGFloat frameRate = [leftEyeTrack nominalFrameRate]; NSInteger maxFrameCount = 100; // 根据需求设置最大提取帧数 for (NSInteger i = 1; i <= maxFrameCount; i++) { NSError *error = nil; CMTime time = CMTimeMake(i, (int32_t)frameRate); // 生成左眼图像 AVAssetImageGenerator *leftGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:inputAsset]; leftGenerator.requestedTimeToleranceBefore = kCMTimeZero; leftGenerator.requestedTimeToleranceAfter = kCMTimeZero; leftGenerator.tracks = @[leftEyeTrack]; CGImageRef leftImageRef = [leftGenerator copyCGImageAtTime:time actualTime:NULL error:&error]; if (error) { NSLog(@"左眼帧提取失败:%@", error.localizedDescription); continue; } UIImage *leftImage = [UIImage imageWithCGImage:leftImageRef]; CGImageRelease(leftImageRef); // 生成右眼图像 AVAssetImageGenerator *rightGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:inputAsset]; rightGenerator.requestedTimeToleranceBefore = kCMTimeZero; rightGenerator.requestedTimeToleranceAfter = kCMTimeZero; rightGenerator.tracks = @[rightEyeTrack]; CGImageRef rightImageRef = [rightGenerator copyCGImageAtTime:time actualTime:NULL error:&error]; if (error) { NSLog(@"右眼帧提取失败:%@", error.localizedDescription); continue; } UIImage *rightImage = [UIImage imageWithCGImage:rightImageRef]; CGImageRelease(rightImageRef); // 这里添加保存/处理左右图像的逻辑 NSLog(@"已提取第%ld帧左右眼图像", (long)i); } return; // 单轨道拼接场景处理 SingleTrackProcessing: AVAssetImageGenerator *imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:inputAsset]; imageGenerator.requestedTimeToleranceBefore = kCMTimeZero; imageGenerator.requestedTimeToleranceAfter = kCMTimeZero; CGFloat singleTrackFrameRate = [[inputAsset tracksWithMediaType:AVMediaTypeVideo][0] nominalFrameRate]; for (NSInteger i = 1; i <= maxFrameCount; i++) { NSError *error = nil; CMTime time = CMTimeMake(i, (int32_t)singleTrackFrameRate); CGImageRef imageRef = [imageGenerator copyCGImageAtTime:time actualTime:NULL error:&error]; if (error) { NSLog(@"帧提取失败:%@", error.localizedDescription); continue; } UIImage *fullImage = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); // 假设是左右拼接格式,拆分左右视图 CGFloat width = fullImage.size.width; CGFloat height = fullImage.size.height; CGRect leftRect = CGRectMake(0, 0, width/2, height); CGImageRef leftCropRef = CGImageCreateWithImageInRect(fullImage.CGImage, leftRect); UIImage *leftImage = [UIImage imageWithCGImage:leftCropRef]; CGImageRelease(leftCropRef); CGRect rightRect = CGRectMake(width/2, 0, width/2, height); CGImageRef rightCropRef = CGImageCreateWithImageInRect(fullImage.CGImage, rightRect); UIImage *rightImage = [UIImage imageWithCGImage:rightCropRef]; CGImageRelease(rightCropRef); // 处理拆分后的左右图像 NSLog(@"已拆分第%ld帧左右眼图像", (long)i); }
关键注意事项
- 轨道识别:优先通过轨道的
extendedLanguageTag、metadata等元数据判断左右眼轨道,避免依赖默认顺序导致错误。 - 帧精度:设置
requestedTimeToleranceBefore和requestedTimeToleranceAfter为kCMTimeZero,确保提取精确时间点的帧。 - 内存管理:手动调用
CGImageRelease释放CGImageRef,避免内存泄漏。 - 单轨道适配:若视频是上下堆叠格式,只需修改裁剪的
CGRect参数即可(取上半部分为左眼,下半部分为右眼)。
内容的提问来源于stack exchange,提问作者pocketkai
相关产品推荐
相关产品推荐

