Beispiel #1
0
uint32_t ResourceQueue::EvictBefore(uint64_t aOffset, ErrorResult& aRv)
{
  SBR_DEBUG("EvictBefore(%llu)", aOffset);
  uint32_t evicted = 0;
  while (ResourceItem* item = ResourceAt(0)) {
    SBR_DEBUG("item=%p length=%d offset=%llu",
              item, item->mData->Length(), mOffset);
    if (item->mData->Length() + mOffset >= aOffset) {
      if (aOffset <= mOffset) {
        break;
      }
      uint32_t offset = aOffset - mOffset;
      mOffset += offset;
      evicted += offset;
      RefPtr<MediaByteBuffer> data = new MediaByteBuffer;
      if (!data->AppendElements(item->mData->Elements() + offset,
                                item->mData->Length() - offset,
                                fallible)) {
        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
        return 0;
      }

      item->mData = data;
      break;
    }
    mOffset += item->mData->Length();
    evicted += item->mData->Length();
    delete PopFront();
  }
  return evicted;
}
Beispiel #2
0
TEST(ContainerParser, ADTSBlankMedia)
{
  nsAutoPtr<ContainerParser> parser;
  parser = ContainerParser::CreateForMIMEType(
      MediaContainerType(MEDIAMIMETYPE("audio/aac")));
  ASSERT_NE(parser, nullptr);

  // Audio data should have no gaps.
  EXPECT_EQ(parser->GetRoundingError(), 0);

  // Test the header only.
  RefPtr<MediaByteBuffer> header = make_adts_header();
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)));

  // Test with the correct length of (invalid) frame data.
  size_t header_length = header->Length();
  size_t data_length = 24;
  size_t frame_length = header_length + data_length;
  header->AppendElements(data_length);
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
      << "Rejected a valid header.";
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
      << "Rejected a full (but zeroed) media segment.";
  int64_t start = 0;
  int64_t end = 0;
  // We don't report timestamps from ADTS.
  EXPECT_TRUE(
      NS_FAILED(parser->ParseStartAndEndTimestamps(header, start, end)));
  EXPECT_EQ(start, 0);
  EXPECT_EQ(end, 0);

  // Verify the parser calculated header and packet data boundaries.
  EXPECT_TRUE(parser->HasInitData());
  EXPECT_TRUE(parser->HasCompleteInitData());
  MediaByteBuffer* init = parser->InitData();
  ASSERT_NE(init, nullptr);
  EXPECT_EQ(init->Length(), header_length)
      << "Found incorrect init segment length.";
  EXPECT_EQ(parser->InitSegmentRange(),
            MediaByteRange(0, int64_t(header_length)));
  // In ADTS the Media Header is the same as the Media Segment.
  MediaByteRange expected_media =
      MediaByteRange(int64_t(header_length), int64_t(frame_length));
  EXPECT_EQ(parser->MediaHeaderRange(), expected_media);
  EXPECT_EQ(parser->MediaSegmentRange(), expected_media);
}
Beispiel #3
0
already_AddRefed<MediaByteBuffer>
SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
{
  typedef TrackBuffersManager::EvictDataResult Result;

  if (!IsAttached() || mUpdating) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return nullptr;
  }

  // If the HTMLMediaElement.error attribute is not null, then throw an
  // InvalidStateError exception and abort these steps.
  if (!mMediaSource->GetDecoder() ||
      mMediaSource->GetDecoder()->OwnerHasError()) {
    MSE_DEBUG("HTMLMediaElement.error is not null");
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return nullptr;
  }

  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
  }

  // Eviction uses a byte threshold. If the buffer is greater than the
  // number of bytes then data is evicted.
  // TODO: Drive evictions off memory pressure notifications.
  // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
  // Give a chance to the TrackBuffersManager to evict some data if needed.
  Result evicted =
    mTrackBuffersManager->EvictData(TimeUnit::FromSeconds(mMediaSource->GetDecoder()->GetCurrentTime()),
                                    aLength);

  // See if we have enough free space to append our new data.
  if (evicted == Result::BUFFER_FULL) {
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
    return nullptr;
  }

  RefPtr<MediaByteBuffer> data = new MediaByteBuffer();
  if (!data->AppendElements(aData, aLength, fallible)) {
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
    return nullptr;
  }
  return data.forget();
}
TEST(BitWriter, SPS) {
  uint8_t sps_pps[] = {0x01, 0x4d, 0x40, 0x0c, 0xff, 0xe1, 0x00, 0x1b, 0x67,
                       0x4d, 0x40, 0x0c, 0xe8, 0x80, 0x80, 0x9d, 0x80, 0xb5,
                       0x01, 0x01, 0x01, 0x40, 0x00, 0x00, 0x03, 0x00, 0x40,
                       0x00, 0x00, 0x0f, 0x03, 0xc5, 0x0a, 0x44, 0x80, 0x01,
                       0x00, 0x04, 0x68, 0xeb, 0xef, 0x20};

  RefPtr<MediaByteBuffer> extraData = new MediaByteBuffer();
  extraData->AppendElements(sps_pps, sizeof(sps_pps));
  SPSData spsdata1;
  bool success = H264::DecodeSPSFromExtraData(extraData, spsdata1);
  EXPECT_EQ(success, true);

  RefPtr<MediaByteBuffer> extraData2 =
      H264::CreateExtraData(0x42, 0xc0, 0x1e, {1280, 720});
  SPSData spsdata2;
  success = H264::DecodeSPSFromExtraData(extraData2, spsdata2);
  EXPECT_EQ(success, true);
}
Beispiel #5
0
/* static */ bool
H264::DecodeSPSFromExtraData(const mozilla::MediaByteBuffer* aExtraData, SPSData& aDest)
{
  if (!AnnexB::HasSPS(aExtraData)) {
    return false;
  }
  ByteReader reader(aExtraData);

  if (!reader.Read(5)) {
    return false;
  }

  if (!(reader.ReadU8() & 0x1f)) {
    // No SPS.
    reader.DiscardRemaining();
    return false;
  }
  uint16_t length = reader.ReadU16();

  if ((reader.PeekU8() & 0x1f) != 7) {
    // Not a SPS NAL type.
    reader.DiscardRemaining();
    return false;
  }

  const uint8_t* ptr = reader.Read(length);
  if (!ptr) {
    return false;
  }

  reader.DiscardRemaining();

  RefPtr<mozilla::MediaByteBuffer> rawNAL = new mozilla::MediaByteBuffer;
  rawNAL->AppendElements(ptr, length);

  RefPtr<mozilla::MediaByteBuffer> sps = DecodeNALUnit(rawNAL);

  if (!sps) {
    return false;
  }

  return DecodeSPS(sps, aDest);
}
TEST(stagefright_MP4Metadata, EmptyCTTS)
{
  RefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_libstagefright_gtest_video_init_mp4_len);
  buffer->AppendElements(media_libstagefright_gtest_video_init_mp4, media_libstagefright_gtest_video_init_mp4_len);
  RefPtr<BufferStream> stream = new BufferStream(buffer);

  EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
  RefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
  EXPECT_TRUE(metadataBuffer);

  MP4Metadata metadata(stream);

  EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
  mozilla::UniquePtr<mozilla::TrackInfo> track =
    metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
  EXPECT_TRUE(track != nullptr);
  // We can seek anywhere in any MPEG4.
  EXPECT_TRUE(metadata.CanSeek());
  EXPECT_FALSE(metadata.Crypto().valid);
}
Beispiel #7
0
already_AddRefed<MediaByteBuffer>
SourceBuffer::PrepareAppend(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv)
{
  typedef SourceBufferContentManager::EvictDataResult Result;

  if (!IsAttached() || mUpdating) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return nullptr;
  }

  // If the HTMLMediaElement.error attribute is not null, then throw an
  // InvalidStateError exception and abort these steps.
  if (!mMediaSource->GetDecoder() ||
      mMediaSource->GetDecoder()->IsEndedOrShutdown()) {
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    return nullptr;
  }

  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
  }

  // Eviction uses a byte threshold. If the buffer is greater than the
  // number of bytes then data is evicted. The time range for this
  // eviction is reported back to the media source. It will then
  // evict data before that range across all SourceBuffers it knows
  // about.
  // TODO: Make the eviction threshold smaller for audio-only streams.
  // TODO: Drive evictions off memory pressure notifications.
  // TODO: Consider a global eviction threshold  rather than per TrackBuffer.
  TimeUnit newBufferStartTime;
  // Attempt to evict the amount of data we are about to add by lowering the
  // threshold.
  uint32_t toEvict =
    (mEvictionThreshold > aLength) ? mEvictionThreshold - aLength : aLength;
  Result evicted =
    mContentManager->EvictData(TimeUnit::FromSeconds(mMediaSource->GetDecoder()->GetCurrentTime()),
                               toEvict, &newBufferStartTime);
  if (evicted == Result::DATA_EVICTED) {
    MSE_DEBUG("AppendData Evict; current buffered start=%f",
              GetBufferedStart());

    // We notify that we've evicted from the time range 0 through to
    // the current start point.
    mMediaSource->NotifyEvicted(0.0, newBufferStartTime.ToSeconds());
  }

  // See if we have enough free space to append our new data.
  // As we can only evict once we have playable data, we must give a chance
  // to the DASH player to provide a complete media segment.
  if (aLength > mEvictionThreshold || evicted == Result::BUFFER_FULL) {
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
    return nullptr;
  }

  RefPtr<MediaByteBuffer> data = new MediaByteBuffer();
  if (!data->AppendElements(aData, aLength, fallible)) {
    aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
    return nullptr;
  }
  return data.forget();
}
Beispiel #8
0
TEST(ContainerParser, ADTSHeader)
{
  nsAutoPtr<ContainerParser> parser;
  parser = ContainerParser::CreateForMIMEType(
      MediaContainerType(MEDIAMIMETYPE("audio/aac")));
  ASSERT_NE(parser, nullptr);

  // Audio data should have no gaps.
  EXPECT_EQ(parser->GetRoundingError(), 0);

  // Test a valid header.
  RefPtr<MediaByteBuffer> header = make_adts_header();
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)));

  // Test variations.
  uint8_t save = header->ElementAt(1);
  for (uint8_t i = 1; i < 3; ++i) {
    // Set non-zero layer.
    header->ReplaceElementAt(1, (header->ElementAt(1) & 0xf9) | (i << 1));
    EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
        << "Accepted non-zero layer in header.";
  }
  header->ReplaceElementAt(1, save);
  save = header->ElementAt(2);
  header->ReplaceElementAt(2, (header->ElementAt(2) & 0x3b) | (15 << 2));
  EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
      << "Accepted explicit frequency in header.";
  header->ReplaceElementAt(2, save);

  // Test a short header.
  header->SetLength(6);
  EXPECT_FALSE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
      << "Accepted too-short header.";
  EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
      << "Found media segment when there was just a partial header.";

  // Test a header with short data.
  header = make_adts_header();
  header->AppendElements(1);
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsInitSegmentPresent(header)))
      << "Rejected a valid header.";
  EXPECT_TRUE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
      << "Rejected a one-byte media segment.";

  // Test parse results.
  header = make_adts_header();
  EXPECT_FALSE(NS_SUCCEEDED(parser->IsMediaSegmentPresent(header)))
      << "Found media segment when there was just a header.";
  int64_t start = 0;
  int64_t end = 0;
  EXPECT_TRUE(
      NS_FAILED(parser->ParseStartAndEndTimestamps(header, start, end)));

  EXPECT_TRUE(parser->HasInitData());
  EXPECT_TRUE(parser->HasCompleteInitData());
  MediaByteBuffer* init = parser->InitData();
  ASSERT_NE(init, nullptr);
  EXPECT_EQ(init->Length(), header->Length());

  EXPECT_EQ(parser->InitSegmentRange(),
            MediaByteRange(0, int64_t(header->Length())));
  // Media segment range should be empty here.
  EXPECT_EQ(parser->MediaHeaderRange(), MediaByteRange());
  EXPECT_EQ(parser->MediaSegmentRange(), MediaByteRange());
}