sound-streaming.md 5.4 KB


title: Defold 中的声音流传输

brief: 本手册介绍了如何将声音流式传输到 Defold 游戏引擎中

声音流传输

虽然默认行为是完整加载声音数据,但在使用前分块加载数据可能也是有益的。这通常被称为"流传输"。

声音流传输的一个好处是需要更少的运行时内存,另一个好处是如果您从例如 http url 流式传输内容,您可以随时更新内容,并避免初始下载。

示例

有一个展示此设置的示例项目:https://github.com/defold/example-sound-streaming

如何启用声音流传输

简单方法

使用声音流传输的最简单方法是在 game.project 中启用 sound.stream_enabled 设置。启用此选项后,引擎将开始流式传输声音。

注意:如果您同时加载了许多声音文件,可能需要增加 sound.stream_cache_size 值(见下文)。

运行时资源

您也可以创建一个新的声音数据资源,并将其设置为声音组件。

您可以通过以下方式执行此操作:

  • 加载声音文件数据的初始部分
    • 注意:这是原始声音文件,包括 ogg/wav 头部
  • 通过调用 resource.create_sound_data() 创建一个新的声音数据资源
  • 使用 go.set() 将新的声音数据资源设置为声音组件

以下是示例项目中的摘录,使用 http.request() 获取初始声音文件。

::: sidenote 您从中加载内容的 Web 服务器必须支持 HTTP 范围请求。 :::

local function play_sound(self, hash)
    go.set(self.component, "sound", hash) -- 覆盖组件上的资源数据
    sound.play(self.component)            -- 开始播放声音
end

local function parse_range(s)
    local _, _, rstart, rend, size = string.find(s, "(%d+)-(%d+)/(%d+)") -- "bytes 0-16383/103277"
    return rstart, rend, size
end

-- http 响应的回调函数
local function http_result(self, _id, response, extra)
    if response.status == 200 or response.status == 206 then
        -- 成功的请求
        local relative_path = self.filename
        local range = response.headers['content-range'] -- content-range = "bytes 0-16383/103277"
        local rstart, rend, filesize = parse_range(range)
        -- 创建 Defold 资源
        --   "partial" 将启用流传输模式
        print("Creating resource", relative_path)
        local hash = resource.create_sound_data(relative_path, { data = response.response, filesize = filesize, partial = true })
        -- 发送 "play_sound" 到组件
        play_sound(self, hash)
    end
end

local function load_web_sound(base_url, relative_path)
    local url = base_url .. "/" .. relative_path
    local headers = {}
    headers['Range'] = string.format("bytes=%d-%d", 0, 16384-1)

    http.request(url, "GET", http_result, headers, nil, { ignore_cache = true })
end

资源提供者

您可以使用其他方式加载声音文件的初始块。重要的是要记住,其余的块是从资源系统及其资源提供者加载的。在此示例中,我们通过调用使用 liveupdate.add_mount() 添加一个新的 (http) 文件提供者,通过添加实时更新挂载点。

您可以在 https://github.com/defold/example-sound-streaming 中找到一个工作示例。

-- 参见上面示例中的 http_result()

local function load_web_sound(base_url, relative_path)
    local url = base_url .. "/" .. relative_path
    local headers = {}
    -- 请求文件的初始部分
    headers['Range'] = string.format("bytes=%d-%d", 0, 16384-1)

    http.request(url, "GET", http_result, headers, nil, { ignore_cache = true })
end

function init(self)
    self.base_url = "http://my.server.com"
    self.filename = "/path/to/sound.ogg"

    liveupdate.add_mount("webmount", self.base_url, 100, function ()
                    -- 一旦挂载点准备就绪,我们就可以开始请求下载第一个块
                    load_web_sound(self.base_url, self.filename)
                end)
end

function final(self)
    liveupdate.remove_mount("webmount")
end

声音块缓存

运行时声音消耗的内存量由 game.project 中的 sound.stream_cache_size 设置 控制。在此限制下,加载的声音数据永远不会超过此限制。

每个声音文件的初始块不能被驱逐,只要资源被加载,它们就会占用缓存。初始块的大小由 game.project 中的 sound.stream_preload_size 设置 控制。

您还可以通过更改 game.project 中的 sound.stream_chunk_size 设置 来控制每个声音块的大小。如果您同时加载了许多声音文件,这可能有助于进一步降低声音缓存大小。小于声音块大小的声音文件不会被流式传输,如果新块不适合放入缓存中,最旧的块将被驱逐。

::: important 声音块缓存的总大小应大于加载的声音文件数量乘以流块大小。否则,您可能会冒着每帧驱逐新块的风险,声音将无法正常播放。 :::