BOOL MediaSourceImpl::NeedRestart(const MediaByteRange& request) { OP_ASSERT(!request.IsEmpty()); // Note: this function assumes that request is not in cache // If not loading we certainly need to start. if (m_state == NONE || m_state == IDLE) return TRUE; // Only restart resumable resources. if (!IsResumableURL(m_use_url)) return FALSE; // Get the currently loading range. MediaByteRange loading; m_use_url->GetAttribute(URL::KHTTPRangeStart, &loading.start); m_use_url->GetAttribute(URL::KHTTPRangeEnd, &loading.end); OP_ASSERT(!loading.IsEmpty()); // When streaming, adjust the loading range to not include what // has already been evicted from the cache. Note: This must not be // done for a request that was just started, as the cache can then // contain data from the previous request which is not relevant. if (m_state >= LOADING && IsStreaming()) { BOOL available = FALSE; OpFileLength length = 0; GetPartialCoverage(loading.start, available, length); if (!available && (!loading.IsFinite() || length < loading.Length())) loading.start += length; } // Restart if request is before currently loading range. if (request.start < loading.start) return TRUE; // Restart if request is after currently loading range. if (loading.IsFinite() && request.start > loading.end) return TRUE; // request is now a subset of loading, check how much we have left // to load until request.start. BOOL available = FALSE; OpFileLength length = 0; GetPartialCoverage(loading.start, available, length); if (!available) length = 0; if (request.start > loading.start + length) { // FIXME: calculate download rate and time taken to reach offset (CORE-27952) OpFileLength remaining = request.start - (loading.start + length); if (remaining > MEDIA_SOURCE_MAX_WAIT) return TRUE; } return FALSE; }
MediaSourceImpl::State MediaSourceImpl::EnsureBufferingInternal() { MediaByteRange request; CalcRequest(request); if (!request.IsEmpty()) { m_clamp_request = FALSE; if (NeedRestart(request)) { StopBuffering(); return StartBuffering(request); } // The request wasn't restarted, so it may need to be clamped // by aborting it once enough data has become available. if (request.IsFinite() && !IsStreaming()) { OpFileLength loading_end = FILE_LENGTH_NONE; m_use_url->GetAttribute(URL::KHTTPRangeEnd, &loading_end); if (loading_end == FILE_LENGTH_NONE || request.end < loading_end) m_clamp_request = TRUE; } } else { // We have all the data we wanted, so stop buffering if possible. switch (m_state) { case NONE: // Already loaded (data: URL or in cache). return IDLE; case IDLE: case FAILED: // not loading break; case STARTED: case HEADERS: case LOADING: case PAUSED: // Only stop a load if it's in fact already complete or if // it's one that we can later resume. However, when using // the streaming cache, continue loading until either the // cache fills up and PauseBuffering() is called or (if // the request fits in cache) IsLoadedURL() is true. if (IsLoadedURL(m_use_url) || (IsResumableURL(m_use_url) && !IsStreaming())) { StopBuffering(); return IDLE; } break; } } return NONE; }
void MediaSourceImpl::CalcRequest(MediaByteRange& request) { if (IsStreaming()) { // When streaming we only consider the pending range (if any). // When there are no clients, request the entire resource. // To support preload together with streaming, care must be // taken to not restart a preload request [0,Inf] as soon as // data has been evicted and the cached range is e.g. // [500,1000499] if there is no pending request in that range. if (!m_clients.Empty()) { MediaByteRange preload; // unused ReduceRanges(m_clients, request, preload); if (!request.IsEmpty()) { request.SetLength(FILE_LENGTH_NONE); IntersectWithUnavailable(request, m_use_url); if (!request.IsEmpty()) request.SetLength(FILE_LENGTH_NONE); } } else { request.start = 0; } // At this point we should have an unbounded range, but it may // be clamped to the resource length below. OP_ASSERT(!request.IsFinite()); } else { // When not streaming (using multiple range disk cache), both // pending and preload requests are taken into account. // // Example 1: Everything should be preloaded, but since there is a // pending read, start buffering from that offset first. Also, // don't refetch the end of the resource. // // resource: <----------------------> // buffered: <--> <---> // pending: <----> // preload: <----------------------> // request: <--------> // // Example 2: The requested preload is already buffered, so the // request is the empty range. // // resource: <----------------------> // buffered: <--------> // pending: empty range // preload: <-----> // request: empty range MediaByteRange pending, preload; ReduceRanges(m_clients, pending, preload); CombineRanges(pending, preload, request); IntersectWithUnavailable(request, m_use_url); } if (!request.IsEmpty()) { // Extra restrictions if resource length is known (this won't // be needed after CORE-30311 is fixed). OpFileLength resource_length = GetTotalBytes(); if (resource_length > 0) { OP_ASSERT(request.start <= resource_length); MediaByteRange resource(0, resource_length - 1); // Clamp request to resource. request.IntersectWith(resource); // Increase size if it is unreasonably small at the end... if (!request.IsEmpty() && request.Length() < MEDIA_SOURCE_MIN_SIZE && resource.Length() >= MEDIA_SOURCE_MIN_SIZE && request.end == resource.end) { // ... but only if nothing in that range is available. MediaByteRange cand_request(resource_length - MEDIA_SOURCE_MIN_SIZE, resource_length - 1); OP_ASSERT(cand_request.Length() == MEDIA_SOURCE_MIN_SIZE); IntersectWithUnavailable(cand_request, m_use_url); if (cand_request.Length() == MEDIA_SOURCE_MIN_SIZE) request = cand_request; } } } OP_NEW_DBG("CalcRequest", "MediaSource"); OP_DBG(("request: ") << request); }