chromium/media/filters/chunk_demuxer_unittest.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "media/filters/chunk_demuxer.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <memory>
#include <queue>
#include <utility>

#include "base/command_line.h"
#include "base/containers/heap_array.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/media.h"
#include "media/base/media_switches.h"
#include "media/base/media_tracks.h"
#include "media/base/mock_demuxer_host.h"
#include "media/base/mock_media_log.h"
#include "media/base/test_data_util.h"
#include "media/base/test_helpers.h"
#include "media/base/timestamp_constants.h"
#include "media/formats/webm/cluster_builder.h"
#include "media/formats/webm/webm_cluster_parser.h"
#include "media/formats/webm/webm_constants.h"
#include "media/media_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"

AnyNumber;
AtLeast;
Exactly;
HasSubstr;
InSequence;
NotNull;
Return;
SaveArg;
SetArgPointee;
StrictMock;
WithParamInterface;
_;

namespace media {

const uint8_t kTracksHeader[] =;

const uint8_t kCuesHeader[] =;

const uint8_t kEncryptedMediaInitData[] =;

const int kTracksHeaderSize =;
const int kTracksSizeOffset =;

// The size of TrackEntry element in test file "webm_vorbis_track_entry" starts
// at index 1 and spans 8 bytes.
const int kAudioTrackSizeOffset =;
const int kAudioTrackSizeWidth =;
const int kAudioTrackEntryHeaderSize =;

// The size of TrackEntry element in test file "webm_vp8_track_entry" starts at
// index 1 and spans 8 bytes.
const int kVideoTrackSizeOffset =;
const int kVideoTrackSizeWidth =;
const int kVideoTrackEntryHeaderSize =;

const int kVideoTrackNum =;
const int kAudioTrackNum =;
const int kAlternateVideoTrackNum =;
const int kAlternateAudioTrackNum =;
const int kAlternateTextTrackNum =;

const int kAudioBlockDuration =;
const int kVideoBlockDuration =;
const int kTextBlockDuration =;
const int kBlockSize =;

const char kSourceId[] =;
const char kDefaultFirstClusterRange[] =;
const int kDefaultFirstClusterEndTimestamp =;
const int kDefaultSecondClusterEndTimestamp =;

base::TimeDelta kDefaultDuration() {}

// Write an integer into buffer in the form of vint that spans 8 bytes.
// The data pointed by |buffer| should be at least 8 bytes long.
// |number| should be in the range 0 <= number < 0x00FFFFFFFFFFFFFF.
static void WriteInt64(uint8_t* buffer, int64_t number) {}

static void CheckBuffers(base::TimeDelta expected_start_time,
                         const int expected_duration_time_ms,
                         const DemuxerStream::DecoderBufferVector& buffers) {}

static void OnReadDone_Ok(base::TimeDelta expected_start_time,
                          const int expected_duration_time_ms,
                          const size_t expected_read_count,
                          bool* called,
                          DemuxerStream::Status status,
                          DemuxerStream::DecoderBufferVector buffers) {}

static void OnReadDone_AbortExpected(
    bool* called,
    DemuxerStream::Status status,
    DemuxerStream::DecoderBufferVector buffers) {}

static void OnReadDone_LastBufferEOSExpected(
    bool* called,
    DemuxerStream::Status status,
    DemuxerStream::DecoderBufferVector buffers) {}

static void OnSeekDone_OKExpected(bool* called, PipelineStatus status) {}

static void StoreStatusAndBuffers(
    DemuxerStream::Status* status_out,
    DemuxerStream::DecoderBufferVector* buffers_out,
    DemuxerStream::Status status,
    DemuxerStream::DecoderBufferVector buffers) {}

class ChunkDemuxerTest : public ::testing::Test {};

TEST_F(ChunkDemuxerTest, Init) {}

TEST_F(ChunkDemuxerTest, AddIdDuringOpenCallback) {}

TEST_F(ChunkDemuxerTest, AudioVideoTrackIdsChange) {}

TEST_F(ChunkDemuxerTest, InitSegmentSetsNeedRandomAccessPointFlag) {}

TEST_F(ChunkDemuxerTest, Shutdown_BeforeAllInitSegmentsAppended) {}

// Verifies that all streams waiting for data receive an end of stream
// buffer when Shutdown() is called.
TEST_F(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) {}

// Test that Seek() completes successfully when the first cluster
// arrives.
TEST_F(ChunkDemuxerTest, AppendDataAfterSeek) {}

// Test that parsing errors are handled for clusters appended after init.
TEST_F(ChunkDemuxerTest, ErrorWhileParsingClusterAfterInit) {}

// Test the case where a Seek() is requested while the parser
// is in the middle of cluster. This is to verify that the parser
// does not reset itself on a seek.
TEST_F(ChunkDemuxerTest, SeekWhileParsingCluster) {}

// Test the case where AppendToParseBuffer() and RunSegmentParserLoop() are
// called before ChunkDemuxer::Initialize().
TEST_F(ChunkDemuxerTest, AppendToParseBufferBeforeInit) {}

// Testing for batch read.
TEST_F(ChunkDemuxerTest, ReadMultiBuffer_kOK) {}

TEST_F(ChunkDemuxerTest, ReadMultiBuffer_ActualReadCount_LE_RequestedCount) {}

TEST_F(ChunkDemuxerTest, ReadMultiBuffer_LastBufferIsEOS) {}

// Make sure Read() callbacks are dispatched with the proper data.
TEST_F(ChunkDemuxerTest, ReadOneBuffer) {}

TEST_F(ChunkDemuxerTest, OutOfOrderClusters) {}

TEST_F(ChunkDemuxerTest, NonMonotonicButAboveClusterTimecode) {}

TEST_F(ChunkDemuxerTest, BeforeClusterTimecode) {}

TEST_F(ChunkDemuxerTest, NonMonotonicButBeforeClusterTimecode) {}

TEST_F(ChunkDemuxerTest, PerStreamMonotonicallyIncreasingTimestamps) {}

// Test the case where a cluster is passed to AppendCluster() before
// INFO & TRACKS data.
TEST_F(ChunkDemuxerTest, ClusterBeforeInitSegment) {}

// Test cases where we get an MarkEndOfStream() call during initialization.
TEST_F(ChunkDemuxerTest, EOSDuringInit) {}

TEST_F(ChunkDemuxerTest, EndOfStreamWithNoAppend) {}

TEST_F(ChunkDemuxerTest, EndOfStreamWithNoMediaAppend) {}

TEST_F(ChunkDemuxerTest, DecodeErrorEndOfStream) {}

TEST_F(ChunkDemuxerTest, NetworkErrorEndOfStream) {}

// Helper class to reduce duplicate code when testing end of stream
// Read() behavior.
class EndOfStreamHelper {};

// Make sure that all pending reads that we don't have media data for get an
// "end of stream" buffer when MarkEndOfStream() is called.
TEST_F(ChunkDemuxerTest, EndOfStreamWithPendingReads) {}

// Make sure that all Read() calls after we get an MarkEndOfStream()
// call return an "end of stream" buffer.
TEST_F(ChunkDemuxerTest, ReadsAfterEndOfStream) {}

TEST_F(ChunkDemuxerTest, EndOfStreamDuringCanceledSeek) {}

// Verify buffered range change behavior for audio/video/text tracks.
TEST_F(ChunkDemuxerTest, EndOfStreamRangeChanges) {}

// Make sure AppendData() will accept elements that span multiple calls.
TEST_F(ChunkDemuxerTest, AppendingInPieces) {}

TEST_F(ChunkDemuxerTest, WebMFile_AudioAndVideo) {}

TEST_F(ChunkDemuxerTest, WebMFile_LiveAudioAndVideo) {}

TEST_F(ChunkDemuxerTest, WebMFile_AudioOnly) {}

TEST_F(ChunkDemuxerTest, WebMFile_VideoOnly) {}

TEST_F(ChunkDemuxerTest, WebMFile_AltRefFrames) {}

// Verify that we output buffers before the entire cluster has been parsed.
TEST_F(ChunkDemuxerTest, IncrementalClusterParsing) {}

TEST_F(ChunkDemuxerTest, ParseErrorDuringInit) {}

TEST_F(ChunkDemuxerTest, AVHeadersWithAudioOnlyType) {}

TEST_F(ChunkDemuxerTest, AVHeadersWithVideoOnlyType) {}

TEST_F(ChunkDemuxerTest, AudioOnlyHeaderWithAVType) {}

TEST_F(ChunkDemuxerTest, VideoOnlyHeaderWithAVType) {}

TEST_F(ChunkDemuxerTest, MultipleHeaders) {}

TEST_F(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideo) {}

TEST_F(ChunkDemuxerTest, AddSeparateSourcesForAudioAndVideoText) {}

TEST_F(ChunkDemuxerTest, AddIdFailures) {}

// Test that Read() calls after a RemoveId() return "end of stream" buffers.
TEST_F(ChunkDemuxerTest, RemoveId) {}

// Test that removing an ID immediately after adding it does not interfere with
// quota for new IDs in the future.
TEST_F(ChunkDemuxerTest, RemoveAndAddId) {}

TEST_F(ChunkDemuxerTest, SeekCanceled) {}

TEST_F(ChunkDemuxerTest, SeekCanceledWhileWaitingForSeek) {}

// Test that Seek() successfully seeks to all source IDs.
TEST_F(ChunkDemuxerTest, SeekAudioAndVideoSources) {}

// Test that Seek() completes successfully when EndOfStream
// is called before data is available for that seek point.
// This scenario might be useful if seeking past the end of stream
// of either audio or video (or both).
TEST_F(ChunkDemuxerTest, EndOfStreamAfterPastEosSeek) {}

// Test that EndOfStream is ignored if coming during a pending seek
// whose seek time is before some existing ranges.
TEST_F(ChunkDemuxerTest, EndOfStreamDuringPendingSeek) {}

// Test ranges in an audio-only stream.
TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) {}

