/*
* MediathekView
* Copyright (C) 2008 W. Xaver
* W.Xaver[at]googlemail.com
* http://zdfmediathk.sourceforge.net/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mediathek.controller.starter;
import mSearch.tool.Listener;
import mSearch.tool.Log;
import mSearch.tool.SysMsg;
import mediathek.tool.MVFilmSize;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RuntimeExec {
public static final String TRENNER_PROG_ARRAY = "<>";
private static final int INPUT = 1;
private static final int ERROR = 2;
Thread clearIn;
Thread clearOut;
private Process process = null;
Start start;
private static int procnr = 0; //TH
private static final Pattern patternFlvstreamer = Pattern.compile("([0-9]*.[0-9]{1}%)");
private static final Pattern patternFlvstreamerComplete = Pattern.compile("Download complete");
private static final Pattern patternFfmpeg = Pattern.compile("(?<= Duration: )[^,]*"); // Duration: 00:00:30.28, start: 0.000000, bitrate: N/A
private static final Pattern patternZeit = Pattern.compile("(?<=time=)[^ ]*"); // frame= 147 fps= 17 q=-1.0 size= 1588kB time=00:00:05.84 bitrate=2226.0kbits/s
private static final Pattern patternSize = Pattern.compile("(?<=size=)[^k]*"); // frame= 147 fps= 17 q=-1.0 size= 1588kB time=00:00:05.84 bitrate=2226.0kbits/s
private double totalSecs = 0;
private long oldSize = 0;
private long oldSecs = 0;
// private DatenDownload datenDownload = null;
private MVFilmSize mVFilmSize = null;
private final String strProgCall;
private String[] arrProgCallArray = null;
private String strProgCallArray = "";
public RuntimeExec(MVFilmSize mVFilmSize, Start start,
String strProgCall, String strProgCallArray) {
this.mVFilmSize = mVFilmSize;
this.start = start;
this.strProgCall = strProgCall;
this.arrProgCallArray = strProgCallArray.split(TRENNER_PROG_ARRAY);
this.strProgCallArray = strProgCallArray;
if (arrProgCallArray.length <= 1) {
arrProgCallArray = null;
}
}
public RuntimeExec(String p) {
strProgCall = p;
}
//===================================
// Public
//===================================
public Process exec(boolean log) {
try {
if (arrProgCallArray != null) {
if (log) {
SysMsg.sysMsg("=====================");
SysMsg.sysMsg("Starte Array: ");
SysMsg.sysMsg(" -> " + strProgCallArray);
SysMsg.sysMsg("=====================");
}
process = Runtime.getRuntime().exec(arrProgCallArray);
} else {
if (log) {
SysMsg.sysMsg("=====================");
SysMsg.sysMsg("Starte nicht als Array:");
SysMsg.sysMsg(" -> " + strProgCall);
SysMsg.sysMsg("=====================");
}
process = Runtime.getRuntime().exec(strProgCall);
}
clearIn = new Thread(new ClearInOut(INPUT, process));
clearOut = new Thread(new ClearInOut(ERROR, process));
clearIn.start();
clearOut.start();
} catch (Exception ex) {
Log.errorLog(450028932, ex, "Fehler beim Starten");
}
return process;
}
//===================================
// Private
//===================================
private class ClearInOut implements Runnable {
private final int art;
private BufferedReader buff;
private InputStream in;
private final Process process;
private int percent = 0;
private int percent_start = -1;
public ClearInOut(int a, Process p) {
art = a;
process = p;
}
@Override
public void run() {
String titel = "";
try {
switch (art) {
case INPUT:
in = process.getInputStream();
titel = "INPUTSTREAM";
break;
case ERROR:
in = process.getErrorStream();
//TH
synchronized (this) {
titel = "ERRORSTREAM [" + (++procnr) + ']';
}
break;
}
buff = new BufferedReader(new InputStreamReader(in));
String inStr;
while ((inStr = buff.readLine()) != null) {
GetPercentageFromErrorStream(inStr);
SysMsg.playerMsg(titel + ": " + inStr);
}
} catch (IOException ignored) {
} finally {
try {
buff.close();
} catch (IOException ignored) {
}
}
}
private void GetPercentageFromErrorStream(String input) {
// by: siedlerchr für den flvstreamer und rtmpdump
Matcher matcher;
matcher = patternFlvstreamer.matcher(input);
if (matcher.find()) {
try {
String prozent = matcher.group();
prozent = prozent.substring(0, prozent.length() - 1);
double d = Double.parseDouble(prozent);
meldenDouble(d);
} catch (Exception ex) {
Listener.notify(Listener.EREIGNIS_ART_DOWNLOAD_PROZENT, RuntimeExec.class.getName());
Log.errorLog(912036780, input);
}
return;
}
matcher = patternFlvstreamerComplete.matcher(input);
if (matcher.find()) {
// dann ist der Download fertig, zur sicheren Erkennung von 100%
meldenDouble(100);
return;
}
// für ffmpeg
// ffmpeg muss dazu mit dem Parameter -i gestartet werden:
// -i %f -acodec copy -vcodec copy -y **
try {
// Gesamtzeit
matcher = patternFfmpeg.matcher(input);
if (matcher.find()) {
// Find duration
String dauer = matcher.group().trim();
String[] hms = dauer.split(":");
totalSecs = Integer.parseInt(hms[0]) * 3600
+ Integer.parseInt(hms[1]) * 60
+ Double.parseDouble(hms[2]);
}
// Bandbreite
matcher = patternSize.matcher(input);
if (matcher.find()) {
String s = matcher.group().trim();
if (!s.isEmpty()) {
try {
final long aktSize = Integer.parseInt(s.replace("kB", ""));
mVFilmSize.setAktSize(aktSize * 1_000);
long akt = start.startZeit.diffInSekunden();
if (oldSecs < akt - 5) {
start.bandbreite = (aktSize - oldSize) * 1_000 / (akt - oldSecs);
oldSecs = akt;
oldSize = aktSize;
}
} catch (NumberFormatException ignored) {
}
}
}
// Fortschritt
matcher = patternZeit.matcher(input);
if (totalSecs > 0 && matcher.find()) {
// ffmpeg 1611kB time=00:00:06.73 bitrate=1959.7kbits/s
// avconv size= 26182kB time=100.96 bitrate=2124.5kbits/s
String zeit = matcher.group();
if (zeit.contains(":")) {
String[] hms = zeit.split(":");
final double aktSecs = Integer.parseInt(hms[0]) * 3600
+ Integer.parseInt(hms[1]) * 60
+ Double.parseDouble(hms[2]);
double d = aktSecs / totalSecs * 100;
meldenDouble(d);
} else {
double aktSecs = Double.parseDouble(zeit);
double d = aktSecs / totalSecs * 100;
meldenDouble(d);
}
}
} catch (Exception ex) {
Listener.notify(Listener.EREIGNIS_ART_DOWNLOAD_PROZENT, RuntimeExec.class.getName());
Log.errorLog(912036780, input);
}
}
private void meldenDouble(double d) {
// nur ganze Int speichern, und 1000 Schritte
d *= 10;
int pNeu = (int) d;
start.percent = pNeu;
if (pNeu != percent) {
percent = pNeu;
if (percent_start == -1) {
// für wiedergestartete Downloads
percent_start = percent;
}
if (percent > (percent_start + 5)) {
// sonst macht es noch keinen Sinn
int diffZeit = start.startZeit.diffInSekunden();
int diffProzent = percent - percent_start;
int restProzent = 1000 - percent;
start.restSekunden = (diffZeit * restProzent / diffProzent);
}
Listener.notify(Listener.EREIGNIS_ART_DOWNLOAD_PROZENT, RuntimeExec.class.getName());
}
}
}
}