【问题标题】:How to play multiple videos in AVQueuePlayer or AVMutableComposition without any gapes or freeze?如何在 AVQueuePlayer 或 AVMutableComposition 中播放多个视频而没有任何间隙或冻结?
【发布时间】:2015-03-18 12:28:45
【问题描述】:

我知道这个问题过去曾被问过几次,我已经阅读了对这些问题的回复。但似乎没有什么能按我想要的方式工作。有多个视频,所有视频都添加到AVQueuePlayer 的队列中。

我已尝试通过其他页面中提到的两种方式添加:

AVPlayerItem *item1 = [AVPlayerItem playerItemWithURL:url1];
AVPlayerItem *item2 = [AVPlayerItem playerItemWithURL:url2];

NSArray *playerItems = [[NSArray alloc] initWithObjects:item1, item2, nil];
avPlayer = [[AVQueuePlayer alloc] initWithItems:playerItems]; 

这样:

    avPlayer = [[AVQueuePlayer alloc] init];

    AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:url1 options:nil];
    NSArray *keys = [NSArray arrayWithObject:@"playable"];

    [asset loadValuesAsynchronouslyForKeys:keys completionHandler:^()
        {
            dispatch_async(dispatch_get_main_queue(), ^
                           {
                               AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:asset1];
                               [avPlayer insertItem:playerItem afterItem:nil];
                           });

        }];
AVURLAsset *asset2 = [[AVURLAsset alloc] initWithURL:url2 options:nil];
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^()
         {
             dispatch_async(dispatch_get_main_queue(), ^
                            {
                                AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:asset2];
                                [avPlayer insertItem:playerItem afterItem:nil];
                            });

         }];

但是在前进到下一个项目时,这些都无法消除黑屏。 在下一个项目开始播放之前有大约 1 秒的间隔。我怎样才能消除这个差距?

更新:我也试过AVMutableComposition。差距显着减少,但仍然很明显。有什么方法可以完全消除这些差距?

AVMutableComposition代码:

AVMutableComposition* mixComposition = [[AVMutableComposition alloc] init];

NSMutableArray *arrayInstruction = [[NSMutableArray alloc] init];

AVMutableVideoCompositionInstruction * MainInstruction =
[AVMutableVideoCompositionInstruction videoCompositionInstruction];
AVMutableCompositionTrack *audioTrack;

audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
                                         preferredTrackID:kCMPersistentTrackID_Invalid];

CMTime duration = kCMTimeZero;