// Test ranges in a video-only stream.
TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) {}

TEST_F(ChunkDemuxerTest, GetBufferedRanges_SeparateStreams) {}

TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) {}

// Once MarkEndOfStream() is called, GetBufferedRanges should not cut off any
// over-hanging tails at the end of the ranges as this is likely due to block
// duration differences.
TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) {}

TEST_F(ChunkDemuxerTest, DifferentStreamTimecodes) {}

TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesSeparateSources) {}

TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesOutOfRange) {}

TEST_F(ChunkDemuxerTest, CodecPrefixMatching) {}

// Test codec ID's that are not compliant with RFC6381, but have been
// seen in the wild.
TEST_F(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) {}

TEST_F(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) {}

TEST_F(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) {}

// Test that Seek() completes successfully when the first cluster
// arrives.
TEST_F(ChunkDemuxerTest, EndOfStreamDuringSeek) {}

TEST_F(ChunkDemuxerTest, ConfigChange_Video) {}

TEST_F(ChunkDemuxerTest, ConfigChange_Audio) {}

TEST_F(ChunkDemuxerTest, ConfigChange_Seek) {}

TEST_F(ChunkDemuxerTest, TimestampPositiveOffset) {}

TEST_F(ChunkDemuxerTest, TimestampNegativeOffset) {}

