VIMediaCache icon indicating copy to clipboard operation
VIMediaCache copied to clipboard

如果视频拖动快进这时候会有声音但画面卡住了

Open smallqiang opened this issue 7 years ago • 15 comments

用了你的这个库之后,如果我对视频进行拖动快进播放当缓存后将只听到有声音但画面卡住没有动,但再拖一下就好了,请问有遇到这样的问题吗?

smallqiang avatar May 06 '17 09:05 smallqiang

这个问题必现吗? URL 提供一下?

vitoziv avatar May 09 '17 14:05 vitoziv

我有某些视频也出现这个情况了,暂停一下,再播放就可以了,但是不知道是什么问题导致的

callmewenxi avatar May 15 '17 02:05 callmewenxi

@callmewenxi 你们能提供一下比较容易出问题的 url 地址吗? 我这几天可以看看是哪里有问题

vitoziv avatar May 15 '17 02:05 vitoziv

http://video.nonolive.com:80/download/media/sg/nonolive/video/f508c829204573b5744e5ad740d43091.mp4 http://video.nonolive.com/download/media/sg/nonolive/video/9738ca6ed49c9f5474670496aa7989bc.mp4 http://video.nonolive.com:80/download/media/sg/nonolive/video/5bbb5d2adc593e0e309d49b87b0d2de8.mp4 这些视频有的有可能播着播着没声音, http://video.nonolive.com:80/download/media/sg/nonolive/video/a720385a53bc9c7b81a5d30daa9a62d7.mp4 这个视频是有可能卡住画面额,但是暂停再播放一次就正常,但不是必现,不知道是不是网络卡顿的时候导致的问题,因为我发现出现这个情况的时候,都是在刚播放开始,然后卡了一下,在缓冲,之后再自动播起来的时候就出现了声音在播,视频画面不动的情况了

callmewenxi avatar May 15 '17 03:05 callmewenxi

好像跟视频源地址没有关系的,我用不同的链接都会有概率发生,就是拖动进度的时候会。

smallqiang avatar May 15 '17 08:05 smallqiang

http://video.nonolive.com:80/download/media/sg/nonolive/video/ee835db8e7ec9a5f294d8b5cf8884ec7.mp4 这个视频地址几乎是必现,在我这边测试

callmewenxi avatar May 17 '17 16:05 callmewenxi

iOS 10 的吧AVPlayer 的 automaticallyWaitsToMinimizeStalling 属性设置为YES,我的问题解决了,具体看官方文档

callmewenxi avatar May 18 '17 03:05 callmewenxi

我是已经加了但还是会那样,其实通常是出现在视频比较长的情况下,拖动到比较后的位置就会发现这样的情况,有声音的,但是画面就会卡住。

smallqiang avatar May 18 '17 07:05 smallqiang

@callmewenxi 事实上,根据文档说明 automaticallyWaitsToMinimizeStalling 应该设置为 NO。 在 iOS 10 播放文件类型的视频,automaticallyWaitsToMinimizeStalling 默认就是 YES 的。

而且设置了 automaticallyWaitsToMinimizeStalling 会导致开始播放第一帧的延迟,因为这个设置为 YES 的话,AVPlayer 会去判断当前的网速下,视频是否可以一直播放不会卡顿,满足这个条件预测时才会开始播放。

下面是文档原话:

In versions of iOS prior to iOS 10.0 and versions of OS X prior to 10.12, this property is unavailable, and the behavior of the AVPlayer corresponds to the type of content being played. For streaming content, including HTTP Live Streaming, the AVPlayer acts as if automaticallyWaitsToMinimizeStalling is YES. For file-based content, including file-based content accessed via progressive http download, the AVPlayer acts as if automaticallyWaitsToMinimizeStalling is NO.

If you employ an AVAssetResourceLoader delegate that loads media data for playback, you should set the value of your AVPlayer’s automaticallyWaitsToMinimizeStalling property to NO. Allowing the value of automaticallyWaitsToMinimizeStalling to remain YES when an AVAssetResourceLoader delegate is used for the loading of media data can result in poor start-up times for playback and poor recovery from stalls, because the behaviors provided by AVPlayer when automaticallyWaitsToMinimizeStalling has a value of YES depend on predictions of the future availability of media data that that do not function as expected when data is loaded via a client-controlled means, using the AVAssetResourceLoader delegate interface.

