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