32
votes

We have an MP4 video on our site; it plays fine in IE9+, Firefox, Chrome, and Chrome on mac. However, on Safari, the video doesn't play at all - it does trigger a "stalled" event and then nothing loads. I would post our HTML, but I traced the problem further by finding that Safari wouldn't play it even when navigating to the original MP4's URL. When downloaded and played locally, the video works fine in Quicktime.

The weirdest part of this is that of all our developers, I can get the video to work on Safari when I run the related server from my development computer. What's more, other MP4 files in the same directory have a similar problem. This has been the key to me, and I've been searching for any little difference in the way the videos transfer from the server - request/response headers, exact filesize, etc.

Headers copied from Chrome (only since Safari is harder to copy/paste from)

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-US,en;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
DNT:1
Host:*************:8443
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36

Response Headers
Accept-Ranges:bytes
Content-Length:44875102
Content-Type:video/mp4;charset=UTF-8
Date:Tue, 30 Dec 2014 21:11:51 GMT
ETag:W/"44875102-1419959755000"
Last-Modified:Tue, 30 Dec 2014 17:15:55 GMT
Server:Apache-Coyote/1.1

(Also, just in case this reminds you of an older issue; I'm aware Safari on Windows has been dead for ages. This issue is occurring on OS X)

EDIT: New info that might help a bit. I took a personal video from my own webserver, which was able to work from there on the problematic Safari browsers in question, and downloaded it to our server's local video directory. From there, it encounters the same issue as our other videos. This suggests to me that the MP4 itself may not matter - this is probably a server issue of some sort with our Tomcat 7 webserver. We do have the Content-Types registered correctly, which at least covers the basics, but I am curious if there are other necessary parts.

MORE INFO: I didn't think to mention this initially, but we are loading our webpages and videos over an HTTPS connection. Most of our test servers do not have valid certificates, and so we need to click through the standard browser warning that "This server might not be who it says". We are now looking into what it would take to have correct certificates on all our servers.

12
Can you provide a link to a video where this issue occurs?Alexander O'Mara
@AlexanderO'Mara Sorry, but no; this is a part of a product under development. I'd put together a small example fiddle, but it seems very likely headers are relevant, and of course basic examples of properly encoded videos on default-config Apache servers work correctly.Katana314
I tried configuring my local server to serve a video with similar headers and still can't reproduce it. Unless we can get an example video, I'm not sure there's much we can do but guess.Alexander O'Mara
@AlexanderO'Mara That's unfortunately true. It is difficult for me to pass along all factors in some fiddle, like typical JS problems though. I don't think that the MP4 file in question is a factor in the problem (I may retest that thought, but I think I remember finding similar problems with other video files). otherwise, it could be to do with certain headers, or server support of a special HTTP operation, or Safari minor revisions, etc.Katana314
NOTE: Some discoveries since I posted this question; Safari might have issues playing a video on a page with a self-signed certificate. In Safari, you can expand the cert's details and tell it to permanently accept a self-signed certificate, which may cause videos to work. Also, the stalled event may fire even if it's just taking a long time to retrieve data from the server over a distant connection. This might or might not help you.Katana314

12 Answers

39
votes

Safari requires webserver to support "Range" request header in order to play your media content.

https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/CreatingVideoforSafarioniPhone/CreatingVideoforSafarioniPhone.html#//apple_ref/doc/uid/TP40006514-SW6

For a legit "Range" request response, your webserve need to return status code "206".

2
votes

I uploaded a new MP4 file, but it played in SAFARI only (on both my MAC and my iPhone), not Chrome, Oasis, Firefox, or Brave. HTML code was identical to previous successes. File size and Dimensions were fine. But the Codecs on the old, working files were "H.264, AAC". The Codecs on the new, not working files were "MPEG-4, AAC". I edit my video files on VideoPad. So I looked at the specification selections on the "Export file as" options, and, sure enough, the Codecs was defaulted to MPEG-4. I selected H.264 and exported the file. Uploaded to AWS and made public. Retried my new files in the four failure browsers and BINGO!, they all worked. There is a God!

1
votes

Make sure controls='true' type='video/mp4' is given in your html code.

<video loop controls='true' width='100%' height='100%' src='//some_video.mp4' type='video/mp4'></video>
1
votes

This could indeed be an issue of missing byte-range support, depending on the version you are using. It was added to the DMSDownloadServlet in MAGNOLIA-3855 (Magnolia fix version 4.4.6).

1
votes

Just ran into the same issue. All headers, range, etc. were correct. However, I had a poorly constructed service worker. All other browsers handled the failure, Safari did not. Temporarily removed the service worker, and things are back to normal.

0
votes

...

On a side note, does charset make any sense on the video/mp4 type at all? Try removing the charset on it.

EDIT: Yes, charset might be the problem, see: Specify content-type for documents uploaded in Magnolia

EDIT2: Not charset, woops, reading comprehension fail. Might be byte range? To quote: "[...] we found out that Safari/iOS "uses HTTP byte-ranges for requesting audio and video files." Now we guess that the Magnolia DMS file serving doesn't support this feature, and hence the streaming fails."

0
votes

What happens if you add these to your .htaccess?

AddType video/ogg .ogv
AddType video/mp4 .mp4
AddType video/webm .webm
0
votes

Recently, my team ran into a particular issue that resulted in the same behavior. We were using Apache 2.4 and noticed that if we had an authentication layer such as .htpasswd enabled, Safari would not display videos at all even after authenticating. It's almost as if it does not continue to honor the initial authentication clearance for certain types of subsequent HTTP requests.

Sorry I don't have anything more technical to provide, but it's something to check for anyone experiencing video issues only in Safari.

0
votes

I ran into the same problem and solved it but no other answer here is not involved to mine, so I'll remain the solution here for someone following.

I've been making my own video streaming server, which, in the questioned case, simply returns a "Ranged" mp4 file, and I found Safari does not play video carried in HTTP response lacking of "Connection" response header for some reason.

0
votes

Please, forgive me if you already solve this issue! I've had the same problem with my server videos in Safari. I was abled to solve this using POSTMAN/INSOMNIA for check the headers that my server is sending. Chrome may can trick your, once that in this browser the video works fine!

If the video is not ranged(full video request) your server must return status(200) and check it out if the 'Accept-Ranges:bytes' is sent from your server. Header sample status 200:

Server: nginx
Date: Wed, 25 Jul 2018 17:34:18 GMT
Content-Type: video/mp4
Content-Length: 22995782
Connection: keep-alive
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Headers: X-Requested-With,content-type
Access-Control-Allow-Credentials: true
Accept-Ranges: bytes

if the video is ranged your server must return status(206) with range headers correctly. Header sample status 206:

Server: nginx
Date: Wed, 25 Jul 2018 18:13:07 GMT
Content-Type: video/mp4
Content-Length: 1023
Connection: keep-alive
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, PATCH, DELETE
Access-Control-Allow-Headers: X-Requested-With,content-type
Access-Control-Allow-Credentials: true
Accept-Ranges: bytes
Content-Range: bytes 1-1023/22995782

I hope this help you! my best regards, Paulo Durço

0
votes

Safari and iPhone require the "Range" request header to play your media content. you have to handle Range on the server-side.

        if (request.getHeader("Range") != null) {
            System.out.println("Inside range ");


            System.out.println("range value "+request.getHeader("range"));
        //  String fileLocation = melpUploadFiles.getFilethumbPath();
            resfilename=melpUploadFiles.getFilename();

            response.setStatus(206);
            String rangeValue = request.getHeader("range").trim().substring("bytes=".length());


            File fileloc= new File(melpUploadFiles.getFilePath());
             long fileLength = fileloc.length();
            long start, end;
            if (rangeValue.startsWith("-")) {
                end = fileLength - 1;
                start = fileLength - 1 - Long.parseLong(rangeValue.substring("-".length()));
            } else {
                String[] range = rangeValue.split("-");
                start = Long.parseLong(range[0]);
                end = range.length > 1 ? Long.parseLong(range[1]) : fileLength - 1;
            }
            if (end > fileLength - 1) {
                end = fileLength - 1;
            }
            if (start <= end) {

             System.out.println("inside  response block");

                long contentLength = end - start + 1;
                response.setHeader("Content-Length", contentLength + "");
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileLength);
                response.setHeader("Content-Type", "video/mp4");
                response.setHeader("Accept-Ranges","bytes");
                response.setHeader("ETag","\"a226e70476837efa4df4b4bfd75366c4\"");
                response.setHeader("Server", "Apache");
                response.setHeader("Last-Modified",System.currentTimeMillis()+"");
                response.setDateHeader("Expires", System.currentTimeMillis() + 604800000L);


            //  response.setHeader("Content-Disposition", "inline; filename="+resfilename+"");
                RandomAccessFile raf = new RandomAccessFile(fileloc, "r");
                raf.seek(start);
                output = response.getOutputStream();
                byte[] buffer = new byte[2096];
                int bytesRead = 0;
                long totalRead = 0;

                System.out.println("content length "+contentLength);

                while(totalRead<contentLength) {

                    bytesRead = raf.read(buffer);
                    totalRead += bytesRead;
                    output.write(buffer, 0, bytesRead);
                    }
            }
        } 

else { for other browser }

0
votes

In my case, I needed to remove default attribute from track tag:

<track default kind='captions' />

In case someone else have this problem.