If you need to repeat this workflow for dozens of MP4s, a Bash/Python pipeline can save hours.
#!/usr/bin/env bash
set -euo pipefail
VIDEO=$1
BASE=$(basename "$VIDEO" .mp4)
# 1. Hashes
sha256sum "$VIDEO" > "$BASE_sha256.txt"
# 2. Metadata
ffprobe -v quiet -print_format json -show_format -show_streams "$VIDEO" > "$BASE_ffprobe.json"
exiftool -a -u -g1 "$VIDEO" > "$BASE_exif.txt"
# 3. Contact sheet
ffmpeg -i "$VIDEO" -vf "thumbnail,scale=320:-1,tile=5x5" "$BASE_contact.jpg"
# 4. Frame hash detection (Python script)
python3 detect_edits.py "$VIDEO" "$BASE_edits.txt"
# 5. Audio transcript (Whisper)
whisper "$VIDEO" --model base --output_dir . --output_format txt
echo "Analysis of $VIDEO complete. Files prefixed with $BASE_"
Combine with a cron job or a simple for f in *.mp4; do ./analyze.sh "$f"; done to batch‑process a folder.
| Technique | Tools | What You Get |
|-----------|-------|--------------|
| Frame‑by‑frame extraction | ffmpeg -i JUQ-516.mp4 -vf "select=eq(pict_type\,I)" -vsync vfr keyframes_%04d.jpg | All I‑frames (keyframes) as JPEGs for quick visual inspection. |
| Full frame dump | ffmpeg -i JUQ-516.mp4 frame_%05d.png | Every frame as PNG (useful for detecting subtle tampering). |
| Scene change detection | ffprobe -show_frames -show_entries frame=pict_type -select_streams v -i JUQ-516.mp4 | List of frame types; spikes in I‑frames can hint at cuts. |
| Audio waveform / spectrogram | Audacity (import MP4) or sox (sox JUQ-516.mp4 -n spectrogram) | Visual view of speech, background noises, or hidden audio. |
| Speech‑to‑text | Google Cloud Speech‑to‑Text, Whisper (whisper JUQ-516.mp4 --model base) | Text transcription for keyword search. |
| Object / face detection | OpenCV, YOLOv8, Amazon Rekognition | Automated tagging of people, vehicles, logos, etc. |
Compression Artifacts
Frame‑Level Hashing
import cv2, imagehash, PIL.Image
cap = cv2.VideoCapture('JUQ-516.mp4')
prev_hash = None
frame_no = 0
while True:
ret, frame = cap.read()
if not ret: break
pil = PIL.Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
cur_hash = imagehash.phash(pil)
if prev_hash and cur_hash - prev_hash > 10: # threshold
print(f"Possible edit around frame frame_no")
prev_hash = cur_hash
frame_no += 1
Audio Anomalies
File‑Structure Checks
[ ] Verify file hash (sha256)
[ ] Create a read‑only backup
[ ] Run ffprobe → metadata.json
[ ] Run exiftool → exif.txt
[ ] Generate contact sheet → contact.jpg
[ ] Extract keyframes → keyframes_*.jpg
[ ] Run frame‑hash script → edits.txt
[ ] Transcribe audio (optional) → transcript.txt
[ ] Perform reverse‑image search on a few frames
[ ] Document every step in a PDF report
[ ] Store final hashes of all generated artefacts
| Tool | Platform | Command / Steps |
|------|----------|-----------------|
| ffprobe (part of FFmpeg) | Windows / macOS / Linux | ffprobe -v error -show_format -show_streams JUQ-516.mp4 |
| MediaInfo | GUI (cross‑platform) | Open the file → “View → Text” for a concise summary. |
| ExifTool | All platforms | exiftool JUQ-516.mp4 (shows metadata, creation date, encoder, etc.) |
| File (Unix) | Linux/macOS | file JUQ-516.mp4 (quick MIME‑type check). |
Typical output you’ll see:
Format: MPEG‑4
Duration: 00:02:35.12
Overall bit rate: 3.2 Mb/s
Video: H.264 / AVC, 1920×1080, 30 fps, 2.5 Mb/s
Audio: AAC LC, 48 kHz, stereo, 128 kb/s
| Goal | Typical Questions | |------|-------------------| | Authenticity verification | Was the footage edited, spliced, or otherwise manipulated? | | Source attribution | Who recorded it, with what device, and when? | | Content extraction | What visual/audio events occur, and can they be indexed? | | Legal or compliance review | Does the material contain prohibited or protected content? | | Technical troubleshooting | Why does the file fail to play on certain players? | JUQ-516.mp4
Having a systematic approach ensures you capture every relevant datum and avoid missing subtle clues.
AtomicParsley JUQ-516.mp4 -t
# or
MP4Box -info JUQ-516.mp4
These tools reveal track IDs, fragmentation, and moov atom placement (important for streaming vs. progressive download).