import os
import cv2
import numpy as np
import psutil
import time
import argparse
import json
from datetime import datetime
import logging
import signal
import sys
import traceback
import threading
import subprocess
import gc
import queue
import concurrent.futures
import platform
class VideoProcessor:
def __init__(self, config):
self.config = config
self.canceled = False
self.start_time = time.time()
self.frame_counter = 0
self.progress = 0
self.status = "就绪"
self.fps = 0.0
self.mem_usage = 0.0
self.cpu_percent = 0.0
self.system_mem_percent = 0.0
self.system_mem_used = 0.0
self.system_mem_available = 0.0
self.logger = logging.getLogger("VideoProcessor")
self.resources = []
# 检测移动环境并应用优化配置
self.is_mobile = self.detect_mobile_environment()
if self.is_mobile:
self.logger.info("检测到移动环境,启用移动端优化配置")
# 覆盖配置为移动端优化值
self.config.setdefault('output_resolution', [640, 360])
self.config.setdefault('queue_size', 3)
self.config.setdefault('decoding_threads', 2)
self.config.setdefault('use_gpu_processing', False)
self.config.setdefault('enable_memory_monitor', False)
self.config.setdefault('buffer_size', 1)
self.config.setdefault('cuda_streams', 0)
self.config.setdefault('reduce_latency', True)
self.config.setdefault('mobile_optimized', True)
self.config.setdefault('hardware_acceleration', 'mediacodec')
self.config.setdefault('target_fps', 24)
# 移动端特殊优化:防止后台被杀
self.setup_termux_optimizations()
# 多线程队列
self.frame_queue = queue.Queue(maxsize=self.config.get('queue_size', 3))
self.processed_queue = queue.Queue(maxsize=self.config.get('queue_size', 3))
# 注册信号处理
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
def setup_termux_optimizations(self):
"""设置Termux防杀后台优化"""
try:
# 防止后台被杀
self.logger.info("设置防止后台被杀优化")
subprocess.run(["termux-wake-lock"], check=True)
# 设置电池优化白名单
result = subprocess.run(["termux-battery-optimization", "-s"],
capture_output=True, text=True)
if "already" not in result.stdout:
self.logger.info("电池优化白名单设置成功")
else:
self.logger.info("已在电池优化白名单中")
# 设置后台服务保活
subprocess.run(["termux-job-scheduler",
"-p", "1000",
"-s", "python " + " ".join(sys.argv)],
check=True)
except Exception as e:
self.logger.warning(f"Termux优化设置失败: {str(e)}")
print("提示: 请确保已安装Termux:API应用并授予必要权限")
def detect_mobile_environment(self):
"""精准检测移动环境"""
try:
# Android系统标志
if 'ANDROID_ROOT' in os.environ:
return True
# Termux特定路径检测
if os.path.exists("/data/data/com.termux/files/usr"):
return True
# 系统信息检测
uname = os.uname()
system = platform.system().lower()
if 'linux' in system and 'android' in uname.version.lower():
return True
if system == 'darwin' and 'ios' in uname.machine.lower():
return True
return False
except Exception as e:
self.logger.warning(f"移动环境检测失败: {str(e)}")
return False
def signal_handler(self, signum, frame):
"""处理中断信号"""
self.logger.warning(f"接收到中断信号: {signum}, 正在停止...")
self.cancel()
sys.exit(1)
def start_resource_monitor(self, interval=2):
"""启动简化的资源监控"""
if not self.config.get('enable_memory_monitor', False) or self.is_mobile:
return
self.monitor_active = True
self.monitor_thread = threading.Thread(
target=self.monitor_resources,
args=(interval,),
daemon=True
)
self.monitor_thread.start()
self.logger.info("资源监控线程已启动")
def stop_resource_monitor(self):
"""停止资源监控"""
if hasattr(self, 'monitor_thread') and self.monitor_thread and self.monitor_thread.is_alive():
self.monitor_active = False
self.monitor_thread.join(timeout=1.0)
self.logger.info("资源监控线程已停止")
def monitor_resources(self, interval=2):
"""简化的资源监控"""
self.logger.info("资源监控开始")
print("\n资源监控 | 时间戳 | CPU使用率 | 内存使用")
print("-" * 50)
while self.monitor_active:
try:
# CPU监控
cpu_percent = psutil.cpu_percent(interval=None)
# 内存监控
mem = psutil.virtual_memory()
mem_usage = f"{mem.used / (1024**2):.1f}MB/{mem.total / (1024**2):.1f}MB"
timestamp = time.strftime('%H:%M:%S')
print(f"{timestamp} | {cpu_percent:6.1f}% | {mem_usage}")
self.logger.info(f"资源监控 | {timestamp} | CPU: {cpu_percent}% | 内存: {mem_usage}")
time.sleep(interval)
except Exception as e:
self.logger.error(f"资源监控出错: {str(e)}")
time.sleep(5)
def open_video_with_acceleration(self, file_path):
"""移动端优化的视频打开方法"""
# 移动端优先使用MediaCodec
if self.is_mobile:
try:
self.logger.info("尝试使用Android MediaCodec硬件加速")
cap = cv2.VideoCapture(file_path, cv2.CAP_ANDROID)
if cap.isOpened():
# 设置移动端优化参数
cap.set(cv2.CAP_PROP_BUFFERSIZE, self.config.get('buffer_size', 1))
cap.set(cv2.CAP_PROP_FPS, self.config.get('target_fps', 24))
self.logger.info("MediaCodec加速成功")
self.resources.append(cap)
return cap
except Exception as e:
self.logger.warning(f"MediaCodec加速失败: {str(e)}")
# 通用回退方案
self.logger.info("使用软件解码")
cap = cv2.VideoCapture(file_path)
if cap.isOpened():
self.resources.append(cap)
return cap
self.logger.error("无法打开视频文件")
return None
def update_system_stats(self):
"""更新系统资源统计"""
self.cpu_percent = psutil.cpu_percent(interval=0.1)
mem = psutil.virtual_memory()
self.system_mem_percent = mem.percent
self.system_mem_used = mem.used / (1024 ** 2) # MB
self.system_mem_available = mem.available / (1024 ** 2) # MB
def print_progress(self):
"""移动端友好的进度显示"""
if self.is_mobile:
# 简化移动端输出
progress_str = (
f"进度: {self.progress}% | "
f"速度: {self.fps:.1f}fps | "
f"内存: {self.mem_usage:.1f}MB"
)
print(f"\r{progress_str}", end="")
else:
# 桌面端详细输出
elapsed = time.time() - self.start_time
eta = (100 - self.progress) * elapsed / max(1, self.progress) if self.progress > 0 else 0
bar_length = 20
filled_length = int(bar_length * self.progress / 100)
bar = '█' * filled_length + '-' * (bar_length - filled_length)
progress_str = (
f"进度: |{bar}| {self.progress}% "
f"| 速度: {self.fps:.1f}fps "
f"| 用时: {elapsed:.1f}s "
f"| 剩余: {eta:.1f}s "
f"| CPU: {self.cpu_percent:.0f}% "
f"| 内存: {self.mem_usage:.1f}MB"
)
print(f"\r{progress_str}", end="")
self.logger.info(progress_str)
def capture_thread(self, cap, total_frames):
"""视频捕获线程 (生产者)"""
frame_idx = 0
while cap.isOpened() and not self.canceled and frame_idx < total_frames:
ret, frame = cap.read()
if not ret:
break
# 放入队列,非阻塞方式防止死锁
try:
self.frame_queue.put((frame_idx, frame), timeout=0.5)
frame_idx += 1
except queue.Full:
if self.canceled:
break
# 移动端更短的等待
time.sleep(0.001 if self.is_mobile else 0.01)
# 发送结束信号
self.frame_queue.put((None, None))
self.logger.info(f"捕获线程完成,共捕获 {frame_idx} 帧")
def processing_thread(self, output_resolution):
"""移动端优化的处理线程"""
output_width, output_height = output_resolution
while not self.canceled:
try:
# 获取帧,带超时防止死锁
frame_idx, frame = self.frame_queue.get(timeout=1.0)
# 结束信号
if frame_idx is None:
self.processed_queue.put((None, None))
self.frame_queue.task_done()
break
# 移动端专用轻量处理
if self.is_mobile:
# 内存压力检测 - 跳过处理直接写入原始帧
mem = psutil.virtual_memory()
if mem.percent > 85:
self.logger.warning("内存压力过高,跳过帧处理")
processed_frame = frame
else:
# 使用快速缩放算法
processed_frame = cv2.resize(
frame,
(output_width, output_height),
interpolation=cv2.INTER_LINEAR
)
# 可选: 轻量级图像增强
if self.config.get('mobile_enhance', False):
processed_frame = self.mobile_enhance_frame(processed_frame)
else:
# 桌面端原有处理流程
if output_resolution:
processed_frame = cv2.resize(frame, output_resolution)
else:
processed_frame = frame
# 放入已处理队列
self.processed_queue.put((frame_idx, processed_frame), timeout=0.5)
self.frame_queue.task_done()
# 移动端内存管理 - 定期释放资源
if self.is_mobile and frame_idx % 10 == 0:
gc.collect()
except queue.Empty:
if self.canceled:
break
except Exception as e:
self.logger.error(f"处理线程出错: {str(e)}")
self.logger.info("处理线程退出")
def mobile_enhance_frame(self, frame):
"""移动端友好的帧增强"""
try:
# 使用快速直方图均衡化
lab = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(4, 4))
limg = clahe.apply(l)
enhanced = cv2.merge((limg, a, b))
return cv2.cvtColor(enhanced, cv2.COLOR_LAB2BGR)
except:
return frame
def writer_thread(self, out, total_frames):
"""移动端优化的写入线程"""
frame_idx = 0
last_log_time = time.time()
while not self.canceled and frame_idx < total_frames:
try:
# 获取已处理帧
idx, processed_frame = self.processed_queue.get(timeout=1.0)
# 结束信号
if idx is None:
break
# 写入输出
if processed_frame is not None:
out.write(processed_frame)
# 更新计数
self.frame_counter += 1
frame_idx += 1
# 计算帧率
elapsed = time.time() - self.start_time
self.fps = self.frame_counter / elapsed if elapsed > 0 else 0
# 更新内存使用
process = psutil.Process(os.getpid())
self.mem_usage = process.memory_info().rss / (1024 ** 2) # MB
# 更新系统状态
self.update_system_stats()
# 更新进度
self.progress = min(100, int(frame_idx * 100 / total_frames))
# 定期打印进度
current_time = time.time()
if current_time - last_log_time > 1.0 or frame_idx % 20 == 0:
self.print_progress()
last_log_time = current_time
# 移动端内存管理
if self.is_mobile:
# 更频繁的垃圾回收
if frame_idx % 5 == 0:
gc.collect()
else:
# 桌面端内存管理
if frame_idx % 100 == 0:
gc.collect()
self.processed_queue.task_done()
except queue.Empty:
if self.canceled:
break
except Exception as e:
self.logger.error(f"写入线程出错: {str(e)}")
self.logger.info(f"写入线程完成,共写入 {frame_idx} 帧")
def run(self):
try:
self.status = "处理中..."
self.logger.info("视频处理开始")
self.start_time = time.time()
# 启动资源监控
self.start_resource_monitor()
# 打开主视频
self.logger.info("正在打开主视频...")
main_cap = self.open_video_with_acceleration(self.config['main_video'])
if not main_cap or not main_cap.isOpened():
self.status = "无法打开主视频文件!"
self.logger.error(f"无法打开主视频文件: {self.config['main_video']}")
return False
# 获取主视频信息
main_fps = main_cap.get(cv2.CAP_PROP_FPS)
main_width = int(main_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
main_height = int(main_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
main_total_frames = int(main_cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.logger.info(f"主视频信息: {main_width}x{main_height}@{main_fps:.1f}fps, 总帧数: {main_total_frames}")
# 打开副视频
self.logger.info("正在打开副视频...")
sub_cap = self.open_video_with_acceleration(self.config['sub_video'])
if not sub_cap or not sub_cap.isOpened():
self.status = "无法打开副视频文件!"
self.logger.error(f"无法打开副视频文件: {self.config['sub_video']}")
return False
# 获取副视频信息
sub_fps = sub_cap.get(cv2.CAP_PROP_FPS)
sub_width = int(sub_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
sub_height = int(sub_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
sub_total_frames = int(sub_cap.get(cv2.CAP_PROP_FRAME_COUNT))
self.logger.info(f"副视频信息: {sub_width}x{sub_height}@{sub_fps:.1f}fps, 总帧数: {sub_total_frames}")
# 创建输出目录
output_dir = os.path.dirname(self.config['output_path'])
if output_dir and not os.path.exists(output_dir):
try:
os.makedirs(output_dir)
self.logger.info(f"已创建输出目录: {output_dir}")
except Exception as e:
self.status = f"无法创建输出目录: {output_dir}"
self.logger.error(f"创建输出目录失败: {str(e)}")
return False
# 创建输出视频
output_width, output_height = self.config['output_resolution']
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(self.config['output_path'], fourcc, main_fps, (output_width, output_height))
if out.isOpened():
self.resources.append(out)
else:
self.status = "无法创建输出视频文件!请检查分辨率设置。"
self.logger.error(f"无法创建输出视频: {self.config['output_path']}, 分辨率: {output_width}x{output_height}")
return False
# 简化参数计算
segment_length_main = int(float(self.config['segment_a']) * main_fps)
b1 = int(self.config['b1'])
b2 = int(self.config['b2'])
segment_length_sub = int(float(self.config['segment_c']) * sub_fps)
d = int(self.config['d'])
# 显示移动端优化信息
if self.is_mobile:
print("\n移动端优化配置:")
print(f"输出分辨率: {output_width}x{output_height}")
print(f"队列大小: {self.frame_queue.maxsize}")
print(f"目标帧率: {self.config.get('target_fps', 24)}")
print(f"内存优化: 启用")
if self.config.get('mobile_enhance', False):
print("图像增强: 启用")
self.logger.info("开始视频处理")
self.logger.info(f"输出分辨率: {output_width}x{output_height}")
# 启动多线程处理
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
# 启动捕获线程
capture_future = executor.submit(
self.capture_thread,
main_cap,
main_total_frames
)
# 启动处理线程
processing_future = executor.submit(
self.processing_thread,
(output_width, output_height)
)
# 启动写入线程
writer_future = executor.submit(
self.writer_thread,
out,
main_total_frames
)
# 等待所有线程完成
concurrent.futures.wait(
[capture_future, processing_future, writer_future],
return_when=concurrent.futures.ALL_COMPLETED
)
if not self.canceled:
self.status = "处理完成"
self.progress = 100
self.print_progress()
print(f"\n\n处理完成!输出文件: {self.config['output_path']}")
self.logger.info(f"处理完成! 总帧数: {self.frame_counter}, 耗时: {time.time() - self.start_time:.1f}秒")
return True
return False
except Exception as e:
error_msg = f"处理过程中发生错误: {str(e)}"
if self.is_mobile:
# 移动端友好的错误提示
if "out of memory" in error_msg.lower():
error_msg += "\n建议: 尝试降低分辨率或关闭增强功能"
elif "not implemented" in error_msg.lower():
error_msg += "\n建议: 此功能在移动端不可用"
self.status = error_msg
error_trace = traceback.format_exc()
self.logger.error(f"处理过程中发生错误: {str(e)}")
self.logger.error(f"错误详情:\n{error_trace}")
print(f"\n\n{error_msg}")
return False
finally:
self.stop_resource_monitor()
self.release_resources()
# 释放Termux优化资源
if self.is_mobile:
try:
subprocess.run(["termux-wake-unlock"], check=True)
self.logger.info("释放唤醒锁成功")
except:
pass
def release_resources(self):
"""释放所有资源"""
self.logger.info("正在释放资源...")
for resource in self.resources:
try:
if hasattr(resource, 'release'):
resource.release()
elif hasattr(resource, 'close'):
resource.close()
except Exception as e:
self.logger.warning(f"释放资源时出错: {str(e)}")
self.resources = []
self.logger.info("资源已释放")
def cancel(self):
"""取消处理"""
self.canceled = True
self.status = "正在取消..."
self.logger.warning("用户请求取消处理")
print("\n正在取消处理...")
# 清空队列
while not self.frame_queue.empty():
try:
self.frame_queue.get_nowait()
self.frame_queue.task_done()
except queue.Empty:
break
while not self.processed_queue.empty():
try:
self.processed_queue.get_nowait()
self.processed_queue.task_done()
except queue.Empty:
break
self.stop_resource_monitor()
self.release_resources()
# 释放Termux优化资源
if self.is_mobile:
try:
subprocess.run(["termux-wake-unlock"], check=True)
self.logger.info("释放唤醒锁成功")
except:
pass
def get_video_info(file_path):
"""获取视频文件信息"""
cap = None
try:
cap = cv2.VideoCapture(file_path)
if cap.isOpened():
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
duration = frame_count / fps if fps > 0 else 0
return {
"width": width,
"height": height,
"fps": fps,
"frame_count": frame_count,
"duration": duration
}
return None
except Exception as e:
print(f"获取视频信息时出错: {str(e)}")
return None
finally:
if cap and cap.isOpened():
cap.release()
def validate_config(config):
"""验证配置参数"""
# 检查文件存在
if not os.path.exists(config['main_video']):
print(f"错误: 主视频文件不存在 - {config['main_video']}")
return False
if not os.path.exists(config['sub_video']):
print(f"错误: 副视频文件不存在 - {config['sub_video']}")
return False
# 检查输出目录
output_dir = os.path.dirname(config['output_path'])
if output_dir and not os.path.exists(output_dir):
try:
os.makedirs(output_dir)
print(f"已创建输出目录: {output_dir}")
except:
print(f"错误: 无法创建输出目录 - {output_dir}")
return False
# 检查参数有效性
try:
# 主视频参数
segment_a = float(config['segment_a'])
if segment_a <= 0:
print("错误: 分段长度必须大于0!")
return False
b1 = int(config['b1'])
b2 = int(config['b2'])
if b1 < 0 or b2 < 0:
print("错误: 帧索引不能为负数!")
return False
if b1 > b2:
print("错误: 替换开始帧(b1)必须小于或等于替换结束帧(b2)!")
return False
# 副视频参数
segment_c = float(config['segment_c'])
if segment_c <= 0:
print("错误: 分段长度必须大于0!")
return False
d = int(config['d'])
if d < 0:
print("错误: 帧索引不能为负数!")
return False
# 分辨率
width = int(config['output_resolution'][0])
height = int(config['output_resolution'][1])
if width <= 0 or height <= 0:
print("错误: 分辨率必须大于0!")
return False
return True
except ValueError:
print("错误: 请输入有效的数字参数!")
return False
def save_config(config, file_path):
"""保存配置到文件"""
try:
with open(file_path, 'w') as f:
json.dump(config, f, indent=2)
print(f"配置已保存到: {file_path}")
except Exception as e:
print(f"保存配置时出错: {str(e)}")
def load_config(file_path):
"""从文件加载配置"""
try:
with open(file_path, 'r') as f:
config = json.load(f)
# 确保配置中包含所有必要字段
required_keys = [
'main_video', 'sub_video', 'output_path',
'main_segment_type', 'segment_a', 'b1', 'b2',
'sub_segment_type', 'segment_c', 'd', 'sub_option',
'output_resolution'
]
for key in required_keys:
if key not in config:
print(f"警告: 配置文件中缺少 '{key}' 参数")
return config
except FileNotFoundError:
print(f"错误: 配置文件不存在 - {file_path}")
except Exception as e:
print(f"加载配置时出错: {str(e)}")
return None
def create_default_config():
"""移动端感知的默认配置"""
# 检测是否为移动环境
is_mobile = VideoProcessor({}).detect_mobile_environment()
if is_mobile:
return {
"main_video": "main.mp4",
"sub_video": "sub.mp4",
"output_path": "output/output.mp4",
"main_segment_type": "秒",
"segment_a": "1",
"b1": "1",
"b2": "1",
"sub_segment_type": "帧",
"segment_c": "1",
"d": "1",
"sub_option": "循环使用",
"output_resolution": [640, 360],
"hardware_acceleration": "mediacodec",
"gpu_device_index": 0,
"reduce_latency": True,
"decoding_threads": 2,
"use_gpu_processing": False,
"cuda_streams": 0,
"queue_size": 3,
"buffer_size": 1,
"target_fps": 24,
"use_mjpeg": True,
"enable_memory_monitor": False,
"mobile_optimized": True,
"mobile_enhance": False
}
else:
return {
"main_video": "main_video.mp4",
"sub_video": "sub_video.mp4",
"output_path": "output/output_video.mp4",
"main_segment_type": "秒",
"segment_a": "1",
"b1": "1",
"b2": "1",
"sub_segment_type": "帧",
"segment_c": "1",
"d": "1",
"sub_option": "循环使用",
"output_resolution": [1280, 720],
"hardware_acceleration": "auto",
"gpu_device_index": 0,
"reduce_latency": True,
"decoding_threads": 4,
"use_gpu_processing": True,
"cuda_streams": 4,
"queue_size": 30,
"buffer_size": 3,
"target_fps": 30,
"use_mjpeg": True,
"enable_memory_monitor": False,
"mobile_optimized": True,
"mobile_enhance": False
}
def setup_logging():
"""配置日志系统"""
log_dir = "logs"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
log_file = os.path.join(log_dir, f"video_processor_{timestamp}.log")
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
logger = logging.getLogger()
logger.info(f"日志系统初始化完成, 日志文件: {log_file}")
return logger, log_file
def install_termux_dependencies():
"""安装Termux所需的依赖"""
print("正在安装Termux依赖...")
commands = [
"pkg update -y && pkg upgrade -y",
"pkg install python -y",
"pkg install ffmpeg libjpeg-turbo -y",
"pip install opencv-python-headless numpy psutil",
"pkg install termux-api -y", # 新增Termux:API支持
# 清理缓存节省空间
"pkg clean -y",
"rm -rf ~/.cache/pip"
]
for cmd in commands:
print(f"执行: {cmd}")
try:
subprocess.run(cmd, shell=True, check=True)
except subprocess.CalledProcessError as e:
print(f"安装失败: {e}")
return False
print("\n安装完成! 验证安装:")
subprocess.run(["python", "-c", "import cv2; print(f'OpenCV版本: {cv2.__version__}')"])
print("请确保已安装Termux:API应用并授予必要权限")
return True
def verify_gpu_support():
"""验证GPU支持情况"""
print("\n验证GPU支持:")
# 验证MediaCodec支持
print("\n1. MediaCodec支持:")
result = subprocess.run(["ffmpeg", "-hwaccels"], capture_output=True, text=True)
if "mediacodec" in result.stdout:
print(" ✓ 支持MediaCodec硬件加速")
else:
print(" ✗ 不支持MediaCodec硬件加速")
print("\n设置完成")
def setup_termux_gpu_acceleration():
"""设置Termux GPU加速环境"""
print("="*50)
print("Termux GPU加速视频处理设置")
print("="*50)
# 安装基础依赖
if not install_termux_dependencies():
print("依赖安装失败,无法继续设置")
return
# 验证GPU支持
verify_gpu_support()
print("\nGPU加速环境设置完成!")
print("现在可以使用以下命令进行硬件加速视频处理:")
print("ffmpeg -hwaccel mediacodec -i input.mp4 -c:v h264_mediacodec output.mp4")
# 创建示例批处理脚本
with open("gpu_batch_process.sh", "w") as f:
f.write("""#!/bin/bash
# GPU加速批处理脚本
for f in *.mp4; do
echo "处理: $f"
ffmpeg -hwaccel mediacodec -i "$f" -c:v h264_mediacodec "gpu_$f"
done
echo "所有视频处理完成!"
""")
print("\n已创建批处理脚本: gpu_batch_process.sh")
print("使用命令运行: bash gpu_batch_process.sh")
# 设置防后台杀死优化
print("\n设置防后台杀死优化:")
try:
subprocess.run(["termux-wake-lock"], check=True)
print(" ✓ 防止后台被杀设置成功")
result = subprocess.run(["termux-battery-optimization", "-s"],
capture_output=True, text=True)
if "already" not in result.stdout:
print(" ✓ 电池优化白名单设置成功")
else:
print(" ✓ 已在电池优化白名单中")
except Exception as e:
print(f" ✗ 优化设置失败: {str(e)}")
print("请确保已安装Termux:API应用并授予必要权限")
def main():
# 设置日志
logger, log_file = setup_logging()
# 创建参数解析器
parser = argparse.ArgumentParser(description="移动端视频帧替换工具", formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument("--config", help="配置文件路径", default="")
parser.add_argument("--save-config", help="保存默认配置到文件", action="store_true")
parser.add_argument("--batch", help="批量处理模式,指定批量配置文件", default="")
parser.add_argument("--output-dir", help="批量处理输出目录", default="batch_output")
parser.add_argument("--setup-termux", help="设置Termux GPU加速环境", action="store_true")
parser.add_argument("--mobile-enhance", help="启用移动端图像增强", action="store_true")
args = parser.parse_args()
# Termux GPU加速设置
if args.setup_termux:
setup_termux_gpu_acceleration()
return
# 保存默认配置
if args.save_config:
config_file = args.config if args.config else "video_config.json"
default_config = create_default_config()
save_config(default_config, config_file)
print(f"默认配置已保存到: {config_file}")
return
# 批量处理模式
if args.batch:
if not os.path.exists(args.output_dir):
os.makedirs(args.output_dir)
print("批量处理模式在移动端不可用")
return
# 加载配置
config = None
if args.config:
config = load_config(args.config)
# 如果没有提供配置或加载失败,使用默认配置
if not config:
print("使用默认配置")
config = create_default_config()
# 命令行参数覆盖配置
if args.mobile_enhance:
config['mobile_enhance'] = True
# Termux环境特殊设置
is_mobile = VideoProcessor({}).detect_mobile_environment()
if is_mobile:
print("检测到Termux环境,应用移动端优化")
# 设置环境变量
os.environ["OPENCV_VIDEOIO_PRIORITY"] = "MEDIACODEC"
os.environ["OPENCV_FFMPEG_CAPTURE_OPTIONS"] = "video_codec=h264_mediacodec"
os.environ["OPENCV_LOG_LEVEL"] = "ERROR"
# 禁用非必要功能
config['enable_memory_monitor'] = False
config['use_gpu_processing'] = False
# 显示配置
logger.info("\n当前配置:")
logger.info(f"主视频: {config['main_video']}")
logger.info(f"副视频: {config['sub_video']}")
logger.info(f"输出文件: {config['output_path']}")
logger.info(f"输出分辨率: {config['output_resolution'][0]}x{config['output_resolution'][1]}")
logger.info(f"硬件加速: {config.get('hardware_acceleration', 'auto')}")
logger.info(f"移动端增强: {config.get('mobile_enhance', False)}")
print("\n当前配置:")
print(f"主视频: {config['main_video']}")
print(f"副视频: {config['sub_video']}")
print(f"输出文件: {config['output_path']}")
print(f"输出分辨率: {config['output_resolution'][0]}x{config['output_resolution'][1]}")
print(f"硬件加速: {config.get('hardware_acceleration', 'auto')}")
print(f"移动端增强: {config.get('mobile_enhance', False)}\n")
# 验证配置
if not validate_config(config):
logger.error("配置验证失败")
return
# 显示视频信息
main_info = get_video_info(config['main_video'])
if main_info:
print("主视频信息:")
print(f" 尺寸: {main_info['width']}x{main_info['height']}")
print(f" 帧率: {main_info['fps']:.1f} fps")
print(f" 时长: {main_info['duration']:.1f}秒")
sub_info = get_video_info(config['sub_video'])
if sub_info:
print("\n副视频信息:")
print(f" 尺寸: {sub_info['width']}x{sub_info['height']}")
print(f" 帧率: {sub_info['fps']:.1f} fps")
print(f" 时长: {sub_info['duration']:.1f}秒")
# 确认开始处理
print("\n按 Enter 开始处理,或输入 'c' 取消...")
user_input = input().strip().lower()
if user_input == 'c':
logger.info("用户取消处理")
print("处理已取消")
return
# 创建并运行处理器
logger.info("开始视频处理")
processor = VideoProcessor(config)
processor.run()
# 保存配置
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
config_file = f"video_config_{timestamp}.json"
save_config(config, config_file)
logger.info(f"配置已保存: {config_file}")
if __name__ == "__main__":
main()