Exemple #1
0
nsresult
ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
{
  NS_ASSERTION(mChannel.get() == aRequest, "Wrong channel!");

  MediaDecoderOwner* owner = mCallback->GetMediaOwner();
  NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
  dom::HTMLMediaElement* element = owner->GetMediaElement();
  NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
  nsresult status;
  nsresult rv = aRequest->GetStatus(&status);
  NS_ENSURE_SUCCESS(rv, rv);

  if (status == NS_BINDING_ABORTED) {
    // Request was aborted before we had a chance to receive any data, or
    // even an OnStartRequest(). Close the channel. This is important, as
    // we don't want to mess up our state, as if we're cloned that would
    // cause the clone to copy incorrect metadata (like whether we're
    // infinite for example).
    CloseChannel();
    return status;
  }

  if (element->ShouldCheckAllowOrigin()) {
    // If the request was cancelled by nsCORSListenerProxy due to failing
    // the CORS security check, send an error through to the media element.
    if (status == NS_ERROR_DOM_BAD_URI) {
      mCallback->NotifyNetworkError();
      return NS_ERROR_DOM_BAD_URI;
    }
  }

  nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(aRequest);
  bool seekable = false;
  if (hc) {
    uint32_t responseStatus = 0;
    hc->GetResponseStatus(&responseStatus);
    bool succeeded = false;
    hc->GetRequestSucceeded(&succeeded);

    if (!succeeded && NS_SUCCEEDED(status)) {
      // HTTP-level error (e.g. 4xx); treat this as a fatal network-level error.
      // We might get this on a seek.
      // (Note that lower-level errors indicated by NS_FAILED(status) are
      // handled in OnStopRequest.)
      // A 416 error should treated as EOF here... it's possible
      // that we don't get Content-Length, we read N bytes, then we
      // suspend and resume, the resume reopens the channel and we seek to
      // offset N, but there are no more bytes, so we get a 416
      // "Requested Range Not Satisfiable".
      if (responseStatus == HTTP_REQUESTED_RANGE_NOT_SATISFIABLE_CODE) {
        // OnStopRequest will not be fired, so we need to do some of its
        // work here.
        mCacheStream.NotifyDataEnded(status);
      } else {
        mCallback->NotifyNetworkError();
      }

      // This disconnects our listener so we don't get any more data. We
      // certainly don't want an error page to end up in our cache!
      CloseChannel();
      return NS_OK;
    }

    nsAutoCString ranges;
    hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
                          ranges);
    bool acceptsRanges = ranges.EqualsLiteral("bytes");
    // True if this channel will not return an unbounded amount of data
    bool dataIsBounded = false;

    int64_t contentLength = -1;
    hc->GetContentLength(&contentLength);
    if (contentLength >= 0 && responseStatus == HTTP_OK_CODE) {
      // "OK" status means Content-Length is for the whole resource.
      // Since that's bounded, we know we have a finite-length resource.
      dataIsBounded = true;
    }

    // Assume Range requests have a bounded upper limit unless the
    // Content-Range header tells us otherwise.
    bool boundedSeekLimit = true;
    // Check response code for byte-range requests (seeking, chunk requests).
    if (responseStatus == HTTP_PARTIAL_RESPONSE_CODE) {
      // Parse Content-Range header.
      int64_t rangeStart = 0;
      int64_t rangeEnd = 0;
      int64_t rangeTotal = 0;
      rv = ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal);

      // We received 'Content-Range', so the server accepts range requests.
      bool gotRangeHeader = NS_SUCCEEDED(rv);

      if (gotRangeHeader) {
        // We received 'Content-Range', so the server accepts range requests.
        // Notify media cache about the length and start offset of data received.
        // Note: If aRangeTotal == -1, then the total bytes is unknown at this stage.
        //       For now, tell the decoder that the stream is infinite.
        if (rangeTotal == -1) {
          boundedSeekLimit = false;
        } else {
          contentLength = std::max(contentLength, rangeTotal);
        }
        // Give some warnings if the ranges are unexpected.
        // XXX These could be error conditions.
        NS_WARN_IF_FALSE(mOffset == rangeStart,
                         "response range start does not match current offset");
        mOffset = rangeStart;
        mCacheStream.NotifyDataStarted(rangeStart);
      }
      acceptsRanges = gotRangeHeader;
    } else if (mOffset > 0 && responseStatus == HTTP_OK_CODE) {
      // If we get an OK response but we were seeking, or requesting a byte
      // range, then we have to assume that seeking doesn't work. We also need
      // to tell the cache that it's getting data for the start of the stream.
      mCacheStream.NotifyDataStarted(0);
      mOffset = 0;

      // The server claimed it supported range requests.  It lied.
      acceptsRanges = false;
    }
    if (mOffset == 0 && contentLength >= 0 &&
        (responseStatus == HTTP_OK_CODE ||
         responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
      mCacheStream.NotifyDataLength(contentLength);
    }
    // XXX we probably should examine the Content-Range header in case
    // the server gave us a range which is not quite what we asked for

    // If we get an HTTP_OK_CODE response to our byte range request,
    // and the server isn't sending Accept-Ranges:bytes then we don't
    // support seeking.
    seekable = acceptsRanges;
    if (seekable && boundedSeekLimit) {
      // If range requests are supported, and we did not see an unbounded
      // upper range limit, we assume the resource is bounded.
      dataIsBounded = true;
    }

    mCallback->SetInfinite(!dataIsBounded);
  }
  mCacheStream.SetTransportSeekable(seekable);

  {
    MutexAutoLock lock(mLock);
    mIsTransportSeekable = seekable;
    mChannelStatistics->Start();
  }

  mReopenOnError = false;
  mIgnoreClose = false;

  mSuspendAgent.UpdateSuspendedStatusIfNeeded();

  // Fires an initial progress event.
  owner->DownloadProgressed();

  return NS_OK;
}
nsresult
RtspMediaResource::OnConnected(uint8_t aTrackIdx,
                               nsIStreamingProtocolMetaData *meta)
{
  if (mIsConnected) {
    for (uint32_t i = 0 ; i < mTrackBuffer.Length(); ++i) {
      mTrackBuffer[i]->Start();
    }
    return NS_OK;
  }

  uint8_t tracks;
  mMediaStreamController->GetTotalTracks(&tracks);

  // If the preference of RTSP video feature is not enabled and the streaming is
  // video, we give up moving forward.
  if (!IsVideoEnabled() && IsVideo(tracks, meta)) {
    // Give up, report error to media element.
    nsCOMPtr<nsIRunnable> event =
      NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
    NS_DispatchToMainThread(event);
    return NS_ERROR_FAILURE;
  }
  uint64_t durationUs = 0;
  for (int i = 0; i < tracks; ++i) {
    nsCString rtspTrackId("RtspTrack");
    rtspTrackId.AppendInt(i);
    nsCOMPtr<nsIStreamingProtocolMetaData> trackMeta;
    mMediaStreamController->GetTrackMetaData(i, getter_AddRefs(trackMeta));
    MOZ_ASSERT(trackMeta);
    trackMeta->GetDuration(&durationUs);

    // Here is a heuristic to estimate the slot size.
    // For video track, calculate the width*height.
    // For audio track, use the BUFFER_SLOT_DEFAULT_SIZE because the w*h is 0.
    // Finally clamp them into (BUFFER_SLOT_DEFAULT_SIZE,BUFFER_SLOT_MAX_SIZE)
    uint32_t w, h;
    uint32_t slotSize;
    trackMeta->GetWidth(&w);
    trackMeta->GetHeight(&h);
    slotSize = clamped((int32_t)(w * h), BUFFER_SLOT_DEFAULT_SIZE,
                       BUFFER_SLOT_MAX_SIZE);
    mTrackBuffer.AppendElement(new RtspTrackBuffer(rtspTrackId.get(),
                                                   i, slotSize));
    mTrackBuffer[i]->Start();
  }

  if (!mDecoder) {
    return NS_ERROR_FAILURE;
  }

  // If the durationUs is 0, imply the stream is live stream.
  if (durationUs) {
    // Not live stream.
    mIsLiveStream = false;
    mDecoder->SetInfinite(false);
    mDecoder->SetDuration((double)(durationUs) / USECS_PER_S);
  } else {
    // Live stream.
    // Check the preference "media.realtime_decoder.enabled".
    if (!Preferences::GetBool("media.realtime_decoder.enabled", false)) {
      // Give up, report error to media element.
      nsCOMPtr<nsIRunnable> event =
        NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
      NS_DispatchToMainThread(event);
      return NS_ERROR_FAILURE;
    } else {
      mIsLiveStream = true;
      bool seekable = false;
      mDecoder->SetInfinite(true);
      mDecoder->SetMediaSeekable(seekable);
    }
  }
  MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
  NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
  // Fires an initial progress event.
  owner->DownloadProgressed();

  dom::HTMLMediaElement* element = owner->GetMediaElement();
  NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);

  element->FinishDecoderSetup(mDecoder, this);
  mIsConnected = true;

  return NS_OK;
}