blob: 0027ebf894786144a1d80434b91ee882d985480f [file] [log] [blame]
// Copyright 2023 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.
#ifndef STARBOARD_NPLB_PLAYER_TEST_FIXTURE_H_
#define STARBOARD_NPLB_PLAYER_TEST_FIXTURE_H_
#include <atomic>
#include <deque>
#include <set>
#include <string>
#include <vector>
#include "starboard/common/queue.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/drm.h"
#include "starboard/nplb/player_test_util.h"
#include "starboard/player.h"
#include "starboard/shared/starboard/player/video_dmp_reader.h"
#include "starboard/shared/starboard/thread_checker.h"
#include "starboard/testing/fake_graphics_context_provider.h"
namespace starboard {
namespace nplb {
class SbPlayerTestFixture {
public:
// A simple encapsulation of grouped samples.
class GroupedSamplesIterator;
class GroupedSamples {
public:
struct AudioSamplesDescriptor {
int start_index = 0;
int samples_count = 0;
int64_t timestamp_offset = 0;
int64_t discarded_duration_from_front = 0;
int64_t discarded_duration_from_back = 0;
bool is_end_of_stream = false;
};
struct VideoSamplesDescriptor {
int start_index = 0;
int samples_count = 0;
bool is_end_of_stream = false;
};
GroupedSamples& AddAudioSamples(int start_index, int number_of_samples);
GroupedSamples& AddAudioSamples(int start_index,
int number_of_samples,
int64_t timestamp_offset,
int64_t discarded_duration_from_front,
int64_t discarded_duration_from_back);
GroupedSamples& AddAudioEOS();
GroupedSamples& AddVideoSamples(int start_index, int number_of_samples);
GroupedSamples& AddVideoEOS();
friend class GroupedSamplesIterator;
private:
std::vector<AudioSamplesDescriptor> audio_samples_;
std::vector<VideoSamplesDescriptor> video_samples_;
};
SbPlayerTestFixture(
const SbPlayerTestConfig& config,
testing::FakeGraphicsContextProvider* fake_graphics_context_provider);
~SbPlayerTestFixture();
void Seek(const int64_t time);
// Write audio and video samples. It waits for
// |kSbPlayerDecoderStateNeedsData| internally. When writing EOS are
// requested, the function will write EOS after all samples of the same type
// are written.
void Write(const GroupedSamples& grouped_samples);
// Wait until kSbPlayerStatePresenting received.
void WaitForPlayerPresenting();
// Wait until kSbPlayerStateEndOfStream received.
void WaitForPlayerEndOfStream();
int64_t GetCurrentMediaTime() const;
void SetAudioWriteDuration(int64_t duration);
SbPlayer GetPlayer() const { return player_; }
bool HasAudio() const { return audio_dmp_reader_; }
bool HasVideo() const { return video_dmp_reader_; }
int64_t GetAudioSampleTimestamp(int index) const;
int ConvertDurationToAudioBufferCount(int64_t duration) const;
int ConvertDurationToVideoBufferCount(int64_t duration) const;
private:
static constexpr int64_t kDefaultWaitForPlayerStateTimeout = 5'000'000LL;
static constexpr int64_t kDefaultWaitForCallbackEventTimeout = 15'000;
typedef shared::starboard::player::video_dmp::VideoDmpReader VideoDmpReader;
typedef enum CallbackEventType {
kEmptyEvent,
kDecoderStateEvent,
kPlayerStateEvent,
} CallbackEventType;
struct CallbackEvent {
CallbackEvent();
CallbackEvent(SbPlayer player,
SbMediaType type,
SbPlayerDecoderState state,
int ticket);
CallbackEvent(SbPlayer player, SbPlayerState state, int ticket);
CallbackEventType event_type;
SbPlayer player = kSbPlayerInvalid;
SbMediaType media_type;
SbPlayerDecoderState decoder_state;
SbPlayerState player_state;
int ticket = SB_PLAYER_INITIAL_TICKET;
};
static void DecoderStatusCallback(SbPlayer player,
void* context,
SbMediaType type,
SbPlayerDecoderState state,
int ticket);
static void PlayerStatusCallback(SbPlayer player,
void* context,
SbPlayerState state,
int ticket);
static void ErrorCallback(SbPlayer player,
void* context,
SbPlayerError error,
const char* message);
// Callback methods from the underlying player. Note that callbacks may be
// invoked on underlying player's thread.
void OnDecoderState(SbPlayer player,
SbMediaType media_type,
SbPlayerDecoderState state,
int ticket);
void OnPlayerState(SbPlayer player, SbPlayerState state, int ticket);
void OnError(SbPlayer player, SbPlayerError error, const char* message);
void Initialize();
void TearDown();
bool CanWriteMoreAudioData();
bool CanWriteMoreVideoData();
void WriteAudioSamples(int start_index,
int samples_to_write,
int64_t timestamp_offset,
int64_t discarded_duration_from_front,
int64_t discarded_duration_from_back);
void WriteVideoSamples(int start_index, int samples_to_write);
void WriteEndOfStream(SbMediaType media_type);
// Checks if there are pending callback events and, if so, logs the received
// state update in the appropriate tracking container:
// * |can_accept_more_*_data_| for SbPlayerDecoderState updates.
// * |player_state_set_| for SbPlayerState updates.
// Executes a blocking wait for any new CallbackEvents to be enqueued.
void WaitAndProcessNextEvent(
int64_t timeout = kDefaultWaitForCallbackEventTimeout);
// Waits for |kSbPlayerDecoderStateNeedsData| to be sent.
void WaitForDecoderStateNeedsData(
const int64_t timeout = kDefaultWaitForCallbackEventTimeout);
// Waits for desired player state update to be sent.
void WaitForPlayerState(
const SbPlayerState desired_state,
const int64_t timeout = kDefaultWaitForPlayerStateTimeout);
// When the |output_mode| is decoding to texture, then this method is used to
// advance the decoded frames.
void GetDecodeTargetWhenSupported();
// Determine if the the current event is valid based on previously received
// player state updates, or other inputs to the player.
void AssertPlayerStateIsValid(SbPlayerState state) const;
bool HasReceivedPlayerState(SbPlayerState state) const {
return player_state_set_.find(state) != player_state_set_.end();
}
shared::starboard::ThreadChecker thread_checker_;
const SbPlayerOutputMode output_mode_;
std::string key_system_;
std::string max_video_capabilities_;
scoped_ptr<VideoDmpReader> audio_dmp_reader_;
scoped_ptr<VideoDmpReader> video_dmp_reader_;
testing::FakeGraphicsContextProvider* fake_graphics_context_provider_;
SbPlayer player_ = kSbPlayerInvalid;
SbDrmSystem drm_system_ = kSbDrmSystemInvalid;
// Queue of events from the underlying player.
Queue<CallbackEvent> callback_event_queue_;
// States of if decoder can accept more inputs.
bool can_accept_more_audio_data_ = false;
bool can_accept_more_video_data_ = false;
// The duration of how far past the current playback position we will write
// audio samples, in microseconds.
int64_t audio_write_duration_ = 0;
int64_t last_written_audio_timestamp_ = 0;
// Set of received player state updates from the underlying player. This is
// used to check that the state updates occur in a valid order during normal
// playback.
std::set<SbPlayerState> player_state_set_;
bool destroy_player_called_ = false;
bool audio_end_of_stream_written_ = false;
bool video_end_of_stream_written_ = false;
std::atomic_bool error_occurred_{false};
int ticket_ = SB_PLAYER_INITIAL_TICKET;
};
} // namespace nplb
} // namespace starboard
#endif // STARBOARD_NPLB_PLAYER_TEST_FIXTURE_H_