// 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 "packager/media/base/media_handler_test_base.h" #include "packager/media/base/audio_stream_info.h" #include "packager/media/base/text_stream_info.h" #include "packager/media/base/video_stream_info.h" #include "packager/status_test_util.h" namespace { const int kTrackId = 1; const int64_t kDuration = 10000; const char kCodecString[] = "codec string"; const uint8_t kSampleBits = 1; const uint8_t kNumChannels = 2; const uint32_t kSamplingFrequency = 48000; const uint64_t kSeekPrerollNs = 12345; const uint64_t kCodecDelayNs = 56789; const uint32_t kMaxBitrate = 13579; const uint32_t kAvgBitrate = 13000; const char kLanguage[] = "eng"; const uint16_t kWidth = 10u; const uint16_t kHeight = 20u; const uint32_t kPixelWidth = 2u; const uint32_t kPixelHeight = 3u; const uint8_t kTransferCharacteristics = 0; const int16_t kTrickPlayFactor = 0; const uint8_t kNaluLengthSize = 1u; const bool kEncrypted = true; // Use H264 code config. const uint8_t kCodecConfig[]{ // clang-format off // Header 0x01, 0x64, 0x00, 0x1e, 0xff, // SPS count (ignore top three bits) 0xe1, // SPS 0x00, 0x19, // Size 0x67, 0x64, 0x00, 0x1e, 0xac, 0xd9, 0x40, 0xa0, 0x2f, 0xf9, 0x70, 0x11, 0x00, 0x00, 0x03, 0x03, 0xe9, 0x00, 0x00, 0xea, 0x60, 0x0f, 0x16, 0x2d, 0x96, // PPS count 0x01, // PPS 0x00, 0x06, // Size 0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0, // clang-format on }; // Mock data, we don't really care about what is inside. const uint8_t kData[]{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, }; } // namespace namespace shaka { namespace media { std::string BoolToString(bool value) { return value ? "true" : "false"; } bool TryMatchStreamDataType(const StreamDataType& actual, const StreamDataType& expected, ::testing::MatchResultListener* listener) { if (actual != expected) { std::string expected_as_string = StreamDataTypeToString(expected); std::string actual_as_string = StreamDataTypeToString(actual); *listener << "which is " << actual_as_string << " (expected " << expected_as_string << ")"; return false; } return true; } bool TryMatchStreamType(const StreamType& actual, const StreamType& expected, ::testing::MatchResultListener* listener) { if (actual != expected) { std::string expected_as_string = StreamTypeToString(expected); std::string actual_as_string = StreamTypeToString(actual); *listener << "which is " << actual_as_string << " (expected " << expected_as_string << ")"; return false; } return true; } std::string ToPrettyString(const std::string& str) { std::string out; // Opening quotation. out.push_back('"'); for (char c : str) { if (isspace(c)) { // Make all white space characters spaces to avoid print issues in // the terminal. out.push_back(' '); } else if (isalnum(c)) { // If the character is alpha-numeric, then print it as is. Just using // these characters, it should be enough to understand the string. out.push_back(c); } else { // Replace all other characters with '.'. This is to avoid print issues // (e.g. \n) or readability issues (e.g. "). out.push_back('.'); } } // Closing quotation. out.push_back('"'); return out; } bool FakeInputMediaHandler::ValidateOutputStreamIndex(size_t index) const { return true; } Status FakeInputMediaHandler::InitializeInternal() { return Status::OK; } Status FakeInputMediaHandler::Process(std::unique_ptr stream_data) { return Status(error::INTERNAL_ERROR, "FakeInputMediaHandler should never be a downstream handler."); } Status MockOutputMediaHandler::InitializeInternal() { return Status::OK; } Status MockOutputMediaHandler::Process( std::unique_ptr stream_data) { OnProcess(stream_data.get()); return Status::OK; } Status MockOutputMediaHandler::OnFlushRequest(size_t index) { OnFlush(index); return Status::OK; } Status CachingMediaHandler::InitializeInternal() { return Status::OK; } Status CachingMediaHandler::Process(std::unique_ptr stream_data) { stream_data_vector_.push_back(std::move(stream_data)); return Status::OK; } Status CachingMediaHandler::OnFlushRequest(size_t input_stream_index) { return Status::OK; } bool CachingMediaHandler::ValidateOutputStreamIndex(size_t stream_index) const { return true; } bool MediaHandlerTestBase::IsVideoCodec(Codec codec) const { return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne; } std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( int32_t time_scale) const { return GetVideoStreamInfo(time_scale, kCodecVP9, kWidth, kHeight); } std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( int32_t time_scale, uint32_t width, uint64_t height) const { return GetVideoStreamInfo(time_scale, kCodecVP9, width, height); } std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( int32_t time_scale, Codec codec) const { return GetVideoStreamInfo(time_scale, codec, kWidth, kHeight); } std::unique_ptr MediaHandlerTestBase::GetVideoStreamInfo( int32_t time_scale, Codec codec, uint32_t width, uint64_t height) const { return std::unique_ptr(new VideoStreamInfo( kTrackId, time_scale, kDuration, codec, H26xStreamFormat::kUnSpecified, kCodecString, kCodecConfig, sizeof(kCodecConfig), width, height, kPixelWidth, kPixelHeight, kTransferCharacteristics, kTrickPlayFactor, kNaluLengthSize, kLanguage, !kEncrypted)); } std::unique_ptr MediaHandlerTestBase::GetAudioStreamInfo( int32_t time_scale) const { return GetAudioStreamInfo(time_scale, kCodecAAC); } std::unique_ptr MediaHandlerTestBase::GetAudioStreamInfo( int32_t time_scale, Codec codec) const { return std::unique_ptr(new AudioStreamInfo( kTrackId, time_scale, kDuration, codec, kCodecString, kCodecConfig, sizeof(kCodecConfig), kSampleBits, kNumChannels, kSamplingFrequency, kSeekPrerollNs, kCodecDelayNs, kMaxBitrate, kAvgBitrate, kLanguage, !kEncrypted)); } std::shared_ptr MediaHandlerTestBase::GetMediaSample( int64_t timestamp, int64_t duration, bool is_keyframe) const { return GetMediaSample(timestamp, duration, is_keyframe, kData, sizeof(kData)); } std::shared_ptr MediaHandlerTestBase::GetMediaSample( int64_t timestamp, int64_t duration, bool is_keyframe, const uint8_t* data, size_t data_length) const { std::shared_ptr sample = MediaSample::CopyFrom(data, data_length, nullptr, 0, is_keyframe); sample->set_dts(timestamp); sample->set_pts(timestamp); sample->set_duration(duration); return sample; } std::unique_ptr MediaHandlerTestBase::GetSegmentInfo( int64_t start_timestamp, int64_t duration, bool is_subsegment) const { std::unique_ptr info(new SegmentInfo); info->start_timestamp = start_timestamp; info->duration = duration; info->is_subsegment = is_subsegment; return info; } std::unique_ptr MediaHandlerTestBase::GetTextStreamInfo( int32_t timescale) const { // None of this information is actually used by the text out handler. // The stream info is just needed to signal the start of the stream. return std::unique_ptr( new TextStreamInfo(0, timescale, 0, kUnknownCodec, "", "", 0, 0, "")); } std::unique_ptr MediaHandlerTestBase::GetTextSample( const std::string& id, int64_t start, int64_t end, const std::string& payload) const { return std::unique_ptr{ new TextSample(id, start, end, {}, TextFragment{{}, payload})}; } std::unique_ptr MediaHandlerTestBase::GetCueEvent( double time_in_seconds) const { std::unique_ptr event(new CueEvent); event->time_in_seconds = time_in_seconds; return event; } Status MediaHandlerTestBase::SetUpAndInitializeGraph( std::shared_ptr handler, size_t input_count, size_t output_count) { DCHECK(handler); DCHECK_EQ(nullptr, handler_); DCHECK(inputs_.empty()); DCHECK(outputs_.empty()); handler_ = std::move(handler); Status status; // Add and connect all the requested inputs. for (size_t i = 0; i < input_count; i++) { inputs_.emplace_back(new FakeInputMediaHandler); } for (auto& input : inputs_) { status.Update(input->AddHandler(handler_)); } if (!status.ok()) { return status; } // Add and connect all the requested outputs. for (size_t i = 0; i < output_count; i++) { outputs_.emplace_back(new testing::NiceMock); } for (auto& output : outputs_) { status.Update(handler_->AddHandler(output)); } if (!status.ok()) { return status; } // Initialize the graph. for (auto& input : inputs_) { status.Update(input->Initialize()); } // In the case that there are no inputs, the start of the graph // is at |handler_| so it needs to be initialized or else the graph // won't be initialized. if (inputs_.empty()) { status.Update(handler_->Initialize()); } return status; } FakeInputMediaHandler* MediaHandlerTestBase::Input(size_t index) { DCHECK_LT(index, inputs_.size()); return inputs_[index].get(); } MockOutputMediaHandler* MediaHandlerTestBase::Output(size_t index) { DCHECK_LT(index, outputs_.size()); return outputs_[index].get(); } MediaHandlerGraphTestBase::MediaHandlerGraphTestBase() : next_handler_(new CachingMediaHandler), some_handler_(new CachingMediaHandler) {} void MediaHandlerGraphTestBase::SetUpGraph( size_t num_inputs, size_t num_outputs, std::shared_ptr handler) { // Input handler is not really used anywhere else except to validate number of // allowed inputs for the handler to be tested. auto input_handler = std::make_shared(); for (size_t i = 0; i < num_inputs; ++i) ASSERT_OK(input_handler->SetHandler(i, handler)); // All outputs are routed to |next_handler_|. for (size_t i = 0; i < num_outputs; ++i) ASSERT_OK(handler->SetHandler(i, next_handler_)); } const std::vector>& MediaHandlerGraphTestBase::GetOutputStreamDataVector() const { return next_handler_->Cache(); } void MediaHandlerGraphTestBase::ClearOutputStreamDataVector() { next_handler_->Clear(); } } // namespace media } // namespace shaka