12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

会員制ライブ動画配信サイトの骨組をAWSで作ろう

Last updated at Posted at 2020-07-04

20200708: サンプルHLS動画をAppleさんのものに差し替えました。

こんにちは😃
今日は、タイトルのことを↓のような方法で実現してみます。


  • S3にHLS動画を置く。パブリックアクセスは許可しない。
  • CloudFront経由でのみ、その動画を再生できるようにする。
    ただし再生にはCloudFrontの暗号鍵で署名したCookieが必要となるよう設定する。
  • CloudFrontにドメインを割り当てる。
  • AWS SDKのCloudFrontクライアントが使えて署名Cookieを操作できる言語(本記事ではPHP)で、動画配信WEBアプリケーションを作る。
  • WEBサーバーに、CloudFrontとセカンドレベルドメインまで同じドメインを割り当てる。
    またCloudFrontの暗号鍵を持たせる。(これでCloudFrontとWEBサーバーで署名Cookieを共有できる)
  • あとは署名Cookie発行をWEBアプリケーションの業務ロジックでコントロールすれば、会員制動画配信が実現可能!(これ以降は本記事では割愛します。)

ついでにCORS対応を施して、
Safari以外のブラウザ(本記事ではChrome)でHLS動画再生も実現します。


正味作業時間はそれほどかかりませんでしたが、

  • つまづきポイントが多い
  • 回避不能な待ち時間が発生する

関係でブヒブヒ言うハメになったので、スクリーンショット付きで詳しく説明していきます。
結構長いですが、お付き合いくださいませ...。

おことわり

本記事中のAWSリソースなどは執筆のために一時的に作成したものです。
流用はできませんので、あしからずご了承くださいませ。

必要なもの

  • AWSアカウント。
    ドメイン取得などを実施するので若干料金が発生します。2,000円くらいあれば十分。
    またドメイン名を何にするか決めておくとスムーズです。
    (本記事の例ではyagrush.netにしています。)

  • シェルが使えてHLS動画が再生できる端末(Macおすすめ。)

  • Docker & docker-compose(Docker Desktopならdocker-composeも付いてくるので、おすすめ。)

  • Chrome

HLS(HTTP Live Streaming)とは

Appleさんが作った動画配信技術。
AbemaTVとかでも使われてるそうです。
本記事ではこのHLSを使っていきます。

昨今の事情に合ったメリットがいっぱいあります。

  • HTTPと相性が良く、一般的なWEBサーバーで動画配信できる。
  • 再生品質をリアルタイム変更できるので安定して配信しやすい。
  • 暗号や認証対応が充実。
    etc...

Safariが一番相性良いですが、他ブラウザでも再生方法はあります。
(後のほうで説明します。)

HLSの動画ファイル形式

.ts.m3u8という拡張子の2種類のファイルで構成されます。
(.mp4 から変換してくれるソフトやオンラインサービスがいっぱいあります。)

.tsファイル

動画データファイル。
1本の動画を数秒単位で細切れにしたもの。
HLS動画はこの .ts ファイル何百何千個で構成されており、しかもそれが再生品質パターン分あったりする。

.m3u8ファイル

HLS動画ファイルのメイン。
山ほどある .ts のファイルパスや再生する順番が記録されている。

ちなみに他の.m3u8ファイルを入れ子にもできる。
例えば、低品質用 .m3u8 にサッと挿げ替えれば、動画再生を止めずに再生品質を下げれたり。

本物のライブ配信システムは .ts をリアルタイムでガシガシ作成していき .m3u8 をガンガン書き換えることで実現しているそうです。

作業開始

.m3u8ファイルと.tsファイルを用意する

いきなり言われても大変ですよね。

そこで、Appleさんが公開しているサンプルHLS動画ファイルをお借りします。

Deploying a Basic HTTP Live Stream

全編だと.tsファイルが100個以上あります。
迷惑をかけるので、無理なダウンロードはやめましょう!

今回は、0~24までを使わせて頂きましょう。

.m3u8

http://devimages.apple.com/iphone/samples/bipbop/bipbopall.m3u8
http://devimages.apple.com/iphone/samples/bipbop/gear1/plog_index.m3u8
http://devimages.apple.com/iphone/samples/bipbop/gear2/plog_index.m3u8
http://devimages.apple.com/iphone/samples/bipbop/gear3/plog_index.m3u8
http://devimages.apple.com/iphone/samples/bipbop/gear4/plog_index.m3u8

.ts(/gear1~/gear4 各サブディレクトリごとに25個ずつ)

