Help us understand the problem. What is going on with this article?

SinatraでHTTP Live Streaming

動画をダイレクトにサーバから視聴する方法は他にもストリーミング系の仕組みがあるらしいのですが、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

おしまい

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした