HTML <audio> 流无法在 Apple 的 iOS Safari 和 iPhone 上播放 - https://sidcloud.net
HTML <audio> stream don't play on Apple's iOS Safari and iPhone - https://sidcloud.net
我的网页需要一些帮助。我流式传输 wav 文件,仅 Apple 软件音乐无法播放。我在互联网上寻找解决方案(其他人的问题)并尝试了不同的解决方案,但没有成功。也许最好的办法是检查我的网站:
https://sidcloud.net。
我在控件中使用 html audio html element。流式传输似乎开始了(从后端的角度来看),但是 audio html element 不播放它。
任何人都可以帮助和检查该网站吗?
提前致谢!
巴泰克
成功了\o/。我必须正确处理字节范围。 Apple 的软件使用范围功能,所以我必须满足它的需求:)。这是我在 Golang/GIN:
中的 REST API 函数
func AudioGet(c *gin.Context) {
// Typ połączania
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Connection", "Keep-Alive")
c.Header("Transfer-Encoding", "identity")
c.Header("Accept-Ranges", "bytes")
// Odczytujemy parametr - numer muzy
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
var size int64
if fileExists(filenameWAV) {
s, err := fileSize(filenameWAV)
if ErrCheck(err) {
log.Println("[GIN:AudioGet] Size of file " + filenameWAV + " = " + strconv.Itoa(int(s)))
size = s
} else {
log.Println("[GIN:AudioGet] Can't read size of file " + filenameWAV)
c.JSON(http.StatusInternalServerError, "Can't read size of file")
return
}
} else {
log.Println("[GIN:AudioGet] No WAV file " + filenameWAV)
c.JSON(http.StatusInternalServerError, "No WAV file")
return
}
//
// Analiza nagłówka - ile bajtów mamy wysłać
//
bytesToSend := 0
bytesToSendStart := 0
bytesToSendEnd := 0
headerRange := c.GetHeader("Range")
log.Println("[GIN:AudioGet2] Header:Range = " + headerRange)
if len(headerRange) > 0 {
headerRangeSplitted1 := strings.Split(headerRange, "=")
if len(headerRangeSplitted1) > 0 {
log.Println("[GIN:AudioGet2] range in " + headerRangeSplitted1[0])
if len(headerRangeSplitted1) > 1 {
headerRangeSplitted2 := strings.Split(headerRangeSplitted1[1], "-")
if len(headerRangeSplitted2) > 0 {
log.Println("[GIN:AudioGet2] start = " + headerRangeSplitted2[0])
if len(headerRangeSplitted2) > 1 {
log.Println("[GIN:AudioGet2] end = " + headerRangeSplitted2[1])
bytesToSendStart, err := strconv.Atoi(headerRangeSplitted2[0])
if ErrCheck2(err) {
bytesToSendEnd, err := strconv.Atoi(headerRangeSplitted2[1])
if ErrCheck2(err) {
bytesToSend = bytesToSendEnd - bytesToSendStart + 1
}
}
}
}
}
}
}
log.Println("[GIN:AudioGet2] Bytes to send " + strconv.Itoa(bytesToSend))
log.Println("[GIN:AudioGet2] From " + strconv.Itoa(bytesToSendStart) + " to " + strconv.Itoa(bytesToSendEnd))
if bytesToSend > 0 {
c.Header("Content-length", strconv.Itoa(bytesToSend))
c.Header("Content-range", "bytes "+strconv.Itoa(bytesToSendStart)+"-"+strconv.Itoa(bytesToSendEnd)+"/"+strconv.Itoa(int(size)))
size = int64(bytesToSend)
}
// Streaming LOOP...
// ----------------------------------------------------------------------------------------------
// Otwieraamy plik - bez sprawdzania błędów
file, err := os.Open(filenameWAV)
defer file.Close()
if ErrCheck(err) {
// Info o wejściu do GET
log.Println("[GIN:AudioGet] Sending " + id + "...")
p := make([]byte, size)
file.ReadAt(p, int64(bytesToSendStart))
file.Close()
if bytesToSend > 0 {
c.Data(http.StatusPartialContent, "audio/wav", p)
} else {
c.Data(http.StatusOK, "audio/wav", p)
}
} else {
log.Println("[GIN:AudioGet] Can't open file " + filenameWAV)
}
}
这是日志:
2020/05/04 14:28:59 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:28:59 [GIN:AudioGet2] Header:Range = bytes=0-1
2020/05/04 14:28:59 [GIN:AudioGet2] range in bytes
2020/05/04 14:28:59 [GIN:AudioGet2] start = 0
2020/05/04 14:28:59 [GIN:AudioGet2] end = 1
2020/05/04 14:28:59 [GIN:AudioGet2] Bytes to send 2
2020/05/04 14:28:59 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:28:59 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:00 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:00 [GIN:AudioGet2] Header:Range = bytes=0-65535
2020/05/04 14:29:00 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:00 [GIN:AudioGet2] start = 0
2020/05/04 14:29:00 [GIN:AudioGet2] end = 65535
2020/05/04 14:29:00 [GIN:AudioGet2] Bytes to send 65536
2020/05/04 14:29:00 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:00 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:00 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:00 [GIN:AudioGet2] Header:Range = bytes=26411008-26460043
2020/05/04 14:29:00 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:00 [GIN:AudioGet2] start = 26411008
2020/05/04 14:29:00 [GIN:AudioGet2] end = 26460043
2020/05/04 14:29:00 [GIN:AudioGet2] Bytes to send 49036
2020/05/04 14:29:00 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:00 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:01 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:01 [GIN:AudioGet2] Header:Range = bytes=65536-26411007
2020/05/04 14:29:01 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:01 [GIN:AudioGet2] start = 65536
2020/05/04 14:29:01 [GIN:AudioGet2] end = 26411007
2020/05/04 14:29:01 [GIN:AudioGet2] Bytes to send 26345472
2020/05/04 14:29:01 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:01 [GIN:AudioGet] Sending 190651...
因此,如您所见,Apple 浏览器首先请求前两个字节,然后是 c.a。 64kB,然后是音乐文件的其余部分。
我在为同样的问题苦苦挣扎时发现了这个,真的浪费了很多时间!我也开始自己实现这个,按照你的例子(谢谢!)但最后真的很幸运地发现 GoLang 的 http 库实际上可以为你做这件事。所以你可以这样做:
func AudioGet(c *gin.Context) {
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
file := goGetFile(filenameWAV) // assuming goGetFile returns []byte
http.ServeContent(c.Writer, c.Request, "recording.wav", time.Now(), bytes.NewReader(file))
}
或者,如果文件在文件系统上,甚至:
func AudioGet(c *gin.Context) {
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
http.ServeFile(c.Writer, c.Request, filenameWAV) // assuming filenameWAV is the location
}
这是它的文档:https://golang.org/pkg/net/http/#ServeContent(ServeFile 就在它的正下方)
我的网页需要一些帮助。我流式传输 wav 文件,仅 Apple 软件音乐无法播放。我在互联网上寻找解决方案(其他人的问题)并尝试了不同的解决方案,但没有成功。也许最好的办法是检查我的网站: https://sidcloud.net。 我在控件中使用 html audio html element。流式传输似乎开始了(从后端的角度来看),但是 audio html element 不播放它。 任何人都可以帮助和检查该网站吗? 提前致谢! 巴泰克
成功了\o/。我必须正确处理字节范围。 Apple 的软件使用范围功能,所以我必须满足它的需求:)。这是我在 Golang/GIN:
中的 REST API 函数func AudioGet(c *gin.Context) {
// Typ połączania
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Connection", "Keep-Alive")
c.Header("Transfer-Encoding", "identity")
c.Header("Accept-Ranges", "bytes")
// Odczytujemy parametr - numer muzy
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
var size int64
if fileExists(filenameWAV) {
s, err := fileSize(filenameWAV)
if ErrCheck(err) {
log.Println("[GIN:AudioGet] Size of file " + filenameWAV + " = " + strconv.Itoa(int(s)))
size = s
} else {
log.Println("[GIN:AudioGet] Can't read size of file " + filenameWAV)
c.JSON(http.StatusInternalServerError, "Can't read size of file")
return
}
} else {
log.Println("[GIN:AudioGet] No WAV file " + filenameWAV)
c.JSON(http.StatusInternalServerError, "No WAV file")
return
}
//
// Analiza nagłówka - ile bajtów mamy wysłać
//
bytesToSend := 0
bytesToSendStart := 0
bytesToSendEnd := 0
headerRange := c.GetHeader("Range")
log.Println("[GIN:AudioGet2] Header:Range = " + headerRange)
if len(headerRange) > 0 {
headerRangeSplitted1 := strings.Split(headerRange, "=")
if len(headerRangeSplitted1) > 0 {
log.Println("[GIN:AudioGet2] range in " + headerRangeSplitted1[0])
if len(headerRangeSplitted1) > 1 {
headerRangeSplitted2 := strings.Split(headerRangeSplitted1[1], "-")
if len(headerRangeSplitted2) > 0 {
log.Println("[GIN:AudioGet2] start = " + headerRangeSplitted2[0])
if len(headerRangeSplitted2) > 1 {
log.Println("[GIN:AudioGet2] end = " + headerRangeSplitted2[1])
bytesToSendStart, err := strconv.Atoi(headerRangeSplitted2[0])
if ErrCheck2(err) {
bytesToSendEnd, err := strconv.Atoi(headerRangeSplitted2[1])
if ErrCheck2(err) {
bytesToSend = bytesToSendEnd - bytesToSendStart + 1
}
}
}
}
}
}
}
log.Println("[GIN:AudioGet2] Bytes to send " + strconv.Itoa(bytesToSend))
log.Println("[GIN:AudioGet2] From " + strconv.Itoa(bytesToSendStart) + " to " + strconv.Itoa(bytesToSendEnd))
if bytesToSend > 0 {
c.Header("Content-length", strconv.Itoa(bytesToSend))
c.Header("Content-range", "bytes "+strconv.Itoa(bytesToSendStart)+"-"+strconv.Itoa(bytesToSendEnd)+"/"+strconv.Itoa(int(size)))
size = int64(bytesToSend)
}
// Streaming LOOP...
// ----------------------------------------------------------------------------------------------
// Otwieraamy plik - bez sprawdzania błędów
file, err := os.Open(filenameWAV)
defer file.Close()
if ErrCheck(err) {
// Info o wejściu do GET
log.Println("[GIN:AudioGet] Sending " + id + "...")
p := make([]byte, size)
file.ReadAt(p, int64(bytesToSendStart))
file.Close()
if bytesToSend > 0 {
c.Data(http.StatusPartialContent, "audio/wav", p)
} else {
c.Data(http.StatusOK, "audio/wav", p)
}
} else {
log.Println("[GIN:AudioGet] Can't open file " + filenameWAV)
}
}
这是日志:
2020/05/04 14:28:59 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:28:59 [GIN:AudioGet2] Header:Range = bytes=0-1
2020/05/04 14:28:59 [GIN:AudioGet2] range in bytes
2020/05/04 14:28:59 [GIN:AudioGet2] start = 0
2020/05/04 14:28:59 [GIN:AudioGet2] end = 1
2020/05/04 14:28:59 [GIN:AudioGet2] Bytes to send 2
2020/05/04 14:28:59 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:28:59 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:00 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:00 [GIN:AudioGet2] Header:Range = bytes=0-65535
2020/05/04 14:29:00 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:00 [GIN:AudioGet2] start = 0
2020/05/04 14:29:00 [GIN:AudioGet2] end = 65535
2020/05/04 14:29:00 [GIN:AudioGet2] Bytes to send 65536
2020/05/04 14:29:00 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:00 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:00 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:00 [GIN:AudioGet2] Header:Range = bytes=26411008-26460043
2020/05/04 14:29:00 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:00 [GIN:AudioGet2] start = 26411008
2020/05/04 14:29:00 [GIN:AudioGet2] end = 26460043
2020/05/04 14:29:00 [GIN:AudioGet2] Bytes to send 49036
2020/05/04 14:29:00 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:00 [GIN:AudioGet] Sending 190651...
2020/05/04 14:29:01 [GIN:AudioGet] Size of file cache/190651.wav = 26460044
2020/05/04 14:29:01 [GIN:AudioGet2] Header:Range = bytes=65536-26411007
2020/05/04 14:29:01 [GIN:AudioGet2] range in bytes
2020/05/04 14:29:01 [GIN:AudioGet2] start = 65536
2020/05/04 14:29:01 [GIN:AudioGet2] end = 26411007
2020/05/04 14:29:01 [GIN:AudioGet2] Bytes to send 26345472
2020/05/04 14:29:01 [GIN:AudioGet2] From 0 to 0
2020/05/04 14:29:01 [GIN:AudioGet] Sending 190651...
因此,如您所见,Apple 浏览器首先请求前两个字节,然后是 c.a。 64kB,然后是音乐文件的其余部分。
我在为同样的问题苦苦挣扎时发现了这个,真的浪费了很多时间!我也开始自己实现这个,按照你的例子(谢谢!)但最后真的很幸运地发现 GoLang 的 http 库实际上可以为你做这件事。所以你可以这样做:
func AudioGet(c *gin.Context) {
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
file := goGetFile(filenameWAV) // assuming goGetFile returns []byte
http.ServeContent(c.Writer, c.Request, "recording.wav", time.Now(), bytes.NewReader(file))
}
或者,如果文件在文件系统上,甚至:
func AudioGet(c *gin.Context) {
id := c.Param("id")
filenameWAV := cacheDir + id + ".wav"
http.ServeFile(c.Writer, c.Request, filenameWAV) // assuming filenameWAV is the location
}
这是它的文档:https://golang.org/pkg/net/http/#ServeContent(ServeFile 就在它的正下方)