Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
7 #include "mozilla/DebugOnly.h"
9 #include "RtspMediaResource.h"
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;
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
35 namespace mozilla {
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
50 struct BufferSlotData {
51 int32_t mLength;
52 uint64_t mTime;
53 };
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 };
77 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
78 // including this
79 size_t size = aMallocSizeOf(this);
81 // excluding this
82 size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf);
84 return size;
85 }
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 }
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();
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 }
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 }
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;
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];
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;
159 // Set true/false when |Start()/Stop()| is called.
160 bool mIsStarted;
161 };
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);
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 }
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 }
309 memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount);
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 }
341 mMonitor.NotifyAll();
342 }
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 }
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 }
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 }
387 size_t
388 RtspMediaResource::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
389 {
390 size_t size = BaseMediaResource::SizeOfExcludingThis(aMallocSizeOf);
391 size += mTrackBuffer.SizeOfExcludingThis(aMallocSizeOf);
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 }
398 // Could add in the future:
399 // - mMediaStreamController
401 return size;
402 }
404 //----------------------------------------------------------------------------
405 // RtspMediaResource::Listener
406 //----------------------------------------------------------------------------
407 NS_IMPL_ISUPPORTS(RtspMediaResource::Listener,
408 nsIInterfaceRequestor, nsIStreamingProtocolListener);
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 }
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 }
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 }
439 nsresult
440 RtspMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
441 {
442 return QueryInterface(aIID, aResult);
443 }
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 }
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);
464 return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes,
465 aTime, aFrameSize);
466 }
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 }
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 }
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 }
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 }
524 uint8_t tracks;
525 mMediaStreamController->GetTotalTracks(&tracks);
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);
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 }
560 if (!mDecoder) {
561 return NS_ERROR_FAILURE;
562 }
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);
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);
598 element->FinishDecoderSetup(mDecoder, this);
599 mIsConnected = true;
601 return NS_OK;
602 }
604 nsresult
605 RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
606 {
607 NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");
609 for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
610 mTrackBuffer[i]->Stop();
611 mTrackBuffer[i]->Reset();
612 }
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 }
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 }
635 return NS_OK;
636 }
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 }
645 MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
646 NS_ENSURE_TRUE_VOID(owner);
647 dom::HTMLMediaElement* element = owner->GetMediaElement();
648 NS_ENSURE_TRUE_VOID(element);
650 mMediaStreamController->Suspend();
651 element->DownloadSuspended();
652 }
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 }
661 MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
662 NS_ENSURE_TRUE_VOID(owner);
663 dom::HTMLMediaElement* element = owner->GetMediaElement();
664 NS_ENSURE_TRUE_VOID(element);
666 if (mChannel) {
667 element->DownloadResumed();
668 }
669 mMediaStreamController->Resume();
670 }
672 nsresult RtspMediaResource::Open(nsIStreamListener **aStreamListener)
673 {
674 return NS_OK;
675 }
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 }
690 already_AddRefed<nsIPrincipal> RtspMediaResource::GetCurrentPrincipal()
691 {
692 NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
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 }
702 nsresult RtspMediaResource::SeekTime(int64_t aOffset)
703 {
704 NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
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 }
713 return mMediaStreamController->Seek(aOffset);
714 }
716 } // namespace mozilla