// Copyright 2017 Google LLC. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd #include #include #include "packager/packager.h" using testing::_; using testing::HasSubstr; using testing::Invoke; using testing::MockFunction; using testing::Return; using testing::ReturnArg; using testing::StrEq; using testing::UnitTest; using testing::WithArgs; namespace shaka { namespace { const char kTestFile[] = "packager/media/test/data/bear-640x360.mp4"; const char kOutputVideo[] = "output_video.mp4"; const char kOutputVideoTemplate[] = "output_video_$Number$.m4s"; const char kOutputAudio[] = "output_audio.mp4"; const char kOutputAudioTemplate[] = "output_audio_$Number$.m4s"; const char kOutputMpd[] = "output.mpd"; const double kSegmentDurationInSeconds = 1.0; const uint8_t kKeyId[] = { 0xe5, 0x00, 0x7e, 0x6e, 0x9d, 0xcd, 0x5a, 0xc0, 0x95, 0x20, 0x2e, 0xd3, 0x75, 0x83, 0x82, 0xcd, }; const uint8_t kKey[]{ 0x6f, 0xc9, 0x6f, 0xe6, 0x28, 0xa2, 0x65, 0xb1, 0x3a, 0xed, 0xde, 0xc0, 0xbc, 0x42, 0x1f, 0x4d, }; const double kClearLeadInSeconds = 1.0; const double kFragmentDurationInSeconds = 5.0; } // namespace class PackagerTest : public ::testing::Test { public: void SetUp() override { FILE* f = fopen(kTestFile, "rb"); if (!f) { FAIL() << "The test is expected to run from packager repository root."; return; } fclose(f); // Use memory file for testing and generate different test directories for // different tests. test_directory_ = std::string("memory://test/") + UnitTest::GetInstance()->current_test_info()->name() + "/"; } std::string GetFullPath(const std::string& file_name) { return test_directory_ + file_name; } PackagingParams SetupPackagingParams() { PackagingParams packaging_params; packaging_params.temp_dir = test_directory_; packaging_params.chunking_params.segment_duration_in_seconds = kSegmentDurationInSeconds; packaging_params.mpd_params.mpd_output = GetFullPath(kOutputMpd); packaging_params.encryption_params.clear_lead_in_seconds = kClearLeadInSeconds; packaging_params.encryption_params.key_provider = KeyProvider::kRawKey; packaging_params.encryption_params.raw_key.key_map[""].key_id.assign( std::begin(kKeyId), std::end(kKeyId)); packaging_params.encryption_params.raw_key.key_map[""].key.assign( std::begin(kKey), std::end(kKey)); return packaging_params; } std::vector SetupStreamDescriptors() { std::vector stream_descriptors; StreamDescriptor stream_descriptor; stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "video"; stream_descriptor.output = GetFullPath(kOutputVideo); stream_descriptors.push_back(stream_descriptor); stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "audio"; stream_descriptor.output = GetFullPath(kOutputAudio); stream_descriptors.push_back(stream_descriptor); return stream_descriptors; } protected: std::string test_directory_; }; TEST_F(PackagerTest, Version) { EXPECT_FALSE(Packager::GetLibraryVersion().empty()); } TEST_F(PackagerTest, Success) { Packager packager; ASSERT_EQ(Status::OK, packager.Initialize(SetupPackagingParams(), SetupStreamDescriptors())); ASSERT_EQ(Status::OK, packager.Run()); } TEST_F(PackagerTest, MissingStreamDescriptors) { std::vector stream_descriptors; Packager packager; auto status = packager.Initialize(SetupPackagingParams(), stream_descriptors); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); } TEST_F(PackagerTest, MixingSegmentTemplateAndSingleSegment) { std::vector stream_descriptors; StreamDescriptor stream_descriptor; stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "video"; stream_descriptor.output = GetFullPath(kOutputVideo); stream_descriptor.segment_template = GetFullPath(kOutputVideoTemplate); stream_descriptors.push_back(stream_descriptor); stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "audio"; stream_descriptor.output = GetFullPath(kOutputAudio); stream_descriptor.segment_template.clear(); stream_descriptors.push_back(stream_descriptor); Packager packager; auto status = packager.Initialize(SetupPackagingParams(), stream_descriptors); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); } TEST_F(PackagerTest, DuplicatedOutputs) { std::vector stream_descriptors; StreamDescriptor stream_descriptor; stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "video"; stream_descriptor.output = GetFullPath(kOutputVideo); stream_descriptor.segment_template = GetFullPath(kOutputVideoTemplate); stream_descriptors.push_back(stream_descriptor); stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "audio"; stream_descriptor.output = GetFullPath(kOutputVideo); stream_descriptor.segment_template = GetFullPath(kOutputAudioTemplate); stream_descriptors.push_back(stream_descriptor); Packager packager; auto status = packager.Initialize(SetupPackagingParams(), stream_descriptors); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); EXPECT_THAT(status.error_message(), HasSubstr("duplicated outputs")); } TEST_F(PackagerTest, DuplicatedSegmentTemplates) { std::vector stream_descriptors; StreamDescriptor stream_descriptor; stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "video"; stream_descriptor.output = GetFullPath(kOutputVideo); stream_descriptor.segment_template = GetFullPath(kOutputVideoTemplate); stream_descriptors.push_back(stream_descriptor); stream_descriptor.input = kTestFile; stream_descriptor.stream_selector = "audio"; stream_descriptor.output = GetFullPath(kOutputAudio); stream_descriptor.segment_template = GetFullPath(kOutputVideoTemplate); stream_descriptors.push_back(stream_descriptor); Packager packager; auto status = packager.Initialize(SetupPackagingParams(), stream_descriptors); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); EXPECT_THAT(status.error_message(), HasSubstr("duplicated segment templates")); } TEST_F(PackagerTest, SegmentAlignedAndSubsegmentNotAligned) { auto packaging_params = SetupPackagingParams(); packaging_params.chunking_params.segment_sap_aligned = true; packaging_params.chunking_params.subsegment_sap_aligned = false; Packager packager; ASSERT_EQ(Status::OK, packager.Initialize(packaging_params, SetupStreamDescriptors())); ASSERT_EQ(Status::OK, packager.Run()); } TEST_F(PackagerTest, SegmentNotAlignedButSubsegmentAligned) { auto packaging_params = SetupPackagingParams(); packaging_params.chunking_params.segment_sap_aligned = false; packaging_params.chunking_params.subsegment_sap_aligned = true; Packager packager; auto status = packager.Initialize(packaging_params, SetupStreamDescriptors()); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); } TEST_F(PackagerTest, WriteOutputToBuffer) { auto packaging_params = SetupPackagingParams(); MockFunction mock_write_func; packaging_params.buffer_callback_params.write_func = mock_write_func.AsStdFunction(); EXPECT_CALL(mock_write_func, Call(StrEq(GetFullPath(kOutputVideo)), _, _)) .WillRepeatedly(ReturnArg<2>()); EXPECT_CALL(mock_write_func, Call(StrEq(GetFullPath(kOutputAudio)), _, _)) .WillRepeatedly(ReturnArg<2>()); EXPECT_CALL(mock_write_func, Call(StrEq(GetFullPath(kOutputMpd)), _, _)) .WillRepeatedly(ReturnArg<2>()); Packager packager; ASSERT_EQ(Status::OK, packager.Initialize(packaging_params, SetupStreamDescriptors())); ASSERT_EQ(Status::OK, packager.Run()); } TEST_F(PackagerTest, ReadFromBuffer) { auto packaging_params = SetupPackagingParams(); MockFunction mock_read_func; packaging_params.buffer_callback_params.read_func = mock_read_func.AsStdFunction(); const std::string file_name = kTestFile; FILE* file_ptr = fopen(file_name.c_str(), "rb"); ASSERT_TRUE(file_ptr); EXPECT_CALL(mock_read_func, Call(StrEq(file_name), _, _)) .WillRepeatedly( WithArgs<1, 2>(Invoke([file_ptr](void* buffer, uint64_t size) { return fread(buffer, sizeof(char), size, file_ptr); }))); Packager packager; ASSERT_EQ(Status::OK, packager.Initialize(packaging_params, SetupStreamDescriptors())); ASSERT_EQ(Status::OK, packager.Run()); fclose(file_ptr); } TEST_F(PackagerTest, ReadFromBufferFailed) { auto packaging_params = SetupPackagingParams(); MockFunction mock_read_func; packaging_params.buffer_callback_params.read_func = mock_read_func.AsStdFunction(); EXPECT_CALL(mock_read_func, Call(_, _, _)).WillOnce(Return(-1)); Packager packager; ASSERT_EQ(Status::OK, packager.Initialize(packaging_params, SetupStreamDescriptors())); ASSERT_EQ(error::FILE_FAILURE, packager.Run().error_code()); } TEST_F(PackagerTest, LowLatencyDashEnabledAndFragmentDurationSet) { auto packaging_params = SetupPackagingParams(); packaging_params.chunking_params.low_latency_dash_mode = true; packaging_params.chunking_params.subsegment_duration_in_seconds = kFragmentDurationInSeconds; Packager packager; auto status = packager.Initialize(packaging_params, SetupStreamDescriptors()); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); EXPECT_THAT(status.error_message(), HasSubstr("--fragment_duration cannot be set")); } TEST_F(PackagerTest, LowLatencyDashEnabledAndUtcTimingNotSet) { auto packaging_params = SetupPackagingParams(); packaging_params.mpd_params.low_latency_dash_mode = true; Packager packager; auto status = packager.Initialize(packaging_params, SetupStreamDescriptors()); ASSERT_EQ(error::INVALID_ARGUMENT, status.error_code()); EXPECT_THAT(status.error_message(), HasSubstr("--utc_timings must be be set")); } // TODO(kqyang): Add more tests. } // namespace shaka