使用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
相关产品推荐
相关产品推荐

