Skip to content

Commit

Permalink
Merge pull request #37 from Tencent/feature/vap_java_tool
Browse files Browse the repository at this point in the history
vaptool java
  • Loading branch information
hexleo authored Dec 16, 2020
2 parents 841f465 + 0b40b94 commit 70d4e6c
Show file tree
Hide file tree
Showing 344 changed files with 1,394 additions and 285 deletions.
2 changes: 1 addition & 1 deletion Android/PlayerProj/animplayer/publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ext {
// library artifact(单个module一般就填写library name)
artifact = 'animplayer'
libraryName = 'animplayer'
libraryVersion = '2.0.9'
libraryVersion = '2.0.10'
libraryDescription = ''
// bintrayName 是你在网页Repository页面能看到的名称
bintrayName = 'vap'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,21 @@ class AnimConfigManager(val player: AnimPlayer) {
* 解析配置
* @return true 解析成功 false 解析失败
*/
fun parseConfig(fileContainer: FileContainer, defaultVideoMode: Int, defaultFps: Int): Int {
fun parseConfig(fileContainer: FileContainer, enableVersion1: Boolean, defaultVideoMode: Int, defaultFps: Int): Int {
try {
isParsingConfig = true
// 解析vapc
val time = SystemClock.elapsedRealtime()
val result = parse(fileContainer, defaultVideoMode, defaultFps)
ALog.i(TAG, "parseConfig cost=${SystemClock.elapsedRealtime() - time}ms")
ALog.i(TAG, "parseConfig cost=${SystemClock.elapsedRealtime() - time}ms enableVersion1=$enableVersion1 result=$result")
if (!result) {
isParsingConfig = false
return Constant.REPORT_ERROR_TYPE_PARSE_CONFIG
}
if (config?.isDefaultConfig == true && !enableVersion1) {
isParsingConfig = false
return Constant.REPORT_ERROR_TYPE_PARSE_CONFIG
}
// 插件解析配置
val resultCode = config?.let {
player.pluginManager.onConfigCreate(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class AnimPlayer(val animView: AnimView) {
}
var supportMaskBoolean : Boolean = false
var maskEdgeBlurBoolean : Boolean = false
// 是否兼容老版本 默认不兼容
var enableVersion1 : Boolean = false
// 视频模式
var videoMode: Int = Constant.VIDEO_MODE_SPLIT_HORIZONTAL
var isDetachedFromWindow = false
Expand Down Expand Up @@ -79,7 +81,7 @@ class AnimPlayer(val animView: AnimView) {
}
// 在线程中解析配置
decoder?.renderThread?.handler?.post {
val result = configManager.parseConfig(fileContainer, videoMode, fps)
val result = configManager.parseConfig(fileContainer, enableVersion1, videoMode, fps)
if (result != Constant.OK) {
decoder?.onFailed(result, Constant.getErrorMsg(result))
isStartRunning = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ open class AnimView @JvmOverloads constructor(context: Context, attrs: Attribute
player?.updateMaskConfig(maskConfig)
}


@Deprecated("Compatible older version mp4, default false")
fun enableVersion1(enable: Boolean) {
player?.enableVersion1 = enable
}

// 兼容老版本视频模式
@Deprecated("Compatible older version mp4")
fun setVideoMode(mode: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
*/
package com.tencent.qgame.playerproj.animtool;

import com.tencent.qgame.playerproj.animtool.vapx.FrameSet;
import com.tencent.qgame.playerproj.animtool.vapx.GetMaskFrame;
import com.tencent.qgame.playerproj.animtool.vapx.SrcSet;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;

public class AnimTool {

Expand All @@ -32,7 +33,8 @@ public class AnimTool {
public static final String OUTPUT_DIR = "output"+ File.separator;
public static final String FRAME_IMAGE_DIR = "frames"+ File.separator;
public static final String VIDEO_FILE = "video.mp4";
public static final String TEM_VIDEO_FILE = "tmp_video.mp4";
public static final String TEMP_VIDEO_FILE = "tmp_video.mp4";
public static final String TEMP_VIDEO_AUDIO_FILE = "tmp_video_audio.mp4";
public static final String VAPC_BIN_FILE = "vapc.bin";
public static final String VAPC_JSON_FILE = "vapc.json";

Expand All @@ -42,6 +44,7 @@ public class AnimTool {
private volatile int finishThreadCount = 0;
private long time;
private GetAlphaFrame getAlphaFrame = new GetAlphaFrame();
private GetMaskFrame getMaskFrame = new GetMaskFrame();
private IToolListener toolListener;

public void setToolListener(IToolListener toolListener) {
Expand All @@ -57,7 +60,7 @@ public void create(final CommonArg commonArg, final boolean needVideo) throws Ex
createAllFrameImage(commonArg, new Runnable() {
@Override
public void run() {
if (needVideo) {
if (finalCheck(commonArg) && needVideo) {
// 最终生成视频文件
createVideo(commonArg);
}
Expand All @@ -71,7 +74,23 @@ public void run() {
* @return
*/
private boolean checkCommonArg(CommonArg commonArg) throws Exception {
return CommonArgTool.autoFillAndCheck(commonArg);
return CommonArgTool.autoFillAndCheck(commonArg, toolListener);
}

private boolean finalCheck(CommonArg commonArg) {
if (commonArg.isVapx) {
if (commonArg.srcSet.srcs.isEmpty()) {
TLog.i(TAG, "vapx error: src is empty");
return false;
}
for (SrcSet.Src src : commonArg.srcSet.srcs) {
if (src.w <=0 || src.h <= 0) {
TLog.i(TAG, "vapx error: src.id=" + src.srcId + ",src.w=" + src.w + ",src.h=" + src.h);
return false;
}
}
}
return true;
}

private void createAllFrameImage(final CommonArg commonArg, final Runnable finishRunnable) throws Exception{
Expand Down Expand Up @@ -107,6 +126,11 @@ private void createAllFrameImage(final CommonArg commonArg, final Runnable finis
@Override
public void run() {
for(int i = threadIndexSet[k][0]; i<threadIndexSet[k][1]; i++) {
try {
createFrame(commonArg, i);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (AnimTool.class) {
totalP++;
float progress = totalP * 1.0f / commonArg.totalFrame;
Expand All @@ -116,11 +140,6 @@ public void run() {
TLog.i(TAG, "progress " + progress);
}
}
try {
createFrame(commonArg, i);
} catch (Exception e) {
e.printStackTrace();
}
}
synchronized (AnimTool.class) {
finishThreadCount++;
Expand All @@ -141,18 +160,21 @@ public void run() {
}

private void createFrame(CommonArg commonArg, int frameIndex) throws Exception {
int w = commonArg.videoW;
int h = commonArg.videoH;
File inputFile = new File(commonArg.inputPath + String.format("%03d", frameIndex)+".png");
GetAlphaFrame.AlphaFrameOut videoFrame = getAlphaFrame.createFrame(commonArg.orin, w, h,
commonArg.gap, commonArg.wFill, commonArg.hFill, inputFile);
GetAlphaFrame.AlphaFrameOut videoFrame = getAlphaFrame.createFrame(commonArg, inputFile);
if (commonArg.isVapx) {
FrameSet.FrameObj frameObj = getMaskFrame.getFrameObj(frameIndex, commonArg, videoFrame.argb);
if (frameObj != null) {
commonArg.frameSet.frameObjs.add(frameObj);
}
}
if (videoFrame == null) {
TLog.i(TAG, "frameIndex="+frameIndex +" is empty");
return;
}
// 最后保存图片
BufferedImage outBuf = new BufferedImage(videoFrame.outW, videoFrame.outH, BufferedImage.TYPE_INT_ARGB);
outBuf.setRGB(0,0, videoFrame.outW, videoFrame.outH, videoFrame.argb, 0, videoFrame.outW);
BufferedImage outBuf = new BufferedImage(commonArg.outputW, commonArg.outputH, BufferedImage.TYPE_INT_ARGB);
outBuf.setRGB(0,0, commonArg.outputW, commonArg.outputH, videoFrame.argb, 0, commonArg.outputW);

File outputFile = new File(commonArg.frameOutputPath + String.format("%03d", frameIndex) +".png");
ImageIO.write(outBuf, "PNG", outputFile);
Expand Down Expand Up @@ -180,17 +202,30 @@ private void createVideo(CommonArg commonArg){
TLog.i(TAG, "createMp4 fail");
return;
}
String tempVideoName = TEMP_VIDEO_FILE;
if (commonArg.needAudio) {
result = mergeAudio2Mp4(commonArg, tempVideoName);
if (!result) {
TLog.i(TAG, "mergeAudio2Mp4 fail");
return;
}
tempVideoName = TEMP_VIDEO_AUDIO_FILE;
}

String input = commonArg.outputPath + VAPC_JSON_FILE;
// 由json变为bin文件
String vapcBinPath = mp4BoxTool(input, commonArg.outputPath);
// 将bin文件合并到mp4里
result = mergeBin2Mp4(commonArg, vapcBinPath, commonArg.outputPath);
result = mergeBin2Mp4(commonArg, vapcBinPath, tempVideoName, commonArg.outputPath);
if (!result) {
TLog.i(TAG, "mergeBin2Mp4 fail");
return;
}
// 删除临时视频文件
new File(commonArg.outputPath + TEM_VIDEO_FILE).delete();
new File(commonArg.outputPath + TEMP_VIDEO_FILE).delete();
if (commonArg.needAudio) {
new File(commonArg.outputPath + TEMP_VIDEO_AUDIO_FILE).delete();
}
new File(commonArg.outputPath + VAPC_BIN_FILE).delete();
// 计算文件md5
String md5 = new Md5Util().getFileMD5(new File(commonArg.outputPath + VIDEO_FILE), commonArg.outputPath);
Expand All @@ -205,36 +240,34 @@ private void createVideo(CommonArg commonArg){
* @param commonArg
*/
private void createVapcJson(CommonArg commonArg) {
String json = "{\"info\":{\"v\":$(v),\"f\":$(f),\"w\":$(w),\"h\":$(h),\"videoW\":$(videoW),\"videoH\":$(videoH),\"orien\":0,\"fps\":$(fps),\"isVapx\":0,\"aFrame\":$(aFrame),\"rgbFrame\":$(rgbFrame)}}";
json = json.replace("$(v)", String.valueOf(commonArg.version));
json = json.replace("$(f)", String.valueOf(commonArg.totalFrame));
json = json.replace("$(w)", String.valueOf(commonArg.videoW));
json = json.replace("$(h)", String.valueOf(commonArg.videoH));
json = json.replace("$(fps)", String.valueOf(commonArg.fps));
int realW = 0;
int realH = 0;
int cx, cy;
String aFrame = "[0,0,"+commonArg.videoW+","+commonArg.videoH+"]";
String rgbFrame = "[0,0,0,0]";
if (commonArg.orin == CommonArg.ORIN_H) { // 水平对齐
realW = 2 * commonArg.videoW + commonArg.gap;
realH = commonArg.videoH;
cx = commonArg.videoW + commonArg.gap;
cy = 0;
} else { // 上下对齐
realW = commonArg.videoW;
realH = 2 * commonArg.videoH + commonArg.gap;
cx = 0;
cy = commonArg.videoH + commonArg.gap;

String json = "\"info\":{" +
"\"v\":" + commonArg.version + "," +
"\"f\":" + commonArg.totalFrame + "," +
"\"w\":" + commonArg.rgbPoint.w + "," +
"\"h\":" + commonArg.rgbPoint.h + "," +
"\"fps\":" + commonArg.fps + "," +
"\"videoW\":" + commonArg.outputW + "," +
"\"videoH\":" + commonArg.outputH + "," +
"\"aFrame\":" + commonArg.alphaPoint.toString() + "," +
"\"rgbFrame\":" + commonArg.rgbPoint.toString() + "," +
"\"isVapx\":" + (commonArg.isVapx ? 1 : 0) + "," +
"\"orien\":" + 0 +
"}";
TLog.i(TAG, "{" + json + "}");

StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(json);
if (commonArg.isVapx) {
sb.append(",");
sb.append(commonArg.srcSet.toString());
sb.append(",");
sb.append(commonArg.frameSet.toString());
}
rgbFrame = "["+cx+","+cy+","+commonArg.videoW+","+commonArg.videoH+"]";

realW += commonArg.wFill;
realH += commonArg.hFill;
json = json.replace("$(videoW)", String.valueOf(realW));
json = json.replace("$(videoH)", String.valueOf(realH));
json = json.replace("$(aFrame)", aFrame);
json = json.replace("$(rgbFrame)", rgbFrame);
sb.append("}");
json = sb.toString();

try {
BufferedWriter writer = new BufferedWriter(new FileWriter(commonArg.outputPath + VAPC_JSON_FILE));
writer.write(json);
Expand All @@ -244,8 +277,6 @@ private void createVapcJson(CommonArg commonArg) {
e.printStackTrace();
throw new RuntimeException();
}
TLog.i(TAG,json);

}


Expand All @@ -254,8 +285,6 @@ private void createVapcJson(CommonArg commonArg) {

/**
* 创建mp4
* @param commonArg
* @throws Exception
*/
private boolean createMp4(CommonArg commonArg, String videoPath, String frameImagePath) throws Exception {
String[] cmd = null;
Expand All @@ -269,17 +298,18 @@ private boolean createMp4(CommonArg commonArg, String videoPath, String frameIma
"-level", "4.0",
"-tag:v", "hvc1",
"-bufsize", "2000k",
"-y", videoPath + TEM_VIDEO_FILE};
"-y", videoPath + TEMP_VIDEO_FILE};
} else {
cmd = new String[]{commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
"-i", frameImagePath + "%03d.png",
"-pix_fmt", "yuv420p",
"-vcodec", "libx264",
"-b:v", "3000k",
"-profile:v", "baseline",
"-level", "3.0",
"-profile:v", "main",
"-level", "4.0",
"-bf", "0",
"-y", videoPath + TEM_VIDEO_FILE};
"-bufsize", "3000k",
"-y", videoPath + TEMP_VIDEO_FILE};
}

TLog.i(TAG, "run createMp4");
Expand All @@ -288,13 +318,28 @@ private boolean createMp4(CommonArg commonArg, String videoPath, String frameIma
return result == 0;
}

/**
* 合并音频文件
*/
private boolean mergeAudio2Mp4(CommonArg commonArg, String tempVideoFile) throws Exception {
String[] cmd = new String[] {commonArg.ffmpegCmd,
"-i", commonArg.audioPath,
"-i", commonArg.outputPath + tempVideoFile,
"-c:v", "copy",
"-c:a", "aac",
"-y", commonArg.outputPath + TEMP_VIDEO_AUDIO_FILE};
TLog.i(TAG, "run mergeAudio2Mp4");
int result = ProcessUtil.run(cmd);
TLog.i(TAG, "mergeAudio2Mp4 result=" + (result == 0? "success" : "fail"));
return result == 0;
}


/**
* 合并vapc.bin到mp4里
* @param inputFile
* @throws Exception
*/
private boolean mergeBin2Mp4(CommonArg commonArg, String inputFile, String videoPath) throws Exception{
String[] cmd = new String[] {commonArg.mp4editCmd, "--insert", ":"+inputFile+":1", videoPath + TEM_VIDEO_FILE, videoPath + VIDEO_FILE};
private boolean mergeBin2Mp4(CommonArg commonArg, String inputFile, String tempVideoFile, String videoPath) throws Exception{
String[] cmd = new String[] {commonArg.mp4editCmd, "--insert", ":"+inputFile+":1", videoPath + tempVideoFile, videoPath + VIDEO_FILE};
TLog.i(TAG, "run mergeBin2Mp4");
int result = ProcessUtil.run(cmd);
TLog.i(TAG, "mergeBin2Mp4 result=" + (result == 0? "success" : "fail"));
Expand All @@ -313,6 +358,7 @@ private String mp4BoxTool(String inputFile, String outputPath) throws Exception

public interface IToolListener {
void onProgress(float progress);
void onWarning(String msg);
void onError();
void onComplete();
}
Expand Down
Loading

0 comments on commit 70d4e6c

Please sign in to comment.