-
Notifications
You must be signed in to change notification settings - Fork 128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
AAudio: don't count the time of stopped stream and replace stop request with pause. #789
Conversation
c912469
to
f356a07
Compare
src/cubeb_aaudio.cpp
Outdated
} | ||
|
||
private: | ||
std::mutex mutex; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, we can't use locks here because we're on a real-time thread. Thankfully, we only have a few arithmetic operations to do here so we can use std::atomic
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the review! I've updated the logic to use std::atomic
rather than std::mutex
. Please let me know if any issues.
f356a07
to
b93825d
Compare
b93825d
to
e75d3a6
Compare
In the latest revision, the synchronization logic was removed—access to states/values only in user-called functions. |
Not sure why the test failed on Windows. The patches only affect Android behavior. |
The windows failure is probably an intermittent. It succeeded after a re-run. Just looking fundamentally at this I am on board with fixing the interpolation (we also have other issues in the area, see https://bugzilla.mozilla.org/show_bug.cgi?id=1902263). I see an issue: That the flushed data doesn't reach the user's ears doesn't seem like a problem. It's short enough so they're unlikely to notice. For the sake of Let me know if I understood this right! |
Thanks a lot for the review!
Exactly, the already buffered data in the previous data callback will be flushed by
Luckily, seeking destroys then initializes audio stream, so old data isn't played.
TBH, I never noticed skipped audio. I only found that some video frames were dropped when resuming a paused video because the audio clock advanced further than expected. This change didn't eliminate the issue completely in my tests, but it seems mitigated. Besides, this behavior seems inconsistent with the other backends.
|
Ok, so that's true for Gecko but might not be for other clients.
Makes sense that some dropped video frames is more noticable.
Out of curiosity I went through the common ones as I'm not familiar with all:
Paul confirms that cubeb_stream_stop is more for pausing also, let me take a deeper look in a bit. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some early feedback. I'll have to look closer at the interpolator later.
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)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me stopping and stopped should be considered invalid or handled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry but I don't follow. In the patch, only AAUDIO_STREAM_STATE_{PAUSING|PAUSED}
are allowed, and AAUDIO_STREAM_STATE_{STOPPING|STOPPED}
are considered invalid. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re invalid I was thinking higher up under handle invalid stream states
. Maybe we cannot do that because we still call AAudioStream_requestStop
in some other cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation. Yes, since AAudioStream_requestStop()
is called in case stream_state::DRAINING
, I don't think AAUDIO_STREAM_STATE_{STOPPING|STOPPED}
need to be listed in the handle invalid stream states
conditions.
src/cubeb_aaudio.cpp
Outdated
// index == 0 && get<0> == 0: stream has no stop record | ||
// index == 0 && get<0> > 0: stream stopped | ||
// index == 1: stream stopped and then started | ||
std::variant<uint64_t, uint64_t> time; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these uint64_t
s could be distinct types so you can do something like std::get<StopTimestamp>(time)
, I think this would be easier to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe even
struct None {};
struct Stopped {
uint64_t time = {};
};
struct StartedAfterStopped {
uint64_t time = {};
};
struct Running {};
std::variant<None, Stopped, StartedAfterStopped, Running> state;
where both None and Running correspond to your current std::get<0>(time) == 0
(but with explicit semantics), and changing from index to type getters like if (auto* sas = std::get_if<StartedAfterStopped>(state)) {...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea. I will do it in the next revision. Thanks!
@@ -135,6 +136,66 @@ struct AAudioTimingInfo { | |||
uint32_t input_latency; | |||
}; | |||
|
|||
class position_interpolator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give a high-level overview of what this class' intended behavior is. Expected inputs and outputs and that sort of thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me we are really tracking the time spent in the starting/started states with a stale callback timestamp.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give a high-level overview of what this class' intended behavior is. Expected inputs and outputs and that sort of thing.
I will add comments for the class in the next revision.
It seems to me we are really tracking the time spent in the starting/started states with a stale callback timestamp.
Agree. Calling AAudioStream_requestStop()
does generate a new callback invocation immediately after starting to get more data, but since it flushes previously buffered data, the position always jumps ahead, and video frames get dropped. I hope calling AAudioStream_requestPause()
can make it less serious.
src/cubeb_aaudio.cpp
Outdated
assert(time.index() == 0 && (std::get<0>(time) > 0 /* stopped */ || | ||
callback_timestamp == 0 /* inited */)); | ||
if (std::get<0>(time) > 0) { | ||
// Has been stopped: calculate elipsed time. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/elipsed/elapsed/
(and below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic seems to check out to me, thanks!
I left some ideas for making it easier to read. Do with them as you wish.
I also left a comment about AAUDIO_STREAM_STATE_{STOPPING|STOPPED}. Please at a minimum address this one before landing.
src/cubeb_aaudio.cpp
Outdated
assert(time.index() == 1 || (time.index() == 0 && std::get<0>(time) == 0)); | ||
uint64_t prev = 0; | ||
if (time.index() == 1) { | ||
// Offset timestamp by the last elasped time if stream stops again before |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/elasped/elapsed/
src/cubeb_aaudio.cpp
Outdated
// index == 0 && get<0> == 0: stream has no stop record | ||
// index == 0 && get<0> > 0: stream stopped | ||
// index == 1: stream stopped and then started | ||
std::variant<uint64_t, uint64_t> time; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe even
struct None {};
struct Stopped {
uint64_t time = {};
};
struct StartedAfterStopped {
uint64_t time = {};
};
struct Running {};
std::variant<None, Stopped, StartedAfterStopped, Running> state;
where both None and Running correspond to your current std::get<0>(time) == 0
(but with explicit semantics), and changing from index to type getters like if (auto* sas = std::get_if<StartedAfterStopped>(state)) {...}
@@ -135,6 +136,66 @@ struct AAudioTimingInfo { | |||
uint32_t input_latency; | |||
}; | |||
|
|||
class position_interpolator { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me we are really tracking the time spent in the starting/started states with a stale callback timestamp.
aaudio_stream_get_position() calculates position using elapsed time since last callback, including the time during stream is stopped. To interpolate correctly, record the stop time and use it in calculation.
According to the document, AAudioStream_requestStop(): "The stream will stop after all of the data currently buffered has been played." AAudioStream_requestPause(): "Pausing a stream will freeze the data flow but not flush any buffers."
If a stream has previously been stopped and then started, the elapsed time in between should not be included when calculating the position.
Also,
AAudioStream_requestStop()
doesn't behave as expected.