HTTP缓存改为异步

This commit is contained in:
nnwang
2026-02-08 15:29:09 +08:00
parent ec8672160d
commit 8edb021b70
2 changed files with 85 additions and 62 deletions

124
play.lua
View File

@@ -3,6 +3,7 @@ local gpu = peripheral.wrap("tm_gpu_0")
gpu.refreshSize()
gpu.setSize(64)
local w, h = gpu.getSize()
if not fs.exists("speakerlib.lua") then shell.run("wget https://git.liulikeji.cn/xingluo/computer_craft_video_play/raw/branch/main/speakerlib.lua") end
server_url = "https://newgmapi.liulikeji.cn"
@@ -15,7 +16,7 @@ if not videoUrl then
return
end
_G.audio_ready = false
local task_id
local status_url
@@ -240,10 +241,12 @@ print("Initial caching completed.")
local frameDelay = tonumber(string.format("%.3f",1 / videoInfo.fps))
print("Starting playback (FPS: " .. videoInfo.fps .. ")")
print(frameDelay * #videoInfo.frame_urls)
repeat
sleep(0.05)
until _G.audio_ready
local starttime1 = os.clock()
sleep(4)
-- 播放前已缓存 10 秒
@@ -257,95 +260,116 @@ local running = true
-- 全局帧索引(由播放线程更新)
local frameIndex = 1
-- 缓存协程:边播边下(并发下载多个 framepack
-- 共享状态(用于 cacheAhead 和 httpResponseHandler 通信)
local pendingRequests = {} -- url -> { batch = {...}, retry = N }
local downloadedFrames = {} -- frameIndex -> true (防止重复调度)
-- 缓存协程:边播边下(使用异步 http.request
local function cacheAhead()
while running do
-- 计算当前应缓存的范围 [frameIndex, frameIndex + maxCachedFrames)
local currentStart = frameIndex
local currentEnd = math.min(frameIndex + videoInfo.fps * 20 - 1, totalFramesToPlay)
-- 找出尚未缓存的帧段,按 BATCH_SIZE 切分
local batches = {}
local i = currentStart
while i <= currentEnd do
if not allFrameData[i] then
if not allFrameData[i] and not downloadedFrames[i] then
local batchStart = i
local batchEnd = math.min(batchStart + BATCH_SIZE - 1, currentEnd)
-- 构造 URL 列表(相对路径)
local urls = {}
for j = batchStart, batchEnd do
if not allFrameData[j] then
if not allFrameData[j] and not downloadedFrames[j] then
table.insert(urls, videoInfo.frame_urls[j])
downloadedFrames[j] = true
end
end
if #urls > 0 then
table.insert(batches, {
start = batchStart,
urls = urls
urls = urls,
retry = 0
})
end
i = batchEnd + 1
else
i = i + 1
end
end
-- 如果没有需要下载的批次,休眠后重试
if #batches == 0 then
sleep(0.5)
end
-- 并发下载所有缺失的批次
downloadTasks = {}
for i, batch in ipairs(batches) do
table.insert(downloadTasks, function()
print("Downloading batch: " .. batch.start .. " - " .. (batch.start + #batch.urls - 1))
while true do
local resp = http.post({
url = server_url .. "/api/framepack?"..batch.urls[1],
-- 发起所有新请求(不等待!)
for _, batch in ipairs(batches) do
local url = server_url .. "/api/framepack?" .. batch.urls[1]
local body = textutils.serializeJSON({ urls = batch.urls })
http.request({
url = url,
headers = { ["Content-Type"] = "application/json" },
body = textutils.serializeJSON({ urls = batch.urls } ),
body = body,
timeout = 2,
binary = true
}
)
})
pendingRequests[url] = batch
end
if resp then
local binData = resp.readAll()
-- 如果没有要下载的,小睡
if next(batches) == nil then
sleep(0.5)
end
end
end
resp.close()
-- HTTP 处理协程
local function httpResponseHandler()
while running do
local event, url, handleOrErr = os.pullEvent()
if event == "http_success" then
local batch = pendingRequests[url]
if batch then
pendingRequests[url] = nil
local binData = handleOrErr.readAll()
handleOrErr.close()
local batchFrames = unpackFramePack(binData)
local success, batchFrames = pcall(unpackFramePack, binData)
if success then
for idx = 1, #batchFrames do
local globalIdx = batch.start + idx - 1
if not allFrameData[globalIdx] then
allFrameData[globalIdx] = batchFrames[idx]
end
end
print("Cached batch: " .. batch.start .. " - " .. (batch.start + #batchFrames - 1))
break
print("[V] Cached batch: " .. batch.start .. " - " .. (batch.start + #batchFrames - 1))
else
print("Failed to cache batch starting at " .. batch.start)
print("[R] Unpack failed for batch " .. batch.start .. ": " .. tostring(batchFrames))
-- 标记这些帧可重试
for j = batch.start, batch.start + #batch.urls - 1 do
downloadedFrames[j] = nil
end
end
end)
end
-- 并发执行所有下载任务
parallel.waitForAll(table.unpack(downloadTasks))
-- 检查是否已缓存到最后一帧
if currentEnd >= #videoInfo.frame_urls then
break
elseif event == "http_failure" then
local batch = pendingRequests[url]
if batch then
pendingRequests[url] = nil
batch.retry = (batch.retry or 0) + 1
if batch.retry < 3 then
print("[R] Retrying batch " .. batch.start .. " (attempt " .. (batch.retry + 1) .. ")")
-- 允许重试
for j = batch.start, batch.start + #batch.urls - 1 do
downloadedFrames[j] = nil
end
-- 重新触发 cacheAhead 下一轮会重发
else
print("[X] Giving up on batch " .. batch.start)
-- 永久失败,不再重试
for j = batch.start, batch.start + #batch.urls - 1 do
downloadedFrames[j] = true
end
end
end
-- 小睡
sleep(0.2)
end
end
end
@@ -416,11 +440,8 @@ local function renderVideo()
local play_time = tonumber(string.format("%.3f",os.clock() - starttime))
frameIndex2 = math.ceil(play_time / frameDelay)
if frameIndex2 + (frameIndex - frameIndex2) * 0.5 >= 4 then
frameIndex = frameIndex2
else
@@ -435,7 +456,8 @@ end
-- 启动协程
parallel.waitForAll(renderVideo, cacheAhead)
-- 启动三个协程:渲染 + 缓存调度 + HTTP 响应处理
parallel.waitForAny(renderVideo, cacheAhead, httpResponseHandler)
local endtime = os.clock()
local time = endtime-starttime1

View File

@@ -201,6 +201,7 @@ elseif cmd == "play" then
end
-- 主播放循环
_G.audio_ready = true
os.pullEvent("audio_start")
while bytes_read < total_size and _G.Playopen do
-- 检查是否需要设置播放位置