зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1877948 - Update libcubeb to revision 063a090. r=cubeb-reviewers,chunmin
Differential Revision: https://phabricator.services.mozilla.com/D215931
This commit is contained in:
Родитель
4186d0a8bb
Коммит
ce5f581601
|
@ -9,8 +9,8 @@ origin:
|
|||
description: "Cross platform audio library"
|
||||
url: https://github.com/mozilla/cubeb
|
||||
license: ISC
|
||||
release: 5d5008d25cef5237711b500f7d8a8653942530f1 (2024-07-01T10:18:39Z).
|
||||
revision: 5d5008d25cef5237711b500f7d8a8653942530f1
|
||||
release: 063a090221250a5e7c6c61a3f94c95f60cfda722 (2024-07-08T16:08:38Z).
|
||||
revision: 063a090221250a5e7c6c61a3f94c95f60cfda722
|
||||
|
||||
vendoring:
|
||||
url: https://github.com/mozilla/cubeb
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
@ -136,6 +137,128 @@ struct AAudioTimingInfo {
|
|||
uint32_t input_latency;
|
||||
};
|
||||
|
||||
/* To guess the current position of the stream when it's playing, the elapsed
|
||||
* time between the last callback and now is used. However, when the stream was
|
||||
* stopped and there was no new callback after playing restarted yet, the time
|
||||
* spent in stopped state should be excluded.
|
||||
* This class defines an internal state machine that takes the stream state
|
||||
* changes and callback emissions as events to changes it own statesm and
|
||||
* calculates played time accordingly.
|
||||
*
|
||||
* A simplified |stream_state| transitions of playing looks like:
|
||||
* INIT -> [STARTING/STARTED -> callback* -> STOPPING/STOPPED]* -> SHUTDOWN|INIT
|
||||
*
|
||||
* Internal states:
|
||||
* - None: the initial state.
|
||||
* - Play: stream is playing.
|
||||
* - Pause: stream is not playing. Holds stop timestamp.
|
||||
* - Resume: stream is playing after stopping and no callback emitted yet. Holds
|
||||
* time elapsed in the previous Pause state.
|
||||
* Transitions:
|
||||
* - None -(STARTING)-> Play
|
||||
* - Play -(STOPPING)-> Pause
|
||||
* - Pause -(STARTING)-> Resume
|
||||
* - Resume -(callback)-> Play
|
||||
* - Resume -(STARTING)-> Resume
|
||||
* - Pause -(INIT)-> None
|
||||
*/
|
||||
class position_interpolator {
|
||||
public:
|
||||
// Called with the current time when stopping the stream.
|
||||
void stop(uint64_t timestamp)
|
||||
{
|
||||
assert(in_state<Play>() || in_state<Resume>());
|
||||
// Change to Pause and save the current time in it. Timestamp offset by the
|
||||
// elapsed time in previous Pause if stream stops again before any callback
|
||||
// clears it.
|
||||
set_pause_timestamp(in_state<Play>() ? timestamp
|
||||
: timestamp - get_pause_time());
|
||||
}
|
||||
|
||||
// Called with the current time when starting the stream.
|
||||
void start(uint64_t timestamp)
|
||||
{
|
||||
assert(in_state<None>() || in_state<Pause>());
|
||||
if (in_state<Pause>()) {
|
||||
// Change to Resume and record elapsed time in it.
|
||||
set_pause_time(timestamp - get_pause_timestamp());
|
||||
} else {
|
||||
set_state<Play>();
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate how much time the stream bas been playing since last callback.
|
||||
uint64_t compute(uint64_t now, uint64_t last_callback_timestamp)
|
||||
{
|
||||
if (in_state<Play>()) {
|
||||
if (callback_timestamp != last_callback_timestamp) {
|
||||
callback_timestamp = last_callback_timestamp;
|
||||
}
|
||||
return now - last_callback_timestamp;
|
||||
} else if (in_state<Resume>()) {
|
||||
if (callback_timestamp == last_callback_timestamp) {
|
||||
// Stream was stopped and no callback emited yet: exclude elapsed time
|
||||
// in Pause state.
|
||||
return now - last_callback_timestamp - get_pause_time();
|
||||
}
|
||||
// Callback emitted: update callback timestamp and change to Play.
|
||||
callback_timestamp = last_callback_timestamp;
|
||||
set_state<Play>();
|
||||
return now - last_callback_timestamp;
|
||||
} else if (in_state<Pause>()) {
|
||||
assert(callback_timestamp == last_callback_timestamp);
|
||||
// Use recorded timestamps when Paused.
|
||||
return get_pause_timestamp() - callback_timestamp;
|
||||
} else {
|
||||
assert(in_state<None>());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T> void set_state() { state.emplace<T>(); }
|
||||
|
||||
template <typename T> bool in_state()
|
||||
{
|
||||
return std::holds_alternative<T>(state);
|
||||
}
|
||||
|
||||
void set_pause_time(uint64_t time) { state.emplace<Resume>(time); }
|
||||
|
||||
uint64_t get_pause_time()
|
||||
{
|
||||
assert(in_state<Resume>());
|
||||
return std::get<Resume>(state).pause_time;
|
||||
}
|
||||
|
||||
void set_pause_timestamp(uint64_t timestamp)
|
||||
{
|
||||
state.emplace<Pause>(timestamp);
|
||||
}
|
||||
|
||||
uint64_t get_pause_timestamp()
|
||||
{
|
||||
assert(in_state<Pause>());
|
||||
return std::get<Pause>(state).timestamp;
|
||||
}
|
||||
|
||||
struct None {};
|
||||
struct Play {};
|
||||
struct Pause {
|
||||
Pause() = delete;
|
||||
explicit Pause(uint64_t timestamp) : timestamp(timestamp) {}
|
||||
uint64_t timestamp; // The time when stopping stream.
|
||||
};
|
||||
struct Resume {
|
||||
Resume() = delete;
|
||||
explicit Resume(uint64_t time) : pause_time(time) {}
|
||||
uint64_t pause_time; // Elapsed time from stopping to starting stream.
|
||||
};
|
||||
std::variant<None, Play, Pause, Resume> state;
|
||||
// Keep track input callback timestamp to detect callback emission.
|
||||
uint64_t callback_timestamp{0};
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
||||
cubeb * context{};
|
||||
|
@ -174,6 +297,7 @@ struct cubeb_stream {
|
|||
bool voice_input{};
|
||||
bool voice_output{};
|
||||
uint64_t previous_clock{};
|
||||
position_interpolator interpolator;
|
||||
};
|
||||
|
||||
struct cubeb {
|
||||
|
@ -360,9 +484,7 @@ update_state(cubeb_stream * stm)
|
|||
}
|
||||
|
||||
// handle invalid stream states
|
||||
if (istate == AAUDIO_STREAM_STATE_PAUSING ||
|
||||
istate == AAUDIO_STREAM_STATE_PAUSED ||
|
||||
istate == AAUDIO_STREAM_STATE_FLUSHING ||
|
||||
if (istate == AAUDIO_STREAM_STATE_FLUSHING ||
|
||||
istate == AAUDIO_STREAM_STATE_FLUSHED ||
|
||||
istate == AAUDIO_STREAM_STATE_UNKNOWN ||
|
||||
istate == AAUDIO_STREAM_STATE_DISCONNECTED) {
|
||||
|
@ -372,9 +494,7 @@ update_state(cubeb_stream * stm)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ostate == AAUDIO_STREAM_STATE_PAUSING ||
|
||||
ostate == AAUDIO_STREAM_STATE_PAUSED ||
|
||||
ostate == AAUDIO_STREAM_STATE_FLUSHING ||
|
||||
if (ostate == AAUDIO_STREAM_STATE_FLUSHING ||
|
||||
ostate == AAUDIO_STREAM_STATE_FLUSHED ||
|
||||
ostate == AAUDIO_STREAM_STATE_UNKNOWN ||
|
||||
ostate == AAUDIO_STREAM_STATE_DISCONNECTED) {
|
||||
|
@ -433,12 +553,12 @@ update_state(cubeb_stream * stm)
|
|||
}
|
||||
break;
|
||||
case stream_state::STOPPING:
|
||||
assert(!istate || istate == AAUDIO_STREAM_STATE_STOPPING ||
|
||||
istate == AAUDIO_STREAM_STATE_STOPPED);
|
||||
assert(!ostate || ostate == AAUDIO_STREAM_STATE_STOPPING ||
|
||||
ostate == AAUDIO_STREAM_STATE_STOPPED);
|
||||
if ((!istate || istate == AAUDIO_STREAM_STATE_STOPPED) &&
|
||||
(!ostate || ostate == AAUDIO_STREAM_STATE_STOPPED)) {
|
||||
assert(!istate || istate == AAUDIO_STREAM_STATE_PAUSING ||
|
||||
istate == AAUDIO_STREAM_STATE_PAUSED);
|
||||
assert(!ostate || ostate == AAUDIO_STREAM_STATE_PAUSING ||
|
||||
ostate == AAUDIO_STREAM_STATE_PAUSED);
|
||||
if ((!istate || istate == AAUDIO_STREAM_STATE_PAUSED) &&
|
||||
(!ostate || ostate == AAUDIO_STREAM_STATE_PAUSED)) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
|
||||
new_state = stream_state::STOPPED;
|
||||
}
|
||||
|
@ -1054,6 +1174,7 @@ aaudio_stream_destroy_locked(cubeb_stream * stm, lock_guard<mutex> & lock)
|
|||
}
|
||||
|
||||
stm->timing_info.invalidate();
|
||||
stm->interpolator = {};
|
||||
|
||||
if (stm->resampler) {
|
||||
cubeb_resampler_destroy(stm->resampler);
|
||||
|
@ -1294,10 +1415,14 @@ aaudio_stream_init(cubeb * ctx, cubeb_stream ** stream,
|
|||
if (output_stream_params) {
|
||||
stm->output_stream_params = std::make_unique<cubeb_stream_params>();
|
||||
*(stm->output_stream_params) = *output_stream_params;
|
||||
} else {
|
||||
stm->output_stream_params = nullptr;
|
||||
}
|
||||
if (input_stream_params) {
|
||||
stm->input_stream_params = std::make_unique<cubeb_stream_params>();
|
||||
*(stm->input_stream_params) = *input_stream_params;
|
||||
} else {
|
||||
stm->input_stream_params = nullptr;
|
||||
}
|
||||
|
||||
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
|
||||
|
@ -1420,6 +1545,7 @@ aaudio_stream_start_locked(cubeb_stream * stm, lock_guard<mutex> & lock)
|
|||
}
|
||||
|
||||
if (success) {
|
||||
stm->interpolator.start(now_ns());
|
||||
stm->context->state.waiting.store(true);
|
||||
stm->context->state.cond.notify_one();
|
||||
}
|
||||
|
@ -1464,16 +1590,16 @@ aaudio_stream_stop_locked(cubeb_stream * stm, lock_guard<mutex> & lock)
|
|||
|
||||
aaudio_result_t res;
|
||||
|
||||
// No callbacks are triggered anymore when requestStop returns.
|
||||
// No callbacks are triggered anymore when requestPause returns.
|
||||
// That is important as we otherwise might read from a closed istream
|
||||
// for a duplex stream.
|
||||
// Therefor it is important to close ostream first.
|
||||
if (stm->ostream) {
|
||||
// Could use pause + flush here as well, the public cubeb interface
|
||||
// doesn't state behavior.
|
||||
res = WRAP(AAudioStream_requestStop)(stm->ostream);
|
||||
res = WRAP(AAudioStream_requestPause)(stm->ostream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOG("AAudioStream_requestStop (ostream): %s",
|
||||
LOG("AAudioStream_requestPause (ostream): %s",
|
||||
WRAP(AAudio_convertResultToText)(res));
|
||||
stm->state.store(stream_state::ERROR);
|
||||
return CUBEB_ERROR;
|
||||
|
@ -1481,9 +1607,9 @@ aaudio_stream_stop_locked(cubeb_stream * stm, lock_guard<mutex> & lock)
|
|||
}
|
||||
|
||||
if (stm->istream) {
|
||||
res = WRAP(AAudioStream_requestStop)(stm->istream);
|
||||
res = WRAP(AAudioStream_requestPause)(stm->istream);
|
||||
if (res != AAUDIO_OK) {
|
||||
LOG("AAudioStream_requestStop (istream): %s",
|
||||
LOG("AAudioStream_requestPause (istream): %s",
|
||||
WRAP(AAudio_convertResultToText)(res));
|
||||
stm->state.store(stream_state::ERROR);
|
||||
return CUBEB_ERROR;
|
||||
|
@ -1528,6 +1654,7 @@ aaudio_stream_stop_locked(cubeb_stream * stm, lock_guard<mutex> & lock)
|
|||
}
|
||||
|
||||
if (success) {
|
||||
stm->interpolator.stop(now_ns());
|
||||
stm->context->state.waiting.store(true);
|
||||
stm->context->state.cond.notify_one();
|
||||
}
|
||||
|
@ -1578,8 +1705,9 @@ aaudio_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|||
LOGV("AAudioTimingInfo idx:%lu tstamp:%lu latency:%u",
|
||||
info.output_frame_index, info.tstamp, info.output_latency);
|
||||
// Interpolate client side since the last callback.
|
||||
uint64_t interpolation =
|
||||
stm->sample_rate * (now_ns() - info.tstamp) / NS_PER_S;
|
||||
uint64_t interpolation = stm->sample_rate *
|
||||
stm->interpolator.compute(now_ns(), info.tstamp) /
|
||||
NS_PER_S;
|
||||
*position = info.output_frame_index + interpolation - info.output_latency;
|
||||
if (*position < stm->previous_clock) {
|
||||
*position = stm->previous_clock;
|
||||
|
|
Загрузка…
Ссылка в новой задаче