App zur Steuerung des mpv Mediaplayers auf einem Raspberry Pi über HTTP
ulrich
2021-03-24 0af362883d659d9481a7f2bf6cf0c7459997f5e2
Skripte zum Start des AV-Direktor als Dienst hinzugefuegt, Konstanten von App nach OMXPlayer verschoben, Doku ergaenzt
5 files modified
3 files added
269 ■■■■ changed files
README.md 87 ●●●●● patch | view | raw | blame | history
resources/avd.service 15 ●●●●● patch | view | raw | blame | history
resources/logging.properties 72 ●●●●● patch | view | raw | blame | history
resources/start 3 ●●●●● patch | view | raw | blame | history
src/de/uhilger/avdirektor/App.java 40 ●●●●● patch | view | raw | blame | history
src/de/uhilger/avdirektor/Server.java 11 ●●●● patch | view | raw | blame | history
src/de/uhilger/avdirektor/handler/OMXPlayer.java 39 ●●●●● patch | view | raw | blame | history
src/de/uhilger/avdirektor/handler/StopServerHandler.java 2 ●●● patch | view | raw | blame | history
README.md
@@ -3,4 +3,89 @@
Eine Anwendung zur Steuerung des Mediaplayers 
[OMXPlayer](https://www.raspberrypi.org/documentation/raspbian/applications/omxplayer.md) 
auf einem Raspberry Pi über HTTP.
## Video und Audio abspielen
Der AV-Direktor arbeitet als Empfänger von HTTP-Signalen und setzt diese in Kommandos an den OMXPlayer um. Auf diese Weise kann ein Raspberry Pi als Abspieler von anderen Geräten aus fernbedient werden, am Pi selbst ist keine Bedienung erforderlich.
## Media-Quellen einrichen
Der AV-Direktor kann in zwei Betriebsarten gestartet werden:
. NFS-Client
. HTTP-Client
### NFS-Client
Mit dem Parameter `nfs-prefix` spielt der AV-Direktor Media-Inhalte aus NFS-Quellen ab.
```
java -jar av-director.jar nfs-prefix="/media/mc" port=9000
```
In dieser Betriebsart wird dem Inhalt, der beim Abspielen über den Parameter title angegeben wird, besagter Präfix vorangestellt. Wird beispielsweise der AV-Direktor aufgerufen mit
```
http://rpi4-wz:9090/avd/play?title=/Filme/H/heat.m4v
```
wird die Datei `/media/mc/Filme/H/heat-m4v` abgespielt. Zur Verwendung des AV-Direktors in der Betriebsart NFS Client muss der Raspberry Pi die Pakete für den NFS Client installiert bekommen und es muss auf dem Raspberry Pi in der Datei `/etc/fstab` ein Eintrag gemacht werden, der die entsprechende Quelle angibt, z.B.
```
mein-media-server:/media/extssd/mc /media/mc nfs rw 0 0
```
Welche Quellen für einen solchen Eintrag verfügbar sind lässt sich auf dem Raspberry Pi mit folgendem Kommando sehen.
```
showmount -e mein-media-server
```
Die Maschine `mein-mdeia-server` in den obigen Beispielen muss dafür als NFS-Server eingerichtet sein und entsprechende Inhalte via NFS freigeben.
## AV-Direktor als Dienst einrichten
```
cd /home/pi/prg/av-direktor
sudo cp avd.service /etc/systemd/system/avd.service
```
### Dienst starten
```
sudo systemctl start avd.service
```
### Dienst stoppen
Der laufende Dienst kann mit folgendem Signal via HTTP veranlasst werden, sich zu beenden.
```
http://rpi4-wz:9090/avd/server/stop
```
Ueber systemd kann stattdessen der Prozess wie folgt 'hart' beendet werden.
```
sudo systemctl stop avd.service
```
### Dienst dauerhaft aktivieren
Der folgende Befehl bewirkt, dass der Dienst nach einem Neustart automatisch startet.
```
sudo systemctl enable avd.service
```
### Dienst deaktivieren
```
sudo systemctl disable avd.service
```
### Status des Dienstes pruefen
```
sudo systemctl status avd
```
resources/avd.service
New file
@@ -0,0 +1,15 @@
[Unit]
Description=AV Direktor
After=network.target syslog.target
[Service]
ExecStart=/home/pi/prg/av-direktor/start
WorkingDirectory=/home/pi/prg/av-direktor/
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi
Type=forking
[Install]
WantedBy=multi-user.target
resources/logging.properties
New file
@@ -0,0 +1,72 @@
############################################################
#      Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.
# For example java -Djava.util.logging.config.file=myfile
############################################################
############################################################
#      Global properties
############################################################
# "handlers" specifies a comma separated list of log Handler
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
# handlers= java.util.logging.ConsoleHandler
# To also add the FileHandler, use the following line instead.
# handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= FINEST
# .level = NONE
############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################
# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
# java.util.logging.FileHandler.pattern = /media/extmirror/tomcat747/logs/tv_%u.log
# java.util.logging.FileHandler.pattern = ${catalina.base}/logs/file-cms_%u.log
# java.util.logging.FileHandler.limit = 50000
# java.util.logging.FileHandler.count = 1
# java.util.logging.FileHandler.count = 2
# java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
# java.util.logging.FileHandler.level = FINER
# Limit the message that are printed on the console to INFO and above.
# java.util.logging.ConsoleHandler.level = INFO
# java.util.logging.ConsoleHandler.level = FINER
# java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# Example to customize the SimpleFormatter output format
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n
############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################
# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
# com.xyz.foo.level = SEVERE
# de.uhilger.filecms.handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
# de.uhilger.filecms.level = FINEST
# de.uhilger.wbx.handlers = java.util.logging.ConsoleHandler
# de.uhilger.wbx.level = FINEST
de.uhilger.avdirektor.handlers = java.util.logging.FileHandler, java.util.logging.ConsoleHandler
de.uhilger.avdirektor.level = FINEST
resources/start
New file
@@ -0,0 +1,3 @@
#!/bin/sh
java -Djava.util.logging.config.file=logging.properties -jar av-direktor.jar port=9090 nfs-prefix="/media/mc" &
src/de/uhilger/avdirektor/App.java
@@ -23,44 +23,7 @@
  
  private static final Logger logger = Logger.getLogger(App.class.getName());
  public static final String PI_PLAYER = "pi_player";
  //public static final String FBI_PROC = "fbi_proc";
  public static final String CMD_STOP = "q";
  public static final String CMD_DEC_SPEED = "1";
  public static final String CMD_INC_SPEED = "2";
  public static final String CMD_PREV_AUDIO = "j";
  public static final String CMD_NEXT_AUDIO = "k";
  public static final String CMD_PREV_CHAPTER = "i";
  public static final String CMD_NEXT_CHAPTER = "o";
  public static final String CMD_PREV_SUB = "n";
  public static final String CMD_NEXT_SUB = "m";
  public static final String CMD_TOGGLE_SUB = "s";
  public static final String CMD_PAUSE_RESUME = "p";
  public static final String CMD_DEC_VOL = "-";
  public static final String CMD_INC_VOL = "+";
  public static final String PFEIL_LINKS = "5b44";
  public static final String PFEIL_RECHTS = "5b43";
  public static final String PFEIL_HERAUF = "5b41";
  public static final String PFEIL_HERUNTER = "5b42";
  public static final String SP_RUECK_30 = "rueck30";
  public static final String SP_VOR_30 = "rueck30";
  public static final String SP_VOR_600 = "vor600";
  public static final String SP_RUECK_600 = "rueck600";
  public static final String OPT_LOCAL_AUDIO = "-o%20local";
  public static final String OPT_HDMI_AUDIO = "-o%20hdmi";
  public static final String F_PLAY = "play";
  public static final String F_SEEK = "seek";
  public static final String F_PING = "ping";
  public static final String BLANK = " ";
  private static HashMap initParams;
  private static Process playerproc;
  
  /**
@@ -69,10 +32,7 @@
  public static void main(String[] args) {
    initParams = new HashMap();
    for(String arg: args) {
      //logger.info("arg: " + arg);
      String[] argParts = arg.split("=");
      //logger.info(argParts[0]);
      //logger.info(argParts[1]);
      initParams.put(argParts[0], argParts[1]);
    }
    
src/de/uhilger/avdirektor/Server.java
@@ -2,6 +2,7 @@
import com.sun.net.httpserver.HttpServer;
import de.uhilger.avdirektor.handler.CmdHandler;
import de.uhilger.avdirektor.handler.OMXPlayer;
import de.uhilger.avdirektor.handler.PingHandler;
import de.uhilger.avdirektor.handler.PlayHandler;
import de.uhilger.avdirektor.handler.SeekHandler;
@@ -34,11 +35,11 @@
    logger.info("Server starting on port " + port);
    HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
    server.createContext("/avd/play", new PlayHandler(App.F_PLAY));
    server.createContext("/avd/seek", new SeekHandler(App.F_SEEK));
    server.createContext("/avd/stop", new CmdHandler(App.CMD_STOP));
    server.createContext("/avd/pause", new CmdHandler(App.CMD_PAUSE_RESUME));
    server.createContext("/avd/ping", new PingHandler(App.F_PING));
    server.createContext("/avd/play", new PlayHandler(OMXPlayer.F_PLAY));
    server.createContext("/avd/seek", new SeekHandler(OMXPlayer.F_SEEK));
    server.createContext("/avd/stop", new CmdHandler(OMXPlayer.CMD_STOP));
    server.createContext("/avd/pause", new CmdHandler(OMXPlayer.CMD_PAUSE_RESUME));
    server.createContext("/avd/ping", new PingHandler(OMXPlayer.F_PING));
    server.createContext("/avd/server/stop", new StopServerHandler());
    //server.setExecutor(null); // creates a default executor
    server.setExecutor(Executors.newFixedThreadPool(20));
src/de/uhilger/avdirektor/handler/OMXPlayer.java
@@ -27,6 +27,39 @@
  
  private static final Logger logger = Logger.getLogger(OMXPlayer.class.getName());
  
  public static final String CMD_STOP = "q";
  public static final String CMD_DEC_SPEED = "1";
  public static final String CMD_INC_SPEED = "2";
  public static final String CMD_PREV_AUDIO = "j";
  public static final String CMD_NEXT_AUDIO = "k";
  public static final String CMD_PREV_CHAPTER = "i";
  public static final String CMD_NEXT_CHAPTER = "o";
  public static final String CMD_PREV_SUB = "n";
  public static final String CMD_NEXT_SUB = "m";
  public static final String CMD_TOGGLE_SUB = "s";
  public static final String CMD_PAUSE_RESUME = "p";
  public static final String CMD_DEC_VOL = "-";
  public static final String CMD_INC_VOL = "+";
  public static final String PFEIL_LINKS = "5b44";
  public static final String PFEIL_RECHTS = "5b43";
  public static final String PFEIL_HERAUF = "5b41";
  public static final String PFEIL_HERUNTER = "5b42";
  public static final String SP_RUECK_30 = "rueck30";
  public static final String SP_VOR_30 = "rueck30";
  public static final String SP_VOR_600 = "vor600";
  public static final String SP_RUECK_600 = "rueck600";
  public static final String OPT_LOCAL_AUDIO = "-o%20local";
  public static final String OPT_HDMI_AUDIO = "-o%20hdmi";
  public static final String F_PLAY = "play";
  public static final String F_SEEK = "seek";
  public static final String F_PING = "ping";
  public static final String BLANK = " ";
  /**
   * Einen Prozess zum Abspielen mit dem omxplayer starten
   * @param urlStr  URL der Quelle, die abgespielt werden soll
@@ -74,7 +107,7 @@
      StringBuilder kommando = new StringBuilder("omxplayer ");
      if(parameter != null) {
        kommando.append(parameter);
        kommando.append(App.BLANK);
        kommando.append(BLANK);
      }
      if(urlStr.startsWith("http")) {
        kommando.append(urlStr.replace(" ", "%20"));
@@ -143,7 +176,7 @@
        // t.removeAttribute(App.PI_PLAYER);
        antwort = "Es ist kein Player zum Beenden vorhanden, aber der Servlet-Kontext wurde bereinigt.";
      } else {
        kommando(App.CMD_STOP);
        kommando(CMD_STOP);
        //t.removeAttribute(PI_PLAYER);
        antwort = "Player gestoppt, Kontext bereinigt.";
      }
@@ -177,7 +210,7 @@
        Writer out = new BufferedWriter(new OutputStreamWriter(os));
        out.write(k);
        out.flush();
        if(k.equals(App.CMD_STOP)) {
        if(k.equals(CMD_STOP)) {
          out.close();
          App.setPlayerProcess(null);
          //player_process.destroy();
src/de/uhilger/avdirektor/handler/StopServerHandler.java
@@ -16,7 +16,7 @@
  @Override
  public void handle(HttpExchange exchange) throws IOException {
    Logger.getLogger(StopServerHandler.class.getName()).info(exchange.getRequestURI().toString());    
    this.kommando(App.CMD_STOP);
    this.kommando(CMD_STOP);
    String response = "Server stopped";
    exchange.sendResponseHeaders(200, response.length());
    OutputStream os = exchange.getResponseBody();