OSDN Git Service

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