新增FFmpeg转码功能

This commit is contained in:
yutou 2021-04-01 18:47:14 +08:00
parent ef87b719fd
commit 51a0a3d111
7 changed files with 256 additions and 21 deletions

View File

@ -28,6 +28,7 @@
<script type="text/html" id="listTools"> <script type="text/html" id="listTools">
<a class="layui-btn layui-btn-xs" lay-event="play">播放</a> <a class="layui-btn layui-btn-xs" lay-event="play">播放</a>
<a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="download">下载</a> <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="download">下载</a>
<a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="ffmpeg">转码</a>
</script> </script>
<script src="/layui/layui.js"></script> <script src="/layui/layui.js"></script>
<script src="/js/jquery-3.2.1.js"></script> <script src="/js/jquery-3.2.1.js"></script>
@ -47,9 +48,9 @@
{field: 'name', title: 'UP主/房间号', width: 150, sort: true, fixed: 'left'} {field: 'name', title: 'UP主/房间号', width: 150, sort: true, fixed: 'left'}
, {field: 'date', title: '日期', width: 120, sort: true} , {field: 'date', title: '日期', width: 120, sort: true}
, {field: 'time', title: '时间', width: 80, sort: true} , {field: 'time', title: '时间', width: 80, sort: true}
, {field: 'fileName', title: '文件名', width: 300} , {field: 'fileName', title: '文件名', width: 250}
, {field: 'fileSize', title: '大小', width: 80} , {field: 'fileSize', title: '大小', width: 80}
, {field: "right", width: 120, toolbar: '#listTools'} , {field: "right", width: 170, toolbar: '#listTools'}
]] ]]
}); });
table.on('tool(listTools)', function (obj) { table.on('tool(listTools)', function (obj) {
@ -61,6 +62,15 @@
case 'play': case 'play':
play("/download/" + obj.data.date + "/" + encodeURI(obj.data.fileName), obj.data.fileName); play("/download/" + obj.data.date + "/" + encodeURI(obj.data.fileName), obj.data.fileName);
break; break;
case 'ffmpeg':
$.post('/bili/video/set/ffmpeg.do',{fileName:encodeURI(obj.data.fileName)},function (json) {
if (json.code === undefined || json.code !== 0) {
layer.msg("您无权操作")
return;
}
layer.msg(json.msg)
})
break;
} }
}) })
@ -79,13 +89,11 @@
, area: ['640px', '480px'] , area: ['640px', '480px']
, maxmin: false , maxmin: false
, resize: false , resize: false
, content: '<div><video id="' + id + '" style="height: 100%;width: 100%">' + , content: '<div><video id="' + id + '" style="height: 100%;width: 100%" controls>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="pause">暂停/播放</a>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="reset">重播</a>' + '</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="reset">重播</a>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="danmu">弹幕</a>' + '<a href="javascript:;" class="layui-btn layui-btn-xs" id="danmu">弹幕</a>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="volume">静音</a>' + '<a href="javascript:;" class="layui-btn layui-btn-xs" id="seekDown">-10s</a>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="seekDown">-10s</a>' + '<a href="javascript:;" class="layui-btn layui-btn-xs" id="seekUp">+10s</a>' +
'</video><a href="javascript:;" class="layui-btn layui-btn-xs" id="seekUp">+10s</a>' +
'</div>' '</div>'
, success: function (layero, index) { , success: function (layero, index) {
@ -100,8 +108,12 @@
stashInitialSize: 512 * 1024, stashInitialSize: 512 * 1024,
enableStashBuffer: true enableStashBuffer: true
} }
let playType = 'flv'
if (url.indexOf('_ffmpeg') !== -1) {
playType = 'mp4'
}
flvPlayer = flvjs.createPlayer({ flvPlayer = flvjs.createPlayer({
type: 'flv', type: playType,
url: url url: url
}, playerConfig) }, playerConfig)

View File

