#ifdef UNSAFE_BUFFERS_BUILD
#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 = …;
const int kAudioTrackSizeOffset = …;
const int kAudioTrackSizeWidth = …;
const int kAudioTrackEntryHeaderSize = …;
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() { … }
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) { … }
TEST_F(ChunkDemuxerTest, Shutdown_EndOfStreamWhileWaitingForData) { … }
TEST_F(ChunkDemuxerTest, AppendDataAfterSeek) { … }
TEST_F(ChunkDemuxerTest, ErrorWhileParsingClusterAfterInit) { … }
TEST_F(ChunkDemuxerTest, SeekWhileParsingCluster) { … }
TEST_F(ChunkDemuxerTest, AppendToParseBufferBeforeInit) { … }
TEST_F(ChunkDemuxerTest, ReadMultiBuffer_kOK) { … }
TEST_F(ChunkDemuxerTest, ReadMultiBuffer_ActualReadCount_LE_RequestedCount) { … }
TEST_F(ChunkDemuxerTest, ReadMultiBuffer_LastBufferIsEOS) { … }
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_F(ChunkDemuxerTest, ClusterBeforeInitSegment) { … }
TEST_F(ChunkDemuxerTest, EOSDuringInit) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamWithNoAppend) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamWithNoMediaAppend) { … }
TEST_F(ChunkDemuxerTest, DecodeErrorEndOfStream) { … }
TEST_F(ChunkDemuxerTest, NetworkErrorEndOfStream) { … }
class EndOfStreamHelper { … };
TEST_F(ChunkDemuxerTest, EndOfStreamWithPendingReads) { … }
TEST_F(ChunkDemuxerTest, ReadsAfterEndOfStream) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamDuringCanceledSeek) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamRangeChanges) { … }
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) { … }
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_F(ChunkDemuxerTest, RemoveId) { … }
TEST_F(ChunkDemuxerTest, RemoveAndAddId) { … }
TEST_F(ChunkDemuxerTest, SeekCanceled) { … }
TEST_F(ChunkDemuxerTest, SeekCanceledWhileWaitingForSeek) { … }
TEST_F(ChunkDemuxerTest, SeekAudioAndVideoSources) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamAfterPastEosSeek) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamDuringPendingSeek) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRanges_SeparateStreams) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { … }
TEST_F(ChunkDemuxerTest, DifferentStreamTimecodes) { … }
TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesSeparateSources) { … }
TEST_F(ChunkDemuxerTest, DifferentStreamTimecodesOutOfRange) { … }
TEST_F(ChunkDemuxerTest, CodecPrefixMatching) { … }
TEST_F(ChunkDemuxerTest, CodecIDsThatAreNotRFC6381Compliant) { … }
TEST_F(ChunkDemuxerTest, EndOfStreamStillSetAfterSeek) { … }
TEST_F(ChunkDemuxerTest, GetBufferedRangesBeforeInitSegment) { … }
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));
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("bear-1280x720.ts");
EXPECT_CALL(*this, InitSegmentReceivedMock(_));
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));
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;
}
ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));
Ranges<base::TimeDelta> range_before_abort =
demuxer_->GetBufferedRanges(kSourceId);
demuxer_->ResetParserState(kSourceId,
append_window_start_for_next_append_,
append_window_end_for_next_append_,
×tamp_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));
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("bear-1280x720.ts");
EXPECT_CALL(*this, InitSegmentReceivedMock(_));
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));
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;
}
ASSERT_TRUE(demuxer_->IsParsingMediaSegment(kSourceId));
Seek(base::Milliseconds(4110));
demuxer_->ResetParserState(kSourceId,
append_window_start_for_next_append_,
append_window_end_for_next_append_,
×tamp_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_F(ChunkDemuxerTest, Shutdown_BeforeInitialize) { … }
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) { … }
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) { … }
}
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_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
}