OSDN Git Service

c9f1aeac2834e31f05d539483930ef1b72f3a0de
[coroid/inqubus.git] / frontend / src / saccubus / converter / Ffmpeg.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 java.util.logging.Level;
13 import java.util.logging.Logger;
14 import javax.swing.SwingWorker;
15 import org.apache.commons.lang.StringUtils;
16 import saccubus.conv.ConvertToVideoHook;
17 import saccubus.converter.profile.ConvertProfile;
18 import saccubus.converter.profile.FfmpegProfile;
19 import saccubus.converter.profile.GeneralProfile;
20 import yukihane.mediainfowrapper.Info;
21 import yukihane.mediainfowrapper.MediaInfo;
22 import yukihane.mediainfowrapper.Size;
23 import saccubus.converter.profile.ConvertProfile.HideCondition;
24 import yukihane.swf.Cws2Fws;
25
26 /**
27  *
28  * @author yuki
29  */
30 public class Ffmpeg extends SwingWorker<FfmpegResult, FfmpegProgress> {
31
32     private static final Logger logger = Logger.getLogger(Ffmpeg.class.getName());
33     private final File commentMiddleFile;
34     private final File tcommMiddleFile;
35     private final File TMP_CWS;
36     private final File commentFile;
37     private final File videoFile;
38     private final File convertedVideoFile;
39     private final ConvertProfile ffmpeg;
40
41     Ffmpeg(File commentFile, File videoFile, File convertedVideoFile, ConvertProfile ffmpeg,
42             GeneralProfile general) throws IOException {
43         this.commentFile = commentFile;
44         this.videoFile = videoFile;
45         this.convertedVideoFile = convertedVideoFile;
46         this.ffmpeg = ffmpeg;
47
48         File tmpDir = general.getTempDir();
49         commentMiddleFile = File.createTempFile("vhk", ".tmp", tmpDir);
50         tcommMiddleFile = File.createTempFile("tcom", ".tmp", tmpDir);
51         TMP_CWS = File.createTempFile("cws", ".swf", tmpDir);
52     }
53
54     @Override
55     protected FfmpegResult doInBackground() throws Exception {
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 FfmpegResult exec() throws InterruptedException, IOException {
72         final HideCondition ngSetting = getFfmpeg().getNgSetting();
73         if (commentFile != null) {
74             publish(new FfmpegProgress("コメントの中間ファイルへの変換中"));
75             boolean conv = ConvertToVideoHook.convert(commentFile, commentMiddleFile, ngSetting.getId(), ngSetting.
76                     getWord());
77             if (!conv) {
78                 publish(new FfmpegProgress("コメント変換に失敗。ファイル名に使用できない文字が含まれているか正規表現の間違い?"));
79                 return new FfmpegResult(false);
80             }
81         }
82         checkStop();
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         publish(new FfmpegProgress("動画の変換を開始"));
93         int code;
94         if ((code = converting_video(videoFile, convertedVideoFile, (commentFile != null), commentMiddleFile.getPath(),
95                 false, tcommMiddleFile.getPath(), getFfmpeg().getFfmpegOption())) == 0) {
96             publish(new FfmpegProgress("変換が正常に終了しました。"));
97             return new FfmpegResult(true);
98         } else {
99             publish(new FfmpegProgress("変換エラー:" + convertedVideoFile.getPath()));
100         }
101         return new FfmpegResult(false);
102     }
103
104     private int converting_video(File videoFile, File convertedVideoFile, boolean addComment, String commPath,
105             boolean addTcomment, String tcommPath, FfmpegProfile ov) throws InterruptedException, IOException {
106         File fwsFile = Cws2Fws.createFws(videoFile, TMP_CWS);
107
108         List<String> cmdList = new ArrayList<String>();
109         cmdList.add(getFfmpeg().getFfmpeg().getPath());
110         cmdList.add("-y");
111         String[] mainOptions = ov.getMainOption().split(" +");
112         for (String opt : mainOptions) {
113             if (StringUtils.isNotBlank(opt)) {
114                 cmdList.add(opt);
115             }
116         }
117         String[] inOptions = ov.getInOption().split(" +");
118         for (String opt : inOptions) {
119             if (StringUtils.isNotBlank(opt)) {
120                 cmdList.add(opt);
121             }
122         }
123         cmdList.add("-i");
124
125         if (fwsFile == null) {
126             cmdList.add(videoFile.getPath());
127         } else {
128             cmdList.add(fwsFile.getPath());
129         }
130         String[] outOptions = ov.getOutOption().split(" +");
131         for (String opt : outOptions) {
132             if (StringUtils.isNotBlank(opt)) {
133                 cmdList.add(opt);
134             }
135         }
136
137         final Info info = MediaInfo.getInfo(new File("bin", "MediaInfo"), videoFile);
138         // 4:3 なら1.33, 16:9 なら1.76
139         boolean isHD = ((double) info.getWidth() / (double) info.getHeight() > 1.5);
140         if (ov.isResize()) {
141             final Size scaled = (ov.isAdjustRatio()) ? MediaInfo.adjustSize(info, ov.getResizeWidth(), ov.
142                     getResizeHeight()) : new Size(info.getWidth(), info.getHeight());
143             cmdList.add("-s");
144             cmdList.add(scaled.getWidth() + "x" + scaled.getHeight());
145         }
146
147         List<String> avfilterArgs = getAvfilterOptions(ov.getAvfilterOption());
148         if (!getFfmpeg().isVhookDisabled()) {
149             String vhookArg = getVhookArg(addComment, commPath, addTcomment, tcommPath, isHD);
150             if (StringUtils.isNotBlank(vhookArg)) {
151                 avfilterArgs.add(vhookArg);
152             }
153         }
154
155         if (!avfilterArgs.isEmpty()) {
156             cmdList.add("-vfilters");
157             final String args = "\"" + StringUtils.join(avfilterArgs, ", ") + "\"";
158             cmdList.add(args);
159         }
160
161         cmdList.add(convertedVideoFile.getPath());
162
163         final StringBuilder argMsg = new StringBuilder();
164         argMsg.append("arg:");
165         for (String s : cmdList) {
166             argMsg.append(" ").append(s);
167         }
168         logger.log(Level.INFO, argMsg.toString());
169
170         Process process = null;
171         try {
172             logger.log(Level.INFO, "\n\n----\nProcessing FFmpeg...\n----\n\n");
173             process = Runtime.getRuntime().exec(cmdList.toArray(new String[0]));
174             BufferedReader ebr = new BufferedReader(new InputStreamReader(
175                     process.getErrorStream()));
176             String e;
177             while ((e = ebr.readLine()) != null) {
178                 String state = e;
179                 if (state.startsWith("frame=")) {
180                     publish(new FfmpegProgress(state));
181                 } else if (!state.endsWith("No accelerated colorspace conversion found")) {
182                     logger.log(Level.INFO, e);
183                 }
184
185                 try {
186                     checkStop();
187                 } catch (InterruptedException ex) {
188                     throw ex;
189                 }
190
191             }
192             process.waitFor();
193             return process.exitValue();
194         } finally {
195             // TODO 正常終了した場合もdestroyしていいのか?
196             if (process != null) {
197                 process.destroy();
198             }
199             if (fwsFile != null) {
200                 fwsFile.delete();
201             }
202         }
203     }
204
205     private List<String> getAvfilterOptions(String avfilterOption) {
206         final List<String> avfilterArgs = new ArrayList<String>();
207         if (StringUtils.isNotBlank(avfilterOption)) {
208             avfilterArgs.add(avfilterOption);
209         }
210         return avfilterArgs;
211     }
212
213     private String getVhookArg(boolean addComment, String commPath, boolean addTcomment,
214             String tcommPath, boolean isHD) throws UnsupportedEncodingException {
215         StringBuilder sb = new StringBuilder();
216         sb.append("vhext=");
217         sb.append(getFfmpeg().getVhook().getPath().replace("\\", "/"));
218         if (addComment) {
219             sb.append("|");
220             sb.append("--data-user:");
221             sb.append(URLEncoder.encode(commPath.replace("\\", "/"), "Shift_JIS"));
222         }
223         if (addTcomment) {
224             sb.append("|");
225             sb.append("--data-owner:");
226             sb.append(URLEncoder.encode(tcommPath.replace("\\", "/"), "Shift_JIS"));
227         }
228         sb.append("|");
229         sb.append("--font:");
230         sb.append(URLEncoder.encode(
231                 getFfmpeg().getFont().getPath().replace("\\", "/"), "Shift_JIS"));
232         sb.append("|");
233         sb.append("--font-index:");
234         sb.append(getFfmpeg().getFontIndex());
235         sb.append("|");
236         sb.append("--show-user:");
237         sb.append(getFfmpeg().getMaxNumOfComment());
238         sb.append("|");
239         sb.append("--shadow:");
240         sb.append(getFfmpeg().getShadowIndex());
241         sb.append("|");
242         if (getFfmpeg().isShowConverting()) {
243             sb.append("--enable-show-video");
244             sb.append("|");
245         }
246         if (!getFfmpeg().isDisableFontSizeArrange()) {
247             sb.append("--enable-fix-font-size");
248             sb.append("|");
249         }
250         if (getFfmpeg().isCommentOpaque()) {
251             sb.append("--enable-opaque-comment");
252             sb.append("|");
253         }
254         if (isHD) {
255             sb.append("--aspect-mode:1");
256             sb.append("|");
257         }
258         return sb.toString();
259     }
260
261     private ConvertProfile getFfmpeg() {
262         return ffmpeg;
263     }
264
265     protected void checkStop() throws InterruptedException {
266         if (Thread.interrupted()) {
267             throw new InterruptedException("中止要求を受け付けました");
268         }
269     }
270 }