Created
April 13, 2024 13:22
-
-
Save branneman/d32c77434462e9fbf222f72fa14c4856 to your computer and use it in GitHub Desktop.
HTML5 Video: MediaSource, SourceBuffer, video segments, etc.
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, viewport-fit=cover" | |
/> | |
<title>video test</title> | |
</head> | |
<body> | |
<video | |
id="video" | |
controls | |
width="720" | |
poster="/video/2012/thumbnail.jpg" | |
></video> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
const file = '2012' | |
const segments = 22 | |
const duration = 226.92 | |
const MIME_MP4 = 'video/mp4; codecs="avc1.42c01e, mp4a.40.2"' | |
const MIME_WEBM = 'video/webm; codecs="vp9, vorbis"' | |
const getVideoFormat = () => { | |
if (MediaSource.isTypeSupported(MIME_MP4)) | |
return { ext: 'mp4', mime: MIME_MP4 } | |
if (MediaSource.isTypeSupported(MIME_WEBM)) | |
return { ext: 'webm', mime: MIME_WEBM } | |
return null | |
} | |
if (getVideoFormat() === null) { | |
console.error('No support video format for this browser') | |
return | |
} | |
const fetchSegment = async (sourceBuffer, number) => { | |
const formattedNumber = String(number).padStart(3, '0') | |
const url = `/video/${file}/segment${formattedNumber}.${getVideoFormat().ext}` | |
const response = await fetch(url) | |
const data = await response.arrayBuffer() | |
// https://stackoverflow.com/a/60643343 | |
sourceBuffer.appendBuffer(data) | |
return Promise.resolve() | |
} | |
const fetchAllSegments = (sourceBuffer, segments) => { | |
const arr0toN = Array.from(Array(segments + 1).keys()) | |
arr0toN.reduce((chain, segment) => { | |
return chain.then(() => { | |
return fetchSegment(sourceBuffer, segment) | |
}) | |
}, Promise.resolve()) | |
} | |
const mediaSource = new MediaSource() | |
mediaSource.addEventListener('sourceopen', () => { | |
const sourceBuffer = mediaSource.addSourceBuffer( | |
getVideoFormat().mime, | |
) | |
mediaSource.duration = duration | |
fetchAllSegments(sourceBuffer, segments) | |
}) | |
const video = document.getElementById('video') | |
video.src = URL.createObjectURL(mediaSource) | |
}) | |
</script> | |
</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
#!/bin/sh -e | |
# HOWTO | |
# | |
# 1. Make sure you have an mp4 file and change dir: | |
# mv ~/Downloads/Video2012.mp4 ~/Source/videorepo/public/video/2012.mp4 | |
# cd ~/Source/videorepo/public/video | |
# | |
# 2. Run the script: | |
# ./process.sh 2012.mp4 | |
# | |
SOURCE_FILE=$1 | |
BASENAME="${1%.*}" | |
TARGET_DIR="./$BASENAME" | |
SEGMENT_TIME=10 | |
if [ -z "$SOURCE_FILE" ]; then | |
echo "Source file not specified!" | |
exit 1 | |
fi | |
if [ ! -f "$SOURCE_FILE" ]; then | |
echo "Source file not found!" | |
exit 1 | |
fi | |
if [ -d "$TARGET_DIR" ]; then | |
echo "Directory $TARGET_DIR already exists!" | |
exit 1 | |
fi | |
mkdir -p "$TARGET_DIR" | |
# Create thumbmail: 2012/thumbnail.jpg | |
ffmpeg -i "$SOURCE_FILE" -ss 00:00:00.000 -vframes 1 "$TARGET_DIR/thumbnail.jpg" | |
# Convert source file to: [container: MP4, video: AVC/H.264 (Main 3), audio: AAC (Main)] | |
ffmpeg -i "$SOURCE_FILE" -y -strict experimental -acodec aac -ac 2 -ab 160k -vcodec libx264 -pix_fmt yuv420p -preset slow -profile:v baseline -level 30 -maxrate 10000000 -bufsize 10000000 -b 1200k -f mp4 -threads 0 "$TARGET_DIR/$BASENAME.mp4" | |
#ffmpeg -i "$SOURCE_FILE" -c:v libx264 -vprofile main -vlevel 3 -pix_fmt yuv420p -c:a aac -profile main "$TARGET_DIR/$BASENAME.mp4" | |
# Convert source file to: [container: WEBM, video: VP8, audio: OggVorbis] | |
ffmpeg -i "$SOURCE_FILE" -c:v libvpx-vp9 -pix_fmt yuv420p -c:a libopus -crf 30 -pass 1 -an -f webm -y /dev/null | |
ffmpeg -i "$SOURCE_FILE" -c:v libvpx-vp9 -pix_fmt yuv420p -c:a libopus -crf 30 -pass 2 "$TARGET_DIR/$BASENAME.webm" | |
# Split mp4 into segments: 2012/segment0.mp4, 2012/segment1.mp4, ... | |
ffmpeg -i "$TARGET_DIR/$BASENAME.mp4" -c copy -map 0 -segment_time $SEGMENT_TIME -f segment "$TARGET_DIR/segment%03d.mp4" | |
# Split webm into segments: 2012/segment0.webm, 2012/segment1.webm, ... | |
ffmpeg -i "$TARGET_DIR/$BASENAME.webm" -c copy -map 0 -segment_time $SEGMENT_TIME -f segment "$TARGET_DIR/segment%03d.webm" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment