Commit 886dbcb5 authored by Mickaël Desfrênes's avatar Mickaël Desfrênes
Browse files

add hls support in backend

parent eca8d956
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
__version__ = "0.1.65"
__version__ = "0.1.66"
+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ for tool in [
    "pdftoppm",
    "pdftotext",
    "tesseract",
    "ffmpeg",
]:
    if not Path(which(tool) or "/does/not/exist").exists():
        sys.exit(f"Couldn't find tool {tool}, please read installation instructions.")
@@ -76,6 +77,9 @@ IIIF_DIR = os.getenv("JAMA_IIIF_DIR") or str(Path(VAR_DIR, "iiif").resolve())
os.makedirs(IIIF_DIR, exist_ok=True)
IIIF_PATH_SEPARATOR = os.getenv("JAMA_IIIF_PATH_SEPARATOR") or os.path.sep

HLS_DIR = os.getenv("JAMA_HLS_DIR") or str(Path(VAR_DIR, "hls").resolve())
os.makedirs(HLS_DIR, exist_ok=True)


ARK_SERVER = os.getenv("JAMA_ARK_SERVER", "")
ARK_APP_ID = os.getenv("JAMA_ARK_APP_ID", "")
@@ -99,6 +103,8 @@ JAMA_IIIF_UPSTREAM_URL = os.getenv("JAMA_IIIF_UPSTREAM_URL") or JAMA_IIIF_ENDPOI
ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = [JAMA_SITE[:-1]]

JAMA_HLS_ENDPOINT = os.getenv("JAMA_HLS_ENDPOINT") or "http://localhost/hls/"

# Application definition

INSTALLED_APPS = [
+124 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ from unidecode import unidecode
import logging
from typing import Union
from pathlib import Path
import subprocess

logger = logging.getLogger(__name__)

@@ -160,6 +161,129 @@ def iiif_destination_dir_from_hash(hash: str) -> str:
    )


def hls_destination_dir_from_hash(hash: str) -> str:
    return "{}{}{}{}{}{}{}{}".format(
        settings.HLS_DIR,
        os.path.sep,
        hash[:2],
        os.path.sep,
        hash[2:4],
        os.path.sep,
        hash,
        os.path.sep,
    )


def make_hls(f: Union[models.File, int], force: bool = False):
    """
    Convert a video to HLS format

    Will silently return if not a video format
    or if conversion already exists, except if force is True.
    """

    if type(f) is int:
        f = models.File.objects.get(pk=f)
    if f.should_have_hls:
        hls_destination_dir = hls_destination_dir_from_hash(f.hash)
        os.makedirs(hls_destination_dir, exist_ok=True)
        ffmpeg_cmd = [
            "ffmpeg",
            "-i",
            f.local_path(),
            "-filter_complex",
            (
                "[0:v]split=3[v1][v2][v3];"
                "[v1]scale=w=1920:h=1080[v1out];"
                "[v2]scale=w=1280:h=720[v2out];"
                "[v3]scale=w=854:h=480[v3out]"
            ),
            # Video streams
            # video 1
            "-map",
            "[v1out]",
            "-c:v:0",
            "libx264",
            "-b:v:0",
            "5000k",
            "-maxrate:v:0",
            "5350k",
            "-bufsize:v:0",
            "7500k",
            # video 2
            "-map",
            "[v2out]",
            "-c:v:1",
            "libx264",
            "-b:v:1",
            "2800k",
            "-maxrate:v:1",
            "2996k",
            "-bufsize:v:1",
            "4200k",
            # video 3
            "-map",
            "[v3out]",
            "-c:v:2",
            "libx264",
            "-b:v:2",
            "1400k",
            "-maxrate:v:2",
            "1498k",
            "-bufsize:v:2",
            "2100k",
            # Audio streams
            # audio 1
            "-map",
            "a:0",
            "-c:a:0",
            "aac",
            "-b:a:0",
            "192k",
            "-ac",
            "2",
            # audio 2
            "-map",
            "a:0",
            "-c:a:1",
            "aac",
            "-b:a:1",
            "128k",
            "-ac",
            "2",
            # audio 3
            "-map",
            "a:0",
            "-c:a:2",
            "aac",
            "-b:a:2",
            "96k",
            "-ac",
            "2",
            # HLS settings
            "-f",
            "hls",
            "-hls_time",
            "10",
            "-hls_playlist_type",
            "vod",
            "-hls_flags",
            "independent_segments",
            "-hls_segment_type",
            "mpegts",
            "-hls_segment_filename",
            f"{hls_destination_dir}stream_%v{os.path.sep}data%03d.ts",
            "-master_pl_name",
            "master.m3u8",
            "-var_stream_map",
            "v:0,a:0 v:1,a:1 v:2,a:2",
            # playlist output
            f"{hls_destination_dir}stream_%v{os.path.sep}playlist.m3u8",
        ]

        subprocess.run(ffmpeg_cmd, check=True)


def make_iiif(f: Union[models.File, int], force: bool = False):
    """
    Make a Tiled TIF ready for IIIF server.
+1 −0
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ def _add_file(
    tasks.iiif_task(resource_id)
    tasks.exif_task(resource_id)
    tasks.ocr_task(resource_id)
    tasks.hls_task(resource_id)
    collection_path = os.path.dirname(file_path)[len(start_dir) :]
    if collection_path:
        hierarchy_of_collections = cached_add_collection_from_path(
+22 −0
Original line number Diff line number Diff line
@@ -22,6 +22,20 @@ IIIF_SUPPORT = [
]


HLS_SUPPORT = [
    "video/mp4",
    "video/webm",
    "video/ogg",
    "video/x-msvideo",
    "video/mpeg",
    "video/quicktime",
    "video/x-matroska",
    "video/x-flv",
    "video/3gpp",
    "video/3gpp2",
]


def set_base_permissions():
    obj_types = [
        "collection",
@@ -63,6 +77,14 @@ def set_file_types():
        except FileType.DoesNotExist:
            pass

    for mime in HLS_SUPPORT:
        try:
            file_type = FileType.objects.get(mime=mime)
            file_type.serve_with_hls = True
            file_type.save()
        except FileType.DoesNotExist:
            pass


# deprecate ?
def set_basic_vocabularies_metas(project: Project):
Loading