Mike Smith
Mike Smith

Reputation: 21

Video streaming in rails 4.2

I am not sure if this is a programming question or a web server config question.

The video files are not in public/ so requests have to go through a controller.

In my controller:

  def play_video
   video=Video.find params[:id]
   response.headers['Content-Length'] = File.size(video.path).to_s
   send_data File.read(video.path,mode: 'rb'), type: video.mime_type, disposition: 'inline'

end

On the client-side I am using JPlayer for now.In Firefox running on Linux in both development and production it works properly, I can jump forward or backward without issue. In just about every other browser on Linux and Windows, it plays properly, but that is it, no skipping forward or backwards, in development and production. I would put it up to a JPlayer bug but I get the exact same behavior with using just <video> with control attribute, as well as mediaelement.js.

In development I am using Thin, on production Passenger and Nginx.

nginx.conf:

    worker_processes  4;
    events {
        worker_connections  1024;
    }
     sendfile        on;
     #tcp_nopush     on;
  http {
    passenger_root /usr/local/lib64/ruby/gems/2.2.0/gems/passenger-5.0.11;
    passenger_ruby /usr/local/bin/ruby;
    keepalive_timeout  120;
   gzip  on;
   server {
        server_tokens off;
        listen 80 default_server;
        listen 443 ssl;
        server_name www.example.com;
        root /path/to/public;  
        passenger_enabled on;
        client_max_body_size 100M;
        add_header Strict-Transport-Security "max-age=31536000;  includeSubdomains;";       
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
        include mime.types;
        default_type application/octet-stream;
        #removed SSL config stuff
        ...

I am not sure where the problem lies. I get no error messages when trying to skip forward.

Please don't link to Rails HTTP Streaming tutorials as they are all lame and not-at-all-helpful send "hello world" to the client 10 times examples, unless you have one that actually addresses video streaming. Anything I did find were for old versions of rails and other web servers.

Upvotes: 1

Views: 796

Answers (1)

Anatoly
Anatoly

Reputation: 15530

This is an anti pattern what you are trying to do, send_file is fully blocking synchronical mechanism, basically it requires N number of Rails processes as many requests as your app will serve at a time. It is being done by a reason to have controlled access to file or perform any kind of pre/post processing: permission check, audit etc.

If this is the anti pattern how the pattern looks like: 1) X-Accel-Redirect 2) Async permission check via auth_request module

In the first case your Rails app returns back only the file's URL within the X-Accel-Redirect header with empty body followed by closing this request so Rails can continue serving a new request again immediately not waiting until a client gets the file (in case of slow client's connection Rails can't free process up until it finished). Basically to serve 10 large video files simultaneously 10 workers require you to have. X-Accel-Redirect makes same job with only one worker:

Rails

def download
  video=Video.find params[:id]
  headers['X-Accel-Redirect'] = "#{video.file_name}"
  render nothing: true
end

Nginx

location /video {
  internal;
  root /mnt/video;
}

The alternative is auth_request which is similar by efficiency but you need to know file target URL beforehand not generate by server. Auth_request location is internal and it handles authentication by Rails route followed by rejection or pass user to target video file URL:

Nginx

location /video/ {
    root /mnt/video;
    auth_request /auth;
    ...
}

location = /auth {
    internal;
    proxy_pass http://localhost;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_set_header X-Original-URI $request_uri;
}

Upvotes: 2

Related Questions