http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence0.ts
http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence1.ts

http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence23.ts
http://devimages.apple.com/iphone/samples/bipbop/gear1/fileSequence24.ts

.tsファイルを一発ダウンロードするシェルの例.悪用しないでね!
URLBASE=http://devimages.apple.com/iphone/samples/bipbop

SUBDIR=$1
mkdir -p $SUBDIR
cd $SUBDIR
wget $URLBASE/$SUBDIR/plog_index.m3u8
for i in `seq 0 24`
do
  fileno=`echo $i`
  wget $URLBASE/$SUBDIR/fileSequence$fileno.ts
  if [ $? -gt 0 ]; then
    exit 99
  fi
done

cd ..

S3作業

S3は動画ファイル置き場です。
CloudFront経由かつ特定ドメインからの再生 のみアクセスできるよう、設定を施します。

まずはバケットを作る

スクリーンショット 2020-07-03 16.24.49.png スクリーンショット 2020-07-03 16.26.46.png スクリーンショット 2020-07-03 16.28.12.png

CloudFront経由でアクセスするので、パブリックアクセス許可は不要です。
スクリーンショット 2020-07-04 0.44.10.png

HLS動画ファイルをS3にアップロード

サンプル動画の.m3u8に合わせたディレクトリ構造でアップロードしてください。

S3バケット/
  bipbopall.m3u8
  gear1/
     plog_index.m3u8
     fileSequence0.ts
      ...
     fileSequence24.ts
  gear2/
     plog_index.m3u8
     fileSequence0.ts
      ...
     fileSequence24.ts
  gear3/
     plog_index.m3u8
     fileSequence0.ts
      ...
     fileSequence24.ts
  gear4/
     plog_index.m3u8
     fileSequence0.ts
      ...
     fileSequence24.ts

CloudFront作業

ここで一旦、CloudFrontの作業に移ります。

CloudFrontは、S3の門番役です。
特定アクセスのみ動画データを転送するよう、色々設定します。

CloudFrontディストリビューション作成

スクリーンショット 2020-07-03 17.03.00.png スクリーンショット 2020-07-03 17.03.22.png

以下の設定だけを変更して、

設定項目名
Origin Domain Name クリックするとドロップリストが出るので、先ほど作成したS3を選んでください。
Origin ID ドロップリストを選択すれば自動的にセットされると思います。
Ristrict Buchet Access YESを選んでください。
Whitelist Headers 左のリストからOriginを選択して【Add >>】ボタンを押し、右のリストにOriginが移動するようにしてください。
Ristrict Viewer Access YESを選んでください。

それ以外は触らないでOKです。
最後に【Create Distribution】ボタンを押してください。

スクリーンショット 2020-07-03 17.04.39.png スクリーンショット 2020-07-03 17.06.45.png スクリーンショット 2020-07-03 17.08.00.png スクリーンショット 2020-07-03 17.08.19.png

CloudFrontディストリビューションが作成されます。
ちなみにID(本例ではERDBXHEEWQCAE)をクリックすると詳細画面に遷移できます。

スクリーンショット 2020-07-03 17.08.57.png

S3がCloudFrontからのアクセスを許可するのに必要な情報を調べる

Origin Access Identityという値を調べます。
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html

これはCloudFrontがS3にアクセスするための特別なユーザーで、ディストリビューション作成したときに自動的に作成されているはずです。

これのIDを控えておいてください。
欄がせまくて途中で見切れていますが、ダブルクリックで全選択→クリップボードなどで確実に控えましょう。

スクリーンショット 2020-07-03 17.21.37.png

再びS3作業

バケットポリシーを設定する

S3のバケットポリシー設定画面を開きます。

スクリーンショット 2020-07-03 17.36.25.png