@ -57,13 +57,23 @@
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">开放注册</label> <label class="layui-form-label">开放注册</label>
<div class="layui-input-block"> <div class="layui-input-block">
<input id="userReg" type="checkbox" lay-filter="userReg" lay-skin="switch" lay-text="开启|关闭"> <input id="userReg" type="checkbox" lay-filter="userReg" lay-skin="switch"
lay-text="开启|关闭">
</div> </div>
</div> </div>
<div class="layui-form-item"> <div class="layui-form-item">
<label class="layui-form-label">保存阿B数据</label> <label class="layui-form-label">保存阿B数据</label>
<div class="layui-input-block"> <div class="layui-input-block">
<input id="saveBiliLive" type="checkbox" lay-filter="saveBiliLive" lay-skin="switch" lay-text="开启|关闭"> <input id="saveBiliLive" type="checkbox" lay-filter="saveBiliLive" lay-skin="switch"
lay-text="开启|关闭">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">FFmpeg</label>
<div class="layui-input-block">
<input type="text" id="ffmpeg" required lay-verify="required"
placeholder="ffmpeg绝对路径,回车保存"
autocomplete="off" class="layui-input">
</div> </div>
</div> </div>
</form> </form>
@ -135,6 +145,13 @@
$('#bili_login_text').text(json.data.uname); $('#bili_login_text').text(json.data.uname);
$('#bili_icon_img').attr("src", json.data.icon); $('#bili_icon_img').attr("src", json.data.icon);
})
$.post('/system/get/ffmpeg.do', function (json) {
if (json.code === undefined || json.code !== 0) {
return;
}
$('#ffmpeg').val(json.data.ffmpeg_path);
}) })
$('#login').click(function () { $('#login').click(function () {
$.post('/bili/login/set/login.do', function (json) { $.post('/bili/login/set/login.do', function (json) {
@ -157,6 +174,14 @@
}) })
}) })
}) })
$('#ffmpeg').bind('keypress', function (event) {
if (event.keyCode === 13) {
$.post('/system/set/ffmpeg.do', {ffmpeg: encodeURI($('#ffmpeg').val())}, function (json) {
layer.msg(json.msg)
})
event.preventDefault();
}
})
$('#header').load("/html/header.html"); $('#header').load("/html/header.html");
$('#footer').load("/html/footer.html"); $('#footer').load("/html/footer.html");

View File

