Redesign Hope iOS UI, harden playback, and document project handoff
This commit is contained in:
1 parent
40aaf4fea7
commit
24f95639dc
24 files changed
+1470
-594
No files matched your search
@@ -1,5 +1,6 @@
|
||||
import AVFoundation
|
||||
import Foundation
|
||||
import UniformTypeIdentifiers
|
||||
import os
|
||||
|
||||
private let logger = Logger(subsystem: "dev.shane.voice", category: "media")
|
||||
@@ -114,21 +115,29 @@ final class PinnedMediaResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
|
||||
shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest
|
||||
) -> Bool {
|
||||
// All state access is on the main queue (the delegate queue).
|
||||
guard let dataRequest = loadingRequest.dataRequest else { return false }
|
||||
guard loadingRequest.dataRequest != nil else { return false }
|
||||
guard let url = loaderURLs[ObjectIdentifier(resourceLoader)] else {
|
||||
logger.log("no resolved URL for resource loader")
|
||||
loadingRequest.finishLoading(with: URLError(.badURL))
|
||||
return false
|
||||
}
|
||||
|
||||
fetchChunk(loadingRequest, url: url)
|
||||
return true
|
||||
}
|
||||
|
||||
/// Return bounded chunks promptly instead of buffering an entire song before
|
||||
/// AVPlayer receives its first usable bytes. The same loading request stays
|
||||
/// open until its requested range (or EOF) has been delivered.
|
||||
private func fetchChunk(_ loadingRequest: AVAssetResourceLoadingRequest, url: URL) {
|
||||
guard !loadingRequest.isCancelled, let dataRequest = loadingRequest.dataRequest else { return }
|
||||
var upstream = URLRequest(url: url)
|
||||
let offset = dataRequest.requestedOffset
|
||||
let offset = max(dataRequest.requestedOffset, dataRequest.currentOffset)
|
||||
let length = Int64(dataRequest.requestedLength)
|
||||
if length > 0 {
|
||||
upstream.setValue("bytes=\(offset)-\(offset + length - 1)", forHTTPHeaderField: "Range")
|
||||
} else {
|
||||
upstream.setValue("bytes=\(offset)-", forHTTPHeaderField: "Range")
|
||||
}
|
||||
let requestedEnd = dataRequest.requestedOffset + length
|
||||
let end = dataRequest.requestsAllDataToEndOfResource || length == 0
|
||||
? offset + 262_143 : min(requestedEnd - 1, offset + 262_143)
|
||||
upstream.setValue("bytes=\(offset)-\(end)", forHTTPHeaderField: "Range")
|
||||
logger.log("fetching \(url.absoluteString, privacy: .public) range=\(upstream.value(forHTTPHeaderField: "Range") ?? "-", privacy: .public)")
|
||||
|
||||
// Key the task by the loading request's identity directly — no hash
|
||||
@@ -140,6 +149,7 @@ final class PinnedMediaResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
|
||||
// Runs on .main (the session's delegateQueue), same domain as all
|
||||
// other map mutations.
|
||||
self.activeTasks[requestKey] = nil
|
||||
guard !loadingRequest.isCancelled else { return }
|
||||
|
||||
if let error {
|
||||
logger.log("fetch failed \(url.absoluteString, privacy: .public): \(error.localizedDescription, privacy: .public)")
|
||||
@@ -158,7 +168,8 @@ final class PinnedMediaResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
|
||||
return
|
||||
}
|
||||
if let info = loadingRequest.contentInformationRequest {
|
||||
info.contentType = http.mimeType ?? "application/octet-stream"
|
||||
// AVFoundation requires a UTI, not the HTTP MIME string.
|
||||
info.contentType = http.mimeType.flatMap { UTType(mimeType: $0)?.identifier }
|
||||
// Content-length handling (Iteration 05 Item 7):
|
||||
// - For 206: set full content length ONLY when Content-Range
|
||||
// provides a known total. Never fall back to the range's
|
||||
@@ -180,14 +191,21 @@ final class PinnedMediaResourceLoader: NSObject, AVAssetResourceLoaderDelegate {
|
||||
}
|
||||
info.isByteRangeAccessSupported = true
|
||||
}
|
||||
if let data {
|
||||
if let data, !data.isEmpty {
|
||||
dataRequest.respond(with: data)
|
||||
let nextOffset = offset + Int64(data.count)
|
||||
let total = Self.parseTotalLength(http)
|
||||
let reachedEOF = total.map { nextOffset >= $0 } ?? (nextOffset <= end)
|
||||
let needsMore = dataRequest.requestsAllDataToEndOfResource || nextOffset < requestedEnd
|
||||
if http.statusCode == 206, !reachedEOF, needsMore {
|
||||
self.fetchChunk(loadingRequest, url: url)
|
||||
return
|
||||
}
|
||||
}
|
||||
loadingRequest.finishLoading()
|
||||
}
|
||||
activeTasks[requestKey] = task
|
||||
task.resume()
|
||||
return true
|
||||
}
|
||||
|
||||
func resourceLoader(
|
||||
|
||||
Reference in new issue
Block a user