Import Cobalt 6.14838 Change-Id: I49864fe26f7f6fca3777d185823aa31251e8ae57
diff --git a/src/base/base.gypi b/src/base/base.gypi index fde1a96..79b9dd4 100644 --- a/src/base/base.gypi +++ b/src/base/base.gypi
@@ -661,7 +661,7 @@ 'threading/thread_checker_impl.cc', ], 'conditions': [ - ['target_os!="linux" and target_arch!="android"', { + ['target_os!="linux" and target_os!="android"', { 'sources!': [ # Since wchar_t is 2-bytes wide, string16 == wstring here. 'string16.cc',
diff --git a/src/base/debug/trace_event_impl.cc b/src/base/debug/trace_event_impl.cc index 5af61e5..ac641c3 100644 --- a/src/base/debug/trace_event_impl.cc +++ b/src/base/debug/trace_event_impl.cc
@@ -114,6 +114,7 @@ TraceEvent::TraceEvent(int thread_id, TimeTicks timestamp, + TimeTicks thread_timestamp, char phase, const unsigned char* category_enabled, const char* name, @@ -124,6 +125,7 @@ const unsigned long long* arg_values, unsigned char flags) : timestamp_(timestamp), + thread_timestamp_(thread_timestamp), id_(id), category_enabled_(category_enabled), name_(name), @@ -242,6 +244,7 @@ void TraceEvent::AppendAsJSON(std::string* out) const { int64 time_int64 = timestamp_.ToInternalValue(); + int64 thread_time_int64 = thread_timestamp_.ToInternalValue(); int process_id = TraceLog::GetInstance()->process_id(); // Category name checked at category creation time. DCHECK(!strchr(name_, '"')); @@ -266,6 +269,11 @@ } *out += "}"; + // add thread timestamp only if it was available + if (TimeTicks::HasThreadNow()) { + StringAppendF(out, ",\"tts\":%" PRId64, thread_time_int64); + } + // If id_ is set, print it out as a hex string so we don't loose any // bits (it might be a 64-bit pointer). if (flags_ & TRACE_EVENT_FLAG_HAS_ID) @@ -667,6 +675,7 @@ #endif TimeTicks now = TimeTicks::NowFromSystemTraceTime() - time_offset_; + TimeTicks thread_now = TimeTicks::ThreadNow(); NotificationHelper notifier(this); { AutoLock lock(lock_); @@ -710,7 +719,7 @@ logged_events_.push_back( TraceEvent(thread_id, - now, phase, category_enabled, name, id, + now, thread_now, phase, category_enabled, name, id, num_args, arg_names, arg_types, arg_values, flags)); @@ -795,7 +804,7 @@ trace_event_internal::SetTraceValue(it->second, &arg_type, &arg_value); logged_events_.push_back( TraceEvent(it->first, - TimeTicks(), TRACE_EVENT_PHASE_METADATA, + TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA, &g_category_enabled[g_category_metadata], "thread_name", trace_event_internal::kNoEventId, num_args, &arg_name, &arg_type, &arg_value,
diff --git a/src/base/debug/trace_event_impl.h b/src/base/debug/trace_event_impl.h index b8b4110..6d373c7 100644 --- a/src/base/debug/trace_event_impl.h +++ b/src/base/debug/trace_event_impl.h
@@ -64,6 +64,7 @@ TraceEvent(); TraceEvent(int thread_id, TimeTicks timestamp, + TimeTicks thread_timestamp, char phase, const unsigned char* category_enabled, const char* name, @@ -87,6 +88,7 @@ std::string* out); TimeTicks timestamp() const { return timestamp_; } + TimeTicks thread_timestamp() const { return thread_timestamp_; } // Exposed for unittesting: @@ -107,6 +109,7 @@ private: // Note: these are ordered by size (largest first) for optimal packing. TimeTicks timestamp_; + TimeTicks thread_timestamp_; // id_ can be used to store phase-specific data. unsigned long long id_; TraceValue arg_values_[kTraceMaxNumArgs];
diff --git a/src/base/file_util_proxy_unittest.cc b/src/base/file_util_proxy_unittest.cc index cfa5023..dcd5ad8 100644 --- a/src/base/file_util_proxy_unittest.cc +++ b/src/base/file_util_proxy_unittest.cc
@@ -39,10 +39,18 @@ } void DidFinish(PlatformFileError error) { - error_ = error; + if (error_ == PLATFORM_FILE_OK) { + error_ = error; + } MessageLoop::current()->Quit(); } + void NeedsMoreWork(PlatformFileError error) { + if (error_ == PLATFORM_FILE_OK) { + error_ = error; + } + } + void DidCreateOrOpen(PlatformFileError error, PassPlatformFile file, bool created) { @@ -379,10 +387,8 @@ PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE); FileUtilProxy::Truncate( - file_task_runner(), - file, - 7, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + file_task_runner(), file, 7, + Bind(&FileUtilProxyTest::NeedsMoreWork, weak_factory_.GetWeakPtr())); FileUtilProxy::Flush( file_task_runner(), file, @@ -418,10 +424,8 @@ PlatformFile file = GetTestPlatformFile(PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE); FileUtilProxy::Truncate( - file_task_runner(), - file, - 53, - Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr())); + file_task_runner(), file, 53, + Bind(&FileUtilProxyTest::NeedsMoreWork, weak_factory_.GetWeakPtr())); FileUtilProxy::Flush( file_task_runner(), file,
diff --git a/src/base/threading/thread_local.h b/src/base/threading/thread_local.h index 762d77b..fb6dfb2 100644 --- a/src/base/threading/thread_local.h +++ b/src/base/threading/thread_local.h
@@ -73,7 +73,7 @@ static void AllocateSlot(SlotType& slot); static void FreeSlot(SlotType& slot); - static void* GetValueFromSlot(SlotType& slot); + static void* GetValueFromSlot(const SlotType& slot); static void SetValueInSlot(SlotType& slot, void* value); }; @@ -90,7 +90,7 @@ internal::ThreadLocalPlatform::FreeSlot(slot_); } - Type* Get() { + Type* Get() const { return static_cast<Type*>( internal::ThreadLocalPlatform::GetValueFromSlot(slot_)); } @@ -113,7 +113,7 @@ ThreadLocalBoolean() { } ~ThreadLocalBoolean() { } - bool Get() { + bool Get() const { return tlp_.Get() != NULL; }
diff --git a/src/base/threading/thread_local_posix.cc b/src/base/threading/thread_local_posix.cc index 4951006..1b381c2 100644 --- a/src/base/threading/thread_local_posix.cc +++ b/src/base/threading/thread_local_posix.cc
@@ -25,7 +25,7 @@ } // static -void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { +void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) { return pthread_getspecific(slot); }
diff --git a/src/base/threading/thread_local_starboard.cc b/src/base/threading/thread_local_starboard.cc index e3a96d6..1234425 100644 --- a/src/base/threading/thread_local_starboard.cc +++ b/src/base/threading/thread_local_starboard.cc
@@ -33,7 +33,7 @@ } // static -void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { +void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) { return SbThreadGetLocalValue(slot); }
diff --git a/src/base/threading/thread_local_win.cc b/src/base/threading/thread_local_win.cc index 56d3a3a..7e82c90 100644 --- a/src/base/threading/thread_local_win.cc +++ b/src/base/threading/thread_local_win.cc
@@ -26,7 +26,7 @@ } // static -void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { +void* ThreadLocalPlatform::GetValueFromSlot(const SlotType& slot) { return TlsGetValue(slot); }
diff --git a/src/base/time.h b/src/base/time.h index 38bc53a..9fde0e4 100644 --- a/src/base/time.h +++ b/src/base/time.h
@@ -593,6 +593,15 @@ // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED. static TimeTicks HighResNow(); + // Returns the amount of time the current thread has spent in the execution + // state (i.e. not pre-empted or waiting on some event). If this is not + // available, then HighResNow() will be returned. + static TimeTicks ThreadNow(); + + // Returns whether ThreadNow() is implemented to report thread time (as + // opposed to HighResNow()). + static bool HasThreadNow(); + // Returns the current system trace time or, if none is defined, the current // high-res time (i.e. HighResNow()). On systems where a global trace clock // is defined, timestamping TraceEvents's with this value guarantees
diff --git a/src/base/time_mac.cc b/src/base/time_mac.cc index 883a35b..327f9e6 100644 --- a/src/base/time_mac.cc +++ b/src/base/time_mac.cc
@@ -192,6 +192,16 @@ } // static +TimeTicks TimeTicks::ThreadNow() { + return HighResNow(); +} + +// static +bool TimeTicks::HasThreadNow() { + return false; +} + +// static TimeTicks TimeTicks::NowFromSystemTraceTime() { return HighResNow(); }
diff --git a/src/base/time_posix.cc b/src/base/time_posix.cc index 80b1c4e..d053ff2 100644 --- a/src/base/time_posix.cc +++ b/src/base/time_posix.cc
@@ -267,6 +267,27 @@ return Now(); } +// static +TimeTicks TimeTicks::ThreadNow() { + uint64_t absolute_micro; + + struct timespec ts; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != 0) { + return HighResNow(); + } + + absolute_micro = + (static_cast<int64>(ts.tv_sec) * Time::kMicrosecondsPerSecond) + + (static_cast<int64>(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond); + + return TimeTicks(absolute_micro); +} + +// static +bool TimeTicks::HasThreadNow() { + return true; +} + #if defined(OS_CHROMEOS) // Force definition of the system trace clock; it is a chromeos-only api // at the moment and surfacing it in the right place requires mucking
diff --git a/src/base/time_starboard.cc b/src/base/time_starboard.cc index 2e7785e..0327166 100644 --- a/src/base/time_starboard.cc +++ b/src/base/time_starboard.cc
@@ -97,6 +97,24 @@ } // static +TimeTicks TimeTicks::ThreadNow() { +#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW) + return TimeTicks(SbTimeGetMonotonicThreadNow()); +#else + return HighResNow(); +#endif +} + +// static +bool TimeTicks::HasThreadNow() { +#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW) + return true; +#else + return false; +#endif +} + +// static TimeTicks TimeTicks::NowFromSystemTraceTime() { return HighResNow(); }
diff --git a/src/base/time_win.cc b/src/base/time_win.cc index 6d8e432..294a564 100644 --- a/src/base/time_win.cc +++ b/src/base/time_win.cc
@@ -460,6 +460,16 @@ } // static +TimeTicks TimeTicks::ThreadNow() { + return HighResNow(); +} + +// static +bool TimeTicks::HasThreadNow() { + return false; +} + +// static TimeTicks TimeTicks::NowFromSystemTraceTime() { return HighResNow(); }
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc index ae86fb4..2aa7a62 100644 --- a/src/cobalt/audio/async_audio_decoder.cc +++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -31,8 +31,8 @@ void Decode( const uint8* audio_data, size_t size, const AsyncAudioDecoder::DecodeFinishCallback& decode_finish_callback) { - scoped_ptr<AudioFileReader> reader( - AudioFileReader::TryCreate(audio_data, size)); + scoped_ptr<AudioFileReader> reader(AudioFileReader::TryCreate( + audio_data, size, GetPreferredOutputSampleType())); if (reader) { decode_finish_callback.Run(reader->sample_rate(),
diff --git a/src/cobalt/audio/audio_file_reader.cc b/src/cobalt/audio/audio_file_reader.cc index ed61830..a858789 100644 --- a/src/cobalt/audio/audio_file_reader.cc +++ b/src/cobalt/audio/audio_file_reader.cc
@@ -23,9 +23,10 @@ // static scoped_ptr<AudioFileReader> AudioFileReader::TryCreate(const uint8* data, - size_t size) { + size_t size, + SampleType sample_type) { // Try to create other type of audio file reader. - return AudioFileReaderWAV::TryCreate(data, size).Pass(); + return AudioFileReaderWAV::TryCreate(data, size, sample_type).Pass(); } } // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h index e1eacca..2d35a25 100644 --- a/src/cobalt/audio/audio_file_reader.h +++ b/src/cobalt/audio/audio_file_reader.h
@@ -28,7 +28,8 @@ public: virtual ~AudioFileReader() {} - static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size); + static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size, + SampleType sample_type); // Returns the sample data stored as float sample in planar form. Note that // this function transfers the ownership of the data to the caller so it can
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc index fad6ef0..4edc13f 100644 --- a/src/cobalt/audio/audio_file_reader_wav.cc +++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -46,15 +46,15 @@ } // namespace // static -scoped_ptr<AudioFileReader> AudioFileReaderWAV::TryCreate(const uint8* data, - size_t size) { +scoped_ptr<AudioFileReader> AudioFileReaderWAV::TryCreate( + const uint8* data, size_t size, SampleType sample_type) { // Need at least the |kWAVChunkSize| bytes for this to be a WAV. if (size < kWAVChunkSize) { return scoped_ptr<AudioFileReader>(); } scoped_ptr<AudioFileReaderWAV> audio_file_reader_wav( - new AudioFileReaderWAV(data, size)); + new AudioFileReaderWAV(data, size, sample_type)); if (!audio_file_reader_wav->is_valid()) { return scoped_ptr<AudioFileReader>(); @@ -63,8 +63,12 @@ return make_scoped_ptr<AudioFileReader>(audio_file_reader_wav.release()); } -AudioFileReaderWAV::AudioFileReaderWAV(const uint8* data, size_t size) - : sample_rate_(0.f), number_of_frames_(0), number_of_channels_(0) { +AudioFileReaderWAV::AudioFileReaderWAV(const uint8* data, size_t size, + SampleType sample_type) + : sample_rate_(0.f), + number_of_frames_(0), + number_of_channels_(0), + sample_type_(sample_type) { DCHECK_GE(size, kWAVRIFFChunkHeaderSize); if (ParseRIFFHeader(data, size)) { @@ -179,7 +183,6 @@ static_cast<int32>(is_sample_in_float ? sizeof(float) : sizeof(int16)); number_of_frames_ = static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_)); - sample_type_ = GetPreferredOutputSampleType(); const int32 bytes_per_dest_sample = static_cast<int32>(GetSampleTypeSize(sample_type_)); const bool is_dest_float = sample_type_ == kSampleTypeFloat32;
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h index 06e4883..dc3699a 100644 --- a/src/cobalt/audio/audio_file_reader_wav.h +++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -27,7 +27,8 @@ // http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html class AudioFileReaderWAV : public AudioFileReader { public: - static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size); + static scoped_ptr<AudioFileReader> TryCreate(const uint8* data, size_t size, + SampleType sample_type); scoped_array<uint8> sample_data() OVERRIDE { return sample_data_.Pass(); } float sample_rate() const OVERRIDE { return sample_rate_; } @@ -36,7 +37,7 @@ SampleType sample_type() const OVERRIDE { return sample_type_; } private: - AudioFileReaderWAV(const uint8* data, size_t size); + AudioFileReaderWAV(const uint8* data, size_t size, SampleType sample_type); bool ParseRIFFHeader(const uint8* data, size_t size); void ParseChunks(const uint8* data, size_t size);
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc index 2f8beaf..0911d57 100644 --- a/src/cobalt/browser/browser_module.cc +++ b/src/cobalt/browser/browser_module.cc
@@ -276,7 +276,8 @@ #if defined(ENABLE_FAKE_MICROPHONE) if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kFakeMicrophone) || CommandLine::ForCurrentProcess()->HasSwitch(switches::kInputFuzzer)) { - options.dom_settings_options.enable_fake_microphone = true; + options.dom_settings_options.microphone_options.enable_fake_microphone = + true; } #endif // defined(ENABLE_FAKE_MICROPHONE)
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp index a9747b9..52a802c 100644 --- a/src/cobalt/build/all.gyp +++ b/src/cobalt/build/all.gyp
@@ -59,6 +59,7 @@ '<(DEPTH)/cobalt/samples/samples.gyp:*', '<(DEPTH)/cobalt/script/script.gyp:*', '<(DEPTH)/cobalt/script/engine.gyp:all_engines', + '<(DEPTH)/cobalt/speech/sandbox/sandbox.gyp:*', '<(DEPTH)/cobalt/speech/speech.gyp:*', '<(DEPTH)/cobalt/storage/storage.gyp:*', '<(DEPTH)/cobalt/trace_event/trace_event.gyp:*',
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id index 4868c74..260d24f 100644 --- a/src/cobalt/build/build.id +++ b/src/cobalt/build/build.id
@@ -1 +1 @@ -14197 \ No newline at end of file +14838 \ No newline at end of file
diff --git a/src/cobalt/build/save_build_id.py b/src/cobalt/build/save_build_id.py index 1f79d60..63ab937 100755 --- a/src/cobalt/build/save_build_id.py +++ b/src/cobalt/build/save_build_id.py
@@ -14,13 +14,14 @@ # limitations under the License. """Calculates the current Build ID and writes it to 'build.id'.""" +import argparse import logging import os import sys +import textwrap import gyp_utils - _SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) _BUILD_ID_PATH = gyp_utils.BUILD_ID_PATH @@ -32,12 +33,28 @@ def main(): logging.basicConfig(level=logging.WARNING, format='%(message)s') + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + description=textwrap.dedent(__doc__)) + + parser.add_argument( + '--delete', + '-d', + action='store_true', + default=False, + help='Delete build.id file.') + + options = parser.parse_args() + # Update the build id to the latest, even if one is already set. try: os.unlink(_BUILD_ID_PATH) except OSError: pass + if options.delete: + return 0 + build_id = gyp_utils.GetBuildNumber() if not build_id: logging.error('Unable to retrieve build id.')
diff --git a/src/cobalt/content/ssl/certs/0a775a30.0 b/src/cobalt/content/ssl/certs/0a775a30.0 new file mode 100644 index 0000000..7d540a4 --- /dev/null +++ b/src/cobalt/content/ssl/certs/0a775a30.0
@@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout +736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A +DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk +fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA +njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd +-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/1001acf7.0 b/src/cobalt/content/ssl/certs/1001acf7.0 new file mode 100644 index 0000000..5496703 --- /dev/null +++ b/src/cobalt/content/ssl/certs/1001acf7.0
@@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM +f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX +mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 +zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P +fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc +vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 +Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp +zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO +Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW +k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ +DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF +lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW +Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 +d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z +XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR +gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 +d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv +J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg +DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM ++SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy +F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 +SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws +E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl +-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/626dceaf.0 b/src/cobalt/content/ssl/certs/626dceaf.0 new file mode 100644 index 0000000..984f1d1 --- /dev/null +++ b/src/cobalt/content/ssl/certs/626dceaf.0
@@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH +MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM +QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy +MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl +cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv +CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg +GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu +XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd +re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu +PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 +mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K +8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj +x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR +nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 +kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok +twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV +HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp +8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT +vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT +z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA +pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb +pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB +R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R +RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk +0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC +5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF +izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn +yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC +-----END CERTIFICATE-----
diff --git a/src/cobalt/content/ssl/certs/a3418fda.0 b/src/cobalt/content/ssl/certs/a3418fda.0 new file mode 100644 index 0000000..07372d3 --- /dev/null +++ b/src/cobalt/content/ssl/certs/a3418fda.0
@@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw +CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU +MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw +MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp +Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu +hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l +xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 +CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx +sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== +-----END CERTIFICATE-----
diff --git a/src/cobalt/dom/dom_settings.cc b/src/cobalt/dom/dom_settings.cc index 0db0ac9..e0bb25a 100644 --- a/src/cobalt/dom/dom_settings.cc +++ b/src/cobalt/dom/dom_settings.cc
@@ -29,7 +29,7 @@ script::JavaScriptEngine* engine, script::GlobalEnvironment* global_environment, const Options& options) : max_dom_element_depth_(max_dom_element_depth), - enable_fake_microphone_(options.enable_fake_microphone), + microphone_options_(options.microphone_options), fetcher_factory_(fetcher_factory), network_module_(network_module), window_(window),
diff --git a/src/cobalt/dom/dom_settings.h b/src/cobalt/dom/dom_settings.h index 492d21b..e2099c7 100644 --- a/src/cobalt/dom/dom_settings.h +++ b/src/cobalt/dom/dom_settings.h
@@ -25,6 +25,7 @@ #include "cobalt/dom/window.h" #include "cobalt/media/can_play_type_handler.h" #include "cobalt/script/environment_settings.h" +#include "cobalt/speech/microphone.h" namespace cobalt { @@ -46,10 +47,7 @@ public: // Hold optional settings for DOMSettings. struct Options { - Options() - : array_buffer_allocator(NULL), - array_buffer_cache(NULL), - enable_fake_microphone(false) {} + Options() : array_buffer_allocator(NULL), array_buffer_cache(NULL) {} // ArrayBuffer allocates its memory on the heap by default and ArrayBuffers // may occupy a lot of memory. It is possible to provide an allocator via @@ -60,8 +58,8 @@ // amount of ArrayBuffer inside main memory. So we have provide the // following cache to manage ArrayBuffer in main memory. ArrayBuffer::Cache* array_buffer_cache; - // Use fake microphone if this flag is set to true. - bool enable_fake_microphone; + // Microphone options. + speech::Microphone::Options microphone_options; }; DOMSettings(const int max_dom_element_depth, @@ -77,7 +75,9 @@ ~DOMSettings() OVERRIDE; int max_dom_element_depth() { return max_dom_element_depth_; } - bool enable_fake_microphone() const { return enable_fake_microphone_; } + const speech::Microphone::Options& microphone_options() const { + return microphone_options_; + } void set_window(const scoped_refptr<Window>& window) { window_ = window; } scoped_refptr<Window> window() const { return window_; } @@ -115,7 +115,7 @@ private: const int max_dom_element_depth_; - const bool enable_fake_microphone_; + const speech::Microphone::Options microphone_options_; loader::FetcherFactory* fetcher_factory_; network::NetworkModule* network_module_; scoped_refptr<Window> window_;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc index b94ee93..e4b5178 100644 --- a/src/cobalt/dom/html_script_element.cc +++ b/src/cobalt/dom/html_script_element.cc
@@ -505,7 +505,9 @@ return; } - TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::Execute()"); + TRACE_EVENT2("cobalt::dom", "HTMLScriptElement::Execute()", "file_path", + script_location.file_path, "line_number", + script_location.line_number); // Since error is already handled, it is guaranteed the load is successful. // 1. 2. 3. Not needed by Cobalt.
diff --git a/src/cobalt/dom/url.cc b/src/cobalt/dom/url.cc index 911c619..e7b256e 100644 --- a/src/cobalt/dom/url.cc +++ b/src/cobalt/dom/url.cc
@@ -97,11 +97,18 @@ const char** data, size_t* size) { DCHECK(data); DCHECK(size); + + *size = 0; + *data = NULL; + dom::Blob* blob = registry->Retrieve(url.spec()).get(); if (blob) { *size = static_cast<size_t>(blob->size()); - *data = reinterpret_cast<const char*>(blob->data()); + + if (*size > 0) { + *data = reinterpret_cast<const char*>(blob->data()); + } return true; } else {
diff --git a/src/cobalt/layout/box.cc b/src/cobalt/layout/box.cc index 4a57476..4426498 100644 --- a/src/cobalt/layout/box.cc +++ b/src/cobalt/layout/box.cc
@@ -472,10 +472,17 @@ // 'visibility: visible'. // https://www.w3.org/TR/CSS21/visufx.html#propdef-visibility if (computed_style()->visibility() == cssom::KeywordValue::GetVisible()) { - RenderAndAnimateBackgroundColor( - padding_rounded_corners, &border_node_builder, &animate_node_builder); - RenderAndAnimateBackgroundImage( - padding_rounded_corners, &border_node_builder, &animate_node_builder); + RenderAndAnimateBackgroundImageResult background_image_result = + RenderAndAnimateBackgroundImage(padding_rounded_corners); + // If the background image is opaque, then it will occlude the background + // color and so we do not need to render the background color. + if (!background_image_result.is_opaque) { + RenderAndAnimateBackgroundColor( + padding_rounded_corners, &border_node_builder, &animate_node_builder); + } + if (background_image_result.node) { + border_node_builder.AddChild(background_image_result.node); + } RenderAndAnimateBorder(border_radius_provider.rounded_corners(), &border_node_builder, &animate_node_builder); RenderAndAnimateBoxShadow(border_radius_provider.rounded_corners(), @@ -1066,11 +1073,16 @@ } } -void Box::RenderAndAnimateBackgroundImage( - const base::optional<RoundedCorners>& rounded_corners, - CompositionNode::Builder* border_node_builder, - AnimateNode::Builder* animate_node_builder) { - UNREFERENCED_PARAMETER(animate_node_builder); +Box::RenderAndAnimateBackgroundImageResult Box::RenderAndAnimateBackgroundImage( + const base::optional<RoundedCorners>& rounded_corners) { + RenderAndAnimateBackgroundImageResult result; + // We track a single render tree node because most of the time there will only + // be one. If there is more, we set |single_node| to NULL and instead + // populate |composition|. The code here tries to avoid using CompositionNode + // if possible to avoid constructing an std::vector. + scoped_refptr<render_tree::Node> single_node = NULL; + base::optional<CompositionNode::Builder> composition; + result.is_opaque = false; math::RectF image_frame( math::PointF(border_left_width().toFloat(), border_top_width().toFloat()), @@ -1105,9 +1117,34 @@ background_node = new FilterNode(filter_node_builder); } - border_node_builder->AddChild(background_node); + // If any of the background image layers are opaque, we set that the + // background image is opaque. This is used to avoid setting up the + // background color if the background image is just going to cover it + // anyway. + result.is_opaque |= background_node_provider.is_opaque(); + + // If this is not the first node to return, then our |single_node| + // shortcut won't work, copy that single node into |composition| before + // continuing. + if (single_node) { + composition.emplace(); + composition->AddChild(single_node); + single_node = NULL; + } + if (!composition) { + single_node = background_node; + } else { + composition->AddChild(background_node); + } } } + + if (single_node) { + result.node = single_node; + } else if (composition) { + result.node = new CompositionNode(composition->Pass()); + } + return result; } scoped_refptr<render_tree::Node> Box::RenderAndAnimateOpacity(
diff --git a/src/cobalt/layout/box.h b/src/cobalt/layout/box.h index 676b04d..60e9ccb 100644 --- a/src/cobalt/layout/box.h +++ b/src/cobalt/layout/box.h
@@ -603,10 +603,17 @@ const base::optional<render_tree::RoundedCorners>& rounded_corners, render_tree::CompositionNode::Builder* border_node_builder, render_tree::animations::AnimateNode::Builder* animate_node_builder); - void RenderAndAnimateBackgroundImage( - const base::optional<render_tree::RoundedCorners>& rounded_corners, - render_tree::CompositionNode::Builder* border_node_builder, - render_tree::animations::AnimateNode::Builder* animate_node_builder); + struct RenderAndAnimateBackgroundImageResult { + // The node representing the background image (may be a CompositionNode if + // there are multiple layers). + scoped_refptr<render_tree::Node> node; + // Returns whether the background image opaquely fills the entire frame. + // If true, then we don't need to even consider rendering the background + // color, since it will be occluded by the image. + bool is_opaque; + }; + RenderAndAnimateBackgroundImageResult RenderAndAnimateBackgroundImage( + const base::optional<render_tree::RoundedCorners>& rounded_corners); void RenderAndAnimateBoxShadow( const base::optional<render_tree::RoundedCorners>& rounded_corners, render_tree::CompositionNode::Builder* border_node_builder,
diff --git a/src/cobalt/layout/used_style.cc b/src/cobalt/layout/used_style.cc index f252231..26cc44d 100644 --- a/src/cobalt/layout/used_style.cc +++ b/src/cobalt/layout/used_style.cc
@@ -566,7 +566,8 @@ background_size_(background_size), background_position_(background_position), background_repeat_(background_repeat), - used_style_provider_(used_style_provider) {} + used_style_provider_(used_style_provider), + is_opaque_(false) {} void UsedBackgroundNodeProvider::VisitAbsoluteURL( cssom::AbsoluteURLValue* url_value) { @@ -595,10 +596,16 @@ &used_background_size_provider, &used_background_position_provider, &used_background_repeat_provider, frame_, single_image_size); + math::RectF image_rect(image_transform_data.composition_node_translation, + image_transform_data.image_node_size); + + is_opaque_ = used_background_image->IsOpaque() && + image_rect.x() <= frame_.x() && image_rect.y() <= frame_.y() && + image_rect.right() >= frame_.right() && + image_rect.bottom() >= frame_.bottom(); + background_node_ = new render_tree::ImageNode( - used_background_image, - math::RectF(image_transform_data.composition_node_translation, - image_transform_data.image_node_size), + used_background_image, image_rect, image_transform_data.image_node_transform_matrix); } }
diff --git a/src/cobalt/layout/used_style.h b/src/cobalt/layout/used_style.h index ca32a1c..8c964a1 100644 --- a/src/cobalt/layout/used_style.h +++ b/src/cobalt/layout/used_style.h
@@ -134,6 +134,8 @@ return background_node_; } + bool is_opaque() const { return is_opaque_; } + private: const math::RectF frame_; const scoped_refptr<cssom::PropertyValue> background_size_; @@ -143,6 +145,8 @@ scoped_refptr<render_tree::Node> background_node_; + bool is_opaque_; + DISALLOW_COPY_AND_ASSIGN(UsedBackgroundNodeProvider); };
diff --git a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png new file mode 100644 index 0000000..6418185 --- /dev/null +++ b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color-expected.png Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html new file mode 100644 index 0000000..49defbc --- /dev/null +++ b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-with-opaque-image-color.html
@@ -0,0 +1,49 @@ +<!DOCTYPE html> +<!-- + | Tests that opaque images render correctly and exercises an optimization + | code path where a background color does not need to be rendered if an + | opaque image covers an element's frame instead. + --> +<html> +<head> + <style> + div { + width: 300px; + height: 80px; + background-color: #f88; + background-repeat: no-repeat; + } + </style> + <script> + if (window.testRunner) { + window.testRunner.waitUntilDone(); + } + + var image = new Image(); + var image_name = 'cobalt_opaque.jpg'; + + image.onload = function() { + var image_elements = document.getElementsByClassName('image'); + for (var i = 0; i < image_elements.length; ++i) { + image_elements[i].style.backgroundImage = + 'url(' + image_name + ')'; + } + + if (window.testRunner) { + window.testRunner.notifyDone(); + } + } + + image.src = image_name; + +</script> +</head> +<body> + <div class='image' style="background-repeat: repeat;"></div> + <div class='image' style="width: 100px; background-size: cover;"></div> + <div class='image' style="background-size: contain;"></div> + <!-- Matches the image's dimensions exactly. --> + <div class='image' + style="width: 87px; height: 100px; background-size: contain;"></div> +</body> +</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg b/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg new file mode 100644 index 0000000..a1dc59e --- /dev/null +++ b/src/cobalt/layout_tests/testdata/css3-background/cobalt_opaque.jpg Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt index e5e5ccb..45cffd4 100644 --- a/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt +++ b/src/cobalt/layout_tests/testdata/css3-background/layout_tests.txt
@@ -27,6 +27,7 @@ 14-2-1-background-with-image-repeat-color-position 14-2-1-background-with-image-repeat-position 14-2-1-background-with-image-repeat-position-color +14-2-1-background-with-opaque-image-color 14-2-1-background-with-position-color 14-2-1-background-with-position-color-image 14-2-1-background-with-position-color-image-repeat
diff --git a/src/cobalt/loader/blob_fetcher.h b/src/cobalt/loader/blob_fetcher.h index aa8f991..9b40c3f 100644 --- a/src/cobalt/loader/blob_fetcher.h +++ b/src/cobalt/loader/blob_fetcher.h
@@ -26,10 +26,17 @@ // For fetching the 'blob:' scheme. class BlobFetcher : public Fetcher { public: - // Returns true if the blob is succesfully fetched, and only then writes - // the address of the buffer and its size to |data| and |size|. |data| can - // be NULL when size is 0. This callback avoids a dependency from the - // fetcher to the actual blob implementation. + // This callback avoids a dependency from the fetcher to the actual blob + // implementation. + // If the blob is succesfully fetched: + // 1. Writes the size of its buffer to |size|. + // 2. If |size| > 0, then it also writes the address of the non-empty buffer + // to |data|, otherwise writes NULL to |data|. + // 3. Returns true. + // If the fetch fails: + // 1. Writes 0 to |size| + // 2. Writes NULL to |data| + // 3. Returns false. typedef base::Callback<bool(const GURL& url, const char** data, size_t* size)> ResolverCallback;
diff --git a/src/cobalt/loader/blob_fetcher_test.cc b/src/cobalt/loader/blob_fetcher_test.cc index a1e1fd6..e2baea3 100644 --- a/src/cobalt/loader/blob_fetcher_test.cc +++ b/src/cobalt/loader/blob_fetcher_test.cc
@@ -43,11 +43,18 @@ DCHECK(data); DCHECK(size); + *size = 0; + *data = NULL; + TestRegistry::const_iterator match = registry.find(url.spec()); if (match != registry.end()) { const std::vector<char>& buffer = match->second; *size = buffer.size(); - *data = &buffer[0]; + + if (!buffer.empty()) { + *data = &buffer[0]; + } + return true; } else { return false;
diff --git a/src/cobalt/loader/image/dummy_gif_image_decoder.h b/src/cobalt/loader/image/dummy_gif_image_decoder.h index 5c3775c..3ea5b38 100644 --- a/src/cobalt/loader/image/dummy_gif_image_decoder.h +++ b/src/cobalt/loader/image/dummy_gif_image_decoder.h
@@ -38,11 +38,6 @@ // From ImageDataDecoder std::string GetTypeString() const OVERRIDE { return "DummyGIFImageDecoder"; } - // Returns true if the signature is valid for the particular image type. - static bool IsValidSignature(const uint8* header) { - return !memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6); - } - private: // From ImageDataDecoder size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/image/image_data_decoder.h b/src/cobalt/loader/image/image_data_decoder.h index d97c329..ac19988 100644 --- a/src/cobalt/loader/image/image_data_decoder.h +++ b/src/cobalt/loader/image/image_data_decoder.h
@@ -22,6 +22,9 @@ #include "cobalt/render_tree/image.h" #include "cobalt/render_tree/resource_provider.h" +#if defined(STARBOARD) +#include "starboard/decode_target.h" +#endif namespace cobalt { namespace loader { @@ -44,6 +47,23 @@ return image_data_.Pass(); } +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + // Starboard version 3 adds support for hardware accelerated image decoding. + // In order to make use of this feature, subclasses of ImageDataDecoder may + // override this method in order to return an SbDecodeTarget, rather than a + // render_tree::ImageData, which could potentially save a copy from CPU + // memory to GPU memory, depending on the render_tree implementation and + // hardware image decoding functionality available. If + // |RetrieveSbDecodeTarget| returns any value other than the default + // |kSbDecodeInvalid|, ImageDecoder will interpret it as a preference + // towards SbDecodeTarget rather than render_tree::ImageData. + virtual SbDecodeTarget RetrieveSbDecodeTarget() { + return kSbDecodeTargetInvalid; + } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + void DecodeChunk(const uint8* data, size_t size); // Return true if decoding succeeded. bool FinishWithSuccess();
diff --git a/src/cobalt/loader/image/image_decoder.cc b/src/cobalt/loader/image/image_decoder.cc index 5161dd3..6fcf6ec 100644 --- a/src/cobalt/loader/image/image_decoder.cc +++ b/src/cobalt/loader/image/image_decoder.cc
@@ -20,6 +20,7 @@ #include "base/debug/trace_event.h" #include "cobalt/loader/image/dummy_gif_image_decoder.h" +#include "cobalt/loader/image/image_decoder_starboard.h" #if defined(__LB_PS3__) #include "cobalt/loader/image/jpeg_image_decoder_ps3.h" #else // defined(__LB_PS3__) @@ -30,6 +31,9 @@ #include "cobalt/loader/image/webp_image_decoder.h" #include "net/base/mime_util.h" #include "net/http/http_status_code.h" +#if defined(STARBOARD) +#include "starboard/image.h" +#endif namespace cobalt { namespace loader { @@ -49,6 +53,41 @@ result->append(message); } +// The various types of images we support decoding. +enum ImageType { + kImageTypeInvalid, + kImageTypeGIF, + kImageTypeJPEG, + kImageTypePNG, + kImageTypeWebP, +}; + +// Determine the ImageType of an image from its signature. +ImageType DetermineImageType(const uint8* header) { + if (!memcmp(header, "\xFF\xD8\xFF", 3)) { + return kImageTypeJPEG; + } else if (!memcmp(header, "GIF87a", 6) || !memcmp(header, "GIF89a", 6)) { + return kImageTypeGIF; + } else if (!memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)) { + return kImageTypePNG; + } else if (!memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBPVP", 6)) { + return kImageTypeWebP; + } else { + return kImageTypeInvalid; + } +} + +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) +// clang-format off +SbDecodeTargetFormat kPreferredFormats[] = { + kSbDecodeTargetFormat1PlaneRGBA, + kSbDecodeTargetFormat1PlaneBGRA, +}; +// clang-format on +#endif +#endif + } // namespace ImageDecoder::ImageDecoder(render_tree::ResourceProvider* resource_provider, @@ -91,9 +130,8 @@ CacheMessage(&failure_message_, "No content returned."); } - std::string mime_style; - bool success = headers->GetMimeType(&mime_style); - if (!success || !net::IsSupportedImageMimeType(mime_style)) { + bool success = headers->GetMimeType(&mime_type_); + if (!success || !net::IsSupportedImageMimeType(mime_type_)) { state_ = kNotApplicable; CacheMessage(&failure_message_, "Not an image mime type."); } @@ -118,11 +156,22 @@ case kDecoding: DCHECK(decoder_); if (decoder_->FinishWithSuccess()) { - scoped_ptr<render_tree::ImageData> image_data = - decoder_->RetrieveImageData(); - success_callback_.Run( - image_data ? resource_provider_->CreateImage(image_data.Pass()) - : NULL); +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + SbDecodeTarget target = decoder_->RetrieveSbDecodeTarget(); + if (SbDecodeTargetIsValid(target)) { + success_callback_.Run( + resource_provider_->CreateImageFromSbDecodeTarget(target)); + } else // NOLINT +#endif +#endif + { + scoped_ptr<render_tree::ImageData> image_data = + decoder_->RetrieveImageData(); + success_callback_.Run( + image_data ? resource_provider_->CreateImage(image_data.Pass()) + : NULL); + } } else { error_callback_.Run(decoder_->GetTypeString() + " failed to decode image."); @@ -224,26 +273,50 @@ return false; } + const ImageType image_type = DetermineImageType(signature_cache_.data); + +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + const char* mime_type_c_string = mime_type_.c_str(); + + // Find out if any of our preferred formats are supported for this mime + // type. + SbDecodeTargetFormat format = kSbDecodeTargetFormatInvalid; + for (size_t i = 0; i < SB_ARRAY_SIZE(kPreferredFormats); ++i) { + if (SbImageIsDecodeSupported(mime_type_c_string, kPreferredFormats[i])) { + format = kPreferredFormats[i]; + break; + } + } + + if (SbDecodeTargetIsFormatValid(format) && + resource_provider_->SupportsSbDecodeTarget()) { + decoder_ = make_scoped_ptr<ImageDataDecoder>(new ImageDecoderStarboard( + resource_provider_, mime_type_c_string, format)); + return true; + } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + // Call different types of decoders by matching the image signature. if (s_use_stub_image_decoder) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new StubImageDecoder(resource_provider_)); + } else if (image_type == kImageTypeJPEG) { #if defined(__LB_PS3__) - } else if (JPEGImageDecoderPS3::IsValidSignature(signature_cache_.data)) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new JPEGImageDecoderPS3(resource_provider_)); #else // defined(__LB_PS3__) - } else if (JPEGImageDecoder::IsValidSignature(signature_cache_.data)) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new JPEGImageDecoder(resource_provider_)); #endif // defined(__LB_PS3__) - } else if (PNGImageDecoder::IsValidSignature(signature_cache_.data)) { + } else if (image_type == kImageTypePNG) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new PNGImageDecoder(resource_provider_)); - } else if (WEBPImageDecoder::IsValidSignature(signature_cache_.data)) { + } else if (image_type == kImageTypeWebP) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new WEBPImageDecoder(resource_provider_)); - } else if (DummyGIFImageDecoder::IsValidSignature(signature_cache_.data)) { + } else if (image_type == kImageTypeGIF) { decoder_ = make_scoped_ptr<ImageDataDecoder>( new DummyGIFImageDecoder(resource_provider_)); } else {
diff --git a/src/cobalt/loader/image/image_decoder.h b/src/cobalt/loader/image/image_decoder.h index 610d27b..00902bc 100644 --- a/src/cobalt/loader/image/image_decoder.h +++ b/src/cobalt/loader/image/image_decoder.h
@@ -89,6 +89,7 @@ SignatureCache signature_cache_; State state_; std::string failure_message_; + std::string mime_type_; }; } // namespace image
diff --git a/src/cobalt/loader/image/image_decoder_starboard.cc b/src/cobalt/loader/image/image_decoder_starboard.cc new file mode 100644 index 0000000..5a90442 --- /dev/null +++ b/src/cobalt/loader/image/image_decoder_starboard.cc
@@ -0,0 +1,70 @@ +/* + * Copyright 2016 Google Inc. 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. + */ + +#if defined(STARBOARD) + +#include "cobalt/loader/image/image_decoder_starboard.h" + +#include <algorithm> + +#include "base/debug/trace_event.h" +#include "base/logging.h" +#include "starboard/decode_target.h" +#include "starboard/image.h" + +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + +namespace cobalt { +namespace loader { +namespace image { + +ImageDecoderStarboard::ImageDecoderStarboard( + render_tree::ResourceProvider* resource_provider, const char* mime_type, + SbDecodeTargetFormat format) + : ImageDataDecoder(resource_provider), + mime_type_(mime_type), + format_(format), + provider_(resource_provider->GetSbDecodeTargetProvider()), + target_(kSbDecodeTargetInvalid) {} + +ImageDecoderStarboard::~ImageDecoderStarboard() {} + +size_t ImageDecoderStarboard::DecodeChunkInternal(const uint8* data, + size_t input_byte) { + buffer_.insert(buffer_.end(), data, data + input_byte); + return input_byte; +} + +void ImageDecoderStarboard::FinishInternal() { + DCHECK(!buffer_.empty()); + DCHECK(SbImageIsDecodeSupported(mime_type_, format_)); + target_ = + SbImageDecode(provider_, &buffer_[0], static_cast<int>(buffer_.size()), + mime_type_, format_); + if (SbDecodeTargetIsValid(target_)) { + set_state(kDone); + } else { + set_state(kError); + } +} + +} // namespace image +} // namespace loader +} // namespace cobalt + +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) + +#endif // #if defined(STARBOARD)
diff --git a/src/cobalt/loader/image/image_decoder_starboard.h b/src/cobalt/loader/image/image_decoder_starboard.h new file mode 100644 index 0000000..ac14d4c --- /dev/null +++ b/src/cobalt/loader/image/image_decoder_starboard.h
@@ -0,0 +1,68 @@ +/* + * Copyright 2016 Google Inc. 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 COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_ +#define COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_ + +#if defined(STARBOARD) + +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "cobalt/loader/image/image_data_decoder.h" +#include "starboard/decode_target.h" + +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + +namespace cobalt { +namespace loader { +namespace image { + +class ImageDecoderStarboard : public ImageDataDecoder { + public: + explicit ImageDecoderStarboard( + render_tree::ResourceProvider* resource_provider, const char* mime_type, + SbDecodeTargetFormat format); + ~ImageDecoderStarboard() OVERRIDE; + + // From ImageDataDecoder + std::string GetTypeString() const OVERRIDE { return "ImageDecoderStarboard"; } + + SbDecodeTarget RetrieveSbDecodeTarget() OVERRIDE { return target_; } + + private: + // From ImageDataDecoder + size_t DecodeChunkInternal(const uint8* data, size_t size) OVERRIDE; + void FinishInternal() OVERRIDE; + + const char* mime_type_; + SbDecodeTargetFormat format_; + std::vector<uint8> buffer_; + SbDecodeTargetProvider* provider_; + SbDecodeTarget target_; +}; + +} // namespace image +} // namespace loader +} // namespace cobalt + +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) + +#endif // defined(STARBOARD) + +#endif // COBALT_LOADER_IMAGE_IMAGE_DECODER_STARBOARD_H_
diff --git a/src/cobalt/loader/image/jpeg_image_decoder.h b/src/cobalt/loader/image/jpeg_image_decoder.h index 9d8c487..242fd0e 100644 --- a/src/cobalt/loader/image/jpeg_image_decoder.h +++ b/src/cobalt/loader/image/jpeg_image_decoder.h
@@ -41,11 +41,6 @@ // From ImageDataDecoder std::string GetTypeString() const OVERRIDE { return "JPEGImageDecoder"; } - // Returns true if the signature is valid for the particular image type. - static bool IsValidSignature(const uint8* header) { - return !memcmp(header, "\xFF\xD8\xFF", 3); - } - private: // From ImageDataDecoder size_t DecodeChunkInternal(const uint8* data, size_t size) OVERRIDE;
diff --git a/src/cobalt/loader/image/png_image_decoder.h b/src/cobalt/loader/image/png_image_decoder.h index 29a3887..d270087 100644 --- a/src/cobalt/loader/image/png_image_decoder.h +++ b/src/cobalt/loader/image/png_image_decoder.h
@@ -36,11 +36,6 @@ // From ImageDataDecoder std::string GetTypeString() const OVERRIDE { return "PNGImageDecoder"; } - // Returns true if the signature is valid for the particular image type. - static bool IsValidSignature(const uint8* header) { - return !memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8); - } - private: // From ImageDataDecoder size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/image/webp_image_decoder.h b/src/cobalt/loader/image/webp_image_decoder.h index da9ce59..187ca62 100644 --- a/src/cobalt/loader/image/webp_image_decoder.h +++ b/src/cobalt/loader/image/webp_image_decoder.h
@@ -34,11 +34,6 @@ // From ImageDataDecoder std::string GetTypeString() const OVERRIDE { return "WEBPImageDecoder"; } - // Returns true if the signature is valid for the particular image type. - static bool IsValidSignature(const uint8* header) { - return !memcmp(header, "RIFF", 4) && !memcmp(header + 8, "WEBPVP", 6); - } - private: // From ImageDataDecoder size_t DecodeChunkInternal(const uint8* data, size_t input_byte) OVERRIDE;
diff --git a/src/cobalt/loader/loader.gyp b/src/cobalt/loader/loader.gyp index b80a2cb..f3460d8 100644 --- a/src/cobalt/loader/loader.gyp +++ b/src/cobalt/loader/loader.gyp
@@ -42,6 +42,8 @@ 'image/image_data_decoder.h', 'image/image_decoder.cc', 'image/image_decoder.h', + 'image/image_decoder_starboard.cc', + 'image/image_decoder_starboard.h', 'image/jpeg_image_decoder.cc', 'image/jpeg_image_decoder.h', 'image/png_image_decoder.cc',
diff --git a/src/cobalt/loader/net_fetcher.cc b/src/cobalt/loader/net_fetcher.cc index 1ed1376..efb946e 100644 --- a/src/cobalt/loader/net_fetcher.cc +++ b/src/cobalt/loader/net_fetcher.cc
@@ -81,32 +81,33 @@ const Options& options) : Fetcher(handler), security_callback_(security_callback), - ALLOW_THIS_IN_INITIALIZER_LIST(start_callback_( - base::Bind(&NetFetcher::Start, base::Unretained(this)))) { + ALLOW_THIS_IN_INITIALIZER_LIST(csp_reject_callback_( + base::Bind(&NetFetcher::ProcessCSPReject, base::Unretained(this)))) { url_fetcher_.reset( net::URLFetcher::Create(url, options.request_method, this)); url_fetcher_->SetRequestContext(network_module->url_request_context_getter()); url_fetcher_->DiscardResponse(); - // Delay the actual start until this function is complete. Otherwise we might - // call handler's callbacks at an unexpected time- e.g. receiving OnError() - // while a loader is still being constructed. - MessageLoop::current()->PostTask(FROM_HERE, start_callback_.callback()); -} - -void NetFetcher::Start() { - DCHECK(thread_checker_.CalledOnValidThread()); const GURL& original_url = url_fetcher_->GetOriginalURL(); if (security_callback_.is_null() || security_callback_.Run(original_url, false /* did not redirect */)) { url_fetcher_->Start(); } else { - std::string msg(base::StringPrintf("URL %s rejected by security policy.", - original_url.spec().c_str())); - return HandleError(msg).InvalidateThis(); + // Delay the callback until this function is complete. Otherwise we might + // call handler's callbacks at an unexpected time- e.g. receiving OnError() + // while a loader is still being constructed. + MessageLoop::current()->PostTask(FROM_HERE, + csp_reject_callback_.callback()); } } +void NetFetcher::ProcessCSPReject() { + const GURL& original_url = url_fetcher_->GetOriginalURL(); + std::string msg(base::StringPrintf("URL %s rejected by security policy.", + original_url.spec().c_str())); + return HandleError(msg).InvalidateThis(); +} + void NetFetcher::OnURLFetchResponseStarted(const net::URLFetcher* source) { DCHECK(thread_checker_.CalledOnValidThread()); if (source->GetURL() != source->GetOriginalURL()) { @@ -162,7 +163,7 @@ NetFetcher::~NetFetcher() { DCHECK(thread_checker_.CalledOnValidThread()); - start_callback_.Cancel(); + csp_reject_callback_.Cancel(); } NetFetcher::ReturnWrapper NetFetcher::HandleError(const std::string& message) {
diff --git a/src/cobalt/loader/net_fetcher.h b/src/cobalt/loader/net_fetcher.h index ce816b5..1d6bc5d 100644 --- a/src/cobalt/loader/net_fetcher.h +++ b/src/cobalt/loader/net_fetcher.h
@@ -59,7 +59,7 @@ net::URLFetcher* url_fetcher() const { return url_fetcher_.get(); } private: - void Start(); + void ProcessCSPReject(); // Empty struct to ensure the caller of |HandleError()| knows that |this| // may have been destroyed and handles it appropriately. @@ -81,9 +81,9 @@ base::ThreadChecker thread_checker_; scoped_ptr<net::URLFetcher> url_fetcher_; csp::SecurityCallback security_callback_; - // Ensure we can cancel any in-flight Start() task if we are destroyed - // after being constructed, but before Start() runs. - base::CancelableClosure start_callback_; + // Ensure we can cancel any in-flight ProcessCSPReject() task if we are + // destroyed after being constructed, but before ProcessCSPReject() runs. + base::CancelableClosure csp_reject_callback_; DISALLOW_COPY_AND_ASSIGN(NetFetcher); };
diff --git a/src/cobalt/render_tree/mock_resource_provider.h b/src/cobalt/render_tree/mock_resource_provider.h index 219a980..f51cba3 100644 --- a/src/cobalt/render_tree/mock_resource_provider.h +++ b/src/cobalt/render_tree/mock_resource_provider.h
@@ -83,6 +83,20 @@ scoped_refptr<Image> CreateImage(scoped_ptr<ImageData> pixel_data) { return scoped_refptr<Image>(CreateImageMock(pixel_data.get())); } + +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + scoped_refptr<Image> CreateImageFromSbDecodeTarget(SbDecodeTarget target) { + UNREFERENCED_PARAMETER(target); + return NULL; + } + + SbDecodeTargetProvider* GetSbDecodeTargetProvider() { return NULL; } + + bool SupportsSbDecodeTarget() { return false; } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes, size_t alignment) { return scoped_ptr<RawImageMemory>(
diff --git a/src/cobalt/render_tree/resource_provider.h b/src/cobalt/render_tree/resource_provider.h index c1bd3cb..4590121 100644 --- a/src/cobalt/render_tree/resource_provider.h +++ b/src/cobalt/render_tree/resource_provider.h
@@ -27,6 +27,9 @@ #include "cobalt/render_tree/glyph_buffer.h" #include "cobalt/render_tree/image.h" #include "cobalt/render_tree/typeface.h" +#if defined(STARBOARD) +#include "starboard/decode_target.h" +#endif // defined(STARBOARD) namespace cobalt { namespace render_tree { @@ -72,6 +75,23 @@ virtual scoped_refptr<Image> CreateImage( scoped_ptr<ImageData> pixel_data) = 0; +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + // This function will consume an SbDecodeTarget object produced by + // SbDecodeTargetCreate(), wrap it in a render_tree::Image that can be used + // in a render tree, and return it to the caller. + virtual scoped_refptr<Image> CreateImageFromSbDecodeTarget( + SbDecodeTarget target) = 0; + + // Return the associated SbDecodeTargetProvider with the ResourceProvider, + // if it exists. Returns NULL if SbDecodeTarget is not supported. + virtual SbDecodeTargetProvider* GetSbDecodeTargetProvider() = 0; + + // Whether SbDecodeTargetIsSupported or not. + virtual bool SupportsSbDecodeTarget() = 0; +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + // Returns a raw chunk of memory that can later be passed into a function like // CreateMultiPlaneImageFromRawMemory() in order to create a texture. // If possible, the memory returned will be GPU memory that can be directly
diff --git a/src/cobalt/render_tree/resource_provider_stub.h b/src/cobalt/render_tree/resource_provider_stub.h index bf0a8bd..d40787f 100644 --- a/src/cobalt/render_tree/resource_provider_stub.h +++ b/src/cobalt/render_tree/resource_provider_stub.h
@@ -187,6 +187,21 @@ return make_scoped_refptr(new ImageStub(skia_source_data.Pass())); } +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + scoped_refptr<Image> CreateImageFromSbDecodeTarget( + SbDecodeTarget decode_target) OVERRIDE { + NOTREACHED(); + SbDecodeTargetDestroy(decode_target); + return NULL; + } + + SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; } + + bool SupportsSbDecodeTarget() OVERRIDE { return false; } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + scoped_ptr<RawImageMemory> AllocateRawImageMemory(size_t size_in_bytes, size_t alignment) OVERRIDE { return scoped_ptr<RawImageMemory>(
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.cc b/src/cobalt/renderer/rasterizer/blitter/image.cc index bd82e11..af46382 100644 --- a/src/cobalt/renderer/rasterizer/blitter/image.cc +++ b/src/cobalt/renderer/rasterizer/blitter/image.cc
@@ -69,6 +69,16 @@ render_tree::kAlphaFormatOpaque; } +SinglePlaneImage::SinglePlaneImage(SbBlitterSurface surface, bool is_opaque) + : surface_(surface), is_opaque_(is_opaque) { + CHECK(SbBlitterIsSurfaceValid(surface_)); + SbBlitterSurfaceInfo info; + if (!SbBlitterGetSurfaceInfo(surface_, &info)) { + NOTREACHED(); + } + size_ = math::Size(info.width, info.height); +} + bool SinglePlaneImage::EnsureInitialized() { return false; } const SkBitmap& SinglePlaneImage::GetBitmap() const {
diff --git a/src/cobalt/renderer/rasterizer/blitter/image.h b/src/cobalt/renderer/rasterizer/blitter/image.h index 949e3a2..65229ab 100644 --- a/src/cobalt/renderer/rasterizer/blitter/image.h +++ b/src/cobalt/renderer/rasterizer/blitter/image.h
@@ -71,6 +71,7 @@ class SinglePlaneImage : public skia::SinglePlaneImage { public: explicit SinglePlaneImage(scoped_ptr<ImageData> image_data); + SinglePlaneImage(SbBlitterSurface surface, bool is_opaque); const math::Size& GetSize() const OVERRIDE { return size_; }
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc index f518c52..b23aa15 100644 --- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc +++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.cc
@@ -68,6 +68,31 @@ return make_scoped_refptr(new SinglePlaneImage(blitter_source_data.Pass())); } +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + +scoped_refptr<render_tree::Image> +ResourceProvider::CreateImageFromSbDecodeTarget(SbDecodeTarget decode_target) { + SbDecodeTargetFormat format = SbDecodeTargetGetFormat(decode_target); + if (format == kSbDecodeTargetFormat1PlaneRGBA) { + SbBlitterSurface surface = + SbDecodeTargetGetPlane(decode_target, kSbDecodeTargetPlaneRGBA); + DCHECK(SbBlitterIsSurfaceValid(surface)); + bool is_opaque = SbDecodeTargetIsOpaque(decode_target); + + // Now that we have the surface it contained, we are free to delete + // |decode_target|. + SbDecodeTargetDestroy(decode_target); + return make_scoped_refptr(new SinglePlaneImage(surface, is_opaque)); + } + + NOTREACHED() + << "Only format kSbDecodeTargetFormat1PlaneRGBA is currently supported."; + SbDecodeTargetDestroy(decode_target); + return NULL; +} + +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) + scoped_ptr<render_tree::RawImageMemory> ResourceProvider::AllocateRawImageMemory(size_t size_in_bytes, size_t alignment) {
diff --git a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h index a7edd28..c0ae7ab 100644 --- a/src/cobalt/renderer/rasterizer/blitter/resource_provider.h +++ b/src/cobalt/renderer/rasterizer/blitter/resource_provider.h
@@ -25,6 +25,7 @@ #include "cobalt/render_tree/image.h" #include "cobalt/render_tree/resource_provider.h" #include "starboard/blitter.h" +#include "starboard/decode_target.h" #if SB_HAS(BLITTER) @@ -42,6 +43,15 @@ void Finish() OVERRIDE {} +#if SB_VERSION(3) + scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget( + SbDecodeTarget decode_target) OVERRIDE; + + SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; } + + bool SupportsSbDecodeTarget() OVERRIDE { return true; } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) + bool PixelFormatSupported(render_tree::PixelFormat pixel_format) OVERRIDE; bool AlphaFormatSupported(render_tree::AlphaFormat alpha_format) OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc index aaadf4b..2fd11a4 100644 --- a/src/cobalt/renderer/rasterizer/pixel_test.cc +++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -463,7 +463,7 @@ TestTree(new ImageNode(image)); } -TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNone) { +TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatOpaque) { scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat( GetResourceProvider(), output_surface_size(), render_tree::kAlphaFormatOpaque); @@ -471,7 +471,7 @@ TestTree(new ImageNode(image)); } -TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatNoneAndRoundedCorners) { +TEST_F(PixelTest, SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners) { scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat( GetResourceProvider(), output_surface_size(), render_tree::kAlphaFormatOpaque); @@ -481,6 +481,35 @@ new ImageNode(image))); } +TEST_F(PixelTest, + SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor) { + scoped_refptr<Image> image = CreateColoredCheckersImageForAlphaFormat( + GetResourceProvider(), output_surface_size(), + render_tree::kAlphaFormatOpaque); + + CompositionNode::Builder builder; + builder.AddChild(new RectNode( + RectF(output_surface_size()), + scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(0.0, 1.0, 0.0, 1))))); + builder.AddChild(new FilterNode( + ViewportFilter(RectF(25, 25, 150, 150), RoundedCorners(75, 75)), + new ImageNode(image))); + TestTree(new CompositionNode(builder.Pass())); +} + +TEST_F(PixelTest, RectWithRoundedCornersOnSolidColor) { + CompositionNode::Builder builder; + builder.AddChild(new RectNode( + RectF(output_surface_size()), + scoped_ptr<Brush>(new SolidColorBrush(ColorRGBA(0.0, 1.0, 0.0, 1))))); + builder.AddChild(new FilterNode( + ViewportFilter(RectF(25, 25, 150, 150), RoundedCorners(75, 75)), + new RectNode(RectF(output_surface_size()), + scoped_ptr<Brush>( + new SolidColorBrush(ColorRGBA(0.0, 0.0, 1.0, 1)))))); + TestTree(new CompositionNode(builder.Pass())); +} + TEST_F(PixelTest, SingleRGBAImageLargerThanRenderTarget) { // Tests that rasterizing images that are larger than the render target // work as expected (e.g. they are cropped).
diff --git a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h index 8d85fea..1d18280 100644 --- a/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h +++ b/src/cobalt/renderer/rasterizer/skia/hardware_resource_provider.h
@@ -51,6 +51,27 @@ scoped_refptr<render_tree::Image> CreateImage( scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE; +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + + scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget( + SbDecodeTarget decode_target) OVERRIDE { + NOTREACHED() + << "CreateImageFromSbDecodeTarget is not supported on EGL yet."; + SbDecodeTargetDestroy(decode_target); + return NULL; + } + + // Return the associated SbDecodeTargetProvider with the ResourceProvider, + // if it exists. Returns NULL if SbDecodeTarget is not supported. + SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; } + + // Whether SbDecodeTargetIsSupported or not. + bool SupportsSbDecodeTarget() OVERRIDE { return false; } + +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory( size_t size_in_bytes, size_t alignment) OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc index 06ed04b..9e2116d 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor.cc
@@ -189,7 +189,7 @@ } void ApplyViewportMask( - SkCanvas* canvas, + RenderTreeNodeVisitorDrawState* draw_state, const base::optional<render_tree::ViewportFilter>& filter) { if (!filter) { return; @@ -197,11 +197,12 @@ if (!filter->has_rounded_corners()) { SkRect filter_viewport(CobaltRectFToSkiaRect(filter->viewport())); - canvas->clipRect(filter_viewport); + draw_state->render_target->clipRect(filter_viewport); } else { - canvas->clipPath( + draw_state->render_target->clipPath( RoundedRectToSkiaPath(filter->viewport(), filter->rounded_corners()), SkRegion::kIntersect_Op, true /* doAntiAlias */); + draw_state->clip_is_rect = false; } } @@ -287,7 +288,7 @@ // the offscreen surface, so reset the scale for now. draw_state_.render_target->save(); ApplyBlurFilterToPaint(&paint, filter_node.blur_filter); - ApplyViewportMask(draw_state_.render_target, filter_node.viewport_filter); + ApplyViewportMask(&draw_state_, filter_node.viewport_filter); draw_state_.render_target->setMatrix(CobaltMatrixToSkia( math::TranslateMatrix(coord_mapping.output_pre_translate) * total_matrix * @@ -452,13 +453,17 @@ RenderTreeNodeVisitorDrawState original_draw_state(draw_state_); draw_state_.render_target->save(); - ApplyViewportMask(draw_state_.render_target, - filter_node->data().viewport_filter); + // Remember the value of |clip_is_rect| because ApplyViewportMask may + // modify it. + bool clip_was_rect = draw_state_.clip_is_rect; + ApplyViewportMask(&draw_state_, filter_node->data().viewport_filter); if (filter_node->data().opacity_filter) { draw_state_.opacity *= filter_node->data().opacity_filter->opacity(); } filter_node->data().source->Accept(this); + + draw_state_.clip_is_rect = clip_was_rect; draw_state_.render_target->restore(); draw_state_ = original_draw_state; } else { @@ -511,7 +516,7 @@ if (draw_state.opacity < 1.0f) { paint.setAlpha(draw_state.opacity * 255); - } else if (is_opaque) { + } else if (is_opaque && draw_state.clip_is_rect) { paint.setXfermodeMode(SkXfermode::kSrc_Mode); }
diff --git a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h index 01a5d62..d3cb0fe 100644 --- a/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h +++ b/src/cobalt/renderer/rasterizer/skia/render_tree_node_visitor_draw_state.h
@@ -26,10 +26,14 @@ struct RenderTreeNodeVisitorDrawState { explicit RenderTreeNodeVisitorDrawState(SkCanvas* render_target) - : render_target(render_target), opacity(1.0f) {} + : render_target(render_target), opacity(1.0f), clip_is_rect(true) {} SkCanvas* render_target; float opacity; + + // True if the current clip is a rectangle or not. If it is not, we need + // to enable blending when rendering clipped rectangles. + bool clip_is_rect; }; } // namespace skia
diff --git a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h index 6b979ce..9528e22 100644 --- a/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h +++ b/src/cobalt/renderer/rasterizer/skia/software_resource_provider.h
@@ -46,6 +46,21 @@ scoped_refptr<render_tree::Image> CreateImage( scoped_ptr<render_tree::ImageData> pixel_data) OVERRIDE; +#if defined(STARBOARD) +#if SB_VERSION(3) && SB_HAS(GRAPHICS) + scoped_refptr<render_tree::Image> CreateImageFromSbDecodeTarget( + SbDecodeTarget decode_target) OVERRIDE { + NOTREACHED(); + SbDecodeTargetDestroy(decode_target); + return NULL; + } + + SbDecodeTargetProvider* GetSbDecodeTargetProvider() OVERRIDE { return NULL; } + + bool SupportsSbDecodeTarget() OVERRIDE { return false; } +#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // defined(STARBOARD) + scoped_ptr<render_tree::RawImageMemory> AllocateRawImageMemory( size_t size_in_bytes, size_t alignment) OVERRIDE;
diff --git a/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png b/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png new file mode 100644 index 0000000..e72e8d1 --- /dev/null +++ b/src/cobalt/renderer/rasterizer/testdata/RectWithRoundedCornersOnSolidColor-expected.png Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png new file mode 100644 index 0000000..b8638eb --- /dev/null +++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaque-expected.png Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png new file mode 100644 index 0000000..2076087 --- /dev/null +++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCorners-expected.png Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png new file mode 100644 index 0000000..6251d5f --- /dev/null +++ b/src/cobalt/renderer/rasterizer/testdata/SingleRGBAImageWithAlphaFormatOpaqueAndRoundedCornersOnSolidColor-expected.png Binary files differ
diff --git a/src/cobalt/script/mozjs/mozjs.gyp b/src/cobalt/script/mozjs/mozjs.gyp index 6e856f5..7e833e4 100644 --- a/src/cobalt/script/mozjs/mozjs.gyp +++ b/src/cobalt/script/mozjs/mozjs.gyp
@@ -26,6 +26,7 @@ 'mozjs_global_environment.cc', 'mozjs_property_enumerator.cc', 'mozjs_source_code.cc', + 'mozjs_trace_logging.cc', 'opaque_root_tracker.cc', 'proxy_handler.cc', 'referenced_object_map.cc',
diff --git a/src/cobalt/script/mozjs/mozjs_trace_logging.cc b/src/cobalt/script/mozjs/mozjs_trace_logging.cc new file mode 100644 index 0000000..2e11ce8 --- /dev/null +++ b/src/cobalt/script/mozjs/mozjs_trace_logging.cc
@@ -0,0 +1,157 @@ +/* + * Copyright 2016 Google Inc. 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 "third_party/mozjs/js/src/TraceLogging.h" + +#include "base/debug/trace_event.h" +#include "base/memory/singleton.h" + +namespace js { + +namespace { + +// Container class to transform the 3rd-party-declared TraceLogging class +// into our Singleton type. +class TraceLoggingContainer { + public: + TraceLoggingContainer() {} + + static TraceLoggingContainer* GetInstance() { + return Singleton<TraceLoggingContainer, + StaticMemorySingletonTraits<TraceLoggingContainer> >::get(); + } + + TraceLogging* GetLogger() { + return &logger_; + } + + private: + TraceLogging logger_; + + DISALLOW_COPY_AND_ASSIGN(TraceLoggingContainer); +}; + +const char kTraceLoggingCategory[] = "JavaScript"; + +} // namespace + +const char* const TraceLogging::type_name[] = { + "IonCompile", + "IonCompile", + "IonCannon", + "IonCannon", + "IonCannon", + "IonSideCannon", + "IonSideCannon", + "IonSideCannon", + "YarrJIT", + "YarrJIT", + "JMSafepoint", + "JMSafepoint", + "JMNormal", + "JMNormal", + "JMCompile", + "JMCompile", + "GarbageCollect", + "GarbageCollect", + "Interpreter", + "Interpreter", + "Info", +}; + +// None of the member variables will be needed. Instead, logging will be +// funneled to the trace event system. +TraceLogging::TraceLogging() { +} + +TraceLogging::~TraceLogging() { +} + +// static +TraceLogging* TraceLogging::defaultLogger() { + return TraceLoggingContainer::GetInstance()->GetLogger(); +} + +// static +void TraceLogging::releaseDefaultLogger() { + // Do nothing. Lifetime is managed by the TraceLoggingContainer singleton. +} + +void TraceLogging::log(Type type, const char* filename, unsigned int line) { + // Unfortunately, we don't have access to the enum, so can't declare a + // "count"-type enum. Instead, INFO is assumed to be the last enumeration. + COMPILE_ASSERT(ARRAYSIZE_UNSAFE(type_name)-1 == Type::INFO, array_mismatch); + + switch (type) { + // "Start" types + case ION_COMPILE_START: + case ION_CANNON_START: + case ION_SIDE_CANNON_START: + case YARR_JIT_START: + case JM_SAFEPOINT_START: + case JM_START: + case JM_COMPILE_START: + case GC_START: + case INTERPRETER_START: + if (filename != NULL) { + TRACE_EVENT_BEGIN2(kTraceLoggingCategory, type_name[type], + "file", TRACE_STR_COPY(filename), + "line", line); + } else { + TRACE_EVENT_BEGIN0(kTraceLoggingCategory, type_name[type]); + } + break; + + // Ignored types + case INFO: + break; + + // "Stop" types + default: + TRACE_EVENT_END0(kTraceLoggingCategory, type_name[type]); + break; + } +} + +void TraceLogging::log(Type type, JSScript* script) { + log(type, script->filename(), script->lineno); +} + +void TraceLogging::log(const char* log) { + UNREFERENCED_PARAMETER(log); +} + +void TraceLogging::log(Type type) { + log(type, NULL, 0); +} + +void TraceLogging::flush() { +} + +// Helper functions declared in TraceLogging.h +void TraceLog(TraceLogging* logger, TraceLogging::Type type, JSScript* script) { + logger->log(type, script); +} + +void TraceLog(TraceLogging* logger, const char* log) { + logger->log(log); +} + +void TraceLog(TraceLogging* logger, TraceLogging::Type type) { + logger->log(type); +} + +} // namespace js
diff --git a/src/cobalt/speech/endpointer/endpointer.cc b/src/cobalt/speech/endpointer/endpointer.cc index 0b19a22..cabad1a 100644 --- a/src/cobalt/speech/endpointer/endpointer.cc +++ b/src/cobalt/speech/endpointer/endpointer.cc
@@ -93,7 +93,6 @@ EpStatus Endpointer::ProcessAudio( const ShellAudioBus& audio_bus, float* rms_out) { DCHECK_EQ(audio_bus.channels(), 1); - DCHECK_LE(kFrameSize, static_cast<int>(audio_bus.frames())); const size_t num_samples = audio_bus.frames(); const int16_t* audio_data = NULL;
diff --git a/src/cobalt/speech/microphone.h b/src/cobalt/speech/microphone.h index 3b9e6ae..4c0d87b 100644 --- a/src/cobalt/speech/microphone.h +++ b/src/cobalt/speech/microphone.h
@@ -19,7 +19,9 @@ #include <string> +#include "base/file_path.h" #include "base/memory/scoped_ptr.h" +#include "base/optional.h" namespace cobalt { namespace speech { @@ -27,6 +29,24 @@ // An abstract class is used for interacting platform specific microphone. class Microphone { public: + struct Options { + Options() + : enable_fake_microphone(false), + external_audio_data(NULL), + audio_data_size(0) {} + + // Use fake microphone if this flag is set to true. + bool enable_fake_microphone; + // The following members are only applicable to the fake microphone. + // + // External audio input data for microphone input. + const uint8* external_audio_data; + // Audio data size. + int audio_data_size; + // Input file path. + base::optional<FilePath> file_path; + }; + virtual ~Microphone() {} // Opens microphone port and starts recording audio.
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc index 7ee601c..0dad830 100644 --- a/src/cobalt/speech/microphone_fake.cc +++ b/src/cobalt/speech/microphone_fake.cc
@@ -23,6 +23,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/rand_util.h" +#include "cobalt/audio/audio_file_reader.h" #include "starboard/file.h" #include "starboard/memory.h" #include "starboard/time.h" @@ -31,14 +32,14 @@ namespace speech { namespace { -const int kMaxBufferSize = 512 * 1024; +const int kMaxBufferSize = 1024 * 1024; const int kMinMicrophoneReadInBytes = 1024; // The possiblity of microphone creation failed is 1/20. const int kCreationRange = 20; // The possiblity of microphone open failed is 1/20. const int kOpenRange = 20; -// The possiblity of microphone read failed is 1/200. -const int kReadRange = 200; +// The possiblity of microphone read failed is 1/300. +const int kReadRange = 300; // The possiblity of microphone close failed is 1/20. const int kCloseRange = 20; const int kFailureNumber = 5; @@ -49,8 +50,9 @@ } // namespace -MicrophoneFake::MicrophoneFake() +MicrophoneFake::MicrophoneFake(const Options& options) : Microphone(), + read_data_from_file_(options.audio_data_size == 0), file_length_(-1), read_index_(0), is_valid_(!ShouldFail(kCreationRange)) { @@ -59,46 +61,91 @@ return; } - FilePath audio_files_path; - SB_CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path)); - audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt")) - .Append(FILE_PATH_LITERAL("speech")) - .Append(FILE_PATH_LITERAL("testdata")); + if (read_data_from_file_) { + if (options.file_path) { + SB_DCHECK(!options.file_path->empty()); + // External input file. + file_paths_.push_back(options.file_path.value()); + } else { + FilePath audio_files_path; + SB_CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &audio_files_path)); + audio_files_path = audio_files_path.Append(FILE_PATH_LITERAL("cobalt")) + .Append(FILE_PATH_LITERAL("speech")) + .Append(FILE_PATH_LITERAL("testdata")); - file_util::FileEnumerator file_enumerator(audio_files_path, - false /* Not recursive */, - file_util::FileEnumerator::FILES); - for (FilePath next = file_enumerator.Next(); !next.empty(); - next = file_enumerator.Next()) { - file_paths_.push_back(next); + file_util::FileEnumerator file_enumerator( + audio_files_path, false /* Not recursive */, + file_util::FileEnumerator::FILES); + for (FilePath next = file_enumerator.Next(); !next.empty(); + next = file_enumerator.Next()) { + file_paths_.push_back(next); + } + } + } else { + file_length_ = std::min(options.audio_data_size, kMaxBufferSize); + SB_DCHECK(file_length_ > 0); + file_buffer_.reset(new uint8[file_length_]); + SbMemoryCopy(file_buffer_.get(), options.external_audio_data, file_length_); } } bool MicrophoneFake::Open() { + SB_DCHECK(thread_checker_.CalledOnValidThread()); + if (ShouldFail(kOpenRange)) { SB_DLOG(WARNING) << "Mocking microphone open failed."; return false; } - SB_DCHECK(file_paths_.size() != 0); - uint64 random_index = base::RandGenerator(file_paths_.size()); - starboard::ScopedFile file(file_paths_[random_index].value().c_str(), - kSbFileOpenOnly | kSbFileRead, NULL, NULL); - SB_DCHECK(file.IsValid()); - int file_buffer_size = - std::min(static_cast<int>(file.GetSize()), kMaxBufferSize); - SB_DCHECK(file_buffer_size > 0); - file_buffer_.reset(new char[file_buffer_size]); - int read_bytes = file.ReadAll(file_buffer_.get(), file_buffer_size); - if (read_bytes < 0) { - return false; - } + if (read_data_from_file_) { + // Read from local files. + SB_DCHECK(file_paths_.size() != 0); + uint64 random_index = base::RandGenerator(file_paths_.size()); + starboard::ScopedFile file(file_paths_[random_index].value().c_str(), + kSbFileOpenOnly | kSbFileRead, NULL, NULL); + SB_DCHECK(file.IsValid()); + int file_buffer_size = + std::min(static_cast<int>(file.GetSize()), kMaxBufferSize); + SB_DCHECK(file_buffer_size > 0); - file_length_ = read_bytes; + scoped_array<char> audio_input(new char[file_buffer_size]); + int read_bytes = file.ReadAll(audio_input.get(), file_buffer_size); + if (read_bytes < 0) { + return false; + } + + scoped_ptr<audio::AudioFileReader> reader(audio::AudioFileReader::TryCreate( + reinterpret_cast<const uint8*>(audio_input.get()), file_buffer_size, + audio::kSampleTypeInt16)); + const float kSupportedSampleRate = 16000.0f; + const int kSupportedMonoChannel = 1; + if (!reader) { + // If it is not a WAV file, read audio data as raw audio. + file_buffer_.reset(new uint8[file_buffer_size]); + SbMemoryCopy(file_buffer_.get(), audio_input.get(), file_buffer_size); + file_length_ = file_buffer_size; + } else if (reader->sample_type() != ::media::ShellAudioBus::kInt16 || + reader->sample_rate() != kSupportedSampleRate || + reader->number_of_channels() != kSupportedMonoChannel) { + // If it is a WAV file but it doesn't meet the audio input criteria, treat + // it as an error. + return false; + } else { + // Read WAV PCM16 audio data. + int size = + static_cast<int>(reader->number_of_frames() * + audio::GetSampleTypeSize(reader->sample_type())); + file_buffer_.reset(new uint8[size]); + SbMemoryCopy(file_buffer_.get(), reader->sample_data().get(), size); + file_length_ = size; + } + } return true; } int MicrophoneFake::Read(char* out_data, int data_size) { + SB_DCHECK(thread_checker_.CalledOnValidThread()); + if (ShouldFail(kReadRange)) { SB_DLOG(WARNING) << "Mocking microphone read failed."; return -1; @@ -115,8 +162,13 @@ } bool MicrophoneFake::Close() { - file_buffer_.reset(); - file_length_ = -1; + SB_DCHECK(thread_checker_.CalledOnValidThread()); + + if (read_data_from_file_) { + file_buffer_.reset(); + file_length_ = -1; + } + read_index_ = 0; if (ShouldFail(kCloseRange)) {
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h index dd17c1c..068eca3 100644 --- a/src/cobalt/speech/microphone_fake.h +++ b/src/cobalt/speech/microphone_fake.h
@@ -24,8 +24,10 @@ #include <string> #include <vector> +#include "base/callback.h" #include "base/file_path.h" #include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" #include "cobalt/speech/microphone.h" namespace cobalt { @@ -35,7 +37,7 @@ // audio. class MicrophoneFake : public Microphone { public: - MicrophoneFake(); + explicit MicrophoneFake(const Options& options); ~MicrophoneFake() SB_OVERRIDE {} bool Open() SB_OVERRIDE; @@ -45,8 +47,11 @@ bool IsValid() SB_OVERRIDE { return is_valid_; } private: + base::ThreadChecker thread_checker_; + + bool read_data_from_file_; std::vector<FilePath> file_paths_; - scoped_array<char> file_buffer_; + scoped_array<uint8> file_buffer_; int file_length_; int read_index_; bool is_valid_;
diff --git a/src/cobalt/speech/microphone_manager.cc b/src/cobalt/speech/microphone_manager.cc index 7b491d9..5d8210d 100644 --- a/src/cobalt/speech/microphone_manager.cc +++ b/src/cobalt/speech/microphone_manager.cc
@@ -38,21 +38,21 @@ const DataReceivedCallback& data_received, const CompletionCallback& completion, const ErrorCallback& error, - bool enable_fake_microphone) + const Microphone::Options& options) : sample_rate_(sample_rate), data_received_callback_(data_received), completion_callback_(completion), error_callback_(error), #if defined(ENABLE_FAKE_MICROPHONE) - enable_fake_microphone_(enable_fake_microphone), + microphone_options_(options), #endif // defined(ENABLE_FAKE_MICROPHONE) state_(kStopped), thread_("microphone_thread") { UNREFERENCED_PARAMETER(sample_rate_); #if defined(ENABLE_FAKE_MICROPHONE) - UNREFERENCED_PARAMETER(enable_fake_microphone_); + UNREFERENCED_PARAMETER(microphone_options_); #else - UNREFERENCED_PARAMETER(enable_fake_microphone); + UNREFERENCED_PARAMETER(options); #endif // defined(ENABLE_FAKE_MICROPHONE) thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); } @@ -84,8 +84,8 @@ #if defined(SB_USE_SB_MICROPHONE) #if defined(ENABLE_FAKE_MICROPHONE) - if (enable_fake_microphone_) { - microphone_.reset(new MicrophoneFake()); + if (microphone_options_.enable_fake_microphone) { + microphone_.reset(new MicrophoneFake(microphone_options_)); } else { microphone_.reset( new MicrophoneStarboard(sample_rate_, kBufferSizeInBytes)); @@ -185,6 +185,7 @@ microphone_.reset(); state_ = kStopped; + poll_mic_events_timer_ = base::nullopt; } } // namespace speech
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h index e91ca8c..c07c21a 100644 --- a/src/cobalt/speech/microphone_manager.h +++ b/src/cobalt/speech/microphone_manager.h
@@ -41,7 +41,8 @@ MicrophoneManager(int sample_rate, const DataReceivedCallback& data_received, const CompletionCallback& completion, - const ErrorCallback& error, bool enable_fake_microphone); + const ErrorCallback& error, + const Microphone::Options& options); ~MicrophoneManager(); @@ -71,7 +72,7 @@ scoped_ptr<Microphone> microphone_; #if defined(ENABLE_FAKE_MICROPHONE) - bool enable_fake_microphone_; + Microphone::Options microphone_options_; #endif // defined(ENABLE_FAKE_MICROPHONE) // Microphone state.
diff --git a/src/cobalt/speech/sandbox/audio_loader.cc b/src/cobalt/speech/sandbox/audio_loader.cc new file mode 100644 index 0000000..1f83132 --- /dev/null +++ b/src/cobalt/speech/sandbox/audio_loader.cc
@@ -0,0 +1,105 @@ +/* + * Copyright 2016 Google Inc. 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 "cobalt/speech/sandbox/audio_loader.h" + +#include <vector> + +namespace cobalt { +namespace speech { +namespace sandbox { + +namespace { +class DummyDecoder : public loader::Decoder { + public: + typedef base::Callback<void(const uint8*, int)> DoneCallback; + + explicit DummyDecoder(const DoneCallback& done_callback) + : done_callback_(done_callback) {} + ~DummyDecoder() OVERRIDE {} + + // This function is used for binding callback for creating DummyDecoder. + static scoped_ptr<Decoder> Create(const DoneCallback& done_callback) { + return scoped_ptr<Decoder>(new DummyDecoder(done_callback)); + } + + // From Decoder. + void DecodeChunk(const char* data, size_t size) OVERRIDE { + DCHECK(thread_checker_.CalledOnValidThread()); + + // Because we load data into memory, set a maximum buffer size. + const int kMaxBufferSize = 1024 * 1024; + if (buffer_.size() + size > kMaxBufferSize) { + return; + } + buffer_.insert(buffer_.end(), data, data + size); + } + + void Finish() OVERRIDE { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (buffer_.size() == 0) { + // No data loaded. + done_callback_.Run(NULL, 0); + return; + } + + done_callback_.Run(reinterpret_cast<uint8*>(&buffer_[0]), buffer_.size()); + } + bool Suspend() OVERRIDE { + NOTIMPLEMENTED(); + return false; + } + void Resume(render_tree::ResourceProvider* /*resource_provider*/) OVERRIDE { + NOTIMPLEMENTED(); + } + + private: + base::ThreadChecker thread_checker_; + std::vector<char> buffer_; + DoneCallback done_callback_; +}; +} // namespace + +AudioLoader::AudioLoader(const GURL& url, + network::NetworkModule* network_module, + const DoneCallback& callback) + : network_module_(network_module), done_callback_(callback) { + DCHECK(url.is_valid()); + DCHECK(!callback.is_null()); + + fetcher_factory_.reset(new loader::FetcherFactory(network_module_)); + loader_ = make_scoped_ptr(new loader::Loader( + base::Bind(&loader::FetcherFactory::CreateFetcher, + base::Unretained(fetcher_factory_.get()), url), + scoped_ptr<loader::Decoder>(new DummyDecoder( + base::Bind(&AudioLoader::OnLoadingDone, base::Unretained(this)))), + base::Bind(&AudioLoader::OnLoadingError, base::Unretained(this)))); +} + +AudioLoader::~AudioLoader() {} + +void AudioLoader::OnLoadingDone(const uint8* data, int size) { + done_callback_.Run(data, size); +} + +void AudioLoader::OnLoadingError(const std::string& error) { + DLOG(WARNING) << "OnLoadingError with error message: " << error; +} + +} // namespace sandbox +} // namespace speech +} // namespace cobalt
diff --git a/src/cobalt/speech/sandbox/audio_loader.h b/src/cobalt/speech/sandbox/audio_loader.h new file mode 100644 index 0000000..921713e --- /dev/null +++ b/src/cobalt/speech/sandbox/audio_loader.h
@@ -0,0 +1,52 @@ +/* + * Copyright 2016 Google Inc. 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 COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_ +#define COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_ + +#include <string> + +#include "base/callback.h" +#include "cobalt/loader/fetcher_factory.h" +#include "cobalt/loader/loader.h" +#include "cobalt/network/network_module.h" + +namespace cobalt { +namespace speech { +namespace sandbox { + +class AudioLoader { + public: + typedef base::Callback<void(const uint8*, int)> DoneCallback; + AudioLoader(const GURL& url, network::NetworkModule* network_module, + const DoneCallback& callback); + ~AudioLoader(); + + private: + void OnLoadingDone(const uint8* data, int size); + void OnLoadingError(const std::string& error); + + const DoneCallback done_callback_; + network::NetworkModule* network_module_; + scoped_ptr<loader::FetcherFactory> fetcher_factory_; + scoped_ptr<loader::Loader> loader_; +}; + +} // namespace sandbox +} // namespace speech +} // namespace cobalt + +#endif // COBALT_SPEECH_SANDBOX_AUDIO_LOADER_H_
diff --git a/src/cobalt/speech/sandbox/sandbox.gyp b/src/cobalt/speech/sandbox/sandbox.gyp new file mode 100644 index 0000000..82f36b4 --- /dev/null +++ b/src/cobalt/speech/sandbox/sandbox.gyp
@@ -0,0 +1,54 @@ +# Copyright 2016 Google Inc. 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. + +# This is a sample sandbox application for experimenting with the Cobalt +# Speech API. + +{ + 'targets': [ + { + 'target_name': 'speech_sandbox', + 'type': '<(final_executable_type)', + 'sources': [ + 'audio_loader.cc', + 'audio_loader.h', + 'speech_sandbox.cc', + 'speech_sandbox.h', + 'speech_sandbox_main.cc', + ], + 'dependencies': [ + '<(DEPTH)/cobalt/audio/audio.gyp:audio', + '<(DEPTH)/cobalt/base/base.gyp:base', + '<(DEPTH)/cobalt/loader/loader.gyp:loader', + '<(DEPTH)/cobalt/network/network.gyp:network', + '<(DEPTH)/cobalt/script/engine.gyp:engine', + '<(DEPTH)/cobalt/speech/speech.gyp:speech', + '<(DEPTH)/cobalt/trace_event/trace_event.gyp:trace_event', + '<(DEPTH)/googleurl/googleurl.gyp:googleurl', + ], + }, + + { + 'target_name': 'speech_sandbox_deploy', + 'type': 'none', + 'dependencies': [ + 'speech_sandbox', + ], + 'variables': { + 'executable_name': 'speech_sandbox', + }, + 'includes': [ '../../../starboard/build/deploy.gypi' ], + }, + ], +}
diff --git a/src/cobalt/speech/sandbox/speech_sandbox.cc b/src/cobalt/speech/sandbox/speech_sandbox.cc new file mode 100644 index 0000000..b0946b6 --- /dev/null +++ b/src/cobalt/speech/sandbox/speech_sandbox.cc
@@ -0,0 +1,100 @@ +/* + * Copyright 2016 Google Inc. 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 "cobalt/speech/sandbox/speech_sandbox.h" + +#include "base/path_service.h" +#include "cobalt/script/environment_settings.h" + +namespace cobalt { +namespace speech { +namespace sandbox { + +namespace { +// The maximum number of element depth in the DOM tree. Elements at a level +// deeper than this could be discarded, and will not be rendered. +const int kDOMMaxElementDepth = 32; +} // namespace + +SpeechSandbox::SpeechSandbox(const std::string& file_path_string, + const FilePath& trace_log_path) { + trace_to_file_.reset(new trace_event::ScopedTraceToFile(trace_log_path)); + + network::NetworkModule::Options network_options; + network_options.require_https = false; + network_module_.reset(new network::NetworkModule(network_options)); + + GURL url(file_path_string); + if (url.is_valid()) { + audio_loader_.reset(new AudioLoader( + url, network_module_.get(), + base::Bind(&SpeechSandbox::OnLoadingDone, base::Unretained(this)))); + } else { + FilePath file_path(file_path_string); + if (!file_path.IsAbsolute()) { + FilePath exe_path; + PathService::Get(base::FILE_EXE, &exe_path); + DCHECK(exe_path.IsAbsolute()); + std::string exe_path_string(exe_path.value()); + std::size_t found = exe_path_string.find_last_of("/\\"); + DCHECK_NE(found, std::string::npos); + // Find the executable directory. Using exe_path.DirName() doesn't work + // on Windows based platforms due to the path is mixed with "/" and "\". + exe_path_string = exe_path_string.substr(0, found); + file_path = FilePath(exe_path_string).Append(file_path_string); + DCHECK(file_path.IsAbsolute()); + } + + dom::DOMSettings::Options dom_settings_options; + dom_settings_options.microphone_options.enable_fake_microphone = true; + dom_settings_options.microphone_options.file_path = file_path; + + StartRecognition(dom_settings_options); + } +} + +SpeechSandbox::~SpeechSandbox() { + if (speech_recognition_) { + speech_recognition_->Stop(); + } +} + +void SpeechSandbox::StartRecognition( + const dom::DOMSettings::Options& dom_settings_options) { + scoped_ptr<script::EnvironmentSettings> environment_settings( + new dom::DOMSettings(kDOMMaxElementDepth, NULL, network_module_.get(), + NULL, NULL, NULL, NULL, NULL, NULL, + dom_settings_options)); + DCHECK(environment_settings); + + speech_recognition_ = new SpeechRecognition(environment_settings.get()); + speech_recognition_->set_continuous(true); + speech_recognition_->set_interim_results(true); + speech_recognition_->Start(NULL); +} + +void SpeechSandbox::OnLoadingDone(const uint8* data, int size) { + dom::DOMSettings::Options dom_settings_options; + dom_settings_options.microphone_options.enable_fake_microphone = true; + dom_settings_options.microphone_options.external_audio_data = data; + dom_settings_options.microphone_options.audio_data_size = size; + + StartRecognition(dom_settings_options); +} + +} // namespace sandbox +} // namespace speech +} // namespace cobalt
diff --git a/src/cobalt/speech/sandbox/speech_sandbox.h b/src/cobalt/speech/sandbox/speech_sandbox.h new file mode 100644 index 0000000..15bbd60 --- /dev/null +++ b/src/cobalt/speech/sandbox/speech_sandbox.h
@@ -0,0 +1,60 @@ +/* + * Copyright 2016 Google Inc. 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 COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_ +#define COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_ + +#include <string> + +#include "base/file_path.h" +#include "base/threading/thread.h" +#include "cobalt/dom/dom_settings.h" +#include "cobalt/speech/sandbox/audio_loader.h" +#include "cobalt/speech/speech_recognition.h" +#include "cobalt/trace_event/scoped_trace_to_file.h" + +namespace cobalt { +namespace speech { +namespace sandbox { + +// This class is for speech sandbox application to experiment with voice search. +// It takes a wav audio as audio input for a fake microphone, and starts/stops +// speech recognition. +class SpeechSandbox { + public: + // The constructor takes a file path string for an audio input and a log path + // for tracing. + SpeechSandbox(const std::string& file_path_string, + const FilePath& trace_log_path); + ~SpeechSandbox(); + + private: + void StartRecognition(const dom::DOMSettings::Options& dom_settings_options); + void OnLoadingDone(const uint8* data, int size); + + scoped_ptr<trace_event::ScopedTraceToFile> trace_to_file_; + scoped_ptr<network::NetworkModule> network_module_; + scoped_ptr<AudioLoader> audio_loader_; + scoped_refptr<SpeechRecognition> speech_recognition_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SpeechSandbox); +}; + +} // namespace sandbox +} // namespace speech +} // namespace cobalt + +#endif // COBALT_SPEECH_SANDBOX_SPEECH_SANDBOX_H_
diff --git a/src/cobalt/speech/sandbox/speech_sandbox_main.cc b/src/cobalt/speech/sandbox/speech_sandbox_main.cc new file mode 100644 index 0000000..2cbb874 --- /dev/null +++ b/src/cobalt/speech/sandbox/speech_sandbox_main.cc
@@ -0,0 +1,68 @@ +/* + * Copyright 2016 Google Inc. 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 "base/file_path.h" +#include "base/path_service.h" +#include "base/string_number_conversions.h" +#include "cobalt/base/wrap_main.h" +#include "cobalt/speech/sandbox/speech_sandbox.h" +#include "googleurl/src/gurl.h" + +namespace cobalt { +namespace speech { +namespace sandbox { + +SpeechSandbox* g_speech_sandbox = NULL; + +// The application takes an audio url or path, and a timeout in second. +// The timeout is optional. If it is not set or set to 0, the application +// doesn't shut down. +void StartApplication(int argc, char** argv, const char* /*link*/, + const base::Closure& quit_closure) { + if (argc != 3 && argc != 2) { + LOG(ERROR) << "Usage: " << argv[0] + << " <audio url|path> [timeout in seconds]"; + return; + } + + int timeout = 0; + if (argc == 3) { + base::StringToInt(argv[2], &timeout); + } + + DCHECK(!g_speech_sandbox); + g_speech_sandbox = new SpeechSandbox( + std::string(argv[1]), + FilePath(FILE_PATH_LITERAL("speech_sandbox_trace.json"))); + + if (timeout != 0) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, quit_closure, base::TimeDelta::FromSeconds(timeout)); + } +} + +void StopApplication() { + DCHECK(g_speech_sandbox); + delete g_speech_sandbox; + g_speech_sandbox = NULL; +} + +} // namespace sandbox +} // namespace speech +} // namespace cobalt + +COBALT_WRAP_BASE_MAIN(cobalt::speech::sandbox::StartApplication, + cobalt::speech::sandbox::StopApplication);
diff --git a/src/cobalt/speech/speech_recognition.cc b/src/cobalt/speech/speech_recognition.cc index 8ea7c49..06deca0 100644 --- a/src/cobalt/speech/speech_recognition.cc +++ b/src/cobalt/speech/speech_recognition.cc
@@ -28,12 +28,11 @@ SpeechRecognition::SpeechRecognition(script::EnvironmentSettings* settings) : ALLOW_THIS_IN_INITIALIZER_LIST( manager_(base::polymorphic_downcast<dom::DOMSettings*>(settings) - ->fetcher_factory() ->network_module(), base::Bind(&SpeechRecognition::OnEventAvailable, base::Unretained(this)), base::polymorphic_downcast<dom::DOMSettings*>(settings) - ->enable_fake_microphone())), + ->microphone_options())), config_("" /*lang*/, false /*continuous*/, false /*interim_results*/, 1 /*max alternatives*/) {}
diff --git a/src/cobalt/speech/speech_recognition_manager.cc b/src/cobalt/speech/speech_recognition_manager.cc index 114d374..0b4ffdf 100644 --- a/src/cobalt/speech/speech_recognition_manager.cc +++ b/src/cobalt/speech/speech_recognition_manager.cc
@@ -19,6 +19,7 @@ #include "base/bind.h" #include "cobalt/base/tokens.h" #include "cobalt/dom/dom_exception.h" +#include "cobalt/speech/microphone_manager.h" namespace cobalt { namespace speech { @@ -30,7 +31,7 @@ SpeechRecognitionManager::SpeechRecognitionManager( network::NetworkModule* network_module, const EventCallback& event_callback, - bool enable_fake_microphone) + const Microphone::Options& microphone_options) : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), weak_this_(weak_ptr_factory_.GetWeakPtr()), main_message_loop_(base::MessageLoopProxy::current()), @@ -39,14 +40,14 @@ recognizer_(network_module, base::Bind(&SpeechRecognitionManager::OnRecognizerEvent, base::Unretained(this)))), - ALLOW_THIS_IN_INITIALIZER_LIST(microphone_manager_( + ALLOW_THIS_IN_INITIALIZER_LIST(microphone_manager_(new MicrophoneManager( kSampleRate, base::Bind(&SpeechRecognitionManager::OnDataReceived, base::Unretained(this)), base::Bind(&SpeechRecognitionManager::OnDataCompletion, base::Unretained(this)), base::Bind(&SpeechRecognitionManager::OnMicError, base::Unretained(this)), - enable_fake_microphone)), + microphone_options))), endpointer_delegate_(kSampleRate), state_(kStopped) {} @@ -65,7 +66,7 @@ } recognizer_.Start(config, kSampleRate); - microphone_manager_.Open(); + microphone_manager_->Open(); endpointer_delegate_.Start(); state_ = kStarted; } @@ -80,7 +81,7 @@ } endpointer_delegate_.Stop(); - microphone_manager_.Close(); + microphone_manager_->Close(); recognizer_.Stop(); state_ = kStopped; event_callback_.Run(new dom::Event(base::Tokens::soundend())); @@ -96,7 +97,7 @@ } endpointer_delegate_.Stop(); - microphone_manager_.Close(); + microphone_manager_->Close(); recognizer_.Stop(); state_ = kAborted; event_callback_.Run(new dom::Event(base::Tokens::soundend()));
diff --git a/src/cobalt/speech/speech_recognition_manager.h b/src/cobalt/speech/speech_recognition_manager.h index 9367a10..6f0faa7 100644 --- a/src/cobalt/speech/speech_recognition_manager.h +++ b/src/cobalt/speech/speech_recognition_manager.h
@@ -22,7 +22,7 @@ #include "cobalt/network/network_module.h" #include "cobalt/script/exception_state.h" #include "cobalt/speech/endpointer_delegate.h" -#include "cobalt/speech/microphone_manager.h" +#include "cobalt/speech/microphone.h" #include "cobalt/speech/speech_configuration.h" #include "cobalt/speech/speech_recognition_config.h" #include "cobalt/speech/speech_recognition_error.h" @@ -33,6 +33,8 @@ namespace cobalt { namespace speech { +class MicrophoneManager; + // Owned by SpeechRecognition to manage major speech recognition logic. // This class interacts with microphone, speech recognition service and audio // encoder. It provides the interface to start/stop microphone and @@ -45,7 +47,7 @@ SpeechRecognitionManager(network::NetworkModule* network_module, const EventCallback& event_callback, - bool enable_fake_microphone); + const Microphone::Options& microphone_options); ~SpeechRecognitionManager(); // Start/Stop speech recognizer and microphone. Multiple calls would be @@ -80,7 +82,7 @@ EventCallback event_callback_; SpeechRecognizer recognizer_; - MicrophoneManager microphone_manager_; + scoped_ptr<MicrophoneManager> microphone_manager_; // Delegate of endpointer which is used for detecting sound energy. EndPointerDelegate endpointer_delegate_;
diff --git a/src/cobalt/speech/speech_recognizer.cc b/src/cobalt/speech/speech_recognizer.cc index d7ad5dd..27eb43a 100644 --- a/src/cobalt/speech/speech_recognizer.cc +++ b/src/cobalt/speech/speech_recognizer.cc
@@ -32,7 +32,7 @@ #include "net/url_request/url_fetcher.h" #if defined(SB_USE_SB_MICROPHONE) -#include "starboard/microphone.h" +#include "starboard/system.h" #endif // defined(SB_USE_SB_MICROPHONE) namespace cobalt { @@ -274,12 +274,18 @@ up_url = AppendQueryParameter(up_url, "pair", pair); up_url = AppendQueryParameter(up_url, "output", "pb"); - const char* speech_api_key = NULL; -#if defined(SB_USE_SB_MICROPHONE) - speech_api_key = SbMicrophoneGetSpeechApiKey(); -#else - speech_api_key = ""; -#endif + const char* speech_api_key = ""; +#if defined(OS_STARBOARD) +#if SB_VERSION(2) + const int kSpeechApiKeyLength = 100; + char buffer[kSpeechApiKeyLength] = {0}; + bool result = SbSystemGetProperty(kSbSystemPropertySpeechApiKey, buffer, + SB_ARRAY_SIZE_INT(buffer)); + SB_DCHECK(result); + speech_api_key = result ? buffer : ""; +#endif // SB_VERSION(2) +#endif // defined(OS_STARBOARD) + up_url = AppendQueryParameter(up_url, "key", speech_api_key); // Language is required. If no language is specified, use the system language.
diff --git a/src/cobalt/webdriver/server.cc b/src/cobalt/webdriver/server.cc index 94ad4ca..ab7b3eb 100644 --- a/src/cobalt/webdriver/server.cc +++ b/src/cobalt/webdriver/server.cc
@@ -179,10 +179,10 @@ WebDriverServer::WebDriverServer(int port, const HandleRequestCallback& callback) : handle_request_callback_(callback) { - LOG(INFO) << "Starting WebDriver server on port " << port; // Create http server factory_.reset(new net::TCPListenSocketFactory("0.0.0.0", port)); server_ = new net::HttpServer(*factory_, this); + LOG(INFO) << "Starting WebDriver server on port " << port; } void WebDriverServer::OnHttpRequest(int connection_id,
diff --git a/src/cobalt/webdriver_benchmarks/README.md b/src/cobalt/webdriver_benchmarks/README.md new file mode 100644 index 0000000..da77083 --- /dev/null +++ b/src/cobalt/webdriver_benchmarks/README.md
@@ -0,0 +1,3 @@ +Framework for webdriver-driven benchmarks. + +Please see tests/README.md
diff --git a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py index cc7547c..cd03ee2 100755 --- a/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py +++ b/src/cobalt/webdriver_benchmarks/partial_layout_benchmark.py
@@ -13,7 +13,6 @@ import inspect import os import re -import socket import sys import thread import threading @@ -142,12 +141,13 @@ # potentially from thread.interrupt_main(). We will treat as # a timeout regardless raise TimeoutException + return self def __exit__(self, exc_type, exc_value, traceback): # The unittest module terminates with a SystemExit # If this is a successful exit, then this is a successful run - success = exc_type is None or (exc_type is SystemExit - and not exc_value.code) + success = exc_type is None or (exc_type is SystemExit and + not exc_value.code) self.SetShouldExit(failed=not success) self.thread.join(COBALT_EXIT_TIMEOUT_SECONDS) @@ -163,7 +163,7 @@ return port = match.group(1) - sys.stderr.write("WebDriver port opened:" + port + "\n") + print("WebDriver port opened:" + port + "\n", file=self.log_file) self._StartWebdriver(port) def SetShouldExit(self, failed=False): @@ -180,7 +180,7 @@ url = "http://{}:{}/".format(self._GetIPAddress(), port) self.webdriver = self.selenium_webdriver_module.Remote( url, COBALT_WEBDRIVER_CAPABILITIES) - sys.stderr.write("Selenium Connected\n") + print("Selenium Connected\n", file=self.log_file) _webdriver = self.webdriver self.test_script_started.set() @@ -189,7 +189,7 @@ if not self.test_script_started.wait(STARTUP_TIMEOUT_SECONDS): self.SetShouldExit(failed=True) raise TimeoutException - sys.stderr.write("Cobalt started\n") + print("Cobalt started", file=self.log_file) def Run(self): """Thread run routine.""" @@ -205,12 +205,14 @@ self.log_file = sys.stdout self.launcher.SetOutputFile(self.log_file) - sys.stderr.write("Running launcher \n") + print("Running launcher", file=self.log_file) self.launcher.Run() - sys.stderr.write("Cobalt terminated. failed: " + str(self.failed) + "\n") + print( + "Cobalt terminated. failed: " + str(self.failed), file=self.log_file) # This is watched for in webdriver_benchmark_test.py if not self.failed: sys.stdout.write("partial_layout_benchmark TEST COMPLETE\n") + # pylint: disable=broad-except except Exception as ex: print("Exception running Cobalt " + str(ex), file=sys.stderr) finally: @@ -255,13 +257,11 @@ if executable is None: executable = GetCobaltExecutablePath(platform, args.config) - devkit_name = args.devkit_name - if devkit_name is None: - devkit_name = socket.gethostname() - try: - with CobaltRunner(platform, executable, devkit_name, args.log_file): - unittest.main() + with CobaltRunner(platform, executable, args.devkit_name, + args.log_file) as runner: + unittest.main(testRunner=unittest.TextTestRunner( + verbosity=0, stream=runner.log_file)) return 0 except TimeoutException: print("Timeout waiting for Cobalt to start", file=sys.stderr)
diff --git a/src/cobalt/webdriver_benchmarks/test_tv_testcase.py b/src/cobalt/webdriver_benchmarks/test_tv_testcase.py new file mode 100755 index 0000000..742fa66 --- /dev/null +++ b/src/cobalt/webdriver_benchmarks/test_tv_testcase.py
@@ -0,0 +1,116 @@ +#!/usr/bin/python2 +# Copyright 2016 Google Inc. 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. +# ============================================================================== +"""Unit tests for tv_testcase.py.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import sys +import unittest + +# This directory is a package +sys.path.insert(0, os.path.abspath('.')) +# pylint: disable=C6203,C6203 +# pylint: disable=g-import-not-at-top +import tv_testcase + + +class MedianPercentileTest(unittest.TestCase): + + def test_empty_case(self): + self.assertEqual(tv_testcase._percentile([], 50), None) + + def test_one_item(self): + self.assertEqual(tv_testcase._percentile([1], 50), 1) + + def test_two_items(self): + self.assertAlmostEqual(tv_testcase._percentile([4, 1], 50), 2.5) + + def test_three_items(self): + self.assertAlmostEqual(tv_testcase._percentile([4, -4, -2], 50), -2) + + def test_four_items(self): + self.assertAlmostEqual(tv_testcase._percentile([4, 0, 1, -2], 50), 0.5) + + +class PercentileTest(unittest.TestCase): + + def test_one_item(self): + self.assertEqual(tv_testcase._percentile([1], 10), 1) + self.assertEqual(tv_testcase._percentile([2], 50), 2) + self.assertEqual(tv_testcase._percentile([3], 90), 3) + + def test_two_items(self): + self.assertEqual(tv_testcase._percentile([2, 1], 10), 1.1) + self.assertEqual(tv_testcase._percentile([2, 3], 50), 2.5) + self.assertEqual(tv_testcase._percentile([3, 4], 90), 3.9) + self.assertEqual(tv_testcase._percentile([3, 4], 100), 4) + + def test_five_items(self): + self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 10), 1.4) + self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 50), 3) + self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 90), 4.6) + self.assertEqual(tv_testcase._percentile([2, 1, 3, 4, 5], 100), 5) + + +class MergeDictTest(unittest.TestCase): + + def test_empty_merge(self): + a = {'x': 4} + b = {} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': 4}) + + def test_merge_into_empty(self): + a = {} + b = {'x': 4} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': 4}) + + def test_merge_non_overlapping_item(self): + a = {'x': 4} + b = {'y': 5} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': 4, 'y': 5}) + + def test_overlapping_item_with_item(self): + a = {'x': 4} + b = {'x': 5} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': [4, 5]}) + + def test_overlapping_list_with_item(self): + a = {'x': [4]} + b = {'x': 5} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': [4, 5]}) + + def test_overlapping_list_with_list(self): + a = {'x': [4]} + b = {'x': [5]} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': [4, 5]}) + + def test_overlapping_item_with_list(self): + a = {'x': 4} + b = {'x': [5]} + tv_testcase._merge_dict(a, b) + self.assertEqual(a, {'x': [4, 5]}) + + +if __name__ == '__main__': + sys.exit(unittest.main())
diff --git a/src/cobalt/webdriver_benchmarks/tests/README.md b/src/cobalt/webdriver_benchmarks/tests/README.md new file mode 100644 index 0000000..a8e30ad --- /dev/null +++ b/src/cobalt/webdriver_benchmarks/tests/README.md
@@ -0,0 +1,29 @@ +Cobalt Webdriver-driven Benchmarks +--------------------- + +This directory contains a set of webdriver-driven benchmarks +for Cobalt. + +Each file should contain a set of tests in Python "unittest" format. + +All tests in all of the files in this directory will be run on the build system. +Results can be recorded in the build results database. + +To run an individual test, simply execute a script directly (or run +all of them via "all.py"). Platform configuration will be inferred from +the environment if set. Otherwise, it must be specified via commandline +parameters. + +To make a new test: + + 1. If appropriate, create a new file borrowing the boilerplate from + an existing simple file, such as "shelf.py" + + 2. If this file contains internal names or details, consider adding it + to the "EXCLUDE.FILES" list. + + 3. Use the `record_result*` methods in the `tv_testcase.TvTestCase` base + class where appropriate. + + 4. Results must be added to the build results database schema. See + the internal "README-Updating-Result-Schema.md" file
diff --git a/src/cobalt/webdriver_benchmarks/tests/guide.py b/src/cobalt/webdriver_benchmarks/tests/guide.py index 324daef..5c0a011 100755 --- a/src/cobalt/webdriver_benchmarks/tests/guide.py +++ b/src/cobalt/webdriver_benchmarks/tests/guide.py
@@ -38,7 +38,7 @@ self.wait_for_layout_complete_after_focused_shelf() layout_times_us.append(self.get_keyup_layout_duration_us()) - self.record_results("GuideTest.test_simple", layout_times_us) + self.record_result_percentile("guideTestLayout95thUs", layout_times_us, 95) if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/shelf.py b/src/cobalt/webdriver_benchmarks/tests/shelf.py index ce058e0..c73255b 100755 --- a/src/cobalt/webdriver_benchmarks/tests/shelf.py +++ b/src/cobalt/webdriver_benchmarks/tests/shelf.py
@@ -40,7 +40,8 @@ self.wait_for_layout_complete_after_focused_shelf() layout_times_us.append(self.get_keyup_layout_duration_us()) - self.record_results("ShelfTest.test_simple", layout_times_us) + self.record_result_percentile("webdriverBenchmarkShelfLayout95thUsec", + layout_times_us, 95) if __name__ == "__main__":
diff --git a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py index b1ede63..e180362 100755 --- a/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py +++ b/src/cobalt/webdriver_benchmarks/tests/time_to_shelf.py
@@ -51,9 +51,10 @@ startup_time_microseconds = t1 - t0 metrics_array.append(startup_time_microseconds) - self.record_results('TimeToShelf.test_time_shelf_display', metrics_array) - self.record_results('TimeToShelf.blank_startup_time', - blank_startup_time_microseconds) + self.record_result_median('timeToShelfTestTimeShelfDisplayMedianUs', + metrics_array) + self.record_result('timeToShelfBlankStartupTimeUs', + blank_startup_time_microseconds) if __name__ == '__main__':
diff --git a/src/cobalt/webdriver_benchmarks/tv_testcase.py b/src/cobalt/webdriver_benchmarks/tv_testcase.py index cbb5903..57a7b77 100644 --- a/src/cobalt/webdriver_benchmarks/tv_testcase.py +++ b/src/cobalt/webdriver_benchmarks/tv_testcase.py
@@ -5,6 +5,8 @@ from __future__ import print_function import json +import logging +import math import os import sys import time @@ -41,36 +43,69 @@ LAYOUT_TIMEOUT_SECONDS = 5 -def _median(l): - """Returns median value of items in a list. +def _percentile(results, percentile): + """Returns the percentile of an array. - Note: This function is setup for convieniance, and might not be performant - for large datasets. Also, no care has been taken to deal with nans, or - infinities, so please expand the functionality and tests if that is desired. - The other option is to use median. - - Running time: O(n^2) - Space complexity: O(n) + This method interpolates between two numbers if the percentile lands between + two data points. Args: - l: List containing sortable items. + results: Sortable results array. + percentile: A number ranging from 0-100. + Returns: + Appropriate value. + Raises: + RuntimeError: Raised on invalid args. + """ + if not results: + return None + if percentile > 100 or percentile < 0: + raise RuntimeError("percentile must be 0-100") + sorted_results = sorted(results) + + if percentile == 100: + return sorted_results[-1] + fractional, index = math.modf((len(sorted_results) - 1) * (percentile * 0.01)) + index = int(index) + + if len(sorted_results) == index + 1: + return sorted_results[index] + + return sorted_results[index] * (1 - fractional + ) + sorted_results[index + 1] * fractional + + +def _merge_dict(merge_into, merge_from): + """Merges the second dict into the first dict. + + Merge into differs from update in that it will not override values. If the + values already exist, the resulting value will be a list with a union of + existing and new items. + + Args: + merge_into: An output dict to merge values into. + merge_from: An input dict to iterate over and insert values from. Returns: - Median of the items in a list. None if |l| is empty. + None """ - if not l: - return None - l_length = len(l) - if l_length == 0: - return l[0] + if not merge_from: + return + for k, v in merge_from.items(): + try: + existing_value = merge_into[k] + except KeyError: + merge_into[k] = v + continue - l_sorted = sorted(l) - middle_index = l_length // 2 - if len(l) % 2 == 1: - return l_sorted[middle_index] - - # If there are even number of items, take the average of two middle values. - return 0.5 * (l_sorted[middle_index] + l_sorted[middle_index - 1]) + if not isinstance(v, list): + v = [v] + if isinstance(existing_value, list): + existing_value.extend(v) + else: + new_value = [existing_value] + new_value.extend(v) + merge_into[k] = new_value class TvTestCase(unittest.TestCase): @@ -83,6 +118,14 @@ class LayoutTimeoutException(BaseException): """Exception thrown when layout did not complete in time.""" + @classmethod + def setUpClass(cls): + print("Running " + cls.__name__) + + @classmethod + def tearDownClass(cls): + print("Done " + cls.__name__) + def get_webdriver(self): return partial_layout_benchmark.GetWebDriver() @@ -129,22 +172,25 @@ query_dict = BASE_PARAMS.copy() if query_params: query_dict.update(urlparse.parse_qsl(parsed_url[4])) - query_dict.update(query_params) - parsed_url[4] = urlencode(query_dict) + _merge_dict(query_dict, query_params) + parsed_url[4] = urlencode(query_dict, doseq=True) final_url = urlparse.urlunparse(parsed_url) self.get_webdriver().get(final_url) - def load_tv(self, label=None): + def load_tv(self, label=None, additional_query_params=None): """Loads the main TV page and waits for it to display. Args: label: A value for the label query parameter. + additional_query_params: A dict containing additional query parameters. Raises: Underlying WebDriver exceptions """ - query_params = None + query_params = {} if label is not None: query_params = {"label": label} + if additional_query_params is not None: + query_params.update(additional_query_params) self.goto(TV_APP_PATH, query_params) # Note that the internal tests use "expect_transition" which is # a mechanism that sets a maximum timeout for a "@with_retries" @@ -257,23 +303,45 @@ self.assert_displayed(tv.FOCUSED_SHELF_TITLE) self.wait_for_layout_complete() - def record_results(self, name, results): - """Records results of benchmark. - - The duration of KeyUp events will be recorded. + def record_result(self, name, result): + """Records an individual scalar result of a benchmark. Args: name: name of test case - results: Test results. Must be JSON encodable + result: Test result. Must be JSON encodable scalar. """ - if isinstance(results, list): - value_to_record = _median(results) - else: - value_to_record = results + value_to_record = result string_value_to_record = json.JSONEncoder().encode(value_to_record) print("tv_testcase RESULT: {} {}".format(name, string_value_to_record)) + def record_result_median(self, name, results): + """Records the median of an array of results. + + Args: + name: name of test case + results: Test results array. Must be array of JSON encodable scalar. + """ + value_to_record = _percentile(results, 50) + + string_value_to_record = json.JSONEncoder().encode(value_to_record) + print("tv_testcase RESULT: {} {}".format(name, string_value_to_record)) + + def record_result_percentile(self, name, results, percentile): + """Records the percentile of an array of results. + + Args: + name: The (string) name of test case. + results: Test results array. Must be array of JSON encodable scalars. + percentile: A number ranging from 0-100. + Raises: + RuntimeError: Raised on invalid args. + """ + value_to_record = _percentile(results, percentile) + string_value_to_record = json.JSONEncoder().encode(value_to_record) + print("tv_testcase RESULT: {} {}".format(name, string_value_to_record)) + def main(): + logging.basicConfig(level=logging.DEBUG) partial_layout_benchmark.main()
diff --git a/src/media/audio/shell_audio_streamer_starboard.cc b/src/media/audio/shell_audio_streamer_starboard.cc index ddcc139..22a143f 100644 --- a/src/media/audio/shell_audio_streamer_starboard.cc +++ b/src/media/audio/shell_audio_streamer_starboard.cc
@@ -14,9 +14,7 @@ * limitations under the License. */ -#include "media/audio/null_audio_streamer.h" - -#include "base/logging.h" +#include "media/audio/shell_audio_streamer.h" namespace media { @@ -25,7 +23,7 @@ void ShellAudioStreamer::Terminate() {} ShellAudioStreamer* ShellAudioStreamer::Instance() { - return NullAudioStreamer::GetInstance(); + return NULL; } } // namespace media
diff --git a/src/media/base/starboard_player.cc b/src/media/base/starboard_player.cc index 2708468..b62db8a 100644 --- a/src/media/base/starboard_player.cc +++ b/src/media/base/starboard_player.cc
@@ -290,11 +290,15 @@ SbMediaVideoCodec video_codec = MediaVideoCodecToSbMediaVideoCodec(video_config_.codec()); - player_ = SbPlayerCreate(window_, video_codec, audio_codec, - SB_PLAYER_NO_DURATION, drm_system_, &audio_header, - &StarboardPlayer::DeallocateSampleCB, - &StarboardPlayer::DecoderStatusCB, - &StarboardPlayer::PlayerStatusCB, this); + player_ = SbPlayerCreate( + window_, video_codec, audio_codec, SB_PLAYER_NO_DURATION, drm_system_, + &audio_header, &StarboardPlayer::DeallocateSampleCB, + &StarboardPlayer::DecoderStatusCB, &StarboardPlayer::PlayerStatusCB, this +#if SB_VERSION(3) + , + NULL // provider +#endif + ); set_bounds_helper_->SetPlayer(this); }
diff --git a/src/nb/analytics/memory_tracker.cc b/src/nb/analytics/memory_tracker.cc new file mode 100644 index 0000000..cf6c868 --- /dev/null +++ b/src/nb/analytics/memory_tracker.cc
@@ -0,0 +1,33 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker.h" + +#include "nb/analytics/memory_tracker_impl.h" +#include "starboard/once.h" + +namespace nb { +namespace analytics { +namespace { +SB_ONCE_INITIALIZE_FUNCTION(MemoryTrackerImpl, GetMemoryTrackerImplSingleton); +} // namespace + +MemoryTracker* MemoryTracker::Get() { + return GetMemoryTrackerImplSingleton(); +} + +} // namespace analytics +} // namespace nb
diff --git a/src/nb/analytics/memory_tracker.h b/src/nb/analytics/memory_tracker.h new file mode 100644 index 0000000..27ef75f --- /dev/null +++ b/src/nb/analytics/memory_tracker.h
@@ -0,0 +1,137 @@ +/* + * Copyright 2016 Google Inc. 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 NB_MEMORY_TRACKER_H_ +#define NB_MEMORY_TRACKER_H_ + +#include <vector> +#include "starboard/configuration.h" +#include "starboard/types.h" + +namespace nb { +namespace analytics { + +class MemoryTracker; +class AllocationVisitor; +class AllocationGroup; +class AllocationRecord; + +// Creates a MemoryTracker instance that implements the +// MemoryTracker. Once the instance is created it can begin tracking +// system allocations by calling InstallGlobalTrackingHooks(). +// Deleting the MemoryTracker is forbidden. +// +// Example, Creation and Hooking: +// static MemoryTracker* s_global_tracker = +// GetOrCreateMemoryTracker(); +// s_global_tracker->InstallGlobalTrackingHooks(); // now tracking memory. +// +// Data about the allocations are aggregated under AllocationGroups and it's +// recommended that GetAllocationGroups(...) is used to get simple allocation +// statistics. +// +// Deeper analytics are possible by creating an AllocationVisitor subclass and +// traversing through the internal allocations of the tracker. In this way all +// known information about allocation state of the program is made accessible. +// The visitor does not need to perform any locking as this is guaranteed by +// the MemoryTracker. +// +// Example (AllocationVisitor): +// MyAllocation visitor = ...; +// s_global_tracker->Accept(&visitor); +// visitor.PrintAllocations(); +// +// Performance: +// 1) Gold builds disallow memory tracking and therefore have zero-cost +// for this feature. +// 2) All other builds that allow memory tracking have minimal cost as long +// as memory tracking has not been activated. This is facilitated by NOT +// using locks, at the expense of thread safety during teardown (hence the +// reason why you should NOT delete a memory tracker with hooks installed). +// 3) When the memory tracking has been activated then there is a non-trivial +// performance cost in terms of CPU and memory for the feature. +class MemoryTracker { + public: + // Gets the singleton instance of the default MemoryTracker. This + // is created the first time it is used. + static MemoryTracker* Get(); + + MemoryTracker() {} + virtual bool InstallGlobalTrackingHooks() = 0; + + // It's recommended the MemoryTracker is never removed or deleted during the + // runtime. + virtual void RemoveGlobalTrackingHooks() = 0; + + // Returns the total amount of bytes that are tracked. + virtual int64_t GetTotalAllocationBytes() = 0; + virtual int64_t GetTotalNumberOfAllocations() = 0; + + // Allows probing of all memory allocations. The visitor does not need to + // perform any locking and can allocate memory during it's operation. + virtual void Accept(AllocationVisitor* visitor) = 0; + + // Collects all memory groups that exist. The AllocationGroups lifetime + // exists for as long as the MemoryTracker instance is alive. + virtual void GetAllocationGroups( + std::vector<const AllocationGroup*>* output) = 0; + + // Enabled/disables memory tracking in the current thread. + virtual void SetMemoryTrackingEnabled(bool on) = 0; + // Returns the memory tracking state in the current thread. + virtual bool IsMemoryTrackingEnabled() const = 0; + + // Returns true if the memory was successfully tracked. + virtual bool AddMemoryTracking(const void* memory, size_t size) = 0; + // Returns a non-zero size if the memory was successfully removed. + virtual size_t RemoveMemoryTracking(const void* memory) = 0; + // Returns true if the memory has tracking. When true is returned then the + // supplied AllocRecord is written. + virtual bool GetMemoryTracking(const void* memory, + AllocationRecord* record) const = 0; + + protected: + virtual ~MemoryTracker() {} + + SB_DISALLOW_COPY_AND_ASSIGN(MemoryTracker); +}; + +// A visitor class which is useful for inspecting data. +class AllocationVisitor { + public: + // Returns true to keep visiting, otherwise abort. + virtual bool Visit(const void* memory, + const AllocationRecord& alloc_record) = 0; + virtual ~AllocationVisitor() {} +}; + +// Contains an allocation record for a pointer including it's size and what +// AllocationGroup it was constructed under. +struct AllocationRecord { + AllocationRecord() : size(0), allocation_group(NULL) {} + AllocationRecord(size_t sz, AllocationGroup* group) + : size(sz), allocation_group(group) {} + + static AllocationRecord Empty() { return AllocationRecord(); } + bool IsEmpty() const { return !size && !allocation_group; } + size_t size; + AllocationGroup* allocation_group; +}; + +} // namespace analytics +} // namespace nb + +#endif // NB_MEMORY_TRACKER_H_
diff --git a/src/nb/analytics/memory_tracker_helpers.cc b/src/nb/analytics/memory_tracker_helpers.cc new file mode 100644 index 0000000..2f4776a --- /dev/null +++ b/src/nb/analytics/memory_tracker_helpers.cc
@@ -0,0 +1,311 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker_helpers.h" + +#include <stdint.h> +#include <vector> + +#include "nb/hash.h" +#include "starboard/configuration.h" +#include "starboard/log.h" + +namespace nb { +namespace analytics { + +AllocationGroup::AllocationGroup(const std::string& name) + : name_(name), allocation_bytes_(0), num_allocations_(0) {} + +AllocationGroup::~AllocationGroup() {} + +void AllocationGroup::AddAllocation(int64_t num_bytes) { + if (num_bytes == 0) + return; + int num_alloc_diff = num_bytes > 0 ? 1 : -1; + + allocation_bytes_.fetch_add(num_bytes); + num_allocations_.fetch_add(num_alloc_diff); +} + +void AllocationGroup::GetAggregateStats(int32_t* num_allocs, + int64_t* allocation_bytes) const { + *num_allocs = num_allocations_.load(); + *allocation_bytes = allocation_bytes_.load(); +} + +int64_t AllocationGroup::allocation_bytes() const { + return allocation_bytes_.load(); +} + +int32_t AllocationGroup::num_allocations() const { + return num_allocations_.load(); +} + +AtomicStringAllocationGroupMap::AtomicStringAllocationGroupMap() { + unaccounted_group_ = Ensure("Unaccounted"); +} + +AtomicStringAllocationGroupMap::~AtomicStringAllocationGroupMap() { + unaccounted_group_ = NULL; + while (!group_map_.empty()) { + Map::iterator it = group_map_.begin(); + delete it->second; + group_map_.erase(it); + } +} + +AllocationGroup* AtomicStringAllocationGroupMap::Ensure( + const std::string& name) { + starboard::ScopedLock lock(mutex_); + Map::const_iterator found_it = group_map_.find(name); + if (found_it != group_map_.end()) { + return found_it->second; + } + AllocationGroup* group = new AllocationGroup(name); + group_map_[name] = group; + return group; +} + +AllocationGroup* AtomicStringAllocationGroupMap::GetDefaultUnaccounted() { + return unaccounted_group_; +} + +bool AtomicStringAllocationGroupMap::Erase(const std::string& name) { + starboard::ScopedLock lock(mutex_); + Map::iterator found_it = group_map_.find(name); + if (found_it == group_map_.end()) { + // Didn't find it. + return false; + } + group_map_.erase(found_it); + return true; +} + +void AtomicStringAllocationGroupMap::GetAll( + std::vector<const AllocationGroup*>* output) const { + starboard::ScopedLock lock(mutex_); + for (Map::const_iterator it = group_map_.begin(); it != group_map_.end(); + ++it) { + output->push_back(it->second); + } +} + +void AllocationGroupStack::Push(AllocationGroup* group) { + alloc_group_stack_.push_back(group); +} + +void AllocationGroupStack::Pop() { + alloc_group_stack_.pop_back(); +} + +AllocationGroup* AllocationGroupStack::Peek() { + if (alloc_group_stack_.empty()) { + return NULL; + } + return alloc_group_stack_.back(); +} + +AtomicAllocationMap::AtomicAllocationMap() {} + +AtomicAllocationMap::~AtomicAllocationMap() {} + +bool AtomicAllocationMap::Add(const void* memory, + const AllocationRecord& alloc_record) { + starboard::ScopedLock lock(mutex_); + const bool inserted = + pointer_map_.insert(std::make_pair(memory, alloc_record)).second; + return inserted; +} + +bool AtomicAllocationMap::Get(const void* memory, + AllocationRecord* alloc_record) const { + starboard::ScopedLock lock(mutex_); + PointerMap::const_iterator found_it = pointer_map_.find(memory); + if (found_it == pointer_map_.end()) { + if (alloc_record) { + *alloc_record = AllocationRecord::Empty(); + } + return false; + } + if (alloc_record) { + *alloc_record = found_it->second; + } + return true; +} + +bool AtomicAllocationMap::Remove(const void* memory, + AllocationRecord* alloc_record) { + starboard::ScopedLock lock(mutex_); + PointerMap::iterator found_it = pointer_map_.find(memory); + + if (found_it == pointer_map_.end()) { + if (alloc_record) { + *alloc_record = AllocationRecord::Empty(); + } + return false; + } + if (alloc_record) { + *alloc_record = found_it->second; + } + pointer_map_.erase(found_it); + return true; +} + +bool AtomicAllocationMap::Accept(AllocationVisitor* visitor) const { + starboard::ScopedLock lock(mutex_); + for (PointerMap::const_iterator it = pointer_map_.begin(); + it != pointer_map_.end(); ++it) { + const void* memory = it->first; + const AllocationRecord& alloc_rec = it->second; + if (!visitor->Visit(memory, alloc_rec)) { + return false; + } + } + return true; +} + +size_t AtomicAllocationMap::Size() const { + starboard::ScopedLock lock(mutex_); + return pointer_map_.size(); +} + +bool AtomicAllocationMap::Empty() const { + starboard::ScopedLock lock(mutex_); + return pointer_map_.empty(); +} + +void AtomicAllocationMap::Clear() { + starboard::ScopedLock lock(mutex_); + for (PointerMap::iterator it = pointer_map_.begin(); + it != pointer_map_.end(); ++it) { + const AllocationRecord& rec = it->second; + AllocationGroup* group = rec.allocation_group; + group->AddAllocation(-rec.size); + } + return pointer_map_.clear(); +} + +ConcurrentAllocationMap::ConcurrentAllocationMap() : pointer_map_array_() {} + +ConcurrentAllocationMap::~ConcurrentAllocationMap() { + Clear(); +} + +bool ConcurrentAllocationMap::Add(const void* memory, + const AllocationRecord& alloc_record) { + AtomicAllocationMap& map = GetMapForPointer(memory); + return map.Add(memory, alloc_record); +} + +bool ConcurrentAllocationMap::Get(const void* memory, + AllocationRecord* alloc_record) const { + const AtomicAllocationMap& map = GetMapForPointer(memory); + return map.Get(memory, alloc_record); +} + +bool ConcurrentAllocationMap::Remove(const void* memory, + AllocationRecord* alloc_record) { + AtomicAllocationMap& map = GetMapForPointer(memory); + bool output = map.Remove(memory, alloc_record); + return output; +} + +size_t ConcurrentAllocationMap::Size() const { + size_t size = 0; + for (int i = 0; i < kNumElements; ++i) { + const AtomicAllocationMap& map = pointer_map_array_[i]; + size += map.Size(); + } + return size; +} + +bool ConcurrentAllocationMap::Empty() const { + return 0 == Size(); +} + +void ConcurrentAllocationMap::Clear() { + for (int i = 0; i < kNumElements; ++i) { + AtomicAllocationMap& map = pointer_map_array_[i]; + map.Clear(); + } +} + +bool ConcurrentAllocationMap::Accept(AllocationVisitor* visitor) const { + for (int i = 0; i < kNumElements; ++i) { + const AtomicAllocationMap& map = pointer_map_array_[i]; + if (!map.Accept(visitor)) { + return false; // Early out. + } + } + return true; +} + +size_t ConcurrentAllocationMap::hash_ptr(const void* ptr) { + uintptr_t val = reinterpret_cast<uintptr_t>(ptr); + + return RuntimeHash32(reinterpret_cast<const char*>(&val), sizeof(val)); +} + +int ConcurrentAllocationMap::ToIndex(const void* ptr) const { + SB_DCHECK(0 != kNumElements); + uint32_t hash_val = hash_ptr(ptr); + int index = hash_val % kNumElements; + return index; +} + +AtomicAllocationMap& ConcurrentAllocationMap::GetMapForPointer( + const void* ptr) { + return pointer_map_array_[ToIndex(ptr)]; +} + +const AtomicAllocationMap& ConcurrentAllocationMap::GetMapForPointer( + const void* ptr) const { + return pointer_map_array_[ToIndex(ptr)]; +} + +SimpleThread::SimpleThread(const std::string& name) + : thread_(kSbThreadInvalid), name_(name) {} + +SimpleThread::~SimpleThread() {} + +void SimpleThread::Start() { + SbThreadEntryPoint entry_point = ThreadEntryPoint; + + thread_ = SbThreadCreate(0, // default stack_size. + kSbThreadNoPriority, // default priority. + kSbThreadNoAffinity, // default affinity. + true, // joinable. + name_.c_str(), entry_point, this); + + // SbThreadCreate() above produced an invalid thread handle. + SB_DCHECK(thread_ != kSbThreadInvalid); + return; +} + +void* SimpleThread::ThreadEntryPoint(void* context) { + SimpleThread* this_ptr = static_cast<SimpleThread*>(context); + this_ptr->Run(); + return NULL; +} + +void SimpleThread::DoJoin() { + if (!SbThreadJoin(thread_, NULL)) { + SB_DCHECK(false) << "Could not join thread."; + } +} + +} // namespace analytics +} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_helpers.h b/src/nb/analytics/memory_tracker_helpers.h new file mode 100644 index 0000000..d07651c --- /dev/null +++ b/src/nb/analytics/memory_tracker_helpers.h
@@ -0,0 +1,264 @@ +/* + * Copyright 2016 Google Inc. 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 NB_MEMORY_TRACKER_HELPERS_H_ +#define NB_MEMORY_TRACKER_HELPERS_H_ + +#include <map> +#include <vector> + +#include "nb/analytics/memory_tracker.h" +#include "nb/atomic.h" +#include "starboard/mutex.h" +#include "starboard/thread.h" +#include "starboard/types.h" +#include "starboard/log.h" + +namespace nb { +namespace analytics { + +class AllocationGroup; +struct AllocationRecord; + +template <typename Type> +class ThreadLocalPointer { + public: + ThreadLocalPointer() { + slot_ = SbThreadCreateLocalKey(NULL); // No destructor for pointer. + SB_DCHECK(kSbThreadLocalKeyInvalid != slot_); + } + + ~ThreadLocalPointer() { SbThreadDestroyLocalKey(slot_); } + + Type* Get() const { + void* ptr = SbThreadGetLocalValue(slot_); + Type* type_ptr = static_cast<Type*>(ptr); + return type_ptr; + } + + void Set(Type* ptr) { + void* void_ptr = static_cast<void*>(ptr); + SbThreadSetLocalValue(slot_, void_ptr); + } + + private: + SbThreadLocalKey slot_; + SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>); +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() : default_value_(false) {} + explicit ThreadLocalBoolean(bool default_value) + : default_value_(default_value) {} + ~ThreadLocalBoolean() {} + + bool Get() const { + bool val = tlp_.Get() != NULL; + return val ^ default_value_; + } + + void Set(bool val) { + val = val ^ default_value_; + tlp_.Set(val ? TruePointer() : FalsePointer()); + } + + private: + static void* TruePointer() { return reinterpret_cast<void*>(0x1); } + static void* FalsePointer() { return NULL; } + ThreadLocalPointer<void> tlp_; + const bool default_value_; + + SB_DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +// An AllocationGroup is a collection of allocations that are logically lumped +// together, such as "Javascript" or "Graphics". +class AllocationGroup { + public: + AllocationGroup(const std::string& name); + ~AllocationGroup(); + const std::string& name() const { return name_; } + + void AddAllocation(int64_t num_bytes); + void GetAggregateStats(int32_t* num_allocs, int64_t* allocation_bytes) const; + + int64_t allocation_bytes() const; + int32_t num_allocations() const; + + private: + const std::string name_; + nb::atomic_int64_t allocation_bytes_; + nb::atomic_int32_t num_allocations_; +}; + +// A self locking data structure that maps strings -> AllocationGroups. This is +// used to resolve MemoryGroup names (e.g. "Javascript") to an AllocationGroup +// which can be used to group allocations together. +class AtomicStringAllocationGroupMap { + public: + AtomicStringAllocationGroupMap(); + ~AtomicStringAllocationGroupMap(); + + AllocationGroup* Ensure(const std::string& name); + AllocationGroup* GetDefaultUnaccounted(); + + bool Erase(const std::string& key); + void GetAll(std::vector<const AllocationGroup*>* output) const; + + private: + typedef std::map<std::string, AllocationGroup*> Map; + Map group_map_; + AllocationGroup* unaccounted_group_; + mutable starboard::Mutex mutex_; +}; + +class AllocationGroupStack { + public: + AllocationGroupStack() { Push_DebugBreak(NULL); } + ~AllocationGroupStack() {} + + void Push(AllocationGroup* group); + void Pop(); + AllocationGroup* Peek(); + + void Push_DebugBreak(AllocationGroup* ag) { debug_stack_.push_back(ag); } + void Pop_DebugBreak() { debug_stack_.pop_back(); } + AllocationGroup* Peek_DebugBreak() { + if (debug_stack_.empty()) { + return NULL; + } + return debug_stack_.back(); + } + + private: + SB_DISALLOW_COPY_AND_ASSIGN(AllocationGroupStack); + typedef std::vector<AllocationGroup*> AllocationGroupPtrVec; + AllocationGroupPtrVec alloc_group_stack_, debug_stack_; +}; + +// A per-pointer map of allocations to AllocRecords. This map is thread safe. +class AtomicAllocationMap { + public: + AtomicAllocationMap(); + ~AtomicAllocationMap(); + + // Returns true if Added. Otherwise false means that the pointer + // already existed. + bool Add(const void* memory, const AllocationRecord& alloc_record); + + // Returns true if the memory exists in this set. + bool Get(const void* memory, AllocationRecord* alloc_record) const; + + // Return true if the memory existed in this set. If true + // then output alloc_record is written with record that was found. + // otherwise the record is written as 0 bytes and null key. + bool Remove(const void* memory, AllocationRecord* alloc_record); + + bool Accept(AllocationVisitor* visitor) const; + + size_t Size() const; + bool Empty() const; + void Clear(); + + private: + SB_DISALLOW_COPY_AND_ASSIGN(AtomicAllocationMap); + typedef std::map<const void*, AllocationRecord> PointerMap; + + PointerMap pointer_map_; + mutable starboard::Mutex mutex_; +}; + +// A per-pointer map of allocations to AllocRecords. This is a hybrid data +// structure consisting of a hashtable of maps. Each pointer that is +// stored or retrieved is hashed to a random bucket. Each bucket has it's own +// lock. This distributed pattern increases performance significantly by +// reducing contention. The top-level hashtable is of constant size and does +// not resize. Each bucket is implemented as it's own map of elements. +class ConcurrentAllocationMap { + public: + static const int kNumElements = 511; + ConcurrentAllocationMap(); + ~ConcurrentAllocationMap(); + + // Returns true if Added. Otherwise false means that the pointer + // already existed. + bool Add(const void* memory, const AllocationRecord& alloc_record); + // Returns true if the memory exists in this set. + bool Get(const void* memory, AllocationRecord* alloc_record) const; + // Return true if the memory existed in this set. If true + // then output alloc_record is written with record that was found. + // otherwise the record is written as 0 bytes and null key. + bool Remove(const void* memory, AllocationRecord* alloc_record); + size_t Size() const; + bool Empty() const; + void Clear(); + + // Provides access to all the allocations within in a thread safe manner. + bool Accept(AllocationVisitor* visitor) const; + + AtomicAllocationMap& GetMapForPointer(const void* ptr); + const AtomicAllocationMap& GetMapForPointer(const void* ptr) const; + + private: + SB_DISALLOW_COPY_AND_ASSIGN(ConcurrentAllocationMap); + // Takes a pointer and generates a hash. + static size_t hash_ptr(const void* ptr); + + int ToIndex(const void* ptr) const; + AtomicAllocationMap pointer_map_array_[kNumElements]; +}; + +class SimpleThread { + public: + explicit SimpleThread(const std::string& name); + virtual ~SimpleThread() = 0; + + // Subclasses should override the Run method. + virtual void Run() = 0; + + void Join() { + Cancel(); + DoJoin(); + } + + // If Join() is intended to interrupt the Run() function then override + // Cancel() to send a signal. + // Example: + // virtual void Cancel() { finished_ = true; } + // virtual void Run() { + // while (!finished_) { /* do work */ } + // } + virtual void Cancel() {} + + // Calls SbThreadCreate() and starts running code. + void Start(); + + private: + static void* ThreadEntryPoint(void* context); + void DoJoin(); + void DoStart(); + + const std::string name_; + SbThread thread_; + + SB_DISALLOW_COPY_AND_ASSIGN(SimpleThread); +}; + +} // namespace analytics +} // namespace nb + +#endif // NB_MEMORY_TRACKER_HELPERS_H_
diff --git a/src/nb/analytics/memory_tracker_helpers_test.cc b/src/nb/analytics/memory_tracker_helpers_test.cc new file mode 100644 index 0000000..ebef340 --- /dev/null +++ b/src/nb/analytics/memory_tracker_helpers_test.cc
@@ -0,0 +1,124 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace nb { +namespace analytics { +namespace { + +/////////////////////////////////////////////////////////////////////////////// +TEST(AtomicStringAllocationGroupMap, Use) { + AtomicStringAllocationGroupMap map; + AllocationGroup* tag = map.Ensure("MemoryRegion"); + EXPECT_TRUE(tag != NULL); + EXPECT_EQ(std::string("MemoryRegion"), tag->name()); + AllocationGroup* tag2 = map.Ensure("MemoryRegion"); + EXPECT_EQ(tag, tag2); +} + +TEST(AtomicAllocationMap, AddHasRemove) { + AtomicAllocationMap atomic_pointer_map; + int32_t int_a = 0; + int32_t int_b = 1; + + // Initially empty. + EXPECT_TRUE(atomic_pointer_map.Empty()); + + const AllocationRecord int32_alloc_record = + AllocationRecord(sizeof(int32_t), NULL); + AllocationRecord alloc_output; + + EXPECT_TRUE(atomic_pointer_map.Add(&int_a, int32_alloc_record)); + EXPECT_FALSE(atomic_pointer_map.Add(&int_a, int32_alloc_record)); + EXPECT_TRUE(atomic_pointer_map.Get(&int_a, &alloc_output)); + EXPECT_EQ(alloc_output.size, sizeof(int32_t)); + + EXPECT_FALSE(atomic_pointer_map.Get(&int_b, &alloc_output)); + EXPECT_EQ(0, alloc_output.size); + // Adding pointer to int_a increases set to 1 element. + EXPECT_EQ(atomic_pointer_map.Size(), 1); + EXPECT_FALSE(atomic_pointer_map.Empty()); + + // Adds pointer to int_b. + EXPECT_TRUE(atomic_pointer_map.Add(&int_b, int32_alloc_record)); + EXPECT_TRUE(atomic_pointer_map.Get(&int_b, &alloc_output)); + EXPECT_EQ(sizeof(int32_t), alloc_output.size); + // Expect that the second pointer added will increase the number of elements. + EXPECT_EQ(atomic_pointer_map.Size(), 2); + EXPECT_FALSE(atomic_pointer_map.Empty()); + + // Now remove the elements and ensure that they no longer found and that + // the size of the table shrinks to empty. + EXPECT_TRUE(atomic_pointer_map.Remove(&int_a, &alloc_output)); + EXPECT_EQ(sizeof(int32_t), alloc_output.size); + EXPECT_EQ(atomic_pointer_map.Size(), 1); + EXPECT_FALSE(atomic_pointer_map.Remove(&int_a, &alloc_output)); + EXPECT_EQ(0, alloc_output.size); + EXPECT_TRUE(atomic_pointer_map.Remove(&int_b, &alloc_output)); + EXPECT_EQ(atomic_pointer_map.Size(), 0); + + EXPECT_TRUE(atomic_pointer_map.Empty()); +} + +TEST(ConcurrentAllocationMap, AddHasRemove) { + ConcurrentAllocationMap alloc_map; + int32_t int_a = 0; + int32_t int_b = 1; + + // Initially empty. + EXPECT_TRUE(alloc_map.Empty()); + + const AllocationRecord int32_alloc_record = + AllocationRecord(sizeof(int32_t), NULL); + AllocationRecord alloc_output; + + EXPECT_TRUE(alloc_map.Add(&int_a, int32_alloc_record)); + EXPECT_FALSE(alloc_map.Add(&int_a, int32_alloc_record)); + EXPECT_TRUE(alloc_map.Get(&int_a, &alloc_output)); + EXPECT_EQ(alloc_output.size, sizeof(int32_t)); + + EXPECT_FALSE(alloc_map.Get(&int_b, &alloc_output)); + EXPECT_EQ(0, alloc_output.size); + // Adding pointer to int_a increases set to 1 element. + EXPECT_EQ(alloc_map.Size(), 1); + EXPECT_FALSE(alloc_map.Empty()); + + // Adds pointer to int_b. + EXPECT_TRUE(alloc_map.Add(&int_b, int32_alloc_record)); + EXPECT_TRUE(alloc_map.Get(&int_b, &alloc_output)); + EXPECT_EQ(sizeof(int32_t), alloc_output.size); + // Expect that the second pointer added will increase the number of elements. + EXPECT_EQ(alloc_map.Size(), 2); + EXPECT_FALSE(alloc_map.Empty()); + + // Now remove the elements and ensure that they no longer found and that + // the size of the table shrinks to empty. + EXPECT_TRUE(alloc_map.Remove(&int_a, &alloc_output)); + EXPECT_EQ(sizeof(int32_t), alloc_output.size); + EXPECT_EQ(alloc_map.Size(), 1); + EXPECT_FALSE(alloc_map.Remove(&int_a, &alloc_output)); + EXPECT_EQ(0, alloc_output.size); + EXPECT_TRUE(alloc_map.Remove(&int_b, &alloc_output)); + EXPECT_EQ(alloc_map.Size(), 0); + + EXPECT_TRUE(alloc_map.Empty()); +} + +} // namespace +} // namespace analytics +} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.cc b/src/nb/analytics/memory_tracker_impl.cc new file mode 100644 index 0000000..fecda24 --- /dev/null +++ b/src/nb/analytics/memory_tracker_impl.cc
@@ -0,0 +1,541 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker_impl.h" + +#include <iomanip> +#include <sstream> + +#include "nb/atomic.h" +#include "starboard/atomic.h" + +namespace nb { +namespace analytics { + +SbMemoryReporter* MemoryTrackerImpl::GetMemoryReporter() { + return &sb_memory_tracker_; +} + +NbMemoryScopeReporter* MemoryTrackerImpl::GetMemoryScopeReporter() { + return &nb_memory_scope_reporter_; +} + +int64_t MemoryTrackerImpl::GetTotalAllocationBytes() { + return total_bytes_allocated_.load(); +} + +AllocationGroup* MemoryTrackerImpl::GetAllocationGroup(const char* name) { + DisableMemoryTrackingInScope no_tracking(this); + AllocationGroup* alloc_group = alloc_group_map_.Ensure(name); + return alloc_group; +} + +void MemoryTrackerImpl::PushAllocationGroupByName(const char* group_name) { + AllocationGroup* group = GetAllocationGroup(group_name); + PushAllocationGroup(group); +} + +void MemoryTrackerImpl::PushAllocationGroup(AllocationGroup* alloc_group) { + if (alloc_group == NULL) { + alloc_group = alloc_group_map_.GetDefaultUnaccounted(); + } + DisableMemoryTrackingInScope no_tracking(this); + allocation_group_stack_tls_.GetOrCreate()->Push(alloc_group); +} + +AllocationGroup* MemoryTrackerImpl::PeekAllocationGroup() { + DisableMemoryTrackingInScope no_tracking(this); + AllocationGroup* out = + allocation_group_stack_tls_.GetOrCreate()->Peek(); + if (out == NULL) { + out = alloc_group_map_.GetDefaultUnaccounted(); + } + return out; +} + +void MemoryTrackerImpl::PopAllocationGroup() { + DisableMemoryTrackingInScope no_tracking(this); + AllocationGroupStack* alloc_tls = allocation_group_stack_tls_.GetOrCreate(); + alloc_tls->Pop(); + AllocationGroup* group = alloc_tls->Peek(); + // We don't allow null, so if this is encountered then push the + // "default unaccounted" alloc group. + if (group == NULL) { + alloc_tls->Push(alloc_group_map_.GetDefaultUnaccounted()); + } +} + +void MemoryTrackerImpl::GetAllocationGroups( + std::vector<const AllocationGroup*>* output) { + DisableMemoryTrackingInScope no_allocation_tracking(this); + output->reserve(100); + alloc_group_map_.GetAll(output); +} + +void MemoryTrackerImpl::GetAllocationGroups( + std::map<std::string, const AllocationGroup*>* output) { + output->clear(); + DisableMemoryTrackingInScope no_tracking(this); + std::vector<const AllocationGroup*> tmp; + GetAllocationGroups(&tmp); + for (size_t i = 0; i < tmp.size(); ++i) { + output->insert(std::make_pair(tmp[i]->name(), tmp[i])); + } +} + +void MemoryTrackerImpl::Accept(AllocationVisitor* visitor) { + DisableMemoryTrackingInScope no_mem_tracking(this); + atomic_allocation_map_.Accept(visitor); +} + +void MemoryTrackerImpl::Clear() { + // Prevent clearing of the tree from triggering a re-entrant + // memory deallocation. + atomic_allocation_map_.Clear(); + total_bytes_allocated_.store(0); +} + +void MemoryTrackerImpl::Debug_PushAllocationGroupBreakPointByName( + const char* group_name) { + DisableMemoryTrackingInScope no_tracking(this); + SB_DCHECK(group_name != NULL); + AllocationGroup* group = alloc_group_map_.Ensure(group_name); + Debug_PushAllocationGroupBreakPoint(group); +} + +void MemoryTrackerImpl::Debug_PushAllocationGroupBreakPoint( + AllocationGroup* alloc_group) { + DisableMemoryTrackingInScope no_tracking(this); + allocation_group_stack_tls_.GetOrCreate()->Push_DebugBreak(alloc_group); +} + +void MemoryTrackerImpl::Debug_PopAllocationGroupBreakPoint() { + DisableMemoryTrackingInScope no_tracking(this); + allocation_group_stack_tls_.GetOrCreate()->Pop_DebugBreak(); +} + +// Converts "2345.54" => "2,345.54". +std::string InsertCommasIntoNumberString(const std::string& input) { + typedef std::vector<char> CharVector; + typedef CharVector::iterator CharIt; + + CharVector chars(input.begin(), input.end()); + std::reverse(chars.begin(), chars.end()); + + CharIt curr_it = chars.begin(); + CharIt mid = std::find(chars.begin(), chars.end(), '.'); + if (mid == chars.end()) { + mid = curr_it; + } + + CharVector out(curr_it, mid); + + int counter = 0; + for (CharIt it = mid; it != chars.end(); ++it) { + if (counter != 0 && (counter % 3 == 0)) { + out.push_back(','); + } + if (*it != '.') { + counter++; + } + out.push_back(*it); + } + + std::reverse(out.begin(), out.end()); + std::stringstream ss; + for (int i = 0; i < out.size(); ++i) { + ss << out[i]; + } + return ss.str(); +} + +template <typename T> +std::string NumberFormatWithCommas(T val) { + // Convert value to string. + std::stringstream ss; + ss << val; + std::string s = InsertCommasIntoNumberString(ss.str()); + return s; +} + +void MemoryTrackerImpl::OnMalloc(void* context, + const void* memory, + size_t size) { + MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context); + t->AddMemoryTracking(memory, size); +} + +void MemoryTrackerImpl::OnDealloc(void* context, const void* memory) { + MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context); + t->RemoveMemoryTracking(memory); +} + +void MemoryTrackerImpl::OnMapMem(void* context, + const void* memory, + size_t size) { + // We might do something more interesting with MapMemory calls later. + OnMalloc(context, memory, size); +} + +void MemoryTrackerImpl::OnUnMapMem(void* context, + const void* memory, + size_t size) { + // We might do something more interesting with UnMapMemory calls later. + OnDealloc(context, memory); +} + +void MemoryTrackerImpl::OnPushAllocationGroup( + void* context, + NbMemoryScopeInfo* memory_scope_info) { + MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context); + uintptr_t* cached_handle = &(memory_scope_info->cached_handle_); + const bool allows_caching = memory_scope_info->allows_caching_; + const char* group_name = memory_scope_info->memory_scope_name_; + + AllocationGroup* group = NULL; + if (allows_caching && *cached_handle != 0) { + group = reinterpret_cast<AllocationGroup*>(cached_handle); + } else { + group = t->GetAllocationGroup(group_name); + if (allows_caching) { + // Flush all pending writes so that the the pointee is well formed + // by the time the pointer becomes visible to other threads. + SbAtomicMemoryBarrier(); + *cached_handle = reinterpret_cast<uintptr_t>(group); + } + } + + t->PushAllocationGroup(group); +} + +void MemoryTrackerImpl::OnPopAllocationGroup(void* context) { + MemoryTrackerImpl* t = static_cast<MemoryTrackerImpl*>(context); + t->PopAllocationGroup(); +} + +void MemoryTrackerImpl::Initialize( + SbMemoryReporter* sb_memory_reporter, + NbMemoryScopeReporter* memory_scope_reporter) { + SbMemoryReporter mem_reporter = { + MemoryTrackerImpl::OnMalloc, MemoryTrackerImpl::OnDealloc, + + MemoryTrackerImpl::OnMapMem, MemoryTrackerImpl::OnUnMapMem, + + this}; + + NbMemoryScopeReporter mem_scope_reporter = { + MemoryTrackerImpl::OnPushAllocationGroup, + MemoryTrackerImpl::OnPopAllocationGroup, + + this, + }; + + *sb_memory_reporter = mem_reporter; + *memory_scope_reporter = mem_scope_reporter; +} + +void MemoryTrackerImpl::SetThreadFilter(SbThreadId tid) { + thread_filter_id_ = tid; +} + +bool MemoryTrackerImpl::IsCurrentThreadAllowedToReport() const { + if (thread_filter_id_ == kSbThreadInvalidId) { + return true; + } + return SbThreadGetId() == thread_filter_id_; +} + +MemoryTrackerImpl::DisableDeletionInScope::DisableDeletionInScope( + MemoryTrackerImpl* owner) + : owner_(owner) { + prev_state_ = owner->MemoryDeletionEnabled(); + owner_->SetMemoryDeletionEnabled(false); +} + +MemoryTrackerImpl::DisableDeletionInScope::~DisableDeletionInScope() { + owner_->SetMemoryDeletionEnabled(prev_state_); +} + +// TODO: Get rid of the nb::SimpleThread +class MemoryTrackerPrintThread : public SimpleThread { + public: + MemoryTrackerPrintThread(MemoryTracker* owner) + : SimpleThread("MemoryTrackerPrintThread"), + finished_(false), + owner_(owner) {} + + // Overridden so that the thread can exit gracefully. + virtual void Cancel() SB_OVERRIDE { finished_.store(true); } + + virtual void Run() { + struct NoMemTracking { + NoMemTracking(MemoryTracker* owner) : owner_(owner) { + prev_val_ = owner_->IsMemoryTrackingEnabled(); + owner_->SetMemoryTrackingEnabled(false); + } + ~NoMemTracking() { owner_->SetMemoryTrackingEnabled(prev_val_); } + + bool prev_val_; + MemoryTracker* owner_; + }; + + while (!finished_.load()) { + NoMemTracking no_mem_tracking_in_this_scope(owner_); + + // std::map<std::string, const AllocationGroup*> output; + // typedef std::map<std::string, const AllocationGroup*>::const_iterator + // MapIt; + std::vector<const AllocationGroup*> vector_output; + owner_->GetAllocationGroups(&vector_output); + + typedef std::map<std::string, const AllocationGroup*> Map; + typedef Map::const_iterator MapIt; + + Map output; + for (int i = 0; i < vector_output.size(); ++i) { + const AllocationGroup* group = vector_output[i]; + output[group->name()] = group; + } + + int32_t num_allocs = 0; + int64_t total_bytes = 0; + + struct F { + static void PrintRow(std::stringstream& ss, + const std::string& v1, + const std::string& v2, + const std::string& v3) { + ss.width(20); + ss << std::left << v1; + ss.width(13); + ss << std::right << v2 << " "; + ss.width(7); + ss << std::right << v3 << "\n"; + } + }; + + if (owner_->IsMemoryTrackingEnabled()) { + // If this isn't true then it would cause an infinite loop. The + // following will likely crash. + SB_DCHECK(false) << "Unexpected, memory tracking should be disabled."; + } + + std::stringstream ss; + for (MapIt it = output.begin(); it != output.end(); ++it) { + const AllocationGroup* group = it->second; + if (!group) { + continue; + } + + int32_t num_group_allocs = -1; + int64_t total_group_bytes = -1; + + group->GetAggregateStats(&num_group_allocs, &total_group_bytes); + SB_DCHECK(-1 != num_group_allocs); + SB_DCHECK(-1 != total_group_bytes); + num_allocs += num_group_allocs; + total_bytes += total_group_bytes; + + ss.width(20); + ss << std::left << it->first; + ss.width(13); + ss << std::right << NumberFormatWithCommas(total_group_bytes) << " "; + ss.width(7); + ss << std::right << NumberFormatWithCommas(num_group_allocs) << "\n"; + } + ss << "-------------------------------\n"; + + SB_LOG(INFO) << "\n" + << "Total Bytes Allocated: " + << NumberFormatWithCommas(total_bytes) << "\n" + << "Total allocations: " + << NumberFormatWithCommas(num_allocs) << "\n\n" << ss.str(); + + SbThreadSleep(250); + } + } + + private: + atomic_bool finished_; + MemoryTracker* owner_; +}; + +void MemoryTrackerImpl::Debug_EnablePrintOutThread() { + if (debug_output_thread_) { + return; + } // Already enabled. + debug_output_thread_.reset(new MemoryTrackerPrintThread(this)); + debug_output_thread_->Start(); +} + +MemoryTrackerImpl::MemoryTrackerImpl() + : thread_filter_id_(kSbThreadInvalidId) { + total_bytes_allocated_.store(0); + global_hooks_installed_ = false; + Initialize(&sb_memory_tracker_, &nb_memory_scope_reporter_); + // Push the default region so that stats can be accounted for. + PushAllocationGroup(alloc_group_map_.GetDefaultUnaccounted()); +} + +MemoryTrackerImpl::~MemoryTrackerImpl() { + // If we are currently hooked into allocation tracking... + if (global_hooks_installed_) { + SbMemorySetReporter(NULL); + // For performance reasons no locking is used on the tracker. + // Therefore give enough time for other threads to exit this tracker + // before fully destroying this object. + SbThreadSleep(250 * kSbTimeMillisecond); // 250 millisecond wait. + } + if (debug_output_thread_) { + debug_output_thread_->Join(); + debug_output_thread_.reset(); + } +} + +bool MemoryTrackerImpl::AddMemoryTracking(const void* memory, size_t size) { + // Vars are stored to assist in debugging. + const bool thread_allowed_to_report = IsCurrentThreadAllowedToReport(); + const bool valid_memory_request = (memory != NULL) && (size != 0); + const bool mem_track_enabled = IsMemoryTrackingEnabled(); + + const bool tracking_enabled = + mem_track_enabled && valid_memory_request && thread_allowed_to_report; + + if (!tracking_enabled) { + return false; + } + + // End all memory tracking in subsequent data structures. + DisableMemoryTrackingInScope no_memory_tracking(this); + AllocationGroupStack* alloc_stack = + allocation_group_stack_tls_.GetOrCreate(); + AllocationGroup* group = alloc_stack->Peek(); + if (!group) { + group = alloc_group_map_.GetDefaultUnaccounted(); + } + +#ifndef NDEBUG + // This section of the code is designed to allow a developer to break + // execution whenever the debug allocation stack is in scope, so that the + // allocations can be stepped through. + // Example: + // Debug_PushAllocationGroupBreakPointByName("Javascript"); + // ...now set a break point below at "static int i = 0" + if (group && (group == alloc_stack->Peek_DebugBreak()) && + alloc_stack->Peek_DebugBreak()) { + static int i = 0; // This static is here to allow an + ++i; // easy breakpoint in the debugger + } +#endif + + AllocationRecord alloc_record(size, group); + bool added = atomic_allocation_map_.Add(memory, alloc_record); + if (added) { + AddAllocationBytes(size); + group->AddAllocation(size); + } else { + AllocationRecord unexpected_alloc; + atomic_allocation_map_.Get(memory, &unexpected_alloc); + AllocationGroup* prev_group = unexpected_alloc.allocation_group; + + std::string prev_group_name; + if (prev_group) { + prev_group_name = unexpected_alloc.allocation_group->name(); + } else { + prev_group_name = "none"; + } + + SB_DCHECK(added) + << "\nUnexpected condition, previous allocation was not removed:\n" + << "\tprevious alloc group: " << prev_group_name << "\n" + << "\tnew alloc group: " << group->name() << "\n" + << "\tprevious size: " << unexpected_alloc.size << "\n" + << "\tnew size: " << size << "\n"; + } + return added; +} + +size_t MemoryTrackerImpl::RemoveMemoryTracking(const void* memory) { + const bool do_remove = memory && MemoryDeletionEnabled(); + if (!do_remove) { + return 0; + } + + AllocationRecord alloc_record; + bool removed = false; + + // Prevent a map::erase() from causing an endless stack overflow by + // disabling memory deletion for the very limited scope. + { + // Not a correct name. TODO - change. + DisableDeletionInScope no_memory_deletion(this); + removed = atomic_allocation_map_.Remove(memory, &alloc_record); + } + + if (!removed) { + return 0; + } else { + const int64_t alloc_size = (static_cast<int64_t>(alloc_record.size)); + AllocationGroup* group = alloc_record.allocation_group; + if (group) { + group->AddAllocation(-alloc_size); + } + AddAllocationBytes(-alloc_size); + return alloc_record.size; + } +} + +bool MemoryTrackerImpl::GetMemoryTracking(const void* memory, + AllocationRecord* record) const { + const bool exists = atomic_allocation_map_.Get(memory, record); + return exists; +} + +void MemoryTrackerImpl::SetMemoryTrackingEnabled(bool on) { + memory_tracking_disabled_tls_.Set(!on); +} + +bool MemoryTrackerImpl::IsMemoryTrackingEnabled() const { + const bool enabled = !memory_tracking_disabled_tls_.Get(); + return enabled; +} + +void MemoryTrackerImpl::AddAllocationBytes(int64_t val) { + total_bytes_allocated_.fetch_add(val); +} + +bool MemoryTrackerImpl::MemoryDeletionEnabled() const { + return !memory_deletion_enabled_tls_.Get(); +} + +void MemoryTrackerImpl::SetMemoryDeletionEnabled(bool on) { + memory_deletion_enabled_tls_.Set(!on); +} + +MemoryTrackerImpl::DisableMemoryTrackingInScope::DisableMemoryTrackingInScope( + MemoryTrackerImpl* t) + : owner_(t) { + prev_value_ = owner_->IsMemoryTrackingEnabled(); + owner_->SetMemoryTrackingEnabled(false); +} + +MemoryTrackerImpl::DisableMemoryTrackingInScope:: + ~DisableMemoryTrackingInScope() { + owner_->SetMemoryTrackingEnabled(prev_value_); +} + +} // namespace analytics +} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_impl.h b/src/nb/analytics/memory_tracker_impl.h new file mode 100644 index 0000000..7508a71 --- /dev/null +++ b/src/nb/analytics/memory_tracker_impl.h
@@ -0,0 +1,184 @@ +/* + * Copyright 2016 Google Inc. 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 NB_MEMORY_TRACKER_IMPL_H_ +#define NB_MEMORY_TRACKER_IMPL_H_ + +#include "nb/analytics/memory_tracker_helpers.h" +#include "nb/analytics/memory_tracker.h" +#include "nb/memory_scope.h" +#include "nb/scoped_ptr.h" +#include "nb/thread_local_object.h" +#include "starboard/configuration.h" +#include "starboard/memory_reporter.h" +#include "starboard/memory.h" +#include "starboard/mutex.h" + +namespace nb { +namespace analytics { + +class MemoryTrackerImpl : public MemoryTracker { + public: + typedef ConcurrentAllocationMap AllocationMapType; + + MemoryTrackerImpl(); + virtual ~MemoryTrackerImpl(); + + // MemoryTracker adapter which is compatible with the SbMemoryReporter + // interface. + SbMemoryReporter* GetMemoryReporter(); + NbMemoryScopeReporter* GetMemoryScopeReporter(); + + AllocationGroup* GetAllocationGroup(const char* name); + // Declares the start of a memory region. After this call, all + // memory regions will be tagged with this allocation group. + // Note that AllocationGroup is a tracking characteristic and + // does not imply any sort of special allocation pool. + void PushAllocationGroupByName(const char* group_name); + void PushAllocationGroup(AllocationGroup* alloc_group); + AllocationGroup* PeekAllocationGroup(); + // Ends the current memory region and the previous memory region + // is restored. + void PopAllocationGroup(); + + // CONTROL + // + // Adds tracking to the supplied memory pointer. An AllocationRecord is + // generated for the supplied allocation which can be queried immediately + // with GetMemoryTracking(...). + bool InstallGlobalTrackingHooks() SB_OVERRIDE { + global_hooks_installed_ = true; + bool ok = SbMemorySetReporter(GetMemoryReporter()); + ok |= NbSetMemoryScopeReporter(GetMemoryScopeReporter()); + return ok; + } + void RemoveGlobalTrackingHooks() SB_OVERRIDE { + SbMemorySetReporter(NULL); + NbSetMemoryScopeReporter(NULL); + } + + bool AddMemoryTracking(const void* memory, size_t size) SB_OVERRIDE; + size_t RemoveMemoryTracking(const void* memory) SB_OVERRIDE; + // Returns true if the allocation record was successfully found. + // If true then the output will be written to with the values. + // Otherwise the output is reset to the empty AllocationRecord. + bool GetMemoryTracking(const void* memory, + AllocationRecord* record) const SB_OVERRIDE; + // Thread local function to get and set the memory tracking state. When set + // to disabled then memory allocations are not recorded. However memory + // deletions are still recorded. + void SetMemoryTrackingEnabled(bool on) SB_OVERRIDE; + bool IsMemoryTrackingEnabled() const SB_OVERRIDE; + + // REPORTING + // + // Total allocation bytes that have been allocated by this + // MemoryTrackerImpl. + int64_t GetTotalAllocationBytes() SB_OVERRIDE; + // Retrieves a collection of all known allocation groups. Locking is done + // internally. + void GetAllocationGroups(std::vector<const AllocationGroup*>* output) + SB_OVERRIDE; + // Retrieves a collection of all known allocation groups. Locking is done + // internally. The output is a map of names to AllocationGroups. + void GetAllocationGroups( + std::map<std::string, const AllocationGroup*>* output); + + // Provides access to the internal allocations in a thread safe way. + // Allocation tracking is disabled in the current thread for the duration + // of the visitation. + void Accept(AllocationVisitor* visitor) SB_OVERRIDE; + + int64_t GetTotalNumberOfAllocations() SB_OVERRIDE { + return pointer_map()->Size(); + } + + // TESTING. + AllocationMapType* pointer_map() { return &atomic_allocation_map_; } + void Clear(); + + // This is useful for debugging. Allows the developer to set a breakpoint + // and see only allocations that are in the defined allocation group. This + // is only active in the current thread. + void Debug_PushAllocationGroupBreakPointByName(const char* group_name); + void Debug_PushAllocationGroupBreakPoint(AllocationGroup* alloc_group); + void Debug_PopAllocationGroupBreakPoint(); + + // This is useful for testing, setting this to a thread will allow ONLY + // those allocations from the set thread. + // Setting this to kSbThreadInvalidId (default) allows all threads to report + // allocations. + void SetThreadFilter(SbThreadId tid); + bool IsCurrentThreadAllowedToReport() const; + // Spawns a thread who's lifetime is coupled to that of this owning + // MemoryTrackerImpl. This thread will output the state of the memory + // periodically. + void Debug_EnablePrintOutThread(); + + private: + struct DisableMemoryTrackingInScope { + DisableMemoryTrackingInScope(MemoryTrackerImpl* t); + ~DisableMemoryTrackingInScope(); + MemoryTrackerImpl* owner_; + bool prev_value_; + }; + + // Disables all memory deletion in the current scope. This is used in one + // location. + struct DisableDeletionInScope { + DisableDeletionInScope(MemoryTrackerImpl* owner); + ~DisableDeletionInScope(); + MemoryTrackerImpl* owner_; + bool prev_state_; + }; + + // These are functions that are used specifically SbMemoryReporter. + static void OnMalloc(void* context, const void* memory, size_t size); + static void OnDealloc(void* context, const void* memory); + static void OnMapMem(void* context, const void* memory, size_t size); + static void OnUnMapMem(void* context, const void* memory, size_t size); + static void OnPushAllocationGroup(void* context, + NbMemoryScopeInfo* memory_scope_info); + static void OnPopAllocationGroup(void* context); + + void Initialize(SbMemoryReporter* memory_reporter, + NbMemoryScopeReporter* nb_memory_scope_reporter); + void AddAllocationBytes(int64_t val); + bool MemoryDeletionEnabled() const; + + void SetMemoryDeletionEnabled(bool on); + + SbMemoryReporter sb_memory_tracker_; + NbMemoryScopeReporter nb_memory_scope_reporter_; + SbThreadId thread_filter_id_; + + AllocationMapType atomic_allocation_map_; + AtomicStringAllocationGroupMap alloc_group_map_; + + atomic_int64_t total_bytes_allocated_; + scoped_ptr<SimpleThread> debug_output_thread_; + + // THREAD LOCAL SECTION. + ThreadLocalBoolean memory_deletion_enabled_tls_; + ThreadLocalBoolean memory_tracking_disabled_tls_; + ThreadLocalObject<AllocationGroupStack> allocation_group_stack_tls_; + bool global_hooks_installed_; +}; + +} // namespace analytics +} // namespace nb + +#endif // NB_MEMORY_TRACKER_IMPL_H_
diff --git a/src/nb/analytics/memory_tracker_impl_test.cc b/src/nb/analytics/memory_tracker_impl_test.cc new file mode 100644 index 0000000..8236447 --- /dev/null +++ b/src/nb/analytics/memory_tracker_impl_test.cc
@@ -0,0 +1,802 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker_impl.h" +#include "nb/memory_scope.h" +#include "nb/scoped_ptr.h" +#include "nb/test_thread.h" +#include "starboard/system.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define STRESS_TEST_DURATION_SECONDS 1 +#define NUM_STRESS_TEST_THREADS 3 + +namespace nb { +namespace analytics { +namespace { + +MemoryTrackerImpl* s_memory_tracker_ = NULL; + +struct NoMemTracking { + bool prev_val; + NoMemTracking() : prev_val(false) { + if (s_memory_tracker_) { + prev_val = s_memory_tracker_->IsMemoryTrackingEnabled(); + s_memory_tracker_->SetMemoryTrackingEnabled(false); + } + } + ~NoMemTracking() { + if (s_memory_tracker_) { + s_memory_tracker_->SetMemoryTrackingEnabled(prev_val); + } + } +}; + +// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when +// for unit testing allocations. These overrides disable memory +// tracking for the duration of the EXPECT and ASSERT operations. +#define EXPECT_EQ_NO_TRACKING(A, B) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_EQ(A, B); \ + } + +#define EXPECT_TRUE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_TRUE(A); \ + } + +#define EXPECT_FALSE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_FALSE(A); \ + } + +#define ASSERT_EQ_NO_TRACKING(A, B) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_EQ(A, B); \ + } + +#define ASSERT_TRUE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_TRUE(A); \ + } + +// !! converts int -> bool. +bool FlipCoin() { + return !!(SbSystemGetRandomUInt64() & 0x1); +} + +/////////////////////////////////////////////////////////////////////////////// +// Stress testing the Allocation Tracker. +class MemoryScopeThread : public nb::TestThread { + public: + typedef nb::TestThread Super; + + explicit MemoryScopeThread(MemoryTrackerImpl* memory_tracker) + : memory_tracker_(memory_tracker) { + static int s_counter = 0; + + std::stringstream ss; + ss << "MemoryScopeThread_" << s_counter++; + unique_name_ = ss.str(); + } + virtual ~MemoryScopeThread() {} + + // Overridden so that the thread can exit gracefully. + virtual void Join() { + finished_ = true; + Super::Join(); + } + virtual void Run() { + while (!finished_) { + TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str()); + AllocationGroup* group = memory_tracker_->PeekAllocationGroup(); + + const int cmp_result = group->name().compare(unique_name_); + if (cmp_result != 0) { + GTEST_FAIL() << "unique name mismatch"; + return; + } + } + } + + private: + MemoryTrackerImpl* memory_tracker_; + bool finished_; + std::string unique_name_; + int do_delete_counter_; + int do_malloc_counter_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Stress testing the Allocation Tracker. +class AllocationStressThread : public nb::TestThread { + public: + explicit AllocationStressThread(MemoryTrackerImpl* memory_tracker); + virtual ~AllocationStressThread(); + + // Overridden so that the thread can exit gracefully. + virtual void Join(); + virtual void Run(); + + private: + typedef std::map<const void*, AllocationRecord> AllocMap; + + void CheckPointers(); + bool RemoveRandomAllocation(std::pair<const void*, AllocationRecord>* output); + bool DoDelete(); + void DoMalloc(); + + MemoryTrackerImpl* memory_tracker_; + bool finished_; + std::map<const void*, AllocationRecord> allocated_pts_; + std::string unique_name_; + int do_delete_counter_; + int do_malloc_counter_; +}; + +class AddAllocationStressThread : public nb::TestThread { + public: + typedef std::map<const void*, AllocationRecord> AllocMap; + + AddAllocationStressThread(MemoryTracker* memory_tracker, + int num_elements_add, + AllocMap* destination_map, + starboard::Mutex* destination_map_mutex) + : memory_tracker_(memory_tracker), + num_elements_to_add_(num_elements_add), + destination_map_(destination_map), + destination_map_mutex_(destination_map_mutex) {} + + virtual void Run() { + for (int i = 0; i < num_elements_to_add_; ++i) { + const int alloc_size = std::rand() % 100 + 8; + void* ptr = SbMemoryAllocate(alloc_size); + + AllocationRecord record; + if (memory_tracker_->GetMemoryTracking(ptr, &record)) { + NoMemTracking no_mem_tracking; // simplifies test. + + starboard::ScopedLock lock(*destination_map_mutex_); + destination_map_->insert(std::make_pair(ptr, record)); + } else { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Could not add pointer."; + } + if (FlipCoin()) { + SbThreadYield(); // Give other threads a chance to run. + } + } + } + + private: + MemoryTracker* memory_tracker_; + AllocMap* destination_map_; + starboard::Mutex* destination_map_mutex_; + int num_elements_to_add_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Framework which initializes the MemoryTracker once and installs it +// for the first test and the removes the MemoryTracker after the +// the last test finishes. +class MemoryTrackerImplTest : public ::testing::Test { + public: + typedef MemoryTrackerImpl::AllocationMapType AllocationMapType; + MemoryTrackerImplTest() {} + + MemoryTrackerImpl* memory_tracker() { return s_memory_tracker_; } + + bool GetAllocRecord(void* alloc_memory, AllocationRecord* output) { + return memory_tracker()->GetMemoryTracking(alloc_memory, output); + } + + AllocationMapType* pointer_map() { return memory_tracker()->pointer_map(); } + + size_t NumberOfAllocations() { + AllocationMapType* map = pointer_map(); + return map->Size(); + } + + int64_t TotalAllocationBytes() { + return memory_tracker()->GetTotalAllocationBytes(); + } + + bool MemoryTrackerEnabled() const { return s_memory_tracker_enabled_; } + + protected: + static void SetUpTestCase() { + if (!s_memory_tracker_) { + s_memory_tracker_ = new MemoryTrackerImpl; + s_memory_tracker_enabled_ = + s_memory_tracker_->InstallGlobalTrackingHooks(); + } + } + static void TearDownTestCase() { SbMemorySetReporter(NULL); } + + virtual void SetUp() { + memory_tracker()->Clear(); + memory_tracker()->SetThreadFilter(SbThreadGetId()); + } + + virtual void TearDown() { memory_tracker()->Clear(); } + static bool s_memory_tracker_enabled_; +}; +bool MemoryTrackerImplTest::s_memory_tracker_enabled_ = false; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryTrackerImplTest +TEST_F(MemoryTrackerImplTest, NoMemTracking) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + ASSERT_EQ_NO_TRACKING(0, NumberOfAllocations()); + scoped_ptr<int> dummy(new int()); + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + { + // Now that memory allocation is disabled, no more allocations should + // be recorded. + NoMemTracking no_memory_tracking_in_this_scope; + int* dummy2 = new int(); + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + delete dummy2; + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + } + scoped_ptr<int> dummy2(new int()); + EXPECT_EQ_NO_TRACKING(2, NumberOfAllocations()); + dummy.reset(NULL); + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + dummy2.reset(NULL); + EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations()); +} + +TEST_F(MemoryTrackerImplTest, RemovePointerOnNoMemoryTracking) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + + int* int_ptr = new int(); + { + NoMemTracking no_memory_tracking_in_this_scope; + delete int_ptr; + } + EXPECT_FALSE_NO_TRACKING(pointer_map()->Get(int_ptr, NULL)); +} + +TEST_F(MemoryTrackerImplTest, NewDeleteOverridenTest) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations()); + int* int_a = new int(0); + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + delete int_a; + EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations()); +} + +TEST_F(MemoryTrackerImplTest, TotalAllocationBytes) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + int32_t* int_a = new int32_t(0); + EXPECT_EQ_NO_TRACKING(1, NumberOfAllocations()); + EXPECT_EQ_NO_TRACKING(4, TotalAllocationBytes()); + delete int_a; + EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations()); +} + +// Tests the expectation that a lot of allocations can be executed and that +// internal data structures won't overflow. +TEST_F(MemoryTrackerImplTest, NoStackOverflow) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + static const int kNumAllocations = 1000; + std::vector<int*> allocations; + + // Also it turns out that this test is great for catching + // background threads pushing allocations through the allocator. + // This is supposed to be filtered, but if it's not then this test will + // fail. + // SbThreadYield() is used to give other threads a chance to enter into our + // allocator and catch a test failure. + SbThreadSleep(1); + ASSERT_EQ_NO_TRACKING(0, NumberOfAllocations()); + + for (int i = 0; i < kNumAllocations; ++i) { + SbThreadYield(); + EXPECT_EQ_NO_TRACKING(i, NumberOfAllocations()); + int* val = new int(0); + NoMemTracking no_tracking_in_scope; + allocations.push_back(val); + } + + EXPECT_EQ_NO_TRACKING(kNumAllocations, NumberOfAllocations()); + + for (int i = 0; i < kNumAllocations; ++i) { + SbThreadYield(); + EXPECT_EQ_NO_TRACKING(kNumAllocations - i, NumberOfAllocations()); + delete allocations[i]; + } + + EXPECT_EQ_NO_TRACKING(0, NumberOfAllocations()); +} + +// Tests the expectation that the macros will push/pop the memory scope. +TEST_F(MemoryTrackerImplTest, MacrosPushPop) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + scoped_ptr<int> dummy; + { + TRACK_MEMORY_SCOPE("TestAllocations"); + dummy.reset(new int()); + } + + scoped_ptr<int> dummy2(new int()); + + AllocationRecord alloc_rec; + pointer_map()->Get(dummy.get(), &alloc_rec); + + ASSERT_TRUE_NO_TRACKING(alloc_rec.allocation_group); + EXPECT_EQ_NO_TRACKING(std::string("TestAllocations"), + alloc_rec.allocation_group->name()); + + pointer_map()->Get(dummy2.get(), &alloc_rec); + + ASSERT_TRUE_NO_TRACKING(alloc_rec.allocation_group); + EXPECT_EQ_NO_TRACKING(std::string("Unaccounted"), + alloc_rec.allocation_group->name()); +} + +// Tests the expectation that if the cached flag on the NbMemoryScopeInfo is +// set to false that the caching of the handle is not performed. +TEST_F(MemoryTrackerImplTest, RespectsNonCachedHandle) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + const bool kCaching = false; + NbMemoryScopeInfo memory_scope = { + 0, "MyName", __FILE__, + __LINE__, __FUNCTION__, false}; // false to disallow caching. + + // Pushing the memory scope should trigger the caching operation to be + // attempted. However, because caching was explicitly disabled this handle + // should retain the value of 0. + NbPushMemoryScope(&memory_scope); + EXPECT_EQ_NO_TRACKING(memory_scope.cached_handle_, uintptr_t(0)); + + // ... and still assert that the group was created with the expected name. + AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName"); + // Equality check. + EXPECT_EQ_NO_TRACKING(0, group->name().compare("MyName")); + NbPopMemoryScope(); +} + +// Tests the expectation that if the cached flag on the NbMemoryScopeInfo is +// set to true that the caching will be applied for the cached_handle of the +// memory scope. +TEST_F(MemoryTrackerImplTest, PushAllocGroupCachedHandle) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + NbMemoryScopeInfo memory_scope = { + 0, // Cached handle. + "MyName", // Memory scope name. + __FILE__, __LINE__, __FUNCTION__, + true // Allows caching. + }; + + NbPushMemoryScope(&memory_scope); + EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ != uintptr_t(0)); + AllocationGroup* group = memory_tracker()->GetAllocationGroup("MyName"); + + EXPECT_TRUE_NO_TRACKING(memory_scope.cached_handle_ == + reinterpret_cast<uintptr_t>(group)); +} + +// Tests the expectation that the macro TRACK_MEMORY_SCOPE will capture the +// allocation in the MemoryTrackerImpl. +TEST_F(MemoryTrackerImplTest, MacrosGroupAccounting) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + MemoryTrackerImpl* track_alloc = memory_tracker(); // Debugging. + track_alloc->Clear(); + + memory_tracker()->Clear(); + const AllocationGroup* group_a = + memory_tracker()->GetAllocationGroup("MemoryTrackerTest-ScopeA"); + + const AllocationGroup* group_b = + memory_tracker()->GetAllocationGroup("MemoryTrackerTest-ScopeB"); + + ASSERT_TRUE_NO_TRACKING(group_a); + ASSERT_TRUE_NO_TRACKING(group_b); + + int32_t num_allocations = -1; + int64_t allocation_bytes = -1; + + // Expect that both groups have no allocations in them. + group_a->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); + + group_b->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); + + scoped_ptr<int> alloc_a, alloc_b, alloc_b2; + { + TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA"); + alloc_a.reset(new int()); + { + TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeB"); + alloc_b.reset(new int()); + alloc_b2.reset(new int()); + + group_a->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(1, num_allocations); + EXPECT_EQ_NO_TRACKING(4, allocation_bytes); + alloc_a.reset(NULL); + group_a->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); + + num_allocations = allocation_bytes = -1; + group_b->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(2, num_allocations); + EXPECT_EQ_NO_TRACKING(8, allocation_bytes); + + alloc_b2.reset(NULL); + group_b->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(1, num_allocations); + EXPECT_EQ_NO_TRACKING(4, allocation_bytes); + + alloc_b.reset(NULL); + group_b->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); + } + } +} + +// Tests the expectation that the visitor can access the allocations. +TEST_F(MemoryTrackerImplTest, VisitorAccess) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + class SimpleVisitor : public AllocationVisitor { + public: + SimpleVisitor() : num_memory_allocs_(0) {} + virtual bool Visit(const void* memory, + const AllocationRecord& alloc_record) { + num_memory_allocs_++; + return true; // Keep traversing. + } + + size_t num_memory_allocs_; + }; + + SimpleVisitor visitor; + scoped_ptr<int> int_ptr(new int); + + // Should see the int_ptr allocation. + memory_tracker()->Accept(&visitor); + EXPECT_EQ_NO_TRACKING(1, visitor.num_memory_allocs_); + visitor.num_memory_allocs_ = 0; + + int_ptr.reset(NULL); + // Now no allocations should be available. + memory_tracker()->Accept(&visitor); + EXPECT_EQ_NO_TRACKING(0, visitor.num_memory_allocs_); +} + +// A stress test that rapidly adds allocations, but saves all deletions +// for the main thread. This test will catch concurrency errors related +// to reporting new allocations. +TEST_F(MemoryTrackerImplTest, MultiThreadedStressAddTest) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + // Disable allocation filtering. + memory_tracker()->SetThreadFilter(kSbThreadInvalidId); + + std::vector<nb::TestThread*> threads; + + const int kNumObjectsToAdd = 10000 / NUM_STRESS_TEST_THREADS; + AddAllocationStressThread::AllocMap map; + starboard::Mutex map_mutex; + + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + nb::TestThread* thread = new AddAllocationStressThread( + memory_tracker(), kNumObjectsToAdd, &map, &map_mutex); + + threads.push_back(thread); + } + + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + threads[i]->Start(); + } + + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + threads[i]->Join(); + } + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + delete threads[i]; + } + + while (!map.empty()) { + const void* ptr = map.begin()->first; + map.erase(map.begin()); + + if (!memory_tracker()->GetMemoryTracking(ptr, NULL)) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "No tracking?!"; + } + + SbMemoryDeallocate(const_cast<void*>(ptr)); + if (memory_tracker()->GetMemoryTracking(ptr, NULL)) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Tracking?!"; + } + } +} + +// Tests the expectation that memory scopes are multi-threaded safe. +TEST_F(MemoryTrackerImplTest, MultiThreadedMemoryScope) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + memory_tracker()->SetThreadFilter(kSbThreadInvalidId); + TRACK_MEMORY_SCOPE("MultiThreadedStressUseTest"); + + std::vector<MemoryScopeThread*> threads; + + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + threads.push_back(new MemoryScopeThread(memory_tracker())); + } + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Start(); + } + + SbThreadSleep(STRESS_TEST_DURATION_SECONDS * 1000 * 1000); + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Join(); + } + + for (int i = 0; i < threads.size(); ++i) { + delete threads[i]; + } + + threads.clear(); +} + +// Tests the expectation that new/delete can be done by different threads. +TEST_F(MemoryTrackerImplTest, MultiThreadedStressUseTest) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + // Disable allocation filtering. + memory_tracker()->SetThreadFilter(kSbThreadInvalidId); + TRACK_MEMORY_SCOPE("MultiThreadedStressUseTest"); + + std::vector<AllocationStressThread*> threads; + + for (int i = 0; i < NUM_STRESS_TEST_THREADS; ++i) { + threads.push_back(new AllocationStressThread(memory_tracker())); + } + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Start(); + } + + SbThreadSleep(STRESS_TEST_DURATION_SECONDS * 1000 * 1000); + + for (int i = 0; i < threads.size(); ++i) { + threads[i]->Join(); + } + + for (int i = 0; i < threads.size(); ++i) { + delete threads[i]; + } + + threads.clear(); +} + +//////////////////////////// Implementation /////////////////////////////////// +/// Impl of AllocationStressThread +AllocationStressThread::AllocationStressThread(MemoryTrackerImpl* tracker) + : memory_tracker_(tracker), finished_(false) { + static int counter = 0; + std::stringstream ss; + ss << "AllocStressThread-" << counter++; + unique_name_ = ss.str(); +} + +AllocationStressThread::~AllocationStressThread() { + if (!allocated_pts_.empty()) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "allocated pointers still exist"; + } +} + +void AllocationStressThread::Join() { + finished_ = true; + nb::TestThread::Join(); +} + +void AllocationStressThread::CheckPointers() { + typedef AllocMap::iterator Iter; + + for (Iter it = allocated_pts_.begin(); it != allocated_pts_.end(); ++it) { + const void* ptr = it->first; + const bool found = memory_tracker_->GetMemoryTracking(ptr, NULL); + if (!found) { + NoMemTracking no_tracking_in_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) << "Not found"; + } + } +} + +void AllocationStressThread::Run() { + while (!finished_) { + const bool do_delete = FlipCoin(); + if (FlipCoin()) { + DoDelete(); + } else { + DoMalloc(); + } + CheckPointers(); + + // Randomly give other threads the opportunity run. + if (FlipCoin()) { + SbThreadYield(); + } + } + + // Clear out all memory. + while (DoDelete()) { + ; + } +} + +bool AllocationStressThread::RemoveRandomAllocation( + std::pair<const void*, AllocationRecord>* output) { + if (allocated_pts_.empty()) { + return false; + } + + // Select a random pointer to delete. + int idx = std::rand() % allocated_pts_.size(); + AllocMap::iterator iter = allocated_pts_.begin(); + while (idx > 0) { + idx--; + iter++; + } + output->first = iter->first; + output->second = iter->second; + allocated_pts_.erase(iter); + return true; +} + +bool AllocationStressThread::DoDelete() { + NoMemTracking no_memory_tracking_in_this_scope; + ++do_delete_counter_; + + std::pair<const void*, AllocationRecord> alloc; + if (!RemoveRandomAllocation(&alloc)) { + return false; + } + + const void* ptr = alloc.first; + const AllocationRecord expected_alloc_record = alloc.second; + + TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str()); + AllocationGroup* current_group = memory_tracker_->PeekAllocationGroup(); + + // Expect that the name of the current allocation group name is the same as + // what we expect. + if (current_group->name() != unique_name_) { + NoMemTracking no_memory_tracking_in_this_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) << " " << current_group->name() + << " != " << unique_name_; + } + + MemoryTrackerImpl::AllocationMapType* internal_alloc_map = + memory_tracker_->pointer_map(); + + AllocationRecord existing_alloc_record; + + const bool found_existing_record = + memory_tracker_->GetMemoryTracking(ptr, &existing_alloc_record); + + if (!found_existing_record) { + ADD_FAILURE_AT(__FILE__, __LINE__) + << "expected to find existing record, but did not"; + } else if (current_group != existing_alloc_record.allocation_group) { + ADD_FAILURE_AT(__FILE__, __LINE__) + << "group allocation mismatch: " << current_group->name() + << " != " << existing_alloc_record.allocation_group->name() << "\n"; + } + SbMemoryDeallocate(const_cast<void*>(ptr)); + return true; +} + +void AllocationStressThread::DoMalloc() { + ++do_malloc_counter_; + if (allocated_pts_.size() > 10000) { + return; + } + + TRACK_MEMORY_SCOPE_DYNAMIC(unique_name_.c_str()); + AllocationGroup* current_group = memory_tracker_->PeekAllocationGroup(); + + // Sanity check, make sure that the current_group name is the same as + // our unique name. + if (current_group->name() != unique_name_) { + NoMemTracking no_tracking_in_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) << " " << current_group->name() + << " != " << unique_name_; + } + + if (!memory_tracker_->IsMemoryTrackingEnabled()) { + NoMemTracking no_tracking_in_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) + << " memory tracking state was disabled."; + } + + const int alloc_size = std::rand() % 100 + 8; + + void* memory = SbMemoryAllocate(alloc_size); + + AllocationRecord record; + bool found = memory_tracker_->GetMemoryTracking(memory, &record); + if (!found) { + NoMemTracking no_tracking_in_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) + << "Violated expectation, malloc counter: " << do_malloc_counter_; + } + AllocMap::iterator found_it = allocated_pts_.find(memory); + + if (found_it != allocated_pts_.end()) { + NoMemTracking no_tracking_in_scope; + ADD_FAILURE_AT(__FILE__, __LINE__) + << "This pointer should not be in the map."; + } + + NoMemTracking no_tracking_in_scope; // DEBUG!! + allocated_pts_[memory] = AllocationRecord(alloc_size, current_group); +} + +} // namespace +} // namespace analytics +} // namespace nb
diff --git a/src/nb/analytics/memory_tracker_test.cc b/src/nb/analytics/memory_tracker_test.cc new file mode 100644 index 0000000..92cb8b3 --- /dev/null +++ b/src/nb/analytics/memory_tracker_test.cc
@@ -0,0 +1,222 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/analytics/memory_tracker.h" +#include "nb/analytics/memory_tracker_helpers.h" +#include "nb/memory_scope.h" +#include "nb/scoped_ptr.h" +#include "starboard/memory.h" +#include "starboard/memory_reporter.h" +#include "starboard/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace nb { +namespace analytics { +namespace { + +MemoryTracker* s_memory_tracker_ = NULL; + +struct NoMemTracking { + bool prev_val; + NoMemTracking() : prev_val(false) { + if (s_memory_tracker_) { + prev_val = s_memory_tracker_->IsMemoryTrackingEnabled(); + s_memory_tracker_->SetMemoryTrackingEnabled(false); + } + } + ~NoMemTracking() { + if (s_memory_tracker_) { + s_memory_tracker_->SetMemoryTrackingEnabled(prev_val); + } + } +}; + +// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when +// for unit testing allocations. These overrides disable memory +// tracking for the duration of the EXPECT and ASSERT operations. +#define EXPECT_EQ_NO_TRACKING(A, B) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_EQ(A, B); \ + } + +#define EXPECT_TRUE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_TRUE(A); \ + } + +#define EXPECT_FALSE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_FALSE(A); \ + } + +#define ASSERT_TRUE_NO_TRACKING(A) \ + { \ + NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_TRUE(A); \ + } + +/////////////////////////////////////////////////////////////////////////////// +// Framework which initializes the MemoryTracker once and installs it +// for the first test and the removes the MemoryTracker after the +// the last test finishes. +class MemoryTrackerTest : public ::testing::Test { + public: + MemoryTrackerTest() {} + + MemoryTracker* memory_tracker() { return s_memory_tracker_; } + + bool GetAllocRecord(void* alloc_memory, AllocationRecord* output) { + return memory_tracker()->GetMemoryTracking(alloc_memory, output); + } + + int64_t TotalNumberOfAllocations() { + return memory_tracker()->GetTotalNumberOfAllocations(); + } + + int64_t TotalAllocationBytes() { + return memory_tracker()->GetTotalAllocationBytes(); + } + + bool MemoryTrackerEnabled() const { return s_memory_tracker_enabled_; } + + protected: + static void SetUpTestCase() { + s_memory_tracker_ = MemoryTracker::Get(); + s_memory_tracker_enabled_ = s_memory_tracker_->InstallGlobalTrackingHooks(); + } + static void TearDownTestCase() { + s_memory_tracker_->RemoveGlobalTrackingHooks(); + } + static bool s_memory_tracker_enabled_; +}; +bool MemoryTrackerTest::s_memory_tracker_enabled_ = false; + +/////////////////////////////////////////////////////////////////////////////// +class FindAllocationVisitor : public AllocationVisitor { + public: + FindAllocationVisitor() : found_(false), memory_to_find_(NULL) {} + + bool found() const { return found_; } + void set_found(bool val) { found_ = val; } + void set_memory_to_find(const void* memory) { + memory_to_find_ = memory; + found_ = false; + } + + virtual bool Visit(const void* memory, const AllocationRecord& alloc_record) { + if (memory_to_find_ == memory) { + found_ = true; + return false; + } + return true; + } + + private: + bool found_; + const void* memory_to_find_; +}; + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(MemoryTrackerTest, MacrosScopedObject) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + + scoped_ptr<int> alloc_a, alloc_b; + { + TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA"); + alloc_a.reset(new int()); + { + TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeB"); + alloc_b.reset(new int()); + } + } + + // Now test that the allocations now exist in the memory tracker. + AllocationRecord alloc_record_a, alloc_record_b; + // Expect that the allocations exist and that the AllocRecords are written + // with the allocation information. + EXPECT_TRUE_NO_TRACKING( + memory_tracker()->GetMemoryTracking(alloc_a.get(), &alloc_record_a)); + EXPECT_TRUE_NO_TRACKING( + memory_tracker()->GetMemoryTracking(alloc_b.get(), &alloc_record_b)); + + // Sanity test that the allocations are non-null. + + const AllocationGroup* group_a = alloc_record_a.allocation_group; + const AllocationGroup* group_b = alloc_record_b.allocation_group; + ASSERT_TRUE_NO_TRACKING(group_a); + ASSERT_TRUE_NO_TRACKING(group_b); + + EXPECT_EQ_NO_TRACKING(group_a->name(), + std::string("MemoryTrackerTest-ScopeA")); + EXPECT_EQ_NO_TRACKING(group_b->name(), + std::string("MemoryTrackerTest-ScopeB")); + + // When the allocation is returned to the free store then it's expected that + // the memory tracker will indicate that the allocation no longer exists. + alloc_a.reset(); + alloc_b.reset(); + + EXPECT_FALSE_NO_TRACKING( + memory_tracker()->GetMemoryTracking(alloc_a.get(), &alloc_record_a)); + EXPECT_FALSE_NO_TRACKING( + memory_tracker()->GetMemoryTracking(alloc_b.get(), &alloc_record_b)); + + int32_t num_allocations = -1; + int64_t allocation_bytes = -1; + + group_a->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); + + group_b->GetAggregateStats(&num_allocations, &allocation_bytes); + EXPECT_EQ_NO_TRACKING(0, num_allocations); + EXPECT_EQ_NO_TRACKING(0, allocation_bytes); +} + +/////////////////////////////////////////////////////////////////////////////// +TEST_F(MemoryTrackerTest, Visitor) { + // Memory tracker is not enabled for this build. + if (!MemoryTrackerEnabled()) { + return; + } + + FindAllocationVisitor visitor; + + scoped_ptr<int> alloc_a; + { + TRACK_MEMORY_SCOPE("MemoryTrackerTest-ScopeA"); + + alloc_a.reset(new int()); + visitor.set_memory_to_find(alloc_a.get()); + memory_tracker()->Accept(&visitor); + EXPECT_TRUE_NO_TRACKING(visitor.found()); + + alloc_a.reset(NULL); + visitor.set_found(false); + memory_tracker()->Accept(&visitor); + EXPECT_FALSE_NO_TRACKING(visitor.found()); + } +} + +} // namespace +} // namespace analytics +} // namespace nb
diff --git a/src/nb/hash.cc b/src/nb/hash.cc new file mode 100644 index 0000000..b04c7d5 --- /dev/null +++ b/src/nb/hash.cc
@@ -0,0 +1,32 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/hash.h" + +#include "starboard/types.h" + +namespace nb { + +namespace { +// Injects the super fast hash into the nb namespace. +#include "third_party/super_fast_hash/super_fast_hash.cc" +} // namespace + +uint32_t RuntimeHash32(const char* data, int length, uint32_t prev_hash) { + return SuperFastHash(data, length, prev_hash); +} + +} // namespace nb
diff --git a/src/nb/hash.h b/src/nb/hash.h new file mode 100644 index 0000000..fe48e3d --- /dev/null +++ b/src/nb/hash.h
@@ -0,0 +1,37 @@ +/* + * Copyright 2016 Google Inc. 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 NB_HASH_H_ +#define NB_HASH_H_ + +#include "starboard/types.h" + +namespace nb { + +enum { kDefaultSeed = 0x12345789 }; + +// RuntimeHash32 is a 32 bit hash for data. The only guarantee is that this +// hash is persistent for the lifetime of the program. This hash function +// is not guaranteed to be consistent across platforms. The hash value should +// never be saved to disk. +// It's sufficient however, to use as an in-memory hashtable. +uint32_t RuntimeHash32(const char* data, + int length, + uint32_t prev_hash = kDefaultSeed); + +} // namespace nb + +#endif // NB_HASH_H_
diff --git a/src/nb/memory_scope.cc b/src/nb/memory_scope.cc new file mode 100644 index 0000000..f475a16 --- /dev/null +++ b/src/nb/memory_scope.cc
@@ -0,0 +1,59 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/memory_scope.h" +#include "starboard/log.h" +#include "starboard/atomic.h" + +NbMemoryScopeReporter* s_memory_reporter_ = NULL; + +bool NbSetMemoryScopeReporter(NbMemoryScopeReporter* reporter) { + // Flush all the pending memory writes out to main memory so that + // other threads see a fully constructed reporter. + SbAtomicMemoryBarrier(); + s_memory_reporter_ = reporter; +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + SbLogRaw("\nMemory Scope Reporting is disabled because this build does " + "not support it. Try a QA, devel or debug build.\n"); + return false; +#else + return true; +#endif +} + +void NbPushMemoryScope(NbMemoryScopeInfo* memory_scope_info) { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter_)) { + return; + } + s_memory_reporter_->push_memory_scope_cb( + s_memory_reporter_->context, + memory_scope_info); +#endif +} + +void NbPopMemoryScope() { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter_)) { + return; + } + s_memory_reporter_->pop_memory_scope_cb(s_memory_reporter_->context); +#endif +}
diff --git a/src/nb/memory_scope.h b/src/nb/memory_scope.h new file mode 100644 index 0000000..9c9446b --- /dev/null +++ b/src/nb/memory_scope.h
@@ -0,0 +1,145 @@ +/* + * Copyright 2016 Google Inc. 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 NB_MEMORY_SCOPE_H_ +#define NB_MEMORY_SCOPE_H_ + +#include "starboard/types.h" + +/////////////////////////////////////////////////////////////////////////////// +// Macros to define the memory scope objects. These are objects that are used +// to annotate sections of the code base as belonging to a particular memory +// scope. Note that this is an annotation and does not memory allocation. +/////////////////////////////////////////////////////////////////////////////// + +// Macro to track the memory scope inside a function or block of code. The +// memory scope is in effect until the end of the code block. +// Example: +// void Foo() { +// TRACK_MEMORY_SCOPE("FooMemoryScope"); +// // pops the memory scope at the end. +// } + +#if !defined(__cplusplus) +// Disallow macro use for non-C++ builds. +#define TRACK_MEMORY_SCOPE(STR) error_forbidden_in_non_c_plus_plus_code +#define TRACK_MEMORY_SCOPE_DYNAMIC(STR) error_forbidden_in_non_c_plus_plus_code +#elif defined(STARBOARD_ALLOWS_MEMORY_TRACKING) +#define TRACK_MEMORY_SCOPE(STR) TRACK_MEMORY_STATIC_CACHED(STR) +#define TRACK_MEMORY_SCOPE_DYNAMIC(STR) TRACK_MEMORY_STATIC_NOT_CACHED(STR) +#else +// No-op when starboard does not allow memory tracking. +#define TRACK_MEMORY_SCOPE(STR) +#define TRACK_MEMORY_SCOPE_DYNAMIC(STR) +#endif + +// Preprocessor needs double expansion in order to __FILE__, __LINE__ and +// __FUNCTION__ properly. +#define TRACK_MEMORY_STATIC_CACHED(STR) \ + TRACK_MEMORY_STATIC_CACHED_IMPL_2(STR, __FILE__, __LINE__, __FUNCTION__) + +#define TRACK_MEMORY_STATIC_NOT_CACHED(STR) \ + TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(STR, __FILE__, __LINE__, __FUNCTION__) + +// Only enable TRACK_MEMORY_STATIC_CACHED_IMPL_2 if starboard allows memory +// tracking. +#define TRACK_MEMORY_STATIC_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \ + static NbMemoryScopeInfo memory_scope_handle_##LineNum = \ + { 0, Str, FileStr, LineNum, FuncStr, true }; \ + NbPushMemoryScope(&memory_scope_handle_##LineNum); \ + NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum; + +#define TRACK_MEMORY_STATIC_NOT_CACHED_IMPL_2(Str, FileStr, LineNum, FuncStr) \ + NbMemoryScopeInfo memory_scope_handle_##LineNum = { \ + 0, Str, FileStr, LineNum, FuncStr, false}; \ + NbPushMemoryScope(&memory_scope_handle_##LineNum); \ + NbPopMemoryScopeOnScopeEnd pop_on_scope_end_##LineNum; + +#ifdef __cplusplus +extern "C" { +#endif + +struct NbMemoryScopeReporter; +struct NbMemoryScopeInfo; + +// Sets the memory reporter. Returns true on success, false something +// goes wrong. +bool NbSetMemoryScopeReporter(NbMemoryScopeReporter* reporter); + +// Note that we pass by pointer because the memory scope contains a +// variable allowing the result to be cached. +void NbPushMemoryScope(NbMemoryScopeInfo* memory_scope); +void NbPopMemoryScope(); + +/////////////////////////////////////////////////////////////////////////////// +// Implementation +/////////////////////////////////////////////////////////////////////////////// +// Interface for handling memory scopes. +typedef void (*NbReportPushMemoryScopeCallback)(void* context, + NbMemoryScopeInfo* info); +typedef void (*NbReportPopMemoryScopeCallback)(void* context); + +struct NbMemoryScopeReporter { + // Callback to report pushing of memory scope. + NbReportPushMemoryScopeCallback push_memory_scope_cb; + + // Callback to report poping of the memory scope. + NbReportPopMemoryScopeCallback pop_memory_scope_cb; + + // Optional, is passed to the callbacks as first argument. + void* context; +}; + +// This MemoryScope must remain a POD data type so that it can be statically +// initialized. +struct NbMemoryScopeInfo { + // cached_handle_ allows a cached result of the the fields represented in + // this struct to be generated and the handle be placed into this field. + // See also allows_caching_. + uintptr_t cached_handle_; + + // Represents the name of the memory scope. I.E. "Javascript" or "Gfx". + const char* memory_scope_name_; + + // Represents the file name that this memory scope was created at. + const char* file_name_; + + // Represents the line number that this memory scope was created at. + int line_number_; + + // Represents the function name that this memory scope was created at. + const char* function_name_; + + // When true, if cached_handle_ is 0 then an object may be created that + // represents the fields of this object. The handle that represents this + // cached object is then placed in cached_hanlde_. + const bool allows_caching_; +}; + +// NbPopMemoryScopeOnScopeEnd is only allowed for C++ builds. +#ifdef __cplusplus +// A helper that pops the memory scope at the end of the current code block. +struct NbPopMemoryScopeOnScopeEnd { + ~NbPopMemoryScopeOnScopeEnd() { NbPopMemoryScope(); } +}; +#endif + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // NB_MEMORY_SCOPE_H_
diff --git a/src/nb/memory_scope_test.cc b/src/nb/memory_scope_test.cc new file mode 100644 index 0000000..e734c2f --- /dev/null +++ b/src/nb/memory_scope_test.cc
@@ -0,0 +1,257 @@ +/* + * Copyright 2016 Google Inc. 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 "nb/memory_scope.h" +#include "starboard/mutex.h" +#include "nb/thread_local_object.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace nb { +namespace { + +bool StarboardAllowsMemoryTracking() { +#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return true; +#else + return false; +#endif +} + +// This is a memory scope reporter that is compatible +// with the MemoryScopeReporter. +class TestMemoryScopeReporter { + public: + typedef std::vector<NbMemoryScopeInfo*> MemoryScopeVector; + + TestMemoryScopeReporter() { + memory_scope_reporter_ = CreateMemoryScopeReporter(); + } + + NbMemoryScopeReporter* memory_scope_reporter() { + return &memory_scope_reporter_; + } + + MemoryScopeVector* stack_thread_local() { return stack_tlo_.GetOrCreate(); } + + void OnPushMemoryScope(NbMemoryScopeInfo* memory_scope) { + stack_thread_local()->push_back(memory_scope); + } + + void OnPopMemoryScope() { + MemoryScopeVector* stack = stack_thread_local(); + if (!stack->empty()) { + stack->pop_back(); + } else { + ADD_FAILURE_AT(__FILE__, __LINE__) + << " stack was empty and could not be popped."; + } + } + + private: + static void OnPushMemoryScopeCallback(void* context, + NbMemoryScopeInfo* info) { + TestMemoryScopeReporter* t = static_cast<TestMemoryScopeReporter*>(context); + t->OnPushMemoryScope(info); + } + + static void OnPopMemoryScopeCallback(void* context) { + TestMemoryScopeReporter* t = static_cast<TestMemoryScopeReporter*>(context); + t->OnPopMemoryScope(); + } + + NbMemoryScopeReporter CreateMemoryScopeReporter() { + NbMemoryScopeReporter reporter = {OnPushMemoryScopeCallback, + OnPopMemoryScopeCallback, this}; + return reporter; + } + + NbMemoryScopeReporter memory_scope_reporter_; + ThreadLocalObject<MemoryScopeVector> stack_tlo_; +}; + +// A test framework for testing the Pushing & popping memory scopes. +// The key feature here is that reporter is setup on the first test +// instance and torn down after the last test has run. +class MemoryScopeReportingTest : public ::testing::Test { + public: + TestMemoryScopeReporter* test_memory_reporter() { return s_reporter_; } + + bool reporting_enabled() const { return s_reporter_enabled_; } + + protected: + static void SetUpTestCase() { + if (!s_reporter_) { + s_reporter_ = new TestMemoryScopeReporter; + } + s_reporter_enabled_ = + NbSetMemoryScopeReporter(s_reporter_->memory_scope_reporter()); + + EXPECT_EQ(StarboardAllowsMemoryTracking(), s_reporter_enabled_) + << "Expected the memory scope reporter to be enabled whenever " + "starboard memory tracking is allowed."; + } + + static void TearDownTestCase() { + // The reporter itself is not deleted because other threads could + // be traversing through it's data structures. It's better just to leave + // the object alive for the purposes of this unit test and set the pointer + // to NULL. + // This is done in order to make the MemoryScopeReport object lock free. + // This increases performance and reduces complexity of design. + NbSetMemoryScopeReporter(NULL); + } + + // Per test setup. + virtual void SetUp() { + test_memory_reporter()->stack_thread_local()->clear(); + } + + static TestMemoryScopeReporter* s_reporter_; + static bool s_reporter_enabled_; +}; +TestMemoryScopeReporter* MemoryScopeReportingTest::s_reporter_ = NULL; +bool MemoryScopeReportingTest::s_reporter_enabled_; + +/////////////////////////////////////////////////////////////////////////////// +// TESTS. +// There are two sets of tests: POSITIVE and NEGATIVE. +// The positive tests are active when STARBOARD_ALLOWS_MEMORY_TRACKING is +// defined and test that memory tracking is enabled. +// NEGATIVE tests ensure that tracking is disabled when +// STARBOARD_ALLOWS_MEMORY_TRACKING is not defined. +// When adding new tests: +// POSITIVE tests are named normally. +// NEGATIVE tests are named with "No" prefixed to the beginning. +// Example: +// TEST_F(MemoryScopeReportingTest, PushPop) <--- POSITIVE test. +// TEST_F(MemoryScopeReportingTest, NoPushPop) <- NEGATIVE test. +// All positive & negative tests are grouped together. +/////////////////////////////////////////////////////////////////////////////// + +#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING) +// These are POSITIVE tests, which test the expectation that when the define +// STARBOARD_ALLOWS_MEMORY_TRACKING is active that the memory scope reporter +// will receive memory scope notifications. + +// Tests the assumption that the SbMemoryAllocate and SbMemoryDeallocate +// will report memory allocations. +TEST_F(MemoryScopeReportingTest, PushPop) { + ASSERT_TRUE(reporting_enabled()); + const int line_number = __LINE__; + const char* file_name = __FILE__; + const char* function_name = __FUNCTION__; + NbMemoryScopeInfo info = {0, // Cached value (null). + "Javascript", // Name of the memory scope. + file_name, // Filename that invoked this. + line_number, // Line number. + function_name, // Function name. + true}; // true allows caching. + + NbPushMemoryScope(&info); + + ASSERT_FALSE(test_memory_reporter()->stack_thread_local()->empty()); + NbMemoryScopeInfo* info_ptr = + test_memory_reporter()->stack_thread_local()->front(); + + EXPECT_EQ(&info, info_ptr); + EXPECT_STREQ(info.file_name_, file_name); + EXPECT_STREQ(info.function_name_, function_name); + EXPECT_EQ(info.line_number_, line_number); + + NbPopMemoryScope(); + EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty()); +} + +// Tests the expectation that the memory reporting macros will +// push/pop memory regions and will also correctly bind to the +// file, linenumber and also the function name. +TEST_F(MemoryScopeReportingTest, Macros) { + ASSERT_TRUE(reporting_enabled()); + // There should be no leftover stack objects. + EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty()); + { + const int line_before = __LINE__; + TRACK_MEMORY_SCOPE("TestMemoryScope"); + const int predicted_line = line_before + 1; + + NbMemoryScopeInfo* info_ptr = + test_memory_reporter()->stack_thread_local()->front(); + + // TRACK_MEMORY_SCOPE is defined to allow caching. + EXPECT_EQ(true, info_ptr->allows_caching_); + + // The cached_handle_ is not mutated by TestMemoryScopeReporter so + // therefore it should be the default value of 0. + EXPECT_EQ(0, info_ptr->cached_handle_); + + EXPECT_STREQ("TestMemoryScope", info_ptr->memory_scope_name_); + EXPECT_STREQ(__FILE__, info_ptr->file_name_); + EXPECT_EQ(predicted_line, info_ptr->line_number_); + EXPECT_STREQ(__FUNCTION__, info_ptr->function_name_); + } + // Expect that the stack object is now empty again. + EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty()); +} + +#else // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) +// These are NEGATIVE tests, which test the expectation that when the +// STARBOARD_ALLOWS_MEMORY_TRACKING is undefined that the memory scope reprter +// does not receive memory scope notifications. + +// Tests the expectation that push pop does not send notifications to the +// reporter when disabled. +TEST_F(MemoryScopeReportingTest, NoPushPop) { + ASSERT_FALSE(reporting_enabled()); + const int line_number = __LINE__; + const char* file_name = __FILE__; + const char* function_name = __FUNCTION__; + NbMemoryScopeInfo info = {0, // Cached value (null). + "Javascript", // Name of the memory scope. + file_name, // Filename that invoked this. + line_number, // Line number. + function_name, // Function name. + true}; // true allows caching. + + NbPushMemoryScope(&info); + + ASSERT_FALSE(test_memory_reporter()->stack_thread_local()->empty()); + NbMemoryScopeInfo* info_ptr = + test_memory_reporter()->stack_thread_local()->front(); + + EXPECT_EQ(&info, info_ptr); + EXPECT_STREQ(info.file_name_, file_name); + EXPECT_STREQ(info.function_name_, function_name); + EXPECT_EQ(info.line_number_, line_number); + + NbPopMemoryScope(); + EXPECT_TRUE(test_memory_reporter()->stack_thread_local()->empty()); +} + +// Tests the expectation that the memory reporting macros are disabled when +// memory tracking is not allowed. +TEST_F(MemoryScopeReportingTest, NoMacros) { + ASSERT_FALSE(reporting_enabled()); + // Test that the macros do nothing when memory reporting has been + // disabled. + TRACK_MEMORY_SCOPE("InternalMemoryRegion"); + ASSERT_TRUE(test_memory_reporter()->stack_thread_local()->empty()) + << "Memory reporting received notifications when it should be disabled."; +} +#endif + +} // namespace. +} // namespace nb.
diff --git a/src/nb/nb.gyp b/src/nb/nb.gyp index f87d42a..855c800 100644 --- a/src/nb/nb.gyp +++ b/src/nb/nb.gyp
@@ -24,11 +24,21 @@ 'allocator.h', 'allocator_decorator.cc', 'allocator_decorator.h', + 'analytics/memory_tracker.cc', + 'analytics/memory_tracker.h', + 'analytics/memory_tracker_impl.cc', + 'analytics/memory_tracker_impl.h', + 'analytics/memory_tracker_helpers.cc', + 'analytics/memory_tracker_helpers.h', 'atomic.h', 'fixed_no_free_allocator.cc', 'fixed_no_free_allocator.h', + 'hash.cc', + 'hash.h', 'memory_pool.cc', 'memory_pool.h', + 'memory_scope.cc', + 'memory_scope.h', 'move.h', 'pointer_arithmetic.h', 'rect.h', @@ -62,8 +72,12 @@ 'target_name': 'nb_test', 'type': '<(gtest_target_type)', 'sources': [ + 'analytics/memory_tracker_helpers_test.cc', + 'analytics/memory_tracker_impl_test.cc', + 'analytics/memory_tracker_test.cc', 'atomic_test.cc', 'fixed_no_free_allocator_test.cc', + 'memory_scope_test.cc', 'reuse_allocator_test.cc', 'run_all_unittests.cc', 'test_thread.h',
diff --git a/src/net/base/file_stream_unittest.cc b/src/net/base/file_stream_unittest.cc index 04b613d..2cdb252 100644 --- a/src/net/base/file_stream_unittest.cc +++ b/src/net/base/file_stream_unittest.cc
@@ -437,6 +437,8 @@ drainable->DidConsume(rv); total_bytes_written += rv; } + rv = stream.FlushSync(); + EXPECT_EQ(OK, rv); ok = file_util::GetFileSize(temp_file_path(), &file_size); EXPECT_TRUE(ok); EXPECT_EQ(file_size, total_bytes_written); @@ -533,6 +535,8 @@ drainable->DidConsume(rv); total_bytes_written += rv; } + rv = stream.FlushSync(); + EXPECT_EQ(OK, rv); ok = file_util::GetFileSize(temp_file_path(), &file_size); EXPECT_TRUE(ok); EXPECT_EQ(file_size, kTestDataSize * 2); @@ -881,6 +885,8 @@ EXPECT_LT(0, rv); EXPECT_EQ(kTestDataSize, total_bytes_written); + rv = stream->FlushSync(); + EXPECT_EQ(OK, rv); stream.reset(); ok = file_util::GetFileSize(temp_file_path(), &file_size);
diff --git a/src/net/quic/crypto/crypto_framer_test.cc b/src/net/quic/crypto/crypto_framer_test.cc index eed175f..b959541 100644 --- a/src/net/quic/crypto/crypto_framer_test.cc +++ b/src/net/quic/crypto/crypto_framer_test.cc
@@ -10,6 +10,7 @@ #include "net/quic/crypto/crypto_framer.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/disabled_if_big_endian.h" using base::StringPiece; using std::map; @@ -62,7 +63,7 @@ } // namespace test -TEST(CryptoFramerTest, ConstructHandshakeMessage) { +TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructHandshakeMessage)) { CryptoHandshakeMessage message; message.tag = 0xFFAA7733; message.tag_value_map[0x12345678] = "abcdef"; @@ -107,7 +108,8 @@ AsChars(packet), arraysize(packet)); } -TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { +TEST(CryptoFramerTest, + DISABLED_IF_BIG_ENDIAN(ConstructHandshakeMessageWithTwoKeys)) { CryptoHandshakeMessage message; message.tag = 0xFFAA7733; message.tag_value_map[0x12345678] = "abcdef"; @@ -166,7 +168,7 @@ EXPECT_TRUE(data.get() == NULL); } -TEST(CryptoFramerTest, ProcessInput) { +TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInput)) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -201,7 +203,7 @@ EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); } -TEST(CryptoFramerTest, ProcessInputIncrementally) { +TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputIncrementally)) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -237,7 +239,7 @@ EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); } -TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { +TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputTagsOutOfOrder)) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor); @@ -275,7 +277,7 @@ EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); } -TEST(CryptoFramerTest, ProcessInputInvalidLength) { +TEST(CryptoFramerTest, DISABLED_IF_BIG_ENDIAN(ProcessInputInvalidLength)) { test::TestCryptoVisitor visitor; CryptoFramer framer; framer.set_visitor(&visitor);
diff --git a/src/net/quic/crypto/null_decrypter_test.cc b/src/net/quic/crypto/null_decrypter_test.cc index ad73cea..f76de31 100644 --- a/src/net/quic/crypto/null_decrypter_test.cc +++ b/src/net/quic/crypto/null_decrypter_test.cc
@@ -4,13 +4,14 @@ #include "net/quic/crypto/null_decrypter.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/disabled_if_big_endian.h" using base::StringPiece; namespace net { namespace test { -TEST(NullDecrypterTest, Decrypt) { +TEST(NullDecrypterTest, DISABLED_IF_BIG_ENDIAN(Decrypt)) { unsigned char expected[] = { // fnv hash 0x47, 0x11, 0xea, 0x5f,
diff --git a/src/net/quic/crypto/null_encrypter_test.cc b/src/net/quic/crypto/null_encrypter_test.cc index 9f2cec2..9f9f2c4 100644 --- a/src/net/quic/crypto/null_encrypter_test.cc +++ b/src/net/quic/crypto/null_encrypter_test.cc
@@ -4,13 +4,14 @@ #include "net/quic/crypto/null_encrypter.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/disabled_if_big_endian.h" using base::StringPiece; namespace net { namespace test { -TEST(NullEncrypterTest, Encrypt) { +TEST(NullEncrypterTest, DISABLED_IF_BIG_ENDIAN(Encrypt)) { unsigned char expected[] = { // fnv hash 0x47, 0x11, 0xea, 0x5f,
diff --git a/src/net/quic/quic_connection_test.cc b/src/net/quic/quic_connection_test.cc index 6e14df7..97c9399 100644 --- a/src/net/quic/quic_connection_test.cc +++ b/src/net/quic/quic_connection_test.cc
@@ -13,6 +13,7 @@ #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/quic_utils.h" +#include "net/test/disabled_if_big_endian.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -794,7 +795,8 @@ EXPECT_TRUE(last_feedback() == NULL); } -TEST_F(QuicConnectionTest, WithQuicCongestionFeedbackFrame) { +TEST_F(QuicConnectionTest, + DISABLED_IF_BIG_ENDIAN(WithQuicCongestionFeedbackFrame)) { QuicCongestionFeedbackFrame info; info.type = kFixRate; info.fix_rate.bitrate_in_bytes_per_second = 123;
diff --git a/src/net/quic/quic_framer_test.cc b/src/net/quic/quic_framer_test.cc index 25c3f39..9ef87dc 100644 --- a/src/net/quic/quic_framer_test.cc +++ b/src/net/quic/quic_framer_test.cc
@@ -16,6 +16,7 @@ #include "net/quic/quic_protocol.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/quic_test_utils.h" +#include "net/test/disabled_if_big_endian.h" using base::hash_set; using base::StringPiece; @@ -249,7 +250,7 @@ EXPECT_EQ(QUIC_INVALID_PACKET_HEADER, framer_.error()); } -TEST_F(QuicFramerTest, LargePacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(LargePacket)) { unsigned char packet[kMaxPacketSize + 1] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -271,13 +272,14 @@ EXPECT_FALSE(framer_.ProcessPacket(self_address_, peer_address_, encrypted)); ASSERT_TRUE(visitor_.header_.get()); + // Make sure we've parsed the packet header, so we can send an error. EXPECT_EQ(GG_UINT64_C(0xFEDCBA9876543210), visitor_.header_->guid); // Make sure the correct error is propogated. EXPECT_EQ(QUIC_PACKET_TOO_LARGE, framer_.error()); } -TEST_F(QuicFramerTest, PacketHeader) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(PacketHeader)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -318,7 +320,7 @@ } } -TEST_F(QuicFramerTest, StreamFrame) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(StreamFrame)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -435,7 +437,7 @@ EXPECT_EQ(0u, visitor_.ack_frames_.size()); } -TEST_F(QuicFramerTest, RevivedStreamFrame) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(RevivedStreamFrame)) { unsigned char payload[] = { // frame count 0x01, @@ -486,7 +488,7 @@ EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } -TEST_F(QuicFramerTest, StreamFrameInFecGroup) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(StreamFrameInFecGroup)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -539,7 +541,7 @@ EXPECT_EQ("hello world!", visitor_.stream_frames_[0]->data); } -TEST_F(QuicFramerTest, AckFrame) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(AckFrame)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -606,7 +608,7 @@ } } -TEST_F(QuicFramerTest, CongestionFeedbackFrameTCP) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameTCP)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -666,7 +668,8 @@ } } -TEST_F(QuicFramerTest, CongestionFeedbackFrameInterArrival) { +TEST_F(QuicFramerTest, + DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameInterArrival)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -775,7 +778,7 @@ } } -TEST_F(QuicFramerTest, CongestionFeedbackFrameFixRate) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(CongestionFeedbackFrameFixRate)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -858,7 +861,7 @@ EXPECT_EQ(QUIC_INVALID_FRAME_DATA, framer_.error()); } -TEST_F(QuicFramerTest, RstStreamFrame) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(RstStreamFrame)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -923,7 +926,7 @@ } } -TEST_F(QuicFramerTest, ConnectionCloseFrame) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConnectionCloseFrame)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -1008,7 +1011,7 @@ } } -TEST_F(QuicFramerTest, FecPacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(FecPacket)) { unsigned char packet[] = { // guid 0x10, 0x32, 0x54, 0x76, @@ -1047,7 +1050,7 @@ EXPECT_EQ("abcdefghijklmnop", fec_data.redundancy); } -TEST_F(QuicFramerTest, ConstructStreamFramePacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructStreamFramePacket)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1102,7 +1105,7 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructAckFramePacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructAckFramePacket)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1154,7 +1157,8 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketTCP) { +TEST_F(QuicFramerTest, + DISABLED_IF_BIG_ENDIAN(ConstructCongestionFeedbackFramePacketTCP)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1201,7 +1205,9 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketInterArrival) { +TEST_F(QuicFramerTest, + DISABLED_IF_BIG_ENDIAN( + ConstructCongestionFeedbackFramePacketInterArrival)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1274,7 +1280,8 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructCongestionFeedbackFramePacketFixRate) { +TEST_F(QuicFramerTest, + DISABLED_IF_BIG_ENDIAN(ConstructCongestionFeedbackFramePacketFixRate)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1337,7 +1344,7 @@ ASSERT_TRUE(data == NULL); } -TEST_F(QuicFramerTest, ConstructRstFramePacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructRstFramePacket)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1393,7 +1400,7 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructCloseFramePacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructCloseFramePacket)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); @@ -1460,7 +1467,7 @@ AsChars(packet), arraysize(packet)); } -TEST_F(QuicFramerTest, ConstructFecPacket) { +TEST_F(QuicFramerTest, DISABLED_IF_BIG_ENDIAN(ConstructFecPacket)) { QuicPacketHeader header; header.guid = GG_UINT64_C(0xFEDCBA9876543210); header.packet_sequence_number = (GG_UINT64_C(0x123456789ABC));
diff --git a/src/net/test/disabled_if_big_endian.h b/src/net/test/disabled_if_big_endian.h new file mode 100644 index 0000000..dfc0eed --- /dev/null +++ b/src/net/test/disabled_if_big_endian.h
@@ -0,0 +1,30 @@ +/* + * Copyright 2015 Google Inc. 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 NET_TEST_DISABLED_IF_BIG_ENDIAN_H_ +#define NET_TEST_DISABLED_IF_BIG_ENDIAN_H_ + +#define DISABLED_IF_BIG_ENDIAN(Test) Test + +#if defined(OS_STARBOARD) +#include "starboard/configuration.h" +#if SB_IS(BIG_ENDIAN) +#undef DISABLED_IF_BIG_ENDIAN +#define DISABLED_IF_BIG_ENDIAN(Test) DISABLED_##Test +#endif // SB_IS(BIG_ENDIAN) +#endif // defined(OS_STARBOARD) + +#endif // NET_TEST_DISABLED_IF_BIG_ENDIAN_H_
diff --git a/src/net/url_request/url_fetcher_core.cc b/src/net/url_request/url_fetcher_core.cc index 2097903..ea36c74 100644 --- a/src/net/url_request/url_fetcher_core.cc +++ b/src/net/url_request/url_fetcher_core.cc
@@ -5,6 +5,7 @@ #include "net/url_request/url_fetcher_core.h" #include "base/bind.h" +#include "base/debug/trace_event.h" #include "base/file_util_proxy.h" #include "base/logging.h" #include "base/single_thread_task_runner.h" @@ -611,6 +612,10 @@ if (!request_->status().is_success() || bytes_read <= 0) break; + if (current_response_bytes_ == 0) { + TRACE_EVENT_ASYNC_STEP0("net::url_request", "URLFetcher", this, + "Fetch Content"); + } current_response_bytes_ += bytes_read; InformDelegateDownloadDataIfNecessary(bytes_read); @@ -710,6 +715,8 @@ void URLFetcherCore::StartURLRequest() { DCHECK(network_task_runner_->BelongsToCurrentThread()); + TRACE_EVENT_ASYNC_STEP0("net::url_request", "URLFetcher", this, + "Waiting For Data"); if (was_cancelled_) { // Since StartURLRequest() is posted as a *delayed* task, it may @@ -801,6 +808,8 @@ } void URLFetcherCore::StartURLRequestWhenAppropriate() { + TRACE_EVENT_ASYNC_BEGIN1("net::url_request", "URLFetcher", this, "url", + original_url_.path()); DCHECK(network_task_runner_->BelongsToCurrentThread()); if (was_cancelled_) @@ -837,6 +846,7 @@ if (request_.get()) { request_->Cancel(); ReleaseRequest(); + TRACE_EVENT_ASYNC_END0("net::url_request", "URLFetcher", this); } // Release the reference to the request context. There could be multiple // references to URLFetcher::Core at this point so it may take a while to @@ -852,6 +862,8 @@ void URLFetcherCore::OnCompletedURLRequest( base::TimeDelta backoff_delay) { + TRACE_EVENT_ASYNC_END1("net::url_request", "URLFetcher", this, "url", + original_url_.path()); DCHECK(delegate_task_runner_->BelongsToCurrentThread()); // Save the status and backoff_delay so that delegates can read it.
diff --git a/src/starboard/README.md b/src/starboard/README.md index 3d35ee3..5e81034 100644 --- a/src/starboard/README.md +++ b/src/starboard/README.md
@@ -160,7 +160,7 @@ In order to use a new platform configuration in a build, you need to ensure that you have a `gyp_configuration.py`, `gyp_configuration.gypi`, and -`starboard_platform.gypi` in their own directory for each binary variant, plus +`starboard_platform.gyp` in their own directory for each binary variant, plus the header files `configuration_public.h`, `atomic_public.h`, and `thread_types_public.h`. `gyp_cobalt` will scan your directories for these files, and then calculate a port name based on the directories between
diff --git a/src/starboard/atomic.h b/src/starboard/atomic.h index e47391f..243c4ea 100644 --- a/src/starboard/atomic.h +++ b/src/starboard/atomic.h
@@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Module Overview: Starboard Atomic API +// // Defines a set of atomic integer operations that can be used as lightweight -// synchronization or as building blocks for heavier synchronization -// primitives. Their use is very subtle and requires detailed understanding of -// the behavior of supported architectures, so their direct use is not -// recommended except when rigorously deemed absolutely necessary for -// performance reasons. +// synchronization or as building blocks for heavier synchronization primitives. +// Their use is very subtle and requires detailed understanding of the behavior +// of supported architectures, so their direct use is not recommended except +// when rigorously deemed absolutely necessary for performance reasons. #ifndef STARBOARD_ATOMIC_H_ #define STARBOARD_ATOMIC_H_
diff --git a/src/starboard/blitter.h b/src/starboard/blitter.h index d183475..e807f63 100644 --- a/src/starboard/blitter.h +++ b/src/starboard/blitter.h
@@ -12,14 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Starboard Blitter API. The Blitter API provides support for issuing simple -// blit-style draw commands to either an offscreen surface or to a Starboard -// SbWindow object. This API is designed to allow implementations make use -// of GPU hardware acceleration, if it is available. Draw commands exist for -// solid-color rectangles and rasterization/blitting of rectangular images onto +// Module Overview: Starboard Blitter API +// +// The Blitter API provides support for issuing simple blit-style draw +// commands to either an offscreen surface or to a Starboard SbWindow object. +// Blitter is jargon that means "BLock Transfer," which might be abbreviated +// as BLT and, hence, the word "blit." +// +// This API is designed to allow implementations make use of GPU hardware +// acceleration, if it is available. Draw commands exist for solid-color +// rectangles and rasterization/blitting of rectangular images onto // rectangular target patches. - -// Threading Concerns: +// +// #### Threading Concerns +// // Note that in general the Blitter API is not thread safe, except for all // SbBlitterDevice-related functions. All functions that are not required to // internally ensure any thread safety guarantees are prefaced with a comment @@ -97,12 +103,16 @@ #define kSbBlitterInvalidSurface ((SbBlitterSurface)NULL) // SbBlitterContext objects represent a stateful communications channel with -// a device. All state changes and draw calls will be made through a specific -// SbBlitterContext object. Every draw call made on a SbBlitterContext will -// be submitted to the device with the SbBlitterContext's current state applied -// to it. Draw calls may be submitted to the device as they are made on the -// SbBlitterContext, however they are not guaranteed to be submitted until -// the SbBlitterContext object is flushed. +// a device. All state changes and draw calls are made through a specific +// SbBlitterContext object. Every draw call made on a SbBlitterContext is +// submitted to the device with the SbBlitterContext's current state applied +// to it. +// +// Draw calls may be submitted to the device as they are made on the +// SbBlitterContext. However, they are not guaranteed to be submitted until +// the SbBlitterContext object is flushed. That is, until you call +// SbBlitterFlushContext, you are not guaranteed that any API calls you have +// made have been received or acted on by the graphics device. typedef struct SbBlitterContextPrivate SbBlitterContextPrivate; typedef SbBlitterContextPrivate* SbBlitterContext; #define kSbBlitterInvalidContext ((SbBlitterContext)NULL) @@ -283,66 +293,84 @@ return context != kSbBlitterInvalidContext; } -// Creates and returns a SbBlitterDevice object based on the Blitter API -// implementation's decision of which device should be default. The -// SbBlitterDevice object represents a connection to a device (like a GPU). On -// many platforms there is always one single obvious choice for a device to use, -// and that is the one that this function will return. For example, if this is -// called on a platform that has a single GPU, a device representing that GPU -// should be returned by this call. On a platform that has no GPU, a device -// representing a software CPU implementation may be returned. Only one -// default device can exist within a process at a time. -// This function is thread safe. +// Creates and returns an SbBlitterDevice based on the Blitter API +// implementation's decision of which device should be the default. The +// returned SbBlitterDevice represents a connection to a device (like a GPU). +// +// On many platforms there is always one single obvious choice for a device +// to use, and that is the one that this function will return. For example, +// if this is called on a platform that has a single GPU, this call should +// return an object that represents that GPU. On a platform that has no GPU, +// an object representing a software CPU implementation may be returned. +// +// Only one default device can exist within a process at a time. +// This function is thread-safe. // Returns kSbBlitterInvalidDevice on failure. SB_EXPORT SbBlitterDevice SbBlitterCreateDefaultDevice(); // Destroys |device|, cleaning up all resources associated with it. -// Returns whether the destruction succeeded. -// This function is thread safe, though of course it should not be called if -// |device| is still being accessed elsewhere. +// This function is thread-safe, but it should not be called if |device| is +// still being accessed elsewhere. +// +// The return value indicates whether the destruction succeeded. +// +// |device|: The SbBlitterDevice object to be destroyed. SB_EXPORT bool SbBlitterDestroyDevice(SbBlitterDevice device); -// Creates and returns a SbBlitterSwapChain object that can be used to send -// graphics to the display. By calling this function, |device| will be linked -// to |window|'s output and drawing to the returned swap chain will result in -// |device| being used to render to |window|. kSbBlitterInvalidSwapChain is -// returned on failure. +// Creates and returns an SbBlitterSwapChain that can then be used to send +// graphics to the display. This function links |device| to |window|'s output, +// and drawing to the returned swap chain will result in |device| being used +// to render to |window|. +// // This function must be called from the thread that called SbWindowCreate() // to create |window|. +// +// Returns kSbBlitterInvalidSwapChain on failure. SB_EXPORT SbBlitterSwapChain SbBlitterCreateSwapChainFromWindow(SbBlitterDevice device, SbWindow window); // Destroys |swap_chain|, cleaning up all resources associated with it. -// This function must be called on the same thread that called -// SbBlitterCreateSwapChainFromWindow(). -// This function is not thread safe. -// Returns the destruction succeeded. +// This function is not thread-safe and must be called on the same thread +// that called SbBlitterCreateSwapChainFromWindow(). +// +// The return value indicates whether the destruction succeeded. +// +// |swap_chain|: The SbBlitterSwapChain to be destroyed. SB_EXPORT bool SbBlitterDestroySwapChain(SbBlitterSwapChain swap_chain); // Returns the |SbBlitterRenderTarget| object that is owned by |swap_chain|. // The returned object can be used to provide a target to blitter draw calls -// wishing to draw directly to the display buffer. -// This function is not thread safe. -// kSbBlitterInvalidRenderTarget is returned on failure. +// that draw directly to the display buffer. This function is not thread-safe. +// +// Returns kSbBlitterInvalidRenderTarget on failure. +// +// |swap_chain|: The SbBlitterSwapChain for which the target object is being +// retrieved. SB_EXPORT SbBlitterRenderTarget SbBlitterGetRenderTargetFromSwapChain(SbBlitterSwapChain swap_chain); -// Returns whether |device| supports calls to SbBlitterCreatePixelData() -// with |pixel_format|. -// This function is thread safe. +// Indicates whether |device| supports calls to SbBlitterCreatePixelData +// with the specified |pixel_format|. This function is thread-safe. +// +// |device|: The device for which compatibility is being checked. +// |pixel_format|: The SbBlitterPixelDataFormat for which compatibility is +// being checked. SB_EXPORT bool SbBlitterIsPixelFormatSupportedByPixelData( SbBlitterDevice device, SbBlitterPixelDataFormat pixel_format); -// Allocates a SbBlitterPixelData object through |device| with |width|, |height| -// and |pixel_format|. |pixel_format| must be supported by |device| (see -// SbBlitterIsPixelFormatSupportedByPixelData()). Calling this function will -// result in the allocation of CPU-accessible (though perhaps -// blitter-device-resident) memory to store pixel data of the requested -// size/format. A SbBlitterPixelData object should either eventually be passed -// into a call to SbBlitterCreateSurfaceFromPixelData(), or passed into a call -// to SbBlitterDestroyPixelData(). -// This function is thread safe. +// Allocates an SbBlitterPixelData object through |device| with |width|, +// |height| and |pixel_format|. |pixel_format| must be supported by |device| +// (see SbBlitterIsPixelFormatSupportedByPixelData()). This function is +// thread-safe. +// +// Calling this function results in the allocation of CPU-accessible +// (though perhaps blitter-device-resident) memory to store pixel data +// of the requested size/format. An SbBlitterPixelData object should +// eventually be passed either into a call to +// SbBlitterCreateSurfaceFromPixelData() or into a call to +// SbBlitterDestroyPixelData(). +// // Returns kSbBlitterInvalidPixelData upon failure. SB_EXPORT SbBlitterPixelData SbBlitterCreatePixelData(SbBlitterDevice device, @@ -350,57 +378,74 @@ int height, SbBlitterPixelDataFormat pixel_format); -// Destroys the |pixel_data| object. Note that -// if SbBlitterCreateSurfaceFromPixelData() has been called on |pixel_data| -// before, this function does not need to be and should not be called. -// This function is thread safe. -// Returns whether the destruction succeeded. +// Destroys |pixel_data|. Note that this function does not need to be called +// and should not be called if SbBlitterCreateSurfaceFromPixelData() has been +// called on |pixel_data| before. This function is thread-safe. +// +// The return value indicates whether the destruction succeeded. +// +// |pixel_data|: The object to be destroyed. SB_EXPORT bool SbBlitterDestroyPixelData(SbBlitterPixelData pixel_data); -// Getter method to return the pitch (in bytes) for |pixel_data|. This -// indicates the number of bytes per row of pixel data in the image. -1 is -// returned in case of an error. -// This function is not thread safe. +// Retrieves the pitch (in bytes) for |pixel_data|. This indicates the number of +// bytes per row of pixel data in the image. This function is not thread-safe. +// +// Returns |-1| in the event of an error. +// +// |pixel_data|: The object for which you are retrieving the pitch. SB_EXPORT int SbBlitterGetPixelDataPitchInBytes(SbBlitterPixelData pixel_data); -// Getter method to return a CPU-accessible pointer to the pixel data -// represented by |pixel_data|. This pixel data can be modified by the CPU -// in order to initialize it on the CPU before calling -// SbBlitterCreateSurfaceFromPixelData(). Note that the pointe returned here -// is valid as long as |pixel_data| is valid, i.e. until either +// Retrieves a CPU-accessible pointer to the pixel data represented by +// |pixel_data|. This pixel data can be modified by the CPU to initialize it +// on the CPU before calling SbBlitterCreateSurfaceFromPixelData(). +// +// Note that the pointer returned here is valid as long as |pixel_data| is +// valid, which means it is valid until either // SbBlitterCreateSurfaceFromPixelData() or SbBlitterDestroyPixelData() is // called. -// This function is not thread safe. -// If there is an error, NULL is returned. +// +// This function is not thread-safe. +// +// Returns |NULL| in the event of an error. SB_EXPORT void* SbBlitterGetPixelDataPointer(SbBlitterPixelData pixel_data); -// Creates a SbBlitterSurface object on |device| (which must match the device -// used to create the input SbBlitterPixelData object, |pixel_format|). -// This function will destroy the input |pixel_data| object and so -// |pixel_data| should not be accessed again after this function is called. -// The returned surface cannot be used as a render target (e.g. calling +// Creates an SbBlitterSurface object on |device|. Note that |device| must +// match the device that was used to create the SbBlitterPixelData object +// provided via the |pixel_data| parameter. +// +// This function also destroys the input |pixel_data| object. As a result, +// |pixel_data| should not be accessed again after a call to this function. +// +// The returned object cannot be used as a render target (e.g. calling // SbBlitterGetRenderTargetFromSurface() on it will return // SbBlitterInvalidRenderTarget). -// This function is thread safe with respect to |device|, however -// |pixel_data| should not be modified on another thread while this function -// is called. -// kSbBlitterInvalidSurface is returned if there was an error. +// +// This function is thread-safe with respect to |device|, but |pixel_data| +// should not be modified on another thread while this function is called. +// +// Returns kSbBlitterInvalidSurface in the event of an error. SB_EXPORT SbBlitterSurface SbBlitterCreateSurfaceFromPixelData(SbBlitterDevice device, SbBlitterPixelData pixel_data); -// Returns whether the |device| supports calls to -// SbBlitterCreateRenderTargetSurface() with |pixel_format|. -// This function is thread safe. +// Indicates whether the |device| supports calls to +// SbBlitterCreateRenderTargetSurface() with |surface_format|. +// +// This function is thread-safe. +// +// |device|: The device being checked for compatibility. +// |surface_format|: The surface format being checked for compatibility. SB_EXPORT bool SbBlitterIsSurfaceFormatSupportedByRenderTargetSurface( SbBlitterDevice device, SbBlitterSurfaceFormat surface_format); // Creates a new surface with undefined pixel data on |device| with the -// specified |width|, |height| and |pixel_format|. One can set the pixel data +// specified |width|, |height| and |surface_format|. One can set the pixel data // on the resulting surface by getting its associated SbBlitterRenderTarget -// object by calling SbBlitterGetRenderTargetFromSurface(). -// This function is thread safe. +// object and then calling SbBlitterGetRenderTargetFromSurface(). +// +// This function is thread-safe. +// // Returns kSbBlitterInvalidSurface upon failure. SB_EXPORT SbBlitterSurface SbBlitterCreateRenderTargetSurface(SbBlitterDevice device, @@ -408,195 +453,278 @@ int height, SbBlitterSurfaceFormat surface_format); -// Destroys |surface|, cleaning up all resources associated with it. +// Destroys the |surface| object, cleaning up all resources associated with it. // This function is not thread safe. -// Returns whether the destruction succeeded. +// +// The return value indicates whether the destruction succeeded. +// +// |surface|: The object to be destroyed. SB_EXPORT bool SbBlitterDestroySurface(SbBlitterSurface surface); // Returns the SbBlitterRenderTarget object owned by |surface|. The returned // object can be used as a target for draw calls. -// This function is not thread safe. -// Returns kSbBlitterInvalidRenderTarget if |surface| is not able to provide a -// render target, or on any other error. +// +// This function returns kSbBlitterInvalidRenderTarget if |surface| is not +// able to provide a render target or on any other error. +// +// This function is not thread-safe. SB_EXPORT SbBlitterRenderTarget SbBlitterGetRenderTargetFromSurface(SbBlitterSurface surface); -// Returns a SbBlitterSurfaceInfo structure describing immutable parameters of -// |surface|, such as width, height and pixel format. The results will be -// set on the output parameter |surface_info| which cannot be NULL. -// This function is not thread safe. -// Returns whether the information was retrieved successfully. +// Retrieves an SbBlitterSurfaceInfo structure, which describes immutable +// parameters of the |surface|, such as its width, height and pixel format. +// The results are set on the output parameter |surface_info|, which cannot +// be NULL. +// +// The return value indicates whether the information was retrieved +// successfully. +// +// This function is not thread-safe. SB_EXPORT bool SbBlitterGetSurfaceInfo(SbBlitterSurface surface, SbBlitterSurfaceInfo* surface_info); -// Returns whether the combination of parameters (|surface|, |pixel_format|) are -// valid for calls to SbBlitterDownloadSurfacePixels(). -// This function is not thread safe. +// Indicates whether the combination of parameter values is valid for calls +// to SbBlitterDownloadSurfacePixels(). +// +// This function is not thread-safe. +// +// |surface|: The surface being checked. +// |pixel_format|: The pixel format that would be used on the surface. SB_EXPORT bool SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels( SbBlitterSurface surface, SbBlitterPixelDataFormat pixel_format); // Downloads |surface| pixel data into CPU memory pointed to by // |out_pixel_data|, formatted according to the requested |pixel_format| and -// the requested |pitch_in_bytes|. Thus, |out_pixel_data| must point to a -// region of memory with a size of surface_height * |pitch_in_bytes| * -// SbBlitterBytesPerPixelForFormat(pixel_format) bytes. The function -// SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels() can be called first -// to check that your requested |pixel_format| is valid for |surface|. When -// this function is called, it will first wait for all previously flushed +// the requested |pitch_in_bytes|. Before calling this function, you can call +// SbBlitterIsPixelFormatSupportedByDownloadSurfacePixels() to confirm that +// |pixel_format| is, in fact, valid for |surface|. +// +// When this function is called, it first waits for all previously flushed // graphics commands to be executed by the device before downloading the data. // Since this function waits for the pipeline to empty, it should be used -// sparingly, such as within in debug or test environments. The returned -// alpha format will be premultiplied. -// This function is not thread safe. -// Returns whether the pixel data was downloaded successfully or not. +// sparingly, such as within in debug or test environments. +// +// The return value indicates whether the pixel data was downloaded +// successfully. +// +// The returned alpha format is premultiplied. +// +// This function is not thread-safe. +// +// |out_pixel_data|: A pointer to a region of memory with a size of +// (surface_height * |pitch_in_bytes| * +// SbBlitterBytesPerPixelForFormat(pixel_format) bytes. SB_EXPORT bool SbBlitterDownloadSurfacePixels( SbBlitterSurface surface, SbBlitterPixelDataFormat pixel_format, int pitch_in_bytes, void* out_pixel_data); -// Flips |swap_chain|, making the buffer that was previously accessible to -// draw commands via SbBlitterGetRenderTargetFromSwapChain() now visible on the -// display, while another buffer in an initially undefined state is setup as the -// draw command target. Note that you do not need to call +// Flips the |swap_chain| by making the buffer previously accessible to +// draw commands via SbBlitterGetRenderTargetFromSwapChain() visible on the +// display, while another buffer in an initially undefined state is set up +// as the new draw command target. Note that you do not need to call // SbBlitterGetRenderTargetFromSwapChain() again after flipping, the swap -// chain's render target will always refer to its current back buffer. This -// function will stall the calling thread until the next vertical refresh. -// Note that to ensure consistency with the Starboard Player API when rendering -// punch-out video, calls to SbPlayerSetBounds() will not take effect until -// this method is called. -// This function is not thread safe. -// Returns whether the flip succeeded. +// chain's render target always refers to its current back buffer. +// +// This function stalls the calling thread until the next vertical refresh. +// In addition, to ensure consistency with the Starboard Player API when +// rendering punch-out video, calls to SbPlayerSetBounds() do not take effect +// until this method is called. +// +// The return value indicates whether the flip succeeded. +// +// This function is not thread-safe. +// +// |swap_chain|: The SbBlitterSwapChain to be flipped. SB_EXPORT bool SbBlitterFlipSwapChain(SbBlitterSwapChain swap_chain); -// Returns the maximum number of contexts that |device| can support in parallel. -// In many cases, devices support only a single context. If -// SbBlitterCreateContext() has been used to create the maximum number of -// contexts, all subsequent calls to SbBlitterCreateContext() will fail. -// This function is thread safe. -// This function returns -1 upon failure. +// Returns the maximum number of contexts that |device| can support in +// parallel. Note that devices often support only a single context. +// +// This function is thread-safe. +// +// This function returns |-1| upon failure. +// +// |device|: The SbBlitterDevice for which the maximum number of contexts is +// returned. SB_EXPORT int SbBlitterGetMaxContexts(SbBlitterDevice device); -// Creates a SbBlitterContext object on the specified |device|. The returned -// context can be used to setup draw state and issue draw calls. Note that -// there is a limit on the number of contexts that can exist at the same time -// (in many cases this is 1), and this can be queried by calling -// SbBlitterGetMaxContexts(). SbBlitterContext objects keep track of draw -// state between a series of draw calls. Please refer to the documentation -// around the definition of SbBlitterContext for more information about -// contexts. -// This function is thread safe. -// This function returns kSbBlitterInvalidContext upon failure. +// Creates an SbBlitterContext object on |device|. The returned context can be +// used to set up draw state and issue draw calls. +// +// Note that there is a limit on the number of contexts that can exist +// simultaneously, which can be queried by calling SbBlitterGetMaxContexts(). +// (The limit is often |1|.) +// +// SbBlitterContext objects keep track of draw state between a series of draw +// calls. Please refer to the SbBlitterContext() definition for more +// information about contexts. +// +// This function is thread-safe. +// +// This function returns kSbBlitterInvalidContext upon failure. Note that the +// function fails if it has already been used to create the maximum number +// of contexts. +// +// |device|: The SbBlitterDevice for which the SbBlitterContext object is +// created. SB_EXPORT SbBlitterContext SbBlitterCreateContext(SbBlitterDevice device); -// Destroys the specified |context| created by |device|, freeing all its -// resources. -// This function is not thread safe. -// Returns whether the destruction succeeded. +// Destroys the specified |context|, freeing all its resources. This function +// is not thread-safe. +// +// The return value indicates whether the destruction succeeded. +// +// |context|: The object to be destroyed. SB_EXPORT bool SbBlitterDestroyContext(SbBlitterContext context); -// Flushes all draw calls previously issued to |context|. After this call, -// all subsequent draw calls (on any context) are guaranteed to be processed -// by the device after all previous draw calls issued on this |context|. -// In many cases you will want to call this before calling -// SbBlitterFlipSwapChain(), to ensure that all draw calls are submitted before -// the flip occurs. -// This function is not thread safe. -// Returns whether the flush succeeded. +// Flushes all draw calls previously issued to |context|. Calling this function +// guarantees that the device processes all draw calls issued to this point on +// this |context| before processing any subsequent draw calls on any context. +// +// Before calling SbBlitterFlipSwapChain(), it is often prudent to call this +// function to ensure that all draw calls are submitted before the flip occurs. +// +// This function is not thread-safe. +// +// The return value indicates whether the flush succeeded. +// +// |context|: The context for which draw calls are being flushed. SB_EXPORT bool SbBlitterFlushContext(SbBlitterContext context); // Sets up |render_target| as the render target that all subsequent draw calls -// made on |context| will draw to. -// This function is not thread safe. -// Returns whether the render target was successfully set. +// made on |context| will use. +// +// This function is not thread-safe. +// +// The return value indicates whether the render target was set successfully. +// +// |context|: The object for which the render target is being set. +// |render_target|: The target that the |context| should use for draw calls. SB_EXPORT bool SbBlitterSetRenderTarget(SbBlitterContext context, SbBlitterRenderTarget render_target); -// Sets blending state on the specified |context|. If |blending| is true, the -// source alpha of subsequent draw calls will be used to blend with the -// destination color. In particular, Fc = Sc * Sa + Dc * (1 - Sa), where -// Fc is the final color, Sc is the source color, Sa is the source alpha, and -// Dc is the destination color. If |blending| is false, the source color and -// alpha will overwrite the destination color and alpha. By default blending +// Sets the blending state for the specified |context|. By default, blending // is disabled on a SbBlitterContext. -// This function is not thread safe. -// Returns whether the blending state was succcessfully set. +// +// This function is not thread-safe. +// +// The return value indicates whether the blending state was set successfully. +// +// |context|: The context for which the blending state is being set. +// |blending|: The blending state for the |context|. +// If |blending| is |true|, the source alpha of subsequent draw calls +// is used to blend with the destination color. In particular, +// (|Fc = Sc * Sa + Dc * (1 - Sa)|), where: +// - |Fc| is the final color. +// - |Sc| is the source color. +// - |Sa| is the source alpha. +// - |Dc| is the destination color. +// If |blending| is |false|, the source color and source alpha overwrite +// the destination color and alpha. SB_EXPORT bool SbBlitterSetBlending(SbBlitterContext context, bool blending); // Sets the context's current color. The current color's default value is -// SbBlitterColorFromRGBA(255, 255, 255 255). The current color affects the -// fill rectangle's color in calls to SbBlitterFillRect(), and if -// SbBlitterSetModulateBlitsWithColor() has been called to enable blit color -// modulation, the source blit surface pixel color will also be modulated by -// the color before being output. The color is specified in unpremultiplied -// alpha format. -// This function is not thread safe. -// Returns whether the color was successfully set. +// |SbBlitterColorFromRGBA(255, 255, 255 255)|. +// +// The current color affects the fill rectangle's color in calls to +// SbBlitterFillRect(). If SbBlitterSetModulateBlitsWithColor() has been called +// to enable blit color modulation, the source blit surface pixel color is also +// modulated by the color before being output. +// +// This function is not thread-safe. +// +// The return value indicates whether the color was set successfully. +// +// |context|: The context for which the color is being set. +// |color|: The context's new color, specified in unpremultiplied alpha format. SB_EXPORT bool SbBlitterSetColor(SbBlitterContext context, SbBlitterColor color); // Sets whether or not blit calls should have their source pixels modulated by -// the current color (set via a call to SbBlitterSetColor()) before being -// output. This can be used to apply opacity to blit calls, as well as for -// coloring alpha-only surfaces, and other effects. -// This function is not thread safe. -// Returns whether the state was successfully set. +// the current color, which is set using SbBlitterSetColor(), before being +// output. This function can apply opacity to blit calls, color alpha-only +// surfaces, and apply other effects. +// +// This function is not thread-safe. +// +// The return value indicates whether the state was set successfully. +// +// |modulate_blits_with_color|: Indicates whether to modulate source pixels +// in blit calls. SB_EXPORT bool SbBlitterSetModulateBlitsWithColor( SbBlitterContext context, bool modulate_blits_with_color); -// Sets the scissor rectangle. The scissor rectangle dictates a rectangle of -// visibility that affects all draw calls. Only pixels within the scissor -// rectangle will be rendered, all drawing outside of the scissor rectangle will -// be clipped. When SbBlitterSetRenderTarget() is called, the scissor rectangle -// is automatically set to the extents of the specified render target. If a +// Sets the scissor rectangle, which dictates a visibility area that affects +// all draw calls. Only pixels within the scissor rectangle are rendered, and +// all drawing outside of that area is clipped. +// +// When SbBlitterSetRenderTarget() is called, that function automatically sets +// the scissor rectangle to the size of the specified render target. If a // scissor rectangle is specified outside of the extents of the current render -// target bounds, it will be intersected with the render target boudns. It is -// an error to call this function before a render target has been specified for -// the context. -// This function is not thread safe. -// Returns whether the scissor was successfully set. +// target bounds, it will be intersected with the render target bounds. +// +// This function is not thread-safe. +// +// Returns whether the scissor was successfully set. It returns an error if +// it is called before a render target has been specified for the context. SB_EXPORT bool SbBlitterSetScissor(SbBlitterContext context, SbBlitterRect rect); -// Issues a draw call on |context| that fills a rectangle |rect|. The color -// of the rectangle is specified by the last call to SbBlitterSetColor(). -// This function is not thread safe. -// Returns whether the draw call succeeded. +// Issues a draw call on |context| that fills the specified rectangle |rect|. +// The rectangle's color is determined by the last call to SbBlitterSetColor(). +// +// This function is not thread-safe. +// +// The return value indicates whether the draw call succeeded. +// +// |context|: The context on which the draw call will operate. +// |rect|: The rectangle to be filled. SB_EXPORT bool SbBlitterFillRect(SbBlitterContext context, SbBlitterRect rect); -// Issues a draw call on |context| that blits an area of |source_surface| -// specified by a |src_rect| to |context|'s current render target at |dst_rect|. -// The source rectangle must lie within the dimensions of |source_surface|. The -// |source_surface|'s alpha will be modulated by |opacity| before being drawn. -// For |opacity|, a value of 0 implies complete invisibility and a value of -// 255 implies complete opaqueness. -// This function is not thread safe. -// Returns whether the draw call succeeded. +// Issues a draw call on |context| that blits the area of |source_surface| +// specified by |src_rect| to |context|'s current render target at |dst_rect|. +// The source rectangle must lie within the dimensions of |source_surface|. +// Note that the |source_surface|'s alpha is modulated by |opacity| before +// being drawn. For |opacity|, a value of 0 implies complete invisibility, +// and a value of 255 implies complete opacity. +// +// This function is not thread-safe. +// +// The return value indicates whether the draw call succeeded. +// +// |src_rect|: The area to be block transferred (blitted). SB_EXPORT bool SbBlitterBlitRectToRect(SbBlitterContext context, SbBlitterSurface source_surface, SbBlitterRect src_rect, SbBlitterRect dst_rect); -// This function does the exact same as SbBlitterBlitRectToRect(), except -// it permits values of |src_rect| outside of the dimensions of -// |source_surface| and in these regions the pixel data from |source_surface| -// will be wrapped. Negative values for |src_rect.x| and |src_rect.y| are -// allowed. The output will all be stretched to fit inside of |dst_rect|. -// This function is not thread safe. -// Returns whether the draw call succeeded. +// This function functions identically to SbBlitterBlitRectToRect(), except +// it permits values of |src_rect| outside the dimensions of |source_surface|. +// In those regions, the pixel data from |source_surface| will be wrapped. +// Negative values for |src_rect.x| and |src_rect.y| are allowed. +// +// The output is all stretched to fit inside of |dst_rect|. +// +// This function is not thread-safe. +// +// The return value indicates whether the draw call succeeded. SB_EXPORT bool SbBlitterBlitRectToRectTiled(SbBlitterContext context, SbBlitterSurface source_surface, SbBlitterRect src_rect, SbBlitterRect dst_rect); // This function achieves the same effect as calling SbBlitterBlitRectToRect() -// |num_rects| time with each of the |num_rects| values of |src_rects| and -// |dst_rects|. Using this function allows for greater efficiency than calling -// SbBlitterBlitRectToRect() in a loop. -// This function is not thread safe. -// Returns whether the draw call succeeded. +// |num_rects| times with each of the |num_rects| values of |src_rects| and +// |dst_rects|. This function allows for greater efficiency than looped calls +// to SbBlitterBlitRectToRect(). +// +// This function is not thread-safe. +// +// The return value indicates whether the draw call succeeded. SB_EXPORT bool SbBlitterBlitRectsToRects(SbBlitterContext context, SbBlitterSurface source_surface, const SbBlitterRect* src_rects,
diff --git a/src/starboard/byte_swap.h b/src/starboard/byte_swap.h index 7c2e968..9fbd1fa 100644 --- a/src/starboard/byte_swap.h +++ b/src/starboard/byte_swap.h
@@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Functions for swapping byte order, used to deal with endianness when -// performing I/O +// Module Overview: Starboard Byte Swap module +// +// Specifies functions for swapping byte order. These functions are used to +// deal with endianness when performing I/O. #ifndef STARBOARD_BYTE_SWAP_H_ #define STARBOARD_BYTE_SWAP_H_ @@ -53,21 +55,27 @@ // and then act on that determination somehow. // Unconditionally swaps the byte order in signed 16-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT int16_t SbByteSwapS16(int16_t value); // Unconditionally swaps the byte order in unsigned 16-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT uint16_t SbByteSwapU16(uint16_t value); // Unconditionally swaps the byte order in signed 32-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT int32_t SbByteSwapS32(int32_t value); // Unconditionally swaps the byte order in unsigned 32-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT uint32_t SbByteSwapU32(uint32_t value); // Unconditionally swaps the byte order in signed 64-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT int64_t SbByteSwapS64(int64_t value); // Unconditionally swaps the byte order in unsigned 64-bit |value|. +// |value|: The value for which the byte order will be swapped. SB_EXPORT uint64_t SbByteSwapU64(uint64_t value); #ifdef __cplusplus
diff --git a/src/starboard/character.h b/src/starboard/character.h index f4d064f..f7758c7 100644 --- a/src/starboard/character.h +++ b/src/starboard/character.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Functions for interacting with characters. +// Module Overview: Starboard Character module +// +// Provides functions for interacting with characters. #ifndef STARBOARD_CHARACTER_H_ #define STARBOARD_CHARACTER_H_ @@ -26,36 +28,50 @@ #endif // Converts the given 8-bit character (as an int) to uppercase in the current -// locale, returning the result, also an 8-bit character. If there is no -// uppercase version of the character, or the character is already uppercase, it -// will just return the character as-is. -int SbCharacterToUpper(int c); +// locale and returns an 8-bit character. If there is no uppercase version of +// the character, or the character is already uppercase, the function just +// returns the character as-is. +// +// |c|: The character to be converted. +SB_EXPORT int SbCharacterToUpper(int c); // Converts the given 8-bit character (as an int) to lowercase in the current -// locale, returning the result, also an 8-bit character. If there is no -// lowercase version of the character, or the character is already lowercase, it -// will just return the character as-is. -int SbCharacterToLower(int c); +// locale and returns an 8-bit character. If there is no lowercase version of +// the character, or the character is already lowercase, the function just +// returns the character as-is. +// +// |c|: The character to be converted. +SB_EXPORT int SbCharacterToLower(int c); -// Returns whether the given 8-bit character |c| (as an int) is a space in the -// current locale. -bool SbCharacterIsSpace(int c); - -// Returns whether the given 8-bit character |c| (as an int) is uppercase in the -// current locale. -bool SbCharacterIsUpper(int c); - -// Returns whether the given 8-bit character |c| (as an int) is a decimal digit -// in the current locale. -bool SbCharacterIsDigit(int c); - -// Returns whether the given 8-bit character |c| (as an int) is a hexidecimal -// digit in the current locale. -bool SbCharacterIsHexDigit(int c); - -// Returns whether the given 8-bit character |c| (as an int) is alphanumeric in +// Indicates whether the given 8-bit character |c| (as an int) is a space in // the current locale. -bool SbCharacterIsAlphanumeric(int c); +// +// |c|: The character to be evaluated. +SB_EXPORT bool SbCharacterIsSpace(int c); + +// Indicates whether the given 8-bit character |c| (as an int) is uppercase +// in the current locale. +// +// |c|: The character to be evaluated. +SB_EXPORT bool SbCharacterIsUpper(int c); + +// Indicates whether the given 8-bit character |c| (as an int) is a +// decimal digit in the current locale. +// +// |c|: The character to be evaluated. +SB_EXPORT bool SbCharacterIsDigit(int c); + +// Indicates whether the given 8-bit character |c| (as an int) is a hexadecimal +// in the current locale. +// +// |c|: The character to be evaluated. +SB_EXPORT bool SbCharacterIsHexDigit(int c); + +// Indicates whether the given 8-bit character |c| (as an int) is alphanumeric +// in the current locale. +// +// |c|: The character to be evaluated. +SB_EXPORT bool SbCharacterIsAlphanumeric(int c); #ifdef __cplusplus } // extern "C"
diff --git a/src/starboard/client_porting/eztime/eztime.h b/src/starboard/client_porting/eztime/eztime.h index 8402006..8221c2f 100644 --- a/src/starboard/client_porting/eztime/eztime.h +++ b/src/starboard/client_porting/eztime/eztime.h
@@ -15,7 +15,9 @@ #ifndef STARBOARD_CLIENT_PORTING_EZTIME_EZTIME_H_ #define STARBOARD_CLIENT_PORTING_EZTIME_EZTIME_H_ +#include "starboard/log.h" #include "starboard/time.h" +#include "starboard/types.h" #ifdef __cplusplus extern "C" { @@ -119,7 +121,12 @@ // Converts SbTime to EzTimeValue. static SB_C_FORCE_INLINE EzTimeValue EzTimeValueFromSbTime(SbTime in_time) { EzTimeT sec = EzTimeTFromSbTime(in_time); - EzTimeValue value = {sec, in_time - EzTimeTToSbTime(sec)}; + SbTime diff = in_time - EzTimeTToSbTime(sec); + SB_DCHECK(diff >= INT_MIN); + SB_DCHECK(diff <= INT_MAX); + EzTimeValue value = {sec, (int)diff}; // Some compilers do not support + // returning the initializer list + // directly. return value; }
diff --git a/src/starboard/common/common.gyp b/src/starboard/common/common.gyp index e354d5e..c6a509b 100644 --- a/src/starboard/common/common.gyp +++ b/src/starboard/common/common.gyp
@@ -28,8 +28,12 @@ 'decode_target_provider.cc', 'memory.cc', 'move.h', + 'ref_counted.cc', + 'ref_counted.h', 'reset_and_return.h', 'scoped_ptr.h', + 'thread_collision_warner.cc', + 'thread_collision_warner.h', ], 'defines': [ # This must be defined when building Starboard, and must not when
diff --git a/src/starboard/common/memory.cc b/src/starboard/common/memory.cc index 6067214..4ed3362 100644 --- a/src/starboard/common/memory.cc +++ b/src/starboard/common/memory.cc
@@ -12,32 +12,87 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "starboard/atomic.h" +#include "starboard/log.h" #include "starboard/memory.h" +#include "starboard/memory_reporter.h" +#include "starboard/shared/starboard/memory_reporter_internal.h" +namespace { inline void* SbMemoryAllocateImpl(size_t size); inline void* SbMemoryAllocateAlignedImpl(size_t alignment, size_t size); inline void* SbMemoryReallocateImpl(void* memory, size_t size); +inline void SbReportAllocation(const void* memory, size_t size); +inline void SbReportDeallocation(const void* memory); + +SbMemoryReporter* s_memory_reporter = NULL; + +bool LeakTraceEnabled(); // True when leak tracing enabled. +bool StarboardAllowsMemoryTracking(); // True when build enabled. +} // namespace. + +bool SbMemorySetReporter(SbMemoryReporter* reporter) { + // TODO: We should run a runtime test here with a test memory + // reporter that determines whether global operator new/delete are properly + // overridden. This problem appeared with address sanitizer and given + // how tricky operator new/delete are in general (see a google search) + // it's reasonable to assume that the likely hood of this happening again + // is high. To allow the QA/developer to take corrective action, this + // condition needs to be detected at runtime with a simple error message so + // that corrective action (i.e. running a different build) can be applied. + // + // RunOperatorNewDeleteRuntimeTest(); // Implement me. + + // Flush local memory to main so that other threads don't + // see a partially constructed reporter due to memory + // re-ordering. + SbAtomicMemoryBarrier(); + s_memory_reporter = reporter; + + // These are straight forward error messages. We use the build settings to + // predict whether the MemoryReporter is likely to fail. + if (!StarboardAllowsMemoryTracking()) { + SbLogRaw("\nMemory Reporting is disabled because this build does " + "not support it. Try a QA, devel or debug build.\n"); + return false; + } else if (LeakTraceEnabled()) { + SbLogRaw("\nMemory Reporting might be disabled because leak trace " + "(from address sanitizer?) is active.\n"); + return false; + } + return true; +} void* SbMemoryAllocate(size_t size) { void* memory = SbMemoryAllocateImpl(size); + SbReportAllocation(memory, size); return memory; } void* SbMemoryAllocateAligned(size_t alignment, size_t size) { void* memory = SbMemoryAllocateAlignedImpl(alignment, size); + SbReportAllocation(memory, size); return memory; } void* SbMemoryReallocate(void* memory, size_t size) { + SbReportDeallocation(memory); void* new_memory = SbMemoryReallocateImpl(memory, size); + SbReportAllocation(new_memory, size); return new_memory; } void SbMemoryDeallocate(void* memory) { + // Report must happen first or else a race condition allows the memory to + // be freed and then reported as allocated, before the allocation is removed. + SbReportDeallocation(memory); SbMemoryFree(memory); } void SbMemoryDeallocateAligned(void* memory) { + // Report must happen first or else a race condition allows the memory to + // be freed and then reported as allocated, before the allocation is removed. + SbReportDeallocation(memory); SbMemoryFreeAligned(memory); } @@ -63,6 +118,63 @@ return address; } +void SbMemoryReporterReportMappedMemory(const void* memory, size_t size) { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter)) { + return; + } + s_memory_reporter->on_mapmem_cb( + s_memory_reporter->context, + memory, + size); +#endif // STARBOARD_ALLOWS_MEMORY_TRACKING +} + +void SbMemoryReporterReportUnmappedMemory(const void* memory, size_t size) { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter)) { + return; + } + s_memory_reporter->on_unmapmem_cb( + s_memory_reporter->context, + memory, + size); +#endif // STARBOARD_ALLOWS_MEMORY_TRACKING +} + +namespace { // anonymous namespace. + +inline void SbReportAllocation(const void* memory, size_t size) { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter)) { + return; + } + s_memory_reporter->on_alloc_cb( + s_memory_reporter->context, + memory, + size); +#endif // STARBOARD_ALLOWS_MEMORY_TRACKING +} + +inline void SbReportDeallocation(const void* memory) { +#if !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return; +#else + if (SB_LIKELY(!s_memory_reporter)) { + return; + } + s_memory_reporter->on_dealloc_cb( + s_memory_reporter->context, + memory); +#endif // STARBOARD_ALLOWS_MEMORY_TRACKING +} + inline void* SbMemoryAllocateImpl(size_t size) { #if SB_ABORT_ON_ALLOCATION_FAILURE return SbMemoryAllocateChecked(size); @@ -86,3 +198,25 @@ return SbMemoryReallocateUnchecked(memory, size); #endif } + +bool LeakTraceEnabled() { +#if defined(HAS_LEAK_SANITIZER) && (0 == HAS_LEAK_SANITIZER) + // In this build the leak tracer is specifically disabled. This + // build condition is typical for some builds of address sanitizer. + return true; +#elif defined(ADDRESS_SANITIZER) + // Leak tracer is not specifically disabled and address sanitizer is running. + return true; +#else + return false; +#endif +} + +bool StarboardAllowsMemoryTracking() { +#if defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + return true; +#else + return false; +#endif +} +} // namespace
diff --git a/src/starboard/common/ref_counted.cc b/src/starboard/common/ref_counted.cc new file mode 100644 index 0000000..32f67d5 --- /dev/null +++ b/src/starboard/common/ref_counted.cc
@@ -0,0 +1,95 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "starboard/common/ref_counted.h" + +#include "starboard/log.h" + +namespace starboard { + +namespace subtle { + +RefCountedBase::RefCountedBase() + : ref_count_(0) +#ifndef NDEBUG + , + in_dtor_(false) +#endif +{ +} + +RefCountedBase::~RefCountedBase() { +#ifndef NDEBUG + SB_DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; +#endif +} + +void RefCountedBase::AddRef() const { +// TODO(maruel): Add back once it doesn't assert 500 times/sec. +// Current thread books the critical section "AddRelease" without release it. +// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + SB_DCHECK(!in_dtor_); +#endif + ++ref_count_; +} + +bool RefCountedBase::Release() const { +// TODO(maruel): Add back once it doesn't assert 500 times/sec. +// Current thread books the critical section "AddRelease" without release it. +// DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + SB_DCHECK(!in_dtor_); +#endif + if (--ref_count_ == 0) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +bool RefCountedThreadSafeBase::HasOneRef() const { + return (SbAtomicAcquire_Load( + &const_cast<RefCountedThreadSafeBase*>(this)->ref_count_) == 1); +} + +RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) { +#ifndef NDEBUG + in_dtor_ = false; +#endif +} + +RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { +#ifndef NDEBUG + SB_DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " + "calling Release()"; +#endif +} + +void RefCountedThreadSafeBase::AddRef() const { +#ifndef NDEBUG + SB_DCHECK(!in_dtor_); +#endif + SbAtomicNoBarrier_Increment(&ref_count_, 1); +} + +bool RefCountedThreadSafeBase::Release() const { +#ifndef NDEBUG + SB_DCHECK(!in_dtor_); + SB_DCHECK(!(SbAtomicAcquire_Load(&ref_count_) == 0)); +#endif + if (SbAtomicBarrier_Increment(&ref_count_, -1) == 0) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +} // namespace subtle + +} // namespace starboard
diff --git a/src/starboard/common/ref_counted.h b/src/starboard/common/ref_counted.h new file mode 100644 index 0000000..0ab74db --- /dev/null +++ b/src/starboard/common/ref_counted.h
@@ -0,0 +1,288 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef STARBOARD_COMMON_REF_COUNTED_H_ +#define STARBOARD_COMMON_REF_COUNTED_H_ + +#include "starboard/atomic.h" +#include "starboard/common/thread_collision_warner.h" +#include "starboard/log.h" + +namespace starboard { + +namespace subtle { + +class RefCountedBase { + public: + bool HasOneRef() const { return ref_count_ == 1; } + + protected: + RefCountedBase(); + ~RefCountedBase(); + + void AddRef() const; + + // Returns true if the object should self-delete. + bool Release() const; + + private: + mutable int ref_count_; +#ifndef NDEBUG + mutable bool in_dtor_; +#endif + + DFAKE_MUTEX(add_release_); +}; + +class RefCountedThreadSafeBase { + public: + bool HasOneRef() const; + + protected: + RefCountedThreadSafeBase(); + ~RefCountedThreadSafeBase(); + + void AddRef() const; + + // Returns true if the object should self-delete. + bool Release() const; + + private: + mutable SbAtomic32 ref_count_; +#ifndef NDEBUG + mutable bool in_dtor_; +#endif +}; + +} // namespace subtle + +// +// A base class for reference counted classes. Otherwise, known as a cheap +// knock-off of WebKit's RefCounted<T> class. To use this guy just extend your +// class from it like so: +// +// class MyFoo : public starboard::RefCounted<MyFoo> { +// ... +// private: +// friend class starboard::RefCounted<MyFoo>; +// ~MyFoo(); +// }; +// +// You should always make your destructor private, to avoid any code deleting +// the object accidently while there are references to it. +template <class T> +class RefCounted : public subtle::RefCountedBase { + public: + RefCounted() {} + + void AddRef() const { subtle::RefCountedBase::AddRef(); } + + void Release() const { + if (subtle::RefCountedBase::Release()) { + delete static_cast<const T*>(this); + } + } + + protected: + ~RefCounted() {} +}; + +// Forward declaration. +template <class T, typename Traits> +class RefCountedThreadSafe; + +// Default traits for RefCountedThreadSafe<T>. Deletes the object when its ref +// count reaches 0. Overload to delete it on a different thread etc. +template <typename T> +struct DefaultRefCountedThreadSafeTraits { + static void Destruct(const T* x) { + // Delete through RefCountedThreadSafe to make child classes only need to be + // friend with RefCountedThreadSafe instead of this struct, which is an + // implementation detail. + RefCountedThreadSafe<T, DefaultRefCountedThreadSafeTraits>::DeleteInternal( + x); + } +}; + +// +// A thread-safe variant of RefCounted<T> +// +// class MyFoo : public starboard::RefCountedThreadSafe<MyFoo> { +// ... +// }; +// +// If you're using the default trait, then you should add compile time +// asserts that no one else is deleting your object. i.e. +// private: +// friend class starboard::RefCountedThreadSafe<MyFoo>; +// ~MyFoo(); +template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> > +class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { + public: + RefCountedThreadSafe() {} + + void AddRef() const { subtle::RefCountedThreadSafeBase::AddRef(); } + + void Release() const { + if (subtle::RefCountedThreadSafeBase::Release()) { + Traits::Destruct(static_cast<const T*>(this)); + } + } + + protected: + ~RefCountedThreadSafe() {} + + private: + friend struct DefaultRefCountedThreadSafeTraits<T>; + static void DeleteInternal(const T* x) { delete x; } +}; + +// +// A thread-safe wrapper for some piece of data so we can place other +// things in scoped_refptrs<>. +// +template <typename T> +class RefCountedData : public starboard::RefCountedThreadSafe<starboard::RefCountedData<T> > { + public: + RefCountedData() : data() {} + RefCountedData(const T& in_value) : data(in_value) {} + + T data; + + private: + friend class starboard::RefCountedThreadSafe<starboard::RefCountedData<T> >; + ~RefCountedData() {} +}; + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted<MyFoo> { +// ... +// }; +// +// void some_function() { +// scoped_refptr<MyFoo> foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr<MyFoo> foo = new MyFoo(); +// ... +// foo = NULL; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr<T> acts like a pointer to T. +// Given two scoped_refptr<T> classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr<MyFoo> a = new MyFoo(); +// scoped_refptr<MyFoo> b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references NULL. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr<MyFoo> a = new MyFoo(); +// scoped_refptr<MyFoo> b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +template <class T> +class scoped_refptr { + public: + typedef T element_type; + + scoped_refptr() : ptr_(NULL) {} + + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + template <typename U> + scoped_refptr(const scoped_refptr<U>& r) + : ptr_(r.get()) { + if (ptr_) + ptr_->AddRef(); + } + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { + SB_DCHECK(ptr_ != NULL); + return ptr_; + } + + T& operator*() const { + SB_DCHECK(ptr_ != NULL); + return *ptr_; + } + + scoped_refptr<T>& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + T* old_ptr = ptr_; + ptr_ = p; + if (old_ptr) + old_ptr->Release(); + return *this; + } + + scoped_refptr<T>& operator=(const scoped_refptr<T>& r) { + return * this = r.ptr_; + } + + template <typename U> + scoped_refptr<T>& operator=(const scoped_refptr<U>& r) { + return * this = r.get(); + } + + void swap(T** pp) { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr<T>& r) { swap(&r.ptr_); } + + protected: + T* ptr_; +}; + +// Handy utility for creating a scoped_refptr<T> out of a T* explicitly without +// having to retype all the template arguments +template <typename T> +scoped_refptr<T> make_scoped_refptr(T* t) { + return scoped_refptr<T>(t); +} + +} // namespace starboard + +#endif // STARBOARD_COMMON_REF_COUNTED_H_
diff --git a/src/starboard/common/thread_collision_warner.cc b/src/starboard/common/thread_collision_warner.cc new file mode 100644 index 0000000..5a9e853 --- /dev/null +++ b/src/starboard/common/thread_collision_warner.cc
@@ -0,0 +1,63 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "starboard/common/thread_collision_warner.h" + +#include "starboard/atomic.h" +#include "starboard/log.h" +#include "starboard/thread.h" +#include "starboard/types.h" + +namespace starboard { + +void DCheckAsserter::warn() { + SB_NOTREACHED() << "Thread Collision"; +} + +static SbAtomic32 CurrentThread() { + const SbThreadId current_thread_id = SbThreadGetId(); + // We need to get the thread id into an atomic data type. This might be a + // truncating conversion, but any loss-of-information just increases the + // chance of a false negative, not a false positive. + const SbAtomic32 atomic_thread_id = + static_cast<SbAtomic32>(current_thread_id); + return atomic_thread_id; +} + +void ThreadCollisionWarner::EnterSelf() { + // If the active thread is 0 then I'll write the current thread ID + // if two or more threads arrive here only one will succeed to + // write on valid_thread_id_ the current thread ID. + SbAtomic32 current_thread_id = CurrentThread(); + + int previous_value = + SbAtomicNoBarrier_CompareAndSwap(&valid_thread_id_, 0, current_thread_id); + if (previous_value != 0 && previous_value != current_thread_id) { + // gotcha! a thread is trying to use the same class and that is + // not current thread. + asserter_->warn(); + } + + SbAtomicNoBarrier_Increment(&counter_, 1); +} + +void ThreadCollisionWarner::Enter() { + SbAtomic32 current_thread_id = CurrentThread(); + + if (SbAtomicNoBarrier_CompareAndSwap(&valid_thread_id_, 0, + current_thread_id) != 0) { + // gotcha! another thread is trying to use the same class. + asserter_->warn(); + } + + SbAtomicNoBarrier_Increment(&counter_, 1); +} + +void ThreadCollisionWarner::Leave() { + if (SbAtomicBarrier_Increment(&counter_, -1) == 0) { + SbAtomicNoBarrier_Store(&valid_thread_id_, 0); + } +} + +} // namespace starboard
diff --git a/src/starboard/common/thread_collision_warner.h b/src/starboard/common/thread_collision_warner.h new file mode 100644 index 0000000..4aefb21 --- /dev/null +++ b/src/starboard/common/thread_collision_warner.h
@@ -0,0 +1,220 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_ +#define STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_ + +#include "starboard/atomic.h" + +// A helper class alongside macros to be used to verify assumptions about thread +// safety of a class. +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow. +// +// In this case the macro DFAKE_SCOPED_LOCK has to be +// used, it checks that if a thread is inside the push/pop then +// noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow, it calls a method to "protect" from +// a "protected" method +// +// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK +// has to be used, it checks that if a thread is inside the push/pop +// then noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// void push(int) { +// DFAKE_SCOPED_LOCK(push_pop_); +// ... +// } +// int pop() { +// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); +// bar(); +// ... +// } +// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation not usable even if clients are synchronized, +// so only one thread in the class life cycle can use the two members +// push/pop. +// +// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the +// specified +// critical section the first time a thread enters push or pop, from +// that time on only that thread is allowed to execute push or pop. +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Class that has to be contructed/destroyed on same thread, it has +// a "shareable" method (with external synchronization) and a not +// shareable method (even with external synchronization). +// +// In this case 3 Critical sections have to be defined +// +// class ExoticClass { +// public: +// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// +// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } +// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ... +// private: +// DFAKE_MUTEX(ctor_dtor_); +// DFAKE_MUTEX(shareable_section_); +// }; + +#if !defined(NDEBUG) + +// Defines a class member that acts like a mutex. It is used only as a +// verification tool. +#define DFAKE_MUTEX(obj) mutable starboard::ThreadCollisionWarner obj +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. +#define DFAKE_SCOPED_LOCK(obj) \ + starboard::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ + starboard::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) +// Asserts the code is always executed in the same thread. +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ + starboard::ThreadCollisionWarner::Check check_##obj(&obj) + +#else + +#define DFAKE_MUTEX(obj) typedef void InternalFakeMutexType##obj +#define DFAKE_SCOPED_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) + +#endif + +namespace starboard { + +// The class ThreadCollisionWarner uses an Asserter to notify the collision +// AsserterBase is the interfaces and DCheckAsserter is the default asserter +// used. During the unit tests is used another class that doesn't "DCHECK" +// in case of collision (check thread_collision_warner_unittests.cc) +struct AsserterBase { + virtual ~AsserterBase() {} + virtual void warn() = 0; +}; + +struct DCheckAsserter : public AsserterBase { + virtual ~DCheckAsserter() {} + virtual void warn(); +}; + +class ThreadCollisionWarner { + public: + // The parameter asserter is there only for test purpose + ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) + : valid_thread_id_(0), counter_(0), asserter_(asserter) {} + + ~ThreadCollisionWarner() { delete asserter_; } + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK_THREAD_LOCKED + // it doesn't leave the critical section, as opposed to ScopedCheck, + // because the critical section being pinned is allowed to be used only + // from one thread + class Check { + public: + explicit Check(ThreadCollisionWarner* warner) : warner_(warner) { + warner_->EnterSelf(); + } + + ~Check() {} + + private: + ThreadCollisionWarner* warner_; + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK + class ScopedCheck { + public: + explicit ScopedCheck(ThreadCollisionWarner* warner) : warner_(warner) { + warner_->Enter(); + } + + ~ScopedCheck() { warner_->Leave(); } + + private: + ThreadCollisionWarner* warner_; + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_RECURSIVE_LOCK + class ScopedRecursiveCheck { + public: + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~ScopedRecursiveCheck() { warner_->Leave(); } + + private: + ThreadCollisionWarner* warner_; + }; + + private: + // This method stores the current thread identifier and does a DCHECK + // if a another thread has already done it, it is safe if same thread + // calls this multiple time (recursion allowed). + void EnterSelf(); + + // Same as EnterSelf but recursion is not allowed. + void Enter(); + + // Removes the thread_id stored in order to allow other threads to + // call EnterSelf or Enter. + void Leave(); + + // This stores the thread id that is inside the critical section, if the + // value is 0 then no thread is inside. + volatile SbAtomic32 valid_thread_id_; + + // Counter to trace how many time a critical section was "pinned" + // (when allowed) in order to unpin it when counter_ reaches 0. + volatile SbAtomic32 counter_; + + // Here only for class unit tests purpose, during the test I need to not + // DCHECK but notify the collision with something else. + AsserterBase* asserter_; +}; + +} // namespace starboard + +#endif // STARBOARD_COMMON_THREAD_COLLISION_WARNER_H_
diff --git a/src/starboard/condition_variable.h b/src/starboard/condition_variable.h index 463f523..673b7d9 100644 --- a/src/starboard/condition_variable.h +++ b/src/starboard/condition_variable.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Condition variables. +// Module Overview: Starboard Condition Variable module +// +// Defines an interface for condition variables. #ifndef STARBOARD_CONDITION_VARIABLE_H_ #define STARBOARD_CONDITION_VARIABLE_H_ @@ -51,18 +53,22 @@ } // Creates a new condition variable to work with |opt_mutex|, which may be null, -// placing the newly created condition variable in |out_condition|. Returns -// whether the condition variable could be created. +// placing the newly created condition variable in |out_condition|. +// +// The return value indicates whether the condition variable could be created. +// // TODO: It looks like WTF does not have the mutex available when creating // the condition variable, and pthreads doesn't appear to require the mutex on // condvar creation, so we should just remove the parameter. SB_EXPORT bool SbConditionVariableCreate(SbConditionVariable* out_condition, SbMutex* opt_mutex); -// Destroys a condition variable, returning whether the destruction was -// successful. The condition variable specified by |condition| is -// invalidated. Behavior is undefined if other threads are currently waiting -// on this condition variable. +// Destroys the specified SbConditionVariable. The return value indicates +// whether the destruction was successful. The behavior is undefined if other +// threads are currently waiting on this condition variable. +// +// |condition|: The SbConditionVariable to be destroyed. This invalidates the +// condition variable. SB_EXPORT bool SbConditionVariableDestroy(SbConditionVariable* condition); // Waits for |condition|, releasing the held lock |mutex|, blocking @@ -72,19 +78,30 @@ SbConditionVariableWait(SbConditionVariable* condition, SbMutex* mutex); // Waits for |condition|, releasing the held lock |mutex|, blocking up to -// |timeout_duration|, and returning the acquisition result. If -// |timeout_duration| is less than or equal to zero, it will return as quickly -// as possible with a kSbConditionVariableTimedOut result. Behavior is undefined -// if |mutex| is not held. +// |timeout_duration|, and returning the acquisition result. Behavior is +// undefined if |mutex| is not held. +// +// |timeout_duration|: The maximum amount of time that function should wait +// for |condition|. If the |timeout_duration| value is less than or equal to +// zero, the function returns as quickly as possible with a +// kSbConditionVariableTimedOut result. SB_EXPORT SbConditionVariableResult SbConditionVariableWaitTimed(SbConditionVariable* condition, SbMutex* mutex, SbTime timeout_duration); -// Broadcasts to all current waiters of |condition| to stop waiting. +// Broadcasts to all current waiters of |condition| to stop waiting. This +// function wakes all of the threads waiting on |condition| while +// SbConditionVariableSignal wakes a single thread. +// +// |condition|: The condition that should no longer be waited for. SB_EXPORT bool SbConditionVariableBroadcast(SbConditionVariable* condition); -// Signals the next waiter of |condition| to stop waiting. +// Signals the next waiter of |condition| to stop waiting. This function wakes +// a single thread waiting on |condition| while SbConditionVariableBroadcast +// wakes all threads waiting on it. +// +// |condition|: The condition that the waiter should stop waiting for. SB_EXPORT bool SbConditionVariableSignal(SbConditionVariable* condition); #ifdef __cplusplus
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h index 444cc9f..838b5e8 100644 --- a/src/starboard/configuration.h +++ b/src/starboard/configuration.h
@@ -12,13 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A description of the current platform in lurid detail such that common code -// never needs to actually know what the current operating system and -// architecture are. It is both very pragmatic and canonical in that if any -// application code finds itself needing to make a platform decision, it should -// always define a Starboard Configuration feature instead. This implies the -// continued existence of very narrowly-defined configuration features, but it -// retains porting control in Starboard. +// Module Overview: Starboard Configuration module +// +// Provides a description of the current platform in lurid detail so that +// common code never needs to actually know what the current operating system +// and architecture are. +// +// It is both very pragmatic and canonical in that if any application code +// finds itself needing to make a platform decision, it should always define +// a Starboard Configuration feature instead. This implies the continued +// existence of very narrowly-defined configuration features, but it retains +// porting control in Starboard. #ifndef STARBOARD_CONFIGURATION_H_ #define STARBOARD_CONFIGURATION_H_ @@ -365,6 +369,10 @@ #error "Your platform must define SB_HAS_MICROPHONE in API versions 2 or later." #endif +#if SB_VERSION(3) && !defined(SB_HAS_TIME_THREAD_NOW) +#error "Your platform must define SB_HAS_TIME_THREAD_NOW in API 3 or later." +#endif + #if SB_HAS(PLAYER) #if !SB_IS(PLAYER_COMPOSITED) && !SB_IS(PLAYER_PUNCHED_OUT) && \ !SB_IS(PLAYER_PRODUCING_TEXTURE)
diff --git a/src/starboard/creator/ci20directfb/starboard_platform.gyp b/src/starboard/creator/ci20directfb/starboard_platform.gyp index b695c91..284be6b 100644 --- a/src/starboard/creator/ci20directfb/starboard_platform.gyp +++ b/src/starboard/creator/ci20directfb/starboard_platform.gyp
@@ -209,6 +209,7 @@ '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', + '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc', '<(DEPTH)/starboard/shared/posix/time_get_now.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
diff --git a/src/starboard/creator/ci20directfb/system_get_property.cc b/src/starboard/creator/ci20directfb/system_get_property.cc index db0f0e0..51ca55f 100644 --- a/src/starboard/creator/ci20directfb/system_get_property.cc +++ b/src/starboard/creator/ci20directfb/system_get_property.cc
@@ -47,6 +47,7 @@ case kSbSystemPropertyModelName: case kSbSystemPropertyModelYear: case kSbSystemPropertyNetworkOperatorName: + case kSbSystemPropertySpeechApiKey: return false; case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/creator/ci20x11/starboard_platform.gyp b/src/starboard/creator/ci20x11/starboard_platform.gyp index bab02c6..34be5d0 100644 --- a/src/starboard/creator/ci20x11/starboard_platform.gyp +++ b/src/starboard/creator/ci20x11/starboard_platform.gyp
@@ -173,6 +173,7 @@ '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', + '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc', '<(DEPTH)/starboard/shared/posix/time_get_now.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc',
diff --git a/src/starboard/creator/ci20x11/system_get_property.cc b/src/starboard/creator/ci20x11/system_get_property.cc index 8a7a795..9d71165 100644 --- a/src/starboard/creator/ci20x11/system_get_property.cc +++ b/src/starboard/creator/ci20x11/system_get_property.cc
@@ -47,6 +47,7 @@ case kSbSystemPropertyModelName: case kSbSystemPropertyModelYear: case kSbSystemPropertyNetworkOperatorName: + case kSbSystemPropertySpeechApiKey: return false; case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/creator/shared/configuration_public.h b/src/starboard/creator/shared/configuration_public.h index 8898d5b..bcf356a 100644 --- a/src/starboard/creator/shared/configuration_public.h +++ b/src/starboard/creator/shared/configuration_public.h
@@ -88,7 +88,7 @@ #define SB_HAS_CROSS_CORE_SCHEDULER 1 // The API version implemented by this platform. -#define SB_API_VERSION 1 +#define SB_API_VERSION 2 // --- System Header Configuration ------------------------------------------- @@ -119,6 +119,12 @@ // Whether the current platform provides the standard header float.h. #define SB_HAS_FLOAT_H 1 +// Whether the current platform has microphone supported. +#define SB_HAS_MICROPHONE 0 + +// Whether the current platform has speech synthesis. +#define SB_HAS_SPEECH_SYNTHESIS 0 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) @@ -439,6 +445,12 @@ // The maximum number of users that can be signed in at the same time. #define SB_USER_MAX_SIGNED_IN 1 +// --- Timing API ------------------------------------------------------------ + +// Whether this platform has an API to retrieve how long the current thread +// has spent in the executing state. +#define SB_HAS_TIME_THREAD_NOW 1 + // --- Platform Specific Audits ---------------------------------------------- #if !defined(__GNUC__)
diff --git a/src/starboard/decode_target.h b/src/starboard/decode_target.h index 6ea6951..557c511 100644 --- a/src/starboard/decode_target.h +++ b/src/starboard/decode_target.h
@@ -49,7 +49,8 @@ // Let's say there's an image decoder for .foo files: // // bool SbImageDecodeFooSupportsFormat(SbDecodeTargetFormat format); -// bool SbImageDecodeFoo(void *data, int data_size, SbDecodeTarget target); +// SbDecodeTarget SbImageDecodeFoo(void* data, int data_size, +// SbDecodeTargetFormat format); // // First, the client should enumerate which SbDecodeTargetFormats are supported // by that decoder. @@ -68,25 +69,13 @@ // } // } // -// Now that the client has a format, it can create a decode target that it will -// use to decode the .foo file into. Let's assume format is -// kSbDecodeTargetFormat1PlaneRGBA, and that we are on an EGL/GLES2 platform. +// Now that the client has a format, it can create a decode target that it +// will use to decode the .foo file into. Let's assume format is +// kSbDecodeTargetFormat1PlaneRGBA, that we are on an EGL/GLES2 platform. // Also, we won't do any error checking, to keep things even simpler. // -// // Allocate a sized texture of the right format. -// GLuint texture_handle; -// glGenTextures(1, &texture_handle); -// glBindTexture(GL_TEXTURE_2D, texture_handle); -// glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, width, height, -// 0 /*border*/, GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/); -// glBindTexture(GL_TEXTURE_2D, 0); -// -// // Create an SbDecodeTarget wrapping the new texture handle. -// SbDecodeTarget target = -// SbDecodeTargetCreate(display, context, format, &texture_handle); -// -// // Now pass the SbDecodeTarget into the decoder. -// SbImageDecodeFoo(encoded_foo_data, encoded_foo_data_size, target); +// SbDecodeTarget target = SbImageDecodeFoo(encoded_foo_data, +// encoded_foo_data_size, format); // // // If the decode works, you can get the texture out and render it. // GLuint texture = @@ -100,14 +89,14 @@ #include "starboard/export.h" #include "starboard/types.h" -#if SB_VERSION(3) && SB_HAS(GRAPHICS) +#if SB_VERSION(3) #if SB_HAS(BLITTER) #include "starboard/blitter.h" -#else +#elif SB_HAS(GLES2) // SB_HAS(BLITTER) #include <EGL/egl.h> -#include <GL/gl.h> -#endif +#include <GLES2/gl2.h> +#endif // SB_HAS(BLITTER) #ifdef __cplusplus extern "C" { @@ -189,6 +178,9 @@ // The function to release an acquired SbDecodeTarget back to the provider. SbDecodeTargetReleaseFunction release; + + // |context| will be passed into every call to |acquire| and |release|. + void* context; } SbDecodeTargetProvider; // --- Constants ------------------------------------------------------------- @@ -203,6 +195,12 @@ return handle != kSbDecodeTargetInvalid; } +// Returns whether a given format is valid. +static SB_C_INLINE bool SbDecodeTargetIsFormatValid( + SbDecodeTargetFormat format) { + return format != kSbDecodeTargetFormatInvalid; +} + #if SB_HAS(BLITTER) // Creates a new SbBlitter-compatible SbDecodeTarget from one or more |planes| // created from |display|. @@ -218,7 +216,7 @@ // Gets the surface that represents the given plane. SB_EXPORT SbBlitterSurface SbDecodeTargetGetPlane(SbDecodeTarget decode_target, SbDecodeTargetPlane plane); -#else // SB_HAS(BLITTER) +#elif SB_HAS(GLES2) // SB_HAS(BLITTER) // Creates a new EGL/GLES2-compatible SbDecodeTarget from one or more |planes| // owned by |context|, created from |display|. Must be called from a thread // where |context| is current. @@ -239,48 +237,71 @@ // Gets the texture that represents the given plane. SB_EXPORT GLuint SbDecodeTargetGetPlane(SbDecodeTarget decode_target, SbDecodeTargetPlane plane); + +#else // SB_HAS(BLITTER) + +// Stub function for when graphics aren't enabled. Always creates +// kSbDecodeTargetInvalid. +static SB_C_INLINE SbDecodeTarget +SbDecodeTargetCreate(SbDecodeTargetFormat format) { + SB_UNREFERENCED_PARAMETER(format); + return kSbDecodeTargetInvalid; +} + +// Stub function for when graphics aren't enabled. There is no concept of a +// plane, and |NULL| is always returned. +static SB_C_INLINE void* SbDecodeTargetGetPlane(SbDecodeTarget decode_target, + SbDecodeTargetPlane plane) { + SB_UNREFERENCED_PARAMETER(decode_target); + SB_UNREFERENCED_PARAMETER(plane); + return NULL; +} + #endif // SB_HAS(BLITTER) -// Destroys the given SbDecodeTarget and all associated surfaces. +// Destroys the given SbDecodeTarget. Note that calling this function does NOT +// destroy the associated surfaces with it, in order to accommodate the case +// in which it is desirable for the lifetime of the surface to outlive the +// SbDecodeTarget that contains it. If the surfaces should be destroyed along +// with the SbDecodeTarget, then they should be extracted with +// |SbDecodeTargetGetPlane| and destroyed manually by the client. SB_EXPORT void SbDecodeTargetDestroy(SbDecodeTarget decode_target); // Gets the format that |decode_target| was created with. SB_EXPORT SbDecodeTargetFormat SbDecodeTargetGetFormat(SbDecodeTarget decode_target); -// Registers |provider| as the SbDecodeTargetProvider with the given |context|, -// displacing any previous registered provider. The provider is expected to be -// kept alive by the caller until unregistered, so this function is NOT passing -// ownership in any way. |context| will be passed into every call to -// SbDecodeTargetProvider::acquire or SbDecodeTargetProvider::release. May be -// called from any thread. -SB_EXPORT bool SbDecodeTargetRegisterProvider(SbDecodeTargetProvider* provider, - void* context); +// Gets whether |decode_target| is opaque or not. The underlying source of +// this value is expected to be properly maintained by the Starboard +// implementation. So, for example, if an opaque only image type were decoded +// into an SbDecodeTarget, then the implementation would configure things in +// such a way that this function would return true. By opaque, it is meant +// that all alpha values are guaranteed to be 255, if |decode_target| is of a +// format that has alpha values. If |decode_target| is of a format that does +// not have alpha values, then this function should return |true|. +SB_EXPORT bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target); -// Unregisters |provider| as the SbDecodeTargetProvider, with |context|, if it -// is the current registered provider-context. This is checked by comparing the -// pointer values of both |provider| and |context| to the currently registered -// provider. May be called from any thread. -SB_EXPORT void SbDecodeTargetUnregisterProvider( - SbDecodeTargetProvider* provider, - void* context); - -// Acquires a decode target from the registered SbDecodeTargetProvider, -// returning NULL if none is registered. May be called from any thread. -SB_EXPORT SbDecodeTarget -SbDecodeTargetAcquireFromProvider(SbDecodeTargetFormat format, +// Inline convenience function to acquire an SbDecodeTarget of type |format|, +// |width|, and |height| from |provider|. +static SB_C_INLINE SbDecodeTarget +SbDecodeTargetAcquireFromProvider(SbDecodeTargetProvider* provider, + SbDecodeTargetFormat format, int width, - int height); + int height) { + return provider->acquire(provider->context, format, width, height); +} -// Releases |decode_target| back to the registered SbDecodeTargetProvider. If no -// provider is registered, just calls SbDecodeTargetDestroy on |decode_target|. -// May be called from any thread. -SB_EXPORT void SbDecodeTargetReleaseToProvider(SbDecodeTarget decode_target); +// Inline convenience function to release |decode_target| back to |provider|. +static SB_C_INLINE void SbDecodeTargetReleaseToProvider( + SbDecodeTargetProvider* provider, + SbDecodeTarget decode_target) { + provider->release(provider->context, decode_target); +} #ifdef __cplusplus } // extern "C" #endif -#endif // SB_VERSION(3) && SB_HAS(GRAPHICS) +#endif // SB_VERSION(3) #endif // STARBOARD_DECODE_TARGET_H_
diff --git a/src/starboard/directory.h b/src/starboard/directory.h index 728b875..0147306 100644 --- a/src/starboard/directory.h +++ b/src/starboard/directory.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Directory listing API. +// Module Overview: Starboard Directory module +// +// Provides directory listing functions. #ifndef STARBOARD_DIRECTORY_H_ #define STARBOARD_DIRECTORY_H_ @@ -46,26 +48,43 @@ return directory != kSbDirectoryInvalid; } -// Opens the given existing directory for listing. Will return -// kSbDirectoryInvalidHandle if not successful. If |out_error| is provided by -// the caller, it will be set to the appropriate SbFileError code on failure. +// Opens the given existing directory for listing. This function returns +// kSbDirectoryInvalidHandle if it is not successful. +// +// If |out_error| is provided by the caller, it will be set to the appropriate +// SbFileError code on failure. +// +// |out_error|: An output parameter that, in case of an error, is set to the +// reason that the directory could not be opened. SB_EXPORT SbDirectory SbDirectoryOpen(const char* path, SbFileError* out_error); -// Closes an open directory stream handle. Returns whether the close was -// successful. +// Closes an open directory stream handle. The return value indicates whether +// the directory was closed successfully. +// +// |directory|: The directory stream handle to close. SB_EXPORT bool SbDirectoryClose(SbDirectory directory); -// Populates |out_entry| with the next entry in that directory stream, and moves -// the stream forward by one entry. Returns |true| if there was a next -// directory, and |false| at the end of the directory stream. +// Populates |out_entry| with the next entry in the specified directory stream, +// and moves the stream forward by one entry. +// +// This function returns |true| if there was a next directory, and |false| +// at the end of the directory stream. +// +// |directory|: The directory stream from which to retrieve the next directory. +// |out_entry|: The variable to be populated with the next directory entry. SB_EXPORT bool SbDirectoryGetNext(SbDirectory directory, SbDirectoryEntry* out_entry); -// Returns whether SbDirectoryOpen is allowed for the given |path|. +// Indicates whether SbDirectoryOpen is allowed for the given |path|. +// +// |path|: The path to be checked. SB_EXPORT bool SbDirectoryCanOpen(const char* path); // Creates the directory |path|, assuming the parent directory already exists. -// Returns whether the directory now exists (even if it existed before). +// This function returns |true| if the directory now exists (even if it existed +// before) and returns |false| if the directory does not exist. +// +// |path|: The path to be created. SB_EXPORT bool SbDirectoryCreate(const char* path); #ifdef __cplusplus
diff --git a/src/starboard/double.h b/src/starboard/double.h index bde6acc..8a28bbc 100644 --- a/src/starboard/double.h +++ b/src/starboard/double.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Double-precision floating point helper functions. +// Module Overview: Starboard Double module +// +// Provides double-precision floating point helper functions. #ifndef STARBOARD_DOUBLE_H_ #define STARBOARD_DOUBLE_H_ @@ -25,21 +27,32 @@ #endif // Floors double-precision floating-point number |d| to the nearest integer. +// +// |d|: The number to be floored. SB_EXPORT double SbDoubleFloor(const double d); // Returns the absolute value of the given double-precision floating-point -// number |d|, preserving NaN and Infinity. +// number |d|, preserving |NaN| and infinity. +// +// |d|: The number to be adjusted. SB_EXPORT double SbDoubleAbsolute(const double d); // Returns |base| taken to the power of |exponent|. +// +// |base|: The number to be adjusted. +// |exponent|: The power to which the |base| number should be raised. SB_EXPORT double SbDoubleExponent(const double base, const double exponent); // Determines whether double-precision floating-point number |d| represents a -// fininte number. +// finite number. +// +// |d|: The number to be evaluated. SB_EXPORT bool SbDoubleIsFinite(const double d); -// Determines whether double-precision floating-point number |d| represents "Not -// a Number." +// Determines whether double-precision floating-point number |d| represents +// "Not a Number." +// +// |d|: The number to be evaluated. SB_EXPORT bool SbDoubleIsNan(const double d); #ifdef __cplusplus
diff --git a/src/starboard/event.h b/src/starboard/event.h index cec1884..44cdd79 100644 --- a/src/starboard/event.h +++ b/src/starboard/event.h
@@ -12,7 +12,47 @@ // See the License for the specific language governing permissions and // limitations under the License. -// The event system that wraps the Starboard main loop and entry point. +// Module Overview: Starboard Event module +// +// Defines the event system that wraps the Starboard main loop and entry point. +// +// ## The Starboard Application life cycle +// +// | * +// | | _________________________ +// | Start | | +// | | | Resume +// | V V | +// | [ STARTED ] --Pause--> [ PAUSED ] --Suspend--> [ SUSPENDED ] +// | ^ | | +// | | Unpause Stop +// | |_____________________| | +// | V +// | [ STOPPED ] +// +// The first event that a Starboard application receives is Start +// (kSbEventTypeStart). This puts the application in the |STARTED| state. +// The application is in the foreground and can expect to do all of the normal +// things it might want to do. Once in the |STARTED| state, it may receive a +// |Pause| event, putting the application into the |PAUSED| state. +// +// In the |PAUSED| state, the application is still visible, but has lost +// focus, or it is partially obscured by a modal dialog, or it is on its way +// to being shut down. The application should pause activity in this state. +// In this state, it can receive |Unpause| to be brought back to the foreground +// state (|STARTED|), or |Suspend| to be pushed further in the background +// to the |SUSPENDED| state. +// +// In the |SUSPENDED| state, the application is generally not visible. It +// should immediately release all graphics and video resources, and shut down +// all background activity (timers, rendering, etc). Additionally, the +// application should flush storage to ensure that if the application is +// killed, the storage will be up-to-date. The application may be killed at +// this point, but will ideally receive a |Stop| event for a more graceful +// shutdown. +// +// Note that the application is always expected to transition through |PAUSED| +// to |SUSPENDED| before receiving |Stop| or being killed. #ifndef STARBOARD_EVENT_H_ #define STARBOARD_EVENT_H_ @@ -26,43 +66,6 @@ extern "C" { #endif -// The Starboard Application life cycle -// ------------------------------------ -// -// * -// | _________________________ -// Start | | -// | | Resume -// V V | -// [ STARTED ] --Pause--> [ PAUSED ] --Suspend--> [ SUSPENDED ] -// ^ | | -// | Unpause Stop -// |_____________________| | -// V -// [ STOPPED ] -// -// The first event that a Starboard application will receive is Start -// (kSbEventTypeStart). This puts the application in the STARTED state; it is in -// the foreground and can expect to do all the normal things it might want to -// do. Once in the STARTED state, it may receive a Pause event, putting the -// application into the PAUSED state. -// -// In the PAUSED state, the application is still visible, but has lost focus, or -// it is partially obscured by a modal dialog, or it is on its way to being shut -// down. The application should pause activity in this state. In this state, it -// can receive Unpause to be brought back to the foreground state, STARTED, or -// Suspend to be pushed further in the background to the SUSPENDED state. -// -// In the SUSPENDED state, the application is generally not visible. It should -// immediately release all graphics and video resources, and shut down all -// background activity (timers, rendering, etc). Additionally, the application -// should flush storage to ensure that if the application is killed, the storage -// will be up-to-date. The application may be killed at this point, but will -// ideally receive a Stop event for a more graceful shutdown. -// -// Note that the application is always expected to transition through PAUSED to -// SUSPENDED before receiving Stop or being killed. - // An enumeration of all possible event types dispatched directly by the // system. Each event is accompanied by a void* data argument, and each event // must define the type of the value pointed to by that data argument, if any. @@ -185,31 +188,36 @@ return handle != kSbEventIdInvalid; } -// Declaration of the entry point that Starboard applications MUST implement. -// Any memory pointed at by |event| or the |data| field inside |event| is owned -// by the system, and will be reclaimed after this function returns, so the -// implementation must copy this data to extend its life. This should also be -// assumed of all fields within the data object, unless otherwise explicitly -// specified. This function will only be called from the main Starboard thread. -// There is no specification about what other work might happen on this thread, -// so the application should generally do as little work as possible on this -// thread, and just dispatch it over to another thread. +// The entry point that Starboard applications MUST implement. Any memory +// pointed at by |event| or the |data| field inside |event| is owned by the +// system, and that memory is reclaimed after this function returns, so the +// implementation must copy this data to extend its life. This behavior should +// also be assumed of all fields within the |data| object, unless otherwise +// explicitly specified. +// +// This function is only called from the main Starboard thread. There is no +// specification about what other work might happen on this thread, so the +// application should generally do as little work as possible on this thread, +// and just dispatch it over to another thread. SB_IMPORT void SbEventHandle(const SbEvent* event); -// Schedules an event |callback| into the main Starboard event loop, with -// accompanying |context|. This function may be called from any thread, but -// |callback| will always be called from the main Starboard thread, queued with -// other pending events. |callback| will not be called any earlier than |delay| -// microseconds from the time SbEventInject is called. Set |delay| to 0 to call -// the event as soon as possible. +// Schedules an event |callback| into the main Starboard event loop. +// This function may be called from any thread, but |callback| is always +// called from the main Starboard thread, queued with other pending events. +// +// |callback|: The callback function to be called. +// |context|: The context that is passed to the |callback| function. +// |delay|: The minimum number of microseconds to wait before calling the +// |callback| function. Set |delay| to |0| to call the callback as soon as +// possible. SB_EXPORT SbEventId SbEventSchedule(SbEventCallback callback, void* context, SbTime delay); -// Cancels the injected |event_id|. Does nothing if the event already fired. Can -// be safely called from any thread, but there is no guarantee that the event -// will not run anyway unless it is called from the main Starboard event loop -// thread. +// Cancels the specified |event_id|. Note that this function is a no-op +// if the event already fired. This function can be safely called from any +// thread, but the only way to guarantee that the event does not run anyway +// is to call it from the main Starboard event loop thread. SB_EXPORT void SbEventCancel(SbEventId event_id); #ifdef __cplusplus
diff --git a/src/starboard/export.h b/src/starboard/export.h index af2303f..b19c1c7 100644 --- a/src/starboard/export.h +++ b/src/starboard/export.h
@@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Macros to properly export or import symbols from shared libraries. +// Module Overview: Starboard Export module +// +// Provides macros for properly exporting or importing symbols from shared +// libraries. #ifndef STARBOARD_EXPORT_H_ #define STARBOARD_EXPORT_H_
diff --git a/src/starboard/file.h b/src/starboard/file.h index 3f15daf..0e92907 100644 --- a/src/starboard/file.h +++ b/src/starboard/file.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// File system Input/Output +// Module Overview: Starboard File module +// +// Defines file system input/output functions. #ifndef STARBOARD_FILE_H_ #define STARBOARD_FILE_H_ @@ -32,17 +34,33 @@ // A handle to an open file. typedef SbFilePrivate* SbFile; -// kSbFile(Open|Create)(Only|Always|Truncated) are mutually exclusive. You -// should specify exactly one of the five (possibly combining with other flags) -// when opening or creating a file. +// Flags that define how a file is used in the application. These flags should +// be or'd together when passed to SbFileOpen to open or create a file. +// +// The following five flags are mutually exclusive. You must specify exactly one +// of them: +// - |kSbFileOpenAlways| +// - |kSbFileOpenOnly| +// - |kSbFileOpenTruncated| +// - |kSbFileCreateAlways| +// - |kSbFileCreateOnly| +// +// In addition, one or more of the following flags must be specified: +// - |kSbFileRead| +// - |kSbFileWrite| +// +// The |kSbFileAsync| flag is optional. typedef enum SbFileFlags { kSbFileOpenOnly = 1 << 0, // Opens a file, only if it exists. kSbFileCreateOnly = 1 << 1, // Creates a new file, only if it // does not already exist. - kSbFileOpenAlways = 1 << 2, // May create a new file. - kSbFileCreateAlways = 1 << 3, // May overwrite an old file. - kSbFileOpenTruncated = 1 << 4, // Opens a file and truncates it - // to zero, only if it exists. + kSbFileOpenAlways = 1 << 2, // Opens an existing file at the specified + // path or creates a new file at that path. + kSbFileCreateAlways = 1 << 3, // Creates a new file at the specified path + // or overwrites an existing file at that + // path. + kSbFileOpenTruncated = 1 << 4, // Opens a file and truncates it to zero, + // only if it exists. kSbFileRead = 1 << 5, kSbFileWrite = 1 << 6, kSbFileAsync = 1 << 7, // May allow asynchronous I/O on some @@ -112,81 +130,140 @@ } // Opens the file at |path|, which must be absolute, creating it if specified by -// |flags|. |out_created|, if provided, will be set to true if a new file was -// created (or an old one truncated to zero length to simulate a new file, which -// can happen with kSbFileCreateAlways), and false otherwise. |out_error| can -// be NULL. If creation failed, it will return kSbFileInvalid. The read/write -// position is at the beginning of the file. +// |flags|. The read/write position is at the beginning of the file. // -// It only guarantees the correct behavior when |path| points to a file. If -// |path| points to directory, the behavior is undefined. +// Note that this function only guarantees the correct behavior when |path| +// points to a file. The behavior is undefined if |path| points to a directory. +// +// |path|: The absolute path of the file to be opened. +// |flags|: |SbFileFlags| that determine how the file is used in the +// application. Among other things, |flags| can indicate whether the +// application should create |path| if |path| does not already exist. +// |out_created|: Starboard sets this value to |true| if a new file is created +// or if an old file is truncated to zero length to simulate a new file, +// which can happen if the |kSbFileCreateAlways| flag is set. Otherwise, +// Starboard sets this value to |false|. +// |out_error|: If |path| cannot be created, this is set to |kSbFileInvalid|. +// Otherwise, it is |NULL|. SB_EXPORT SbFile SbFileOpen(const char* path, int flags, bool* out_created, SbFileError* out_error); -// Closes |file|. Returns whether the close was successful. +// Closes |file|. The return value indicates whether the file was closed +// successfully. +// +// |file|: The absolute path of the file to be closed. SB_EXPORT bool SbFileClose(SbFile file); -// Changes current read/write position in |file| to |offset| relative to the -// origin defined by |whence|. Returns the resultant current read/write position -// in the file (relative to the start) or -1 in case of error. May not support +// Changes the current read/write position in |file|. The return value +// identifies the resultant current read/write position in the file (relative +// to the start) or |-1| in case of an error. This function might not support // seeking past the end of the file on some platforms. +// +// |file|: The SbFile in which the read/write position will be changed. +// |whence|: The starting read/write position. The position is modified relative +// to this value. +// |offset|: The amount that the read/write position is changed, relative to +// |whence|. SB_EXPORT int64_t SbFileSeek(SbFile file, SbFileWhence whence, int64_t offset); // Reads |size| bytes (or until EOF is reached) from |file| into |data|, -// starting at the file's current position. Returns the number of bytes read, or -// -1 on error. Note that this function does NOT make a best effort to read all -// data on all platforms, it just reads however many bytes are quickly -// available. Can be run in a loop to make it a best-effort read. +// starting at the file's current position. +// +// The return value specifies the number of bytes read or |-1| if there was +// an error. Note that this function does NOT make a best effort to read all +// data on all platforms; rather, it just reads however many bytes are quickly +// available. However, this function can be run in a loop to make it a +// best-effort read. +// +// |file|: The SbFile from which to read data. +// |data|: The variable to which data is read. +// |size|: The amount of data (in bytes) to read. SB_EXPORT int SbFileRead(SbFile file, char* data, int size); // Writes the given buffer into |file| at the file's current position, -// overwritting any data that was previously there. Returns the number of bytes -// written, or -1 on error. Note that this function does NOT make a best effort -// to write all data, it writes however many bytes can be written quickly. It -// should be run in a loop to ensure all data is written. +// overwriting any data that was previously there. +// +// The return value identifies the number of bytes written, or |-1| on error. +// Note that this function does NOT make a best effort to write all data; +// rather, it writes however many bytes can be written quickly. Generally, this +// means that it writes however many bytes as possible without blocking on IO. +// Run this function in a loop to ensure that all data is written. +// +// |file|: The SbFile to which data will be written. +// |data|: The data to be written. +// |size|: The amount of data (in bytes) to write. SB_EXPORT int SbFileWrite(SbFile file, const char* data, int size); -// Truncates the given |file| to the given |length|. If length is greater than -// the current size of the file, the file is extended with zeros. Negative -// |length| will do nothing and return false. +// Truncates the given |file| to the given |length|. The return value indicates +// whether the file was truncated successfully. +// +// |file|: The file to be truncated. +// |length|: The expected length of the file after it is truncated. If |length| +// is greater than the current size of the file, then the file is extended +// with zeros. If |length| is negative, then the function is a no-op and +// returns |false|. SB_EXPORT bool SbFileTruncate(SbFile file, int64_t length); -// Flushes the write buffer to |file|. Data written via SbFileWrite isn't -// necessarily comitted right away until the file is flushed or closed. +// Flushes the write buffer to |file|. Data written via SbFileWrite is not +// necessarily committed until the SbFile is flushed or closed. +// +// |file|: The SbFile to which the write buffer is flushed. SB_EXPORT bool SbFileFlush(SbFile file); -// Places some information about |file|, an open SbFile, in |out_info|. Returns -// |true| if successful. If unsuccessful, |out_info| is untouched. +// Retrieves information about |file|. The return value indicates whether the +// file information was retrieved successfully. +// +// |file|: The SbFile for which information is retrieved. +// |out_info|: The variable into which the retrieved data is placed. This +// variable is not touched if the operation is not successful. SB_EXPORT bool SbFileGetInfo(SbFile file, SbFileInfo* out_info); -// Places information about the file or directory at |path|, which must be -// absolute, into |out_info|. Returns |true| if successful. If unsuccessful, -// |out_info| is untouched. +// Retrieves information about the file at |path|. The return value indicates +// whether the file information was retrieved successfully. +// +// |file|: The absolute path of the file for which information is retrieved. +// |out_info|: The variable into which the retrieved data is placed. This +// variable is not touched if the operation is not successful. SB_EXPORT bool SbFileGetPathInfo(const char* path, SbFileInfo* out_info); -// Deletes the regular file, symlink, or empty directory at |path|, which must -// be absolute. Used mainly to clean up after unit tests. May fail on some -// platforms if the file in question is being held open. +// Deletes the regular file, symlink, or empty directory at |path|. This +// function is used primarily to clean up after unit tests. On some platforms, +// this function fails if the file in question is being held open. +// +// |path|: The absolute path fo the file, symlink, or directory to be deleted. SB_EXPORT bool SbFileDelete(const char* path); -// Returns whether a file or directory exists at |path|, which must be absolute. +// Indicates whether a file or directory exists at |path|. +// +// |path|: The absolute path of the file or directory being checked. SB_EXPORT bool SbFileExists(const char* path); -// Returns whether SbFileOpen() with the given |flags| is allowed for |path|, -// which must be absolute. +// Indicates whether SbFileOpen() with the given |flags| is allowed for |path|. +// +// |path|: The absolute path to be checked. +// |flags|: The flags that are being evaluated for the given |path|. SB_EXPORT bool SbFileCanOpen(const char* path, int flags); -// Converts an ISO fopen() mode string into flags that can be equivalently +// Converts an ISO |fopen()| mode string into flags that can be equivalently // passed into SbFileOpen(). +// +// |mode|: The mode string to be converted into flags. SB_EXPORT int SbFileModeStringToFlags(const char* mode); -// Reads the given number of bytes (or until EOF is reached). Returns the number -// of bytes read, or -1 on error. Note that this function makes a best effort to -// read all data on all platforms, so it is not intended for stream oriented -// files but instead for cases when the normal expectation is that actually -// |size| bytes are read unless there is an error. +// Reads |size| bytes (or until EOF is reached) from |file| into |data|, +// starting from the beginning of the file. +// +// The return value specifies the number of bytes read or |-1| if there was +// an error. Note that, unlike |SbFileRead|, this function does make a best +// effort to read all data on all platforms. As such, it is not intended for +// stream-oriented files but instead for cases when the normal expectation is +// that |size| bytes can be read unless there is an error. +// +// |file|: The SbFile from which to read data. +// |data|: The variable to which data is read. +// |size|: The amount of data (in bytes) to read. static inline int SbFileReadAll(SbFile file, char* data, int size) { if (!SbFileIsValid(file) || size < 0) { return -1; @@ -204,9 +281,15 @@ return bytes_read ? bytes_read : rv; } -// Writes the given buffer into the file, overwritting any data that was -// previously there. Returns the number of bytes written, or -1 on error. Note -// that this function makes a best effort to write all data on all platforms. +// Writes the given buffer into |file|, starting at the beginning of the file, +// and overwriting any data that was previously there. Unlike SbFileWrite, this +// function does make a best effort to write all data on all platforms. +// +// The return value identifies the number of bytes written, or |-1| on error. +// +// |file|: The file to which data will be written. +// |data|: The data to be written. +// |size|: The amount of data (in bytes) to write. static inline int SbFileWriteAll(SbFile file, const char* data, int size) { if (!SbFileIsValid(file) || size < 0) { return -1; @@ -287,7 +370,7 @@ int64_t GetSize() const { SbFileInfo file_info; - static bool success = GetInfo(&file_info); + bool success = GetInfo(&file_info); return (success ? file_info.size : -1); }
diff --git a/src/starboard/image.h b/src/starboard/image.h new file mode 100644 index 0000000..5ff6e3f --- /dev/null +++ b/src/starboard/image.h
@@ -0,0 +1,102 @@ +// Copyright 2016 Google Inc. 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. + +// Module Overview: Starboard Image Decoding Module +// +// API for hardware accelerated image decoding. This module allows for the +// client to feed in raw, encoded data to be decoded directly into an +// SbDecodeTarget. It also provides an interface for the client to query what +// combinations of encoded image formats and SbDecodeTargetFormats are +// supported or not. +// +// All functions in this module are safe to call from any thread at any point +// in time. +// +// #### SbImageIsDecodeSupported and SbImageDecode Example +// +// Let's assume that we're on a Blitter platform. +// +// SbDecodeTargetProvider* provider = GetProviderFromSomewhere(); +// void* data = GetCompressedJPEGFromSomewhere(); +// int data_size = GetCompressedJPEGSizeFromSomewhere(); +// const char* mime_type = "image/jpeg"; +// SbDecodeTargetFormat format = kSbDecodeTargetFormat1PlaneRGBA; +// +// if (!SbImageIsDecodeSupported(mime_type, format)) { +// return; +// } +// +// SbDecodeTarget result_target = SbDecodeImage(provider, data, data_size, +// mime_type, format); +// SbBlitterSurface surface = +// SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA); +// // Do stuff with surface... +// + +#ifndef STARBOARD_IMAGE_H_ +#define STARBOARD_IMAGE_H_ + +#include "starboard/configuration.h" +#include "starboard/decode_target.h" +#include "starboard/export.h" +#include "starboard/types.h" + +#if SB_VERSION(3) + +#ifdef __cplusplus +extern "C" { +#endif + +// Whether the current platform supports hardware accelerated decoding an +// image of mime type |mime_type| into SbDecodeTargetFormat |format|. The +// result of this function must not change over the course of the program, +// which means that the results of this function may be cached indefinitely. +SB_EXPORT bool SbImageIsDecodeSupported(const char* mime_type, + SbDecodeTargetFormat format); + +// Attempt to decode encoded |mime_type| image data |data| of size |data_size| +// into an SbDecodeTarget of SbDecodeFormatType |format|, possibly using +// SbDecodeTargetProvider |provider|, if it is non-null. Thus, four following +// scenarios regarding the provider may happen: +// +// 1. The provider is required by the |SbImageDecode| implementation and no +// provider is given. The implementation should gracefully fail by +// immediately returning kSbDecodeTargetInvalid. +// 2. The provider is required and is passed in. The implementation will +// proceed forward, using the SbDecodeTarget from the provider. +// 3. The provider is not required and is passed in. The provider will NOT be +// called, and the implementation will proceed to decoding however it +// desires. +// 4. The provider is not required and is not passed in. The implementation +// will proceed forward. +// +// Thus, it is NOT safe for clients of this API to assume that the |provider| +// it passes in will be called. Finally, if the decode succeeds, a new +// SbDecodeTarget will be allocated. If |mime_type| image decoding for the +// requested format is not supported or the decode fails, +// kSbDecodeTargetInvalid will be returned, with any intermediate allocations +// being cleaned up in the implementation. +SB_EXPORT SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider, + void* data, + int data_size, + const char* mime_type, + SbDecodeTargetFormat format); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SB_VERSION(3) + +#endif // STARBOARD_IMAGE_H_
diff --git a/src/starboard/input.h b/src/starboard/input.h index fbce0f2..e8f98b2 100644 --- a/src/starboard/input.h +++ b/src/starboard/input.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Definition of input events and associated data types. +// Module Overview: Starboard Input module +// +// Defines input events and associated data types. #ifndef STARBOARD_INPUT_H_ #define STARBOARD_INPUT_H_ @@ -27,57 +29,58 @@ extern "C" { #endif -// All possible input subsystem types. +// Identifies possible input subsystem types. The types of events that each +// device type produces correspond to |SbInputEventType| values. typedef enum SbInputDeviceType { - // Input from some gesture-detection mechanism. Examples include Kinect, + // Input from a gesture-detection mechanism. Examples include Kinect, // Wiimotes, LG Magic Remotes, etc... // - // Produces Move, Grab, Ungrab, Press and Unpress events. + // Produces |Move|, |Grab|, |Ungrab|, |Press| and |Unpress| events. kSbInputDeviceTypeGesture, // Input from a gamepad, following the layout provided in the W3C Web Gamepad - // API. + // API. [https://www.w3.org/TR/gamepad/] // - // Produces Move, Press and Unpress events. + // Produces |Move|, |Press| and |Unpress| events. kSbInputDeviceTypeGamepad, - // Keyboard input from a traditional keyboard, or game controller chatpad. + // Keyboard input from a traditional keyboard or game controller chatpad. // - // Produces Press and Unpress events. + // Produces |Press| and |Unpress| events. kSbInputDeviceTypeKeyboard, // Input from a microphone that would provide audio data to the caller, who // may then find some way to detect speech or other sounds within it. It may // have processed or filtered the audio in some way before it arrives. // - // Produces Audio events. + // Produces |Audio| events. kSbInputDeviceTypeMicrophone, // Input from a traditional mouse. // - // Produces Move, Press, and Unpress events. + // Produces |Move|, |Press|, and |Unpress| events. kSbInputDeviceTypeMouse, - // Input from a TV remote control-style device. + // Input from a TV remote-control-style device. // - // Produces Press and Unpress events. + // Produces |Press| and |Unpress| events. kSbInputDeviceTypeRemote, // Input from a speech command analyzer, which is some hardware or software // that, given a set of known phrases, activates when one of the registered // phrases is heard. // - // Produces Command events. + // Produces |Command| events. kSbInputDeviceTypeSpeechCommand, // Input from a single- or multi-touchscreen. // - // Produces Move, Press, and Unpress events. + // Produces |Move|, |Press|, and |Unpress| events. kSbInputDeviceTypeTouchScreen, - // Input from a touchpad that isn't masquerading as a mouse. + // Input from a touchpad that is not masquerading as a mouse. // - // Produces Move, Press, and Unpress events. + // Produces |Move|, |Press|, and |Unpress| events. kSbInputDeviceTypeTouchPad, } SbInputDeviceType; @@ -90,28 +93,26 @@ // like a speech recognizer. kSbInputEventTypeCommand, - // Grab activation. In some gesture systems a grab gesture is dfferent from an - // activate gesture. An Ungrab event will follow when the grab gesture is - // terminated. + // Grab activation. This event type is deprecated. kSbInputEventTypeGrab, - // Device Movement. In the case of Mouse, and perhaps Gesture, the movement - // tracks an absolute cursor position. In the case of TouchPad, only relative - // movements are provided. + // Device Movement. In the case of |Mouse|, and perhaps |Gesture|, the + // movement tracks an absolute cursor position. In the case of |TouchPad|, + // only relative movements are provided. kSbInputEventTypeMove, - // Key or button press activation. This could be a key on a keyboard, a button - // on a mouse or game controller, or a push from a touch screen or gesture. An - // Unpress event will be delivered once the key/button/finger is - // raised. Injecting repeat presses is up to the client. + // Key or button press activation. This could be a key on a keyboard, a + // button on a mouse or game controller, a push from a touch screen, or + // a gesture. An |Unpress| event is subsequently delivered when the + // |Press| event terminates, such as when the key/button/finger is raised. + // Injecting repeat presses is up to the client. kSbInputEventTypePress, - // Grab deactivation. An Ungrab event will be sent when a grab gesture is - // terminated. + // Grab deactivation. This event type is deprecated. kSbInputEventTypeUngrab, - // Key or button deactivation. The counterpart to the Press event, this event - // is sent when the key or button being pressed is released. + // Key or button deactivation. The counterpart to the |Press| event, this + // event is sent when the key or button being pressed is released. kSbInputEventTypeUnpress, } SbInputEventType; @@ -121,42 +122,44 @@ int y; } SbInputVector; -// Event data for kSbEventTypeInput events. +// Event data for |kSbEventTypeInput| events. typedef struct SbInputData { - // The window that this input was generated at. + // The window in which the input was generated. SbWindow window; - // The type of input event this represents. + // The type of input event that this represents. SbInputEventType type; // The type of device that generated this input event. SbInputDeviceType device_type; - // An identifier unique amongst all connected devices. + // An identifier that is unique among all connected devices. int device_id; // An identifier that indicates which keyboard key or mouse button was // involved in this event, if any. All known keys for all devices are mapped - // to a single ID space, defined by the enum SbKey in key.h. + // to a single ID space, defined by the |SbKey| enum in |key.h|. SbKey key; // The character that corresponds to the key. For an external keyboard, this - // character also depends on the language of keyboard type. Will be 0 if there + // character also depends on the keyboard language. The value is |0| if there // is no corresponding character. wchar_t character; // The location of the specified key, in cases where there are multiple - // instances of the button on the keyboard. The "shift" key, for example. + // instances of the button on the keyboard. For example, some keyboards + // have more than one "shift" key. SbKeyLocation key_location; - // Key modifiers (e.g. Ctrl, Chift) held down during this input event. + // Key modifiers (e.g. |Ctrl|, |Shift|) held down during this input event. unsigned int key_modifiers; // The (x, y) coordinates of the persistent cursor controlled by this - // device. Will be 0 if not applicable. + // device. The value is |0| if this data is not applicable. SbInputVector position; - // The relative motion vector of this input. Will be 0 if not applicable. + // The relative motion vector of this input. The value is |0| if this data + // is not applicable. SbInputVector delta; } SbInputData;
diff --git a/src/starboard/key.h b/src/starboard/key.h index 05f05d9..b50608f 100644 --- a/src/starboard/key.h +++ b/src/starboard/key.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// The canonical set of Starboard key codes. +// Module Overview: Starboard Key module +// +// Defines the canonical set of Starboard key codes. #ifndef STARBOARD_KEY_H_ #define STARBOARD_KEY_H_ @@ -21,17 +23,16 @@ extern "C" { #endif -// A standard set of key codes representing each possible input key across all -// kinds of devices, we use the semi-standard Windows virtual key codes. -// -// Windows virtual key codes doc: +// A standard set of key codes, ordered by value, that represent each possible +// input key across all kinds of devices. Starboard uses the semi-standard +// Windows virtual key codes documented at: // https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731%28v=vs.85%29.aspx -// -// The order here is by value. typedef enum SbKey { kSbKeyUnknown = 0, kSbKeyCancel = 0x03, - kSbKeyBack = 0x08, + kSbKeyBackspace = 0x08, + kSbKeyBack = kSbKeyBackspace, // You probably want kSbKeyEscape for a + // semantic "back". kSbKeyTab = 0x09, kSbKeyBacktab = 0x0A, kSbKeyClear = 0x0C,
diff --git a/src/starboard/linux/shared/configuration_public.h b/src/starboard/linux/shared/configuration_public.h index b374b00..afd4574 100644 --- a/src/starboard/linux/shared/configuration_public.h +++ b/src/starboard/linux/shared/configuration_public.h
@@ -24,7 +24,7 @@ #define STARBOARD_LINUX_SHARED_CONFIGURATION_PUBLIC_H_ #ifndef SB_API_VERSION -#define SB_API_VERSION 1 +#define SB_API_VERSION 2 #endif // --- System Header Configuration ------------------------------------------- @@ -56,6 +56,12 @@ // Whether the current platform provides the standard header float.h. #define SB_HAS_FLOAT_H 1 +// Whether the current platform has microphone supported. +#define SB_HAS_MICROPHONE 0 + +// Whether the current platform has speech synthesis. +#define SB_HAS_SPEECH_SYNTHESIS 0 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) @@ -375,6 +381,12 @@ // The maximum number of users that can be signed in at the same time. #define SB_USER_MAX_SIGNED_IN 1 +// --- Timing API ------------------------------------------------------------ + +// Whether this platform has an API to retrieve how long the current thread +// has spent in the executing state. +#define SB_HAS_TIME_THREAD_NOW 1 + // --- Platform Specific Audits ---------------------------------------------- #if !defined(__GNUC__)
diff --git a/src/starboard/linux/shared/starboard_base_symbolize.gyp b/src/starboard/linux/shared/starboard_base_symbolize.gyp new file mode 100644 index 0000000..5cd3440 --- /dev/null +++ b/src/starboard/linux/shared/starboard_base_symbolize.gyp
@@ -0,0 +1,25 @@ +# Copyright 2016 Google Inc. 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. +{ + 'targets': [ + { + 'target_name': 'starboard_base_symbolize', + 'type': 'static_library', + 'sources': [ + '<(DEPTH)/base/third_party/symbolize/demangle.cc', + '<(DEPTH)/base/third_party/symbolize/symbolize.cc', + ], + }, + ], +}
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi new file mode 100644 index 0000000..1030459 --- /dev/null +++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -0,0 +1,281 @@ +# Copyright 2016 Google Inc. 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. +{ + 'variables': { + 'starboard_platform_sources': [ + '<(DEPTH)/starboard/linux/shared/atomic_public.h', + '<(DEPTH)/starboard/linux/shared/configuration_public.h', + '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc', + '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc', + '<(DEPTH)/starboard/linux/shared/system_get_path.cc', + '<(DEPTH)/starboard/linux/shared/system_has_capability.cc', + '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc', + '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h', + '<(DEPTH)/starboard/shared/alsa/alsa_util.cc', + '<(DEPTH)/starboard/shared/alsa/alsa_util.h', + '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc', + '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc', + '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc', + '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc', + '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', + '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', + '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h', + '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', + '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', + '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc', + '<(DEPTH)/starboard/shared/iso/character_is_space.cc', + '<(DEPTH)/starboard/shared/iso/character_is_upper.cc', + '<(DEPTH)/starboard/shared/iso/character_to_lower.cc', + '<(DEPTH)/starboard/shared/iso/character_to_upper.cc', + '<(DEPTH)/starboard/shared/iso/directory_close.cc', + '<(DEPTH)/starboard/shared/iso/directory_get_next.cc', + '<(DEPTH)/starboard/shared/iso/directory_open.cc', + '<(DEPTH)/starboard/shared/iso/double_absolute.cc', + '<(DEPTH)/starboard/shared/iso/double_exponent.cc', + '<(DEPTH)/starboard/shared/iso/double_floor.cc', + '<(DEPTH)/starboard/shared/iso/double_is_finite.cc', + '<(DEPTH)/starboard/shared/iso/double_is_nan.cc', + '<(DEPTH)/starboard/shared/iso/memory_compare.cc', + '<(DEPTH)/starboard/shared/iso/memory_copy.cc', + '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc', + '<(DEPTH)/starboard/shared/iso/memory_move.cc', + '<(DEPTH)/starboard/shared/iso/memory_set.cc', + '<(DEPTH)/starboard/shared/iso/string_compare.cc', + '<(DEPTH)/starboard/shared/iso/string_compare_all.cc', + '<(DEPTH)/starboard/shared/iso/string_find_character.cc', + '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc', + '<(DEPTH)/starboard/shared/iso/string_find_string.cc', + '<(DEPTH)/starboard/shared/iso/string_get_length.cc', + '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc', + '<(DEPTH)/starboard/shared/iso/string_parse_double.cc', + '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc', + '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc', + '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc', + '<(DEPTH)/starboard/shared/iso/string_scan.cc', + '<(DEPTH)/starboard/shared/iso/system_binary_search.cc', + '<(DEPTH)/starboard/shared/iso/system_sort.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc', + '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc', + '<(DEPTH)/starboard/shared/linux/byte_swap.cc', + '<(DEPTH)/starboard/shared/linux/get_home_directory.cc', + '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc', + '<(DEPTH)/starboard/shared/linux/page_internal.cc', + '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc', + '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc', + '<(DEPTH)/starboard/shared/linux/system_get_stack.cc', + '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc', + '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc', + '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc', + '<(DEPTH)/starboard/shared/linux/system_symbolize.cc', + '<(DEPTH)/starboard/shared/linux/thread_get_id.cc', + '<(DEPTH)/starboard/shared/linux/thread_get_name.cc', + '<(DEPTH)/starboard/shared/linux/thread_set_name.cc', + '<(DEPTH)/starboard/shared/nouser/user_get_current.cc', + '<(DEPTH)/starboard/shared/nouser/user_get_property.cc', + '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc', + '<(DEPTH)/starboard/shared/nouser/user_internal.cc', + '<(DEPTH)/starboard/shared/posix/directory_create.cc', + '<(DEPTH)/starboard/shared/posix/file_can_open.cc', + '<(DEPTH)/starboard/shared/posix/file_close.cc', + '<(DEPTH)/starboard/shared/posix/file_delete.cc', + '<(DEPTH)/starboard/shared/posix/file_exists.cc', + '<(DEPTH)/starboard/shared/posix/file_flush.cc', + '<(DEPTH)/starboard/shared/posix/file_get_info.cc', + '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc', + '<(DEPTH)/starboard/shared/posix/file_open.cc', + '<(DEPTH)/starboard/shared/posix/file_read.cc', + '<(DEPTH)/starboard/shared/posix/file_seek.cc', + '<(DEPTH)/starboard/shared/posix/file_truncate.cc', + '<(DEPTH)/starboard/shared/posix/file_write.cc', + '<(DEPTH)/starboard/shared/posix/log.cc', + '<(DEPTH)/starboard/shared/posix/log_flush.cc', + '<(DEPTH)/starboard/shared/posix/log_format.cc', + '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', + '<(DEPTH)/starboard/shared/posix/log_raw.cc', + '<(DEPTH)/starboard/shared/posix/memory_flush.cc', + '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', + '<(DEPTH)/starboard/shared/posix/socket_accept.cc', + '<(DEPTH)/starboard/shared/posix/socket_bind.cc', + '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc', + '<(DEPTH)/starboard/shared/posix/socket_connect.cc', + '<(DEPTH)/starboard/shared/posix/socket_create.cc', + '<(DEPTH)/starboard/shared/posix/socket_destroy.cc', + '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc', + '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc', + '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc', + '<(DEPTH)/starboard/shared/posix/socket_internal.cc', + '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc', + '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc', + '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc', + '<(DEPTH)/starboard/shared/posix/socket_listen.cc', + '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc', + '<(DEPTH)/starboard/shared/posix/socket_resolve.cc', + '<(DEPTH)/starboard/shared/posix/socket_send_to.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc', + '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc', + '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc', + '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc', + '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc', + '<(DEPTH)/starboard/shared/posix/string_format.cc', + '<(DEPTH)/starboard/shared/posix/string_format_wide.cc', + '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc', + '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc', + '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc', + '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc', + '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc', + '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', + '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', + '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', + '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc', + '<(DEPTH)/starboard/shared/posix/time_get_now.cc', + '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', + '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc', + '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc', + '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc', + '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc', + '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc', + '<(DEPTH)/starboard/shared/pthread/mutex_create.cc', + '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc', + '<(DEPTH)/starboard/shared/pthread/mutex_release.cc', + '<(DEPTH)/starboard/shared/pthread/once.cc', + '<(DEPTH)/starboard/shared/pthread/thread_create.cc', + '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc', + '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc', + '<(DEPTH)/starboard/shared/pthread/thread_detach.cc', + '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc', + '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc', + '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc', + '<(DEPTH)/starboard/shared/pthread/thread_join.cc', + '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc', + '<(DEPTH)/starboard/shared/pthread/thread_yield.cc', + '<(DEPTH)/starboard/shared/signal/crash_signals.h', + '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc', + '<(DEPTH)/starboard/shared/signal/suspend_signals.cc', + '<(DEPTH)/starboard/shared/signal/suspend_signals.h', + '<(DEPTH)/starboard/shared/starboard/application.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h', + '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc', + '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h', + '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc', + '<(DEPTH)/starboard/shared/starboard/event_cancel.cc', + '<(DEPTH)/starboard/shared/starboard/event_schedule.cc', + '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc', + '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc', + '<(DEPTH)/starboard/shared/starboard/log_message.cc', + '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc', + '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc', + '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc', + '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc', + '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc', + '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc', + '<(DEPTH)/starboard/shared/starboard/media/mime_type.h', + '<(DEPTH)/starboard/shared/starboard/new.cc', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc', + '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc', + '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc', + '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc', + '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/player_create.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', + '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_worker.h', + '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc', + '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc', + '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc', + '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h', + '<(DEPTH)/starboard/shared/starboard/queue_application.cc', + '<(DEPTH)/starboard/shared/starboard/string_concat.cc', + '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc', + '<(DEPTH)/starboard/shared/starboard/string_copy.cc', + '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc', + '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc', + '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc', + '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc', + '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc', + '<(DEPTH)/starboard/shared/stub/drm_close_session.cc', + '<(DEPTH)/starboard/shared/stub/drm_create_system.cc', + '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc', + '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc', + '<(DEPTH)/starboard/shared/stub/drm_system_internal.h', + '<(DEPTH)/starboard/shared/stub/drm_update_session.cc', + '<(DEPTH)/starboard/shared/stub/media_is_supported.cc', + '<(DEPTH)/starboard/shared/stub/microphone_close.cc', + '<(DEPTH)/starboard/shared/stub/microphone_create.cc', + '<(DEPTH)/starboard/shared/stub/microphone_destroy.cc', + '<(DEPTH)/starboard/shared/stub/microphone_get_available.cc', + '<(DEPTH)/starboard/shared/stub/microphone_is_sample_rate_supported.cc', + '<(DEPTH)/starboard/shared/stub/microphone_open.cc', + '<(DEPTH)/starboard/shared/stub/microphone_read.cc', + '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc', + '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc', + '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc', + '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc', + '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc', + ], + 'starboard_platform_dependencies': [ + '<(DEPTH)/starboard/common/common.gyp:common', + '<(DEPTH)/starboard/linux/shared/starboard_base_symbolize.gyp:starboard_base_symbolize', + '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', + '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', + ], + }, +}
diff --git a/src/starboard/linux/x64directfb/configuration_public.h b/src/starboard/linux/x64directfb/configuration_public.h index c891232..6f91914 100644 --- a/src/starboard/linux/x64directfb/configuration_public.h +++ b/src/starboard/linux/x64directfb/configuration_public.h
@@ -52,4 +52,8 @@ #define SB_PREFERRED_RGBA_BYTE_ORDER SB_PREFERRED_RGBA_BYTE_ORDER_BGRA #endif +// The current platform has microphone supported. +#undef SB_HAS_MICROPHONE +#define SB_HAS_MICROPHONE 1 + #endif // STARBOARD_LINUX_X64DIRECTFB_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gyp b/src/starboard/linux/x64directfb/starboard_platform.gyp index 2419586..c45f24a 100644 --- a/src/starboard/linux/x64directfb/starboard_platform.gyp +++ b/src/starboard/linux/x64directfb/starboard_platform.gyp
@@ -12,309 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. { + 'includes': [ + 'starboard_platform.gypi' + ], 'targets': [ { - 'target_name': 'starboard_base_symbolize', - 'type': 'static_library', - 'sources': [ - '<(DEPTH)/base/third_party/symbolize/demangle.cc', - '<(DEPTH)/base/third_party/symbolize/symbolize.cc', - ], - }, - { 'target_name': 'starboard_platform', 'type': 'static_library', - 'sources': [ - '<(DEPTH)/starboard/linux/shared/atomic_public.h', - '<(DEPTH)/starboard/linux/shared/configuration_public.h', - '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc', - '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc', - '<(DEPTH)/starboard/linux/shared/system_get_path.cc', - '<(DEPTH)/starboard/linux/shared/system_has_capability.cc', - '<(DEPTH)/starboard/linux/x64directfb/main.cc', - '<(DEPTH)/starboard/linux/x64directfb/system_get_property.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h', - '<(DEPTH)/starboard/shared/alsa/alsa_util.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_util.h', - '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc', - '<(DEPTH)/starboard/shared/directfb/application_directfb.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_blit_rect_to_rect.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_context.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_default_device.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_pixel_data.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_render_target_surface.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_surface_from_pixel_data.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_create_swap_chain_from_window.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_destroy_context.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_destroy_device.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_destroy_pixel_data.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_destroy_surface.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_destroy_swap_chain.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_download_surface_pixels.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_fill_rect.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_flip_swap_chain.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_flush_context.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_max_contexts.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pitch_in_bytes.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pointer.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_surface.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_swap_chain.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_get_surface_info.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_internal.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_download_surface_pixels.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_pixel_data.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_is_surface_format_supported_by_render_target_surface.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_set_blending.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_set_color.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_set_modulate_blits_with_color.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_set_render_target.cc', - '<(DEPTH)/starboard/shared/directfb/blitter_set_scissor.cc', - '<(DEPTH)/starboard/shared/directfb/window_create.cc', - '<(DEPTH)/starboard/shared/directfb/window_destroy.cc', - '<(DEPTH)/starboard/shared/directfb/window_get_platform_handle.cc', - '<(DEPTH)/starboard/shared/directfb/window_get_size.cc', - '<(DEPTH)/starboard/shared/directfb/window_internal.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', - '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h', - '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', - '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', - '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc', - '<(DEPTH)/starboard/shared/iso/character_is_space.cc', - '<(DEPTH)/starboard/shared/iso/character_is_upper.cc', - '<(DEPTH)/starboard/shared/iso/character_to_lower.cc', - '<(DEPTH)/starboard/shared/iso/character_to_upper.cc', - '<(DEPTH)/starboard/shared/iso/directory_close.cc', - '<(DEPTH)/starboard/shared/iso/directory_get_next.cc', - '<(DEPTH)/starboard/shared/iso/directory_open.cc', - '<(DEPTH)/starboard/shared/iso/double_absolute.cc', - '<(DEPTH)/starboard/shared/iso/double_exponent.cc', - '<(DEPTH)/starboard/shared/iso/double_floor.cc', - '<(DEPTH)/starboard/shared/iso/double_is_finite.cc', - '<(DEPTH)/starboard/shared/iso/double_is_nan.cc', - '<(DEPTH)/starboard/shared/iso/memory_compare.cc', - '<(DEPTH)/starboard/shared/iso/memory_copy.cc', - '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc', - '<(DEPTH)/starboard/shared/iso/memory_move.cc', - '<(DEPTH)/starboard/shared/iso/memory_set.cc', - '<(DEPTH)/starboard/shared/iso/string_compare.cc', - '<(DEPTH)/starboard/shared/iso/string_compare_all.cc', - '<(DEPTH)/starboard/shared/iso/string_find_character.cc', - '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc', - '<(DEPTH)/starboard/shared/iso/string_find_string.cc', - '<(DEPTH)/starboard/shared/iso/string_get_length.cc', - '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_double.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc', - '<(DEPTH)/starboard/shared/iso/string_scan.cc', - '<(DEPTH)/starboard/shared/iso/system_binary_search.cc', - '<(DEPTH)/starboard/shared/iso/system_sort.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc', - '<(DEPTH)/starboard/shared/linux/byte_swap.cc', - '<(DEPTH)/starboard/shared/linux/get_home_directory.cc', - '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc', - '<(DEPTH)/starboard/shared/linux/page_internal.cc', - '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc', - '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc', - '<(DEPTH)/starboard/shared/linux/system_get_stack.cc', - '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc', - '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc', - '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc', - '<(DEPTH)/starboard/shared/linux/system_symbolize.cc', - '<(DEPTH)/starboard/shared/linux/thread_get_id.cc', - '<(DEPTH)/starboard/shared/linux/thread_get_name.cc', - '<(DEPTH)/starboard/shared/linux/thread_set_name.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_current.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_property.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc', - '<(DEPTH)/starboard/shared/nouser/user_internal.cc', - '<(DEPTH)/starboard/shared/posix/directory_create.cc', - '<(DEPTH)/starboard/shared/posix/file_can_open.cc', - '<(DEPTH)/starboard/shared/posix/file_close.cc', - '<(DEPTH)/starboard/shared/posix/file_delete.cc', - '<(DEPTH)/starboard/shared/posix/file_exists.cc', - '<(DEPTH)/starboard/shared/posix/file_flush.cc', - '<(DEPTH)/starboard/shared/posix/file_get_info.cc', - '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc', - '<(DEPTH)/starboard/shared/posix/file_open.cc', - '<(DEPTH)/starboard/shared/posix/file_read.cc', - '<(DEPTH)/starboard/shared/posix/file_seek.cc', - '<(DEPTH)/starboard/shared/posix/file_truncate.cc', - '<(DEPTH)/starboard/shared/posix/file_write.cc', - '<(DEPTH)/starboard/shared/posix/log.cc', - '<(DEPTH)/starboard/shared/posix/log_flush.cc', - '<(DEPTH)/starboard/shared/posix/log_format.cc', - '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', - '<(DEPTH)/starboard/shared/posix/log_raw.cc', - '<(DEPTH)/starboard/shared/posix/memory_flush.cc', - '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', - '<(DEPTH)/starboard/shared/posix/socket_accept.cc', - '<(DEPTH)/starboard/shared/posix/socket_bind.cc', - '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc', - '<(DEPTH)/starboard/shared/posix/socket_connect.cc', - '<(DEPTH)/starboard/shared/posix/socket_create.cc', - '<(DEPTH)/starboard/shared/posix/socket_destroy.cc', - '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc', - '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc', - '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc', - '<(DEPTH)/starboard/shared/posix/socket_internal.cc', - '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc', - '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc', - '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc', - '<(DEPTH)/starboard/shared/posix/socket_listen.cc', - '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc', - '<(DEPTH)/starboard/shared/posix/socket_resolve.cc', - '<(DEPTH)/starboard/shared/posix/socket_send_to.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc', - '<(DEPTH)/starboard/shared/posix/string_format.cc', - '<(DEPTH)/starboard/shared/posix/string_format_wide.cc', - '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc', - '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc', - '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc', - '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc', - '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc', - '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', - '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', - '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', - '<(DEPTH)/starboard/shared/posix/time_get_now.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_create.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_release.cc', - '<(DEPTH)/starboard/shared/pthread/once.cc', - '<(DEPTH)/starboard/shared/pthread/thread_create.cc', - '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc', - '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc', - '<(DEPTH)/starboard/shared/pthread/thread_detach.cc', - '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc', - '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc', - '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc', - '<(DEPTH)/starboard/shared/pthread/thread_join.cc', - '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc', - '<(DEPTH)/starboard/shared/pthread/thread_yield.cc', - '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc', - '<(DEPTH)/starboard/shared/signal/crash_signals.h', - '<(DEPTH)/starboard/shared/signal/suspend_signals.cc', - '<(DEPTH)/starboard/shared/signal/suspend_signals.h', - '<(DEPTH)/starboard/shared/starboard/application.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h', - '<(DEPTH)/starboard/shared/starboard/blitter_blit_rect_to_rect_tiled.cc', - '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc', - '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc', - '<(DEPTH)/starboard/shared/starboard/event_cancel.cc', - '<(DEPTH)/starboard/shared/starboard/event_schedule.cc', - '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc', - '<(DEPTH)/starboard/shared/starboard/log_message.cc', - '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc', - '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc', - '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc', - '<(DEPTH)/starboard/shared/starboard/media/mime_type.h', - '<(DEPTH)/starboard/shared/starboard/new.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/player_create.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_worker.h', - '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc', - '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h', - '<(DEPTH)/starboard/shared/starboard/queue_application.cc', - '<(DEPTH)/starboard/shared/starboard/string_concat.cc', - '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc', - '<(DEPTH)/starboard/shared/starboard/string_copy.cc', - '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc', - '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc', - '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc', - '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc', - '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc', - '<(DEPTH)/starboard/shared/stub/drm_close_session.cc', - '<(DEPTH)/starboard/shared/stub/drm_create_system.cc', - '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc', - '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc', - '<(DEPTH)/starboard/shared/stub/drm_system_internal.h', - '<(DEPTH)/starboard/shared/stub/drm_update_session.cc', - '<(DEPTH)/starboard/shared/stub/media_is_supported.cc', - '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc', - '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc', - '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc', - '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc', - '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc', - ], + 'sources': ['<@(starboard_platform_sources)'], 'include_dirs': [ '/usr/include/directfb', ], @@ -324,10 +29,7 @@ 'STARBOARD_IMPLEMENTATION', ], 'dependencies': [ - '<(DEPTH)/starboard/common/common.gyp:common', - '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', - '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', - 'starboard_base_symbolize', + '<@(starboard_platform_dependencies)', ], }, ],
diff --git a/src/starboard/linux/x64directfb/starboard_platform.gypi b/src/starboard/linux/x64directfb/starboard_platform.gypi new file mode 100644 index 0000000..f5d40ad --- /dev/null +++ b/src/starboard/linux/x64directfb/starboard_platform.gypi
@@ -0,0 +1,62 @@ +# Copyright 2016 Google Inc. 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. +{ + 'includes': ['../shared/starboard_platform.gypi'], + + 'variables': { + 'starboard_platform_sources': [ + '<(DEPTH)/starboard/linux/x64directfb/main.cc', + '<(DEPTH)/starboard/linux/x64directfb/system_get_property.cc', + '<(DEPTH)/starboard/shared/directfb/application_directfb.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_blit_rect_to_rect.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_context.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_default_device.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_pixel_data.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_render_target_surface.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_surface_from_pixel_data.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_create_swap_chain_from_window.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_destroy_context.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_destroy_device.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_destroy_pixel_data.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_destroy_surface.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_destroy_swap_chain.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_download_surface_pixels.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_fill_rect.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_flip_swap_chain.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_flush_context.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_max_contexts.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pitch_in_bytes.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_pixel_data_pointer.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_surface.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_render_target_from_swap_chain.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_get_surface_info.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_internal.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_download_surface_pixels.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_is_pixel_format_supported_by_pixel_data.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_is_surface_format_supported_by_render_target_surface.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_set_blending.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_set_color.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_set_modulate_blits_with_color.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_set_render_target.cc', + '<(DEPTH)/starboard/shared/directfb/blitter_set_scissor.cc', + '<(DEPTH)/starboard/shared/directfb/window_create.cc', + '<(DEPTH)/starboard/shared/directfb/window_destroy.cc', + '<(DEPTH)/starboard/shared/directfb/window_get_platform_handle.cc', + '<(DEPTH)/starboard/shared/directfb/window_get_size.cc', + '<(DEPTH)/starboard/shared/directfb/window_internal.cc', + '<(DEPTH)/starboard/shared/starboard/blitter_blit_rect_to_rect_tiled.cc', + '<(DEPTH)/starboard/shared/starboard/blitter_blit_rects_to_rects.cc', + ], + }, +}
diff --git a/src/starboard/linux/x64directfb/system_get_property.cc b/src/starboard/linux/x64directfb/system_get_property.cc index b1529fe..1d20f7f 100644 --- a/src/starboard/linux/x64directfb/system_get_property.cc +++ b/src/starboard/linux/x64directfb/system_get_property.cc
@@ -47,6 +47,7 @@ case kSbSystemPropertyModelName: case kSbSystemPropertyModelYear: case kSbSystemPropertyNetworkOperatorName: + case kSbSystemPropertySpeechApiKey: return false; case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/linux/x64x11/configuration_public.h b/src/starboard/linux/x64x11/configuration_public.h index 4aec879..8022c88 100644 --- a/src/starboard/linux/x64x11/configuration_public.h +++ b/src/starboard/linux/x64x11/configuration_public.h
@@ -109,9 +109,14 @@ // textures. These textures typically originate from video decoders. #define SB_HAS_NV12_TEXTURE_SUPPORT 1 -#define SB_HAS_MICROPHONE 0 +// Whether the current platform has speech synthesis. +#define SB_HAS_SPEECH_SYNTHESIS 0 // Include the Linux configuration that's common between all Desktop Linuxes. #include "starboard/linux/shared/configuration_public.h" +// The current platform has microphone supported. +#undef SB_HAS_MICROPHONE +#define SB_HAS_MICROPHONE 1 + #endif // STARBOARD_LINUX_X64X11_CONFIGURATION_PUBLIC_H_
diff --git a/src/starboard/linux/x64x11/future/gyp_configuration.gypi b/src/starboard/linux/x64x11/future/gyp_configuration.gypi index 0cb12cf..0bd1f09 100644 --- a/src/starboard/linux/x64x11/future/gyp_configuration.gypi +++ b/src/starboard/linux/x64x11/future/gyp_configuration.gypi
@@ -14,6 +14,13 @@ { 'target_defaults': { + # By default, <EGL/eglplatform.h> pulls in some X11 headers that have some + # nasty macros (|Status|, for example) that conflict with Chromium base. + # Since certain Cobalt headers now depend on EGL through SbDecodeTarget, + # we define this macro to configure EGL to not pull in these headers. + 'defines': [ + 'MESA_EGL_NO_X11_HEADERS' + ], 'default_configuration': 'linux-x64x11-future_debug', 'configurations': { 'linux-x64x11-future_debug': {
diff --git a/src/starboard/linux/x64x11/future/starboard_platform.gyp b/src/starboard/linux/x64x11/future/starboard_platform.gyp index 9648c9d..31eaa25 100644 --- a/src/starboard/linux/x64x11/future/starboard_platform.gyp +++ b/src/starboard/linux/x64x11/future/starboard_platform.gyp
@@ -12,16 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. { + 'includes': [ + '../starboard_platform.gypi' + ], 'targets': [ { 'target_name': 'starboard_platform', 'product_name': 'starboard_platform_future', 'type': 'static_library', 'sources': [ + '<@(starboard_platform_sources)', '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc', '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc', '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc', '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc', + '<(DEPTH)/starboard/shared/stub/image_decode.cc', + '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc', ], 'defines': [ # This must be defined when building Starboard, and must not when @@ -29,11 +36,7 @@ 'STARBOARD_IMPLEMENTATION', ], 'dependencies': [ - '<(DEPTH)/starboard/common/common.gyp:common', - '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_platform', - '<(DEPTH)/starboard/linux/x64x11/starboard_platform.gyp:starboard_base_symbolize', - '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', - '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', + '<@(starboard_platform_dependencies)', ], }, ],
diff --git a/src/starboard/linux/x64x11/starboard_platform.gyp b/src/starboard/linux/x64x11/starboard_platform.gyp index 228ceb0..56e5781 100644 --- a/src/starboard/linux/x64x11/starboard_platform.gyp +++ b/src/starboard/linux/x64x11/starboard_platform.gyp
@@ -12,287 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. { + 'includes': [ + 'starboard_platform.gypi' + ], 'targets': [ { - 'target_name': 'starboard_base_symbolize', - 'type': 'static_library', - 'sources': [ - '<(DEPTH)/base/third_party/symbolize/demangle.cc', - '<(DEPTH)/base/third_party/symbolize/symbolize.cc', - ], - }, - { 'target_name': 'starboard_platform', 'type': 'static_library', - 'sources': [ - '<(DEPTH)/starboard/linux/shared/atomic_public.h', - '<(DEPTH)/starboard/linux/shared/configuration_public.h', - '<(DEPTH)/starboard/linux/shared/system_get_connection_type.cc', - '<(DEPTH)/starboard/linux/shared/system_get_device_type.cc', - '<(DEPTH)/starboard/linux/shared/system_get_path.cc', - '<(DEPTH)/starboard/linux/shared/system_has_capability.cc', - '<(DEPTH)/starboard/linux/x64x11/main.cc', - '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc', - '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_audio_sink_type.h', - '<(DEPTH)/starboard/shared/alsa/alsa_util.cc', - '<(DEPTH)/starboard/shared/alsa/alsa_util.h', - '<(DEPTH)/starboard/shared/alsa/audio_sink_get_max_channels.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_get_nearest_supported_sample_frequency.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_frame_storage_type_supported.cc', - '<(DEPTH)/starboard/shared/alsa/audio_sink_is_audio_sample_type_supported.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_aligned_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_allocate_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_free.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_free_aligned.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_map.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_reallocate_unchecked.cc', - '<(DEPTH)/starboard/shared/dlmalloc/memory_unmap.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', - '<(DEPTH)/starboard/shared/gcc/atomic_gcc_public.h', - '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', - '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', - '<(DEPTH)/starboard/shared/iso/character_is_hex_digit.cc', - '<(DEPTH)/starboard/shared/iso/character_is_space.cc', - '<(DEPTH)/starboard/shared/iso/character_is_upper.cc', - '<(DEPTH)/starboard/shared/iso/character_to_lower.cc', - '<(DEPTH)/starboard/shared/iso/character_to_upper.cc', - '<(DEPTH)/starboard/shared/iso/directory_close.cc', - '<(DEPTH)/starboard/shared/iso/directory_get_next.cc', - '<(DEPTH)/starboard/shared/iso/directory_open.cc', - '<(DEPTH)/starboard/shared/iso/double_absolute.cc', - '<(DEPTH)/starboard/shared/iso/double_exponent.cc', - '<(DEPTH)/starboard/shared/iso/double_floor.cc', - '<(DEPTH)/starboard/shared/iso/double_is_finite.cc', - '<(DEPTH)/starboard/shared/iso/double_is_nan.cc', - '<(DEPTH)/starboard/shared/iso/memory_compare.cc', - '<(DEPTH)/starboard/shared/iso/memory_copy.cc', - '<(DEPTH)/starboard/shared/iso/memory_find_byte.cc', - '<(DEPTH)/starboard/shared/iso/memory_move.cc', - '<(DEPTH)/starboard/shared/iso/memory_set.cc', - '<(DEPTH)/starboard/shared/iso/string_compare.cc', - '<(DEPTH)/starboard/shared/iso/string_compare_all.cc', - '<(DEPTH)/starboard/shared/iso/string_find_character.cc', - '<(DEPTH)/starboard/shared/iso/string_find_last_character.cc', - '<(DEPTH)/starboard/shared/iso/string_find_string.cc', - '<(DEPTH)/starboard/shared/iso/string_get_length.cc', - '<(DEPTH)/starboard/shared/iso/string_get_length_wide.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_double.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_signed_integer.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_uint64.cc', - '<(DEPTH)/starboard/shared/iso/string_parse_unsigned_integer.cc', - '<(DEPTH)/starboard/shared/iso/string_scan.cc', - '<(DEPTH)/starboard/shared/iso/system_binary_search.cc', - '<(DEPTH)/starboard/shared/iso/system_sort.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_add.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_create.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_destroy.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_internal.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_remove.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wait_timed.cc', - '<(DEPTH)/starboard/shared/libevent/socket_waiter_wake_up.cc', - '<(DEPTH)/starboard/shared/linux/byte_swap.cc', - '<(DEPTH)/starboard/shared/linux/get_home_directory.cc', - '<(DEPTH)/starboard/shared/linux/memory_get_stack_bounds.cc', - '<(DEPTH)/starboard/shared/linux/page_internal.cc', - '<(DEPTH)/starboard/shared/linux/socket_get_local_interface_address.cc', - '<(DEPTH)/starboard/shared/linux/system_get_random_data.cc', - '<(DEPTH)/starboard/shared/linux/system_get_stack.cc', - '<(DEPTH)/starboard/shared/linux/system_get_total_cpu_memory.cc', - '<(DEPTH)/starboard/shared/linux/system_get_used_cpu_memory.cc', - '<(DEPTH)/starboard/shared/linux/system_is_debugger_attached.cc', - '<(DEPTH)/starboard/shared/linux/system_symbolize.cc', - '<(DEPTH)/starboard/shared/linux/thread_get_id.cc', - '<(DEPTH)/starboard/shared/linux/thread_get_name.cc', - '<(DEPTH)/starboard/shared/linux/thread_set_name.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_current.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_property.cc', - '<(DEPTH)/starboard/shared/nouser/user_get_signed_in.cc', - '<(DEPTH)/starboard/shared/nouser/user_internal.cc', - '<(DEPTH)/starboard/shared/posix/directory_create.cc', - '<(DEPTH)/starboard/shared/posix/file_can_open.cc', - '<(DEPTH)/starboard/shared/posix/file_close.cc', - '<(DEPTH)/starboard/shared/posix/file_delete.cc', - '<(DEPTH)/starboard/shared/posix/file_exists.cc', - '<(DEPTH)/starboard/shared/posix/file_flush.cc', - '<(DEPTH)/starboard/shared/posix/file_get_info.cc', - '<(DEPTH)/starboard/shared/posix/file_get_path_info.cc', - '<(DEPTH)/starboard/shared/posix/file_open.cc', - '<(DEPTH)/starboard/shared/posix/file_read.cc', - '<(DEPTH)/starboard/shared/posix/file_seek.cc', - '<(DEPTH)/starboard/shared/posix/file_truncate.cc', - '<(DEPTH)/starboard/shared/posix/file_write.cc', - '<(DEPTH)/starboard/shared/posix/log.cc', - '<(DEPTH)/starboard/shared/posix/log_flush.cc', - '<(DEPTH)/starboard/shared/posix/log_format.cc', - '<(DEPTH)/starboard/shared/posix/log_is_tty.cc', - '<(DEPTH)/starboard/shared/posix/log_raw.cc', - '<(DEPTH)/starboard/shared/posix/memory_flush.cc', - '<(DEPTH)/starboard/shared/posix/set_non_blocking_internal.cc', - '<(DEPTH)/starboard/shared/posix/socket_accept.cc', - '<(DEPTH)/starboard/shared/posix/socket_bind.cc', - '<(DEPTH)/starboard/shared/posix/socket_clear_last_error.cc', - '<(DEPTH)/starboard/shared/posix/socket_connect.cc', - '<(DEPTH)/starboard/shared/posix/socket_create.cc', - '<(DEPTH)/starboard/shared/posix/socket_destroy.cc', - '<(DEPTH)/starboard/shared/posix/socket_free_resolution.cc', - '<(DEPTH)/starboard/shared/posix/socket_get_last_error.cc', - '<(DEPTH)/starboard/shared/posix/socket_get_local_address.cc', - '<(DEPTH)/starboard/shared/posix/socket_internal.cc', - '<(DEPTH)/starboard/shared/posix/socket_is_connected.cc', - '<(DEPTH)/starboard/shared/posix/socket_is_connected_and_idle.cc', - '<(DEPTH)/starboard/shared/posix/socket_join_multicast_group.cc', - '<(DEPTH)/starboard/shared/posix/socket_listen.cc', - '<(DEPTH)/starboard/shared/posix/socket_receive_from.cc', - '<(DEPTH)/starboard/shared/posix/socket_resolve.cc', - '<(DEPTH)/starboard/shared/posix/socket_send_to.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_broadcast.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_receive_buffer_size.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_reuse_address.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_send_buffer_size.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_keep_alive.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_no_delay.cc', - '<(DEPTH)/starboard/shared/posix/socket_set_tcp_window_scaling.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_no_case.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_no_case_n.cc', - '<(DEPTH)/starboard/shared/posix/string_compare_wide.cc', - '<(DEPTH)/starboard/shared/posix/string_format.cc', - '<(DEPTH)/starboard/shared/posix/string_format_wide.cc', - '<(DEPTH)/starboard/shared/posix/system_break_into_debugger.cc', - '<(DEPTH)/starboard/shared/posix/system_clear_last_error.cc', - '<(DEPTH)/starboard/shared/posix/system_get_error_string.cc', - '<(DEPTH)/starboard/shared/posix/system_get_last_error.cc', - '<(DEPTH)/starboard/shared/posix/system_get_locale_id.cc', - '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', - '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', - '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', - '<(DEPTH)/starboard/shared/posix/time_get_now.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc', - '<(DEPTH)/starboard/shared/posix/time_zone_get_name.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_broadcast.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_create.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_destroy.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_signal.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_wait.cc', - '<(DEPTH)/starboard/shared/pthread/condition_variable_wait_timed.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_acquire.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_acquire_try.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_create.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_destroy.cc', - '<(DEPTH)/starboard/shared/pthread/mutex_release.cc', - '<(DEPTH)/starboard/shared/pthread/once.cc', - '<(DEPTH)/starboard/shared/pthread/thread_create.cc', - '<(DEPTH)/starboard/shared/pthread/thread_create_local_key.cc', - '<(DEPTH)/starboard/shared/pthread/thread_destroy_local_key.cc', - '<(DEPTH)/starboard/shared/pthread/thread_detach.cc', - '<(DEPTH)/starboard/shared/pthread/thread_get_current.cc', - '<(DEPTH)/starboard/shared/pthread/thread_get_local_value.cc', - '<(DEPTH)/starboard/shared/pthread/thread_is_equal.cc', - '<(DEPTH)/starboard/shared/pthread/thread_join.cc', - '<(DEPTH)/starboard/shared/pthread/thread_set_local_value.cc', - '<(DEPTH)/starboard/shared/pthread/thread_yield.cc', - '<(DEPTH)/starboard/shared/signal/crash_signals.h', - '<(DEPTH)/starboard/shared/signal/crash_signals_sigaction.cc', - '<(DEPTH)/starboard/shared/signal/suspend_signals.cc', - '<(DEPTH)/starboard/shared/signal/suspend_signals.h', - '<(DEPTH)/starboard/shared/starboard/application.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_create.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_destroy.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_internal.h', - '<(DEPTH)/starboard/shared/starboard/audio_sink/audio_sink_is_valid.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.cc', - '<(DEPTH)/starboard/shared/starboard/audio_sink/stub_audio_sink_type.h', - '<(DEPTH)/starboard/shared/starboard/directory_can_open.cc', - '<(DEPTH)/starboard/shared/starboard/event_cancel.cc', - '<(DEPTH)/starboard/shared/starboard/event_schedule.cc', - '<(DEPTH)/starboard/shared/starboard/file_mode_string_to_flags.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_close_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_delete_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_get_record_size.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_open_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_read_record.cc', - '<(DEPTH)/starboard/shared/starboard/file_storage/storage_write_record.cc', - '<(DEPTH)/starboard/shared/starboard/log_message.cc', - '<(DEPTH)/starboard/shared/starboard/log_raw_dump_stack.cc', - '<(DEPTH)/starboard/shared/starboard/log_raw_format.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_can_play_mime_and_key_system.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_is_output_protected.cc', - '<(DEPTH)/starboard/shared/starboard/media/media_set_output_protection.cc', - '<(DEPTH)/starboard/shared/starboard/media/mime_type.cc', - '<(DEPTH)/starboard/shared/starboard/media/mime_type.h', - '<(DEPTH)/starboard/shared/starboard/new.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_decoder_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/audio_renderer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_decoder_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/filter/video_renderer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/input_buffer_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/player_create.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_destroy.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_get_info.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_internal.h', - '<(DEPTH)/starboard/shared/starboard/player/player_seek.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_bounds.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_pause.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_set_volume.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_worker.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_worker.h', - '<(DEPTH)/starboard/shared/starboard/player/player_write_end_of_stream.cc', - '<(DEPTH)/starboard/shared/starboard/player/player_write_sample.cc', - '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.cc', - '<(DEPTH)/starboard/shared/starboard/player/video_frame_internal.h', - '<(DEPTH)/starboard/shared/starboard/queue_application.cc', - '<(DEPTH)/starboard/shared/starboard/string_concat.cc', - '<(DEPTH)/starboard/shared/starboard/string_concat_wide.cc', - '<(DEPTH)/starboard/shared/starboard/string_copy.cc', - '<(DEPTH)/starboard/shared/starboard/string_copy_wide.cc', - '<(DEPTH)/starboard/shared/starboard/string_duplicate.cc', - '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc', - '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc', - '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc', - '<(DEPTH)/starboard/shared/stub/drm_close_session.cc', - '<(DEPTH)/starboard/shared/stub/drm_create_system.cc', - '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc', - '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc', - '<(DEPTH)/starboard/shared/stub/drm_system_internal.h', - '<(DEPTH)/starboard/shared/stub/drm_update_session.cc', - '<(DEPTH)/starboard/shared/stub/media_is_supported.cc', - '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc', - '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc', - '<(DEPTH)/starboard/shared/stub/system_get_used_gpu_memory.cc', - '<(DEPTH)/starboard/shared/stub/system_hide_splash_screen.cc', - '<(DEPTH)/starboard/shared/stub/system_raise_platform_error.cc', - '<(DEPTH)/starboard/shared/x11/application_x11.cc', - '<(DEPTH)/starboard/shared/x11/window_create.cc', - '<(DEPTH)/starboard/shared/x11/window_destroy.cc', - '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc', - '<(DEPTH)/starboard/shared/x11/window_get_size.cc', - '<(DEPTH)/starboard/shared/x11/window_internal.cc', - ], + 'sources': ['<@(starboard_platform_sources)'], 'defines': [ # This must be defined when building Starboard, and must not when # building Starboard client code. 'STARBOARD_IMPLEMENTATION', ], 'dependencies': [ - '<(DEPTH)/starboard/common/common.gyp:common', - '<(DEPTH)/third_party/dlmalloc/dlmalloc.gyp:dlmalloc', - '<(DEPTH)/third_party/libevent/libevent.gyp:libevent', - 'starboard_base_symbolize', + '<@(starboard_platform_dependencies)', ], }, ],
diff --git a/src/starboard/linux/x64x11/starboard_platform.gypi b/src/starboard/linux/x64x11/starboard_platform.gypi new file mode 100644 index 0000000..05f1e12 --- /dev/null +++ b/src/starboard/linux/x64x11/starboard_platform.gypi
@@ -0,0 +1,30 @@ +# Copyright 2016 Google Inc. 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. +{ + 'includes': ['../shared/starboard_platform.gypi'], + + 'variables': { + 'starboard_platform_sources': [ + '<(DEPTH)/starboard/linux/x64x11/main.cc', + '<(DEPTH)/starboard/linux/x64x11/sanitizer_options.cc', + '<(DEPTH)/starboard/linux/x64x11/system_get_property.cc', + '<(DEPTH)/starboard/shared/x11/application_x11.cc', + '<(DEPTH)/starboard/shared/x11/window_create.cc', + '<(DEPTH)/starboard/shared/x11/window_destroy.cc', + '<(DEPTH)/starboard/shared/x11/window_get_platform_handle.cc', + '<(DEPTH)/starboard/shared/x11/window_get_size.cc', + '<(DEPTH)/starboard/shared/x11/window_internal.cc', + ], + }, +}
diff --git a/src/starboard/linux/x64x11/system_get_property.cc b/src/starboard/linux/x64x11/system_get_property.cc index cffaa35..b72747d 100644 --- a/src/starboard/linux/x64x11/system_get_property.cc +++ b/src/starboard/linux/x64x11/system_get_property.cc
@@ -47,6 +47,7 @@ case kSbSystemPropertyModelName: case kSbSystemPropertyModelYear: case kSbSystemPropertyNetworkOperatorName: + case kSbSystemPropertySpeechApiKey: return false; case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/log.h b/src/starboard/log.h index 3693925..8e86cd8 100644 --- a/src/starboard/log.h +++ b/src/starboard/log.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Debug logging. +// Module Overview: Starboard Logging module +// +// Defines debug logging functions. #ifndef STARBOARD_LOG_H_ #define STARBOARD_LOG_H_ @@ -41,28 +43,38 @@ kSbLogPriorityFatal, } SbLogPriority; -// Writes |message| at |priority| to the debug output log for this platform. -// Passing kSbLogPriorityFatal will not terminate the program, such policy must -// be enforced at the application level. |priority| may, in fact, be completely -// ignored on many platforms. No formatting is required to be done on the -// message, not even newline termination. That said, platforms may wish to -// adjust the message to be more suitable for their output method. This could be -// wrapping the text, or stripping unprintable characters. +// Writes |message| to the platform's debug output log. +// +// |priority|: The SbLogPriority at which the message should be logged. Note +// that passing |kSbLogPriorityFatal| does not terminate the program. Such a +// policy must be enforced at the application level. In fact, |priority| may +// be completely ignored on many platforms. +// |message|: The message to be logged. No formatting is required to be done +// on the value, including newline termination. That said, platforms can +// adjust the message to be more suitable for their output method by +// wrapping the text, stripping unprintable characters, etc. SB_EXPORT void SbLog(SbLogPriority priority, const char* message); // A bare-bones log output method that is async-signal-safe, i.e. safe to call -// from an asynchronous signal handler (e.g. a SIGSEGV handler). It should do no -// additional formatting. +// from an asynchronous signal handler (e.g. a |SIGSEGV| handler). It should not +// do any additional formatting. +// +// |message|: The message to be logged. SB_EXPORT void SbLogRaw(const char* message); // Dumps the stack of the current thread to the log in an async-signal-safe -// manner, i.e. safe to call from an asynchronous signal handler (e.g. a SIGSEGV -// handler). Does not include SbLogRawDumpStack itself, and will additionally -// skip |frames_to_skip| frames from the top of the stack. +// manner, i.e. safe to call from an asynchronous signal handler (e.g. a +// |SIGSEGV| handler). Does not include SbLogRawDumpStack itself. +// +// |frames_to_skip|: The number of frames to skip from the top of the stack +// when dumping the current thread to the log. This parameter lets you remove +// noise from helper functions that might end up on top of every stack dump +// so that the stack dump is just the relevant function stack where the +// problem occurred. SB_EXPORT void SbLogRawDumpStack(int frames_to_skip); // A formatted log output method that is async-signal-safe, i.e. safe to call -// from an asynchronous signal handler (e.g. a SIGSEGV handler). +// from an asynchronous signal handler (e.g. a |SIGSEGV| handler). SB_EXPORT void SbLogRawFormat(const char* format, va_list args) SB_PRINTF_FORMAT(1, 0); @@ -76,12 +88,12 @@ va_end(args); } -// A log output method, that additionally performs a string format on the way -// out. +// A log output method that additionally performs a string format on the +// data being logged. SB_EXPORT void SbLogFormat(const char* format, va_list args) SB_PRINTF_FORMAT(1, 0); -// Inline wrapper of SbLogFormat to convert from ellipsis to va_args. +// Inline wrapper of SbLogFormat that converts from ellipsis to va_args. static SB_C_INLINE void SbLogFormatF(const char* format, ...) SB_PRINTF_FORMAT(1, 2); void SbLogFormatF(const char* format, ...) { @@ -91,10 +103,10 @@ va_end(args); } -// Flushes the log buffer, on some platforms. +// Flushes the log buffer on some platforms. SB_EXPORT void SbLogFlush(); -// Returns whether the log output goes to a TTY or is being redirected. +// Indicates whether the log output goes to a TTY or is being redirected. SB_EXPORT bool SbLogIsTty(); #ifdef __cplusplus
diff --git a/src/starboard/media.h b/src/starboard/media.h index 89f2608..2c800ba 100644 --- a/src/starboard/media.h +++ b/src/starboard/media.h
@@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Media definitions that are common between the Decoder and Player interfaces. +// Module Overview: Starboard Media module +// +// Provides media definitions that are common between the Decoder and Player +// interfaces. #ifndef STARBOARD_MEDIA_H_ #define STARBOARD_MEDIA_H_ @@ -40,7 +43,7 @@ kSbMediaTypeVideo, } SbMediaType; -// Possibly supported types of video elementary streams. +// Types of video elementary streams that could be supported. typedef enum SbMediaVideoCodec { kSbMediaVideoCodecNone, @@ -54,7 +57,7 @@ kSbMediaVideoCodecVp9, } SbMediaVideoCodec; -// Possibly supported types of audio elementary streams. +// Types of audio elementary streams that can be supported. typedef enum SbMediaAudioCodec { kSbMediaAudioCodecNone, @@ -63,8 +66,9 @@ kSbMediaAudioCodecVorbis, } SbMediaAudioCodec; -// Types of media support which is a direct map as the result of canPlayType() -// specified in the following link: +// Indicates how confident the device is that it can play media resources +// of the given type. The values are a direct map of the canPlayType() method +// specified at the following link: // https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype typedef enum SbMediaSupportType { // The media type cannot be played. @@ -111,24 +115,27 @@ kSbMediaAudioSampleTypeFloat32, } SbMediaAudioSampleType; -// Possible audio frame storage types. Interleaved means the samples of a -// multi-channel audio stream are stored in one continuous buffer, samples at -// the same timestamp are stored one after another. Planar means the samples -// of each channel are stored in their own continuous buffer. For example, for -// stereo stream with channels L and R that contains samples with timestamp -// 0, 1, ..., interleaved means the samples are stored in one buffer as -// "L0 R0 L1 R1 L2 R2 ...". Planar means the samples are stored in two buffers -// "L0 L1 L2 ..." and "R0 R1 R2 ...". +// Possible audio frame storage types. typedef enum SbMediaAudioFrameStorageType { + // The samples of a multi-channel audio stream are stored in one continuous + // buffer. Samples at the same timestamp are stored one after another. For + // example, for a stereo stream with channels L and R that contains samples + // with timestamps 0, 1, 2, etc., the samples are stored in one buffer as + // "L0 R0 L1 R1 L2 R2 ...". kSbMediaAudioFrameStorageTypeInterleaved, + + // The samples of each channel are stored in their own continuous buffer. + // For example, for a stereo stream with channels L and R that contains + // samples with timestamps 0, 1, 2, etc., the samples are stored in two + // buffers "L0 L1 L2 ..." and "R0 R1 R2 ...". kSbMediaAudioFrameStorageTypePlanar, } SbMediaAudioFrameStorageType; // The set of information required by the decoder or player for each video // sample. typedef struct SbMediaVideoSampleInfo { - // Whether the associated sample is a key frame (I-frame). Video key frames - // must always start with SPS and PPS NAL units. + // Indicates whether the associated sample is a key frame (I-frame). + // Video key frames must always start with SPS and PPS NAL units. bool is_key_frame; // The frame width of this sample, in pixels. Also could be parsed from the @@ -148,11 +155,11 @@ // The platform-defined index of the associated audio output. int index; - // The type of audio connector. Will be the empty kSbMediaAudioConnectorNone + // The type of audio connector. Will be the empty |kSbMediaAudioConnectorNone| // if this device cannot provide this information. SbMediaAudioConnector connector; - // The expected latency of audio over this output, in microseconds, or 0 if + // The expected latency of audio over this output, in microseconds, or |0| if // this device cannot provide this information. SbTime latency; @@ -160,8 +167,8 @@ SbMediaAudioCodingType coding_type; // The number of audio channels currently supported by this device output, or - // 0 if this device cannot provide this information, in which case the caller - // can probably assume stereo output. + // |0| if this device cannot provide this information, in which case the + // caller can probably assume stereo output. int number_of_channels; } SbMediaAudioConfiguration; @@ -170,18 +177,14 @@ // decoder. // // The Sequence Header consists of a little-endian hexadecimal encoded -// WAVEFORMATEX structure followed by an Audio-specific configuration field. -// -// Specification of WAVEFORMATEX structure: +// |WAVEFORMATEX| structure followed by an Audio-specific configuration field. +// The |WAVEFORMATEX| structure is specified at: // http://msdn.microsoft.com/en-us/library/dd390970(v=vs.85).aspx -// -// AudioSpecificConfig defined here in section 1.6.2.1: -// http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF typedef struct SbMediaAudioHeader { // The waveform-audio format type code. uint16_t format_tag; - // The number of audio channels in this format. 1 for mono, 2 for stereo. + // The number of audio channels in this format. |1| for mono, |2| for stereo. uint16_t number_of_channels; // The sampling rate. @@ -193,13 +196,14 @@ // Byte block alignment, e.g, 4. uint16_t block_alignment; - // The bit depth for the stream this represents, e.g. 8 or 16. + // The bit depth for the stream this represents, e.g. |8| or |16|. uint16_t bits_per_sample; // The size, in bytes, of the audio_specific_config. uint16_t audio_specific_config_size; - // The AudioSpecificConfig, as specified in ISO/IEC-14496-3. + // The AudioSpecificConfig, as specified in ISO/IEC-14496-3, section 1.6.2.1: + // http://read.pudn.com/downloads98/doc/comm/401153/14496/ISO_IEC_14496-3%20Part%203%20Audio/C036083E_SUB1.PDF int8_t audio_specific_config[8]; } SbMediaAudioHeader; @@ -209,73 +213,99 @@ // --- Functions ------------------------------------------------------------- -// Returns whether decoding |video_codec|, |audio_codec|, and decrypting using -// |key_system| is supported together by this platform. If |video_codec| is -// kSbMediaVideoCodecNone or |audio_codec| is kSbMediaAudioCodecNone, this -// function should return true if |key_system| is supported on the platform to -// decode any supported input formats. +// Indicates whether this platform supports decoding |video_codec| and +// |audio_codec| along with decrypting using |key_system|. If |video_codec| is +// |kSbMediaVideoCodecNone| or if |audio_codec| is |kSbMediaAudioCodecNone|, +// this function should return |true| as long as |key_system| is supported on +// the platform to decode any supported input formats. +// +// |video_codec|: The |SbMediaVideoCodec| being checked for platform +// compatibility. +// |audio_codec|: The |SbMediaAudioCodec| being checked for platform +// compatibility. +// |key_system|: The key system being checked for platform compatibility. SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec, SbMediaAudioCodec audio_codec, const char* key_system); -// Returns whether a given combination of |frame_width| x |frame_height| frames -// at |bitrate| and |fps| is supported on this platform with |video_codec|. If -// |video_codec| is not supported under any condition, this function will always -// return false. +// Indicates whether a given combination of +// (|frame_width| x |frame_height|) frames at |bitrate| and |fps| is supported +// on this platform with |video_codec|. If |video_codec| is not supported under +// any condition, this function returns |false|. // -// Any of the parameters may be set to 0 to mean that they shouldn't be +// Setting any of the parameters to |0| indicates that they shouldn't be // considered. +// +// |video_codec|: The video codec used in the media content. +// |frame_width|: The frame width of the media content. +// |frame_height|: The frame height of the media content. +// |bitrate|: The bitrate of the media content. +// |fps|: The number of frames per second in the media content. SB_EXPORT bool SbMediaIsVideoSupported(SbMediaVideoCodec video_codec, int frame_width, int frame_height, int64_t bitrate, int fps); -// Returns whether |audio_codec| is supported on this platform at |bitrate|. If -// |audio_codec| is not supported under any condition, this function will always -// return false. +// Indicates whether this platform supports |audio_codec| at |bitrate|. +// If |audio_codec| is not supported under any condition, this function +// returns |false|. +// +// |audio_codec|: The media's audio codec (|SbMediaAudioCodec|). +// |bitrate|: The media's bitrate. SB_EXPORT bool SbMediaIsAudioSupported(SbMediaVideoCodec audio_codec, int64_t bitrate); -// Returns information on whether the playback of the specific media described -// by |mime| and encrypted using |key_system| can be played. -// |mime| contains the mime information of the media in the form of 'video/webm' -// or 'video/mp4; codecs="avc1.42001E"'. It may include arbitrary parameters -// like "codecs", "channels", etc.. -// |key_system| should be a lower case in fhe form of -// "com.example.somesystem" as suggested by -// https://w3c.github.io/encrypted-media/#key-system +// Returns information about whether the playback of the specific media +// described by |mime| and encrypted using |key_system| can be played. +// +// Note that neither |mime| nor |key_system| can be NULL. This function returns +// |kSbMediaSupportNotSupported| if either is NULL. +// +// |mime|: The mime information of the media in the form of |video/webm| +// or |video/mp4; codecs="avc1.42001E"|. It may include arbitrary parameters +// like "codecs", "channels", etc. +// |key_system|: A lowercase value in fhe form of "com.example.somesystem" +// as suggested by https://w3c.github.io/encrypted-media/#key-system // that can be matched exactly with known DRM key systems of the platform. // When |key_system| is an empty string, the return value is an indication for // non-encrypted media. -// Note that both |mime| and |key_system| cannot be NULL. This function returns -// kSbMediaSupportNotSupported if any of them is NULL. SB_EXPORT SbMediaSupportType SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system); -// Returns the number of audio outputs currently available on this device. It -// is expected that, even if the number of outputs or their audio configurations -// can't be determined, the platform will at least return a single output that -// supports at least stereo. +// Returns the number of audio outputs currently available on this device. +// Even if the number of outputs or their audio configurations can't be +// determined, it is expected that the platform will at least return a single +// output that supports at least stereo. SB_EXPORT int SbMediaGetAudioOutputCount(); -// Places the current physical audio configuration of audio output -// |output_index| on this device into |out_configuration|, which must not be -// NULL, or returns false if nothing could be determined on this platform, or if -// |output_index| doesn't exist on this device. +// Retrieves the current physical audio configuration of audio output +// |output_index| on this device and places it in |out_configuration|, +// which must not be NULL. +// +// This function returns |false| if nothing could be determined on this +// platform or if |output_index| does not exist on this device. +// +// |out_configuration|: The variable that holds the audio configuration +// information. SB_EXPORT bool SbMediaGetAudioConfiguration( int output_index, SbMediaAudioConfiguration* out_configuration); -// Returns whether output copy protection is currently enabled on all capable -// outputs. If true, then non-protection-capable outputs are expected to be +// Indicates whether output copy protection is currently enabled on all capable +// outputs. If |true|, then non-protection-capable outputs are expected to be // blanked. SB_EXPORT bool SbMediaIsOutputProtected(); // Enables or disables output copy protection on all capable outputs. If // enabled, then non-protection-capable outputs are expected to be blanked. -// Returns whether the operation was successful. Returns a success even if the -// call was redundant. +// +// The return value indicates whether the operation was successful, and the +// function returns a success even if the call is redundant in that it doesn't +// change the current value. +// +// |enabled|: Indicates whether output protection is enabled (|true|) or +// disabled. SB_EXPORT bool SbMediaSetOutputProtection(bool enabled); #ifdef __cplusplus
diff --git a/src/starboard/memory.h b/src/starboard/memory.h index f48b44e..51102d6 100644 --- a/src/starboard/memory.h +++ b/src/starboard/memory.h
@@ -11,13 +11,29 @@ // 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. + +// Module Overview: Starboard Memory module // -// Memory allocation, alignment, copying, and comparing. +// Defines functions for memory allocation, alignment, copying, and comparing. // -// DO NOT CALL SbMemoryAllocateUnchecked(), SbMemoryAllocateChecked() -// SbMemoryReallocateUnchecked(), SbMemoryReallocateChecked(), -// SbMemoryAllocateAlignedUnchecked(), SbMemoryAllocateAlignedChecked(), -// SbMemoryFree(), SbMemoryFreeAligned(). They are internal. +// #### Porters +// +// All of the "Unchecked" and "Free" functions must be implemented, but they +// should not be called directly. The Starboard platform wraps them with extra +// accounting under certain circumstances. +// +// #### Porters and Cobalt developers +// +// Nobody should call the "Checked", "Unchecked" or "Free" functions directly +// because that evades Starboard's memory tracking. In both port implementations +// and Cobalt code, you should always call SbMemoryAllocate and +// SbMemoryDeallocate rather than SbMemoryAllocateUnchecked and SbMemoryFree. +// +// - The "checked" functions are SbMemoryAllocateChecked(), +// SbMemoryReallocateChecked(), and SbMemoryAllocateAlignedChecked(). +// - The "unchecked" functions are SbMemoryAllocateUnchecked(), +// SbMemoryReallocateUnchecked(), and SbMemoryAllocateAlignedUnchecked(). +// - The "free" functions are SbMemoryFree() and SbMemoryFreeAligned(). #ifndef STARBOARD_MEMORY_H_ #define STARBOARD_MEMORY_H_ @@ -64,46 +80,63 @@ } } -// Allocates a chunk of memory of at least |size| bytes, returning it. If unable -// to allocate the memory, it returns NULL. If |size| is 0, it may return NULL -// or it may return a unique pointer value that can be passed to -// SbMemoryDeallocate. Meant to be a drop-in replacement for malloc. +// Allocates and returns a chunk of memory of at least |size| bytes. This +// function should be called from the Cobalt codebase. It is intended to +// be a drop-in replacement for |malloc|. // -// This memory function should be called from the Cobalt codebase. +// Note that this function returns |NULL| if it is unable to allocate the +// memory. +// +// |size|: The amount of memory to be allocated. If |size| is 0, the function +// may return |NULL| or it may return a unique pointer value that can be +// passed to SbMemoryDeallocate. SB_EXPORT void* SbMemoryAllocate(size_t size); -// Attempts to resize |memory| to be at least |size| bytes, without touching the -// contents. If it cannot perform the fast resize, it will allocate a new chunk -// of memory, copy the contents over, and free the previous chunk, returning a -// pointer to the new chunk. If it cannot perform the slow resize, it will -// return NULL, leaving the given memory chunk unchanged. |memory| may be NULL, -// in which case it behaves exactly like SbMemoryAllocateUnchecked. If |size| -// is 0, it may return NULL or it may return a unique pointer value that can be -// passed to SbMemoryDeallocate. Meant to be a drop-in replacement for realloc. +// Attempts to resize |memory| to be at least |size| bytes, without touching +// the contents of memory. +// - If the function cannot perform the fast resize, it allocates a new chunk +// of memory, copies the contents over, and frees the previous chunk, +// returning a pointer to the new chunk. +// - If the function cannot perform the slow resize, it returns |NULL|, +// leaving the given memory chunk unchanged. // -// This memory function should be called from the Cobalt codebase. +// This function should be called from the Cobalt codebase. It is meant +// to be a drop-in replacement for |realloc|. +// +// |memory|: The chunk of memory to be resized. |memory| may be NULL, in which +// case it behaves exactly like SbMemoryAllocateUnchecked. +// |size|: The size to which |memory| will be resized. If |size| is |0|, +// the function may return |NULL| or it may return a unique pointer value +// that can be passed to SbMemoryDeallocate. SB_EXPORT void* SbMemoryReallocate(void* memory, size_t size); -// Allocates a chunk of memory of at least |size| bytes, aligned to -// |alignment|, returning it. |alignment| must be a power of two, otherwise -// behavior is undefined. If unable to allocate the memory, it returns NULL. -// If |size| is 0, it may return NULL or it may return a unique aligned pointer -// value that can be passed to SbMemoryDeallocateAligned. Meant to be a drop-in -// replacement for memalign. +// Allocates and returns a chunk of memory of at least |size| bytes, aligned +// to |alignment|. This function should be called from the Cobalt codebase. +// It is meant to be a drop-in replacement for |memalign|. // -// This memory function should be called from the Cobalt codebase. +// The function returns |NULL| if it cannot allocate the memory. In addition, +// the function's behavior is undefined if |alignment| is not a power of two. +// +// |alignment|: The way that data is arranged and accessed in memory. The value +// must be a power of two. +// |size|: The size of the memory to be allocated. If |size| is |0|, the +// function may return |NULL| or it may return a unique aligned pointer value +// that can be passed to SbMemoryDeallocateAligned. SB_EXPORT void* SbMemoryAllocateAligned(size_t alignment, size_t size); -// Frees a previously allocated chunk of memory. If |memory| is NULL, then it -// does nothing. Meant to be a drop-in replacement for free. +// Frees a previously allocated chunk of memory. If |memory| is NULL, then the +// operation is a no-op. This function should be called from the Cobalt +// codebase. It is meant to be a drop-in replacement for |free|. // -// This memory function should be called from the Cobalt codebase. +// |memory|: The chunk of memory to be freed. SB_EXPORT void SbMemoryDeallocate(void* memory); -// Frees a previously allocated chunk of aligned memory. If |memory| is NULL, -// then it does nothing. Meant to be a drop-in replacement for _aligned_free. -// -// This memory function should be called from the Cobalt codebase. +// Frees a previously allocated chunk of aligned memory. This function should +// be called from the Cobalt codebase. It is meant to be a drop-in replacement +// for |_aligned_free|. + +// |memory|: The chunk of memory to be freed. If |memory| is NULL, then the +// function is a no-op. SB_EXPORT void SbMemoryDeallocateAligned(void* memory); ///////////////////////////////////////////////////////////////// @@ -148,19 +181,24 @@ #if SB_HAS(MMAP) // Allocates |size_bytes| worth of physical memory pages and maps them into an -// available virtual region. |flags| is the bitwise or of the protection flags -// for the mapped memory as specified in SbMemoryMapFlags. On some platforms, -// |name| appears in the debugger and can be up to 32 bytes. Returns -// SB_MEMORY_MAP_FAILED on failure, as NULL is a valid return value. +// available virtual region. This function returns |SB_MEMORY_MAP_FAILED| on +// failure. |NULL| is a valid return value. +// +// |size_bytes|: The amount of physical memory pages to be allocated. +// |flags|: The bitwise OR of the protection flags for the mapped memory +// as specified in |SbMemoryMapFlags|. +// |name|: A value that appears in the debugger on some platforms. The value +// can be up to 32 bytes. SB_EXPORT void* SbMemoryMap(int64_t size_bytes, int flags, const char* name); // Unmap |size_bytes| of physical pages starting from |virtual_address|, -// returning true on success. After this, [virtual_address, virtual_address + -// size_bytes) will not be read/writable. SbMemoryUnmap() can unmap multiple -// contiguous regions that were mapped with separate calls to -// SbMemoryMap(). E.g. if one call to SbMemoryMap(0x1000) returns (void*)0xA000 -// and another call to SbMemoryMap(0x1000) returns (void*)0xB000, -// SbMemoryUnmap(0xA000, 0x2000) should free both. +// returning |true| on success. After this function completes, +// [virtual_address, virtual_address + size_bytes) will not be read/writable. +// This function can unmap multiple contiguous regions that were mapped with +// separate calls to SbMemoryMap(). For example, if one call to +// |SbMemoryMap(0x1000)| returns |(void*)0xA000|, and another call to +// |SbMemoryMap(0x1000)| returns |(void*)0xB000|, +// |SbMemoryUnmap(0xA000, 0x2000)| should free both regions. SB_EXPORT bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes); #if SB_CAN(MAP_EXECUTABLE_MEMORY) @@ -172,51 +210,78 @@ #endif #endif // SB_HAS(MMAP) -// Gets the stack bounds for the current thread, placing the highest addressable -// byte + 1 in |out_high|, and the lowest addressable byte in |out_low|. +// Gets the stack bounds for the current thread. +// +// |out_high|: The highest addressable byte + 1 for the current thread. +// |out_low|: The lowest addressable byte for the current thread. SB_EXPORT void SbMemoryGetStackBounds(void** out_high, void** out_low); // Copies |count| sequential bytes from |source| to |destination|, without -// support for the |source| and |destination| regions overlapping. Behavior is -// undefined if |destination| or |source| are NULL. Does nothing if |count| is -// 0. Returns |destination|, for some reason. Meant to be a drop-in replacement -// for memcpy. +// support for the |source| and |destination| regions overlapping. This +// function is meant to be a drop-in replacement for |memcpy|. +// +// The function's behavior is undefined if |destination| or |source| are NULL, +// and the function is a no-op if |count| is 0. The return value is +// |destination|. +// +// |destination|: The destination of the copied memory. +// |source|: The source of the copied memory. +// |count|: The number of sequential bytes to be copied. SB_EXPORT void* SbMemoryCopy(void* destination, const void* source, size_t count); // Copies |count| sequential bytes from |source| to |destination|, with support -// for the |source| and |destination| regions overlapping. Behavior is -// undefined if |destination| or |source| are NULL. Does nothing if |count| is -// 0. Returns |destination|, for some reason. Meant to be a drop-in replacement -// for memmove. +// for the |source| and |destination| regions overlapping. This function is +// meant to be a drop-in replacement for |memmove|. +// +// The function's behavior is undefined if |destination| or |source| are NULL, +// and the function is a no-op if |count| is 0. The return value is +// |destination|. +// +// |destination|: The destination of the copied memory. +// |source|: The source of the copied memory. +// |count|: The number of sequential bytes to be copied. SB_EXPORT void* SbMemoryMove(void* destination, const void* source, size_t count); // Fills |count| sequential bytes starting at |destination|, with the unsigned -// char coercion of |byte_value|. Behavior is undefined if |destination| is -// NULL. Returns |destination|, for some reason. Does nothing if |count| is 0. -// Meant to be a drop-in replacement for memset. +// char coercion of |byte_value|. This function is meant to be a drop-in +// replacement for |memset|. +// +// The function's behavior is undefined if |destination| is NULL, and the +// function is a no-op if |count| is 0. The return value is |destination|. +// +// |destination|: The destination of the copied memory. +// |count|: The number of sequential bytes to be set. SB_EXPORT void* SbMemorySet(void* destination, int byte_value, size_t count); // Compares the contents of the first |count| bytes of |buffer1| and |buffer2|. -// returns -1 if |buffer1| is "less-than" |buffer2|, 0 if they are equal, or 1 -// if |buffer1| is "greater-than" |buffer2|. Meant to be a drop-in replacement -// for memcmp. +// This function returns: +// - |-1| if |buffer1| is "less-than" |buffer2| +// - |0| if |buffer1| and |buffer2| are equal +// - |1| if |buffer1| is "greater-than" |buffer2|. +// +// This function is meant to be a drop-in replacement for |memcmp|. +// +// |buffer1|: The first buffer to be compared. +// |buffer2|: The second buffer to be compared. +// |count|: The number of bytes to be compared. SB_EXPORT int SbMemoryCompare(const void* buffer1, const void* buffer2, size_t count); -// Finds the lower 8-bits of |value| in the first |count| bytes of |buffer|, -// returning a pointer to the first found occurrence, or NULL if not -// found. Meant to be a drop-in replacement for memchr. +// Finds the lower 8-bits of |value| in the first |count| bytes of |buffer| +// and returns either a pointer to the first found occurrence or |NULL| if +// the value is not found. This function is meant to be a drop-in replacement +// for |memchr|. SB_EXPORT const void* SbMemoryFindByte(const void* buffer, int value, size_t count); -// A wrapper that implements a drop-in replacement for calloc, since some -// packages actually seem to use it. +// A wrapper that implements a drop-in replacement for |calloc|, which is used +// in some packages. static SB_C_INLINE void* SbMemoryCalloc(size_t count, size_t size) { size_t total = count * size; void* result = SbMemoryAllocate(total);
diff --git a/src/starboard/memory_reporter.h b/src/starboard/memory_reporter.h new file mode 100644 index 0000000..1a17c38 --- /dev/null +++ b/src/starboard/memory_reporter.h
@@ -0,0 +1,105 @@ +// Copyright 2016 Google Inc. 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. + +// Module Overview: Memory Reporting API. +// +// Provides an interface for memory reporting. + +#ifndef STARBOARD_MEMORY_REPORTER_H_ +#define STARBOARD_MEMORY_REPORTER_H_ + +#include "starboard/configuration.h" +#include "starboard/export.h" +#include "starboard/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////// +// These are callbacks used by SbMemoryReporter to report memory +// allocation and deallocation. +/////////////////////////////////////////////////////////////////////////////// + +// A function to report a memory allocation from SbMemoryAllocate(). Note that +// operator new calls SbMemoryAllocate which will delegate to this callback. +typedef void (*SbMemoryReporterOnAlloc)(void* context, const void* memory, + size_t size); + +// A function to report a memory deallocation from SbMemoryDeallcoate(). Note +// that operator delete calls SbMemoryDeallocate which will delegate to this +// callback. +typedef void (*SbMemoryReporterOnDealloc)(void* context, const void* memory); + +// A function to report a memory mapping from SbMemoryMap(). +typedef void (*SbMemoryReporterOnMapMemory)(void* context, const void* memory, + size_t size); + +// A function to report a memory unmapping from SbMemoryUnmap(). +typedef void (*SbMemoryReporterOnUnMapMemory)(void* context, + const void* memory, + size_t size); + +// SbMemoryReporter allows memory reporting via user-supplied functions. +// The void* context is passed to every call back. +// It's strongly recommended that C-Style struct initialization is used +// so that the arguments can be typed check by the compiler. +// For example, +// SbMemoryReporter mem_reporter = { MallocCallback, .... context }; +struct SbMemoryReporter { + // Callback to report allocations. + SbMemoryReporterOnAlloc on_alloc_cb; + + // Callback to report deallocations. + SbMemoryReporterOnDealloc on_dealloc_cb; + + // Callback to report memory map. + SbMemoryReporterOnMapMemory on_mapmem_cb; + + // Callback to report memory unmap. + SbMemoryReporterOnUnMapMemory on_unmapmem_cb; + + // Optional, is passed to callbacks as first argument. + void* context; +}; + +// Sets the MemoryReporter. Any previous memory reporter is unset. No lifetime +// management is done internally on input pointer. +// +// Returns true if the memory reporter was set with no errors. If an error +// was reported then check the log for why it failed. +// +// Note that other than a thread-barrier-write of the input pointer, there is +// no thread safety guarantees with this function due to performance +// considerations. It's recommended that this be called once during the +// lifetime of the program, or not at all. Do not delete the supplied pointer, +// ever. +// Example (Good): +// SbMemoryReporter* mem_reporter = new ...; +// SbMemorySetReporter(&mem_reporter); +// ... +// SbMemorySetReporter(NULL); // allow value to leak. +// Example (Bad): +// SbMemoryReporter* mem_reporter = new ...; +// SbMemorySetReporter(&mem_reporter); +// ... +// SbMemorySetReporter(NULL); +// delete mem_reporter; // May crash. +SB_EXPORT bool SbMemorySetReporter(struct SbMemoryReporter* tracker); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // STARBOARD_MEMORY_REPORTER_H_
diff --git a/src/starboard/microphone.h b/src/starboard/microphone.h index b742f71..52464c3 100644 --- a/src/starboard/microphone.h +++ b/src/starboard/microphone.h
@@ -12,24 +12,29 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Microphone creation, control, audio data fetching and destruction. -// Multiple calls to |SbMicrophoneOpen| and |SbMicrophoneClose| are allowed, and -// the implementation also should take care of same calls of open and close in a -// row on a microphone. -// This API is not threadsafe and must be called from a single thread. +// Module Overview: Starboard Microphone module +// +// Defines functions for microphone creation, control, audio data fetching, +// and destruction. This module supports multiple calls to |SbMicrophoneOpen| +// and |SbMicrophoneClose|, and the implementation should handle multiple calls +// to one of those functions on the same microphone. For example, your +// implementation should handle cases where |SbMicrophoneOpen| is called twice +// on the same microphone without a call to |SbMicrophoneClose| in between. +// +// This API is not thread-safe and must be called from a single thread. // // How to use this API: -// 1) |SbMicrophoneGetAvailableInfos| to get a list of available microphone +// 1. Call |SbMicrophoneGetAvailableInfos| to get a list of available microphone // information. -// 2) Choose one to create microphone |SbMicrophoneCreate| with enough buffer -// size and sample rate. The sample rate can be verified by -// |SbMicrophoneIsSampleRateSupported|. -// 3) Open the microphone port and start recording audio data by -// |SbMicrophoneOpen|. -// 4) Periodically read out the data from microphone by |SbMicrophoneRead|. -// 5) Close the microphone port and stop recording audio data by -// |SbMicrophoneClose|. -// 6) Destroy the microphone |SbMicrophoneDestroy|. +// 2. Create a supported microphone, using |SbMicrophoneCreate|, with enough +// buffer size and sample rate. Use |SbMicrophoneIsSampleRateSupported| to +// verify the sample rate. +// 3. Use |SbMicrophoneOpen| to open the microphone port and start recording +// audio data. +// 4. Periodically read out the data from microphone with |SbMicrophoneRead|. +// 5. Call |SbMicrophoneClose| to close the microphone port and stop recording +// audio data. +// 6. Destroy the microphone with |SbMicrophoneDestroy|. #ifndef STARBOARD_MICROPHONE_H_ #define STARBOARD_MICROPHONE_H_ @@ -46,10 +51,10 @@ // All possible microphone types. typedef enum SbMicrophoneType { - // Build-in microphone in camera. + // Built-in microphone in camera. kSbMicrophoneCamera, - // Microphone in the headset which can be wire or wireless USB headset. + // Microphone in the headset that can be a wired or wireless USB headset. kSbMicrophoneUSBHeadset, // Microphone in the VR headset. @@ -58,19 +63,19 @@ // Microphone in the analog headset. kSBMicrophoneAnalogHeadset, - // Unknown microphone type. Microphone other than those listed or could be - // either of those listed. + // Unknown microphone type. The microphone could be different than the other + // enum descriptions or could fall under one of those descriptions. kSbMicrophoneUnknown, } SbMicrophoneType; -// An opaque handle to an implementation-private structure representing a -// microphone id. +// An opaque handle to an implementation-private structure that represents a +// microphone ID. typedef struct SbMicrophoneIdPrivate* SbMicrophoneId; -// Well-defined value for an invalid microphone id handle. +// Well-defined value for an invalid microphone ID handle. #define kSbMicrophoneIdInvalid ((SbMicrophoneId)NULL) -// Returns whether the given microphone id is valid. +// Indicates whether the given microphone ID is valid. static SB_C_INLINE bool SbMicrophoneIdIsValid(SbMicrophoneId id) { return id != kSbMicrophoneIdInvalid; } @@ -83,99 +88,108 @@ // Microphone type. SbMicrophoneType type; - // Microphone max supported sampling rate. + // The microphone's maximum supported sampling rate. int max_sample_rate_hz; // The minimum read size required for each read from microphone. int min_read_size; } SbMicrophoneInfo; -// An opaque handle to an implementation-private structure representing a +// An opaque handle to an implementation-private structure that represents a // microphone. typedef struct SbMicrophonePrivate* SbMicrophone; // Well-defined value for an invalid microphone handle. #define kSbMicrophoneInvalid ((SbMicrophone)NULL) -// Returns whether the given microphone is valid. +// Indicates whether the given microphone is valid. static SB_C_INLINE bool SbMicrophoneIsValid(SbMicrophone microphone) { return microphone != kSbMicrophoneInvalid; } -// Gets all currently-available microphone information and the results are -// stored in |out_info_array|. |info_array_size| is the size of -// |out_info_array|. -// Returns the number of the available microphones. A negative return -// value indicates that an internal error has occurred. If the number of -// available microphones is larger than |info_array_size|, |out_info_array| will -// be filled up with as many available microphones as possible and the actual -// number of available microphones will be returned. +// Retrieves all currently available microphone information and stores it in +// |out_info_array|. The return value is the number of the available +// microphones. If the number of available microphones is larger than +// |info_array_size|, then |out_info_array| is filled up with as many available +// microphones as possible and the actual number of available microphones is +// returned. A negative return value indicates that an internal error occurred. +// +// |out_info_array|: All currently available information about the microphone +// is placed into this output parameter. +// |info_array_size|: The size of |out_info_array|. SB_EXPORT int SbMicrophoneGetAvailable(SbMicrophoneInfo* out_info_array, int info_array_size); -// Returns true if the sample rate is supported by the microphone. +// Indicates whether the microphone supports the sample rate. SB_EXPORT bool SbMicrophoneIsSampleRateSupported(SbMicrophoneId id, int sample_rate_in_hz); -// Creates a microphone with |id|, audio sample rate in HZ, and the size of -// the cached audio buffer. +// Creates a microphone with the specified ID, audio sample rate, and cached +// audio buffer size. Starboard only requires support for creating one +// microphone at a time, and implementations may return an error if a second +// microphone is created before the first is destroyed. // -// If you try to create a microphone that has already been initialized or -// the sample rate is unavailable or the buffer size is invalid, it should -// return |kSbMicrophoneInvalid|. |buffer_size_bytes| is the size of the buffer -// where signed 16-bit integer audio data is temporarily cached to during the -// capturing. The audio data will be removed from the audio buffer if it has -// been read. New audio data can be read from this buffer in smaller chunks than -// this size. |buffer_size_bytes| must be set to a value greater than zero and -// the ideal size is 2^n. We only require support for creating one microphone at -// a time, and that implementations may return an error if a second microphone -// is created before destroying the first. +// The function returns the newly created SbMicrophone object. However, if you +// try to create a microphone that has already been initialized, if the sample +// rate is unavailable, or if the buffer size is invalid, the function should +// return |kSbMicrophoneInvalid|. +// +// |id|: The ID that will be assigned to the newly created SbMicrophone. +// |sample_rate_in_hz|: The new microphone's audio sample rate in Hz. +// |buffer_size_bytes|: The size of the buffer where signed 16-bit integer +// audio data is temporarily cached to during the capturing. The audio data +// is removed from the audio buffer if it has been read, and new audio data +// can be read from this buffer in smaller chunks than this size. This +// parameter must be set to a value greater than zero and the ideal size is +// |2^n|. SB_EXPORT SbMicrophone SbMicrophoneCreate(SbMicrophoneId id, int sample_rate_in_hz, int buffer_size_bytes); // Opens the microphone port and starts recording audio on |microphone|. // -// Once started, the client will have to periodically call |SbMicrophoneRead| to +// Once started, the client needs to periodically call |SbMicrophoneRead| to // receive the audio data. If the microphone has already been started, this call -// will clear the unread buffer. The return value indicates if the microphone is -// open. +// clears the unread buffer. The return value indicates whether the microphone +// is open. +// |microphone|: The microphone that will be opened and will start recording +// audio. SB_EXPORT bool SbMicrophoneOpen(SbMicrophone microphone); -// Closes the microphone port and stops recording audio on |microphone|. -// -// Clear the unread buffer if it is not empty. If the microphone has already -// been stopped, this call would be ignored. The return value indicates if the +// Closes the microphone port, stops recording audio on |microphone|, and +// clears the unread buffer if it is not empty. If the microphone has already +// been stopped, this call is ignored. The return value indicates whether the // microphone is closed. +// +// |microphone|: The microphone to close. SB_EXPORT bool SbMicrophoneClose(SbMicrophone microphone); -// Gets the recorded audio data from the microphone. +// Retrieves the recorded audio data from the microphone and writes that data +// to |out_audio_data|. // -// |out_audio_data| is where the recorded audio data is written to. -// |audio_data_size| is the number of requested bytes. The return value is zero -// or the positive number of bytes that were read. Neither the return value nor -// |audio_data_size| exceeds the buffer size. Negative return value indicates -// an error. This function should be called frequently, otherwise microphone -// only buffers |buffer_size| bytes which is configured in |SbMicrophoneCreate| -// and the new audio data will be thrown out. No audio data will be read from a -// stopped microphone. If |audio_data_size| is smaller than |min_read_size| of -// |SbMicrophoneInfo|, the extra audio data which is already read from device -// will be discarded. +// The return value is zero or the positive number of bytes that were read. +// Neither the return value nor |audio_data_size| exceeds the buffer size. +// A negative return value indicates that an error occurred. +// +// This function should be called frequently. Otherwise, the microphone only +// buffers |buffer_size| bytes as configured in |SbMicrophoneCreate| and the +// new audio data is thrown out. No audio data is read from a stopped +// microphone. +// +// |microphone|: The microphone from which to retrieve recorded audio data. +// |out_audio_data|: The buffer to which the retrieved data will be written. +// |audio_data_size|: The number of requested bytes. If |audio_data_size| is +// smaller than |min_read_size| of |SbMicrophoneInfo|, the extra audio data +// that has already been read from the device is discarded. SB_EXPORT int SbMicrophoneRead(SbMicrophone microphone, void* out_audio_data, int audio_data_size); -// Destroys a microphone. If the microphone is in started state, it will be -// stopped first and then be destroyed. Any data that has been recorded and not -// read will be thrown away. +// Destroys a microphone. If the microphone is in started state, it is first +// stopped and then destroyed. Any data that has been recorded and not read +// is thrown away. SB_EXPORT void SbMicrophoneDestroy(SbMicrophone microphone); -// Returns the Google Speech API key. The platform manufacturer is responsible -// for registering a Google Speech API key for their products. In the API -// Console (http://developers.google.com/console), you are able to enable the -// Speech APIs and generate a Speech API key. -SB_EXPORT const char* SbMicrophoneGetSpeechApiKey(); - #ifdef __cplusplus } // extern "C" #endif
diff --git a/src/starboard/mutex.h b/src/starboard/mutex.h index d06c752..953fa0d 100644 --- a/src/starboard/mutex.h +++ b/src/starboard/mutex.h
@@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A mutually exclusive lock that can be used to coordinate with other threads. +// Module Overview: Starboard Mutex module +// +// Defines a mutually exclusive lock that can be used to coordinate with other +// threads. #ifndef STARBOARD_MUTEX_H_ #define STARBOARD_MUTEX_H_ @@ -37,31 +40,46 @@ kSbMutexDestroyed, } SbMutexResult; -// Returns whether the given result is a success. +// Indicates whether the given result is a success. A value of |true| indicates +// that the mutex was acquired. +// +// |result|: The result being checked. static SB_C_FORCE_INLINE bool SbMutexIsSuccess(SbMutexResult result) { return result == kSbMutexAcquired; } -// Creates a new mutex, placing the handle to the newly created mutex in -// |out_mutex|. Returns whether able to create a new mutex. +// Creates a new mutex. The return value indicates whether the function +// was able to create a new mutex. +// +// |out_mutex|: The handle to the newly created mutex. SB_EXPORT bool SbMutexCreate(SbMutex* out_mutex); -// Destroys a mutex, returning whether the destruction was successful. The mutex -// specified by |mutex| is invalidated. +// Destroys a mutex. The return value indicates whether the destruction was +// successful. +// +// |mutex|: The mutex to be invalidated. SB_EXPORT bool SbMutexDestroy(SbMutex* mutex); -// Acquires |mutex|, blocking indefinitely, returning the acquisition result. -// SbMutexes are not reentrant, so a recursive acquisition will block forever. +// Acquires |mutex|, blocking indefinitely. The return value identifies +// the acquisition result. SbMutexes are not reentrant, so a recursive +// acquisition blocks forever. +// +// |mutex|: The mutex to be acquired. SB_EXPORT SbMutexResult SbMutexAcquire(SbMutex* mutex); -// Acquires |mutex|, without blocking, returning the acquisition result. -// SbMutexes are not reentrant, so a recursive acquisition will always fail. +// Acquires |mutex|, without blocking. The return value identifies +// the acquisition result. SbMutexes are not reentrant, so a recursive +// acquisition always fails. +// +// |mutex|: The mutex to be acquired. SB_EXPORT SbMutexResult SbMutexAcquireTry(SbMutex* mutex); -// Releases |mutex| held by the current thread, returning whether the release -// was successful. Releases should always be successful if the mutex is held by -// the current thread. -SB_EXPORT bool SbMutexRelease(SbMutex* handle); +// Releases |mutex| held by the current thread. The return value indicates +// whether the release was successful. Releases should always be successful +// if |mutex| is held by the current thread. +// +// |mutex|: The mutex to be released. +SB_EXPORT bool SbMutexRelease(SbMutex* mutex); #ifdef __cplusplus } // extern "C"
diff --git a/src/starboard/nplb/decode_target_create_test.cc b/src/starboard/nplb/decode_target_create_test.cc index 9358c68..e8f3ea4 100644 --- a/src/starboard/nplb/decode_target_create_test.cc +++ b/src/starboard/nplb/decode_target_create_test.cc
@@ -53,7 +53,7 @@ SbDecodeTargetDestroy(target); EXPECT_TRUE(SbBlitterDestroySurface(surface)); } -#else // SB_HAS(BLITTER) +#elif SB_HAS(GLES2) // SB_HAS(BLITTER) // clang-format off EGLint const kAttributeList[] = { EGL_RED_SIZE, 8, @@ -66,7 +66,7 @@ EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER, EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_NONE + EGL_NONE, }; // clang-format on @@ -160,17 +160,31 @@ glGenTextures(1, &texture_handle); glBindTexture(GL_TEXTURE_2D, texture_handle); glTexImage2D(GL_TEXTURE_2D, 0 /*level*/, GL_RGBA, 256, 256, 0 /*border*/, - GL_RGBA8, GL_UNSIGNED_BYTE, NULL /*data*/); + GL_RGBA, GL_UNSIGNED_BYTE, NULL /*data*/); glBindTexture(GL_TEXTURE_2D, 0); SbDecodeTarget target = SbDecodeTargetCreate( display_, context_, kSbDecodeTargetFormat1PlaneRGBA, &texture_handle); - GLuint plane = SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA); - EXPECT_EQ(texture_handle, plane); - SbDecodeTargetDestroy(target); + if (SbDecodeTargetIsValid(target)) { + GLuint plane = SbDecodeTargetGetPlane(target, kSbDecodeTargetPlaneRGBA); + EXPECT_EQ(texture_handle, plane); + SbDecodeTargetDestroy(target); + } glDeleteTextures(1, &texture_handle); } +#else // SB_HAS(BLITTER) + +TEST(SbDecodeTargetTest, SunnyDayCreate) { + // When graphics are not enabled, we expect to always create a + // kSbDecodeTargetInvalid, and get NULL back for planes. + EXPECT_EQ(SbDecodeTargetCreate(kSbDecodeTargetFormat1PlaneRGBA), + kSbDecodeTargetInvalid); + EXPECT_EQ( + SbDecodeTargetGetPlane(kSbDecodeTargetInvalid, kSbDecodeTargetPlaneRGBA), + NULL); +} + #endif // SB_HAS(BLITTER) } // namespace
diff --git a/src/starboard/nplb/decode_target_provider_test.cc b/src/starboard/nplb/decode_target_provider_test.cc index a5fece5..cd53e40 100644 --- a/src/starboard/nplb/decode_target_provider_test.cc +++ b/src/starboard/nplb/decode_target_provider_test.cc
@@ -43,130 +43,110 @@ int called_release; }; -template <typename T> -SbDecodeTargetProvider MakeProvider() { - return {&T::Acquire, &T::Release}; -} - -void AcquireFalse() { - SbDecodeTarget target = SbDecodeTargetAcquireFromProvider( - kSbDecodeTargetFormat1PlaneRGBA, 16, 16); - bool valid = SbDecodeTargetIsValid(target); - EXPECT_FALSE(valid); - if (valid) { - SbDecodeTargetDestroy(target); +void AcquireFalse(SbDecodeTargetProvider* provider) { + if (provider) { + SbDecodeTarget target = SbDecodeTargetAcquireFromProvider( + provider, kSbDecodeTargetFormat1PlaneRGBA, 16, 16); + bool valid = SbDecodeTargetIsValid(target); + EXPECT_FALSE(valid); + if (valid) { + SbDecodeTargetDestroy(target); + } } } -void ReleaseInvalid() { - SbDecodeTargetReleaseToProvider(kSbDecodeTargetInvalid); -} - -TEST(SbDecodeTargetProviderTest, SunnyDayRegisterUnregister) { - ProviderContext context = {}; - SbDecodeTargetProvider provider = MakeProvider<ProviderContext>(); - ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context)); - SbDecodeTargetUnregisterProvider(&provider, &context); +void ReleaseInvalid(SbDecodeTargetProvider* provider) { + if (provider) { + SbDecodeTargetReleaseToProvider(provider, kSbDecodeTargetInvalid); + } } TEST(SbDecodeTargetProviderTest, SunnyDayCallsThroughToProvider) { ProviderContext context = {}; - SbDecodeTargetProvider provider = MakeProvider<ProviderContext>(); - ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context)); + SbDecodeTargetProvider provider = {&ProviderContext::Acquire, + &ProviderContext::Release, + reinterpret_cast<void*>(&context)}; + EXPECT_EQ(0, context.called_acquire); EXPECT_EQ(0, context.called_release); - AcquireFalse(); + AcquireFalse(&provider); EXPECT_EQ(1, context.called_acquire); - AcquireFalse(); + AcquireFalse(&provider); EXPECT_EQ(2, context.called_acquire); - ReleaseInvalid(); + ReleaseInvalid(&provider); EXPECT_EQ(1, context.called_release); - ReleaseInvalid(); + ReleaseInvalid(&provider); EXPECT_EQ(2, context.called_release); - - SbDecodeTargetUnregisterProvider(&provider, &context); } -TEST(SbDecodeTargetProviderTest, SunnyDayRegisterOver) { +TEST(SbDecodeTargetProviderTest, SunnyDayMultipleProviders) { ProviderContext context = {}; - SbDecodeTargetProvider provider = MakeProvider<ProviderContext>(); - ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider, &context)); + SbDecodeTargetProvider provider = {&ProviderContext::Acquire, + &ProviderContext::Release, + reinterpret_cast<void*>(&context)}; ProviderContext context2 = {}; - SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>(); - ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2)); + SbDecodeTargetProvider provider2 = {&ProviderContext::Acquire, + &ProviderContext::Release, + reinterpret_cast<void*>(&context2)}; - AcquireFalse(); + AcquireFalse(&provider2); EXPECT_EQ(1, context2.called_acquire); EXPECT_EQ(0, context.called_acquire); - AcquireFalse(); + AcquireFalse(&provider2); EXPECT_EQ(2, context2.called_acquire); EXPECT_EQ(0, context.called_acquire); - ReleaseInvalid(); + ReleaseInvalid(&provider2); EXPECT_EQ(1, context2.called_release); EXPECT_EQ(0, context.called_release); - ReleaseInvalid(); + ReleaseInvalid(&provider2); EXPECT_EQ(2, context2.called_release); EXPECT_EQ(0, context.called_release); - - SbDecodeTargetUnregisterProvider(&provider2, &context2); } TEST(SbDecodeTargetProviderTest, RainyDayAcquireNoProvider) { - AcquireFalse(); + AcquireFalse(NULL); } TEST(SbDecodeTargetProviderTest, RainyDayReleaseNoProvider) { - ReleaseInvalid(); + ReleaseInvalid(NULL); } -TEST(SbDecodeTargetProviderTest, RainyDayUnregisterUnregistered) { +TEST(SbDecodeTargetProviderTest, RainyDayMultipleProviders) { ProviderContext context = {}; - SbDecodeTargetProvider provider = MakeProvider<ProviderContext>(); - SbDecodeTargetUnregisterProvider(&provider, &context); + SbDecodeTargetProvider provider = {&ProviderContext::Acquire, + &ProviderContext::Release, + reinterpret_cast<void*>(&context)}; ProviderContext context2 = {}; - SbDecodeTargetProvider provider2 = MakeProvider<ProviderContext>(); - ASSERT_TRUE(SbDecodeTargetRegisterProvider(&provider2, &context2)); - SbDecodeTargetUnregisterProvider(&provider, &context); - SbDecodeTargetUnregisterProvider(&provider, &context2); - SbDecodeTargetUnregisterProvider(&provider2, &context); + SbDecodeTargetProvider provider2 = {&ProviderContext::Acquire, + &ProviderContext::Release, + reinterpret_cast<void*>(&context2)}; - AcquireFalse(); + AcquireFalse(&provider2); EXPECT_EQ(1, context2.called_acquire); - AcquireFalse(); + AcquireFalse(&provider2); EXPECT_EQ(2, context2.called_acquire); - ReleaseInvalid(); + ReleaseInvalid(&provider2); EXPECT_EQ(1, context2.called_release); - ReleaseInvalid(); + ReleaseInvalid(&provider2); EXPECT_EQ(2, context2.called_release); - SbDecodeTargetUnregisterProvider(&provider2, &context2); - - AcquireFalse(); + AcquireFalse(&provider); EXPECT_EQ(2, context2.called_acquire); - AcquireFalse(); + AcquireFalse(&provider); EXPECT_EQ(2, context2.called_acquire); - ReleaseInvalid(); + ReleaseInvalid(&provider); EXPECT_EQ(2, context2.called_release); - ReleaseInvalid(); + ReleaseInvalid(&provider); EXPECT_EQ(2, context2.called_release); } -TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNull) { - ProviderContext context = {}; - SbDecodeTargetUnregisterProvider(NULL, &context); -} - -TEST(SbDecodeTargetProviderTest, RainyDayUnregisterNullNull) { - SbDecodeTargetUnregisterProvider(NULL, NULL); -} - } // namespace } // namespace nplb } // namespace starboard
diff --git a/src/starboard/nplb/include_all.c b/src/starboard/nplb/include_all.c index 5ac5aa1..e55a271 100644 --- a/src/starboard/nplb/include_all.c +++ b/src/starboard/nplb/include_all.c
@@ -39,6 +39,7 @@ #include "starboard/player.h" #include "starboard/socket.h" #include "starboard/socket_waiter.h" +#include "starboard/speech_synthesis.h" #include "starboard/storage.h" #include "starboard/string.h" #include "starboard/system.h"
diff --git a/src/starboard/nplb/memory_reporter_test.cc b/src/starboard/nplb/memory_reporter_test.cc new file mode 100644 index 0000000..f49e339 --- /dev/null +++ b/src/starboard/nplb/memory_reporter_test.cc
@@ -0,0 +1,496 @@ +// Copyright 2016 Google Inc. 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/configuration.h" +#include "starboard/memory.h" +#include "starboard/memory_reporter.h" +#include "starboard/mutex.h" +#include "starboard/thread.h" +#include "starboard/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace starboard { +namespace nplb { +namespace { + +// This thread local boolean is used to filter allocations and +// 1) Prevent allocations from other threads. +// 2) Selectively disable allocations so that unintended allocs from +// gTest (ASSERT_XXX) don't cause the current test to fail. +void InitMemoryTrackingState_ThreadLocal(); +bool GetMemoryTrackingEnabled_ThreadLocal(); +void SetMemoryTrackingEnabled_ThreadLocal(bool value); + +// Scoped object that temporary turns off MemoryTracking. This is needed +// to avoid allocations from gTest being inserted into the memory tracker. +struct NoMemTracking { + bool prev_val; + NoMemTracking() { + prev_val = GetMemoryTrackingEnabled_ThreadLocal(); + SetMemoryTrackingEnabled_ThreadLocal(false); + } + ~NoMemTracking() { + SetMemoryTrackingEnabled_ThreadLocal(prev_val); + } +}; + +// EXPECT_XXX and ASSERT_XXX allocate memory, a big no-no when +// for unit testing allocations. These overrides disable memory +// tracking for the duration of the EXPECT and ASSERT operations. +#define EXPECT_EQ_NO_TRACKING(A, B) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_EQ(A, B); \ +} + +#define EXPECT_NE_NO_TRACKING(A, B) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_NE(A, B); \ +} + +#define EXPECT_TRUE_NO_TRACKING(A) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_TRUE(A); \ +} + +#define EXPECT_FALSE_NO_TRACKING(A) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + EXPECT_FALSE(A); \ +} + +#define ASSERT_EQ_NO_TRACKING(A, B) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_EQ(A, B); \ +} + +#define ASSERT_NE_NO_TRACKING(A, B) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_NE(A, B); \ +} + +#define ASSERT_TRUE_NO_TRACKING(A) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_TRUE(A); \ +} + +#define ASSERT_FALSE_NO_TRACKING(A) \ +{ NoMemTracking no_memory_tracking_in_this_scope; \ + ASSERT_FALSE(A); \ +} + +/////////////////////////////////////////////////////////////////////////////// +// A memory reporter that is used to watch allocations from the system. +class TestMemReporter { + public: + TestMemReporter() { Construct(); } + + SbMemoryReporter* memory_reporter() { + return &memory_reporter_; + } + + // Removes this object from listening to allocations. + void RemoveGlobalHooks() { + SbMemorySetReporter(NULL); + // Sleep to allow other threads time to pass through. + SbThreadSleep(250 * kSbTimeMillisecond); + } + + // Total number allocations outstanding. + int number_allocs() const { return number_allocs_; } + + void Clear() { + starboard::ScopedLock lock(mutex_); + number_allocs_ = 0; + last_allocation_ = NULL; + last_deallocation_ = NULL; + last_mem_map_ = NULL; + last_mem_unmap_ = NULL; + } + + const void* last_allocation() const { + return last_allocation_; + } + const void* last_deallocation() const { + return last_deallocation_; + } + const void* last_mem_map() const { + return last_mem_map_; + } + const void* last_mem_unmap() const { + return last_mem_unmap_; + } + + private: + // Boilerplate to delegate static function callbacks to the class instance. + static void OnMalloc(void* context, const void* memory, size_t size) { + TestMemReporter* t = static_cast<TestMemReporter*>(context); + t->ReportAlloc(memory, size); + } + + static void OnMapMem(void* context, const void* memory, size_t size) { + TestMemReporter* t = static_cast<TestMemReporter*>(context); + t->ReportMapMem(memory, size); + } + + static void OnDealloc(void* context, const void* memory) { + TestMemReporter* t = static_cast<TestMemReporter*>(context); + t->ReportDealloc(memory); + } + + static void SbUnMapMem(void* context, const void* memory, size_t size) { + TestMemReporter* t = static_cast<TestMemReporter*>(context); + t->ReportUnMapMemory(memory, size); + } + + static SbMemoryReporter CreateSbMemoryReporter(TestMemReporter* context) { + SbMemoryReporter cb = { OnMalloc, OnDealloc, + OnMapMem, SbUnMapMem, + context }; + return cb; + } + + void ReportAlloc(const void* memory, size_t /*size*/) { + if (!GetMemoryTrackingEnabled_ThreadLocal()) { + return; + } + starboard::ScopedLock lock(mutex_); + last_allocation_ = memory; + number_allocs_++; + } + + void ReportMapMem(const void* memory, size_t size) { + if (!GetMemoryTrackingEnabled_ThreadLocal()) { + return; + } + starboard::ScopedLock lock(mutex_); + last_mem_map_ = memory; + number_allocs_++; + } + + void ReportDealloc(const void* memory) { + if (!GetMemoryTrackingEnabled_ThreadLocal()) { + return; + } + starboard::ScopedLock lock(mutex_); + last_deallocation_ = memory; + number_allocs_--; + } + + void ReportUnMapMemory(const void* memory, size_t size) { + if (!GetMemoryTrackingEnabled_ThreadLocal()) { + return; + } + starboard::ScopedLock lock(mutex_); + last_mem_unmap_ = memory; + number_allocs_--; + } + + void Construct() { + Clear(); + memory_reporter_ = CreateSbMemoryReporter(this); + } + + SbMemoryReporter memory_reporter_; + starboard::Mutex mutex_; + const void* last_allocation_; + const void* last_deallocation_; + const void* last_mem_map_; + const void* last_mem_unmap_; + int number_allocs_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Needed by all tests that require a memory tracker to be installed. On the +// first test the test memory tracker is installed and then after the last test +// the memory tracker is removed. +class MemoryReportingTest : public ::testing::Test { + public: + TestMemReporter* mem_reporter() { return s_test_alloc_tracker_; } + bool MemoryReportingEnabled() const { + return s_memory_reporter_error_enabled_; + } + + protected: + // Global setup - runs before the first test in the series. + static void SetUpTestCase() { + InitMemoryTrackingState_ThreadLocal(); + // global init test code should run here. + if (s_test_alloc_tracker_ == NULL) { + s_test_alloc_tracker_ = new TestMemReporter; + } + s_memory_reporter_error_enabled_ = + SbMemorySetReporter(s_test_alloc_tracker_->memory_reporter()); + } + + // Global Teardown after last test has run it's course. + static void TearDownTestCase() { + s_test_alloc_tracker_->RemoveGlobalHooks(); + } + + // Per test setup. + virtual void SetUp() { + mem_reporter()->Clear(); + // Allows current thread to capture memory allocations. If this wasn't + // done then background threads spawned by a framework could notify this + // class about allocations and fail the test. + SetMemoryTrackingEnabled_ThreadLocal(true); + } + + // Per test teardown. + virtual void TearDown() { + SetMemoryTrackingEnabled_ThreadLocal(false); + if (mem_reporter()->number_allocs() != 0) { + ADD_FAILURE_AT(__FILE__, __LINE__) << "Memory Leak detected."; + } + mem_reporter()->Clear(); + } + static TestMemReporter* s_test_alloc_tracker_; + static bool s_memory_reporter_error_enabled_; +}; +TestMemReporter* MemoryReportingTest::s_test_alloc_tracker_ = NULL; +bool MemoryReportingTest::s_memory_reporter_error_enabled_ = false; + +/////////////////////////////////////////////////////////////////////////////// +// TESTS. +// There are two sets of tests: POSITIVE and NEGATIVE. +// The positive tests are active when STARBOARD_ALLOWS_MEMORY_TRACKING is +// defined and test that memory tracking is enabled. +// NEGATIVE tests ensure that tracking is disabled when +// STARBOARD_ALLOWS_MEMORY_TRACKING is not defined. +// When adding new tests: +// POSITIVE tests are named normally. +// NEGATIVE tets are named with "No" prefixed to the beginning. +// Example: +// TEST_F(MemoryReportingTest, CapturesAllocDealloc) <--- POSITIVE test. +// TEST_F(MemoryReportingTest, NoCapturesAllocDealloc) <- NEGATIVE test. +// All positive & negative tests are grouped together. +/////////////////////////////////////////////////////////////////////////////// + +#ifdef STARBOARD_ALLOWS_MEMORY_TRACKING +// These are POSITIVE tests, which test the expectation that when the define +// STARBOARD_ALLOWS_MEMORY_TRACKING is active that the memory tracker will +// receive memory allocations notifications. +// +// Tests the assumption that the SbMemoryAllocate and SbMemoryDeallocate +// will report memory allocations. +TEST_F(MemoryReportingTest, CapturesAllocDealloc) { + if (!MemoryReportingEnabled()) { + GTEST_SUCCEED() << "Memory reporting is disabled."; + return; + } + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + void* memory = SbMemoryAllocate(4); + EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs()); + + EXPECT_EQ_NO_TRACKING(memory, mem_reporter()->last_allocation()); + + SbMemoryDeallocate(memory); + EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0); + + // Should equal the last allocation. + EXPECT_EQ_NO_TRACKING(memory, mem_reporter()->last_deallocation()); +} + +// Tests the assumption that SbMemoryReallocate() will report a +// deallocation and an allocation to the memory tracker. +TEST_F(MemoryReportingTest, CapturesRealloc) { + if (!MemoryReportingEnabled()) { + GTEST_SUCCEED() << "Memory reporting is disabled."; + return; + } + void* prev_memory = SbMemoryAllocate(4); + void* new_memory = SbMemoryReallocate(prev_memory, 8); + + EXPECT_EQ_NO_TRACKING(new_memory, mem_reporter()->last_allocation()); + EXPECT_EQ_NO_TRACKING(prev_memory, mem_reporter()->last_deallocation()); + + EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs()); + + SbMemoryDeallocate(new_memory); + EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0); +} + +// Tests the assumption that the SbMemoryMap and SbMemoryUnmap +// will report memory allocations. +TEST_F(MemoryReportingTest, CapturesMemMapUnmap) { + if (!MemoryReportingEnabled()) { + GTEST_SUCCEED() << "Memory reporting is disabled."; + return; + } + const int64_t kMemSize = 4096; + const int kFlags = 0; + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + void* mem_chunk = SbMemoryMap(kMemSize, kFlags, "TestMemMap"); + EXPECT_EQ_NO_TRACKING(1, mem_reporter()->number_allocs()); + + // Now unmap the memory and confirm that this memory was reported as free. + EXPECT_EQ_NO_TRACKING(mem_chunk, mem_reporter()->last_mem_map()); + SbMemoryUnmap(mem_chunk, kMemSize); + EXPECT_EQ_NO_TRACKING(mem_chunk, mem_reporter()->last_mem_unmap()); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); +} + +// Tests the assumption that the operator/delete will report +// memory allocations. +TEST_F(MemoryReportingTest, CapturesOperatorNewDelete) { + if (!MemoryReportingEnabled()) { + GTEST_SUCCEED() << "Memory reporting is disabled."; + return; + } + EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 0); + int* my_int = new int(); + EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 1); + + bool is_last_allocation = + my_int == mem_reporter()->last_allocation(); + + EXPECT_TRUE_NO_TRACKING(is_last_allocation); + + delete my_int; + EXPECT_TRUE_NO_TRACKING(mem_reporter()->number_allocs() == 0); + + // Expect last deallocation to be the expected pointer. + EXPECT_EQ_NO_TRACKING(my_int, mem_reporter()->last_deallocation()); +} + +#else // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) +// These are NEGATIVE tests, which test the expectation that when the +// STARBOARD_ALLOWS_MEMORY_TRACKING is undefined that the memory tracker does +// not receive memory allocations notifications. + +TEST_F(MemoryReportingTest, NoCapturesAllocDealloc) { + EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled()); + + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + void* memory = SbMemoryAllocate(4); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation()); + + SbMemoryDeallocate(memory); + EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation()); +} + +TEST_F(MemoryReportingTest, NoCapturesRealloc) { + EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled()); + void* prev_memory = SbMemoryAllocate(4); + void* new_memory = SbMemoryReallocate(prev_memory, 8); + + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation()); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation()); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + + SbMemoryDeallocate(new_memory); + EXPECT_EQ_NO_TRACKING(mem_reporter()->number_allocs(), 0); +} + +TEST_F(MemoryReportingTest, NoCapturesMemMapUnmap) { + EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled()); + const int64_t kMemSize = 4096; + const int kFlags = 0; + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + void* mem_chunk = SbMemoryMap(kMemSize, kFlags, "TestMemMap"); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + + // Now unmap the memory and confirm that this memory was reported as free. + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_mem_map()); + SbMemoryUnmap(mem_chunk, kMemSize); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_mem_unmap()); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); +} + +TEST_F(MemoryReportingTest, NoCapturesOperatorNewDelete) { + EXPECT_FALSE_NO_TRACKING(MemoryReportingEnabled()); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + int* my_int = new int(); + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_allocation()); + + delete my_int; + EXPECT_EQ_NO_TRACKING(0, mem_reporter()->number_allocs()); + EXPECT_EQ_NO_TRACKING(NULL, mem_reporter()->last_deallocation()); +} + +#endif // !defined(STARBOARD_ALLOWS_MEMORY_TRACKING) + +/////////////////////////////// Implementation //////////////////////////////// + +// Simple ThreadLocalBool class which allows a default value to be +// true or false. +class ThreadLocalBool { + public: + ThreadLocalBool() { + // NULL is the destructor. + slot_ = SbThreadCreateLocalKey(NULL); + } + + ~ThreadLocalBool() { + SbThreadDestroyLocalKey(slot_); + } + + void SetEnabled(bool value) { + SetEnabledThreadLocal(value); + } + + bool Enabled() const { + return GetEnabledThreadLocal(); + } + + private: + void SetEnabledThreadLocal(bool value) { + void* bool_as_pointer; + if (value) { + bool_as_pointer = reinterpret_cast<void*>(0x1); + } else { + bool_as_pointer = NULL; + } + SbThreadSetLocalValue(slot_, bool_as_pointer); + } + + bool GetEnabledThreadLocal() const { + void* ptr = SbThreadGetLocalValue(slot_); + return ptr != NULL; + } + + mutable SbThreadLocalKey slot_; + bool default_value_; +}; + +void InitMemoryTrackingState_ThreadLocal() { + GetMemoryTrackingEnabled_ThreadLocal(); +} + +static ThreadLocalBool* GetMemoryTrackingState() { + static ThreadLocalBool* thread_local_bool = new ThreadLocalBool(); + return thread_local_bool; +} + +bool GetMemoryTrackingEnabled_ThreadLocal() { + return GetMemoryTrackingState()->Enabled(); +} + +void SetMemoryTrackingEnabled_ThreadLocal(bool value) { + GetMemoryTrackingState()->SetEnabled(value); +} + +// No use for these macros anymore. +#undef EXPECT_EQ_NO_TRACKING +#undef EXPECT_TRUE_NO_TRACKING +#undef EXPECT_FALSE_NO_TRACKING +#undef ASSERT_EQ_NO_TRACKING +#undef ASSERT_TRUE_NO_TRACKING +#undef ASSERT_FALSE_NO_TRACKING + +} // namespace +} // namespace nplb +} // namespace starboard
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp index cf31d23..dc39b90 100644 --- a/src/starboard/nplb/nplb.gyp +++ b/src/starboard/nplb/nplb.gyp
@@ -122,12 +122,12 @@ 'memory_map_test.cc', 'memory_move_test.cc', 'memory_reallocate_test.cc', + 'memory_reporter_test.cc', 'memory_set_test.cc', 'microphone_close_test.cc', 'microphone_create_test.cc', 'microphone_destroy_test.cc', 'microphone_get_available_test.cc', - 'microphone_get_speech_api_key_test.cc', 'microphone_is_sample_rate_supported_test.cc', 'microphone_open_test.cc', 'microphone_read_test.cc', @@ -163,6 +163,7 @@ 'socket_waiter_wait_test.cc', 'socket_waiter_wait_timed_test.cc', 'socket_waiter_wake_up_test.cc', + 'speech_synthesis_basic_test.cc', 'storage_close_record_test.cc', 'storage_delete_record_test.cc', 'storage_get_record_size_test.cc',
diff --git a/src/starboard/nplb/once_test.cc b/src/starboard/nplb/once_test.cc index b40817a..a504fc2 100644 --- a/src/starboard/nplb/once_test.cc +++ b/src/starboard/nplb/once_test.cc
@@ -22,6 +22,7 @@ namespace starboard { namespace nplb { +namespace { int s_global_value; @@ -149,5 +150,14 @@ EXPECT_EQ(0, s_global_value); } -} // namespace nplb -} // namespace starboard +SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton); +TEST(SbOnceTest, InitializeOnceMacroFunction) { + int* int_singelton = GetIntSingleton(); + ASSERT_TRUE(int_singelton); + EXPECT_EQ(0, *int_singelton) + << "Singleton Macro does not default initialize."; +} + +} // namespace. +} // namespace nplb. +} // namespace starboard.
diff --git a/src/starboard/nplb/player_create_test.cc b/src/starboard/nplb/player_create_test.cc index d81c51c..dffa92c 100644 --- a/src/starboard/nplb/player_create_test.cc +++ b/src/starboard/nplb/player_create_test.cc
@@ -44,7 +44,12 @@ SbPlayer player = SbPlayerCreate(window, kSbMediaVideoCodecH264, kSbMediaAudioCodecAac, SB_PLAYER_NO_DURATION, kSbDrmSystemInvalid, &audio_header, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL +#if SB_VERSION(3) + , + NULL +#endif + ); // NOLINT EXPECT_TRUE(SbPlayerIsValid(player)); SbPlayerDestroy(player); SbWindowDestroy(window);
diff --git a/src/starboard/nplb/speech_synthesis_basic_test.cc b/src/starboard/nplb/speech_synthesis_basic_test.cc new file mode 100644 index 0000000..4e01d35 --- /dev/null +++ b/src/starboard/nplb/speech_synthesis_basic_test.cc
@@ -0,0 +1,28 @@ +// Copyright 2016 Google Inc. 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/speech_synthesis.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) + +TEST(SbSpeechSynthesisBasicTest, Basic) { + // An implementation must at least support US English + EXPECT_TRUE(SbSpeechSynthesisSetLanguage("en-US")); + SbSpeechSynthesisSpeak("Hello"); + SbSpeechSynthesisCancel(); +} + +#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) +
diff --git a/src/starboard/nplb/system_get_property_test.cc b/src/starboard/nplb/system_get_property_test.cc index de94895..ef4078f 100644 --- a/src/starboard/nplb/system_get_property_test.cc +++ b/src/starboard/nplb/system_get_property_test.cc
@@ -70,6 +70,9 @@ BasicTest(kSbSystemPropertyChipsetModelNumber, false, true, __LINE__); BasicTest(kSbSystemPropertyFirmwareVersion, false, true, __LINE__); BasicTest(kSbSystemPropertyNetworkOperatorName, false, true, __LINE__); +#if SB_VERSION(2) + BasicTest(kSbSystemPropertySpeechApiKey, false, true, __LINE__); +#endif // SB_VERSION(2) if (IsCEDevice(SbSystemGetDeviceType())) { BasicTest(kSbSystemPropertyBrandName, true, true, __LINE__);
diff --git a/src/starboard/once.h b/src/starboard/once.h index 03039c9..8eba6cb 100644 --- a/src/starboard/once.h +++ b/src/starboard/once.h
@@ -1,43 +1,75 @@ -// Copyright 2015 Google Inc. 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. - -// Onces represent initializations that should only ever happen once per -// process, in a thread safe way. - -#ifndef STARBOARD_ONCE_H_ -#define STARBOARD_ONCE_H_ - -#include "starboard/export.h" -#include "starboard/thread_types.h" -#include "starboard/types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// Function pointer type for methods that can be called va the SbOnce() system. -typedef void (*SbOnceInitRoutine)(void); - -// If SbOnce() was never called before on with |once_control|, this function -// will run |init_routine| in a thread-safe way and then returns true. If -// SbOnce() was called before, the function returns (true) immediately. -// If |once_control| or |init_routine| are invalid, the function returns false. -SB_EXPORT bool SbOnce(SbOnceControl* once_control, - SbOnceInitRoutine init_routine); - -#ifdef __cplusplus -} -#endif - -#endif // STARBOARD_ONCE_H_ +// Copyright 2015 Google Inc. 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. + +// Module Overview: Starboard Once module +// +// Onces represent initializations that should only ever happen once per +// process, in a thread-safe way. + +#ifndef STARBOARD_ONCE_H_ +#define STARBOARD_ONCE_H_ + +#include "starboard/export.h" +#include "starboard/thread_types.h" +#include "starboard/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Function pointer type for methods that can be called via the SbOnce() system. +typedef void (*SbOnceInitRoutine)(void); + +// Thread-safely runs |init_routine| only once. +// - If this |once_control| has not run a function yet, this function runs +// |init_routine| in a thread-safe way and then returns |true|. +// - If SbOnce() was called with |once_control| before, the function returns +// |true| immediately. +// - If |once_control| or |init_routine| is invalid, the function returns +// |false|. +SB_EXPORT bool SbOnce(SbOnceControl* once_control, + SbOnceInitRoutine init_routine); + +#ifdef __cplusplus +// Defines a function that will initialize the indicated type once and return +// it. This initialization is thread safe if the type being created is side +// effect free. +// +// These macros CAN ONLY BE USED IN A CC file, never in a header file. +// +// Example (in cc file): +// SB_ONCE_INITIALIZE_FUNCTION(MyClass, GetOrCreateMyClass) +// .. +// MyClass* instance = GetOrCreateMyClass(); +// MyClass* instance2 = GetOrCreateMyClass(); +// DCHECK_EQ(instance, instance2); +#define SB_ONCE_INITIALIZE_FUNCTION(Type, FunctionName) \ +Type* FunctionName() { \ + static SbOnceControl s_once_flag = SB_ONCE_INITIALIZER; \ + static Type* s_singleton = NULL; \ + struct Local { \ + static void Init() { \ + s_singleton = new Type(); \ + } \ + }; \ + SbOnce(&s_once_flag, Local::Init); \ + return s_singleton; \ +} +#endif // __cplusplus + +#ifdef __cplusplus +} +#endif + +#endif // STARBOARD_ONCE_H_
diff --git a/src/starboard/player.h b/src/starboard/player.h index 333c9dd..6fbc381 100644 --- a/src/starboard/player.h +++ b/src/starboard/player.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// An interface for controlling playback of media elementary streams. +// Module Overview: Starboard Player module +// +// Defines an interface for controlling playback of media elementary streams. #ifndef STARBOARD_PLAYER_H_ #define STARBOARD_PLAYER_H_ @@ -21,6 +23,7 @@ #if SB_HAS(PLAYER) +#include "starboard/decode_target.h" #include "starboard/drm.h" #include "starboard/export.h" #include "starboard/media.h" @@ -49,7 +52,7 @@ // An indicator of the general playback state. typedef enum SbPlayerState { - // The player has just been initialized. It is expecting an SbPlayerSeek() + // The player has just been initialized. It is expecting an SbPlayerSeek() // call to enter the prerolling state. kSbPlayerStateInitialized, @@ -59,8 +62,8 @@ // Prerolling after a Seek. kSbPlayerStatePrerolling, - // The player is presenting media, and it is either actively playing in - // real-time, or it is paused. Note that the implementation should use this + // The player is presenting media, and it is either paused or actively + // playing in real-time. Note that the implementation should use this // state to signal that the preroll has been finished. kSbPlayerStatePresenting, @@ -70,8 +73,8 @@ // The player has been destroyed, and will send no more callbacks. kSbPlayerStateDestroyed, - // The player encounters an error. It is expecting an SbPlayerDestroy() call - // to tear down the player. Any call to other functions maybe ignored and + // The player encountered an error. It expects an SbPlayerDestroy() call + // to tear down the player. Calls to other functions may be ignored and // callbacks may not be triggered. kSbPlayerStateError, } SbPlayerState; @@ -178,108 +181,146 @@ // Creates a player that will be displayed on |window| for the specified // |video_codec| and |audio_codec|, acquiring all resources needed to operate -// it, and returning an opaque handle to it. |window| can be kSbWindowInvalid -// for platforms where video will be only displayed on a particular window -// which the underlying implementation already has access to. If |video_codec| -// is kSbMediaVideoCodecNone, the player is an audio-only player. Otherwise, the -// player is an audio/video decoder. |audio_codec| should never be -// kSbMediaAudioCodecNone. The expectation is that a new player will be created -// and destroyed for every playback. +// it, and returning an opaque handle to it. The expectation is that a new +// player will be created and destroyed for every playback. // -// |duration_pts| is the expected media duration in 90KHz ticks (PTS). It may be -// set to SB_PLAYER_NO_DURATION for live streams. +// This function returns the created player. Note the following: +// - The associated decoder of the returned player should be assumed to not be +// in |kSbPlayerDecoderStateNeedsData| until SbPlayerSeek() has been called +// on it. +// - It is expected either that the thread that calls SbPlayerCreate is the same +// thread that calls the other |SbPlayer| functions for that player, or that +// there is a mutex guarding calls into each |SbPlayer| instance. +// - If there is a platform limitation on how many players can coexist +// simultaneously, then calls made to this function that attempt to exceed +// that limit will return |kSbPlayerInvalid|. // -// If the media stream has encrypted portions, then an appropriate DRM system -// must first be created with SbDrmCreateSystem() and passed into |drm_system|. -// If not, then |drm_system| may be kSbDrmSystemInvalid. +// |window|: The window that will display the player. |window| can be +// |kSbWindowInvalid| for platforms where video is only displayed on a +// particular window that the underlying implementation already has access to. // -// If |audio_codec| is kSbMediaAudioCodecAac, then the caller must provide a -// populated |audio_header|. Otherwise, this may be NULL. +// |video_codec|: The video codec used for the player. If |video_codec| is +// |kSbMediaVideoCodecNone|, the player is an audio-only player. If +// |video_codec| is any other value, the player is an audio/video decoder. // -// If not NULL, the player will call |sample_deallocator_func| on an internal -// thread to free the sample buffers passed into SbPlayerWriteSample(). +// |audio_codec|: The audio codec used for the player. The value should never +// be |kSbMediaAudioCodecNone|. In addition, the caller must provide a +// populated |audio_header| if the audio codec is |kSbMediaAudioCodecAac|. // -// If not NULL, the decoder will call |decoder_status_func| on an internal -// thread to provide an update on the decoder's status. No work should be done -// on this thread, it should just signal the client thread interacting with the -// decoder. +// |duration_pts|: The expected media duration in 90KHz ticks (PTS). It may be +// set to |SB_PLAYER_NO_DURATION| for live streams. // -// If not NULL, the player will call |player_status_func| on an internal thread -// to provide an update on the playback status. No work should be done on this -// thread, it should just signal the client thread interacting with the decoder. +// |drm_system|: If the media stream has encrypted portions, then this +// parameter provides an appropriate DRM system, created with +// |SbDrmCreateSystem()|. If the stream does not have encrypted portions, +// then |drm_system| may be |kSbDrmSystemInvalid|. // -// |context| will be passed back into all callbacks, and is generally used to -// point at a class or struct that contains state associated with the player. +// |audio_header|: Note that the caller must provide a populated |audio_header| +// if the audio codec is |kSbMediaAudioCodecAac|. Otherwise, |audio_header| +// can be NULL. See media.h for the format of the |SbMediaAudioHeader| struct. // -// The associated decoder of the returned player should be assumed to be not in -// kSbPlayerDecoderStateNeedsData until SbPlayerSeek() has been called on it. +// |sample_deallocator_func|: If not |NULL|, the player calls this function +// on an internal thread to free the sample buffers passed into +// SbPlayerWriteSample(). // -// It is expected that the thread that calls SbPlayerCreate is the same thread -// that calls the other SbPlayer functions for that player, or that there is a -// mutex guarding calls into each SbPlayer instance. +// |decoder_status_func|: If not |NULL|, the decoder calls this function on an +// internal thread to provide an update on the decoder's status. No work +// should be done on this thread. Rather, it should just signal the client +// thread interacting with the decoder. // -// If there is a platform limitation on how many players can coexist -// simultaneously, then calls made to this function that attempt to exceed that -// limit will return kSbPlayerInvalid. -SB_EXPORT SbPlayer -SbPlayerCreate(SbWindow window, - SbMediaVideoCodec video_codec, - SbMediaAudioCodec audio_codec, - SbMediaTime duration_pts, - SbDrmSystem drm_system, - const SbMediaAudioHeader* audio_header, - SbPlayerDeallocateSampleFunc sample_deallocate_func, - SbPlayerDecoderStatusFunc decoder_status_func, - SbPlayerStatusFunc player_status_func, - void* context); +// |player_status_func|: If not |NULL|, the player calls this function on an +// internal thread to provide an update on the playback status. No work +// should be done on this thread. Rather, it should just signal the client +// thread interacting with the decoder. +// +// |context|: This is passed to all callbacks and is generally used to point +// at a class or struct that contains state associated with the player. +// +// |provider|: Only present in Starboard version 3 and up. If not |NULL|, +// then when SB_HAS(PLAYER_PRODUCING_TEXTURE) is true, the player MAY use the +// provider to create SbDecodeTargets. A provider could also potentially be +// required by the player, in which case, if the provider is not given, the +// player will fail by returning kSbPlayerInvalid. +SB_EXPORT SbPlayer SbPlayerCreate( + SbWindow window, + SbMediaVideoCodec video_codec, + SbMediaAudioCodec audio_codec, + SbMediaTime duration_pts, + SbDrmSystem drm_system, + const SbMediaAudioHeader* audio_header, + SbPlayerDeallocateSampleFunc sample_deallocate_func, + SbPlayerDecoderStatusFunc decoder_status_func, + SbPlayerStatusFunc player_status_func, + void* context +#if SB_VERSION(3) + , + SbDecodeTargetProvider* provider +#endif // SB_VERSION(3) + ); // NOLINT // Destroys |player|, freeing all associated resources. Each callback must -// receive one more callback to say that the player was destroyed. Callbacks +// receive one more callback to say that the player was destroyed. Callbacks // may be in-flight when SbPlayerDestroy is called, and should be ignored once // this function is called. // -// It is not allowed to pass |player| into any other SbPlayer function once -// SbPlayerDestroy has been called on it. +// It is not allowed to pass |player| into any other |SbPlayer| function once +// SbPlayerDestroy has been called on that player. +// +// |player|: The player to be destroyed. SB_EXPORT void SbPlayerDestroy(SbPlayer player); -// Tells the player to freeze playback where it is (if it has already started), -// reset/flush the decoder pipeline, and go back to the Prerolling state. The -// player should restart playback once it can display the frame at -// |seek_to_pts|, or the closest it can get (some players can only seek to -// I-Frames, for example). The client should send no more audio or video samples -// until SbPlayerDecoderStatusFunc is called back with -// kSbPlayerDecoderStateNeedsData, for each required media type. +// Tells the player to freeze playback (if playback has already started), +// reset or flush the decoder pipeline, and go back to the Prerolling state. +// The player should restart playback once it can display the frame at +// |seek_to_pts|, or the closest it can get. (Some players can only seek to +// I-Frames, for example.) // -// A call to seek may interrupt another seek. +// - Seek must be called before samples are sent when starting playback for +// the first time, or the client never receives the +// |kSbPlayerDecoderStateNeedsData| signal. +// - A call to seek may interrupt another seek. +// - After this function is called, the client should not send any more audio +// or video samples until |SbPlayerDecoderStatusFunc| is called back with +// |kSbPlayerDecoderStateNeedsData| for each required media type. +// |SbPlayerDecoderStatusFunc| is the |decoder_status_func| callback function +// that was specified when the player was created (SbPlayerCreate). // -// |ticket| is a user-supplied unique ID that will be passed to all subsequent -// SbPlayerDecoderStatusFunc calls. This is used to filter calls that may have -// been in flight when SbPlayerSeek is called. To be very specific, once -// SbPlayerSeek has been called with ticket X, a client should ignore all -// SbPlayerDecoderStatusFunc calls that don't pass in ticket X. +// |player|: The SbPlayer in which the seek operation is being performed. +// |seek_to_pts|: The frame at which playback should begin. +// |ticket|: A user-supplied unique ID that is be passed to all subsequent +// |SbPlayerDecoderStatusFunc| calls. (That is the |decoder_status_func| +// callback function specified when calling SbPlayerCreate.) // -// Seek must also be called before samples are sent when starting playback for -// the first time, or the client will never receive the -// kSbPlayerDecoderStateNeedsData signal. +// The |ticket| value is used to filter calls that may have been in flight +// when SbPlayerSeek was called. To be very specific, once SbPlayerSeek has +// been called with ticket X, a client should ignore all +// |SbPlayerDecoderStatusFunc| calls that do not pass in ticket X. SB_EXPORT void SbPlayerSeek(SbPlayer player, SbMediaTime seek_to_pts, int ticket); -// Writes a sample of the given media type to |player|'s input stream. -// |sample_type| is the type of sample, audio or video. |sample_buffer| is a -// pointer to a buffer with the data for this sample. This buffer is expected to -// be a portion of a H.264 bytestream, containing a sequence of whole NAL Units -// for video, or a complete audio frame. |sample_buffer_size| is the number of -// bytes in the given sample. |sample_pts| is the timestamp of the sample in -// 90KHz ticks (PTS), and samples MAY be written "slightly" out-of-order. -// |video_sample_info| must be provided for every call where |sample_type| is -// kSbMediaTypeVideo, and must be NULL otherwise. |sample_drm_info| must be -// provided for encrypted samples, and must be NULL otherwise. +// Writes a sample of the given media type to |player|'s input stream. The +// lifetime of |video_sample_info| and |sample_drm_info| (as well as member +// |subsample_mapping| contained inside it) are not guaranteed past the call +// to SbPlayerWriteSample. That means that before returning, the implementation +// must synchronously copy any information it wants to retain from those +// structures. // -// The lifetime of |video_sample_info| and |sample_drm_info| (as well as member -// |subsample_mapping| contained inside it) are not guaranteed past the call to -// SbPlayerWriteSample, so the implementation must copy any information it wants -// to retain from those structures synchronously, before it returns. +// |player|: The player to which the sample is written. +// |sample_type|: The type of sample being written. See the |SbMediaType| +// enum in media.h. +// |sample_buffer|: A pointer to a buffer with the data for this sample. The +// buffer is expected to be a portion of a bytestream of the codec type that +// the player was created with. The buffer should contain a sequence of whole +// NAL Units for video, or a complete audio frame. +// |sample_buffer_size|: The number of bytes in the given sample. +// |sample_pts|: The timestamp of the sample in 90KHz ticks (PTS). Note that +// samples MAY be written "slightly" out of order. +// |video_sample_info|: Information about a video sample. This value is +// required if |sample_type| is |kSbMediaTypeVideo|. Otherwise, it must be +// |NULL|. +// |sample_drm_info|: The DRM system for the media sample. This value is +// required for encrypted samples. Otherwise, it must be |NULL|. SB_EXPORT void SbPlayerWriteSample( SbPlayer player, SbMediaType sample_type, @@ -289,19 +330,32 @@ const SbMediaVideoSampleInfo* video_sample_info, const SbDrmSampleInfo* sample_drm_info); -// Writes a marker to |player|'s input stream of |stream_type| that there are no -// more samples for that media type for the remainder of this media stream. -// This marker is invalidated, along with the rest of the contents of the -// stream, after a call to SbPlayerSeek. +// Writes a marker to |player|'s input stream of |stream_type| indicating that +// there are no more samples for that media type for the remainder of this +// media stream. This marker is invalidated, along with the rest of the stream's +// contents, after a call to SbPlayerSeek. +// +// |player|: The player to which the marker is written. +// |stream_type|: The type of stream for which the marker is written. SB_EXPORT void SbPlayerWriteEndOfStream(SbPlayer player, SbMediaType stream_type); #if SB_IS(PLAYER_PUNCHED_OUT) -// Sets the player bounds to the given graphics plane coordinates. Will not take -// effect until the next graphics frame buffer swap. The default bounds for a -// player are the full screen. This function should be expected to be called up -// to once per frame, so implementors should take care to avoid related -// performance concerns with such frequent calls. +// Sets the player bounds to the given graphics plane coordinates. The changes +// do not take effect until the next graphics frame buffer swap. The default +// bounds for a player is the full screen. +// +// This function is called on every graphics frame that changes the video +// bounds. For example, if the video bounds are being animated, then this will +// be called at up to 60 Hz. Since the function could be called up to once per +// frame, implementors should take care to avoid related performance concerns +// with such frequent calls. +// +// |player|: The player that is being resized. +// |x|: The x-coordinate of the upper-left corner of the player. +// |y|: The y-coordinate of the upper-left corner of the player. +// |width|: The width of the player, in pixels. +// |height|: The height of the player, in pixels. SB_EXPORT void SbPlayerSetBounds(SbPlayer player, int x, int y, @@ -309,30 +363,42 @@ int height); #endif -// Sets the pause status for |player|. If |player| is in kPlayerStatePrerolling, -// this will set the initial pause state for the current seek target. +// Pauses or unpauses the |player|. If the |player|'s state is +// |kPlayerStatePrerolling|, this function sets the initial pause state for +// the current seek target. SB_EXPORT void SbPlayerSetPause(SbPlayer player, bool pause); -// Sets the volume for |player|, in [0.0, 1.0]. 0.0 means the audio should be -// muted, and 1.0 means it should be played at full volume. +// Sets the player's volume. +// +// |player|: The player in which the volume is being adjusted. +// |volume|: The new player volume. The value must be between |0.0| and |1.0|, +// inclusive. A value of |0.0| means that the audio should be muted, and a +// value of |1.0| means that it should be played at full volume. SB_EXPORT void SbPlayerSetVolume(SbPlayer player, double volume); -// Gets a snapshot of the current player state and writes it into -// |out_player_info|. This function is expected to be inexpensive, and may be -// called very frequently. +// Gets a snapshot of the current player state and writes it to +// |out_player_info|. This function may be called very frequently and is +// expected to be inexpensive. +// +// |player|: The player about which information is being retrieved. +// |out_player_info|: The information retrieved for the player. SB_EXPORT void SbPlayerGetInfo(SbPlayer player, SbPlayerInfo* out_player_info); #if SB_IS(PLAYER_COMPOSITED) // Gets a handle that represents the player's video output, for the purpose of -// composing with SbCompositor (currently undefined). +// composing with |SbCompositor|, which is currently undefined. +// +// |player|: The player for which the video output handle is retrieved. SB_EXPORT SbPlayerCompositionHandle SbPlayerGetCompositionHandle(SbPlayer player); #endif #if SB_IS(PLAYER_PRODUCING_TEXTURE) // Gets an OpenGL texture ID that points to the player's video output frame at -// the time it was called. This can be called once, and the same texture ID will -// be appropriately mapped to the current video frame when drawn. +// the time it was called. This function can be called once, and the texture ID +// will be appropriately remapped to the current video frame when it is drawn. +// +// |player|: The player for which the texture ID is retrieved. SB_EXPORT uint32_t SbPlayerGetTextureId(SbPlayer player); #endif
diff --git a/src/starboard/queue.h b/src/starboard/queue.h index 65127f0..53f7633 100644 --- a/src/starboard/queue.h +++ b/src/starboard/queue.h
@@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A C++-only synchronized queue implementation, built entirely on top of -// Starboard synchronization primitives. Can be safely used by both clients and -// implementations. +// Module Overview: Starboard Queue module +// +// Defines a C++-only synchronized queue implementation, built entirely on top +// of Starboard synchronization primitives. It can be safely used by both +// clients and implementations. #ifndef STARBOARD_QUEUE_H_ #define STARBOARD_QUEUE_H_
diff --git a/src/starboard/raspi/1/starboard_platform.gyp b/src/starboard/raspi/1/starboard_platform.gyp index c33f33d..4f3603f 100644 --- a/src/starboard/raspi/1/starboard_platform.gyp +++ b/src/starboard/raspi/1/starboard_platform.gyp
@@ -34,7 +34,13 @@ '<(DEPTH)/starboard/raspi/1/system_get_property.cc', '<(DEPTH)/starboard/raspi/shared/application_dispmanx.cc', '<(DEPTH)/starboard/raspi/shared/audio_sink_is_audio_sample_type_supported.cc', + '<(DEPTH)/starboard/raspi/shared/dispmanx_util.cc', + '<(DEPTH)/starboard/raspi/shared/dispmanx_util.h', '<(DEPTH)/starboard/raspi/shared/main.cc', + '<(DEPTH)/starboard/raspi/shared/open_max/open_max_component.cc', + '<(DEPTH)/starboard/raspi/shared/open_max/open_max_component.h', + '<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.cc', + '<(DEPTH)/starboard/raspi/shared/open_max/video_decoder.h', '<(DEPTH)/starboard/raspi/shared/window_create.cc', '<(DEPTH)/starboard/raspi/shared/window_destroy.cc', '<(DEPTH)/starboard/raspi/shared/window_get_platform_handle.cc', @@ -58,8 +64,6 @@ '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h', '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.cc', '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_common.h', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc', - '<(DEPTH)/starboard/shared/ffmpeg/ffmpeg_video_decoder.h', '<(DEPTH)/starboard/shared/gcc/atomic_gcc.h', '<(DEPTH)/starboard/shared/iso/character_is_alphanumeric.cc', '<(DEPTH)/starboard/shared/iso/character_is_digit.cc', @@ -179,6 +183,7 @@ '<(DEPTH)/starboard/shared/posix/system_get_number_of_processors.cc', '<(DEPTH)/starboard/shared/posix/thread_sleep.cc', '<(DEPTH)/starboard/shared/posix/time_get_monotonic_now.cc', + '<(DEPTH)/starboard/shared/posix/time_get_monotonic_thread_now.cc', '<(DEPTH)/starboard/shared/posix/time_get_now.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_current.cc', '<(DEPTH)/starboard/shared/posix/time_zone_get_dst_name.cc', @@ -270,12 +275,19 @@ '<(DEPTH)/starboard/shared/starboard/system_get_random_uint64.cc', '<(DEPTH)/starboard/shared/starboard/system_request_stop.cc', '<(DEPTH)/starboard/shared/starboard/window_set_default_options.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_create_egl.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_destroy.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_get_format.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_get_plane_egl.cc', + '<(DEPTH)/starboard/shared/stub/decode_target_is_opaque.cc', '<(DEPTH)/starboard/shared/stub/drm_close_session.cc', '<(DEPTH)/starboard/shared/stub/drm_create_system.cc', '<(DEPTH)/starboard/shared/stub/drm_destroy_system.cc', '<(DEPTH)/starboard/shared/stub/drm_generate_session_update_request.cc', '<(DEPTH)/starboard/shared/stub/drm_system_internal.h', '<(DEPTH)/starboard/shared/stub/drm_update_session.cc', + '<(DEPTH)/starboard/shared/stub/image_decode.cc', + '<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc', '<(DEPTH)/starboard/shared/stub/media_is_supported.cc', '<(DEPTH)/starboard/shared/stub/system_clear_platform_error.cc', '<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
diff --git a/src/starboard/raspi/1/system_get_property.cc b/src/starboard/raspi/1/system_get_property.cc index 6b5cee9..50f9d92 100644 --- a/src/starboard/raspi/1/system_get_property.cc +++ b/src/starboard/raspi/1/system_get_property.cc
@@ -47,6 +47,7 @@ case kSbSystemPropertyModelName: case kSbSystemPropertyModelYear: case kSbSystemPropertyNetworkOperatorName: + case kSbSystemPropertySpeechApiKey: return false; case kSbSystemPropertyFriendlyName:
diff --git a/src/starboard/raspi/shared/application_dispmanx.cc b/src/starboard/raspi/shared/application_dispmanx.cc index ae3fb8e..fcaf43f 100644 --- a/src/starboard/raspi/shared/application_dispmanx.cc +++ b/src/starboard/raspi/shared/application_dispmanx.cc
@@ -36,6 +36,10 @@ using ::starboard::shared::dev_input::DevInput; +namespace { +const int kVideoLayer = -1; +} // namespace + SbWindow ApplicationDispmanx::CreateWindow(const SbWindowOptions* options) { if (SbWindowIsValid(window_)) { return kSbWindowInvalid; @@ -44,8 +48,13 @@ InitializeDispmanx(); SB_DCHECK(IsDispmanxInitialized()); - window_ = new SbWindowPrivate(display_, options); + window_ = new SbWindowPrivate(*display_, options); input_ = DevInput::Create(window_); + + // Create the dispmanx element to display video frames. + int result = 0; + uint32_t vc_image_ptr; + return window_; } @@ -76,6 +85,18 @@ SbAudioSinkPrivate::TearDown(); } +void ApplicationDispmanx::AcceptFrame(SbPlayer player, + const VideoFrame& frame, + int x, + int y, + int width, + int height) { + if (!video_renderer_) { + video_renderer_.reset(new DispmanxVideoRenderer(*display_, kVideoLayer)); + } + video_renderer_->Update(frame); +} + bool ApplicationDispmanx::MayHaveSystemEvents() { return input_ != NULL; } @@ -102,8 +123,7 @@ return; } - bcm_host_init(); - display_ = vc_dispmanx_display_open(0); + display_.reset(new DispmanxDisplay); SB_DCHECK(IsDispmanxInitialized()); } @@ -113,10 +133,8 @@ } SB_DCHECK(!SbWindowIsValid(window_)); - int result = vc_dispmanx_display_close(display_); - SB_DCHECK(result == 0); - display_ = DISPMANX_NO_HANDLE; - bcm_host_deinit(); + + display_.reset(); } } // namespace shared
diff --git a/src/starboard/raspi/shared/application_dispmanx.h b/src/starboard/raspi/shared/application_dispmanx.h index 6f6a938..ba49ff5 100644 --- a/src/starboard/raspi/shared/application_dispmanx.h +++ b/src/starboard/raspi/shared/application_dispmanx.h
@@ -15,11 +15,9 @@ #ifndef STARBOARD_RASPI_SHARED_APPLICATION_DISPMANX_H_ #define STARBOARD_RASPI_SHARED_APPLICATION_DISPMANX_H_ -#include <bcm_host.h> - -#include <vector> - +#include "starboard/common/scoped_ptr.h" #include "starboard/configuration.h" +#include "starboard/raspi/shared/dispmanx_util.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/linux/dev_input/dev_input.h" #include "starboard/shared/starboard/application.h" @@ -35,8 +33,7 @@ class ApplicationDispmanx : public ::starboard::shared::starboard::QueueApplication { public: - ApplicationDispmanx() - : display_(DISPMANX_NO_HANDLE), window_(kSbWindowInvalid), input_(NULL) {} + ApplicationDispmanx() : window_(kSbWindowInvalid), input_(NULL) {} ~ApplicationDispmanx() SB_OVERRIDE {} static ApplicationDispmanx* Get() { @@ -51,6 +48,12 @@ // --- Application overrides --- void Initialize() SB_OVERRIDE; void Teardown() SB_OVERRIDE; + void AcceptFrame(SbPlayer player, + const VideoFrame& frame, + int x, + int y, + int width, + int height) SB_OVERRIDE; // --- QueueApplication overrides --- bool MayHaveSystemEvents() SB_OVERRIDE; @@ -60,7 +63,7 @@ private: // Returns whether DISPMANX has been initialized. - bool IsDispmanxInitialized() { return display_ != DISPMANX_NO_HANDLE; } + bool IsDispmanxInitialized() { return display_ != NULL; } // Ensures that X is up, display is populated and connected. void InitializeDispmanx(); @@ -69,7 +72,10 @@ void ShutdownDispmanx(); // The DISPMANX display. - DISPMANX_DISPLAY_HANDLE_T display_; + scoped_ptr<DispmanxDisplay> display_; + + // DISPMANX helper to render video frames. + scoped_ptr<DispmanxVideoRenderer> video_renderer_; // The single open window, if any. SbWindow window_;
diff --git a/src/starboard/raspi/shared/configuration_public.h b/src/starboard/raspi/shared/configuration_public.h index 15c18ca..0d4b088 100644 --- a/src/starboard/raspi/shared/configuration_public.h +++ b/src/starboard/raspi/shared/configuration_public.h
@@ -18,7 +18,7 @@ #define STARBOARD_RASPI_SHARED_CONFIGURATION_PUBLIC_H_ // The API version implemented by this platform. -#define SB_API_VERSION 1 +#define SB_API_VERSION 3 // --- System Header Configuration ------------------------------------------- @@ -49,6 +49,12 @@ // Whether the current platform provides the standard header float.h. #define SB_HAS_FLOAT_H 1 +// Whether the current platform has microphone supported. +#define SB_HAS_MICROPHONE 0 + +// Whether the current platform has speech synthesis. +#define SB_HAS_SPEECH_SYNTHESIS 0 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) @@ -371,6 +377,12 @@ // The maximum number of users that can be signed in at the same time. #define SB_USER_MAX_SIGNED_IN 1 +// --- Timing API ------------------------------------------------------------ + +// Whether this platform has an API to retrieve how long the current thread +// has spent in the executing state. +#define SB_HAS_TIME_THREAD_NOW 1 + // --- Platform Specific Audits ---------------------------------------------- #if !defined(__GNUC__)
diff --git a/src/starboard/raspi/shared/dispmanx_util.cc b/src/starboard/raspi/shared/dispmanx_util.cc new file mode 100644 index 0000000..88c7f52 --- /dev/null +++ b/src/starboard/raspi/shared/dispmanx_util.cc
@@ -0,0 +1,105 @@ +// Copyright 2016 Google Inc. 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/raspi/shared/dispmanx_util.h" + +#include "starboard/common/scoped_ptr.h" +#include "starboard/memory.h" + +namespace starboard { +namespace raspi { +namespace shared { + +namespace { + +class DispmanxAutoUpdate { + public: + DispmanxAutoUpdate() { + handle_ = vc_dispmanx_update_start(0 /*screen*/); + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); + } + ~DispmanxAutoUpdate() { + if (handle_ != DISPMANX_NO_HANDLE) { + Update(); + } + } + + DISPMANX_UPDATE_HANDLE_T handle() const { return handle_; } + + void Update() { + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); + int32_t result = vc_dispmanx_update_submit_sync(handle_); + SB_DCHECK(result == 0) << " result=" << result; + handle_ = DISPMANX_NO_HANDLE; + } + + private: + DISPMANX_UPDATE_HANDLE_T handle_; + + SB_DISALLOW_COPY_AND_ASSIGN(DispmanxAutoUpdate); +}; + +} // namespace + +void DispmanxYUV420Resource::ClearWithBlack() { + scoped_array<uint8_t> data(new uint8_t[width_ * height_ * 3 / 2]); + SbMemorySet(data.get(), width_ * height_, 0); + SbMemorySet(data.get() + width_ * height_, width_ * height_ / 2, 0x80); + WriteData(data.get()); +} + +DispmanxElement::DispmanxElement(const DispmanxDisplay& display, + int32_t layer, + const DispmanxRect& dest_rect, + const DispmanxResource& src, + const DispmanxRect& src_rect) { + DispmanxAutoUpdate update; + handle_ = vc_dispmanx_element_add(update.handle(), display.handle(), layer, + &dest_rect, src.handle(), &src_rect, + DISPMANX_PROTECTION_NONE, NULL /*alpha*/, + NULL /*clamp*/, DISPMANX_NO_ROTATE); + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); +} + +DispmanxElement::~DispmanxElement() { + DispmanxAutoUpdate update; + int32_t result = vc_dispmanx_element_remove(update.handle(), handle_); + SB_DCHECK(result == 0) << " result=" << result; +} + +void DispmanxVideoRenderer::Update(const VideoFrame& video_frame) { + if (video_frame.IsEndOfStream()) { + element_.reset(); + resource_.reset(); + return; + } + if (!resource_ || resource_->width() != video_frame.width() || + resource_->height() != video_frame.height()) { + element_.reset(); + resource_.reset(); + + DispmanxRect src_rect(0, 0, video_frame.width() << 16, + video_frame.height() << 16); + resource_.reset( + new DispmanxYUV420Resource(video_frame.width(), video_frame.height())); + element_.reset(new DispmanxElement(display_, layer_, DispmanxRect(), + *resource_, src_rect)); + } + + resource_->WriteData(video_frame.GetPlane(0).data); +} + +} // namespace shared +} // namespace raspi +} // namespace starboard
diff --git a/src/starboard/raspi/shared/dispmanx_util.h b/src/starboard/raspi/shared/dispmanx_util.h new file mode 100644 index 0000000..c4b65f6 --- /dev/null +++ b/src/starboard/raspi/shared/dispmanx_util.h
@@ -0,0 +1,145 @@ +// Copyright 2016 Google Inc. 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_RASPI_SHARED_DISPMANX_UTIL_H_ +#define STARBOARD_RASPI_SHARED_DISPMANX_UTIL_H_ + +#include <bcm_host.h> + +#include "starboard/common/scoped_ptr.h" +#include "starboard/configuration.h" +#include "starboard/log.h" +#include "starboard/shared/internal_only.h" +#include "starboard/shared/starboard/player/video_frame_internal.h" +#include "starboard/types.h" + +namespace starboard { +namespace raspi { +namespace shared { + +class DispmanxRect : public VC_RECT_T { + public: + DispmanxRect() { vc_dispmanx_rect_set(this, 0, 0, 0, 0); } + DispmanxRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height) { + vc_dispmanx_rect_set(this, x, y, width, height); + } +}; + +class DispmanxDisplay { + public: + DispmanxDisplay() { + bcm_host_init(); + handle_ = vc_dispmanx_display_open(0); + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); + } + ~DispmanxDisplay() { + int result = vc_dispmanx_display_close(handle_); + SB_DCHECK(result == 0); + bcm_host_deinit(); + } + + DISPMANX_DISPLAY_HANDLE_T handle() const { return handle_; } + + private: + DISPMANX_DISPLAY_HANDLE_T handle_; + + SB_DISALLOW_COPY_AND_ASSIGN(DispmanxDisplay); +}; + +class DispmanxResource { + public: + DispmanxResource() : handle_(DISPMANX_NO_HANDLE) {} + + virtual ~DispmanxResource() { + if (handle_ != DISPMANX_NO_HANDLE) { + int32_t result = vc_dispmanx_resource_delete(handle_); + SB_DCHECK(result == 0) << " result=" << result; + } + } + + DISPMANX_RESOURCE_HANDLE_T handle() const { return handle_; } + uint32_t width() const { return width_; } + uint32_t height() const { return height_; } + + protected: + DispmanxResource(VC_IMAGE_TYPE_T image_type, uint32_t width, uint32_t height) + : width_(width), height_(height) { + uint32_t vc_image_ptr; + + handle_ = + vc_dispmanx_resource_create(image_type, width, height, &vc_image_ptr); + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); + } + + DISPMANX_RESOURCE_HANDLE_T handle_; + uint32_t width_; + uint32_t height_; + + SB_DISALLOW_COPY_AND_ASSIGN(DispmanxResource); +}; + +class DispmanxYUV420Resource : public DispmanxResource { + public: + DispmanxYUV420Resource(uint32_t width, uint32_t height) + : DispmanxResource(VC_IMAGE_YUV420, width, height) {} + + void WriteData(const void* data) { + SB_DCHECK(handle_ != DISPMANX_NO_HANDLE); + DispmanxRect dst_rect(0, 0, width(), height() * 3 / 2); + int32_t result = vc_dispmanx_resource_write_data( + handle_, VC_IMAGE_YUV420, width(), const_cast<void*>(data), &dst_rect); + SB_DCHECK(result == 0); + } + void ClearWithBlack(); +}; + +class DispmanxElement { + public: + DispmanxElement(const DispmanxDisplay& display, + int32_t layer, + const DispmanxRect& dest_rect, + const DispmanxResource& src, + const DispmanxRect& src_rect); + ~DispmanxElement(); + + DISPMANX_ELEMENT_HANDLE_T handle() const { return handle_; } + + private: + DISPMANX_ELEMENT_HANDLE_T handle_; + + SB_DISALLOW_COPY_AND_ASSIGN(DispmanxElement); +}; + +class DispmanxVideoRenderer { + public: + typedef starboard::shared::starboard::player::VideoFrame VideoFrame; + DispmanxVideoRenderer(const DispmanxDisplay& display, int32_t layer) + : display_(display), layer_(layer) {} + + void Update(const VideoFrame& video_frame); + + private: + const DispmanxDisplay& display_; + int32_t layer_; + scoped_ptr<DispmanxYUV420Resource> resource_; + scoped_ptr<DispmanxElement> element_; + + SB_DISALLOW_COPY_AND_ASSIGN(DispmanxVideoRenderer); +}; + +} // namespace shared +} // namespace raspi +} // namespace starboard + +#endif // STARBOARD_RASPI_SHARED_DISPMANX_UTIL_H_
diff --git a/src/starboard/raspi/shared/open_max/open_max_component.cc b/src/starboard/raspi/shared/open_max/open_max_component.cc new file mode 100644 index 0000000..eb05825 --- /dev/null +++ b/src/starboard/raspi/shared/open_max/open_max_component.cc
@@ -0,0 +1,365 @@ +// Copyright 2016 Google Inc. 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/raspi/shared/open_max/open_max_component.h" + +#include <algorithm> + +#include "starboard/configuration.h" +#include "starboard/once.h" +#include "starboard/thread.h" + +namespace starboard { +namespace raspi { +namespace shared { +namespace open_max { + +namespace { + +const int kInvalidPort = -1; + +OMX_INDEXTYPE kPortTypes[] = {OMX_IndexParamAudioInit, OMX_IndexParamVideoInit, + OMX_IndexParamImageInit, OMX_IndexParamOtherInit}; + +SbOnceControl s_open_max_initialization_once = SB_ONCE_INITIALIZER; + +void DoInitializeOpenMax() { + OMX_ERRORTYPE error = OMX_Init(); + SB_DCHECK(error == OMX_ErrorNone); +} + +void InitializeOpenMax() { + bool initialized = + SbOnce(&s_open_max_initialization_once, DoInitializeOpenMax); + SB_DCHECK(initialized); +} + +} // namespace + +OpenMaxComponent::OpenMaxComponent(const char* name, size_t minimum_output_size) + : condition_variable_(mutex_), + minimum_output_size_(minimum_output_size), + handle_(NULL), + input_port_(kInvalidPort), + output_port_(kInvalidPort), + output_setting_changed_(false), + output_buffer_(NULL), + output_buffer_filled_(false) { + InitializeOpenMax(); + + OMX_CALLBACKTYPE callbacks; + callbacks.EventHandler = OpenMaxComponent::EventHandler; + callbacks.EmptyBufferDone = OpenMaxComponent::EmptyBufferDone; + callbacks.FillBufferDone = OpenMaxComponent::FillBufferDone; + + OMX_ERRORTYPE error = + OMX_GetHandle(&handle_, const_cast<char*>(name), this, &callbacks); + SB_DCHECK(error == OMX_ErrorNone); + + for (size_t i = 0; i < SB_ARRAY_SIZE(kPortTypes); ++i) { + OMX_PORT_PARAM_TYPE port; + port.nSize = sizeof(OMX_PORT_PARAM_TYPE); + port.nVersion.nVersion = OMX_VERSION; + + error = OMX_GetParameter(handle_, kPortTypes[i], &port); + if (error == OMX_ErrorNone && port.nPorts == 2) { + input_port_ = port.nStartPortNumber; + output_port_ = input_port_ + 1; + SendCommandAndWaitForCompletion(OMX_CommandPortDisable, input_port_); + SendCommandAndWaitForCompletion(OMX_CommandPortDisable, output_port_); + break; + } + } + SB_CHECK(input_port_ != kInvalidPort); + SB_CHECK(output_port_ != kInvalidPort); + SB_DLOG(INFO) << "Opened \"" << name << "\" with port " << input_port_ + << " and " << output_port_; + + SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateIdle); +} + +OpenMaxComponent::~OpenMaxComponent() { + if (!handle_) { + return; + } + SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateIdle); + + SendCommandAndWaitForCompletion(OMX_CommandFlush, input_port_); + SendCommandAndWaitForCompletion(OMX_CommandFlush, output_port_); + + SendCommand(OMX_CommandPortDisable, input_port_); + for (BufferHeaders::iterator iter = unused_buffers_.begin(); + iter != unused_buffers_.end(); ++iter) { + OMX_ERRORTYPE error = OMX_FreeBuffer(handle_, input_port_, *iter); + SB_DCHECK(error == OMX_ErrorNone); + } + WaitForCommandCompletion(); + + SendCommand(OMX_CommandPortDisable, output_port_); + if (output_buffer_) { + OMX_ERRORTYPE error = OMX_FreeBuffer(handle_, output_port_, output_buffer_); + SB_DCHECK(error == OMX_ErrorNone); + } + WaitForCommandCompletion(); + + SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateLoaded); + OMX_FreeHandle(handle_); +} + +void OpenMaxComponent::Start() { + EnableInputPortAndAllocateBuffers(); + SendCommandAndWaitForCompletion(OMX_CommandStateSet, OMX_StateExecuting); +} + +void OpenMaxComponent::Flush() { + SendCommandAndWaitForCompletion(OMX_CommandFlush, input_port_); + SendCommandAndWaitForCompletion(OMX_CommandFlush, output_port_); +} + +void OpenMaxComponent::WriteData(const void* data, + size_t size, + SbTime timestamp) { + size_t offset = 0; + + while (offset != size) { + OMX_BUFFERHEADERTYPE* buffer_header = GetUnusedInputBuffer(); + + int size_to_append = std::min(size - offset, buffer_header->nAllocLen); + buffer_header->nOffset = 0; + buffer_header->nFilledLen = size_to_append; + buffer_header->nFlags = 0; + + buffer_header->nTimeStamp.nLowPart = timestamp; + buffer_header->nTimeStamp.nHighPart = timestamp >> 32; + + memcpy(buffer_header->pBuffer, (const char*)data + offset, size_to_append); + offset += size_to_append; + + OMX_ERRORTYPE error = OMX_EmptyThisBuffer(handle_, buffer_header); + SB_DCHECK(error == OMX_ErrorNone); + } +} + +void OpenMaxComponent::WriteEOS() { + OMX_BUFFERHEADERTYPE* buffer_header = GetUnusedInputBuffer(); + + buffer_header->nOffset = 0; + buffer_header->nFilledLen = 0; + buffer_header->nFlags = OMX_BUFFERFLAG_EOS; + + OMX_ERRORTYPE error = OMX_EmptyThisBuffer(handle_, buffer_header); + SB_DCHECK(error == OMX_ErrorNone); +} + +bool OpenMaxComponent::ReadVideoFrame(VideoFrame* frame) { + { + ScopedLock scoped_lock(mutex_); + if (output_buffer_ && !output_buffer_filled_) { + return false; + } + if (!output_setting_changed_) { + return false; + } + } + SB_DCHECK(output_setting_changed_); + if (!output_buffer_) { + GetOutputPortParam(&output_port_definition_); + SB_DCHECK(output_port_definition_.format.video.eColorFormat == + OMX_COLOR_FormatYUV420PackedPlanar); + EnableOutputPortAndAllocateBuffer(); + return false; + } + + if (output_buffer_->nFlags & OMX_BUFFERFLAG_EOS) { + *frame = VideoFrame(); + return true; + } + SbMediaTime timestamp = + ((output_buffer_->nTimeStamp.nHighPart * 0x100000000ull) + + output_buffer_->nTimeStamp.nLowPart) * + kSbMediaTimeSecond / kSbTimeSecond; + int width = output_port_definition_.format.video.nFrameWidth; + int height = output_port_definition_.format.video.nSliceHeight; + int pitch = output_port_definition_.format.video.nStride; + *frame = VideoFrame::CreateYV12Frame( + width, height, pitch, timestamp, output_buffer_->pBuffer, + output_buffer_->pBuffer + pitch * height, + output_buffer_->pBuffer + pitch * height * 5 / 4); + output_buffer_filled_ = false; + output_buffer_->nFilledLen = 0; + OMX_ERRORTYPE error = OMX_FillThisBuffer(handle_, output_buffer_); + SB_DCHECK(error == OMX_ErrorNone); + return true; +} + +void OpenMaxComponent::SendCommand(OMX_COMMANDTYPE command, int param) { + OMX_ERRORTYPE error = OMX_SendCommand(handle_, command, param, NULL); + SB_DCHECK(error == OMX_ErrorNone); +} + +void OpenMaxComponent::WaitForCommandCompletion() { + for (;;) { + ScopedLock scoped_lock(mutex_); + for (EventDescriptions::iterator iter = event_descriptions_.begin(); + iter != event_descriptions_.end(); ++iter) { + if (iter->event == OMX_EventCmdComplete) { + event_descriptions_.erase(iter); + return; + } + // Special case for OMX_CommandStateSet. + if (iter->event == OMX_EventError && iter->data1 == OMX_ErrorSameState) { + event_descriptions_.erase(iter); + return; + } + } + condition_variable_.Wait(); + } +} + +void OpenMaxComponent::SendCommandAndWaitForCompletion(OMX_COMMANDTYPE command, + int param) { + SendCommand(command, param); + WaitForCommandCompletion(); +} + +void OpenMaxComponent::EnableInputPortAndAllocateBuffers() { + SB_DCHECK(unused_buffers_.empty()); + + OMXParamPortDefinition port_definition; + GetInputPortParam(&port_definition); + + SendCommand(OMX_CommandPortEnable, input_port_); + + unused_buffers_.resize(port_definition.nBufferCountActual); + for (int i = 0; i != port_definition.nBufferCountActual; ++i) { + OMX_ERRORTYPE error = + OMX_AllocateBuffer(handle_, &unused_buffers_[i], input_port_, NULL, + port_definition.nBufferSize); + SB_DCHECK(error == OMX_ErrorNone); + } + + WaitForCommandCompletion(); +} + +void OpenMaxComponent::EnableOutputPortAndAllocateBuffer() { + if (output_buffer_ != NULL) { + return; + } + + SendCommand(OMX_CommandPortEnable, output_port_); + + OMX_ERRORTYPE error = OMX_AllocateBuffer( + handle_, &output_buffer_, output_port_, NULL, + std::max(output_port_definition_.nBufferSize, minimum_output_size_)); + SB_DCHECK(error == OMX_ErrorNone); + WaitForCommandCompletion(); + + error = OMX_FillThisBuffer(handle_, output_buffer_); + SB_DCHECK(error == OMX_ErrorNone); +} + +OMX_BUFFERHEADERTYPE* OpenMaxComponent::GetUnusedInputBuffer() { + for (;;) { + ScopedLock scoped_lock(mutex_); + if (!unused_buffers_.empty()) { + OMX_BUFFERHEADERTYPE* buffer_header = unused_buffers_.back(); + unused_buffers_.pop_back(); + return buffer_header; + } + SbThreadSleep(kSbTimeMillisecond); + } + SB_NOTREACHED(); + return NULL; +} + +OMX_ERRORTYPE OpenMaxComponent::OnEvent(OMX_EVENTTYPE event, + OMX_U32 data1, + OMX_U32 data2, + OMX_PTR event_data) { + if (event == OMX_EventError && data1 != OMX_ErrorSameState) { + SB_NOTREACHED() << "OMX_EventError received with " << std::hex << data1 + << " " << data2; + return OMX_ErrorNone; + } + + ScopedLock scoped_lock(mutex_); + if (event == OMX_EventPortSettingsChanged && data1 == output_port_) { + output_setting_changed_ = true; + return OMX_ErrorNone; + } + EventDescription event_desc; + event_desc.event = event; + event_desc.data1 = data1; + event_desc.data2 = data2; + event_desc.event_data = event_data; + event_descriptions_.push_back(event_desc); + condition_variable_.Signal(); + + return OMX_ErrorNone; +} + +OMX_ERRORTYPE OpenMaxComponent::OnEmptyBufferDone( + OMX_BUFFERHEADERTYPE* buffer) { + ScopedLock scoped_lock(mutex_); + unused_buffers_.push_back(buffer); +} + +void OpenMaxComponent::OnFillBufferDone(OMX_BUFFERHEADERTYPE* buffer) { + ScopedLock scoped_lock(mutex_); + SB_DCHECK(!output_buffer_filled_); + output_buffer_filled_ = true; +} + +// static +OMX_ERRORTYPE OpenMaxComponent::EventHandler(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_EVENTTYPE event, + OMX_U32 data1, + OMX_U32 data2, + OMX_PTR event_data) { + SB_DCHECK(app_data != NULL); + OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data); + SB_DCHECK(handle == component->handle_); + + return component->OnEvent(event, data1, data2, event_data); +} + +// static +OMX_ERRORTYPE OpenMaxComponent::EmptyBufferDone(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_BUFFERHEADERTYPE* buffer) { + SB_DCHECK(app_data != NULL); + OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data); + SB_DCHECK(handle == component->handle_); + + return component->OnEmptyBufferDone(buffer); +} + +// static +OMX_ERRORTYPE OpenMaxComponent::FillBufferDone(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_BUFFERHEADERTYPE* buffer) { + SB_DCHECK(app_data != NULL); + OpenMaxComponent* component = reinterpret_cast<OpenMaxComponent*>(app_data); + SB_DCHECK(handle == component->handle_); + + component->OnFillBufferDone(buffer); + + return OMX_ErrorNone; +} + +} // namespace open_max +} // namespace shared +} // namespace raspi +} // namespace starboard
diff --git a/src/starboard/raspi/shared/open_max/open_max_component.h b/src/starboard/raspi/shared/open_max/open_max_component.h new file mode 100644 index 0000000..9085c1b --- /dev/null +++ b/src/starboard/raspi/shared/open_max/open_max_component.h
@@ -0,0 +1,154 @@ +// Copyright 2016 Google Inc. 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_RASPI_SHARED_OPEN_MAX_OPEN_MAX_COMPONENT_H_ +#define STARBOARD_RASPI_SHARED_OPEN_MAX_OPEN_MAX_COMPONENT_H_ + +// OMX_SKIP64BIT is required for using VC GPU code. +#define OMX_SKIP64BIT 1 + +#include <IL/OMX_Broadcom.h> +#include <interface/vcos/vcos.h> +#include <interface/vcos/vcos_logging.h> +#include <interface/vmcs_host/vchost.h> +#include <vector> + +#include "starboard/condition_variable.h" +#include "starboard/log.h" +#include "starboard/mutex.h" +#include "starboard/shared/internal_only.h" +#include "starboard/shared/starboard/player/video_frame_internal.h" +#include "starboard/time.h" + +namespace starboard { +namespace raspi { +namespace shared { +namespace open_max { + +template <typename ParamType, OMX_INDEXTYPE index> +struct OMXParam : public ParamType { + static const OMX_INDEXTYPE Index = index; + + OMXParam() : ParamType() { + ParamType::nSize = sizeof(ParamType); + ParamType::nVersion.nVersion = OMX_VERSION; + } +}; + +typedef OMXParam<OMX_PARAM_PORTDEFINITIONTYPE, OMX_IndexParamPortDefinition> + OMXParamPortDefinition; +typedef OMXParam<OMX_VIDEO_PARAM_PORTFORMATTYPE, OMX_IndexParamVideoPortFormat> + OMXVideoParamPortFormat; + +class OpenMaxComponent { + public: + typedef starboard::shared::starboard::player::VideoFrame VideoFrame; + + explicit OpenMaxComponent(const char* name, size_t minimum_output_size = 0); + ~OpenMaxComponent(); + + void Start(); + void Flush(); + + void WriteData(const void* data, size_t size, SbTime timestamp); + void WriteEOS(); + + bool ReadVideoFrame(VideoFrame* frame); + + template <typename ParamType> + void GetInputPortParam(ParamType* param) const { + param->nPortIndex = input_port_; + OMX_ERRORTYPE error = OMX_GetParameter(handle_, ParamType::Index, param); + SB_DCHECK(error == OMX_ErrorNone) << std::hex << "OMX_GetParameter(" + << ParamType::Index + << ") failed with error " << error; + } + + template <typename ParamType> + void GetOutputPortParam(ParamType* param) const { + param->nPortIndex = output_port_; + OMX_ERRORTYPE error = OMX_GetParameter(handle_, ParamType::Index, param); + SB_DCHECK(error == OMX_ErrorNone) << std::hex << "OMX_GetParameter(" + << ParamType::Index + << ") failed with error " << error; + } + + template <typename ParamType> + void SetPortParam(const ParamType& param) const { + OMX_ERRORTYPE error = OMX_SetParameter(handle_, ParamType::Index, + const_cast<ParamType*>(¶m)); + SB_DCHECK(error == OMX_ErrorNone) << std::hex << "OMX_SetParameter(" + << ParamType::Index + << ") failed with error " << error; + } + + private: + typedef std::vector<OMX_BUFFERHEADERTYPE*> BufferHeaders; + + struct EventDescription { + OMX_EVENTTYPE event; + OMX_U32 data1; + OMX_U32 data2; + OMX_PTR event_data; + }; + + typedef std::vector<EventDescription> EventDescriptions; + + void SendCommand(OMX_COMMANDTYPE command, int param); + void WaitForCommandCompletion(); + void SendCommandAndWaitForCompletion(OMX_COMMANDTYPE command, int param); + void EnableInputPortAndAllocateBuffers(); + void EnableOutputPortAndAllocateBuffer(); + OMX_BUFFERHEADERTYPE* GetUnusedInputBuffer(); + + OMX_ERRORTYPE OnEvent(OMX_EVENTTYPE event, + OMX_U32 data1, + OMX_U32 data2, + OMX_PTR event_data); + OMX_ERRORTYPE OnEmptyBufferDone(OMX_BUFFERHEADERTYPE* buffer); + void OnFillBufferDone(OMX_BUFFERHEADERTYPE* buffer); + + static OMX_ERRORTYPE EventHandler(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_EVENTTYPE event, + OMX_U32 data1, + OMX_U32 data2, + OMX_PTR event_data); + static OMX_ERRORTYPE EmptyBufferDone(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_BUFFERHEADERTYPE* buffer); + static OMX_ERRORTYPE FillBufferDone(OMX_HANDLETYPE handle, + OMX_PTR app_data, + OMX_BUFFERHEADERTYPE* buffer); + + Mutex mutex_; + ConditionVariable condition_variable_; + const size_t minimum_output_size_; + OMX_HANDLETYPE handle_; + int input_port_; + int output_port_; + bool output_setting_changed_; + EventDescriptions event_descriptions_; + BufferHeaders unused_buffers_; + OMX_BUFFERHEADERTYPE* output_buffer_; + OMXParamPortDefinition output_port_definition_; + bool output_buffer_filled_; +}; + +} // namespace open_max +} // namespace shared +} // namespace raspi +} // namespace starboard + +#endif // STARBOARD_RASPI_SHARED_OPEN_MAX_OPEN_MAX_COMPONENT_H_
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.cc b/src/starboard/raspi/shared/open_max/video_decoder.cc new file mode 100644 index 0000000..702a9f8 --- /dev/null +++ b/src/starboard/raspi/shared/open_max/video_decoder.cc
@@ -0,0 +1,103 @@ +// Copyright 2016 Google Inc. 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/raspi/shared/open_max/video_decoder.h" + +#include "starboard/log.h" + +namespace starboard { +namespace raspi { +namespace shared { +namespace open_max { + +using starboard::shared::starboard::player::VideoFrame; + +namespace { + +const char kVideoDecodeComponentName[] = "OMX.broadcom.video_decode"; +const size_t kMaxVideoFrameSize = 1920 * 1088 * 3 / 2; + +} // namespace + +VideoDecoder::VideoDecoder(SbMediaVideoCodec video_codec) + : component_(kVideoDecodeComponentName, kMaxVideoFrameSize), + host_(NULL), + stream_ended_(false) { + SB_DCHECK(video_codec == kSbMediaVideoCodecH264); + + OMXVideoParamPortFormat port_format; + component_.GetInputPortParam(&port_format); + port_format.eCompressionFormat = OMX_VIDEO_CodingAVC; + component_.SetPortParam(port_format); + + component_.Start(); +} + +VideoDecoder::~VideoDecoder() {} + +void VideoDecoder::SetHost(Host* host) { + SB_DCHECK(host != NULL); + SB_DCHECK(host_ == NULL); + host_ = host; +} + +void VideoDecoder::WriteInputBuffer(const InputBuffer& input_buffer) { + SB_DCHECK(host_ != NULL); + + if (stream_ended_) { + SB_LOG(ERROR) << "WriteInputFrame() was called after WriteEndOfStream()."; + return; + } + component_.WriteData(input_buffer.data(), input_buffer.size(), + input_buffer.pts() * kSbTimeSecond / kSbMediaTimeSecond); + + VideoFrame frame; + if (component_.ReadVideoFrame(&frame)) { + host_->OnDecoderStatusUpdate(kNeedMoreInput, &frame); + } else { + host_->OnDecoderStatusUpdate(kNeedMoreInput, NULL); + } +} + +void VideoDecoder::WriteEndOfStream() { + SB_DCHECK(!stream_ended_); + stream_ended_ = true; + component_.WriteEOS(); +} + +void VideoDecoder::Reset() { + stream_ended_ = false; + component_.Flush(); +} + +} // namespace open_max +} // namespace shared +} // namespace raspi + +namespace shared { +namespace starboard { +namespace player { +namespace filter { + +// static +VideoDecoder* VideoDecoder::Create(SbMediaVideoCodec video_codec) { + return new raspi::shared::open_max::VideoDecoder(video_codec); +} + +} // namespace filter +} // namespace player +} // namespace starboard +} // namespace shared + +} // namespace starboard
diff --git a/src/starboard/raspi/shared/open_max/video_decoder.h b/src/starboard/raspi/shared/open_max/video_decoder.h new file mode 100644 index 0000000..92850da --- /dev/null +++ b/src/starboard/raspi/shared/open_max/video_decoder.h
@@ -0,0 +1,56 @@ +// Copyright 2016 Google Inc. 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_RASPI_SHARED_OPEN_MAX_VIDEO_DECODER_H_ +#define STARBOARD_RASPI_SHARED_OPEN_MAX_VIDEO_DECODER_H_ + +#include "starboard/media.h" +#include "starboard/raspi/shared/open_max/open_max_component.h" +#include "starboard/shared/internal_only.h" +#include "starboard/shared/starboard/player/filter/video_decoder_internal.h" + +namespace starboard { +namespace raspi { +namespace shared { +namespace open_max { + +class VideoDecoder + : public starboard::shared::starboard::player::filter::VideoDecoder { + public: + typedef starboard::shared::starboard::player::InputBuffer InputBuffer; + + explicit VideoDecoder(SbMediaVideoCodec video_codec); + ~VideoDecoder() SB_OVERRIDE; + + void SetHost(Host* host) SB_OVERRIDE; + void WriteInputBuffer(const InputBuffer& input_buffer) SB_OVERRIDE; + void WriteEndOfStream() SB_OVERRIDE; + void Reset() SB_OVERRIDE; + + private: + OpenMaxComponent component_; + + // These variables will be initialized inside ctor or SetHost() and will not + // be changed during the life time of this class. + Host* host_; + + bool stream_ended_; +}; + +} // namespace open_max +} // namespace shared +} // namespace raspi +} // namespace starboard + +#endif // STARBOARD_RASPI_SHARED_OPEN_MAX_VIDEO_DECODER_H_
diff --git a/src/starboard/raspi/shared/window_internal.cc b/src/starboard/raspi/shared/window_internal.cc index 532ecd4..cb03c2d 100644 --- a/src/starboard/raspi/shared/window_internal.cc +++ b/src/starboard/raspi/shared/window_internal.cc
@@ -14,21 +14,19 @@ #include "starboard/raspi/shared/window_internal.h" -#include <bcm_host.h> - #include "starboard/log.h" namespace { const int32_t kLayer = 0; -const DISPMANX_RESOURCE_HANDLE_T kResource = DISPMANX_NO_HANDLE; } // namespace -SbWindowPrivate::SbWindowPrivate(DISPMANX_DISPLAY_HANDLE_T display, - const SbWindowOptions* options) - : display(display), element(DISPMANX_NO_HANDLE) { - VC_RECT_T destination_rect; - VC_RECT_T source_rect; +using starboard::raspi::shared::DispmanxDisplay; +using starboard::raspi::shared::DispmanxElement; +using starboard::raspi::shared::DispmanxRect; +using starboard::raspi::shared::DispmanxResource; +SbWindowPrivate::SbWindowPrivate(const DispmanxDisplay& display, + const SbWindowOptions* options) { uint32_t window_width = 0; uint32_t window_height = 0; if (options && options->size.width > 0 && options->size.height > 0) { @@ -41,41 +39,23 @@ SB_DCHECK(result >= 0); } - destination_rect.x = 0; - destination_rect.y = 0; - destination_rect.width = window_width; - destination_rect.height = window_height; - - source_rect.x = 0; - source_rect.y = 0; - // This shift is part of the examples, but unexplained. It appears to work. - source_rect.width = window_width << 16; - source_rect.height = window_height << 16; - + DispmanxRect destination_rect(0, 0, window_width, window_height); + // The "<< 16"s are part of the examples, but unexplained. It appears to work. + DispmanxRect source_rect(0, 0, window_width << 16, window_height << 16); + // The window doesn't have an image resource associated with it. + DispmanxResource resource; // Creating a window (called an "element" here, created by adding it to the // display) must happen within an "update", which seems to represent a sort of // window manager transaction. - DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0 /*screen*/); - SB_DCHECK(update != DISPMANX_NO_HANDLE); - element = vc_dispmanx_element_add(update, display, kLayer, &destination_rect, - kResource, &source_rect, - DISPMANX_PROTECTION_NONE, NULL /*alpha*/, - NULL /*clamp*/, DISPMANX_NO_ROTATE); - SB_DCHECK(element != DISPMANX_NO_HANDLE); - int32_t result = vc_dispmanx_update_submit_sync(update); - SB_DCHECK(result == 0) << " result=" << result; - + element.reset(new DispmanxElement(display, kLayer, destination_rect, resource, + source_rect)); // We can then populate this struct, a pointer to which is what EGL expects as // a "native window" handle. - window.element = element; + window.element = element->handle(); window.width = window_width; window.height = window_height; } SbWindowPrivate::~SbWindowPrivate() { - DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0 /*screen*/); - int32_t result = vc_dispmanx_element_remove(update, element); - SB_DCHECK(result == 0) << " result=" << result; - vc_dispmanx_update_submit_sync(update); - element = DISPMANX_NO_HANDLE; + element.reset(); }
diff --git a/src/starboard/raspi/shared/window_internal.h b/src/starboard/raspi/shared/window_internal.h index 06a77d5..3f51a8a 100644 --- a/src/starboard/raspi/shared/window_internal.h +++ b/src/starboard/raspi/shared/window_internal.h
@@ -15,19 +15,19 @@ #ifndef STARBOARD_RASPI_SHARED_WINDOW_INTERNAL_H_ #define STARBOARD_RASPI_SHARED_WINDOW_INTERNAL_H_ -#include <bcm_host.h> #include <EGL/egl.h> +#include "starboard/common/scoped_ptr.h" +#include "starboard/raspi/shared/dispmanx_util.h" #include "starboard/shared/internal_only.h" #include "starboard/window.h" struct SbWindowPrivate { - SbWindowPrivate(DISPMANX_DISPLAY_HANDLE_T display, + SbWindowPrivate(const starboard::raspi::shared::DispmanxDisplay& display, const SbWindowOptions* options); ~SbWindowPrivate(); - DISPMANX_DISPLAY_HANDLE_T display; - DISPMANX_ELEMENT_HANDLE_T element; + starboard::scoped_ptr<starboard::raspi::shared::DispmanxElement> element; EGL_DISPMANX_WINDOW_T window; };
diff --git a/src/starboard/shared/dlmalloc/memory_map.cc b/src/starboard/shared/dlmalloc/memory_map.cc index 59730fc..c5d3829 100644 --- a/src/starboard/shared/dlmalloc/memory_map.cc +++ b/src/starboard/shared/dlmalloc/memory_map.cc
@@ -13,9 +13,11 @@ // limitations under the License. #include "starboard/memory.h" - +#include "starboard/shared/starboard/memory_reporter_internal.h" #include "starboard/shared/dlmalloc/page_internal.h" void* SbMemoryMap(int64_t size_bytes, int flags, const char* name) { - return SbPageMap(size_bytes, flags, name); + void* memory = SbPageMap(size_bytes, flags, name); + SbMemoryReporterReportMappedMemory(memory, size_bytes); + return memory; }
diff --git a/src/starboard/shared/dlmalloc/memory_unmap.cc b/src/starboard/shared/dlmalloc/memory_unmap.cc index ff45918..dc32164 100644 --- a/src/starboard/shared/dlmalloc/memory_unmap.cc +++ b/src/starboard/shared/dlmalloc/memory_unmap.cc
@@ -13,9 +13,10 @@ // limitations under the License. #include "starboard/memory.h" - +#include "starboard/shared/starboard/memory_reporter_internal.h" #include "starboard/shared/dlmalloc/page_internal.h" bool SbMemoryUnmap(void* virtual_address, int64_t size_bytes) { + SbMemoryReporterReportUnmappedMemory(virtual_address, size_bytes); return SbPageUnmap(virtual_address, size_bytes); }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc index 618b9e3..1444180 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.cc
@@ -14,6 +14,7 @@ #include "starboard/shared/ffmpeg/ffmpeg_audio_decoder.h" +#include "starboard/audio_sink.h" #include "starboard/log.h" namespace starboard { @@ -22,15 +23,23 @@ namespace { -// The required output format for the decoder is interleaved float. However -// the output of the ffmpeg decoder can be in other formats. So libavresample -// is used to convert the output into the required format. -void ConvertToInterleavedFloat(int source_sample_format, - int channel_layout, - int sample_rate, - int samples_per_channel, - uint8_t** input_buffer, - uint8_t* output_buffer) { +SbMediaAudioSampleType GetSupportedSampleType() { + if (SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)) { + return kSbMediaAudioSampleTypeFloat32; + } + return kSbMediaAudioSampleTypeInt16; +} + +// The required output format and the output of the ffmpeg decoder can be +// different. In this case libavresample is used to convert the ffmpeg output +// into the required format. +void ConvertSamples(int source_sample_format, + int target_sample_format, + int channel_layout, + int sample_rate, + int samples_per_channel, + uint8_t** input_buffer, + uint8_t* output_buffer) { AVAudioResampleContext* context = avresample_alloc_context(); SB_DCHECK(context != NULL); @@ -39,7 +48,7 @@ av_opt_set_int(context, "in_sample_rate", sample_rate, 0); av_opt_set_int(context, "out_sample_rate", sample_rate, 0); av_opt_set_int(context, "in_sample_fmt", source_sample_format, 0); - av_opt_set_int(context, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); + av_opt_set_int(context, "out_sample_fmt", target_sample_format, 0); av_opt_set_int(context, "internal_sample_fmt", source_sample_format, 0); int result = avresample_open(context); @@ -58,7 +67,8 @@ AudioDecoder::AudioDecoder(SbMediaAudioCodec audio_codec, const SbMediaAudioHeader& audio_header) - : codec_context_(NULL), + : sample_type_(GetSupportedSampleType()), + codec_context_(NULL), av_frame_(NULL), stream_ended_(false), audio_header_(audio_header) { @@ -72,7 +82,7 @@ } void AudioDecoder::Decode(const InputBuffer& input_buffer, - std::vector<float>* output) { + std::vector<uint8_t>* output) { SB_CHECK(output != NULL); SB_CHECK(codec_context_ != NULL); @@ -105,11 +115,16 @@ audio_header_.samples_per_second = codec_context_->sample_rate; if (decoded_audio_size > 0) { - output->resize(decoded_audio_size / sizeof(float)); - ConvertToInterleavedFloat( - codec_context_->sample_fmt, codec_context_->channel_layout, - audio_header_.samples_per_second, av_frame_->nb_samples, - av_frame_->extended_data, reinterpret_cast<uint8_t*>(&(*output)[0])); + output->resize(codec_context_->channels * av_frame_->nb_samples * + (sample_type_ == kSbMediaAudioSampleTypeInt16 ? 2 : 4)); + if (codec_context_->sample_fmt == codec_context_->request_sample_fmt) { + memcpy(&(*output)[0], av_frame_->extended_data, output->size()); + } else { + ConvertSamples( + codec_context_->sample_fmt, codec_context_->request_sample_fmt, + codec_context_->channel_layout, audio_header_.samples_per_second, + av_frame_->nb_samples, av_frame_->extended_data, &(*output)[0]); + } } else { // TODO: Consider fill it with silence. SB_LOG(ERROR) << "Decoded audio frame is empty."; @@ -127,7 +142,11 @@ stream_ended_ = false; } -int AudioDecoder::GetSamplesPerSecond() { +SbMediaAudioSampleType AudioDecoder::GetSampleType() const { + return sample_type_; +} + +int AudioDecoder::GetSamplesPerSecond() const { return audio_header_.samples_per_second; } @@ -144,7 +163,11 @@ codec_context_->codec_type = AVMEDIA_TYPE_AUDIO; codec_context_->codec_id = AV_CODEC_ID_AAC; // Request_sample_fmt is set by us, but sample_fmt is set by the decoder. - codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT; // interleaved float + if (sample_type_ == kSbMediaAudioSampleTypeInt16) { + codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16; + } else { + codec_context_->request_sample_fmt = AV_SAMPLE_FMT_FLT; + } codec_context_->channels = audio_header_.number_of_channels; codec_context_->sample_rate = audio_header_.samples_per_second;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h index 3eda447..c2a78db 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_audio_decoder.h
@@ -35,10 +35,11 @@ ~AudioDecoder() SB_OVERRIDE; void Decode(const InputBuffer& input_buffer, - std::vector<float>* output) SB_OVERRIDE; + std::vector<uint8_t>* output) SB_OVERRIDE; void WriteEndOfStream() SB_OVERRIDE; void Reset() SB_OVERRIDE; - int GetSamplesPerSecond() SB_OVERRIDE; + SbMediaAudioSampleType GetSampleType() const SB_OVERRIDE; + int GetSamplesPerSecond() const SB_OVERRIDE; bool is_valid() const { return codec_context_ != NULL; } @@ -46,6 +47,7 @@ void InitializeCodec(); void TeardownCodec(); + SbMediaAudioSampleType sample_type_; AVCodecContext* codec_context_; AVFrame* av_frame_;
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc index 0d04361..7b9efed 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -78,6 +78,8 @@ frame->height = codec_context->height; frame->format = codec_context->pix_fmt; + frame->reordered_opaque = codec_context->reordered_opaque; + return 0; } @@ -180,6 +182,7 @@ packet.data = const_cast<uint8_t*>(event.input_buffer.data()); packet.size = event.input_buffer.size(); packet.pts = event.input_buffer.pts(); + codec_context_->reordered_opaque = packet.pts; DecodePacket(&packet); host_->OnDecoderStatusUpdate(kNeedMoreInput, NULL); @@ -221,8 +224,9 @@ int pitch = AlignUp(av_frame_->width, kAlignment * 2); VideoFrame frame = VideoFrame::CreateYV12Frame( - av_frame_->width, av_frame_->height, pitch, av_frame_->pkt_pts, - av_frame_->data[0], av_frame_->data[1], av_frame_->data[2]); + av_frame_->width, av_frame_->height, pitch, + codec_context_->reordered_opaque, av_frame_->data[0], av_frame_->data[1], + av_frame_->data[2]); host_->OnDecoderStatusUpdate(kBufferFull, &frame); return true; }
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h index 8c5e99a..6fcc2f1 100644 --- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h +++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.h
@@ -34,7 +34,7 @@ typedef starboard::player::InputBuffer InputBuffer; typedef starboard::player::VideoFrame VideoFrame; - explicit VideoDecoder(SbMediaVideoCodec); + explicit VideoDecoder(SbMediaVideoCodec video_codec); ~VideoDecoder() SB_OVERRIDE; void SetHost(Host* host) SB_OVERRIDE;
diff --git a/src/starboard/shared/posix/time_get_monotonic_thread_now.cc b/src/starboard/shared/posix/time_get_monotonic_thread_now.cc new file mode 100644 index 0000000..7050050 --- /dev/null +++ b/src/starboard/shared/posix/time_get_monotonic_thread_now.cc
@@ -0,0 +1,30 @@ +// Copyright 2016 Google Inc. 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/time.h" + +#include <time.h> + +#include "starboard/log.h" +#include "starboard/shared/posix/time_internal.h" + +SbTimeMonotonic SbTimeGetMonotonicThreadNow() { + struct timespec time; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time) != 0) { + SB_NOTREACHED() << "clock_gettime(CLOCK_THREAD_CPUTIME_ID) failed."; + return 0; + } + + return FromTimespecDelta(&time); +}
diff --git a/src/starboard/shared/pthread/thread_create.cc b/src/starboard/shared/pthread/thread_create.cc index 8718ba4..f2ab13d 100644 --- a/src/starboard/shared/pthread/thread_create.cc +++ b/src/starboard/shared/pthread/thread_create.cc
@@ -61,19 +61,19 @@ int SbThreadPriorityToNice(SbThreadPriority priority) { switch (priority) { case kSbThreadPriorityLowest: - return 10; + return 19; case kSbThreadPriorityLow: - return 5; + return 18; case kSbThreadNoPriority: // Fall through on purpose to default to kThreadPriority_Normal. case kSbThreadPriorityNormal: - return -5; + return 10; case kSbThreadPriorityHigh: - return -15; + return 2; case kSbThreadPriorityHighest: - return -18; + return 1; case kSbThreadPriorityRealTime: - return -19; + return 0; default: SB_NOTREACHED(); return 0;
diff --git a/src/starboard/shared/starboard/application.cc b/src/starboard/shared/starboard/application.cc index 1adc590..96efbdb 100644 --- a/src/starboard/shared/starboard/application.cc +++ b/src/starboard/shared/starboard/application.cc
@@ -130,7 +130,7 @@ #if SB_HAS(PLAYER) && SB_IS(PLAYER_PUNCHED_OUT) void Application::HandleFrame(SbPlayer player, - const player::VideoFrame& frame, + const VideoFrame& frame, int x, int y, int width,
diff --git a/src/starboard/shared/starboard/application.h b/src/starboard/shared/starboard/application.h index 7328f2e..f8609ce 100644 --- a/src/starboard/shared/starboard/application.h +++ b/src/starboard/shared/starboard/application.h
@@ -38,6 +38,8 @@ // dispatching events to the Starboard event handler, SbEventHandle. class Application { public: + typedef player::VideoFrame VideoFrame; + // You can use a void(void *) function to signal that a state-transition event // has completed. typedef SbEventDataDestructor EventHandledCallback; @@ -200,7 +202,7 @@ // used when the application needs to composite video frames with punch-out // video manually (should be rare). Will be called from an external thread. void HandleFrame(SbPlayer player, - const player::VideoFrame& frame, + const VideoFrame& frame, int x, int y, int width, @@ -222,7 +224,7 @@ // Subclasses may override this method to accept video frames from the media // system. Will be called from an external thread. virtual void AcceptFrame(SbPlayer player, - const player::VideoFrame& frame, + const VideoFrame& frame, int x, int y, int width,
diff --git a/src/starboard/shared/starboard/memory_reporter_internal.h b/src/starboard/shared/starboard/memory_reporter_internal.h new file mode 100644 index 0000000..72b2bef --- /dev/null +++ b/src/starboard/shared/starboard/memory_reporter_internal.h
@@ -0,0 +1,40 @@ +// Copyright 2016 Google Inc. 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_SHARED_STARBOARD_MEMORY_REPORTER_INTERNAL_H_ +#define STARBOARD_SHARED_STARBOARD_MEMORY_REPORTER_INTERNAL_H_ + +#include "starboard/export.h" +#include "starboard/shared/internal_only.h" +#include "starboard/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Internal function used to track mapped memory. This is used internally by +// implementations of SbMemoryMap(). +SB_EXPORT void SbMemoryReporterReportMappedMemory(const void* memory, + size_t size); + +// Internal function used to track mapped memory. This is used internally by +// implementations of SbMemoryUnmap(). +SB_EXPORT void SbMemoryReporterReportUnmappedMemory(const void* memory, + size_t size); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // STARBOARD_SHARED_STARBOARD_MEMORY_REPORTER_INTERNAL_H_
diff --git a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h index b8bdafe..cb1850c 100644 --- a/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h +++ b/src/starboard/shared/starboard/player/filter/audio_decoder_internal.h
@@ -20,6 +20,7 @@ #include "starboard/media.h" #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/player/input_buffer_internal.h" +#include "starboard/types.h" namespace starboard { namespace shared { @@ -35,7 +36,7 @@ // Decode the encoded audio data stored in |input_buffer| and store the // result in |output|. virtual void Decode(const InputBuffer& input_buffer, - std::vector<float>* output) = 0; + std::vector<uint8_t>* output) = 0; // Note that there won't be more input data unless Reset() is called. virtual void WriteEndOfStream() = 0; // Clear any cached buffer of the codec and reset the state of the codec. @@ -43,10 +44,13 @@ // data from previous buffers are cleared. virtual void Reset() = 0; + // Return the sample type of the decoded pcm data. + virtual SbMediaAudioSampleType GetSampleType() const = 0; + // Return the sample rate of the incoming audio. This should be used by the // audio renderer as the sample rate of the underlying audio stream can be // different than the sample rate stored in the meta data. - virtual int GetSamplesPerSecond() = 0; + virtual int GetSamplesPerSecond() const = 0; // Individual implementation has to implement this function to create an // audio decoder.
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc index 3a53533..105e23f 100644 --- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.cc
@@ -33,10 +33,13 @@ AudioRenderer::AudioRenderer(scoped_ptr<AudioDecoder> decoder, const SbMediaAudioHeader& audio_header) : channels_(audio_header.number_of_channels), + bytes_per_frame_( + (decoder->GetSampleType() == kSbMediaAudioSampleTypeInt16 ? 2 : 4) * + channels_), paused_(true), seeking_(false), seeking_to_pts_(0), - frame_buffer_(kMaxCachedFrames * audio_header.number_of_channels), + frame_buffer_(kMaxCachedFrames * bytes_per_frame_), frames_in_buffer_(0), offset_in_frames_(0), frames_consumed_(0), @@ -61,7 +64,7 @@ } SbMediaTime input_pts = input_buffer.pts(); - std::vector<float> decoded_audio; + std::vector<uint8_t> decoded_audio; decoder_->Decode(input_buffer, &decoded_audio); if (decoded_audio.empty()) { SB_DLOG(ERROR) << "decoded_audio contains no frames."; @@ -76,9 +79,9 @@ } } - AppendFrames(&decoded_audio[0], decoded_audio.size() / channels_); + AppendFrames(&decoded_audio[0], decoded_audio.size() / bytes_per_frame_); - if (seeking_ && frame_buffer_.size() > kPrerollFrames * channels_) { + if (seeking_ && frame_buffer_.size() > kPrerollFrames * bytes_per_frame_) { seeking_ = false; } } @@ -91,7 +94,7 @@ SbAudioSinkGetNearestSupportedSampleFrequency(sample_rate)); // TODO: Handle sink creation failure. audio_sink_ = SbAudioSinkCreate( - channels_, sample_rate, kSbMediaAudioSampleTypeFloat32, + channels_, sample_rate, decoder_->GetSampleType(), kSbMediaAudioFrameStorageTypeInterleaved, reinterpret_cast<SbAudioSinkFrameBuffers>(frame_buffers_), kMaxCachedFrames, &AudioRenderer::UpdateSourceStatusFunc, @@ -220,23 +223,23 @@ frames_consumed_ += frames_consumed; } -void AudioRenderer::AppendFrames(const float* source_buffer, +void AudioRenderer::AppendFrames(const uint8_t* source_buffer, int frames_to_append) { SB_DCHECK(frames_in_buffer_ + frames_to_append <= kMaxCachedFrames); int offset_to_append = (offset_in_frames_ + frames_in_buffer_) % kMaxCachedFrames; if (frames_to_append > kMaxCachedFrames - offset_to_append) { - SbMemoryCopy( - &frame_buffer_[offset_to_append * channels_], source_buffer, - (kMaxCachedFrames - offset_to_append) * sizeof(float) * channels_); - source_buffer += (kMaxCachedFrames - offset_to_append) * channels_; + SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_], + source_buffer, + (kMaxCachedFrames - offset_to_append) * bytes_per_frame_); + source_buffer += (kMaxCachedFrames - offset_to_append) * bytes_per_frame_; frames_to_append -= kMaxCachedFrames - offset_to_append; frames_in_buffer_ += kMaxCachedFrames - offset_to_append; offset_to_append = 0; } - SbMemoryCopy(&frame_buffer_[offset_to_append * channels_], source_buffer, - frames_to_append * sizeof(float) * channels_); + SbMemoryCopy(&frame_buffer_[offset_to_append * bytes_per_frame_], + source_buffer, frames_to_append * bytes_per_frame_); frames_in_buffer_ += frames_to_append; }
diff --git a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h index c85f1fe..e14457c 100644 --- a/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h +++ b/src/starboard/shared/starboard/player/filter/audio_renderer_internal.h
@@ -25,6 +25,7 @@ #include "starboard/shared/internal_only.h" #include "starboard/shared/starboard/player/filter/audio_decoder_internal.h" #include "starboard/shared/starboard/player/input_buffer_internal.h" +#include "starboard/types.h" namespace starboard { namespace shared { @@ -76,17 +77,18 @@ bool* is_eos_reached); void ConsumeFrames(int frames_consumed); - void AppendFrames(const float* source_buffer, int frames_to_append); + void AppendFrames(const uint8_t* source_buffer, int frames_to_append); const int channels_; + const int bytes_per_frame_; Mutex mutex_; bool paused_; bool seeking_; SbMediaTime seeking_to_pts_; - std::vector<float> frame_buffer_; - float* frame_buffers_[1]; + std::vector<uint8_t> frame_buffer_; + uint8_t* frame_buffers_[1]; int frames_in_buffer_; int offset_in_frames_;
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc index 3898af5..65ee800 100644 --- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc +++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.cc
@@ -33,19 +33,24 @@ } void VideoRenderer::WriteSample(const InputBuffer& input_buffer) { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); if (end_of_stream_written_) { SB_LOG(ERROR) << "Appending video sample at " << input_buffer.pts() << " after EOS reached."; return; } - need_more_input_ = false; + + { + ScopedLock lock(mutex_); + need_more_input_ = false; + } + decoder_->WriteInputBuffer(input_buffer); } void VideoRenderer::WriteEndOfStream() { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); SB_LOG_IF(WARNING, end_of_stream_written_) << "Try to write EOS after EOS is reached"; @@ -57,6 +62,7 @@ } void VideoRenderer::Seek(SbMediaTime seek_to_pts) { + SB_DCHECK(thread_checker_.CalledOnValidThread()); SB_DCHECK(seek_to_pts >= 0); decoder_->Reset(); @@ -74,7 +80,7 @@ } const VideoFrame& VideoRenderer::GetCurrentFrame(SbMediaTime media_time) { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); if (frames_.empty()) { return seeking_frame_; @@ -89,18 +95,18 @@ } bool VideoRenderer::IsEndOfStreamPlayed() const { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); return end_of_stream_written_ && frames_.size() <= 1; } bool VideoRenderer::CanAcceptMoreData() const { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); return frames_.size() < kMaxCachedFrames && !end_of_stream_written_ && need_more_input_; } bool VideoRenderer::IsSeekingInProgress() const { - ScopedLock lock(mutex_); + SB_DCHECK(thread_checker_.CalledOnValidThread()); return seeking_; }
diff --git a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h index 43f899b..8450e59 100644 --- a/src/starboard/shared/starboard/player/filter/video_renderer_internal.h +++ b/src/starboard/shared/starboard/player/filter/video_renderer_internal.h
@@ -25,6 +25,7 @@ #include "starboard/shared/starboard/player/filter/video_decoder_internal.h" #include "starboard/shared/starboard/player/input_buffer_internal.h" #include "starboard/shared/starboard/player/video_frame_internal.h" +#include "starboard/shared/starboard/thread_checker.h" namespace starboard { namespace shared { @@ -66,6 +67,7 @@ void OnDecoderStatusUpdate(VideoDecoder::Status status, VideoFrame* frame) SB_OVERRIDE; + ThreadChecker thread_checker_; ::starboard::Mutex mutex_; bool seeking_;
diff --git a/src/starboard/shared/starboard/player/player_create.cc b/src/starboard/shared/starboard/player/player_create.cc index 5581ca8..c09f526 100644 --- a/src/starboard/shared/starboard/player/player_create.cc +++ b/src/starboard/shared/starboard/player/player_create.cc
@@ -15,6 +15,7 @@ #include "starboard/player.h" #include "starboard/configuration.h" +#include "starboard/decode_target.h" #include "starboard/log.h" #include "starboard/shared/starboard/player/filter/filter_based_player_worker_handler.h" #include "starboard/shared/starboard/player/player_internal.h" @@ -33,8 +34,16 @@ SbPlayerDeallocateSampleFunc sample_deallocate_func, SbPlayerDecoderStatusFunc decoder_status_func, SbPlayerStatusFunc player_status_func, - void* context) { + void* context +#if SB_VERSION(3) + , + SbDecodeTargetProvider* provider +#endif + ) { SB_UNREFERENCED_PARAMETER(window); +#if SB_VERSION(3) + SB_UNREFERENCED_PARAMETER(provider); +#endif if (audio_codec != kSbMediaAudioCodecAac) { SB_LOG(ERROR) << "Unsupported audio codec " << audio_codec;
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc index d1a0099..793066d 100644 --- a/src/starboard/shared/starboard/player/video_frame_internal.cc +++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -183,6 +183,8 @@ int y_plane_size_in_bytes = height * pitch_in_bytes; int uv_plane_size_in_bytes = uv_height * uv_pitch_in_bytes; + frame.pixel_buffer_.reserve(y_plane_size_in_bytes + + uv_plane_size_in_bytes * 2); frame.pixel_buffer_.assign(y, y + y_plane_size_in_bytes); frame.pixel_buffer_.insert(frame.pixel_buffer_.end(), u, u + uv_plane_size_in_bytes);
diff --git a/src/starboard/shared/stub/decode_target_is_opaque.cc b/src/starboard/shared/stub/decode_target_is_opaque.cc new file mode 100644 index 0000000..1798975 --- /dev/null +++ b/src/starboard/shared/stub/decode_target_is_opaque.cc
@@ -0,0 +1,24 @@ +// Copyright 2016 Google Inc. 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/configuration.h" +#include "starboard/decode_target.h" + +#if !(SB_VERSION(3) && SB_HAS(GRAPHICS)) +#error "SbDecodeTargetIsOpaque requires SB_VERSION(3) and SB_HAS(GRAPHICS)." +#endif + +bool SbDecodeTargetIsOpaque(SbDecodeTarget decode_target) { + return false; +}
diff --git a/src/starboard/shared/stub/image_decode.cc b/src/starboard/shared/stub/image_decode.cc new file mode 100644 index 0000000..ca1ff97 --- /dev/null +++ b/src/starboard/shared/stub/image_decode.cc
@@ -0,0 +1,28 @@ +// Copyright 2016 Google Inc. 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/configuration.h" +#include "starboard/image.h" + +#if !(SB_VERSION(3) && SB_HAS(GRAPHICS)) +#error "SbImageDecode requires SB_VERSION(3) and SB_HAS(GRAPHICS)." +#endif + +SbDecodeTarget SbImageDecode(SbDecodeTargetProvider* provider, + void* data, + int data_size, + const char* mime_type, + SbDecodeTargetFormat format) { + return kSbDecodeTargetInvalid; +}
diff --git a/src/starboard/shared/stub/image_is_decode_supported.cc b/src/starboard/shared/stub/image_is_decode_supported.cc new file mode 100644 index 0000000..2b0b367 --- /dev/null +++ b/src/starboard/shared/stub/image_is_decode_supported.cc
@@ -0,0 +1,25 @@ +// Copyright 2016 Google Inc. 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/configuration.h" +#include "starboard/image.h" + +#if !(SB_VERSION(3) && SB_HAS(GRAPHICS)) +#error "SbImageIsDecodeSupported requires SB_VERSION(3) and SB_HAS(GRAPHICS)." +#endif + +bool SbImageIsDecodeSupported(const char* mime_type, + SbDecodeTargetFormat format) { + return false; +}
diff --git a/src/starboard/shared/stub/microphone_get_available.cc b/src/starboard/shared/stub/microphone_get_available.cc index 126149d..5fdb309 100644 --- a/src/starboard/shared/stub/microphone_get_available.cc +++ b/src/starboard/shared/stub/microphone_get_available.cc
@@ -18,7 +18,7 @@ int SbMicrophoneGetAvailable(SbMicrophoneInfo* out_info_array, int info_array_size) { - return -1; + return 0; } #endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
diff --git a/src/starboard/shared/stub/microphone_read.cc b/src/starboard/shared/stub/microphone_read.cc index 430195f..2965831 100644 --- a/src/starboard/shared/stub/microphone_read.cc +++ b/src/starboard/shared/stub/microphone_read.cc
@@ -19,7 +19,7 @@ int SbMicrophoneRead(SbMicrophone microphone, void* out_audio_data, int audio_data_size) { - return 0; + return -1; } #endif // SB_HAS(MICROPHONE) && SB_VERSION(2)
diff --git a/src/starboard/shared/stub/player_create.cc b/src/starboard/shared/stub/player_create.cc index c9cb1e8..639315c 100644 --- a/src/starboard/shared/stub/player_create.cc +++ b/src/starboard/shared/stub/player_create.cc
@@ -14,6 +14,10 @@ #include "starboard/player.h" +#if !SB_HAS(PLAYER) +#error "SbPlayerCreate requires SB_HAS(PLAYER)." +#endif + SbPlayer SbPlayerCreate(SbWindow /*window*/, SbMediaVideoCodec /*video_codec*/, SbMediaAudioCodec /*audio_codec*/, @@ -23,6 +27,11 @@ SbPlayerDeallocateSampleFunc /*sample_deallocate_func*/, SbPlayerDecoderStatusFunc /*decoder_status_func*/, SbPlayerStatusFunc /*player_status_func*/, - void* /*context*/) { + void* /*context*/ +#if SB_VERSION(3) + , + SbDecodeTargetProvider* /*provider*/ +#endif + ) { return kSbPlayerInvalid; }
diff --git a/src/starboard/shared/stub/speech_synthesis_cancel.cc b/src/starboard/shared/stub/speech_synthesis_cancel.cc new file mode 100644 index 0000000..5a4cf19 --- /dev/null +++ b/src/starboard/shared/stub/speech_synthesis_cancel.cc
@@ -0,0 +1,22 @@ +// Copyright 2016 Google Inc. 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/speech_synthesis.h" + +#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) +#error If speech synthesis not enabled on this platform, please exclude it\ + from the build +#endif + +void SbSpeechSynthesisCancel() {}
diff --git a/src/starboard/shared/stub/speech_synthesis_set_language.cc b/src/starboard/shared/stub/speech_synthesis_set_language.cc new file mode 100644 index 0000000..383239b --- /dev/null +++ b/src/starboard/shared/stub/speech_synthesis_set_language.cc
@@ -0,0 +1,24 @@ +// Copyright 2016 Google Inc. 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/speech_synthesis.h" + +#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) +#error If speech synthesis not enabled on this platform, please exclude it\ + from the build +#endif + +bool SbSpeechSynthesisSetLanguage(const char* lang) { + return false; +}
diff --git a/src/starboard/shared/stub/speech_synthesis_speak.cc b/src/starboard/shared/stub/speech_synthesis_speak.cc new file mode 100644 index 0000000..53cd5f5 --- /dev/null +++ b/src/starboard/shared/stub/speech_synthesis_speak.cc
@@ -0,0 +1,22 @@ +// Copyright 2016 Google Inc. 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/speech_synthesis.h" + +#if !SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) +#error If speech synthesis not enabled on this platform, please exclude it\ + from the build +#endif + +void SbSpeechSynthesisSpeak(const char* text) {}
diff --git a/src/starboard/shared/stub/time_get_monotonic_thread_now.cc b/src/starboard/shared/stub/time_get_monotonic_thread_now.cc new file mode 100644 index 0000000..feee2b6 --- /dev/null +++ b/src/starboard/shared/stub/time_get_monotonic_thread_now.cc
@@ -0,0 +1,19 @@ +// Copyright 2016 Google Inc. 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/time.h" + +SbTimeMonotonic SbTimeGetMonotonicThreadNow() { + return SbTimeMonotonic(0); +}
diff --git a/src/starboard/socket.h b/src/starboard/socket.h index b61bb3d..d2893ba 100644 --- a/src/starboard/socket.h +++ b/src/starboard/socket.h
@@ -12,14 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Starboard socket I/O. Starboard supports IPv4 and IPv6, TCP and UDP, server -// and client sockets. Some platforms may not support IPv6, some may not support -// listening sockets, and some may not support any kind of sockets at all (or -// only allow them in debug builds). Starboard ONLY supports non-blocking socket -// I/O, so all sockets are non-blocking at creation time. -// Note that, on some platforms, API calls on one end of a socket connection may -// not be instantaneously aware of manipulations on the socket at the other end -// of the connection, thus requiring use of either an SbSocketWaiter or +// Module Overview: Starboard Socket module +// +// Defines Starboard socket I/O functions. Starboard supports IPv4 and IPv6, +// TCP and UDP, server and client sockets. Some platforms may not support IPv6, +// some may not support listening sockets, and some may not support any kind +// of sockets at all (or only allow them in debug builds). +// +// Starboard ONLY supports non-blocking socket I/O, so all sockets are +// non-blocking at creation time. +// +// Note that, on some platforms, API calls on one end of a socket connection +// may not be instantaneously aware of manipulations on the socket at the other +// end of the connection, thus requiring use of either an SbSocketWaiter or // spin-polling. // // TODO: For platforms that do not support sockets at all, they must @@ -48,9 +53,11 @@ // A handle to a socket. typedef SbSocketPrivate* SbSocket; -// Enumeration of all Starboard socket operation results. +// Enumeration of all Starboard socket operation results. Despite the enum +// name, note that the value actually describes the outcome of an operation, +// which is not always an error. typedef enum SbSocketError { - // The operation succeeded. Yep. + // The operation succeeded. kSbSocketOk = 0, // The operation is blocked on I/O. Either try again "later," or be very @@ -128,161 +135,249 @@ } // Creates a new non-blocking socket for protocol |protocol| using address -// family |address_type|. Returns the newly created handle, if successful, or -// kSbSocketInvalid, if not. If unsuccessful, sets the last system error -// appropriately. +// family |address_type|. +// +// - If successful, this function returns the newly created handle. +// - If unsuccessful, this function returns |kSbSocketInvalid| and also sets +// the last system error appropriately. +// +// |address_type|: The type of IP address to use for the socket. +// |protocol|: The protocol to use for the socket. SB_EXPORT SbSocket SbSocketCreate(SbSocketAddressType address_type, SbSocketProtocol protocol); -// Destroys |socket|. Flushes the socket, closes any connection that may be -// active on |socket|, and reclaims any resources associated with |socket|, -// including any registration with an SbSocketWaiter. Returns whether the -// destruction was successful. Even if this function returns false, you -// shouldn't be able to use the socket anymore. +// Destroys the |socket| by flushing it, closing any connection that may be +// active on it, and reclaiming any resources associated with it, including +// any registration with an |SbSocketWaiter|. +// +// The return value indicates whether the destruction was successful. However, +// even if this function returns |false|, you should not be able to use the +// socket any more. +// +// |socket|: The SbSocket to be destroyed. SB_EXPORT bool SbSocketDestroy(SbSocket socket); // Opens a connection of |socket|'s type to the host and port specified by -// |address|. Sets and returns the socket error if unable to bind to -// |local_address|. +// |address|. This function sets and returns the socket error if it is unable +// to connect to |address|. (It returns |kSbSocketOk| if it creates the +// connection successfully.) +// +// |socket|: The type of connection that should be opened. +// |address|: The host and port to which the socket should connect. SB_EXPORT SbSocketError SbSocketConnect(SbSocket socket, const SbSocketAddress* address); // Binds |socket| to a specific local interface and port specified by -// |local_address|, which must not be NULL. Port 0 means choose a port for me -// and IP address 0.0.0.0 means bind to all interfaces. Sets and returns the -// socket error if unable to bind to |local_address|. +// |local_address|. This function sets and returns the socket error if it +// is unable to bind to |local_address|. +// +// |socket|: The SbSocket to be bound to the local interface. +// |local_address|: The local address to which the socket is to be bound. +// This value must not be |NULL|. +// - Setting the local address to port |0| (or not specifying a port) indicates +// that the function should choose a port for you. +// - Setting the IP address to |0.0.0.0| means that the socket should be bound +// to all interfaces. SB_EXPORT SbSocketError SbSocketBind(SbSocket socket, const SbSocketAddress* local_address); -// Causes |socket| to listen on |local_address|. Sets and returns the socket -// error if unable to listen for some reason. +// Causes |socket| to listen on the local address that |socket| was previously +// bound to by SbSocketBind. This function sets and returns the socket error if +// it is unable to listen for some reason. (It returns |kSbSocketOk| if it +// creates the connection successfully.) +// +// |socket|: The SbSocket on which the function operates. SB_EXPORT SbSocketError SbSocketListen(SbSocket socket); -// Accepts a pending connection on |socket|, returning a new SbSocket -// representing that connection. Sets the error on |socket| and returns -// kSbSocketInvalid if unable to accept a new connection. +// Accepts a pending connection on |socket| and returns a new SbSocket +// representing that connection. This function sets the error on |socket| +// and returns |kSbSocketInvalid| if it is unable to accept a new connection. +// +// |socket|: The SbSocket that is accepting a pending connection. SB_EXPORT SbSocket SbSocketAccept(SbSocket socket); -// Returns whether |socket| is connected to anything. Invalid sockets are not +// Indicates whether |socket| is connected to anything. Invalid sockets are not // connected. +// +// |socket|: The SbSocket to be checked. SB_EXPORT bool SbSocketIsConnected(SbSocket socket); -// Returns whether |socket| is connected to anything, and whether it is +// Returns whether |socket| is connected to anything, and, if so, whether it is // receiving any data. +// +// |socket|: The SbSocket to be checked. SB_EXPORT bool SbSocketIsConnectedAndIdle(SbSocket socket); -// Returns the last error set on |socket|. If |socket| is not valid, always -// returns kSbSocketErrorFailed. +// Returns the last error set on |socket|. If |socket| is not valid, this +// function returns |kSbSocketErrorFailed|. +// +// |socket|: The SbSocket that the last error is returned for. SB_EXPORT SbSocketError SbSocketGetLastError(SbSocket socket); -// Clears the last error set on |socket|. Returns whether the socket error was -// cleared. +// Clears the last error set on |socket|. The return value indicates whether +// the socket error was cleared. SB_EXPORT bool SbSocketClearLastError(SbSocket socket); // Gets the address that this socket is bound to locally, if the socket is -// connected. Returns whether getting the address was successful. +// connected. The return value indicates whether the address was retrieved +// successfully. +// +// |socket|: The SbSocket for which the local address is retrieved. +// |out_address|: The SbSocket's local address. SB_EXPORT bool SbSocketGetLocalAddress(SbSocket socket, SbSocketAddress* out_address); -// Reads up to |data_size| bytes from |socket| into |out_data|. Also places the -// source address of the packet in |out_source|, if out_source is not NULL. -// Returns the number of bytes read, or a negative number on error. -// SbSocketGetLastError can provide the precise error encountered. +// Reads up to |data_size| bytes from |socket| into |out_data| and places the +// source address of the packet in |out_source| if out_source is not NULL. +// Returns the number of bytes read, or a negative number if there is an error, +// in which case SbSocketGetLastError can provide the precise error encountered. // -// Note that this function is NOT specified to (but MAY) make a best effort to -// read all data on all platforms, it just reads however many bytes are -// available conveniently. Can be run in a loop until pending to make it a -// best-effort read (but still only up to not blocking, unless you want to -// spin). +// Note that this function is NOT specified to make a best effort to read all +// data on all platforms, but it MAY still do so. It is specified to read +// however many bytes are available conveniently, meaning that it should avoid +// blocking until there is data. It can be run in a loop until +// SbSocketGetLastError returns |kSbSocketPending| to make it a best-effort +// read (but still only up to not blocking, unless you want to spin). // -// The primary use of |out_source| is for receiving datagram packets from +// The primary use of |out_source| is to receive datagram packets from // multiple sources on a UDP server socket. TCP has two endpoints connected // persistently, so the address is unnecessary, but allowed. +// +// |socket|: The SbSocket from which data is read. +// |out_data|: The data read from the socket. +// |data_size|: The number of bytes to read. +// |out_source|: The source address of the packet. SB_EXPORT int SbSocketReceiveFrom(SbSocket socket, char* out_data, int data_size, SbSocketAddress* out_source); // Writes up to |data_size| bytes of |data| to |destination| via -// |socket|. Returns the number of bytes written, or a negative number on -// error. SbSocketGetLastError can provide the precise error encountered. -// |destination| must be NULL for TCP connections, which can only have a single -// endpoint. +// |socket|. Returns the number of bytes written, or a negative number if +// there is an error, in which case |SbSocketGetLastError| can provide the +// precise error encountered. // -// Note that this function is NOT specified to (but MAY) make a best effort to -// write all data on all platforms, it just writes however many bytes that are -// conveniently. Can be run in a loop until pending to make it a best-effort -// write (but still only up to not blocking, unless you want to spin). +// Note that this function is NOT specified to make a best effort to write all +// data on all platforms, but it MAY still do so. It is specified to write +// however many bytes are available conveniently. It can be run in a loop +// until SbSocketGetLastError returns |kSbSocketPending| to make it a +// best-effort write (but still only up to not blocking, unless you want to +// spin). // -// The primary use of |destination| is to send datagram packets, which can go -// out to multiple sources from a single UDP server socket. TCP has two -// endpoints connected persistently, so |destination| cannot be set when sending -// to a TCP socket -- it will cause an error. +// |socket|: The SbSocket to use to write data. +// |data|: The data read from the socket. +// |data_size|: The number of bytes of |data| to write. +// |destination|: The location to which data is written. This value must be +// |NULL| for TCP connections, which can only have a single endpoint. +// +// The primary use of |destination| is to send datagram packets, which can +// go out to multiple sources from a single UDP server socket. TCP has two +// endpoints connected persistently, so setting |destination| when sending +// to a TCP socket will cause an error. SB_EXPORT int SbSocketSendTo(SbSocket socket, const char* data, int data_size, const SbSocketAddress* destination); -// Sets the SO_BROADCAST, or equivalent, option to |value| on |socket|. Returns -// whether the option was actually set. +// Sets the |SO_BROADCAST|, or equivalent, option to |value| on |socket|. The +// return value indicates whether the option was actually set. // -// This option is only meaningful for UDP sockets, and will allow the socket to +// This option is only meaningful for UDP sockets and allows the socket to // send to the broadcast address. +// +// |socket|: The SbSocket for which the option is set. +// |value|: The new value for the option. SB_EXPORT bool SbSocketSetBroadcast(SbSocket socket, bool value); -// Sets the SO_REUSEADDR, or equivalent, option to |value| on |socket|. Returns -// whether the option was actually set. +// Sets the |SO_REUSEADDR|, or equivalent, option to |value| on |socket|. +// The return value indicates whether the option was actually set. // // This option allows a bound address to be reused if a socket isn't actively // bound to it. +// +// |socket|: The SbSocket for which the option is set. +// |value|: The new value for the option. SB_EXPORT bool SbSocketSetReuseAddress(SbSocket socket, bool value); -// Sets SO_RCVBUF, or equivalent, option to |size| on |socket|. +// Sets the |SO_RCVBUF|, or equivalent, option to |size| on |socket|. The +// return value indicates whether the option was actually set. +// +// |socket|: The SbSocket for which the option is set. +// |size|: The value for the option. SB_EXPORT bool SbSocketSetReceiveBufferSize(SbSocket socket, int32_t size); -// Sets SO_SNDBUF, or equivalent, option to |size| on |socket|. +// Sets the |SO_SNDBUF|, or equivalent, option to |size| on |socket|. The +// return value indicates whether the option was actually set. +// +// |socket|: The SbSocket for which the option is set. +// |size|: The value for the option. SB_EXPORT bool SbSocketSetSendBufferSize(SbSocket socket, int32_t size); -// Sets the SO_KEEPALIVE, or equivalent, option to |value| on |socket|. If -// |value| is true, then |period| specifies the minimum time (SbTime is always -// in microseconds) between keep-alive packets, otherwise |period| is -// ignored. Returns whether the option was actually set. +// Sets the |SO_KEEPALIVE|, or equivalent, option to |value| on |socket|. The +// return value indicates whether the option was actually set. +// +// |socket|: The SbSocket for which the option is set. +// |value|: If set to |true|, then |period| specifies the minimum time +// (SbTime) is always in microseconds) between keep-alive packets. If +// set to |false|, |period| is ignored. +// |period|: The time between keep-alive packets. This value is only relevant +// if |value| is |true|. SB_EXPORT bool SbSocketSetTcpKeepAlive(SbSocket socket, bool value, SbTime period); -// Sets the TCP_NODELAY, or equivalent, option to |value| on |socket|. Returns -// whether the option was actually set. +// Sets the |TCP_NODELAY|, or equivalent, option to |value| on |socket|. The +// return value indicates whether the option was actually set. // -// This disables the Nagle algorithm for reducing the number of packets sent -// when converting from a stream to packets. Disabling Nagle will generally put -// the data for each Send call into its own packet, but does not guarantee that -// behavior. +// This function disables the Nagle algorithm for reducing the number of +// packets sent when converting from a stream to packets. Disabling Nagle +// generally puts the data for each Send call into its own packet, but does +// not guarantee that behavior. +// +// |socket|: The SbSocket for which the option is set. +// |value|: Indicates whether the Nagle algorithm should be disabled +// (|value|=|true|). SB_EXPORT bool SbSocketSetTcpNoDelay(SbSocket socket, bool value); -// Sets SO_WINSCALE, or equivalent, option to |value| on |socket|. +// Sets the |SO_WINSCALE|, or equivalent, option to |value| on |socket|. The +// return value indicates whether the option was actually set. +// +// |socket|: The SbSocket for which the option is set. +// |value|: The value for the option. SB_EXPORT bool SbSocketSetTcpWindowScaling(SbSocket socket, bool value); // Joins |socket| to an IP multicast group identified by |address|. The -// equivalent of IP_ADD_MEMBERSHIP. +// equivalent of IP_ADD_MEMBERSHIP. The return value indicates whether the +// socket was joined to the group successfully. +// +// |socket|: The SbSocket to be joined to the IP multicast group. +// |address|: The location of the IP multicast group. SB_EXPORT bool SbSocketJoinMulticastGroup(SbSocket socket, const SbSocketAddress* address); -// Gets the address of the local IPv4 network interface. Does not include -// loopback (or IPv6) addresses. +// Gets the address of the local IPv4 network interface. The return value +// indicates whether the address was retrieved successfully. +// +// |out_address|: The retrieved address. The address does not include loopback +// (or IPv6) addresses. SB_EXPORT bool SbSocketGetLocalInterfaceAddress(SbSocketAddress* out_address); -// Synchronously resolves |hostname| into an SbSocketResolution, filtered by -// |filters|, which is a mask of SbSocketResolveFilter values. If no IP address -// family filter is specified in |filters|, all address families will be -// included, but if one is specified, only that address familiy will be -// included. Unrecognized filter bits are ignored. Returns NULL if unable to -// resolve |hostname|. The returned SbSocketResolution must be freed with -// SbSocketFreeResolution. +// Synchronously resolves |hostname| into the returned SbSocketResolution, +// which must be freed with SbSocketFreeResolution. The function returns +// |NULL| if it is unable to resolve |hostname|. +// +// |hostname|: The hostname to be resolved. +// |filters|: A mask of SbSocketResolveFilter values used to filter the +// resolution. If |filters| does not specify an IP address family filter, +// all address families are included. However, if one IP address family filter +// is specified, only that address family is included. The function ignores +// unrecognized filter bits. SB_EXPORT SbSocketResolution* SbSocketResolve(const char* hostname, int filters); // Frees a resolution allocated by SbSocketResolve. +// +// |resolution|: The resolution to be freed. SB_EXPORT void SbSocketFreeResolution(SbSocketResolution* resolution); #ifdef __cplusplus
diff --git a/src/starboard/socket_waiter.h b/src/starboard/socket_waiter.h index c964d6f..51fce0f 100644 --- a/src/starboard/socket_waiter.h +++ b/src/starboard/socket_waiter.h
@@ -12,17 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Allows a thread to wait on many sockets at once. The standard usage pattern -// would be for a single I/O thread to: -// 1. Create its own SbSocketWaiter. -// 2. Wait on the SbSocketWaiter, indefinitely if no scheduled tasks, or timed -// if there are scheduled future tasks. -// 3. While waiting, the SbSocketWaiter will call back to service ready -// SbSockets. -// 4. Wake up, if signaled to do so. -// 5. If ready to exit, go to 7. -// 6. Add and remove SbSockets to and from the SbSocketWaiter, and go to 2. -// 7. Destroy its SbSocketWaiter and exit. +// Module Overview: Starboard Socket Waiter module +// +// Allows a thread to wait on many sockets at once. The standard +// usage pattern would be for a single I/O thread to: +// 1. Create its own SbSocketWaiter. +// 2. Wait on the SbSocketWaiter, indefinitely if no scheduled tasks, or timed +// if there are scheduled future tasks. +// 3. While waiting, the SbSocketWaiter will call back to service ready +// SbSockets. +// 4. Wake up, if signaled to do so. +// 5. If ready to exit, go to 7. +// 6. Add and remove SbSockets to and from the SbSocketWaiter, and go to 2. +// 7. Destroy its SbSocketWaiter and exit. // // If another thread wants to queue immediate or schedule future work on the I/O // thread, it needs to call SbSocketWaiterWakeUp() on the SbSocketWaiter after @@ -41,7 +43,7 @@ extern "C" { #endif -// Private structure representing a waiter which can wait for many sockets at +// Private structure representing a waiter that can wait for many sockets at // once on a single thread. typedef struct SbSocketWaiterPrivate SbSocketWaiterPrivate; @@ -86,39 +88,50 @@ return watcher != kSbSocketWaiterInvalid; } -// Creates a new socket waiter for the current thread. A socket waiter should -// have a 1-1 relationship to a thread that wants to wait for socket -// activity. There is no reason to create multiple waiters for a single thread, -// because the thread can only block on one waiter at a time. The results of two -// threads waiting on the same waiter is undefined (and definitely won't work, -// so don't do it). SbSocketWaiters are not thread-safe, except for the -// SbSocketWaiterWakeUp() function, and don't expect to be modified -// concurrently. +// Creates and returns a new SbSocketWaiter for the current thread. A socket +// waiter should have a 1:1 relationship to a thread that wants to wait for +// socket activity. There is no reason to create multiple waiters for a single +// thread because the thread can only block on one waiter at a time. + +// The results of two threads waiting on the same waiter is undefined and will +// not work. Except for the SbSocketWaiterWakeUp() function, SbSocketWaiters +// are not thread-safe and don't expect to be modified concurrently. SB_EXPORT SbSocketWaiter SbSocketWaiterCreate(); -// Destroys a socket waiter. This will also remove all sockets still registered -// by way of SbSocketWaiterAdd. This function may be called on any thread, but -// only if there is no worry of concurrent access to the waiter. +// Destroys |waiter| and removes all sockets still registered by way of +// SbSocketWaiterAdd. This function may be called on any thread as long as +// there is no risk of concurrent access to the waiter. +// +// |waiter|: The SbSocketWaiter to be destroyed. SB_EXPORT bool SbSocketWaiterDestroy(SbSocketWaiter waiter); // Adds a new socket to be waited on by the |waiter| with a bitfield of -// |interests|. When the event fires, |callback| will be called with |waiter|, -// |socket|, and |context|, as well as a bitfield of which interests the socket -// is actually ready for. This should only be called on the thread that +// |interests|. This function should only be called on the thread that // waits on this waiter. // // If |socket| is already registered with this or another waiter, the function -// will do nothing and return false. The client must remove the socket and then +// does nothing and returns |false|. The client must remove the socket and then // add it back with the new |interests|. // -// If |socket| is already ready for one or more operations set in the +// If |socket| is already ready for one or more of the operations set in the // |interests| mask, then the callback will be called on the next call to -// SbSocketWaiterWait() or SbSocketWaiterWaitTimed(). +// either SbSocketWaiterWait() or SbSocketWaiterWaitTimed(). // -// If |persistent| is true, then |socket| will stay registered with |waiter| -// until SbSocketWaiterRemove() is called with |waiter| and |socket|. Otherwise, -// |socket| will be removed after the next call to |callback|, even if not all -// registered |interests| became ready. +// |waiter|: An SbSocketWaiter that waits on the socket for the specified set +// of operations (|interests|). +// |socket|: The SbSocket on which the waiter waits. +// |context|: +// |callback|: The function that is called when the event fires. The |waiter|, +// |socket|, |context| are all passed to the callback, along with a bitfield +// of |interests| that the socket is actually ready for. +// |interests|: A bitfield that identifies operations for which the socket is +// waiting. +// |persistent|: Identifies the procedure that will be followed for removing +// the socket: +// - If |persistent| is |true|, then |socket| stays registered with |waiter| +// until SbSocketWaiterRemove() is called with |waiter| and |socket|. +// - If |persistent| is |false|, then |socket| is removed after the next call +// to |callback|, even if not all registered |interests| became ready. SB_EXPORT bool SbSocketWaiterAdd(SbSocketWaiter waiter, SbSocket socket, void* context, @@ -126,38 +139,52 @@ int interests, bool persistent); -// Removes |socket| from |waiter|, previously added with SbSocketWaiterAdd. If -// |socket| wasn't registered with |waiter|, then this does nothing. This should -// only be called on the thread that waits on this waiter. +// Removes a socket, previously added with SbSocketWaiterAdd(), from a waiter. +// This function should only be called on the thread that waits on this waiter. +// +// The return value indicates whether the waiter still waits on the socket. +// If |socket| wasn't registered with |waiter|, then the function is a no-op +// and returns |true|. +// +// |waiter|: The waiter from which the socket is removed. +// |socket|: The socket to remove from the waiter. SB_EXPORT bool SbSocketWaiterRemove(SbSocketWaiter waiter, SbSocket socket); // Waits on all registered sockets, calling the registered callbacks if and when // the corresponding sockets become ready for an interested operation. This -// version exits only after SbSocketWaiterWakeUp() is called. This should only -// be called on the thread that waits on this waiter. +// version exits only after SbSocketWaiterWakeUp() is called. This function +// should only be called on the thread that waits on this waiter. SB_EXPORT void SbSocketWaiterWait(SbSocketWaiter waiter); -// Behaves the same as SbSocketWaiterWait(), waking up when -// SbSocketWaiterWakeUp(), but also exits on its own after at least |duration| -// has passed, whichever comes first, returning an indicator of which one -// happened. Note that, as with SbThreadSleep(), it may wait longer than -// |duration|. For example if the timeout expires while a callback is being -// fired. This should only be called on the thread that waits on this waiter. +// Behaves similarly to SbSocketWaiterWait(), but this function also causes +// |waiter| to exit on its own after at least |duration| has passed if +// SbSocketWaiterWakeUp() it not called by that time. +// +// The return value indicates the reason that the socket waiter exited. +// This function should only be called on the thread that waits on this waiter. +// +// |duration|: The minimum amount of time after which the socket waiter should +// exit if it is not woken up before then. As with SbThreadSleep() (see +// thread.h), this function may wait longer than |duration|, such as if the +// timeout expires while a callback is being fired. SB_EXPORT SbSocketWaiterResult SbSocketWaiterWaitTimed(SbSocketWaiter waiter, SbTime duration); -// Wakes up |waiter| once. Can be called from a SbSocketWaiterCallback to wake -// up its own waiter. Can also be called from another thread at any time, it's -// the only thread-safe waiter function. In either case, the waiter will exit -// the next wait gracefully, first completing any in-progress callback. +// Wakes up |waiter| once. This is the only thread-safe waiter function. +// It can can be called from a SbSocketWaiterCallback to wake up its own waiter, +// and it can also be called from another thread at any time. In either case, +// the waiter will exit the next wait gracefully, first completing any +// in-progress callback. // -// For every time this function is called, it will cause the waiter to wake up -// one time. This is true whether the waiter is currently waiting or not. If not -// waiting, it will just take affect immediately the next time the waiter -// waits. The number of wake-ups accumulate and are only consumed by waiting and -// getting woken up. e.g. If you call this function 7 times, then -// SbSocketWaiterWait() and WaitTimed() will not block the next 7 times they are -// called. +// Each time this function is called, it causes the waiter to wake up once, +// regardless of whether the waiter is currently waiting. If the waiter is not +// waiting, the function takes effect immediately the next time the waiter +// waits. The number of wake-ups accumulates, and the queue is only consumed +// as the waiter waits and then is subsequently woken up again. For example, +// if you call this function 7 times, then SbSocketWaiterWait() and WaitTimed() +// will not block the next 7 times they are called. +// +// |waiter|: The socket waiter to be woken up. SB_EXPORT void SbSocketWaiterWakeUp(SbSocketWaiter waiter); #ifdef __cplusplus
diff --git a/src/starboard/speech_synthesis.h b/src/starboard/speech_synthesis.h new file mode 100644 index 0000000..2852920 --- /dev/null +++ b/src/starboard/speech_synthesis.h
@@ -0,0 +1,65 @@ +// Copyright 2016 Google Inc. 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. + +// Module Overview: Starboard Speech Synthesis API +// +// A basic text-to-speech API intended to be used for audio accessibilty. +// +// Implementations of this API should audibly play back text to assist +// users in non-visual navigation of the application. +// +// Note that these functions do not have to be thread-safe. They must +// only be called from a single application thread. + +#ifndef STARBOARD_SPEECH_SYNTHESIS_H_ +#define STARBOARD_SPEECH_SYNTHESIS_H_ + +#include "starboard/configuration.h" +#include "starboard/export.h" +#include "starboard/types.h" + +#if SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) + +#ifdef __cplusplus +extern "C" { +#endif + +// Sets a language for speech synthesis. +// +// Must be called before any other function in this module, +// or subsequent calls are allowed to fail silently. +// +// |lang| should be a BCP 47 language string, eg "en-US". +// Return true if language is supported, false if not. +SB_EXPORT bool SbSpeechSynthesisSetLanguage(const char* lang); + +// Enqueues |text|, a UTF-8 string, to be spoken the currently +// selected language. Returns immediately. +// +// If audio from previous SbSpeechSynthesisSpeak() invocations is still +// processing, the current speaking should continue and this new +// text should be queued to play when the previous utterances are complete. +SB_EXPORT void SbSpeechSynthesisSpeak(const char* text); + +// Cancels all speaking and queued speech synthesis audio. Must +// return immediately. +SB_EXPORT void SbSpeechSynthesisCancel(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // SB_HAS(SPEECH_SYNTHESIS) && SB_VERSION(3) + +#endif // STARBOARD_SPEECH_SYNTHESIS_H_
diff --git a/src/starboard/spin_lock.h b/src/starboard/spin_lock.h index 60c9843..bdc4cdb 100644 --- a/src/starboard/spin_lock.h +++ b/src/starboard/spin_lock.h
@@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A C++-only spin-lock implementation, built entirely on top of -// Starboard atomics and threads. Can be safely used by both clients and +// Module Overview: Starboard Spin Lock module +// +// Defines a C++-only spin-lock implementation, built entirely on top of +// Starboard atomics and threads. It can be safely used by both clients and // implementations. #ifndef STARBOARD_SPIN_LOCK_H_
diff --git a/src/starboard/starboard_base_target.gypi b/src/starboard/starboard_base_target.gypi index 3137a1b..dd66b79 100644 --- a/src/starboard/starboard_base_target.gypi +++ b/src/starboard/starboard_base_target.gypi
@@ -36,6 +36,11 @@ 'SB_ABORT_ON_ALLOCATION_FAILURE', ], }], + ['sb_allows_memory_tracking==1', { + 'defines': [ + 'STARBOARD_ALLOWS_MEMORY_TRACKING', + ], + }], ['starboard_path == ""', { 'defines': [ # There doesn't appear to be any way to use the C preprocessor to do
diff --git a/src/starboard/storage.h b/src/starboard/storage.h index c90c438..37ccc10 100644 --- a/src/starboard/storage.h +++ b/src/starboard/storage.h
@@ -12,14 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -// User-based Storage API. This is a simple all-at-once BLOB storage and -// retrieval API that is intended for robust long-term, per-user storage. Some -// platforms have different mechanisms for this kind of storage, so this API -// exists to allow a client application to access this kind of storage. - -// Only a single open record can exist for each user. Attempting to open a +// Module Overview: Starboard Storage module +// +// Defines a user-based Storage API. This is a simple, all-at-once BLOB storage +// and retrieval API that is intended for robust long-term, per-user storage. +// Some platforms have different mechanisms for this kind of storage, so this +// API exists to allow a client application to access this kind of storage. +// +// Note that there can be only one storage record per user and, thus, a maximum +// of one open storage record can exist for each user. Attempting to open a // second record for a user will result in undefined behavior. - +// // These APIs are NOT expected to be thread-safe, so either call them from a // single thread, or perform proper synchronization around all calls. @@ -48,42 +51,67 @@ return record != kSbStorageInvalidRecord; } -// Opens the storage record for |user|, providing a handle to the opened -// record. |user| must be a valid SbUser, or kSbStorageInvalidRecord will be -// returned. Performs blocking I/O on the calling thread. +// Opens and returns a SbStorageRecord for |user|, blocking I/O on the calling +// thread until it returns. If |user| is not a valid |SbUser|, the function +// returns |kSbStorageInvalidRecord|. +// +// |user|: The user for which the storage record is opened. SB_EXPORT SbStorageRecord SbStorageOpenRecord(SbUser user); -// Closes |record|, synchronously ensuring all written data is flushed, and -// returning whether the operation was successful. Storage writes should be as -// atomic as possible, so either the record should be fully written, or it -// should delete the record (or, even better, leave it untouched). |record| will -// be invalidated after this point, and subsequent calls to |record| will -// fail. Performs blocking I/O on the calling thread. +// Closes |record|, synchronously ensuring that all written data is flushed. +// This function performs blocking I/O on the calling thread. +// +// The return value indicates whether the operation succeeded. Storage writes +// should be as atomic as possible, so the record should either be fully +// written or deleted (or, even better, untouched). +// +// |record|: The storage record to close. |record| is invalid after this point, +// and subsequent calls referring to |record| will fail. SB_EXPORT bool SbStorageCloseRecord(SbStorageRecord record); -// Returns the size of |record|, or -1 on error. Performs blocking I/O on the -// calling thread. +// Returns the size of |record|, or |-1| if there is an error. This function +// performs blocking I/O on the calling thread. +// +// |record|: The record to retrieve the size of. SB_EXPORT int64_t SbStorageGetRecordSize(SbStorageRecord record); -// Reads up to |data_size| bytes from |record| into |out_data|, starting at the -// beginning of the record. Returns the number of actual bytes read, which must -// be <= |size|, or -1 on error. This function makes a best-effort and performs -// blocking I/O on the calling thread. +// Reads up to |data_size| bytes from |record|, starting at the beginning of +// the record. The function returns the actual number of bytes read, which +// must be <= |data_size|. The function returns |-1| in the event of an error. +// This function makes a best-effort to read the entire record, and it performs +// blocking I/O on the calling thread until the entire record is read or an +// error is encountered. +// +// |record|: The record to be read. +// |out_data|: The data read from the record. +// |data_size|: The amount of data, in bytes, to read. SB_EXPORT int64_t SbStorageReadRecord(SbStorageRecord record, char* out_data, int64_t data_size); -// Replaces the data in |record| with |size| bytes from |data|. This always -// deletes any previous data in that record. Returns whether the write -// succeeded. This function makes a best-effort and performs blocking I/O on the -// calling thread. +// Replaces the data in |record| with |data_size| bytes from |data|. This +// function always deletes any previous data in that record. The return value +// indicates whether the write succeeded. This function makes a best-effort +// to read the entire record, and it performs performs blocking I/O on the +// calling thread until the entire record is read or an error is encountered. +// +// |record|: The record to be written to. +// |data|: The data to write to the record. +// |data_size|: The amount of |data|, in bytes, to write to the record. Thus, +// if |data_size| is smaller than the total size of |data|, only part of +// |data| is written to the record. SB_EXPORT bool SbStorageWriteRecord(SbStorageRecord record, const char* data, int64_t data_size); -// Deletes |user|'s storage record, returning whether the record both existed -// and was successfully deleted. This must not be called while |user|'s storage -// record is open. This function performs blocking I/O on the calling thread. +// Deletes the |SbStorageRecord| for the specified user. The return value +// indicates whether the record existed and was successfully deleted. If the +// record did not exist or could not be deleted, the function returns |false|. +// +// This function must not be called while the user's storage record is open. +// This function performs blocking I/O on the calling thread. +// +// |user|: The user for whom the record is deleted. SB_EXPORT bool SbStorageDeleteRecord(SbUser user); #ifdef __cplusplus
diff --git a/src/starboard/string.h b/src/starboard/string.h index 5782023..430c6b6 100644 --- a/src/starboard/string.h +++ b/src/starboard/string.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Functions for interacting with c-style strings. +// Module Overview: Starboard String module +// +// Defines functions for interacting with c-style strings. #ifndef STARBOARD_STRING_H_ #define STARBOARD_STRING_H_ @@ -27,103 +29,171 @@ extern "C" { #endif -// Returns the length, in characters, of |str|, a zero-terminated ASCII string. +// Returns the length, in characters, of |str|. +// +// |str|: A zero-terminated ASCII string. SB_EXPORT size_t SbStringGetLength(const char* str); -// Same as SbStringGetLength but for wide characters. This assumes that there -// are no multi-element characters. +// Returns the length of a wide character string. (This function is the same +// as SbStringGetLength, but for a string comprised of wide characters.) This +// function assumes that there are no multi-element characters. +// +// |str|: A zero-terminated ASCII string. SB_EXPORT size_t SbStringGetLengthWide(const wchar_t* str); -// Copies as much |source| as possible into |out_destination| and -// null-terminates it, given that it has |destination_size| characters of -// storage available. Returns the length of |source|. Meant to be a drop-in -// replacement for strlcpy +// Copies as much of a |source| string as possible and null-terminates it, +// given that |destination_size| characters of storage are available. This +// function is meant to be a drop-in replacement for |strlcpy|. +// +// The return value specifies the length of |source|. +// +// |out_destination|: The location to which the string is copied. +// |source|: The string to be copied. +// |destination_size|: The amount of the source string to copy. SB_EXPORT int SbStringCopy(char* out_destination, const char* source, int destination_size); -// Inline wrapper for an unsafe SbStringCopy that assumes |out_destination| is -// big enough. Meant to be a drop-in replacement for strcpy. +// An inline wrapper for an unsafe SbStringCopy that assumes that the +// destination provides enough storage space for the entire string. The return +// value is a pointer to the destination string. This function is meant to be +// a drop-in replacement for |strcpy|. +// +// |out_destination|: The location to which the string is copied. +// |source|: The string to be copied. static SB_C_INLINE char* SbStringCopyUnsafe(char* out_destination, const char* source) { SbStringCopy(out_destination, source, INT_MAX); return out_destination; } -// Same as SbStringCopy but for wide characters. +// Identical to SbStringCopy, but for wide characters. +// +// |out_destination|: The location to which the string is copied. +// |source|: The string to be copied. +// |destination_size|: The amount of the source string to copy. SB_EXPORT int SbStringCopyWide(wchar_t* out_destination, const wchar_t* source, int destination_size); -// Concatenates |source| onto the end of |out_destination|, presuming it has -// |destination_size| total characters of storage available. Returns -// length of |source|. Meant to be a drop-in replacement for strlcpy -// Note: This function's signature is NOT compatible with strncat +// Appends |source| to |out_destination| as long as |out_destination| has +// enough storage space to hold the concatenated string. +// +// This function is meant to be a drop-in replacement for |strlcat|. Also note +// that this function's signature is NOT compatible with |strncat|. +// +// |out_destination|: The string to which the |source| string is appended. +// |source|: The string to be appended to the destination string. +// |destination_size|: The amount of storage space available for the +// concatenated string. SB_EXPORT int SbStringConcat(char* out_destination, const char* source, int destination_size); -// Inline wrapper for an unsafe SbStringConcat that assumes |out_destination| is -// big enough. -// Note: This function's signature is NOT compatible with strcat. +// An inline wrapper for an unsafe SbStringConcat that assumes that the +// |out_destination| provides enough storage space for the concatenated string. +// Note that this function's signature is NOT compatible with |strcat|. +// +// |out_destination|: The string to which the |source| string is appended. +// |source|: The string to be appended to the destination string. static SB_C_INLINE int SbStringConcatUnsafe(char* out_destination, const char* source) { return SbStringConcat(out_destination, source, INT_MAX); } -// Same as SbStringConcat but for wide characters. +// Identical to SbStringCat, but for wide characters. +// +// |out_destination|: The string to which the |source| string is appended. +// |source|: The string to be appended to the destination string. +// |destination_size|: The amount of storage space available for the +// concatenated string. SB_EXPORT int SbStringConcatWide(wchar_t* out_destination, const wchar_t* source, int destination_size); -// Copies the string |source| into a buffer allocated by this function that can -// be freed with SbMemoryDeallocate. Meant to be a drop-in replacement for -// strdup. +// Copies |source| into a buffer that is allocated by this function and that +// can be freed with SbMemoryDeallocate. This function is meant to be a drop-in +// replacement for |strdup|. +// +// |source|: The string to be copied. SB_EXPORT char* SbStringDuplicate(const char* source); -// Finds the first occurrence of |character| in |str|, returning a pointer to -// the found character in the given string, or NULL if not found. -// NOTE: The function signature of this function does NOT match the function -// strchr. +// Finds the first occurrence of a |character| in |str|. The return value is a +// pointer to the found character in the given string or |NULL| if the +// character is not found. Note that this function's signature does NOT match +// that of the |strchr| function. +// +// |str|: The string to search for the character. +// |character|: The character to find in the string. SB_EXPORT const char* SbStringFindCharacter(const char* str, char character); -// Finds the last occurrence of |character| in |str|, returning a pointer to the -// found character in the given string, or NULL if not found. -// NOTE: The function signature of this function does NOT match the function -// strrchr. +// Finds the last occurrence of a specified character in a string. +// The return value is a pointer to the found character in the given string or +// |NULL| if the character is not found. Note that this function's signature +// does NOT match that of the |strrchr| function. +// +// |str|: The string to search for the character. +// |character|: The character to find in the string. SB_EXPORT const char* SbStringFindLastCharacter(const char* str, char character); -// Finds the first occurrence of |str2| in |str1|, returning a pointer to the -// beginning of the found occurrencein |str1|, or NULL if not found. Meant to -// be a drop-in replacement for strstr. +// Finds the first occurrence of |str2| in |str1|. The return value is a +// pointer to the beginning of the found string or |NULL| if the string is +// not found. This function is meant to be a drop-in replacement for |strstr|. +// +// |str1|: The string in which to search for the presence of |str2|. +// |str2|: The string to locate in |str1|. SB_EXPORT const char* SbStringFindString(const char* str1, const char* str2); -// Compares |string1| and |string2|, ignoring differences in case. Returns < 0 -// if |string1| is ASCII-betically lower than |string2|, 0 if they are equal, -// and > 0 if |string1| is ASCII-betically higher than |string2|. Meant to be a -// drop-in replacement for strcasecmp. +// Compares two strings, ignoring differences in case. The return value is: +// - |< 0| if |string1| is ASCII-betically lower than |string2|. +// - |0| if the two strings are equal. +// - |> 0| if |string1| is ASCII-betically higher than |string2|. +// +// This function is meant to be a drop-in replacement for |strcasecmp|. +// +// |string1|: The first string to compare. +// |string2|: The second string to compare. SB_EXPORT int SbStringCompareNoCase(const char* string1, const char* string2); -// Compares the first |count| characters of |string1| and |string2|, ignoring -// differences in case. Returns < 0 if |string1| is ASCII-betically lower than -// |string2|, 0 if they are equal, and > 0 if |string1| is ASCII-betically -// higher than |string2|. Meant to be a drop-in replacement for strncasecmp. +// Compares the first |count| characters of two strings, ignoring differences +// in case. The return value is: +// - |< 0| if |string1| is ASCII-betically lower than |string2|. +// - |0| if the two strings are equal. +// - |> 0| if |string1| is ASCII-betically higher than |string2|. +// +// This function is meant to be a drop-in replacement for |strncasecmp|. +// +// |string1|: The first string to compare. +// |string2|: The second string to compare. +// |count|: The number of characters to compare. SB_EXPORT int SbStringCompareNoCaseN(const char* string1, const char* string2, size_t count); -// Formats the given |format| and |arguments|, placing as much of the result -// will fit in |out_buffer|, whose size is specified by |buffer_size|. Returns -// the number of characters the format would produce if |buffer_size| was -// infinite. Meant to be a drop-in replacement for vsnprintf. +// Produces a string formatted with |format| and |arguments|, placing as much +// of the result that will fit into |out_buffer|. The return value specifies +// the number of characters that the format would produce if |buffer_size| were +// infinite. +// +// This function is meant to be a drop-in replacement for |vsnprintf|. +// +// |out_buffer|: The location where the formatted string is stored. +// |buffer_size|: The size of |out_buffer|. +// |format|: A string that specifies how the data should be formatted. +// |arguments|: Variable arguments used in the string. SB_EXPORT int SbStringFormat(char* out_buffer, size_t buffer_size, const char* format, va_list arguments) SB_PRINTF_FORMAT(3, 0); -// Inline wrapper of SbStringFormat to convert from ellipsis to va_args. -// Meant to be a drop-in replacement for snprintf. +// An inline wrapper of SbStringFormat that converts from ellipsis to va_args. +// This function is meant to be a drop-in replacement for |snprintf|. +// +// |out_buffer|: The location where the formatted string is stored. +// |buffer_size|: The size of |out_buffer|. +// |format|: A string that specifies how the data should be formatted. +// |...|: Arguments used in the string. static SB_C_INLINE int SbStringFormatF(char* out_buffer, size_t buffer_size, const char* format, @@ -139,8 +209,12 @@ return result; } -// Inline wrapper of SbStringFormat to be a drop-in replacement for the unsafe -// but commonly used sprintf. +// An inline wrapper of SbStringFormat that is meant to be a drop-in +// replacement for the unsafe but commonly used |sprintf|. +// +// |out_buffer|: The location where the formatted string is stored. +// |format|: A string that specifies how the data should be formatted. +// |...|: Arguments used in the string. static SB_C_INLINE int SbStringFormatUnsafeF(char* out_buffer, const char* format, ...) SB_PRINTF_FORMAT(2, 3); @@ -154,14 +228,25 @@ return result; } -// Same as SbStringFormat, but for wide characters. Meant to be a drop-in -// replacement for vswprintf. +// This function is identical to SbStringFormat, but is for wide characters. +// It is meant to be a drop-in replacement for |vswprintf|. +// +// |out_buffer|: The location where the formatted string is stored. +// |buffer_size|: The size of |out_buffer|. +// |format|: A string that specifies how the data should be formatted. +// |arguments|: Variable arguments used in the string. SB_EXPORT int SbStringFormatWide(wchar_t* out_buffer, size_t buffer_size, const wchar_t* format, va_list arguments); -// Inline wrapper of SbStringFormatWide to convert from ellipsis to va_args. +// An inline wrapper of SbStringFormatWide that converts from ellipsis to +// |va_args|. +// +// |out_buffer|: The location where the formatted string is stored. +// |buffer_size|: The size of |out_buffer|. +// |format|: A string that specifies how the data should be formatted. +// |...|: Arguments used in the string. static SB_C_INLINE int SbStringFormatWideF(wchar_t* out_buffer, size_t buffer_size, const wchar_t* format, @@ -173,38 +258,65 @@ return result; } -// Compares the first |count| characters of |string1| and |string2|, which are -// 16-bit character strings. Returns < 0 if |string1| is ASCII-betically lower -// than |string2|, 0 if they are equal, and > 0 if |string1| is ASCII-betically -// higher than |string2|. Meant to be a drop-in replacement of wcsncmp. +// Compares the first |count| characters of two 16-bit character strings. +// The return value is: +// - |< 0| if |string1| is ASCII-betically lower than |string2|. +// - |0| if the two strings are equal. +// - |> 0| if |string1| is ASCII-betically higher than |string2|. +// +// This function is meant to be a drop-in replacement for |wcsncmp|. +// +// |string1|: The first 16-bit character string to compare.weird +// |string2|: The second 16-bit character string to compare. +// |count|: The number of characters to compare. SB_EXPORT int SbStringCompareWide(const wchar_t* string1, const wchar_t* string2, size_t count); -// Compares the first |count| characters of |string1| and |string2|, which are -// 8-bit character strings. Returns < 0 if |string1| is ASCII-betically lower -// than |string2|, 0 if they are equal, and > 0 if |string1| is ASCII-betically -// higher than |string2|. Meant to be a drop-in replacement of strncmp. +// Compares the first |count| characters of two 8-bit character strings. +// The return value is: +// - |< 0| if |string1| is ASCII-betically lower than |string2|. +// - |0| if the two strings are equal. +// - |> 0| if |string1| is ASCII-betically higher than |string2|. +// +// This function is meant to be a drop-in replacement for |strncmp|. +// +// |string1|: The first 8-bit character string to compare. +// |string2|: The second 8-bit character string to compare. +// |count|: The number of characters to compare. SB_EXPORT int SbStringCompare(const char* string1, const char* string2, size_t count); -// Compares all the characters of |string1| and |string2|, which are 8-bit -// character strings, up to their natural termination. Returns < 0 if |string1| -// is ASCII-betically lower than |string2|, 0 if they are equal, and > 0 if -// |string1| is ASCII-betically higher than |string2|. Meant to be a drop-in -// replacement of strcmp. +// Compares two entire 8-bit character strings. The return value is: +// - |< 0| if |string1| is ASCII-betically lower than |string2|. +// - |0| if the two strings are equal. +// - |> 0| if |string1| is ASCII-betically higher than |string2|. +// +// This function is meant to be a drop-in replacement for |strcmp|. +// +// |string1|: The first 8-bit character string to compare. +// |string2|: The second 8-bit character string to compare. SB_EXPORT int SbStringCompareAll(const char* string1, const char* string2); // Scans |buffer| for |pattern|, placing the extracted values in |arguments|. -// Returns the number of successfully matched items, which may be zero. Meant to -// be a drop-in replacement for vsscanf. +// The return value specifies the number of successfully matched items, which +// may be |0|. +// +// This function is meant to be a drop-in replacement for |vsscanf|. +// +// |buffer|: The string to scan for the pattern. +// |pattern|: The string to search for in |buffer|. +// |arguments|: Values matching |pattern| that were extracted from |buffer|. SB_EXPORT int SbStringScan(const char* buffer, const char* pattern, va_list arguments); -// Inline wrapper of SbStringScan to convert from ellipsis to va_args. Meant to -// be a drop-in replacement for sscanf. +// An inline wrapper of SbStringScan that converts from ellipsis to |va_args|. +// This function is meant to be a drop-in replacement for |sscanf|. +// |buffer|: The string to scan for the pattern. +// |pattern|: The string to search for in |buffer|. +// |...|: Values matching |pattern| that were extracted from |buffer|. static SB_C_INLINE int SbStringScanF(const char* buffer, const char* pattern, ...) { @@ -215,49 +327,77 @@ return result; } -// Parses a string at the beginning of |start| into a signed integer in the -// given |base|. It will place the pointer to the end of the consumed portion -// of the string in |out_end|, if it is provided. Meant to be a drop-in -// replacement for strtol. +// Extracts a string that represents an integer from the beginning of |start| +// into a signed integer in the given |base|. This function is meant to be a +// drop-in replacement for |strtol|. +// +// |start|: The string that begins with the number to be converted. +// |out_end|: If provided, the function places a pointer to the end of the +// consumed portion of the string into |out_end|. +// |base|: The base into which the number will be converted. The value must +// be between |2| and |36|, inclusive. // NOLINTNEXTLINE(runtime/int) SB_EXPORT long SbStringParseSignedInteger(const char* start, char** out_end, int base); -// Shorthand replacement for atoi. Parses |value| into a base-10 int. +// Parses a string into a base-10 integer. This is a shorthand replacement for +// |atoi|. +// +// |value|: The string to be converted. static SB_C_INLINE int SbStringAToI(const char* value) { // NOLINTNEXTLINE(readability/casting) return (int)SbStringParseSignedInteger(value, NULL, 10); } -// Shorthand replacement for atol. Parses |value| into a base-10 int. +// Parses a string into a base-10, long integer. This is a shorthand +// replacement for |atol|. +// +// |value|: The string to be converted. // NOLINTNEXTLINE(runtime/int) static SB_C_INLINE long SbStringAToL(const char* value) { // NOLINTNEXTLINE(readability/casting) return SbStringParseSignedInteger(value, NULL, 10); } -// Parses a string at the beginning of |start| into a unsigned integer in the -// given |base|. It will place the pointer to the end of the consumed portion -// of the string in |out_end|, if it is provided. Meant to be a drop-in -// replacement for strtoul. +// Extracts a string that represents an integer from the beginning of |start| +// into an unsigned integer in the given |base|. +// This function is meant to be a drop-in replacement for |strtoul|. +// +// |start|: The string that begins with the number to be converted. +// |out_end|: If provided, the function places a pointer to the end of the +// consumed portion of the string into |out_end|. +// |base|: The base into which the number will be converted. The value must +// be between |2| and |36|, inclusive. // NOLINTNEXTLINE(runtime/int) SB_EXPORT unsigned long SbStringParseUnsignedInteger(const char* start, char** out_end, int base); -// Parses a string at the beginning of |start| into a unsigned 64-bit integer in -// the given |base|. It will place the pointer to the end of the consumed -// portion of the string in |out_end|, if it is provided. Meant to be a drop-in -// replacement for strtoull, but explicity declared to return uint64_t. +// Extracts a string that represents an integer from the beginning of |start| +// into an unsigned 64-bit integer in the given |base|. +// +// This function is meant to be a drop-in replacement for |strtoull|, except +// that it is explicitly declared to return |uint64_t|. +// +// |start|: The string that begins with the number to be converted. +// |out_end|: If provided, the function places a pointer to the end of the +// consumed portion of the string into |out_end|. +// |base|: The base into which the number will be converted. The value must +// be between |2| and |36|, inclusive. SB_EXPORT uint64_t SbStringParseUInt64(const char* start, char** out_end, int base); -// Parses a string at the beginning of |start| into a double. -// It will place the pointer to the end of the consumed -// portion of the string in |out_end|, if it is provided. Meant to be a drop-in -// replacement for strtod, but explicity declared to return double. +// Extracts a string that represents an integer from the beginning of |start| +// into a double. +// +// This function is meant to be a drop-in replacement for |strtod|, except +// that it is explicitly declared to return a double. +// +// |start|: The string that begins with the number to be converted. +// |out_end|: If provided, the function places a pointer to the end of the +// consumed portion of the string into |out_end|. SB_EXPORT double SbStringParseDouble(const char* start, char** out_end); #ifdef __cplusplus
diff --git a/src/starboard/stub/configuration_public.h b/src/starboard/stub/configuration_public.h index 75b5256..036175e 100644 --- a/src/starboard/stub/configuration_public.h +++ b/src/starboard/stub/configuration_public.h
@@ -128,6 +128,9 @@ // Whether the current platform has microphone supported. #define SB_HAS_MICROPHONE 1 +// Whether the current platform has speech synthesis. +#define SB_HAS_SPEECH_SYNTHESIS 1 + // Type detection for wchar_t. #if defined(__WCHAR_MAX__) && \ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) @@ -456,6 +459,12 @@ // The maximum number of users that can be signed in at the same time. #define SB_USER_MAX_SIGNED_IN 1 +// --- Timing API ------------------------------------------------------------ + +// Whether this platform has an API to retrieve how long the current thread +// has spent in the executing state. +#define SB_HAS_TIME_THREAD_NOW 1 + // --- Platform Specific Audits ---------------------------------------------- #if !defined(__GNUC__)
diff --git a/src/starboard/stub/starboard_platform.gyp b/src/starboard/stub/starboard_platform.gyp index bfacb36..438e4df 100644 --- a/src/starboard/stub/starboard_platform.gyp +++ b/src/starboard/stub/starboard_platform.gyp
@@ -102,7 +102,6 @@ '<(DEPTH)/starboard/shared/stub/microphone_create.cc', '<(DEPTH)/starboard/shared/stub/microphone_destroy.cc', '<(DEPTH)/starboard/shared/stub/microphone_get_available.cc', - '<(DEPTH)/starboard/shared/stub/microphone_get_speech_api_key.cc', '<(DEPTH)/starboard/shared/stub/microphone_is_sample_rate_supported.cc', '<(DEPTH)/starboard/shared/stub/microphone_open.cc', '<(DEPTH)/starboard/shared/stub/microphone_read.cc', @@ -152,6 +151,9 @@ '<(DEPTH)/starboard/shared/stub/socket_waiter_wait.cc', '<(DEPTH)/starboard/shared/stub/socket_waiter_wait_timed.cc', '<(DEPTH)/starboard/shared/stub/socket_waiter_wake_up.cc', + '<(DEPTH)/starboard/shared/stub/speech_synthesis_cancel.cc', + '<(DEPTH)/starboard/shared/stub/speech_synthesis_set_language.cc', + '<(DEPTH)/starboard/shared/stub/speech_synthesis_speak.cc', '<(DEPTH)/starboard/shared/stub/storage_close_record.cc', '<(DEPTH)/starboard/shared/stub/storage_delete_record.cc', '<(DEPTH)/starboard/shared/stub/storage_get_record_size.cc', @@ -222,6 +224,7 @@ '<(DEPTH)/starboard/shared/stub/thread_types_public.h', '<(DEPTH)/starboard/shared/stub/thread_yield.cc', '<(DEPTH)/starboard/shared/stub/time_get_monotonic_now.cc', + '<(DEPTH)/starboard/shared/stub/time_get_monotonic_thread_now.cc', '<(DEPTH)/starboard/shared/stub/time_get_now.cc', '<(DEPTH)/starboard/shared/stub/time_zone_get_current.cc', '<(DEPTH)/starboard/shared/stub/time_zone_get_dst_name.cc',
diff --git a/src/starboard/system.h b/src/starboard/system.h index c52a4e1..2d5a1a2 100644 --- a/src/starboard/system.h +++ b/src/starboard/system.h
@@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A broad set of APIs that allow the client application to query build and -// runtime properties of the enclosing system. +// Module Overview: Starboard System module +// +// Defines a broad set of APIs that allow the client application to query +// build and runtime properties of the enclosing system. #ifndef STARBOARD_SYSTEM_H_ #define STARBOARD_SYSTEM_H_ @@ -94,6 +96,14 @@ // A universally-unique ID for the current user. kSbSystemPropertyPlatformUuid, + +#if SB_VERSION(2) + // The Google Speech API key. The platform manufacturer is responsible + // for registering a Google Speech API key for their products. In the API + // Console (http://developers.google.com/console), you can enable the + // Speech APIs and generate a Speech API key. + kSbSystemPropertySpeechApiKey, +#endif // SB_VERSION(2) } SbSystemPropertyId; // Enumeration of device types. @@ -136,9 +146,9 @@ } SbSystemConnectionType; // Runtime capabilities are boolean properties of a platform that can't be -// determined at compile-time, and may vary from device to device, but will not -// change over the course of a single execution. They often specify particular -// behavior of other APIs within the bounds of their operating range. +// determined at compile-time. They may vary from device to device, but they +// will not change over the course of a single execution. They often specify +// particular behavior of other APIs within the bounds of their operating range. typedef enum SbSystemCapabilityId { // Whether this system has reversed Enter and Back keys. kSbSystemCapabilityReversedEnterAndBack, @@ -196,77 +206,87 @@ return handle != kSbSystemPlatformErrorInvalid; } -// Called by Cobalt to notify the platform that an error has occurred in the -// application that may have to be handled by the platform. It is expected the -// platform will notify the user of the error, and provide interaction if -// required, for example by showing a dialog. +// Cobalt calls this function to notify the platform that an error has occurred +// in the application that the platform may need to handle. The platform is +// expected to then notify the user of the error and to provide a means for +// any required interaction, such as by showing a dialog. // -// |type| is one of the enumerated types above to define the error; |callback| -// is a function that may be called by the platform to let the caller know the -// user has reacted to the error; |user_data| is an opaque pointer that the -// platform should pass as an argument to the callback, if called. -// Returns a handle that may be used in a subsequent call to -// |SbClearPlatformError|, for example to programatically dismiss a dialog that -// may have been raised in response to the error. The lifetime of -// the object referenced by the handle is until the user reacts to the error -// or the error is dismissed by a call to |SbSystemClearPlatformError|, -// whichever happens first. If the platform cannot respond to the error, then -// this function should return |kSbSystemPlatformErrorInvalid|. -// -// This function may be called from any thread; it is the responsibility of the -// platform to decide how to handle an error received while a previous error is -// still pending - if only one error can be handled at a time, then the -// platform may queue the second error or ignore it by returning +// The return value is a handle that may be used in a subsequent call to +// |SbClearPlatformError|. For example, the handle could be used to +// programatically dismiss a dialog that was raised in response to the error. +// The lifetime of the object referenced by the handle is until the user reacts +// to the error or the error is dismissed by a call to +// SbSystemClearPlatformError, whichever happens first. Note that if the +// platform cannot respond to the error, then this function should return // |kSbSystemPlatformErrorInvalid|. +// +// This function may be called from any thread, and it is the platform's +// responsibility to decide how to handle an error received while a previous +// error is still pending. If that platform can only handle one error at a +// time, then it may queue the second error or ignore it by returning +// |kSbSystemPlatformErrorInvalid|. +// +// |type|: An error type, from the SbSystemPlatformErrorType enum, +// that defines the error. +// |callback|: A function that may be called by the platform to let the caller +// know that the user has reacted to the error. +// |user_data|: An opaque pointer that the platform should pass as an argument +// to the callback function, if it is called. SB_EXPORT SbSystemPlatformError SbSystemRaisePlatformError(SbSystemPlatformErrorType type, SbSystemPlatformErrorCallback callback, void* user_data); // Clears a platform error that was previously raised by a call to -// |SbSystemRaisePlatformError|, specified by the handle that was returned by -// that function. The platform may use this, for example, to close a dialog -// that was opened in response to the error. +// |SbSystemRaisePlatformError|. The platform may use this, for example, +// to close a dialog that was opened in response to the error. +// +// |handle|: The platform error to be cleared. SB_EXPORT void SbSystemClearPlatformError(SbSystemPlatformError handle); -// Pointer to a function to compare two items, returning less than zero, zero, -// or greater than zero depending on whether |a| is less than |b|, equal to |b|, -// or greater than |b|, respectively (standard *cmp semantics). +// Pointer to a function to compare two items. The return value uses standard +// |*cmp| semantics: +// - |< 0| if |a| is less than |b| +// - |0| if the two items are equal +// - |> 1| if |a| is greater than |b| +// +// |a|: The first value to compare. +// |b|: The second value to compare. typedef int (*SbSystemComparator)(const void* a, const void* b); -// Breaks the current program into the debugger, if a debugger is -// attached. Aborts the program otherwise. +// Breaks the current program into the debugger, if a debugger is attached. +// If a debugger is not attached, this function aborts the program. SB_EXPORT void SbSystemBreakIntoDebugger(); -// Attempts to determine if the current program is running inside or attached to -// a debugger. +// Attempts to determine whether the current program is running inside or +// attached to a debugger. The function returns |false| if neither of those +// cases is true. SB_EXPORT bool SbSystemIsDebuggerAttached(); // Returns the number of processor cores available to this application. If the -// process is sandboxed to a subset of the physical cores, it will return that -// sandboxed limit. +// process is sandboxed to a subset of the physical cores, the function returns +// that sandboxed limit. SB_EXPORT int SbSystemGetNumberOfProcessors(); -// Gets the total CPU memory (in bytes) potentially available to this -// application. If the process is sandboxed to a maximum allowable limit, it -// will return the lesser of the physical and sandbox limits. +// Returns the total CPU memory (in bytes) potentially available to this +// application. If the process is sandboxed to a maximum allowable limit, +// the function returns the lesser of the physical and sandbox limits. SB_EXPORT int64_t SbSystemGetTotalCPUMemory(); // Returns the total physical CPU memory (in bytes) used by this application. -// This value should always be less than (or in particularly exciting +// This value should always be less than (or, in particularly exciting // situations, equal to) SbSystemGetTotalCPUMemory(). SB_EXPORT int64_t SbSystemGetUsedCPUMemory(); -// Returns the total GPU memory available (in bytes) for use by this -// application. -// This function may only be called if true is the return value for calls to -// SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats). +// Returns the total GPU memory (in bytes) available for use by this +// application. This function may only be called the return value for calls to +// SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats) is |true|. SB_EXPORT int64_t SbSystemGetTotalGPUMemory(); // Returns the current amount of GPU memory (in bytes) that is currently being -// used by this application. -// This function may only be called if true is the return value for calls to -// SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats). +// used by this application. This function may only be called if the return +// value for calls to +// SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats) is |true|. SB_EXPORT int64_t SbSystemGetUsedGPUMemory(); // Returns the type of the device. @@ -275,59 +295,83 @@ // Returns the device's current network connection type. SB_EXPORT SbSystemConnectionType SbSystemGetConnectionType(); -// Gets the platform-defined system path specified by |path_id|, placing it as a -// zero-terminated string into the user-allocated |out_path|, unless it is -// longer than |path_length| - 1. Returns false if |path_id| is invalid for this -// platform, or if |path_length| is too short for the given result, or if -// |out_path| is NULL. On failure, |out_path| will not be changed. -// Implementation must be thread-safe. +// Retrieves the platform-defined system path specified by |path_id| and +// places it as a zero-terminated string into the user-allocated |out_path| +// unless it is longer than |path_length| - 1. This implementation must be +// thread-safe. +// +// This function returns |true| if the path is retrieved successfully. It +// returns |false| under any of the following conditions and, in any such +// case, |out_path| is not changed: +// - |path_id| is invalid for this platform +// - |path_length| is too short for the given result +// - |out_path| is NULL +// +// |path_id|: The system path to be retrieved. +// |out_path|: The platform-defined system path specified by |path_id|. +// |path_length|: The length of the system path. SB_EXPORT bool SbSystemGetPath(SbSystemPathId path_id, char* out_path, int path_length); -// Gets the platform-defined system property specified by |property_id|, placing -// it as a zero-terminated string into the user-allocated |out_value|, unless it -// is longer than |value_length| - 1. Returns false if |property_id| is invalid -// for this platform, or if |value_length| is too short for the given result, or -// if |out_value| is NULL. On failure, |out_value| will not be changed. -// Implementation must be thread-safe. +// Retrieves the platform-defined system property specified by |property_id| and +// places its value as a zero-terminated string into the user-allocated +// |out_value| unless it is longer than |value_length| - 1. This implementation +// must be thread-safe. +// +// This function returns |true| if the property is retrieved successfully. It +// returns |false| under any of the following conditions and, in any such +// case, |out_value| is not changed: +// - |property_id| is invalid for this platform +// - |value_length| is too short for the given result +// - |out_value| is NULL +// +// |property_id|: The system path to be retrieved. +// |out_value|: The platform-defined system property specified by |property_id|. +// |value_length|: The length of the system property. SB_EXPORT bool SbSystemGetProperty(SbSystemPropertyId property_id, char* out_value, int value_length); // Returns whether the platform has the runtime capability specified by -// |capability_id|. Returns false for any unknown capabilities. Implementation -// must be thread-safe. +// |capability_id|. Returns false for any unknown capabilities. This +// implementation must be thread-safe. +// +// |capability_id|: The runtime capability to check. SB_EXPORT bool SbSystemHasCapability(SbSystemCapabilityId capability_id); // Gets the system's current POSIX-style Locale ID. The locale represents the // location, language, and cultural conventions that the system wants to use, -// which affects which text is displayed to the user, and how display numbers, -// dates, currency, and the like are formatted. +// which affects which text is displayed to the user as well as how displayed +// numbers, dates, currency, and similar values are formatted. // -// At its simplest, it can just be a BCP 47 language code, like "en_US". These -// days, POSIX also wants to include the encoding, like "en_US.UTF8". POSIX -// additionally allows a couple very bare-bones locales, like "C" or "POSIX", -// but they are not supported here. POSIX also supports different locale -// settings for a few different purposes, but Starboard only exposes a single +// At its simplest, the locale ID can just be a BCP 47 language code, like +// |en_US|. Currently, POSIX also wants to include the encoding as in +// |en_US.UTF8|. POSIX also allows a couple very bare-bones locales, like "C" +// or "POSIX", but they are not supported here. POSIX also supports different +// locale settings for a few different purposes, but Starboard only exposes one // locale at a time. // -// RFC 5646 describing BCP 47 language codes: +// RFC 5646 describes BCP 47 language codes: // https://tools.ietf.org/html/bcp47 // -// For more information than you want on POSIX locales: +// For more information than you probably want about POSIX locales, see: // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html SB_EXPORT const char* SbSystemGetLocaleId(); -// Gets sixty-four random bits returned as an uint64_t. This is expected to be a -// cryptographically secure random number generator, and doesn't require manual +// A cryptographically secure random number generator that gets 64 random bits +// and returns them as an |uint64_t|. This function does not require manual // seeding. SB_EXPORT uint64_t SbSystemGetRandomUInt64(); -// Produces an arbitrary non-negative number, |buffer_size|, of random bytes -// which must not be negative, placing the result in |out_buffer|, which must -// not be null. This is expected to be a cryptographically secure random number -// generator, and doesn't require manual seeding. +// A cryptographically secure random number generator that produces an +// arbitrary, non-negative number of |buffer_size| random, non-negative bytes. +// The generated number is placed in |out_buffer|. This function does not +// require manual seeding. +// +// |out_buffer|: A pointer for the generated random number. This value must +// not be null. +// |buffer_size|: The size of the random number, in bytes. SB_EXPORT void SbSystemGetRandomData(void* out_buffer, int buffer_size); // Gets the last platform-specific error code produced by any Starboard call in @@ -338,48 +382,73 @@ // Clears the last error set by a Starboard call in the current thread. SB_EXPORT void SbSystemClearLastError(); -// Writes a human-readable string for |error|, up to |string_length| bytes, into -// the provided |out_string|. Returns the total desired length of the string. -// |out_string| may be null, in which case just the total desired length of the -// string is returned. |out_string| is always terminated with a null byte. +// Generates a human-readable string for an error. The return value specifies +// the total desired length of the string. +// +// |error|: The error for which a human-readable string is generated. +// |out_string|: The generated string. This value may be null, and it is +// always terminated with a null byte. +// |string_length|: The maximum length of the error string. SB_EXPORT int SbSystemGetErrorString(SbSystemError error, char* out_string, int string_length); // Places up to |stack_size| instruction pointer addresses of the current -// execution stack into |out_stack|, returning the number of entries -// populated. |out_stack| must point to a non-NULL array of void * of at least -// |stack_size| entries. The returned stack frames will be in "downward" order -// from the calling frame towards the entry point of the thread. So, if all the -// stack frames do not fit, the ones truncated will be the less interesting ones -// towards the thread entry point. This function must be async-signal-safe on -// platforms that support signals, as it will be used in crash signal handlers. +// execution stack into |out_stack|. The return value specifies the number +// of entries added. // -// See the following about what it means to be async-signal-safe on POSIX: +// The returned stack frames are in "downward" order from the calling frame +// toward the entry point of the thread. So, if all the stack frames do not +// fit, the ones truncated will be the less interesting ones toward the thread +// entry point. +// +// This function is used in crash signal handlers and, therefore, it must +// be async-signal-safe on platforms that support signals. The following +// document discusses what it means to be async-signal-safe on POSIX: // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html#tag_02_04_03 +// +// |out_stack|: A non-NULL array of |void *| of at least |stack_size| entries. +// |stack_size|: The maximum number of instruction pointer addresses to be +// placed into |out_stack| from the current execution stack. SB_EXPORT int SbSystemGetStack(void** out_stack, int stack_size); -// Looks up |address| as an instruction pointer and places up to |buffer_size| - -// 1 characters of the symbol associated with it in |out_buffer|, which must not -// be NULL. |out_buffer| will be NULL-terminated. Returns whether it found a -// reasonable match for |address|. If the return value is false, |out_buffer| -// will not be modified. This function must be async-signal-safe on platforms -// that support signals, as it will be used in crash signal handlers. +// Looks up |address| as an instruction pointer and places up to +// (|buffer_size - 1|) characters of the symbol associated with it in +// |out_buffer|, which must not be NULL. |out_buffer| will be NULL-terminated. +// +// The return value indicates whether the function found a reasonable match +// for |address|. If the return value is |false|, then |out_buffer| is not +// modified. +// +// This function is used in crash signal handlers and, therefore, it must +// be async-signal-safe on platforms that support signals. SB_EXPORT bool SbSystemSymbolize(const void* address, char* out_buffer, int buffer_size); -// Requests that the application be terminated gracefully at the next convenient -// point. Some work may continue to be done, and unrelated system events -// dispatched, in the meantime. This will eventually result in a -// kSbEventTypeStop event being dispatched to the application. When the process -// finally terminates, it will return |error_level|, if that has any meaning on -// the current platform. +// Requests that the application be terminated gracefully at the next +// convenient point. In the meantime, some work may continue to be done, and +// unrelated system events may be dispatched. This function eventually causes +// a |kSbEventTypeStop| event to be dispatched to the application. When the +// process finally terminates, it returns |error_level|, if that has any +// meaning on the current platform. +// +// |error_level|: An integer that serves as the return value for the process +// that is eventually terminated as a result of a call to this function. SB_EXPORT void SbSystemRequestStop(int error_level); // Binary searches a sorted table |base| of |element_count| objects, each // element |element_width| bytes in size for an element that |comparator| -// compares equal to |key|. Meant to be a drop-in replacement for bsearch. +// compares equal to |key|. +// +// This function is meant to be a drop-in replacement for |bsearch|. +// +// |key|: The key to search for in the table. +// |base|: The sorted table of elements to be searched. +// |element_count|: The number of elements in the table. +// |element_width|: The size, in bytes, of each element in the table. +// |comparator|: A value that indicates how the element in the table should +// compare to the specified |key|. SB_EXPORT void* SbSystemBinarySearch(const void* key, const void* base, size_t element_count, @@ -388,13 +457,19 @@ // Sorts an array of elements |base|, with |element_count| elements of // |element_width| bytes each, using |comparator| as the comparison function. -// Meant to be a drop-in replacement for qsort. +// +// This function is meant to be a drop-in replacement for |qsort|. +// +// |base|: The array of elements to be sorted. +// |element_count|: The number of elements in the array. +// |element_width|: The size, in bytes, of each element in the array. +// |comparator|: A value that indicates how the array should be sorted. SB_EXPORT void SbSystemSort(void* base, size_t element_count, size_t element_width, SbSystemComparator comparator); -// Hides the system splash screen, on systems that support a splash screen that +// Hides the system splash screen on systems that support a splash screen that // is displayed while the application is loading. This function may be called // from any thread and must be idempotent. SB_EXPORT void SbSystemHideSplashScreen();
diff --git a/src/starboard/thread.h b/src/starboard/thread.h index 2ad1006..d2138e0 100644 --- a/src/starboard/thread.h +++ b/src/starboard/thread.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Thread creation and cleanup. +// Module Overview: Starboard Thread module +// +// Defines functionality related to thread creation and cleanup. #ifndef STARBOARD_THREAD_H_ #define STARBOARD_THREAD_H_ @@ -128,22 +130,29 @@ return key != kSbThreadLocalKeyInvalid; } -// Creates a new thread, which starts immediately. The |stack_size| parameter -// can be 0 to indicate that the default stack size should be used. |priority| -// can be set to kSbThreadNoPriority to use the default priority for the -// platform (e.g. possibly a fixed, standard priority, or possibly a priority -// inherited from the thread that is calling SbThreadCreate()). |affinity| can -// be set to kSbThreadNoAffinity to use the default affinity for the platform. -// |joinable| sets whether the thread can be joined, or should start out -// "detached." |name| is mainly used for debugging, may be NULL, and may not -// even be used in production builds. Returns a handle to the newly created -// thread upon success, and returns kSbThreadInvalid if not. |entry_point| will -// be executed on the newly created thread, and passed |context|. +// Creates a new thread, which starts immediately. +// - If the function succeeds, the return value is a handle to the newly +// created thread. +// - If the function fails, the return value is |kSbThreadInvalid|. // -// NOTE: For joinable threads, when you are done with the thread handle, you -// must call SbThreadJoin to release system resources associated with the -// thread. This is not necessary for detached threads, but a detached thread -// cannot be joined. +// |stack_size|: The amount of memory reserved for the thread. Set the value +// to |0| to indicate that the default stack size should be used. +// |priority|: The thread's priority. This value can be set to +// |kSbThreadNoPriority| to use the platform's default priority. As examples, +// it could be set to a fixed, standard priority or to a priority inherited +// from the thread that is calling SbThreadCreate(), or to something else. +// |affinity|: The thread's affinity. This value can be set to +// |kSbThreadNoAffinity| to use the platform's default affinity. +// |joinable|: Indicates whether the thread can be joined (|true|) or should +// start out "detached" (|false|). Note that for joinable threads, when +// you are done with the thread handle, you must call |SbThreadJoin| to +// release system resources associated with the thread. This is not necessary +// for detached threads, but detached threads cannot be joined. +// |name|: A name used to identify the thread. This value is used mainly for +// debugging, it can be |NULL|, and it might not be used in production builds. +// |entry_point|: A pointer to a function that will be executed on the newly +// created thread. +// |context|: This value will be passed to the |entry_point| function. SB_EXPORT SbThread SbThreadCreate(int64_t stack_size, SbThreadPriority priority, SbThreadAffinity affinity, @@ -152,73 +161,100 @@ SbThreadEntryPoint entry_point, void* context); -// Joins with joinable |thread|, created with SbThreadCreate. This function -// blocks the caller until the designated thread exits, and then cleans up that -// thread's resources. Returns true on success, false if |thread| is invalid or -// detached. This will essentially detach |thread|. Each joinable thread must be -// joined to be fully cleaned up. If |out_return| is not NULL, the return value -// of the thread's main function is placed there. +// Joins the thread on which this function is called with joinable |thread|. +// This function blocks the caller until the designated thread exits, and then +// cleans up that thread's resources. The cleanup process essentially detaches +// thread. // -// Each joinable thread can only be joined once. Once SbThreadJoin is called, -// the thread behaves as if it was detached to all threads other than the -// joining thread. +// The return value is |true| if the function is successful and |false| if +// |thread| is invalid or detached. +// +// Each joinable thread can only be joined once and must be joined to be fully +// cleaned up. Once SbThreadJoin is called, the thread behaves as if it were +// detached to all threads other than the joining thread. +// +// |thread|: The thread to which the current thread will be joined. The +// |thread| must have been created with SbThreadCreate. +// |out_return|: If this is not |NULL|, then the SbThreadJoin function populates +// it with the return value of the thread's |main| function. SB_EXPORT bool SbThreadJoin(SbThread thread, void** out_return); // Detaches |thread|, which prevents it from being joined. This is sort of like -// a non-blocking join. Does nothing if the thread is already detached, or is -// already being joined by another thread. +// a non-blocking join. This function is a no-op if the thread is already +// detached or if the thread is already being joined by another thread. +// +// |thread|: The thread to be detached. SB_EXPORT void SbThreadDetach(SbThread thread); // Yields the currently executing thread, so another thread has a chance to run. SB_EXPORT void SbThreadYield(); -// Sleeps the currently executing thread for at least the given |duration| in -// microseconds. A negative duration does nothing. +// Sleeps the currently executing thread. +// +// |duration|: The minimum amount of time, in microseconds, that the currently +// executing thread should sleep. The function is a no-op if this value is +// negative or |0|. SB_EXPORT void SbThreadSleep(SbTime duration); -// Gets the handle of the currently executing thread. +// Returns the handle of the currently executing thread. SB_EXPORT SbThread SbThreadGetCurrent(); -// Gets the Thread ID of the currently executing thread. +// Returns the Thread ID of the currently executing thread. SB_EXPORT SbThreadId SbThreadGetId(); -// Returns whether |thread1| and |thread2| refer to the same thread. +// Indicates whether |thread1| and |thread2| refer to the same thread. +// +// |thread1|: The first thread to compare. +// |thread2|: The second thread to compare. SB_EXPORT bool SbThreadIsEqual(SbThread thread1, SbThread thread2); -// Gets the debug name of the currently executing thread. +// Returns the debug name of the currently executing thread. SB_EXPORT void SbThreadGetName(char* buffer, int buffer_size); -// Sets the debug name of the currently executing thread. Will copy the +// Sets the debug name of the currently executing thread by copying the // specified name string. +// +// |name|: The name to assign to the thread. SB_EXPORT void SbThreadSetName(const char* name); -// Creates a new, unique key for thread local data with given optional -// |destructor|. |destructor| may be NULL if there is no clean up -// needed. Returns kSbThreadLocalKeyInvalid if creation was unsuccessful. +// Creates and returns a new, unique key for thread local data. If the function +// does not succeed, the function returns |kSbThreadLocalKeyInvalid|. // -// When does |destructor| get called? It can only be called in the owning -// thread, and let's just say thread interruption isn't viable. The destructor, -// if specified, is called on every thread's local values when the thread exits, -// if and only if the value in the key is non-NULL. +// If |destructor| is specified, it will be called in the owning thread, and +// only in the owning thread, when the thread exits. In that case, it is called +// on the local value associated with the key in the current thread as long as +// the local value is not NULL. +// +// |destructor|: A pointer to a function. The value may be NULL if no clean up +// is needed. SB_EXPORT SbThreadLocalKey SbThreadCreateLocalKey(SbThreadLocalDestructor destructor); -// Destroys thread local data |key|. Does nothing if key is -// kSbThreadLocalKeyInvalid or has already been destroyed. This does NOT call -// the destructor on any stored values. +// Destroys thread local data for the specified key. The function is a no-op +// if the key is invalid (kSbThreadLocalKeyInvalid|) or has already been +// destroyed. This function does NOT call the destructor on any stored values. +// +// |key|: The key for which to destroy thread local data. SB_EXPORT void SbThreadDestroyLocalKey(SbThreadLocalKey key); -// Gets the pointer-sized value for |key| in the currently executing thread's -// local storage. Returns NULL if key is kSbThreadLocalKeyInvalid or has already -// been destroyed. +// Returns the pointer-sized value for |key| in the currently executing thread's +// local storage. Returns |NULL| if key is |kSbThreadLocalKeyInvalid| or if the +// key has already been destroyed. +// +// |key|: The key for which to return the value. SB_EXPORT void* SbThreadGetLocalValue(SbThreadLocalKey key); // Sets the pointer-sized value for |key| in the currently executing thread's -// local storage. Returns whether |key| is valid and has not already been -// destroyed. +// local storage. The return value indicates whether |key| is valid and has +// not already been destroyed. +// +// |key|: The key for which to set the key value. +// |value|: The new pointer-sized key value. SB_EXPORT bool SbThreadSetLocalValue(SbThreadLocalKey key, void* value); // Returns whether |thread| is the current thread. +// +// |thread|: The thread to check. SB_C_INLINE bool SbThreadIsCurrent(SbThread thread) { return SbThreadGetCurrent() == thread; }
diff --git a/src/starboard/thread_types.h b/src/starboard/thread_types.h index 36c8868..0c3c3ff 100644 --- a/src/starboard/thread_types.h +++ b/src/starboard/thread_types.h
@@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Module Overview: Starboard Thread Types module +// // Defines platform-specific threading and synchronization primitive types and -// initializers. We hide but pass-through the platform's primitives to avoid +// initializers. We hide, but pass through, the platform's primitives to avoid // doing a lot of work to replicate initialization-less synchronization // primitives.
diff --git a/src/starboard/time.h b/src/starboard/time.h index e98a07e..bae41d4 100644 --- a/src/starboard/time.h +++ b/src/starboard/time.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Access to system time and timers. +// Module Overview: Starboard Time module +// +// Provides access to system time and timers. #ifndef STARBOARD_TIME_H_ #define STARBOARD_TIME_H_ @@ -60,28 +62,44 @@ // microseconds since the POSIX epoch. #define kSbTimeToPosixDelta (SB_INT64_C(-11644473600) * kSbTimeSecond) -// Converts an SbTime into microseconds from the POSIX epoch. +// Converts |SbTime| into microseconds from the POSIX epoch. +// +// |time|: A time that is either measured in microseconds since the epoch of +// January 1, 1601, UTC, or that measures the number of microseconds +// between two times. static SB_C_FORCE_INLINE int64_t SbTimeToPosix(SbTime time) { return time + kSbTimeToPosixDelta; } -// Converts microseconds from the POSIX epoch into an SbTime. +// Converts microseconds from the POSIX epoch into an |SbTime|. +// +// |time|: A time that measures the number of microseconds since +// January 1, 1970, 00:00:00, UTC. static SB_C_FORCE_INLINE SbTime SbTimeFromPosix(int64_t time) { return time - kSbTimeToPosixDelta; } -// Safely narrows a number from a more-precise unit to a less-precise one. This -// rounds negative values towards negative infinity. +// Safely narrows a number from a more precise unit to a less precise one. This +// function rounds negative values toward negative infinity. static SB_C_FORCE_INLINE int64_t SbTimeNarrow(int64_t time, int64_t divisor) { return time >= 0 ? time / divisor : (time - divisor + 1) / divisor; } -// Gets the current system time as a SbTime. +// Gets the current system time as an |SbTime|. SB_EXPORT SbTime SbTimeGetNow(); // Gets a monotonically increasing time representing right now. SB_EXPORT SbTimeMonotonic SbTimeGetMonotonicNow(); +#if SB_VERSION(3) && SB_HAS(TIME_THREAD_NOW) +// Gets a monotonically increasing time representing how long the current +// thread has been in the executing state (i.e. not pre-empted nor waiting +// on an event). This is not necessarily total time and is intended to allow +// measuring thread execution time between two timestamps. If this is not +// available then SbTimeGetMonotonicNow() should be used. +SB_EXPORT SbTimeMonotonic SbTimeGetMonotonicThreadNow(); +#endif + #ifdef __cplusplus } // extern "C" #endif
diff --git a/src/starboard/types.h b/src/starboard/types.h index 746689a..04a1263 100644 --- a/src/starboard/types.h +++ b/src/starboard/types.h
@@ -12,8 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -// A suite of standard types that should be universally available on all -// platforms, specifically focused on explicitly-sized integer types and +// Module Overview: Starboard Types module +// +// Provides a suite of standard types that should be universally available on +// all platforms, specifically focused on explicitly-sized integer types and // booleans. This module also includes some related ubiquitous definitions like // limits of the explicitly-sized integer types, and standard pointer and int32 // sentinel values.
diff --git a/src/starboard/user.h b/src/starboard/user.h index 04443ee..b05d7de 100644 --- a/src/starboard/user.h +++ b/src/starboard/user.h
@@ -12,9 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -// User management API. Platforms that do not have users must still implement -// this API, always reporting a single user current and signed in. - +// Module Overview: Starboard User module +// +// Defines a user management API. This module defines functions only for +// managing signed-in users. Platforms that do not have users must still +// implement this API, always reporting a single user that is current and +// signed in. +// // These APIs are NOT expected to be thread-safe, so either call them from a // single thread, or perform proper synchronization around all calls. @@ -81,29 +85,45 @@ return user != kSbUserInvalid; } -// Gets a list of up to |users_size| signed-in users, placing the results in -// |out_users|, and returning the number of actual users signed in, whether -// greater or less than |users_size|. It is expected that there will be a unique -// SbUser per signed in user, and that the referenced objects will persist for -// the lifetime of the app. +// Gets a list of up to |users_size| signed-in users and places the results in +// |out_users|. The return value identifies the actual number of signed-in +// users, which may be greater or less than |users_size|. +// +// It is expected that there will be a unique |SbUser| per signed-in user and +// that the referenced objects will persist for the lifetime of the app. +// +// |out_users|: Handles for the retrieved users. +// |users_size|: The maximum number of signed-in users to retrieve. SB_EXPORT int SbUserGetSignedIn(SbUser* out_users, int users_size); -// Gets the current primary user, if any. This is the user that is determined, -// in a platform specific way, to be the primary user controlling the -// application. This may be because that user launched the app, or using -// whatever criteria is appropriate for the platform. It is expected that there -// will be a unique SbUser per signed in user, and that the referenced objects -// will persist for the lifetime of the app. +// Gets the current primary user, if one exists. This is the user that is +// determined, in a platform-specific way, to be the primary user controlling +// the application. For example, the determination might be made because that +// user launched the app, though it should be made using whatever criteria are +// appropriate for the platform. +// +// It is expected that there will be a unique SbUser per signed-in user, and +// that the referenced objects will persist for the lifetime of the app. SB_EXPORT SbUser SbUserGetCurrent(); -// Gets the size of the value of |property_id| for |user|, INCLUDING the -// terminating null character. Returns 0 if if |user| is invalid, |property_id| -// isn't recognized, supported, or set for |user|. +// Returns the size of the value of |property_id| for |user|, INCLUDING the +// terminating null character. The function returns |0| if |user| is invalid +// or if |property_id| is not recognized, supported, or set for the user. +// +// |user|: The user for which property size data is being retrieved. +// |property_id|: The property for which the data is requested. SB_EXPORT int SbUserGetPropertySize(SbUser user, SbUserPropertyId property_id); -// Gets the value of |property_id| for |user|, and places it in |out_value|, -// returning false if |user| is invalid, |property_id| isn't recognized, -// supported, or set for |user|, or if |value_size| is too small. +// Retrieves the value of |property_id| for |user| and places it in |out_value|. +// The function returns: +// - |true| if the property value is retrieved successfully +// - |false| if |user| is invalid; if |property_id| isn't recognized, supported, +// or set for |user|; or if |value_size| is too small. +// +// |user|: The user for which property size data is being retrieved. +// |property_id|: The property for which the data is requested. +// |out_value|: The retrieved property value. +// |value_size|: The size of the retrieved property value. SB_EXPORT bool SbUserGetProperty(SbUser user, SbUserPropertyId property_id, char* out_value,
diff --git a/src/starboard/window.h b/src/starboard/window.h index 44bb3e1..370109e 100644 --- a/src/starboard/window.h +++ b/src/starboard/window.h
@@ -12,7 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Display Window creation and management. +// Module Overview: Starboard Window module +// +// Provides functionality to handle Window creation and management. #ifndef STARBOARD_WINDOW_H_ #define STARBOARD_WINDOW_H_ @@ -81,39 +83,52 @@ return window != kSbWindowInvalid; } -// Creates a new system window with the given |options|, which may be -// NULL. Returns kSbWindowInvalid if unable to create the requested SbWindow, -// either due to policy, or unsatisfiable options. +// Creates and returns a new system window with the given |options|, which may +// be |NULL|. The function returns |kSbWindowInvalid| if it cannot create the +// requested |SbWindow| due to policy, unsatisfiable options, or any other +// reason. // -// If options are not specified, this function will use all defaults, which must +// If |options| are not specified, this function uses all defaults, which must // work on every platform. In general, it defaults to creating a fullscreen -// window at the highest 16:9 resolution that it can. If the platform does not -// support fullscreen windows, then it will create a normal windowed window. +// window at the highest 16:9 resolution possible. If the platform does not +// support fullscreen windows, then it creates a normal, windowed window. // -// Some devices are fullscreen-only (most of the production targets for -// Starboard). In those cases, only one SbWindow may be created, and it must be -// fullscreen. Additionally, the requested size will actually be the requested -// resolution, and must be a supported resolution, as specified by the -// resolutions returned by SbWindowGetSupportedResolutionIterator(). +// Some devices are fullscreen-only, including many production targets for +// Starboard. In those cases, only one SbWindow may be created, and it must be +// fullscreen. Additionally, in those cases, the requested size will actually +// be the requested resolution. // -// A SbWindow must be created in order to receive window-based events, like -// input events, even on fullscreen-only devices. These will be dispatched to +// An SbWindow must be created to receive window-based events, like input +// events, even on fullscreen-only devices. These events are dispatched to // the Starboard entry point. +// +// |options|: Options that specify parameters for the window being created. SB_EXPORT SbWindow SbWindowCreate(const SbWindowOptions* options); -// Sets |options| to all the defaults. |options| must not be NULL. +// Sets the default options for system windows. +// +// |options|: The option values to use as default values. This object must not +// be |NULL|. SB_EXPORT void SbWindowSetDefaultOptions(SbWindowOptions* options); // Destroys |window|, reclaiming associated resources. +// +// |window|: The |SbWindow| to destroy. SB_EXPORT bool SbWindowDestroy(SbWindow window); -// Sets |size| to the dimensions of |window|. Returns true on success. If false -// is returned, |size| will not be modified. +// Retrieves the dimensions of |window| and sets |size| accordingly. This +// function returns |true| if it completes successfully. If the function +// returns |false|, then |size| will not have been modified. +// +// |window|: The SbWindow to retrieve the size of. +// |size|: The retrieved size. SB_EXPORT bool SbWindowGetSize(SbWindow window, SbWindowSize* size); // Gets the platform-specific handle for |window|, which can be passed as an // EGLNativeWindowType to initialize EGL/GLES. This return value is entirely -// platform specific, so there are no constraints about expected ranges. +// platform-specific, so there are no constraints about expected ranges. +// +// |window|: The SbWindow to retrieve the platform handle for. SB_EXPORT void* SbWindowGetPlatformHandle(SbWindow window); #ifdef __cplusplus
diff --git a/src/third_party/harfbuzz-ng/src/hb-starboard.hh b/src/third_party/harfbuzz-ng/src/hb-starboard.hh index e235306..4ba44c3 100644 --- a/src/third_party/harfbuzz-ng/src/hb-starboard.hh +++ b/src/third_party/harfbuzz-ng/src/hb-starboard.hh
@@ -23,7 +23,7 @@ #define hb_malloc_impl SbMemoryAllocate #define hb_realloc_impl SbMemoryReallocate #define hb_calloc_impl SbMemoryCalloc -#define hb_free_impl SbMemoryFree +#define hb_free_impl SbMemoryDeallocate // Micro Posix Emulation #define assert SB_DCHECK
diff --git a/src/third_party/libevent/libevent.gyp b/src/third_party/libevent/libevent.gyp index d82ba5c..2a7bf21 100644 --- a/src/third_party/libevent/libevent.gyp +++ b/src/third_party/libevent/libevent.gyp
@@ -68,6 +68,16 @@ ], }, }], + # TODO: Make this android specific, not a linux copy. + [ 'target_os == "android"', { + 'sources': [ 'epoll_sub.c' ], + 'include_dirs': [ 'starboard/linux' ], + 'link_settings': { + 'libraries': [ + '-lrt', + ], + }, + }], [ 'target_os == "orbis"', { 'include_dirs': [ 'starboard/ps4' ], }
diff --git a/src/third_party/mozjs/js/src/jit/Ion.cpp b/src/third_party/mozjs/js/src/jit/Ion.cpp index 66cce66..2f00859 100644 --- a/src/third_party/mozjs/js/src/jit/Ion.cpp +++ b/src/third_party/mozjs/js/src/jit/Ion.cpp
@@ -2031,7 +2031,8 @@ IonExecStatus jit::Cannon(JSContext *cx, RunState &state) { - IonScript *ion = state.script()->ionScript(); + JSScript *script = state.script(); + IonScript *ion = script->ionScript(); EnterJitData data(cx); data.jitcode = ion->method()->raw();
diff --git a/src/third_party/mozjs/js/src/jit/arm/Architecture-arm.cpp b/src/third_party/mozjs/js/src/jit/arm/Architecture-arm.cpp index a5b04e8..770fd12 100644 --- a/src/third_party/mozjs/js/src/jit/arm/Architecture-arm.cpp +++ b/src/third_party/mozjs/js/src/jit/arm/Architecture-arm.cpp
@@ -99,6 +99,13 @@ return flags; #endif +#if defined(__GNUC__) && defined(__VFP_FP__) + // Mimic behavior of JSC::isVFPPresent(). + // On ARMv6, lack of this feature will prevent the ion jit path. + flags |= HWCAP_VFP; + return flags; +#endif + return false; }
diff --git a/src/third_party/mozjs/mozjs.gyp b/src/third_party/mozjs/mozjs.gyp index 8895e64..3de7f79 100644 --- a/src/third_party/mozjs/mozjs.gyp +++ b/src/third_party/mozjs/mozjs.gyp
@@ -88,6 +88,11 @@ 'JS_DEBUG', ], }], + [ 'cobalt_config != "gold"', { + 'defines': [ + 'JS_TRACE_LOGGING=1', + ], + }], ], }, 'targets': [
diff --git a/src/third_party/super_fast_hash/LICENSE b/src/third_party/super_fast_hash/LICENSE new file mode 100644 index 0000000..3f9e016 --- /dev/null +++ b/src/third_party/super_fast_hash/LICENSE
@@ -0,0 +1,28 @@ +// Copyright (c) 2010, Paul Hsieh +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither my name, Paul Hsieh, nor the names of any other contributors to the +// code use may not be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// From http://www.azillionmonkeys.com/qed/hash.html \ No newline at end of file
diff --git a/src/third_party/super_fast_hash/README.chromium b/src/third_party/super_fast_hash/README.chromium new file mode 100644 index 0000000..5e8134e --- /dev/null +++ b/src/third_party/super_fast_hash/README.chromium
@@ -0,0 +1,13 @@ +Name: SuperFastHash +Short Name: super_fast_hash +URL: http://www.azillionmonkeys.com/qed/hash.html +Version: unknown +License: BSD 3-Clause +Security Critical: yes + +Description: +The super_hash_file was modified to use the big/little endian platform +defines provided by starboard/configuration.h. Therefore the hash function +definition should be cc included. Additionally, a previous hash value can +be used if this hash is being chained. +Example: #include "third_party/super_fast_hash.cc"
diff --git a/src/third_party/super_fast_hash/super_fast_hash.cc b/src/third_party/super_fast_hash/super_fast_hash.cc new file mode 100644 index 0000000..6618791 --- /dev/null +++ b/src/third_party/super_fast_hash/super_fast_hash.cc
@@ -0,0 +1,88 @@ +// Copyright (c) 2010, Paul Hsieh +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither my name, Paul Hsieh, nor the names of any other contributors to the +// code use may not be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +// From http://www.azillionmonkeys.com/qed/hash.html + + +#if SB_IS(BIG_ENDIAN) +#define get16bits(d) (*((const uint16_t *) (d))) +#else +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif + +uint32_t SuperFastHash(const char* data, int len, uint32_t prev_hash) { + uint32_t hash = prev_hash, tmp; + int rem; + + if (len <= 0 || data == NULL) + return 0; + + rem = len & 3; + len >>= 2; + + /* Main loop */ + for (; len > 0; len--) { + hash += get16bits(data); + tmp = (get16bits(data + 2) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2 * sizeof(uint16_t); + hash += hash >> 11; + } + + /* Handle end cases */ + switch (rem) { + case 3: + hash += get16bits(data); + hash ^= hash << 16; + + // Treat the final character as signed. This ensures all platforms behave + // consistently with the original x86 code. + hash ^= static_cast<signed char>(data[sizeof(uint16_t)]) << 18; + hash += hash >> 11; + break; + case 2: + hash += get16bits(data); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: + hash += static_cast<signed char>(*data); + hash ^= hash << 10; + hash += hash >> 1; + } + + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; +} \ No newline at end of file