@ -5,8 +5,11 @@ import com.alibaba.fastjson.JSONObject;
import com.yutou.bilibili.BiliBili.LiveUtils; import com.yutou.bilibili.BiliBili.LiveUtils;
import com.yutou.bilibili.BiliBili.Services.IBiliBiliLiveService; import com.yutou.bilibili.BiliBili.Services.IBiliBiliLiveService;
import com.yutou.bilibili.BiliBili.Tools.BiliTools; import com.yutou.bilibili.BiliBili.Tools.BiliTools;
import com.yutou.bilibili.Services.ISystemConfigService;
import com.yutou.bilibili.Services.IUserService; import com.yutou.bilibili.Services.IUserService;
import com.yutou.bilibili.Tools.AppTools; import com.yutou.bilibili.Tools.AppTools;
import com.yutou.bilibili.Tools.Config;
import com.yutou.bilibili.Tools.FFmpegUtils;
import com.yutou.bilibili.Tools.ServiceTools; import com.yutou.bilibili.Tools.ServiceTools;
import com.yutou.bilibili.mybatis.Bili.mybatis.model.BilibiliUpInfo; import com.yutou.bilibili.mybatis.Bili.mybatis.model.BilibiliUpInfo;
import com.yutou.bilibili.mybatis.model.UUser; import com.yutou.bilibili.mybatis.model.UUser;
@ -19,6 +22,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.File; import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -29,6 +34,8 @@ public class BiliVideoController {
IUserService service; IUserService service;
@Resource @Resource
IBiliBiliLiveService liveService; IBiliBiliLiveService liveService;
@Resource
ISystemConfigService configService;
@ResponseBody @ResponseBody
@RequestMapping("/bili/video/get/list.do") @RequestMapping("/bili/video/get/list.do")
@ -52,9 +59,19 @@ public class BiliVideoController {
List<File> list = AppTools.scanFilePath("live"); List<File> list = AppTools.scanFilePath("live");
JSONArray array = new JSONArray(); JSONArray array = new JSONArray();
for (File f : list) { for (File f : list) {
JSONObject item = new JSONObject();
String _time = f.getName().split(" ")[0].substring(1);
File mpeg = new File("ffmpeg_out" + File.separator + _time + File.separator + f.getName());
if (mpeg.exists()) {
f = mpeg;
item.put("ffmpeg", true);
} else {
item.put("ffmpeg", false);
}
String date = f.getName().split(" ")[0].substring(1); String date = f.getName().split(" ")[0].substring(1);
String time = f.getName().split(" ")[1].split("\\]")[0]; String time = f.getName().split(" ")[1].split("\\]")[0];
String roomid = f.getName().split("\\]")[1].replace(".mp4", "").trim(); String roomid = f.getName().split("\\]")[1].replace(".mp4", "").replace("_ffmpeg", "").trim();
BilibiliUpInfo info = liveService.queryUpToRoomId(Integer.parseInt(roomid)); BilibiliUpInfo info = liveService.queryUpToRoomId(Integer.parseInt(roomid));
long size = f.length(); long size = f.length();
String _size; String _size;
@ -67,7 +84,7 @@ public class BiliVideoController {
} else { } else {
_size = (size / 1073741824) + "GB"; _size = (size / 1073741824) + "GB";
} }
JSONObject item = new JSONObject();
item.put("fileName", f.getName()); item.put("fileName", f.getName());
item.put("fileSize", _size); item.put("fileSize", _size);
item.put("date", date); item.put("date", date);
@ -89,11 +106,34 @@ public class BiliVideoController {
@RequestMapping("/bili/video/download/get.do") @RequestMapping("/bili/video/download/get.do")
public ResponseEntity<FileSystemResource> download(String fileName) { public ResponseEntity<FileSystemResource> download(String fileName) {
System.out.println(fileName);
String time = fileName.split(" ")[0].substring(1); String time = fileName.split(" ")[0].substring(1);
System.out.println(time); File file = new File("ffmpeg_out" + File.separator + time + File.separator + fileName);
File file = new File("live" + File.separator + time + File.separator + fileName); if (!file.exists())
file = new File("live" + File.separator + time + File.separator + fileName);
System.out.println(file.getAbsolutePath()); System.out.println(file.getAbsolutePath());
return AppTools.getFile(file); return AppTools.getFile(file);
} }
@ResponseBody
@RequestMapping("/bili/video/set/ffmpeg.do")
public JSONObject ffmpegOut(String fileName) throws UnsupportedEncodingException {
fileName= URLDecoder.decode(fileName,"UTF-8");
JSONObject json=new JSONObject();
json.put("code",0);
json.put("msg","ok");
String time = fileName.split(" ")[0].substring(1);
File file = new File("live" + File.separator + time + File.separator + fileName);
int i= FFmpegUtils.add(configService.getConfig(Config.SYSTEM_VIDEO_FFMPEG),file,new File("ffmpeg_out"+File.separator+time));
json.put("model",i);
switch (i){
case 0:
json.put("msg","已添加到后台转码");
case 1:
json.put("msg","已经转码,无须二次转码");
break;
case 2:
json.put("msg","正在转码");
break;
}
return json;
}
} }

View File

@ -8,6 +8,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
@Controller @Controller
public class SystemConfigController { public class SystemConfigController {
@ -61,5 +63,25 @@ public class SystemConfigController {
json.put("data",data); json.put("data",data);
return json; return json;
} }
@ResponseBody
@RequestMapping("/system/set/ffmpeg.do")
public JSONObject setFFmpeg(String ffmpeg) throws UnsupportedEncodingException {
ffmpeg= URLDecoder.decode(ffmpeg,"UTF-8");
configService.setConfig(Config.SYSTEM_VIDEO_FFMPEG,ffmpeg);
JSONObject json=new JSONObject();
json.put("code",0);
json.put("msg","ok");
return json;
}
@ResponseBody
@RequestMapping("/system/get/ffmpeg.do")
public JSONObject getFFmpeg(){
JSONObject json = new JSONObject();
JSONObject data=new JSONObject();
String reg = configService.getConfig(Config.SYSTEM_VIDEO_FFMPEG);
data.put(Config.SYSTEM_VIDEO_FFMPEG, reg);
json.put("code",0);
json.put("data",data);
return json;
}
} }

View File

@ -248,4 +248,60 @@ public class AppTools {
} }
return ""; return "";
} }
public static boolean copyFileToName(String srcFileName,String destFileName,String fileName,boolean overlay) {
File srcFile = new File(srcFileName);
// 判断源文件是否存在
if (!srcFile.exists()) {
System.err.println("源文件不存在:"+srcFile.getAbsolutePath()+" > "+destFileName);
return false;
} else if (!srcFile.isFile()) {
System.err.println("源文件是目录:"+srcFile.getAbsolutePath());
return false;
}
// 判断目标文件是否存在
File destFile = new File(destFileName);
// 如果目标文件所在目录不存在则创建目录
if (!destFile.exists()) {
// 目标文件所在目录不存在
if (!destFile.mkdirs()) {
// 复制文件失败创建目标文件所在目录失败
System.err.println("创建文件夹失败:"+destFile.getAbsolutePath());
return false;
}
}else{
if(srcFileName.equals("Activity.smali")){
System.out.println("文件夹已存在:"+destFileName);
}
}
// 复制文件
int byteread = 0; // 读取的字节数
InputStream in = null;
OutputStream out = null;
try {
if(fileName==null) {
fileName=srcFile.getName();
}
in = new FileInputStream(srcFile);
out = new FileOutputStream(destFile + File.separator +fileName );
byte[] buffer = new byte[1024];
while ((byteread = in.read(buffer)) != -1) {
out.write(buffer, 0, byteread);
}
out.close();
in.close();
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
} }

View File

@ -3,4 +3,5 @@ package com.yutou.bilibili.Tools;
public class Config { public class Config {
public static final String USER_REG="userReg"; public static final String USER_REG="userReg";
public static final String BILI_LIVE_FLAG="biliLive"; public static final String BILI_LIVE_FLAG="biliLive";
public static final String SYSTEM_VIDEO_FFMPEG="ffmpeg_path";
} }

View File

@ -0,0 +1,79 @@
package com.yutou.bilibili.Tools;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class FFmpegUtils {
private static final List<File> nowFFmpegList = new ArrayList<>();
public static int add(String ffmpeg, File file, File out) {
if (nowFFmpegList.contains(file)) {
return 2;
}
File tmp = new File(file.getAbsolutePath().replace(".mp4", "_ffmpeg.mp4"));
if (tmp.exists()) {
return 1;
} else {
startFFmpeg(ffmpeg, file, out);
}
return 0;
}
private synchronized static void startFFmpeg(String ffmpeg, File file, File out) {
if (nowFFmpegList.contains(file)) {
return;
}
nowFFmpegList.add(file);
new Thread(new Runnable() {
@Override
public void run() {
try {
if (!out.exists()) {
out.mkdirs();
}
String exec = String.format(
"%s -i \"%s\" -c:v copy -y \"%s\" ",
ffmpeg,
file.getAbsolutePath(),
out.getAbsolutePath() + File.separator + file.getName());
System.out.println(exec);
Process process = Runtime.getRuntime().exec(new String[]{
"cmd",
"/c",
exec
}
);
InputStream inputStream = process.getErrorStream();
byte[] bytes = new byte[1024];
while (inputStream.read(bytes) > -1) {
}
inputStream.close();
AppTools.copyFileToName(out.getAbsolutePath()+File.separator+file.getName(), file.getParent() + File.separator, file.getName().replace(".mp4", "_ffmpeg.mp4"), true);
// file.delete();
out.delete();
} catch (Exception e) {
e.printStackTrace();
}
nowFFmpegList.remove(file);
}
}).start();
}
public static void main(String[] args) {
File file = new File("D:\\ieda\\bilibili\\live\\2021-03-20\\[2021-03-20 003537]1064046.mp4");
int i = -1;
while (i != 1) {
i = add("D:\\ffmpeg-4.3.1-2020-11-19-full_build\\bin\\ffmpeg.exe", file.getAbsoluteFile(), new File("ffmpeg_out"));
System.out.println(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("转码完成");
}
}