forked from j62/ctbrec
Add max resolution setting for the player
This commit is contained in:
parent
79892b1a13
commit
b87f090ac3
|
@ -1,6 +1,7 @@
|
||||||
package ctbrec.ui;
|
package ctbrec.ui;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
@ -9,11 +10,18 @@ import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.iheartradio.m3u8.ParseException;
|
||||||
|
import com.iheartradio.m3u8.PlaylistException;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Hmac;
|
import ctbrec.Hmac;
|
||||||
import ctbrec.Model;
|
import ctbrec.Model;
|
||||||
|
@ -32,11 +40,7 @@ public class Player {
|
||||||
private Player() {
|
private Player() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean play(String url) {
|
private static boolean play(String url, boolean async) {
|
||||||
return play(url, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean play(String url, boolean async) {
|
|
||||||
boolean singlePlayer = Config.getInstance().getSettings().singlePlayer;
|
boolean singlePlayer = Config.getInstance().getSettings().singlePlayer;
|
||||||
try {
|
try {
|
||||||
if (singlePlayer && playerThread != null && playerThread.isRunning()) {
|
if (singlePlayer && playerThread != null && playerThread.isRunning()) {
|
||||||
|
@ -80,11 +84,9 @@ public class Player {
|
||||||
if (singlePlayer && playerThread != null && playerThread.isRunning()) {
|
if (singlePlayer && playerThread != null && playerThread.isRunning()) {
|
||||||
playerThread.stopThread();
|
playerThread.stopThread();
|
||||||
}
|
}
|
||||||
List<StreamSource> sources = model.getStreamSources();
|
String playlistUrl = getPlaylistUrl(model);
|
||||||
Collections.sort(sources);
|
LOG.debug("Playing {}", playlistUrl);
|
||||||
StreamSource best = sources.get(sources.size() - 1);
|
return Player.play(playlistUrl, async);
|
||||||
LOG.debug("Playing {}", best.getMediaPlaylistUrl());
|
|
||||||
return Player.play(best.getMediaPlaylistUrl(), async);
|
|
||||||
} else {
|
} else {
|
||||||
Dialogs.showError(scene, "Room not public", "Room is currently not public", null);
|
Dialogs.showError(scene, "Room not public", "Room is currently not public", null);
|
||||||
return false;
|
return false;
|
||||||
|
@ -96,6 +98,29 @@ public class Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String getPlaylistUrl(Model model) throws IOException, ExecutionException, ParseException, PlaylistException, JAXBException {
|
||||||
|
List<StreamSource> sources = model.getStreamSources();
|
||||||
|
Collections.sort(sources);
|
||||||
|
StreamSource best;
|
||||||
|
int maxRes = Config.getInstance().getSettings().maximumResolutionPlayer;
|
||||||
|
if (maxRes > 0 && !sources.isEmpty()) {
|
||||||
|
for (Iterator<StreamSource> iterator = sources.iterator(); iterator.hasNext();) {
|
||||||
|
StreamSource streamSource = iterator.next();
|
||||||
|
if (streamSource.height > 0 && maxRes < streamSource.height) {
|
||||||
|
LOG.trace("Res too high {} > {}", streamSource.height, maxRes);
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sources.isEmpty()) {
|
||||||
|
throw new RuntimeException("No stream left in playlist, because player resolution is set to " + maxRes);
|
||||||
|
} else {
|
||||||
|
LOG.debug("{} selected {}", model.getName(), sources.get(sources.size() - 1));
|
||||||
|
best = sources.get(sources.size() - 1);
|
||||||
|
}
|
||||||
|
return best.getMediaPlaylistUrl();
|
||||||
|
}
|
||||||
|
|
||||||
public static void stop() {
|
public static void stop() {
|
||||||
if (playerThread != null) {
|
if (playerThread != null) {
|
||||||
playerThread.stopThread();
|
playerThread.stopThread();
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package ctbrec.ui.settings;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import ctbrec.Config;
|
||||||
|
import ctbrec.Settings;
|
||||||
|
import ctbrec.ui.controls.Dialogs;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Dialog;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.stage.Modality;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
|
||||||
|
public class PlayerSettingsDialog extends Dialog<Exception> {
|
||||||
|
|
||||||
|
private Scene parent;
|
||||||
|
private Config config;
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
private TextField playerParams;
|
||||||
|
private TextField maxResolution;
|
||||||
|
|
||||||
|
public PlayerSettingsDialog(Scene parent, Config config) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.config = config;
|
||||||
|
this.settings = config.getSettings();
|
||||||
|
|
||||||
|
initGui();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGui() {
|
||||||
|
setTitle("Player Settings");
|
||||||
|
setHeaderText("Player Settings");
|
||||||
|
getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
|
||||||
|
initModality(Modality.APPLICATION_MODAL);
|
||||||
|
setResizable(true);
|
||||||
|
InputStream icon = Dialogs.class.getResourceAsStream("/icon.png");
|
||||||
|
Stage stage = (Stage) getDialogPane().getScene().getWindow();
|
||||||
|
stage.getIcons().add(new Image(icon));
|
||||||
|
if (parent != null) {
|
||||||
|
stage.getScene().getStylesheets().addAll(parent.getStylesheets());
|
||||||
|
}
|
||||||
|
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.setHgap(10);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.setPadding(new Insets(20, 150, 10, 10));
|
||||||
|
|
||||||
|
grid.add(new Label("Start parameters"), 0, 0);
|
||||||
|
playerParams = new TextField(settings.mediaPlayerParams);
|
||||||
|
grid.add(playerParams, 1, 0);
|
||||||
|
getDialogPane().setContent(grid);
|
||||||
|
GridPane.setFillWidth(playerParams, true);
|
||||||
|
GridPane.setHgrow(playerParams, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Label l = new Label("Maximum resolution (0 = unlimited)");
|
||||||
|
grid.add(l, 0, 1);
|
||||||
|
maxResolution = new TextField(Integer.toString(settings.maximumResolutionPlayer));
|
||||||
|
Tooltip tt = new Tooltip("video height, e.g. 720 or 1080");
|
||||||
|
l.setTooltip(tt);
|
||||||
|
maxResolution.setTooltip(tt);
|
||||||
|
grid.add(maxResolution, 1, 1);
|
||||||
|
getDialogPane().setContent(grid);
|
||||||
|
GridPane.setFillWidth(maxResolution, true);
|
||||||
|
GridPane.setHgrow(maxResolution, Priority.ALWAYS);
|
||||||
|
|
||||||
|
Platform.runLater(playerParams::requestFocus);
|
||||||
|
|
||||||
|
setResultConverter(dialogButton -> {
|
||||||
|
try {
|
||||||
|
if (dialogButton == ButtonType.OK) {
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSettings() throws IOException {
|
||||||
|
settings.mediaPlayerParams = playerParams.getText();
|
||||||
|
String res = maxResolution.getText();
|
||||||
|
if (res.matches("\\d+")) {
|
||||||
|
int newRes = Integer.parseInt(maxResolution.getText());
|
||||||
|
if (newRes != Config.getInstance().getSettings().maximumResolutionPlayer) {
|
||||||
|
settings.maximumResolutionPlayer = newRes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,7 +67,7 @@ import javafx.stage.FileChooser;
|
||||||
|
|
||||||
public class SettingsTab extends Tab implements TabSelectionListener {
|
public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
|
|
||||||
private static final String PATTERN_NOT_A_DIGIT = "[^\\d]";
|
public static final String PATTERN_NOT_A_DIGIT = "[^\\d]";
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsTab.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsTab.class);
|
||||||
private static final int ONE_GIB_IN_BYTES = 1024 * 1024 * 1024;
|
private static final int ONE_GIB_IN_BYTES = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
@ -626,12 +626,17 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
layout.add(mediaPlayer, 1, row);
|
layout.add(mediaPlayer, 1, row);
|
||||||
Button mediaPlayerParamsButton = new Button("⚙");
|
Button mediaPlayerParamsButton = new Button("⚙");
|
||||||
mediaPlayerParamsButton.setOnAction(e -> {
|
mediaPlayerParamsButton.setOnAction(e -> {
|
||||||
Optional<String> playerParams = Dialogs.showTextInput(mediaPlayerParamsButton.getScene(), "Media Player Parameters",
|
// Optional<String> playerParams = Dialogs.showTextInput(mediaPlayerParamsButton.getScene(), "Media Player Parameters",
|
||||||
"Media Player Start Parameters", settings.mediaPlayerParams);
|
// "Media Player Start Parameters", settings.mediaPlayerParams);
|
||||||
playerParams.ifPresent(p -> {
|
// playerParams.ifPresent(p -> {
|
||||||
settings.mediaPlayerParams = p;
|
// settings.mediaPlayerParams = p;
|
||||||
saveConfig();
|
// saveConfig();
|
||||||
});
|
// });
|
||||||
|
PlayerSettingsDialog dialog = new PlayerSettingsDialog(getTabPane().getScene(), Config.getInstance());
|
||||||
|
Optional<Exception> exception = dialog.showAndWait();
|
||||||
|
if (exception.isPresent()) {
|
||||||
|
Dialogs.showError("Saving player parameters", "Player parameters couldn't be saved", exception.get());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
layout.add(mediaPlayerParamsButton, 3, row++);
|
layout.add(mediaPlayerParamsButton, 3, row++);
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ public class Settings {
|
||||||
public boolean livePreviews = false;
|
public boolean livePreviews = false;
|
||||||
public boolean localRecording = true;
|
public boolean localRecording = true;
|
||||||
public int maximumResolution = 0;
|
public int maximumResolution = 0;
|
||||||
|
public int maximumResolutionPlayer = 0;
|
||||||
public String mediaPlayer = "/usr/bin/mpv";
|
public String mediaPlayer = "/usr/bin/mpv";
|
||||||
public String mediaPlayerParams = "";
|
public String mediaPlayerParams = "";
|
||||||
public String mfcBaseUrl = "https://www.myfreecams.com";
|
public String mfcBaseUrl = "https://www.myfreecams.com";
|
||||||
|
|
|
@ -5,6 +5,7 @@ import static ctbrec.io.HttpConstants.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -44,7 +45,7 @@ public class CamsodaModel extends AbstractModel {
|
||||||
private static final String STATUS = "status";
|
private static final String STATUS = "status";
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CamsodaModel.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CamsodaModel.class);
|
||||||
private String streamUrl;
|
private String streamUrl;
|
||||||
private List<StreamSource> streamSources = null;
|
private transient List<StreamSource> streamSources = null;
|
||||||
private float sortOrder = 0;
|
private float sortOrder = 0;
|
||||||
private Random random = new Random();
|
private Random random = new Random();
|
||||||
int[] resolution = new int[2];
|
int[] resolution = new int[2];
|
||||||
|
@ -126,7 +127,8 @@ public class CamsodaModel extends AbstractModel {
|
||||||
streamsource.width = 0;
|
streamsource.width = 0;
|
||||||
streamsource.height = 0;
|
streamsource.height = 0;
|
||||||
}
|
}
|
||||||
streamSources = Collections.singletonList(streamsource);
|
streamSources = new ArrayList<>();
|
||||||
|
streamSources.add(streamsource);
|
||||||
} else {
|
} else {
|
||||||
LOG.trace("Response: {}", response.body().string());
|
LOG.trace("Response: {}", response.body().string());
|
||||||
throw new HttpException(response.code(), response.message());
|
throw new HttpException(response.code(), response.message());
|
||||||
|
|
|
@ -104,8 +104,6 @@ public class ShowupHttpClient extends HttpClient {
|
||||||
JSONObject json = new JSONObject(responseBody);
|
JSONObject json = new JSONObject(responseBody);
|
||||||
return json.optString("status").equalsIgnoreCase("success");
|
return json.optString("status").equalsIgnoreCase("success");
|
||||||
} else {
|
} else {
|
||||||
String msg = "Login was not successful";
|
|
||||||
LOG.warn("{}\n{}", msg, responseBody);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import static ctbrec.Model.State.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
@ -70,7 +70,9 @@ public class ShowupModel extends AbstractModel {
|
||||||
|
|
||||||
int cdnHost = 1 + new Random().nextInt(5);
|
int cdnHost = 1 + new Random().nextInt(5);
|
||||||
src.mediaPlaylistUrl = MessageFormat.format("https://cdn-e0{0}.showup.tv/h5live/http/playlist.m3u8?url=rtmp%3A%2F%2F{1}%3A1935%2Fwebrtc&stream={2}_aac", cdnHost, streamTranscoderAddr, streamId);
|
src.mediaPlaylistUrl = MessageFormat.format("https://cdn-e0{0}.showup.tv/h5live/http/playlist.m3u8?url=rtmp%3A%2F%2F{1}%3A1935%2Fwebrtc&stream={2}_aac", cdnHost, streamTranscoderAddr, streamId);
|
||||||
return Collections.singletonList(src);
|
List<StreamSource> sources = new ArrayList<>();
|
||||||
|
sources.add(src);
|
||||||
|
return sources;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue