| // 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/player_components.h" |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include "starboard/common/scoped_ptr.h" |
| #include "starboard/media.h" |
| #include "starboard/player.h" |
| #include "starboard/shared/starboard/player/filter/testing/test_util.h" |
| #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" |
| #include "starboard/shared/starboard/player/job_queue.h" |
| #include "starboard/shared/starboard/player/video_dmp_reader.h" |
| #include "starboard/testing/fake_graphics_context_provider.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace player { |
| namespace filter { |
| namespace testing { |
| namespace { |
| |
| using ::starboard::testing::FakeGraphicsContextProvider; |
| using std::placeholders::_1; |
| using std::placeholders::_2; |
| using std::string; |
| using std::unique_ptr; |
| using std::vector; |
| using ::testing::ValuesIn; |
| using video_dmp::VideoDmpReader; |
| |
| typedef VideoDmpReader::AudioAccessUnit AudioAccessUnit; |
| typedef VideoDmpReader::VideoAccessUnit VideoAccessUnit; |
| typedef PlayerComponents::Factory::CreationParameters CreationParameters; |
| typedef std::tuple<const char*, const char*, SbPlayerOutputMode> |
| PlayerComponentsTestParam; |
| |
| const SbTimeMonotonic kDefaultPrerollTimeOut = 5 * kSbTimeSecond; |
| const SbTimeMonotonic kDefaultEndTimeOut = 5 * kSbTimeSecond; |
| const SbTimeMonotonic kDefaultWriteTimeOut = 5 * kSbTimeSecond; |
| |
| const SbPlayer kDummyPlayer = reinterpret_cast<SbPlayer>(1); |
| |
| class PlayerComponentsTest |
| : public ::testing::TestWithParam<PlayerComponentsTestParam> { |
| protected: |
| PlayerComponentsTest() |
| : audio_filename_(std::get<0>(GetParam())), |
| video_filename_(std::get<1>(GetParam())), |
| output_mode_(std::get<2>(GetParam())) { |
| SB_LOG(INFO) << "Testing: \"" << audio_filename_ << "\", \"" |
| << video_filename_ |
| << (output_mode_ == kSbPlayerOutputModeDecodeToTexture |
| ? "\", kSbPlayerOutputModeDecodeToTexture." |
| : "\", kSbPlayerOutputModePunchOut."); |
| } |
| |
| void SetUp() override { |
| if (audio_filename_.length() > 0) { |
| audio_reader_.reset(new VideoDmpReader( |
| ResolveTestFileName(audio_filename_.c_str()).c_str(), |
| VideoDmpReader::kEnableReadOnDemand)); |
| } |
| if (video_filename_.length() > 0) { |
| video_reader_.reset(new VideoDmpReader( |
| ResolveTestFileName(video_filename_.c_str()).c_str(), |
| VideoDmpReader::kEnableReadOnDemand)); |
| } |
| |
| scoped_ptr<PlayerComponents::Factory> factory = |
| PlayerComponents::Factory::Create(); |
| string error_message; |
| if (audio_reader_ && video_reader_) { |
| CreationParameters creation_parameters( |
| audio_reader_->audio_codec(), audio_reader_->audio_sample_info(), |
| video_reader_->video_codec(), |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, 0) |
| .video_sample_info, |
| kDummyPlayer, output_mode_, |
| fake_graphics_context_provider_.decoder_target_provider()); |
| player_components_ = |
| factory->CreateComponents(creation_parameters, &error_message); |
| } else if (audio_reader_) { |
| // Audio only |
| CreationParameters creation_parameters( |
| audio_reader_->audio_codec(), audio_reader_->audio_sample_info()); |
| player_components_ = |
| factory->CreateComponents(creation_parameters, &error_message); |
| } else { |
| // Video only |
| ASSERT_TRUE(video_reader_); |
| CreationParameters creation_parameters( |
| video_reader_->video_codec(), |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, 0) |
| .video_sample_info, |
| kDummyPlayer, output_mode_, |
| fake_graphics_context_provider_.decoder_target_provider()); |
| player_components_ = |
| factory->CreateComponents(creation_parameters, &error_message); |
| } |
| ASSERT_TRUE(player_components_); |
| |
| if (GetAudioRenderer()) { |
| GetAudioRenderer()->Initialize( |
| std::bind(&PlayerComponentsTest::OnError, this, _1, _2), |
| std::bind(&PlayerComponentsTest::OnPrerolled, this, |
| kSbMediaTypeAudio), |
| std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeAudio)); |
| } |
| SetPlaybackRate(playback_rate_); |
| if (GetVideoRenderer()) { |
| GetVideoRenderer()->Initialize( |
| std::bind(&PlayerComponentsTest::OnError, this, _1, _2), |
| std::bind(&PlayerComponentsTest::OnPrerolled, this, |
| kSbMediaTypeVideo), |
| std::bind(&PlayerComponentsTest::OnEnded, this, kSbMediaTypeVideo)); |
| } |
| } |
| |
| MediaTimeProvider* GetMediaTimeProvider() const { |
| return player_components_->GetMediaTimeProvider(); |
| } |
| AudioRenderer* GetAudioRenderer() const { |
| return player_components_->GetAudioRenderer(); |
| } |
| VideoRenderer* GetVideoRenderer() const { |
| return player_components_->GetVideoRenderer(); |
| } |
| |
| bool IsPlaybackPrerolled() { |
| if (GetAudioRenderer() && !audio_prerolled_) { |
| return false; |
| } |
| if (GetVideoRenderer() && !video_prerolled_) { |
| return false; |
| } |
| return true; |
| } |
| |
| bool IsPlaybackEnded() { |
| if (GetAudioRenderer() && !audio_ended_) { |
| return false; |
| } |
| if (GetVideoRenderer() && !video_ended_) { |
| return false; |
| } |
| return true; |
| } |
| |
| void Seek(SbTime seek_to_time) { |
| has_error_ = false; |
| audio_prerolled_ = false; |
| video_prerolled_ = false; |
| audio_ended_ = false; |
| video_ended_ = false; |
| audio_index_ = 0; |
| video_index_ = 0; |
| // Find the closest key frame prior to |seek_to_time|. |
| if (GetAudioRenderer()) { |
| for (int index = 1; index < audio_reader_->number_of_audio_buffers(); |
| index++) { |
| SbPlayerSampleInfo sample_info = |
| audio_reader_->GetPlayerSampleInfo(kSbMediaTypeAudio, index); |
| if (sample_info.timestamp > seek_to_time) { |
| break; |
| } |
| audio_index_ = index; |
| } |
| } |
| if (GetVideoRenderer()) { |
| for (int index = 1; index < video_reader_->number_of_video_buffers(); |
| index++) { |
| SbPlayerSampleInfo sample_info = |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, index); |
| if (sample_info.timestamp > seek_to_time) { |
| break; |
| } |
| if (sample_info.video_sample_info.is_key_frame) { |
| video_index_ = index; |
| } |
| } |
| } |
| GetMediaTimeProvider()->Pause(); |
| if (GetVideoRenderer()) { |
| GetVideoRenderer()->Seek(seek_to_time); |
| } |
| GetMediaTimeProvider()->Seek(seek_to_time); |
| } |
| |
| void Play() { GetMediaTimeProvider()->Play(); } |
| |
| void Pause() { GetMediaTimeProvider()->Pause(); } |
| |
| void SetPlaybackRate(double playback_rate) { |
| playback_rate_ = playback_rate; |
| GetMediaTimeProvider()->SetPlaybackRate(playback_rate_); |
| } |
| |
| SbTime GetMediaTime() { |
| bool is_playing, is_eos_played, is_underflow; |
| double playback_rate; |
| return GetMediaTimeProvider()->GetCurrentMediaTime( |
| &is_playing, &is_eos_played, &is_underflow, &playback_rate); |
| } |
| |
| bool IsPlaying() { |
| bool is_playing, is_eos_played, is_underflow; |
| double playback_rate; |
| GetMediaTimeProvider()->GetCurrentMediaTime(&is_playing, &is_eos_played, |
| &is_underflow, &playback_rate); |
| return is_playing; |
| } |
| |
| SbTime GetCurrentAudioBufferTimestamp() const { |
| if (!GetAudioRenderer()) { |
| return 0; |
| } |
| if (audio_index_ >= audio_reader_->number_of_audio_buffers()) { |
| return audio_reader_->audio_duration(); |
| } |
| return audio_reader_->GetPlayerSampleInfo(kSbMediaTypeAudio, audio_index_) |
| .timestamp; |
| } |
| |
| SbTime GetCurrentVideoBufferTimestamp() const { |
| if (!GetVideoRenderer()) { |
| return 0; |
| } |
| if (video_index_ >= video_reader_->number_of_video_buffers()) { |
| return video_reader_->video_duration(); |
| } |
| if (video_index_ >= video_reader_->number_of_video_buffers() - 1) { |
| return video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, video_index_) |
| .timestamp; |
| } |
| // The buffers are ordered by decoding order. We need to find the |
| // presentation timestamp of next frame. |
| SbTime next_timestamps[2] = { |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, video_index_) |
| .timestamp, |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, video_index_ + 1) |
| .timestamp}; |
| return std::min(next_timestamps[0], next_timestamps[1]); |
| } |
| |
| void WriteDataUntilPrerolled(SbTime timeout = kDefaultPrerollTimeOut) { |
| SbTimeMonotonic start_time = SbTimeGetMonotonicNow(); |
| SbTime max_timestamp = GetMediaTime() + kMaxWriteAheadDuration; |
| while (!IsPlaybackPrerolled()) { |
| ASSERT_LE(SbTimeGetMonotonicNow() - start_time, timeout) |
| << "WriteDataUntilPrerolled() timed out, buffered audio (" |
| << GetCurrentAudioBufferTimestamp() << "), buffered video (" |
| << GetCurrentVideoBufferTimestamp() << "), max timestamp (" |
| << max_timestamp << ")."; |
| bool written = TryToWriteOneInputBuffer(max_timestamp); |
| if (!written) { |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| } |
| } |
| |
| // The function will exit after all buffers before |eos_timestamp| are written |
| // into the player. Note that, to avoid audio or video underflow, the function |
| // allow to write buffers of timestamp greater than |timestamp|. |
| void WriteDataUntil(SbTime timestamp, SbTime timeout = kDefaultWriteTimeOut) { |
| SB_CHECK(playback_rate_ != 0); |
| |
| SbTimeMonotonic last_input_filled_time = SbTimeGetMonotonicNow(); |
| while ( |
| (GetAudioRenderer() && GetCurrentAudioBufferTimestamp() < timestamp) || |
| (GetVideoRenderer() && GetCurrentVideoBufferTimestamp() < timestamp)) { |
| if (last_input_filled_time != -1) { |
| ASSERT_LE(SbTimeGetMonotonicNow() - last_input_filled_time, timeout) |
| << "WriteDataUntil() timed out, buffered audio (" |
| << GetCurrentAudioBufferTimestamp() << "), buffered video (" |
| << GetCurrentVideoBufferTimestamp() << "), timestamp (" << timestamp |
| << ")."; |
| } |
| bool written = |
| TryToWriteOneInputBuffer(timestamp + kMaxWriteAheadDuration); |
| if (written) { |
| last_input_filled_time = SbTimeGetMonotonicNow(); |
| } else { |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| } |
| } |
| |
| // The function will write EOS immediately after all buffers before |
| // |eos_timestamp| are written into the player. |
| void WriteDataAndEOS(SbTime eos_timestamp, |
| SbTime timeout = kDefaultWriteTimeOut) { |
| SB_CHECK(playback_rate_ != 0); |
| bool audio_eos_written = !GetAudioRenderer(); |
| bool video_eos_written = !GetVideoRenderer(); |
| |
| SbTimeMonotonic last_input_filled_time = SbTimeGetMonotonicNow(); |
| while (!audio_eos_written || !video_eos_written) { |
| if (!audio_eos_written && |
| GetCurrentAudioBufferTimestamp() >= eos_timestamp) { |
| GetAudioRenderer()->WriteEndOfStream(); |
| audio_eos_written = true; |
| } |
| if (!video_eos_written && |
| GetCurrentVideoBufferTimestamp() >= eos_timestamp) { |
| GetVideoRenderer()->WriteEndOfStream(); |
| video_eos_written = true; |
| } |
| if (last_input_filled_time != -1) { |
| ASSERT_LE(SbTimeGetMonotonicNow() - last_input_filled_time, timeout) |
| << "WriteDataAndEOS() timed out, buffered audio (" |
| << GetCurrentAudioBufferTimestamp() << "), buffered video (" |
| << GetCurrentVideoBufferTimestamp() << "), eos_timestamp (" |
| << eos_timestamp << ")."; |
| } |
| bool written = TryToWriteOneInputBuffer(eos_timestamp); |
| if (written) { |
| last_input_filled_time = SbTimeGetMonotonicNow(); |
| } else { |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| } |
| } |
| |
| void WriteEOSDirectly() { |
| if (GetAudioRenderer()) { |
| GetAudioRenderer()->WriteEndOfStream(); |
| } |
| if (GetVideoRenderer()) { |
| GetVideoRenderer()->WriteEndOfStream(); |
| } |
| } |
| |
| void WaitUntilPlaybackEnded() { |
| SB_CHECK(playback_rate_ != 0); |
| |
| SbTime duration = std::max(GetCurrentAudioBufferTimestamp(), |
| GetCurrentVideoBufferTimestamp()); |
| SbTime current_time = GetMediaTime(); |
| SbTime expected_end_time = |
| SbTimeGetMonotonicNow() + |
| static_cast<SbTime>((duration - current_time) / playback_rate_) + |
| kDefaultEndTimeOut; |
| |
| while (!IsPlaybackEnded()) { |
| // If this fails, timeout must have been reached. |
| ASSERT_LE(SbTimeGetMonotonicNow(), expected_end_time) |
| << "WaitUntilPlaybackEnded() timed out, buffered audio (" |
| << GetCurrentAudioBufferTimestamp() << "), buffered video (" |
| << GetCurrentVideoBufferTimestamp() << "), current media time is " |
| << GetMediaTime() << "."; |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| current_time = GetMediaTime(); |
| // TODO: investigate and reduce the tolerance. |
| ASSERT_LE(std::abs(current_time - duration), 500 * kSbTimeMillisecond); |
| } |
| |
| // This function needs to be called periodically to keep player components |
| // working. |
| void RenderAndProcessPendingJobs() { |
| // Call GetCurrentDecodeTarget() periodically for decode to texture mode. |
| if (output_mode_ == kSbPlayerOutputModeDecodeToTexture && |
| GetVideoRenderer()) { |
| #if SB_HAS(GLES2) |
| fake_graphics_context_provider_.RunOnGlesContextThread( |
| std::bind(&PlayerComponentsTest::RenderOnGlesContextThread, this)); |
| #endif // SB_HAS(GLES2) |
| } |
| // Process jobs in the job queue. |
| job_queue_.RunUntilIdle(); |
| ASSERT_FALSE(has_error_); |
| } |
| |
| private: |
| // We won't write audio data more than 1s ahead of current media time in |
| // cobalt. So, to test with the same condition, we limit max inputs ahead to |
| // 1s in the tests. |
| const SbTime kMaxWriteAheadDuration = kSbTimeSecond; |
| |
| void OnError(SbPlayerError error, const std::string& error_message) { |
| has_error_ = true; |
| SB_LOG(ERROR) << "Caught renderer error (" << error |
| << "): " << error_message; |
| } |
| void OnPrerolled(SbMediaType media_type) { |
| if (media_type == kSbMediaTypeAudio) { |
| audio_prerolled_ = true; |
| } else { |
| EXPECT_EQ(media_type, kSbMediaTypeVideo); |
| video_prerolled_ = true; |
| } |
| } |
| void OnEnded(SbMediaType media_type) { |
| if (media_type == kSbMediaTypeAudio) { |
| SB_DLOG(INFO) << "Audio OnEnded is called."; |
| audio_ended_ = true; |
| } else { |
| EXPECT_EQ(media_type, kSbMediaTypeVideo); |
| SB_DLOG(INFO) << "Video OnEnded is called."; |
| video_ended_ = true; |
| } |
| } |
| |
| scoped_refptr<InputBuffer> GetAudioInputBuffer(size_t index) const { |
| auto player_sample_info = |
| audio_reader_->GetPlayerSampleInfo(kSbMediaTypeAudio, index); |
| auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, nullptr, |
| nullptr, player_sample_info); |
| return input_buffer; |
| } |
| |
| scoped_refptr<InputBuffer> GetVideoInputBuffer(size_t index) const { |
| auto video_sample_info = |
| video_reader_->GetPlayerSampleInfo(kSbMediaTypeVideo, index); |
| auto input_buffer = new InputBuffer(StubDeallocateSampleFunc, NULL, NULL, |
| video_sample_info); |
| return input_buffer; |
| } |
| |
| void RenderOnGlesContextThread() { |
| SbDecodeTargetRelease(GetVideoRenderer()->GetCurrentDecodeTarget()); |
| } |
| |
| bool TryToWriteOneInputBuffer(SbTime max_timestamp) { |
| bool input_buffer_written = false; |
| if (GetAudioRenderer() && GetAudioRenderer()->CanAcceptMoreData() && |
| audio_index_ < audio_reader_->number_of_audio_buffers() && |
| GetCurrentAudioBufferTimestamp() < max_timestamp) { |
| GetAudioRenderer()->WriteSample(GetAudioInputBuffer(audio_index_++)); |
| input_buffer_written = true; |
| } |
| if (GetVideoRenderer() && GetVideoRenderer()->CanAcceptMoreData() && |
| video_index_ < video_reader_->number_of_video_buffers() && |
| GetCurrentVideoBufferTimestamp() < max_timestamp) { |
| GetVideoRenderer()->WriteSample(GetVideoInputBuffer(video_index_++)); |
| input_buffer_written = true; |
| } |
| if (input_buffer_written) { |
| job_queue_.RunUntilIdle(); |
| } |
| return input_buffer_written; |
| } |
| |
| const std::string audio_filename_; |
| const std::string video_filename_; |
| const SbPlayerOutputMode output_mode_; |
| JobQueue job_queue_; |
| FakeGraphicsContextProvider fake_graphics_context_provider_; |
| unique_ptr<VideoDmpReader> audio_reader_; |
| unique_ptr<VideoDmpReader> video_reader_; |
| scoped_ptr<PlayerComponents> player_components_; |
| double playback_rate_ = 1.0; |
| int audio_index_ = 0; |
| int video_index_ = 0; |
| bool has_error_ = false; |
| bool audio_prerolled_ = false; |
| bool video_prerolled_ = false; |
| bool audio_ended_ = false; |
| bool video_ended_ = false; |
| }; |
| |
| TEST_P(PlayerComponentsTest, Preroll) { |
| Seek(0); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| } |
| |
| TEST_P(PlayerComponentsTest, SunnyDay) { |
| Seek(0); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| ASSERT_EQ(GetMediaTime(), 0); |
| ASSERT_FALSE(IsPlaying()); |
| |
| SbTimeMonotonic play_requested_at = SbTimeGetMonotonicNow(); |
| Play(); |
| SbTime media_duration = std::max(GetCurrentVideoBufferTimestamp(), |
| GetCurrentAudioBufferTimestamp()); |
| media_duration = std::max(kSbTimeSecond, media_duration); |
| |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(media_duration)); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| |
| // TODO: investigate and reduce the tolerance. |
| // ASSERT_LE( |
| // std::abs(SbTimeGetMonotonicNow() - (play_requested_at + |
| // media_duration)), 300 * kSbTimeMillisecond); |
| } |
| |
| TEST_P(PlayerComponentsTest, ShortPlayback) { |
| Seek(0); |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(50 * kSbTimeMillisecond)); |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| } |
| |
| TEST_P(PlayerComponentsTest, EOSWithoutInput) { |
| Seek(0); |
| ASSERT_NO_FATAL_FAILURE(WriteEOSDirectly()); |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| // TODO: investigate and reduce the tolerance. |
| // ASSERT_LE(std::abs(GetMediaTime()), 100 * kSbTimeMillisecond); |
| } |
| |
| TEST_P(PlayerComponentsTest, Pause) { |
| Seek(0); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntil(kSbTimeSecond)); |
| Pause(); |
| ASSERT_FALSE(IsPlaying()); |
| |
| SbTimeMonotonic start_time = SbTimeGetMonotonicNow(); |
| while (SbTimeGetMonotonicNow() < start_time + 200 * kSbTimeMillisecond) { |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| SbTime media_time = GetMediaTime(); |
| start_time = SbTimeGetMonotonicNow(); |
| while (SbTimeGetMonotonicNow() < start_time + 200 * kSbTimeMillisecond) { |
| ASSERT_NO_FATAL_FAILURE(RenderAndProcessPendingJobs()); |
| SbThreadSleep(5 * kSbTimeMillisecond); |
| } |
| ASSERT_EQ(media_time, GetMediaTime()); |
| |
| Play(); |
| SbTime duration = std::max(GetCurrentAudioBufferTimestamp(), |
| GetCurrentVideoBufferTimestamp()); |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(duration)); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| } |
| |
| // TODO: Enable variable playback rate tests |
| // TEST_P(PlayerComponentsTest, PlaybackRateHalf) { |
| // const double kPlaybackRate = 0.5; |
| |
| // Seek(0); |
| // SetPlaybackRate(kPlaybackRate); |
| // ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| // ASSERT_EQ(GetMediaTime(), 0); |
| // ASSERT_FALSE(IsPlaying()); |
| |
| // SbTime media_duration_to_write = std::max(GetCurrentVideoBufferTimestamp(), |
| // GetCurrentAudioBufferTimestamp()); |
| // media_duration_to_write = std::max(kSbTimeSecond, media_duration_to_write); |
| // SbTime media_duration_to_play = |
| // static_cast<SbTime>(media_duration_to_write / kPlaybackRate); |
| |
| // SbTimeMonotonic play_requested_at = SbTimeGetMonotonicNow(); |
| // Play(); |
| |
| // ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(media_duration_to_write)); |
| // ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| |
| // TODO: Enable the below check after we improve the accuracy of varied |
| // playback rate time reporting. |
| // ASSERT_GE(SbTimeGetMonotonicNow(), |
| // play_requested_at + media_duration_to_play - 200 * |
| // kSbTimeMillisecond); |
| // } |
| |
| // TEST_P(PlayerComponentsTest, PlaybackRateDouble) { |
| // const double kPlaybackRate = 2.0; |
| |
| // Seek(0); |
| // SetPlaybackRate(kPlaybackRate); |
| // ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| // ASSERT_EQ(GetMediaTime(), 0); |
| // ASSERT_FALSE(IsPlaying()); |
| |
| // SbTime media_duration_to_write = std::max(GetCurrentVideoBufferTimestamp(), |
| // GetCurrentAudioBufferTimestamp()); |
| // media_duration_to_write = |
| // std::max(kSbTimeSecond * 2, media_duration_to_write); |
| // SbTime media_duration_to_play = |
| // static_cast<SbTime>(media_duration_to_write / kPlaybackRate); |
| |
| // SbTimeMonotonic play_requested_at = SbTimeGetMonotonicNow(); |
| // Play(); |
| |
| // ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(media_duration_to_write)); |
| // ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| |
| // playback rate time reporting. |
| // ASSERT_LE(SbTimeGetMonotonicNow(), |
| // play_requested_at + media_duration_to_play + 200 * |
| // kSbTimeMillisecond); |
| // } |
| |
| TEST_P(PlayerComponentsTest, SeekForward) { |
| SbTime seek_to_time = 0; |
| Seek(seek_to_time); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| ASSERT_EQ(GetMediaTime(), seek_to_time); |
| ASSERT_FALSE(IsPlaying()); |
| |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntil(kSbTimeSecond)); |
| |
| Pause(); |
| seek_to_time = 2 * kSbTimeSecond; |
| Seek(seek_to_time); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| ASSERT_EQ(GetMediaTime(), seek_to_time); |
| ASSERT_FALSE(IsPlaying()); |
| |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(seek_to_time + kSbTimeSecond)); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| } |
| |
| TEST_P(PlayerComponentsTest, SeekBackward) { |
| SbTime seek_to_time = 3 * kSbTimeSecond; |
| Seek(seek_to_time); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| ASSERT_EQ(GetMediaTime(), seek_to_time); |
| ASSERT_FALSE(IsPlaying()); |
| |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(seek_to_time + kSbTimeSecond)); |
| |
| Pause(); |
| seek_to_time = 1 * kSbTimeSecond; |
| Seek(seek_to_time); |
| ASSERT_NO_FATAL_FAILURE(WriteDataUntilPrerolled()); |
| ASSERT_EQ(GetMediaTime(), seek_to_time); |
| ASSERT_FALSE(IsPlaying()); |
| |
| Play(); |
| ASSERT_NO_FATAL_FAILURE(WriteDataAndEOS(seek_to_time + kSbTimeSecond)); |
| ASSERT_NO_FATAL_FAILURE(WaitUntilPlaybackEnded()); |
| } |
| |
| PlayerComponentsTestParam CreateParam(const char* audio_file, |
| const VideoTestParam& video_param) { |
| return std::make_tuple(audio_file, std::get<0>(video_param), |
| std::get<1>(video_param)); |
| } |
| |
| vector<PlayerComponentsTestParam> GetSupportedCreationParameters() { |
| vector<PlayerComponentsTestParam> supported_parameters; |
| |
| // TODO: Enable tests of "heaac.dmp". |
| vector<const char*> audio_files = |
| GetSupportedAudioTestFiles(kExcludeHeaac, SbAudioSinkGetMaxChannels()); |
| vector<VideoTestParam> video_params = GetSupportedVideoTests(); |
| |
| // Filter too short dmp files, as the tests need at least 4s of data. |
| for (auto iter = audio_files.begin(); iter != audio_files.end();) { |
| VideoDmpReader audio_dmp_reader(ResolveTestFileName(*iter).c_str(), |
| VideoDmpReader::kEnableReadOnDemand); |
| if (audio_dmp_reader.audio_duration() < 5 * kSbTimeSecond) { |
| iter = audio_files.erase(iter); |
| } else { |
| iter++; |
| } |
| } |
| for (auto iter = video_params.begin(); iter != video_params.end();) { |
| VideoDmpReader video_dmp_reader( |
| ResolveTestFileName(std::get<0>(*iter)).c_str(), |
| VideoDmpReader::kEnableReadOnDemand); |
| if (video_dmp_reader.video_duration() < 5 * kSbTimeSecond) { |
| iter = video_params.erase(iter); |
| } else { |
| iter++; |
| } |
| } |
| |
| for (size_t i = 0; i < video_params.size(); i++) { |
| supported_parameters.push_back(CreateParam("", video_params[i])); |
| for (size_t j = 0; j < audio_files.size(); j++) { |
| supported_parameters.push_back( |
| CreateParam(audio_files[j], video_params[i])); |
| } |
| } |
| SB_DCHECK(supported_parameters.size() < 50) |
| << "There're " << supported_parameters.size() |
| << " tests added. It may take too long time to run and result in timeout"; |
| |
| for (size_t i = 0; i < audio_files.size(); i++) { |
| if (VideoDecoder::OutputModeSupported(kSbPlayerOutputModeDecodeToTexture, |
| kSbMediaVideoCodecNone, |
| kSbDrmSystemInvalid)) { |
| supported_parameters.push_back(std::make_tuple( |
| audio_files[i], "", kSbPlayerOutputModeDecodeToTexture)); |
| } |
| if (VideoDecoder::OutputModeSupported(kSbPlayerOutputModePunchOut, |
| kSbMediaVideoCodecNone, |
| kSbDrmSystemInvalid)) { |
| supported_parameters.push_back( |
| std::make_tuple(audio_files[i], "", kSbPlayerOutputModePunchOut)); |
| } |
| } |
| |
| return supported_parameters; |
| } |
| |
| INSTANTIATE_TEST_CASE_P(PlayerComponentsTests, |
| PlayerComponentsTest, |
| ValuesIn(GetSupportedCreationParameters())); |
| } // namespace |
| } // namespace testing |
| } // namespace filter |
| } // namespace player |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |