class
MediaBoxFileMap (对文件分片的包装)
1
2
3
4
fileprivate(set) var sum: Int32
private(set) var ranges: IndexSet
private(set) var truncationSize: Int32?
private(set) var progress: Float?
class
ManagedFile
1
2
3
private let queue: Queue?
private let fd: Int32
private let mode: ManagedFileMode
class
MediaBoxFileMissingRanges (管理分片请求)
1
2
3
private var requestedRanges = Bag<MediaBoxFileMissingRange>()
private var missingRangesFlattened = IndexSet()
private var missingRangesByPriority: [MediaBoxFetchPriority: IndexSet] = [:]
核心函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
private func updateRequestRanges(_ intervals: [(Range<Int>, MediaBoxFetchPriority)], fetch: ((Signal<[(Range<Int>, MediaBoxFetchPriority)], NoError>) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError>)?) {
assert(self.queue.isCurrent())
#if DEBUG
for interval in intervals {
assert(!interval.0.isEmpty)
}
#endif
if intervals.isEmpty {
if let (_, disposable) = self.currentFetch {
self.currentFetch = nil
disposable.dispose()
}
} else {
if let (promise, _) = self.currentFetch {
promise.set(.single(intervals))
} else if let fetch = fetch {
let promise = Promise<[(Range<Int>, MediaBoxFetchPriority)]>()
let disposable = MetaDisposable()
self.currentFetch = (promise, disposable)
disposable.set((fetch(promise.get())
|> deliverOn(self.queue)).start(next: { [weak self] data in
if let strongSelf = self {
switch data {
case .reset:
if !strongSelf.fileMap.ranges.isEmpty {
strongSelf.reset()
}
case let .resourceSizeUpdated(size):
strongSelf.truncate(Int32(size))
case let .dataPart(resourceOffset, data, range, complete):
if !data.isEmpty {
strongSelf.write(offset: Int32(resourceOffset), data: data, dataRange: range)
}
if complete {
if let maxOffset = strongSelf.fileMap.ranges.rangeView.reversed().first?.upperBound {
let maxValue = max(resourceOffset + range.count, maxOffset)
strongSelf.truncate(Int32(maxValue))
}
}
case let .replaceHeader(data, range):
strongSelf.write(offset: 0, data: data, dataRange: range)
case let .moveLocalFile(path):
strongSelf.moveLocalFile(tempPath: path)
case let .moveTempFile(file):
strongSelf.moveLocalFile(tempPath: file.path)
TempBox.shared.dispose(file)
case let .copyLocalItem(item):
strongSelf.copyLocalItem(item)
case let .progressUpdated(progress):
strongSelf.progressUpdated(progress)
}
if !strongSelf.processedAtLeastOneFetch {
strongSelf.processedAtLeastOneFetch = true
for request in strongSelf.dataRequests.copyItems() {
if request.waitingUntilAfterInitialFetch {
request.waitingUntilAfterInitialFetch = false
if strongSelf.fileMap.contains(request.range) {
request.completion(MediaResourceData(path: strongSelf.path, offset: Int(request.range.lowerBound), size: request.range.count, complete: true))
} else {
request.completion(MediaResourceData(path: strongSelf.path, offset: Int(request.range.lowerBound), size: 0, complete: false))
}
}
}
}
}
}, error: { [weak self] e in
guard let strongSelf = self else {
return
}
for (error, _) in strongSelf.missingRanges.clear() {
error(e)
}
}))
promise.set(.single(intervals))
}
}
}
fetchedResourceData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public func fetchedResourceData(_ resource: MediaResource, in range: Range<Int>, priority: MediaBoxFetchPriority = .default, parameters: MediaResourceFetchParameters?) -> Signal<Void, FetchResourceError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.dataQueue.async {
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
subscriber.putCompletion()
return
}
let fetchResource = self.wrappedFetchResource.get()
// hcl 崩溃
/*
Fatal error: Can't form Range with upperBound < lowerBound
Printing description of range:
(Range<Int32>) range = 342486825..<2147483647
*/
let fetchedDisposable = fileContext.fetched(range: Int32(range.lowerBound) ..< Int32(range.upperBound), priority: priority, fetch: { intervals in
return fetchResource
|> introduceError(MediaResourceDataFetchError.self)
|> mapToSignal { fetch in
return fetch(resource, intervals, parameters)
}
}, error: { _ in
subscriber.putCompletion()
}, completed: {
subscriber.putCompletion()
})
disposable.set(ActionDisposable {
fetchedDisposable.dispose()
releaseContext()
})
}
return disposable
}
}
resourceData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public func resourceData(_ resource: MediaResource, size: Int, in range: Range<Int>, mode: ResourceDataRangeMode = .complete) -> Signal<Data, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.dataQueue.async {
guard let (fileContext, releaseContext) = self.fileContext(for: resource) else {
subscriber.putCompletion()
return
}
let range = Int32(range.lowerBound) ..< Int32(range.upperBound)
let dataDisposable = fileContext.data(range: range, waitUntilAfterInitialFetch: false, next: { result in
if let file = ManagedFile(queue: self.dataQueue, path: result.path, mode: .read), let fileSize = file.getSize() {
if result.complete {
if result.offset + result.size <= fileSize {
if fileSize >= result.offset + result.size {
file.seek(position: Int64(result.offset))
let resultData = file.readData(count: result.size)
subscriber.putNext(resultData)
subscriber.putCompletion()
} else {
assertionFailure("data.count >= result.offset + result.size")
}
} else {
assertionFailure()
}
} else {
switch mode {
case .complete:
break
case .incremental:
break
case .partial:
subscriber.putNext(Data())
}
}
}
})
disposable.set(ActionDisposable {
dataDisposable.dispose()
releaseContext()
})
}
return disposable
}
}