iOS视频边下边播--缓存播放数据流

作者: 夜千寻墨 | 来源:发表于2016-02-15 15:36 被阅读51941次

google搜索“iOS视频变下边播”,有好几篇博客写到了实现方法,其实只有一篇,其他都是copy的,不过他们都是使用的本地代理服务器的方式,原理很简单,但是缺点也很明显,需要自己写一个本地代理服务器或者使用第三方库httpSever。如果使用httpSever作为本地代理服务器,如果只缓存一个视频是没有问题的,如果缓存多个视频互相切换,本地代理服务器提供的数据很不稳定,crash概率非常大。


这里我采用ios7以后系统自带的方法实现视频边下边播,这里的边下边播不是单独开一个子线程去下载,而是把视频播放的数据给保存到本地。简而言之,就是使用一遍的流量,既播放了视频,也保存了视频。

用到的框架:<AVFoundation/AVFoundation.h>

用到的播放器:AVplayer

先说一下avplayer自身的播放原理,当我们给播放器设置好url等一些参数后,播放器就会向url所在的服务器发送请求(请求参数有两个值,一个是offset偏移量,另一个是length长度,其实就相当于NSRange一样),服务器就根据range参数给播放器返回数据。这就是大致的原理,当然实际的过程还是略微比较复杂。


下面进入主题

产品需求:

  • 1.支持正常播放器的一切功能,包括暂停、播放和拖拽

  • 2.如果视频加载完成且完整,将视频文件保存到本地cache,下一次播放本地cache中的视频,不再请求网络数据

  • 3.如果视频没有加载完(半路关闭或者拖拽)就不用保存到本地cache

实现方案:

  • 1.需要在视频播放器和服务器之间添加一层类似代理的机制,视频播放器不再直接访问服务器,而是访问代理对象,代理对象去访问服务器获得数据,之后返回给视频播放器,同时代理对象根据一定的策略缓存数据。

  • 2.AVURLAsset中的resourceLoader可以实现这个机制,resourceLoader的delegate就是上述的代理对象。

  • 3.视频播放器在开始播放之前首先检测是本地cache中是否有此视频,如果没有才通过代理获得数据,如果有,则直接播放本地cache中的视频即可。

视频播放器需要实现的功能

  • 1.有开始暂停按钮

  • 2.显示播放进度及总时长

  • 3.可以通过拖拽从任意位置开始播放视频

  • 4.视频加载中的过程和加载失败需要有相应的提示

代理对象需要实现的功能

  • 1.接收视频播放器的请求,并根据请求的range向服务器请求本地没有获得的数据

  • 2.缓存向服务器请求回的数据到本地

  • 3.如果向服务器的请求出现错误,需要通知给视频播放器,以便视频播放器对用户进行提示

具体流程图

1.png

视频播放器处理流程

  • 1.当开始播放视频时,通过视频url判断本地cache中是否已经缓存当前视频,如果有,则直接播放本地cache中视频

  • 2.如果本地cache中没有视频,则视频播放器向代理请求数据

  • 3.加载视频时展示正在加载的提示(菊花转)

  • 4.如果可以正常播放视频,则去掉加载提示,播放视频,如果加载失败,去掉加载提示并显示失败提示

  • 5.在播放过程中如果由于网络过慢或拖拽原因导致没有播放数据时,要展示加载提示,跳转到第4步

代理对象处理流程

  • 1.当视频播放器向代理请求dataRequest时,判断代理是否已经向服务器发起了请求,如果没有,则发起下载整个视频文件的请求

  • 2.如果代理已经和服务器建立链接,则判断当前的dataRequest请求的offset是否大于当前已经缓存的文件的offset,如果大于则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向后拖拽,并且超过了已缓存的数据时才会出现)

  • 3.如果当前的dataRequest请求的offset小于已经缓存的文件的offset,同时大于代理向服务器请求的range的offset,说明有一部分已经缓存的数据可以传给播放器,则将这部分数据返回给播放器(此时应该是由于播放器向前拖拽,请求的数据已经缓存过才会出现)

  • 4.如果当前的dataRequest请求的offset小于代理向服务器请求的range的offset,则取消当前与服务器的请求,并从offset开始到文件尾向服务器发起请求(此时应该是由于播放器向前拖拽,并且超过了已缓存的数据时才会出现)

  • 5.只要代理重新向服务器发起请求,就会导致缓存的数据不连续,则加载结束后不用将缓存的数据放入本地cache

  • 6.如果代理和服务器的链接超时,重试一次,如果还是错误则通知播放器网络错误

  • 7.如果服务器返回其他错误,则代理通知播放器网络错误


