2 package saccubus.converter;
4 import java.io.BufferedReader;
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;
31 public class Ffmpeg extends SwingWorker<FfmpegResult, FfmpegProgress> {
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;
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;
49 this.profile = profile;
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);
58 protected FfmpegResult doInBackground() throws Exception {
62 if (commentMiddleFile.exists()) {
63 commentMiddleFile.delete();
65 if (tcommMiddleFile.exists()) {
66 tcommMiddleFile.delete();
68 if (TMP_CWS.exists()) {
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.
82 publish(new FfmpegProgress("コメント変換に失敗。ファイル名に使用できない文字が含まれているか正規表現の間違い?"));
83 return new FfmpegResult(false);
87 // if (tcommFile != null) {
88 // sendText("投稿者コメントの中間ファイルへの変換中");
89 // boolean conv = ConvertToVideoHook.convert(tcommFile, tcommMiddleFile, ngSetting.getId(), ngSetting.getWord());
91 // sendText("コメント変換に失敗。ファイル名に使用できない文字が含まれているか正規表現の間違い?");
96 publish(new FfmpegProgress("動画の変換を開始"));
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);
103 publish(new FfmpegProgress("変換エラー:" + convertedVideoFile.getPath()));
105 return new FfmpegResult(false);
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);
112 List<String> cmdList = new ArrayList<String>();
113 cmdList.add(getFfmpeg().getFfmpeg().getPath());
115 String[] mainOptions = ov.getMainOption().split(" +");
116 for (String opt : mainOptions) {
117 if (StringUtils.isNotBlank(opt)) {
121 String[] inOptions = ov.getInOption().split(" +");
122 for (String opt : inOptions) {
123 if (StringUtils.isNotBlank(opt)) {
129 if (fwsFile == null) {
130 cmdList.add(videoFile.getPath());
132 cmdList.add(fwsFile.getPath());
134 String[] outOptions = ov.getOutOption().split(" +");
135 for (String opt : outOptions) {
136 if (StringUtils.isNotBlank(opt)) {
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);
145 final Size scaled = (ov.isAdjustRatio()) ? MediaInfo.adjustSize(info, ov.getResizeWidth(), ov.
146 getResizeHeight()) : new Size(info.getWidth(), info.getHeight());
148 cmdList.add(scaled.getWidth() + "x" + scaled.getHeight());
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);
159 if (!avfilterArgs.isEmpty()) {
160 cmdList.add("-vfilters");
161 final String args = "\"" + StringUtils.join(avfilterArgs, ", ") + "\"";
165 cmdList.add(convertedVideoFile.getPath());
167 final StringBuilder argMsg = new StringBuilder();
168 argMsg.append("arg:");
169 for (String s : cmdList) {
170 argMsg.append(" ").append(s);
172 logger.log(Level.INFO, argMsg.toString());
174 Process process = null;
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()));
181 while ((e = ebr.readLine()) != null) {
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);
191 } catch (InterruptedException ex) {
197 return process.exitValue();
199 // TODO 正常終了した場合もdestroyしていいのか?
200 if (process != null) {
203 if (fwsFile != null) {
209 private List<String> getAvfilterOptions(String avfilterOption) {
210 final List<String> avfilterArgs = new ArrayList<String>();
211 if (StringUtils.isNotBlank(avfilterOption)) {
212 avfilterArgs.add(avfilterOption);
217 private String getVhookArg(boolean addComment, String commPath, boolean addTcomment,
218 String tcommPath, boolean isHD) throws UnsupportedEncodingException {
219 StringBuilder sb = new StringBuilder();
221 sb.append(getFfmpeg().getVhook().getPath().replace("\\", "/"));
224 sb.append("--data-user:");
225 sb.append(URLEncoder.encode(commPath.replace("\\", "/"), "Shift_JIS"));
229 sb.append("--data-owner:");
230 sb.append(URLEncoder.encode(tcommPath.replace("\\", "/"), "Shift_JIS"));
233 sb.append("--font:");
234 sb.append(URLEncoder.encode(
235 getFfmpeg().getFont().getPath().replace("\\", "/"), "Shift_JIS"));
237 sb.append("--font-index:");
238 sb.append(getFfmpeg().getFontIndex());
240 sb.append("--show-user:");
241 sb.append(getFfmpeg().getMaxNumOfComment());
243 sb.append("--shadow:");
244 sb.append(getFfmpeg().getShadowIndex());
246 if (getFfmpeg().isShowConverting()) {
247 sb.append("--enable-show-video");
250 if (!getFfmpeg().isDisableFontSizeArrange()) {
251 sb.append("--enable-fix-font-size");
254 if (getFfmpeg().isCommentOpaque()) {
255 sb.append("--enable-opaque-comment");
259 sb.append("--aspect-mode:1");
262 return sb.toString();
265 private ConvertProfile getFfmpeg() {
269 protected void checkStop() throws InterruptedException {
270 if (Thread.interrupted()) {
271 throw new InterruptedException("中止要求を受け付けました");