一、需求
用手机从B站上缓存了一个列表的视频,但是它的音视频文件是分开的,而且只能用B站APP打开。想转成mp4格式在电脑上播放。
二、现有办法
单个视频倒也好办,到该目录下cmd运行下面这行代码:
ffmpeg -i video.m4s -i audio.m4s -codec copy output.mp4
马上就可以完事。
三、困惑
但问题在于,一个列表的视频有二十多个,一个一个打开似乎太啰嗦,而且小破站放缓存文件的方式很阴间:
点进一个视频的目录后,还要再进一个叫80的子目录才能看到源文件。点来点去实在麻烦,而且点开六七个后退出来找下一个,面对这一大堆以九位数字命名的目录,很可能找不到自己上一个是点了哪个文件夹。
四、思路
于是我想,能不能用Java的File IO流来帮我们做点事。把这个过程自动化一下。
我们已经知道如何手动处理单个音视频合并。如果我们用Java运行bat文件,bat文件的命令包括移动到指定目录和执行ffmpeg(上面那行代码),这不就是相当于上面手动运行cmd的效果了吗?
cd D:\Videos\545686540\c_341980251\80
ffmpeg -i video.m4s -i audio.m4s -codec copy output.mp4
那么如何扩展到多文件呢?只要我们能得到其他源文件的路径,就能如法炮制,得到mp4文件。如果能把该路径自动化写出来,那这整个过程不就自动化了吗?
我们观察到所有源文件的路径中只有c_341981036这一部分是不同的,那我们就重点解决它。
正好Java File类提供给了我们一个方法file.list(),可以得到当前目录下的子文件和子文件夹,这正是我们需要的:
File file = new File(headPath);
String[] list = file.list();
然后遍历该数组,数组元素和两头两尾固定的路径拼接一下,就得到了一个完整的路径。至此核心问题已经解决。
五、动手
编写相关代码跑一个看看:
这样做之后,已得到了所有视频的mp4文件,随便打开一个文件夹看一下:
确实已经生成了mp4文件。不过生成的mp4文件分散在各个子目录里面,如果要去手动复制出来了话依然很繁琐。我们不妨在生成bat文件的时候就指定一个固定的输出目录:
"ffmpeg -i video.m4s -i audio.m4s -codec copy D:\\Videos\\createdMp4\\output" + i +".mp4"
这样我们就得到了一打开就是mp4文件的文件夹。理想很美好,实际上我这么做,只得到两个mp4文件代码就不干活了,也不报错,就在那卡着,状态还是运行状态。不管他,就让它分散在自己的子目录里,我们换个办法,用IO流把文件复制一份到指定目录:
public static void copy(int i) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(mp4Path + "\\output.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Videos\\createdMp4\\output" + i + ".mp4"));
byte[] bytes = new byte[1024];
int num = 0;
while ((num = bis.read(bytes)) != -1){
bos.write(bytes,0,num);
}
bos.close();
bis.close();
}
至此,需求得到解决。
附:源代码
package com.serein.freedom;
import java.io.*;
/**
* 需求:批量合并m4s音视频。每个视频的路径:d:\\a\\c_xxxxx(随机数)\\80\\video.m4s d:\\a\\c_xxxxx(随机数)\\80\\audio.m4s
* 思路:1.读取到某一条视频的信息
* 2.运行命令行工具:ffmpeg -i video.m4s -i audio.m4s -codec copy output.mp4
* 3.放到循环执行,遍历所有文件。(功能已经做完了)(做到这一步相当于是半自动洗衣机)
* 4.优雅的把mp4文件取出来,放到一个文件夹中,最好文件名是有顺序的。(全自动洗衣机)
*/
public class Test1 {
static String batPath = "";
static String mp4Path = "";
static String headPath = "D:\\Videos\\545686540\\";
public static void main(String[] args) throws Exception {
//得到缓存列表视频直接父目录 下所有的子目录
File file = new File(headPath);
String[] list = file.list();
for (int i = 0; i < list.length; i++) {
batPath = headPath + list[i] + "\\80\\test.bat";
mp4Path = headPath + list[i] + "\\80";
// 黑窗口最多同时打开15个
// 当视频不长“嗖”一下就搞定时没关系,当任务量大单个视频长的时候,用此方法可以当甩手掌柜,自己完全不用管事。
if (i % 15 == 0){
Thread.sleep(2000);
}
createBat();
run_cmd("cmd /c" + batPath);
copy(i);
System.out.println("正在处理第 " + i + "个视频");
}
}
/**
* 复制文件集中到指定目录
*/
public static void copy(int i) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(mp4Path + "\\output.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\Videos\\createdMp4\\output" + i + ".mp4"));
byte[] bytes = new byte[1024];
int num = 0;
while ((num = bis.read(bytes)) != -1){
bos.write(bytes,0,num);
}
bos.close();
bis.close();
}
/**
* 创建bat文件
*/
public static void createBat() throws IOException{
//在当前目录创建bat文件
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(batPath));
//把命令写入bat文件
bufferedWriter.write("cd " + mp4Path);
bufferedWriter.newLine();
bufferedWriter.write("ffmpeg -i video.m4s -i audio.m4s -codec copy output.mp4");
bufferedWriter.newLine();
//执行完后退出黑窗口
bufferedWriter.write("exit");
bufferedWriter.close();
}
/**
* 运行bat文件
* 感谢_Line_老哥,提供了Java打开批处理文件的办法。
*/
public static void run_cmd(String strcmd) throws IOException, InterruptedException {
Runtime rt = Runtime.getRuntime();
Process ps = null;
//该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序
// 并返回与该子进程对应的Process对象实例。
ps = rt.exec(strcmd);
//等待子进程完成再往下执行。
ps.waitFor();
//销毁子进程
ps.destroy();
}
}