GoLang http web server provides video (mp4)

I developed a web server using golang. Pretty flat stuff, it just provides html / js / css and images that work just fine:

func main() { http.Handle("/", new(viewHandler)) http.ListenAndServe(":8080", nil) } func (vh *viewHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[1:] log.Println(path) data, err := ioutil.ReadFile(string(path)) if err == nil { var contentType string if strings.HasSuffix(path, ".html") { contentType = "text/html" } else if strings.HasSuffix(path, ".css") { contentType = "text/css" } else if strings.HasSuffix(path, ".js") { contentType = "application/javascript" } else if strings.HasSuffix(path, ".png") { contentType = "image/png" } else if strings.HasSuffix(path, ".jpg") { contentType = "image/jpeg" } w.Header().Add("Content-Type", contentType) w.Write(data) } else { log.Println("ERROR!") w.WriteHeader(404) w.Write([]byte("404 - " + http.StatusText(404))) } } 

I tried to add mp4 video to my website in the same way:

 <video width="685" height="525" autoplay loop style="margin-top: 20px"> <source src="../public/video/marketing.mp4" type="video/mp4">Your browser does not support the video tag.</video> 

and improved the content type part of my go application:

 else if strings.HasSuffix(path, ".mp4") { contentType = "video/mp4" } 

When I open the html file directly in Chrome, the video plays correctly, but if I open the site by calling the web server http: // localhost ... it cannot be loaded and therefore cannot be played.

There is no video uploaded, if I try to click it directly, the browser simply displays some video contacts without downloading the file:

enter image description here

media loaded twice

media information

Any ideas on this? thanks in advance.

UPDATE:

As I said, I also tried another video that works great! but I don’t know why, I compared both videos:

enter image description here enter image description here

Could this be the size

Greetings and thanks!

UPDATE2:

icza is right, and I marked his post as the correct answer to my question. But let me explain what I finally did to make it work:

I tried to solve my problem without using the http.FileServe method. So I implemented like this:

 } else if strings.HasSuffix(path, ".mp4") { contentType = "video/mp4" size := binary.Size(data) if size > 0 { requestedBytes := r.Header.Get("Range") w.Header().Add("Accept-Ranges", "bytes") w.Header().Add("Content-Length", strconv.Itoa(size)) w.Header().Add("Content-Range", "bytes "+requestedBytes[6:len(requestedBytes)]+strconv.Itoa(size-1)+"/"+strconv.Itoa(size)) w.WriteHeader(206) } } w.Header().Add("Content-Type", contentType) w.Write(data) 

This worked the first time I played a video. But since I want it to loop, it should start from the very beginning, but it is stuck in the very last frame of the video.

So, I applied the ServeFile () method, for example:

 if contentType == "video/mp4" { http.ServeFile(w, r, path) } else { w.Header().Add("Content-Type", contentType) w.Write(data) } 

Video also plays correctly in the loop. But I still don't know why ServeFile () works, and the other method does not work, although the answer is the same for both methods. However, the content looks like this:

enter image description here

Greetings to all who helped me.

+5
source share
4 answers

Your “problem” is related to the length and size of the video. I don’t know how the default buffer used by Chrome is used (this may depend on several things, such as free memory, free disk space, etc.), but you should not rely on it to play your video!

One of your videos is few seconds smaller and almost a third more than the other. Chrome fully loads the smaller video (probably in memory), but doesn't do the same with the other. And since it is larger than the buffer, it needs a server to support range requests (partial content maintenance). As you serve the content of your video, you do not support the partial provision of content (which Chrome expects in the case of "large" videos), so Chrome simply refuses to process / play the video.

You must serve your files using http.ServeFile() , which supports Range range requests (required for videos that are larger than a certain size). See Related Question: How do I serve HTTP partial content with Go?

Once you do this, both of your video files will play normally.

Going further, there is no practical reason not to use http.ServeFile() . It will not load the complete file into memory, as you did, which is a very bad idea in the case of large (video) files. http.ServeFile() processes range requests, if specified, and also correctly sets response headers, including the Content-Type (and knows that a .mp4 file requires a video/mp4 MIME type).

Analysis of large video file requests

Testing this simple application:

 func fileh(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "/path/to/a/big/video.mp4") } func main() { http.HandleFunc("/", fileh) panic(http.ListenAndServe("localhost:8080", nil)) } 

And opening http://localhost:8080 from Chrome, the browser makes 2 requests (I intentionally filtered out non-essential / unrelated request-response headers):

Request No. 1

 GET / HTTP/1.1 

To get the requested (root) path.

Answer # 1

 HTTP/1.1 200 OK Accept-Ranges: bytes Content-Length: 104715956 Content-Type: video/mp4 

As you can see, the Go app reports that the video is about 100 MB in size. http.ServeFile() also includes an Accept-Ranges: bytes response header, which lets clients know that we support range requests.

How does Chrome react to this? Closes the connection after receiving 32 KB (32.2 KB, including headers, which is enough to look into the stream and say what it can do with it), then starts another request (which also happens even if the Accept-Ranges response header is not sent - your sample code does not send):

Request No. 2

 GET / HTTP/1.1 Referer: http://localhost:8080/ Range: bytes=0- 

Chrome starts a range request (requesting everything from the first byte), so if your Go application does not support this, you have problems. See Answer # 2.

Answer # 2

 HTTP/1.1 206 Partial Content Accept-Ranges: bytes Content-Length: 104715956 Content-Range: bytes 0-104715955/104715956 Content-Type: video/mp4 

http.ServeFile() behaves well and correctly responds to a range request ( HTTP 206 Partial Content status code and Content-Range header) and sends the requested range (all in this case).

Your sample code does not respond with HTTP 206 Partial Content , but with plain HTTP 200 OK . When Chrome receives an unexpected HTTP 200 OK , the moment it refuses to play your video. If the video file is small, Chrome will not make anything out of it because it will simply accept an HTTP 200 OK response (and most likely will just save a small video in memory or in a cache file).

+6
source

Your code seems to work just fine for me. Here is a complete working example that I tested using Chrome on OS X.

main.go:

 package main import "io/ioutil" import "log" import "net/http" import "strings" type viewHandler struct{} func (vh *viewHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { path := r.URL.Path[1:] data, err := ioutil.ReadFile(string(path)) if err != nil { log.Printf("Error with path %s: %v", path, err) w.WriteHeader(404) w.Write([]byte("404")) } if strings.HasSuffix(path, ".html") { w.Header().Add("Content-Type", "text/html") } else if strings.HasSuffix(path, ".mp4") { w.Header().Add("Content-Type", "video/mp4") } w.Write(data) } func main() { http.Handle("/", new(viewHandler)) http.ListenAndServe(":8080", nil) } 

index.html

 <!doctype html> <html> <body> <video autoplay loop> <source src="big_buck_bunny.mp4" type="video/mp4"> </video> </body> </html> 

Video downloaded from http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4 .

+2
source

Well, since this is not a direct stream (in terms of size and duration, since the source is an mp4 file), I think your server should respect the Range header in the request and respond with the corresponding Content-Length , Content-Range and Accept-Range (otherwise the search will be impossible, and I think you do not want this). That should make Chrome happy. Be careful when implementing the processing of the Range header value in a query - if it is not 0- , then you should actually look for the requested point and return data from there. Also, I don’t think the video should be transferred with Transfer-Encoding set to chunked .

0
source

This is an example that supports all types of files. contentType: = http.DetectContentType (buffer) https://play.golang.org/p/XuVgmsCHUZB

0
source

All Articles