- Streaming Settings Page

- Activate Streaming
- Set Port
- Set Bitrate
This commit is contained in:
Andreas 2014-05-15 18:51:21 +02:00
parent ea2de21921
commit d2e9cb510f
11 changed files with 357 additions and 18 deletions

View File

@ -361,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
@ -642,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
@ -763,6 +765,7 @@ set(UI
ui/organiseerrordialog.ui
ui/playbacksettingspage.ui
ui/settingsdialog.ui
ui/streamingsettingspage.ui
ui/trackselectiondialog.ui
widgets/equalizerslider.ui

View File

@ -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

View File

@ -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_;

View File

@ -114,8 +114,6 @@ Q_IMPORT_PLUGIN(qsqlite)
namespace {
#include "streaming/streamserver.h"
void LoadTranslation(const QString& prefix, const QString& path,
const QString& language) {
#if QT_VERSION < 0x040700
@ -496,9 +494,6 @@ int main(int argc, char* argv[]) {
SLOT(CommandlineOptionsReceived(QByteArray)));
w.CommandlineOptionsReceived(options);
StreamServer stream_server(app.player());;
stream_server.Listen();
int ret = a.exec();
#ifdef Q_OS_LINUX

View File

@ -1,6 +1,7 @@
#include "streamserver.h"
#include <QByteArray>
#include <QSettings>
#include <QTcpSocket>
#include <QTcpServer>
@ -13,18 +14,34 @@
#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();
//GstEngine::InitialiseGstreamer();
gst_init(nullptr, nullptr);
}
void StreamServer::Listen(quint16 port) {
server_->listen(QHostAddress::LocalHost, port);
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;
@ -60,7 +77,7 @@ static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage* msg, gpointer data)
GST_OBJECT_NAME(msg->src), err->message);
g_error_free(err);
g_free(dbg_info);
socket->deleteLater();
socket->close();
break;
}
default:
@ -77,7 +94,6 @@ void StreamServer::ReadyRead(QTcpSocket* socket, QByteArray buffer) {
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");
@ -86,9 +102,9 @@ void StreamServer::ReadyRead(QTcpSocket* socket, QByteArray buffer) {
QUrl url(QString::fromUtf8(response), QUrl::StrictMode);
UrlHandler* handler = player_->HandlerForUrl(url);
connect(handler, SIGNAL(AsyncLoadComplete(const UrlHandler::LoadResult&)),
SLOT(AsyncLoadComplete(const UrlHandler::LoadResult&)), Qt::UniqueConnection);
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);
@ -118,6 +134,7 @@ void StreamServer::SendStream(const QUrl& url, QTcpSocket* socket) {
decodebin, audioconvert, audioresample, vorbisenc, oggmux, fdsink,
NULL);
g_object_set(vorbisenc, "bitrate", getBitrate(), NULL);
g_object_set(decodebin, "uri", url.toString().toUtf8().constData(), NULL);
g_object_set(fdsink, "fd", socket->socketDescriptor(), NULL);
@ -143,3 +160,9 @@ void StreamServer::AsyncLoadComplete(const UrlHandler::LoadResult& result) {
QTcpSocket* socket = sockets_.take(result.original_url_);
SendStream(result.media_url_, socket);
}
int StreamServer::getBitrate() {
QSettings s;
s.beginGroup(kSettingsGroup);
return s.value("bitrate", 128).toInt() * 1000;
}

View File

@ -1,5 +1,5 @@
#ifndef STREAMSERVER_H
#define STREAMSERVER_h
#define STREAMSERVER_H
#include <QByteArray>
#include <QObject>
@ -15,8 +15,12 @@ class QTcpSocket;
class StreamServer : public QObject {
Q_OBJECT
public:
StreamServer(Player* player, QObject* parent = 0);
void Listen(quint16 port = 8080);
static const char* kSettingsGroup;
static const quint16 kDefaultServerPort;
StreamServer(Player* player, QObject* parent = nullptr);
void Listen();
void StopListening();
private slots:
void AcceptConnection();
@ -26,6 +30,7 @@ class StreamServer : public QObject {
private:
QByteArray ParseRequest(const QByteArray& data);
void SendStream(const QUrl& url, QTcpSocket* socket);
int getBitrate();
Player* player_;
QTcpServer* server_;

View File

@ -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

View File

@ -65,6 +65,7 @@ class SettingsDialog : public QDialog {
Page_GlobalSearch,
Page_Appearance,
Page_NetworkRemote,
Page_Streaming,
Page_Notifications,
Page_Library,
Page_Lastfm,

View 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("bitrate", 128).toInt());
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("bitrate", ui_->bitrate_box->value());
s.endGroup();
if (wasStreaming != ui_->use_streaming->isChecked()) {
if (ui_->use_streaming->isChecked())
dialog()->app()->stream_server()->Listen();
else
dialog()->app()->stream_server()->StopListening();
}
}

View 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

View File

@ -0,0 +1,190 @@
<?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>Bitrate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="bitrate_slider">
<property name="maximum">
<number>250</number>
</property>
<property name="value">
<number>128</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="bitrate_box">
<property name="suffix">
<string> kBps</string>
</property>
<property name="maximum">
<number>250</number>
</property>
<property name="value">
<number>128</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>