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_, &timestamp_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, &timestamp_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