<video> plays in other browsers, but not Safari
Asked Answered
C

14

41

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.

Cuckoopint answered 30/12, 2014 at 21:24 Comment(6)
Can you provide a link to a video where this issue occurs?Phifer
@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.Cuckoopint
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.Phifer
@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.Cuckoopint
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.Cuckoopint
chiming in a few years later -- I ran into this issue. have a static site playing video hosted on netlify. it was working everywhere, then suddenly MP4s stopped playing in desktop/mobile safari. I could confirm netlify's CDN was an issue as the content is now in S3 behind cloudfront with no problems in safari.Stilliform
C
48

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".

Coo answered 30/3, 2016 at 3:42 Comment(2)
This is true. It also means you have to either add an extra layer (e.g. Varnish or Nginx) or completely reimplement range handling in your application code for dynamically generated or converted content.Sharecrop
"iOS supports movies larger than 2 GB" meanwhile it has issues playing a 50kb gif video because of this odd requirement...Kimkimball
C
3

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!

Celebrated answered 12/11, 2018 at 4:27 Comment(2)
So the solution was basically to choose H.264 over MPEG-4 codec?Hebdomadal
This worked for me! I had the same issue and found the root cause was the videos were H.265-encoded MP4s but Apple recommends (requires?) H.264-encoded MP4s and mention this in [the "Delivering Video Content for Safari" documentation][1]. Strangely 1 of my H.265-encoded MP4s did load and play but the other, much larger MP4 did not until I changed to H.264-encoded video. It seems the devil really is in the details. [1]: developer.apple.com/documentation/webkit/…Erma
R
3

I had a similar problem with audio. The solution was to add the source tag, to the audio tag. Can you try in your case the following:

<video loop controls='true' width='100%' height='100%'>      
   <source src='//some_video.mp4' type='video/mp4'>
</video>
Raye answered 16/9, 2021 at 7:19 Comment(2)
There is no audio tag involved here?Hebdomadal
He mentioned "Can you try in your case the following:"Classis
T
1

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).

Tulle answered 23/2, 2015 at 20:42 Comment(0)
P
1

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.

Pren answered 25/10, 2019 at 0:47 Comment(0)
B
0

...

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."

Babylonia answered 30/1, 2015 at 20:12 Comment(2)
What's really annoying is that I'd like an easy way of confirming this, but in Safari's developer tools, when I click on the network request for the MP4 file, it incorrectly claims that there were no request or response headers. Maybe I'll find another way of verifying.Cuckoopint
No chance of a test link? Private message me if need be (twitter = @nexii)Babylonia
J
0

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>
Jeffery answered 6/2, 2015 at 7:58 Comment(6)
Hm...To my knowledge, 'controls' is a boolean attribute, meaning it doesn't need ='true', and no resource I know of mentions a 'type=etc' attribute on the video tag. I never did give my HTML itself, but basically I have it very similar to yours, except with the src / type defined inside a source element inside the video.Cuckoopint
@Cuckoopint I agree with you in most of cases but not this one.Jeffery
@huazihihao Agree about what?Cuckoopint
what was the conclusion? did anyone get this working?Swett
type="*" was the catchTypeset
Anyone found this answer helpful?Patinous
L
0

What happens if you add these to your .htaccess?

AddType video/ogg .ogv
AddType video/mp4 .mp4
AddType video/webm .webm
Lona answered 6/2, 2015 at 15:50 Comment(5)
It's not an apache server, so not htaccess. It does have the MIME types through other configuration files, though.Cuckoopint
if you save the generated HTML as an html file, and load that into Safari (after fixing any broken urls), does it work?Lona
No; it doesn't even work if the video url is loaded directly in the browser.Cuckoopint
Safari is rubbish with streamed media mime types. It ignores the MIME type and looks at the file name extension instead. Getting the MIME type right is good for other browsers but fruitless for Safari.Hypocrite
Safari is rubbish full stop. It really is the NEW IEBecame
W
0

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.

Wardle answered 25/8, 2017 at 13:10 Comment(0)
B
0

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

Bum answered 25/7, 2018 at 18:21 Comment(1)
my server does this. still can't play on Safari.Micromillimeter
N
0

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 }

Netty answered 29/2, 2020 at 12:30 Comment(0)
E
0

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

<track default kind='captions' />

In case someone else have this problem.

Ermelindaermengarde answered 10/6, 2021 at 17:46 Comment(0)
L
0

Safari requires webserver to support "Range" request header.

Here's a PHP code to read mp4 files:

function streamvideo($fileLocation) {
    if (connection_status() != 0)
        return (false);
    $array = explode('.', $fileLocation);
    $extension = strtolower(end($array));
    

    $contentType = 'video/mp4';
    
    header("Cache-Control: public");
    header("Content-Transfer-Encoding: binary\n");
    header("Content-Type: $contentType");
    
    header("Accept-Ranges: bytes");
    $range = 0;
    
    
    $size = filesize($fileLocation);
    
    if (isset($_SERVER['HTTP_RANGE'])) {
        list ($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
        
        $range = str_replace($range, "-", $range);
        $size2 = $size - 1;
        $new_length = $size - $range;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $range$size2/$size");
    } else {
        $size2 = $size - 1;
        header("Content-Range: bytes 0-$size2/$size");
        header("Content-Length: " . $size);
    }
    
    if ($size == 0) {
        die('Zero byte file! Aborting download');
    }   

    $fp = fopen("$fileLocation", "rb");
    
    fseek($fp, $range);
    
    while (! feof($fp) and (connection_status() == 0)) {
        //set_time_limit(0); 
        print(fread($fp, 1024 * 4));
        flush();
        ob_flush();
    }
    fclose($fp);
    
    return ((connection_status() == 0) and ! connection_aborted());
}
Laura answered 23/12, 2023 at 11:15 Comment(0)
F
-1

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.

Flagstone answered 4/9, 2017 at 9:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.