示例#1
0
already_AddRefed<MediaRawData>
WAVTrackDemuxer::GetFileHeader(const MediaByteRange& aRange)
{
  if (!aRange.Length()) {
    return nullptr;
  }

  RefPtr<MediaRawData> fileHeader = new MediaRawData();
  fileHeader->mOffset = aRange.mStart;

  nsAutoPtr<MediaRawDataWriter> headerWriter(fileHeader->CreateWriter());
  if (!headerWriter->SetSize(aRange.Length())) {
    return nullptr;
  }

  const uint32_t read =
    Read(headerWriter->Data(), fileHeader->mOffset, fileHeader->Size());

  if (read != aRange.Length()) {
    return nullptr;
  }

  UpdateState(aRange);

  return fileHeader.forget();
}
示例#2
0
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;
	}
}
示例#3
0
nsRefPtr<MP4Demuxer::InitPromise>
MP4Demuxer::Init()
{
  AutoPinned<mp4_demuxer::ResourceStream> stream(mStream);

  // Check that we have enough data to read the metadata.
  MediaByteRange br = mp4_demuxer::MP4Metadata::MetadataRange(stream);
  if (br.IsNull()) {
    return InitPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA, __func__);
  }

  if (!mInitData->SetLength(br.Length(), fallible)) {
    // OOM
    return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
  }

  size_t size;
  mStream->ReadAt(br.mStart, mInitData->Elements(), br.Length(), &size);
  if (size != size_t(br.Length())) {
    return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
  }

  nsRefPtr<mp4_demuxer::BufferStream> bufferstream =
    new mp4_demuxer::BufferStream(mInitData);

  mMetadata = MakeUnique<mp4_demuxer::MP4Metadata>(bufferstream);

  if (!mMetadata->GetNumberTracks(mozilla::TrackInfo::kAudioTrack) &&
      !mMetadata->GetNumberTracks(mozilla::TrackInfo::kVideoTrack)) {
    return InitPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
  }

  return InitPromise::CreateAndResolve(NS_OK, __func__);
}
示例#4
0
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);
}
示例#5
0
bool
MoofParser::HasMetadata()
{
  int64_t length = std::numeric_limits<int64_t>::max();
  mSource->Length(&length);
  nsTArray<MediaByteRange> byteRanges;
  byteRanges.AppendElement(MediaByteRange(0, length));
  nsRefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);

  MediaByteRange ftyp;
  MediaByteRange moov;
  BoxContext context(stream, byteRanges);
  for (Box box(&context, mOffset); box.IsAvailable(); box = box.Next()) {
    if (box.IsType("ftyp")) {
      ftyp = box.Range();
      continue;
    }
    if (box.IsType("moov")) {
      moov = box.Range();
      break;
    }
  }
  if (!ftyp.Length() || !moov.Length()) {
    return false;
  }
  mInitRange = ftyp.Extents(moov);
  return true;
}
示例#6
0
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;
}
示例#7
0
void
MP3TrackDemuxer::UpdateState(const MediaByteRange& aRange) {
  // Prevent overflow.
  if (mTotalFrameLen + aRange.Length() < mTotalFrameLen) {
    // These variables have a linear dependency and are only used to derive the
    // average frame length.
    mTotalFrameLen /= 2;
    mNumParsedFrames /= 2;
  }

  // Full frame parsed, move offset to its end.
  mOffset = aRange.mEnd;

  mTotalFrameLen += aRange.Length();

  if (!mSamplesPerFrame) {
    mSamplesPerFrame = mParser.CurrentFrame().Header().SamplesPerFrame();
    mSamplesPerSecond = mParser.CurrentFrame().Header().SampleRate();
    mChannels = mParser.CurrentFrame().Header().Channels();
  }

  ++mNumParsedFrames;
  ++mFrameIndex;
  MOZ_ASSERT(mFrameIndex > 0);

  // Prepare the parser for the next frame parsing session.
  mParser.EndFrameSession();
}
示例#8
0
already_AddRefed<mozilla::MediaByteBuffer>
MoofParser::Metadata()
{
  MediaByteRange moov;
  ScanForMetadata(moov);
  CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length();
  if (!moovLength.isValid() || !moovLength.value()) {
    // No moov, or cannot be used as array size.
    return nullptr;
  }

  RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
  if (!metadata->SetLength(moovLength.value(), fallible)) {
    LOG(Moof, "OOM");
    return nullptr;
  }

  RefPtr<BlockingStream> stream = new BlockingStream(mSource);
  size_t read;
  bool rv =
    stream->ReadAt(moov.mStart, metadata->Elements(), moovLength.value(), &read);
  if (!rv || read != moovLength.value()) {
    return nullptr;
  }
  return metadata.forget();
}
示例#9
0
bool
MoofParser::HasMetadata()
{
  MediaByteRange ftyp;
  MediaByteRange moov;
  ScanForMetadata(ftyp, moov);
  return !!ftyp.Length() && !!moov.Length();
}
示例#10
0
already_AddRefed<MediaRawData>
MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
  MP3LOG("GetNext() Begin({mStart=%" PRId64 " Length()=%" PRId64 "})",
         aRange.mStart, aRange.Length());
  if (!aRange.Length()) {
    return nullptr;
  }

  RefPtr<MediaRawData> frame = new MediaRawData();
  frame->mOffset = aRange.mStart;

  nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
  if (!frameWriter->SetSize(aRange.Length())) {
    MP3LOG("GetNext() Exit failed to allocated media buffer");
    return nullptr;
  }

  const uint32_t read = Read(frameWriter->Data(), frame->mOffset, frame->Size());

  if (read != aRange.Length()) {
    MP3LOG("GetNext() Exit read=%u frame->Size()=%u", read, frame->Size());
    return nullptr;
  }

  UpdateState(aRange);

  frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
  frame->mDuration = Duration(1).ToMicroseconds();
  frame->mTimecode = frame->mTime;
  frame->mKeyframe = true;

  MOZ_ASSERT(frame->mTime >= 0);
  MOZ_ASSERT(frame->mDuration > 0);

  if (mNumParsedFrames == 1) {
    // First frame parsed, let's read VBR info if available.
    // TODO: read info that helps with seeking (bug 1163667).
    ByteReader reader(frame->Data(), frame->Size());
    mParser.ParseVBRHeader(&reader);
    reader.DiscardRemaining();
    mFirstFrameOffset = frame->mOffset;
  }

  MP3LOGV("GetNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
          " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
          " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
          mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen,
          mSamplesPerFrame, mSamplesPerSecond, mChannels);

  return frame.forget();
}
示例#11
0
void
WAVTrackDemuxer::UpdateState(const MediaByteRange& aRange)
{
  // Full chunk parsed, move offset to its end.
  mOffset = aRange.mEnd;
  mTotalChunkLen += aRange.Length();
}
示例#12
0
MediaByteRange
MP3TrackDemuxer::FindFirstFrame() {
  static const int MIN_SUCCESSIVE_FRAMES = 4;

  MediaByteRange candidateFrame = FindNextFrame();
  int numSuccFrames = candidateFrame.Length() > 0;
  MediaByteRange currentFrame = candidateFrame;
  MP3LOGV("FindFirst() first candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
          candidateFrame.mStart, candidateFrame.Length());

  while (candidateFrame.Length() && numSuccFrames < MIN_SUCCESSIVE_FRAMES) {
    mParser.EndFrameSession();
    mOffset = currentFrame.mEnd;
    const MediaByteRange prevFrame = currentFrame;

    // FindNextFrame() here will only return frames consistent with our candidate frame.
    currentFrame = FindNextFrame();
    numSuccFrames += currentFrame.Length() > 0;
    // Multiple successive false positives, which wouldn't be caught by the consistency
    // checks alone, can be detected by wrong alignment (non-zero gap between frames).
    const int64_t frameSeparation = currentFrame.mStart - prevFrame.mEnd;

    if (!currentFrame.Length() || frameSeparation != 0) {
      MP3LOGV("FindFirst() not enough successive frames detected, "
              "rejecting candidate frame: successiveFrames=%d, last Length()=%" PRIu64
              ", last frameSeparation=%" PRId64, numSuccFrames, currentFrame.Length(),
              frameSeparation);

      mParser.ResetFrameData();
      mOffset = candidateFrame.mStart + 1;
      candidateFrame = FindNextFrame();
      numSuccFrames = candidateFrame.Length() > 0;
      currentFrame = candidateFrame;
      MP3LOGV("FindFirst() new candidate frame: mOffset=%" PRIu64 " Length()=%" PRIu64,
              candidateFrame.mStart, candidateFrame.Length());
    }
  }

  if (numSuccFrames >= MIN_SUCCESSIVE_FRAMES) {
    MP3LOG("FindFirst() accepting candidate frame: "
            "successiveFrames=%d", numSuccFrames);
  } else {
    MP3LOG("FindFirst() no suitable first frame found");
  }
  return candidateFrame;
}
示例#13
0
already_AddRefed<MediaRawData>
WAVTrackDemuxer::GetNextChunk(const MediaByteRange& aRange)
{
  if (!aRange.Length()) {
    return nullptr;
  }

  RefPtr<MediaRawData> datachunk = new MediaRawData();
  datachunk->mOffset = aRange.mStart;

  nsAutoPtr<MediaRawDataWriter> chunkWriter(datachunk->CreateWriter());
  if (!chunkWriter->SetSize(aRange.Length())) {
    return nullptr;
  }

  const uint32_t read =
    Read(chunkWriter->Data(), datachunk->mOffset, datachunk->Size());

  if (read != aRange.Length()) {
    return nullptr;
  }

  UpdateState(aRange);
  ++mNumParsedChunks;
  ++mChunkIndex;

  datachunk->mTime = Duration(mChunkIndex - 1);

  if (static_cast<uint32_t>(mChunkIndex) * DATA_CHUNK_SIZE < mDataLength) {
    datachunk->mDuration = Duration(1);
  } else {
    uint32_t mBytesRemaining =
      mDataLength - mChunkIndex * DATA_CHUNK_SIZE;
    datachunk->mDuration = DurationFromBytes(mBytesRemaining);
  }
  datachunk->mTimecode = datachunk->mTime;
  datachunk->mKeyframe = true;

  MOZ_ASSERT(!datachunk->mTime.IsNegative());
  MOZ_ASSERT(!datachunk->mDuration.IsNegative());

  return datachunk.forget();
}
示例#14
0
bool
MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) {
  if (!mNumParsedFrames || !aRange.Length()) {
    // We can't skip the first frame, since it could contain VBR headers.
    nsRefPtr<MediaRawData> frame(GetNextFrame(aRange));
    return frame;
  }

  UpdateState(aRange);

  return true;
}
示例#15
0
void ReduceRanges(const List<MediaSourceClient>& clients,
				  MediaByteRange& pending, MediaByteRange& preload)
{
	OP_ASSERT(preload.IsEmpty());
	MediaSourceClient* client = clients.First();
	if (client)
	{
		for ( ; client; client = client->Suc())
		{
			// take the first non-empty pending
			if (pending.IsEmpty())
				pending = client->GetPending();
			// preload the union of all ranges
			preload.UnionWith(client->GetPreload());
		}
	}
	else
	{
		// with no clients, preload the whole resource
		preload.start = 0;
	}
}
示例#16
0
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;
}
示例#17
0
already_AddRefed<mozilla::MediaByteBuffer>
MoofParser::Metadata()
{
  MediaByteRange ftyp;
  MediaByteRange moov;
  ScanForMetadata(ftyp, moov);
  CheckedInt<MediaByteBuffer::size_type> ftypLength = ftyp.Length();
  CheckedInt<MediaByteBuffer::size_type> moovLength = moov.Length();
  if (!ftypLength.isValid() || !moovLength.isValid()
      || !ftypLength.value() || !moovLength.value()) {
    // No ftyp or moov, or they cannot be used as array size.
    return nullptr;
  }
  CheckedInt<MediaByteBuffer::size_type> totalLength = ftypLength + moovLength;
  if (!totalLength.isValid()) {
    // Addition overflow, or sum cannot be used as array size.
    return nullptr;
  }
  RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
  if (!metadata->SetLength(totalLength.value(), fallible)) {
    // OOM
    return nullptr;
  }

  RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
  size_t read;
  bool rv =
    stream->ReadAt(ftyp.mStart, metadata->Elements(), ftypLength.value(), &read);
  if (!rv || read != ftypLength.value()) {
    return nullptr;
  }
  rv =
    stream->ReadAt(moov.mStart, metadata->Elements() + ftypLength.value(), moovLength.value(), &read);
  if (!rv || read != moovLength.value()) {
    return nullptr;
  }
  return metadata.forget();
}
示例#18
0
already_AddRefed<MediaRawData>
MP3TrackDemuxer::GetNextFrame(const MediaByteRange& aRange) {
  if (!aRange.Length()) {
    return nullptr;
  }

  nsRefPtr<MediaRawData> frame = new MediaRawData();
  frame->mOffset = aRange.mStart;

  nsAutoPtr<MediaRawDataWriter> frameWriter(frame->CreateWriter());
  if (!frameWriter->SetSize(aRange.Length())) {
    return nullptr;
  }

  const uint32_t read = Read(frameWriter->mData, frame->mOffset, frame->mSize);

  if (read != aRange.Length()) {
    return nullptr;
  }

  UpdateState(aRange);

  frame->mTime = Duration(mFrameIndex - 1).ToMicroseconds();
  frame->mDuration = Duration(1).ToMicroseconds();

  MOZ_ASSERT(frame->mTime >= 0);
  MOZ_ASSERT(frame->mDuration > 0);

  if (mNumParsedFrames == 1) {
    // First frame parsed, let's read VBR info if available.
    // TODO: read info that helps with seeking (bug 1163667).
    mParser.ParseVBRHeader(frame->mData, frame->mData + frame->mSize);
    mFirstFrameOffset = frame->mOffset;
  }

  return frame.forget();
}
示例#19
0
bool
MP3TrackDemuxer::SkipNextFrame(const MediaByteRange& aRange) {
  if (!mNumParsedFrames || !aRange.Length()) {
    // We can't skip the first frame, since it could contain VBR headers.
    nsRefPtr<MediaRawData> frame(GetNextFrame(aRange));
    return frame;
  }

  UpdateState(aRange);

  MP3DEMUXER_LOGV("SkipNext() End mOffset=%" PRIu64 " mNumParsedFrames=%" PRIu64
                  " mFrameIndex=%" PRId64 " mTotalFrameLen=%" PRIu64
                  " mSamplesPerFrame=%d mSamplesPerSecond=%d mChannels=%d",
                  mOffset, mNumParsedFrames, mFrameIndex, mTotalFrameLen, mSamplesPerFrame,
                  mSamplesPerSecond, mChannels);

  return true;
}
示例#20
0
nsresult
DASHRepDecoder::GetByteRangeForSeek(int64_t const aOffset,
                                    MediaByteRange& aByteRange)
{
  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");

  // Only check data ranges if they're available and if this decoder is active,
  // i.e. inactive rep decoders should only load metadata.
  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());

  for (uint32_t i = 0; i < mByteRanges.Length(); i++) {
    NS_ENSURE_FALSE(mByteRanges[i].IsNull(), NS_ERROR_NOT_INITIALIZED);
    // Check if |aOffset| lies within the current data range.
    if (mByteRanges[i].mStart <= aOffset && aOffset <= mByteRanges[i].mEnd) {
      if (mMainDecoder->IsDecoderAllowedToDownloadSubsegment(this, i)) {
        mCurrentByteRange = aByteRange = mByteRanges[i];
        mSubsegmentIdx = i;
        // XXX Hack: should be setting subsegment outside this function, but
        // need to review seeking for multiple switches anyhow.
        mMainDecoder->SetSubsegmentIndex(this, i);
        LOG("Getting DATA range [%d] for seek offset [%lld]: "
            "bytes [%lld] to [%lld]",
            i, aOffset, aByteRange.mStart, aByteRange.mEnd);
        return NS_OK;
      }
      break;
    }
  }
  // Don't allow metadata downloads once they're loaded and byte ranges have
  // been populated.
  bool canDownloadMetadata = mByteRanges.IsEmpty();
  if (canDownloadMetadata) {
    // Check metadata ranges; init range.
    if (mInitByteRange.mStart <= aOffset && aOffset <= mInitByteRange.mEnd) {
      mCurrentByteRange = aByteRange = mInitByteRange;
      mSubsegmentIdx = 0;
        LOG("Getting INIT range for seek offset [%lld]: bytes [%lld] to "
            "[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
      return NS_OK;
    }
    // ... index range.
    if (mIndexByteRange.mStart <= aOffset && aOffset <= mIndexByteRange.mEnd) {
      mCurrentByteRange = aByteRange = mIndexByteRange;
      mSubsegmentIdx = 0;
      LOG("Getting INDEXES range for seek offset [%lld]: bytes [%lld] to "
          "[%lld]", aOffset, aByteRange.mStart, aByteRange.mEnd);
      return NS_OK;
    }
  } else {
    LOG1("Metadata should be read; inhibiting further metadata downloads.");
  }

  // If no byte range is found by this stage, clear the parameter and return.
  aByteRange.Clear();
  if (mByteRanges.IsEmpty() || !canDownloadMetadata) {
    // Assume mByteRanges will be populated after metadata is read.
    LOG("Data ranges not populated [%s]; metadata download restricted [%s]: "
        "offset[%lld].",
        (mByteRanges.IsEmpty() ? "yes" : "no"),
        (canDownloadMetadata ? "no" : "yes"), aOffset);
    return NS_ERROR_NOT_AVAILABLE;
  } else {
    // Cannot seek to an unknown offset.
    // XXX Revisit this for dynamic MPD profiles if MPD is regularly updated.
    LOG("Error! Offset [%lld] is in an unknown range!", aOffset);
    return NS_ERROR_ILLEGAL_VALUE;
  }
}
示例#21
0
already_AddRefed<mozilla::MediaByteBuffer>
MoofParser::Metadata()
{
  MediaByteRange ftyp;
  MediaByteRange moov;
  ScanForMetadata(ftyp, moov);
  if (!ftyp.Length() || !moov.Length()) {
    return nullptr;
  }
  RefPtr<MediaByteBuffer> metadata = new MediaByteBuffer();
  if (!metadata->SetLength(ftyp.Length() + moov.Length(), fallible)) {
    // OOM
    return nullptr;
  }

  RefPtr<mp4_demuxer::BlockingStream> stream = new BlockingStream(mSource);
  size_t read;
  bool rv =
    stream->ReadAt(ftyp.mStart, metadata->Elements(), ftyp.Length(), &read);
  if (!rv || read != ftyp.Length()) {
    return nullptr;
  }
  rv =
    stream->ReadAt(moov.mStart, metadata->Elements() + ftyp.Length(), moov.Length(), &read);
  if (!rv || read != moov.Length()) {
    return nullptr;
  }
  return metadata.forget();
}
示例#22
0
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);
}