Aset video mentah sering kali berukuran gigabyte dengan codec kompleks seperti ProRes atau H.265 intra-only, membuat web player di antarmuka kolaborasi review video macet. Menyelesaikan masalah ini membutuhkan otomatisasi processing di edge atau pipeline CI/CD sebelum file didistribusikan ke object storage. Melalui workflow otomatis, video mentah diolah menjadi proxy video H.264 resolusi rendah, thumbnail berkala diekstraksi, dan manifest metadata dibuat secara terstruktur.

Arsitektur Pemrosesan Media di Runner CI

Menjalankan pemrosesan video pada runner CI seperti GitHub Actions membutuhkan efisiensi resource. Compute runner memiliki batas waktu dan disk terbatas. Pendekatan modular memisahkan validasi metadata, encoding proxy, dan ekstraksi preview frame ke dalam tahap-tahap terukur.

Diagram alir pipeline:

  1. Ekstraksi dan validasi metadata via ffprobe (durasi, codec, resolusi).
  2. Pengecekan batas durasi maksimal untuk mencegah runner timeout.
  3. Transcoding proxy resolusi 720p 8-bit H.264 dengan audio AAC.
  4. Ekstraksi thumbnail berkala setiap N detik dengan penamaan berbasis interval timestamp.
  5. Pengemasan artefak (JSON metadata + media proxy + thumbnail) untuk konsumsi frontend review.

Validasi Metadata dan Batasan Durasi Menggunakan FFprobe

Langkah awal adalah memastikan file media valid dan berdurasi wajar sebelum mengalokasikan CPU untuk transcoding. Skrip berikut membaca properti video dan menghentikan pipeline jika durasi melewati batas yang ditentukan (misalnya 600 detik).

#!/usr/bin/env bash
set -euo pipefail

INPUT_FILE="${1:-}"
OUTPUT_META="${2:-metadata.json}"
MAX_DURATION_SECONDS=600

if [[ -z "$INPUT_FILE" || ! -f "$INPUT_FILE" ]]; then
  echo "Error: File input tidak ditemukan: $INPUT_FILE" >&2
  exit 1
fi

# Ekstraksi metadata teknis menggunakan ffprobe
ffprobe -v quiet -print_format json -show_format -show_streams "$INPUT_FILE" > "$OUTPUT_META"

# Ambil durasi stream video utama
DURATION=$(jq -r '.streams[] | select(.codec_type=="video") | .duration // .tags.DURATION // empty' "$OUTPUT_META" | head -n 1)

# Fallback ke durasi container jika stream duration null
if [[ -z "$DURATION" || "$DURATION" == "null" ]]; then
  DURATION=$(jq -r '.format.duration' "$OUTPUT_META")
fi

DURATION_INT=${DURATION%.*}

if (( DURATION_INT > MAX_DURATION_SECONDS )); then
  echo "Error: Durasi video (${DURATION_INT}s) melebihi batas (${MAX_DURATION_SECONDS}s)." >&2
  exit 1
fi

echo "Validasi sukses: Durasi video ${DURATION_INT} detik."

Transcoding Proxy dan Ekstraksi Thumbnail FFmpeg

Frontend collaborative review membutuhkan scrub bar yang responsif dan playback cepat di browser standar. Preset veryfast dengan Constant Rate Factor (CRF) 23 menyeimbangkan throughput CPU CI dan kompresi visual.

#!/usr/bin/env bash
set -euo pipefail

INPUT_FILE="${1:-}"
OUTPUT_DIR="${2:-./dist}"

mkdir -p "$OUTPUT_DIR/thumbnails"

PROXY_OUTPUT="$OUTPUT_DIR/proxy_720p.mp4"

# 1. Transcode proxy: 720p, H.264, AAC, faststart untuk progressive download
ffmpeg -y -i "$INPUT_FILE" \
  -vf "scale=-2:720" \
  -c:v libx264 -preset veryfast -crf 23 -pix_fmt yuv420p \
  -c:a aac -b:a 128k \
  -movflags +faststart \
  "$PROXY_OUTPUT"

# 2. Generate frame thumbnails setiap 5 detik
ffmpeg -y -i "$INPUT_FILE" \
  -vf "fps=1/5,scale=320:-1" \
  -q:v 4 \
  "$OUTPUT_DIR/thumbnails/thumb_%04d.jpg"

# 3. Bentuk manifest indeks thumbnail untuk scrub bar frontend
TOTAL_FRAMES=$(find "$OUTPUT_DIR/thumbnails" -name "thumb_*.jpg" | wc -l | tr -d ' ')
jq -n \
  --arg total "$TOTAL_FRAMES" \
  --arg interval "5" \
  --arg proxy "proxy_720p.mp4" \
  '{proxy_file: $proxy, interval_seconds: ($interval | tonumber), frame_count: ($total | tonumber)}' \
  > "$OUTPUT_DIR/manifest.json"

Implementasi Pipeline CI/CD di GitHub Actions

Konfigurasi berikut memanfaatkan caching direktori binary dan artefak sementara agar runner tidak mengunduh ulang dependensi berat.

name: Media Review Processing

on:
  push:
    paths:
      - 'assets/**.mp4'
      - 'assets/**.mov'

jobs:
  transcode:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Cache FFmpeg Binary
        id: cache-ffmpeg
        uses: actions/cache@v4
        with:
          path: /usr/local/bin/ffmpeg
          key: ffmpeg-static-7.0

      - name: Setup FFmpeg Static Build
        if: steps.cache-ffmpeg.outputs.cache-hit != 'true'
        run: |
          curl -sLO https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
          tar -xf ffmpeg-release-amd64-static.tar.xz --wildcards '*/ffmpeg' '*/ffprobe' --strip-components=1
          sudo mv ffmpeg ffprobe /usr/local/bin/
          rm ffmpeg-release-amd64-static.tar.xz

      - name: Process Media Assets
        run: |
          chmod +x ./scripts/validate.sh ./scripts/transcode.sh
          ./scripts/validate.sh assets/input.mp4 dist/metadata.json
          ./scripts/transcode.sh assets/input.mp4 dist

      - name: Upload Review Artifacts
        uses: actions/upload-artifact@v4
        with:
          name: review-package
          path: dist/
          retention-days: 7

Pencegahan Bottleneck dan Error Handling

  • Pixel Format Incompatibility: Kamera bioskop sering merekam dalam format 10-bit 4:2:2. Tambahkan parameter -pix_fmt yuv420p secara eksplisit agar video bisa diputar di web browser berbasis Chromium dan WebKit.
  • Moov Atom Placement: Flag -movflags +faststart memindahkan moov atom (index header) ke awal file MP4. Tanpa flag ini, browser harus mengunduh keseluruhan file proxy sebelum frame pertama dapat dirender.
  • OOM pada Runner: Penggunaan preset encoding yang terlalu lambat (seperti veryslow) memakan memori berlebih dan CPU timeout. Gunakan preset veryfast atau faster untuk proxy review karena prioritas utama adalah turn-around time, bukan rasio kompresi optimal file arsip.