| import subprocess |
| from functools import lru_cache |
| from typing import Dict, List |
|
|
| from facefusion import ffprobe_builder |
| from facefusion.types import AudioMetadata, Buffer, Command, Fps, VideoMetadata |
|
|
|
|
| def run_ffprobe(commands : List[Command]) -> subprocess.Popen[Buffer]: |
| commands = ffprobe_builder.run(commands) |
| return subprocess.Popen(commands, stderr = subprocess.PIPE, stdout = subprocess.PIPE) |
|
|
|
|
| def parse_entries(output : Buffer) -> Dict[str, str]: |
| media_entries = {} |
|
|
| if output: |
| lines = output.decode().strip().splitlines() |
|
|
| for line in lines: |
| if '=' in line: |
| key, value = line.split('=', 1) |
| media_entries[key] = value |
|
|
| return media_entries |
|
|
|
|
| def probe_audio_entries(audio_path : str, entries : List[str]) -> Dict[str, str]: |
| commands = ffprobe_builder.chain( |
| ffprobe_builder.select_stream('a:0'), |
| ffprobe_builder.show_stream_entries(entries), |
| ffprobe_builder.format_to_key_value(), |
| ffprobe_builder.set_input(audio_path) |
| ) |
|
|
| output, _ = run_ffprobe(commands).communicate() |
|
|
| return parse_entries(output) |
|
|
|
|
| def probe_video_entries(video_path : str, entries : List[str]) -> Dict[str, str]: |
| commands = ffprobe_builder.chain( |
| ffprobe_builder.select_stream('v:0'), |
| ffprobe_builder.show_stream_entries(entries), |
| ffprobe_builder.format_to_key_value(), |
| ffprobe_builder.set_input(video_path) |
| ) |
|
|
| output, _ = run_ffprobe(commands).communicate() |
|
|
| return parse_entries(output) |
|
|
|
|
| def probe_format_entries(media_path : str, entries : List[str]) -> Dict[str, str]: |
| commands = ffprobe_builder.chain( |
| ffprobe_builder.show_format_entries(entries), |
| ffprobe_builder.format_to_key_value(), |
| ffprobe_builder.set_input(media_path) |
| ) |
|
|
| output, _ = run_ffprobe(commands).communicate() |
|
|
| return parse_entries(output) |
|
|
|
|
| @lru_cache(maxsize = 128) |
| def extract_static_audio_metadata(audio_path : str) -> AudioMetadata: |
| return extract_audio_metadata(audio_path) |
|
|
|
|
| def extract_audio_metadata(audio_path : str) -> AudioMetadata: |
| audio_entries = probe_audio_entries(audio_path, [ 'sample_rate', 'channels' ]) |
| format_entries = probe_format_entries(audio_path, [ 'duration', 'bit_rate' ]) |
|
|
| duration = float(format_entries.get('duration')) |
| sample_rate = int(audio_entries.get('sample_rate')) |
| frame_total = round(duration * sample_rate) |
| channel_total = int(audio_entries.get('channels')) |
| bit_rate = int(format_entries.get('bit_rate')) |
|
|
| audio_metadata : AudioMetadata =\ |
| { |
| 'duration' : duration, |
| 'frame_total' : frame_total, |
| 'channel_total' : channel_total, |
| 'sample_rate' : sample_rate, |
| 'bit_rate' : bit_rate |
| } |
|
|
| return audio_metadata |
|
|
|
|
| @lru_cache(maxsize = 128) |
| def extract_static_video_metadata(video_path : str) -> VideoMetadata: |
| return extract_video_metadata(video_path) |
|
|
|
|
| def extract_video_metadata(video_path : str) -> VideoMetadata: |
| video_entries = probe_video_entries(video_path, [ 'width', 'height', 'r_frame_rate', 'color_transfer' ]) |
| format_entries = probe_format_entries(video_path, [ 'duration', 'bit_rate' ]) |
|
|
| duration = float(format_entries.get('duration')) |
| fps = extract_video_fps(video_entries.get('r_frame_rate')) |
| frame_total = round(duration * fps) |
| width = int(video_entries.get('width')) |
| height = int(video_entries.get('height')) |
| bit_rate = int(format_entries.get('bit_rate')) |
| color_transfer = video_entries.get('color_transfer', 'unknown') |
|
|
| video_metadata : VideoMetadata =\ |
| { |
| 'duration' : duration, |
| 'frame_total' : frame_total, |
| 'fps' : fps, |
| 'resolution' : (width, height), |
| 'bit_rate' : bit_rate, |
| 'color_transfer' : color_transfer |
| } |
|
|
| return video_metadata |
|
|
|
|
| def extract_video_fps(frame_rate : str) -> Fps: |
| if frame_rate and '/' in frame_rate: |
| numerator, denominator = frame_rate.split('/') |
|
|
| if int(numerator) and int(denominator): |
| return int(numerator) / int(denominator) |
|
|
| return 0.0 |
|
|