gecko/content/media/AudioStream.h
Paul Adenot 06e81488b1 Bug 919215 - Start the AudioStream on creation when in low-latency mode, and let it underrun. r=roc
The BufferedAudioStream buffers the data it gets through the Write() calls and
what is consumed by the callback. This means that if the audio producer starts
Write()ing data right after Start()ing the stream, data will accumulate in this
buffer and won't be consumed. Eventually, the buffer will be of a certain size
before it begins to be consumed by the callback, and this means an
umcompressible latency (because the data will be written at more or less the
same rate as it is produced).

This patch start the BufferedAudioStream right away when it is created, dropping
the silent AudioSegment until it finds real data (and padding with silence is
then done at the beginning). The stream will underrun, but the callback will
synthetize silence, avoiding overbuffering in the BufferedAudioStream. This
ensures minimal latency cause by the buffering.

Note that the clock will still advance, so this will not change the behavior of
content that has leading silence.
2013-11-19 10:43:15 +13:00

227 lines
8.6 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if !defined(AudioStream_h_)
#define AudioStream_h_
#include "AudioSampleFormat.h"
#include "AudioChannelCommon.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "Latency.h"
#include "mozilla/StaticMutex.h"
namespace soundtouch {
class SoundTouch;
}
namespace mozilla {
class AudioStream;
class AudioClock
{
public:
AudioClock(mozilla::AudioStream* aStream);
// Initialize the clock with the current AudioStream. Need to be called
// before querying the clock. Called on the audio thread.
void Init();
// Update the number of samples that has been written in the audio backend.
// Called on the state machine thread.
void UpdateWritePosition(uint32_t aCount);
// Get the read position of the stream, in microseconds.
// Called on the state machine thead.
uint64_t GetPosition();
// Get the read position of the stream, in frames.
// Called on the state machine thead.
uint64_t GetPositionInFrames();
// Set the playback rate.
// Called on the audio thread.
void SetPlaybackRate(double aPlaybackRate);
// Get the current playback rate.
// Called on the audio thread.
double GetPlaybackRate();
// Set if we are preserving the pitch.
// Called on the audio thread.
void SetPreservesPitch(bool aPreservesPitch);
// Get the current pitch preservation state.
// Called on the audio thread.
bool GetPreservesPitch();
// Get the number of frames written to the backend.
int64_t GetWritten();
private:
// This AudioStream holds a strong reference to this AudioClock. This
// pointer is garanteed to always be valid.
AudioStream* mAudioStream;
// The old output rate, to compensate audio latency for the period inbetween
// the moment resampled buffers are pushed to the hardware and the moment the
// clock should take the new rate into account for A/V sync.
int mOldOutRate;
// Position at which the last playback rate change occured
int64_t mBasePosition;
// Offset, in frames, at which the last playback rate change occured
int64_t mBaseOffset;
// Old base offset (number of samples), used when changing rate to compute the
// position in the stream.
int64_t mOldBaseOffset;
// Old base position (number of microseconds), when changing rate. This is the
// time in the media, not wall clock position.
int64_t mOldBasePosition;
// Write position at which the playbackRate change occured.
int64_t mPlaybackRateChangeOffset;
// The previous position reached in the media, used when compensating
// latency, to have the position at which the playbackRate change occured.
int64_t mPreviousPosition;
// Number of samples effectivelly written in backend, i.e. write position.
int64_t mWritten;
// Output rate in Hz (characteristic of the playback rate)
int mOutRate;
// Input rate in Hz (characteristic of the media being played)
int mInRate;
// True if the we are timestretching, false if we are resampling.
bool mPreservesPitch;
// True if we are playing at the old playbackRate after it has been changed.
bool mCompensatingLatency;
};
// Access to a single instance of this class must be synchronized by
// callers, or made from a single thread. One exception is that access to
// GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels}
// is thread-safe without external synchronization.
class AudioStream
{
public:
enum LatencyRequest {
HighLatency,
LowLatency
};
AudioStream();
virtual ~AudioStream();
// Initialize Audio Library. Some Audio backends require initializing the
// library before using it.
static void InitLibrary();
// Shutdown Audio Library. Some Audio backends require shutting down the
// library after using it.
static void ShutdownLibrary();
// AllocateStream will return either a local stream or a remoted stream
// depending on where you call it from. If you call this from a child process,
// you may receive an implementation which forwards to a compositing process.
static AudioStream* AllocateStream();
// Returns the maximum number of channels supported by the audio hardware.
static int MaxNumberOfChannels();
// Returns the samplerate the systems prefer, because it is the
// samplerate the hardware/mixer supports.
static int PreferredSampleRate();
// Initialize the audio stream. aNumChannels is the number of audio
// channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
// (22050Hz, 44100Hz, etc).
virtual nsresult Init(int32_t aNumChannels, int32_t aRate,
const dom::AudioChannelType aAudioStreamType,
LatencyRequest aLatencyRequest) = 0;
// Closes the stream. All future use of the stream is an error.
virtual void Shutdown() = 0;
// Write audio data to the audio hardware. aBuf is an array of AudioDataValues
// AudioDataValue of length aFrames*mChannels. If aFrames is larger
// than the result of Available(), the write will block until sufficient
// buffer space is available. aTime is the time in ms associated with the first sample
// for latency calculations
virtual nsresult Write(const mozilla::AudioDataValue* aBuf, uint32_t aFrames, TimeStamp *aTime = nullptr) = 0;
// Return the number of audio frames that can be written without blocking.
virtual uint32_t Available() = 0;
// Set the current volume of the audio playback. This is a value from
// 0 (meaning muted) to 1 (meaning full volume). Thread-safe.
virtual void SetVolume(double aVolume) = 0;
// Block until buffered audio data has been consumed.
virtual void Drain() = 0;
// Start the stream.
virtual void Start() = 0;
// Return the number of frames written so far in the stream. This allow the
// caller to check if it is safe to start the stream, if needed.
virtual int64_t GetWritten();
// Pause audio playback.
virtual void Pause() = 0;
// Resume audio playback.
virtual void Resume() = 0;
// Return the position in microseconds of the audio frame being played by
// the audio hardware, compensated for playback rate change. Thread-safe.
virtual int64_t GetPosition() = 0;
// Return the position, measured in audio frames played since the stream
// was opened, of the audio hardware. Thread-safe.
virtual int64_t GetPositionInFrames() = 0;
// Return the position, measured in audio framed played since the stream was
// opened, of the audio hardware, not adjusted for the changes of playback
// rate.
virtual int64_t GetPositionInFramesInternal() = 0;
// Returns true when the audio stream is paused.
virtual bool IsPaused() = 0;
int GetRate() { return mOutRate; }
int GetChannels() { return mChannels; }
// This should be called before attempting to use the time stretcher.
virtual nsresult EnsureTimeStretcherInitialized();
// Set playback rate as a multiple of the intrinsic playback rate. This is to
// be called only with aPlaybackRate > 0.0.
virtual nsresult SetPlaybackRate(double aPlaybackRate);
// Switch between resampling (if false) and time stretching (if true, default).
virtual nsresult SetPreservesPitch(bool aPreservesPitch);
protected:
// This mutex protects the mPreferedSamplerate member below.
static StaticMutex mMutex;
// Prefered samplerate, in Hz (characteristic of the
// hardware/mixer/platform/API used).
static uint32_t mPreferredSampleRate;
// Input rate in Hz (characteristic of the media being played)
int mInRate;
// Output rate in Hz (characteristic of the playback rate)
int mOutRate;
int mChannels;
// Number of frames written to the buffers.
int64_t mWritten;
AudioClock mAudioClock;
nsAutoPtr<soundtouch::SoundTouch> mTimeStretcher;
nsRefPtr<AsyncLatencyLogger> mLatencyLog;
// copy of Latency logger's starting time for offset calculations
TimeStamp mStartTime;
// Whether we are playing a low latency stream, or a normal stream.
LatencyRequest mLatencyRequest;
// Where in the current mInserts[0] block cubeb has read to
int64_t mReadPoint;
// Keep track of each inserted block of samples and the time it was inserted
// so we can estimate the clock time for a specific sample's insertion (for when
// we send data to cubeb). Blocks are aged out as needed.
struct Inserts {
int64_t mTimeMs;
int64_t mFrames;
};
nsAutoTArray<Inserts,8> mInserts;
};
} // namespace mozilla
#endif