HTTP缓存改为异步
This commit is contained in:
124
play.lua
124
play.lua
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
-- 检查是否需要设置播放位置
|
||||
|
||||
Reference in New Issue
Block a user