LoginSignup
1
0

More than 3 years have passed since last update.

SinatraでHTTP Live Streaming

Posted at

動画をダイレクトにサーバから視聴する方法は他にもストリーミング系の仕組みがあるらしいのですが、Sinatraがストリーミングにうまく対応できてないような感じなので、ガチガチの配信ではなく「なんちゃってストリーミング動画配信」をする為のメモです。Appleが考案していてiOSとも親和性が高い技術なので採用しました。

環境

  • Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-99-generic x86_64)
  • Sinatra 2.0.5 (bundlerで起動する前提です)
  • ffmpeg N-97994-g83fa39e

UbuntuとSinatraは既に環境に配置されているとします。
また、インストール作業用フォルダとffmpegのバイナリを格納するフォルダをhomeに作成します。

  • ffmpeg_sources
  • ffmpeg_build
  • bin

ffmpegのインストール

ffmpegをaptでインストールするとlibfdk-aacが含まれていませんので、自分でカスタマイズインストールしなければなりません。libfdk-aacはHTTP Live Streamingを行うために必要な動画を細切れに分割してプレイリストファイルを作るのに必要です。

ffmpegで利用するコーデック群のライブラリをインストールする為に事前に必要なツール群のインストール

console
sudo apt-get update -qq && sudo apt-get -y install \
  autoconf \
  build-essential \
  cmake \
  git-core \
  libass-dev \
  libfreetype6-dev \
  libgnutls28-dev \
  libsdl2-dev \
  libtool \
  libva-dev \
  libvdpau-dev \
  libvorbis-dev \
  libxcb1-dev \
  libxcb-shm0-dev \
  libxcb-xfixes0-dev \
  pkg-config \
  texinfo \
  wget \
  yasm \
  zlib1g-dev

ffmpegで利用するコーデック群のライブラリのインストール

console
sudo apt-get install nasm
sudo apt-get install libx264-dev
sudo apt-get install libx265-dev libnuma-dev
sudo apt-get install libvpx-dev
sudo apt-get install libfdk-aac-dev
sudo apt-get install libmp3lame-dev
sudo apt-get install libopus-dev

ffmpegのビルド

console
cd ~/ffmpeg_sources && \
  wget -O ffmpeg-snapshot.tar.bz2 https://ffmpeg.org/releases/ffmpeg-snapshot.tar.bz2 && \
  tar xjvf ffmpeg-snapshot.tar.bz2 && \
  cd ffmpeg && \
  env PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \
    --prefix="$HOME/ffmpeg_build" \
    --pkg-config-flags="--static" \
    --extra-cflags="-I$HOME/ffmpeg_build/include" \
    --extra-ldflags="-L$HOME/ffmpeg_build/lib" \
    --extra-libs="-lpthread -lm" \
    --bindir="$HOME/bin" \
    --enable-gpl \
    --enable-libass \
    --enable-libfdk-aac \
    --enable-libfreetype \
    --enable-libmp3lame \
    --enable-libopus \
    --enable-libvorbis \
    --enable-libvpx \
    --enable-libx264 \
    --enable-libx265 \
    --enable-nonfree && \
  env PATH="$HOME/bin:$PATH" make && \
  make install

※gnutlsとlibaomはなんかエラーが出たので(今回は必要ないので)除外

テスト

今回は人気アニメ「ミンクの刃」をストリーミング配信で観てみましょう。

mp4形式に変換

console
sudo ffmpeg -i 'ミンクの刃 01.mkv' -vcodec copy 'ミンクの刃 01.mp4'

mp4形式からストリーミング配信用のHLS形式へ変換

console
sudo /home/ユーザ名/bin/ffmpeg -i 'ミンクの刃 01.mp4' -codec copy -vbsf h264_mp4toannexb -map 0 -f segment -segment_format mpegts -segment_time 5 -segment_list 'ミンクの刃 01'/playlist.m3u8 'ミンクの刃 01'/a%03d.ts

配信用サイトの作成

test.rb
require 'sinatra'
require 'uri'
set :environment, :production

get '/' do
  erb(:test);
end

get '/:file_name' do |file_name|
  sendFilePath = "disk/live/minknoyaiba/#{file_name}";
  if file_name.match(/.+m3u8$/) then
    send_file(sendFilePath, :filename => file_name, :type => "application/vnd.apple.mpegurl");
  else
    send_file(sendFilePath, :filename => file_name, :type => "video/mp2t");
  end
end

/source/onlineフォルダ内にtest.rbがあり、同フォルダ内にdiskフォルダが存在する環境です。minknoyaibaフォルダ内にはHLS用のプレイリスト(.m3u8)や分割動画ファイル(.ts)が入っていると思ってください。
先ほど「ミンクの刃 01」フォルダにHLSのファイル一式を変換したのに何故「minknoyaiba」フォルダに入れるのか?それはSinatraのsend_fileが日本語フォルダをうまく認識してくれないからです。
また外部から接続する為にset :environment, :productionしています。

test.erb
<!DOCTYPE html>
<html>
  <head>
    <title>HTTP Live Streaming Example</title>
  </head>
  <body>
    <video src="/playlist.m3u8" width="400" height="300" controls></video>
  </body>
</html>

配信用サイトの起動

console
bundle exec ruby -I /source/class -C/source/online test.rb

この時点で以下にアクセスするとサイトで動画が見れます。
http://テスト用サーバのIP:4567/
次にiOS側で動画を観てみましょう。

iOSアプリの作成

MinkTubeBase.h
// global require
#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>

// local require

@interface MagiTubeBase : AVPlayerViewController {

}

AVKitとAVFoundationをプロジェクトに追加しておいてください。

MinkTubeBase.m
@implementation MagiTubeBase

-(void)viewDidLoad {
    [super viewDidLoad];

    __strong NSURL *url = [NSURL URLWithString:@"http://テスト用サーバのIP:4567/playlist.m3u8"];
    __strong AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
    __strong AVPlayer *player = [AVPlayer playerWithPlayerItem:item];
    self.player = player;    
}

@end

おしまい

ミンクの刃面白いですよね!
最後に溶鉱炉の中に沈むミンクが最高にカッコイイ!🥺

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0