Add support for merging video and chat with configurable layout options
This commit is contained in:
parent
e078cada3b
commit
832bf4cf36
6 changed files with 199 additions and 10 deletions
|
|
@ -167,5 +167,96 @@ class StreamProcessor:
|
|||
# Default software encoding
|
||||
result = f'-c:v libx264 -preset veryfast -crf 18 -pix_fmt yuv420p "{{save_path}}"'
|
||||
|
||||
print(f'{Fore.CYAN}DEBUG - Generated output_args: {result}{Style.RESET_ALL}')
|
||||
return result
|
||||
|
||||
def merge_video_and_chat(self, video_path: str, chat_path: str, output_path: str, layout: str = 'side-by-side') -> bool:
|
||||
"""
|
||||
Merge video and chat into a single file.
|
||||
|
||||
Args:
|
||||
video_path: Path to the main video file
|
||||
chat_path: Path to the chat video file
|
||||
output_path: Path for the merged output file
|
||||
layout: Merge layout - 'side-by-side' or 'overlay' (default: 'side-by-side')
|
||||
|
||||
Returns:
|
||||
bool: True if merge succeeded, False otherwise
|
||||
"""
|
||||
if not os.path.exists(video_path):
|
||||
print(f'{Fore.RED}✗ Video file not found: {video_path}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
if not os.path.exists(chat_path):
|
||||
print(f'{Fore.RED}✗ Chat file not found: {chat_path}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
print(f'{Fore.YELLOW}Merging video and chat ({layout})...{Style.RESET_ALL}')
|
||||
|
||||
try:
|
||||
if layout == 'overlay':
|
||||
# Overlay chat on top of video (right side)
|
||||
filter_complex = (
|
||||
'[0:v]scale=-2:1080[main];'
|
||||
'[1:v]scale=500:1080[chat];'
|
||||
'[main][chat]overlay=main_w-overlay_w:0[outv]'
|
||||
)
|
||||
else:
|
||||
# Side-by-side layout (default)
|
||||
filter_complex = (
|
||||
'[0:v]scale=-2:1080[main];'
|
||||
'[1:v]scale=500:1080[chat];'
|
||||
'[main][chat]hstack=inputs=2[outv]'
|
||||
)
|
||||
|
||||
cmd = [
|
||||
self.ffmpeg_path,
|
||||
'-y',
|
||||
'-i', video_path,
|
||||
'-i', chat_path,
|
||||
'-filter_complex', filter_complex,
|
||||
'-map', '[outv]',
|
||||
'-map', '0:a?', # Use audio from main video if available
|
||||
]
|
||||
|
||||
# Add hardware acceleration for encoding if available
|
||||
if self.hwaccel_type and self.hwaccel_type != 'none':
|
||||
encoder = get_hwaccel_encoder(self.hwaccel_type)
|
||||
cmd.extend(['-c:v', encoder])
|
||||
|
||||
if 'nvenc' in encoder:
|
||||
cmd.extend(['-preset', 'p4', '-cq', '18'])
|
||||
elif 'qsv' in encoder:
|
||||
cmd.extend(['-global_quality', '18'])
|
||||
elif 'amf' in encoder:
|
||||
cmd.extend(['-qp_i', '18'])
|
||||
else:
|
||||
cmd.extend(['-preset', 'medium', '-crf', '18'])
|
||||
else:
|
||||
cmd.extend(['-c:v', 'libx264', '-preset', 'medium', '-crf', '18'])
|
||||
|
||||
# Audio codec
|
||||
cmd.extend(['-c:a', 'copy']) # Copy audio without re-encoding
|
||||
|
||||
# Pixel format and faststart
|
||||
cmd.extend(['-pix_fmt', 'yuv420p'])
|
||||
if self.ffmpeg_faststart and output_path.endswith('.mp4'):
|
||||
cmd.extend(['-movflags', '+faststart'])
|
||||
|
||||
cmd.append(output_path)
|
||||
|
||||
# Run FFmpeg
|
||||
if self.ffmpeg_progress:
|
||||
result = subprocess.call(cmd)
|
||||
else:
|
||||
result = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
|
||||
|
||||
if result == 0:
|
||||
print(f'{Fore.GREEN}✓ Video and chat merged successfully{Style.RESET_ALL}')
|
||||
return True
|
||||
else:
|
||||
print(f'{Fore.RED}✗ Merge failed with exit code: {result}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f'{Fore.RED}✗ Merge failed: {str(e)}{Style.RESET_ALL}')
|
||||
return False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue