Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 16 kB image not shown  

Quelle  MediaResource.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "MediaResource.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Logging.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/ErrorNames.h"
#include "mozilla/SchedulerGroup.h"

using mozilla::media::TimeUnit;

#undef ILOG

mozilla::LazyLogModule gMediaResourceIndexLog("MediaResourceIndex");
// Debug logging macro with object pointer and class name.
#define ILOG(msg, ...)                                             \
  DDMOZ_LOG(gMediaResourceIndexLog, mozilla::LogLevel::Debug, msg, \
            ##__VA_ARGS__)

namespace mozilla {

static const uint32_t kMediaResourceIndexCacheSize = 8192;
static_assert(IsPowerOfTwo(kMediaResourceIndexCacheSize),
              "kMediaResourceIndexCacheSize cache size must be a power of 2");

MediaResourceIndex::MediaResourceIndex(MediaResource* aResource)
    : mResource(aResource),
      mOffset(0),
      mCacheBlockSize(
          aResource->ShouldCacheReads() ? kMediaResourceIndexCacheSize : 0),
      mCachedOffset(0),
      mCachedBytes(0),
      mCachedBlock(MakeUnique<char[]>(mCacheBlockSize)) {
  DDLINKCHILD("resource", aResource);
}

nsresult MediaResourceIndex::Read(char* aBuffer, uint32_t aCount,
                                  uint32_t* aBytes) {
  NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");

  // We purposefuly don't check that we may attempt to read past
  // mResource->GetLength() as the resource's length may change over time.

  nsresult rv = ReadAt(mOffset, aBuffer, aCount, aBytes);
  if (NS_FAILED(rv)) {
    return rv;
  }
  mOffset += *aBytes;
  if (mOffset < 0) {
    // Very unlikely overflow; just return to position 0.
    mOffset = 0;
  }
  return NS_OK;
}

static nsCString ResultName(nsresult aResult) {
  nsCString name;
  GetErrorName(aResult, name);
  return name;
}

nsresult MediaResourceIndex::ReadAt(int64_t aOffset, char* aBuffer,
                                    uint32_t aCount, uint32_t* aBytes) {
  if (mCacheBlockSize == 0) {
    return UncachedReadAt(aOffset, aBuffer, aCount, aBytes);
  }

  *aBytes = 0;

  if (aCount == 0) {
    return NS_OK;
  }

  const int64_t endOffset = aOffset + aCount;
  if (aOffset < 0 || endOffset < aOffset) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  const int64_t lastBlockOffset = CacheOffsetContaining(endOffset - 1);

  if (mCachedBytes != 0 && mCachedOffset + mCachedBytes >= aOffset &&
      mCachedOffset < endOffset) {
    // There is data in the cache that is not completely before aOffset and not
    // completely after endOffset, so it could be usable (with potential
    // top-up).
    if (aOffset < mCachedOffset) {
      // We need to read before the cached data.
      const uint32_t toRead = uint32_t(mCachedOffset - aOffset);
      MOZ_ASSERT(toRead > 0);
      MOZ_ASSERT(toRead < aCount);
      uint32_t read = 0;
      nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read);
      if (NS_FAILED(rv)) {
        ILOG("ReadAt(%" PRIu32 "@%" PRId64
             ") uncached read before cache -> %s, %" PRIu32,
             aCount, aOffset, ResultName(rv).get(), *aBytes);
        return rv;
      }
      *aBytes = read;
      if (read < toRead) {
        // Could not read everything we wanted, we're done.
        ILOG("ReadAt(%" PRIu32 "@%" PRId64
             ") uncached read before cache, incomplete -> OK, %" PRIu32,
             aCount, aOffset, *aBytes);
        return NS_OK;
      }
      ILOG("ReadAt(%" PRIu32 "@%" PRId64
           ") uncached read before cache: %" PRIu32 ", remaining: %" PRIu32
           "@%" PRId64 "...",
           aCount, aOffset, read, aCount - read, aOffset + read);
      aOffset += read;
      aBuffer += read;
      aCount -= read;
      // We should have reached the cache.
      MOZ_ASSERT(aOffset == mCachedOffset);
    }
    MOZ_ASSERT(aOffset >= mCachedOffset);

    // We've reached our cache.
    const uint32_t toCopy =
        std::min(aCount, uint32_t(mCachedOffset + mCachedBytes - aOffset));
    // Note that we could in fact be just after the last byte of the cache, in
    // which case we can't actually read from it! (But we will top-up next.)
    if (toCopy != 0) {
      memcpy(aBuffer, &mCachedBlock[IndexInCache(aOffset)], toCopy);
      *aBytes += toCopy;
      aCount -= toCopy;
      if (aCount == 0) {
        // All done!
        ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied everything (%" PRIu32
             ") from cache(%" PRIu32 "@%" PRId64 ") :-D -> OK, %" PRIu32,
             aCount, aOffset, toCopy, mCachedBytes, mCachedOffset, *aBytes);
        return NS_OK;
      }
      aOffset += toCopy;
      aBuffer += toCopy;
      ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") copied %" PRIu32
           " from cache(%" PRIu32 "@%" PRId64 ") :-), remaining: %" PRIu32
           "@%" PRId64 "...",
           aCount + toCopy, aOffset - toCopy, toCopy, mCachedBytes,
           mCachedOffset, aCount, aOffset);
    }

    if (aOffset - 1 >= lastBlockOffset) {
      // We were already reading cached data from the last block, we need more
      // from it -> try to top-up, read what we can, and we'll be done.
      MOZ_ASSERT(aOffset == mCachedOffset + mCachedBytes);
      MOZ_ASSERT(endOffset <= lastBlockOffset + mCacheBlockSize);
      return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
    }

    // We were not in the last block (but we may just have crossed the line now)
    MOZ_ASSERT(aOffset <= lastBlockOffset);
    // Continue below...
  } else if (aOffset >= lastBlockOffset) {
    // There was nothing we could get from the cache.
    // But we're already in the last block -> Cache or read what we can.
    // Make sure to invalidate the cache first.
    mCachedBytes = 0;
    return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
  }

  // If we're here, either there was nothing usable in the cache, or we've just
  // read what was in the cache but there's still more to read.

  if (aOffset < lastBlockOffset) {
    // We need to read before the last block.
    // Start with an uncached read up to the last block.
    const uint32_t toRead = uint32_t(lastBlockOffset - aOffset);
    MOZ_ASSERT(toRead > 0);
    MOZ_ASSERT(toRead < aCount);
    uint32_t read = 0;
    nsresult rv = UncachedReadAt(aOffset, aBuffer, toRead, &read);
    if (NS_FAILED(rv)) {
      ILOG("ReadAt(%" PRIu32 "@%" PRId64
           ") uncached read before last block failed -> %s, %" PRIu32,
           aCount, aOffset, ResultName(rv).get(), *aBytes);
      return rv;
    }
    if (read == 0) {
      ILOG("ReadAt(%" PRIu32 "@%" PRId64
           ") uncached read 0 before last block -> OK, %" PRIu32,
           aCount, aOffset, *aBytes);
      return NS_OK;
    }
    *aBytes += read;
    if (read < toRead) {
      // Could not read everything we wanted, we're done.
      ILOG("ReadAt(%" PRIu32 "@%" PRId64
           ") uncached read before last block, incomplete -> OK, %" PRIu32,
           aCount, aOffset, *aBytes);
      return NS_OK;
    }
    ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") read %" PRIu32
         " before last block, remaining: %" PRIu32 "@%" PRId64 "...",
         aCount, aOffset, read, aCount - read, aOffset + read);
    aOffset += read;
    aBuffer += read;
    aCount -= read;
  }

  // We should just have reached the start of the last block.
  MOZ_ASSERT(aOffset == lastBlockOffset);
  MOZ_ASSERT(aCount <= mCacheBlockSize);
  // Make sure to invalidate the cache first.
  mCachedBytes = 0;
  return CacheOrReadAt(aOffset, aBuffer, aCount, aBytes);
}

nsresult MediaResourceIndex::CacheOrReadAt(int64_t aOffset, char* aBuffer,
                                           uint32_t aCount, uint32_t* aBytes) {
  // We should be here because there is more data to read.
  MOZ_ASSERT(aCount > 0);
  // We should be in the last block, so we shouldn't try to read past it.
  MOZ_ASSERT(IndexInCache(aOffset) + aCount <= mCacheBlockSize);

  const int64_t length = GetLength();
  // If length is unknown (-1), look at resource-cached data.
  // If length is known and equal or greater than requested, also look at
  // resource-cached data.
  // Otherwise, if length is known but same, or less than(!?), requested, don't
  // attempt to access resource-cached data, as we're not expecting it to ever
  // be greater than the length.
  if (length < 0 || length >= aOffset + aCount) {
    // Is there cached data covering at least the requested range?
    const int64_t cachedDataEnd = mResource->GetCachedDataEnd(aOffset);
    if (cachedDataEnd >= aOffset + aCount) {
      // Try to read as much resource-cached data as can fill our local cache.
      // Assume we can read as much as is cached without blocking.
      const uint32_t cacheIndex = IndexInCache(aOffset);
      const uint32_t toRead = uint32_t(std::min(
          cachedDataEnd - aOffset, int64_t(mCacheBlockSize - cacheIndex)));
      MOZ_ASSERT(toRead >= aCount);
      uint32_t read = 0;
      // We would like `toRead` if possible, but ok with at least `aCount`.
      nsresult rv = UncachedRangedReadAt(aOffset, &mCachedBlock[cacheIndex],
                                         aCount, toRead - aCount, &read);
      if (NS_SUCCEEDED(rv)) {
        if (read == 0) {
          ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
               "..%" PRIu32 "@%" PRId64
               ") to top-up succeeded but read nothing -> OK anyway",
               aCount, aOffset, aCount, toRead, aOffset);
          // Couldn't actually read anything, but didn't error out, so count
          // that as success.
          return NS_OK;
        }
        if (mCachedOffset + mCachedBytes == aOffset) {
          // We were topping-up the cache, just update its size.
          ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
               "..%" PRIu32 "@%" PRId64 ") to top-up succeeded to read %" PRIu32
               "...",
               aCount, aOffset, aCount, toRead, aOffset, read);
          mCachedBytes += read;
        } else {
          // We were filling the cache from scratch, save new cache information.
          ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
               "..%" PRIu32 "@%" PRId64
               ") to fill cache succeeded to read %" PRIu32 "...",
               aCount, aOffset, aCount, toRead, aOffset, read);
          mCachedOffset = aOffset;
          mCachedBytes = read;
        }
        // Copy relevant part into output.
        uint32_t toCopy = std::min(aCount, read);
        memcpy(aBuffer, &mCachedBlock[cacheIndex], toCopy);
        *aBytes += toCopy;
        ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - copied %" PRIu32 "@%" PRId64
             " -> OK, %" PRIu32,
             aCount, aOffset, toCopy, aOffset, *aBytes);
        // We may not have read all that was requested, but we got everything
        // we could get, so we're done.
        return NS_OK;
      }
      ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - UncachedRangedReadAt(%" PRIu32
           "..%" PRIu32 "@%" PRId64
           ") failed: %s, will fallback to blocking read...",
           aCount, aOffset, aCount, toRead, aOffset, ResultName(rv).get());
      // Failure during reading. Note that this may be due to the cache
      // changing between `GetCachedDataEnd` and `ReadAt`, so it's not
      // totally unexpected, just hopefully rare; but we do need to handle it.

      // Invalidate part of cache that may have been partially overridden.
      if (mCachedOffset + mCachedBytes == aOffset) {
        // We were topping-up the cache, just keep the old untouched data.
        // (i.e., nothing to do here.)
      } else {
        // We were filling the cache from scratch, invalidate cache.
        mCachedBytes = 0;
      }
    } else {
      ILOG("ReadAt(%" PRIu32 "@%" PRId64
           ") - no cached data, will fallback to blocking read...",
           aCount, aOffset);
    }
  } else {
    ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - length is %" PRId64
         " (%s), will fallback to blocking read as the caller requested...",
         aCount, aOffset, length, length < 0 ? "unknown" : "too short!");
  }
  uint32_t read = 0;
  nsresult rv = UncachedReadAt(aOffset, aBuffer, aCount, &read);
  if (NS_SUCCEEDED(rv)) {
    *aBytes += read;
    ILOG("ReadAt(%" PRIu32 "@%" PRId64 ") - fallback uncached read got %" PRIu32
         " bytes -> %s, %" PRIu32,
         aCount, aOffset, read, ResultName(rv).get(), *aBytes);
  } else {
    ILOG("ReadAt(%" PRIu32 "@%" PRId64
         ") - fallback uncached read failed -> %s, %" PRIu32,
         aCount, aOffset, ResultName(rv).get(), *aBytes);
  }
  return rv;
}

nsresult MediaResourceIndex::UncachedReadAt(int64_t aOffset, char* aBuffer,
                                            uint32_t aCount,
                                            uint32_t* aBytes) const {
  if (aOffset < 0) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  if (aCount == 0) {
    *aBytes = 0;
    return NS_OK;
  }
  return mResource->ReadAt(aOffset, aBuffer, aCount, aBytes);
}

nsresult MediaResourceIndex::UncachedRangedReadAt(int64_t aOffset,
                                                  char* aBuffer,
                                                  uint32_t aRequestedCount,
                                                  uint32_t aExtraCount,
                                                  uint32_t* aBytes) const {
  uint32_t count = aRequestedCount + aExtraCount;
  if (aOffset < 0 || count < aRequestedCount) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  if (count == 0) {
    *aBytes = 0;
    return NS_OK;
  }
  return mResource->ReadAt(aOffset, aBuffer, count, aBytes);
}

nsresult MediaResourceIndex::Seek(int32_t aWhence, int64_t aOffset) {
  switch (aWhence) {
    case SEEK_SET:
      break;
    case SEEK_CUR:
      aOffset += mOffset;
      break;
    case SEEK_END: {
      int64_t length = mResource->GetLength();
      if (length == -1 || length - aOffset < 0) {
        return NS_ERROR_FAILURE;
      }
      aOffset = mResource->GetLength() - aOffset;
    } break;
    default:
      return NS_ERROR_FAILURE;
  }

  if (aOffset < 0) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  mOffset = aOffset;

  return NS_OK;
}

