Python合并ts文件至mp4格式及解密教程详解


配置ffmpeg运行环境

ffmpeg官网

验证ffmpeg的环境变量配置

ffmpeg -version

下载加密的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();
}
}

}