3 | Date: Thu, 18 Jun 2020 11:54:19 +0200
4 | Subject: [PATCH 4/4] always write an stss box in CMAF mode
5 |
6 | ---
7 | libavformat/movenc.c | 4 ++++
8 | 1 file changed, 4 insertions(+)
9 |
10 | diff --git a/libavformat/movenc.c b/libavformat/movenc.c
11 | index 7cee522eac..7e9a8a8899 100644
12 | --- a/libavformat/movenc.c
13 | +++ b/libavformat/movenc.c
14 | @@ -2635,6 +2635,10 @@ static int mov_write_stbl_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext
15 | track->par->codec_tag == MKTAG('r','t','p',' ')) &&
16 | track->has_keyframes && track->has_keyframes < track->entry)
17 | mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE);
18 | + if (track->par->codec_type == AVMEDIA_TYPE_VIDEO &&
19 | + mov->flags & FF_MOV_FLAG_CMAF &&
20 | + !track->has_keyframes)
21 | + mov_write_stss_tag(pb, track, MOV_SYNC_SAMPLE);
22 | if (track->par->codec_type == AVMEDIA_TYPE_VIDEO && track->has_disposable)
23 | mov_write_sdtp_tag(pb, track);
24 | if (track->mode == MODE_MOV && track->flags & MOV_TRACK_STPS)
25 | --
26 | 2.24.1 (Apple Git-126)
27 |
28 |
--------------------------------------------------------------------------------
/ffmpeg/Dockerfile:
--------------------------------------------------------------------------------
1 | # ffmpeg - http://ffmpeg.org/download.html
2 | # based on image from
3 | # https://hub.docker.com/r/jrottenberg/ffmpeg/
4 | #
5 | #
6 | FROM alpine:3.21
7 |
8 |
9 | ENV X264_VERSION=20191217-2245-stable \
10 | X265_VERSION=x265_4.1 \
11 | PKG_CONFIG_PATH=/usr/local/lib/pkgconfig \
12 | SRC=/usr/local
13 |
14 | RUN buildDeps="autoconf \
15 | automake \
16 | bash \
17 | binutils \
18 | bzip2 \
19 | cmake \
20 | curl \
21 | coreutils \
22 | g++ \
23 | gcc \
24 | git \
25 | libtool \
26 | make \
27 | openssl-dev \
28 | tar \
29 | yasm \
30 | python3 \
31 | pkgconfig \
32 | zlib-dev" && \
33 | export MAKEFLAGS="-j$(($(grep -c ^processor /proc/cpuinfo) + 1))" && \
34 | apk add --update ${buildDeps} freetype-dev fontconfig-dev ttf-droid libgcc libstdc++ ca-certificates && \
35 | DIR=$(mktemp -d) && cd ${DIR} && \
36 | echo "**** COMPILING x264 ****" && \
37 | curl -sL https://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-${X264_VERSION}.tar.bz2 | \
38 | tar -jx --strip-components=1 && \
39 | ./configure --prefix="${SRC}" --bindir="${SRC}/bin" --enable-pic --enable-shared --disable-cli && \
40 | make -j$(nproc) && \
41 | make install && \
42 | rm -rf ${DIR} && \
43 | # x265 http://www.videolan.org/developers/x265.html
44 | DIR=$(mktemp -d) && cd ${DIR} && \
45 | echo "**** COMPILING x265 ****" && \
46 | curl -sL https://download.videolan.org/pub/videolan/x265/${X265_VERSION}.tar.gz | \
47 | tar -zx --strip-components=1 && \
48 | cd build && cmake ../source && \
49 | make -j$(nproc) && \
50 | make install && \
51 | rm -rf ${DIR}
52 |
53 | COPY *.patch /root/
54 |
55 | ## ffmpeg source from github
56 | # checkout working commit ca21cb1e36ccae2ee71d4299d477fa9284c1f551 from 12/01/2021
57 | RUN DIR=$(mktemp -d) && cd ${DIR} && \
58 | git clone https://github.com/FFmpeg/FFmpeg.git . && \
59 | git checkout --detach ca21cb1e36ccae2ee71d4299d477fa9284c1f551 && \
60 | cp /root/*.patch . && \
61 | git apply -v *.patch && \
62 | ./configure --prefix="${SRC}" \
63 | --extra-cflags="-I${SRC}/include" \
64 | --extra-ldflags="-L${SRC}/lib" \
65 | --bindir="${SRC}/bin" \
66 | --disable-doc \
67 | --disable-static \
68 | --enable-shared \
69 | --disable-ffplay \
70 | --extra-libs=-ldl \
71 | --enable-version3 \
72 | --enable-libx264 \
73 | --enable-libx265 \
74 | --enable-libfontconfig \
75 | --enable-libfreetype \
76 | --enable-gpl \
77 | --enable-avresample \
78 | --enable-postproc \
79 | --enable-nonfree \
80 | --disable-debug \
81 | --enable-openssl && \
82 | make -j$(nproc) && \
83 | make install && \
84 | make distclean && \
85 | hash -r && \
86 | rm -rf ${DIR} && \
87 | cd && \
88 | apk del ${buildDeps} && \
89 | rm -rf /var/cache/apk/* /usr/local/include && \
90 | ffmpeg -buildconf
91 |
92 | COPY entrypoint.sh /usr/local/bin/entrypoint.sh
93 | COPY entrypoint.py /usr/local/bin/entrypoint.py
94 | RUN chmod +x /usr/local/bin/entrypoint.sh
95 | RUN chmod +x /usr/local/bin/entrypoint.py
96 |
97 | ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
98 |
--------------------------------------------------------------------------------
/ffmpeg/README.md:
--------------------------------------------------------------------------------
1 | # FFmpeg in Docker
2 |
3 | This repository provides a Docker version of [FFmpeg](https://ffmpeg.org/) based on Alpine linux,
4 | to demonstrate live streaming using CMAF.
5 |
6 | By default it can be used to push mulitple streams of SMPTE colour bars with a burnt in UTC timecode audio.
7 |
8 | It is designed to stream to a [Unified Streaming](http://www.unified-streaming.com/products/unified-origin) publishing point.
9 |
10 |
11 | ## Building
12 |
13 | To build the image run:
14 |
15 | ```bash
16 | docker build -t ffmpeg .
17 | ```
18 |
19 |
20 | ## Usage
21 |
22 | The default command generates a stream with EBU colour bars, BITC, logo and
23 | audio (1kHz tone) with various bitrates and framerate.
24 |
25 | ```bash
26 | ffmpeg -re \
27 | -f lavfi \
28 | -i smptehdbars=size=${V1_ASPECT_W}x${V1_ASPECT_H}:rate=${V1_FRAME_RATE} \
29 | -i "https://raw.githubusercontent.com/unifiedstreaming/live-demo/master/ffmpeg/usp_logo_white.png" \
30 | -filter_complex \
31 | "sine=frequency=1:beep_factor=480:sample_rate=48000, \
32 | atempo=0.5[a1]; \
33 | sine=frequency=1:beep_factor=960:sample_rate=48000, \
34 | atempo=0.5, \
35 | adelay=1000[a2]; \
36 | [a1][a2]amix, \
37 | highpass=40, \
38 | adelay='$(date +%3N)', \
39 | asplit=3[a1][a2][a3]; \
40 | [a1]showwaves=mode=p2p:colors=white:size=${V1_ASPECT_W}x100:scale=lin:rate=$((${V1_FRAME_RATE}))[waves]; \
41 | color=size=${V1_ASPECT_W}x100:color=black[blackbg]; \
42 | [blackbg][waves]overlay[waves2]; \
43 | [0][waves2]overlay=y=620[v]; \
44 | [v]drawbox=y=25: x=iw/2-iw/7: c=0x00000000@1: w=iw/3.5: h=36: t=fill, \
45 | drawtext=text='DASH-IF Live Media Ingest Protocol': fontsize=32: x=(w-text_w)/2: y=75: fontsize=32: fontcolor=white,\
46 | drawtext=text='Interface 1 - CMAF': fontsize=32: x=(w-text_w)/2: y=125: fontsize=32: fontcolor=white, \
47 | drawtext=text='%{pts\:gmtime\:${DATE_PART1}\:%Y-%m-%d}%{pts\:hms\:${DATE_MOD_DAYS}.${DATE_PART2}}':\
48 | fontsize=32: x=(w-tw)/2: y=30: fontcolor=white[v+tc]; \
49 | [v+tc][1]overlay=eval=init:x=W-15-w:y=15[vid]; \
50 | [vid]split=2[vid0][vid1]" \
51 | -map "[vid0]" -s ${V1_ASPECT_W}x${V1_ASPECT_H} -c:v ${V1_CODEC} -b:v ${V1_BITRATE} -profile:v main -preset ultrafast -tune zerolatency \
52 | -g ${V1_GOP_LENGTH} \
53 | -r ${V1_FRAME_RATE} \
54 | -keyint_min ${V1_GOP_LENGTH} \
55 | -fflags +genpts \
56 | -movflags +frag_keyframe+empty_moov+separate_moof+default_base_moof \
57 | -write_prft pts \
58 | -video_track_timescale 10000000 \
59 | -ism_offset $VIDEO_ISM_OFFSET \
60 | -f mp4 "${PUB_POINT}/Streams(video-${V1_ASPECT_W}p${V1_FRAME_RATE}-${V1_BITRATE}.cmfv)" \
61 | -map "[vid1]" -s ${V2_ASPECT_W}x${V2_ASPECT_H} -c:v ${V2_CODEC} -b:v ${V2_BITRATE} -profile:v main -preset ultrafast -tune zerolatency \
62 | -g ${V2_GOP_LENGTH} \
63 | -r ${V2_FRAME_RATE} \
64 | -keyint_min ${V2_GOP_LENGTH} \
65 | -fflags +genpts \
66 | -movflags +frag_keyframe+empty_moov+separate_moof+default_base_moof \
67 | -write_prft pts \
68 | -video_track_timescale 10000000 \
69 | -ism_offset $VIDEO_ISM_OFFSET \
70 | -f mp4 "${PUB_POINT}/Streams(video-${V2_ASPECT_W}p${V2_FRAME_RATE}-${V2_BITRATE}.cmfv)" \
71 | -map "[a2]" -c:a ${A1_CODEC} -b:a ${A1_BITRATE} -metadata:s:a:0 language=${A1_LANGUAGE} \
72 | -fflags +genpts \
73 | -frag_duration $AUDIO_FRAG_DUR_MICROS \
74 | -min_frag_duration $AUDIO_FRAG_DUR_MICROS \
75 | -movflags +empty_moov+separate_moof+default_base_moof \
76 | -write_prft pts \
77 | -video_track_timescale 48000 \
78 | -ism_offset $AUDIO_ISM_OFFSET \
79 | -f mp4 "$PUB_POINT/Streams(audio-${A1_CODEC}-${A1_BITRATE}.cmfa)" \
80 | -map "[a3]" -c:a ${A2_CODEC} -b:a ${A2_BITRATE} -metadata:s:a:0 language=${A2_LANGUAGE} \
81 | -fflags +genpts \
82 | -frag_duration $AUDIO_FRAG_DUR_MICROS \
83 | -min_frag_duration $AUDIO_FRAG_DUR_MICROS \
84 | -movflags +empty_moov+separate_moof+default_base_moof \
85 | -write_prft pts \
86 | -video_track_timescale 48000 \
87 | -ism_offset $AUDIO_ISM_OFFSET \
88 | -f mp4 "$PUB_POINT/Streams(audio-${A2_CODEC}-${A2_BITRATE}.cmfa)" \
89 | ```
90 |
91 | Configuration is done by passing in environment variables defined in the docker-compose.yaml.
92 |
--------------------------------------------------------------------------------
/ffmpeg/entrypoint.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | """
3 | Entrypoint to run ffmpeg
4 | """
5 | import json
6 | import logging
7 | import os
8 | import subprocess
9 | from collections.abc import Iterable
10 | from datetime import datetime
11 | from fractions import Fraction
12 |
13 |
14 | logger = logging.getLogger(__name__)
15 | handler = logging.StreamHandler()
16 | formatter = logging.Formatter(
17 | "%(asctime)s - %(name)s - %(levelname)s - %(message)s")
18 | handler.setFormatter(formatter)
19 | logger.addHandler(handler)
20 | logger.setLevel(logging.DEBUG)
21 |
22 |
23 | def flatten(items):
24 | """Yield items from any nested iterable, use to flatten command"""
25 | for x in items:
26 | if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
27 | yield from flatten(x)
28 | else:
29 | yield x
30 |
31 |
32 | # fixed options
33 | FFMPEG = ["ffmpeg"]
34 | MOVFLAGS = "frag_every_frame+empty_moov+separate_moof+default_base_moof"
35 | ALL_TRACK_OPTS = [
36 | "-fflags", "genpts",
37 | "-write_prft", "pts",
38 | "-movflags", MOVFLAGS,
39 | "-f", "mp4",
40 | ]
41 |
42 | # env options
43 | if "PUB_POINT_URI" in os.environ:
44 | pub_point_uri = os.environ["PUB_POINT_URI"]
45 | else:
46 | logger.critical("must set PUB_POINT_URI")
47 | exit(1)
48 |
49 | hostname = os.environ["HOSTNAME"] if "HOSTNAME" in os.environ else "ffmpeg"
50 | frame_rate = os.environ["FRAME_RATE"] if "FRAME_RATE" in os.environ else "25"
51 | gop_length = os.environ["GOP_LENGTH"] if "GOP_LENGTH" in os.environ else "24"
52 |
53 | logo_overlay = os.environ["LOGO_OVERLAY"] if "LOGO_OVERLAY" in os.environ else "https://raw.githubusercontent.com/unifiedstreaming/live-demo/master/ffmpeg/usp_logo_white.png"
54 | logo_filter = ""
55 | if logo_overlay:
56 | logo_overlay = ["-i", logo_overlay]
57 | logo_filter = ";[v][1]overlay=eval=init:x=15:y=15[v]"
58 |
59 | # defaults
60 | DEFAULT_TRACKS = {
61 | "video": [
62 | {
63 | "width": 1280,
64 | "height": 720,
65 | "bitrate": "700k",
66 | "codec": "libx264",
67 | "framerate": frame_rate,
68 | "gop": gop_length,
69 | "timescale": 10000000
70 | }
71 | ],
72 | "audio": [
73 | {
74 | "samplerate": 48000,
75 | "bitrate": "64k",
76 | "codec": "aac",
77 | "language": "eng",
78 | "timescale": 48000
79 | }
80 | ]
81 | }
82 |
83 | # handle tracks
84 | tracks = json.loads(os.environ["TRACKS"]) if "TRACKS" in os.environ else DEFAULT_TRACKS
85 |
86 | # verify tracks make sense
87 | # if multiple videos, do their frame rates & gops line up
88 | if len(tracks["video"]) > 1:
89 | if len(set([Fraction(x["framerate"])/Fraction(x["gop"]) for x in tracks["video"]])) != 1:
90 | logger.critical("mismatched framerates/gop lengths")
91 | exit(1)
92 | if len(set([x["timescale"] for x in tracks["video"]])) != 1:
93 | logger.critical("mismatched video timescales not supported")
94 | exit(1)
95 |
96 | # audio check sample rate and timescales
97 | if len(tracks["audio"]) > 1:
98 | if len(set([x["samplerate"] for x in tracks["audio"]])) != 1:
99 | logger.critical("mismatched audio sample rates not supported")
100 | exit(1)
101 | if len(set([x["timescale"] for x in tracks["audio"]])) != 1:
102 | logger.critical("mismatched audio timescales not supported")
103 | exit(1)
104 |
105 | # use highest framerate, resolution, etc for source and filters
106 | max_framerate = max([Fraction(x["framerate"]) for x in tracks["video"]])
107 | max_width = max([x["width"] for x in tracks["video"]])
108 | max_height = max([x["height"] for x in tracks["video"]])
109 |
110 | # Timing stuff
111 | # floor to gop length based offset from epoch
112 | gop = Fraction(Fraction(tracks["video"][0]["gop"]), Fraction(tracks["video"][0]["framerate"]))
113 | now = Fraction(
114 | int(Fraction(Fraction(datetime.now().timestamp()), gop)),
115 | 1/gop)
116 |
117 | now_seconds = int(now)
118 | now_micro = int(now % 1 * 1000000)
119 |
120 | audio_delay = int((1000000 - now_micro)/1000)
121 |
122 | video_offset = int(tracks["video"][0]["timescale"] * now)
123 | audio_offset = int(tracks["audio"][0]["timescale"] * now)
124 |
125 | now_mod_days = Fraction(int(now * 1000000) % 86400000000, 1000000)
126 |
127 | max_framerate_int = int(max_framerate)
128 | now_timecode = (datetime.utcfromtimestamp(float(now)).strftime("%H\:%M\:%S"))
129 | now_milliseconds = int((datetime.utcfromtimestamp(float(now)).strftime("%f"))[:-3])
130 | now_frames = int(now_milliseconds / (1000 / max_framerate_int))
131 |
132 | logger.debug(f"max_framerate_int {max_framerate_int}")
133 | logger.debug(f"now_timecode {now_timecode}")
134 | logger.debug(f"now_milliseconds {now_milliseconds}")
135 | logger.debug(f"now_frames {now_frames}")
136 | logger.debug(f"now {now}")
137 | logger.debug(f"float(now) {float(now)}")
138 | logger.debug(f"now_seconds {now_seconds}")
139 | logger.debug(f"now_micro {now_micro}")
140 | logger.debug(f"audio_delay {audio_delay}")
141 | logger.debug(f"video_offset {video_offset}")
142 | logger.debug(f"audio_offset {audio_offset}")
143 | logger.debug(f"now_mod_days {now_mod_days}")
144 | logger.debug(f"float(now_mod_days) {float(now_mod_days)}")
145 |
146 | # build the stupid command
147 |
148 | # input smptebars
149 | smptebars = [
150 | "-f", "lavfi",
151 | "-i", f"smptehdbars=size={max_width}x{max_height}:rate={max_framerate}"
152 | ]
153 |
154 | # build the filter
155 | filter_complex = f"""
156 | [0]drawbox=
157 | y=25: x=iw/2-iw/7: c=0x00000000@1: w=iw/3.5: h=36: t=fill,
158 | drawtext=timecode_rate={max_framerate_int}: timecode='{now_timecode}\\:{now_frames}'" : tc24hmax=1: fontsize=32: x=(w-tw)/2+tw/2: y=30: fontcolor=white,
159 | drawtext=text='%{{pts\:gmtime\:{now_seconds}\:%Y-%m-%d}}\ ': fontsize=32: x=(w-tw)/2-tw/2: y=30: fontcolor=white,
160 | drawtext=
161 | text='Live Media Ingest (CMAF)':
162 | fontsize=32:
163 | x=(w-text_w)/2:
164 | y=75:
165 | fontcolor=white,
166 | drawtext=
167 | text='Live Media Ingest (CMAF)':
168 | fontsize=32:
169 | x=(w-text_w)/2:
170 | y=75:
171 | fontsize=32:
172 | fontcolor=white,
173 | drawtext=
174 | fontcolor=white:
175 | fontsize=20:
176 | text='Dual Encoder Sync - Active ContainerID {hostname}':
177 | x=(w-text_w)/2:
178 | y=125
179 | [v];
180 | sine=frequency=1:beep_factor=480:sample_rate=48000,
181 | atempo=1,
182 | adelay={audio_delay},
183 | highpass=40,
184 | asplit=2[a][a_waves];
185 | [a_waves]showwaves=
186 | mode=p2p:
187 | colors=white:
188 | size=1280x100:
189 | scale=lin:
190 | rate={max_framerate}
191 | [waves];
192 | color=size={max_width}x100:color=black[blackbg];
193 | [blackbg][waves]overlay[waves2];
194 | [v][waves2]overlay=y=620[v]
195 | {logo_filter}
196 | ;[v]split={len(tracks["video"])}{"".join(["[v"+str(x)+"]" for x in range(1, len(tracks["video"])+1)])};
197 | [a]asplit={len(tracks["audio"])}{"".join(["[a"+str(x)+"]" for x in range(1, len(tracks["audio"])+1)])}
198 | """
199 |
200 | command = [
201 | FFMPEG,
202 | "-nostats",
203 | "-re",
204 | smptebars,
205 | logo_overlay,
206 | "-filter_complex", filter_complex
207 | ]
208 |
209 | # all the various outputs
210 | count = 0
211 | for video in tracks["video"]:
212 | count += 1
213 | command.append([
214 | "-map", f"[v{count}]",
215 | "-s", f"{video['width']}x{video['height']}",
216 | "-c:v", str(video["codec"]),
217 | "-b:v", video["bitrate"],
218 | "-profile:v", "main",
219 | "-preset", "ultrafast",
220 | "-tune", "zerolatency",
221 | "-g", str(video["gop"]),
222 | "-r", str(video["framerate"]),
223 | "-ism_offset", str(video_offset),
224 | "-video_track_timescale", str(video["timescale"]),
225 | ALL_TRACK_OPTS,
226 | f"{pub_point_uri}/Streams(video-{video['width']}x{video['height']}-{video['bitrate']}.cmfv)"
227 | ])
228 |
229 | count = 0
230 | for audio in tracks["audio"]:
231 | count += 1
232 | command.append([
233 | "-map", f"[a{count}]",
234 | "-c:a", str(audio["codec"]),
235 | "-b:a", str(audio["bitrate"]),
236 | "-ar", str(audio["samplerate"]),
237 | "-metadata:s:a:0", f"language={audio['language']}",
238 | "-ism_offset", str(audio_offset),
239 | "-audio_track_timescale", str(audio["timescale"]),
240 | ALL_TRACK_OPTS,
241 | f"{pub_point_uri}/Streams(audio-{audio['language']}-{audio['bitrate']}.cmfa)"
242 | ])
243 |
244 | logger.info(f"ffmpeg command: {list(flatten(command))}")
245 |
246 | subprocess.run(list(flatten(command)))
247 |
--------------------------------------------------------------------------------
/ffmpeg/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ $# -eq 0 ]
4 | then
5 | python3 /usr/local/bin/entrypoint.py
6 | else
7 | exec "$@"
8 | fi
--------------------------------------------------------------------------------
/ffmpeg/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/ffmpeg/example.png
--------------------------------------------------------------------------------
/ffmpeg/example_cmaf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/ffmpeg/example_cmaf.png
--------------------------------------------------------------------------------
/ffmpeg/example_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/ffmpeg/example_logo.png
--------------------------------------------------------------------------------
/ffmpeg/usp_logo_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/ffmpeg/usp_logo_white.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 | foo
2 |
--------------------------------------------------------------------------------
/origin/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/origin/.DS_Store
--------------------------------------------------------------------------------
/origin/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG ALPINEVERSION=3.21
2 |
3 | FROM alpine:$ALPINEVERSION
4 |
5 | # ARGs declared before FROM are in a different scope, so need to be stated again
6 | # https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
7 | ARG ALPINEVERSION
8 | ARG REPO=https://stable.apk.unified-streaming.com/alpine
9 | ARG VERSION=1.15.5
10 |
11 | # Get USP public key
12 | RUN wget -q -O /etc/apk/keys/alpine@unified-streaming.com.rsa.pub \
13 | https://stable.apk.unified-streaming.com/alpine@unified-streaming.com.rsa.pub
14 |
15 | # Install Origin
16 | RUN apk \
17 | --update \
18 | --repository $REPO/v$ALPINEVERSION \
19 | add \
20 | mp4split~$VERSION \
21 | mp4split-ffmpeg-plugins~$VERSION \
22 | mod_smooth_streaming~$VERSION \
23 | mod_unified_s3_auth~$VERSION \
24 | manifest-edit~$VERSION \
25 | && rm -f /var/cache/apk/*
26 |
27 | # Set up directories and log file redirection
28 | RUN mkdir -p /run/apache2 \
29 | && ln -s /dev/stderr /var/log/apache2/error.log \
30 | && ln -s /dev/stdout /var/log/apache2/access.log \
31 | && mkdir -p /var/www/unified-origin \
32 | && rm -f /etc/apache2/conf.d/default.conf \
33 | /etc/apache2/conf.d/info.conf \
34 | /etc/apache2/conf.d/languages.conf \
35 | /etc/apache2/conf.d/mpm.conf \
36 | /etc/apache2/conf.d/proxy.conf \
37 | /etc/apache2/conf.d/ssl.conf \
38 | /etc/apache2/conf.d/userdir.conf
39 |
40 | # Enable default Manifest Edit pipelines
41 | RUN mkdir -p /etc/manifest-edit \
42 | && cp -R /usr/share/manifest-edit/* /etc/manifest-edit/
43 |
44 | # Copy apache config and entrypoint script
45 | COPY conf.d /etc/apache2/conf.d
46 | COPY entrypoint.sh /usr/local/bin/entrypoint.sh
47 |
48 | RUN chmod +x /usr/local/bin/entrypoint.sh
49 |
50 | # Copy webpage
51 | COPY html /var/www/unified-origin/
52 |
53 | # Copy Transcoder Config for ffmpeg-usp
54 | COPY ffmpeg-transcoders.usp /etc/ffmpeg-transcoders.usp
55 |
56 | # set Apache as owner of /var/www/unified-origin so it can write from API
57 | RUN chown apache:apache /var/www/unified-origin
58 |
59 | EXPOSE 80
60 |
61 | ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
62 |
63 | CMD ["-D", "FOREGROUND"]
64 |
--------------------------------------------------------------------------------
/origin/conf.d/mpm.conf:
--------------------------------------------------------------------------------
1 | #
2 | # Server-Pool Management (MPM specific)
3 | #
4 |
5 | #
6 | # PidFile: The file in which the server should record its process
7 | # identification number when it starts.
8 | #
9 | # Note that this is the default PidFile for most MPMs.
10 | #
11 |
12 | PidFile "/run/apache2/httpd.pid"
13 |
14 |
15 | #
16 | # Only one of the below sections will be relevant on your
17 | # installed httpd. Use "apachectl -l" to find out the
18 | # active mpm.
19 | #
20 |
21 | # prefork MPM
22 | # StartServers: number of server processes to start
23 | # MinSpareServers: minimum number of server processes which are kept spare
24 | # MaxSpareServers: maximum number of server processes which are kept spare
25 | # MaxRequestWorkers: maximum number of server processes allowed to start
26 | # MaxConnectionsPerChild: maximum number of connections a server process serves
27 | # before terminating
28 |
29 | StartServers 5
30 | MinSpareServers 5
31 | MaxSpareServers 10
32 | MaxRequestWorkers 250
33 | MaxConnectionsPerChild 0
34 |
35 |
36 | # worker MPM
37 | # StartServers: initial number of server processes to start
38 | # MinSpareThreads: minimum number of worker threads which are kept spare
39 | # MaxSpareThreads: maximum number of worker threads which are kept spare
40 | # ThreadsPerChild: constant number of worker threads in each server process
41 | # MaxRequestWorkers: maximum number of worker threads
42 | # MaxConnectionsPerChild: maximum number of connections a server process serves
43 | # before terminating
44 |
45 | StartServers 3
46 | MinSpareThreads 75
47 | MaxSpareThreads 250
48 | ThreadsPerChild 25
49 | MaxRequestWorkers 400
50 | MaxConnectionsPerChild 0
51 |
52 |
53 | # event MPM
54 | # StartServers: initial number of server processes to start
55 | # MinSpareThreads: minimum number of worker threads which are kept spare
56 | # MaxSpareThreads: maximum number of worker threads which are kept spare
57 | # ThreadsPerChild: constant number of worker threads in each server process
58 | # MaxRequestWorkers: maximum number of worker threads
59 | # MaxConnectionsPerChild: maximum number of connections a server process serves
60 | # before terminating
61 |
62 | StartServers 3
63 | MinSpareThreads 75
64 | MaxSpareThreads 250
65 | ThreadsPerChild 25
66 | MaxRequestWorkers 400
67 | MaxConnectionsPerChild 0
68 |
69 |
70 | # NetWare MPM
71 | # ThreadStackSize: Stack size allocated for each worker thread
72 | # StartThreads: Number of worker threads launched at server startup
73 | # MinSpareThreads: Minimum number of idle threads, to handle request spikes
74 | # MaxSpareThreads: Maximum number of idle threads
75 | # MaxThreads: Maximum number of worker threads alive at the same time
76 | # MaxConnectionsPerChild: Maximum number of connections a thread serves. It
77 | # is recommended that the default value of 0 be set
78 | # for this directive on NetWare. This will allow the
79 | # thread to continue to service requests indefinitely.
80 |
81 | ThreadStackSize 65536
82 | StartThreads 250
83 | MinSpareThreads 25
84 | MaxSpareThreads 250
85 | MaxThreads 1000
86 | MaxConnectionsPerChild 0
87 |
88 |
89 | # OS/2 MPM
90 | # StartServers: Number of server processes to maintain
91 | # MinSpareThreads: Minimum number of idle threads per process,
92 | # to handle request spikes
93 | # MaxSpareThreads: Maximum number of idle threads per process
94 | # MaxConnectionsPerChild: Maximum number of connections per server process
95 |
96 | StartServers 2
97 | MinSpareThreads 5
98 | MaxSpareThreads 10
99 | MaxConnectionsPerChild 0
100 |
101 |
102 | # WinNT MPM
103 | # ThreadsPerChild: constant number of worker threads in the server process
104 | # MaxConnectionsPerChild: maximum number of connections a server process serves
105 |
106 | ThreadsPerChild 150
107 | MaxConnectionsPerChild 0
108 |
109 |
110 | # The maximum number of free Kbytes that every allocator is allowed
111 | # to hold without calling free(). In threaded MPMs, every thread has its own
112 | # allocator. When not set, or when set to zero, the threshold will be set to
113 | # unlimited.
114 |
115 | MaxMemFree 2048
116 |
117 |
118 | MaxMemFree 100
119 |
120 |
--------------------------------------------------------------------------------
/origin/conf.d/unified-origin.conf:
--------------------------------------------------------------------------------
1 | # Load required modules, if needed
2 |
3 | LoadModule headers_module /usr/lib/apache2/mod_headers.so
4 |
5 |
6 | LoadModule proxy_module /usr/lib/apache2/mod_proxy.so
7 |
8 |
9 | LoadModule proxy_http_module /usr/lib/apache2/mod_proxy_http.so
10 |
11 |
12 | LoadModule socache_shmcb_module /usr/lib/apache2/mod_socache_shmcb.so
13 |
14 |
15 | LoadModule ssl_module /usr/lib/apache2/mod_ssl.so
16 |
17 |
18 | LoadModule ext_filter_module /usr/lib/apache2/mod_ext_filter.so
19 |
20 |
21 | LoadModule unified_s3_auth_module /usr/lib/apache2/mod_unified_s3_auth.so
22 |
23 |
24 | LoadModule smooth_streaming_module /usr/lib/apache2/mod_smooth_streaming.so
25 |
26 |
27 | AddHandler smooth-streaming.extensions .ism .isml
28 |
29 | ServerName unified-origin
30 |
31 | UspLicenseKey /etc/usp-license.key
32 |
33 |
34 | LogFormat '${LOG_FORMAT}' log_format
35 |
36 |
37 | LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" %D" log_format
38 |
39 |
40 | # Don't timeout to support long running POST from live encoder
41 | LimitRequestBody 0
42 |
43 | RequestReadTimeout header=0 body=0
44 |
45 |
46 |
47 | # Don't log kubernetes probes
48 | SetEnvIf User-Agent "kube-probe" dontlog
49 | CustomLog /dev/stdout log_format env=!dontlog
50 | ErrorLog /dev/stderr
51 |
52 |
53 | LogLevel ${LOG_LEVEL}
54 |
55 |
56 | LogLevel warn
57 |
58 |
59 | SSLProxyEngine on
60 |
61 | DocumentRoot /var/www/unified-origin
62 |
63 | Header set Access-Control-Allow-Headers "origin, range"
64 | Header set Access-Control-Allow-Methods "GET, HEAD, OPTIONS"
65 | Header set Access-Control-Allow-Origin "*"
66 | Header set Access-Control-Expose-Headers "Server,range"
67 |
68 | # Enable Origin and use subrequests instead of libcurl
69 |
70 | UspHandleIsm on
71 | UspEnableSubreq on
72 |
73 |
74 | # Remote storage configuration
75 |
76 |
77 | IsmProxyPass "${REMOTE_STORAGE_URL}"
78 |
79 |
80 |
81 | ProxySet connectiontimeout=5 enablereuse=on keepalive=on retry=0 timeout=30 ttl=300
82 | RequestHeader unset Accept-Encoding
83 | RequestHeader unset x-amz-cf-id
84 | S3UseHeaders on
85 |
86 | S3AccessKey ${S3_ACCESS_KEY}
87 |
88 |
89 | S3SecretKey ${S3_SECRET_KEY}
90 |
91 |
92 | S3SecurityToken ${S3_SECURITY_TOKEN}
93 |
94 |
95 | S3Region ${S3_REGION}
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | Require all granted
104 | Options -Indexes
105 |
106 |
107 | # Optional REST API for publishing point management
108 |
109 | Listen 0.0.0.0:${REST_API_PORT}
110 |
111 | CustomLog /dev/stdout log_format
112 | ErrorLog /dev/stderr
113 |
114 |
115 | LogLevel ${LOG_LEVEL}
116 |
117 |
118 | LogLevel warn
119 |
120 |
121 | DocumentRoot /var/www/unified-origin
122 |
123 | # Enable REST API
124 |
125 | UspHandleApi on
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/origin/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | set -e
3 |
4 | # Validate license key variable is set
5 | if [ -z "$UspLicenseKey" ] && [ -z "$USP_LICENSE_KEY" ]
6 | then
7 | echo >&2 "Error: UspLicenseKey environment variable is required but not set."
8 | exit 1
9 | elif [ -z "$UspLicenseKey" ]
10 | then
11 | export UspLicenseKey=$USP_LICENSE_KEY
12 | fi
13 |
14 | # write license key to file
15 | echo "$UspLicenseKey" > /etc/usp-license.key
16 |
17 | # If specified, override default log level and format config
18 | if [ "$LOG_FORMAT" ]
19 | then
20 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D LOG_FORMAT"
21 | fi
22 | if [ "$LOG_LEVEL" ]
23 | then
24 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D LOG_LEVEL"
25 | fi
26 |
27 | # Remote storage URL and storage proxy config
28 | if [ "$REMOTE_STORAGE_URL" ]
29 | then
30 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D REMOTE_STORAGE_URL"
31 | if [ -z "$REMOTE_PATH" ]
32 | then
33 | export REMOTE_PATH=remote
34 | fi
35 | fi
36 | if [ "$S3_ACCESS_KEY" ]
37 | then
38 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D S3_ACCESS_KEY"
39 | fi
40 | if [ "$S3_SECRET_KEY" ]
41 | then
42 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D S3_SECRET_KEY"
43 | fi
44 | if [ "$S3_SECURITY_TOKEN" ]
45 | then
46 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D S3_SECURITY_TOKEN"
47 | fi
48 | if [ "$S3_REGION" ]
49 | then
50 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D S3_REGION"
51 | fi
52 |
53 | # REST API
54 | if [ "$REST_API_PORT" ]
55 | then
56 | export EXTRA_OPTIONS="$EXTRA_OPTIONS -D REST_API_PORT"
57 | fi
58 |
59 | # Change 'Listen 80' to 'Listen 0.0.0.0:80' to avoid some strange issues when IPv6 is available
60 | /bin/sed -i "s@Listen 80@Listen 0.0.0.0:80@g" /etc/apache2/httpd.conf
61 |
62 | rm -f /run/apache2/httpd.pid
63 |
64 | # create ingest publishing point
65 | if [ ! -f /var/www/unified-origin/$PUB_POINT_NAME/$PUB_POINT_NAME.isml ]
66 | then
67 | mkdir -p /var/www/unified-origin/$PUB_POINT_NAME
68 | chown -R apache:apache /var/www/unified-origin/$PUB_POINT_NAME
69 | mp4split \
70 | -o "/var/www/unified-origin/$PUB_POINT_NAME/$PUB_POINT_NAME.isml" \
71 | $PUB_POINT_OPTS
72 | fi
73 |
74 |
75 | # First arg is `-f` or `--some-option`
76 | if [ "${1#-}" != "$1" ]; then
77 | set -- httpd $EXTRA_OPTIONS "$@"
78 | fi
79 |
80 | exec "$@"
81 |
--------------------------------------------------------------------------------
/origin/ffmpeg-transcoders.usp:
--------------------------------------------------------------------------------
1 | # Override default transcoders with FFmpeg-based ones.
2 |
3 | video_decoder_avc avcodec
4 | video_decoder_hvc avcodec
5 | video_filter_resize swscale
6 | video_encoder_jpg avcodec
--------------------------------------------------------------------------------
/origin/html/clientaccesspolicy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/origin/html/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/origin/html/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/origin/html/favicon.ico
--------------------------------------------------------------------------------
/origin/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Unified Origin is installed.
4 | For more information see the documentation
5 |
--------------------------------------------------------------------------------
/unifiedstreaming-logo-black.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/unifiedstreaming/live-demo-cmaf/5f6202ae825df7b9bd9398ba10f53e1486422cca/unifiedstreaming-logo-black.jpg
--------------------------------------------------------------------------------