1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/test/test_media_source.h"
6
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/threading/thread_task_runner_handle.h"
10 #include "media/base/test_data_util.h"
11 #include "media/base/timestamp_constants.h"
12
13 namespace {
14
15 // Copies parsed type and codecs from |mimetype| into |type| and |codecs|.
16 // This code assumes that |mimetype| is one of the following forms:
17 // 1. mimetype without codecs (e.g. audio/mpeg)
18 // 2. mimetype with codecs (e.g. video/webm; codecs="vorbis,vp8")
SplitMime(const std::string & mimetype,std::string * type,std::string * codecs)19 void SplitMime(const std::string& mimetype,
20 std::string* type,
21 std::string* codecs) {
22 DCHECK(type);
23 DCHECK(codecs);
24 size_t semicolon = mimetype.find(";");
25 if (semicolon == std::string::npos) {
26 *type = mimetype;
27 *codecs = "";
28 return;
29 }
30
31 *type = mimetype.substr(0, semicolon);
32 size_t codecs_param_start = mimetype.find("codecs=\"", semicolon);
33 CHECK_NE(codecs_param_start, std::string::npos);
34 codecs_param_start += 8; // Skip over the codecs=".
35 size_t codecs_param_end = mimetype.find("\"", codecs_param_start);
36 CHECK_NE(codecs_param_end, std::string::npos);
37 *codecs = mimetype.substr(codecs_param_start,
38 codecs_param_end - codecs_param_start);
39 }
40
41 } // namespace
42
43 namespace media {
44
45 constexpr char kSourceId[] = "SourceId";
46
TestMediaSource(const std::string & filename,const std::string & mimetype,size_t initial_append_size,bool initial_sequence_mode)47 TestMediaSource::TestMediaSource(const std::string& filename,
48 const std::string& mimetype,
49 size_t initial_append_size,
50 bool initial_sequence_mode)
51 : current_position_(0),
52 initial_append_size_(initial_append_size),
53 initial_sequence_mode_(initial_sequence_mode),
54 mimetype_(mimetype),
55 chunk_demuxer_(new ChunkDemuxer(
56 base::BindOnce(&TestMediaSource::DemuxerOpened,
57 base::Unretained(this)),
58 base::DoNothing(),
59 base::BindRepeating(&TestMediaSource::OnEncryptedMediaInitData,
60 base::Unretained(this)),
61 &media_log_)),
62 owned_chunk_demuxer_(chunk_demuxer_) {
63 file_data_ = ReadTestDataFile(filename);
64
65 if (initial_append_size_ == kAppendWholeFile)
66 initial_append_size_ = file_data_->data_size();
67
68 CHECK_GT(initial_append_size_, 0u);
69 CHECK_LE(initial_append_size_, file_data_->data_size());
70 }
71
TestMediaSource(const std::string & filename,size_t initial_append_size,bool initial_sequence_mode)72 TestMediaSource::TestMediaSource(const std::string& filename,
73 size_t initial_append_size,
74 bool initial_sequence_mode)
75 : TestMediaSource(filename,
76 GetMimeTypeForFile(filename),
77 initial_append_size,
78 initial_sequence_mode) {}
79
TestMediaSource(scoped_refptr<DecoderBuffer> data,const std::string & mimetype,size_t initial_append_size,bool initial_sequence_mode)80 TestMediaSource::TestMediaSource(scoped_refptr<DecoderBuffer> data,
81 const std::string& mimetype,
82 size_t initial_append_size,
83 bool initial_sequence_mode)
84 : file_data_(data),
85 current_position_(0),
86 initial_append_size_(initial_append_size),
87 initial_sequence_mode_(initial_sequence_mode),
88 mimetype_(mimetype),
89 chunk_demuxer_(new ChunkDemuxer(
90 base::BindOnce(&TestMediaSource::DemuxerOpened,
91 base::Unretained(this)),
92 base::DoNothing(),
93 base::BindRepeating(&TestMediaSource::OnEncryptedMediaInitData,
94 base::Unretained(this)),
95 &media_log_)),
96 owned_chunk_demuxer_(chunk_demuxer_) {
97 if (initial_append_size_ == kAppendWholeFile)
98 initial_append_size_ = file_data_->data_size();
99
100 CHECK_GT(initial_append_size_, 0u);
101 CHECK_LE(initial_append_size_, file_data_->data_size());
102 }
103
104 TestMediaSource::~TestMediaSource() = default;
105
GetDemuxer()106 std::unique_ptr<Demuxer> TestMediaSource::GetDemuxer() {
107 return std::move(owned_chunk_demuxer_);
108 }
109
SetAppendWindow(base::TimeDelta timestamp_offset,base::TimeDelta append_window_start,base::TimeDelta append_window_end)110 void TestMediaSource::SetAppendWindow(base::TimeDelta timestamp_offset,
111 base::TimeDelta append_window_start,
112 base::TimeDelta append_window_end) {
113 last_timestamp_offset_ = timestamp_offset;
114 append_window_start_ = append_window_start;
115 append_window_end_ = append_window_end;
116 }
117
Seek(base::TimeDelta seek_time,size_t new_position,size_t seek_append_size)118 void TestMediaSource::Seek(base::TimeDelta seek_time,
119 size_t new_position,
120 size_t seek_append_size) {
121 chunk_demuxer_->StartWaitingForSeek(seek_time);
122
123 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(),
124 kInfiniteDuration, &last_timestamp_offset_);
125
126 CHECK_LT(new_position, file_data_->data_size());
127 current_position_ = new_position;
128
129 AppendData(seek_append_size);
130 }
131
Seek(base::TimeDelta seek_time)132 void TestMediaSource::Seek(base::TimeDelta seek_time) {
133 chunk_demuxer_->StartWaitingForSeek(seek_time);
134 }
135
SetSequenceMode(bool sequence_mode)136 void TestMediaSource::SetSequenceMode(bool sequence_mode) {
137 CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
138 chunk_demuxer_->SetSequenceMode(kSourceId, sequence_mode);
139 }
140
AppendData(size_t size)141 void TestMediaSource::AppendData(size_t size) {
142 CHECK(chunk_demuxer_);
143 CHECK_LT(current_position_, file_data_->data_size());
144 CHECK_LE(current_position_ + size, file_data_->data_size());
145
146 bool success = chunk_demuxer_->AppendData(
147 kSourceId, file_data_->data() + current_position_, size,
148 append_window_start_, append_window_end_, &last_timestamp_offset_);
149 current_position_ += size;
150
151 VerifyExpectedAppendResult(success);
152
153 if (do_eos_after_next_append_) {
154 do_eos_after_next_append_ = false;
155 if (success)
156 EndOfStream();
157 }
158 }
159
AppendAtTime(base::TimeDelta timestamp_offset,const uint8_t * pData,int size)160 bool TestMediaSource::AppendAtTime(base::TimeDelta timestamp_offset,
161 const uint8_t* pData,
162 int size) {
163 CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
164 bool success =
165 chunk_demuxer_->AppendData(kSourceId, pData, size, append_window_start_,
166 append_window_end_, ×tamp_offset);
167 last_timestamp_offset_ = timestamp_offset;
168 return success;
169 }
170
AppendAtTimeWithWindow(base::TimeDelta timestamp_offset,base::TimeDelta append_window_start,base::TimeDelta append_window_end,const uint8_t * pData,int size)171 void TestMediaSource::AppendAtTimeWithWindow(
172 base::TimeDelta timestamp_offset,
173 base::TimeDelta append_window_start,
174 base::TimeDelta append_window_end,
175 const uint8_t* pData,
176 int size) {
177 CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
178 VerifyExpectedAppendResult(
179 chunk_demuxer_->AppendData(kSourceId, pData, size, append_window_start,
180 append_window_end, ×tamp_offset));
181 last_timestamp_offset_ = timestamp_offset;
182 }
183
SetMemoryLimits(size_t limit_bytes)184 void TestMediaSource::SetMemoryLimits(size_t limit_bytes) {
185 chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::AUDIO, limit_bytes);
186 chunk_demuxer_->SetMemoryLimitsForTest(DemuxerStream::VIDEO, limit_bytes);
187 }
188
EvictCodedFrames(base::TimeDelta currentMediaTime,size_t newDataSize)189 bool TestMediaSource::EvictCodedFrames(base::TimeDelta currentMediaTime,
190 size_t newDataSize) {
191 return chunk_demuxer_->EvictCodedFrames(kSourceId, currentMediaTime,
192 newDataSize);
193 }
194
RemoveRange(base::TimeDelta start,base::TimeDelta end)195 void TestMediaSource::RemoveRange(base::TimeDelta start, base::TimeDelta end) {
196 chunk_demuxer_->Remove(kSourceId, start, end);
197 }
198
EndOfStream()199 void TestMediaSource::EndOfStream() {
200 chunk_demuxer_->MarkEndOfStream(PIPELINE_OK);
201 }
202
UnmarkEndOfStream()203 void TestMediaSource::UnmarkEndOfStream() {
204 chunk_demuxer_->UnmarkEndOfStream();
205 }
206
Shutdown()207 void TestMediaSource::Shutdown() {
208 if (!chunk_demuxer_)
209 return;
210 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(),
211 kInfiniteDuration, &last_timestamp_offset_);
212 chunk_demuxer_->Shutdown();
213 chunk_demuxer_ = nullptr;
214 }
215
DemuxerOpened()216 void TestMediaSource::DemuxerOpened() {
217 base::ThreadTaskRunnerHandle::Get()->PostTask(
218 FROM_HERE, base::BindOnce(&TestMediaSource::DemuxerOpenedTask,
219 base::Unretained(this)));
220 }
221
DemuxerOpenedTask()222 void TestMediaSource::DemuxerOpenedTask() {
223 ChunkDemuxer::Status status = AddId();
224 if (status != ChunkDemuxer::kOk) {
225 CHECK(demuxer_failure_cb_);
226 demuxer_failure_cb_.Run(DEMUXER_ERROR_COULD_NOT_OPEN);
227 return;
228 }
229 chunk_demuxer_->SetTracksWatcher(
230 kSourceId, base::BindRepeating(&TestMediaSource::InitSegmentReceived,
231 base::Unretained(this)));
232
233 chunk_demuxer_->SetParseWarningCallback(
234 kSourceId, base::BindRepeating(&TestMediaSource::OnParseWarningMock,
235 base::Unretained(this)));
236
237 SetSequenceMode(initial_sequence_mode_);
238 AppendData(initial_append_size_);
239 }
240
AddId()241 ChunkDemuxer::Status TestMediaSource::AddId() {
242 std::string type;
243 std::string codecs;
244 SplitMime(mimetype_, &type, &codecs);
245 return chunk_demuxer_->AddId(kSourceId, type, codecs);
246 }
247
ChangeType(const std::string & mimetype)248 void TestMediaSource::ChangeType(const std::string& mimetype) {
249 chunk_demuxer_->ResetParserState(kSourceId, base::TimeDelta(),
250 kInfiniteDuration, &last_timestamp_offset_);
251 std::string type;
252 std::string codecs;
253 SplitMime(mimetype, &type, &codecs);
254 mimetype_ = mimetype;
255 chunk_demuxer_->ChangeType(kSourceId, type, codecs);
256 }
257
OnEncryptedMediaInitData(EmeInitDataType init_data_type,const std::vector<uint8_t> & init_data)258 void TestMediaSource::OnEncryptedMediaInitData(
259 EmeInitDataType init_data_type,
260 const std::vector<uint8_t>& init_data) {
261 CHECK(!init_data.empty());
262 CHECK(encrypted_media_init_data_cb_);
263 encrypted_media_init_data_cb_.Run(init_data_type, init_data);
264 }
265
InitSegmentReceived(std::unique_ptr<MediaTracks> tracks)266 void TestMediaSource::InitSegmentReceived(std::unique_ptr<MediaTracks> tracks) {
267 CHECK(tracks.get());
268 EXPECT_GT(tracks->tracks().size(), 0u);
269 CHECK(chunk_demuxer_);
270 // Verify that track ids are unique.
271 std::set<MediaTrack::Id> track_ids;
272 for (const auto& track : tracks->tracks()) {
273 EXPECT_EQ(track_ids.end(), track_ids.find(track->id()));
274 track_ids.insert(track->id());
275 }
276 InitSegmentReceivedMock(tracks);
277 }
278
VerifyExpectedAppendResult(bool append_result)279 void TestMediaSource::VerifyExpectedAppendResult(bool append_result) {
280 if (expected_append_result_ == ExpectedAppendResult::kSuccessOrFailure)
281 return; // |append_result| is ignored in this case.
282
283 ASSERT_EQ(expected_append_result_ == ExpectedAppendResult::kSuccess,
284 append_result);
285 }
286
287 } // namespace media
288