10

I have a Rails app I'm trying to play an HTML5 video from using the following markup:

Doesn't work:

<video controls poster="http://lvh.me:3000/assets/videos/myvideo.png">
  <source src="http://lvh.me:3000/assets/images/videos/myvideo.mp4" type="video/mp4">
  <source src="http://lvh.me:3000/assets/images/videos/myvideo.webm" type="video/webm">
  <source src="http://lvh.me:3000/assets/images/videos/myvideo.ogv" type="video/ogg">
</video>

On Safari, the video says "Loading..." but never plays, although it works as expected in Chrome and Firefox. I thought it may have been the path at first, but I've tried absolute paths, relative paths, and the Rails image_path helper with no results.

To debug, I copied this example HTML5 video tag and it plays in Safari as expected (the only difference here is the source video):

Works: externally hosted sample video

<video controls poster="http://easyhtml5video.com/assets/video/Penguins_of_Madagascar.jpg">
  <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.mp4" type="video/mp4">
  <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.webm" type="video/webm">
  <source src="http://easyhtml5video.com/assets/video/new/Penguins_of_Madagascar.ogv" type="video/ogg">
</video>

However, when I take this exact same markup and host these same files locally, the video stops working in Safari:

Doesn't work: locally hosted sample video

<video controls poster="http://lvh.me:3000/assets/videos/Penguins_of_Madagascar.jpg">
  <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.mp4" type="video/mp4">
  <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.webm" type="video/webm">
  <source src="http://lvh.me:3000/assets/videos/new/Penguins_of_Madagascar.ogv" type="video/ogg">
</video>

Notes:

  • I'm not getting errors in the Safari console or Rails log; no 404s on the files or anything.
  • Locally hosted videos work in Chrome and FF, so I know the paths are correct.
  • Externally hosted videos work fine in Safari.
  • Locally hosted videos work in Safari outside of the Rails app—I created a static page and used all the examples above to good effect.

Based on all of this, it seems like some combination of Safari and Rails that's preventing the videos from loading.

sea_monster
  • 659
  • 3
  • 8
  • 18
  • Maybe it has something to do with content-types can you try to add this to your `mime_types.rb` `Rack::Mime::MIME_TYPES.merge!( ".ogg" => "application/ogg", ".ogx" => "application/ogg", ".ogv" => "video/ogg", ".wemb" => "video/webm", ".mp4" => "video/mp4", ".m4v" => "video/mp4")` – Chris Feb 09 '16 at 09:08
  • @Chris I added that and restarted Rails, but no luck. I do see that the MIME type is already correct without this change, according to curl. In case you took that list from somewhere important, note that there's a typo in it: `".wemb"`. I changed that to `".webm"` when testing locally. – Shepmaster Feb 09 '16 at 23:15
  • An interesting thing to note is that the file **does** correctly download and start playing when I deploy my application to Heroku. – Shepmaster Feb 09 '16 at 23:16
  • ok I tested it locally and have the same problem. However it works when I put my video in public folder. Do you really need the videos to be inside assets? It probably works on heroku cause in production.rb you have `config.serve_static_files = false` which means that files are served by nginx or apache – Chris Feb 10 '16 at 09:29
  • @Chris I'm using the [rails_12factor gem](https://github.com/heroku/rails_12factor) in production as suggested by Heroku. As I understand it, that forcibly sets `config.serve_static_assets = true`. It's hard to answer if I really need it to be an asset; does anything really need to be an asset? As I understand it, one benefit of doing so is that I get the asset digest, allowing for automatic cache busting when the asset changes. That seems like a good thing to me. – Shepmaster Feb 10 '16 at 16:58
  • as fare as i remember rails in older versions is not able to serve videos in chunks. skipping frame to go forth and back needs a gem. maybe thats the reason the film doesn't in every browser. – devanand Feb 11 '16 at 11:34
  • @devanand an interesting point, but can you clarify which versions of Rails you mean? I am using Rails 4.2.5.1, which is the current version hosted on RubyGems as of right now. – Shepmaster Feb 11 '16 at 16:16
  • Is Safari okay with loading via URL with port number? Try: ` – VC.One Feb 11 '16 at 20:07
  • @VC.One running Rails on port 80 seems to have no change on whether Safari loads the video. Additionally, when I placed the video in the `public` folder, it was able to be loaded from port 3000. Of course, that doesn't allow for the asset pipeline, as mentioned in earlier comments. – Shepmaster Feb 11 '16 at 20:36
  • [My Rails `config/application.rb`](https://gist.github.com/shepmaster/c83c08cabd34855602a9). It's the default configuration from `rails new`. I am serving via `rails s` using Puma. – Shepmaster Feb 12 '16 at 18:04

3 Answers3

5

I had the same problem and figured out it's the byte-range you need for moving back and forward in video.

Here's some middleware that adds support for the byte-range HTTP header:

# (c) Thomas Fritzsche
# This is prove-of-concept coding only
# iOS devices insist on support for byte-rage http header, that is not native
# supported by rack apps like dragonfly
# this rack middleware will evaluate the http header and provide byte range support.

# For a dragonfly Rails (3.2.3) app I have tested this will call like this.
# I reload Rack::Cache that case trouble when initialized by Rails.
# This small trick makes it working :-)
#-----------------------
#require 'dragonfly/rails/images'
#require "range"
#
#
#Rails.application.middleware.delete(Rack::Cache)
#Rails.application.middleware.insert 0, Rack::Cache, {
#  :verbose     => true,
#  :metastore   => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/meta"), 
#  :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
#}
#
#Rails.application.middleware.insert_before Rack::Cache, RangeFilter
#
# [...]
#-------------------


class RangeFilter
  def initialize(app)
    @app = app
  end

  def call(env)
    dup._call(env)
  end   

  def _call(env)
    @status, @headers, @response = @app.call(env)    
    range = env["HTTP_RANGE"]
    if @status == 200 and range and /\Abytes=(\d*)-(\d*)\z/ =~ range
      @first_byte, @last_byte = $1, $2


      @data = "".encode("BINARY")
      @response.each do |s|
        @data << s
      end             
      @length = @data.bytesize if @length.nil? 
      if @last_byte.empty?
        @last_byte = @length - 1
      else
        @last_byte = @last_byte.to_i
      end
      if @first_byte.empty?
        @first_byte = @length - @last_byte
        @last_byte = @length - 1
      else
        @first_byte = @first_byte.to_i
      end    
      @range_length = @last_byte - @first_byte + 1
      @headers["Content-Range"] = "bytes #{@first_byte}-#{@last_byte}/#{@length}"
      @headers["Content-Length"] = @range_length.to_s
      [@status, @headers, self]
    else     
      [@status, @headers, @response]
    end  
  end

  def each(&block)
    block.call(@data[@first_byte..@last_byte])
    @response.close if @response.respond_to?(:close)
  end   

end

For further reference, check out rails media file stream accept byte range request through send_data or send_file method or this Ruby Forum post.

Community
  • 1
  • 1
devanand
  • 5,116
  • 2
  • 20
  • 19
  • Thank you. I adapted the gist and it worked well. As mentioned, it's probably not a good production setting, and I would use something like `X-Sendfile` in that case. However, it does allow it to work in Safari during development, which is what I needed! – Shepmaster Feb 13 '16 at 19:40
1

I know this question is over 5 years old, but I ran into the same problem and solved it, so I'll post my solution here.

When you put video files into the asset folders, they get served in a special way. Then, byte-range requests are not served. This means Safari does not play the video as it requires byte-range requests to enable jumping around in the video and playing at different speeds.

If you put the videos into the public folder and serve them from there, it is the middleware that serves them, and in this case range requests are served. (Rails 5)

So all that should be necessary is move your videos to public. At least that fixed it for me...

MDickten
  • 105
  • 1
  • 10
0

Reference link: https://superuser.com/questions/870133/html5-video-tag-not-supported-in-safari-5-1-on-windows-7

As video_tag in rails use HTML5 'video' tag property internally and safari doesn't support HTML5 video tag property.

In order to get HTML5 videos to play through Safari on Windows, you need to install Quicktime.

I did this and the videos would still not work.

But, once I restarted my machine, everything worked as it was supposed to!

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Pragya Sriharsh
  • 529
  • 3
  • 6