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
89b90043
Commit
89b90043
authored
Apr 28, 2017
by
Max Kellermann
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
output/Internal: move thread-specific stuff to AudioOutputControl
The AudioOutput struct (which is exposed to all plugins) should not be aware that it's being controlled by another thread.
parent
8bb9d096
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
336 additions
and
443 deletions
+336
-443
Control.cxx
src/output/Control.cxx
+58
-117
Control.hxx
src/output/Control.hxx
+218
-5
Finish.cxx
src/output/Finish.cxx
+0
-4
Init.cxx
src/output/Init.cxx
+1
-2
Internal.hxx
src/output/Internal.hxx
+8
-265
Thread.cxx
src/output/Thread.cxx
+48
-48
AlsaOutputPlugin.cxx
src/output/plugins/AlsaOutputPlugin.cxx
+1
-0
HttpdInternal.hxx
src/output/plugins/httpd/HttpdInternal.hxx
+1
-0
SlesOutputPlugin.cxx
src/output/plugins/sles/SlesOutputPlugin.cxx
+1
-0
run_output.cxx
test/run_output.cxx
+0
-2
No files found.
src/output/Control.cxx
View file @
89b90043
...
...
@@ -38,7 +38,9 @@ static constexpr PeriodClock::Duration REOPEN_AFTER = std::chrono::seconds(10);
struct
notify
audio_output_client_notify
;
AudioOutputControl
::
AudioOutputControl
(
AudioOutput
*
_output
)
:
output
(
_output
),
mutex
(
output
->
mutex
)
:
output
(
_output
),
thread
(
BIND_THIS_METHOD
(
Task
)),
mutex
(
output
->
mutex
)
{
}
...
...
@@ -91,20 +93,8 @@ AudioOutputControl::IsOpen() const
return
output
->
IsOpen
();
}
bool
AudioOutputControl
::
IsBusy
()
const
{
return
output
->
IsBusy
();
}
const
std
::
exception_ptr
&
AudioOutputControl
::
GetLastError
()
const
{
return
output
->
GetLastError
();
}
void
AudioOutput
::
WaitForCommand
()
AudioOutput
Control
::
WaitForCommand
()
{
while
(
!
IsCommandFinished
())
{
mutex
.
unlock
();
...
...
@@ -114,13 +104,7 @@ AudioOutput::WaitForCommand()
}
void
AudioOutputControl
::
WaitForCommand
()
{
output
->
WaitForCommand
();
}
void
AudioOutput
::
CommandAsync
(
Command
cmd
)
AudioOutputControl
::
CommandAsync
(
Command
cmd
)
{
assert
(
IsCommandFinished
());
...
...
@@ -129,28 +113,28 @@ AudioOutput::CommandAsync(Command cmd)
}
void
AudioOutput
::
CommandWait
(
Command
cmd
)
AudioOutput
Control
::
CommandWait
(
Command
cmd
)
{
CommandAsync
(
cmd
);
WaitForCommand
();
}
void
AudioOutput
::
LockCommandWait
(
Command
cmd
)
AudioOutput
Control
::
LockCommandWait
(
Command
cmd
)
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
CommandWait
(
cmd
);
}
void
AudioOutput
::
EnableAsync
()
AudioOutput
Control
::
EnableAsync
()
{
if
(
!
thread
.
IsDefined
())
{
if
(
plugin
.
enable
==
nullptr
)
{
if
(
output
->
plugin
.
enable
==
nullptr
)
{
/* don't bother to start the thread now if the
device doesn't even have a enable() method;
just assign the variable and we're done */
really_enabled
=
true
;
output
->
really_enabled
=
true
;
return
;
}
...
...
@@ -161,15 +145,15 @@ AudioOutput::EnableAsync()
}
void
AudioOutput
::
DisableAsync
()
AudioOutput
Control
::
DisableAsync
()
{
if
(
!
thread
.
IsDefined
())
{
if
(
plugin
.
disable
==
nullptr
)
really_enabled
=
false
;
if
(
output
->
plugin
.
disable
==
nullptr
)
output
->
really_enabled
=
false
;
else
/* if there's no thread yet, the device cannot
be enabled */
assert
(
!
really_enabled
);
assert
(
!
output
->
really_enabled
);
return
;
}
...
...
@@ -180,27 +164,27 @@ AudioOutput::DisableAsync()
void
AudioOutputControl
::
EnableDisableAsync
()
{
output
->
EnableDisableAsync
();
}
if
(
output
->
enabled
==
output
->
really_enabled
)
return
;
void
AudioOutputControl
::
LockPauseAsync
()
{
output
->
LockPaus
eAsync
();
if
(
output
->
enabled
)
EnableAsync
();
else
Disabl
eAsync
();
}
inline
bool
AudioOutput
::
Open
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
)
AudioOutput
Control
::
Open
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
)
{
assert
(
allow_play
);
assert
(
audio_format
.
IsValid
());
fail_timer
.
Reset
();
if
(
open
&&
audio_format
==
request
.
audio_format
)
{
assert
(
request
.
pipe
==
&
mp
||
(
always_on
&&
pause
));
if
(
o
utput
->
o
pen
&&
audio_format
==
request
.
audio_format
)
{
assert
(
request
.
pipe
==
&
mp
||
(
output
->
always_on
&&
output
->
pause
));
if
(
!
pause
)
if
(
!
output
->
pause
)
/* already open, already the right parameters
- nothing needs to be done */
return
true
;
...
...
@@ -213,13 +197,14 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
StartThread
();
CommandWait
(
Command
::
OPEN
);
const
bool
open2
=
open
;
const
bool
open2
=
o
utput
->
o
pen
;
if
(
open2
&&
mixer
!=
nullptr
)
{
if
(
open2
&&
output
->
mixer
!=
nullptr
)
{
try
{
mixer_open
(
mixer
);
mixer_open
(
output
->
mixer
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to open mixer for '%s'"
,
name
);
FormatError
(
e
,
"Failed to open mixer for '%s'"
,
GetName
());
}
}
...
...
@@ -227,29 +212,29 @@ AudioOutput::Open(const AudioFormat audio_format, const MusicPipe &mp)
}
void
AudioOutput
::
CloseWait
()
AudioOutput
Control
::
CloseWait
()
{
assert
(
allow_play
);
if
(
mixer
!=
nullptr
)
mixer_auto_close
(
mixer
);
if
(
output
->
mixer
!=
nullptr
)
mixer_auto_close
(
output
->
mixer
);
assert
(
!
open
||
!
fail_timer
.
IsDefined
());
assert
(
!
o
utput
->
o
pen
||
!
fail_timer
.
IsDefined
());
if
(
open
)
if
(
o
utput
->
o
pen
)
CommandWait
(
Command
::
CLOSE
);
else
fail_timer
.
Reset
();
}
bool
AudioOutput
::
LockUpdate
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
,
bool
force
)
AudioOutput
Control
::
LockUpdate
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
,
bool
force
)
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
if
(
enabled
&&
really_enabled
)
{
if
(
output
->
enabled
&&
output
->
really_enabled
)
{
if
(
force
||
!
fail_timer
.
IsDefined
()
||
fail_timer
.
Check
(
REOPEN_AFTER
*
1000
))
{
return
Open
(
audio_format
,
mp
);
...
...
@@ -260,26 +245,6 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
return
false
;
}
void
AudioOutputControl
::
LockRelease
()
{
output
->
LockRelease
();
}
void
AudioOutputControl
::
LockCloseWait
()
{
output
->
LockCloseWait
();
}
bool
AudioOutputControl
::
LockUpdate
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
,
bool
force
)
{
return
output
->
LockUpdate
(
audio_format
,
mp
,
force
);
}
bool
AudioOutputControl
::
LockIsChunkConsumed
(
const
MusicChunk
&
chunk
)
const
{
...
...
@@ -293,7 +258,7 @@ AudioOutputControl::ClearTailChunk(const MusicChunk &chunk)
}
void
AudioOutput
::
LockPlay
()
AudioOutput
Control
::
LockPlay
()
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
...
...
@@ -306,13 +271,13 @@ AudioOutput::LockPlay()
}
void
AudioOutput
::
LockPauseAsync
()
AudioOutput
Control
::
LockPauseAsync
()
{
if
(
mixer
!=
nullptr
&&
plugin
.
pause
==
nullptr
)
if
(
output
->
mixer
!=
nullptr
&&
output
->
plugin
.
pause
==
nullptr
)
/* the device has no pause mode: close the mixer,
unless its "global" flag is set (checked by
mixer_auto_close()) */
mixer_auto_close
(
mixer
);
mixer_auto_close
(
output
->
mixer
);
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
...
...
@@ -322,7 +287,7 @@ AudioOutput::LockPauseAsync()
}
void
AudioOutput
::
LockDrainAsync
()
AudioOutput
Control
::
LockDrainAsync
()
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
...
...
@@ -332,7 +297,7 @@ AudioOutput::LockDrainAsync()
}
void
AudioOutput
::
LockCancelAsync
()
AudioOutput
Control
::
LockCancelAsync
()
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
...
...
@@ -343,7 +308,7 @@ AudioOutput::LockCancelAsync()
}
void
AudioOutput
::
LockAllowPlay
()
AudioOutput
Control
::
LockAllowPlay
()
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
...
...
@@ -353,18 +318,18 @@ AudioOutput::LockAllowPlay()
}
void
AudioOutput
::
LockRelease
()
AudioOutput
Control
::
LockRelease
()
{
if
(
always_on
)
if
(
output
->
always_on
)
LockPauseAsync
();
else
LockCloseWait
();
}
void
AudioOutput
::
LockCloseWait
()
AudioOutput
Control
::
LockCloseWait
()
{
assert
(
!
open
||
!
fail_timer
.
IsDefined
());
assert
(
!
o
utput
->
o
pen
||
!
fail_timer
.
IsDefined
());
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
CloseWait
();
...
...
@@ -377,7 +342,7 @@ AudioOutputControl::SetReplayGainMode(ReplayGainMode _mode)
}
void
AudioOutput
::
StopThread
()
AudioOutput
Control
::
StopThread
()
{
assert
(
thread
.
IsDefined
());
assert
(
allow_play
);
...
...
@@ -391,55 +356,31 @@ AudioOutput::BeginDestroy()
{
if
(
mixer
!=
nullptr
)
mixer_auto_close
(
mixer
);
if
(
thread
.
IsDefined
())
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
CommandAsync
(
Command
::
KILL
);
}
}
void
AudioOutputControl
::
BeginDestroy
()
{
output
->
BeginDestroy
();
if
(
thread
.
IsDefined
())
{
const
std
::
lock_guard
<
Mutex
>
protect
(
mutex
);
CommandAsync
(
Command
::
KILL
);
}
}
void
AudioOutput
::
FinishDestroy
()
{
if
(
thread
.
IsDefined
())
thread
.
Join
();
audio_output_free
(
this
);
}
void
AudioOutputControl
::
FinishDestroy
()
{
if
(
thread
.
IsDefined
())
thread
.
Join
();
output
->
FinishDestroy
();
output
=
nullptr
;
}
void
AudioOutputControl
::
LockPlay
()
{
output
->
LockPlay
();
}
void
AudioOutputControl
::
LockDrainAsync
()
{
output
->
LockDrainAsync
();
}
void
AudioOutputControl
::
LockCancelAsync
()
{
output
->
LockCancelAsync
();
}
void
AudioOutputControl
::
LockAllowPlay
()
{
output
->
LockAllowPlay
();
}
src/output/Control.hxx
View file @
89b90043
...
...
@@ -20,6 +20,10 @@
#ifndef MPD_OUTPUT_CONTROL_HXX
#define MPD_OUTPUT_CONTROL_HXX
#include "AudioFormat.hxx"
#include "thread/Thread.hxx"
#include "thread/Cond.hxx"
#include "system/PeriodClock.hxx"
#include "Compiler.h"
#include <utility>
...
...
@@ -32,7 +36,6 @@
#include <stdint.h>
enum
class
ReplayGainMode
:
uint8_t
;
struct
AudioFormat
;
struct
AudioOutput
;
struct
MusicChunk
;
class
MusicPipe
;
...
...
@@ -46,6 +49,99 @@ class AudioOutputClient;
class
AudioOutputControl
{
AudioOutput
*
output
;
/**
* The error that occurred in the output thread. It is
* cleared whenever the output is opened successfully.
*
* Protected by #mutex.
*/
std
::
exception_ptr
last_error
;
/**
* If not nullptr, the device has failed, and this timer is used
* to estimate how long it should stay disabled (unless
* explicitly reopened with "play").
*/
PeriodClock
fail_timer
;
/**
* The thread handle, or nullptr if the output thread isn't
* running.
*/
Thread
thread
;
/**
* This condition object wakes up the output thread after
* #command has been set.
*/
Cond
cond
;
/**
* Additional data for #command. Protected by #mutex.
*/
struct
Request
{
/**
* The #AudioFormat requested by #Command::OPEN.
*/
AudioFormat
audio_format
;
/**
* The #MusicPipe passed to #Command::OPEN.
*/
const
MusicPipe
*
pipe
;
}
request
;
/**
* The next command to be performed by the output thread.
*/
enum
class
Command
{
NONE
,
ENABLE
,
DISABLE
,
/**
* Open the output, or reopen it if it is already
* open, adjusting for input #AudioFormat changes.
*/
OPEN
,
CLOSE
,
PAUSE
,
/**
* Drains the internal (hardware) buffers of the device. This
* operation may take a while to complete.
*/
DRAIN
,
CANCEL
,
KILL
}
command
=
Command
::
NONE
;
/**
* When this flag is set, the output thread will not do any
* playback. It will wait until the flag is cleared.
*
* This is used to synchronize the "clear" operation on the
* shared music pipe during the CANCEL command.
*/
bool
allow_play
=
true
;
/**
* True while the OutputThread is inside ao_play(). This
* means the PlayerThread does not need to wake up the
* OutputThread when new chunks are added to the MusicPipe,
* because the OutputThread is already watching that.
*/
bool
in_playback_loop
=
false
;
/**
* Has the OutputThread been woken up to play more chunks?
* This is set by audio_output_play() and reset by ao_play()
* to reduce the number of duplicate wakeups.
*/
bool
woken_for_play
=
false
;
public
:
Mutex
&
mutex
;
...
...
@@ -53,6 +149,8 @@ public:
#ifndef NDEBUG
~
AudioOutputControl
()
{
assert
(
!
fail_timer
.
IsDefined
());
assert
(
!
thread
.
IsDefined
());
assert
(
output
==
nullptr
);
}
#endif
...
...
@@ -84,29 +182,104 @@ public:
gcc_pure
bool
IsOpen
()
const
;
gcc_pure
bool
IsBusy
()
const
;
/**
* Caller must lock the mutex.
*/
bool
IsBusy
()
const
{
return
IsOpen
()
&&
!
IsCommandFinished
();
}
/**
* Caller must lock the mutex.
*/
const
std
::
exception_ptr
&
GetLastError
()
const
{
return
last_error
;
}
void
StartThread
();
void
StopThread
();
/**
* Caller must lock the mutex.
*/
gcc_const
const
std
::
exception_ptr
&
GetLastError
()
const
;
bool
IsCommandFinished
()
const
{
return
command
==
Command
::
NONE
;
}
void
CommandFinished
();
/**
* Waits for command completion.
*
* Caller must lock the mutex.
*/
void
WaitForCommand
();
/**
* Sends a command, but does not wait for completion.
*
* Caller must lock the mutex.
*/
void
CommandAsync
(
Command
cmd
);
/**
* Sends a command to the #AudioOutput object and waits for
* completion.
*
* Caller must lock the mutex.
*/
void
CommandWait
(
Command
cmd
);
/**
* Lock the #AudioOutput object and execute the command
* synchronously.
*/
void
LockCommandWait
(
Command
cmd
);
void
BeginDestroy
();
void
FinishDestroy
();
/**
* Enables the device, but don't wait for completion.
*
* Caller must lock the mutex.
*/
void
EnableAsync
();
/**
* Disables the device, but don't wait for completion.
*
* Caller must lock the mutex.
*/
void
DisableAsync
();
/**
* Attempt to enable or disable the device as specified by the
* #enabled attribute; attempt to sync it with #really_enabled
* (wrapper for EnableAsync() or DisableAsync()).
*
* Caller must lock the mutex.
*/
void
EnableDisableAsync
();
void
LockPauseAsync
();
void
CloseWait
();
void
LockCloseWait
();
/**
* Closes the audio output, but if the "always_on" flag is set, put it
* into pause mode instead.
*/
void
LockRelease
();
void
SetReplayGainMode
(
ReplayGainMode
_mode
);
/**
* Caller must lock the mutex.
*/
bool
Open
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
);
/**
* Opens or closes the device, depending on the "enabled"
* flag.
*
...
...
@@ -124,8 +297,48 @@ public:
void
LockPlay
();
void
LockDrainAsync
();
/**
* Clear the "allow_play" flag and send the "CANCEL" command
* asynchronously. To finish the operation, the caller has to
* call LockAllowPlay().
*/
void
LockCancelAsync
();
/**
* Set the "allow_play" and signal the thread.
*/
void
LockAllowPlay
();
private
:
/**
* Wait until the output's delay reaches zero.
*
* @return true if playback should be continued, false if a
* command was issued
*/
bool
WaitForDelay
();
bool
FillSourceOrClose
();
bool
PlayChunk
();
/**
* Plays all remaining chunks, until the tail of the pipe has
* been reached (and no more chunks are queued), or until a
* command is received.
*
* @return true if at least one chunk has been available,
* false if the tail of the pipe was already reached
*/
bool
Play
();
void
Pause
();
/**
* The OutputThread.
*/
void
Task
();
};
#endif
src/output/Finish.cxx
View file @
89b90043
...
...
@@ -28,8 +28,6 @@
AudioOutput
::~
AudioOutput
()
{
assert
(
!
open
);
assert
(
!
fail_timer
.
IsDefined
());
assert
(
!
thread
.
IsDefined
());
if
(
mixer
!=
nullptr
)
mixer_free
(
mixer
);
...
...
@@ -43,8 +41,6 @@ void
audio_output_free
(
AudioOutput
*
ao
)
{
assert
(
!
ao
->
IsOpen
());
assert
(
!
ao
->
fail_timer
.
IsDefined
());
assert
(
!
ao
->
thread
.
IsDefined
());
ao_plugin_finish
(
ao
);
}
src/output/Init.cxx
View file @
89b90043
...
...
@@ -51,8 +51,7 @@
AudioOutput
::
AudioOutput
(
const
AudioOutputPlugin
&
_plugin
,
const
ConfigBlock
&
block
)
:
plugin
(
_plugin
),
thread
(
BIND_THIS_METHOD
(
Task
))
:
plugin
(
_plugin
)
{
assert
(
plugin
.
finish
!=
nullptr
);
assert
(
plugin
.
open
!=
nullptr
);
...
...
src/output/Internal.hxx
View file @
89b90043
...
...
@@ -21,15 +21,9 @@
#define MPD_OUTPUT_INTERNAL_HXX
#include "Source.hxx"
#include "SharedPipeConsumer.hxx"
#include "AudioFormat.hxx"
#include "filter/Observer.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "thread/Thread.hxx"
#include "system/PeriodClock.hxx"
#include <exception>
class
PreparedFilter
;
class
MusicPipe
;
...
...
@@ -43,30 +37,6 @@ struct AudioOutputPlugin;
struct
ReplayGainConfig
;
struct
AudioOutput
{
enum
class
Command
{
NONE
,
ENABLE
,
DISABLE
,
/**
* Open the output, or reopen it if it is already
* open, adjusting for input #AudioFormat changes.
*/
OPEN
,
CLOSE
,
PAUSE
,
/**
* Drains the internal (hardware) buffers of the device. This
* operation may take a while to complete.
*/
DRAIN
,
CANCEL
,
KILL
};
/**
* The device's configured display name.
*/
...
...
@@ -125,37 +95,6 @@ struct AudioOutput {
bool
pause
=
false
;
/**
* When this flag is set, the output thread will not do any
* playback. It will wait until the flag is cleared.
*
* This is used to synchronize the "clear" operation on the
* shared music pipe during the CANCEL command.
*/
bool
allow_play
=
true
;
/**
* True while the OutputThread is inside ao_play(). This
* means the PlayerThread does not need to wake up the
* OutputThread when new chunks are added to the MusicPipe,
* because the OutputThread is already watching that.
*/
bool
in_playback_loop
=
false
;
/**
* Has the OutputThread been woken up to play more chunks?
* This is set by audio_output_play() and reset by ao_play()
* to reduce the number of duplicate wakeups.
*/
bool
woken_for_play
=
false
;
/**
* If not nullptr, the device has failed, and this timer is used
* to estimate how long it should stay disabled (unless
* explicitly reopened with "play").
*/
PeriodClock
fail_timer
;
/**
* The configured audio format.
*/
AudioFormat
config_audio_format
;
...
...
@@ -210,43 +149,11 @@ struct AudioOutput {
FilterObserver
convert_filter
;
/**
* The thread handle, or nullptr if the output thread isn't
* running.
*/
Thread
thread
;
/**
* The next command to be performed by the output thread.
*/
Command
command
=
Command
::
NONE
;
/**
* Additional data for #command. Protected by #mutex.
*/
struct
Request
{
/**
* The #AudioFormat requested by #Command::OPEN.
*/
AudioFormat
audio_format
;
/**
* The #MusicPipe passed to #Command::OPEN.
*/
const
MusicPipe
*
pipe
;
}
request
;
/**
* This mutex protects #open, #fail_timer, #pipe.
*/
mutable
Mutex
mutex
;
/**
* This condition object wakes up the output thread after
* #command has been set.
*/
Cond
cond
;
/**
* The PlayerControl object which "owns" this output. This
* object is needed to signal command completion.
*/
...
...
@@ -258,14 +165,6 @@ struct AudioOutput {
AudioOutputSource
source
;
/**
* The error that occurred in the output thread. It is
* cleared whenever the output is opened successfully.
*
* Protected by #mutex.
*/
std
::
exception_ptr
last_error
;
/**
* Throws #std::runtime_error on error.
*/
AudioOutput
(
const
AudioOutputPlugin
&
_plugin
,
...
...
@@ -282,9 +181,6 @@ public:
MixerListener
&
mixer_listener
,
const
ConfigBlock
&
block
);
void
StartThread
();
void
StopThread
();
void
BeginDestroy
();
void
FinishDestroy
();
...
...
@@ -306,138 +202,11 @@ public:
return
open
;
}
/**
* Caller must lock the mutex.
*/
bool
IsCommandFinished
()
const
{
return
command
==
Command
::
NONE
;
}
/**
* Caller must lock the mutex.
*/
bool
IsBusy
()
const
{
return
IsOpen
()
&&
!
IsCommandFinished
();
}
/**
* Caller must lock the mutex.
*/
const
std
::
exception_ptr
&
GetLastError
()
const
{
return
last_error
;
}
/**
* Waits for command completion.
*
* Caller must lock the mutex.
*/
void
WaitForCommand
();
/**
* Sends a command, but does not wait for completion.
*
* Caller must lock the mutex.
*/
void
CommandAsync
(
Command
cmd
);
/**
* Sends a command to the #AudioOutput object and waits for
* completion.
*
* Caller must lock the mutex.
*/
void
CommandWait
(
Command
cmd
);
/**
* Lock the #AudioOutput object and execute the command
* synchronously.
*/
void
LockCommandWait
(
Command
cmd
);
/**
* Enables the device, but don't wait for completion.
*
* Caller must lock the mutex.
*/
void
EnableAsync
();
/**
* Disables the device, but don't wait for completion.
*
* Caller must lock the mutex.
*/
void
DisableAsync
();
/**
* Attempt to enable or disable the device as specified by the
* #enabled attribute; attempt to sync it with #really_enabled
* (wrapper for EnableAsync() or DisableAsync()).
*
* Caller must lock the mutex.
*/
void
EnableDisableAsync
()
{
if
(
enabled
==
really_enabled
)
return
;
if
(
enabled
)
EnableAsync
();
else
DisableAsync
();
}
void
LockPauseAsync
();
/**
* Same LockCloseWait(), but expects the lock to be
* held by the caller.
*/
void
CloseWait
();
void
LockCloseWait
();
/**
* Closes the audio output, but if the "always_on" flag is set, put it
* into pause mode instead.
*/
void
LockRelease
();
void
SetReplayGainMode
(
ReplayGainMode
_mode
)
{
source
.
SetReplayGainMode
(
_mode
);
}
/**
* Caller must lock the mutex.
*/
bool
Open
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
);
/**
* Opens or closes the device, depending on the "enabled"
* flag.
*
* @param force true to ignore the #fail_timer
* @return true if the device is open
*/
bool
LockUpdate
(
const
AudioFormat
audio_format
,
const
MusicPipe
&
mp
,
bool
force
);
void
LockPlay
();
void
LockDrainAsync
();
/**
* Clear the "allow_play" flag and send the "CANCEL" command
* asynchronously. To finish the operation, the caller has to
* call LockAllowPlay().
*/
void
LockCancelAsync
();
/**
* Set the "allow_play" and signal the thread.
*/
void
LockAllowPlay
();
/**
* Did we already consumed this chunk?
*
* Caller must lock the mutex.
...
...
@@ -455,9 +224,6 @@ public:
source
.
ClearTailChunk
(
chunk
);
}
private
:
void
CommandFinished
();
/**
* Throws #std::runtime_error on error.
*/
...
...
@@ -468,8 +234,11 @@ private:
/**
* Throws #std::runtime_error on error.
*/
void
Open
();
void
Open
(
AudioFormat
audio_format
,
const
MusicPipe
&
pipe
);
void
Close
(
bool
drain
);
private
:
/**
* Invoke OutputPlugin::open() and configure the
* #ConvertFilter.
...
...
@@ -480,8 +249,6 @@ private:
*/
void
OpenOutputAndConvert
(
AudioFormat
audio_format
);
void
Close
(
bool
drain
);
/**
* Close the output plugin.
*
...
...
@@ -494,37 +261,13 @@ private:
*/
void
CloseFilter
();
/**
* Wait until the output's delay reaches zero.
*
* @return true if playback should be continued, false if a
* command was issued
*/
bool
WaitForDelay
();
bool
FillSourceOrClose
();
bool
PlayChunk
();
/**
* Plays all remaining chunks, until the tail of the pipe has
* been reached (and no more chunks are queued), or until a
* command is received.
*
* @return true if at least one chunk has been available,
* false if the tail of the pipe was already reached
*/
bool
Play
();
public
:
void
BeginPause
();
bool
IteratePause
();
void
Pause
();
/**
* The OutputThread.
*/
void
Task
();
void
EndPause
()
{
pause
=
false
;
}
};
/**
...
...
src/output/Thread.cxx
View file @
89b90043
...
...
@@ -48,7 +48,7 @@
#include <string.h>
void
AudioOutput
::
CommandFinished
()
AudioOutput
Control
::
CommandFinished
()
{
assert
(
command
!=
Command
::
NONE
);
command
=
Command
::
NONE
;
...
...
@@ -98,11 +98,9 @@ AudioOutput::CloseFilter()
}
inline
void
AudioOutput
::
Open
()
AudioOutput
::
Open
(
AudioFormat
audio_format
,
const
MusicPipe
&
pipe
)
{
assert
(
request
.
audio_format
.
IsValid
());
fail_timer
.
Reset
();
assert
(
audio_format
.
IsValid
());
/* enable the device (just in case the last enable has failed) */
Enable
();
...
...
@@ -110,7 +108,7 @@ AudioOutput::Open()
AudioFormat
f
;
try
{
f
=
source
.
Open
(
request
.
audio_format
,
*
request
.
pipe
,
f
=
source
.
Open
(
audio_format
,
pipe
,
prepared_replay_gain_filter
,
prepared_other_replay_gain_filter
,
prepared_filter
);
...
...
@@ -239,10 +237,10 @@ AudioOutput::CloseOutput(bool drain)
* was issued
*/
inline
bool
AudioOutput
::
WaitForDelay
()
AudioOutput
Control
::
WaitForDelay
()
{
while
(
true
)
{
const
auto
delay
=
ao_plugin_delay
(
*
this
);
const
auto
delay
=
ao_plugin_delay
(
*
output
);
if
(
delay
<=
std
::
chrono
::
steady_clock
::
duration
::
zero
())
return
true
;
...
...
@@ -254,14 +252,14 @@ AudioOutput::WaitForDelay()
}
bool
AudioOutput
::
FillSourceOrClose
()
AudioOutput
Control
::
FillSourceOrClose
()
try
{
return
source
.
Fill
(
mutex
);
return
output
->
source
.
Fill
(
mutex
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to filter for output
\"
%s
\"
[%s]"
,
name
,
plugin
.
name
);
GetName
(),
output
->
plugin
.
name
);
Close
(
false
);
output
->
Close
(
false
);
/* don't automatically reopen this device for 10
seconds */
...
...
@@ -270,23 +268,23 @@ try {
}
inline
bool
AudioOutput
::
PlayChunk
()
AudioOutput
Control
::
PlayChunk
()
{
if
(
tags
)
{
const
auto
*
tag
=
source
.
ReadTag
();
if
(
output
->
tags
)
{
const
auto
*
tag
=
output
->
source
.
ReadTag
();
if
(
tag
!=
nullptr
)
{
const
ScopeUnlock
unlock
(
mutex
);
try
{
ao_plugin_send_tag
(
*
this
,
*
tag
);
ao_plugin_send_tag
(
*
output
,
*
tag
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"Failed to send tag to
\"
%s
\"
[%s]"
,
name
,
plugin
.
name
);
GetName
(),
output
->
plugin
.
name
);
}
}
}
while
(
command
==
Command
::
NONE
)
{
const
auto
data
=
source
.
PeekData
();
const
auto
data
=
output
->
source
.
PeekData
();
if
(
data
.
IsEmpty
())
break
;
...
...
@@ -297,17 +295,17 @@ AudioOutput::PlayChunk()
try
{
const
ScopeUnlock
unlock
(
mutex
);
nbytes
=
ao_plugin_play
(
*
this
,
data
.
data
,
data
.
size
);
nbytes
=
ao_plugin_play
(
*
output
,
data
.
data
,
data
.
size
);
assert
(
nbytes
<=
data
.
size
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
FormatError
(
e
,
"
\"
%s
\"
[%s] failed to play"
,
name
,
plugin
.
name
);
GetName
(),
output
->
plugin
.
name
);
nbytes
=
0
;
}
if
(
nbytes
==
0
)
{
Close
(
false
);
output
->
Close
(
false
);
/* don't automatically reopen this device for
10 seconds */
...
...
@@ -317,16 +315,16 @@ AudioOutput::PlayChunk()
return
false
;
}
assert
(
nbytes
%
out_audio_format
.
GetFrameSize
()
==
0
);
assert
(
nbytes
%
out
put
->
out
_audio_format
.
GetFrameSize
()
==
0
);
source
.
ConsumeData
(
nbytes
);
output
->
source
.
ConsumeData
(
nbytes
);
}
return
true
;
}
inline
bool
AudioOutput
::
Play
()
AudioOutput
Control
::
Play
()
{
if
(
!
FillSourceOrClose
())
/* no chunk available */
...
...
@@ -351,7 +349,7 @@ AudioOutput::Play()
give it a chance to refill the pipe before
it runs empty */
const
ScopeUnlock
unlock
(
mutex
);
client
->
ChunksConsumed
();
output
->
client
->
ChunksConsumed
();
n
=
0
;
}
...
...
@@ -360,7 +358,7 @@ AudioOutput::Play()
}
while
(
FillSourceOrClose
());
const
ScopeUnlock
unlock
(
mutex
);
client
->
ChunksConsumed
();
output
->
client
->
ChunksConsumed
();
return
true
;
}
...
...
@@ -397,26 +395,26 @@ AudioOutput::IteratePause()
}
inline
void
AudioOutput
::
Pause
()
AudioOutput
Control
::
Pause
()
{
BeginPause
();
output
->
BeginPause
();
CommandFinished
();
do
{
if
(
!
WaitForDelay
())
break
;
if
(
!
IteratePause
())
if
(
!
output
->
IteratePause
())
break
;
}
while
(
command
==
Command
::
NONE
);
pause
=
false
;
output
->
EndPause
()
;
}
void
AudioOutput
::
Task
()
AudioOutput
Control
::
Task
()
{
FormatThreadName
(
"output:%s"
,
name
);
FormatThreadName
(
"output:%s"
,
GetName
()
);
try
{
SetThreadRealtime
();
...
...
@@ -438,7 +436,7 @@ AudioOutput::Task()
last_error
=
nullptr
;
try
{
Enable
();
output
->
Enable
();
}
catch
(
const
std
::
runtime_error
&
e
)
{
LogError
(
e
);
fail_timer
.
Update
();
...
...
@@ -449,15 +447,17 @@ AudioOutput::Task()
break
;
case
Command
:
:
DISABLE
:
Disable
();
output
->
Disable
();
CommandFinished
();
break
;
case
Command
:
:
OPEN
:
last_error
=
nullptr
;
fail_timer
.
Reset
();
try
{
Open
();
output
->
Open
(
request
.
audio_format
,
*
request
.
pipe
);
}
catch
(
const
std
::
runtime_error
&
e
)
{
LogError
(
e
);
fail_timer
.
Update
();
...
...
@@ -468,13 +468,13 @@ AudioOutput::Task()
break
;
case
Command
:
:
CLOSE
:
if
(
open
)
Close
(
false
);
if
(
IsOpen
()
)
output
->
Close
(
false
);
CommandFinished
();
break
;
case
Command
:
:
PAUSE
:
if
(
!
open
)
{
if
(
!
o
utput
->
o
pen
)
{
/* the output has failed after
audio_output_all_pause() has
submitted the PAUSE command; bail
...
...
@@ -491,46 +491,46 @@ AudioOutput::Task()
continue
;
case
Command
:
:
DRAIN
:
if
(
open
)
{
if
(
o
utput
->
o
pen
)
{
const
ScopeUnlock
unlock
(
mutex
);
ao_plugin_drain
(
*
this
);
ao_plugin_drain
(
*
output
);
}
CommandFinished
();
continue
;
case
Command
:
:
CANCEL
:
source
.
Cancel
();
output
->
source
.
Cancel
();
if
(
open
)
{
if
(
o
utput
->
o
pen
)
{
const
ScopeUnlock
unlock
(
mutex
);
ao_plugin_cancel
(
*
this
);
ao_plugin_cancel
(
*
output
);
}
CommandFinished
();
continue
;
case
Command
:
:
KILL
:
Disable
();
source
.
Cancel
();
output
->
Disable
();
output
->
source
.
Cancel
();
CommandFinished
();
return
;
}
if
(
open
&&
allow_play
&&
Play
())
if
(
o
utput
->
o
pen
&&
allow_play
&&
Play
())
/* don't wait for an event if there are more
chunks in the pipe */
continue
;
if
(
command
==
Command
::
NONE
)
{
woken_for_play
=
false
;
cond
.
wait
(
mutex
);
cond
.
wait
(
output
->
mutex
);
}
}
}
void
AudioOutput
::
StartThread
()
AudioOutput
Control
::
StartThread
()
{
assert
(
command
==
Command
::
NONE
);
...
...
src/output/plugins/AlsaOutputPlugin.cxx
View file @
89b90043
...
...
@@ -26,6 +26,7 @@
#include "mixer/MixerList.hxx"
#include "pcm/PcmExport.hxx"
#include "system/ByteOrder.hxx"
#include "thread/Cond.hxx"
#include "util/Manual.hxx"
#include "util/RuntimeError.hxx"
#include "util/Domain.hxx"
...
...
src/output/plugins/httpd/HttpdInternal.hxx
View file @
89b90043
...
...
@@ -30,6 +30,7 @@
#include "output/Internal.hxx"
#include "output/Timer.hxx"
#include "thread/Mutex.hxx"
#include "thread/Cond.hxx"
#include "event/ServerSocket.hxx"
#include "event/DeferredMonitor.hxx"
#include "util/Cast.hxx"
...
...
src/output/plugins/sles/SlesOutputPlugin.cxx
View file @
89b90043
...
...
@@ -25,6 +25,7 @@
#include "AndroidSimpleBufferQueue.hxx"
#include "../../OutputAPI.hxx"
#include "../../Wrapper.hxx"
#include "thread/Cond.hxx"
#include "util/Macros.hxx"
#include "util/Domain.hxx"
#include "system/ByteOrder.hxx"
...
...
test/run_output.cxx
View file @
89b90043
...
...
@@ -43,8 +43,6 @@
#include <stdlib.h>
#include <stdio.h>
void
AudioOutput
::
Task
()
{}
class
DummyAudioOutputClient
final
:
public
AudioOutputClient
{
public
:
/* virtual methods from AudioOutputClient */
...
...
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