/*
AV-Direktor - Control OMXPlayer on Raspberry Pi via HTTP
Copyright (C) 2021 Ulrich Hilger
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
package de.uhilger.avdirektor.handler;
import de.uhilger.avdirektor.App;
import de.uhilger.avdirektor.MeldeThread;
import de.uhilger.avdirektor.ProzessLauscher;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Methoden zur Ausfuehrung des Programmes omxplayer des Raspberry Pi
* sowie zum Senden von Kommandos an eine laufende Instanz des
* omxplayer.
*
* Die Klasse OMXPlayer stellt als abstrakte Basisklasse ihre Methoden
* den Handler-Klassen zur Verfuegung.
*
* @author ulrich
*/
public class OMXPlayer implements Player , ProzessLauscher {
private static final Logger logger = Logger.getLogger(OMXPlayer.class.getName());
public static final String BLANK = " ";
public static final String CMD_DEC_SPEED = "1";
public static final String CMD_DEC_VOL = "-";
public static final String CMD_INC_SPEED = "2";
public static final String CMD_INC_VOL = "+";
public static final String CMD_NEXT_AUDIO = "k";
public static final String CMD_NEXT_CHAPTER = "o";
public static final String CMD_NEXT_SUB = "m";
public static final String CMD_PAUSE_RESUME = "p";
public static final String CMD_PREV_AUDIO = "j";
public static final String CMD_PREV_CHAPTER = "i";
public static final String CMD_PREV_SUB = "n";
public static final String CMD_STOP = "q";
public static final String CMD_TOGGLE_SUB = "s";
public static final String F_PING = "ping";
public static final String F_PLAY = "play";
public static final String F_SEEK = "seek";
public static final String OPT_HDMI_AUDIO = "-o%20hdmi";
public static final String OPT_LOCAL_AUDIO = "-o%20local";
public static final String PFEIL_HERAUF = "5b41";
public static final String PFEIL_HERUNTER = "5b42";
public static final String PFEIL_LINKS = "5b44";
public static final String PFEIL_RECHTS = "5b43";
public static final String SP_RUECK_30 = "rueck30";
public static final String SP_RUECK_600 = "rueck600";
public static final String SP_VOR_30 = "rueck30";
public static final String SP_VOR_600 = "vor600";
/**
* Einen Prozess zum Abspielen mit dem omxplayer starten
* @param urlStr URL der Quelle, die abgespielt werden soll
* @param token
* @return Antwort des Servers
*/
/*
public String abspielen(String urlStr, String token) {
return abspielenMitParametern(urlStr, null, token);
}
*/
/*
public String abspielenMitRueckmeldung(String urlStr, String meldeUrlStr, String token) {
return abspielenMitParameternUndRueckmeldung(urlStr, null, meldeUrlStr, token);
}
*/
/**
* Einen Prozess zum Abspielen mit dem omxplayer starten
* und Parameter uebergeben.Moegliche Parameter fuer das Abspielen mit dem omxplayer
beschreibt die Seite
Aufstellung der Parameter.Die Zeichenkette parameter enthaelt Eintraege wie z.B.
* App.OPT_LOCAL_AUDIO oder App.OPT_HDMI_AUDIO.
Mehrere Parameter werden mit App.BLANK getrennt.
* @param urlStr der URL der Quelle, die abgespielt werden soll
* @param parameter die Parameter, die vom omxplayer angewendet werden sollen
* @param token
* @return Antwort des Servers
*/
/*
public String abspielenMitParametern(String urlStr, String parameter, String token) {
return abspielenMitParameternUndRueckmeldung(urlStr, parameter, null, token);
}
*/
@Override
public String abspielen(String urlStr, String parameter, String meldeUrlStr, String token) {
String antwort;// = null;
try {
//Object o = t.getAttribute(App.PI_PLAYER);
Process o = App.getPlayerProcess();
if(o != null) {
tilgen();
}
StringBuilder kommando = new StringBuilder("omxplayer ");
if(parameter != null) {
kommando.append(parameter);
kommando.append(BLANK);
}
if(urlStr.startsWith("http")) {
kommando.append(urlStr.replace(" ", "%20"));
kommando.append("?t=");
kommando.append(token);
} else {
/*
//url z.B.: Filme/H/HEAT_D2.m4v
hier muss noch der Pfad hinzugefuegt werden, unter
dem auf dem raspi die Datenquelle via NFS eingebunden ist,
z.B. /media/mc/
dieser Teil des Pfades muss in pirc als Init-Parameter oder
etwas aehnliches hinterlegt sein, weil es lokal zum jeweils
verwendeten raspi gehoert
*/
String pfad = App.getInitParameter("nfs-prefix");
kommando.append(pfad);
kommando.append(urlStr);
}
logger.log(Level.FINE, "kommando: {0}", kommando.toString());
Process player_process = Runtime.getRuntime().exec(kommando.toString());
if(meldeUrlStr != null) {
MeldeThread mt = new MeldeThread();
mt.setProcess(player_process);
mt.lauscherHinzufuegen(this);
mt.setMeldeUrl(meldeUrlStr);
mt.start();
}
//servletContext.setAttribute(App.PI_PLAYER, player_process);
//t.setAttribute(App.PI_PLAYER, player_process);
App.setPlayerProcess(player_process);
//Runtime.getRuntime().exec("killall dbus-daemon");
antwort = "Abspielen gestartet, url: " + urlStr;
}
catch(IOException ex) {
antwort = "Fehler: " + ex.getMessage();
}
return antwort;
}
/**
* Einen eventuell laufenden Abspielprozess beenden und den
* Servlet-Kontext bereinigen.Diese Methode kann auch verwendet werden, wenn es beim normalen
Abspielen zu Fehlern kommt und womoeglich der Servlet-Kontext noch
eine Referenz zu einem Abspielprozess enthaelt, die nicht mehr
aktuell ist.
*
* Mit der Methode tilgen kann man eine solche Referenz
entfernen und gibt so das Objekt wieder frei fuer die Ausfuehrung
weiterer Kommandos.
*
* @return die Antwort des Servers
*/
@Override
public String tilgen() {
String antwort; // = null;
try {
//Object o = t.getAttribute(App.PI_PLAYER);
Process o = App.getPlayerProcess();
if(o == null) {
//t.setAttribute(App.PI_PLAYER, null);
App.setPlayerProcess(null);
// t.removeAttribute(App.PI_PLAYER);
antwort = "Es ist kein Player zum Beenden vorhanden, aber der Servlet-Kontext wurde bereinigt.";
} else {
kommando(CMD_STOP);
//t.removeAttribute(PI_PLAYER);
antwort = "Player gestoppt, Kontext bereinigt.";
}
}
catch(Exception ex) {
antwort = "Fehler: " + ex.getMessage();
}
return antwort;
}
/**
* Dem laufenden Abspielprozess ein Kommando uebermitteln
* @param k das Kommando laut
* Liste der Kommandos
* @return die Antwort des Servers
*/
@Override
public String kommando(String k) {
String antwort; // = null;
try {
//Object o = t.getAttribute(App.PI_PLAYER);
Process o = App.getPlayerProcess();
if(o == null) {
App.setPlayerProcess(null);
//servletContext.removeAttribute(PI_PLAYER);
//t.setAttribute(App.PI_PLAYER, null);
antwort = "Es wird nichts abgespielt dem ein Kommando gesendet werden kann.";
} else {
Process player_process = o;
OutputStream os = player_process.getOutputStream();
Writer out = new BufferedWriter(new OutputStreamWriter(os));
out.write(k);
out.flush();
if(k.equals(CMD_STOP)) {
out.close();
App.setPlayerProcess(null);
//player_process.destroy();
//player_process = null;
//t.setAttribute(App.PI_PLAYER, null);
//servletContext.removeAttribute(PI_PLAYER);
}
antwort = "Kommando '" + k + "' ausgefuehrt.";
}
}
catch(IOException ex) {
antwort = "Fehler: " + ex.getMessage();
}
return antwort;
}
/* ------ Implementierung ProzessLauscher ----------------- */
@Override
public void prozessBeendet(String meldeUrlStr) {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(meldeUrlStr).openConnection();
conn.setRequestMethod("GET");
conn.connect();
int status = conn.getResponseCode();
logger.log(Level.INFO, "Abspielen beendet, Meldung an {0} mit Statuscode {1} gesendet.", new Object[]{meldeUrlStr, status});
} catch(IOException ex) {
logger.log(Level.INFO, ex.getMessage(), ex);
}
}
}