欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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 边下边播的实现代码,希望对大家有所帮助

上一篇:

下一篇: