1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/RtspMediaResource.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,717 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/DebugOnly.h" 1.11 + 1.12 +#include "RtspMediaResource.h" 1.13 + 1.14 +#include "MediaDecoder.h" 1.15 +#include "mozilla/dom/HTMLMediaElement.h" 1.16 +#include "mozilla/Monitor.h" 1.17 +#include "mozilla/Preferences.h" 1.18 +#include "nsIScriptSecurityManager.h" 1.19 +#include "nsIStreamingProtocolService.h" 1.20 +#include "nsServiceManagerUtils.h" 1.21 +#ifdef NECKO_PROTOCOL_rtsp 1.22 +#include "mozilla/net/RtspChannelChild.h" 1.23 +#endif 1.24 +using namespace mozilla::net; 1.25 + 1.26 +#ifdef PR_LOGGING 1.27 +PRLogModuleInfo* gRtspMediaResourceLog; 1.28 +#define RTSP_LOG(msg, ...) PR_LOG(gRtspMediaResourceLog, PR_LOG_DEBUG, \ 1.29 + (msg, ##__VA_ARGS__)) 1.30 +// Debug logging macro with object pointer and class name. 1.31 +#define RTSPMLOG(msg, ...) \ 1.32 + RTSP_LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__) 1.33 +#else 1.34 +#define RTSP_LOG(msg, ...) 1.35 +#define RTSPMLOG(msg, ...) 1.36 +#endif 1.37 + 1.38 +namespace mozilla { 1.39 + 1.40 +/* class RtspTrackBuffer: a ring buffer implementation for audio/video track 1.41 + * un-decoded data. 1.42 + * The ring buffer is divided into BUFFER_SLOT_NUM slots, 1.43 + * and each slot's size is fixed(mSlotSize). 1.44 + * Even though the ring buffer is divided into fixed size slots, it still can 1.45 + * store the data which size is larger than one slot size. 1.46 + * */ 1.47 +#define BUFFER_SLOT_NUM 8192 1.48 +#define BUFFER_SLOT_DEFAULT_SIZE 256 1.49 +#define BUFFER_SLOT_MAX_SIZE 512 1.50 +#define BUFFER_SLOT_INVALID -1 1.51 +#define BUFFER_SLOT_EMPTY 0 1.52 + 1.53 +struct BufferSlotData { 1.54 + int32_t mLength; 1.55 + uint64_t mTime; 1.56 +}; 1.57 + 1.58 +class RtspTrackBuffer 1.59 +{ 1.60 +public: 1.61 + RtspTrackBuffer(const char *aMonitor, int32_t aTrackIdx, uint32_t aSlotSize) 1.62 + : mMonitor(aMonitor) 1.63 + , mSlotSize(aSlotSize) 1.64 + , mTotalBufferSize(BUFFER_SLOT_NUM * mSlotSize) 1.65 + , mFrameType(0) 1.66 + , mIsStarted(false) { 1.67 + MOZ_COUNT_CTOR(RtspTrackBuffer); 1.68 +#ifdef PR_LOGGING 1.69 + mTrackIdx = aTrackIdx; 1.70 +#endif 1.71 + MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM); 1.72 + mRingBuffer = new uint8_t[mTotalBufferSize]; 1.73 + Reset(); 1.74 + }; 1.75 + ~RtspTrackBuffer() { 1.76 + MOZ_COUNT_DTOR(RtspTrackBuffer); 1.77 + mRingBuffer = nullptr; 1.78 + }; 1.79 + 1.80 + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 1.81 + // including this 1.82 + size_t size = aMallocSizeOf(this); 1.83 + 1.84 + // excluding this 1.85 + size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf); 1.86 + 1.87 + return size; 1.88 + } 1.89 + 1.90 + void Start() { 1.91 + MonitorAutoLock monitor(mMonitor); 1.92 + mIsStarted = true; 1.93 + mFrameType = 0; 1.94 + } 1.95 + void Stop() { 1.96 + MonitorAutoLock monitor(mMonitor); 1.97 + mIsStarted = false; 1.98 + } 1.99 + 1.100 + // Read the data from mRingBuffer[mConsumerIdx*mSlotSize] into aToBuffer. 1.101 + // If the aToBufferSize is smaller than mBufferSlotDataLength[mConsumerIdx], 1.102 + // early return and set the aFrameSize to notify the reader the aToBuffer 1.103 + // doesn't have enough space. The reader must realloc the aToBuffer if it 1.104 + // wishes to read the data. 1.105 + nsresult ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, 1.106 + uint32_t& aReadCount, uint64_t& aFrameTime, 1.107 + uint32_t& aFrameSize); 1.108 + // Write the data from aFromBuffer into mRingBuffer[mProducerIdx*mSlotSize]. 1.109 + void WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, 1.110 + uint64_t aFrameTime, uint32_t aFrameType); 1.111 + // Reset the mProducerIdx, mConsumerIdx, mBufferSlotDataLength[], 1.112 + // mBufferSlotDataTime[]. 1.113 + void Reset(); 1.114 + 1.115 + // We should call SetFrameType first then reset(). 1.116 + // If we call reset() first, the queue may still has some "garbage" frame 1.117 + // from another thread's |OnMediaDataAvailable| before |SetFrameType|. 1.118 + void ResetWithFrameType(uint32_t aFrameType) { 1.119 + SetFrameType(aFrameType); 1.120 + Reset(); 1.121 + } 1.122 + 1.123 +private: 1.124 + // The FrameType is sync to nsIStreamingProtocolController.h 1.125 + void SetFrameType(uint32_t aFrameType) { 1.126 + MonitorAutoLock monitor(mMonitor); 1.127 + mFrameType = mFrameType | aFrameType; 1.128 + } 1.129 + 1.130 + // A monitor lock to prevent racing condition. 1.131 + Monitor mMonitor; 1.132 +#ifdef PR_LOGGING 1.133 + // Indicate the track number for Rtsp. 1.134 + int32_t mTrackIdx; 1.135 +#endif 1.136 + // mProducerIdx: A slot index that we store data from 1.137 + // nsIStreamingProtocolController. 1.138 + // mConsumerIdx: A slot index that we read when decoder need(from OMX decoder). 1.139 + int32_t mProducerIdx; 1.140 + int32_t mConsumerIdx; 1.141 + 1.142 + // Because each slot's size is fixed, we need an array to record the real 1.143 + // data length and data time stamp. 1.144 + // The value in mBufferSlotData[index].mLength represents: 1.145 + // -1(BUFFER_SLOT_INVALID): The index of slot data is invalid, mConsumerIdx 1.146 + // should go forward. 1.147 + // 0(BUFFER_SLOT_EMPTY): The index slot is empty. mConsumerIdx should wait here. 1.148 + // positive value: The index slot contains valid data and the value is data size. 1.149 + BufferSlotData mBufferSlotData[BUFFER_SLOT_NUM]; 1.150 + 1.151 + // The ring buffer pointer. 1.152 + nsAutoArrayPtr<uint8_t> mRingBuffer; 1.153 + // Each slot's size. 1.154 + uint32_t mSlotSize; 1.155 + // Total mRingBuffer's total size. 1.156 + uint32_t mTotalBufferSize; 1.157 + // A flag that that indicate the incoming data should be dropped or stored. 1.158 + // When we are seeking, the incoming data should be dropped. 1.159 + // Bit definition in |nsIStreamingProtocolController.h| 1.160 + uint32_t mFrameType; 1.161 + 1.162 + // Set true/false when |Start()/Stop()| is called. 1.163 + bool mIsStarted; 1.164 +}; 1.165 + 1.166 +nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, 1.167 + uint32_t& aReadCount, uint64_t& aFrameTime, 1.168 + uint32_t& aFrameSize) 1.169 +{ 1.170 + MonitorAutoLock monitor(mMonitor); 1.171 + RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d " 1.172 + "mBufferSlotData[mConsumerIdx].mLength %d" 1.173 + ,mTrackIdx ,mProducerIdx ,mConsumerIdx 1.174 + ,mBufferSlotData[mConsumerIdx].mLength); 1.175 + // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID. 1.176 + // The loop ends when 1.177 + // 1. Read data successfully 1.178 + // 2. Fail to read data due to aToBuffer's space 1.179 + // 3. No data in this buffer 1.180 + // 4. mIsStarted is not set 1.181 + while (1) { 1.182 + if (mBufferSlotData[mConsumerIdx].mLength > 0) { 1.183 + // Check the aToBuffer space is enough for data copy. 1.184 + if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) { 1.185 + aFrameSize = mBufferSlotData[mConsumerIdx].mLength; 1.186 + break; 1.187 + } 1.188 + uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1; 1.189 + // we have data, copy to aToBuffer 1.190 + MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <= 1.191 + (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize)); 1.192 + memcpy(aToBuffer, 1.193 + (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]), 1.194 + mBufferSlotData[mConsumerIdx].mLength); 1.195 + 1.196 + aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength; 1.197 + aFrameTime = mBufferSlotData[mConsumerIdx].mTime; 1.198 + RTSPMLOG("DataLength %d, data time %lld" 1.199 + ,mBufferSlotData[mConsumerIdx].mLength 1.200 + ,mBufferSlotData[mConsumerIdx].mTime); 1.201 + // After reading the data, we set current index of mBufferSlotDataLength 1.202 + // to BUFFER_SLOT_EMPTY to indicate these slots are free. 1.203 + for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) { 1.204 + mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; 1.205 + mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; 1.206 + } 1.207 + mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM; 1.208 + break; 1.209 + } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) { 1.210 + mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM; 1.211 + RTSPMLOG("BUFFER_SLOT_INVALID move forward"); 1.212 + } else { 1.213 + // No data, and disconnected. 1.214 + if (!mIsStarted) { 1.215 + return NS_ERROR_FAILURE; 1.216 + } 1.217 + // No data, the decode thread is blocked here until we receive 1.218 + // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer() 1.219 + // to wake up the decode thread. 1.220 + RTSPMLOG("monitor.Wait()"); 1.221 + monitor.Wait(); 1.222 + } 1.223 + } 1.224 + return NS_OK; 1.225 +} 1.226 + 1.227 +/* When we perform a WriteBuffer, we check mIsStarted and aFrameType first. 1.228 + * These flags prevent "garbage" frames from being written into the buffer. 1.229 + * 1.230 + * After writing the data into the buffer, we check to see if we wrote over a 1.231 + * slot, and update mConsumerIdx if necessary. 1.232 + * This ensures that the decoder will get the "oldest" data available in the 1.233 + * buffer. 1.234 + * 1.235 + * If the incoming data is larger than one slot size (isMultipleSlots), we do 1.236 + * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the 1.237 + * first slot, in order to notify the reader that some slots are unavailable. 1.238 + * 1.239 + * If the incoming data is isMultipleSlots and crosses the end of 1.240 + * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to 1.241 + * be written from head(index 0). 1.242 + * 1.243 + * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking. 1.244 + * */ 1.245 +void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, 1.246 + uint64_t aFrameTime, uint32_t aFrameType) 1.247 +{ 1.248 + MonitorAutoLock monitor(mMonitor); 1.249 + if (!mIsStarted) { 1.250 + RTSPMLOG("mIsStarted is false"); 1.251 + return; 1.252 + } 1.253 + if (mTotalBufferSize < aWriteCount) { 1.254 + RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large"); 1.255 + return; 1.256 + } 1.257 + // Checking the incoming data's frame type. 1.258 + // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType 1.259 + // imply the RtspTrackBuffer is ready for receive data. 1.260 + if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { 1.261 + mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY); 1.262 + RTSPMLOG("Clear mFrameType"); 1.263 + return; 1.264 + } 1.265 + // Checking current buffer frame type. 1.266 + // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the 1.267 + // RtspTrackBuffer can't receive data now. So we drop the frame until we 1.268 + // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY. 1.269 + if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { 1.270 + RTSPMLOG("Return because the mFrameType is set"); 1.271 + return; 1.272 + } 1.273 + // The flag is true if the incoming data is larger than one slot size. 1.274 + bool isMultipleSlots = false; 1.275 + // The flag is true if the incoming data is larger than remainder free slots 1.276 + bool returnToHead = false; 1.277 + // Calculate how many slots the incoming data needed. 1.278 + int32_t slots = 1; 1.279 + int32_t i; 1.280 + RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d", 1.281 + mTrackIdx, mProducerIdx,mConsumerIdx); 1.282 + if (aWriteCount > mSlotSize) { 1.283 + isMultipleSlots = true; 1.284 + slots = (aWriteCount / mSlotSize) + 1; 1.285 + } 1.286 + if (isMultipleSlots && 1.287 + (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) { 1.288 + returnToHead = true; 1.289 + } 1.290 + RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d", 1.291 + slots, isMultipleSlots, returnToHead); 1.292 + if (returnToHead) { 1.293 + // Clear the rest index of mBufferSlotData[].mLength 1.294 + for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) { 1.295 + mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; 1.296 + } 1.297 + // We wrote one or more slots that the decode thread has not yet read. 1.298 + // So the mConsumerIdx returns to the head of slot buffer and moves forward 1.299 + // to the oldest slot. 1.300 + if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) { 1.301 + mConsumerIdx = 0; 1.302 + for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) { 1.303 + if (mBufferSlotData[i].mLength > 0) { 1.304 + mConsumerIdx = i; 1.305 + break; 1.306 + } 1.307 + } 1.308 + } 1.309 + mProducerIdx = 0; 1.310 + } 1.311 + 1.312 + memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount); 1.313 + 1.314 + if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots 1.315 + && mBufferSlotData[mConsumerIdx].mLength > 0) { 1.316 + // Wrote one or more slots that the decode thread has not yet read. 1.317 + RTSPMLOG("overwrite!! %d time %lld" 1.318 + ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime); 1.319 + mBufferSlotData[mProducerIdx].mLength = aWriteCount; 1.320 + mBufferSlotData[mProducerIdx].mTime = aFrameTime; 1.321 + // Clear the mBufferSlotDataLength except the start slot. 1.322 + if (isMultipleSlots) { 1.323 + for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { 1.324 + mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; 1.325 + } 1.326 + } 1.327 + mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; 1.328 + // Move the mConsumerIdx forward to ensure that the decoder reads the 1.329 + // oldest data available. 1.330 + mConsumerIdx = mProducerIdx; 1.331 + } else { 1.332 + // Normal case, the writer doesn't take over the reader. 1.333 + mBufferSlotData[mProducerIdx].mLength = aWriteCount; 1.334 + mBufferSlotData[mProducerIdx].mTime = aFrameTime; 1.335 + // Clear the mBufferSlotData[].mLength except the start slot. 1.336 + if (isMultipleSlots) { 1.337 + for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { 1.338 + mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; 1.339 + } 1.340 + } 1.341 + mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; 1.342 + } 1.343 + 1.344 + mMonitor.NotifyAll(); 1.345 +} 1.346 + 1.347 +void RtspTrackBuffer::Reset() { 1.348 + MonitorAutoLock monitor(mMonitor); 1.349 + mProducerIdx = 0; 1.350 + mConsumerIdx = 0; 1.351 + for (uint32_t i = 0; i < BUFFER_SLOT_NUM; ++i) { 1.352 + mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; 1.353 + mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; 1.354 + } 1.355 + mMonitor.NotifyAll(); 1.356 +} 1.357 + 1.358 +RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder, 1.359 + nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType) 1.360 + : BaseMediaResource(aDecoder, aChannel, aURI, aContentType) 1.361 + , mIsConnected(false) 1.362 + , mRealTime(false) 1.363 +{ 1.364 +#ifndef NECKO_PROTOCOL_rtsp 1.365 + MOZ_CRASH("Should not be called except for B2G platform"); 1.366 +#else 1.367 + MOZ_ASSERT(aChannel); 1.368 + mMediaStreamController = 1.369 + static_cast<RtspChannelChild*>(aChannel)->GetController(); 1.370 + MOZ_ASSERT(mMediaStreamController); 1.371 + mListener = new Listener(this); 1.372 + mMediaStreamController->AsyncOpen(mListener); 1.373 +#ifdef PR_LOGGING 1.374 + if (!gRtspMediaResourceLog) { 1.375 + gRtspMediaResourceLog = PR_NewLogModule("RtspMediaResource"); 1.376 + } 1.377 +#endif 1.378 +#endif 1.379 +} 1.380 + 1.381 +RtspMediaResource::~RtspMediaResource() 1.382 +{ 1.383 + RTSPMLOG("~RtspMediaResource"); 1.384 + if (mListener) { 1.385 + // Kill its reference to us since we're going away 1.386 + mListener->Revoke(); 1.387 + } 1.388 +} 1.389 + 1.390 +size_t 1.391 +RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.392 +{ 1.393 + size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf); 1.394 + size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf); 1.395 + 1.396 + // Include the size of each track buffer. 1.397 + for (size_t i = 0; i < mTrackBuffer.Length(); i++) { 1.398 + size += mTrackBuffer[i]->SizeOfIncludingThis(aMallocSizeOf); 1.399 + } 1.400 + 1.401 + // Could add in the future: 1.402 + // - mMediaStreamController 1.403 + 1.404 + return size; 1.405 +} 1.406 + 1.407 +//---------------------------------------------------------------------------- 1.408 +// RtspMediaResource::Listener 1.409 +//---------------------------------------------------------------------------- 1.410 +NS_IMPL_ISUPPORTS(RtspMediaResource::Listener, 1.411 + nsIInterfaceRequestor, nsIStreamingProtocolListener); 1.412 + 1.413 +nsresult 1.414 +RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx, 1.415 + const nsACString &data, 1.416 + uint32_t length, 1.417 + uint32_t offset, 1.418 + nsIStreamingProtocolMetaData *meta) 1.419 +{ 1.420 + if (!mResource) 1.421 + return NS_OK; 1.422 + return mResource->OnMediaDataAvailable(aTrackIdx, data, length, offset, meta); 1.423 +} 1.424 + 1.425 +nsresult 1.426 +RtspMediaResource::Listener::OnConnected(uint8_t aTrackIdx, 1.427 + nsIStreamingProtocolMetaData *meta) 1.428 +{ 1.429 + if (!mResource) 1.430 + return NS_OK; 1.431 + return mResource->OnConnected(aTrackIdx, meta); 1.432 +} 1.433 + 1.434 +nsresult 1.435 +RtspMediaResource::Listener::OnDisconnected(uint8_t aTrackIdx, nsresult reason) 1.436 +{ 1.437 + if (!mResource) 1.438 + return NS_OK; 1.439 + return mResource->OnDisconnected(aTrackIdx, reason); 1.440 +} 1.441 + 1.442 +nsresult 1.443 +RtspMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult) 1.444 +{ 1.445 + return QueryInterface(aIID, aResult); 1.446 +} 1.447 + 1.448 +void 1.449 +RtspMediaResource::Listener::Revoke() 1.450 +{ 1.451 + NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); 1.452 + if (mResource) { 1.453 + mResource = nullptr; 1.454 + } 1.455 +} 1.456 + 1.457 +nsresult 1.458 +RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize, 1.459 + uint32_t aTrackIdx, uint32_t& aBytes, 1.460 + uint64_t& aTime, uint32_t& aFrameSize) 1.461 +{ 1.462 + NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); 1.463 + NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(), 1.464 + "ReadTrack index > mTrackBuffer"); 1.465 + MOZ_ASSERT(aBuffer); 1.466 + 1.467 + return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes, 1.468 + aTime, aFrameSize); 1.469 +} 1.470 + 1.471 +nsresult 1.472 +RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx, 1.473 + const nsACString &data, 1.474 + uint32_t length, 1.475 + uint32_t offset, 1.476 + nsIStreamingProtocolMetaData *meta) 1.477 +{ 1.478 + uint64_t time; 1.479 + uint32_t frameType; 1.480 + meta->GetTimeStamp(&time); 1.481 + meta->GetFrameType(&frameType); 1.482 + if (mRealTime) { 1.483 + time = 0; 1.484 + } 1.485 + mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time, 1.486 + frameType); 1.487 + return NS_OK; 1.488 +} 1.489 + 1.490 +// Bug 962309 - Video RTSP support should be disabled in 1.3 1.491 +bool 1.492 +RtspMediaResource::IsVideoEnabled() 1.493 +{ 1.494 + return Preferences::GetBool("media.rtsp.video.enabled", false); 1.495 +} 1.496 + 1.497 +bool 1.498 +RtspMediaResource::IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta) 1.499 +{ 1.500 + bool isVideo = false; 1.501 + for (int i = 0; i < tracks; ++i) { 1.502 + nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta; 1.503 + mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); 1.504 + MOZ_ASSERT(trackMeta); 1.505 + uint32_t w = 0, h = 0; 1.506 + trackMeta->GetWidth(&w); 1.507 + trackMeta->GetHeight(&h); 1.508 + if (w > 0 || h > 0) { 1.509 + isVideo = true; 1.510 + break; 1.511 + } 1.512 + } 1.513 + return isVideo; 1.514 +} 1.515 + 1.516 +nsresult 1.517 +RtspMediaResource::OnConnected(uint8_t aTrackIdx, 1.518 + nsIStreamingProtocolMetaData *meta) 1.519 +{ 1.520 + if (mIsConnected) { 1.521 + for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { 1.522 + mTrackBuffer[i]->Start(); 1.523 + } 1.524 + return NS_OK; 1.525 + } 1.526 + 1.527 + uint8_t tracks; 1.528 + mMediaStreamController->GetTotalTracks(&tracks); 1.529 + 1.530 + // If the preference of RTSP video feature is not enabled and the streaming is 1.531 + // video, we give up moving forward. 1.532 + if (!IsVideoEnabled() && IsVideo(tracks, meta)) { 1.533 + // Give up, report error to media element. 1.534 + nsCOMPtr<nsIRunnable> event = 1.535 + NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); 1.536 + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.537 + return NS_ERROR_FAILURE; 1.538 + } 1.539 + uint64_t duration = 0; 1.540 + for (int i = 0; i < tracks; ++i) { 1.541 + nsCString rtspTrackId("RtspTrack"); 1.542 + rtspTrackId.AppendInt(i); 1.543 + nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta; 1.544 + mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); 1.545 + MOZ_ASSERT(trackMeta); 1.546 + trackMeta->GetDuration(&duration); 1.547 + 1.548 + // Here is a heuristic to estimate the slot size. 1.549 + // For video track, calculate the width*height. 1.550 + // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0. 1.551 + // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE) 1.552 + uint32_t w, h; 1.553 + uint32_t slotSize; 1.554 + trackMeta->GetWidth(&w); 1.555 + trackMeta->GetHeight(&h); 1.556 + slotSize = clamped((int32_t)(w * h), BUFFER_SLOT_DEFAULT_SIZE, 1.557 + BUFFER_SLOT_MAX_SIZE); 1.558 + mTrackBuffer.AppendElement(new RtspTrackBuffer(rtspTrackId.get(), 1.559 + i, slotSize)); 1.560 + mTrackBuffer[i]->Start(); 1.561 + } 1.562 + 1.563 + if (!mDecoder) { 1.564 + return NS_ERROR_FAILURE; 1.565 + } 1.566 + 1.567 + // If the duration is 0, imply the stream is live stream. 1.568 + if (duration) { 1.569 + // Not live stream. 1.570 + mRealTime = false; 1.571 + bool seekable = true; 1.572 + mDecoder->SetInfinite(false); 1.573 + mDecoder->SetTransportSeekable(seekable); 1.574 + mDecoder->SetDuration(duration); 1.575 + } else { 1.576 + // Live stream. 1.577 + // Check the preference "media.realtime_decoder.enabled". 1.578 + if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) { 1.579 + // Give up, report error to media element. 1.580 + nsCOMPtr<nsIRunnable> event = 1.581 + NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); 1.582 + NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); 1.583 + return NS_ERROR_FAILURE; 1.584 + } else { 1.585 + mRealTime = true; 1.586 + bool seekable = false; 1.587 + mDecoder->SetInfinite(true); 1.588 + mDecoder->SetTransportSeekable(seekable); 1.589 + mDecoder->SetMediaSeekable(seekable); 1.590 + } 1.591 + } 1.592 + // Fires an initial progress event and sets up the stall counter so stall events 1.593 + // fire if no download occurs within the required time frame. 1.594 + mDecoder->Progress(false); 1.595 + 1.596 + MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); 1.597 + NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); 1.598 + dom::HTMLMediaElement* element = owner->GetMediaElement(); 1.599 + NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); 1.600 + 1.601 + element->FinishDecoderSetup(mDecoder, this); 1.602 + mIsConnected = true; 1.603 + 1.604 + return NS_OK; 1.605 +} 1.606 + 1.607 +nsresult 1.608 +RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason) 1.609 +{ 1.610 + NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); 1.611 + 1.612 + for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { 1.613 + mTrackBuffer[i]->Stop(); 1.614 + mTrackBuffer[i]->Reset(); 1.615 + } 1.616 + 1.617 + if (mDecoder) { 1.618 + if (aReason == NS_ERROR_NOT_INITIALIZED || 1.619 + aReason == NS_ERROR_CONNECTION_REFUSED || 1.620 + aReason == NS_ERROR_NOT_CONNECTED || 1.621 + aReason == NS_ERROR_NET_TIMEOUT) { 1.622 + // Report error code to Decoder. 1.623 + RTSPMLOG("Error in OnDisconnected 0x%x", aReason); 1.624 + mDecoder->NetworkError(); 1.625 + } else { 1.626 + // Resetting the decoder and media element when the connection 1.627 + // between RTSP client and server goes down. 1.628 + mDecoder->ResetConnectionState(); 1.629 + } 1.630 + } 1.631 + 1.632 + if (mListener) { 1.633 + // Note: Listener's Revoke() kills its reference to us, which means it would 1.634 + // release |this| object. So, ensure it is called in the end of this method. 1.635 + mListener->Revoke(); 1.636 + } 1.637 + 1.638 + return NS_OK; 1.639 +} 1.640 + 1.641 +void RtspMediaResource::Suspend(bool aCloseImmediately) 1.642 +{ 1.643 + NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); 1.644 + if (NS_WARN_IF(!mDecoder)) { 1.645 + return; 1.646 + } 1.647 + 1.648 + MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); 1.649 + NS_ENSURE_TRUE_VOID(owner); 1.650 + dom::HTMLMediaElement* element = owner->GetMediaElement(); 1.651 + NS_ENSURE_TRUE_VOID(element); 1.652 + 1.653 + mMediaStreamController->Suspend(); 1.654 + element->DownloadSuspended(); 1.655 +} 1.656 + 1.657 +void RtspMediaResource::Resume() 1.658 +{ 1.659 + NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); 1.660 + if (NS_WARN_IF(!mDecoder)) { 1.661 + return; 1.662 + } 1.663 + 1.664 + MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); 1.665 + NS_ENSURE_TRUE_VOID(owner); 1.666 + dom::HTMLMediaElement* element = owner->GetMediaElement(); 1.667 + NS_ENSURE_TRUE_VOID(element); 1.668 + 1.669 + if (mChannel) { 1.670 + element->DownloadResumed(); 1.671 + } 1.672 + mMediaStreamController->Resume(); 1.673 +} 1.674 + 1.675 +nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener) 1.676 +{ 1.677 + return NS_OK; 1.678 +} 1.679 + 1.680 +nsresult RtspMediaResource::Close() 1.681 +{ 1.682 + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 1.683 + mMediaStreamController->Stop(); 1.684 + // Since mDecoder is not an nsCOMPtr in BaseMediaResource, we have to 1.685 + // explicitly set it as null pointer in order to prevent misuse from this 1.686 + // object (RtspMediaResource). 1.687 + if (mDecoder) { 1.688 + mDecoder = nullptr; 1.689 + } 1.690 + return NS_OK; 1.691 +} 1.692 + 1.693 +already_AddRefed<nsIPrincipal> RtspMediaResource::GetCurrentPrincipal() 1.694 +{ 1.695 + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); 1.696 + 1.697 + nsCOMPtr<nsIPrincipal> principal; 1.698 + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); 1.699 + if (!secMan || !mChannel) 1.700 + return nullptr; 1.701 + secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); 1.702 + return principal.forget(); 1.703 +} 1.704 + 1.705 +nsresult RtspMediaResource::SeekTime(int64_t aOffset) 1.706 +{ 1.707 + NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); 1.708 + 1.709 + RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]", 1.710 + aOffset, mDecoder); 1.711 + // Clear buffer and raise the frametype flag. 1.712 + for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { 1.713 + mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY); 1.714 + } 1.715 + 1.716 + return mMediaStreamController->Seek(aOffset); 1.717 +} 1.718 + 1.719 +} // namespace mozilla 1.720 +