首页 文章

使用AVPlayer的多个视频

提问于
浏览
35

我正在开发一款适用于iPad的iOS应用,需要在屏幕的某些部分播放视频 . 我有几个视频文件需要按照编译时未给出的顺序相互播放 . 它看起来好像只是一个视频播放 . 从一个视频到下一个视频是很好的,这是两个视频的最后一帧或第一帧显示的延迟,但应该没有闪烁或没有内容的白色屏幕 . 视频不包含音频 . 记住内存使用很重要 . 视频具有非常高的分辨率,并且可以同时彼此相邻地播放几个不同的视频序列 .

为了获得这个,我已经尝试了一些解决方案 . 它们列在下面:

1.带有AVComposition的AVPlayer,其中包含所有视频

在这个解决方案中,我有一个AVPlayer,它只能在AVPlayerItem上使用AVComplayer制作,AVComposition包含彼此相邻的所有视频 . 当我去特定的视频时,我会在下一个视频开始的构图中寻找时间 . 这个解决方案的问题是,当寻找玩家时会快速显示它正在寻找的一些帧,这是不可接受的 . 似乎没有办法直接跳到作曲中的特定时间 . 我尝试通过制作刚刚完成的视频中最后一帧的图像来解决这个问题,然后在搜索时显示在AVPLayer前面,最后在搜索完成后删除它 . 我使用AVAssetImageGenerator制作图像,但由于某种原因,图像的质量与视频不同,因此在视频上显示和隐藏图像时会有显着的变化 . 另一个问题是AVPlayer使用大量内存,因为单个AVPlayerItem包含所有视频 .

2.具有多个AVPlayerItem的AVPlayer

此解决方案为每个视频使用AVPlayerItem,并在切换到新视频时替换AVPlayer的项目 . 这个问题是,当切换AVPlayer的项目时,它会在加载新项目时短时间显示白色屏幕 . 为了解决这个问题,可以使用在加载时将图像放在最后一帧的前面的解决方案,但仍然存在图像和视频的质量不同且值得注意的问题 .

3.两个AVPlayers轮流播放AVPlayerItem

我尝试的下一个解决方案是让两个AVPlayer在彼此之上轮流播放AVPlayerItems . 因此,当玩家完成播放时,它将保留在视频的最后一帧 . 另一个AVPlayer将被带到前面(其项目设置为nil,因此它是透明的),下一个AVPlayerItem将被插入到该AVPlayer中 . 一旦加载它就会开始播放,两个视频之间顺畅交易的错觉将完好无损 . 此解决方案的问题是内存使用情况 . 在某些情况下,我需要同时在屏幕上播放两个视频,这将导致4个AVPlayers同时加载AVPlayerItem . 这只是太多的记忆,因为视频可以分辨率非常高 .


有没有人对上面发布的整体问题和尝试过的解决方案有一些想法,建议,意见或建议 .

3 回答

  • 14

    因此,该项目现在正在App Store中运行,现在是时候回到这个主题并分享我的发现并揭示我最终做的事情 .

    What did not work

    我在大型AVComposition上使用其中包含所有视频的第一个选项是不够好的,因为在没有小的擦洗故障的情况下无法跳到合成中的特定时间 . 此外,我在组合中的两个视频之间准确暂停视频时遇到问题,因为API无法为暂停提供帧保证 .

    第三个有两个AVPlayers,让他们轮流在实践中工作得很好 . 特别是在iPad 4或iPhone 5上 . 具有较低RAM量的设备是一个问题,因为在内存中同时有多个视频消耗了太多内存 . 特别是因为我必须处理非常高分辨率的视频 .

    What I ended up doing

    好吧,左边是选项号2.在需要时为视频创建AVPlayerItem并将其提供给AVPlayer . 这个解决方案的好处是内存消耗 . 通过延迟创建AVPlayerItem并在不再需要它们的时刻丢弃它们可以将内存消耗降至最低,这对于支持RAM有限的旧设备非常重要 . 这个解决方案的问题在于,当从一个视频转到另一个视频时,在下一个视频被加载到内存中的同时,会有一个空白屏幕 . 我解决这个问题的想法是在AVPlayer后面放一个图像,以便在玩家缓冲时显示 . 我知道我需要的图像与视频完全像素到像素完美,所以我拍摄了图像是视频的最后一帧和第一帧的精确副本 . 该解决方案在实践中运作良好 .

    The problem with this solution

    我有问题但是如果视频/图像不是它的原始大小或者模块4的缩放,那么UIImageView内的图像位置与AVPlayer内的视频位置不同 . 换句话说,我遇到了如何用UIImageView和AVPlayer处理半像素的问题 . 它似乎没有相同的方式 .

    How I fixed it

    我尝试了很多东西,因为我的应用程序是以交互方式使用不同大小的视频 . 我尝试更改AVPlayerLayer和CALayer的magnificationfilter和minificationFilter以使用相同的算法,但没有真正改变任何东西 . 最后,我最终创建了一个iPad应用程序,可以自动截取我需要的所有尺寸的视频截图,然后在视频缩放到一定大小时使用正确的图像 . 这使得我在显示特定视频的所有尺寸上都是像素完美的图像 . 不是一个完美的工具链,但结果是完美的 .

    Final reflection

    这个位置问题对我来说非常明显(因此非常重要)的主要原因是因为我的应用程序正在播放的视频内容是绘制动画,其中很多内容处于固定位置且只有部分内容图片正在移动 . 如果所有内容仅移动一个像素,则会产生非常明显且难看的故障 . 在今年的WWDC上,我与一位Apple工程师讨论了这个问题,他是AVFoundation的专家 . 当我向他介绍问题时,他的建议基本上是选择3,但我向他解释说这是不可能的,因为内存消耗和我已经尝试过解决方案了 . 有鉴于此,他说我选择了正确的解决方案并要求我在缩放视频时为UIImage / AVPlayer定位提交错误报告 .

  • 43

    你可能已经看过这个了,但是你检查了一下 AVQueuePlayer Documentation

    它被设计用于在队列中播放 AVPlayerItems 并且是AVPlayer的直接子类,因此只需以相同的方式使用它 . 你设置如下:

    AVPlayerItem *firstItem = [AVPlayerItem playerItemWithURL: firstItemURL];
    AVPlayerItem *secondItem = [AVPlayerItem playerItemWithURL: secondItemURL];
    
    AVQueuePlayer *player = [AVQueuePlayer queuePlayerWithItems:[NSArray arrayWithObjects:firstItem, secondItem, nil]];
    
    [player play];
    

    如果要在运行时向队列中添加新项,请使用以下方法:

    [player insertItem:thirdPlayerItem afterItem:firstPlayerItem];
    

    我还没有测试过,看看这是否会减少你提到的闪烁问题,但似乎这就是要走的路 .

  • 1

    更新 - https://youtu.be/7QlaO7WxjGg

    这里是您使用集合视图作为示例的答案,它将一次播放8个(请注意,不需要任何类型的内存管理;您可以使用ARC):

    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
        UICollectionViewCell *cell = (UICollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:kCellIdentifier forIndexPath:indexPath];
    
        // Enumerate array of visible cells' indexPaths to find a match
        if ([self.collectionView.indexPathsForVisibleItems
             indexOfObjectPassingTest:^BOOL(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                 return (obj.item == indexPath.item);
             }]) dispatch_async(dispatch_get_main_queue(), ^{
                 [self drawLayerForPlayerForCell:cell atIndexPath:indexPath];
             });
    
    
        return cell;
    }
    
    - (void)drawPosterFrameForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
        [self.imageManager requestImageForAsset:AppDelegate.sharedAppDelegate.assetsFetchResults[indexPath.item]
                                                              targetSize:AssetGridThumbnailSize
                                                             contentMode:PHImageContentModeAspectFill
                                                                 options:nil
                                                           resultHandler:^(UIImage *result, NSDictionary *info) {
                                                               cell.contentView.layer.contents = (__bridge id)result.CGImage;
                                                           }];
    }
    
    - (void)drawLayerForPlayerForCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
        cell.contentView.layer.sublayers = nil;
        [self.imageManager requestPlayerItemForVideo:(PHAsset *)self.assetsFetchResults[indexPath.item] options:nil resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                if([[info objectForKey:PHImageResultIsInCloudKey] boolValue]) {
                    [self drawPosterFrameForCell:cell atIndexPath:indexPath];
                } else {
                    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:[AVPlayer playerWithPlayerItem:playerItem]];
                    [playerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
                    [playerLayer setBorderColor:[UIColor whiteColor].CGColor];
                    [playerLayer setBorderWidth:1.0f];
                    [playerLayer setFrame:cell.contentView.layer.bounds];
                    [cell.contentView.layer addSublayer:playerLayer];
                    [playerLayer.player play];
                }
            });
        }];
    }
    

    drawPosterFrameForCell方法将图像放置在无法播放视频的位置,因为它存储在iCloud上,而不是设备上 .

    无论如何,这是起点;一旦你理解了它是如何工作的,你就可以做你想要的所有事情,而不会出现你所描述的任何记忆故障 .

相关问题