michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "RtspMediaResource.h" michael@0: michael@0: #include "MediaDecoder.h" michael@0: #include "mozilla/dom/HTMLMediaElement.h" michael@0: #include "mozilla/Monitor.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIStreamingProtocolService.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #ifdef NECKO_PROTOCOL_rtsp michael@0: #include "mozilla/net/RtspChannelChild.h" michael@0: #endif michael@0: using namespace mozilla::net; michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo* gRtspMediaResourceLog; michael@0: #define RTSP_LOG(msg, ...) PR_LOG(gRtspMediaResourceLog, PR_LOG_DEBUG, \ michael@0: (msg, ##__VA_ARGS__)) michael@0: // Debug logging macro with object pointer and class name. michael@0: #define RTSPMLOG(msg, ...) \ michael@0: RTSP_LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__) michael@0: #else michael@0: #define RTSP_LOG(msg, ...) michael@0: #define RTSPMLOG(msg, ...) michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: /* class RtspTrackBuffer: a ring buffer implementation for audio/video track michael@0: * un-decoded data. michael@0: * The ring buffer is divided into BUFFER_SLOT_NUM slots, michael@0: * and each slot's size is fixed(mSlotSize). michael@0: * Even though the ring buffer is divided into fixed size slots, it still can michael@0: * store the data which size is larger than one slot size. michael@0: * */ michael@0: #define BUFFER_SLOT_NUM 8192 michael@0: #define BUFFER_SLOT_DEFAULT_SIZE 256 michael@0: #define BUFFER_SLOT_MAX_SIZE 512 michael@0: #define BUFFER_SLOT_INVALID -1 michael@0: #define BUFFER_SLOT_EMPTY 0 michael@0: michael@0: struct BufferSlotData { michael@0: int32_t mLength; michael@0: uint64_t mTime; michael@0: }; michael@0: michael@0: class RtspTrackBuffer michael@0: { michael@0: public: michael@0: RtspTrackBuffer(const char *aMonitor, int32_t aTrackIdx, uint32_t aSlotSize) michael@0: : mMonitor(aMonitor) michael@0: , mSlotSize(aSlotSize) michael@0: , mTotalBufferSize(BUFFER_SLOT_NUM * mSlotSize) michael@0: , mFrameType(0) michael@0: , mIsStarted(false) { michael@0: MOZ_COUNT_CTOR(RtspTrackBuffer); michael@0: #ifdef PR_LOGGING michael@0: mTrackIdx = aTrackIdx; michael@0: #endif michael@0: MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM); michael@0: mRingBuffer = new uint8_t[mTotalBufferSize]; michael@0: Reset(); michael@0: }; michael@0: ~RtspTrackBuffer() { michael@0: MOZ_COUNT_DTOR(RtspTrackBuffer); michael@0: mRingBuffer = nullptr; michael@0: }; michael@0: michael@0: size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { michael@0: // including this michael@0: size_t size = aMallocSizeOf(this); michael@0: michael@0: // excluding this michael@0: size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: return size; michael@0: } michael@0: michael@0: void Start() { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: mIsStarted = true; michael@0: mFrameType = 0; michael@0: } michael@0: void Stop() { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: mIsStarted = false; michael@0: } michael@0: michael@0: // Read the data from mRingBuffer[mConsumerIdx*mSlotSize] into aToBuffer. michael@0: // If the aToBufferSize is smaller than mBufferSlotDataLength[mConsumerIdx], michael@0: // early return and set the aFrameSize to notify the reader the aToBuffer michael@0: // doesn't have enough space. The reader must realloc the aToBuffer if it michael@0: // wishes to read the data. michael@0: nsresult ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, michael@0: uint32_t& aReadCount, uint64_t& aFrameTime, michael@0: uint32_t& aFrameSize); michael@0: // Write the data from aFromBuffer into mRingBuffer[mProducerIdx*mSlotSize]. michael@0: void WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, michael@0: uint64_t aFrameTime, uint32_t aFrameType); michael@0: // Reset the mProducerIdx, mConsumerIdx, mBufferSlotDataLength[], michael@0: // mBufferSlotDataTime[]. michael@0: void Reset(); michael@0: michael@0: // We should call SetFrameType first then reset(). michael@0: // If we call reset() first, the queue may still has some "garbage" frame michael@0: // from another thread's |OnMediaDataAvailable| before |SetFrameType|. michael@0: void ResetWithFrameType(uint32_t aFrameType) { michael@0: SetFrameType(aFrameType); michael@0: Reset(); michael@0: } michael@0: michael@0: private: michael@0: // The FrameType is sync to nsIStreamingProtocolController.h michael@0: void SetFrameType(uint32_t aFrameType) { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: mFrameType = mFrameType | aFrameType; michael@0: } michael@0: michael@0: // A monitor lock to prevent racing condition. michael@0: Monitor mMonitor; michael@0: #ifdef PR_LOGGING michael@0: // Indicate the track number for Rtsp. michael@0: int32_t mTrackIdx; michael@0: #endif michael@0: // mProducerIdx: A slot index that we store data from michael@0: // nsIStreamingProtocolController. michael@0: // mConsumerIdx: A slot index that we read when decoder need(from OMX decoder). michael@0: int32_t mProducerIdx; michael@0: int32_t mConsumerIdx; michael@0: michael@0: // Because each slot's size is fixed, we need an array to record the real michael@0: // data length and data time stamp. michael@0: // The value in mBufferSlotData[index].mLength represents: michael@0: // -1(BUFFER_SLOT_INVALID): The index of slot data is invalid, mConsumerIdx michael@0: // should go forward. michael@0: // 0(BUFFER_SLOT_EMPTY): The index slot is empty. mConsumerIdx should wait here. michael@0: // positive value: The index slot contains valid data and the value is data size. michael@0: BufferSlotData mBufferSlotData[BUFFER_SLOT_NUM]; michael@0: michael@0: // The ring buffer pointer. michael@0: nsAutoArrayPtr mRingBuffer; michael@0: // Each slot's size. michael@0: uint32_t mSlotSize; michael@0: // Total mRingBuffer's total size. michael@0: uint32_t mTotalBufferSize; michael@0: // A flag that that indicate the incoming data should be dropped or stored. michael@0: // When we are seeking, the incoming data should be dropped. michael@0: // Bit definition in |nsIStreamingProtocolController.h| michael@0: uint32_t mFrameType; michael@0: michael@0: // Set true/false when |Start()/Stop()| is called. michael@0: bool mIsStarted; michael@0: }; michael@0: michael@0: nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, michael@0: uint32_t& aReadCount, uint64_t& aFrameTime, michael@0: uint32_t& aFrameSize) michael@0: { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d " michael@0: "mBufferSlotData[mConsumerIdx].mLength %d" michael@0: ,mTrackIdx ,mProducerIdx ,mConsumerIdx michael@0: ,mBufferSlotData[mConsumerIdx].mLength); michael@0: // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID. michael@0: // The loop ends when michael@0: // 1. Read data successfully michael@0: // 2. Fail to read data due to aToBuffer's space michael@0: // 3. No data in this buffer michael@0: // 4. mIsStarted is not set michael@0: while (1) { michael@0: if (mBufferSlotData[mConsumerIdx].mLength > 0) { michael@0: // Check the aToBuffer space is enough for data copy. michael@0: if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) { michael@0: aFrameSize = mBufferSlotData[mConsumerIdx].mLength; michael@0: break; michael@0: } michael@0: uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1; michael@0: // we have data, copy to aToBuffer michael@0: MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <= michael@0: (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize)); michael@0: memcpy(aToBuffer, michael@0: (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]), michael@0: mBufferSlotData[mConsumerIdx].mLength); michael@0: michael@0: aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength; michael@0: aFrameTime = mBufferSlotData[mConsumerIdx].mTime; michael@0: RTSPMLOG("DataLength %d, data time %lld" michael@0: ,mBufferSlotData[mConsumerIdx].mLength michael@0: ,mBufferSlotData[mConsumerIdx].mTime); michael@0: // After reading the data, we set current index of mBufferSlotDataLength michael@0: // to BUFFER_SLOT_EMPTY to indicate these slots are free. michael@0: for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) { michael@0: mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; michael@0: mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; michael@0: } michael@0: mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM; michael@0: break; michael@0: } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) { michael@0: mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM; michael@0: RTSPMLOG("BUFFER_SLOT_INVALID move forward"); michael@0: } else { michael@0: // No data, and disconnected. michael@0: if (!mIsStarted) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: // No data, the decode thread is blocked here until we receive michael@0: // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer() michael@0: // to wake up the decode thread. michael@0: RTSPMLOG("monitor.Wait()"); michael@0: monitor.Wait(); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* When we perform a WriteBuffer, we check mIsStarted and aFrameType first. michael@0: * These flags prevent "garbage" frames from being written into the buffer. michael@0: * michael@0: * After writing the data into the buffer, we check to see if we wrote over a michael@0: * slot, and update mConsumerIdx if necessary. michael@0: * This ensures that the decoder will get the "oldest" data available in the michael@0: * buffer. michael@0: * michael@0: * If the incoming data is larger than one slot size (isMultipleSlots), we do michael@0: * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the michael@0: * first slot, in order to notify the reader that some slots are unavailable. michael@0: * michael@0: * If the incoming data is isMultipleSlots and crosses the end of michael@0: * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to michael@0: * be written from head(index 0). michael@0: * michael@0: * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking. michael@0: * */ michael@0: void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, michael@0: uint64_t aFrameTime, uint32_t aFrameType) michael@0: { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: if (!mIsStarted) { michael@0: RTSPMLOG("mIsStarted is false"); michael@0: return; michael@0: } michael@0: if (mTotalBufferSize < aWriteCount) { michael@0: RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large"); michael@0: return; michael@0: } michael@0: // Checking the incoming data's frame type. michael@0: // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType michael@0: // imply the RtspTrackBuffer is ready for receive data. michael@0: if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { michael@0: mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY); michael@0: RTSPMLOG("Clear mFrameType"); michael@0: return; michael@0: } michael@0: // Checking current buffer frame type. michael@0: // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the michael@0: // RtspTrackBuffer can't receive data now. So we drop the frame until we michael@0: // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY. michael@0: if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { michael@0: RTSPMLOG("Return because the mFrameType is set"); michael@0: return; michael@0: } michael@0: // The flag is true if the incoming data is larger than one slot size. michael@0: bool isMultipleSlots = false; michael@0: // The flag is true if the incoming data is larger than remainder free slots michael@0: bool returnToHead = false; michael@0: // Calculate how many slots the incoming data needed. michael@0: int32_t slots = 1; michael@0: int32_t i; michael@0: RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d", michael@0: mTrackIdx, mProducerIdx,mConsumerIdx); michael@0: if (aWriteCount > mSlotSize) { michael@0: isMultipleSlots = true; michael@0: slots = (aWriteCount / mSlotSize) + 1; michael@0: } michael@0: if (isMultipleSlots && michael@0: (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) { michael@0: returnToHead = true; michael@0: } michael@0: RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d", michael@0: slots, isMultipleSlots, returnToHead); michael@0: if (returnToHead) { michael@0: // Clear the rest index of mBufferSlotData[].mLength michael@0: for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) { michael@0: mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; michael@0: } michael@0: // We wrote one or more slots that the decode thread has not yet read. michael@0: // So the mConsumerIdx returns to the head of slot buffer and moves forward michael@0: // to the oldest slot. michael@0: if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) { michael@0: mConsumerIdx = 0; michael@0: for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) { michael@0: if (mBufferSlotData[i].mLength > 0) { michael@0: mConsumerIdx = i; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: mProducerIdx = 0; michael@0: } michael@0: michael@0: memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount); michael@0: michael@0: if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots michael@0: && mBufferSlotData[mConsumerIdx].mLength > 0) { michael@0: // Wrote one or more slots that the decode thread has not yet read. michael@0: RTSPMLOG("overwrite!! %d time %lld" michael@0: ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime); michael@0: mBufferSlotData[mProducerIdx].mLength = aWriteCount; michael@0: mBufferSlotData[mProducerIdx].mTime = aFrameTime; michael@0: // Clear the mBufferSlotDataLength except the start slot. michael@0: if (isMultipleSlots) { michael@0: for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { michael@0: mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; michael@0: } michael@0: } michael@0: mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; michael@0: // Move the mConsumerIdx forward to ensure that the decoder reads the michael@0: // oldest data available. michael@0: mConsumerIdx = mProducerIdx; michael@0: } else { michael@0: // Normal case, the writer doesn't take over the reader. michael@0: mBufferSlotData[mProducerIdx].mLength = aWriteCount; michael@0: mBufferSlotData[mProducerIdx].mTime = aFrameTime; michael@0: // Clear the mBufferSlotData[].mLength except the start slot. michael@0: if (isMultipleSlots) { michael@0: for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { michael@0: mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; michael@0: } michael@0: } michael@0: mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; michael@0: } michael@0: michael@0: mMonitor.NotifyAll(); michael@0: } michael@0: michael@0: void RtspTrackBuffer::Reset() { michael@0: MonitorAutoLock monitor(mMonitor); michael@0: mProducerIdx = 0; michael@0: mConsumerIdx = 0; michael@0: for (uint32_t i = 0; i < BUFFER_SLOT_NUM; ++i) { michael@0: mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; michael@0: mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; michael@0: } michael@0: mMonitor.NotifyAll(); michael@0: } michael@0: michael@0: RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder, michael@0: nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType) michael@0: : BaseMediaResource(aDecoder, aChannel, aURI, aContentType) michael@0: , mIsConnected(false) michael@0: , mRealTime(false) michael@0: { michael@0: #ifndef NECKO_PROTOCOL_rtsp michael@0: MOZ_CRASH("Should not be called except for B2G platform"); michael@0: #else michael@0: MOZ_ASSERT(aChannel); michael@0: mMediaStreamController = michael@0: static_cast(aChannel)->GetController(); michael@0: MOZ_ASSERT(mMediaStreamController); michael@0: mListener = new Listener(this); michael@0: mMediaStreamController->AsyncOpen(mListener); michael@0: #ifdef PR_LOGGING michael@0: if (!gRtspMediaResourceLog) { michael@0: gRtspMediaResourceLog = PR_NewLogModule("RtspMediaResource"); michael@0: } michael@0: #endif michael@0: #endif michael@0: } michael@0: michael@0: RtspMediaResource::~RtspMediaResource() michael@0: { michael@0: RTSPMLOG("~RtspMediaResource"); michael@0: if (mListener) { michael@0: // Kill its reference to us since we're going away michael@0: mListener->Revoke(); michael@0: } michael@0: } michael@0: michael@0: size_t michael@0: RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf); michael@0: size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: // Include the size of each track buffer. michael@0: for (size_t i = 0; i < mTrackBuffer.Length(); i++) { michael@0: size += mTrackBuffer[i]->SizeOfIncludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: // Could add in the future: michael@0: // - mMediaStreamController michael@0: michael@0: return size; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------------- michael@0: // RtspMediaResource::Listener michael@0: //---------------------------------------------------------------------------- michael@0: NS_IMPL_ISUPPORTS(RtspMediaResource::Listener, michael@0: nsIInterfaceRequestor, nsIStreamingProtocolListener); michael@0: michael@0: nsresult michael@0: RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx, michael@0: const nsACString &data, michael@0: uint32_t length, michael@0: uint32_t offset, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: if (!mResource) michael@0: return NS_OK; michael@0: return mResource->OnMediaDataAvailable(aTrackIdx, data, length, offset, meta); michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::Listener::OnConnected(uint8_t aTrackIdx, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: if (!mResource) michael@0: return NS_OK; michael@0: return mResource->OnConnected(aTrackIdx, meta); michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::Listener::OnDisconnected(uint8_t aTrackIdx, nsresult reason) michael@0: { michael@0: if (!mResource) michael@0: return NS_OK; michael@0: return mResource->OnDisconnected(aTrackIdx, reason); michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult) michael@0: { michael@0: return QueryInterface(aIID, aResult); michael@0: } michael@0: michael@0: void michael@0: RtspMediaResource::Listener::Revoke() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); michael@0: if (mResource) { michael@0: mResource = nullptr; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize, michael@0: uint32_t aTrackIdx, uint32_t& aBytes, michael@0: uint64_t& aTime, uint32_t& aFrameSize) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); michael@0: NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(), michael@0: "ReadTrack index > mTrackBuffer"); michael@0: MOZ_ASSERT(aBuffer); michael@0: michael@0: return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes, michael@0: aTime, aFrameSize); michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx, michael@0: const nsACString &data, michael@0: uint32_t length, michael@0: uint32_t offset, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: uint64_t time; michael@0: uint32_t frameType; michael@0: meta->GetTimeStamp(&time); michael@0: meta->GetFrameType(&frameType); michael@0: if (mRealTime) { michael@0: time = 0; michael@0: } michael@0: mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time, michael@0: frameType); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Bug 962309 - Video RTSP support should be disabled in 1.3 michael@0: bool michael@0: RtspMediaResource::IsVideoEnabled() michael@0: { michael@0: return Preferences::GetBool("media.rtsp.video.enabled", false); michael@0: } michael@0: michael@0: bool michael@0: RtspMediaResource::IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: bool isVideo = false; michael@0: for (int i = 0; i < tracks; ++i) { michael@0: nsCOMPtr trackMeta; michael@0: mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); michael@0: MOZ_ASSERT(trackMeta); michael@0: uint32_t w = 0, h = 0; michael@0: trackMeta->GetWidth(&w); michael@0: trackMeta->GetHeight(&h); michael@0: if (w > 0 || h > 0) { michael@0: isVideo = true; michael@0: break; michael@0: } michael@0: } michael@0: return isVideo; michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::OnConnected(uint8_t aTrackIdx, michael@0: nsIStreamingProtocolMetaData *meta) michael@0: { michael@0: if (mIsConnected) { michael@0: for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { michael@0: mTrackBuffer[i]->Start(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint8_t tracks; michael@0: mMediaStreamController->GetTotalTracks(&tracks); michael@0: michael@0: // If the preference of RTSP video feature is not enabled and the streaming is michael@0: // video, we give up moving forward. michael@0: if (!IsVideoEnabled() && IsVideo(tracks, meta)) { michael@0: // Give up, report error to media element. michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: uint64_t duration = 0; michael@0: for (int i = 0; i < tracks; ++i) { michael@0: nsCString rtspTrackId("RtspTrack"); michael@0: rtspTrackId.AppendInt(i); michael@0: nsCOMPtr trackMeta; michael@0: mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); michael@0: MOZ_ASSERT(trackMeta); michael@0: trackMeta->GetDuration(&duration); michael@0: michael@0: // Here is a heuristic to estimate the slot size. michael@0: // For video track, calculate the width*height. michael@0: // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0. michael@0: // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE) michael@0: uint32_t w, h; michael@0: uint32_t slotSize; michael@0: trackMeta->GetWidth(&w); michael@0: trackMeta->GetHeight(&h); michael@0: slotSize = clamped((int32_t)(w * h), BUFFER_SLOT_DEFAULT_SIZE, michael@0: BUFFER_SLOT_MAX_SIZE); michael@0: mTrackBuffer.AppendElement(new RtspTrackBuffer(rtspTrackId.get(), michael@0: i, slotSize)); michael@0: mTrackBuffer[i]->Start(); michael@0: } michael@0: michael@0: if (!mDecoder) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // If the duration is 0, imply the stream is live stream. michael@0: if (duration) { michael@0: // Not live stream. michael@0: mRealTime = false; michael@0: bool seekable = true; michael@0: mDecoder->SetInfinite(false); michael@0: mDecoder->SetTransportSeekable(seekable); michael@0: mDecoder->SetDuration(duration); michael@0: } else { michael@0: // Live stream. michael@0: // Check the preference "media.realtime_decoder.enabled". michael@0: if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) { michael@0: // Give up, report error to media element. michael@0: nsCOMPtr event = michael@0: NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); michael@0: NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: mRealTime = true; michael@0: bool seekable = false; michael@0: mDecoder->SetInfinite(true); michael@0: mDecoder->SetTransportSeekable(seekable); michael@0: mDecoder->SetMediaSeekable(seekable); michael@0: } michael@0: } michael@0: // Fires an initial progress event and sets up the stall counter so stall events michael@0: // fire if no download occurs within the required time frame. michael@0: mDecoder->Progress(false); michael@0: michael@0: MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); michael@0: NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); michael@0: dom::HTMLMediaElement* element = owner->GetMediaElement(); michael@0: NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); michael@0: michael@0: element->FinishDecoderSetup(mDecoder, this); michael@0: mIsConnected = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); michael@0: michael@0: for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { michael@0: mTrackBuffer[i]->Stop(); michael@0: mTrackBuffer[i]->Reset(); michael@0: } michael@0: michael@0: if (mDecoder) { michael@0: if (aReason == NS_ERROR_NOT_INITIALIZED || michael@0: aReason == NS_ERROR_CONNECTION_REFUSED || michael@0: aReason == NS_ERROR_NOT_CONNECTED || michael@0: aReason == NS_ERROR_NET_TIMEOUT) { michael@0: // Report error code to Decoder. michael@0: RTSPMLOG("Error in OnDisconnected 0x%x", aReason); michael@0: mDecoder->NetworkError(); michael@0: } else { michael@0: // Resetting the decoder and media element when the connection michael@0: // between RTSP client and server goes down. michael@0: mDecoder->ResetConnectionState(); michael@0: } michael@0: } michael@0: michael@0: if (mListener) { michael@0: // Note: Listener's Revoke() kills its reference to us, which means it would michael@0: // release |this| object. So, ensure it is called in the end of this method. michael@0: mListener->Revoke(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void RtspMediaResource::Suspend(bool aCloseImmediately) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); michael@0: if (NS_WARN_IF(!mDecoder)) { michael@0: return; michael@0: } michael@0: michael@0: MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); michael@0: NS_ENSURE_TRUE_VOID(owner); michael@0: dom::HTMLMediaElement* element = owner->GetMediaElement(); michael@0: NS_ENSURE_TRUE_VOID(element); michael@0: michael@0: mMediaStreamController->Suspend(); michael@0: element->DownloadSuspended(); michael@0: } michael@0: michael@0: void RtspMediaResource::Resume() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); michael@0: if (NS_WARN_IF(!mDecoder)) { michael@0: return; michael@0: } michael@0: michael@0: MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); michael@0: NS_ENSURE_TRUE_VOID(owner); michael@0: dom::HTMLMediaElement* element = owner->GetMediaElement(); michael@0: NS_ENSURE_TRUE_VOID(element); michael@0: michael@0: if (mChannel) { michael@0: element->DownloadResumed(); michael@0: } michael@0: mMediaStreamController->Resume(); michael@0: } michael@0: michael@0: nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult RtspMediaResource::Close() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); michael@0: mMediaStreamController->Stop(); michael@0: // Since mDecoder is not an nsCOMPtr in BaseMediaResource, we have to michael@0: // explicitly set it as null pointer in order to prevent misuse from this michael@0: // object (RtspMediaResource). michael@0: if (mDecoder) { michael@0: mDecoder = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed RtspMediaResource::GetCurrentPrincipal() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); michael@0: michael@0: nsCOMPtr principal; michael@0: nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); michael@0: if (!secMan || !mChannel) michael@0: return nullptr; michael@0: secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); michael@0: return principal.forget(); michael@0: } michael@0: michael@0: nsresult RtspMediaResource::SeekTime(int64_t aOffset) michael@0: { michael@0: NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); michael@0: michael@0: RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]", michael@0: aOffset, mDecoder); michael@0: // Clear buffer and raise the frametype flag. michael@0: for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { michael@0: mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY); michael@0: } michael@0: michael@0: return mMediaStreamController->Seek(aOffset); michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: