iOS 边下边播的实现代码
程序员文章站
2023-12-17 20:51:28
项目中之前使用的是avplayer直接播放url地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿,就考虑改写成边下边播的模式,查过了许多资料,...
项目中之前使用的是avplayer直接播放url地址,但是不知道是相机的wifi不够稳定还是代码的问题,app总是出现缓冲卡顿,就考虑改写成边下边播的模式,查过了许多资料,发现大部分都是用的同一种方法
avassetresourceloaderdelegate 代理方法,来看看如何实现
首先要实现两个必须的代理方法
avassetresourceloaderdelegateobjective-c #pragma mark - avassetresourceloaderdelegate //开始加载 - (bool)resourceloader:(avassetresourceloader *)resourceloader shouldwaitforloadingofrequestedresource:(avassetresourceloadingrequest *)loadingrequest { [self addloadingrequest:loadingrequest]; return yes; } //取消加载 - (void)resourceloader:(avassetresourceloader *)resourceloader didcancelloadingrequest:(avassetresourceloadingrequest *)loadingrequest { [self removeloadingrequest:loadingrequest]; } #pragma mark - avassetresourceloaderdelegate //开始加载 - (bool)resourceloader:(avassetresourceloader *)resourceloader shouldwaitforloadingofrequestedresource:(avassetresourceloadingrequest *)loadingrequest { [self addloadingrequest:loadingrequest]; return yes; } //取消加载 - (void)resourceloader:(avassetresourceloader *)resourceloader didcancelloadingrequest:(avassetresourceloadingrequest *)loadingrequest { [self removeloadingrequest:loadingrequest]; }
然后要定义一个下载类,其实就是分段下载数据的下载器
avassetresourceloaderdelegateobjective-c - (void)start { nsmutableurlrequest * request = [nsmutableurlrequest requestwithurl:[self.requesturl originalschemeurl] cachepolicy:nsurlrequestreloadignoringcachedata timeoutinterval:requesttimeout]; if (self.requestoffset > 0) { [request addvalue:[nsstring stringwithformat:@"bytes=%ld-%ld", self.requestoffset, self.filelength - 1] forhttpheaderfield:@"range"]; } self.session = [nsurlsession sessionwithconfiguration:[nsurlsessionconfiguration defaultsessionconfiguration] delegate:self delegatequeue:[nsoperationqueue mainqueue]]; self.task = [self.session datataskwithrequest:request]; [self.task resume]; } #pragma mark - nsurlsessiondatadelegate //服务器响应 - (void)urlsession:(nsurlsession *)session datatask:(nsurlsessiondatatask *)datatask didreceiveresponse:(nsurlresponse *)response completionhandler:(void (^)(nsurlsessionresponsedisposition))completionhandler { if (self.cancel) return; srqlog(@"response: %@",response); completionhandler(nsurlsessionresponseallow); nshttpurlresponse * httpresponse = (nshttpurlresponse *)response; nsstring * contentrange = [[httpresponse allheaderfields] objectforkey:@"content-range"]; nsstring * filelength = [[contentrange componentsseparatedbystring:@"/"] lastobject]; self.filelength = filelength.integervalue > 0 ? filelength.integervalue : response.expectedcontentlength; if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidreceiveresponse)]) { [self.delegate requesttaskdidreceiveresponse]; } } //服务器返回数据 可能会调用多次 - (void)urlsession:(nsurlsession *)session datatask:(nsurlsessiondatatask *)datatask didreceivedata:(nsdata *)data { if (self.cancel) return; //srqlog(@"收到响应了: %@",data); self.cachelength += data.length; if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidupdatecache)]) { [self.delegate requesttaskdidupdatecache]; } } //请求完成会调用该方法,请求失败则error有值 - (void)urlsession:(nsurlsession *)session task:(nsurlsessiontask *)task didcompletewitherror:(nserror *)error { if (self.cancel) { srqlog(@"下载取消"); }else { if (error) { if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidfailwitherror:)]) { [self.delegate requesttaskdidfailwitherror:error]; } }else { //可以缓存则保存文件 if (self.cache) { [filehandle cachetempfilewithfilename:[nsstring filenamewithurl:self.requesturl]]; } if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidfinishloadingwithcache:)]) { [self.delegate requesttaskdidfinishloadingwithcache:self.cache]; } } } } - (void)start { nsmutableurlrequest * request = [nsmutableurlrequest requestwithurl:[self.requesturl originalschemeurl] cachepolicy:nsurlrequestreloadignoringcachedata timeoutinterval:requesttimeout]; if (self.requestoffset > 0) { [request addvalue:[nsstring stringwithformat:@"bytes=%ld-%ld", self.requestoffset, self.filelength - 1] forhttpheaderfield:@"range"]; } self.session = [nsurlsession sessionwithconfiguration:[nsurlsessionconfiguration defaultsessionconfiguration] delegate:self delegatequeue:[nsoperationqueue mainqueue]]; self.task = [self.session datataskwithrequest:request]; [self.task resume]; } #pragma mark - nsurlsessiondatadelegate //服务器响应 - (void)urlsession:(nsurlsession *)session datatask:(nsurlsessiondatatask *)datatask didreceiveresponse:(nsurlresponse *)response completionhandler:(void (^)(nsurlsessionresponsedisposition))completionhandler { if (self.cancel) return; srqlog(@"response: %@",response); completionhandler(nsurlsessionresponseallow); nshttpurlresponse * httpresponse = (nshttpurlresponse *)response; nsstring * contentrange = [[httpresponse allheaderfields] objectforkey:@"content-range"]; nsstring * filelength = [[contentrange componentsseparatedbystring:@"/"] lastobject]; self.filelength = filelength.integervalue > 0 ? filelength.integervalue : response.expectedcontentlength; if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidreceiveresponse)]) { [self.delegate requesttaskdidreceiveresponse]; } } //服务器返回数据 可能会调用多次 - (void)urlsession:(nsurlsession *)session datatask:(nsurlsessiondatatask *)datatask didreceivedata:(nsdata *)data { if (self.cancel) return; //srqlog(@"收到响应了: %@",data); self.cachelength += data.length; if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidupdatecache)]) { [self.delegate requesttaskdidupdatecache]; } } //请求完成会调用该方法,请求失败则error有值 - (void)urlsession:(nsurlsession *)session task:(nsurlsessiontask *)task didcompletewitherror:(nserror *)error { if (self.cancel) { srqlog(@"下载取消"); }else { if (error) { if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidfailwitherror:)]) { [self.delegate requesttaskdidfailwitherror:error]; } }else { //可以缓存则保存文件 if (self.cache) { [filehandle cachetempfilewithfilename:[nsstring filenamewithurl:self.requesturl]]; } if (self.delegate && [self.delegate respondstoselector:@selector(requesttaskdidfinishloadingwithcache:)]) { [self.delegate requesttaskdidfinishloadingwithcache:self.cache]; } } } }
最后将拿到的数据塞进avassetresourceloaderdelegate代理中,交还给avplayer,就可以播放了
avassetresourceloaderdelegateobjective-c - (bool)finishloadingwithloadingrequest:(avassetresourceloadingrequest *)loadingrequest { //填充信息 cfstringref contenttype = uttypecreatepreferredidentifierfortag(kuttagclassmimetype, (__bridge cfstringref)(mimetype), null); loadingrequest.contentinformationrequest.contenttype = cfbridgingrelease(contenttype); loadingrequest.contentinformationrequest.byterangeaccesssupported = yes; loadingrequest.contentinformationrequest.contentlength = self.requesttask.filelength; //读文件,填充数据 nsuinteger cachelength = self.requesttask.cachelength; nsuinteger requestedoffset = loadingrequest.datarequest.requestedoffset; if (loadingrequest.datarequest.currentoffset != 0) { requestedoffset = loadingrequest.datarequest.currentoffset; } nsuinteger canreadlength = cachelength - (requestedoffset - self.requesttask.requestoffset); nsuinteger respondlength = min(canreadlength, loadingrequest.datarequest.requestedlength); //srqlog(@"好不容易填充一次"); [loadingrequest.datarequest respondwithdata:[filehandle readtempfiledatawithoffset:requestedoffset - self.requesttask.requestoffset length:respondlength]]; //如果完全响应了所需要的数据,则完成 nsuinteger nowendoffset = requestedoffset + canreadlength; nsuinteger reqendoffset = loadingrequest.datarequest.requestedoffset + loadingrequest.datarequest.requestedlength; if (nowendoffset >= reqendoffset) { [loadingrequest finishloading]; return yes; } return no; } - (void)player{ self.resouerloader = [[resourceloader alloc] init]; self.asset = [avurlasset urlassetwithurl:[self.videourl customschemeurl] options:nil]; [self.asset.resourceloader setdelegate:self.resouerloader queue:dispatch_get_main_queue()]; _playeritem = [avplayeritem playeritemwithasset:self.asset]; _players = [avplayer playerwithplayeritem:_playeritem]; } - (bool)finishloadingwithloadingrequest:(avassetresourceloadingrequest *)loadingrequest { //填充信息 cfstringref contenttype = uttypecreatepreferredidentifierfortag(kuttagclassmimetype, (__bridge cfstringref)(mimetype), null); loadingrequest.contentinformationrequest.contenttype = cfbridgingrelease(contenttype); loadingrequest.contentinformationrequest.byterangeaccesssupported = yes; loadingrequest.contentinformationrequest.contentlength = self.requesttask.filelength; //读文件,填充数据 nsuinteger cachelength = self.requesttask.cachelength; nsuinteger requestedoffset = loadingrequest.datarequest.requestedoffset; if (loadingrequest.datarequest.currentoffset != 0) { requestedoffset = loadingrequest.datarequest.currentoffset; } nsuinteger canreadlength = cachelength - (requestedoffset - self.requesttask.requestoffset); nsuinteger respondlength = min(canreadlength, loadingrequest.datarequest.requestedlength); //srqlog(@"好不容易填充一次"); [loadingrequest.datarequest respondwithdata:[filehandle readtempfiledatawithoffset:requestedoffset - self.requesttask.requestoffset length:respondlength]]; //如果完全响应了所需要的数据,则完成 nsuinteger nowendoffset = requestedoffset + canreadlength; nsuinteger reqendoffset = loadingrequest.datarequest.requestedoffset + loadingrequest.datarequest.requestedlength; if (nowendoffset >= reqendoffset) { [loadingrequest finishloading]; return yes; } return no; } - (void)player{ self.resouerloader = [[resourceloader alloc] init]; self.asset = [avurlasset urlassetwithurl:[self.videourl customschemeurl] options:nil]; [self.asset.resourceloader setdelegate:self.resouerloader queue:dispatch_get_main_queue()]; _playeritem = [avplayeritem playeritemwithasset:self.asset]; _players = [avplayer playerwithplayeritem:_playeritem]; }
注意:此方法服务器端最好支持range头,这样才是分段下载。
总结
以上所述是小编给大家介绍的ios 边下边播的实现代码,希望对大家有所帮助