nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, uint32_t& aReadCount, uint64_t& aFrameTime, uint32_t& aFrameSize) { MonitorAutoLock monitor(mMonitor); RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d " "mBufferSlotData[mConsumerIdx].mLength %d" ,mTrackIdx ,mProducerIdx ,mConsumerIdx ,mBufferSlotData[mConsumerIdx].mLength); // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID. // The loop ends when // 1. Read data successfully // 2. Fail to read data due to aToBuffer's space // 3. No data in this buffer // 4. mIsStarted is not set while (1) { if (mBufferSlotData[mConsumerIdx].mLength > 0) { // Check the aToBuffer space is enough for data copy. if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) { aFrameSize = mBufferSlotData[mConsumerIdx].mLength; break; } uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1; // we have data, copy to aToBuffer MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <= (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize)); memcpy(aToBuffer, (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]), mBufferSlotData[mConsumerIdx].mLength); aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength; aFrameTime = mBufferSlotData[mConsumerIdx].mTime; RTSPMLOG("DataLength %d, data time %lld" ,mBufferSlotData[mConsumerIdx].mLength ,mBufferSlotData[mConsumerIdx].mTime); // After reading the data, we set current index of mBufferSlotDataLength // to BUFFER_SLOT_EMPTY to indicate these slots are free. for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) { mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; } mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM; break; } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) { mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM; RTSPMLOG("BUFFER_SLOT_INVALID move forward"); } else { // No data, and disconnected. if (!mIsStarted) { return NS_ERROR_FAILURE; } // No data, the decode thread is blocked here until we receive // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer() // to wake up the decode thread. RTSPMLOG("monitor.Wait()"); monitor.Wait(); } } return NS_OK; }
nsresult RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason) { NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { mTrackBuffer[i]->Stop(); mTrackBuffer[i]->Reset(); } if (mDecoder) { if (aReason == NS_ERROR_NOT_INITIALIZED || aReason == NS_ERROR_CONNECTION_REFUSED || aReason == NS_ERROR_NOT_CONNECTED || aReason == NS_ERROR_NET_TIMEOUT) { // Report error code to Decoder. RTSPMLOG("Error in OnDisconnected 0x%x", aReason); mDecoder->NetworkError(); } else { // Resetting the decoder and media element when the connection // between RTSP client and server goes down. mDecoder->ResetConnectionState(); } } if (mListener) { // Note: Listener's Revoke() kills its reference to us, which means it would // release |this| object. So, ensure it is called in the end of this method. mListener->Revoke(); } return NS_OK; }
nsresult RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason) { NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { mTrackBuffer[i]->Stop(); mTrackBuffer[i]->Reset(); } // If mDecoder is null pointer, it means this OnDisconnected event is // triggered when media element was destroyed and mDecoder was already // shutdown. if (!mDecoder) { return NS_OK; } if (aReason == NS_ERROR_NOT_INITIALIZED || aReason == NS_ERROR_CONNECTION_REFUSED || aReason == NS_ERROR_NOT_CONNECTED) { RTSPMLOG("Error in OnDisconnected 0x%x", aReason); mDecoder->NetworkError(); return NS_OK; } // Resetting the decoder and media element when the connection // between Rtsp client and server goes down. mDecoder->ResetConnectionState(); return NS_OK; }
RtspMediaResource::~RtspMediaResource() { RTSPMLOG("~RtspMediaResource"); if (mListener) { // Kill its reference to us since we're going away mListener->Revoke(); } }
void RtspMediaResource::NotifySuspend(bool aIsSuspend) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); RTSPMLOG("NotifySuspend %d",aIsSuspend); mIsSuspend = aIsSuspend; if (mDecoder) { mDecoder->NotifySuspendedStatusChanged(); } }
void RtspMediaResource::SetSuspend(bool aIsSuspend) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); RTSPMLOG("SetSuspend %d",aIsSuspend); nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethodWithArg<bool>(this, &RtspMediaResource::NotifySuspend, aIsSuspend); NS_DispatchToMainThread(runnable); }
nsresult RtspMediaResource::SeekTime(int64_t aOffset) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]", aOffset, mDecoder); // Clear buffer and raise the frametype flag. for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY); } return mMediaStreamController->Seek(aOffset); }
nsresult RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize, uint32_t aTrackIdx, uint32_t& aBytes, uint64_t& aTime, uint32_t& aFrameSize) { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(), "ReadTrack index > mTrackBuffer"); MOZ_ASSERT(aBuffer); if (!mIsConnected) { RTSPMLOG("ReadFrameFromTrack: RTSP not connected"); return NS_ERROR_FAILURE; } return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes, aTime, aFrameSize); }
/* When we perform a WriteBuffer, we check mIsStarted and aFrameType first. * These flags prevent "garbage" frames from being written into the buffer. * * After writing the data into the buffer, we check to see if we wrote over a * slot, and update mConsumerIdx if necessary. * This ensures that the decoder will get the "oldest" data available in the * buffer. * * If the incoming data is larger than one slot size (isMultipleSlots), we do * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the * first slot, in order to notify the reader that some slots are unavailable. * * If the incoming data is isMultipleSlots and crosses the end of * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to * be written from head(index 0). * * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking. * */ void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, uint64_t aFrameTime, uint32_t aFrameType) { MonitorAutoLock monitor(mMonitor); if (!mIsStarted) { RTSPMLOG("mIsStarted is false"); return; } if (mTotalBufferSize < aWriteCount) { RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large"); return; } // Checking the incoming data's frame type. // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType // imply the RtspTrackBuffer is ready for receive data. if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY); RTSPMLOG("Clear mFrameType"); return; } // Checking current buffer frame type. // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the // RtspTrackBuffer can't receive data now. So we drop the frame until we // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY. if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { RTSPMLOG("Return because the mFrameType is set"); return; } // 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 bool returnToHead = false; // Calculate how many slots the incoming data needed. int32_t slots = 1; int32_t i; RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d", mTrackIdx, mProducerIdx,mConsumerIdx); if (aWriteCount > mSlotSize) { isMultipleSlots = true; slots = (aWriteCount / mSlotSize) + 1; } if (isMultipleSlots && (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) { returnToHead = true; } RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d", slots, isMultipleSlots, returnToHead); if (returnToHead) { // Clear the rest index of mBufferSlotData[].mLength for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) { mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; } // We wrote one or more slots that the decode thread has not yet read. // So the mConsumerIdx returns to the head of slot buffer and moves forward // to the oldest slot. if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) { mConsumerIdx = 0; for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) { if (mBufferSlotData[i].mLength > 0) { mConsumerIdx = i; break; } } } mProducerIdx = 0; } if (!(aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM)) { memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount); } if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots && mBufferSlotData[mConsumerIdx].mLength > 0) { // Wrote one or more slots that the decode thread has not yet read. RTSPMLOG("overwrite!! %d time %lld" ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime); if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) { mBufferSlotData[mProducerIdx].mLength = 0; mBufferSlotData[mProducerIdx].mTime = 0; } else { mBufferSlotData[mProducerIdx].mLength = aWriteCount; mBufferSlotData[mProducerIdx].mTime = aFrameTime; } mBufferSlotData[mProducerIdx].mFrameType = aFrameType; // Clear the mBufferSlotDataLength except the start slot. if (isMultipleSlots) { for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; } } mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; // Move the mConsumerIdx forward to ensure that the decoder reads the // oldest data available. mConsumerIdx = mProducerIdx; } else { // Normal case, the writer doesn't take over the reader. if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) { mBufferSlotData[mProducerIdx].mLength = 0; mBufferSlotData[mProducerIdx].mTime = 0; } else { mBufferSlotData[mProducerIdx].mLength = aWriteCount; mBufferSlotData[mProducerIdx].mTime = aFrameTime; } mBufferSlotData[mProducerIdx].mFrameType = aFrameType; // Clear the mBufferSlotData[].mLength except the start slot. if (isMultipleSlots) { for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; } } mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; } mMonitor.NotifyAll(); }