Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
428f769c
Commit
428f769c
authored
Mar 03, 2021
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
output/pipewire: new output plugin
Very rough draft. Barely works.
parent
133c8834
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
363 additions
and
0 deletions
+363
-0
NEWS
NEWS
+1
-0
plugins.rst
doc/plugins.rst
+15
-0
meson.build
meson.build
+1
-0
meson_options.txt
meson_options.txt
+1
-0
meson.build
src/lib/pipewire/meson.build
+14
-0
Registry.cxx
src/output/Registry.cxx
+4
-0
PipeWireOutputPlugin.cxx
src/output/plugins/PipeWireOutputPlugin.cxx
+297
-0
PipeWireOutputPlugin.hxx
src/output/plugins/PipeWireOutputPlugin.hxx
+25
-0
meson.build
src/output/plugins/meson.build
+5
-0
No files found.
NEWS
View file @
428f769c
...
...
@@ -3,6 +3,7 @@ ver 0.23 (not yet released)
- new command "getvol"
- show the audio format in "playlistinfo"
* output
- pipewire: new plugin
- snapcast: new plugin
ver 0.22.7 (not yet released)
...
...
doc/plugins.rst
View file @
428f769c
...
...
@@ -1054,6 +1054,21 @@ The pipe plugin starts a program and writes raw PCM data into its standard input
* - **command CMD**
- This command is invoked with the shell.
pipewire
--------
Connect to a `PipeWire <https://pipewire.org/>``_ server. Requires
``libpipewire``.
.. list-table::
:widths: 20 80
:header-rows: 1
* - Setting
- Description
* - **target ID**
- Link to the given target id.
.. _pulse_plugin:
pulse
...
...
meson.build
View file @
428f769c
...
...
@@ -360,6 +360,7 @@ subdir('src/lib/gcrypt')
subdir('src/lib/nfs')
subdir('src/lib/oss')
subdir('src/lib/pcre')
subdir('src/lib/pipewire')
subdir('src/lib/pulse')
subdir('src/lib/sndio')
subdir('src/lib/sqlite')
...
...
meson_options.txt
View file @
428f769c
...
...
@@ -174,6 +174,7 @@ option('jack', type: 'feature', description: 'JACK output plugin')
option('openal', type: 'feature', description: 'OpenAL output plugin')
option('oss', type: 'feature', description: 'Open Sound System support')
option('pipe', type: 'boolean', value: true, description: 'Pipe output plugin')
option('pipewire', type: 'feature', description: 'PipeWire support')
option('pulse', type: 'feature', description: 'PulseAudio support')
option('recorder', type: 'boolean', value: true, description: 'Recorder output plugin')
option('shout', type: 'feature', description: 'Shoutcast streaming support using libshout')
...
...
src/lib/pipewire/meson.build
0 → 100644
View file @
428f769c
pipewire_dep = dependency('libpipewire-0.3', required: get_option('pipewire'))
conf.set('ENABLE_PIPEWIRE', pipewire_dep.found())
if not pipewire_dep.found()
subdir_done()
endif
pipewire_dep = declare_dependency(
dependencies: pipewire_dep,
# disabling -Wpedantic because libpipewire's headers are not
# compatible with C++; using the "#pragma" is not enough, we need to
# disable it at the command line
compile_args: ['-Wno-pedantic'],
)
src/output/Registry.cxx
View file @
428f769c
...
...
@@ -34,6 +34,7 @@
#include "plugins/OssOutputPlugin.hxx"
#include "plugins/OSXOutputPlugin.hxx"
#include "plugins/PipeOutputPlugin.hxx"
#include "plugins/PipeWireOutputPlugin.hxx"
#include "plugins/PulseOutputPlugin.hxx"
#include "plugins/RecorderOutputPlugin.hxx"
#include "plugins/ShoutOutputPlugin.hxx"
...
...
@@ -85,6 +86,9 @@ constexpr const AudioOutputPlugin *audio_output_plugins[] = {
#ifdef ENABLE_SOLARIS_OUTPUT
&
solaris_output_plugin
,
#endif
#ifdef ENABLE_PIPEWIRE
&
pipewire_output_plugin
,
#endif
#ifdef ENABLE_PULSE
&
pulse_output_plugin
,
#endif
...
...
src/output/plugins/PipeWireOutputPlugin.cxx
0 → 100644
View file @
428f769c
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "PipeWireOutputPlugin.hxx"
//#include "lib/pipewire/MainLoop.hxx"
#include "../OutputAPI.hxx"
#include "../Error.hxx"
#include "thread/Thread.hxx"
#ifdef __GNUC__
#pragma GCC diagnostic push
/* oh no, libspa likes to cast away "const"! */
#pragma GCC diagnostic ignored "-Wcast-qual"
/* suppress more annoying warnings */
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#include <pipewire/pipewire.h>
#include <spa/param/audio/format-utils.h>
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#include <boost/lockfree/spsc_queue.hpp>
#include <stdexcept>
class
PipeWireOutput
final
:
AudioOutput
{
Thread
thread
{
BIND_THIS_METHOD
(
RunThread
)};
struct
pw_main_loop
*
loop
;
struct
pw_stream
*
stream
;
std
::
byte
buffer
[
1024
];
struct
spa_pod_builder
pod_builder
;
std
::
size_t
frame_size
;
boost
::
lockfree
::
spsc_queue
<
std
::
byte
>
*
ring_buffer
;
const
uint32_t
target_id
;
volatile
bool
interrupted
;
explicit
PipeWireOutput
(
const
ConfigBlock
&
block
);
public
:
static
AudioOutput
*
Create
(
EventLoop
&
,
const
ConfigBlock
&
block
)
{
pw_init
(
0
,
nullptr
);
return
new
PipeWireOutput
(
block
);
}
static
constexpr
struct
pw_stream_events
MakeStreamEvents
()
noexcept
{
struct
pw_stream_events
events
{};
events
.
version
=
PW_VERSION_STREAM_EVENTS
;
events
.
process
=
Process
;
return
events
;
}
private
:
void
Process
()
noexcept
;
static
void
Process
(
void
*
data
)
noexcept
{
auto
&
o
=
*
(
PipeWireOutput
*
)
data
;
o
.
Process
();
}
void
RunThread
()
noexcept
{
pw_main_loop_run
(
loop
);
}
/* virtual methods from class AudioOutput */
void
Enable
()
override
;
void
Disable
()
noexcept
override
;
void
Open
(
AudioFormat
&
audio_format
)
override
;
void
Close
()
noexcept
override
;
void
Interrupt
()
noexcept
override
{
interrupted
=
true
;
}
size_t
Play
(
const
void
*
chunk
,
size_t
size
)
override
;
// TODO: void Drain() override;
// TODO: void Cancel() noexcept override;
// TODO: bool Pause() noexcept override;
};
static
constexpr
auto
stream_events
=
PipeWireOutput
::
MakeStreamEvents
();
inline
PipeWireOutput
::
PipeWireOutput
(
const
ConfigBlock
&
block
)
:
AudioOutput
(
FLAG_ENABLE_DISABLE
),
target_id
(
block
.
GetBlockValue
(
"target"
,
unsigned
(
PW_ID_ANY
)))
{
}
void
PipeWireOutput
::
Enable
()
{
loop
=
pw_main_loop_new
(
nullptr
);
if
(
loop
==
nullptr
)
throw
std
::
runtime_error
(
"pw_main_loop_new() failed"
);
try
{
thread
.
Start
();
}
catch
(...)
{
pw_main_loop_destroy
(
loop
);
throw
;
}
}
void
PipeWireOutput
::
Disable
()
noexcept
{
pw_main_loop_quit
(
loop
);
thread
.
Join
();
pw_main_loop_destroy
(
loop
);
}
static
constexpr
enum
spa_audio_format
ToPipeWireSampleFormat
(
SampleFormat
format
)
noexcept
{
switch
(
format
)
{
case
SampleFormat
:
:
UNDEFINED
:
break
;
case
SampleFormat
:
:
S8
:
return
SPA_AUDIO_FORMAT_S8
;
case
SampleFormat
:
:
S16
:
return
SPA_AUDIO_FORMAT_S16
;
case
SampleFormat
:
:
S24_P32
:
return
SPA_AUDIO_FORMAT_S24_32
;
case
SampleFormat
:
:
S32
:
return
SPA_AUDIO_FORMAT_S32
;
case
SampleFormat
:
:
FLOAT
:
return
SPA_AUDIO_FORMAT_F32
;
case
SampleFormat
:
:
DSD
:
break
;
}
return
SPA_AUDIO_FORMAT_UNKNOWN
;
}
static
struct
spa_audio_info_raw
ToPipeWireAudioFormat
(
AudioFormat
&
audio_format
)
noexcept
{
struct
spa_audio_info_raw
raw
{};
raw
.
format
=
ToPipeWireSampleFormat
(
audio_format
.
format
);
if
(
raw
.
format
==
SPA_AUDIO_FORMAT_UNKNOWN
)
{
raw
.
format
=
SPA_AUDIO_FORMAT_S16
;
audio_format
.
format
=
SampleFormat
::
S16
;
}
raw
.
flags
=
SPA_AUDIO_FLAG_NONE
;
raw
.
rate
=
audio_format
.
sample_rate
;
raw
.
channels
=
audio_format
.
channels
;
raw
.
flags
|=
SPA_AUDIO_FLAG_UNPOSITIONED
;
// TODO
// TODO raw.position[]
return
raw
;
}
void
PipeWireOutput
::
Open
(
AudioFormat
&
audio_format
)
{
auto
props
=
pw_properties_new
(
PW_KEY_MEDIA_TYPE
,
"Audio"
,
PW_KEY_MEDIA_CATEGORY
,
"Playback"
,
PW_KEY_MEDIA_ROLE
,
"Music"
,
PW_KEY_APP_NAME
,
"Music Player Daemon"
,
PW_KEY_NODE_NAME
,
"mpd"
,
nullptr
);
stream
=
pw_stream_new_simple
(
pw_main_loop_get_loop
(
loop
),
"mpd"
,
props
,
&
stream_events
,
this
);
if
(
stream
==
nullptr
)
throw
std
::
runtime_error
(
"pw_stream_new_simple() failed"
);
auto
raw
=
ToPipeWireAudioFormat
(
audio_format
);
frame_size
=
audio_format
.
GetFrameSize
();
interrupted
=
false
;
/* allocate a ring buffer of 1 second */
ring_buffer
=
new
boost
::
lockfree
::
spsc_queue
<
std
::
byte
>
(
frame_size
*
audio_format
.
sample_rate
);
const
struct
spa_pod
*
params
[
1
];
pod_builder
=
{};
pod_builder
.
data
=
buffer
;
pod_builder
.
size
=
sizeof
(
buffer
);
params
[
0
]
=
spa_format_audio_raw_build
(
&
pod_builder
,
SPA_PARAM_EnumFormat
,
&
raw
);
pw_stream_connect
(
stream
,
PW_DIRECTION_OUTPUT
,
target_id
,
(
enum
pw_stream_flags
)(
PW_STREAM_FLAG_AUTOCONNECT
|
PW_STREAM_FLAG_MAP_BUFFERS
|
PW_STREAM_FLAG_RT_PROCESS
),
params
,
1
);
}
void
PipeWireOutput
::
Close
()
noexcept
{
pw_stream_destroy
(
stream
);
// TODO synchronize with Process()?
delete
ring_buffer
;
}
inline
void
PipeWireOutput
::
Process
()
noexcept
{
auto
*
b
=
pw_stream_dequeue_buffer
(
stream
);
if
(
b
==
nullptr
)
{
pw_log_warn
(
"out of buffers: %m"
);
return
;
}
auto
*
buf
=
b
->
buffer
;
std
::
byte
*
dest
=
(
std
::
byte
*
)
buf
->
datas
[
0
].
data
;
if
(
dest
==
nullptr
)
return
;
const
std
::
size_t
max_frames
=
buf
->
datas
[
0
].
maxsize
/
frame_size
;
const
std
::
size_t
max_size
=
max_frames
*
frame_size
;
size_t
nbytes
=
ring_buffer
->
pop
(
dest
,
max_size
);
if
(
nbytes
==
0
)
{
pw_stream_flush
(
stream
,
true
);
return
;
}
buf
->
datas
[
0
].
chunk
->
offset
=
0
;
buf
->
datas
[
0
].
chunk
->
stride
=
frame_size
;
buf
->
datas
[
0
].
chunk
->
size
=
nbytes
;
pw_stream_queue_buffer
(
stream
,
b
);
}
size_t
PipeWireOutput
::
Play
(
const
void
*
chunk
,
size_t
size
)
{
while
(
true
)
{
std
::
size_t
bytes_written
=
ring_buffer
->
push
((
const
std
::
byte
*
)
chunk
,
size
);
if
(
bytes_written
>
0
)
return
bytes_written
;
if
(
interrupted
)
throw
AudioOutputInterrupted
{};
usleep
(
1000
);
// TODO
}
return
size
;
}
const
struct
AudioOutputPlugin
pipewire_output_plugin
=
{
"pipewire"
,
nullptr
,
&
PipeWireOutput
::
Create
,
nullptr
,
};
src/output/plugins/PipeWireOutputPlugin.hxx
0 → 100644
View file @
428f769c
/*
* Copyright 2003-2021 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX
#define MPD_PIPEWIRE_OUTPUT_PLUGIN_HXX
extern
const
struct
AudioOutputPlugin
pipewire_output_plugin
;
#endif
src/output/plugins/meson.build
View file @
428f769c
...
...
@@ -85,6 +85,10 @@ if enable_pipe_output
output_plugins_sources += 'PipeOutputPlugin.cxx'
endif
if pipewire_dep.found()
output_plugins_sources += 'PipeWireOutputPlugin.cxx'
endif
if pulse_dep.found()
output_plugins_sources += 'PulseOutputPlugin.cxx'
endif
...
...
@@ -169,6 +173,7 @@ output_plugins = static_library(
apple_dep,
libao_dep,
libjack_dep,
pipewire_dep,
pulse_dep,
libshout_dep,
libsndio_dep,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment