Bug 1877948 - Update libcubeb to revision 063a090. r=cubeb-reviewers,chunmin

Differential Revision: https://phabricator.services.mozilla.com/D215931
This commit is contained in:
John Lin 2024-07-09 15:38:24 +00:00
Родитель 4186d0a8bb
Коммит ce5f581601
2 изменённых файлов: 149 добавлений и 21 удалений

Просмотреть файл

@ -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;