implement MPV playback check to restart stuck player (happens with network issues). Make TaskProcess returning just boolean, therefore forcing exception handling to be in implementation. Fix MPV's isAlive call in spawn - you shouldn't do that here, because MPV state at that moment is undefined. Finally make MPVCommandResult spewing String and not nulls
This commit is contained in:
parent
b666981e1e
commit
acdad8acb0
10 changed files with 106 additions and 33 deletions
|
|
@ -84,7 +84,6 @@ public class Main {
|
|||
TaskDispatcher dispatcher = new TaskDispatcher();
|
||||
dispatcher.updateTaskConfig(config.tasks);
|
||||
dispatcher.registerTask(crawler);
|
||||
|
||||
dispatcher.registerTask(processService);
|
||||
|
||||
new Thread(dispatcher).start();
|
||||
|
|
@ -95,12 +94,10 @@ public class Main {
|
|||
|
||||
// start PiIR
|
||||
PiIR piir = new PiIR(config.piir);
|
||||
try {
|
||||
piir.spawn();
|
||||
|
||||
if (piir.spawn()) {
|
||||
processService.registerProcess(piir);
|
||||
} catch (IOException e) {
|
||||
logger.error("failed to spawn piir. fatal. exiting", e);
|
||||
} else {
|
||||
logger.error("failed to spawn piir. exiting");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,11 +83,13 @@ public class MPV implements TaskProcess {
|
|||
private MPVReader reader;
|
||||
private Thread readerThread;
|
||||
|
||||
private Float lastPlaybackTime = null;
|
||||
|
||||
private static final Path MPV_SOCKET_PATH = Path.of("/tmp/mptv-mpv.sock");
|
||||
private static final long WAIT_MILLIS = 250;
|
||||
private static final int WAIT_ATTEMPTS = 5;
|
||||
|
||||
private void waitForConnection(Path socketPath) throws IOException {
|
||||
private void waitForConnection(Path socketPath) throws MPVSocketFailure {
|
||||
for (int i = 0; i < WAIT_ATTEMPTS; i++) {
|
||||
try {
|
||||
Thread.sleep(WAIT_MILLIS);
|
||||
|
|
@ -101,10 +103,12 @@ public class MPV implements TaskProcess {
|
|||
|
||||
logger.info(String.format("connected to socket %s", socket.toString()));
|
||||
} catch (SocketException e) {
|
||||
logger.error("SocketException", e);
|
||||
logger.info("SocketException.. trying to connect");
|
||||
closeConnection();
|
||||
} catch (IOException e) {
|
||||
throw new MPVSocketFailure(e);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
throw new MPVSocketFailure(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,19 +122,62 @@ public class MPV implements TaskProcess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean spawn() throws IOException {
|
||||
process = Runtime.getRuntime().exec(new String[] {
|
||||
"mpv", "--vo=gpu", "--ao=pulse", "--fullscreen", "--input-ipc-server=" + MPV_SOCKET_PATH, url
|
||||
});
|
||||
public boolean spawn() {
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(new String[] {
|
||||
"mpv", "--vo=gpu", "--ao=pulse", "--fullscreen", "--input-ipc-server=" + MPV_SOCKET_PATH, url
|
||||
});
|
||||
|
||||
waitForConnection(MPV_SOCKET_PATH);
|
||||
waitForConnection(MPV_SOCKET_PATH);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
} catch (MPVSocketFailure e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isAlive();
|
||||
return process.isAlive();
|
||||
}
|
||||
|
||||
private boolean checkPlayback() {
|
||||
try {
|
||||
// get playback
|
||||
MPVCommandResult result = executeCommand(
|
||||
new MPVGetProperty(MPVProperty.PLAYBACK_TIME));
|
||||
|
||||
Float playbackTime;
|
||||
try {
|
||||
playbackTime = Float.parseFloat(result.data);
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error("FAILED TO PARSE PLAYBACK DATA: " + result.data);
|
||||
return false;
|
||||
}
|
||||
logger.info("playbackTime " + playbackTime);
|
||||
|
||||
// if we have previous playback - compare them,
|
||||
// if not changed, then player stuck
|
||||
if (lastPlaybackTime != null) {
|
||||
boolean playbackChanged = (playbackTime - lastPlaybackTime) > 0.1;
|
||||
|
||||
lastPlaybackTime = playbackTime;
|
||||
return playbackChanged;
|
||||
} else { // just set first playback
|
||||
lastPlaybackTime = playbackTime;
|
||||
return true;
|
||||
}
|
||||
} catch (MPVCommandTimeout e) {
|
||||
logger.warn("mpv ipc timeout bruh");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlive() {
|
||||
return process != null ? process.isAlive() : false;
|
||||
// if we have process, check if playback still going on
|
||||
if (process != null) {
|
||||
return checkPlayback();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -153,7 +200,7 @@ public class MPV implements TaskProcess {
|
|||
|
||||
private static final long COMMAND_TIMEOUT = 2000L;
|
||||
|
||||
public MPVCommandResult writeCommand(MPVCommand command) {
|
||||
public MPVCommandResult executeCommand(MPVCommand command) {
|
||||
try {
|
||||
commandRequestId = command.setRequestId(requestIdCounter++);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package com.mykola2312.mptv.mpv;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class MPVCommandResult {
|
||||
public int request_id;
|
||||
public String error;
|
||||
|
||||
// parse always as string to avoid headache with different types
|
||||
@JsonProperty("data")
|
||||
public String data;
|
||||
|
||||
public static MPVCommandResult deserialize(String data) throws JsonProcessingException {
|
||||
|
|
|
|||
17
src/main/java/com/mykola2312/mptv/mpv/MPVGetProperty.java
Normal file
17
src/main/java/com/mykola2312/mptv/mpv/MPVGetProperty.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package com.mykola2312.mptv.mpv;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class MPVGetProperty extends MPVCommand {
|
||||
private final MPVProperty property;
|
||||
|
||||
public MPVGetProperty(MPVProperty property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> serializeCommand() {
|
||||
return Arrays.asList("get_property", property.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package com.mykola2312.mptv.mpv;
|
||||
|
||||
public enum MPVProperty {
|
||||
VOLUME ("volume");
|
||||
VOLUME ("volume"),
|
||||
PLAYBACK_TIME ("playback-time");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,7 @@
|
|||
package com.mykola2312.mptv.mpv;
|
||||
|
||||
public class MPVSocketFailure extends RuntimeException {
|
||||
public MPVSocketFailure(Throwable cause) {
|
||||
super("fatal IPC failure. cannot continue");
|
||||
}
|
||||
}
|
||||
|
|
@ -117,10 +117,14 @@ public class PiIR implements TaskProcess {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean spawn() throws IOException {
|
||||
process = Runtime.getRuntime().exec(new String[] {
|
||||
"unbuffer", exec, "dump", "--gpio", String.valueOf(gpio)
|
||||
});
|
||||
public boolean spawn() {
|
||||
try {
|
||||
process = Runtime.getRuntime().exec(new String[] {
|
||||
"unbuffer", exec, "dump", "--gpio", String.valueOf(gpio)
|
||||
});
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
input = process.getInputStream();
|
||||
|
||||
reader = new PiIRReader(this, input);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public class ProcessService implements Task {
|
|||
public void dispatch() {
|
||||
for (TaskProcess process : processes) {
|
||||
if (!process.isAlive()) {
|
||||
logger.info("restarting process " + process.toString());
|
||||
try {
|
||||
process.stop();
|
||||
process.spawn();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
package com.mykola2312.mptv.task;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface TaskProcess {
|
||||
public boolean spawn() throws IOException;
|
||||
public boolean spawn();
|
||||
public boolean isAlive();
|
||||
public void stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import static com.mykola2312.mptv.tables.Channel.*;
|
|||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MenuPanel extends JPanel {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MenuPanel.class);
|
||||
|
|
@ -81,14 +80,12 @@ public class MenuPanel extends JPanel {
|
|||
}
|
||||
|
||||
MPV newPlayer = new MPV(url);
|
||||
try {
|
||||
if (newPlayer.spawn()) {
|
||||
player = newPlayer;
|
||||
if (newPlayer.spawn()) {
|
||||
player = newPlayer;
|
||||
|
||||
Main.processService.registerProcess(player);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("failed to spawn player", e);
|
||||
Main.processService.registerProcess(player);
|
||||
} else {
|
||||
logger.error("failed to spawn mpv");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue