This commit is contained in:
cbax 2024-10-19 16:54:59 -04:00
commit 63b517bc21
8 changed files with 460 additions and 0 deletions

19
README.md Normal file
View file

@ -0,0 +1,19 @@
# SSI Movie Night Processing
### Dependencies
- ffmpeg
- shaka packager
- your time
- some facedesking
- blahaj
### Notes
specifically, I changed `availabilityStartTime`, added `suggestedPresentationDelay="PT10S"`, and removed `timeShiftBufferDepth`
probably non-spec but who cares
oh I also removed `minimumUpdatePeriod` to disable polling
usually it's supposed to be polling that endpoint to see new chunks or end the livestream but you can also just... not do that and make the player hang forever after the livestream ends
the BBC actually just removed the segment list alltogether and their file is literally static and gets refetched every few hours as a sanity check
and then there's Apple, who forces you to create a manifest for each stream (incl. every quality option) and update all of them with a non-dynamic list of available segments
it's literally an M3U playlist with extra steps
SEE `static/manifest.mpd.example` FOR MANIFEST FORMAT!

84
encode.sh Executable file
View file

@ -0,0 +1,84 @@
#!/usr/bin/env bash
if [ "$2" = "true" ]; then
ffmpeg \
-hide_banner \
-hwaccel qsv \
-hwaccel_output_format qsv \
-hwaccel_device /dev/dri/renderD128 \
-i $1 \
-map_metadata -1 -map_chapters -1 \
-vf "scale_qsv=-1:1080" \
-map 0:0 \
-c:v vp9_qsv \
-color_range full \
-colorspace bt709 -color_primaries bt709 \
-color_trc iec61966-2-1 \
-crf 31 -b:v 3M \
-keyint_min 96 -g 96 \
-row-mt 1 -tile-columns 2 \
-deadline good -threads 12 \
encoded/video-1080p-crf31-b3m.mp4
# -hwaccel_device /dev/dri/renderD128 \
ffmpeg \
-hide_banner \
-hwaccel qsv \
-hwaccel_output_format qsv \
-hwaccel_device /dev/dri/renderD128 \
-i $1 \
-map_metadata -1 -map_chapters -1 \
-map 0:0 \
-vf "scale_qsv=-1:720" \
-c:v vp9_qsv \
-color_range full \
-colorspace bt709 -color_primaries bt709 \
-color_trc iec61966-2-1 \
-crf 32 -b:v 2M \
-keyint_min 96 -g 96 \
-row-mt 1 -tile-columns 2 \
-deadline good -threads 12 \
encoded/video-0720p-crf32-b2m.mp4
# -vf "scale_qsv=-1:540,hwdownload"
# -hwaccel_device /dev/dri/renderD128 \
ffmpeg \
-hide_banner \
-hwaccel qsv \
-hwaccel_output_format qsv \
-hwaccel_device /dev/dri/renderD128 \
-i $1 \
-map_metadata -1 -map_chapters -1 \
-map 0:0 \
-vf "scale_qsv=-1:540" \
-c:v vp9_qsv \
-color_range full \
-colorspace bt709 -color_primaries bt709 \
-color_trc iec61966-2-1 \
-crf 33 -b:v 1M \
-keyint_min 96 -g 96 \
-row-mt 1 -tile-columns 1 \
-deadline good -threads 12 \
encoded/video-0540p-crf33-b1m.mp4
fi
#ffmpeg \
# -hide_banner \
# -i $1 \
# -map 0:a:0 -c copy -c:s mov_text -metadata:s:s:0 language=eng encoded/audio-opus-unknown.mp4
if [ "$3" = "true" ]; then
ffmpeg \
-hide_banner \
-i $1 \
-map_metadata -1 -map_chapters -1 -map 0:1 -c:a libopus -copyts -b:a 384k -af "channelmap=channel_layout=5.1" encoded/audio-surround-opus-384k.mp4 \
-map_metadata -1 -map_chapters -1 -map 0:1 -c:a libopus -copyts -b:a 288k -af "channelmap=channel_layout=5.1" encoded/audio-surround-opus-288k.mp4 \
-map_metadata -1 -map_chapters -1 -map 0:1 -c:a libopus -copyts -b:a 128k -ac 2 encoded/audio-stereo-opus-128k.mp4 \
-map_metadata -1 -map_chapters -1 -map 0:1 -c:a libopus -copyts -b:a 96k -ac 2 encoded/audio-stereo-opus-96k.mp4
#-map_metadata -1 -map_chapters -1 -map 0:1 -c:a flac -compression_level 8 -af "channelmap=channel_layout=5.1" encoded/audio-surround-flac-l8.mp4 \
#-map_metadata -1 -map_chapters -1 -map 0:1 -c:a flac -compression_level 8 encoded/audio-stereo-flac-l8.mp4
#ffmpeg \
# -hide_banner \
# -i $1 \
# -map 0:s:1 -c copy subs.pgs
fi

0
encoded/.gitkeep Normal file
View file

30
package.sh Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
packager \
"in=encoded/video-1080p-crf31-b3m.mp4,stream=video,segment_template=packaged/main-video-1080p-crf31-b3m-\$Number%04d\$.mp4,init_segment=packaged/main-video-1080p-crf31-b3m-init.mp4" \
"in=encoded/video-0720p-crf32-b2m.mp4,stream=video,segment_template=packaged/main-video-0720p-crf32-b2m-\$Number%04d\$.mp4,init_segment=packaged/main-video-0720p-crf32-b2m-init.mp4" \
"in=encoded/video-0540p-crf33-b1m.mp4,stream=video,segment_template=packaged/main-video-0540p-crf33-b1m-\$Number%04d\$.mp4,init_segment=packaged/main-video-0540p-crf33-b1m-init.mp4" \
"in=encoded/audio-surround-opus-384k.mp4,lang=en,stream=audio,segment_template=packaged/main-audio-surround-opus-384k-\$Number%04d\$.mp4,init_segment=packaged/main-audio-surround-opus-384k-init.mp4" \
"in=encoded/audio-surround-opus-288k.mp4,lang=en,stream=audio,segment_template=packaged/main-audio-surround-opus-288k-\$Number%04d\$.mp4,init_segment=packaged/main-audio-surround-opus-288k-init.mp4" \
"in=encoded/audio-stereo-opus-128k.mp4,lang=en,stream=audio,segment_template=packaged/main-audio-stereo-opus-128k-\$Number%04d\$.mp4,init_segment=packaged/main-audio-stereo-opus-128k-init.mp4" \
"in=encoded/audio-stereo-opus-96k.mp4,lang=en,stream=audio,segment_template=packaged/main-audio-stereo-opus-96k-\$Number%04d\$.mp4,init_segment=packaged/main-audio-stereo-opus-96k-init.mp4" \
--segment_duration 8 --utc_timings "urn:mpeg:dash:utc:http-xsdate:2014=https://time.akamai.com/?iso&ms" \
--default_language en \
--generate_static_live_mpd --mpd_output "packaged/manifest.mpd"
#"in=encoded/subs.sup,lang=en,stream=text,segment_template=packaged/main-subs-eng-\$Number%04d\$.sup"
#packager \
# "in=encoded/video-1080p-crf31-b3m.mp4,stream=video,segment_template=packaged/video-1080p-crf31-b3m-\$Number%04d\$.mp4,init_segment=packaged/video-1080p-crf31-b3m-init.mp4,drm_label=mrrp" \
# "in=encoded/video-0720p-crf32-b2m.mp4,stream=video,segment_template=packaged/video-0720p-crf32-b2m-\$Number%04d\$.mp4,init_segment=packaged/video-0720p-crf32-b2m-init.mp4,drm_label=mrrp" \
# "in=encoded/video-0540p-crf33-b1m.mp4,stream=video,segment_template=packaged/video-0540p-crf33-b1m-\$Number%04d\$.mp4,init_segment=packaged/video-0540p-crf33-b1m-init.mp4,drm_label=mrrp" \
# "in=encoded/audio-opus-unknown.mp4,lang=en,stream=audio,segment_template=packaged/audio-opus-unknown-\$Number%04d\$.mp4,init_segment=packaged/audio-opus-unknown-init.mp4,drm_label=mrrp" \
# --segment_duration 8 --utc_timings "urn:mpeg:dash:utc:http-xsdate:2014=https://time.akamai.com/?iso&ms" \
# --default_language en \
# --suggested_presentation_delay 30 \
# --generate_static_live_mpd --mpd_output "packaged/manifest.mpd"
#sed -i 's/type="static"/type="dynamic"/g' ./packaged/manifest.mpd
#rep="s/mediaPresentationDuration=\".*\"/publishTime=\"$()\"/g"
#sed -i 's/mediaPresentationDuration=".*"/publishTime=""/g'
#--enable_raw_key_encryption --keys "label=mrrp:key_id=<kid>:key=<key>" \

0
packaged/.gitkeep Normal file
View file

0
source/.gitkeep Normal file
View file

246
static/index.html Normal file
View file

@ -0,0 +1,246 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Silly video player</title>
<link rel="stylesheet" href="./dash.js/contrib/akamai/controlbar/controlbar.css">
<link rel="stylesheet" href="silicon.min.css">
<script src="./dash.js/contrib/akamai/controlbar/ControlBar.js"></script>
<script>
function onFullscreenClick() {
var element = document.querySelector("#player");
if (!document.fullscreenElement) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else {
element.webkitRequestFullScreen();
}
let videoControls = document.querySelector("#videoController");
videoControls.style.top = "95%";
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else {
document.webkitCancelFullScreen();
}
}
}
let wakeLock;
async function initWakelock() {
try {
wakeLock = await navigator.wakeLock.request("screen");
} catch (err) {
// the wake lock request fails - usually system related, such being low on battery
console.log(`${err.name}, ${err.message}`);
}
}
function blur() {
let vid = document.querySelector("#videoPlayer");
if (vid.paused || vid.ended) {
requestAnimationFrame(blur);
return;
}
let canvas = document.querySelector("#bgBlur");
if (canvas.width != vid.offsetWidth || canvas.height != vid.offsetHeight) {
canvas.width = vid.offsetWidth;
canvas.height = vid.offsetHeight;
}
let ctx = canvas.getContext("2d");
// Figure out scaled size
let sw_ratio = vid.videoWidth / vid.videoHeight;
let sh_ratio = vid.videoHeight / vid.videoWidth;
let sw = Math.min(canvas.width, canvas.height*sw_ratio);
let sh = Math.min(canvas.height, canvas.width*sh_ratio);
let left = (canvas.width - sw)/2;
let top = (canvas.height - sh)/2;
ctx.drawImage(vid, left, top, sw, sh);
requestAnimationFrame(blur);
}
window.onload = function() {
let cdt = new Date();
let tzo = parseInt(cdt.toString().split("GMT")[1].substring(0,3));
console.log(tzo);
if (tzo > -11 & tzo < -2) {
url = "https://r2-nam.ssi.fyi/packaged/manifest.mpd";
} else {
url = "https://r2-eur.ssi.fyi/packaged/manifest.mpd";
}
let bypass = true;
if (bypass) {
url = "https://rdmedia.bbc.co.uk/testcard/lowlatency/manifests/ll-avc-ctv-en.mpd";
//url = "https://livesim.dashif.org/dash/vod/testpic_2s/multi_subs.mpd";
}
let player = dashjs.MediaPlayer().create();
let video = document.querySelector("#videoPlayer");
player.updateSettings({
streaming: {
liveCatchup: { enabled: true },
},
});
player.initialize(video, url, true);
// Slight hack
let fullscreenBtn = document.querySelector("#fullscreenBtn");
let addEventListener = fullscreenBtn.addEventListener;
fullscreenBtn.addEventListener = function() {}; // Prevent events from being registered
let TTMLRenderingDiv = document.querySelector("#subsBox");
player.attachTTMLRenderingDiv(TTMLRenderingDiv)
player.attachView(video);
let controlbar = new ControlBar(player);
controlbar.initialize();
fullscreenBtn.addEventListener = addEventListener;
fullscreenBtn.addEventListener('click', onFullscreenClick);
initWakelock();
blur();
}
</script>
<!--
Getting an error?
Make sure to build dash.js
npm install (ignore errors)
npm run webpack-build
-->
<script src="./dash.js/dist/dash.all.debug.js"></script>
<style>
html {
background: black;
}
.videoControls {
position:absolute;
bottom: 0;
left: 0;
width: 100%;
z-index: 10;
}
.videoController {
display: flex;
flex-direction: row;
top: 0;
left: 0;
padding: 0;
margin: 0;
width: 100%;
height: auto;
overflow: hidden;
z-index: 3;
opacity: 1%;
transition: opacity 0.25s ease-in-out;
justify-content: center;
align-items: center;
}
.videoController:hover {
opacity: 100%;
}
.control-icon-layout, .time-display, .duration-display, .seekContainer {
padding-right: 10px;
}
.videoView {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.volumeContainer {
width: 10%;
}
.seekContainer {
display: none;
}
#bgBlur, #videoPlayer {
position: absolute;
top: 0;
left: 0;
padding: 0;
margin: 0;
width: 100%;
height: 100%;
}
#videoPlayer {
z-index: 1;
}
#bgBlur {
filter: blur(100px);
z-index: -1;
}
#subsBox {
z-index: 10;
}
#subsBox * {
color: #ffffff !important;
}
</style>
</head>
<body>
<!-- control bar -->
<div id="player">
<div class="videoControls">
<div id="videoController" class="videoController unselectable">
<div id="playPauseBtn" class="btn-play-pause control-icon-layout" title="Play/Pause">
<span id="iconPlayPause" class="icon-play"></span>
</div>
<div id="fullscreenBtn" class="btn-fullscreen control-icon-layout" title="Fullscreen">
<span class="icon-fullscreen-enter"></span>
</div>
<div id="bitrateListBtn" class="control-icon-layout" title="Bitrate List">
<span class="icon-bitrate"></span>
</div>
<div id="muteBtn" class="btn-mute control-icon-layout" title="Mute">
<span id="iconMute" class="icon-mute-off"></span>
</div>
<div class="volumeContainer">
<input type="range" id="volumebar" class="volumeBar" value="1" min="0" max="1" step=".01"/>
</div>
<div id="trackSwitchBtn" class="control-icon-layout" title="A/V Tracks">
<span class="icon-tracks"></span>
</div>
<div id="captionBtn" class="btn-caption control-icon-layout" title="Closed Caption">
<span class="icon-caption"></span>
</div>
<span id="videoTime" class="time-display">00:00:00</span>
<div class="seekContainer">
<input type="range" id="seekbar" value="0" class="seekbar" min="0" step="0.01"/>
</div>
<span id="videoDuration" class="duration-display">00:00:00</span>
</div>
</div>
<div class="videoView">
<video muted autoplay id="videoPlayer"></video>
<canvas width="100" height="100" id="bgBlur"></canvas>
<div id="subsBox"></div>
</div>
</div>
</body>

View file

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/shaka-project/shaka-packager version v3.2.0-53b8668-release-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" profiles="urn:mpeg:dash:profile:isoff-live:2011" minBufferTime="PT2S" type="dynamic" publishTime="2024-10-19T17:34:00Z" availabilityStartTime="2024-10-19T19:00:00Z" suggestedPresentationDelay="PT5S" timeShiftBufferDepth="PT600S" minimumUpdatePeriod="5">
<Period id="0" start="PT0S">
<AdaptationSet id="0" contentType="video" maxWidth="1920" maxHeight="1080" frameRate="24000/1001" segmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="3485388" codecs="vp09.02.40.10.03.01.13.01.01" mimeType="video/mp4" sar="1:1" width="1920" height="1080">
<SegmentTemplate timescale="24000" initialization="main-video-1080p-crf31-b3m-init.mp4" media="main-video-1080p-crf31-b3m-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="192192" r="499"/>
<S t="96096000" d="96096"/>
<S t="96192096" d="192192" r="218"/>
<S t="138282144" d="139139"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="1" bandwidth="2336117" codecs="vp09.02.31.10.03.01.13.01.01" mimeType="video/mp4" sar="1:1" width="1280" height="720">
<SegmentTemplate timescale="24000" initialization="main-video-0720p-crf32-b2m-init.mp4" media="main-video-0720p-crf32-b2m-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="192192" r="499"/>
<S t="96096000" d="96096"/>
<S t="96192096" d="192192" r="218"/>
<S t="138282144" d="139139"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="2" bandwidth="1113633" codecs="vp09.02.30.10.03.01.13.01.01" mimeType="video/mp4" sar="1:1" width="960" height="540">
<SegmentTemplate timescale="24000" initialization="main-video-0540p-crf33-b1m-init.mp4" media="main-video-0540p-crf33-b1m-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="192192" r="499"/>
<S t="96096000" d="96096"/>
<S t="96192096" d="192192" r="218"/>
<S t="138282144" d="139139"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
<AdaptationSet id="1" contentType="audio" lang="en" segmentAlignment="true">
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"/>
<Representation id="3" bandwidth="513980" codecs="opus" mimeType="audio/mp4" audioSamplingRate="48000">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="6"/>
<SegmentTemplate timescale="48000" initialization="main-audio-surround-opus-384k-init.mp4" media="main-audio-surround-opus-384k-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="384648"/>
<S t="384648" d="384000" r="722"/>
<S t="278016648" d="249720"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="4" bandwidth="396376" codecs="opus" mimeType="audio/mp4" audioSamplingRate="48000">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="6"/>
<SegmentTemplate timescale="48000" initialization="main-audio-surround-opus-288k-init.mp4" media="main-audio-surround-opus-288k-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="384648"/>
<S t="384648" d="384000" r="722"/>
<S t="278016648" d="249720"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="5" bandwidth="159055" codecs="opus" mimeType="audio/mp4" audioSamplingRate="48000">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="48000" initialization="main-audio-stereo-opus-128k-init.mp4" media="main-audio-stereo-opus-128k-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="384648"/>
<S t="384648" d="384000" r="722"/>
<S t="278016648" d="249720"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
<Representation id="6" bandwidth="122015" codecs="opus" mimeType="audio/mp4" audioSamplingRate="48000">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<SegmentTemplate timescale="48000" initialization="main-audio-stereo-opus-96k-init.mp4" media="main-audio-stereo-opus-96k-$Number%04d$.mp4" startNumber="1">
<SegmentTimeline>
<S t="0" d="384648"/>
<S t="384648" d="384000" r="722"/>
<S t="278016648" d="249720"/>
</SegmentTimeline>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>