resourceLoader的难点处理

- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest
{
    [self.pendingRequests addObject:loadingRequest];
    [self dealWithLoadingRequest:loadingRequest];
    
    return YES;
}

播放器发出的数据请求从这里开始,我们保存从这里发出的所有请求存放到数组,自己来处理这些请求,当一个请求完成后,对请求发出finishLoading消息,并从数组中移除。正常状态下,当播放器发出下一个请求的时候,会把上一个请求给finish。

下面这个方法发出的请求说明播放器自己关闭了这个请求,我们不需要再对这个请求进行处理,系统每次结束一个旧的请求,便必然会发出一个或多个新的请求,除了播放器已经获得整个视频完整的数据,这时候就不会再发起请求。

- (void)resourceLoader:(AVAssetResourceLoader *)resourceLoader didCancelLoadingRequest:(AVAssetResourceLoadingRequest *)loadingRequest
{
    [self.pendingRequests removeObject:loadingRequest];
    
}

下面这个方法是对播放器发出的请求进行填充数据

- (BOOL)respondWithDataForRequest:(AVAssetResourceLoadingDataRequest *)dataRequest
{
    long long startOffset = dataRequest.requestedOffset;

    if (dataRequest.currentOffset != 0) {
        startOffset = dataRequest.currentOffset;
    }
    
    if ((self.task.offset +self.task.downLoadingOffset) < startOffset)
    {
        //NSLog(@"NO DATA FOR REQUEST");
        return NO;
    }
    
    if (startOffset < self.task.offset) {
        return NO;
    }
    
    NSData *filedata = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:_videoPath] options:NSDataReadingMappedIfSafe error:nil];
    
    // This is the total data we have from startOffset to whatever has been downloaded so far
    NSUInteger unreadBytes = self.task.downLoadingOffset - ((NSInteger)startOffset - self.task.offset);
    
    // Respond with whatever is available if we can't satisfy the request fully yet
    NSUInteger numberOfBytesToRespondWith = MIN((NSUInteger)dataRequest.requestedLength, unreadBytes);
    
    
    [dataRequest respondWithData:[filedata subdataWithRange:NSMakeRange((NSUInteger)startOffset- self.task.offset, (NSUInteger)numberOfBytesToRespondWith)]];
    
    
    
    long long endOffset = startOffset + dataRequest.requestedLength;
    BOOL didRespondFully = (self.task.offset + self.task.downLoadingOffset) >= endOffset;

    return didRespondFully;
  
    
}

这是对存放所有的请求的数组进行处理

- (void)processPendingRequests
{
    NSMutableArray *requestsCompleted = [NSMutableArray array];  //请求完成的数组
    //每次下载一块数据都是一次请求,把这些请求放到数组,遍历数组
    for (AVAssetResourceLoadingRequest *loadingRequest in self.pendingRequests)
    {
        [self fillInContentInformation:loadingRequest.contentInformationRequest]; //对每次请求加上长度,文件类型等信息
        
        BOOL didRespondCompletely = [self respondWithDataForRequest:loadingRequest.dataRequest]; //判断此次请求的数据是否处理完全
        
        if (didRespondCompletely) {

            [requestsCompleted addObject:loadingRequest];  //如果完整,把此次请求放进 请求完成的数组
            [loadingRequest finishLoading];
            
        }
    }

    [self.pendingRequests removeObjectsInArray:requestsCompleted];   //在所有请求的数组中移除已经完成的
    
}

resourceLoader的难点基本上就是上面这点了,说到播放器,下面便顺便讲下AVPlayer的难点。

难点:对播放器状态的捕获

举个简单的例子,视频总长度60分,现在缓冲的数据才10分钟,然后拖动到20分钟的位置进行播放,在网速较慢的时候,视频从当前位置开始播放,必然会出现一段时间的卡顿,为了有一个更好的用户体验,在卡顿的时候,我们需要加一个菊花转的状态,现在问题就来了。

在拖动到未缓冲区域内,是否需要加菊花转,如果加,要显示多久再消失,而且如果在网速很慢的时候,播放器如果等了太久,哪怕最后有数据了,播放器也已经“死”了,它自己无法恢复播放,这个时候需要我们人为的去恢复播放,如果恢复播放不成功,那么过一段时间需要再次恢复播放,是否恢复播放成功,这里也需要捕获其状态。所以,如果要有一个好的用户体验,我们需要时时知道播放器的状态。

有两个状态需要捕获,一个是正在缓冲,一个是正在播放,监听播放的“playbackBufferEmpty”属性就可以捕获正在缓冲状态,播放器的时间监听器则可以捕获正在播放状态,我的demo中一共有4个状态:

typedef NS_ENUM(NSInteger, TBPlayerState) {
    TBPlayerStateBuffering = 1,
    TBPlayerStatePlaying   = 2,
    TBPlayerStateStopped   = 3,
    TBPlayerStatePause     = 4
};

这样可以对播放器更好的把握和处理了。
然后说一说在缓冲时候的处理,以及缓冲后多久去播放,处理方法:
进入缓冲状态后,缓冲2秒后去手动播放,如果播放不成功(缓冲的数据太少,还不足以播放),那就再缓冲2秒再次播放,如此循环,看详细代码:

- (void)bufferingSomeSecond
{
    // playbackBufferEmpty会反复进入,因此在bufferingOneSecond延时播放执行完之前再调用bufferingSomeSecond都忽略
    static BOOL isBuffering = NO;
    if (isBuffering) {
        return;
    }
    isBuffering = YES;
    
    // 需要先暂停一小会之后再播放,否则网络状况不好的时候时间在走,声音播放不出来
    [self.player pause];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        // 如果此时用户已经暂停了,则不再需要开启播放了
        if (self.isPauseByUser) {
            isBuffering = NO;
            return;
        }
        
        [self.player play];
        // 如果执行了play还是没有播放则说明还没有缓存好,则再次缓存一段时间
        isBuffering = NO;
        if (!self.currentPlayerItem.isPlaybackLikelyToKeepUp) {
            [self bufferingSomeSecond];
        }
    });
}

这个demo花了我很长的时间,实现这个demo我也遇到了很多坑最后才完成的,现在我奉献出来,也许对你会有所帮助。如果你觉得不错,还请为我Star一个,也算是对我的支持和鼓励。
参考文章

demo下载地址

相关文章

网友评论

  • iOS_小绅士://http://220.249.115.46:18080/wav/day_by_day.mp4 //韩语,一般
    //http://220.249.115.46:18080/wav/Lovey_Dovey.mp4 //韩语,有点长
    这两个视频都无法下载完全
  • iOS_小绅士:难道没人测试下吗。我自己写的和楼主的思路很像,但是有些视频,尤其是比较长的视频,无法下载完整,下了作者的demo,也是如此。最终没有下载完全,作者自己的没有这样的问题吗
    小白在成长:建议试试BT或者P2P边下边播技术,或者试试点云影音
  • seven_七夕:您好,想问你一个问题,在iOS 10、9、8主要是10上会有一个闪退在这个函数
    dataRequest respondWithData ,请问你有遇到过吗?要怎么解决呢?是一个EXC_BAD_ACCESS 的闪退
    毛癫子:分享iOS开发的知识,解决大家遇到的问题,欢迎大家投稿~这是小编的一个IOS开发交流群:714042473。
  • f83ead75667e:你好,我在TBPlayer里面看到很多地方发送名字是kTBPlayerProgressChangedNotification的通知,但是不知道谁是接受者,能麻烦讲明一下吗?~~期待答复
    Ko_Neko:只需要在Xcode里搜索kTBPlayerProgressChangedNotification,前缀是add的就是接受者
  • 问夕阙:这个播放完后 点击那个暂停播放按钮 是没用的吗
  • 健健锅:没看懂,就是代理方法实现了 也用session去下载了,然后怎么去播放啊,上面给播放器啊回填数据的方法不太理解是如何回填给播放器的
  • 代码搬运工oO:你好,问下当网络地址是HTTPS时,怎么做可以不认证证书!!
  • Zszen:下的demo用网上速度不错的小视频, 直接就卡在前0.x秒, 然后等半天, 才突然加载完 然后开始播放.... 体验不太好
  • 哄哄的薇薇:请教大侠,在4G环境下,暂停视频播放的同时也想暂停视频的缓冲,从而节省流量,有什么方法吗?
  • HAORAN_Z:你好,麻烦请问一下,你在TBPlayer中自定义了多个通知名称,我只看到发送自定义的通知,没有看到接收自定义的通知,你可以解释一下吗?
  • Sky521:博主,AVFoundation怎么控制缓存进度?当加载url播放,它是直接一次性的缓存全部的视频数据到内存中,可以控制这个进度在当前播放时间的1min内吗?我看爱奇艺就是这样做的,感谢
  • 3409608c2b57:博主大神,请问下播放大视频的时候崩掉了,崩在这行代码了
    [dataRequest respondWithData:[filedata subdataWithRange:NSMakeRange((NSUInteger)startOffset- self.task.offset, (NSUInteger)numberOfBytesToRespondWith)]];
    请问这是什么原因呢?
    3409608c2b57:@seven_七夕 没呢
    seven_七夕:我也遇到过这个问题,你有解决吗?不是必现的而且iOS11上不会有
  • 正直走:你好在看完运行完您代码后,发现TBPlayer在dismiss后重新点击播放按钮,则会出现莫名的问题即有时候无法弹出界面有时播放出现问题,可能是 [self.player replaceCurrentItemWithPlayerItem:self.currentPlayerItem]处问题,望作者指点,急等谢谢。
  • 4a9a6d37177a:lz你好,用你的思路,很好的解决了我的问题,感谢。
    但我还1个疑问和1个问题,
    疑问: shouldWaitForLoadingOfRequestedResource中,即在正常播放,没有任何快进已退的情况下,播放器每次请求的offset顺序感觉完全是乱来的,感觉应该按顺序来请求offset才对呢,这个情况lz知道么?
    问题:我在播放几百m以下的小文件时没有问题,在播放如上G的文件,播放时会出现固定在某个时间点开始,画面不动,声音和时间都继续走的情况,播放多个大文件都有些情况,lz可曾遇到过这样的问题?
  • 雪豹1119:楼主,有一个问题,当我循环播放视频时,有可能导致重新下载数据,请问怎么解决呢?用github上的demo也是同样的问题。
    大好河山_:楼主您好,请问下您在使用这个demo的时候,怎么设置的循环播放啊!!!我这里测试了几次都不正确,,,烦请告知
    雪豹1119:@田伟_a4d9 是的,文件都已经下载到了本地,都可以导出来看到,但是有时AVPlayer还是会重新请求网络。按理说,avplayer第一次全部播放完成,会缓存起来,当循环播放的时候不会再次请求网络,会用缓存的数据。
    4a9a6d37177a:你在第一次播放后,文件全部缓存下来了?
  • YoChar:楼主,你这个框架有记忆播放的功能吗。就是比如播放列表有几个视频,每个视频播放了一段换另一个视频播放。待会回来播放刚才播放过的视频,就从该视频之前退出的地方继续播放。
    35f716b74145:遇到和楼主一样的需求,请问楼主解决了吗?
  • Rumbles:我很奇怪,用大神写的这个播放视频可以边下边播,播放音乐就必须要全部下载完才可以播
  • 小帅er:下了demo但是没有缓存到本地 每次进去还是重新加载
  • iDeveloper:可以参考一翻,使用avplayer发现,有时候缓存比较多了,调用play依然没办法播放,必须等缓存很多很多了,才会播放
  • WS_0909:楼主 Demo 台挺好的 但是现在还不支持划屏快进快退吧 能不能把这个工能也优化上呢 期待期待期待@_@
    洛洛爱吃肉:滑屏快进快退啥意思
  • 8785becd4a6f:明白了,原来是respondWithData这个填充数据,之前还以为那个streaming 是为了实现hls 的分片技术,我无知了
  • 8785becd4a6f:大神,小白问一下啊,是如何实现一次流量,既能播放,又能缓存呢,假设- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForLoadingOfRequestedResource:(AVAssetResourceLoadingRequest *)loadingRequest 这个方法里面只返回Yes,不做任何处理,那么这里能播放,一次流量,然后我看到TBVideoRequestTask 这个setUrl 方法里面,又起了一个网诺请求,这里才是缓存数据的啊。而且那个 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[actualURLComponents URL] cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:20.0];也没有指定缓存,所以我很迷茫,这是两次流量实现的边播放变缓存,还是我理解错了什么,求大神解惑
  • CoffeeKid:我这两天也在研究这个问题,看了作者的解决方案相当于把知识点整理了一遍。原来的项目中没用AVPlayer而是用的阿里云播放器,理解了背后的实现方法以后,我认为可以直接使用AVPlayer,但不明白为什么那么多第三方播放器都不把视频缓存功能加上,这个功能的需求应该很大才对啊,难道是为了赚流量费?:joy:
  • ___魔蝎射___:写的很好
  • 木格措的天空:楼主能详细的说一下为什么不用HTTPSever吗?
  • 哈哈哈我的简书账号:您好,问下您,不使用HLS协议,app能上架嘛
  • 邢泽: 您好 麻烦问下 对于音频这个适用吗? 我发现音频还是等缓冲完成了才能播放 并没有缓冲一点就开始播放
    夜千寻墨:@邢泽 音频也是可以的
  • d51140a38702:请问楼主,怎么做到缓存了10%就开始播放呢?而不是等数据全部加载完了才开始播放呢
    夜千寻墨:@小5_宁 好吧,我还不知道,我当初写这个还是iOS7
    d51140a38702:@夜千寻墨 在10.0以上的系统,就是差不多下载完了才播放,后面发现,在10.0以上加上这个就可以了.self.player.automaticallyWaitsToMinimizeStalling = NO;
    夜千寻墨:@小5_宁 现在就是下载一点就开始播放
  • LeeCen:请问,支持M3U8格式的TS切片下载吗?
  • 383dd5fb6a9b:播放不了呢
  • 383dd5fb6a9b:只要拖过了缓存的视频offset,就不缓存了,可以改一下改成开线程缓存漏的。这样都缓存连续的
    夜千寻墨:@crowBlock 可以实现的
    夜千寻墨:@crowBlock 难度有点大
  • 一本大书:你好,如果要支持断点续传,是不是需要加一个逻辑:记录每一个分段文件是否已经下载完成?
    夜千寻墨:@一本大书 明白你的意思了,我也想过保存下每段下载的数据,有点复杂,后来离职了,就没在弄,可以实现的
  • b5d6eec712a0:请问楼主,服务器返回来url里的视频已经是切割成几个小视频的集合,我要如何加载才可以边下边播
    夜千寻墨:@garfieldxixixi 你拿到哪一块的数据,根据这个range让播放器去seek,播放器会发出一个从当前位置开始的rang请求,然后你把数据返回给播放器就行了!别忘了自己接管自己给播放器返回数据
  • 出门右转掘金见:楼主能否详细解释一下self.task.offset + self.task.downLoadingOffset + 1024 * 300 这几个参数的意义,第一次做视频不明白,谢谢
    出门右转掘金见:就解释下1024是为什么就行,前面的我弄懂了
  • e30f664859d3:1 看你代码只实现了缓存功能,比如我拖动进度条 到之前已经缓存的部分 ,怎么用上缓存的数据,在你代码里面没找到这部分
    2 看视频的时候 中间拖动了一进度条 这样缓存的时候中间就有一部分空白,这种情况 怎么重复利用这种缓存数据?
  • 85b85d4f76eb:感谢楼主分享,我拿来做音频遇到一个问题,有个getSchemeVideoURL的方法,是在http前面拼上streaming,我调用这个方法后请求失败,不调用这个方法就没有问题了,不太懂这里。楼主看到的话可否帮我答疑一下,谢谢啦:heart:
    夜千寻墨:@KKKKKKK 加streaming或者加任意一个字符串都是一样的,它的目的就是为了让播放器处理后的地址请求不到数据,然后我们自己接管给播放器填充数据的方法
  • 497364cad8e5:真棒,算是了解了拖拽播放的原理了,谢谢博主分享
  • WARRON: 大赞一个, 待会儿在你的基础上做个断点续传就更加完美了,以及下载了的数据 都保存到 缓存中去, 下次请求 直接判断某一段的视屏 在缓存中是否有数据
    夜千寻墨:@WARRON 这只是这个很粗糙的简版,还有很多可以优化和完善的
  • MenThus:首先感谢楼主的分享
    github已star

    然后我还有个小小的疑问,参考Bilibili的播放器,在拖动进度条的时候,能够获取某一秒的帧图像,请问楼主知道这种功能是如何做到的吗?
    MenThus:@夜千寻墨 你说的那个方法貌似是同步的,会卡主线程。:fearful:
    夜千寻墨:@官辉 系统里就有这个方法,提供时间就可以获取到每一秒的图片
  • 萧旭:只顾着问问题了,,
    楼主给力!
  • 萧旭:为什么会出现代理向服务器的offset小于缓存下来数据的offset呢?
    不应该是如果播放进度没有缓存下来的快的话,就有去影响代理向服务器的请求么(让它接着下载)?
    夜千寻墨:@JeanRookieXu 因为可以拖动,什么情况的offset都有可能出现,如果没有拖动,逻辑就简单很多了
  • macfai:楼主,你的github代码下载下来,运行出来就是一个红色北京吗,没有播放视频的例子吗,恳请楼主能提供一下,多谢了
    夜千寻墨:@macfai 我在里面写了几个视频地址吧,可能不能用了
  • macfai:楼主,佩服你的学习精神,竟然直接看英文网站,:smile:
  • 木_风:我们服务器的视频只能下载完才能播,这个是服务器需要改什么吗:grin:
  • BobooO:这种方案没办法缓存playlist格式的视频文件 比如M3U8
  • af62ac510495:厉害了 word哥
  • runningJoey:简书的第一次打赏给了你,谢谢你的分享,谢谢
  • e763f1ec1cfe:感觉不错
  • akali:楼主,看了你的demo ,下载这块看明白了,怎么给播放器填充数据能再说一下吗,一个正在下载的视频文件,播放器能播放吗
    夜千寻墨:@akali 我这个就是这么实现啊
  • yqb:特地注册账号来赞你一个 感觉很用心
  • advanceye: 楼主,你参考了这篇国外的blog,抄了人家部分代码,怎么在文章里不说?(http://vombat.tumblr.com/post/86294492874/caching-audio-streamed-using-avplayer
    基本的知识产权意识要有啊~~【如果你有一点儿RP的滑,别再删我评论了】
  • advanceye: 楼主,你参考了这篇国外的blog怎么在文章里不说?(http://vombat.tumblr.com/post/86294492874/caching-audio-streamed-using-avplayer
    基本的知识产权意识要有啊~~
  • Ko_Neko:博主你好,以下者点没有看懂:

    3.如果当前的dataRequest请求的offset小于已经缓存的文件的offset,同时大于代理向服务器请求的range的offset,说明有一部分已经缓存的数据可以传给播放器,则将这部分数据返回给播放器(此时应该是由于播放器向前拖拽,请求的数据已经缓存过才会出现)、

    如果是向前拖动,dataRequest请求的offset应该是小于已经缓存的offset,为什么还要加上一个“同时大于代理向服务器请求的range的offset”这个条件呢,感觉和“代理向服务器请求的range的offset”并没有关系啊。

    能否请教一下呢?
    夜千寻墨:@IGARASHI 你把正常状态下的播放器发出请求的offset打出来看看,然后是前后拖拽时候的offset!我当时就是分析了这两种逻辑后写的
  • LeoDaxia:请问 m3u8 能缓存吗?
  • C_JUN_SNOW:释放不了?我这要把播放器加载cell上,切换视频,但是之前的释放不了啊
    我才不傲娇呢:@C_JUN_SNOW 我也是这个情况,你释放了么? 有什么办法解决没
    夜千寻墨:@C_JUN_SNOW 可以的,我试过这个,好像哪个地方要重置一下,太久了,记不清了,你下断点应该容易找到原因
  • 我才不傲娇呢:楼主你好,下了你的demo真的写得很好,可是我push到播放页面 在pop回来之后,就无法再进入到播放页面了,能请教一下楼主,这个怎么解决么
    我才不傲娇呢:@夜千寻墨 就是我点一个按钮进入播放界面,然后从视频的播放界面退回来之后,我再点击播放按钮就进不了视频的播放界面了
    夜千寻墨:@我才不傲娇呢 没明白你意思
  • xinghunMeng:请教下,我用的nsurlsession来下载数据,当从新请求数据的时候,我取消下载任务,但是不会执行didReceiveResponse代理方法。
    if(self.dataTask) {
    [self.dataTask cancel];
    self.dataTask = nil;
    }
    self.dataTask = [self.session dataTaskWithRequest:request];
    [self.dataTask resume];
  • 海鲜味龙潭方便面:楼主你好,请问你的是边下边播,还是边播边下这两个有很大的却别,请务必回复!!!
    洛洛爱吃肉:@海鲜味龙潭方便面 没看懂
    海鲜味龙潭方便面:楼主你好,我的意思是,变下遍播是一边下一边播,边播边下是一边下一边播,我目前可以做出一边播一边缓存的视频,但是无法实现一边下一边播的视频,所以来求教的,多有打扰,请勿见怪
    夜千寻墨:@海鲜味龙潭方便面 你这个都看不懂,我就是说了对你而言又有什么意义
  • 3aef6558f4b7:如果不使用单例模式,该怎么用,就是类似微信那样,同时播放好几个
    夜千寻墨:@低头式高调 不明白你说的,就算有多个视频,同一时间播放的应该也只有一个吧
  • d87b76356fe4:看楼主的文章和底下的评论学会了很多,谢谢楼主.
  • VincentPeng:你好,最近在根据你的demo改功能,我不太明白修改URL为
    components.scheme = @"streaming";的作用是什么,请赐教哈
    夜千寻墨:@VincentPeng 差不多就是这个意思,只有这样才能让我们自己给播放器填充数据
    VincentPeng:@VincentPeng 有点明白了,是为了把scheme改为系统不识别的scheme,在真正获取数据的时候再改回来,对吧?
  • 流年忆时光:你好 我下载下来的demo 为什么缺少了XCHudHelper文件夹下的文件呢?lz能否发一份到邮箱 suntingwork@126.com
    流年忆时光:@夜千寻墨 恩恩 好的 谢谢啦 :smile:
    夜千寻墨:https://github.com/suifengqjn/TBPlayer/tags
    下载1.0 tag
    夜千寻墨:@Sun123 在加cocoapods支持,到一半,还没弄好
  • JANNA丶:大爱啊
  • 含泪若笑:你好 我下了你的demo 我发现你使用了AVAssetResourceLoaderDelegate这个,但我现在的url是不安全的 https的 为什么用你的demo不能绕过验证呢?使用这个方法- (BOOL)resourceLoader:(AVAssetResourceLoader *)resourceLoader shouldWaitForResponseToAuthenticationChallenge:(NSURLAuthenticationChallenge *)authenticationChallenge NS_AVAILABLE(10_10, 8_0);
    夜千寻墨:@hanleirx 这跟url的安不安全有什么关系,它是从url去分段获取数据然后补充给播放器
  • 3045e3688ec5:请问作者这个能转载吗??
    夜千寻墨:@丶Amarantine 可以,还有些小bug,抽空修下
  • smm987:你好,请问,为什么,我改成了自己的视频源,但是下载到本地之后,不能释放呢?
  • LennonLin:第一次进来是正常的,第二次进视频就不正常了?这是为什么啊
  • 11eddd582230:如果边下边播,缓存播放数据流的话,那岂不是很占用手机内存
    夜千寻墨:@IOS书童 你看代码了麽?看了就不会问这幼稚的问题了
  • 11eddd582230:如果边下边播,缓存播放数据流的话,那岂不是很占用手机内存
  • 11eddd582230:如果边下边播,缓存播放数据流的话,那岂不是很占用手机内存
  • 大斜的张:你好 为什么 当我点击播放第二个视频时就会出现卡死现象
  • 21e790b8fb9e:大楼主、 那如果是 有加密的 视频呢 ? 如何在缓冲播放啊 ??
  • 多鱼影视界:厉害了 我滴哥
  • 4a02187e2078:鼓励鼓励,
  • 魏小庄:l楼主你好,,请问下,我的视频大概是10秒左右的,,为啥缓存不成功
  • 码农甲:localURL 你说说这样有啥意义?、
  • 码农甲:为啥要继承nsobject??为啥不直接用v继承vc
  • 随缘吖:NSURLConnection -> NSURLSession
  • 87e2aaf4e5b7:判断是否已缓存的代码在哪个位置
  • c44bec3091bf:看了Demo后 想请教楼主点问题, 每次didRecieveData后的数据 怎么能让AVPlayItem关联上呢 我的ItemStatus一直是失败的 播放不了 :sweat_smile:
  • zhang_jk:感谢分享
  • 1938f91b36c6:这个怎样查看是否要下载完成啊 要全部播放完吗
  • 我是卖报滴小行家:为什么我将url转换成steaming形式后,不能进行播放了,没有声音、没有图片????/
  • 8c6b8b1537db:大神,对于某些视频,或者某些系统版本的手机,下载到最后还差一点数据没下载完成就不下载了,这是怎么回事呢?
    夜千寻墨:@裂痕 我也遇到过一次,如果网速好的情况下出现,那就是服务端的问题可能性较大
  • KevinMK:支持
  • 3bae57660c60:楼主,您好!我在做播放器的时候发现这样一个问题,断网后,当播放器播放到缓存的结尾时会卡住,如果正常播放到这个状态卡主,stall的通知,islikelytokeepup,bufferempty都会有变化,我监听后都能抓到。但是当我手动拖动到了缓冲区结尾处(此时还能播放,再播放1分钟内就不会播了),此时再卡住时,以上三种通知和状态变化,请问这种情况下如何抓住视频卡住的状态呢?还有一个问题,重新连接网络后,有时播放器户卡死有时会先播放声音大约画面卡顿几秒后和声音同步(猜测是画面下载和解码时间会比声音长的原因),有没有能在视频画面同步后再让视频播放的控制。谢谢!
  • 许仔仔:正在播放时,把网络断开,再次连接网络时,无法自动播放
  • eac70c1f4685:您好,请问我怎么发现注册的通知的方法 只有发送通知的方法 [[NSNotificationCenter defaultCenter] postNotificationName:kTBPlayerProgressChangedNotification object:nil];像这样发送通知有三个,都没找到对应注册的方法
    夜千寻墨:@frankay 这是我好久之前写的,记不太清了,如果有需要,就加上监听方法
  • Mr_FF:博主你好,M3U8格式播放不了,content type = "(null)", content length = 0,为啥呢? :blush:
    1938f91b36c6:M3U8的你解决了吗
    Mr_FF:@夜千寻墨 博主,不行哇。。 :sob:
    夜千寻墨:@Mr_FF contenttype我只写了一种格式,mp4,我写死了,你自己换
  • 大么大二郎:请问楼主知道服务端应该怎么写吗?
    夜千寻墨:@NielLes 不知道,没做过服务端
  • shuolol:有个问题 avplayer 在加载数据的时候 如果网络不好会导致整个主线程卡住 请问有办法吗?
    shuolol:@夜千寻墨 你好 怎样控制呢 能不能给点提示? 谢谢
    夜千寻墨:@shuolol 它是线程阻塞的,你可以自己控制卡住之后的交互方式
  • eac70c1f4685:您好,如果是ftp协议的url 怎么在播放器里面实现数据的请求和填充数据
  • 夜千寻墨:不知道,原因你得自己跟着代码找
  • RealSlimAlan:楼主,你好. 有个问题想请教一下. 我想实现像朋友圈那样直接cell里面播放小视频, 但是遇到一些难点. 如果要做缓存的话. 像楼主一样.通过AVURLAsset生成一个AVPlayerItem,再交由avPlayer去播放. 但是cell的重用出现问题了,整个程序卡死(没有崩溃), 改为url生成一个AVPlayerItem就好了. 这是为什么呢? 如果通过url生成的AVPlayerItem, 是否就无法做缓存呢?
  • SunStart:还望你能回复下。
  • SunStart:楼主,为什么视频缓冲完毕后,断网不能从缓冲中播放,刚下的Demo
    夜千寻墨:@SunStart 断网之后,播放器发出的请求就断了,有网和没网播放器处理方式是不一样的,中途断网只能播播放器自带的部分缓存,不能播放本地的还没缓存完整的缓存

本文标题:iOS视频边下边播--缓存播放数据流

本文链接:https://www.haomeiwen.com/subject/tyjpkttx.html