Merge "Add lost frame handling in AudioSource" into gingerbread
This commit is contained in:
@@ -57,11 +57,10 @@ private:
|
|||||||
|
|
||||||
bool mCollectStats;
|
bool mCollectStats;
|
||||||
bool mTrackMaxAmplitude;
|
bool mTrackMaxAmplitude;
|
||||||
int64_t mTotalReadTimeUs;
|
|
||||||
int64_t mTotalReadBytes;
|
|
||||||
int64_t mTotalReads;
|
|
||||||
int64_t mStartTimeUs;
|
int64_t mStartTimeUs;
|
||||||
int16_t mMaxAmplitude;
|
int16_t mMaxAmplitude;
|
||||||
|
int64_t mPrevSampleTimeUs;
|
||||||
|
int64_t mNumLostFrames;
|
||||||
|
|
||||||
MediaBufferGroup *mGroup;
|
MediaBufferGroup *mGroup;
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,7 @@
|
|||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <cutils/properties.h>
|
#include <cutils/properties.h>
|
||||||
#include <sys/time.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
@@ -35,9 +34,8 @@ AudioSource::AudioSource(
|
|||||||
int inputSource, uint32_t sampleRate, uint32_t channels)
|
int inputSource, uint32_t sampleRate, uint32_t channels)
|
||||||
: mStarted(false),
|
: mStarted(false),
|
||||||
mCollectStats(false),
|
mCollectStats(false),
|
||||||
mTotalReadTimeUs(0),
|
mPrevSampleTimeUs(0),
|
||||||
mTotalReadBytes(0),
|
mNumLostFrames(0),
|
||||||
mTotalReads(0),
|
|
||||||
mGroup(NULL) {
|
mGroup(NULL) {
|
||||||
|
|
||||||
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
|
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
|
||||||
@@ -110,10 +108,7 @@ status_t AudioSource::stop() {
|
|||||||
mStarted = false;
|
mStarted = false;
|
||||||
|
|
||||||
if (mCollectStats) {
|
if (mCollectStats) {
|
||||||
LOGI("%lld reads: %.2f bps in %lld us",
|
LOGI("Total lost audio frames: %lld", mNumLostFrames);
|
||||||
mTotalReads,
|
|
||||||
(mTotalReadBytes * 8000000.0) / mTotalReadTimeUs,
|
|
||||||
mTotalReadTimeUs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
@@ -129,67 +124,113 @@ sp<MetaData> AudioSource::getFormat() {
|
|||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns -1 if frame skipping request is too long.
|
||||||
|
* Returns 0 if there is no need to skip frames.
|
||||||
|
* Returns 1 if we need to skip frames.
|
||||||
|
*/
|
||||||
|
static int skipFrame(int64_t timestampUs,
|
||||||
|
const MediaSource::ReadOptions *options) {
|
||||||
|
|
||||||
|
int64_t skipFrameUs;
|
||||||
|
if (!options || !options->getSkipFrame(&skipFrameUs)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipFrameUs <= timestampUs) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe guard against the abuse of the kSkipFrame_Option.
|
||||||
|
if (skipFrameUs - timestampUs >= 1E6) {
|
||||||
|
LOGE("Frame skipping requested is way too long: %lld us",
|
||||||
|
skipFrameUs - timestampUs);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGV("skipFrame: %lld us > timestamp: %lld us",
|
||||||
|
skipFrameUs, timestampUs);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
status_t AudioSource::read(
|
status_t AudioSource::read(
|
||||||
MediaBuffer **out, const ReadOptions *options) {
|
MediaBuffer **out, const ReadOptions *options) {
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
++mTotalReads;
|
|
||||||
|
|
||||||
MediaBuffer *buffer;
|
MediaBuffer *buffer;
|
||||||
CHECK_EQ(mGroup->acquire_buffer(&buffer), OK);
|
CHECK_EQ(mGroup->acquire_buffer(&buffer), OK);
|
||||||
|
|
||||||
|
int err = 0;
|
||||||
while (mStarted) {
|
while (mStarted) {
|
||||||
|
|
||||||
uint32_t numFramesRecorded;
|
uint32_t numFramesRecorded;
|
||||||
mRecord->getPosition(&numFramesRecorded);
|
mRecord->getPosition(&numFramesRecorded);
|
||||||
int64_t latency = mRecord->latency() * 1000;
|
|
||||||
|
|
||||||
int64_t readTime = systemTime() / 1000;
|
|
||||||
|
|
||||||
if (numFramesRecorded == 0) {
|
if (numFramesRecorded == 0 && mPrevSampleTimeUs == 0) {
|
||||||
// Initial delay
|
// Initial delay
|
||||||
if (mStartTimeUs > 0) {
|
if (mStartTimeUs > 0) {
|
||||||
mStartTimeUs = readTime - mStartTimeUs;
|
mStartTimeUs = systemTime() / 1000 - mStartTimeUs;
|
||||||
} else {
|
} else {
|
||||||
mStartTimeUs += latency;
|
// Assume latency is constant.
|
||||||
|
mStartTimeUs += mRecord->latency() * 1000;
|
||||||
}
|
}
|
||||||
}
|
mPrevSampleTimeUs = mStartTimeUs;
|
||||||
|
|
||||||
ssize_t n = 0;
|
|
||||||
if (mCollectStats) {
|
|
||||||
n = mRecord->read(buffer->data(), buffer->size());
|
|
||||||
int64_t endTime = systemTime() / 1000;
|
|
||||||
mTotalReadTimeUs += (endTime - readTime);
|
|
||||||
if (n >= 0) {
|
|
||||||
mTotalReadBytes += n;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n = mRecord->read(buffer->data(), buffer->size());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n < 0) {
|
|
||||||
buffer->release();
|
|
||||||
buffer = NULL;
|
|
||||||
|
|
||||||
return (status_t)n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sampleRate = mRecord->getSampleRate();
|
uint32_t sampleRate = mRecord->getSampleRate();
|
||||||
int64_t timestampUs = (1000000LL * numFramesRecorded) / sampleRate +
|
|
||||||
mStartTimeUs;
|
|
||||||
int64_t skipFrameUs;
|
|
||||||
if (!options || !options->getSkipFrame(&skipFrameUs)) {
|
|
||||||
skipFrameUs = timestampUs; // Don't skip frame
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipFrameUs > timestampUs) {
|
// Insert null frames when lost frames are detected.
|
||||||
// Safe guard against the abuse of the kSkipFrame_Option.
|
int64_t timestampUs = mPrevSampleTimeUs;
|
||||||
if (skipFrameUs - timestampUs >= 1E6) {
|
uint32_t numLostBytes = mRecord->getInputFramesLost() << 1;
|
||||||
LOGE("Frame skipping requested is way too long: %lld us",
|
#if 0
|
||||||
skipFrameUs - timestampUs);
|
// Simulate lost frames
|
||||||
|
numLostBytes = ((rand() * 1.0 / RAND_MAX)) * kMaxBufferSize;
|
||||||
|
numLostBytes &= 0xFFFFFFFE; // Alignment request
|
||||||
|
|
||||||
|
// Reduce the chance to lose
|
||||||
|
if (rand() * 1.0 / RAND_MAX >= 0.05) {
|
||||||
|
numLostBytes = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (numLostBytes > 0) {
|
||||||
|
// Not expect too many lost frames!
|
||||||
|
CHECK(numLostBytes <= kMaxBufferSize);
|
||||||
|
|
||||||
|
timestampUs += (1000000LL * numLostBytes >> 1) / sampleRate;
|
||||||
|
CHECK(timestampUs > mPrevSampleTimeUs);
|
||||||
|
if (mCollectStats) {
|
||||||
|
mNumLostFrames += (numLostBytes >> 1);
|
||||||
|
}
|
||||||
|
if ((err = skipFrame(timestampUs, options)) == -1) {
|
||||||
buffer->release();
|
buffer->release();
|
||||||
return UNKNOWN_ERROR;
|
return UNKNOWN_ERROR;
|
||||||
|
} else if (err != 0) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
LOGV("skipFrame: %lld us > timestamp: %lld us, samples %d",
|
memset(buffer->data(), 0, numLostBytes);
|
||||||
skipFrameUs, timestampUs, numFramesRecorded);
|
buffer->set_range(0, numLostBytes);
|
||||||
|
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
|
||||||
|
mPrevSampleTimeUs = timestampUs;
|
||||||
|
*out = buffer;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t n = mRecord->read(buffer->data(), buffer->size());
|
||||||
|
if (n < 0) {
|
||||||
|
buffer->release();
|
||||||
|
return (status_t)n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t recordDurationUs = (1000000LL * n >> 1) / sampleRate;
|
||||||
|
timestampUs += recordDurationUs;
|
||||||
|
if ((err = skipFrame(timestampUs, options)) == -1) {
|
||||||
|
buffer->release();
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
} else if (err != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +238,13 @@ status_t AudioSource::read(
|
|||||||
trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
|
trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->meta_data()->setInt64(kKeyTime, timestampUs);
|
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
|
||||||
|
CHECK(timestampUs > mPrevSampleTimeUs);
|
||||||
|
if (mNumLostFrames == 0) {
|
||||||
|
CHECK_EQ(mPrevSampleTimeUs,
|
||||||
|
mStartTimeUs + (1000000LL * numFramesRecorded) / sampleRate);
|
||||||
|
}
|
||||||
|
mPrevSampleTimeUs = timestampUs;
|
||||||
LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld",
|
LOGV("initial delay: %lld, sample rate: %d, timestamp: %lld",
|
||||||
mStartTimeUs, sampleRate, timestampUs);
|
mStartTimeUs, sampleRate, timestampUs);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user