You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Go搭建S3 HLS视频流服务器,浏览器无法播放如何解决?

解决Go HLS视频流服务器浏览器播放问题

问题场景

用Go的echo框架搭建了HLS视频流服务器,从S3下载master.m3u8文件后通过/video接口返回。API调试工具(Insomnia/Postman)能获取到正确的m3u8内容,但浏览器访问时无法正常播放,直接访问S3对应URL却可以正常播放。

现有代码

e.GET("/video", func(c echo.Context) error {
    // download file m3u8
    buf := aws.NewWriteAtBuffer([]byte{})
    _, err := s3downloader.DownloadWithContext(ctx, buf, &s3.GetObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(objectKey), // hls/master.m3u8
    })

    return c.Stream(http.StatusOK, "application/x-mpegURL", bytes.NewReader(buf.Bytes()))
})

返回的m3u8内容

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=800800,RESOLUTION=640x360,CODECS="avc1.4d401e,mp4a.40.2"
360pixel/list.m3u8

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=965800,RESOLUTION=854x480,CODECS="avc1.4d401f,mp4a.40.2"
480pixel/list.m3u8

#EXTM3U
#EXT-X-VERSION:6
#EXT-X-STREAM-INF:BANDWIDTH=1267200,RESOLUTION=1280x720,CODECS="avc1.4d401f,mp4a.40.2"
720pixel/list.m3u8

问题原因

浏览器加载master.m3u8后,会自动请求里面的子播放列表(如360pixel/list.m3u8)和后续的TS分片文件,但当前服务器只有/video接口返回master文件,没有处理这些子路径的请求,导致浏览器找不到依赖文件,无法完成播放。

解决方案

1. 新增通用HLS文件路由

添加一个能匹配所有HLS相关文件的路由,比如/hls/*,让所有子列表、分片文件都能通过该路由从S3获取并返回:

import (
    "fmt"
    "path"
    "strings"
)

e.GET("/hls/*", func(c echo.Context) error {
    // 提取URL中的文件路径,例如"/hls/360pixel/list.m3u8"会得到"360pixel/list.m3u8"
    filePath := c.Param("*")
    // 拼接S3中的对象Key,根据你的实际存储路径调整前缀
    objectKey := fmt.Sprintf("xxx/hls/%s", filePath)

    buf := aws.NewWriteAtBuffer([]byte{})
    _, err := s3downloader.DownloadWithContext(ctx, buf, &s3.GetObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(objectKey),
    })
    if err != nil {
        return c.NoContent(http.StatusNotFound)
    }

    // 根据文件后缀设置正确的Content-Type
    contentType := "application/octet-stream"
    switch strings.ToLower(path.Ext(filePath)) {
    case ".m3u8":
        contentType = "application/x-mpegURL"
    case ".ts":
        contentType = "video/MP2T"
    }

    // 可选:添加跨域头,避免跨域访问问题
    c.Response().Header().Set("Access-Control-Allow-Origin", "*")

    return c.Stream(http.StatusOK, contentType, bytes.NewReader(buf.Bytes()))
})

2. 修正master.m3u8中的路径

将master文件里的相对路径替换为对应新路由的绝对路径,确保浏览器能正确请求到子文件。可以在下载master.m3u8后修改内容再返回:

e.GET("/video", func(c echo.Context) error {
    buf := aws.NewWriteAtBuffer([]byte{})
    _, err := s3downloader.DownloadWithContext(ctx, buf, &s3.GetObjectInput{
        Bucket: aws.String(bucket),
        Key:    aws.String(objectKey), // hls/master.m3u8
    })
    if err != nil {
        return err
    }

    // 替换所有子列表路径为路由路径
    masterContent := string(buf.Bytes())
    modifiedContent := strings.ReplaceAll(masterContent, "360pixel/list.m3u8", "/hls/360pixel/list.m3u8")
    modifiedContent = strings.ReplaceAll(modifiedContent, "480pixel/list.m3u8", "/hls/480pixel/list.m3u8")
    modifiedContent = strings.ReplaceAll(modifiedContent, "720pixel/list.m3u8", "/hls/720pixel/list.m3u8")

    // 可选:添加跨域头
    c.Response().Header().Set("Access-Control-Allow-Origin", "*")

    return c.Stream(http.StatusOK, "application/x-mpegURL", strings.NewReader(modifiedContent))
})

如果子列表命名有规律,也可以用正则批量替换:

import "regexp"

modifiedContent := regexp.MustCompile(`(\d+pixel/list\.m3u8)`).ReplaceAllString(masterContent, "/hls/$1")

3. 验证播放

修改完成后,在浏览器访问localhost:1234/video,此时master.m3u8里的子路径会指向/hls/xxx,浏览器能通过新增的路由获取到子列表和TS文件,即可正常播放视频。

内容的提问来源于stack exchange,提问作者Rza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 07:01:02