void CombineRanges(const MediaByteRange& pending, const MediaByteRange& preload, MediaByteRange& request) { OP_ASSERT(request.IsEmpty()); // If pending and preload overlap, let request be their union, // clamped to pending.start. if (!pending.IsEmpty() && !preload.IsEmpty()) { request = pending; request.IntersectWith(preload); if (!request.IsEmpty()) { // non-empty intersection => overlap request = pending; request.UnionWith(preload); request.IntersectWith(MediaByteRange(pending.start)); OP_ASSERT(!request.IsEmpty()); } } // Otherwise, pick the first non-empty of pending and preload. if (request.IsEmpty()) { if (!pending.IsEmpty()) request = pending; else request = preload; } }
void IntersectWithUnavailable(MediaByteRange& range, URL& url) { if (range.IsEmpty()) return; // find the first unavailable range on or after range.start MediaByteRange unavail; BOOL available = FALSE; OpFileLength length = 0; url.GetPartialCoverage(range.start, available, length, TRUE); if (available) { OP_ASSERT(length > 0); unavail.start = range.start + length; length = 0; url.GetPartialCoverage(unavail.start, available, length, TRUE); OP_ASSERT(!available); } else unavail.start = range.start; // length is now the number of unavailable bytes, or 0 if unknown if (length > 0) unavail.SetLength(length); range.IntersectWith(unavail); }
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); }