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