Skip to content

Instantly share code, notes, and snippets.

@mrbrainz
Created August 15, 2025 16:19
Show Gist options
  • Select an option

  • Save mrbrainz/2e960538af8ef0e12d46a299068149b7 to your computer and use it in GitHub Desktop.

Select an option

Save mrbrainz/2e960538af8ef0e12d46a299068149b7 to your computer and use it in GitHub Desktop.
Shell script to stitch Samsung Gear 360 videos to make them viewable in a player like VLC
#!/bin/bash
# This script processes a folder of MP4 videos recorded on a Samsung Gear 360 so they can be watched in a supported
# 360 video player or uploaded to a site like Facebook
#
# Usage: ./360.sh <input_folder> <output_folder>
#
# Obvs. don't forget to make the script executable with chmod
#
# Requires FFMPEG and EXIFtool to be installed. My recommendation would be to install these using Brew.
# Exit immediately on errors
set -e
# Check arguments
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input_folder> <output_folder>"
exit 1
fi
INPUT_DIR="$1"
OUTPUT_DIR="$2"
# pano.xml content inlined here
PANO_XML_CONTENT='<?xml version="1.0"?>
<rdf:SphericalVideo xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:GSpherical="http://ns.google.com/videos/1.0/spherical/">
<GSpherical:Spherical>true</GSpherical:Spherical>
<GSpherical:Stitched>true</GSpherical:Stitched>
<GSpherical:ProjectionType>equirectangular</GSpherical:ProjectionType>
</rdf:SphericalVideo>'
# Create output directory if it doesn’t exist
mkdir -p "$OUTPUT_DIR"
# Find mp4/MP4 files
shopt -s nullglob nocaseglob
files=("$INPUT_DIR"/*.mp4)
if [ ${#files[@]} -eq 0 ]; then
echo "No MP4 files found in $INPUT_DIR"
exit 0
fi
# Loop over MP4 files
for infile in "${files[@]}"; do
filename=$(basename "$infile")
name="${filename%.*}"
outfile="$OUTPUT_DIR/${name}_fe.mp4" # force lowercase .mp4
echo "Processing: $filename"
# Run ffmpeg quietly (warnings & errors only)
ffmpeg -hide_banner -loglevel warning -y -i "$infile" \
-vf v360=dfisheye:e:yaw=0:ih_fov=192:iv_fov=192 \
-c:v h264_videotoolbox -b:v 40000k -bufsize 5000k \
-c:a copy "$outfile"
# Create a temporary pano.xml file
tmp_pano=$(mktemp)
echo "$PANO_XML_CONTENT" > "$tmp_pano"
# Run exiftool using temp pano.xml
exiftool -tagsfromfile "$tmp_pano" -api largefilesupport=1 -all:all -overwrite_original "$outfile" >/dev/null
# Remove temp pano.xml
rm -f "$tmp_pano"
# Preserve original file timestamps
touch -r "$infile" "$outfile"
echo "Done: $outfile"
done
echo "All files processed successfully."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment