2018-02-27 18:06:05 +01:00
/*
* Strawberry Music Player
* This file was part of Clementine .
* Copyright 2010 , David Sansome < me @ davidsansome . com >
2021-03-20 21:14:47 +01:00
* Copyright 2018 - 2021 , Jonas Kvinge < jonas @ jkvinge . net >
2018-02-27 18:06:05 +01:00
*
* Strawberry 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 .
*
* Strawberry 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 Strawberry . If not , see < http : //www.gnu.org/licenses/>.
2018-08-09 18:39:44 +02:00
*
2018-02-27 18:06:05 +01:00
*/
# ifndef GSTENGINEPIPELINE_H
# define GSTENGINEPIPELINE_H
# include "config.h"
# include <memory>
2018-05-01 00:41:33 +02:00
# include <glib.h>
# include <glib-object.h>
# include <glib/gtypes.h>
2018-02-27 18:06:05 +01:00
# include <gst/gst.h>
2018-05-01 00:41:33 +02:00
# include <QtGlobal>
2018-02-27 18:06:05 +01:00
# include <QObject>
2018-05-01 00:41:33 +02:00
# include <QMutex>
2018-02-27 18:06:05 +01:00
# include <QThreadPool>
2018-05-01 00:41:33 +02:00
# include <QFuture>
2018-02-27 18:06:05 +01:00
# include <QTimeLine>
2020-07-18 17:53:14 +02:00
# include <QEasingCurve>
2018-05-01 00:41:33 +02:00
# include <QBasicTimer>
# include <QList>
2020-02-09 02:29:35 +01:00
# include <QByteArray>
2018-04-02 03:43:56 +02:00
# include <QVariant>
2018-05-01 00:41:33 +02:00
# include <QString>
2018-09-22 23:30:19 +02:00
# include <QUrl>
2018-02-27 18:06:05 +01:00
2020-02-09 02:29:35 +01:00
class QTimerEvent ;
2018-05-01 00:41:33 +02:00
class GstBufferConsumer ;
2018-02-27 18:06:05 +01:00
2018-05-01 00:41:33 +02:00
namespace Engine {
struct SimpleMetaBundle ;
} // namespace Engine
2018-04-02 03:43:56 +02:00
struct GstPlayBin ;
2018-02-27 18:06:05 +01:00
class GstEnginePipeline : public QObject {
Q_OBJECT
public :
2021-10-16 21:28:56 +02:00
explicit GstEnginePipeline ( QObject * parent = nullptr ) ;
2020-06-15 21:55:05 +02:00
~ GstEnginePipeline ( ) override ;
2018-02-27 18:06:05 +01:00
// Globally unique across all pipelines.
int id ( ) const { return id_ ; }
// Call these setters before Init
2020-06-14 18:58:24 +02:00
void set_output_device ( const QString & output , const QVariant & device ) ;
2019-11-08 23:07:21 +01:00
void set_volume_enabled ( const bool enabled ) ;
void set_stereo_balancer_enabled ( const bool enabled ) ;
void set_equalizer_enabled ( const bool enabled ) ;
2021-04-22 21:55:26 +02:00
void set_replaygain ( const bool enabled , const int mode , const double preamp , const double fallbackgain , const bool compression ) ;
2021-10-30 02:21:29 +02:00
void set_buffer_duration_nanosec ( const quint64 duration_nanosec ) ;
2020-10-07 20:29:26 +02:00
void set_buffer_low_watermark ( const double value ) ;
void set_buffer_high_watermark ( const double value ) ;
2020-11-04 22:16:20 +01:00
void set_proxy_settings ( const QString & address , const bool authentication , const QString & user , const QString & pass ) ;
2021-05-11 19:14:00 +02:00
void set_channels ( const bool enabled , const int channels ) ;
2022-03-05 01:30:49 +01:00
void set_bs2b_enabled ( const bool enabled ) ;
2022-12-03 03:46:59 +01:00
void set_fading_enabled ( const bool enabled ) ;
2018-02-27 18:06:05 +01:00
// Creates the pipeline, returns false on error
2021-10-16 21:28:56 +02:00
bool InitFromUrl ( const QByteArray & stream_url , const QUrl & original_url , const qint64 end_nanosec , QString & error ) ;
2018-02-27 18:06:05 +01:00
2018-05-01 00:41:33 +02:00
// GstBufferConsumers get fed audio data. Thread-safe.
void AddBufferConsumer ( GstBufferConsumer * consumer ) ;
void RemoveBufferConsumer ( GstBufferConsumer * consumer ) ;
2018-02-27 18:06:05 +01:00
void RemoveAllBufferConsumers ( ) ;
// Control the music playback
2019-09-15 20:27:32 +02:00
QFuture < GstStateChangeReturn > SetState ( const GstState state ) ;
Q_INVOKABLE bool Seek ( const qint64 nanosec ) ;
2022-12-03 03:46:59 +01:00
void SetVolume ( const uint volume_percent ) ;
2019-11-08 23:07:21 +01:00
void SetStereoBalance ( const float value ) ;
void SetEqualizerParams ( const int preamp , const QList < int > & band_gains ) ;
2020-07-18 17:53:14 +02:00
void StartFader ( const qint64 duration_nanosec , const QTimeLine : : Direction direction = QTimeLine : : Forward , const QEasingCurve : : Type shape = QEasingCurve : : Linear , const bool use_fudge_timer = true ) ;
2018-02-27 18:06:05 +01:00
2018-04-02 03:43:56 +02:00
// If this is set then it will be loaded automatically when playback finishes for gapless playback
2019-09-07 23:34:13 +02:00
void SetNextUrl ( const QByteArray & stream_url , const QUrl & original_url , qint64 beginning_nanosec , qint64 end_nanosec ) ;
2019-09-22 22:47:07 +02:00
bool has_next_valid_url ( ) const { return ! next_stream_url_ . isEmpty ( ) ; }
2018-04-02 03:43:56 +02:00
2021-06-20 19:04:08 +02:00
void SetSourceDevice ( const QString & device ) { source_device_ = device ; }
2018-02-27 18:06:05 +01:00
// Get information about the music playback
2019-09-07 23:34:13 +02:00
QByteArray stream_url ( ) const { return stream_url_ ; }
2020-10-21 00:07:58 +02:00
QByteArray next_stream_url ( ) const { return next_stream_url_ ; }
2018-09-22 23:13:56 +02:00
QUrl original_url ( ) const { return original_url_ ; }
2020-10-21 00:07:58 +02:00
QUrl next_original_url ( ) const { return next_original_url_ ; }
2018-02-27 18:06:05 +01:00
bool is_valid ( ) const { return valid_ ; }
2019-11-08 23:07:21 +01:00
2018-04-02 03:43:56 +02:00
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
2018-02-27 18:06:05 +01:00
qint64 position ( ) const ;
2018-04-02 03:43:56 +02:00
// Please note that this method (unlike GstEngine's.length()) is multiple-section media unaware.
2018-02-27 18:06:05 +01:00
qint64 length ( ) const ;
2018-04-02 03:43:56 +02:00
// Returns this pipeline's state. May return GST_STATE_NULL if the state check timed out. The timeout value is a reasonable default.
2018-02-27 18:06:05 +01:00
GstState state ( ) const ;
qint64 segment_start ( ) const { return segment_start_ ; }
2018-04-02 03:43:56 +02:00
// Don't allow the user to change the playback state (playing/paused) while the pipeline is buffering.
2018-02-27 18:06:05 +01:00
bool is_buffering ( ) const { return buffering_ ; }
2018-04-02 03:43:56 +02:00
QByteArray redirect_url ( ) const { return redirect_url_ ; }
2018-02-27 18:06:05 +01:00
QString source_device ( ) const { return source_device_ ; }
public slots :
2022-12-03 04:47:41 +01:00
void SetFaderVolume ( const qreal volume ) ;
2018-02-27 18:06:05 +01:00
2019-11-08 23:07:21 +01:00
signals :
2022-04-14 20:56:57 +02:00
void Error ( int pipeline_id , int domain , int error_code , QString message , QString debug ) ;
2021-10-16 21:28:56 +02:00
2020-10-13 00:49:34 +02:00
void EndOfStreamReached ( int pipeline_id , bool has_next_track ) ;
void MetadataFound ( int pipeline_id , const Engine : : SimpleMetaBundle & bundle ) ;
2021-10-16 21:28:56 +02:00
2022-12-03 03:46:59 +01:00
void VolumeChanged ( uint volume ) ;
2018-02-27 18:06:05 +01:00
void FaderFinished ( ) ;
void BufferingStarted ( ) ;
void BufferingProgress ( int percent ) ;
void BufferingFinished ( ) ;
protected :
2020-06-15 21:55:05 +02:00
void timerEvent ( QTimerEvent * ) override ;
2018-02-27 18:06:05 +01:00
private :
2021-10-30 02:21:29 +02:00
GstElement * CreateElement ( const QString & factory_name , const QString & name , GstElement * bin , QString & error ) const ;
2021-10-16 21:28:56 +02:00
bool InitAudioBin ( QString & error ) ;
2022-12-04 08:37:33 +01:00
void SetupVolume ( GstElement * element ) ;
2019-11-08 23:07:21 +01:00
2018-04-02 03:43:56 +02:00
// Static callbacks. The GstEnginePipeline instance is passed in the last argument.
2023-01-02 00:06:18 +01:00
static GstPadProbeReturn UpstreamEventsProbeCallback ( GstPad * pad , GstPadProbeInfo * info , gpointer self ) ;
static GstPadProbeReturn BufferProbeCallback ( GstPad * pad , GstPadProbeInfo * info , gpointer self ) ;
static GstPadProbeReturn PlaybinProbeCallback ( GstPad * pad , GstPadProbeInfo * info , gpointer self ) ;
2022-12-04 08:37:33 +01:00
static void ElementAddedCallback ( GstBin * bin , GstBin * , GstElement * element , gpointer self ) ;
2023-01-02 00:06:18 +01:00
static void PadAddedCallback ( GstElement * element , GstPad * pad , gpointer self ) ;
2023-03-19 22:28:12 +01:00
static void SourceSetupCallback ( GstElement * playbin , GstElement * source , gpointer self ) ;
2023-01-02 00:06:18 +01:00
static void NotifyVolumeCallback ( GstElement * element , GParamSpec * param_spec , gpointer self ) ;
static void AboutToFinishCallback ( GstPlayBin * playbin , gpointer self ) ;
static GstBusSyncReply BusSyncCallback ( GstBus * bus , GstMessage * msg , gpointer self ) ;
static gboolean BusWatchCallback ( GstBus * bus , GstMessage * msg , gpointer self ) ;
static void TaskEnterCallback ( GstTask * task , GThread * thread , gpointer self ) ;
void TagMessageReceived ( GstMessage * msg ) ;
void ErrorMessageReceived ( GstMessage * msg ) ;
void ElementMessageReceived ( GstMessage * msg ) ;
void StateChangedMessageReceived ( GstMessage * msg ) ;
void BufferingMessageReceived ( GstMessage * msg ) ;
void StreamStatusMessageReceived ( GstMessage * msg ) ;
2018-04-02 03:43:56 +02:00
void StreamStartMessageReceived ( ) ;
2018-02-27 18:06:05 +01:00
2021-06-22 13:41:38 +02:00
static QString ParseStrTag ( GstTagList * list , const char * tag ) ;
static guint ParseUIntTag ( GstTagList * list , const char * tag ) ;
2018-02-27 18:06:05 +01:00
void UpdateStereoBalance ( ) ;
2019-11-08 23:07:21 +01:00
void UpdateEqualizer ( ) ;
2019-09-07 23:34:13 +02:00
2018-02-27 18:06:05 +01:00
private slots :
void FaderTimelineFinished ( ) ;
private :
static const int kGstStateTimeoutNanosecs ;
static const int kFaderFudgeMsec ;
static const int kEqBandCount ;
static const int kEqBandFrequencies [ ] ;
2018-04-02 03:43:56 +02:00
// Using == to compare two pipelines is a bad idea, because new ones often get created in the same address as old ones. This ID will be unique for each pipeline.
// Threading warning: access to the static ID field isn't protected by a mutex because all pipeline creation is currently done in the main thread.
2018-02-27 18:06:05 +01:00
static int sId ;
int id_ ;
// General settings for the pipeline
bool valid_ ;
2018-06-28 01:15:32 +02:00
QString output_ ;
2018-02-27 18:06:05 +01:00
QVariant device_ ;
2019-11-08 23:07:21 +01:00
bool volume_enabled_ ;
bool stereo_balancer_enabled_ ;
bool eq_enabled_ ;
bool rg_enabled_ ;
2022-12-03 03:46:59 +01:00
bool fading_enabled_ ;
2018-02-27 18:06:05 +01:00
2019-11-08 23:07:21 +01:00
// Stereo balance:
2018-02-27 18:06:05 +01:00
// From -1.0 - 1.0
// -1.0 is left, 1.0 is right.
float stereo_balance_ ;
2019-10-27 23:48:54 +01:00
// Equalizer
int eq_preamp_ ;
QList < int > eq_band_gains_ ;
2018-02-27 18:06:05 +01:00
// ReplayGain
int rg_mode_ ;
2021-04-22 21:55:26 +02:00
double rg_preamp_ ;
double rg_fallbackgain_ ;
2018-02-27 18:06:05 +01:00
bool rg_compression_ ;
// Buffering
quint64 buffer_duration_nanosec_ ;
2020-10-07 20:29:26 +02:00
double buffer_low_watermark_ ;
double buffer_high_watermark_ ;
2018-02-27 18:06:05 +01:00
bool buffering_ ;
2020-11-04 22:16:20 +01:00
// Proxy
QString proxy_address_ ;
bool proxy_authentication_ ;
QString proxy_user_ ;
QString proxy_pass_ ;
2021-05-11 19:14:00 +02:00
// Channels
bool channels_enabled_ ;
int channels_ ;
2022-03-05 01:30:49 +01:00
// Options
bool bs2b_enabled_ ;
2018-06-28 01:15:32 +02:00
// These get called when there is a new audio buffer available
QList < GstBufferConsumer * > buffer_consumers_ ;
QMutex buffer_consumers_mutex_ ;
qint64 segment_start_ ;
bool segment_start_received_ ;
2018-02-27 18:06:05 +01:00
2018-03-10 13:02:56 +01:00
// The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing.
2019-09-07 23:34:13 +02:00
QByteArray stream_url_ ;
2018-09-22 23:13:56 +02:00
QUrl original_url_ ;
2019-09-07 23:34:13 +02:00
QByteArray next_stream_url_ ;
2018-09-22 23:13:56 +02:00
QUrl next_original_url_ ;
2018-02-27 18:06:05 +01:00
2018-03-10 13:02:56 +01:00
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
2018-02-27 18:06:05 +01:00
qint64 end_offset_nanosec_ ;
2018-03-10 13:02:56 +01:00
// We store the beginning and end for the preloading song too, so we can just carry on without reloading the file if the sections carry on from each other.
2018-02-27 18:06:05 +01:00
qint64 next_beginning_offset_nanosec_ ;
qint64 next_end_offset_nanosec_ ;
2022-08-28 02:44:37 +02:00
// Set temporarily when moving to the next contiguous section in a multipart file.
2018-02-27 18:06:05 +01:00
bool ignore_next_seek_ ;
2018-03-10 13:02:56 +01:00
// Set temporarily when switching out the decode bin, so metadata doesn't get sent while the Player still thinks it's playing the last song
2018-02-27 18:06:05 +01:00
bool ignore_tags_ ;
2018-03-10 13:02:56 +01:00
// When the gstreamer source requests a redirect we store the URL here and callers can pick it up after the state change to PLAYING fails.
2018-04-02 03:43:56 +02:00
QByteArray redirect_url_ ;
2018-02-27 18:06:05 +01:00
// When we need to specify the device to use as source (for CD device)
QString source_device_ ;
2019-10-20 02:56:47 +02:00
// Seeking while the pipeline is in the READY state doesn't work, so we have to wait until it goes to PAUSED or PLAYING.
2022-08-28 02:44:37 +02:00
// Also, we have to wait for the playbin to be connected.
2020-10-17 17:29:09 +02:00
bool pipeline_is_initialized_ ;
2018-02-27 18:06:05 +01:00
bool pipeline_is_connected_ ;
qint64 pending_seek_nanosec_ ;
// We can only use gst_element_query_position() when the pipeline is in
2018-04-02 03:43:56 +02:00
// PAUSED nor PLAYING state. Whenever we get a new position (e.g. after a correct call to gst_element_query_position() or after a seek), we store
// it here so that we can use it when using gst_element_query_position() is not possible.
2018-02-27 18:06:05 +01:00
mutable gint64 last_known_position_ns_ ;
2018-04-02 03:43:56 +02:00
// Complete the transition to the next song when it starts playing
bool next_uri_set_ ;
2023-01-03 21:32:20 +01:00
gdouble volume_internal_ ;
2021-10-30 02:21:29 +02:00
uint volume_percent_ ;
2018-02-27 18:06:05 +01:00
2022-02-05 19:33:21 +01:00
std : : shared_ptr < QTimeLine > fader_ ;
2018-02-27 18:06:05 +01:00
QBasicTimer fader_fudge_timer_ ;
bool use_fudge_timer_ ;
GstElement * pipeline_ ;
GstElement * audiobin_ ;
2022-12-04 08:37:33 +01:00
GstElement * audiosink_ ;
2019-10-27 23:48:54 +01:00
GstElement * audioqueue_ ;
2023-01-02 00:06:18 +01:00
GstElement * audioqueueconverter_ ;
2019-03-27 00:27:49 +01:00
GstElement * volume_ ;
2022-12-04 08:37:33 +01:00
GstElement * volume_sw_ ;
2022-12-03 03:46:59 +01:00
GstElement * volume_fading_ ;
2019-10-27 23:48:54 +01:00
GstElement * audiopanorama_ ;
2019-03-27 00:27:49 +01:00
GstElement * equalizer_ ;
2019-10-27 23:48:54 +01:00
GstElement * equalizer_preamp_ ;
2023-01-02 00:06:18 +01:00
GstElement * eventprobe_ ;
gulong upstream_events_probe_cb_id_ ;
gulong buffer_probe_cb_id_ ;
gulong playbin_probe_cb_id_ ;
2023-01-03 19:51:23 +01:00
glong element_added_cb_id_ ;
glong pad_added_cb_id_ ;
glong notify_source_cb_id_ ;
glong about_to_finish_cb_id_ ;
glong notify_volume_cb_id_ ;
2018-02-27 18:06:05 +01:00
QThreadPool set_state_threadpool_ ;
2021-06-20 23:53:28 +02:00
GstSegment last_playbin_segment_ { } ;
2019-09-17 22:42:51 +02:00
2021-10-12 18:36:13 +02:00
bool logged_unsupported_analyzer_format_ ;
2020-07-04 00:29:11 +02:00
2018-02-27 18:06:05 +01:00
} ;
# endif // GSTENGINEPIPELINE_H