| // Copyright 2020 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/shared/starboard/player/filter/testing/test_util.h" |
| |
| #include "starboard/audio_sink.h" |
| #include "starboard/common/log.h" |
| #include "starboard/directory.h" |
| #include "starboard/shared/starboard/media/media_support_internal.h" |
| #include "starboard/shared/starboard/media/mime_type.h" |
| #include "starboard/shared/starboard/player/filter/player_components.h" |
| #include "starboard/shared/starboard/player/filter/stub_player_components_factory.h" |
| #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" |
| #include "starboard/shared/starboard/player/video_dmp_reader.h" |
| #include "starboard/string.h" |
| #include "starboard/system.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace player { |
| namespace filter { |
| namespace testing { |
| namespace { |
| |
| using ::starboard::shared::starboard::media::MimeType; |
| using ::testing::AssertionFailure; |
| using ::testing::AssertionResult; |
| using ::testing::AssertionSuccess; |
| using video_dmp::VideoDmpReader; |
| |
| std::string GetTestInputDirectory() { |
| const size_t kPathSize = kSbFileMaxPath + 1; |
| |
| std::vector<char> content_path(kPathSize); |
| SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(), |
| kPathSize)); |
| std::string directory_path = std::string(content_path.data()) + |
| kSbFileSepChar + "test" + kSbFileSepChar + |
| "starboard" + kSbFileSepChar + "shared" + |
| kSbFileSepChar + "starboard" + kSbFileSepChar + |
| "player" + kSbFileSepChar + "testdata"; |
| |
| SB_CHECK(SbDirectoryCanOpen(directory_path.c_str())) |
| << "Cannot open directory " << directory_path; |
| return directory_path; |
| } |
| |
| } // namespace |
| |
| void StubDeallocateSampleFunc(SbPlayer player, |
| void* context, |
| const void* sample_buffer) {} |
| |
| std::string ResolveTestFileName(const char* filename) { |
| return GetTestInputDirectory() + kSbFileSepChar + filename; |
| } |
| |
| std::string GetContentTypeFromAudioCodec(SbMediaAudioCodec audio_codec, |
| const char* mime_attributes) { |
| SB_DCHECK(audio_codec != kSbMediaAudioCodecNone); |
| |
| std::string content_type; |
| switch (audio_codec) { |
| case kSbMediaAudioCodecAac: |
| content_type = "audio/mp4; codecs=\"mp4a.40.2\""; |
| break; |
| case kSbMediaAudioCodecOpus: |
| content_type = "audio/webm; codecs=\"opus\""; |
| break; |
| case kSbMediaAudioCodecAc3: |
| content_type = "audio/mp4; codecs=\"ac-3\""; |
| break; |
| case kSbMediaAudioCodecEac3: |
| content_type = "audio/mp4; codecs=\"ec-3\""; |
| break; |
| default: |
| SB_NOTREACHED(); |
| } |
| return strlen(mime_attributes) > 0 ? content_type + "; " + mime_attributes |
| : content_type; |
| } |
| |
| std::vector<const char*> GetSupportedAudioTestFiles( |
| HeaacOption heaac_option, |
| int max_channels, |
| const char* extra_mime_attributes) { |
| // beneath_the_canopy_aac_stereo.dmp |
| // codec: kSbMediaAudioCodecAac |
| // sampling rate: 44.1k |
| // frames per AU: 1024 |
| // beneath_the_canopy_opus_stereo.dmp |
| // codec: kSbMediaAudioCodecOpus |
| // sampling rate: 48.0k |
| // frames per AU: 960 |
| |
| // IMPORTANT: do not change the order of test files unless necessary. If you |
| // must change the order of these tests please make sure you update the test |
| // filters for every platform, just in case a particular test case should be |
| // disabled. |
| |
| struct AudioFileInfo { |
| const char* filename; |
| SbMediaAudioCodec audio_codec; |
| int num_channels; |
| int64_t bitrate; |
| bool is_heaac; |
| }; |
| |
| const char* kFilenames[] = {"beneath_the_canopy_aac_stereo.dmp", |
| "beneath_the_canopy_aac_5_1.dmp", |
| "beneath_the_canopy_aac_mono.dmp", |
| "beneath_the_canopy_opus_5_1.dmp", |
| "beneath_the_canopy_opus_stereo.dmp", |
| "beneath_the_canopy_opus_mono.dmp", |
| "sintel_329_ec3.dmp", |
| "sintel_381_ac3.dmp", |
| "heaac.dmp"}; |
| |
| static std::vector<AudioFileInfo> audio_file_info_cache; |
| std::vector<const char*> filenames; |
| |
| if (audio_file_info_cache.empty()) { |
| audio_file_info_cache.reserve(SB_ARRAY_SIZE_INT(kFilenames)); |
| for (auto filename : kFilenames) { |
| VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str(), |
| VideoDmpReader::kEnableReadOnDemand); |
| SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0); |
| |
| audio_file_info_cache.push_back( |
| {filename, dmp_reader.audio_codec(), |
| dmp_reader.audio_sample_info().number_of_channels, |
| dmp_reader.audio_bitrate(), strstr(filename, "heaac") != nullptr}); |
| } |
| } |
| |
| for (auto& audio_file_info : audio_file_info_cache) { |
| // Filter files with channels exceeding |max_channels|. |
| if (audio_file_info.num_channels > max_channels) { |
| continue; |
| } |
| |
| // Filter heaac files when |heaac_option| == kExcludeHeaac. |
| if (heaac_option == kExcludeHeaac && audio_file_info.is_heaac) { |
| continue; |
| } |
| |
| // Filter files of unsupported codec. |
| const std::string audio_mime = GetContentTypeFromAudioCodec( |
| audio_file_info.audio_codec, extra_mime_attributes); |
| const MimeType audio_mime_type(audio_mime.c_str()); |
| if (!SbMediaIsAudioSupported(audio_file_info.audio_codec, &audio_mime_type, |
| audio_file_info.bitrate)) { |
| continue; |
| } |
| |
| filenames.push_back(audio_file_info.filename); |
| } |
| |
| return filenames; |
| } |
| |
| std::vector<VideoTestParam> GetSupportedVideoTests() { |
| SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture, |
| kSbPlayerOutputModePunchOut}; |
| |
| // IMPORTANT: do not change the order of test files unless necessary. If you |
| // must change the order of these tests please make sure you update the test |
| // filters for every platform, just in case a particular test case should be |
| // disabled. |
| |
| const char* kFilenames[] = { |
| "beneath_the_canopy_137_avc.dmp", "beneath_the_canopy_248_vp9.dmp", |
| "sintel_399_av1.dmp", "black_test_avc_1080p_30to60_fps.dmp"}; |
| |
| static std::vector<VideoTestParam> test_params; |
| |
| if (!test_params.empty()) { |
| return test_params; |
| } |
| |
| for (auto filename : kFilenames) { |
| VideoDmpReader dmp_reader(ResolveTestFileName(filename).c_str(), |
| VideoDmpReader::kEnableReadOnDemand); |
| SB_DCHECK(dmp_reader.number_of_video_buffers() > 0); |
| |
| for (auto output_mode : kOutputModes) { |
| if (!VideoDecoder::OutputModeSupported( |
| output_mode, dmp_reader.video_codec(), kSbDrmSystemInvalid)) { |
| continue; |
| } |
| |
| const auto& video_sample_info = |
| dmp_reader.GetPlayerSampleInfo(kSbMediaTypeVideo, 0) |
| .video_sample_info; |
| const std::string video_mime = dmp_reader.video_mime_type(); |
| const MimeType video_mime_type(video_mime.c_str()); |
| if (SbMediaIsVideoSupported( |
| dmp_reader.video_codec(), |
| video_mime.size() > 0 ? &video_mime_type : nullptr, -1, -1, 8, |
| kSbMediaPrimaryIdUnspecified, kSbMediaTransferIdUnspecified, |
| kSbMediaMatrixIdUnspecified, video_sample_info.frame_width, |
| video_sample_info.frame_height, dmp_reader.video_bitrate(), |
| dmp_reader.video_fps(), false)) { |
| test_params.push_back(std::make_tuple(filename, output_mode)); |
| } |
| } |
| } |
| |
| SB_DCHECK(!test_params.empty()); |
| return test_params; |
| } |
| |
| bool CreateAudioComponents(bool using_stub_decoder, |
| SbMediaAudioCodec codec, |
| const SbMediaAudioSampleInfo& audio_sample_info, |
| scoped_ptr<AudioDecoder>* audio_decoder, |
| scoped_ptr<AudioRendererSink>* audio_renderer_sink) { |
| SB_CHECK(audio_decoder); |
| SB_CHECK(audio_renderer_sink); |
| |
| audio_renderer_sink->reset(); |
| audio_decoder->reset(); |
| |
| PlayerComponents::Factory::CreationParameters creation_parameters( |
| codec, audio_sample_info); |
| |
| scoped_ptr<PlayerComponents::Factory> factory; |
| if (using_stub_decoder) { |
| factory = StubPlayerComponentsFactory::Create(); |
| } else { |
| factory = PlayerComponents::Factory::Create(); |
| } |
| std::string error_message; |
| if (factory->CreateSubComponents(creation_parameters, audio_decoder, |
| audio_renderer_sink, nullptr, nullptr, |
| nullptr, &error_message)) { |
| SB_CHECK(*audio_decoder); |
| return true; |
| } |
| audio_renderer_sink->reset(); |
| audio_decoder->reset(); |
| return false; |
| } |
| |
| AssertionResult AlmostEqualTime(SbTime time1, SbTime time2) { |
| const SbTime kEpsilon = kSbTimeSecond / 1000; |
| SbTime diff = time1 - time2; |
| if (-kEpsilon <= diff && diff <= kEpsilon) { |
| return AssertionSuccess(); |
| } |
| return AssertionFailure() |
| << "time " << time1 << " doesn't match with time " << time2; |
| } |
| |
| media::VideoSampleInfo CreateVideoSampleInfo(SbMediaVideoCodec codec) { |
| shared::starboard::media::VideoSampleInfo video_sample_info = {}; |
| |
| video_sample_info.codec = codec; |
| video_sample_info.mime = ""; |
| video_sample_info.max_video_capabilities = ""; |
| |
| video_sample_info.color_metadata.primaries = kSbMediaPrimaryIdBt709; |
| video_sample_info.color_metadata.transfer = kSbMediaTransferIdBt709; |
| video_sample_info.color_metadata.matrix = kSbMediaMatrixIdBt709; |
| video_sample_info.color_metadata.range = kSbMediaRangeIdLimited; |
| |
| video_sample_info.frame_width = 1920; |
| video_sample_info.frame_height = 1080; |
| |
| return video_sample_info; |
| } |
| |
| } // namespace testing |
| } // namespace filter |
| } // namespace player |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |