OSDN Git Service

nicovideo.dll にHD動画かどうかを通知するオプションを追加
[coroid/inqubus.git] / frontend / src / saccubus / converter / FfmpegCommand.java
1 /* $Id$ */
2 package saccubus.converter;
3
4 import java.io.BufferedReader;
5 import java.io.File;
6 import java.io.IOException;
7 import java.io.InputStreamReader;
8 import java.io.UnsupportedEncodingException;
9 import java.net.URLEncoder;
10 import java.util.ArrayList;
11 import java.util.List;
12 import org.apache.commons.lang.StringUtils;
13 import saccubus.ConvertStopFlag;
14 import saccubus.conv.ConvertToVideoHook;
15 import saccubus.converter.profile.Ffmpeg;
16 import saccubus.converter.profile.FfmpegOption;
17 import saccubus.converter.profile.GeneralSetting;
18 import saccubus.converter.profile.NgSetting;
19 import saccubus.net.TextProgressListener;
20 import yukihane.mediainfowrapper.Info;
21 import yukihane.mediainfowrapper.MediaInfo;
22 import yukihane.mediainfowrapper.Size;
23 import yukihane.swf.Cws2Fws;
24
25 /**
26  *
27  * @author yuki
28  */
29 public class FfmpegCommand extends AbstractCommand {
30
31     private final File commentMiddleFile;
32     private final File tcommMiddleFile;
33     private final File TMP_CWS;
34     private final File commentFile;
35     private final File tcommFile;
36     private final File videoFile;
37     private final File convertedVideoFile;
38     private final Ffmpeg ffmpeg;
39
40     FfmpegCommand(TextProgressListener listener, ConvertStopFlag flag, File commentFile, File tcommFile,
41             File videoFile, File convertedVideoFile, Ffmpeg ffmpeg, GeneralSetting general) throws IOException {
42         super(listener, flag);
43         this.commentFile = commentFile;
44         this.tcommFile = tcommFile;
45         this.videoFile = videoFile;
46         this.convertedVideoFile = convertedVideoFile;
47         this.ffmpeg = ffmpeg;
48
49         File tmpDir = general.getTempDir();
50         commentMiddleFile = File.createTempFile("vhk", ".tmp", tmpDir);
51         tcommMiddleFile = File.createTempFile("tcom", ".tmp", tmpDir);
52         TMP_CWS = File.createTempFile("cws", ".swf", tmpDir);
53     }
54
55     public boolean execute() throws InterruptedException, IOException {
56         try {
57             return exec();
58         } finally {
59             if (commentMiddleFile.exists()) {
60                 commentMiddleFile.delete();
61             }
62             if (tcommMiddleFile.exists()) {
63                 tcommMiddleFile.delete();
64             }
65             if (TMP_CWS.exists()) {
66                 TMP_CWS.delete();
67             }
68         }
69     }
70
71     private boolean exec() throws InterruptedException, IOException {
72         final NgSetting ngSetting = getFfmpeg().getNgSetting();
73         if (commentFile != null) {
74             sendText("コメントの中間ファイルへの変換中");
75             boolean conv = ConvertToVideoHook.convert(commentFile, commentMiddleFile, ngSetting.getId(), ngSetting.
76                     getWord());
77             if (!conv) {
78                 sendText("コメント変換に失敗。ファイル名に使用できない文字が含まれているか正規表現の間違い?");
79                 return false;
80             }
81         }
82         stopFlagReturn();
83         if (tcommFile != null) {
84             sendText("投稿者コメントの中間ファイルへの変換中");
85             boolean conv = ConvertToVideoHook.convert(tcommFile, tcommMiddleFile, ngSetting.getId(), ngSetting.getWord());
86             if (!conv) {
87                 sendText("コメント変換に失敗。ファイル名に使用できない文字が含まれているか正規表現の間違い?");
88                 return false;
89             }
90         }
91         stopFlagReturn();
92         sendText("動画の変換を開始");
93         int code;
94         if ((code = converting_video(videoFile, convertedVideoFile, (commentFile != null), commentMiddleFile.getPath(),
95                 (tcommFile
96                 != null), tcommMiddleFile.getPath(), getFfmpeg().getFfmpegOption())) == 0) {
97             sendText("変換が正常に終了しました。");
98             return true;
99         } else {
100             sendText("変換エラー:" + convertedVideoFile.getPath());
101         }
102         return false;
103     }
104
105     private int converting_video(File videoFile, File convertedVideoFile, boolean addComment, String commPath,
106             boolean addTcomment, String tcommPath, FfmpegOption ov) throws InterruptedException, IOException {
107         File fwsFile = Cws2Fws.createFws(videoFile, TMP_CWS);
108
109         List<String> cmdList = new ArrayList<String>();
110         cmdList.add(getFfmpeg().getFfmpeg().getPath());
111         cmdList.add("-y");
112         String[] mainOptions = ov.getMainOption().split(" +");
113         for (String opt : mainOptions) {
114             if (StringUtils.isNotBlank(opt)) {
115                 cmdList.add(opt);
116             }
117         }
118         String[] inOptions = ov.getInOption().split(" +");
119         for (String opt : inOptions) {
120             if (StringUtils.isNotBlank(opt)) {
121                 cmdList.add(opt);
122             }
123         }
124         cmdList.add("-i");
125
126         if (fwsFile == null) {
127             cmdList.add(videoFile.getPath());
128         } else {
129             cmdList.add(fwsFile.getPath());
130         }
131         String[] outOptions = ov.getOutOption().split(" +");
132         for (String opt : outOptions) {
133             if (StringUtils.isNotBlank(opt)) {
134                 cmdList.add(opt);
135             }
136         }
137
138         final Info info = MediaInfo.getInfo(new File("bin", "MediaInfo"), videoFile);
139         // 4:3 なら1.33, 16:9 なら1.76
140         boolean isHD = ((double)info.getWidth()/(double)info.getHeight() > 1.5);
141         if (ov.isResize()) {
142             final Size scaled = (ov.isAdjustRatio()) ? MediaInfo.adjustSize(info, ov.getResizeWidth(), ov.
143                     getResizeHeight()) : new Size(info.getWidth(), info.getHeight());
144             cmdList.add("-s");
145             cmdList.add(scaled.getWidth() + "x" + scaled.getHeight());
146         }
147
148         List<String> avfilterArgs = getAvfilterOptions(ov, addComment, commPath, addTcomment, tcommPath, isHD);
149
150         if (!avfilterArgs.isEmpty()) {
151             cmdList.add("-vfilters");
152             final String args = "\"" + StringUtils.join(avfilterArgs, ", ") + "\"";
153             cmdList.add(args);
154         }
155
156         cmdList.add(convertedVideoFile.getPath());
157
158         System.out.print("arg:");
159         for (String s : cmdList) {
160             System.out.print(" " + s);
161         }
162         System.out.println();
163
164         try {
165             System.out.println("\n\n----\nProcessing FFmpeg...\n----\n\n");
166             Process process = Runtime.getRuntime().exec(cmdList.toArray(new String[0]));
167             BufferedReader ebr = new BufferedReader(new InputStreamReader(
168                     process.getErrorStream()));
169             String e;
170             while ((e = ebr.readLine()) != null) {
171                 String state = e;
172                 if (state.startsWith("frame=")) {
173                     sendText(state);
174                 } else if (!state.endsWith("No accelerated colorspace conversion found")) {
175                     System.out.println(e);
176                 }
177
178                 try {
179                     stopFlagReturn();
180                 } catch (InterruptedException ex) {
181                     process.destroy();
182                     throw ex;
183                 }
184
185             }
186             process.waitFor();
187             return process.exitValue();
188         } finally {
189             if (fwsFile != null) {
190                 fwsFile.delete();
191             }
192         }
193     }
194
195     private List<String> getAvfilterOptions(FfmpegOption ov, boolean addComment, String commPath, boolean addTcomment,
196             String tcommPath, boolean isHD) throws UnsupportedEncodingException {
197         final List<String> avfilterArgs = new ArrayList<String>();
198         final String avfilterOption = ov.getAvfilterOption();
199         if (StringUtils.isNotBlank(avfilterOption)) {
200             avfilterArgs.add(avfilterOption);
201         }
202         final String vhookArg = (getFfmpeg().isVhookDisabled())
203                 ? null : getVhookArg(addComment, commPath, addTcomment, tcommPath, isHD);
204         if (vhookArg != null) {
205             avfilterArgs.add(vhookArg);
206         }
207         return avfilterArgs;
208     }
209
210     private String getVhookArg(boolean addComment, String commPath, boolean addTcomment,
211             String tcommPath, boolean isHD) throws UnsupportedEncodingException {
212         StringBuilder sb = new StringBuilder();
213         sb.append("vhext=");
214         sb.append(getFfmpeg().getVhook().getPath().replace("\\", "/"));
215         if (addComment) {
216             sb.append("|");
217             sb.append("--data-user:");
218             sb.append(URLEncoder.encode(commPath.replace("\\", "/"), "Shift_JIS"));
219         }
220         if (addTcomment) {
221             sb.append("|");
222             sb.append("--data-owner:");
223             sb.append(URLEncoder.encode(tcommPath.replace("\\", "/"), "Shift_JIS"));
224         }
225         sb.append("|");
226         sb.append("--font:");
227         sb.append(URLEncoder.encode(
228                 getFfmpeg().getFont().getPath().replace("\\", "/"), "Shift_JIS"));
229         sb.append("|");
230         sb.append("--font-index:");
231         sb.append(getFfmpeg().getFontIndex());
232         sb.append("|");
233         sb.append("--show-user:");
234         sb.append(getFfmpeg().getMaxNumOfComment());
235         sb.append("|");
236         sb.append("--shadow:");
237         sb.append(getFfmpeg().getShadowIndex());
238         sb.append("|");
239         if (getFfmpeg().isShowConverting()) {
240             sb.append("--enable-show-video");
241             sb.append("|");
242         }
243         if (getFfmpeg().isSelfAdjustFontSize()) {
244             sb.append("--enable-fix-font-size");
245             sb.append("|");
246         }
247         if (getFfmpeg().isCommentOpaque()) {
248             sb.append("--enable-opaque-comment");
249             sb.append("|");
250         }
251         if(isHD){
252             sb.append("--aspect-mode:1");
253             sb.append("|");
254         }
255         return sb.toString();
256     }
257
258     private Ffmpeg getFfmpeg() {
259         return ffmpeg;
260     }
261 }