Browse Source

Merge pull request #137 from Tencent/feature/crf

VapTool添加crf选项
hexleo 4 years ago
parent
commit
6e73c1619d

+ 59 - 26
Android/PlayerProj/animtool/src/main/java/com/tencent/qgame/playerproj/animtool/AnimTool.java

@@ -133,7 +133,7 @@ public class AnimTool {
                         try {
                         try {
                             createFrame(commonArg, i);
                             createFrame(commonArg, i);
                         } catch (Exception e) {
                         } catch (Exception e) {
-                            e.printStackTrace();
+                            TLog.e(TAG, "createFrame error:" + e.getMessage());
                         }
                         }
                         synchronized (AnimTool.class) {
                         synchronized (AnimTool.class) {
                             totalP++;
                             totalP++;
@@ -296,7 +296,7 @@ public class AnimTool {
             writer.flush();
             writer.flush();
             writer.close();
             writer.close();
         } catch (IOException e) {
         } catch (IOException e) {
-            e.printStackTrace();
+            TLog.e(TAG, "createVapcJson error:" + e.getMessage());
             throw new RuntimeException();
             throw new RuntimeException();
         }
         }
     }
     }
@@ -309,35 +309,68 @@ public class AnimTool {
      * 创建mp4
      * 创建mp4
      */
      */
     private boolean createMp4(CommonArg commonArg, String videoPath, String frameImagePath) throws Exception {
     private boolean createMp4(CommonArg commonArg, String videoPath, String frameImagePath) throws Exception {
+
+        TLog.i(TAG, "run createMp4");
+        int result = ProcessUtil.run(getFFmpegCmd(commonArg, videoPath, frameImagePath));
+        TLog.i(TAG, "createMp4 result=" + (result == 0? "success" : "fail"));
+        return result == 0;
+    }
+
+    private String[] getFFmpegCmd(CommonArg commonArg, String videoPath, String frameImagePath) {
         String[] cmd;
         String[] cmd;
         if (commonArg.enableH265) {
         if (commonArg.enableH265) {
-            cmd = new String[] {commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
-                    "-i", frameImagePath + "%03d.png",
-                    "-pix_fmt", "yuv420p",
-                    "-vcodec", "libx265",
-                    "-b:v", commonArg.bitrate + "k",
-                    "-profile:v", "main",
-                    "-level", "4.0",
-                    "-tag:v", "hvc1",
-                    "-bufsize", "2000k",
-                    "-y", videoPath + TEMP_VIDEO_FILE};
+            if (commonArg.enableCrf) {
+                cmd = new String[] {commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
+                        "-i", frameImagePath + "%03d.png",
+                        "-pix_fmt", "yuv420p",
+                        "-vcodec", "libx265",
+                        "-crf", Integer.toString(commonArg.crf),
+                        "-profile:v", "main",
+                        "-level", "4.0",
+                        "-tag:v", "hvc1",
+                        "-bufsize", "2000k",
+                        "-y", videoPath + TEMP_VIDEO_FILE};
+            } else {
+                cmd = new String[] {commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
+                        "-i", frameImagePath + "%03d.png",
+                        "-pix_fmt", "yuv420p",
+                        "-vcodec", "libx265",
+                        "-b:v", commonArg.bitrate + "k",
+                        "-profile:v", "main",
+                        "-level", "4.0",
+                        "-tag:v", "hvc1",
+                        "-bufsize", "2000k",
+                        "-y", videoPath + TEMP_VIDEO_FILE};
+            }
+
         } else {
         } else {
-            cmd = new String[]{commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
-                    "-i", frameImagePath + "%03d.png",
-                    "-pix_fmt", "yuv420p",
-                    "-vcodec", "libx264",
-                    "-b:v", commonArg.bitrate + "k",
-                    "-profile:v", "main",
-                    "-level", "4.0",
-                    "-bf", "0",
-                    "-bufsize", "2000k",
-                    "-y", videoPath + TEMP_VIDEO_FILE};
+            if (commonArg.enableCrf) {
+                cmd = new String[]{commonArg.ffmpegCmd, "-r", String.valueOf(commonArg.fps),
+                        "-i", frameImagePath + "%03d.png",
+                        "-pix_fmt", "yuv420p",
+                        "-vcodec", "libx264",
+                        "-crf", Integer.toString(commonArg.crf),
+                        "-profile:v", "main",
+                        "-level", "4.0",
+                        "-bf", "0",
+                        "-bufsize", "2000k",
+                        "-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", commonArg.bitrate + "k",
+                        "-profile:v", "main",
+                        "-level", "4.0",
+                        "-bf", "0",
+                        "-bufsize", "2000k",
+                        "-y", videoPath + TEMP_VIDEO_FILE};
+            }
+
         }
         }
 
 
-        TLog.i(TAG, "run createMp4");
-        int result = ProcessUtil.run(cmd);
-        TLog.i(TAG, "createMp4 result=" + (result == 0? "success" : "fail"));
-        return result == 0;
+        return cmd;
     }
     }
 
 
     /**
     /**

+ 7 - 1
Android/PlayerProj/animtool/src/main/java/com/tencent/qgame/playerproj/animtool/CommonArg.java

@@ -27,14 +27,18 @@ public class CommonArg {
 
 
     public boolean enableH265 = false; // 是否开启h265
     public boolean enableH265 = false; // 是否开启h265
 
 
-    public int fps = 24;
+    public int fps = 25;
 
 
     public String inputPath; // 输入帧文件地址
     public String inputPath; // 输入帧文件地址
 
 
     public float scale = 0.5f; // alpha 区域缩放大小
     public float scale = 0.5f; // alpha 区域缩放大小
 
 
+    public boolean enableCrf = false; // 是否开启可变码率
+
     public int bitrate = 2000; // 码率
     public int bitrate = 2000; // 码率
 
 
+    public int crf = 29; // 0(无损) - 50(最大压缩)
+
     /**
     /**
      * 自动填充参数配置
      * 自动填充参数配置
      */
      */
@@ -78,7 +82,9 @@ public class CommonArg {
                 ", mp4editCmd='" + mp4editCmd + '\'' +
                 ", mp4editCmd='" + mp4editCmd + '\'' +
                 ", enableH265=" + enableH265 +
                 ", enableH265=" + enableH265 +
                 ", fps=" + fps +
                 ", fps=" + fps +
+                ", enableCrf=" + enableCrf +
                 ", bitrate=" + bitrate +
                 ", bitrate=" + bitrate +
+                ", crf=" + crf +
                 ", scale=" + scale +
                 ", scale=" + scale +
                 ", inputPath='" + inputPath + '\'' +
                 ", inputPath='" + inputPath + '\'' +
                 ", needAudio=" + needAudio + '\'' +
                 ", needAudio=" + needAudio + '\'' +

+ 18 - 11
Android/PlayerProj/animtool/src/main/java/com/tencent/qgame/playerproj/animtool/CommonArgTool.java

@@ -138,14 +138,14 @@ class CommonArgTool {
         }
         }
 
 
         // 计算出 16倍数的视频
         // 计算出 16倍数的视频
-        int[] size = calSizeFill(commonArg.outputW, commonArg.outputH, 0, 0);
+        int[] size = calSizeFill(commonArg.outputW, commonArg.outputH);
         // 得到最终视频宽高
         // 得到最终视频宽高
         commonArg.outputW += size[0];
         commonArg.outputW += size[0];
         commonArg.outputH += size[1];
         commonArg.outputH += size[1];
 
 
         if (commonArg.outputW > 1504 || commonArg.outputH > 1504) {
         if (commonArg.outputW > 1504 || commonArg.outputH > 1504) {
             String msg = "[Warning] Output video width:" + commonArg.outputW + " or height:" + commonArg.outputH
             String msg = "[Warning] Output video width:" + commonArg.outputW + " or height:" + commonArg.outputH
-                    + " is over 1504. Some devices will display exception, like video turn green!";
+                    + " is over 1504. Some devices will display exception. For example green screen!";
             TLog.w(TAG, msg);
             TLog.w(TAG, msg);
             if (toolListener != null) {
             if (toolListener != null) {
                 toolListener.onWarning(msg);
                 toolListener.onWarning(msg);
@@ -170,27 +170,34 @@ class CommonArgTool {
         }
         }
 
 
         // 码率检查
         // 码率检查
-        if (commonArg.bitrate <= 0) {
+        if (!commonArg.enableCrf && commonArg.bitrate <= 0) {
             TLog.e(TAG, "bitrate=" + commonArg.bitrate);
             TLog.e(TAG, "bitrate=" + commonArg.bitrate);
             return false;
             return false;
         }
         }
 
 
+        // crf检查
+        if (commonArg.enableCrf && (commonArg.crf < 0 || commonArg.crf > 51)) {
+            TLog.e(TAG, "crf=" + commonArg.crf + ", no in [0, 51]");
+            return false;
+        }
+
         return true;
         return true;
     }
     }
 
 
     /**
     /**
      * 寻找最小wFill & hFill情况下 整个视频宽高能被16整除
      * 寻找最小wFill & hFill情况下 整个视频宽高能被16整除
      */
      */
-    private static int[] calSizeFill(int outW, int outH, int wFill, int hFill) {
-        boolean wCheck = (outW + wFill)% 16 == 0;
-        boolean hCheck = (outH + hFill) % 16 == 0;
-
-        if (wCheck && hCheck) {
-            return new int[]{wFill, hFill};
+    private static int[] calSizeFill(int outW, int outH) {
+        int wFill = 0;
+        if (outW % 16 != 0) {
+            wFill = ((outW / 16) + 1) * 16 - outW;
         }
         }
 
 
-        // 递归计算
-        return calSizeFill(outW, outH, wCheck? wFill : wFill + 1, hCheck? hFill : hFill + 1);
+        int hFill = 0;
+        if (outH % 16 != 0) {
+            hFill = ((outH / 16) + 1) * 16 - outH;
+        }
+        return new int[]{wFill, hFill};
     }
     }
 
 
 
 

+ 85 - 8
Android/PlayerProj/animtool/src/main/java/com/tencent/qgame/playerproj/animtool/ui/ToolUI.java

@@ -11,6 +11,8 @@ import java.awt.GridLayout;
 import java.awt.Toolkit;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseEvent;
 import java.io.File;
 import java.io.File;
@@ -44,9 +46,9 @@ public class ToolUI {
     private static final String TAG = "ToolUI";
     private static final String TAG = "ToolUI";
     private static final String PROPERTIES_FILE = "setting.properties";
     private static final String PROPERTIES_FILE = "setting.properties";
     public static final int WIDTH = 900;
     public static final int WIDTH = 900;
-    public static final int HEIGHT = 700;
+    public static final int HEIGHT = 750;
 
 
-    private final JFrame frame = new JFrame("VAP tool");
+    private final JFrame frame = new JFrame("VAP tool 2.0.3");
     private final ButtonGroup group = new ButtonGroup();
     private final ButtonGroup group = new ButtonGroup();
     private final JRadioButton btnH264 = new JRadioButton("h264");
     private final JRadioButton btnH264 = new JRadioButton("h264");
     private final JRadioButton btnH265 = new JRadioButton("h265");
     private final JRadioButton btnH265 = new JRadioButton("h265");
@@ -58,7 +60,15 @@ public class ToolUI {
     private final JTextArea txtAreaLog = new JTextArea();
     private final JTextArea txtAreaLog = new JTextArea();
     private final JTextField textAudioPath = new JTextField();
     private final JTextField textAudioPath = new JTextField();
     private final JPanel panelAudioPath = new JPanel();
     private final JPanel panelAudioPath = new JPanel();
+
+    private final JPanel panelBitrate = new JPanel();
     private final JTextField textBitrate = new JTextField();
     private final JTextField textBitrate = new JTextField();
+    private final JPanel panelCrf = new JPanel();
+    private final JTextField textCrf = new JTextField();
+
+    private final ButtonGroup groupQuality = new ButtonGroup();
+    private final JRadioButton btnBitrate = new JRadioButton("bitrate");
+    private final JRadioButton btnCrf = new JRadioButton("crf");
 
 
     private final JLabel labelOutInfo = new JLabel();
     private final JLabel labelOutInfo = new JLabel();
     private final Dimension labelSize = new Dimension(100, 20);
     private final Dimension labelSize = new Dimension(100, 20);
@@ -67,6 +77,19 @@ public class ToolUI {
 
 
     private boolean needAudio = false;
     private boolean needAudio = false;
 
 
+    private final ItemListener qualityGroupListener = new ItemListener() {
+        @Override
+        public void itemStateChanged(ItemEvent itemEvent) {
+            if (itemEvent.getSource() == btnBitrate) {
+                panelBitrate.setVisible(true);
+                panelCrf.setVisible(false);
+            } else if (itemEvent.getSource() == btnCrf) {
+                panelBitrate.setVisible(false);
+                panelCrf.setVisible(true);
+            }
+        }
+    };
+
     public ToolUI() {
     public ToolUI() {
         TLog.logger = new TLog.ITLog() {
         TLog.logger = new TLog.ITLog() {
             @Override
             @Override
@@ -105,8 +128,18 @@ public class ToolUI {
             textInputPath.setText(commonArg.inputPath);
             textInputPath.setText(commonArg.inputPath);
             textAudioPath.setText(commonArg.audioPath);
             textAudioPath.setText(commonArg.audioPath);
             textBitrate.setText(String.valueOf(commonArg.bitrate));
             textBitrate.setText(String.valueOf(commonArg.bitrate));
+            textCrf.setText(String.valueOf(commonArg.crf));
+            groupQuality.setSelected(commonArg.enableCrf ? btnCrf.getModel() : btnBitrate.getModel(), true);
+            if (commonArg.enableCrf) {
+                panelBitrate.setVisible(false);
+                panelCrf.setVisible(true);
+            } else {
+                panelBitrate.setVisible(true);
+                panelCrf.setVisible(false);
+            }
+
             float scale = commonArg.scale;
             float scale = commonArg.scale;
-            for (int i=0; i<scaleArray.length ; i++) {
+            for (int i = 0; i < scaleArray.length ; i++) {
                 if (scaleArray[i] == scale) {
                 if (scaleArray[i] == scale) {
                     boxScale.setSelectedIndex(i);
                     boxScale.setSelectedIndex(i);
                     break;
                     break;
@@ -165,7 +198,9 @@ public class ToolUI {
             }
             }
         }
         }
         try {
         try {
+            commonArg.enableCrf = groupQuality.isSelected(btnCrf.getModel());
             commonArg.bitrate = Integer.parseInt(textBitrate.getText());
             commonArg.bitrate = Integer.parseInt(textBitrate.getText());
+            commonArg.crf = Integer.parseInt(textCrf.getText());
         } catch (NumberFormatException e) {
         } catch (NumberFormatException e) {
             TLog.e(TAG, "bitrate format error " + textBitrate.getText() + e.getMessage());
             TLog.e(TAG, "bitrate format error " + textBitrate.getText() + e.getMessage());
         }
         }
@@ -231,8 +266,12 @@ public class ToolUI {
         panel.add(getCodecLayout());
         panel.add(getCodecLayout());
         // fps
         // fps
         panel.add(getFpsLayout());
         panel.add(getFpsLayout());
+        // bitrate/crf switch
+        panel.add(getQualityLayout());
         // bitrate
         // bitrate
         panel.add(getBitrateLayout());
         panel.add(getBitrateLayout());
+        // crf
+        panel.add(getCrfLayout());
         // scale
         // scale
         panel.add(getScaleLayout());
         panel.add(getScaleLayout());
         // path
         // path
@@ -282,18 +321,50 @@ public class ToolUI {
         return panel;
         return panel;
     }
     }
 
 
-    private JPanel getBitrateLayout() {
+    private JPanel getQualityLayout() {
         JPanel panel = new JPanel();
         JPanel panel = new JPanel();
         panel.setLayout(new FlowLayout(FlowLayout.LEFT));
         panel.setLayout(new FlowLayout(FlowLayout.LEFT));
-        JLabel label = new JLabel("bitrate");
+
+        JLabel label = new JLabel("quality");
         label.setPreferredSize(labelSize);
         label.setPreferredSize(labelSize);
         panel.add(label);
         panel.add(label);
-        textBitrate.setPreferredSize(new Dimension(60, 20));
-        panel.add(textBitrate);
-        panel.add(new JLabel("k (default 2000k)"));
+
+        JPanel panelRadio = new JPanel();
+        panelRadio.setLayout(new GridLayout(1, 2));
+        panelRadio.add(btnBitrate);
+        panelRadio.add(btnCrf);
+        groupQuality.add(btnBitrate);
+        groupQuality.add(btnCrf);
+        groupQuality.setSelected(btnBitrate.getModel(), true);
+        btnBitrate.addItemListener(qualityGroupListener);
+        btnCrf.addItemListener(qualityGroupListener);
+        panel.add(panelRadio);
+
         return panel;
         return panel;
     }
     }
 
 
+    private JPanel getBitrateLayout() {
+        panelBitrate.setLayout(new FlowLayout(FlowLayout.LEFT));
+        JLabel label = new JLabel("bitrate");
+        label.setPreferredSize(labelSize);
+        panelBitrate.add(label);
+        textBitrate.setPreferredSize(new Dimension(60, 20));
+        panelBitrate.add(textBitrate);
+        panelBitrate.add(new JLabel("k (default 2000k)"));
+        return panelBitrate;
+    }
+
+    private JPanel getCrfLayout() {
+        panelCrf.setLayout(new FlowLayout(FlowLayout.LEFT));
+        JLabel label = new JLabel("crf");
+        label.setPreferredSize(labelSize);
+        panelCrf.add(label);
+        textCrf.setPreferredSize(new Dimension(60, 20));
+        panelCrf.add(textCrf);
+        panelCrf.add(new JLabel("[0, 51] (default 29)"));
+        return panelCrf;
+    }
+
     private JPanel getScaleLayout() {
     private JPanel getScaleLayout() {
         JPanel panel = new JPanel();
         JPanel panel = new JPanel();
         panel.setLayout(new FlowLayout(FlowLayout.LEFT));
         panel.setLayout(new FlowLayout(FlowLayout.LEFT));
@@ -476,6 +547,8 @@ public class ToolUI {
             String scale = props.getProperty("scale", String.valueOf(scaleArray[0]));
             String scale = props.getProperty("scale", String.valueOf(scaleArray[0]));
             String audioPath = props.getProperty("audioPath", "");
             String audioPath = props.getProperty("audioPath", "");
             String bitrate = props.getProperty("bitrate", String.valueOf(commonArg.bitrate));
             String bitrate = props.getProperty("bitrate", String.valueOf(commonArg.bitrate));
+            String enableCrf = props.getProperty("enableCrf", String.valueOf(commonArg.enableCrf));
+            String crf = props.getProperty("crf", String.valueOf(commonArg.crf));
 
 
             int v = Integer.parseInt(version);
             int v = Integer.parseInt(version);
             // 版本不符直接返回默认值
             // 版本不符直接返回默认值
@@ -486,6 +559,8 @@ public class ToolUI {
             commonArg.inputPath = inputPath;
             commonArg.inputPath = inputPath;
             commonArg.audioPath = audioPath;
             commonArg.audioPath = audioPath;
             commonArg.bitrate = Integer.parseInt(bitrate);
             commonArg.bitrate = Integer.parseInt(bitrate);
+            commonArg.enableCrf = Boolean.TRUE.toString().equals(enableCrf);
+            commonArg.crf = Integer.parseInt(crf);
         } catch (Exception e) {
         } catch (Exception e) {
             TLog.e(TAG, "getProperties error:" + e.getMessage());
             TLog.e(TAG, "getProperties error:" + e.getMessage());
         }
         }
@@ -501,6 +576,8 @@ public class ToolUI {
         props.setProperty("audioPath", commonArg.audioPath == null ? "" : commonArg.audioPath);
         props.setProperty("audioPath", commonArg.audioPath == null ? "" : commonArg.audioPath);
         props.setProperty("scale", String.valueOf(commonArg.scale));
         props.setProperty("scale", String.valueOf(commonArg.scale));
         props.setProperty("bitrate", String.valueOf(commonArg.bitrate));
         props.setProperty("bitrate", String.valueOf(commonArg.bitrate));
+        props.setProperty("crf", String.valueOf(commonArg.crf));
+        props.setProperty("enableCrf", String.valueOf(commonArg.enableCrf));
         props.store(new OutputStreamWriter(new FileOutputStream(PROPERTIES_FILE), StandardCharsets.UTF_8), "");
         props.store(new OutputStreamWriter(new FileOutputStream(PROPERTIES_FILE), StandardCharsets.UTF_8), "");
     }
     }