for(int i = 0; i <= 5; i++)
{
    AVAsset *currentAsset;
    currentAsset = [self currentAsset:i]; // i take the for loop for getting the asset
        AVMutableCompositionTrack *currentTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        [currentTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, currentAsset.duration) ofTrack:[[currentAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:duration error:nil];

        [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, currentAsset.duration) ofTrack:[[currentAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:duration error:nil];

        AVMutableVideoCompositionLayerInstruction *currentAssetLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:currentTrack];
        AVAssetTrack *currentAssetTrack = [[currentAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
        ALAssetOrientation currentAssetOrientation  = ALAssetOrientationUp;
        BOOL  isCurrentAssetPortrait  = YES;
        CGAffineTransform currentTransform = currentAssetTrack.preferredTransform;

        if(currentTransform.a == 0 && currentTransform.b == 1.0 && currentTransform.c == -1.0 && currentTransform.d == 0)  {currentAssetOrientation= ALAssetOrientationRight; isCurrentAssetPortrait = YES;}
        if(currentTransform.a == 0 && currentTransform.b == -1.0 && currentTransform.c == 1.0 && currentTransform.d == 0)  {currentAssetOrientation =  ALAssetOrientationLeft; isCurrentAssetPortrait = YES;}
        if(currentTransform.a == 1.0 && currentTransform.b == 0 && currentTransform.c == 0 && currentTransform.d == 1.0)   {currentAssetOrientation =  ALAssetOrientationUp;}
        if(currentTransform.a == -1.0 && currentTransform.b == 0 && currentTransform.c == 0 && currentTransform.d == -1.0) {currentAssetOrientation = ALAssetOrientationDown;}

        CGFloat FirstAssetScaleToFitRatio = 640.0/640.0;
        if(isCurrentAssetPortrait){
            FirstAssetScaleToFitRatio = 640.0/640.0;
            CGAffineTransform FirstAssetScaleFactor = CGAffineTransformMakeScale(FirstAssetScaleToFitRatio,FirstAssetScaleToFitRatio);
            [currentAssetLayerInstruction setTransform:CGAffineTransformConcat(currentAssetTrack.preferredTransform, FirstAssetScaleFactor) atTime:duration];
        }else{
            CGAffineTransform FirstAssetScaleFactor = CGAffineTransformMakeScale(FirstAssetScaleToFitRatio,FirstAssetScaleToFitRatio);
            [currentAssetLayerInstruction setTransform:CGAffineTransformConcat(CGAffineTransformConcat(currentAssetTrack.preferredTransform, FirstAssetScaleFactor),CGAffineTransformMakeTranslation(0, 0)) atTime:duration];
        }
        duration=CMTimeAdd(duration, currentAsset.duration);
        [arrayInstruction addObject:currentAssetLayerInstruction];
}

MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, duration);
MainInstruction.layerInstructions = arrayInstruction;
AVMutableVideoComposition *MainCompositionInst = [AVMutableVideoComposition videoComposition];

MainCompositionInst.instructions = [NSArray arrayWithObject:MainInstruction];
MainCompositionInst.frameDuration = CMTimeMake(1, 30);
MainCompositionInst.renderSize = CGSizeMake(640.0, 640.0);

NSString* filename = [NSString stringWithFormat:@"mergedVideo.mp4"];
pathForFile = [NSTemporaryDirectory() stringByAppendingPathComponent:filename];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL delete = [fileManager removeItemAtPath:pathForFile error:NULL];
NSLog(@"Deletion Succesful???? :: %d",delete);

NSURL *url = [NSURL fileURLWithPath:pathForFile];
NSLog(@"\n\nurl ::::::::::: %@\n\n",url);
NSError *err;
if ([url checkResourceIsReachableAndReturnError:&err] == NO)
    NSLog(@"\n\nFINEEEEEEEEEEEEE\n\n");
else
    NSLog(@"\n\nERRRRRORRRRRRRRRRRRRR\n\n");

AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
exporter.outputURL=url;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.videoComposition = MainCompositionInst;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^
 {
     switch (exporter.status)
     {
         case AVAssetExportSessionStatusCompleted:
         {
             NSURL *outputURL = exporter.outputURL;

             ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
             if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:outputURL]) {

                 ALAssetsLibrary* library = [[ALAssetsLibrary alloc]init];
                 [library writeVideoAtPathToSavedPhotosAlbum:outputURL completionBlock:^(NSURL *assetURL, NSError *error)
                  {
                      NSLog(@"ASSET URL %@",assetURL);
                      if (error)
                      {
                          NSLog(@"EROR %@ ", error);
                      }else{
                          NSLog(@"VIDEO SAVED ");
                      }

                  }];
                 NSLog(@"Video Merge SuccessFullt");
                 currentFile ++;
             }
         }
             break;
         case AVAssetExportSessionStatusFailed:
             NSLog(@"Failed:%@", exporter.error.description);
             break;
         case AVAssetExportSessionStatusCancelled:
             NSLog(@"Canceled:%@", exporter.error);
             break;
         case AVAssetExportSessionStatusExporting:
             NSLog(@"Exporting!");
             break;
         case AVAssetExportSessionStatusWaiting:
             NSLog(@"Waiting");
             break;
         default:
             break;
     }
 }];

【问题讨论】:

  • 我认为第一个解决方案更好,但是这段代码在哪里,在 viewDidLoad 上,请在此处发布您的完整代码。
  • @AcácioVeitSchneider AVQueuePlayer 使用这两种方法看起来都是个坏主意。改变轨道时,它有 1 - 1.5 秒的间隙。它位于从 viewDidLoad 调用的方法中。
  • 尝试使用 mixComposition.duration 代替您的持续时间。

标签: ios objective-c avfoundation avmutablecomposition avqueueplayer


【解决方案1】:

对于 Ultravisual,我们使用了AVMutableComposition,只要我们先构建合成,然后构建播放器来播放它,我们就能在任何地方获得完美的无间隙播放,除了循环播放。

您能否遍历AVMutableComposition 中的所有轨道并确认没有间隙?不要忘记音轨。有时音频和视频有不同的时间戳 - 您可能需要向您的 AVMutableComposition 添加另一个曲目才能解决此问题。

【讨论】:

  • 我发布了我的合并代码。如果您能说出问题所在,我将不胜感激。
  • 我无法通过查看代码来判断。遍历mixComposition.tracks 中的所有AVCompositionTracks,对于每个track,遍历所有AVCompositionTrackSegments 通过track.segments 并将其开始和结束时间打印到调试控制台。复制到文本编辑器,按时间排序,眼球,直到你能看到数字的差距。它可能有助于通过它们是视频还是音频来区分曲目。
  • 我在 AVCompositionTrackSegment 中看不到任何用于打印时间戳的属性。您能详细说明这些属性及其名称吗?
  • 你想要的属性是timeMapping。它在AVAssetTrackSegment 中定义,它是AVCompositionTrackSegment 的超类。 Apple 文档非常适合所有这些课程,并且会告诉您所需的一切。
  • 我打印了日志,但我很困惑。应该有多少轨道?示例:我录制了 5 个视频。 Track 0 有 5 个片段,每个片段都有一个视频 url。到这里就很清楚了。但是每次都有随机段数的Track 1和Track 2。并且大部分段 url 为空。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
相关资源
最近更新 更多