content/media/RtspMediaResource.cpp

changeset 0
6474c204b198
     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 +

mercurial