# How to stream Safari-compatible video?

**URL:** https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643
**Category:** How to?
**Tags:** clojure
**Created:** [March 27, 2024, 11:28pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643 "2024-03-27T23:28:48Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [March 27, 2024, 11:28pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/1 "2024-03-27T23:28:48Z")

</div>

We have a well-used app that streams certain videos. They are streamed as partial content. The backend for this system is in Clojure, using Reitit with the Ring approach. However, while they work great in Firefox and Chrome, videos fail to load in Safari. Originally we thought it was the lack of a content type (since they are referenced from UUIDs without a file extension), but fixing this did not solve the problem. Then we thought perhaps it was a webkit problem, since webkit is the engine under Safari and Safari itself is impossible to install off of mac (unless you choose an archaic version from 2017, which probably wouldn’t represent anyone anyway). So I tried Epiphany, purportedly one of the best Webkit options on Linux. Alas, it didn’t show the problem, either! So, I am left with a problem that is apparently only Safari, not even webkit, that they cannot run our videos – disabling the large number of students who are trying to use their iphones, ipads, or macbooks to view the course material videos. Has anyone run into or solved this problem? Do you know why other webkit browsers (and indeed every other vendor) have no problem, but all Mac devices on Safari-type browsers are failing?

Related might be this about `range` headers, but we haven’t tried implementing it yet. [React Player is not Working in Safari for all the video format · Issue #1305 · cookpete/react-player · GitHub](https://github.com/cookpete/react-player/issues/1305#issuecomment-1606094291)

---

<div class="post-metadata">

### Author: ![p-himik](https://clojureverse.org/user_avatar/clojureverse.org/p-himik/32/4137_2.png) [@p-himik](https://clojureverse.org/u/p-himik)
#### Post date: [March 28, 2024, 7:25am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/2 "2024-03-28T07:25:00Z")

</div>

This is 100% a wrong place to ask. You’d have a much higher chance of getting a proper answer on any Apple-specific or even a generic web dev forum.

> Originally we thought it was the lack of a content type (since they are referenced from UUIDs without a file extension)

Just wanted to address this - file extensions have nothing to do with content type. In browsers, `content-type` is a separate header that you should set to a proper value in the response.

You should also check whether the video format is supported by Safari in the first place. Plenty of compatibility tables online for that.

Another thing worth mentioning is that on iOS both Chrome and Firefox also use WebKit.

The `range` header shouldn’t affect the playback itself, it should only affect the ability to seek through the video. But Safari has shown itself as an eccentric browser more than once, so who knows.

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [March 28, 2024, 11:57am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/3 "2024-03-28T11:57:02Z")

</div>

The backend is Clojure and we are using Ring to mediate the content, with a front end using raw react (not reagent), hence our grasping at straws for [the possibility it was the range header](https://github.com/cookpete/react-player/issues/1305#issuecomment-1606094291). I asked here hoping someone has navigated this issue. By default, [ring will assume the content type](https://ring-clojure.github.io/ring/ring.middleware.content-type.html) based on the file extension, so we had suspected that problem first. Thanks for the general information about the other browsers using webkit (or is it Apple’s mutation of webkit?) on Mac as well. We had wondered if range that had been the problem as we tried to compare working services (ie YouTube) to see what differences were there; that was, at present, our last idea. Maybe the next steps are to get myself a mac so we can try it’s Safari out to see if we can isolate the actual error and test possible fixes, and to test raw mp4 without streaming as partial media to see if that, at least, works (in which case Ring might be to blame).

---

<div class="post-metadata">

### Author: ![p-himik](https://clojureverse.org/user_avatar/clojureverse.org/p-himik/32/4137_2.png) [@p-himik](https://clojureverse.org/u/p-himik)
#### Post date: [March 28, 2024, 12:18pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/4 "2024-03-28T12:18:01Z")

</div>

How you build a response is irrelevant. Ring + Clojure, SimpleHTTPServer + Python, nc, whatever - doesn’t matter.

Focus on the content of responses, not on how they’re produced. Even if Ring doesn’t provide some header or something else that Safari needs, it’s not Ring itself that’s to blame, it’s how you use it to create responses.

You have given very little detail so it’s hard to provide any actual insight. But ensuring a tight feedback loop by getting your hands on macOS probably makes the most sense. Then you’ll be able to effectively and efficiently compare working resources with non-working. And you don’t need to own a physical Apple hardware for that.

---

<div class="post-metadata">

### Author: ![Tyrahex](https://clojureverse.org/letter_avatar_proxy/v4/letter/t/b38774/32.png) [@Tyrahex](https://clojureverse.org/u/Tyrahex)
#### Post date: [March 28, 2024, 1:26pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/5 "2024-03-28T13:26:00Z")

</div>

> [@Webdev\_Tory](#):
>
> Thanks for the general information about the other browsers using webkit (or is it Apple’s mutation of webkit?) on Mac as well.

On iOS all browsers use WebKit. On macOS however they can use their own engines so Chromium browsers are using Blink, FireFox uses Gecko etc

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [March 28, 2024, 4:39pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/6 "2024-03-28T16:39:28Z")

</div>

So, is Mac using more than the off-the-shelf Webkit? (if so, that would make a sad sense…)

Edit: I just noticed that you are distinguishing between macOS annd iOS, which is not a distinction I held previously

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [March 28, 2024, 4:46pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/7 "2024-03-28T16:46:20Z")

</div>

Thanks for that. I had just hoped that for such a large user-base as “iOS users” a basic technical hurdle like “streaming video” might be addressed with minimal fuss

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [March 28, 2024, 6:59pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/8 "2024-03-28T18:59:29Z")

</div>

Do you use HLS?

> **[Delivering Video Content for Safari | Apple Developer Documentation](https://developer.apple.com/documentation/webkit/delivering_video_content_for_safari/)**
>
> Improve the performance and appearance of video in your website in Safari.

---

<div class="post-metadata">

### Author: ![maxweber](https://clojureverse.org/user_avatar/clojureverse.org/maxweber/32/2271_2.png) [@maxweber](https://clojureverse.org/u/maxweber)
#### Post date: [March 29, 2024, 8:10am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/9 "2024-03-29T08:10:59Z")

</div>

You can also try to host the videos on S3 or Google Cloud Storage. I don’t know if it will help with your specific Safari issue. But at least S3 and Google Cloud Storage handles all the range queries correctly.

However, you might consider to use something like:

> **[Cloudflare Stream | Video streaming made easy and affordable at scale](https://www.cloudflare.com/products/cloudflare-stream/)**
>
> Cloudflare Stream is an easy-to-use online video streaming platform. Spend less time managing your video platform and more time promoting your product.

It will prepare your videos automatically so that you can use adaptive streaming (DASH and HLS) which is important for mobile users with varying connection speeds. They offer a video player implementation that handles all the details for you.

---

<div class="post-metadata">

### Author: ![Linus\_Ericsson](https://clojureverse.org/user_avatar/clojureverse.org/linus_ericsson/32/1303_2.png) [@Linus\_Ericsson](https://clojureverse.org/u/Linus_Ericsson)
#### Post date: [March 29, 2024, 1:27pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/10 "2024-03-29T13:27:34Z")

</div>

Regarding range-requests, I got them working in the [sunng87/jetty9-wrapper](https://github.com/sunng87/ring-jetty9-adapter) - which is using Jetty 12. I have no idea if this would solve any Safari-related problems.

In short these classes are needed:

```auto
  (:import [org.eclipse.jetty.server.handler ResourceHandler ContextHandler ContextHandlerCollection]
           [org.eclipse.jetty.util.resource Resource])

```

and a configuration function is needed for setting up a Jetty ResourceHandler thing (all the terms here are confusing - a Resource is a webresource, not a JVM-resource, a Handler is, of course, not a ring handler but a Jetty handler class).

```auto
(defn configurator
  "returning a configurator that can add a resource handler"
  [{:keys [resources-path
           welcome-files
           directories-listed?
           accept-ranges?
           uri-path] :as opts
    :or {accept-ranges? true
         directories-listed? false
         welcome-files ["index.html"]
         uri-path "/assets"}}]
  (fn 
    [s]
    (log/info "puts context in a contexthandlercollection")
    (let [resource-handler (doto (new ResourceHandler)
                             (.setBaseResource (org.eclipse.jetty.util.resource.Resource/newResource resources-path))
                             (.setDirectoriesListed directories-listed?)
                             (.setWelcomeFiles (into-array welcome-files))
                             (.setAcceptRanges accept-ranges?))
          file-path-context (new ContextHandler uri-path)
          _ (.setHandler file-path-context resource-handler)
          jetty-adapter-context-handler (.getHandler s)
          ch-coll (new ContextHandlerCollection (into-array [file-path-context jetty-adapter-context-handler]))]
      (.setHandler s ch-coll))))

```

The handler is started like this:

```auto
(jetty/run-jetty handler (-> opts (assoc :join? false
                                         :configurator (configurator {:resources-path "assets"
                                                                                         :accept-ranges true
                                                                                         :uri-path "/assets"}))))

```

This code excerpt is happening before the ring-adapter → ring-handler, so any authentication using ring machinery has to be solved in some other way. It would be really nice to be able to return back to the ResourceHandler from inside Ring, but I’m not sure how that could be made to work.

---

<div class="post-metadata">

### Author: ![DrLjotsson](https://clojureverse.org/user_avatar/clojureverse.org/drljotsson/32/2602_2.png) [@DrLjotsson](https://clojureverse.org/u/DrLjotsson)
#### Post date: [March 29, 2024, 8:22pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/11 "2024-03-29T20:22:22Z")

</div>

Does your backend properly respond to range requests or are you just serving the whole file?

I successfully used this code to respond to range requests in my backend [byte range aware file-response for ring · GitHub](https://gist.github.com/tawus/1835734a47bea6cbb5e1). I can stream at least audio to Safari using this.

I also found this that may be more memory conservative [Readme — ring-range-middleware 0.1.0](https://cljdoc.org/d/ring-range-middleware/ring-range-middleware/0.1.0/doc/readme)

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [April 5, 2024, 10:02pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/12 "2024-04-05T22:02:12Z")

</div>

I have searched ring, reitit and the web in general but can’t find anything more than a single Readme file that even mentions HLS.

---

<div class="post-metadata">

### Author: ![thheller](https://clojureverse.org/user_avatar/clojureverse.org/thheller/32/534_2.png) [@thheller](https://clojureverse.org/u/thheller)
#### Post date: [April 5, 2024, 11:35pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/13 "2024-04-05T23:35:15Z")

</div>

I messed around with `ffmpeg` once and that worked ok for a very basic test. Never took it anywhere though. Didn’t really want to deal with video encoding stuff, so scrapped the entire thing.

It definitely isn’t anything reitit, ring or Clojure would even begin to do. So, ffmpeg is where you should probably start looking.

---

<div class="post-metadata">

### Author: ![mvarela](https://clojureverse.org/user_avatar/clojureverse.org/mvarela/32/1478_2.png) [@mvarela](https://clojureverse.org/u/mvarela)
#### Post date: [April 18, 2024, 10:55am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/14 "2024-04-18T10:55:56Z")

</div>

I think you’d be (significantly) better off by using an off-the-shelf video streaming server setup (for HLS or DASH) than re-implementing it in your Clojure application. You could have your application point to the relevant URLs served by that second server.

---

<div class="post-metadata">

### Author: ![Webdev\_Tory](https://clojureverse.org/user_avatar/clojureverse.org/webdev_tory/32/3781_2.png) [@Webdev\_Tory](https://clojureverse.org/u/Webdev_Tory)
#### Post date: [April 18, 2024, 11:54am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/15 "2024-04-18T11:54:30Z")

</div>

What off the shelf options?

---

<div class="post-metadata">

### Author: ![mvarela](https://clojureverse.org/user_avatar/clojureverse.org/mvarela/32/1478_2.png) [@mvarela](https://clojureverse.org/u/mvarela)
#### Post date: [April 18, 2024, 12:15pm UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/16 "2024-04-18T12:15:02Z")

</div>

Sorry I wasn’t clear! If you want HLS, there’s a bunch of servers that can do that [(see here)](https://en.wikipedia.org/wiki/HTTP_Live_Streaming#Server_implementations). If you want to do DASH, then there are many options for preparing the content (encoding, MPD, segments, etc). You can then serve those from any web server, and there are JS clients that can deal with DASH content.

---

<div class="post-metadata">

### Author: ![system](https://clojureverse.org/uploads/default/original/2X/5/51079bf9e4b7d9466242c06cf1e43b9f8bd6da14.png) [@system](https://clojureverse.org/u/system)
#### Post date: [October 18, 2024, 12:15am UTC](https://clojureverse.org/t/how-to-stream-safari-compatible-video/10643/17 "2024-10-18T00:15:34Z")

</div>

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.
