Python合并ts文件至mp4格式及解密教程详解
配置ffmpeg运行环境
ffmpeg官网



验证ffmpeg的环境变量配置

下载加密的ts的分片视频文件

如果index.m3u8文件中存在#EXT-X-KEY则表示当前文件是加密;反之则无。


合并所有ts分片的视频
| ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy new.mp4
 | 

java-示例代码
| package com.ruoyi.manman;import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
 public class VideoDownloader {
 
 // https://xx.xxx.com/index.m3u8
 // https://xx.xxx.com/enc.key
 // https://xx.xxx.com/index0.ts
 
 // 下载地址前缀URL
 private static final String PREFIX_URL = "https://xx.xxx.com/";
 // 视频文件ts的前缀(完整示例:index1.ts,程序自动拼接`x.ts`,)
 private static final String BASE_URL = PREFIX_URL + "index";
 // 视频文件的索引文件
 private static final String INDEX_M3U8 = "index.m3u8";
 // 视频文件解密key
 private static final String ENC_KEY = "enc.key";
 // 分片ts视频文件下载目录
 private static final String OUTPUT_DIR = "D:\\Env\\Tmp\\";
 // 合并视频文件名
 private static final String MERGED_FILE = "xxx.mp4";
 
 public static void main(String[] args) {
 
 VideoDownloader downloader = new VideoDownloader();
 downloader.downloadAndMergeVideos();
 
 }
 
 public static void convertTsToMp4(String inputFile, String outputFile) {
 try {
 
 // ffmpeg -allowed_extensions ALL -i index.m3u8 -c copy new.mp4
 ProcessBuilder builder = new ProcessBuilder(
 "D:\\Env\\ffmpeg\\ffmpeg-7.0.2-essentials_build\\bin\\ffmpeg.exe",
 "-allowed_extensions",
 "ALL",
 "-i",
 inputFile,
 "-c",
 "copy",
 outputFile
 );
 
 // 将错误输出合并到标准输出
 builder.redirectErrorStream(true);
 Process process = builder.start();
 
 // 读取输出信息
 try (BufferedReader reader = new BufferedReader(
 new InputStreamReader(process.getInputStream()))) {
 String line;
 while ((line = reader.readLine()) != null) {
 System.out.println(line);
 }
 }
 
 // 等待转换完成
 int exitCode = process.waitFor();
 if (exitCode == 0) {
 System.out.println("视频转换成功!");
 } else {
 System.out.println("视频转换失败,错误码:" + exitCode);
 }
 
 } catch (IOException | InterruptedException e) {
 e.printStackTrace();
 }
 }
 
 public void downloadAndMergeVideos() {
 List<String> downloadedFiles = new ArrayList<>();
 int index = 0; // 起始索引
 
 // 创建下载目录
 new File(OUTPUT_DIR).mkdirs();
 
 // 下载所有分片
 while (true) {
 String url = BASE_URL + index + ".ts";
 String outputFile = OUTPUT_DIR + "index" + index + ".ts";
 
 if (!downloadFile(url, outputFile)) {
 break; // 如果下载失败(状态码不是200),退出循环
 }
 
 downloadedFiles.add(outputFile);
 index++;
 }
 
 // 下载视频索引及加密key
 downloadFile(PREFIX_URL+INDEX_M3U8, OUTPUT_DIR+INDEX_M3U8);
 downloadFile(PREFIX_URL+ENC_KEY, OUTPUT_DIR+ENC_KEY);
 
 // 合并文件
 String inputFile = OUTPUT_DIR + INDEX_M3U8;
 String outputFile = OUTPUT_DIR + MERGED_FILE;
 convertTsToMp4(inputFile, outputFile);
 
 // 清理临时文件
 cleanupTempFiles(downloadedFiles);
 }
 
 private boolean downloadFile(String urlStr, String outputFile) {
 try {
 URL url = new URL(urlStr);
 HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 conn.setRequestMethod("GET");
 
 if (conn.getResponseCode() != 200) {
 return false;
 }
 
 try (InputStream in = conn.getInputStream();
 FileOutputStream out = new FileOutputStream(outputFile)) {
 byte[] buffer = new byte[4096];
 int bytesRead;
 while ((bytesRead = in.read(buffer)) != -1) {
 out.write(buffer, 0, bytesRead);
 }
 }
 return true;
 } catch (IOException e) {
 e.printStackTrace();
 return false;
 }
 }
 
 private void cleanupTempFiles(List<String> files) {
 for (String file : files) {
 new File(file).delete();
 }
 }
 
 }
 
 
 |