Compare commits
5 Commits
master
...
streamserv
Author | SHA1 | Date | |
---|---|---|---|
|
6137d047e9 | ||
|
3452da4525 | ||
|
d2e9cb510f | ||
|
ea2de21921 | ||
|
343d738da2 |
@ -317,6 +317,8 @@ set(SOURCES
|
||||
songinfo/ultimatelyricsprovider.cpp
|
||||
songinfo/ultimatelyricsreader.cpp
|
||||
|
||||
streaming/streamserver.cpp
|
||||
|
||||
transcoder/transcodedialog.cpp
|
||||
transcoder/transcoder.cpp
|
||||
transcoder/transcoderoptionsaac.cpp
|
||||
@ -359,6 +361,7 @@ set(SOURCES
|
||||
ui/settingsdialog.cpp
|
||||
ui/settingspage.cpp
|
||||
ui/standarditemiconloader.cpp
|
||||
ui/streamingsettingspage.cpp
|
||||
ui/systemtrayicon.cpp
|
||||
ui/trackselectiondialog.cpp
|
||||
ui/windows7thumbbar.cpp
|
||||
@ -605,6 +608,8 @@ set(HEADERS
|
||||
songinfo/ultimatelyricsprovider.h
|
||||
songinfo/ultimatelyricsreader.h
|
||||
|
||||
streaming/streamserver.h
|
||||
|
||||
transcoder/transcodedialog.h
|
||||
transcoder/transcoder.h
|
||||
transcoder/transcoderoptionsdialog.h
|
||||
@ -638,6 +643,7 @@ set(HEADERS
|
||||
ui/settingsdialog.h
|
||||
ui/settingspage.h
|
||||
ui/standarditemiconloader.h
|
||||
ui/streamingsettingspage.h
|
||||
ui/systemtrayicon.h
|
||||
ui/trackselectiondialog.h
|
||||
ui/windows7thumbbar.h
|
||||
@ -759,6 +765,7 @@ set(UI
|
||||
ui/organiseerrordialog.ui
|
||||
ui/playbacksettingspage.ui
|
||||
ui/settingsdialog.ui
|
||||
ui/streamingsettingspage.ui
|
||||
ui/trackselectiondialog.ui
|
||||
|
||||
widgets/equalizerslider.ui
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "podcasts/podcastbackend.h"
|
||||
#include "podcasts/podcastdownloader.h"
|
||||
#include "podcasts/podcastupdater.h"
|
||||
#include "streaming/streamserver.h"
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
#include "internet/lastfmservice.h"
|
||||
@ -74,7 +75,8 @@ Application::Application(QObject* parent)
|
||||
moodbar_controller_(nullptr),
|
||||
network_remote_(nullptr),
|
||||
network_remote_helper_(nullptr),
|
||||
scrobbler_(nullptr) {
|
||||
scrobbler_(nullptr),
|
||||
stream_server_(nullptr) {
|
||||
tag_reader_client_ = new TagReaderClient(this);
|
||||
MoveToNewThread(tag_reader_client_);
|
||||
tag_reader_client_->Start();
|
||||
@ -114,6 +116,10 @@ Application::Application(QObject* parent)
|
||||
network_remote_ = new NetworkRemote(this);
|
||||
MoveToNewThread(network_remote_);
|
||||
|
||||
// StreamServer
|
||||
stream_server_ = new StreamServer(player_);
|
||||
stream_server_->Listen();
|
||||
|
||||
// This must be before libraray_->Init();
|
||||
// In the constructor the helper waits for the signal
|
||||
// PlaylistManagerInitialized
|
||||
|
@ -45,6 +45,7 @@ class PlaylistManager;
|
||||
class PodcastBackend;
|
||||
class PodcastUpdater;
|
||||
class Scrobbler;
|
||||
class StreamServer;
|
||||
class TagReaderClient;
|
||||
class TaskManager;
|
||||
|
||||
@ -88,6 +89,7 @@ class Application : public QObject {
|
||||
return network_remote_helper_;
|
||||
}
|
||||
Scrobbler* scrobbler() const { return scrobbler_; }
|
||||
StreamServer* stream_server() const { return stream_server_; }
|
||||
|
||||
LibraryBackend* library_backend() const;
|
||||
LibraryModel* library_model() const;
|
||||
@ -131,6 +133,7 @@ signals:
|
||||
NetworkRemote* network_remote_;
|
||||
NetworkRemoteHelper* network_remote_helper_;
|
||||
Scrobbler* scrobbler_;
|
||||
StreamServer* stream_server_;
|
||||
|
||||
QList<QObject*> objects_in_threads_;
|
||||
QList<QThread*> threads_;
|
||||
|
@ -600,7 +600,7 @@ void Player::UnregisterUrlHandler(UrlHandler* handler) {
|
||||
SLOT(HandleLoadResult(UrlHandler::LoadResult)));
|
||||
}
|
||||
|
||||
const UrlHandler* Player::HandlerForUrl(const QUrl& url) const {
|
||||
UrlHandler* Player::HandlerForUrl(const QUrl& url) const {
|
||||
QMap<QString, UrlHandler*>::const_iterator it =
|
||||
url_handlers_.constFind(url.scheme());
|
||||
if (it == url_handlers_.constEnd()) {
|
||||
|
@ -122,7 +122,7 @@ class Player : public PlayerInterface {
|
||||
void RegisterUrlHandler(UrlHandler* handler);
|
||||
void UnregisterUrlHandler(UrlHandler* handler);
|
||||
|
||||
const UrlHandler* HandlerForUrl(const QUrl& url) const;
|
||||
UrlHandler* HandlerForUrl(const QUrl& url) const;
|
||||
|
||||
public slots:
|
||||
void ReloadSettings();
|
||||
|
168
src/streaming/streamserver.cpp
Normal file
168
src/streaming/streamserver.cpp
Normal file
@ -0,0 +1,168 @@
|
||||
#include "streamserver.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QSettings>
|
||||
#include <QTcpSocket>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include "core/closure.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/player.h"
|
||||
#include "core/urlhandler.h"
|
||||
#include "core/signalchecker.h"
|
||||
#include "engines/gstengine.h"
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
const char* StreamServer::kSettingsGroup = "Streaming";
|
||||
const quint16 StreamServer::kDefaultServerPort = 8080;
|
||||
|
||||
StreamServer::StreamServer(Player* player, QObject* parent)
|
||||
: QObject(parent),
|
||||
player_(player),
|
||||
server_(new QTcpServer(this)) {
|
||||
//GstEngine::InitialiseGstreamer();
|
||||
gst_init(nullptr, nullptr);
|
||||
}
|
||||
|
||||
void StreamServer::Listen() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
|
||||
// Streaming activated?
|
||||
if (!s.value("use_streaming", false).toBool()) {
|
||||
return;
|
||||
}
|
||||
|
||||
server_->listen(QHostAddress::LocalHost, s.value("port", kDefaultServerPort).toInt());
|
||||
connect(server_, SIGNAL(newConnection()), SLOT(AcceptConnection()));
|
||||
}
|
||||
|
||||
void StreamServer::StopListening() {
|
||||
server_->close();
|
||||
}
|
||||
|
||||
void StreamServer::AcceptConnection() {
|
||||
QTcpSocket* socket = server_->nextPendingConnection();
|
||||
QByteArray buffer;
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(ReadyRead(QTcpSocket*, QByteArray)), socket, buffer);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static void NewPadCallback(GstElement*, GstPad* pad, gpointer data) {
|
||||
qLog(Debug) << Q_FUNC_INFO;
|
||||
GstElement* converter = reinterpret_cast<GstElement*>(data);
|
||||
GstPad* const audiopad = gst_element_get_static_pad(converter, "sink");
|
||||
|
||||
if (GST_PAD_IS_LINKED(audiopad)) {
|
||||
qLog(Warning) << "Pad already linked";
|
||||
gst_pad_unlink(audiopad, GST_PAD_PEER(audiopad));
|
||||
}
|
||||
|
||||
gst_pad_link(pad, audiopad);
|
||||
gst_object_unref(audiopad);
|
||||
}
|
||||
|
||||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data) {
|
||||
QTcpSocket* socket = reinterpret_cast<QTcpSocket*>(data);
|
||||
|
||||
switch (GST_MESSAGE_TYPE(msg)) {
|
||||
case GST_MESSAGE_ERROR: {
|
||||
GError* err = NULL;
|
||||
gchar* dbg_info = NULL;
|
||||
gst_message_parse_error(msg, &err, &dbg_info);
|
||||
g_printerr("Error from element %s: %s\n",
|
||||
GST_OBJECT_NAME(msg->src), err->message);
|
||||
g_error_free(err);
|
||||
g_free(dbg_info);
|
||||
socket->close();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void StreamServer::ReadyRead(QTcpSocket* socket, QByteArray buffer) {
|
||||
buffer.append(socket->readAll());
|
||||
if (socket->atEnd() || buffer.endsWith("\r\n\r\n")) {
|
||||
QByteArray response = ParseRequest(buffer);
|
||||
qLog(Debug) << response;
|
||||
socket->write("HTTP/1.0 200 OK\r\n");
|
||||
socket->write("Content-type: application/ogg\r\n");
|
||||
socket->write("Connection: close\r\n");
|
||||
socket->write("\r\n");
|
||||
socket->flush();
|
||||
|
||||
QUrl url(QString::fromUtf8(response), QUrl::StrictMode);
|
||||
UrlHandler* handler = player_->HandlerForUrl(url);
|
||||
if (handler) {
|
||||
connect(handler, SIGNAL(AsyncLoadComplete(const UrlHandler::LoadResult&)),
|
||||
SLOT(AsyncLoadComplete(const UrlHandler::LoadResult&)), Qt::UniqueConnection);
|
||||
UrlHandler::LoadResult result = handler->StartLoading(url);
|
||||
if (result.type_ == UrlHandler::LoadResult::TrackAvailable) {
|
||||
SendStream(result.media_url_, socket);
|
||||
} else if (result.type_ == UrlHandler::LoadResult::WillLoadAsynchronously) {
|
||||
sockets_[url] = socket;
|
||||
}
|
||||
} else {
|
||||
SendStream(url, socket);
|
||||
}
|
||||
|
||||
} else {
|
||||
NewClosure(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(ReadyRead(QTcpSocket*, QByteArray)), socket, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamServer::SendStream(const QUrl& url, QTcpSocket* socket) {
|
||||
GstElement* pipeline = gst_pipeline_new("stream_pipeline");
|
||||
|
||||
GstElement* decodebin = gst_element_factory_make("uridecodebin", NULL);
|
||||
GstElement* audioconvert = gst_element_factory_make("audioconvert", NULL);
|
||||
GstElement* audioresample = gst_element_factory_make("audioresample", NULL);
|
||||
GstElement* vorbisenc = gst_element_factory_make("vorbisenc", NULL);
|
||||
GstElement* oggmux = gst_element_factory_make("oggmux", NULL);
|
||||
GstElement* fdsink = gst_element_factory_make("fdsink", NULL);
|
||||
gst_bin_add_many(GST_BIN(pipeline),
|
||||
decodebin, audioconvert, audioresample, vorbisenc, oggmux, fdsink,
|
||||
NULL);
|
||||
|
||||
g_object_set(vorbisenc, "quality", getQuality(), NULL);
|
||||
g_object_set(decodebin, "uri", url.toString().toUtf8().constData(), NULL);
|
||||
g_object_set(fdsink, "fd", socket->socketDescriptor(), NULL);
|
||||
|
||||
gst_element_link_many(
|
||||
audioconvert, audioresample, vorbisenc, oggmux, fdsink, NULL);
|
||||
|
||||
CHECKED_GCONNECT(decodebin, "pad-added", &NewPadCallback, audioconvert);
|
||||
|
||||
gst_bus_set_sync_handler(
|
||||
gst_pipeline_get_bus(GST_PIPELINE(pipeline)), &BusCallbackSync, socket);
|
||||
|
||||
gst_element_set_state(pipeline, GST_STATE_PLAYING);
|
||||
}
|
||||
|
||||
QByteArray StreamServer::ParseRequest(const QByteArray& data) {
|
||||
QList<QByteArray> lines = data.split('\r');
|
||||
QByteArray path = lines[0].split(' ')[1];
|
||||
QByteArray id = path.mid(1);
|
||||
return id;
|
||||
}
|
||||
|
||||
void StreamServer::AsyncLoadComplete(const UrlHandler::LoadResult& result) {
|
||||
QTcpSocket* socket = sockets_.take(result.original_url_);
|
||||
SendStream(result.media_url_, socket);
|
||||
}
|
||||
|
||||
double StreamServer::getQuality() {
|
||||
QSettings s;
|
||||
s.beginGroup(kSettingsGroup);
|
||||
return s.value("quality", 0.3).toDouble();
|
||||
}
|
40
src/streaming/streamserver.h
Normal file
40
src/streaming/streamserver.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef STREAMSERVER_H
|
||||
#define STREAMSERVER_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
|
||||
#include "core/urlhandler.h"
|
||||
|
||||
class Player;
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
|
||||
class StreamServer : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static const char* kSettingsGroup;
|
||||
static const quint16 kDefaultServerPort;
|
||||
|
||||
StreamServer(Player* player, QObject* parent = nullptr);
|
||||
void Listen();
|
||||
void StopListening();
|
||||
|
||||
private slots:
|
||||
void AcceptConnection();
|
||||
void ReadyRead(QTcpSocket* socket, QByteArray buffer);
|
||||
void AsyncLoadComplete(const UrlHandler::LoadResult& result);
|
||||
|
||||
private:
|
||||
QByteArray ParseRequest(const QByteArray& data);
|
||||
void SendStream(const QUrl& url, QTcpSocket* socket);
|
||||
double getQuality();
|
||||
|
||||
Player* player_;
|
||||
QTcpServer* server_;
|
||||
QMap<QUrl, QTcpSocket*> sockets_;
|
||||
};
|
||||
|
||||
#endif // STREAMSERVER_H
|
@ -27,6 +27,7 @@
|
||||
#include "notificationssettingspage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "streamingsettingspage.h"
|
||||
#include "core/application.h"
|
||||
#include "core/backgroundstreams.h"
|
||||
#include "core/logging.h"
|
||||
@ -137,10 +138,14 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams,
|
||||
AddPage(Page_Library, new LibrarySettingsPage(this), general);
|
||||
AddPage(Page_Proxy, new NetworkProxySettingsPage(this), general);
|
||||
AddPage(Page_Transcoding, new TranscoderSettingsPage(this), general);
|
||||
AddPage(Page_NetworkRemote, new NetworkRemoteSettingsPage(this), general);
|
||||
|
||||
QTreeWidgetItem* network = AddCategory(tr("Interfaces"));
|
||||
|
||||
AddPage(Page_NetworkRemote, new NetworkRemoteSettingsPage(this), network);
|
||||
AddPage(Page_Streaming, new StreamingSettingsPage(this), network);
|
||||
|
||||
#ifdef HAVE_WIIMOTEDEV
|
||||
AddPage(Page_Wiimotedev, new WiimoteSettingsPage(this), general);
|
||||
AddPage(Page_Wiimotedev, new WiimoteSettingsPage(this), network);
|
||||
#endif
|
||||
|
||||
// User interface
|
||||
|
@ -65,6 +65,7 @@ class SettingsDialog : public QDialog {
|
||||
Page_GlobalSearch,
|
||||
Page_Appearance,
|
||||
Page_NetworkRemote,
|
||||
Page_Streaming,
|
||||
Page_Notifications,
|
||||
Page_Library,
|
||||
Page_Lastfm,
|
||||
|
69
src/ui/streamingsettingspage.cpp
Normal file
69
src/ui/streamingsettingspage.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine 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
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "core/application.h"
|
||||
#include "iconloader.h"
|
||||
#include "streamingsettingspage.h"
|
||||
#include "ui_streamingsettingspage.h"
|
||||
#include "streaming/streamserver.h"
|
||||
#include "settingsdialog.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QHostInfo>
|
||||
|
||||
StreamingSettingsPage::StreamingSettingsPage(SettingsDialog* dialog)
|
||||
: SettingsPage(dialog), ui_(new Ui_StreamingSettingsPage) {
|
||||
ui_->setupUi(this);
|
||||
setWindowIcon(IconLoader::Load("ipodtouchicon"));
|
||||
}
|
||||
|
||||
StreamingSettingsPage::~StreamingSettingsPage() { delete ui_; }
|
||||
|
||||
void StreamingSettingsPage::Load() {
|
||||
QSettings s;
|
||||
|
||||
s.beginGroup(StreamServer::kSettingsGroup);
|
||||
|
||||
ui_->use_streaming->setChecked(s.value("use_streaming").toBool());
|
||||
ui_->stream_port->setValue(
|
||||
s.value("port", StreamServer::kDefaultServerPort).toInt());
|
||||
|
||||
ui_->bitrate_box->setValue(s.value("quality", 0.3).toDouble() * 10);
|
||||
|
||||
s.endGroup();
|
||||
}
|
||||
|
||||
void StreamingSettingsPage::Save() {
|
||||
QSettings s;
|
||||
|
||||
s.beginGroup(StreamServer::kSettingsGroup);
|
||||
bool wasStreaming = s.value("use_streaming", false).toBool();
|
||||
|
||||
s.setValue("use_streaming", ui_->use_streaming->isChecked());
|
||||
s.setValue("port", ui_->stream_port->value());
|
||||
s.setValue("quality", double(ui_->bitrate_box->value()) / 10);
|
||||
|
||||
s.endGroup();
|
||||
|
||||
if (wasStreaming != ui_->use_streaming->isChecked()) {
|
||||
if (ui_->use_streaming->isChecked())
|
||||
dialog()->app()->stream_server()->Listen();
|
||||
else
|
||||
dialog()->app()->stream_server()->StopListening();
|
||||
}
|
||||
|
||||
}
|
39
src/ui/streamingsettingspage.h
Normal file
39
src/ui/streamingsettingspage.h
Normal file
@ -0,0 +1,39 @@
|
||||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine 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
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STREAMINGSETTINGSPAGE_H
|
||||
#define STREAMINGSETTINGSPAGE_H
|
||||
|
||||
#include "settingspage.h"
|
||||
|
||||
class Ui_StreamingSettingsPage;
|
||||
|
||||
class StreamingSettingsPage : public SettingsPage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
StreamingSettingsPage(SettingsDialog* dialog);
|
||||
~StreamingSettingsPage();
|
||||
|
||||
void Load();
|
||||
void Save();
|
||||
|
||||
private:
|
||||
Ui_StreamingSettingsPage* ui_;
|
||||
};
|
||||
|
||||
#endif // STREAMINGSETTINGSPAGE_H
|
202
src/ui/streamingsettingspage.ui
Normal file
202
src/ui/streamingsettingspage.ui
Normal file
@ -0,0 +1,202 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>StreamingSettingsPage</class>
|
||||
<widget class="QWidget" name="StreamingSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>385</width>
|
||||
<height>528</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Streaming</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_streaming">
|
||||
<property name="text">
|
||||
<string>Active steaming</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>This enables HTTP streaming. A client can stream any Clementine URL except for Spotify.</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="use_stream_container_2">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_stream_port">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>171</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Port</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="stream_port">
|
||||
<property name="maximum">
|
||||
<number>65535</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>8080</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Quality</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QSlider" name="bitrate_slider">
|
||||
<property name="minimum">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="pageStep">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="bitrate_box">
|
||||
<property name="suffix">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="prefix">
|
||||
<string>q</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>use_streaming</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>use_stream_container_2</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>59</x>
|
||||
<y>22</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>57</x>
|
||||
<y>43</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>bitrate_box</sender>
|
||||
<signal>valueChanged(int)</signal>
|
||||
<receiver>bitrate_slider</receiver>
|
||||
<slot>setValue(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>325</x>
|
||||
<y>147</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>241</x>
|
||||
<y>146</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>bitrate_slider</sender>
|
||||
<signal>valueChanged(int)</signal>
|
||||
<receiver>bitrate_box</receiver>
|
||||
<slot>setValue(int)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>241</x>
|
||||
<y>146</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>325</x>
|
||||
<y>147</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
Loading…
x
Reference in New Issue
Block a user