错误域= AVFoundationErrorDomain代码= -11821“无法解码”

尝试将video与AVFoundation合并时,发现了一个奇怪的行为。 我很确定我在某个地方犯了一个错误,但是我太盲目了。 我的目标是合并4个video(稍后会在它们之间交叉淡入淡出)。 每当我试图导出video,我得到这个错误:

Error Domain=AVFoundationErrorDomain Code=-11821 "Cannot Decode" UserInfo=0x7fd94073cc30 {NSLocalizedDescription=Cannot Decode, NSLocalizedFailureReason=The media data could not be decoded. It may be damaged.} 

最有趣的是,如果我不提供AVAssetExportSession AVMutableVideoComposition,那么一切工作正常! 我不明白我做错了什么。 源video从YouTube上下载并具有.mp4扩展名。 我可以用MPMoviePlayerController播放它们。 在检查源代码的同时,请仔细查看AVMutableVideoComposition。 我在iOS模拟器上testing了Xcode 6.0.1中的这个代码。

 #import "VideoStitcher.h" #import <UIKit/UIKit.h> #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> @implementation VideoStitcher { VideoStitcherCompletionBlock _completionBlock; AVMutableComposition *_composition; AVMutableVideoComposition *_videoComposition; } - (instancetype)init { self = [super init]; if (self) { _composition = [AVMutableComposition composition]; _videoComposition = [AVMutableVideoComposition videoComposition]; } return self; } - (void)compileVideoWithAssets:(NSArray *)assets completion:(VideoStitcherCompletionBlock)completion { _completionBlock = [completion copy]; if (assets == nil || assets.count < 2) { // We need at least two video to make a stitch, right? NSAssert(NO, @"VideoStitcher: assets parameter is nil or has not enough items in it"); } else { [self composeAssets:assets]; if (_composition != nil) // if stitching went good and no errors were found [self exportComposition]; } } - (void)composeAssets:(NSArray *)assets { AVMutableCompositionTrack *compositionVideoTrack = [_composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; NSError *compositionError = nil; CMTime currentTime = kCMTimeZero; AVAsset *asset = nil; for (int i = (int)assets.count - 1; i >= 0; i--) //For some reason videos are compiled in reverse order. Find the bug later. 06.10.14 { asset = assets[i]; AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject]; BOOL success = [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, assetVideoTrack.timeRange.duration) ofTrack:assetVideoTrack atTime:currentTime error:&compositionError]; if (success) { CMTimeAdd(currentTime, asset.duration); } else { NSLog(@"VideoStitcher: something went wrong during inserting time range in composition"); if (compositionError != nil) { NSLog(@"%@", compositionError); _completionBlock(nil, compositionError); _composition = nil; return; } } } AVMutableVideoCompositionInstruction *videoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, _composition.duration); videoCompositionInstruction.backgroundColor = [[UIColor redColor] CGColor]; _videoComposition.instructions = @[videoCompositionInstruction]; _videoComposition.renderSize = [self calculateOptimalRenderSizeFromAssets:assets]; _videoComposition.frameDuration = CMTimeMake(1, 600); } - (void)exportComposition { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:@"testVideo.mov"]; NSURL *url = [NSURL fileURLWithPath:myPathDocs]; NSString *filePath = [url path]; NSFileManager *fileManager = [NSFileManager defaultManager]; if ([fileManager fileExistsAtPath:filePath]) { NSError *error; if ([fileManager removeItemAtPath:filePath error:&error] == NO) { NSLog(@"removeItemAtPath %@ error:%@", filePath, error); } } AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:_composition presetName:AVAssetExportPreset1280x720]; exporter.outputURL = url; exporter.outputFileType = AVFileTypeQuickTimeMovie; exporter.shouldOptimizeForNetworkUse = YES; exporter.videoComposition = _videoComposition; [exporter exportAsynchronouslyWithCompletionHandler:^{ [self exportDidFinish:exporter]; }]; } - (void)exportDidFinish:(AVAssetExportSession*)session { NSLog(@"%li", session.status); if (session.status == AVAssetExportSessionStatusCompleted) { NSURL *outputURL = session.outputURL; // time to call delegate methods, but for testing purposes we save the video in 'photos' app ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL]) { [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error){ if (error == nil) { NSLog(@"successfully saved video"); } else { NSLog(@"saving video failed.\n%@", error); } }]; } } else if (session.status == AVAssetExportSessionStatusFailed) { NSLog(@"VideoStitcher: exporting failed.\n%@", session.error); } } - (CGSize)calculateOptimalRenderSizeFromAssets:(NSArray *)assets { AVAsset *firstAsset = assets[0]; AVAssetTrack *firstAssetVideoTrack = [[firstAsset tracksWithMediaType:AVMediaTypeVideo] firstObject]; CGFloat maxWidth = firstAssetVideoTrack.naturalSize.height; CGFloat maxHeight = firstAssetVideoTrack.naturalSize.width; for (AVAsset *asset in assets) { AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] firstObject]; if (assetVideoTrack.naturalSize.width > maxWidth) maxWidth = assetVideoTrack.naturalSize.width; if (assetVideoTrack.naturalSize.height > maxHeight) maxHeight = assetVideoTrack.naturalSize.height; } return CGSizeMake(maxWidth, maxHeight); } @end 

感谢您的关注。 我真的很累,我一直试图find连续四个小时的错误。 我现在去睡觉

我终于find了解决办法。 对错误的描述导致我的方向错误:“无法解码,媒体数据无法解码,有可能被破坏”。 从这个描述你可能会认为你的video文件有问题。 我花了5个小时试验格式,debugging等。

那么,答案是完全不同的!

我的错误是,我忘了CMTimeADD()返回值。 我认为它改变了它的第一个参数的值,在代码中你可以看到:

 CMTime currentTime = kCMTimeZero; for (int i = (int)assets.count - 1; i >= 0; i--) { CMTimeAdd(currentTime, asset.duration); //HERE!! I don't actually increment the value! currentTime is always kCMTimeZero } videoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, _composition.duration); // And that's where everything breaks! 

我学到的教训是:使用AVFoundation时,总是检查你的时间值! 这是非常重要的,否则你会得到很多的错误。