ai用ffmpeg+python实现视频转换ts并生成m3u8小工具

前言

这几天需要把本网站的视频放到服务器提供在线播放,直接用mp4格式的话,加载和快进都会比较慢,主要是流量用的比较多,把MP4视频切片成ts后就能节约流量也能提高播放速度,以后大家就不用去网盘享受那龟速的下载体验,也不用去开网盘会员了;当然我们还是会同步提供下载课程的入口。

说干就干

我们让AI基于 FFmpegPython 写的视频转换m3u8实用小工具,用于高效地将各种格式的视频转换为 TS 格式,同时生成对应的 M3U8 播放列表文件。该工具旨在简化视频处理过程,特别适用于需要快速部署 HLS(HTTP Live Streaming)视频流媒体的场景。

视频转换ts

准备工作

安装python:

python官方下载地址:https://www.python.org/downloads/

安装ffpmeg:

ffpmeg官方下载地址:https://ffmpeg.org/download.html

视频切片ts软件功能特点

  1. 多格式支持
    • 支持常见的视频格式(如 MP4、MKV、AVI、MOV 等),能够轻松进行格式转换而无需复杂配置。
  2. 高效处理
    • 借助 FFmpeg 的强大功能,该工具通过无编码方式直接切片视频为 TS 文件,避免重复编码导致的画质损失和处理速度下降。
  3. 自动生成 M3U8 播放列表
    • 在将视频切片成 TS 文件的同时,自动生成对应的 M3U8 文件,方便用于流媒体播放。
  4. 简单易用
    • 提供用户友好的界面(CLI 或 GUI),无需深入了解 FFmpeg 命令行,任何用户都能轻松上手。
  5. 可配置性强
    • 支持自定义切片长度(例如每 10 秒生成一个 TS 文件),满足不同应用场景的需求。

用AI写的python代码

import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext
import os
import subprocess
import threading
from pathlib import Path
import logging
import re

