Bug 1056187 - Add playout delay to RtspTrackBuffer. r=bechen

This commit is contained in:
Ethan Tseng 2014-10-07 16:44:58 +08:00
Родитель 0836f2451a
Коммит 495a65362a
4 изменённых файлов: 164 добавлений и 5 удалений

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

@ -53,6 +53,14 @@ struct BufferSlotData {
int32_t mFrameType;
};
// This constant is used to determine if the buffer usage is over a threshold.
const float kBufferThresholdPerc = 0.8f;
// The default value of playout delay duration.
const uint32_t kPlayoutDelayMs = 3000;
//-----------------------------------------------------------------------------
// RtspTrackBuffer
//-----------------------------------------------------------------------------
class RtspTrackBuffer
{
public:
@ -61,11 +69,12 @@ public:
, mSlotSize(aSlotSize)
, mTotalBufferSize(BUFFER_SLOT_NUM * mSlotSize)
, mFrameType(0)
, mIsStarted(false) {
, mIsStarted(false)
, mDuringPlayoutDelay(false)
, mPlayoutDelayMs(kPlayoutDelayMs)
, mPlayoutDelayTimer(nullptr) {
MOZ_COUNT_CTOR(RtspTrackBuffer);
#ifdef PR_LOGGING
mTrackIdx = aTrackIdx;
#endif
MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM);
mRingBuffer = new uint8_t[mTotalBufferSize];
Reset();
@ -93,6 +102,7 @@ public:
void Stop() {
MonitorAutoLock monitor(mMonitor);
mIsStarted = false;
StopPlayoutDelay();
}
// Read the data from mRingBuffer[mConsumerIdx*mSlotSize] into aToBuffer.
@ -118,6 +128,34 @@ public:
Reset();
}
// When RtspTrackBuffer is in playout delay duration, it should suspend
// reading data from the buffer until the playout-delay-ended event occurs,
// which wil be trigger by mPlayoutDelayTimer.
void StartPlayoutDelay() {
mDuringPlayoutDelay = true;
}
void LockStartPlayoutDelay() {
MonitorAutoLock monitor(mMonitor);
StartPlayoutDelay();
}
// If the playout delay is stopped, mPlayoutDelayTimer should be canceled.
void StopPlayoutDelay() {
if (mPlayoutDelayTimer) {
mPlayoutDelayTimer->Cancel();
mPlayoutDelayTimer = nullptr;
}
mDuringPlayoutDelay = false;
}
void LockStopPlayoutDelay() {
MonitorAutoLock monitor(mMonitor);
StopPlayoutDelay();
}
bool IsBufferOverThreshold();
void CreatePlayoutDelayTimer(unsigned long delayMs);
static void PlayoutDelayTimerCallback(nsITimer *aTimer, void *aClosure);
private:
// The FrameType is sync to nsIStreamingProtocolController.h
void SetFrameType(uint32_t aFrameType) {
@ -127,10 +165,8 @@ private:
// A monitor lock to prevent racing condition.
Monitor mMonitor;
#ifdef PR_LOGGING
// Indicate the track number for Rtsp.
int32_t mTrackIdx;
#endif
// mProducerIdx: A slot index that we store data from
// nsIStreamingProtocolController.
// mConsumerIdx: A slot index that we read when decoder need(from OMX decoder).
@ -159,6 +195,13 @@ private:
// Set true/false when |Start()/Stop()| is called.
bool mIsStarted;
// Indicate the buffer is in playout delay duration or not.
bool mDuringPlayoutDelay;
// Playout delay duration defined in milliseconds.
uint32_t mPlayoutDelayMs;
// Timer used to fire playout-delay-ended event.
nsCOMPtr<nsITimer> mPlayoutDelayTimer;
};
nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize,
@ -177,9 +220,16 @@ nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize,
// 3. No data in this buffer
// 4. mIsStarted is not set
while (1) {
// Do not read from buffer if we are still in the playout delay duration.
if (mDuringPlayoutDelay) {
monitor.Wait();
continue;
}
if (mBufferSlotData[mConsumerIdx].mFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) {
return NS_BASE_STREAM_CLOSED;
}
if (mBufferSlotData[mConsumerIdx].mLength > 0) {
// Check the aToBuffer space is enough for data copy.
if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) {
@ -271,6 +321,12 @@ void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
RTSPMLOG("Return because the mFrameType is set");
return;
}
// Create a timer to delay ReadBuffer() for a duration.
if (mDuringPlayoutDelay && !mPlayoutDelayTimer) {
CreatePlayoutDelayTimer(mPlayoutDelayMs);
}
// The flag is true if the incoming data is larger than one slot size.
bool isMultipleSlots = false;
// The flag is true if the incoming data is larger than remainder free slots
@ -314,6 +370,12 @@ void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount);
}
// If the buffer is almost full, stop the playout delay to let ReadBuffer()
// consume data in the buffer.
if (mDuringPlayoutDelay && IsBufferOverThreshold()) {
StopPlayoutDelay();
}
if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots
&& mBufferSlotData[mConsumerIdx].mLength > 0) {
// Wrote one or more slots that the decode thread has not yet read.
@ -322,6 +384,7 @@ void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) {
mBufferSlotData[mProducerIdx].mLength = 0;
mBufferSlotData[mProducerIdx].mTime = 0;
StopPlayoutDelay();
} else {
mBufferSlotData[mProducerIdx].mLength = aWriteCount;
mBufferSlotData[mProducerIdx].mTime = aFrameTime;
@ -342,6 +405,7 @@ void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) {
mBufferSlotData[mProducerIdx].mLength = 0;
mBufferSlotData[mProducerIdx].mTime = 0;
StopPlayoutDelay();
} else {
mBufferSlotData[mProducerIdx].mLength = aWriteCount;
mBufferSlotData[mProducerIdx].mTime = aFrameTime;
@ -368,9 +432,58 @@ void RtspTrackBuffer::Reset() {
mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY;
mBufferSlotData[i].mFrameType = MEDIASTREAM_FRAMETYPE_NORMAL;
}
StopPlayoutDelay();
mMonitor.NotifyAll();
}
bool
RtspTrackBuffer::IsBufferOverThreshold()
{
static int32_t numSlotsThreshold =
BUFFER_SLOT_NUM * kBufferThresholdPerc;
int32_t numSlotsUsed = mProducerIdx - mConsumerIdx;
if (numSlotsUsed < 0) { // wrap-around
numSlotsUsed = (BUFFER_SLOT_NUM - mConsumerIdx) + mProducerIdx;
}
if (numSlotsUsed > numSlotsThreshold) {
return true;
}
return false;
}
void
RtspTrackBuffer::CreatePlayoutDelayTimer(unsigned long delayMs)
{
if (delayMs <= 0) {
return;
}
mPlayoutDelayTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mPlayoutDelayTimer) {
mPlayoutDelayTimer->InitWithFuncCallback(PlayoutDelayTimerCallback,
this, delayMs,
nsITimer::TYPE_ONE_SHOT);
}
}
// static
void
RtspTrackBuffer::PlayoutDelayTimerCallback(nsITimer *aTimer,
void *aClosure)
{
MOZ_ASSERT(aTimer);
MOZ_ASSERT(aClosure);
RtspTrackBuffer *self = static_cast<RtspTrackBuffer*>(aClosure);
MonitorAutoLock lock(self->mMonitor);
self->StopPlayoutDelay();
lock.NotifyAll();
}
//-----------------------------------------------------------------------------
// RtspMediaResource
//-----------------------------------------------------------------------------
RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder,
nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType)
: BaseMediaResource(aDecoder, aChannel, aURI, aContentType)
@ -758,5 +871,21 @@ nsresult RtspMediaResource::SeekTime(int64_t aOffset)
return mMediaStreamController->Seek(aOffset);
}
void
RtspMediaResource::EnablePlayoutDelay()
{
for (uint32_t i = 0; i < mTrackBuffer.Length(); ++i) {
mTrackBuffer[i]->LockStartPlayoutDelay();
}
}
void
RtspMediaResource::DisablePlayoutDelay()
{
for (uint32_t i = 0; i < mTrackBuffer.Length(); ++i) {
mTrackBuffer[i]->LockStopPlayoutDelay();
}
}
} // namespace mozilla

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

