Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dialog to display streams' audio details #5547

Merged
merged 13 commits into from
Dec 21, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ pkg_check_modules(GSTREAMER_APP REQUIRED gstreamer-app-1.0)
pkg_check_modules(GSTREAMER_AUDIO REQUIRED gstreamer-audio-1.0)
pkg_check_modules(GSTREAMER_BASE REQUIRED gstreamer-base-1.0)
pkg_check_modules(GSTREAMER_TAG REQUIRED gstreamer-tag-1.0)
pkg_check_modules(GSTREAMER_PBUTILS REQUIRED gstreamer-pbutils-1.0)
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
pkg_check_modules(LIBMTP libmtp>=1.0)
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.9)
Expand Down Expand Up @@ -155,6 +156,7 @@ include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
include_directories(${GSTREAMER_PBUTILS_INCLUDE_DIRS})
include_directories(${GLIB_INCLUDE_DIRS})
include_directories(${GLIBCONFIG_INCLUDE_DIRS})
include_directories(${LIBXML_INCLUDE_DIRS})
Expand Down
6 changes: 6 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ set(SOURCES
songinfo/songkickconcertwidget.cpp
songinfo/songplaystats.cpp
songinfo/spotifyimages.cpp
songinfo/streamdiscoverer.cpp
songinfo/taglyricsinfoprovider.cpp
songinfo/ultimatelyricslyric.cpp
songinfo/ultimatelyricsprovider.cpp
Expand Down Expand Up @@ -358,6 +359,7 @@ set(SOURCES
ui/settingsdialog.cpp
ui/settingspage.cpp
ui/standarditemiconloader.cpp
ui/streamdetailsdialog.cpp
ui/systemtrayicon.cpp
ui/trackselectiondialog.cpp
ui/windows7thumbbar.cpp
Expand Down Expand Up @@ -603,6 +605,7 @@ set(HEADERS
songinfo/songkickconcertwidget.h
songinfo/songplaystats.h
songinfo/spotifyimages.h
songinfo/streamdiscoverer.h
songinfo/taglyricsinfoprovider.h
songinfo/ultimatelyricslyric.h
songinfo/ultimatelyricsprovider.h
Expand Down Expand Up @@ -641,6 +644,7 @@ set(HEADERS
ui/settingsdialog.h
ui/settingspage.h
ui/standarditemiconloader.h
ui/streamdetailsdialog.h
ui/systemtrayicon.h
ui/trackselectiondialog.h
ui/windows7thumbbar.h
Expand Down Expand Up @@ -764,6 +768,7 @@ set(UI
ui/organiseerrordialog.ui
ui/playbacksettingspage.ui
ui/settingsdialog.ui
ui/streamdetailsdialog.ui
ui/trackselectiondialog.ui

widgets/equalizerslider.ui
Expand Down Expand Up @@ -1258,6 +1263,7 @@ target_link_libraries(clementine_lib
${GSTREAMER_LIBRARIES}
${GSTREAMER_APP_LIBRARIES}
${GSTREAMER_TAG_LIBRARIES}
${GSTREAMER_PBUTILS_LIBRARIES}
${QTSINGLEAPPLICATION_LIBRARIES}
${QTSINGLECOREAPPLICATION_LIBRARIES}
${QTIOCOMPRESSOR_LIBRARIES}
Expand Down
3 changes: 3 additions & 0 deletions src/engines/gstengine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <QtConcurrentRun>

#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>

#include "config.h"
#include "devicefinder.h"
Expand Down Expand Up @@ -146,6 +147,8 @@ bool GstEngine::Init() {
void GstEngine::InitialiseGstreamer() {
gst_init(nullptr, nullptr);

gst_pb_utils_init();

#ifdef HAVE_MOODBAR
gstfastspectrum_register_static();
#endif
Expand Down
130 changes: 130 additions & 0 deletions src/songinfo/streamdiscoverer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#include "streamdiscoverer.h"

#include <gst/pbutils/pbutils.h>
#include "core/logging.h"
#include "core/signalchecker.h"

#include <QEventLoop>

const unsigned int StreamDiscoverer::kDiscoveryTimeoutS = 10;

StreamDiscoverer::StreamDiscoverer() : QObject(nullptr) {
// Setting up a discoverer:
discoverer_ = gst_discoverer_new(kDiscoveryTimeoutS * GST_SECOND, NULL);
if (discoverer_ == NULL) {
qLog(Error) << "Error creating discoverer" << endl;
return;
}

// Connecting its signals:
CHECKED_GCONNECT(discoverer_, "discovered", &on_discovered_cb, this);
CHECKED_GCONNECT(discoverer_, "finished", &on_finished_cb, this);

// Starting the discoverer process:
gst_discoverer_start(discoverer_);
}

StreamDiscoverer::~StreamDiscoverer() {
gst_discoverer_stop(discoverer_);
g_object_unref(discoverer_);
}

void StreamDiscoverer::discover(QString url) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the style guide: https://google.github.io/styleguide/cppguide.html

e.g. Discover not discover and `const QString& url

// Adding the request to discover the url given as a parameter:
qLog(Debug) << "Discover" << url;
std::string url_std = url.toStdString();
const char* url_c = url_std.c_str();
if (!gst_discoverer_discover_uri_async(discoverer_, url_c)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inline url.toStdString().c_str()

qLog(Error) << "Failed to start discovering" << url
<< endl;
g_object_unref(discoverer_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done in the destructor anyway

return;
}

// Creating a loop and setting it to run. That way we can wait for signals.
QEventLoop loop;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WaitForSignal(this, SIGNAL(DiscovererFinished()))

loop.connect(this, SIGNAL(DiscoverererFinished()), SLOT(quit()));
loop.exec();
}

void StreamDiscoverer::on_discovered_cb(GstDiscoverer* discoverer,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnDiscovered

GstDiscovererInfo* info, GError* err,
gpointer self) {
StreamDiscoverer* instance = reinterpret_cast<StreamDiscoverer*>(self);

QString discovered_url(gst_discoverer_info_get_uri(info));

GstDiscovererResult result = gst_discoverer_info_get_result(info);
if (result != GST_DISCOVERER_OK) {
qLog(Error) << "Discovery failed:" << gstDiscovererErrorMessage(result) << endl;
emit instance->Error(tr("Error discovering %1: %2").arg(discovered_url).arg(
gstDiscovererErrorMessage(result)));
return;
}

// Get audio streams (we will only care about the first one, which should be the only one).
GList* audio_streams = gst_discoverer_info_get_audio_streams(info);

if (audio_streams != NULL) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nullptr

qLog(Debug) << "Discovery successful" << endl;
// We found a valid audio stream, extracting and saving its info:
GstDiscovererStreamInfo* stream_audio_info =
(GstDiscovererStreamInfo*)g_list_first(audio_streams)->data;

StreamDetails stream_details;
stream_details.url = discovered_url;
stream_details.bitrate = gst_discoverer_audio_info_get_bitrate(
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
stream_details.channels = gst_discoverer_audio_info_get_channels(
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
stream_details.depth = gst_discoverer_audio_info_get_depth(
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));
stream_details.sample_rate = gst_discoverer_audio_info_get_sample_rate(
GST_DISCOVERER_AUDIO_INFO(stream_audio_info));

// Human-readable codec name:
GstCaps* stream_caps =
gst_discoverer_stream_info_get_caps(stream_audio_info);
gchar* decoder_description =
gst_pb_utils_get_codec_description(stream_caps);
stream_details.format = (decoder_description == NULL)
? QString(tr("Unknown"))
: QString(decoder_description);

gst_caps_unref(stream_caps);
g_free(decoder_description);

emit instance->DataReady(stream_details);

} else {
emit instance->Error(
tr("Could not detect an audio stream in %1").arg(discovered_url));
}

gst_discoverer_stream_info_list_free(audio_streams);
}

void StreamDiscoverer::on_finished_cb(GstDiscoverer* discoverer,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OnFinished

gpointer self) {
// The discoverer doesn't have any more urls in its queue. Let the loop know
// it can exit.
StreamDiscoverer* instance = reinterpret_cast<StreamDiscoverer*>(self);
emit instance->DiscoverererFinished();
}

QString StreamDiscoverer::gstDiscovererErrorMessage(
GstDiscovererResult result) {
switch (result) {
case (GST_DISCOVERER_URI_INVALID):
return tr("Invalid URL");
case (GST_DISCOVERER_TIMEOUT):
return tr("Connection timed out");
case (GST_DISCOVERER_BUSY):
return tr("The discoverer is busy");
case (GST_DISCOVERER_MISSING_PLUGINS):
return tr("Missing plugins");
case (GST_DISCOVERER_ERROR):
default:
return tr("Could not get details");
}
}
49 changes: 49 additions & 0 deletions src/songinfo/streamdiscoverer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#ifndef STREAMDISCOVERER_H
#define STREAMDISCOVERER_H

#include <gst/pbutils/pbutils.h>

#include <QMetaType>
#include <QObject>
#include <QString>

struct StreamDetails {
QString url;
QString format;
unsigned int bitrate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unsigned just makes it harder to read.

unsigned int depth;
unsigned int channels;
unsigned int sample_rate;
};
Q_DECLARE_METATYPE(StreamDetails)

class StreamDiscoverer : public QObject {
Q_OBJECT

public:
StreamDiscoverer();
~StreamDiscoverer();

void discover(QString url);

signals:
void DiscoverererFinished();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DiscoverFinished

void DataReady(StreamDetails data);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const StreamDetails&

void Error(const QString& message);

private:
GstDiscoverer* discoverer_;

static const unsigned int kDiscoveryTimeoutS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove unsigned


// GstDiscoverer callbacks:
static void on_discovered_cb(GstDiscoverer* discoverer,
GstDiscovererInfo* info, GError* err,
gpointer instance);
static void on_finished_cb(GstDiscoverer* discoverer, gpointer instance);

// Helper to return descriptive error messages:
static QString gstDiscovererErrorMessage(GstDiscovererResult result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GstDiscovererErrorMessage

};

#endif // STREAMDISCOVERER_H
39 changes: 39 additions & 0 deletions src/ui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
#include "smartplaylists/generatormimedata.h"
#include "songinfo/artistinfoview.h"
#include "songinfo/songinfoview.h"
#include "songinfo/streamdiscoverer.h"
#include "transcoder/transcodedialog.h"
#include "ui/about.h"
#include "ui/addstreamdialog.h"
Expand All @@ -113,6 +114,7 @@
#include "ui/organiseerrordialog.h"
#include "ui/qtsystemtrayicon.h"
#include "ui/settingsdialog.h"
#include "ui/streamdetailsdialog.h"
#include "ui/systemtrayicon.h"
#include "ui/trackselectiondialog.h"
#include "ui/windows7thumbbar.h"
Expand Down Expand Up @@ -173,6 +175,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
tray_icon_(tray_icon),
osd_(osd),
edit_tag_dialog_(std::bind(&MainWindow::CreateEditTagDialog, this)),
stream_discoverer_(std::bind(&MainWindow::CreateStreamDiscoverer, this)),
global_shortcuts_(new GlobalShortcuts(this)),
global_search_view_(new GlobalSearchView(app_, this)),
library_view_(new LibraryViewContainer(this)),
Expand Down Expand Up @@ -477,6 +480,8 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
SLOT(ShowQueueManager()));
connect(ui_->action_add_files_to_transcoder, SIGNAL(triggered()),
SLOT(AddFilesToTranscoder()));
connect(ui_->action_view_stream_details, SIGNAL(triggered()), SLOT(DiscoverStreamDetails()));


background_streams_->AddAction("Rain", ui_->action_rain);
background_streams_->AddAction("Hypnotoad", ui_->action_hypnotoad);
Expand Down Expand Up @@ -684,6 +689,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
playlist_menu_->addAction(ui_->action_remove_from_playlist);
playlist_undoredo_ = playlist_menu_->addSeparator();
playlist_menu_->addAction(ui_->action_edit_track);
playlist_menu_->addAction(ui_->action_view_stream_details);
playlist_menu_->addAction(ui_->action_edit_value);
playlist_menu_->addAction(ui_->action_renumber_tracks);
playlist_menu_->addAction(ui_->action_selection_set_value);
Expand Down Expand Up @@ -1712,6 +1718,11 @@ void MainWindow::PlaylistRightClick(const QPoint& global_pos,
// no 'show in browser' action if only streams are selected
playlist_open_in_browser_->setVisible(streams != all);

// If exactly one stream is selected, enable the 'show details' action.
ui_->action_view_stream_details->setEnabled(all == 1 && streams == 1);
ui_->action_view_stream_details->setVisible(all == 1 && streams == 1);


bool track_column = (index.column() == Playlist::Column_Track);
ui_->action_renumber_tracks->setVisible(editable >= 2 && track_column);
ui_->action_selection_set_value->setVisible(editable >= 2 && !track_column);
Expand Down Expand Up @@ -1882,6 +1893,27 @@ void MainWindow::EditTagDialogAccepted() {
app_->playlist_manager()->current()->Save();
}

void MainWindow::DiscoverStreamDetails() {
int row = playlist_menu_index_.row();
Song song = app_->playlist_manager()->current()->item_at(row)->Metadata();

QString url = song.url().toString();
stream_discoverer_->discover(url);
}

void MainWindow::ShowStreamDetails(StreamDetails details) {
StreamDetailsDialog stream_details_dialog(this);

stream_details_dialog.setUrl(details.url);
stream_details_dialog.setFormat(details.format);
stream_details_dialog.setBitrate(details.bitrate);
stream_details_dialog.setChannels(details.channels);
stream_details_dialog.setDepth(details.depth);
stream_details_dialog.setSampleRate(details.sample_rate);

stream_details_dialog.exec();
}

void MainWindow::RenumberTracks() {
QModelIndexList indexes =
ui_->playlist->view()->selectionModel()->selection().indexes();
Expand Down Expand Up @@ -2490,6 +2522,13 @@ EditTagDialog* MainWindow::CreateEditTagDialog() {
return edit_tag_dialog;
}

StreamDiscoverer* MainWindow::CreateStreamDiscoverer() {
StreamDiscoverer* discoverer = new StreamDiscoverer();
connect(discoverer, SIGNAL(DataReady(StreamDetails)), SLOT(ShowStreamDetails(StreamDetails)));
connect(discoverer, SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString)));
return discoverer;
}

void MainWindow::ShowAboutDialog() { about_dialog_->show(); }

void MainWindow::ShowTranscodeDialog() { transcode_dialog_->show(); }
Expand Down
Loading