Bug 1415090. P4 - don't modify mResourceID off the main thread.
There are some works to do when we allow a stream whose download ends abnormally
to continue sharing the resource:
1. Abort Read() when download error happens. We might still have a chance to
get all the data successfully. However, it doesn't really matter since
the stream data is incomplete and we will encounter decode errors sooner
or later.
2. Update() needs to check mChannelEnded since an ended stream will not
download data needed by other streams.
MozReview-Commit-ID: LGCecQ5rpzq
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1442,17 +1442,17 @@ MediaCache::Update()
enableReading = predictedNewDataUse < latestNextUse;
}
}
if (enableReading) {
for (uint32_t j = 0; j < i; ++j) {
MediaCacheStream* other = mStreams[j];
if (other->mResourceID == stream->mResourceID && !other->mClosed &&
- !other->mClientSuspended &&
+ !other->mClientSuspended && !other->mChannelEnded &&
OffsetToBlockIndexUnchecked(other->mSeekTarget != -1
? other->mSeekTarget
: other->mChannelOffset) ==
OffsetToBlockIndexUnchecked(desiredOffset)) {
// This block is already going to be read by the other stream.
// So don't try to read it from this stream as well.
enableReading = false;
LOG("Stream %p waiting on same block (%" PRId32 ") from stream %p",
@@ -2155,35 +2155,39 @@ MediaCacheStream::NotifyDataEndedInterna
mClient->CacheClientSeek(mChannelOffset, false);
return;
// Note CacheClientSeek() will call Seek() asynchronously which might fail
// and close the stream. This is OK for it is not an error we can recover
// from and we have a consistent behavior with that where CacheClientSeek()
// is called from MediaCache::Update().
}
- if (NS_FAILED(aStatus)) {
- // Disconnect from other streams sharing our resource, since they
- // should continue trying to load. Our load might have been deliberately
- // canceled and that shouldn't affect other streams.
- mResourceID = mMediaCache->AllocateResourceID();
- }
-
// It is prudent to update channel/cache status before calling
// CacheClientNotifyDataEnded() which will read |mChannelEnded|.
- FlushPartialBlockInternal(true, mon);
mChannelEnded = true;
mMediaCache->QueueUpdate();
+ if (NS_FAILED(aStatus)) {
+ // Notify the client about this network error.
+ mDidNotifyDataEnded = true;
+ mNotifyDataEndedStatus = aStatus;
+ mClient->CacheClientNotifyDataEnded(aStatus);
+ // Wake up the readers so they can fail gracefully.
+ mon.NotifyAll();
+ return;
+ }
+
+ // Note we don't flush the partial block when download ends abnormally for
+ // the padding zeros will give wrong data to other streams.
+ FlushPartialBlockInternal(true, mon);
+
MediaCache::ResourceStreamIterator iter(mMediaCache, mResourceID);
while (MediaCacheStream* stream = iter.Next()) {
- if (NS_SUCCEEDED(aStatus)) {
- // We read the whole stream, so remember the true length
- stream->mStreamLength = mChannelOffset;
- }
+ // We read the whole stream, so remember the true length
+ stream->mStreamLength = mChannelOffset;
if (!stream->mDidNotifyDataEnded) {
stream->mDidNotifyDataEnded = true;
stream->mNotifyDataEndedStatus = aStatus;
stream->mClient->CacheClientNotifyDataEnded(aStatus);
}
}
}
@@ -2577,16 +2581,21 @@ MediaCacheStream::Read(char* aBuffer, ui
auto buffer = MakeSpan<char>(aBuffer, aCount);
// Read one block (or part of a block) at a time
while (!buffer.IsEmpty()) {
if (mClosed) {
return NS_ERROR_ABORT;
}
+ if (mDidNotifyDataEnded && NS_FAILED(mNotifyDataEndedStatus)) {
+ // Abort reading since download ends abnormally.
+ return NS_ERROR_FAILURE;
+ }
+
if (!IsOffsetAllowed(streamOffset)) {
LOGE("Stream %p invalid offset=%" PRId64, this, streamOffset);
return NS_ERROR_ILLEGAL_VALUE;
}
if (mStreamLength >= 0 && streamOffset >= mStreamLength) {
// Don't try to read beyond the end of the stream
break;