TEST_F(ChunkDemuxerTest, TimestampOffsetSeparateStreams) {}

TEST_F(ChunkDemuxerTest, IsParsingMediaSegmentMidMediaSegment) {}

#if BUILDFLAG(USE_PROPRIETARY_CODECS)
#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
namespace {
const char* kMp2tMimeType = "video/mp2t";
const char* kMp2tCodecs = "mp4a.40.2,avc1.640028";
}

TEST_F(ChunkDemuxerTest, EmitBuffersDuringAbort) {
  EXPECT_CALL(*this, DemuxerOpened());
  EXPECT_FOUND_CODEC_NAME(Audio, "aac");
  EXPECT_FOUND_CODEC_NAME(Video, "h264");
  demuxer_->Initialize(&host_,
                       CreateInitDoneCallback(kInfiniteDuration, PIPELINE_OK));
  EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, kMp2tMimeType, kMp2tCodecs));

  // For info:
  // DTS/PTS derived using dvbsnoop -s ts -if bear-1280x720.ts -tssubdecode
  // Video: first PES:
  //        PTS: 126912 (0x0001efc0)  [= 90 kHz-Timestamp: 0:00:01.4101]
  //        DTS: 123909 (0x0001e405)  [= 90 kHz-Timestamp: 0:00:01.3767]
  // Audio: first PES:
  //        PTS: 126000 (0x0001ec30)  [= 90 kHz-Timestamp: 0:00:01.4000]
  //        DTS: 123910 (0x0001e406)  [= 90 kHz-Timestamp: 0:00:01.3767]
  // Video: last PES:
  //        PTS: 370155 (0x0005a5eb)  [= 90 kHz-Timestamp: 0:00:04.1128]
  //        DTS: 367152 (0x00059a30)  [= 90 kHz-Timestamp: 0:00:04.0794]
  // Audio: last PES:
  //        PTS: 353788 (0x000565fc)  [= 90 kHz-Timestamp: 0:00:03.9309]

  scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("bear-1280x720.ts");
  EXPECT_CALL(*this, InitSegmentReceivedMock(_));

  // This mp2ts file contains buffers which can trigger media logs related to
  // splicing, especially when appending in small chunks.
  EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(1655422, 1655419, 23217));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(1957277, 4));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2514555, 6));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(3071833, 6));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(3652333, 6));

  // Append the media in small chunks.
  size_t appended_bytes = 0;
  const size_t chunk_size = 1024;
  while (appended_bytes < buffer->size()) {
    size_t cur_chunk_size =
        std::min(chunk_size, buffer->size() - appended_bytes);
    ASSERT_TRUE(AppendData(
        kSourceId,
        base::make_span(buffer->data() + appended_bytes, cur_chunk_size)));
    appended_bytes += cur_chunk_size;
  }

  // Confirm we're in the middle of parsing a media segment.
  ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));

  // ResetParserState on the Mpeg2 TS parser triggers the emission of the last
  // video buffer which is pending in the stream parser.
  Ranges<base::TimeDelta> range_before_abort =
      demuxer_->GetBufferedRanges(kSourceId);
  demuxer_->ResetParserState(kSourceId,
                             append_window_start_for_next_append_,
                             append_window_end_for_next_append_,
                             &timestamp_offset_map_[kSourceId]);
  Ranges<base::TimeDelta> range_after_abort =
      demuxer_->GetBufferedRanges(kSourceId);

  ASSERT_EQ(range_before_abort.size(), 1u);
  ASSERT_EQ(range_after_abort.size(), 1u);
  EXPECT_EQ(range_after_abort.start(0), range_before_abort.start(0));
  EXPECT_GT(range_after_abort.end(0), range_before_abort.end(0));
}

