I need some help with my web page. I stream wav files and only on Apple's software music don't play. I was looking for solution in Internet (other peoples problems) and tried different solutions, but without success. Maybe the best would be to examine my web site: https://sidcloud.net. I use html audio html element with my controls. It seems the streaming is starting (from back-end perspective), but audio html element don't play it. Can anyone help and examine the web site? Thanks in advance! Bartek
-
I debugged your website and you're receiving error `Failed to load resource: Plug-in handled load`. refer to this answer: https://stackoverflow.com/a/26972619/3815069 – M Reza Apr 27 '20 at 09:11
-
Thanks for checking and for the answer link! It's true, I saw in the header that Safari is sending request with range in header **bytes=0-1**, but I didn't bother it somehow and send as much I wanted (64kB chunks). I was more concentrated on the front-end side until now. I will try to decode that field and send as much bytes as wanted in the request. Other browsers/systems send **bytes=0-** so they don't bother chunk size I suppose. – DKT Apr 27 '20 at 11:59
-
Hmm... Will I have to sent entire WAV file bye 2 bytes long chunks? Or will I get another GET request from browser... Maybe that 2 bytes are just for checking file type (WAV ID) and then I will be able to send rest of the file like I do now? Will see. – DKT Apr 27 '20 at 12:16
-
Well, my above try-outs was unsuccessful. I'm using c.Data from GIN package to send chunks, and GIN is sending my data in it's own bigger chunks. The reason I don't want to use Apache as a separate server for WAV files is that I have to modify the WAV data during sending, sometimes. – DKT Apr 27 '20 at 13:29
-
I will try to find method to invoke FLUSH method after filling this 2 bytes. – DKT Apr 27 '20 at 14:14
-
Please don't post updates in the comments. Update the question to including all details you have, including your latest findings. – szatmary Apr 27 '20 at 16:56
2 Answers
Got it working \o/. I had to handle bytes ranges correctly. Apple's software use range feature, so I had to satisfy its needs :). Here is my REST API function in Golang/GIN:
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)
}
}
and here is log:
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...
So, as You can see Apple browsers are asking for first two bytes first, next c.a. 64kB and then the rest of music file.

- 3
- 5
I found this while struggling with the same issue and really wasted a lot of time on it! I started to implement this myself too, following your example (thanks for that!) but in the end really luckily came across the fact that GoLang's http library can actually do this for you. So you can do either:
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))
}
or, if the file is on the filesystem, even:
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
}
Here's the documentation for it: https://golang.org/pkg/net/http/#ServeContent (ServeFile is directly beneath it)

- 36
- 5