@ -7,6 +7,8 @@
#define RtspMediaResource_h_
#include "MediaResource.h"
#include "mozilla/Monitor.h"
#include "nsITimer.h"
namespace mozilla {
@ -112,6 +114,12 @@ public:
// Seek to the given time offset
nsresult SeekTime(int64_t aOffset);
// The idea of playout delay is to hold frames in the playout buffer
// (RtspTrackBuffer) for a period of time in order to smooth timing variations
// caused by the network.
void EnablePlayoutDelay();
void DisablePlayoutDelay();
// dummy
virtual nsresult ReadAt(int64_t aOffset, char* aBuffer,
uint32_t aCount, uint32_t* aBytes) MOZ_OVERRIDE{

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

@ -41,6 +41,7 @@ nsresult RtspOmxReader::Seek(int64_t aTime, int64_t aStartTime,
// RtspMediaResource.
if (mRtspResource) {
mRtspResource->SeekTime(aTime);
mRtspResource->EnablePlayoutDelay();
}
// Call |MediaOmxReader::Seek| to notify the OMX decoder we are performing a
@ -80,4 +81,22 @@ void RtspOmxReader::EnsureActive() {
MediaOmxReader::EnsureActive();
}
nsresult RtspOmxReader::ReadMetadata(MediaInfo *aInfo, MetadataTags **aTags)
{
// Send a PLAY command to the RTSP server before reading metadata.
// Because we might need some decoded samples to ensure we have configuration.
mRtspResource->DisablePlayoutDelay();
EnsureActive();
nsresult rv = MediaOmxReader::ReadMetadata(aInfo, aTags);
if (rv == NS_OK && !IsWaitingMediaResources()) {
mRtspResource->EnablePlayoutDelay();
} else if (IsWaitingMediaResources()) {
// Send a PAUSE to the RTSP server because the underlying media resource is
// not ready.
SetIdle();
}
return rv;
}
} // namespace mozilla

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

@ -65,6 +65,9 @@ public:
virtual void SetIdle() MOZ_OVERRIDE;
virtual nsresult ReadMetadata(MediaInfo *aInfo, MetadataTags **aTags)
MOZ_FINAL MOZ_OVERRIDE;
private:
// A pointer to RtspMediaResource for calling the Rtsp specific function.
// The lifetime of mRtspResource is controlled by MediaDecoder. MediaDecoder