already_AddRefed<MediaByteBuffer> MediaResourceIndex::MediaReadAt(
    int64_t aOffset, uint32_t aCount) const {
  NS_ENSURE_TRUE(aOffset >= 0, nullptr);
  RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
  bool ok = bytes->SetLength(aCount, fallible);
  NS_ENSURE_TRUE(ok, nullptr);

  uint32_t bytesRead = 0;
  nsresult rv = mResource->ReadAt(
      aOffset, reinterpret_cast<char*>(bytes->Elements()), aCount, &bytesRead);
  NS_ENSURE_SUCCESS(rv, nullptr);

  bytes->SetLength(bytesRead);
  return bytes.forget();
}

already_AddRefed<MediaByteBuffer> MediaResourceIndex::CachedMediaReadAt(
    int64_t aOffset, uint32_t aCount) const {
  RefPtr<MediaByteBuffer> bytes = new MediaByteBuffer();
  bool ok = bytes->SetLength(aCount, fallible);
  NS_ENSURE_TRUE(ok, nullptr);
  char* curr = reinterpret_cast<char*>(bytes->Elements());
  nsresult rv = mResource->ReadFromCache(curr, aOffset, aCount);
  NS_ENSURE_SUCCESS(rv, nullptr);
  return bytes.forget();
}

// Get the length of the stream in bytes. Returns -1 if not known.
// This can change over time; after a seek operation, a misbehaving
// server may give us a resource of a different length to what it had
// reported previously --- or it may just lie in its Content-Length
// header and give us more or less data than it reported. We will adjust
// the result of GetLength to reflect the data that's actually arriving.
int64_t MediaResourceIndex::GetLength() const { return mResource->GetLength(); }

uint32_t MediaResourceIndex::IndexInCache(int64_t aOffsetInFile) const {
  const uint32_t index = uint32_t(aOffsetInFile) & (mCacheBlockSize - 1);
  MOZ_ASSERT(index == aOffsetInFile % mCacheBlockSize);
  return index;
}

int64_t MediaResourceIndex::CacheOffsetContaining(int64_t aOffsetInFile) const {
  const int64_t offset = aOffsetInFile & ~(int64_t(mCacheBlockSize) - 1);
  MOZ_ASSERT(offset == aOffsetInFile - IndexInCache(aOffsetInFile));
  return offset;
}

}  // namespace mozilla

// avoid redefined macro in unified build
#undef ILOG

Messung V0.5
C=90 H=97 G=93

¤ Dauer der Verarbeitung: 0.1 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.