TEST_F(ChunkDemuxerTest, SeekCompleteDuringAbort) {
  EXPECT_CALL(*this, DemuxerOpened());
  EXPECT_FOUND_CODEC_NAME(Video, "h264");
  EXPECT_FOUND_CODEC_NAME(Audio, "aac");
  demuxer_->Initialize(&host_,
                       CreateInitDoneCallback(kInfiniteDuration, PIPELINE_OK));
  EXPECT_EQ(ChunkDemuxer::kOk, AddId(kSourceId, kMp2tMimeType, kMp2tCodecs));

  // For info:
  // DTS/PTS derived using dvbsnoop -s ts -if bear-1280x720.ts -tssubdecode
  // Video: first PES:
  //        PTS: 126912 (0x0001efc0)  [= 90 kHz-Timestamp: 0:00:01.4101]
  //        DTS: 123909 (0x0001e405)  [= 90 kHz-Timestamp: 0:00:01.3767]
  // Audio: first PES:
  //        PTS: 126000 (0x0001ec30)  [= 90 kHz-Timestamp: 0:00:01.4000]
  //        DTS: 123910 (0x0001e406)  [= 90 kHz-Timestamp: 0:00:01.3767]
  // Video: last PES:
  //        PTS: 370155 (0x0005a5eb)  [= 90 kHz-Timestamp: 0:00:04.1128]
  //        DTS: 367152 (0x00059a30)  [= 90 kHz-Timestamp: 0:00:04.0794]
  // Audio: last PES:
  //        PTS: 353788 (0x000565fc)  [= 90 kHz-Timestamp: 0:00:03.9309]

  scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("bear-1280x720.ts");
  EXPECT_CALL(*this, InitSegmentReceivedMock(_));

  // This mp2ts file contains buffers which can trigger media logs related to
  // splicing, especially when appending in small chunks.
  EXPECT_MEDIA_LOG(TrimmedSpliceOverlap(1655422, 1655419, 23217));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(1957277, 4));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2514555, 6));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(3071833, 6));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(3652333, 6));

  // Append the media in small chunks.
  size_t appended_bytes = 0;
  const size_t chunk_size = 1024;
  while (appended_bytes < buffer->size()) {
    size_t cur_chunk_size =
        std::min(chunk_size, buffer->size() - appended_bytes);
    ASSERT_TRUE(AppendData(
        kSourceId,
        base::make_span(buffer->data() + appended_bytes, cur_chunk_size)));
    appended_bytes += cur_chunk_size;
  }

  // Confirm we're in the middle of parsing a media segment.
  ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));

  // Seek to a time corresponding to buffers that will be emitted during the
  // abort.
  Seek(base::Milliseconds(4110));

  // ResetParserState on the Mpeg2 TS parser triggers the emission of the last
  // video buffer which is pending in the stream parser.
  demuxer_->ResetParserState(kSourceId,
                             append_window_start_for_next_append_,
                             append_window_end_for_next_append_,
                             &timestamp_offset_map_[kSourceId]);
}