class VideoSlicer:
    def __init__(self, root):
        self.root = root
        self.root.title("视频切片工具-www.baoxiache.com")
        self.root.geometry("800x600")
        
        # 配置日志
        self.setup_logging()
        
        # 创建GUI元素
        self.create_widgets()
        
        # 支持的视频格式
        self.video_extensions = ('.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv')
        
        # 处理状态
        self.is_processing = False
    
    def setup_logging(self):
        logging.basicConfig(level=logging.INFO)
        self.logger = logging.getLogger(__name__)
    
    def create_widgets(self):
        # 输入目录选择
        input_frame = ttk.LabelFrame(self.root, text="输入设置", padding="5")
        input_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(input_frame, text="输入目录:").pack(side="left")
        self.input_path = tk.StringVar()
        ttk.Entry(input_frame, textvariable=self.input_path, width=50).pack(side="left", padx=5)
        ttk.Button(input_frame, text="浏览", command=self.select_input_dir).pack(side="left")
        
        # 输出目录选择
        output_frame = ttk.LabelFrame(self.root, text="输出设置", padding="5")
        output_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(output_frame, text="输出目录:").pack(side="left")
        self.output_path = tk.StringVar()
        ttk.Entry(output_frame, textvariable=self.output_path, width=50).pack(side="left", padx=5)
        ttk.Button(output_frame, text="浏览", command=self.select_output_dir).pack(side="left")
        
        # 切片时长设置
        duration_frame = ttk.LabelFrame(self.root, text="切片设置", padding="5")
        duration_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(duration_frame, text="切片时长(秒):").pack(side="left")
        self.slice_duration = tk.StringVar(value="10")
        ttk.Entry(duration_frame, textvariable=self.slice_duration, width=10).pack(side="left", padx=5)
        
        # 进度条
        self.progress_var = tk.DoubleVar()
        self.progress = ttk.Progressbar(self.root, variable=self.progress_var, maximum=100)
        self.progress.pack(fill="x", padx=5, pady=5)
        
        # 开始按钮
        self.start_button = ttk.Button(self.root, text="开始处理", command=self.start_processing)
        self.start_button.pack(pady=5)
        
        # 日志窗口
        log_frame = ttk.LabelFrame(self.root, text="处理日志", padding="5")
        log_frame.pack(fill="both", expand=True, padx=5, pady=5)
        
        self.log_text = scrolledtext.ScrolledText(log_frame, height=10)
        self.log_text.pack(fill="both", expand=True)
    
    def select_input_dir(self):
        directory = filedialog.askdirectory()
        if directory:
            self.input_path.set(directory)
    
    def select_output_dir(self):
        directory = filedialog.askdirectory()
        if directory:
            self.output_path.set(directory)
    
    def log_message(self, message):
        self.log_text.insert(tk.END, f"{message}\n")
        self.log_text.see(tk.END)
        
    def sanitize_filename(self, filename):
        # 移除非法字符,保留中文和基本字符
        return re.sub(r'[<>:"/\\|?*]', '', filename)
    
    def process_video(self, video_path, output_base_dir):
        try:
            # 获取相对路径以保持目录结构
            rel_path = os.path.relpath(video_path, self.input_path.get())
            video_dir = os.path.dirname(rel_path)
            video_name = os.path.splitext(os.path.basename(video_path))[0]
            
            # 创建输出目录
            output_dir = os.path.join(output_base_dir, video_dir, self.sanitize_filename(video_name))
            os.makedirs(output_dir, exist_ok=True)
            
            # 生成切片
            slice_duration = self.slice_duration.get()
            m3u8_path = os.path.join(output_dir, 'playlist.m3u8')
            
            cmd = [
                'ffmpeg', '-i', video_path,
                '-c', 'copy',
                '-f', 'segment',
                '-segment_time', slice_duration,
                '-segment_list', m3u8_path,
                '-segment_format', 'mpegts',
                os.path.join(output_dir, 'segment_%03d.ts')
            ]
            
            process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            _, stderr = process.communicate()
            
            if process.returncode == 0:
                self.log_message(f"成功处理: {video_path}")
            else:
                self.log_message(f"处理失败: {video_path}\n错误信息: {stderr.decode()}")
                
        except Exception as e:
            self.log_message(f"处理出错: {video_path}\n错误信息: {str(e)}")
    
    def scan_videos(self, directory):
        videos = []
        for root, _, files in os.walk(directory):
            for file in files:
                if file.lower().endswith(self.video_extensions):
                    videos.append(os.path.join(root, file))
        return videos
    
    def start_processing(self):
        if self.is_processing:
            return
        
        input_dir = self.input_path.get()
        output_dir = self.output_path.get()
        
        if not input_dir or not output_dir:
            self.log_message("请选择输入和输出目录")
            return
        
        if not self.slice_duration.get().isdigit():
            self.log_message("请输入有效的切片时长")
            return
        
        self.is_processing = True
        self.start_button.state(['disabled'])
        
        def process_thread():
            try:
                videos = self.scan_videos(input_dir)
                total_videos = len(videos)
                
                if total_videos == 0:
                    self.log_message("未找到支持的视频文件")
                    return
                
                self.log_message(f"找到 {total_videos} 个视频文件")
                
                for i, video in enumerate(videos, 1):
                    self.process_video(video, output_dir)
                    progress = (i / total_videos) * 100
                    self.progress_var.set(progress)
                
                self.log_message("所有文件处理完成")
                
            except Exception as e:
                self.log_message(f"处理过程出错: {str(e)}")
            
            finally:
                self.is_processing = False
                self.start_button.state(['!disabled'])
                self.progress_var.set(0)
        
        threading.Thread(target=process_thread, daemon=True).start()

if __name__ == "__main__":
    root = tk.Tk()
    app = VideoSlicer(root)
    root.mainloop()

下载完整python代码直接运行

视频切片工具.zip
zip文件
2.2K
© 版权声明
THE END
喜欢就支持一下吧
点赞28 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容