Shaka Packager SDK
Loading...
Searching...
No Matches
media_handler_test_base.h
1// Copyright 2022 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#ifndef PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
8#define PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
9
10#include <cstddef>
11#include <cstdint>
12#include <memory>
13#include <string>
14#include <vector>
15
16#include <absl/strings/escaping.h>
17#include <gmock/gmock.h>
18#include <gtest/gtest.h>
19
20#include <packager/media/base/media_handler.h>
21#include <packager/media/base/media_sample.h>
22#include <packager/media/base/stream_info.h>
23#include <packager/media/base/text_sample.h>
24#include <packager/media/base/video_stream_info.h>
25#include <packager/status.h>
26
27namespace shaka {
28namespace media {
29
30std::string BoolToString(bool value);
31std::string ToPrettyString(const std::string& str);
32
33bool TryMatchStreamDataType(const StreamDataType& actual,
34 const StreamDataType& expected,
35 ::testing::MatchResultListener* listener);
36
37bool TryMatchStreamType(const StreamType& actual,
38 const StreamType& expected,
39 ::testing::MatchResultListener* listener);
40
41template <typename T, typename M>
42bool TryMatch(const T& value,
43 const M& matcher,
44 ::testing::MatchResultListener* listener,
45 const char* value_name) {
46 if (!ExplainMatchResult(matcher, value, listener)) {
47 // Need a space at the start of the string in the case that
48 // it gets combined with another string.
49 *listener << " Mismatch on " << value_name;
50 return false;
51 }
52
53 return true;
54}
55
56MATCHER_P(IsPsshInfoWithSystemId,
57 system_id,
58 std::string(negation ? "doesn't " : "") + " have system ID " +
59 testing::PrintToString(system_id)) {
60 *result_listener << "which is (" << testing::PrintToString(arg.system_id)
61 << ")";
62 return arg.system_id == system_id;
63}
64
65MATCHER_P4(IsStreamInfo, stream_index, time_scale, encrypted, language, "") {
66 if (!TryMatchStreamDataType(arg->stream_data_type,
67 StreamDataType::kStreamInfo, result_listener)) {
68 return false;
69 }
70
71 const std::string is_encrypted_string =
72 BoolToString(arg->stream_info->is_encrypted());
73
74 *result_listener << "which is (" << arg->stream_index << ", "
75 << arg->stream_info->time_scale() << ", "
76 << is_encrypted_string << ", "
77 << arg->stream_info->language() << ")";
78
79 return TryMatch(arg->stream_index, stream_index, result_listener,
80 "stream_index") &&
81 TryMatch(arg->stream_info->time_scale(), time_scale, result_listener,
82 "time_scale") &&
83 TryMatch(arg->stream_info->is_encrypted(), encrypted, result_listener,
84 "is_encrypted") &&
85 TryMatch(arg->stream_info->language(), language, result_listener,
86 "language");
87}
88
89MATCHER_P3(IsVideoStream, stream_index, trick_play_factor, playback_rate, "") {
90 if (!TryMatchStreamDataType(arg->stream_data_type,
91 StreamDataType::kStreamInfo, result_listener)) {
92 return false;
93 }
94
95 if (!TryMatchStreamType(arg->stream_info->stream_type(), kStreamVideo,
96 result_listener)) {
97 return false;
98 }
99
100 const VideoStreamInfo* info =
101 static_cast<const VideoStreamInfo*>(arg->stream_info.get());
102
103 *result_listener << "which is (" << arg->stream_index << ", "
104 << info->trick_play_factor() << ", " << info->playback_rate()
105 << ")";
106
107 return TryMatch(arg->stream_index, stream_index, result_listener,
108 "stream_index") &&
109 TryMatch(info->trick_play_factor(), trick_play_factor, result_listener,
110 "trick_play_factor") &&
111 TryMatch(info->playback_rate(), playback_rate, result_listener,
112 "playback_rate");
113}
114
115MATCHER_P5(IsSegmentInfo,
116 stream_index,
117 start_timestamp,
118 duration,
119 subsegment,
120 encrypted,
121 "") {
122 if (!TryMatchStreamDataType(arg->stream_data_type,
123 StreamDataType::kSegmentInfo, result_listener)) {
124 return false;
125 }
126
127 const std::string is_subsegment_string =
128 BoolToString(arg->segment_info->is_subsegment);
129 const std::string is_encrypted_string =
130 BoolToString(arg->segment_info->is_encrypted);
131
132 *result_listener << "which is (" << arg->stream_index << ", "
133 << arg->segment_info->start_timestamp << ", "
134 << arg->segment_info->duration << ", "
135 << is_subsegment_string << ", " << is_encrypted_string
136 << ")";
137
138 return TryMatch(arg->stream_index, stream_index, result_listener,
139 "stream_index") &&
140 TryMatch(arg->segment_info->start_timestamp, start_timestamp,
141 result_listener, "start_timestamp") &&
142 TryMatch(arg->segment_info->duration, duration, result_listener,
143 "duration") &&
144 TryMatch(arg->segment_info->is_subsegment, subsegment, result_listener,
145 "is_subsegment") &&
146 TryMatch(arg->segment_info->is_encrypted, encrypted, result_listener,
147 "is_encrypted");
148}
149
150MATCHER_P6(MatchEncryptionConfig,
151 protection_scheme,
152 crypt_byte_block,
153 skip_byte_block,
154 per_sample_iv_size,
155 constant_iv,
156 key_id,
157 "") {
158 const std::string constant_iv_hex = absl::BytesToHexString(
159 std::string(std::begin(arg.constant_iv), std::end(arg.constant_iv)));
160 const std::string key_id_hex = absl::BytesToHexString(
161 std::string(std::begin(arg.key_id), std::end(arg.key_id)));
162 const std::string protection_scheme_as_string =
163 FourCCToString(arg.protection_scheme);
164 // Convert to integers so that they will print as a number and not a uint8_t
165 // (char).
166 const int crypt_byte_as_int = static_cast<int>(arg.crypt_byte_block);
167 const int skip_byte_as_int = static_cast<int>(arg.skip_byte_block);
168
169 *result_listener << "which is (" << protection_scheme_as_string << ", "
170 << crypt_byte_as_int << ", " << skip_byte_as_int << ", "
171 << arg.per_sample_iv_size << ", " << constant_iv_hex << ", "
172 << key_id_hex << ")";
173
174 return TryMatch(arg.protection_scheme, protection_scheme, result_listener,
175 "protection_scheme") &&
176 TryMatch(arg.crypt_byte_block, crypt_byte_block, result_listener,
177 "crypt_byte_block") &&
178 TryMatch(arg.skip_byte_block, skip_byte_block, result_listener,
179 "skip_byte_block") &&
180 TryMatch(arg.per_sample_iv_size, per_sample_iv_size, result_listener,
181 "per_sample_iv_size") &&
182 TryMatch(arg.constant_iv, constant_iv, result_listener,
183 "constant_iv") &&
184 TryMatch(arg.key_id, key_id, result_listener, "key_id");
185}
186
187MATCHER_P5(IsMediaSample,
188 stream_index,
189 timestamp,
190 duration,
191 encrypted,
192 keyframe,
193 "") {
194 if (!TryMatchStreamDataType(arg->stream_data_type,
195 StreamDataType::kMediaSample, result_listener)) {
196 return false;
197 }
198
199 const std::string is_encrypted_string =
200 BoolToString(arg->media_sample->is_encrypted());
201 const std::string is_key_frame_string =
202 BoolToString(arg->media_sample->is_key_frame());
203
204 *result_listener << "which is (" << arg->stream_index << ", "
205 << arg->media_sample->dts() << ", "
206 << arg->media_sample->duration() << ", "
207 << is_encrypted_string << ", " << is_key_frame_string << ")";
208
209 return TryMatch(arg->stream_index, stream_index, result_listener,
210 "stream_index") &&
211 TryMatch(arg->media_sample->dts(), timestamp, result_listener,
212 "dts") &&
213 TryMatch(arg->media_sample->duration(), duration, result_listener,
214 "duration") &&
215 TryMatch(arg->media_sample->is_encrypted(), encrypted, result_listener,
216 "is_encrypted") &&
217 TryMatch(arg->media_sample->is_key_frame(), keyframe, result_listener,
218 "is_key_frame");
219}
220
221MATCHER_P4(IsTextSample, stream_index, id, start_time, end_time, "") {
222 if (!TryMatchStreamDataType(arg->stream_data_type,
223 StreamDataType::kTextSample, result_listener)) {
224 return false;
225 }
226
227 *result_listener << "which is (" << arg->stream_index << ", "
228 << ToPrettyString(arg->text_sample->id()) << ", "
229 << arg->text_sample->start_time() << ", "
230 << arg->text_sample->EndTime() << ")";
231
232 return TryMatch(arg->stream_index, stream_index, result_listener,
233 "stream_index") &&
234 TryMatch(arg->text_sample->id(), id, result_listener, "id") &&
235 TryMatch(arg->text_sample->start_time(), start_time, result_listener,
236 "start_time") &&
237 TryMatch(arg->text_sample->EndTime(), end_time, result_listener,
238 "EndTime");
239}
240
241MATCHER_P2(IsCueEvent, stream_index, time_in_seconds, "") {
242 if (!TryMatchStreamDataType(arg->stream_data_type, StreamDataType::kCueEvent,
243 result_listener)) {
244 return false;
245 }
246
247 *result_listener << "which is (" << arg->stream_index << ", "
248 << arg->cue_event->time_in_seconds << ")";
249
250 return TryMatch(arg->stream_index, stream_index, result_listener,
251 "stream_index") &&
252 TryMatch(arg->cue_event->time_in_seconds, time_in_seconds,
253 result_listener, "time_in_seconds");
254}
255
257 public:
261
262 private:
263 bool ValidateOutputStreamIndex(size_t index) const override;
264 Status InitializeInternal() override;
265 Status Process(std::unique_ptr<StreamData> stream_data) override;
266};
267
269 public:
270 MOCK_METHOD1(OnProcess, void(const StreamData*));
271 MOCK_METHOD1(OnFlush, void(size_t index));
272
273 private:
274 Status InitializeInternal() override;
275 Status Process(std::unique_ptr<StreamData> stream_data) override;
276 Status OnFlushRequest(size_t index) override;
277};
278
280 public:
281 const std::vector<std::unique_ptr<StreamData>>& Cache() const {
282 return stream_data_vector_;
283 }
284
285 // TODO(vaage) : Remove the use of clear in our tests as it can make flow
286 // of the test harder to understand.
287 void Clear() { stream_data_vector_.clear(); }
288
289 private:
290 Status InitializeInternal() override;
291 Status Process(std::unique_ptr<StreamData> stream_data) override;
292 Status OnFlushRequest(size_t input_stream_index) override;
293 bool ValidateOutputStreamIndex(size_t stream_index) const override;
294
295 std::vector<std::unique_ptr<StreamData>> stream_data_vector_;
296};
297
298class MediaHandlerTestBase : public ::testing::Test {
299 public:
300 MediaHandlerTestBase() = default;
301
302 protected:
303 bool IsVideoCodec(Codec codec) const;
304
305 std::unique_ptr<StreamInfo> GetVideoStreamInfo(int32_t time_scale) const;
306
307 std::unique_ptr<StreamInfo> GetVideoStreamInfo(int32_t time_scale,
308 uint32_t width,
309 uint32_t height) const;
310
311 std::unique_ptr<StreamInfo> GetVideoStreamInfo(int32_t time_scale,
312 Codec codec) const;
313
314 std::unique_ptr<StreamInfo> GetVideoStreamInfo(int32_t time_scale,
315 Codec codec,
316 uint32_t width,
317 uint32_t height) const;
318
319 std::unique_ptr<StreamInfo> GetAudioStreamInfo(int32_t time_scale) const;
320
321 std::unique_ptr<StreamInfo> GetAudioStreamInfo(int32_t time_scale,
322 Codec codec) const;
323
324 std::shared_ptr<MediaSample> GetMediaSample(int64_t timestamp,
325 int64_t duration,
326 bool is_keyframe) const;
327
328 std::shared_ptr<MediaSample> GetMediaSample(int64_t timestamp,
329 int64_t duration,
330 bool is_keyframe,
331 const uint8_t* data,
332 size_t data_length) const;
333
334 std::unique_ptr<SegmentInfo> GetSegmentInfo(int64_t start_timestamp,
335 int64_t duration,
336 bool is_subsegment,
337 int64_t segment_number) const;
338
339 std::unique_ptr<StreamInfo> GetTextStreamInfo(int32_t timescale) const;
340
341 std::unique_ptr<TextSample> GetTextSample(const std::string& id,
342 int64_t start,
343 int64_t end,
344 const std::string& payload) const;
345
346 std::unique_ptr<CueEvent> GetCueEvent(double time_in_seconds) const;
347
348 // Connect and initialize all handlers.
349 Status SetUpAndInitializeGraph(std::shared_ptr<MediaHandler> handler,
350 size_t input_count,
351 size_t output_count);
352
353 // Get the input handler at |index|. The values of |index| will match the
354 // call to |AddInput|.
355 FakeInputMediaHandler* Input(size_t index);
356
357 // Get the output handler at |index|. The values of |index| will match the
358 // call to |AddOutput|.
359 MockOutputMediaHandler* Output(size_t index);
360
361 private:
363 MediaHandlerTestBase& operator=(const MediaHandlerTestBase&) = delete;
364
365 std::shared_ptr<MediaHandler> handler_;
366
367 std::vector<std::shared_ptr<FakeInputMediaHandler>> inputs_;
368 std::vector<std::shared_ptr<MockOutputMediaHandler>> outputs_;
369};
370
372 public:
374
375 protected:
377 void SetUpGraph(size_t num_inputs,
378 size_t num_outputs,
379 std::shared_ptr<MediaHandler> handler);
380
382 const std::vector<std::unique_ptr<StreamData>>& GetOutputStreamDataVector()
383 const;
384
387
389 std::shared_ptr<MediaHandler> some_handler() { return some_handler_; }
390
392 std::shared_ptr<CachingMediaHandler> next_handler() { return next_handler_; }
393
394 private:
397 delete;
398
399 // Downstream handler used in testing graph.
400 std::shared_ptr<CachingMediaHandler> next_handler_;
401 // Some random handler which can be used for testing.
402 std::shared_ptr<MediaHandler> some_handler_;
403};
404
405} // namespace media
406} // namespace shaka
407
408#endif // PACKAGER_MEDIA_BASE_MEDIA_HANDLER_TEST_BASE_H_
void ClearOutputStreamDataVector()
Clear the output stream data vector.
const std::vector< std::unique_ptr< StreamData > > & GetOutputStreamDataVector() const
std::shared_ptr< CachingMediaHandler > next_handler()
std::shared_ptr< MediaHandler > some_handler()
void SetUpGraph(size_t num_inputs, size_t num_outputs, std::shared_ptr< MediaHandler > handler)
Setup a graph using |handler| with |num_inputs| and |num_outputs|.
Status FlushAllDownstreams()
Flush all connected downstream handlers.
Status FlushDownstream(size_t output_stream_index)
Flush the downstream connected at the specified output stream index.
Status Dispatch(std::unique_ptr< StreamData > stream_data) const
All the methods that are virtual are virtual for mocking.