#endif
#endif

TEST_F(ChunkDemuxerTest, WebMIsParsingMediaSegmentDetection) {}

TEST_F(ChunkDemuxerTest, DurationChange) {}

TEST_F(ChunkDemuxerTest, DurationChangeTimestampOffset) {}

TEST_F(ChunkDemuxerTest, EndOfStreamTruncateDuration) {}

TEST_F(ChunkDemuxerTest, ZeroLengthAppend) {}

TEST_F(ChunkDemuxerTest, AppendAfterEndOfStream) {}

// Test receiving a Shutdown() call before we get an Initialize()
// call. This can happen if video element gets destroyed before
// the pipeline has a chance to initialize the demuxer.
TEST_F(ChunkDemuxerTest, Shutdown_BeforeInitialize) {}

// Verifies that signaling end of stream while stalled at a gap
// boundary does not trigger end of stream buffers to be returned.
TEST_F(ChunkDemuxerTest, EndOfStreamWhileWaitingForGapToBeFilled) {}

TEST_F(ChunkDemuxerTest, CanceledSeekDuringInitialPreroll) {}

TEST_F(ChunkDemuxerTest, SetMemoryLimitType) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_SingleRange_SeekForward) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_SingleRange_SeekBack) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_MultipleRanges_SeekForward) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_MultipleRanges_SeekInbetween1) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_MultipleRanges_SeekInbetween2) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek_MultipleRanges_SeekBack) {}

TEST_F(ChunkDemuxerTest, GCDuringSeek) {}

TEST_F(ChunkDemuxerTest, GCKeepPlayhead) {}

TEST_F(ChunkDemuxerTest, AppendWindow_Video) {}

TEST_F(ChunkDemuxerTest, AppendWindow_Audio) {}

TEST_F(ChunkDemuxerTest, AppendWindow_AudioOverlapStartAndEnd) {}

TEST_F(ChunkDemuxerTest, AppendWindow_WebMFile_AudioOnly) {}

TEST_F(ChunkDemuxerTest, AppendWindow_AudioConfigUpdateRemovesPreroll) {}

TEST_F(ChunkDemuxerTest, StartWaitingForSeekAfterParseError) {}

TEST_F(ChunkDemuxerTest, Remove_AudioVideoText) {}

TEST_F(ChunkDemuxerTest, Remove_StartAtDuration) {}

// Verifies that a Seek() will complete without text cues for
// the seek point and will return cues after the seek position
// when they are eventually appended.
TEST_F(ChunkDemuxerTest, SeekCompletesWithoutTextCues) {}

TEST_F(ChunkDemuxerTest, ClusterWithUnknownSize) {}

TEST_F(ChunkDemuxerTest, CuesBetweenClustersWithUnknownSize) {}

TEST_F(ChunkDemuxerTest, CuesBetweenClusters) {}

TEST_F(ChunkDemuxerTest, EvictCodedFramesTest) {}

TEST_F(ChunkDemuxerTest, SegmentMissingAudioFrame_AudioOnly) {}

TEST_F(ChunkDemuxerTest, SegmentMissingVideoFrame_VideoOnly) {}

TEST_F(ChunkDemuxerTest, SegmentMissingAudioFrame_AudioVideo) {}

TEST_F(ChunkDemuxerTest, SegmentMissingVideoFrame_AudioVideo) {}

TEST_F(ChunkDemuxerTest, SegmentMissingAudioVideoFrames) {}

TEST_F(ChunkDemuxerTest, RelaxedKeyframe_FirstSegmentMissingKeyframe) {}

TEST_F(ChunkDemuxerTest, RelaxedKeyframe_SecondSegmentMissingKeyframe) {}

TEST_F(ChunkDemuxerTest, RelaxedKeyframe_RemoveInterruptsCodedFrameGroup_1) {}

TEST_F(ChunkDemuxerTest, RelaxedKeyframe_RemoveInterruptsCodedFrameGroup_2) {}

TEST_F(ChunkDemuxerTest, RelaxedKeyframe_RemoveInterruptsCodedFrameGroup_3) {}

TEST_F(ChunkDemuxerTest,
       RelaxedKeyframe_RemoveInterruptsMuxedCodedFrameGroup_1) {}

TEST_F(ChunkDemuxerTest,
       RelaxedKeyframe_RemoveInterruptsMuxedCodedFrameGroup_2) {}

TEST_F(ChunkDemuxerTest,
       RelaxedKeyframe_RemoveInterruptsMuxedCodedFrameGroup_3) {}

namespace {
void QuitLoop(base::OnceClosure quit_closure,
              DemuxerStream::Type type,
              const std::vector<DemuxerStream*>& streams) {}

void DisableAndEnableDemuxerTracks(
    ChunkDemuxer* demuxer,
    base::test::TaskEnvironment* task_environment) {}
}  // namespace

TEST_F(ChunkDemuxerTest, StreamStatusNotifications) {}

TEST_F(ChunkDemuxerTest, MultipleIds) {}

TEST_F(ChunkDemuxerTest, CompleteInitAfterIdRemoved) {}

TEST_F(ChunkDemuxerTest, RemovingIdMustRemoveStreams) {}

TEST_F(ChunkDemuxerTest, SequenceModeMuxedAppendShouldWarn) {}

TEST_F(ChunkDemuxerTest, SequenceModeSingleTrackNoWarning) {}

TEST_F(ChunkDemuxerTest, GetLowestAndHighestPresentationTimestamps_NonMuxed) {}

TEST_F(ChunkDemuxerTest, GetLowestAndHighestPresentationTimestamps_Muxed) {}

TEST_F(ChunkDemuxerTest, Mp4Vp9CodecSupport) {}

TEST_F(ChunkDemuxerTest, UnmarkEOSRetainsParseErrorState_BeforeInit) {}

TEST_F(ChunkDemuxerTest, UnmarkEOSRetainsParseErrorState_AfterInit) {}

struct ZeroLengthFrameCase {};

// Test that 0-length audio and video coded frames are dropped gracefully.
TEST_F(ChunkDemuxerTest, ZeroLengthFramesDropped) {}

#if BUILDFLAG(ENABLE_HLS_DEMUXER)
TEST_F(ChunkDemuxerTest, AddAutoDetectIDFindsCodecs) {
  CreateNewDemuxer();

  EXPECT_CALL(*this, DemuxerOpened());
  EXPECT_CALL(host_, SetDuration(_));
  demuxer_->Initialize(&host_,
                       CreateInitDoneCallback(kNoTimestamp, PIPELINE_OK));

  const char* kPrimary = "primary";
  AddAutoDetectedCodecsId_Checked(kPrimary, RelaxedParserSupportedType::kMP2T);
  scoped_refptr<DecoderBuffer> data = ReadTestDataFile("hls/bear0.ts");

  EXPECT_FOUND_CODEC_NAME(Video, "h264");
  EXPECT_FOUND_CODEC_NAME(Audio, "aac");
  EXPECT_CALL(*this, InitSegmentReceivedMock(_));

  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(1791811, 5));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2116888, 6));
  EXPECT_MEDIA_LOG(SkippingSpliceTooLittleOverlap(2441966, 6));

  EXPECT_TRUE(
      AppendData(kPrimary, base::make_span(data->data(), data->size())));

  CheckExpectedRanges(kPrimary, "{ [1466,2267) }");
}
#endif

// TODO(servolk): Add a unit test with multiple audio/video tracks using the
// same codec type in a single SourceBufferState, when WebM parser supports
// multiple tracks. crbug.com/646900

}  // namespace media