Encoding: H.265 final montages, slow preset, sunrise retention
- sky-cam.conf: add ENCODE_PRESET=slow (applied to all encodes); split CRF_MONTAGE (intermediates, H.264) from CRF_MONTAGE_FINAL=22 (archive montage, H.265); add SUNRISE_RETENTION_DAYS=10; improve CAPTURE_INTERVAL comment with storage/density table - montage-mvt.sh: steps 1+2 use preset; step 3 final switches to libx265 + CRF_MONTAGE_FINAL + -tag:v hvc1 for broad compatibility - 4-seasons.sh: both encode steps use ENCODE_PRESET - daily_sunrise_video.sh: all encode steps use ENCODE_PRESET; rolling retention deletes sunrise videos older than SUNRISE_RETENTION_DAYS https://claude.ai/code/session_01C4jbd3waXG3eKZYbGUjLUQ
This commit is contained in:
+2
-2
@@ -88,7 +88,7 @@ done > "$temp_file"
|
||||
echo "Step 1/2: encoding raw video..."
|
||||
ffmpeg -loglevel warning \
|
||||
-f concat -safe 0 -i "$temp_file" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SEASONS_RAW" -vsync 2 -an \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SEASONS_RAW" -vsync 2 -an \
|
||||
-y "$temp_video"
|
||||
|
||||
raw_duration=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$temp_video")
|
||||
@@ -102,7 +102,7 @@ final_file="$output_dir/${yesterday}_Mvt${MVT_NUM}-Day${DAY_OF_MVT}of${DAYS_IN_M
|
||||
if ! ffmpeg -loglevel warning \
|
||||
-i "$temp_video" \
|
||||
-vf "setpts=PTS/$speed_factor" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SEASONS_FINAL" \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SEASONS_FINAL" \
|
||||
-t "$target_per_clip" -an \
|
||||
-y "$final_file"; then
|
||||
rm -f "$final_file"
|
||||
|
||||
+13
-3
@@ -110,7 +110,7 @@ raw_video=$(mktemp --suffix=.mp4)
|
||||
echo "Step 1/3: encoding raw video..."
|
||||
if ! ffmpeg -loglevel warning \
|
||||
-f concat -safe 0 -i "$temp_list" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" -vsync 2 -an \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -vsync 2 -an \
|
||||
-y "$raw_video"; then
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 1/3 (encode) $current_date" \
|
||||
"ffmpeg raw encode from images failed — check: journalctl -u sky-cam-sunrise.service" || true
|
||||
@@ -128,7 +128,7 @@ echo "Step 2/3: speed-adjust → $sped_video"
|
||||
if ! ffmpeg -loglevel warning \
|
||||
-i "$raw_video" \
|
||||
-vf "setpts=PTS/${speed_factor}" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" -an \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" -an \
|
||||
-y "$sped_video"; then
|
||||
rm -f "$sped_video"
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: sunrise step 2/3 (speed) $current_date" \
|
||||
@@ -223,7 +223,7 @@ if [ -n "$DT" ]; then
|
||||
if ffmpeg -loglevel warning \
|
||||
-i "$work_video" \
|
||||
-vf "${DT}" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_SUNRISE" \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_SUNRISE" \
|
||||
"${audio_out_flags[@]}" \
|
||||
-y "$final_video"; then
|
||||
step3b_ok=true
|
||||
@@ -253,4 +253,14 @@ if $step3b_ok; then
|
||||
[ -f "$cam_audio" ] && rm -f "$cam_audio"
|
||||
"$SCRIPT_DIR/notify.sh" "Sunrise ready: $current_date" \
|
||||
"$(basename "$final_video") — ${actual_dur}s, sunrise at ${SR_TIME}" || true
|
||||
|
||||
# ── Rolling retention: delete sunrise videos older than SUNRISE_RETENTION_DAYS ─
|
||||
retain="${SUNRISE_RETENTION_DAYS:-10}"
|
||||
if [ "$retain" -gt 0 ]; then
|
||||
deleted_old=$(find "$MOVIES_DIR/$CAM_NAME" \
|
||||
-path "*/sunrise-only/*-daily-sunrise.mp4" \
|
||||
-mtime +"$retain" -print -delete 2>/dev/null | wc -l)
|
||||
[ "$deleted_old" -gt 0 ] && \
|
||||
echo "Retention: deleted $deleted_old sunrise video(s) older than ${retain} days"
|
||||
fi
|
||||
fi
|
||||
|
||||
+4
-3
@@ -133,7 +133,7 @@ ffmpeg -loglevel warning \
|
||||
-f concat -safe 0 -i "$concat_list" \
|
||||
-vf "scale=${VIDEO_W}:${VIDEO_H}:force_original_aspect_ratio=decrease,\
|
||||
pad=${VIDEO_W}:${VIDEO_H}:(ow-iw)/2:(oh-ih)/2,setsar=1" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE" -an \
|
||||
-y "$temp_concat"
|
||||
|
||||
# ── Step 2: Speed-adjust — saved permanently so music/overlay failure is recoverable ─
|
||||
@@ -146,7 +146,7 @@ sped_file="$output_dir/${MVT_START}_${SEASON}_Mvt${MVT_NUM}-Sped.mp4"
|
||||
if ! ffmpeg -loglevel warning \
|
||||
-i "$temp_concat" \
|
||||
-vf "setpts=PTS/$speed_factor" \
|
||||
-c:v libx264 -pix_fmt yuv420p -crf "$CRF_MONTAGE" -an \
|
||||
-c:v libx264 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE" -an \
|
||||
-t "$music_duration" -y "$sped_file"; then
|
||||
rm -f "$sped_file"
|
||||
"$SCRIPT_DIR/notify.sh" "FAILED: montage step 2/3 (speed) $SEASON Mvt$MVT_NUM ($ASTRO_YEAR)" \
|
||||
@@ -198,7 +198,8 @@ if ffmpeg -loglevel warning \
|
||||
"[0:v]fade=t=in:st=0:d=${FADE_DUR},fade=t=out:st=${fade_out_start}:d=${FADE_DUR},${DT}[vout];\
|
||||
[1:a]afade=t=in:st=0:d=${FADE_DUR},afade=t=out:st=${fade_out_start}:d=${FADE_DUR}[aout]" \
|
||||
-map "[vout]" -map "[aout]" \
|
||||
-c:v libx264 -pix_fmt yuv420p -c:a aac -b:a "$AUDIO_BITRATE" \
|
||||
-c:v libx265 -pix_fmt yuv420p -preset "$ENCODE_PRESET" -crf "$CRF_MONTAGE_FINAL" \
|
||||
-tag:v hvc1 -c:a aac -b:a "$AUDIO_BITRATE" \
|
||||
-t "$music_duration" -y "$output_file"; then
|
||||
rm -f "$sped_file"
|
||||
echo "Done: $output_file"
|
||||
|
||||
+23
-12
@@ -217,20 +217,28 @@ SUNRISE_TARGET_SECS=10
|
||||
SUNRISE_OVERLAY_ENABLED=true
|
||||
# Opacity of the sunrise-time text (0.0 = invisible, 1.0 = fully opaque).
|
||||
SUNRISE_OVERLAY_OPACITY=0.45
|
||||
# Delete daily sunrise videos older than this many days (rolling window).
|
||||
# Set 0 to keep forever.
|
||||
SUNRISE_RETENTION_DAYS=10
|
||||
|
||||
# ── Video encoding quality ────────────────────────────────────────────────────
|
||||
# FFmpeg CRF: lower = higher quality / larger files. Typical range 18–30.
|
||||
# 18 ≈ visually lossless 23 = ffmpeg default 28 = smaller/lower
|
||||
# Different scripts use different defaults by design — intermediate/draft
|
||||
# encodings use a higher CRF (smaller files, quality less critical), while
|
||||
# final "polished" outputs use a lower CRF for better quality.
|
||||
# Each is independent — changing one does NOT change the others.
|
||||
# H.265 (libx265) CRF is not directly comparable — H.265 CRF 22 ≈ H.264 CRF 26
|
||||
# in visual quality but ~40-50% smaller file. Final montages use H.265;
|
||||
# intermediates (deleted after use) stay H.264.
|
||||
#
|
||||
CRF_SUNRISE=28 # daily_sunrise_video.sh — daily Mattermost upload
|
||||
CRF_SEASONS_RAW=28 # 4-seasons.sh — raw concat before speed-adjust
|
||||
CRF_SEASONS_FINAL=26 # 4-seasons.sh — final daily clip (goes into montage)
|
||||
CRF_MONTAGE=26 # montage-mvt.sh — concat and speed-adjust and final
|
||||
AUDIO_BITRATE=192k # montage-mvt.sh — music track on movement montages
|
||||
# ENCODE_PRESET: slow = better compression, same CRF quality, ~2× encode time.
|
||||
# Use 'medium' on slower machines, 'slow' or 'slower' on fast ones.
|
||||
# All encodes run at night so 'slow' is the right default.
|
||||
#
|
||||
CRF_SUNRISE=28 # daily_sunrise_video.sh — daily Mattermost upload (H.264)
|
||||
CRF_SEASONS_RAW=28 # 4-seasons.sh — raw concat before speed-adjust (H.264)
|
||||
CRF_SEASONS_FINAL=26 # 4-seasons.sh — final daily clip (goes into montage) (H.264)
|
||||
CRF_MONTAGE=28 # montage-mvt.sh — intermediate concat + sped steps (H.264)
|
||||
CRF_MONTAGE_FINAL=22 # montage-mvt.sh — final archive montage (H.265)
|
||||
ENCODE_PRESET=slow # ffmpeg preset for all encodes (slow/medium/fast)
|
||||
AUDIO_BITRATE=192k # montage-mvt.sh — music track on movement montages
|
||||
|
||||
# ── Montage attribution overlay ───────────────────────────────────────────────
|
||||
# A translucent bar across the top of each movement montage, naming the
|
||||
@@ -254,12 +262,15 @@ MONTAGE_ATTR_FADE=1 # attribution fade-in and fade-out length (seconds)
|
||||
# CAM_RTSP_<cam> goes in .env (see bottom of this file), not here.
|
||||
|
||||
# Seconds between captured frames (applies to all cameras).
|
||||
# 10 = one frame every 10 s → 8640 frames/day → good timelapse density.
|
||||
# Lower = smoother timelapse + larger storage footprint:
|
||||
# 10 s → 8,640 frames/day ~2.5 GB/day (default, good balance)
|
||||
# 5 s → 17,280 frames/day ~5 GB/day (smoother motion)
|
||||
# 2 s → 43,200 frames/day ~12 GB/day (very smooth, high storage)
|
||||
# CAPTURE_STALE_SECS should always be at least 2× CAPTURE_INTERVAL.
|
||||
CAPTURE_INTERVAL=10
|
||||
|
||||
# Seconds without a new frame before the watchdog sends a stall notification.
|
||||
# Should be at least 2× CAPTURE_INTERVAL. A recovery notification fires
|
||||
# automatically when frames start arriving again.
|
||||
# A recovery notification fires automatically when capture resumes.
|
||||
CAPTURE_STALE_SECS=30
|
||||
|
||||
# ── Sunrise audio capture ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user