#ifndef MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
#define MEDIA_AUDIO_ALSA_ALSA_OUTPUT_H_
#include <alsa/asoundlib.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/base/audio_parameters.h"
namespace media {
class AlsaWrapper;
class AudioManagerBase;
class ChannelMixer;
class SeekableBuffer;
class MEDIA_EXPORT AlsaPcmOutputStream : public AudioOutputStream {
public:
static constexpr char kDefaultDevice[] = "default";
static constexpr char kAutoSelectDevice[] = "";
static constexpr char kPlugPrefix[] = "plug:";
static constexpr uint32_t kMinLatencyMicros = 40 * 1000;
AlsaPcmOutputStream(const std::string& device_name,
const AudioParameters& params,
AlsaWrapper* wrapper,
AudioManagerBase* manager);
AlsaPcmOutputStream(const AlsaPcmOutputStream&) = delete;
AlsaPcmOutputStream& operator=(const AlsaPcmOutputStream&) = delete;
~AlsaPcmOutputStream() override;
bool Open() override;
void Close() override;
void Start(AudioSourceCallback* callback) override;
void Stop() override;
void Flush() override;
void SetVolume(double volume) override;
void GetVolume(double* volume) override;
void SetTickClockForTesting(const base::TickClock* tick_clock);
private:
friend class AlsaPcmOutputStreamTest;
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
AutoSelectDevice_DeviceSelect);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
AutoSelectDevice_FallbackDevices);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, AutoSelectDevice_HintFail);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Negative);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_Underrun);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, BufferPacket_FullBuffer);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ConstructedState);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, LatencyFloor);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, OpenClose);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmOpenFailed);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, PcmSetParamsFailed);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, ScheduleNextWrite);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest,
ScheduleNextWrite_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, StartStop);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_FinishedPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_NormalPacket);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_StopStream);
FRIEND_TEST_ALL_PREFIXES(AlsaPcmOutputStreamTest, WritePacket_WriteFails);
enum InternalState {
kInError = 0,
kCreated,
kIsOpened,
kIsPlaying,
kIsStopped,
kIsClosed
};
friend std::ostream& operator<<(std::ostream& os, InternalState);
void BufferPacket(bool* source_exhausted);
void WritePacket();
void WriteTask();
void ScheduleNextWrite(bool source_exhausted);
std::string FindDeviceForChannels(uint32_t channels);
snd_pcm_sframes_t GetAvailableFrames();
snd_pcm_sframes_t GetCurrentDelay();
snd_pcm_t* AutoSelectDevice(uint32_t latency);
bool CanTransitionTo(InternalState to);
InternalState TransitionTo(InternalState to);
InternalState state();
int RunDataCallback(base::TimeDelta delay,
base::TimeTicks delay_timestamp,
AudioBus* audio_bus);
void RunErrorCallback(int code);
void set_source_callback(AudioSourceCallback* callback);
const std::string requested_device_name_;
const snd_pcm_format_t pcm_format_;
const uint32_t channels_;
const ChannelLayout channel_layout_;
const uint32_t sample_rate_;
const uint32_t bytes_per_sample_;
const uint32_t bytes_per_frame_;
std::string device_name_;
uint32_t packet_size_;
base::TimeDelta latency_;
uint32_t bytes_per_output_frame_;
uint32_t alsa_buffer_frames_;
bool stop_stream_;
raw_ptr<AlsaWrapper> wrapper_;
raw_ptr<AudioManagerBase> manager_;
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
raw_ptr<snd_pcm_t> playback_handle_;
std::unique_ptr<SeekableBuffer> buffer_;
uint32_t frames_per_packet_;
InternalState state_;
float volume_;
raw_ptr<AudioSourceCallback> source_callback_;
std::unique_ptr<AudioBus> audio_bus_;
std::unique_ptr<ChannelMixer> channel_mixer_;
std::unique_ptr<AudioBus> mixed_audio_bus_;
raw_ptr<const base::TickClock> tick_clock_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<AlsaPcmOutputStream> weak_factory_{this};
};
MEDIA_EXPORT std::ostream& operator<<(std::ostream& os,
AlsaPcmOutputStream::InternalState);
}
#endif