parseIOS method
Parses and responds to range requests on iOS.
Handles segmented download and response for large files.
Implementation
Future<void> parseIOS(
Socket socket,
Uri uri,
List<String> responseHeaders,
int requestRangeStart,
int requestRangeEnd,
bool partial,
Map<String, String> headers,
) async {
final totalContentLength = await _contentLength(uri, headers);
final rangeResponse = Mp4RangeResponse.fromRequest(
start: requestRangeStart,
end: requestRangeEnd,
totalLength: totalContentLength,
partial: partial,
);
responseHeaders.add('content-length: ${rangeResponse.contentLength}');
if (rangeResponse.contentRangeHeader != null) {
responseHeaders.add(rangeResponse.contentRangeHeader!);
}
await socket.append(responseHeaders.join('\r\n'));
bool downloading = true;
int startRange =
rangeResponse.start - (rangeResponse.start % Config.segmentSize);
int endRange = startRange + Config.segmentSize - 1;
int retry = 3;
while (downloading) {
// The last segment's endRange must not exceed the file's last byte. When
// the requested range end is past EOF, some origins (e.g. Aliyun OSS) reply
// 200 + the whole file instead of a clamped 206; the serve loop then
// splices the file's head where its tail belongs, corrupting playback
// around the segment boundary (jump to EOF). rangeResponse.end is the
// resolved last byte (the request end, or contentLength-1 for `bytes=N-`),
// so clamp to it. Note requestRangeEnd is still -1 for open-ended requests.
if (endRange > rangeResponse.end) endRange = rangeResponse.end;
DownloadTask task = DownloadTask(
uri: uri,
startRange: startRange,
endRange: endRange,
headers: headers,
);
logD(
'Start ${task.url} '
'Request rangeļ¼${task.startRange}-${task.endRange}',
);
await concurrent(task, headers, totalContentLength);
Uint8List? data = await cache(task);
// if the task has been added, wait for the download to complete
bool exitUri = VideoProxy.downloadManager.isTaskExit(task);
if (exitUri) {
while (data == null) {
await Future.delayed(const Duration(milliseconds: 100));
data = await cache(task);
}
}
if (data == null) {
task.priority += 2;
data = await download(task);
}
if (data == null) {
retry--;
if (retry == 0) {
downloading = false;
break;
}
continue;
}
int startIndex = 0;
int? endIndex;
if (startRange < rangeResponse.start) {
startIndex = rangeResponse.start - startRange;
}
if (endRange > rangeResponse.end) {
endIndex = rangeResponse.end - startRange + 1;
}
data = data.sublist(startIndex, endIndex);
socket.done.then((value) {
downloading = false;
}).catchError((e) {
downloading = false;
});
bool success = await socket.append(data);
if (!success) downloading = false;
startRange += Config.segmentSize;
endRange = startRange + Config.segmentSize - 1;
if (startRange > rangeResponse.end) {
downloading = false;
}
}
}