テキストボックスに↓のJSONコードをコピペしてください。
ただし2箇所、あなたの環境に合わせて変更する必要があります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Grant a CloudFront Origin Identity access",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E1DCCPJY6X9NYO"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::yagrush-hls/*"
        }
    ]
}
  • "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity のあとに、さきほどコピーしたCFディストリビューションのOrigin Access IdentityのIDを貼り付けてください。
  • "Resource": "arn:aws:s3::: のあとに、あなたが作成したS3のバケット名/*を貼り付けてください。

最後に【保存】ボタンを押してください。
エラーが表示されなければOK。

CORS構成を設定する

CORSとは

異なるサーバー間でリソース(動画とか)を共有できるようにする仕組みです。
これをちゃんと設定しないと、Safari以外のブラウザではS3に置かれたHLS動画を再生できません。
ある意味クロスサイトスクリプティングみたいなことになってしまい、エラーになります。

ちなみにSafariだけは(HLSを開発したAppleさんだけあって)ダイレクトにプラグインが起動してあっけなく再生できてしまいます。

設定する

S3バケットの【アクセス権限】タブから【CORSの設定】画面を開きます。

image.png

↓のXMLをコピペして下さい。
ただ1ヶ所、変更して頂く必要があります。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>http://web.yagrush.net:8000</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
  • <AllowedOrigin>の中のhttp://web.yagrush.net:8000は、みなさんの環境に合わせて書き換えてください。(トップレベルとセカンドレベルドメイン(本例ではyagrush.net)の部分は、予め決めたものから変えないで下さいね。)

またこの値は、後ほど構築するWEBサーバーのURLに使用するので控えておいてください。

最後に【保存】ボタンを押して下さい。
エラーにならなければOK。

※ XMLのタグを小文字にしたり、<AllowedOrigin>の値の末尾に/が付いてたりするだけですぐエラーになります。かなりセンシティブなので、コピペや編集の際はご注意を。

Route53作業

ドメインを取得する

ちょっと料金がかかります。

また、ここ、ウェイトポイントです!
購入したドメインが有効になるまで数時間かかる場合があります。

では、AWSのRoute53にアクセスしましょう。
そして、ドメインを作成する画面まで進んでください。

スクリーンショット 2020-07-03 17.42.28.png スクリーンショット 2020-07-03 17.43.13.png スクリーンショット 2020-07-03 17.43.36.png

指定したドメインが世界でまだ使われていなければOK。
カートに入れて購入します。

スクリーンショット 2020-07-03 17.44.24.png

ドメイン購入は結構ウルサイです。
ちゃんと個人情報を入力しないといけません。
(※ 一度買ったことがあれば、この手順は発生しないと思います。)

スクリーンショット 2020-07-03 17.51.00.png

その後も「規約に同意しろ」とか色々質問が出ますが、ここでは説明は省略します。
無事購入すると、世界中に浸透させる処理が開始されます。

これがまたリアル数時間かかります。
完了するとAWSからメールが来ると思うので、それまで休憩するのも良いでしょう。

スクリーンショット 2020-07-03 17.53.42.png

ドメインが有効になったら続きをやりましょう。

CloudFrontに振るサブドメインを決める

本例ではcf.を付けてcf.yagrush.netにしています。

ドメインとCloudFrontを結びつける

Route53でCNAMEという設定を追加すると、そういうことができます。
別名を登録する感じです。

その前にまずCloudFrontの画面で、CloudFrontディストリビューションのデフォルトアドレスを調べておきましょう。
↓このへんに表示されているやつですね。

image.png

本例ではd14br4vlcj23gd.cloudfront.netです。
※ またもや欄がせまくて途中で見切れているので、クリップボードコピーするときはご注意ください。

Route53にCNAMEレコードを追加する

まずはRoute53の画面を辿って「レコードセットの作成」まで進んでください。

image.png

image.png

image.png

「レコードセットの作成」ボタンを押すと、画面右側に設定小窓が出るので、先ほど調べておいた情報を設定して【作成】ボタンを押します。

image.png

これでOK。

image.png

再びCloudFront作業へ

CloudFront用サーバー証明書を取得する

CloudFrontディストリビューションの詳細設定画面を開いてください。

image.png

image.png

中段あたりにある【Request or Import a Certificate with ACM】ボタンを押して下さい。
image.png

「証明書のリクエスト」画面が開きます。

image.png
まずこのとき、画面右上のリージョン表示が「東京」などではなく「バージニア北部」になっていることを確認してください。(CloudFrontのエンドポイントはバージニア北部にあるため、バージニア北部で取得した証明書じゃないと使えないそうです。罠ポイント。)

それでは、先ほどのCloudFront用サブドメインを入力して【次へ】を押して下さい。
image.png

「DNSの検証」を選択して【次へ】。
image.png

タグは、必要でしたらお好みで設定してください。
【確認】ボタンを押します。
image.png

内容に誤りがなければ【確定とリクエスト】を押して下さい。
image.png

なんだか不安にさせられる画面になりました...
でも大丈夫、【続行】を押して下さい。
image.png

こんな画面に遷移しました。
これまた不穏な...

もちろんこのままではダメです。
画面中段あたり、CloudFront用ドメインの左にある ▼ を押して下さい。

image.png

小窓が展開されます。
その中にあるボタン【Route53でのレコードの作成】を押して下さい。
image.png

【作成】を押して下さい。
image.png

これで処理が進みます。
image.png

はい、来ました!ここもウェイトポイントです!
証明書が使えるようになるまで数時間かかる場合があります...。
(わりとすぐの場合もあります。)

完了すると↓に変わるはずです。
時間をおいてチェックしてみてください。
image.png

OKになったら作業の続きを。

CloudFrontに証明書をセットする

CloudFrontの詳細画面をまた開いてください。

中段あたりのSSL Certificateで、2番目の選択肢Custom SSL Certificate(example.com)がアクティブになり選択できるようになっているはずです。
それを選択しましょう。

※ たまに、証明書OKなのに選択肢Custom SSL Certificate(example.com)がアクティブになるまで時間がかかる場合があるようです...。

そしてその下にあるボックスをクリックするとドロップリストが開き、先ほどの証明書が選択できます。

更にそれらより上の方、Alternate Domain NamesにCloudFront用サブドメインを入力します。

全部終わったら、一番右下の【Yes, Edit】ボタンを押しましょう。
問題なければ、前の画面にスッと戻るはずです。
(もしエラーだと、赤字で下の方に表示されます。)

image.png

CloudFrontキーペアを作成する

今作業をしているAWSアカウントがルートアカウントでない場合、ログアウトしてルートアカウントでAWSコンソールにログインしなおしてください。(ルートアカウントじゃないとできない作業なのです。)

ルートアカウントで画面右上のユーザーメニューから「マイセキュリティ資格情報」を開きます。

image.png

ルートアカウントのみ【CloudFrontのキーペア】というタブが出現します。
それを開くと【新しいキーペアの作成】ボタンがあるので押して下さい。
image.png

image.png

こうなればOK。

  • 【プライベートキーファイルのダウンロード】
  • 【パブリックキーファイルのダウンロード】

は、ダウンロードしておいてください。
後で使います。

後はもうルートアカウントじゃなくてOK。

WEBアプリケーションを作る

以上でAWS作業からは離れます。

次にDockerとPHPを使ってWEBアプリケーションを作りましょう。
動画配信サイトにあたる部分です。

本記事ではローカルに構築して、hostsファイルを書き換えて対応します。
本格的にリモートに構築すると面倒なので...。

hostsを編集する

WEBサーバーのURL(本例ではweb.yagrush.net)をローカル(127.0.0.1)に向けます。

sudo vim /etc/hosts
/etc/hosts.
127.0.0.1	localhost web.yagrush.net

これでweb.yagrush.netのURLでアクセスするかぎり、HLS動画を再生できます。
CloudFrontをだます ...って言い方もアレですけどね。

さすがに記事が辛くなってきた:sob:
あと少し...!

WEBアプリケーション一式セットをダウンロードする

今回、動作確認済のWEBアプリケーション一式セットを予めご用意しました。
https://github.com/yagrush/hls-web

git cloneでもいいですが、ZIPダウンロード&解凍でもOK。
wgetコマンドが使える方は↓の一発コマンドをどうぞ。

wget https://github.com/yagrush/hls-web/archive/master.zip -O hls-web.zip; unzip hls-web.zip; rm -f hls-web.zip

# ついでにディレクトリを移動しておく
cd hls-web-master

CloudFrontのプライベートキーファイルを配備する

前の方の手順でCloudFrontキーペアを作成してプライベートキーファイルをダウンロードしたと思います。
(こんな名前のファイル pk-XXXXXXXXXXXXXXXXXXX.pem
それを hls-web-master/backend/storage/app に置いてください。

Laravelのstorageディレクトリのアクセス権限を設定する

以下のコマンドを打ってください。

chmod -R 777 backend/storage

設定をカスタマイズする

数か所、みなさんの環境に合わせて変更して頂く必要があります。
(一応目印として**hls**というコメントを付けてあります。)

hls-web-master/backend/resources/views/play_video.blade.php, hls-web-master/backend/app/Http/Controllers/PlayVideoController.php

cf.yagrush.netyagrush.netの部分を、みなさんのケースに合わせて書き換えて下さい。

hls-web-master/backend/app/Http/Controllers/PlayVideoController.php

みなさんのCloudFrontキーペアのプライベートキーファイル名に合わせてXXXXXXXXXXXXXXXXXXXの部分を書き換えて下さい。
ちなみにキーペアIDも、プライベートキーファイル名のXXXXXXXXXXXXXXXXXXXと同じです。

hls-web-master/backend/resources/views/play_video.blade.php
...
...
    var videoSrc = 'https://cf.yagrush.net/bipbopall.m3u8'; //**hls**
...
...
hls-web-master/backend/app/Http/Controllers/PlayVideoController.php
...
...
        $resourceKey = "https://cf.yagrush.net/*"; //**hls**
...
...
            'private_key' => storage_path() . '/app/pk-XXXXXXXXXXXXXXXXXXX.pem', //**hls**
            'key_pair_id' => 'XXXXXXXXXXXXXXXXXXX' //**hls**
...
...
        $domain = 'yagrush.net';  //**hls**
...
...

起動

hls-web-master/で以下のコマンドを打ってください。(数分かかるかも。)

make init

問題なければ、

  • ポート8000でnginxコンテナがWEBサーバーのフロントとして起動します。
  • PHPを処理するためのバックエンドとしてphp-fpmコンテナが起動します。
  • Laravelフレームワークのためだけにポート3306でmysqlコンテナが起動しますが、気にしないでください。

もしポート被りで起動に失敗したら、hls-web-master/docker-compose.ymlを編集してポートを変更するなどしてくださいね。

動画再生してみる

動画再生ページにChromeでアクセスしてみて下さい。
(本例だと http://web.yagrush.net:8000/play_video

再生されましたでしょうか?

今回は動画データの一部だけしか配備してないので、途中で停止します。

お時間あればSafariでもアクセスしてみて下さい。
ちなみにSafari以外での再生のために hls.js を採用しました。

動画に直アクセスできないのを確認してみる

以下のコマンドでcookieを使わずにアクセスしてみます。
(例によってyagrush.netはみなさんの環境に合わせて書き換えて下さいね。)

まずはOriginヘッダを付けて送信してみます。

curl -X GET -H 'Origin: http://web.yagrush.net:8000' -i -v -f https://cf.yagrush.net/bipbopall.m3u8

403エラー!
拒否されちゃいました。

Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to cf.yagrush.net (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/cert.pem
  CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=cf.yagrush.net
*  start date: Jul  3 00:00:00 2020 GMT
*  expire date: Aug  3 12:00:00 2021 GMT
*  subjectAltName: host "cf.yagrush.net" matched certs "cf.yagrush.net"
*  issuer: C=US; O=Amazon; OU=Server CA 1B; CN=Amazon
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0xxxxxxxxxxxxx)
> GET /bipbopall.m3u8 HTTP/2
> Host: cf.yagrush.net
> User-Agent: curl/7.64.1
> Accept: */*
> Origin: http://web.yagrush.net:8000
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
* The requested URL returned error: 403 
* stopped the pause stream!
* Connection #0 to host cf.yagrush.net left intact
curl: (22) The requested URL returned error: 403 
* Closing connection 0

今度はプライベートキーファイルまで付けて送信してみます。

curl -X GET -H 'Origin: http://web.yagrush.net:8000' -i -v -f -E backend/storage/app/pk-XXXXXXXXXXXXXXXXXX.pem https://cf.yagrush.net/bipbopall.m3u8

やはり怒られました。

Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to sample.flhls.net (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* could not load PEM client certificate, LibreSSL error error:09FFF06C:PEM routines:CRYPTO_internal:no start line, (no key found, wrong pass phrase, or wrong file format?)
* Closing connection 0
curl: (58) could not load PEM client certificate, LibreSSL error error:09FFF06C:PEM routines:CRYPTO_internal:no start line, (no key found, wrong pass phrase, or wrong file format?)

終了

以下のコマンドでWEBアプリケーションを終了&破棄します。

make destroy

おつかれさまでしたぁーー!!!

WEBアプリケーション変更点 まとめ

今回は、定番のこちら -> 最強のLaravel開発環境をDockerを使って構築する【新編集版】 をベースに以下の手を加えました。

PHP用AWS SDKインストールをビルド工程(Makefile @ create-project)に追加。

docker-compose exec app composer require aws/aws-sdk-php

★ CloudFrontキーペアのプライベートキーファイルをbackend/storage/appに配備。

backend/routes/web.phpにルーティング設定追記。

Route::get('/play_video', 'PlayVideoController@show');

★ ソースコード2つ作成。

  • backend/app/Http/Controllers/PlayVideoController.php
  • backend/resources/views/play_video.blade.php

CloudFront+S3+Video.js+ 署名付きCookieでクローズドな動画配信 を参考に書きました。
↑お時間あれば是非ご覧になってみて下さい。

参考文献

今回はこれらのサイトにお世話になりました。
ありがとうございます。

12
12
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
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?