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