player/Outputs: abstract interface wrapping class MultipleOutputs

This commit is contained in:
Max Kellermann 2017-12-29 16:23:19 +01:00
parent c04aafb4e3
commit c40354bbcb
7 changed files with 148 additions and 90 deletions

View File

@ -143,6 +143,7 @@ libmpd_a_SOURCES = \
src/player/Thread.cxx src/player/Thread.hxx \
src/player/Control.cxx src/player/Control.hxx \
src/player/Listener.hxx \
src/player/Outputs.hxx \
src/PlaylistError.cxx src/PlaylistError.hxx \
src/PlaylistPrint.cxx src/PlaylistPrint.hxx \
src/PlaylistSave.cxx src/PlaylistSave.hxx \

View File

@ -315,7 +315,7 @@ MultipleOutputs::ClearTailChunk(const MusicChunk *chunk,
}
unsigned
MultipleOutputs::Check() noexcept
MultipleOutputs::CheckPipe() noexcept
{
const MusicChunk *chunk;
bool is_tail;

View File

@ -27,6 +27,7 @@
#define OUTPUT_ALL_H
#include "Control.hxx"
#include "player/Outputs.hxx"
#include "AudioFormat.hxx"
#include "ReplayGainMode.hxx"
#include "Chrono.hxx"
@ -44,7 +45,7 @@ class AudioOutputClient;
struct MusicChunk;
struct ReplayGainConfig;
class MultipleOutputs {
class MultipleOutputs final : public PlayerOutputs {
MixerListener &mixer_listener;
std::vector<AudioOutputControl *> outputs;
@ -115,85 +116,8 @@ public:
gcc_pure
AudioOutputControl *FindByName(const char *name) noexcept;
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
*/
void EnableDisable();
/**
* Opens all audio outputs which are not disabled.
*
* Throws #std::runtime_error on error.
*
* @param audio_format the preferred audio format
* @param _buffer the #music_buffer where consumed #MusicChunk objects
* should be returned
*/
void Open(const AudioFormat audio_format, MusicBuffer &_buffer);
/**
* Closes all audio outputs.
*/
void Close() noexcept;
/**
* Closes all audio outputs. Outputs with the "always_on"
* flag are put into pause mode.
*/
void Release() noexcept;
void SetReplayGainMode(ReplayGainMode mode) noexcept;
/**
* Enqueue a #MusicChunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
* Throws #std::runtime_error on error (all closed then).
*
* @param chunk the #MusicChunk object to be played
*/
void Play(MusicChunk *chunk);
/**
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
*
* @return the number of chunks to play left in the #MusicPipe
*/
unsigned Check() noexcept;
/**
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
*/
void Pause() noexcept;
/**
* Drain all audio outputs.
*/
void Drain() noexcept;
/**
* Try to cancel data which may still be in the device's buffers.
*/
void Cancel() noexcept;
/**
* Indicate that a new song will begin now.
*/
void SongBorder() noexcept;
/**
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* finished yet.
*/
gcc_pure
SignedSongTime GetElapsedTime() const noexcept {
return elapsed_time;
}
/**
* Returns the average volume of all available mixers (range
* 0..100). Returns -1 if no mixer can be queried.
@ -259,6 +183,22 @@ private:
* reference.
*/
void ClearTailChunk(const MusicChunk *chunk, bool *locked) noexcept;
/* virtual methods from class PlayerOutputs */
void EnableDisable() override;
void Open(const AudioFormat audio_format,
MusicBuffer &_buffer) override;
void Close() noexcept override;
void Release() noexcept override;
void Play(MusicChunk *chunk) override;
unsigned CheckPipe() noexcept override;
void Pause() noexcept override;
void Drain() noexcept override;
void Cancel() noexcept override;
void SongBorder() noexcept override;
SignedSongTime GetElapsedTime() const noexcept override {
return elapsed_time;
}
};
#endif

View File

@ -19,16 +19,16 @@
#include "config.h"
#include "Control.hxx"
#include "Outputs.hxx"
#include "Idle.hxx"
#include "DetachedSong.hxx"
#include "output/MultipleOutputs.hxx"
#include <algorithm>
#include <assert.h>
PlayerControl::PlayerControl(PlayerListener &_listener,
MultipleOutputs &_outputs,
PlayerOutputs &_outputs,
unsigned _buffer_chunks,
unsigned _buffered_before_play,
AudioFormat _configured_audio_format,
@ -50,10 +50,10 @@ PlayerControl::~PlayerControl() noexcept
bool
PlayerControl::WaitOutputConsumed(unsigned threshold) noexcept
{
bool result = outputs.Check() < threshold;
bool result = outputs.CheckPipe() < threshold;
if (!result && command == PlayerCommand::NONE) {
Wait();
result = outputs.Check() < threshold;
result = outputs.CheckPipe() < threshold;
}
return result;

View File

@ -36,7 +36,7 @@
#include <stdint.h>
class PlayerListener;
class MultipleOutputs;
class PlayerOutputs;
class DetachedSong;
enum class PlayerState : uint8_t {
@ -108,7 +108,7 @@ struct player_status {
struct PlayerControl final : AudioOutputClient {
PlayerListener &listener;
MultipleOutputs &outputs;
PlayerOutputs &outputs;
const unsigned buffer_chunks;
@ -223,7 +223,7 @@ struct PlayerControl final : AudioOutputClient {
double total_play_time = 0;
PlayerControl(PlayerListener &_listener,
MultipleOutputs &_outputs,
PlayerOutputs &_outputs,
unsigned buffer_chunks,
unsigned buffered_before_play,
AudioFormat _configured_audio_format,

117
src/player/Outputs.hxx Normal file
View File

@ -0,0 +1,117 @@
/*
* Copyright 2003-2017 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_PLAYER_OUTPUT_INTERFACE_HXX
#define MPD_PLAYER_OUTPUT_INTERFACE_HXX
#include "Chrono.hxx"
#include "Compiler.h"
struct AudioFormat;
struct MusicChunk;
class MusicBuffer;
/**
* An interface for the player thread to control all outputs. This
* interface is implemented only by #MultipleOutputs, and exists only
* to decouple the player code from the output code, to be able to
* unit-test the player code.
*/
class PlayerOutputs {
public:
/**
* Checks the "enabled" flag of all audio outputs, and if one has
* changed, commit the change.
*
* Throws on error.
*/
virtual void EnableDisable() = 0;
/**
* Opens all audio outputs which are not disabled.
*
* Throws on error.
*
* @param audio_format the preferred audio format
* @param buffer the #MusicBuffer where consumed #MusicChunk
* objects should be returned
*/
virtual void Open(const AudioFormat audio_format,
MusicBuffer &buffer) = 0;
/**
* Closes all audio outputs.
*/
virtual void Close() noexcept = 0;
/**
* Closes all audio outputs. Outputs with the "always_on"
* flag are put into pause mode.
*/
virtual void Release() noexcept = 0;
/**
* Enqueue a #MusicChunk object for playing, i.e. pushes it to a
* #MusicPipe.
*
* Throws on error (all closed then).
*
* @param chunk the #MusicChunk object to be played
*/
virtual void Play(MusicChunk *chunk) = 0;
/**
* Checks if the output devices have drained their music pipe, and
* returns the consumed music chunks to the #music_buffer.
*
* @return the number of chunks to play left in the #MusicPipe
*/
virtual unsigned CheckPipe() noexcept = 0;
/**
* Puts all audio outputs into pause mode. Most implementations will
* simply close it then.
*/
virtual void Pause() noexcept = 0;
/**
* Drain all audio outputs.
*/
virtual void Drain() noexcept = 0;
/**
* Try to cancel data which may still be in the device's buffers.
*/
virtual void Cancel() noexcept = 0;
/**
* Indicate that a new song will begin now.
*/
virtual void SongBorder() noexcept = 0;
/**
* Returns the "elapsed_time" stamp of the most recently finished
* chunk. A negative value is returned when no chunk has been
* finished yet.
*/
gcc_pure
virtual SignedSongTime GetElapsedTime() const noexcept = 0;
};
#endif

View File

@ -19,6 +19,7 @@
#include "config.h"
#include "Thread.hxx"
#include "Outputs.hxx"
#include "Listener.hxx"
#include "decoder/DecoderThread.hxx"
#include "decoder/DecoderControl.hxx"
@ -29,7 +30,6 @@
#include "DetachedSong.hxx"
#include "CrossFade.hxx"
#include "Control.hxx"
#include "output/MultipleOutputs.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx"
#include "system/PeriodClock.hxx"
@ -334,7 +334,7 @@ private:
unsigned UnlockCheckOutputs() noexcept {
const ScopeUnlock unlock(pc.mutex);
return pc.outputs.Check();
return pc.outputs.CheckPipe();
}
/**
@ -750,7 +750,7 @@ Player::ProcessCommand() noexcept
case PlayerCommand::REFRESH:
if (output_open && !paused) {
const ScopeUnlock unlock(pc.mutex);
pc.outputs.Check();
pc.outputs.CheckPipe();
}
pc.elapsed_time = !pc.outputs.GetElapsedTime().IsNegative()
@ -1004,7 +1004,7 @@ Player::Run() noexcept
{
const ScopeUnlock unlock(pc.mutex);
if (!paused && output_open &&
pc.outputs.Check() < 4 &&
pc.outputs.CheckPipe() < 4 &&
!SendSilence())
break;
}