Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
michael@0 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "mozilla/DebugOnly.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "RtspMediaResource.h" |
michael@0 | 10 | |
michael@0 | 11 | #include "MediaDecoder.h" |
michael@0 | 12 | #include "mozilla/dom/HTMLMediaElement.h" |
michael@0 | 13 | #include "mozilla/Monitor.h" |
michael@0 | 14 | #include "mozilla/Preferences.h" |
michael@0 | 15 | #include "nsIScriptSecurityManager.h" |
michael@0 | 16 | #include "nsIStreamingProtocolService.h" |
michael@0 | 17 | #include "nsServiceManagerUtils.h" |
michael@0 | 18 | #ifdef NECKO_PROTOCOL_rtsp |
michael@0 | 19 | #include "mozilla/net/RtspChannelChild.h" |
michael@0 | 20 | #endif |
michael@0 | 21 | using namespace mozilla::net; |
michael@0 | 22 | |
michael@0 | 23 | #ifdef PR_LOGGING |
michael@0 | 24 | PRLogModuleInfo* gRtspMediaResourceLog; |
michael@0 | 25 | #define RTSP_LOG(msg, ...) PR_LOG(gRtspMediaResourceLog, PR_LOG_DEBUG, \ |
michael@0 | 26 | (msg, ##__VA_ARGS__)) |
michael@0 | 27 | // Debug logging macro with object pointer and class name. |
michael@0 | 28 | #define RTSPMLOG(msg, ...) \ |
michael@0 | 29 | RTSP_LOG("%p [RtspMediaResource]: " msg, this, ##__VA_ARGS__) |
michael@0 | 30 | #else |
michael@0 | 31 | #define RTSP_LOG(msg, ...) |
michael@0 | 32 | #define RTSPMLOG(msg, ...) |
michael@0 | 33 | #endif |
michael@0 | 34 | |
michael@0 | 35 | namespace mozilla { |
michael@0 | 36 | |
michael@0 | 37 | /* class RtspTrackBuffer: a ring buffer implementation for audio/video track |
michael@0 | 38 | * un-decoded data. |
michael@0 | 39 | * The ring buffer is divided into BUFFER_SLOT_NUM slots, |
michael@0 | 40 | * and each slot's size is fixed(mSlotSize). |
michael@0 | 41 | * Even though the ring buffer is divided into fixed size slots, it still can |
michael@0 | 42 | * store the data which size is larger than one slot size. |
michael@0 | 43 | * */ |
michael@0 | 44 | #define BUFFER_SLOT_NUM 8192 |
michael@0 | 45 | #define BUFFER_SLOT_DEFAULT_SIZE 256 |
michael@0 | 46 | #define BUFFER_SLOT_MAX_SIZE 512 |
michael@0 | 47 | #define BUFFER_SLOT_INVALID -1 |
michael@0 | 48 | #define BUFFER_SLOT_EMPTY 0 |
michael@0 | 49 | |
michael@0 | 50 | struct BufferSlotData { |
michael@0 | 51 | int32_t mLength; |
michael@0 | 52 | uint64_t mTime; |
michael@0 | 53 | }; |
michael@0 | 54 | |
michael@0 | 55 | class RtspTrackBuffer |
michael@0 | 56 | { |
michael@0 | 57 | public: |
michael@0 | 58 | RtspTrackBuffer(const char *aMonitor, int32_t aTrackIdx, uint32_t aSlotSize) |
michael@0 | 59 | : mMonitor(aMonitor) |
michael@0 | 60 | , mSlotSize(aSlotSize) |
michael@0 | 61 | , mTotalBufferSize(BUFFER_SLOT_NUM * mSlotSize) |
michael@0 | 62 | , mFrameType(0) |
michael@0 | 63 | , mIsStarted(false) { |
michael@0 | 64 | MOZ_COUNT_CTOR(RtspTrackBuffer); |
michael@0 | 65 | #ifdef PR_LOGGING |
michael@0 | 66 | mTrackIdx = aTrackIdx; |
michael@0 | 67 | #endif |
michael@0 | 68 | MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM); |
michael@0 | 69 | mRingBuffer = new uint8_t[mTotalBufferSize]; |
michael@0 | 70 | Reset(); |
michael@0 | 71 | }; |
michael@0 | 72 | ~RtspTrackBuffer() { |
michael@0 | 73 | MOZ_COUNT_DTOR(RtspTrackBuffer); |
michael@0 | 74 | mRingBuffer = nullptr; |
michael@0 | 75 | }; |
michael@0 | 76 | |
michael@0 | 77 | size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { |
michael@0 | 78 | // including this |
michael@0 | 79 | size_t size = aMallocSizeOf(this); |
michael@0 | 80 | |
michael@0 | 81 | // excluding this |
michael@0 | 82 | size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 83 | |
michael@0 | 84 | return size; |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | void Start() { |
michael@0 | 88 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 89 | mIsStarted = true; |
michael@0 | 90 | mFrameType = 0; |
michael@0 | 91 | } |
michael@0 | 92 | void Stop() { |
michael@0 | 93 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 94 | mIsStarted = false; |
michael@0 | 95 | } |
michael@0 | 96 | |
michael@0 | 97 | // Read the data from mRingBuffer[mConsumerIdx*mSlotSize] into aToBuffer. |
michael@0 | 98 | // If the aToBufferSize is smaller than mBufferSlotDataLength[mConsumerIdx], |
michael@0 | 99 | // early return and set the aFrameSize to notify the reader the aToBuffer |
michael@0 | 100 | // doesn't have enough space. The reader must realloc the aToBuffer if it |
michael@0 | 101 | // wishes to read the data. |
michael@0 | 102 | nsresult ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, |
michael@0 | 103 | uint32_t& aReadCount, uint64_t& aFrameTime, |
michael@0 | 104 | uint32_t& aFrameSize); |
michael@0 | 105 | // Write the data from aFromBuffer into mRingBuffer[mProducerIdx*mSlotSize]. |
michael@0 | 106 | void WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, |
michael@0 | 107 | uint64_t aFrameTime, uint32_t aFrameType); |
michael@0 | 108 | // Reset the mProducerIdx, mConsumerIdx, mBufferSlotDataLength[], |
michael@0 | 109 | // mBufferSlotDataTime[]. |
michael@0 | 110 | void Reset(); |
michael@0 | 111 | |
michael@0 | 112 | // We should call SetFrameType first then reset(). |
michael@0 | 113 | // If we call reset() first, the queue may still has some "garbage" frame |
michael@0 | 114 | // from another thread's |OnMediaDataAvailable| before |SetFrameType|. |
michael@0 | 115 | void ResetWithFrameType(uint32_t aFrameType) { |
michael@0 | 116 | SetFrameType(aFrameType); |
michael@0 | 117 | Reset(); |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | private: |
michael@0 | 121 | // The FrameType is sync to nsIStreamingProtocolController.h |
michael@0 | 122 | void SetFrameType(uint32_t aFrameType) { |
michael@0 | 123 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 124 | mFrameType = mFrameType | aFrameType; |
michael@0 | 125 | } |
michael@0 | 126 | |
michael@0 | 127 | // A monitor lock to prevent racing condition. |
michael@0 | 128 | Monitor mMonitor; |
michael@0 | 129 | #ifdef PR_LOGGING |
michael@0 | 130 | // Indicate the track number for Rtsp. |
michael@0 | 131 | int32_t mTrackIdx; |
michael@0 | 132 | #endif |
michael@0 | 133 | // mProducerIdx: A slot index that we store data from |
michael@0 | 134 | // nsIStreamingProtocolController. |
michael@0 | 135 | // mConsumerIdx: A slot index that we read when decoder need(from OMX decoder). |
michael@0 | 136 | int32_t mProducerIdx; |
michael@0 | 137 | int32_t mConsumerIdx; |
michael@0 | 138 | |
michael@0 | 139 | // Because each slot's size is fixed, we need an array to record the real |
michael@0 | 140 | // data length and data time stamp. |
michael@0 | 141 | // The value in mBufferSlotData[index].mLength represents: |
michael@0 | 142 | // -1(BUFFER_SLOT_INVALID): The index of slot data is invalid, mConsumerIdx |
michael@0 | 143 | // should go forward. |
michael@0 | 144 | // 0(BUFFER_SLOT_EMPTY): The index slot is empty. mConsumerIdx should wait here. |
michael@0 | 145 | // positive value: The index slot contains valid data and the value is data size. |
michael@0 | 146 | BufferSlotData mBufferSlotData[BUFFER_SLOT_NUM]; |
michael@0 | 147 | |
michael@0 | 148 | // The ring buffer pointer. |
michael@0 | 149 | nsAutoArrayPtr<uint8_t> mRingBuffer; |
michael@0 | 150 | // Each slot's size. |
michael@0 | 151 | uint32_t mSlotSize; |
michael@0 | 152 | // Total mRingBuffer's total size. |
michael@0 | 153 | uint32_t mTotalBufferSize; |
michael@0 | 154 | // A flag that that indicate the incoming data should be dropped or stored. |
michael@0 | 155 | // When we are seeking, the incoming data should be dropped. |
michael@0 | 156 | // Bit definition in |nsIStreamingProtocolController.h| |
michael@0 | 157 | uint32_t mFrameType; |
michael@0 | 158 | |
michael@0 | 159 | // Set true/false when |Start()/Stop()| is called. |
michael@0 | 160 | bool mIsStarted; |
michael@0 | 161 | }; |
michael@0 | 162 | |
michael@0 | 163 | nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize, |
michael@0 | 164 | uint32_t& aReadCount, uint64_t& aFrameTime, |
michael@0 | 165 | uint32_t& aFrameSize) |
michael@0 | 166 | { |
michael@0 | 167 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 168 | RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d " |
michael@0 | 169 | "mBufferSlotData[mConsumerIdx].mLength %d" |
michael@0 | 170 | ,mTrackIdx ,mProducerIdx ,mConsumerIdx |
michael@0 | 171 | ,mBufferSlotData[mConsumerIdx].mLength); |
michael@0 | 172 | // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID. |
michael@0 | 173 | // The loop ends when |
michael@0 | 174 | // 1. Read data successfully |
michael@0 | 175 | // 2. Fail to read data due to aToBuffer's space |
michael@0 | 176 | // 3. No data in this buffer |
michael@0 | 177 | // 4. mIsStarted is not set |
michael@0 | 178 | while (1) { |
michael@0 | 179 | if (mBufferSlotData[mConsumerIdx].mLength > 0) { |
michael@0 | 180 | // Check the aToBuffer space is enough for data copy. |
michael@0 | 181 | if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) { |
michael@0 | 182 | aFrameSize = mBufferSlotData[mConsumerIdx].mLength; |
michael@0 | 183 | break; |
michael@0 | 184 | } |
michael@0 | 185 | uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1; |
michael@0 | 186 | // we have data, copy to aToBuffer |
michael@0 | 187 | MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <= |
michael@0 | 188 | (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize)); |
michael@0 | 189 | memcpy(aToBuffer, |
michael@0 | 190 | (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]), |
michael@0 | 191 | mBufferSlotData[mConsumerIdx].mLength); |
michael@0 | 192 | |
michael@0 | 193 | aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength; |
michael@0 | 194 | aFrameTime = mBufferSlotData[mConsumerIdx].mTime; |
michael@0 | 195 | RTSPMLOG("DataLength %d, data time %lld" |
michael@0 | 196 | ,mBufferSlotData[mConsumerIdx].mLength |
michael@0 | 197 | ,mBufferSlotData[mConsumerIdx].mTime); |
michael@0 | 198 | // After reading the data, we set current index of mBufferSlotDataLength |
michael@0 | 199 | // to BUFFER_SLOT_EMPTY to indicate these slots are free. |
michael@0 | 200 | for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) { |
michael@0 | 201 | mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; |
michael@0 | 202 | mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; |
michael@0 | 203 | } |
michael@0 | 204 | mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM; |
michael@0 | 205 | break; |
michael@0 | 206 | } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) { |
michael@0 | 207 | mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM; |
michael@0 | 208 | RTSPMLOG("BUFFER_SLOT_INVALID move forward"); |
michael@0 | 209 | } else { |
michael@0 | 210 | // No data, and disconnected. |
michael@0 | 211 | if (!mIsStarted) { |
michael@0 | 212 | return NS_ERROR_FAILURE; |
michael@0 | 213 | } |
michael@0 | 214 | // No data, the decode thread is blocked here until we receive |
michael@0 | 215 | // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer() |
michael@0 | 216 | // to wake up the decode thread. |
michael@0 | 217 | RTSPMLOG("monitor.Wait()"); |
michael@0 | 218 | monitor.Wait(); |
michael@0 | 219 | } |
michael@0 | 220 | } |
michael@0 | 221 | return NS_OK; |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | /* When we perform a WriteBuffer, we check mIsStarted and aFrameType first. |
michael@0 | 225 | * These flags prevent "garbage" frames from being written into the buffer. |
michael@0 | 226 | * |
michael@0 | 227 | * After writing the data into the buffer, we check to see if we wrote over a |
michael@0 | 228 | * slot, and update mConsumerIdx if necessary. |
michael@0 | 229 | * This ensures that the decoder will get the "oldest" data available in the |
michael@0 | 230 | * buffer. |
michael@0 | 231 | * |
michael@0 | 232 | * If the incoming data is larger than one slot size (isMultipleSlots), we do |
michael@0 | 233 | * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the |
michael@0 | 234 | * first slot, in order to notify the reader that some slots are unavailable. |
michael@0 | 235 | * |
michael@0 | 236 | * If the incoming data is isMultipleSlots and crosses the end of |
michael@0 | 237 | * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to |
michael@0 | 238 | * be written from head(index 0). |
michael@0 | 239 | * |
michael@0 | 240 | * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking. |
michael@0 | 241 | * */ |
michael@0 | 242 | void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount, |
michael@0 | 243 | uint64_t aFrameTime, uint32_t aFrameType) |
michael@0 | 244 | { |
michael@0 | 245 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 246 | if (!mIsStarted) { |
michael@0 | 247 | RTSPMLOG("mIsStarted is false"); |
michael@0 | 248 | return; |
michael@0 | 249 | } |
michael@0 | 250 | if (mTotalBufferSize < aWriteCount) { |
michael@0 | 251 | RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large"); |
michael@0 | 252 | return; |
michael@0 | 253 | } |
michael@0 | 254 | // Checking the incoming data's frame type. |
michael@0 | 255 | // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType |
michael@0 | 256 | // imply the RtspTrackBuffer is ready for receive data. |
michael@0 | 257 | if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { |
michael@0 | 258 | mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY); |
michael@0 | 259 | RTSPMLOG("Clear mFrameType"); |
michael@0 | 260 | return; |
michael@0 | 261 | } |
michael@0 | 262 | // Checking current buffer frame type. |
michael@0 | 263 | // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the |
michael@0 | 264 | // RtspTrackBuffer can't receive data now. So we drop the frame until we |
michael@0 | 265 | // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY. |
michael@0 | 266 | if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) { |
michael@0 | 267 | RTSPMLOG("Return because the mFrameType is set"); |
michael@0 | 268 | return; |
michael@0 | 269 | } |
michael@0 | 270 | // The flag is true if the incoming data is larger than one slot size. |
michael@0 | 271 | bool isMultipleSlots = false; |
michael@0 | 272 | // The flag is true if the incoming data is larger than remainder free slots |
michael@0 | 273 | bool returnToHead = false; |
michael@0 | 274 | // Calculate how many slots the incoming data needed. |
michael@0 | 275 | int32_t slots = 1; |
michael@0 | 276 | int32_t i; |
michael@0 | 277 | RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d", |
michael@0 | 278 | mTrackIdx, mProducerIdx,mConsumerIdx); |
michael@0 | 279 | if (aWriteCount > mSlotSize) { |
michael@0 | 280 | isMultipleSlots = true; |
michael@0 | 281 | slots = (aWriteCount / mSlotSize) + 1; |
michael@0 | 282 | } |
michael@0 | 283 | if (isMultipleSlots && |
michael@0 | 284 | (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) { |
michael@0 | 285 | returnToHead = true; |
michael@0 | 286 | } |
michael@0 | 287 | RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d", |
michael@0 | 288 | slots, isMultipleSlots, returnToHead); |
michael@0 | 289 | if (returnToHead) { |
michael@0 | 290 | // Clear the rest index of mBufferSlotData[].mLength |
michael@0 | 291 | for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) { |
michael@0 | 292 | mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; |
michael@0 | 293 | } |
michael@0 | 294 | // We wrote one or more slots that the decode thread has not yet read. |
michael@0 | 295 | // So the mConsumerIdx returns to the head of slot buffer and moves forward |
michael@0 | 296 | // to the oldest slot. |
michael@0 | 297 | if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) { |
michael@0 | 298 | mConsumerIdx = 0; |
michael@0 | 299 | for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) { |
michael@0 | 300 | if (mBufferSlotData[i].mLength > 0) { |
michael@0 | 301 | mConsumerIdx = i; |
michael@0 | 302 | break; |
michael@0 | 303 | } |
michael@0 | 304 | } |
michael@0 | 305 | } |
michael@0 | 306 | mProducerIdx = 0; |
michael@0 | 307 | } |
michael@0 | 308 | |
michael@0 | 309 | memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount); |
michael@0 | 310 | |
michael@0 | 311 | if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots |
michael@0 | 312 | && mBufferSlotData[mConsumerIdx].mLength > 0) { |
michael@0 | 313 | // Wrote one or more slots that the decode thread has not yet read. |
michael@0 | 314 | RTSPMLOG("overwrite!! %d time %lld" |
michael@0 | 315 | ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime); |
michael@0 | 316 | mBufferSlotData[mProducerIdx].mLength = aWriteCount; |
michael@0 | 317 | mBufferSlotData[mProducerIdx].mTime = aFrameTime; |
michael@0 | 318 | // Clear the mBufferSlotDataLength except the start slot. |
michael@0 | 319 | if (isMultipleSlots) { |
michael@0 | 320 | for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { |
michael@0 | 321 | mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; |
michael@0 | 322 | } |
michael@0 | 323 | } |
michael@0 | 324 | mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; |
michael@0 | 325 | // Move the mConsumerIdx forward to ensure that the decoder reads the |
michael@0 | 326 | // oldest data available. |
michael@0 | 327 | mConsumerIdx = mProducerIdx; |
michael@0 | 328 | } else { |
michael@0 | 329 | // Normal case, the writer doesn't take over the reader. |
michael@0 | 330 | mBufferSlotData[mProducerIdx].mLength = aWriteCount; |
michael@0 | 331 | mBufferSlotData[mProducerIdx].mTime = aFrameTime; |
michael@0 | 332 | // Clear the mBufferSlotData[].mLength except the start slot. |
michael@0 | 333 | if (isMultipleSlots) { |
michael@0 | 334 | for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) { |
michael@0 | 335 | mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID; |
michael@0 | 336 | } |
michael@0 | 337 | } |
michael@0 | 338 | mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM; |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | mMonitor.NotifyAll(); |
michael@0 | 342 | } |
michael@0 | 343 | |
michael@0 | 344 | void RtspTrackBuffer::Reset() { |
michael@0 | 345 | MonitorAutoLock monitor(mMonitor); |
michael@0 | 346 | mProducerIdx = 0; |
michael@0 | 347 | mConsumerIdx = 0; |
michael@0 | 348 | for (uint32_t i = 0; i < BUFFER_SLOT_NUM; ++i) { |
michael@0 | 349 | mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY; |
michael@0 | 350 | mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY; |
michael@0 | 351 | } |
michael@0 | 352 | mMonitor.NotifyAll(); |
michael@0 | 353 | } |
michael@0 | 354 | |
michael@0 | 355 | RtspMediaResource::RtspMediaResource(MediaDecoder* aDecoder, |
michael@0 | 356 | nsIChannel* aChannel, nsIURI* aURI, const nsACString& aContentType) |
michael@0 | 357 | : BaseMediaResource(aDecoder, aChannel, aURI, aContentType) |
michael@0 | 358 | , mIsConnected(false) |
michael@0 | 359 | , mRealTime(false) |
michael@0 | 360 | { |
michael@0 | 361 | #ifndef NECKO_PROTOCOL_rtsp |
michael@0 | 362 | MOZ_CRASH("Should not be called except for B2G platform"); |
michael@0 | 363 | #else |
michael@0 | 364 | MOZ_ASSERT(aChannel); |
michael@0 | 365 | mMediaStreamController = |
michael@0 | 366 | static_cast<RtspChannelChild*>(aChannel)->GetController(); |
michael@0 | 367 | MOZ_ASSERT(mMediaStreamController); |
michael@0 | 368 | mListener = new Listener(this); |
michael@0 | 369 | mMediaStreamController->AsyncOpen(mListener); |
michael@0 | 370 | #ifdef PR_LOGGING |
michael@0 | 371 | if (!gRtspMediaResourceLog) { |
michael@0 | 372 | gRtspMediaResourceLog = PR_NewLogModule("RtspMediaResource"); |
michael@0 | 373 | } |
michael@0 | 374 | #endif |
michael@0 | 375 | #endif |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | RtspMediaResource::~RtspMediaResource() |
michael@0 | 379 | { |
michael@0 | 380 | RTSPMLOG("~RtspMediaResource"); |
michael@0 | 381 | if (mListener) { |
michael@0 | 382 | // Kill its reference to us since we're going away |
michael@0 | 383 | mListener->Revoke(); |
michael@0 | 384 | } |
michael@0 | 385 | } |
michael@0 | 386 | |
michael@0 | 387 | size_t |
michael@0 | 388 | RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
michael@0 | 389 | { |
michael@0 | 390 | size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 391 | size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 392 | |
michael@0 | 393 | // Include the size of each track buffer. |
michael@0 | 394 | for (size_t i = 0; i < mTrackBuffer.Length(); i++) { |
michael@0 | 395 | size += mTrackBuffer[i]->SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 396 | } |
michael@0 | 397 | |
michael@0 | 398 | // Could add in the future: |
michael@0 | 399 | // - mMediaStreamController |
michael@0 | 400 | |
michael@0 | 401 | return size; |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | //---------------------------------------------------------------------------- |
michael@0 | 405 | // RtspMediaResource::Listener |
michael@0 | 406 | //---------------------------------------------------------------------------- |
michael@0 | 407 | NS_IMPL_ISUPPORTS(RtspMediaResource::Listener, |
michael@0 | 408 | nsIInterfaceRequestor, nsIStreamingProtocolListener); |
michael@0 | 409 | |
michael@0 | 410 | nsresult |
michael@0 | 411 | RtspMediaResource::Listener::OnMediaDataAvailable(uint8_t aTrackIdx, |
michael@0 | 412 | const nsACString &data, |
michael@0 | 413 | uint32_t length, |
michael@0 | 414 | uint32_t offset, |
michael@0 | 415 | nsIStreamingProtocolMetaData *meta) |
michael@0 | 416 | { |
michael@0 | 417 | if (!mResource) |
michael@0 | 418 | return NS_OK; |
michael@0 | 419 | return mResource->OnMediaDataAvailable(aTrackIdx, data, length, offset, meta); |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | nsresult |
michael@0 | 423 | RtspMediaResource::Listener::OnConnected(uint8_t aTrackIdx, |
michael@0 | 424 | nsIStreamingProtocolMetaData *meta) |
michael@0 | 425 | { |
michael@0 | 426 | if (!mResource) |
michael@0 | 427 | return NS_OK; |
michael@0 | 428 | return mResource->OnConnected(aTrackIdx, meta); |
michael@0 | 429 | } |
michael@0 | 430 | |
michael@0 | 431 | nsresult |
michael@0 | 432 | RtspMediaResource::Listener::OnDisconnected(uint8_t aTrackIdx, nsresult reason) |
michael@0 | 433 | { |
michael@0 | 434 | if (!mResource) |
michael@0 | 435 | return NS_OK; |
michael@0 | 436 | return mResource->OnDisconnected(aTrackIdx, reason); |
michael@0 | 437 | } |
michael@0 | 438 | |
michael@0 | 439 | nsresult |
michael@0 | 440 | RtspMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult) |
michael@0 | 441 | { |
michael@0 | 442 | return QueryInterface(aIID, aResult); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | void |
michael@0 | 446 | RtspMediaResource::Listener::Revoke() |
michael@0 | 447 | { |
michael@0 | 448 | NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); |
michael@0 | 449 | if (mResource) { |
michael@0 | 450 | mResource = nullptr; |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | nsresult |
michael@0 | 455 | RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize, |
michael@0 | 456 | uint32_t aTrackIdx, uint32_t& aBytes, |
michael@0 | 457 | uint64_t& aTime, uint32_t& aFrameSize) |
michael@0 | 458 | { |
michael@0 | 459 | NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); |
michael@0 | 460 | NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(), |
michael@0 | 461 | "ReadTrack index > mTrackBuffer"); |
michael@0 | 462 | MOZ_ASSERT(aBuffer); |
michael@0 | 463 | |
michael@0 | 464 | return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes, |
michael@0 | 465 | aTime, aFrameSize); |
michael@0 | 466 | } |
michael@0 | 467 | |
michael@0 | 468 | nsresult |
michael@0 | 469 | RtspMediaResource::OnMediaDataAvailable(uint8_t aTrackIdx, |
michael@0 | 470 | const nsACString &data, |
michael@0 | 471 | uint32_t length, |
michael@0 | 472 | uint32_t offset, |
michael@0 | 473 | nsIStreamingProtocolMetaData *meta) |
michael@0 | 474 | { |
michael@0 | 475 | uint64_t time; |
michael@0 | 476 | uint32_t frameType; |
michael@0 | 477 | meta->GetTimeStamp(&time); |
michael@0 | 478 | meta->GetFrameType(&frameType); |
michael@0 | 479 | if (mRealTime) { |
michael@0 | 480 | time = 0; |
michael@0 | 481 | } |
michael@0 | 482 | mTrackBuffer[aTrackIdx]->WriteBuffer(data.BeginReading(), length, time, |
michael@0 | 483 | frameType); |
michael@0 | 484 | return NS_OK; |
michael@0 | 485 | } |
michael@0 | 486 | |
michael@0 | 487 | // Bug 962309 - Video RTSP support should be disabled in 1.3 |
michael@0 | 488 | bool |
michael@0 | 489 | RtspMediaResource::IsVideoEnabled() |
michael@0 | 490 | { |
michael@0 | 491 | return Preferences::GetBool("media.rtsp.video.enabled", false); |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | bool |
michael@0 | 495 | RtspMediaResource::IsVideo(uint8_t tracks, nsIStreamingProtocolMetaData *meta) |
michael@0 | 496 | { |
michael@0 | 497 | bool isVideo = false; |
michael@0 | 498 | for (int i = 0; i < tracks; ++i) { |
michael@0 | 499 | nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta; |
michael@0 | 500 | mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); |
michael@0 | 501 | MOZ_ASSERT(trackMeta); |
michael@0 | 502 | uint32_t w = 0, h = 0; |
michael@0 | 503 | trackMeta->GetWidth(&w); |
michael@0 | 504 | trackMeta->GetHeight(&h); |
michael@0 | 505 | if (w > 0 || h > 0) { |
michael@0 | 506 | isVideo = true; |
michael@0 | 507 | break; |
michael@0 | 508 | } |
michael@0 | 509 | } |
michael@0 | 510 | return isVideo; |
michael@0 | 511 | } |
michael@0 | 512 | |
michael@0 | 513 | nsresult |
michael@0 | 514 | RtspMediaResource::OnConnected(uint8_t aTrackIdx, |
michael@0 | 515 | nsIStreamingProtocolMetaData *meta) |
michael@0 | 516 | { |
michael@0 | 517 | if (mIsConnected) { |
michael@0 | 518 | for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { |
michael@0 | 519 | mTrackBuffer[i]->Start(); |
michael@0 | 520 | } |
michael@0 | 521 | return NS_OK; |
michael@0 | 522 | } |
michael@0 | 523 | |
michael@0 | 524 | uint8_t tracks; |
michael@0 | 525 | mMediaStreamController->GetTotalTracks(&tracks); |
michael@0 | 526 | |
michael@0 | 527 | // If the preference of RTSP video feature is not enabled and the streaming is |
michael@0 | 528 | // video, we give up moving forward. |
michael@0 | 529 | if (!IsVideoEnabled() && IsVideo(tracks, meta)) { |
michael@0 | 530 | // Give up, report error to media element. |
michael@0 | 531 | nsCOMPtr<nsIRunnable> event = |
michael@0 | 532 | NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); |
michael@0 | 533 | NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
michael@0 | 534 | return NS_ERROR_FAILURE; |
michael@0 | 535 | } |
michael@0 | 536 | uint64_t duration = 0; |
michael@0 | 537 | for (int i = 0; i < tracks; ++i) { |
michael@0 | 538 | nsCString rtspTrackId("RtspTrack"); |
michael@0 | 539 | rtspTrackId.AppendInt(i); |
michael@0 | 540 | nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta; |
michael@0 | 541 | mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta)); |
michael@0 | 542 | MOZ_ASSERT(trackMeta); |
michael@0 | 543 | trackMeta->GetDuration(&duration); |
michael@0 | 544 | |
michael@0 | 545 | // Here is a heuristic to estimate the slot size. |
michael@0 | 546 | // For video track, calculate the width*height. |
michael@0 | 547 | // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0. |
michael@0 | 548 | // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE) |
michael@0 | 549 | uint32_t w, h; |
michael@0 | 550 | uint32_t slotSize; |
michael@0 | 551 | trackMeta->GetWidth(&w); |
michael@0 | 552 | trackMeta->GetHeight(&h); |
michael@0 | 553 | slotSize = clamped((int32_t)(w * h), BUFFER_SLOT_DEFAULT_SIZE, |
michael@0 | 554 | BUFFER_SLOT_MAX_SIZE); |
michael@0 | 555 | mTrackBuffer.AppendElement(new RtspTrackBuffer(rtspTrackId.get(), |
michael@0 | 556 | i, slotSize)); |
michael@0 | 557 | mTrackBuffer[i]->Start(); |
michael@0 | 558 | } |
michael@0 | 559 | |
michael@0 | 560 | if (!mDecoder) { |
michael@0 | 561 | return NS_ERROR_FAILURE; |
michael@0 | 562 | } |
michael@0 | 563 | |
michael@0 | 564 | // If the duration is 0, imply the stream is live stream. |
michael@0 | 565 | if (duration) { |
michael@0 | 566 | // Not live stream. |
michael@0 | 567 | mRealTime = false; |
michael@0 | 568 | bool seekable = true; |
michael@0 | 569 | mDecoder->SetInfinite(false); |
michael@0 | 570 | mDecoder->SetTransportSeekable(seekable); |
michael@0 | 571 | mDecoder->SetDuration(duration); |
michael@0 | 572 | } else { |
michael@0 | 573 | // Live stream. |
michael@0 | 574 | // Check the preference "media.realtime_decoder.enabled". |
michael@0 | 575 | if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) { |
michael@0 | 576 | // Give up, report error to media element. |
michael@0 | 577 | nsCOMPtr<nsIRunnable> event = |
michael@0 | 578 | NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError); |
michael@0 | 579 | NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); |
michael@0 | 580 | return NS_ERROR_FAILURE; |
michael@0 | 581 | } else { |
michael@0 | 582 | mRealTime = true; |
michael@0 | 583 | bool seekable = false; |
michael@0 | 584 | mDecoder->SetInfinite(true); |
michael@0 | 585 | mDecoder->SetTransportSeekable(seekable); |
michael@0 | 586 | mDecoder->SetMediaSeekable(seekable); |
michael@0 | 587 | } |
michael@0 | 588 | } |
michael@0 | 589 | // Fires an initial progress event and sets up the stall counter so stall events |
michael@0 | 590 | // fire if no download occurs within the required time frame. |
michael@0 | 591 | mDecoder->Progress(false); |
michael@0 | 592 | |
michael@0 | 593 | MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); |
michael@0 | 594 | NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE); |
michael@0 | 595 | dom::HTMLMediaElement* element = owner->GetMediaElement(); |
michael@0 | 596 | NS_ENSURE_TRUE(element, NS_ERROR_FAILURE); |
michael@0 | 597 | |
michael@0 | 598 | element->FinishDecoderSetup(mDecoder, this); |
michael@0 | 599 | mIsConnected = true; |
michael@0 | 600 | |
michael@0 | 601 | return NS_OK; |
michael@0 | 602 | } |
michael@0 | 603 | |
michael@0 | 604 | nsresult |
michael@0 | 605 | RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason) |
michael@0 | 606 | { |
michael@0 | 607 | NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); |
michael@0 | 608 | |
michael@0 | 609 | for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { |
michael@0 | 610 | mTrackBuffer[i]->Stop(); |
michael@0 | 611 | mTrackBuffer[i]->Reset(); |
michael@0 | 612 | } |
michael@0 | 613 | |
michael@0 | 614 | if (mDecoder) { |
michael@0 | 615 | if (aReason == NS_ERROR_NOT_INITIALIZED || |
michael@0 | 616 | aReason == NS_ERROR_CONNECTION_REFUSED || |
michael@0 | 617 | aReason == NS_ERROR_NOT_CONNECTED || |
michael@0 | 618 | aReason == NS_ERROR_NET_TIMEOUT) { |
michael@0 | 619 | // Report error code to Decoder. |
michael@0 | 620 | RTSPMLOG("Error in OnDisconnected 0x%x", aReason); |
michael@0 | 621 | mDecoder->NetworkError(); |
michael@0 | 622 | } else { |
michael@0 | 623 | // Resetting the decoder and media element when the connection |
michael@0 | 624 | // between RTSP client and server goes down. |
michael@0 | 625 | mDecoder->ResetConnectionState(); |
michael@0 | 626 | } |
michael@0 | 627 | } |
michael@0 | 628 | |
michael@0 | 629 | if (mListener) { |
michael@0 | 630 | // Note: Listener's Revoke() kills its reference to us, which means it would |
michael@0 | 631 | // release |this| object. So, ensure it is called in the end of this method. |
michael@0 | 632 | mListener->Revoke(); |
michael@0 | 633 | } |
michael@0 | 634 | |
michael@0 | 635 | return NS_OK; |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | void RtspMediaResource::Suspend(bool aCloseImmediately) |
michael@0 | 639 | { |
michael@0 | 640 | NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); |
michael@0 | 641 | if (NS_WARN_IF(!mDecoder)) { |
michael@0 | 642 | return; |
michael@0 | 643 | } |
michael@0 | 644 | |
michael@0 | 645 | MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); |
michael@0 | 646 | NS_ENSURE_TRUE_VOID(owner); |
michael@0 | 647 | dom::HTMLMediaElement* element = owner->GetMediaElement(); |
michael@0 | 648 | NS_ENSURE_TRUE_VOID(element); |
michael@0 | 649 | |
michael@0 | 650 | mMediaStreamController->Suspend(); |
michael@0 | 651 | element->DownloadSuspended(); |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | void RtspMediaResource::Resume() |
michael@0 | 655 | { |
michael@0 | 656 | NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread"); |
michael@0 | 657 | if (NS_WARN_IF(!mDecoder)) { |
michael@0 | 658 | return; |
michael@0 | 659 | } |
michael@0 | 660 | |
michael@0 | 661 | MediaDecoderOwner* owner = mDecoder->GetMediaOwner(); |
michael@0 | 662 | NS_ENSURE_TRUE_VOID(owner); |
michael@0 | 663 | dom::HTMLMediaElement* element = owner->GetMediaElement(); |
michael@0 | 664 | NS_ENSURE_TRUE_VOID(element); |
michael@0 | 665 | |
michael@0 | 666 | if (mChannel) { |
michael@0 | 667 | element->DownloadResumed(); |
michael@0 | 668 | } |
michael@0 | 669 | mMediaStreamController->Resume(); |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener) |
michael@0 | 673 | { |
michael@0 | 674 | return NS_OK; |
michael@0 | 675 | } |
michael@0 | 676 | |
michael@0 | 677 | nsresult RtspMediaResource::Close() |
michael@0 | 678 | { |
michael@0 | 679 | NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); |
michael@0 | 680 | mMediaStreamController->Stop(); |
michael@0 | 681 | // Since mDecoder is not an nsCOMPtr in BaseMediaResource, we have to |
michael@0 | 682 | // explicitly set it as null pointer in order to prevent misuse from this |
michael@0 | 683 | // object (RtspMediaResource). |
michael@0 | 684 | if (mDecoder) { |
michael@0 | 685 | mDecoder = nullptr; |
michael@0 | 686 | } |
michael@0 | 687 | return NS_OK; |
michael@0 | 688 | } |
michael@0 | 689 | |
michael@0 | 690 | already_AddRefed<nsIPrincipal> RtspMediaResource::GetCurrentPrincipal() |
michael@0 | 691 | { |
michael@0 | 692 | NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); |
michael@0 | 693 | |
michael@0 | 694 | nsCOMPtr<nsIPrincipal> principal; |
michael@0 | 695 | nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); |
michael@0 | 696 | if (!secMan || !mChannel) |
michael@0 | 697 | return nullptr; |
michael@0 | 698 | secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); |
michael@0 | 699 | return principal.forget(); |
michael@0 | 700 | } |
michael@0 | 701 | |
michael@0 | 702 | nsresult RtspMediaResource::SeekTime(int64_t aOffset) |
michael@0 | 703 | { |
michael@0 | 704 | NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); |
michael@0 | 705 | |
michael@0 | 706 | RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]", |
michael@0 | 707 | aOffset, mDecoder); |
michael@0 | 708 | // Clear buffer and raise the frametype flag. |
michael@0 | 709 | for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) { |
michael@0 | 710 | mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY); |
michael@0 | 711 | } |
michael@0 | 712 | |
michael@0 | 713 | return mMediaStreamController->Seek(aOffset); |
michael@0 | 714 | } |
michael@0 | 715 | |
michael@0 | 716 | } // namespace mozilla |
michael@0 | 717 |