Skip to content

Instantly share code, notes, and snippets.

@phuongdh
Last active October 22, 2020 17:13
Show Gist options
  • Save phuongdh/a1e48c8f2beae16ff594adc419571a08 to your computer and use it in GitHub Desktop.
Save phuongdh/a1e48c8f2beae16ff594adc419571a08 to your computer and use it in GitHub Desktop.

Intro

  • Workshop is only about the streaming part, not the transcoding part. We assume the transcoding has already been done. If interested look at active-encode or connect2018-workshop
  • Clone repo git clone https://github.com/avalonmediasystem/connect2020-workshop.git
  • Build Docker image docker-compose build
  • Show docker-compose.yml, this is a standard Rails setup, with DB
  • Explain what has been scaffolded: Item, Devise for login
  • Start the stack docker-compose up
  • Show Items view, login

Add streaming service: docker-compose.yml

  streaming:
    build: ./nginx
    volumes:
      - ./streams:/data
    ports:
      - "3333:80"
  • Explain mount volume, streaming port
  • Explain nginx build.sh, vod module, config
  • Build service: docker-compose build streaming
  • Bring it up: docker-compose up -d streaming

Add player: app/views/items/show.html.erb

Add HTML

<p>
  <video
    id="my-video"
    class="video-js"
    width="640"
    height="480"
    controls
    preload="auto"
  >
    <% @item.streams.each do |stream| %>
      <source src="<%= stream["url"] %>" type="application/x-mpegURL" label="<%= stream["label"] %>"/>
    <% end %>
  </video>
</p>

Explain multiple sources in 1 video

Add videojs CDN resources

<link href="https://vjs.zencdn.net/7.8.4/video-js.css" rel="stylesheet" />
<link href="https://unpkg.com/@silvermine/videojs-quality-selector/dist/css/quality-selector.css" rel="stylesheet">
<script src="https://vjs.zencdn.net/7.8.4/video.js"></script>
<script src="https://unpkg.com/@silvermine/videojs-quality-selector/dist/js/silvermine-videojs-quality-selector.min.js"></script>

Intialize player with plugin

<script>
  var options, player;

  options = {
    controlBar: {
        children: [
          'playToggle',
          'progressControl',
          'volumePanel',
          'qualitySelector',
          'fullscreenToggle',
        ],
    },
  };

  player = videojs('my-video', options);
</script> 
  • Check player working
  • Show quality selector

Security

  • Item is protected but the stream is not. Someone can guess the URL and stream it.

Add authorization

How token-based auth works

Stream auth

Enable auth in nginx/nginx.conf.template

  • Uncomment auth_request /auth;
  • Explain how nginx get token and call auth
  • Rebuild docker-compose build streaming
  • Recreate docker-compose up -d streaming
  • Shift-reload verify streaming not working
  • Look at web log to see the authorize call

Add token items_controller.rb

  • in show action
    session[:token] ||= SecureRandom.hex(16)
    Rails.cache.write(session[:token], 
                      @item.streams.collect { |s| s["url"] }, 
                      expires_in: 1.hours)
  • in show view
      <source src="<%= stream["url"] %>?token=<%= session[:token] %>" type="application/x-mpegURL" label="<%= stream["label"] %>"/>
  • Enable cache store in config/environments/development.rb

config.cache_store = :memory_store

  • Add authorize action in items_controller.rb
  def authorize
    authorized_streams = Rails.cache.read(params[:token])

    if params[:name] and not authorized_streams.any? { |valid| valid.index(params[:name]).present? }
      return head :forbidden
    else
      return head :ok
    end
  end

This code looks at authorized streams associated with this token and see if the requested stream matches any of them

Make it publicly available so streaming service can reach it

before_action :require_login, except: [:index, :authorize]
  • Add route
  resources :items do
    collection do
      get :authorize
    end
  end
  • Check auth call returns 200
  • Check auth streaming working

Add adaptive streaming

  • Show example of adaptive HLS manifest

  • Add m3u8 view template adaptive.erb

#EXTM3U
<% @streams.each do |stream| %>
#EXT-X-STREAM-INF:BANDWIDTH=<%= stream["bitrate"] %>
<%= stream["url"] + "?token=" + session[:token]%>
<% end %> 
  • Add route items_controller.rb
    member do
      get :adaptive
    end
  • Add action
  # GET /items/1/adaptive.m3u8
  def adaptive
    @streams = @item.streams
    render layout: false, content_type: 'text/plain'
  end
  before_action :set_item, only: [:show, :edit, :update, :destroy, :adaptive]

Explain format, how player will choose which stream based on bandwidth

  • Add source to show.html.erb
    <source src="<%= adaptive_item_path(@item, format: "m3u8") %>" type="application/x-mpegURL" label="auto"/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment