5
1

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 1 year has passed since last update.

モイAdvent Calendar 2023

Day 3

Python で CORS 対応の HLS サーバをサクッと書く

Last updated at Posted at 2023-12-02

概要

  • ffmpeg でマニフェストファイル、セグメントファイルを生成する
  • Python で CORS 対応の HLS サーバをサクッと書く
  • hls.js の demo ページで動画を再生する

はじめに

HLS (HTTP Live Streaming) は、動画配信を行う上で広く使われている技術です。
モイが運営するツイキャスでも利用されています。

開発を進める上ではデバッグやモックなど、サービスと切り離してHLS配信を利用したくなることがあります。
そのため、Python でミニマムなHLSサーバを実装して動作させてみます。

作業ディレクトリを作成します。

$ mkdir qiita-hls; cd qiita-hls

ffmpeg でマニフェストファイル、セグメントファイルを生成する

動画素材から ffmpeg を利用して、HLS 配信に必要なマニフェストファイル、セグメントファイルを生成します。

配置ディレクトリを作成します。

$ mkdir resource; cd resource 

動画素材には Big Buck Bunny を利用させていただきます。

Big Buck Bunny is licensed under the Creative Commons Attribution 3.0 license.
(c) copyright 2008, Blender Foundation / www.bigbuckbunny.org

以下の ffmpeg を実行します。

$ ffmpeg -i ~/Downloads/big_buck_bunny_720p_h264.mov \
-c:v copy -c:a copy \
-f hls \
-hls_time 9 \
-hls_playlist_type vod \
-hls_segment_type fmp4 \
-hls_fmp4_init_filename "init.mp4" \
-hls_segment_filename "seg.%3d.mp4" \
media.m3u8

-f hls を指定して HLS 形式の出力を行います。また -hls_segment_type fmp4 を指定して FMP4 形式でセグメントファイルを生成します。 この -hls_segment_type fmp4 がないと初期化セグメントが生成されないので注意です。

HLS サーバ

resource からでて qiita-hls ディレクトリに戻ります。

$ cd ..

以下を srv.py として保存します。

srv.py
#!/usr/bin/env python3

import os
from http.server import HTTPServer, SimpleHTTPRequestHandler


class MyRequestHandler(SimpleHTTPRequestHandler):
    def send_acao(self):
        origin = self.headers["Origin"]
        self.send_header("Access-Control-Allow-Origin", origin)
        self.send_header("Access-Control-Allow-Headers", "*")
        self.send_header("Access-Control-Allow-Methods", "*")
        self.send_header("Access-Control-Allow-Credentials", "true")

    def send_content_type(self):
        content_type = ""
        if self.path.endswith(".m3u8"):
            content_type = "application/vnd.apple.mpegurl"
        elif self.path.endswith(".mp4"):
            content_type = "video/mp4"

        if content_type != "":
            self.send_header("Content-type", content_type)

    def do_GET(self):
        data, ok = read_resource(os.path.basename(self.path))
        if not ok:
            self.send_response(404)
            self.end_headers()
            return

        self.send_response(200)
        self.send_acao()
        self.send_content_type()
        self.end_headers()
        self.wfile.write(data)
        return

    def do_OPTIONS(self):
        self.send_response(200)
        self.send_acao()
        self.end_headers()
        return


def read_resource(basename):
    filepath = "resource/" + basename
    if not os.path.exists(filepath):
        return b"", False

    with open(filepath, "rb") as f:
        return f.read(), True


httpd = HTTPServer(("0.0.0.0", 8003), MyRequestHandler)
httpd.serve_forever()

CORS でのリクエストを受け入れるよう、リクエストのはじめに CORS を許可するヘッダー群を返しています。
デバッグやモックなど、開発補助を目的にしているためきちんとした検証は行っていません。

ディレクトリレイアウトはこんな感じになってるはずです。

qiita-hls
├── resource
│   ├── init.mp4
│   ├── media.m3u8
│   ├── seg.000.mp4
│   ├── seg.001.mp4
│   ├── seg.002.mp4
... 省略 ...
└── srv.py

HLSサーバを起動します。

$ python3 srv.py

hls.js の demo ページで動画を再生する

以下のリンクを開き、 http://localhost:8003/media.m3u8 を入力してエンターを押して再生が開始されればOK :tada:

5
1
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
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?