You can allow the value of automaticallyWaitsToMinimizeStalling to remain YES if you use an AVAssetResourceLoader delegate to manage content keys for FairPlay Streaming, to provide dynamically-generated master playlists for HTTP Live Streaming, or to respond to authentication challenges, but not to load media data for playback.

最重要的是,无论设置 automaticallyWaitsToMinimizeStalling 为 YES 还是 NO,我这边都能复现播放没有声音的问题,所以根本原因和这个属性没有关系。不过这里设置为 NO 是官方推荐的选择。

vitoziv avatar Jun 05 '17 15:06 vitoziv

@vitoziv

我用charles抓包发现,有很多请求的Range是2-end, 但是响应了一部分后,这个请求会被cancel掉,然后从当前的startoffset发起一个新的请求。我把同样的地址mock到其他带预加载和边缓存边播放功能的短视频App上,他们会让这个2-end的请求一直执行到finish状态,而且播放正常,没有出现声音消失的情况。然后我就试着按照这个思路改了代码,最后发现:

播放到一半没有声音是因为VIResourceLoader实现方式有问题。 应该把VIResourceLoadingRequestWorkers做成一个workers队列(NSMutableArray),每次有新request过来,不能把正在进行的request给cancel掉,而应该把新生成的worker放workers队列尾部,最后让workers串行工作:上一个worker执行完毕后检查队列是否有待执行列表,如果有,执行队列中的第一个worker。

原先的这种实现方式有问题:

- (void)addRequest:(AVAssetResourceLoadingRequest *)request {
    [self.pendingRequestWorkers enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, VIResourceLoadingRequestWorker * _Nonnull obj, BOOL * _Nonnull stop) {
        [obj cancel];
        [obj finish];
    }];
    [self.pendingRequestWorkers removeAllObjects];
    
    [self startWorkerWithRequest:request];
}

我大概按照这样的思路去做, 发现没有声音的问题解决了:

- (void)addRequest:(AVAssetResourceLoadingRequest *)request {
    dispatch_async(dispatch_get_main_queue(), ^{
        VIResourceLoadingRequestWorker *requestWorker = [[VIResourceLoadingRequestWorker alloc] initWithMediaDownloader:self.mediaDownloader
                                                                                                 resourceLoadingRequest:request];
        requestWorker.delegate = self;
        
        [self.pendingWorkers addObject:requestWorker];
        [self tryStartWork];
    });
}

- (void)tryStartWork
{
    if (self.pendingWorkers.count == 0 || self.currentWorker) {
        return ;
    }
    
    VIResourceLoadingRequestWorker *worker = [self.pendingWorkers firstObject];
    self.currentWorker = worker;
    [worker startWork];
}

- (void)resourceLoadingRequestWorker:(VIResourceLoadingRequestWorker *)requestWorker didCompleteWithError:(NSError *)error {
    dispatch_async(dispatch_get_main_queue(), ^{
        [requestWorker finish];
        if (error && [self.delegate respondsToSelector:@selector(resourceLoader:didFailWithError:)]) {
            [self.delegate resourceLoader:self didFailWithError:error];
        }
        
        [self.pendingWorkers removeObject:requestWorker];
        if ([requestWorker isEqual:self.currentWorker]) {
            self.currentWorker = nil;
            [self tryStartWork];
        }
    });
}

tonyzhou avatar Aug 01 '17 02:08 tonyzhou

这样的话,player 做 seek 操作的时候,需要等待播放器持续下载到 seek 的位置才可以继续播放。

cancel 的方式的确有问题,这里的下载策略要修改,不过最新的 request 依然需要优先响应,之前的 request 可以取消下载,但不调用 request 的 cancel,等最新的 request 处理完后,再会过来处理之前的 request(如果 request 还没有被 player 取消的话)

我有空了也试试

vitoziv avatar Aug 01 '17 10:08 vitoziv

你好,可以自定义输出文件名么

jiangxiaopeng avatar Nov 20 '17 13:11 jiangxiaopeng

@jiangxiaopeng 不行哦,只能用 VICacheManager 修改文件存储路径

vitoziv avatar Nov 21 '17 14:11 vitoziv

@tonyzhou 多 request 处理的实现了一下,可以用这个commit 试试 21926ff

vitoziv avatar Dec 02 '17 15:12 vitoziv

@tonyzhou 你好,我按照你这个方法去试了,但会一直卡在缓存的界面,一直转圈圈,拖动一下进度,就完全正常播放了,这是咋回事了?

mapleleaf99 avatar Jan 06 '21 03:01 mapleleaf99