ai用ffmpeg+python實(shí)現(xiàn)視頻轉(zhuǎn)換ts并生成m3u8小工具

前言

這幾天需要把本網(wǎng)站的視頻放到服務(wù)器提供在線播放,直接用mp4格式的話,加載和快進(jìn)都會(huì)比較慢,主要是流量用的比較多,把MP4視頻切片成ts后就能節(jié)約流量也能提高播放速度,以后大家就不用去網(wǎng)盤享受那龜速的下載體驗(yàn),也不用去開網(wǎng)盤會(huì)員了;當(dāng)然我們還是會(huì)同步提供下載課程的入口。

說干就干

我們讓AI基于 FFmpegPython 寫的視頻轉(zhuǎn)換m3u8實(shí)用小工具,用于高效地將各種格式的視頻轉(zhuǎn)換為 TS 格式,同時(shí)生成對應(yīng)的 M3U8 播放列表文件。該工具旨在簡化視頻處理過程,特別適用于需要快速部署 HLS(HTTP Live Streaming)視頻流媒體的場景。

視頻轉(zhuǎn)換ts

準(zhǔn)備工作

安裝python:

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

安裝ffpmeg:

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

視頻切片ts軟件功能特點(diǎn)

  1. 多格式支持
    • 支持常見的視頻格式(如 MP4、MKV、AVI、MOV 等),能夠輕松進(jìn)行格式轉(zhuǎn)換而無需復(fù)雜配置。
  2. 高效處理
    • 借助 FFmpeg 的強(qiáng)大功能,該工具通過無編碼方式直接切片視頻為 TS 文件,避免重復(fù)編碼導(dǎo)致的畫質(zhì)損失和處理速度下降。
  3. 自動(dòng)生成 M3U8 播放列表
    • 在將視頻切片成 TS 文件的同時(shí),自動(dòng)生成對應(yīng)的 M3U8 文件,方便用于流媒體播放。
  4. 簡單易用
    • 提供用戶友好的界面(CLI 或 GUI),無需深入了解 FFmpeg 命令行,任何用戶都能輕松上手。
  5. 可配置性強(qiáng)
    • 支持自定義切片長度(例如每 10 秒生成一個(gè) TS 文件),滿足不同應(yīng)用場景的需求。

用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.kq1o.cn")
        self.root.geometry("800x600")
        
        # 配置日志
        self.setup_logging()
        
        # 創(chuàng)建GUI元素
        self.create_widgets()
        
        # 支持的視頻格式
        self.video_extensions = ('.mp4', '.mkv', '.avi', '.mov', '.flv', '.wmv')
        
        # 處理狀態(tài)
        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="輸入設(shè)置", 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="輸出設(shè)置", 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")
        
        # 切片時(shí)長設(shè)置
        duration_frame = ttk.LabelFrame(self.root, text="切片設(shè)置", padding="5")
        duration_frame.pack(fill="x", padx=5, pady=5)
        
        ttk.Label(duration_frame, text="切片時(shí)長(秒):").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)
        
        # 進(jìn)度條
        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:
            # 獲取相對路徑以保持目錄結(jié)構(gòu)
            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]
            
            # 創(chuàng)建輸出目錄
            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錯(cuò)誤信息: {stderr.decode()}")
                
        except Exception as e:
            self.log_message(f"處理出錯(cuò): {video_path}\n錯(cuò)誤信息: {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("請輸入有效的切片時(shí)長")
            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} 個(gè)視頻文件")
                
                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"處理過程出錯(cuò): {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代碼直接運(yùn)行

視頻切片工具.zip
zip文件
2.2K
? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊28 分享
評論 搶沙發(fā)

請登錄后發(fā)表評論

    暫無評論內(nèi)容