nsresult RtspTrackBuffer::ReadBuffer(uint8_t* aToBuffer, uint32_t aToBufferSize,
                                     uint32_t& aReadCount, uint64_t& aFrameTime,
                                     uint32_t& aFrameSize)
{
  MonitorAutoLock monitor(mMonitor);
  RTSPMLOG("ReadBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d "
           "mBufferSlotData[mConsumerIdx].mLength %d"
           ,mTrackIdx ,mProducerIdx ,mConsumerIdx
           ,mBufferSlotData[mConsumerIdx].mLength);
  // Reader should skip the slots with mLength==BUFFER_SLOT_INVALID.
  // The loop ends when
  // 1. Read data successfully
  // 2. Fail to read data due to aToBuffer's space
  // 3. No data in this buffer
  // 4. mIsStarted is not set
  while (1) {
    if (mBufferSlotData[mConsumerIdx].mLength > 0) {
      // Check the aToBuffer space is enough for data copy.
      if ((int32_t)aToBufferSize < mBufferSlotData[mConsumerIdx].mLength) {
        aFrameSize = mBufferSlotData[mConsumerIdx].mLength;
        break;
      }
      uint32_t slots = (mBufferSlotData[mConsumerIdx].mLength / mSlotSize) + 1;
      // we have data, copy to aToBuffer
      MOZ_ASSERT(mBufferSlotData[mConsumerIdx].mLength <=
                 (int32_t)((BUFFER_SLOT_NUM - mConsumerIdx) * mSlotSize));
      memcpy(aToBuffer,
             (void *)(&mRingBuffer[mSlotSize * mConsumerIdx]),
             mBufferSlotData[mConsumerIdx].mLength);

      aFrameSize = aReadCount = mBufferSlotData[mConsumerIdx].mLength;
      aFrameTime = mBufferSlotData[mConsumerIdx].mTime;
      RTSPMLOG("DataLength %d, data time %lld"
               ,mBufferSlotData[mConsumerIdx].mLength
               ,mBufferSlotData[mConsumerIdx].mTime);
      // After reading the data, we set current index of mBufferSlotDataLength
      // to BUFFER_SLOT_EMPTY to indicate these slots are free.
      for (uint32_t i = mConsumerIdx; i < mConsumerIdx + slots; ++i) {
        mBufferSlotData[i].mLength = BUFFER_SLOT_EMPTY;
        mBufferSlotData[i].mTime = BUFFER_SLOT_EMPTY;
      }
      mConsumerIdx = (mConsumerIdx + slots) % BUFFER_SLOT_NUM;
      break;
    } else if (mBufferSlotData[mConsumerIdx].mLength == BUFFER_SLOT_INVALID) {
      mConsumerIdx = (mConsumerIdx + 1) % BUFFER_SLOT_NUM;
      RTSPMLOG("BUFFER_SLOT_INVALID move forward");
    } else {
      // No data, and disconnected.
      if (!mIsStarted) {
        return NS_ERROR_FAILURE;
      }
      // No data, the decode thread is blocked here until we receive
      // OnMediaDataAvailable. The OnMediaDataAvailable will call WriteBuffer()
      // to wake up the decode thread.
      RTSPMLOG("monitor.Wait()");
      monitor.Wait();
    }
  }
  return NS_OK;
}
nsresult
RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
{
  NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");

  for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
    mTrackBuffer[i]->Stop();
    mTrackBuffer[i]->Reset();
  }

  if (mDecoder) {
    if (aReason == NS_ERROR_NOT_INITIALIZED ||
        aReason == NS_ERROR_CONNECTION_REFUSED ||
        aReason == NS_ERROR_NOT_CONNECTED ||
        aReason == NS_ERROR_NET_TIMEOUT) {
      // Report error code to Decoder.
      RTSPMLOG("Error in OnDisconnected 0x%x", aReason);
      mDecoder->NetworkError();
    } else {
      // Resetting the decoder and media element when the connection
      // between RTSP client and server goes down.
      mDecoder->ResetConnectionState();
    }
  }

  if (mListener) {
    // Note: Listener's Revoke() kills its reference to us, which means it would
    // release |this| object. So, ensure it is called in the end of this method.
    mListener->Revoke();
  }

  return NS_OK;
}
nsresult
RtspMediaResource::OnDisconnected(uint8_t aTrackIdx, nsresult aReason)
{
  NS_ASSERTION(NS_IsMainThread(), "Don't call on non-main thread");

  for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
    mTrackBuffer[i]->Stop();
    mTrackBuffer[i]->Reset();
  }

  // If mDecoder is null pointer, it means this OnDisconnected event is
  // triggered when media element was destroyed and mDecoder was already
  // shutdown.
  if (!mDecoder) {
    return NS_OK;
  }

  if (aReason == NS_ERROR_NOT_INITIALIZED ||
      aReason == NS_ERROR_CONNECTION_REFUSED ||
      aReason == NS_ERROR_NOT_CONNECTED) {

    RTSPMLOG("Error in OnDisconnected 0x%x", aReason);

    mDecoder->NetworkError();
    return NS_OK;
  }

  // Resetting the decoder and media element when the connection
  // between Rtsp client and server goes down.
  mDecoder->ResetConnectionState();
  return NS_OK;
}
RtspMediaResource::~RtspMediaResource()
{
  RTSPMLOG("~RtspMediaResource");
  if (mListener) {
    // Kill its reference to us since we're going away
    mListener->Revoke();
  }
}
void RtspMediaResource::NotifySuspend(bool aIsSuspend)
{
  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
  RTSPMLOG("NotifySuspend %d",aIsSuspend);

  mIsSuspend = aIsSuspend;
  if (mDecoder) {
    mDecoder->NotifySuspendedStatusChanged();
  }
}
void RtspMediaResource::SetSuspend(bool aIsSuspend)
{
  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
  RTSPMLOG("SetSuspend %d",aIsSuspend);

  nsCOMPtr<nsIRunnable> runnable =
    NS_NewRunnableMethodWithArg<bool>(this, &RtspMediaResource::NotifySuspend,
                                      aIsSuspend);
  NS_DispatchToMainThread(runnable);
}
nsresult RtspMediaResource::SeekTime(int64_t aOffset)
{
  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");

  RTSPMLOG("Seek requested for aOffset [%lld] for decoder [%p]",
           aOffset, mDecoder);
  // Clear buffer and raise the frametype flag.
  for(uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
    mTrackBuffer[i]->ResetWithFrameType(MEDIASTREAM_FRAMETYPE_DISCONTINUITY);
  }

  return mMediaStreamController->Seek(aOffset);
}
nsresult
RtspMediaResource::ReadFrameFromTrack(uint8_t* aBuffer, uint32_t aBufferSize,
                                      uint32_t aTrackIdx, uint32_t& aBytes,
                                      uint64_t& aTime, uint32_t& aFrameSize)
{
  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
  NS_ASSERTION(aTrackIdx < mTrackBuffer.Length(),
               "ReadTrack index > mTrackBuffer");
  MOZ_ASSERT(aBuffer);

  if (!mIsConnected) {
    RTSPMLOG("ReadFrameFromTrack: RTSP not connected");
    return NS_ERROR_FAILURE;
  }

  return mTrackBuffer[aTrackIdx]->ReadBuffer(aBuffer, aBufferSize, aBytes,
                                             aTime, aFrameSize);
}
/* When we perform a WriteBuffer, we check mIsStarted and aFrameType first.
 * These flags prevent "garbage" frames from being written into the buffer.
 *
 * After writing the data into the buffer, we check to see if we wrote over a
 * slot, and update mConsumerIdx if necessary.
 * This ensures that the decoder will get the "oldest" data available in the
 * buffer.
 *
 * If the incoming data is larger than one slot size (isMultipleSlots), we do
 * |mBufferSlotData[].mLength = BUFFER_SLOT_INVALID;| for other slots except the
 * first slot, in order to notify the reader that some slots are unavailable.
 *
 * If the incoming data is isMultipleSlots and crosses the end of
 * BUFFER_SLOT_NUM, returnToHead is set to true and the data will continue to
 * be written from head(index 0).
 *
 * MEDIASTREAM_FRAMETYPE_DISCONTINUITY currently is used when we are seeking.
 * */
void RtspTrackBuffer::WriteBuffer(const char *aFromBuffer, uint32_t aWriteCount,
                                  uint64_t aFrameTime, uint32_t aFrameType)
{
  MonitorAutoLock monitor(mMonitor);
  if (!mIsStarted) {
    RTSPMLOG("mIsStarted is false");
    return;
  }
  if (mTotalBufferSize < aWriteCount) {
    RTSPMLOG("mTotalBufferSize < aWriteCount, incoming data is too large");
    return;
  }
  // Checking the incoming data's frame type.
  // If we receive MEDIASTREAM_FRAMETYPE_DISCONTINUITY, clear the mFrameType
  // imply the RtspTrackBuffer is ready for receive data.
  if (aFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) {
    mFrameType = mFrameType & (~MEDIASTREAM_FRAMETYPE_DISCONTINUITY);
    RTSPMLOG("Clear mFrameType");
    return;
  }
  // Checking current buffer frame type.
  // If the MEDIASTREAM_FRAMETYPE_DISCONTINUNITY bit is set, imply the
  // RtspTrackBuffer can't receive data now. So we drop the frame until we
  // receive MEDIASTREAM_FRAMETYPE_DISCONTINUNITY.
  if (mFrameType & MEDIASTREAM_FRAMETYPE_DISCONTINUITY) {
    RTSPMLOG("Return because the mFrameType is set");
    return;
  }
  // The flag is true if the incoming data is larger than one slot size.
  bool isMultipleSlots = false;
  // The flag is true if the incoming data is larger than remainder free slots
  bool returnToHead = false;
  // Calculate how many slots the incoming data needed.
  int32_t slots = 1;
  int32_t i;
  RTSPMLOG("WriteBuffer mTrackIdx %d mProducerIdx %d mConsumerIdx %d",
           mTrackIdx, mProducerIdx,mConsumerIdx);
  if (aWriteCount > mSlotSize) {
    isMultipleSlots = true;
    slots = (aWriteCount / mSlotSize) + 1;
  }
  if (isMultipleSlots &&
      (aWriteCount > (BUFFER_SLOT_NUM - mProducerIdx) * mSlotSize)) {
    returnToHead = true;
  }
  RTSPMLOG("slots %d isMultipleSlots %d returnToHead %d",
           slots, isMultipleSlots, returnToHead);
  if (returnToHead) {
    // Clear the rest index of mBufferSlotData[].mLength
    for (i = mProducerIdx; i < BUFFER_SLOT_NUM; ++i) {
      mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
    }
    // We wrote one or more slots that the decode thread has not yet read.
    // So the mConsumerIdx returns to the head of slot buffer and moves forward
    // to the oldest slot.
    if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots) {
      mConsumerIdx = 0;
      for (i = mConsumerIdx; i < BUFFER_SLOT_NUM; ++i) {
        if (mBufferSlotData[i].mLength > 0) {
          mConsumerIdx = i;
          break;
        }
      }
    }
    mProducerIdx = 0;
  }

  if (!(aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM)) {
    memcpy(&(mRingBuffer[mSlotSize * mProducerIdx]), aFromBuffer, aWriteCount);
  }

  if (mProducerIdx <= mConsumerIdx && mConsumerIdx < mProducerIdx + slots
      && mBufferSlotData[mConsumerIdx].mLength > 0) {
    // Wrote one or more slots that the decode thread has not yet read.
    RTSPMLOG("overwrite!! %d time %lld"
             ,mTrackIdx,mBufferSlotData[mConsumerIdx].mTime);
    if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) {
      mBufferSlotData[mProducerIdx].mLength = 0;
      mBufferSlotData[mProducerIdx].mTime = 0;
    } else {
      mBufferSlotData[mProducerIdx].mLength = aWriteCount;
      mBufferSlotData[mProducerIdx].mTime = aFrameTime;
    }
    mBufferSlotData[mProducerIdx].mFrameType = aFrameType;
    // Clear the mBufferSlotDataLength except the start slot.
    if (isMultipleSlots) {
      for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) {
        mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
      }
    }
    mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM;
    // Move the mConsumerIdx forward to ensure that the decoder reads the
    // oldest data available.
    mConsumerIdx = mProducerIdx;
  } else {
    // Normal case, the writer doesn't take over the reader.
    if (aFrameType & MEDIASTREAM_FRAMETYPE_END_OF_STREAM) {
      mBufferSlotData[mProducerIdx].mLength = 0;
      mBufferSlotData[mProducerIdx].mTime = 0;
    } else {
      mBufferSlotData[mProducerIdx].mLength = aWriteCount;
      mBufferSlotData[mProducerIdx].mTime = aFrameTime;
    }
    mBufferSlotData[mProducerIdx].mFrameType = aFrameType;
    // Clear the mBufferSlotData[].mLength except the start slot.
    if (isMultipleSlots) {
      for (i = mProducerIdx + 1; i < mProducerIdx + slots; ++i) {
        mBufferSlotData[i].mLength = BUFFER_SLOT_INVALID;
      }
    }
    mProducerIdx = (mProducerIdx + slots) % BUFFER_SLOT_NUM;
  }

  mMonitor.NotifyAll();
}