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; }