Skip to content

Instantly share code, notes, and snippets.

@branneman
Created April 13, 2024 13:22
Show Gist options
  • Save branneman/d32c77434462e9fbf222f72fa14c4856 to your computer and use it in GitHub Desktop.
Save branneman/d32c77434462e9fbf222f72fa14c4856 to your computer and use it in GitHub Desktop.
HTML5 Video: MediaSource, SourceBuffer, video segments, etc.
<!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>
#!/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