Created
August 1, 2024 04:54
-
-
Save rmarscher/03de535826a180e13ea34dbf38729e2e to your computer and use it in GitHub Desktop.
Functions for building a webvtt preview thumbnail file generated by AWS MediaConvert - compatible with media chrome, plyr and other players
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<style> | |
html, | |
body { | |
margin: 0; | |
padding: 0; | |
} | |
media-theme-yt { | |
display: block; /* expands the container if preload=none */ | |
aspect-ratio: 16 / 9; /* set container aspect ratio if preload=none */ | |
} | |
hls-video { | |
width: 100%; /* prevents video to expand beyond its container */ | |
background-color: #000; | |
} | |
</style> | |
<script | |
type="module" | |
src="https://cdn.jsdelivr.net/npm/hls-video-element/+esm" | |
></script> | |
<script | |
type="module" | |
src="https://cdn.jsdelivr.net/npm/player.style/yt/+esm" | |
></script> | |
</head> | |
<body> | |
<main> | |
<!-- https://player.style is cool! --> | |
<media-theme-yt> | |
<hls-video | |
slot="media" | |
src="https://my.cloudfront.site/path/to/my/hls/files/video-filename.m3u8" | |
crossorigin | |
playsinline | |
> | |
<track | |
label="thumbnails" | |
default | |
kind="metadata" | |
src="https://my.cloudfront.site/path/to/my/hls/files/storyboard.vtt" | |
/> | |
</hls-video> | |
</media-theme-yt> | |
</main> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Name": "hls-abr-160kbps-h264-aac", | |
"Settings": { | |
"TimecodeConfig": { | |
"Source": "ZEROBASED" | |
}, | |
"OutputGroups": [ | |
{ | |
"Name": "File Group", | |
"Outputs": [ | |
{ | |
"ContainerSettings": { | |
"Container": "RAW" | |
}, | |
"VideoDescription": { | |
"CodecSettings": { | |
"Codec": "FRAME_CAPTURE", | |
"FrameCaptureSettings": { | |
"FramerateNumerator": 1, | |
"FramerateDenominator": 5, | |
"MaxCaptures": 3 | |
} | |
} | |
}, | |
"Extension": "jpg", | |
"NameModifier": "-poster" | |
} | |
], | |
"OutputGroupSettings": { | |
"Type": "FILE_GROUP_SETTINGS", | |
"FileGroupSettings": { | |
"Destination": "REPLACE_WITH_POSTER_S3_FOLDER_PATH" | |
} | |
} | |
}, | |
{ | |
"Name": "Apple HLS", | |
"Outputs": [ | |
{ | |
"ContainerSettings": { | |
"Container": "M3U8", | |
"M3u8Settings": {} | |
}, | |
"VideoDescription": { | |
"CodecSettings": { | |
"Codec": "H_264", | |
"H264Settings": { | |
"FramerateControl": "INITIALIZE_FROM_SOURCE", | |
"RateControlMode": "QVBR", | |
"SceneChangeDetect": "TRANSITION_DETECTION", | |
"QualityTuningLevel": "MULTI_PASS_HQ" | |
} | |
} | |
}, | |
"AudioDescriptions": [ | |
{ | |
"CodecSettings": { | |
"Codec": "AAC", | |
"AacSettings": { | |
"Bitrate": 160000, | |
"CodingMode": "CODING_MODE_2_0", | |
"SampleRate": 48000 | |
} | |
} | |
} | |
], | |
"OutputSettings": { | |
"HlsSettings": {} | |
} | |
} | |
], | |
"OutputGroupSettings": { | |
"Type": "HLS_GROUP_SETTINGS", | |
"HlsGroupSettings": { | |
"SegmentLength": 6, | |
"Destination": "REPLACE_WITH_HLS_OUTPUT_S3_FOLDER_PATH", | |
"MinSegmentLength": 0, | |
"ImageBasedTrickPlay": "ADVANCED", | |
"ImageBasedTrickPlaySettings": { | |
"ThumbnailWidth": 280, | |
"TileHeight": 25, | |
"TileWidth": 14, | |
"IntervalCadence": "FOLLOW_CUSTOM", | |
"ThumbnailInterval": 18 | |
} | |
} | |
}, | |
"AutomatedEncodingSettings": { | |
"AbrSettings": { | |
"MaxAbrBitrate": 8000000, | |
"MinAbrBitrate": 360000 | |
} | |
} | |
} | |
], | |
"Inputs": [ | |
{ | |
"AudioSelectors": { | |
"Audio Selector 1": { | |
"DefaultSelection": "DEFAULT" | |
} | |
}, | |
"VideoSelector": {}, | |
"TimecodeSource": "ZEROBASED", | |
"FileInput": "REPLACE_WITH_INPUT_S3_PATH" | |
} | |
] | |
}, | |
"AccelerationSettings": { | |
"Mode": "PREFERRED" | |
}, | |
"StatusUpdateInterval": "SECONDS_15", | |
"Priority": 0, | |
"HopDestinations": [] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// use Bun or ts-node to run without transpiling | |
main(); | |
async function main() { | |
console.log( | |
generateVttTrickplay( | |
"https://my.cloudfront.site/path/to/my/hls/files/", | |
376, | |
280, | |
158, | |
), | |
); | |
} | |
export function generateVttTrickplay( | |
outputPath: string, | |
duration: number, | |
width = 284, | |
height = 160, | |
interval = 18, | |
tileX = 14, | |
tileY = 25, | |
): string { | |
let vtt = "WEBVTT\n\n"; | |
const segments = Math.ceil(duration / interval); | |
let i = 0; | |
let startTime = 0; | |
while (i < segments) { | |
const x = i % tileX; | |
const y = Math.floor(i / tileX); | |
const page = Math.floor(y / tileY) + 1; | |
const filename = `Thumbnail_000000${page.toString().padStart(3, "0")}.jpg`; | |
const imageUrl = `${outputPath}${filename}`; | |
startTime = i * interval; | |
const endTime = startTime + interval; | |
vtt += `${secondsToTimestamp(startTime)} --> ${secondsToTimestamp(endTime)}\n`; | |
vtt += `${imageUrl}#xywh=${x * width},${y * height},${width},${height}\n\n`; | |
i += 1; | |
} | |
return vtt; | |
} | |
export function secondsToTimestamp(duration: number): string { | |
const seconds = Math.round(duration * 1000) / 1000; | |
const milliseconds = Math.floor((seconds - Math.floor(seconds)) * 1000); | |
const sec = Math.floor(seconds) % 60; | |
const minutes = Math.floor(duration / 60) % 60 || 0; | |
const hours = Math.floor(duration / 3600) % 60 || 0; | |
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${sec.toString().padStart(2, "0")}.${milliseconds.toString().padStart(3, "0")}`; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment