-
Notifications
You must be signed in to change notification settings - Fork 565
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
Implement getFramesRead for OpenSL ES OUTPUT #89
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
/* | ||
* Copyright 2016 The Android Open Source Project | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
#ifndef COMMON_MONOTONIC_COUNTER_H | ||
#define COMMON_MONOTONIC_COUNTER_H | ||
|
||
#include <cstdint> | ||
|
||
/** | ||
* Maintain a 64-bit monotonic counter. | ||
* Can be used to track a 32-bit counter that wraps or gets reset. | ||
* | ||
* Note that this is not atomic and has no interior locks. | ||
* A caller will need to provide their own exterior locking | ||
* if they need to use it from multiple threads. | ||
*/ | ||
class MonotonicCounter { | ||
|
||
public: | ||
MonotonicCounter() {}; | ||
virtual ~MonotonicCounter() {}; | ||
|
||
/** | ||
* @return current value of the counter | ||
*/ | ||
int64_t get() const { | ||
return mCounter64; | ||
} | ||
|
||
/** | ||
* set the current value of the counter | ||
*/ | ||
void set(int64_t counter) { | ||
mCounter64 = counter; | ||
} | ||
|
||
/** | ||
* Advance the counter if delta is positive. | ||
* @return current value of the counter | ||
*/ | ||
int64_t increment(int64_t delta) { | ||
if (delta > 0) { | ||
mCounter64 += delta; | ||
} | ||
return mCounter64; | ||
} | ||
|
||
/** | ||
* Advance the 64-bit counter if (current32 - previousCurrent32) > 0. | ||
* This can be used to convert a 32-bit counter that may be wrapping into | ||
* a monotonic 64-bit counter. | ||
* | ||
* This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls. | ||
* Think of the wrapping counter like a sine wave. If the frequency of the signal | ||
* is more than half the sampling rate (Nyquist rate) then you cannot measure it properly. | ||
* If the counter wraps around every 24 hours then we should measure it with a period | ||
* of less than 12 hours. | ||
* | ||
* @return current value of the 64-bit counter | ||
*/ | ||
int64_t update32(int32_t counter32) { | ||
int32_t delta = counter32 - mCounter32; | ||
// protect against the mCounter64 going backwards | ||
if (delta > 0) { | ||
mCounter64 += delta; | ||
mCounter32 = counter32; | ||
} | ||
return mCounter64; | ||
} | ||
|
||
/** | ||
* Reset the stored value of the 32-bit counter. | ||
* This is used if your counter32 has been reset to zero. | ||
*/ | ||
void reset32() { | ||
mCounter32 = 0; | ||
} | ||
|
||
/** | ||
* Round 64-bit counter up to a multiple of the period. | ||
* | ||
* The period must be positive. | ||
* | ||
* @param period might be, for example, a buffer capacity | ||
*/ | ||
void roundUp64(int32_t period) { | ||
if (period > 0) { | ||
int64_t numPeriods = (mCounter64 + period - 1) / period; | ||
mCounter64 = numPeriods * period; | ||
} | ||
} | ||
|
||
private: | ||
int64_t mCounter64 = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe should be atomic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Atomicity isn't for free--there are costs on synchronizing CPU caches. Also, in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an std::atomic way to do that or do you just mean to use a mutex? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make a struct and put it into For two counters, it should usually be lock-free, unless it's a 32-bit ARM CPU and the fields alignment is wrong. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mikhail and I talked. The caller is only updating the internal data from one thread at a time. So I will document that this is not thread-safe and require caller to guarantee synchronization. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
int32_t mCounter32 = 0; | ||
}; | ||
|
||
|
||
#endif //COMMON_MONOTONIC_COUNTER_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -191,6 +191,11 @@ Result AudioOutputStreamOpenSLES::requestPause() { | |
result = Result::ErrorInvalidState; // TODO review | ||
} else { | ||
setState(StreamState::Pausing); | ||
// Note that OpenSL ES does NOT reset its millisecond position when OUTPUT is paused. | ||
int64_t framesWritten = getFramesWritten(); | ||
if (framesWritten >= 0) { | ||
setFramesRead(framesWritten); | ||
} | ||
} | ||
return result; | ||
} | ||
|
@@ -210,10 +215,24 @@ Result AudioOutputStreamOpenSLES::requestStop() { | |
result = Result::ErrorInvalidState; // TODO review | ||
} else { | ||
setState(StreamState::Stopping); | ||
mPositionMillis.reset32(); // OpenSL ES resets its millisecond position when stopped. | ||
int64_t framesWritten = getFramesWritten(); | ||
if (framesWritten >= 0) { | ||
setFramesRead(framesWritten); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is supposed to prevent drift but does not seem to be working. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is no longer drifting. |
||
} | ||
} | ||
return result; | ||
} | ||
|
||
void AudioOutputStreamOpenSLES::setFramesRead(int64_t framesRead) { | ||
int64_t millisWritten = framesRead * kMillisPerSecond / getSampleRate(); | ||
mPositionMillis.set(millisWritten); | ||
} | ||
|
||
int64_t AudioOutputStreamOpenSLES::getFramesRead() const { | ||
return getFramesProcessedByServer(); | ||
} | ||
|
||
Result AudioOutputStreamOpenSLES::waitForStateChange(StreamState currentState, | ||
StreamState *nextState, | ||
int64_t timeoutNanoseconds) { | ||
|
@@ -223,3 +242,20 @@ Result AudioOutputStreamOpenSLES::waitForStateChange(StreamState currentState, | |
} | ||
return Result::ErrorUnimplemented; // TODO | ||
} | ||
|
||
Result AudioOutputStreamOpenSLES::updateServiceFrameCounter() { | ||
if (mPlayInterface == NULL) { | ||
return Result::ErrorNull; | ||
} | ||
SLmillisecond msec = 0; | ||
SLresult slResult = (*mPlayInterface)->GetPosition(mPlayInterface, &msec); | ||
Result result = Result::OK; | ||
if(SL_RESULT_SUCCESS != slResult) { | ||
LOGD("%s(): GetPosition() returned %s", __func__, getSLErrStr(slResult)); | ||
// set result based on SLresult | ||
result = Result::ErrorInternal; | ||
} else { | ||
mPositionMillis.update32(msec); | ||
} | ||
return result; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -137,6 +137,7 @@ SLresult AudioStreamOpenSLES::processBufferCallback(SLAndroidSimpleBufferQueueIt | |
LOGE("Oboe callback returned %d", result); | ||
return SL_RESULT_INTERNAL_ERROR; // TODO How should we stop OpenSL ES. | ||
} else { | ||
updateServiceFrameCounter(); | ||
// Pass the data to OpenSLES. | ||
return enqueueCallbackBuffer(bq); | ||
} | ||
|
@@ -169,3 +170,9 @@ SLresult AudioStreamOpenSLES::registerBufferQueueCallback() { | |
int32_t AudioStreamOpenSLES::getFramesPerBurst() { | ||
return mFramesPerBurst; | ||
} | ||
|
||
int64_t AudioStreamOpenSLES::getFramesProcessedByServer() const { | ||
int64_t millis64 = mPositionMillis.get(); | ||
int64_t framesRead = millis64 * getSampleRate() / kMillisPerSecond; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change to framesProcessed |
||
return framesRead; | ||
} |
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.
2018
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 code was written in 2016. I copied it from inside AAudio.