Video_editing / video_utils.py
suraj-ml-projects's picture
Upload 5 files
4383d5e verified
Raw History Blame Contribute Delete
3.93 kB
"""
utils/video_utils.py
────────────────────
Merge dubbed Hindi audio back into the original video.
Strategy:
1. Use ffmpeg to mix dubbed speech + original BGM (at reduced volume)
2. Replace the video's audio track with the mixed result
3. Copy video codec (no re-encoding) → fast even on CPU
This avoids MoviePy for the final merge (MoviePy re-encodes, very slow on CPU).
We use raw ffmpeg subprocess calls instead — much faster.
"""
import os
import tempfile
import subprocess
def merge_audio_video(
video_path: str,
dubbed_audio_path: str,
original_audio_path: str,
bgm_volume: float = 0.15,
) -> str:
"""
Create final dubbed video:
- Video stream: copied from original (no re-encode)
- Audio stream: Hindi speech mixed with faint original BGM
Parameters:
video_path – path to original MP4
dubbed_audio_path – path to Hindi dubbed WAV
original_audio_path – path to extracted original audio WAV
bgm_volume – 0.0–1.0, volume of background music
Returns path to output MP4.
"""
output_path = os.path.join(tempfile.gettempdir(), "hindi_dubbed_output.mp4")
# ffmpeg filter graph:
# [0:a] = original audio (background music)
# [1:a] = Hindi dubbed speech
#
# Step 1: lower original audio volume → bgm
# Step 2: mix bgm + dubbed speech together
# Step 3: mux with video stream (copy, no re-encode)
bgm_vol_str = str(round(bgm_volume, 3))
cmd = [
"ffmpeg", "-y",
"-i", video_path, # input 0: original video (video + audio)
"-i", dubbed_audio_path, # input 1: Hindi dubbed audio
"-filter_complex",
(
# Lower original audio volume for BGM
f"[0:a]volume={bgm_vol_str}[bgm];"
# Mix BGM with Hindi speech
"[bgm][1:a]amix=inputs=2:duration=first:dropout_transition=3[mixed]"
),
"-map", "0:v", # video stream from original
"-map", "[mixed]", # audio stream = mixed result
"-c:v", "copy", # copy video codec — NO re-encoding (fast!)
"-c:a", "aac", # encode mixed audio as AAC
"-b:a", "192k", # audio bitrate
"-shortest", # trim to shortest stream
output_path,
]
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if result.returncode != 0:
# Try a simpler fallback: just replace audio entirely (no BGM mix)
output_path = _simple_replace_audio(video_path, dubbed_audio_path)
return output_path
def _simple_replace_audio(video_path: str, audio_path: str) -> str:
"""
Fallback: simply replace video audio with dubbed audio.
Used when the complex filter fails.
"""
output_path = os.path.join(tempfile.gettempdir(), "hindi_dubbed_output_simple.mp4")
cmd = [
"ffmpeg", "-y",
"-i", video_path,
"-i", audio_path,
"-map", "0:v", # take video from original
"-map", "1:a", # take audio from dubbed file
"-c:v", "copy", # copy video stream (fast)
"-c:a", "aac",
"-b:a", "192k",
"-shortest",
output_path,
]
subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
return output_path
def get_video_duration(video_path: str) -> float:
"""Return video duration in seconds using ffprobe."""
cmd = [
"ffprobe", "-v", "error",
"-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1",
video_path,
]
result = subprocess.run(cmd, capture_output=True, text=True)
try:
return float(result.stdout.strip())
except Exception:
return 0.0