Import Cobalt.21.master.0.282262
Includes the following patches:
https://cobalt-review.googlesource.com/c/cobalt/+/5810
by rongo.li@mstar.corp-partner.google.com
diff --git a/src/.pre-commit-config.yaml b/src/.pre-commit-config.yaml
new file mode 100644
index 0000000..440ebc1
--- /dev/null
+++ b/src/.pre-commit-config.yaml
@@ -0,0 +1,54 @@
+# See https://pre-commit.com for more information
+# See https://pre-commit.com/hooks.html for more hooks
+default_stages: [commit]
+repos:
+- repo: local
+ hooks:
+ - id: cpplint
+ name: cpplint
+ entry: cpplint
+ language: system
+ types: [c++]
+ args: [--verbose=4, --quiet]
+ exclude: '.*tests?.(cc|h)$'
+ stages: [push]
+ - id: cpplint_test
+ name: cpplint_test
+ entry: cpplint
+ language: system
+ types: [c++]
+ args: [--verbose=5, --quiet]
+ files: '.*tests?.(cc|h)$'
+ stages: [push]
+ - id: yapf
+ name: yapf
+ entry: yapf
+ language: system
+ types: [python]
+ args: [--style=yapf, -i]
+ - id: pylint
+ name: pylint
+ entry: pylint
+ language: system
+ types: [python]
+ args: [-d W0201]
+ stages: [push]
+ - id: clang-format
+ name: clang-format
+ entry: third_party/precommit-hooks/clang-format_wrapper.py
+ language: python
+ types: [c++]
+ args: [-i, -style=file]
+ - id: google-java-format
+ name: google-java-format
+ entry: third_party/precommit-hooks/google-java-format_wrapper.py
+ language: python
+ types: [java]
+ args: [-i]
+ - id: gcheckstyle
+ name: gcheckstyle
+ entry: third_party/precommit-hooks/gcheckstyle_wrapper.py
+ language: python
+ types: [java]
+ verbose: true
+ stages: [push]
diff --git a/src/.pylintrc b/src/.pylintrc
new file mode 100644
index 0000000..77b0063
--- /dev/null
+++ b/src/.pylintrc
@@ -0,0 +1,305 @@
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add files or directories to the blacklist. They should be base names, not
+# paths.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable the message, report, category or checker with the given id(s). You can
+# either give multiple identifier separated by comma (,) or put this option
+# multiple time.
+#enable=
+
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifier separated by comma (,) or put this option
+# multiple time (only on the command line, not in the configuration file where
+# it should appear only once).
+# CHANGED:
+# C0103: Invalid name ""
+# C0111: Missing docstring
+# C0302: Too many lines in module (N)
+# I0010: Unable to consider inline option ''
+# I0011: Locally disabling WNNNN
+#
+# R0801: Similar lines in N files
+# R0901: Too many ancestors (8/7)
+# R0902: Too many instance attributes (N/7)
+# R0903: Too few public methods (N/2)
+# R0904: Too many public methods (N/20)
+# R0911: Too many return statements (N/6)
+# R0912: Too many branches (N/12)
+# R0913: Too many arguments (N/5)
+# R0914: Too many local variables (N/15)
+# R0915: Too many statements (N/50)
+# R0921: Abstract class not referenced
+# R0922: Abstract class is only referenced 1 times
+# W0122: Use of the exec statement
+# W0141: Used builtin function ''
+# W0142: Used * or ** magic
+# W0402: Uses of a deprecated module 'string'
+# W0404: 41: Reimport 'XX' (imported line NN)
+# W0511: TODO
+# W0603: Using the global statement
+# W0703: Catch "Exception"
+# W1201: Specify string format arguments as logging function parameters
+#
+# These should get enabled, but the codebase has too many violations currently.
+# bad-continuation
+# anomalous-backslash-in-string
+# bad-context-manager
+# bad-indentation
+# bad-str-strip-call
+# bad-whitespace
+# cell-var-from-loop
+# deprecated-lambda
+# eval-used
+# function-redefined
+# import-error
+# locally-enabled
+# missing-final-newline
+# no-init
+# no-name-in-module
+# no-self-use
+# not-callable
+# old-style-class
+# protected-access
+# superfluous-parens
+# super-on-old-class
+# too-many-function-args
+# trailing-whitespace
+# unnecessary-semicolon
+# unpacking-non-sequence
+# unused-import
+# useless-else-on-loop
+disable=C0103,C0111,C0302,I0010,I0011,R0801,R0901,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0921,R0922,W0122,W0141,W0142,W0402,W0404,W0511,W0603,W0703,W1201,bad-continuation,anomalous-backslash-in-string,bad-context-manager,bad-indentation,bad-str-strip-call,bad-whitespace,cell-var-from-loop,deprecated-lambda,eval-used,function-redefined,import-error,locally-enabled,missing-final-newline,no-init,no-name-in-module,no-self-use,not-callable,old-style-class,protected-access,superfluous-parens,super-on-old-class,too-many-function-args,trailing-whitespace,unnecessary-semicolon,unpacking-non-sequence,unused-import,useless-else-on-loop
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+# CHANGED:
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (RP0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (RP0004).
+comment=no
+
+
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching the beginning of the name of dummy variables
+# (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject,twisted.internet.reactor,hashlib,google.appengine.api.memcache
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed. Python regular
+# expressions are accepted.
+generated-members=REQUEST,acl_users,aq_parent,multiprocessing.managers.SyncManager
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+# CHANGED:
+indent-string=' '
+
+
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Argument names that match this expression will be ignored. Default to name
+# with leading underscore
+ignored-argument-names=_.*
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report RP0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report RP0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report RP0402 must
+# not be disabled)
+int-import-graph=
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when being caught. Defaults to
+# "Exception"
+overgeneral-exceptions=Exception
diff --git a/src/base/files/file_util.cc b/src/base/files/file_util.cc
index cb9a0d5..d8e445a 100644
--- a/src/base/files/file_util.cc
+++ b/src/base/files/file_util.cc
@@ -21,8 +21,12 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"
+
+#if defined(STARBOARD)
+#include "starboard/common/file.h"
#include "starboard/memory.h"
#include "starboard/types.h"
+#endif
namespace base {
diff --git a/src/base/memory/ref_counted.h b/src/base/memory/ref_counted.h
index f52e803..c19072a 100644
--- a/src/base/memory/ref_counted.h
+++ b/src/base/memory/ref_counted.h
@@ -26,7 +26,6 @@
public:
bool HasOneRef() const { return ref_count_ == 1; }
bool HasAtLeastOneRef() const { return ref_count_ >= 1; }
- int RefCounts() const { return ref_count_; }
protected:
explicit RefCountedBase(StartRefCountFromZeroTag) {
diff --git a/src/base/metrics/persistent_histogram_storage.cc b/src/base/metrics/persistent_histogram_storage.cc
index 8abb646..3801baf 100644
--- a/src/base/metrics/persistent_histogram_storage.cc
+++ b/src/base/metrics/persistent_histogram_storage.cc
@@ -15,7 +15,7 @@
#include "build/build_config.h"
#if defined(STARBOARD)
-#include "starboard/file.h"
+#include "starboard/common/file.h"
#endif
namespace {
diff --git a/src/cobalt/CHANGELOG.md b/src/cobalt/CHANGELOG.md
index 04eb0a6..f2872b4 100644
--- a/src/cobalt/CHANGELOG.md
+++ b/src/cobalt/CHANGELOG.md
@@ -102,6 +102,23 @@
(https://lottiefiles.com/web-player). In order to support Lottie, Cobalt
updated its Skia port from m61 to m79.
+ - **Added support for MediaKeySystemMediaCapability.encryptionScheme.**
+
+ Cobalt now supports `MediaKeySystemMediaCapability.encryptionScheme` for
+ `Navigator.requestMediaKeySystemAccess()`. `encryptionScheme` can be 'cenc',
+ 'cbcs', or 'cbcs-1-9'.
+ The default implementation assumes that:
+ 1. When the Widevine DRM system is used, all the above encryption schemes
+ should be supported across all containers and codecs supported by the
+ platform.
+ 2. When the PlayReady DRM system is used, only 'cenc' is supported across all
+ containers and codecs supported by the platform.
+
+ It is possible to customize this behavior via an extension to
+ `SbMediaCanPlayMimeAndKeySystem()`. Please see the Starboard change log and
+ the comment of `SbMediaCanPlayMimeAndKeySystem()` in `media.h` for more
+ details.
+
## Version 20
- **Support for QUIC and SPDY is now enabled.**
diff --git a/src/cobalt/audio/async_audio_decoder.cc b/src/cobalt/audio/async_audio_decoder.cc
index 174cc57..c7885a1 100644
--- a/src/cobalt/audio/async_audio_decoder.cc
+++ b/src/cobalt/audio/async_audio_decoder.cc
@@ -38,7 +38,7 @@
decode_finish_callback.Run(reader->sample_rate(),
reader->ResetAndReturnAudioBus());
} else {
- decode_finish_callback.Run(0.f, std::unique_ptr<ShellAudioBus>());
+ decode_finish_callback.Run(0.f, std::unique_ptr<AudioBus>());
}
}
diff --git a/src/cobalt/audio/async_audio_decoder.h b/src/cobalt/audio/async_audio_decoder.h
index ced6fed..db8a15a 100644
--- a/src/cobalt/audio/async_audio_decoder.h
+++ b/src/cobalt/audio/async_audio_decoder.h
@@ -28,7 +28,7 @@
class AsyncAudioDecoder {
public:
typedef base::Callback<void(float sample_rate,
- std::unique_ptr<ShellAudioBus> audio_bus)>
+ std::unique_ptr<AudioBus> audio_bus)>
DecodeFinishCallback;
AsyncAudioDecoder();
diff --git a/src/cobalt/audio/audio_buffer.cc b/src/cobalt/audio/audio_buffer.cc
index 1b6ff38..0a4c203 100644
--- a/src/cobalt/audio/audio_buffer.cc
+++ b/src/cobalt/audio/audio_buffer.cc
@@ -25,8 +25,7 @@
namespace cobalt {
namespace audio {
-AudioBuffer::AudioBuffer(float sample_rate,
- std::unique_ptr<ShellAudioBus> audio_bus)
+AudioBuffer::AudioBuffer(float sample_rate, std::unique_ptr<AudioBus> audio_bus)
: sample_rate_(sample_rate), audio_bus_(std::move(audio_bus)) {
DCHECK_GT(sample_rate_, 0);
DCHECK_GT(length(), 0);
diff --git a/src/cobalt/audio/audio_buffer.h b/src/cobalt/audio/audio_buffer.h
index 9028a2b..d20beed 100644
--- a/src/cobalt/audio/audio_buffer.h
+++ b/src/cobalt/audio/audio_buffer.h
@@ -39,7 +39,7 @@
// https://www.w3.org/TR/webaudio/#AudioBuffer
class AudioBuffer : public script::Wrappable {
public:
- AudioBuffer(float sample_rate, std::unique_ptr<ShellAudioBus> audio_bus);
+ AudioBuffer(float sample_rate, std::unique_ptr<AudioBus> audio_bus);
// Web API: AudioBuffer
//
@@ -60,14 +60,14 @@
// Custom, not in any spec
//
- ShellAudioBus* audio_bus() { return audio_bus_.get(); }
+ AudioBus* audio_bus() { return audio_bus_.get(); }
DEFINE_WRAPPABLE_TYPE(AudioBuffer);
private:
const float sample_rate_;
- std::unique_ptr<ShellAudioBus> audio_bus_;
+ std::unique_ptr<AudioBus> audio_bus_;
DISALLOW_COPY_AND_ASSIGN(AudioBuffer);
};
diff --git a/src/cobalt/audio/audio_buffer_source_node.cc b/src/cobalt/audio/audio_buffer_source_node.cc
index 654e6a8..df8ca6b 100644
--- a/src/cobalt/audio/audio_buffer_source_node.cc
+++ b/src/cobalt/audio/audio_buffer_source_node.cc
@@ -27,7 +27,7 @@
namespace audio {
typedef media::InterleavedSincResampler InterleavedSincResampler;
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
// numberOfInputs : 0
// numberOfOutputs : 1
@@ -115,7 +115,7 @@
state_ = kStopped;
}
-std::unique_ptr<ShellAudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
+std::unique_ptr<AudioBus> AudioBufferSourceNode::PassAudioBusFromSource(
int32 number_of_frames, SampleType sample_type, bool* finished) {
DCHECK_GT(number_of_frames, 0);
DCHECK(finished);
@@ -126,7 +126,7 @@
*finished = false;
if (state_ == kNone || !buffer_) {
- return std::unique_ptr<ShellAudioBus>();
+ return std::unique_ptr<AudioBus>();
}
if (state_ == kStopped ||
@@ -140,7 +140,7 @@
base::Unretained(this)));
buffer_source_added_ = false;
}
- return std::unique_ptr<ShellAudioBus>();
+ return std::unique_ptr<AudioBus>();
}
DCHECK_EQ(state_, kStarted);
@@ -151,22 +151,22 @@
int32 frames_to_end = buffer_->length() - read_index_;
int32 channel_count = static_cast<int32>(audio_bus->channels());
- std::unique_ptr<ShellAudioBus> result;
+ std::unique_ptr<AudioBus> result;
if (!interleaved_resampler_) {
int32 audio_bus_frames = std::min(number_of_frames, frames_to_end);
if (sample_type == kSampleTypeInt16) {
- result.reset(new ShellAudioBus(
- channel_count, audio_bus_frames,
- reinterpret_cast<int16*>(audio_bus->interleaved_data()) +
- read_index_ * channel_count));
+ result.reset(
+ new AudioBus(channel_count, audio_bus_frames,
+ reinterpret_cast<int16*>(audio_bus->interleaved_data()) +
+ read_index_ * channel_count));
} else {
DCHECK_EQ(sample_type, kSampleTypeFloat32);
- result.reset(new ShellAudioBus(
- channel_count, audio_bus_frames,
- reinterpret_cast<float*>(audio_bus->interleaved_data()) +
- read_index_ * channel_count));
+ result.reset(
+ new AudioBus(channel_count, audio_bus_frames,
+ reinterpret_cast<float*>(audio_bus->interleaved_data()) +
+ read_index_ * channel_count));
}
read_index_ += audio_bus_frames;
return result;
@@ -219,8 +219,8 @@
interleaved_resampler_->Resample(interleaved_output.get(),
number_of_frames);
- result.reset(new ShellAudioBus(channel_count, number_of_frames,
- kSampleTypeInt16, kStorageTypeInterleaved));
+ result.reset(new AudioBus(channel_count, number_of_frames, kSampleTypeInt16,
+ kStorageTypeInterleaved));
for (int32 i = 0; i < channel_count * number_of_frames; ++i) {
uint8* dest_ptr = result->interleaved_data() + sizeof(int16) * i;
*reinterpret_cast<int16*>(dest_ptr) =
@@ -229,9 +229,8 @@
} else {
DCHECK_EQ(sample_type, kSampleTypeFloat32);
- result.reset(new ShellAudioBus(channel_count, number_of_frames,
- kSampleTypeFloat32,
- kStorageTypeInterleaved));
+ result.reset(new AudioBus(channel_count, number_of_frames,
+ kSampleTypeFloat32, kStorageTypeInterleaved));
interleaved_resampler_->Resample(
reinterpret_cast<float*>(result->interleaved_data()), number_of_frames);
}
diff --git a/src/cobalt/audio/audio_buffer_source_node.h b/src/cobalt/audio/audio_buffer_source_node.h
index 1731aef..b01f54c 100644
--- a/src/cobalt/audio/audio_buffer_source_node.h
+++ b/src/cobalt/audio/audio_buffer_source_node.h
@@ -22,8 +22,8 @@
#include "cobalt/audio/audio_buffer.h"
#include "cobalt/audio/audio_node.h"
#include "cobalt/base/tokens.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media/base/interleaved_sinc_resampler.h"
-#include "cobalt/media/base/shell_audio_bus.h"
#include "cobalt/script/environment_settings.h"
namespace cobalt {
@@ -35,7 +35,7 @@
// https://www.w3.org/TR/webaudio/#AudioBufferSourceNode
class AudioBufferSourceNode : public AudioNode {
typedef media::InterleavedSincResampler InterleavedSincResampler;
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
AudioBufferSourceNode(script::EnvironmentSettings* settings,
@@ -75,8 +75,9 @@
SetAttributeEventListener(base::Tokens::ended(), event_listener);
}
- std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type, bool* finished) override;
+ std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type,
+ bool* finished) override;
DEFINE_WRAPPABLE_TYPE(AudioBufferSourceNode);
void TraceMembers(script::Tracer* tracer) override;
diff --git a/src/cobalt/audio/audio_context.cc b/src/cobalt/audio/audio_context.cc
index e909b72..6c4808e 100644
--- a/src/cobalt/audio/audio_context.cc
+++ b/src/cobalt/audio/audio_context.cc
@@ -64,7 +64,7 @@
DCHECK(main_message_loop_->BelongsToCurrentThread());
return scoped_refptr<AudioBuffer>(new AudioBuffer(
- sample_rate, std::unique_ptr<ShellAudioBus>(new ShellAudioBus(
+ sample_rate, std::unique_ptr<AudioBus>(new AudioBus(
num_of_channels, length, GetPreferredOutputSampleType(),
kStorageTypeInterleaved))));
}
@@ -151,7 +151,7 @@
// Success callback and error callback should be scheduled to run on the main
// thread's event loop.
void AudioContext::DecodeFinish(int callback_id, float sample_rate,
- std::unique_ptr<ShellAudioBus> audio_bus) {
+ std::unique_ptr<AudioBus> audio_bus) {
if (!main_message_loop_->BelongsToCurrentThread()) {
main_message_loop_->PostTask(
FROM_HERE,
diff --git a/src/cobalt/audio/audio_context.h b/src/cobalt/audio/audio_context.h
index a4667bf..536b2bd 100644
--- a/src/cobalt/audio/audio_context.h
+++ b/src/cobalt/audio/audio_context.h
@@ -190,7 +190,7 @@
void DecodeAudioDataInternal(std::unique_ptr<DecodeCallbackInfo> info);
void DecodeFinish(int callback_id, float sample_rate,
- std::unique_ptr<ShellAudioBus> audio_bus);
+ std::unique_ptr<AudioBus> audio_bus);
script::GlobalEnvironment* global_environment_;
diff --git a/src/cobalt/audio/audio_destination_node.cc b/src/cobalt/audio/audio_destination_node.cc
index 4107070..e35f1f9 100644
--- a/src/cobalt/audio/audio_destination_node.cc
+++ b/src/cobalt/audio/audio_destination_node.cc
@@ -64,8 +64,7 @@
audio_device_to_delete_ = NULL;
}
-void AudioDestinationNode::FillAudioBus(bool all_consumed,
- ShellAudioBus* audio_bus,
+void AudioDestinationNode::FillAudioBus(bool all_consumed, AudioBus* audio_bus,
bool* silence) {
// This is called on Audio thread.
AudioLock::AutoLock lock(audio_lock());
diff --git a/src/cobalt/audio/audio_destination_node.h b/src/cobalt/audio/audio_destination_node.h
index 8744034..b758d92 100644
--- a/src/cobalt/audio/audio_destination_node.h
+++ b/src/cobalt/audio/audio_destination_node.h
@@ -22,7 +22,7 @@
#include "cobalt/audio/audio_device.h"
#include "cobalt/audio/audio_helpers.h"
#include "cobalt/audio/audio_node.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/script/environment_settings.h"
namespace cobalt {
@@ -37,7 +37,7 @@
// https://www.w3.org/TR/webaudio/#AudioDestinationNode
class AudioDestinationNode : public AudioNode,
public AudioDevice::RenderCallback {
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
AudioDestinationNode(script::EnvironmentSettings* settings,
@@ -51,14 +51,15 @@
// From AudioNode.
void OnInputNodeConnected() override;
- std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type, bool* finished) override {
+ std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type,
+ bool* finished) override {
NOTREACHED();
- return std::unique_ptr<ShellAudioBus>();
+ return std::unique_ptr<AudioBus>();
}
// From AudioDevice::RenderCallback.
- void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+ void FillAudioBus(bool all_consumed, AudioBus* audio_bus,
bool* silence) override;
DEFINE_WRAPPABLE_TYPE(AudioDestinationNode);
diff --git a/src/cobalt/audio/audio_device.cc b/src/cobalt/audio/audio_device.cc
index d253f51..b177d07 100644
--- a/src/cobalt/audio/audio_device.cc
+++ b/src/cobalt/audio/audio_device.cc
@@ -27,7 +27,7 @@
namespace cobalt {
namespace audio {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
namespace {
const int kRenderBufferSizeFrames = 1024;
@@ -69,7 +69,7 @@
// The |render_callback_| returns audio data in planar form. So we read it
// into |input_audio_bus_| and convert it into interleaved form and store in
// |output_frame_buffer_|.
- ShellAudioBus input_audio_bus_;
+ AudioBus input_audio_bus_;
std::unique_ptr<uint8[]> output_frame_buffer_;
@@ -102,7 +102,7 @@
#endif // SB_API_VERSION >= 11
input_audio_bus_(static_cast<size_t>(number_of_channels),
static_cast<size_t>(kRenderBufferSizeFrames),
- GetPreferredOutputSampleType(), ShellAudioBus::kPlanar),
+ GetPreferredOutputSampleType(), AudioBus::kPlanar),
output_frame_buffer_(
new uint8[frames_per_channel_ * number_of_channels_ *
GetStarboardSampleTypeSize(output_sample_type_)]) {
@@ -226,8 +226,8 @@
for (size_t channel = 0; channel < input_audio_bus_.channels(); ++channel) {
*output_buffer = ConvertSample<InputType, OutputType>(
input_audio_bus_
- .GetSampleForType<InputType, media::ShellAudioBus::kPlanar>(
- channel, frame));
+ .GetSampleForType<InputType, media::AudioBus::kPlanar>(channel,
+ frame));
++output_buffer;
}
}
@@ -237,7 +237,7 @@
TRACE_EVENT0("cobalt::audio", "AudioDevice::Impl::FillOutputAudioBus()");
const bool is_input_int16 =
- input_audio_bus_.sample_type() == media::ShellAudioBus::kInt16;
+ input_audio_bus_.sample_type() == media::AudioBus::kInt16;
const bool is_output_int16 =
output_sample_type_ == kSbMediaAudioSampleTypeInt16Deprecated;
diff --git a/src/cobalt/audio/audio_device.h b/src/cobalt/audio/audio_device.h
index b7974a7..1ccc7f9 100644
--- a/src/cobalt/audio/audio_device.h
+++ b/src/cobalt/audio/audio_device.h
@@ -19,7 +19,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
namespace cobalt {
namespace audio {
@@ -28,7 +28,7 @@
public:
class RenderCallback {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
// |all_consumed| will be set to true if all audio frames has been consumed.
// This gives the AudioDestinationNode a chance to decide if the AudioDevice
@@ -38,7 +38,7 @@
// |silence| will be set to true before calling if |audio_buffer| contains
// only silence samples, it will be set to |false| otherwise. It will be
// set to false on return if |audio_buffer| has been modified.
- virtual void FillAudioBus(bool all_consumed, ShellAudioBus* audio_buffer,
+ virtual void FillAudioBus(bool all_consumed, AudioBus* audio_buffer,
bool* silence) = 0;
protected:
diff --git a/src/cobalt/audio/audio_file_reader.h b/src/cobalt/audio/audio_file_reader.h
index f53eae4..6a20209 100644
--- a/src/cobalt/audio/audio_file_reader.h
+++ b/src/cobalt/audio/audio_file_reader.h
@@ -35,7 +35,7 @@
virtual int32 number_of_channels() const = 0;
virtual SampleType sample_type() const = 0;
- virtual std::unique_ptr<ShellAudioBus> ResetAndReturnAudioBus() = 0;
+ virtual std::unique_ptr<AudioBus> ResetAndReturnAudioBus() = 0;
};
} // namespace audio
diff --git a/src/cobalt/audio/audio_file_reader_wav.cc b/src/cobalt/audio/audio_file_reader_wav.cc
index 94c0bf9..f92b8bd 100644
--- a/src/cobalt/audio/audio_file_reader_wav.cc
+++ b/src/cobalt/audio/audio_file_reader_wav.cc
@@ -192,9 +192,8 @@
static_cast<int32>(size / (bytes_per_src_sample * number_of_channels_));
// We store audio samples in the current platform's preferred format.
- audio_bus_.reset(new ShellAudioBus(number_of_channels_, number_of_frames_,
- sample_type_,
- ShellAudioBus::kInterleaved));
+ audio_bus_.reset(new AudioBus(number_of_channels_, number_of_frames_,
+ sample_type_, AudioBus::kInterleaved));
// Both the source data and the destination data are stored in interleaved.
#if SB_IS(LITTLE_ENDIAN)
diff --git a/src/cobalt/audio/audio_file_reader_wav.h b/src/cobalt/audio/audio_file_reader_wav.h
index 460f268..bf3b95a 100644
--- a/src/cobalt/audio/audio_file_reader_wav.h
+++ b/src/cobalt/audio/audio_file_reader_wav.h
@@ -36,7 +36,7 @@
int32 number_of_channels() const override { return number_of_channels_; }
SampleType sample_type() const override { return sample_type_; }
- std::unique_ptr<ShellAudioBus> ResetAndReturnAudioBus() override {
+ std::unique_ptr<AudioBus> ResetAndReturnAudioBus() override {
return std::move(audio_bus_);
}
@@ -52,7 +52,7 @@
bool is_valid() { return audio_bus_ != NULL; }
- std::unique_ptr<ShellAudioBus> audio_bus_;
+ std::unique_ptr<AudioBus> audio_bus_;
float sample_rate_;
int32 number_of_frames_;
int32 number_of_channels_;
diff --git a/src/cobalt/audio/audio_helpers.h b/src/cobalt/audio/audio_helpers.h
index b4dd6f9..dc98086 100644
--- a/src/cobalt/audio/audio_helpers.h
+++ b/src/cobalt/audio/audio_helpers.h
@@ -15,7 +15,7 @@
#ifndef COBALT_AUDIO_AUDIO_HELPERS_H_
#define COBALT_AUDIO_AUDIO_HELPERS_H_
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#if defined(OS_STARBOARD)
#include "starboard/audio_sink.h"
@@ -25,13 +25,13 @@
namespace cobalt {
namespace audio {
-typedef media::ShellAudioBus ShellAudioBus;
-typedef media::ShellAudioBus::SampleType SampleType;
-typedef media::ShellAudioBus::StorageType StorageType;
-const SampleType kSampleTypeInt16 = media::ShellAudioBus::kInt16;
-const SampleType kSampleTypeFloat32 = media::ShellAudioBus::kFloat32;
-const StorageType kStorageTypeInterleaved = media::ShellAudioBus::kInterleaved;
-const StorageType kStorageTypePlanar = media::ShellAudioBus::kPlanar;
+typedef media::AudioBus AudioBus;
+typedef media::AudioBus::SampleType SampleType;
+typedef media::AudioBus::StorageType StorageType;
+const SampleType kSampleTypeInt16 = media::AudioBus::kInt16;
+const SampleType kSampleTypeFloat32 = media::AudioBus::kFloat32;
+const StorageType kStorageTypeInterleaved = media::AudioBus::kInterleaved;
+const StorageType kStorageTypePlanar = media::AudioBus::kPlanar;
const float kMaxInt16AsFloat32 = 32767.0f;
@@ -52,7 +52,7 @@
#endif
// Get the size in bytes of an internal sample type, which is an alias for
-// media::ShellAudioBus::SampleType.
+// media::AudioBus::SampleType.
inline size_t GetSampleTypeSize(SampleType sample_type) {
switch (sample_type) {
case kSampleTypeInt16:
diff --git a/src/cobalt/audio/audio_node.h b/src/cobalt/audio/audio_node.h
index 1e9c1dd..925356c 100644
--- a/src/cobalt/audio/audio_node.h
+++ b/src/cobalt/audio/audio_node.h
@@ -27,7 +27,7 @@
#include "cobalt/audio/audio_node_output.h"
#include "cobalt/dom/dom_exception.h"
#include "cobalt/dom/event_target.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/script/environment_settings.h"
namespace cobalt {
@@ -49,7 +49,7 @@
// (if it has any).
// https://www.w3.org/TR/webaudio/#AudioNode-section
class AudioNode : public dom::EventTarget {
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
AudioNode(script::EnvironmentSettings* settings, AudioContext* context);
@@ -108,7 +108,7 @@
// Called when a new input node has been connected.
virtual void OnInputNodeConnected() {}
- virtual std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
+ virtual std::unique_ptr<AudioBus> PassAudioBusFromSource(
int32 number_of_frames, SampleType sample_type, bool* finished) = 0;
AudioLock* audio_lock() const { return audio_lock_.get(); }
diff --git a/src/cobalt/audio/audio_node_input.cc b/src/cobalt/audio/audio_node_input.cc
index f8f5503..977d67e 100644
--- a/src/cobalt/audio/audio_node_input.cc
+++ b/src/cobalt/audio/audio_node_input.cc
@@ -26,12 +26,12 @@
namespace {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
void MixAudioBufferBasedOnInterpretation(
const float* speaker, const float* discrete,
- const AudioNodeChannelInterpretation& interpretation, ShellAudioBus* source,
- ShellAudioBus* output_audio_data) {
+ const AudioNodeChannelInterpretation& interpretation, AudioBus* source,
+ AudioBus* output_audio_data) {
const float* kMatrix =
interpretation == kAudioNodeChannelInterpretationSpeakers ? speaker
: discrete;
@@ -49,7 +49,7 @@
// Up down mix equations for mono, stereo, quad, 5.1:
// https://www.w3.org/TR/webaudio/#ChannelLayouts
void MixAudioBuffer(const AudioNodeChannelInterpretation& interpretation,
- ShellAudioBus* source, ShellAudioBus* output_audio_data) {
+ AudioBus* source, AudioBus* output_audio_data) {
DCHECK_GT(source->channels(), 0u);
DCHECK_GT(output_audio_data->channels(), 0u);
DCHECK(interpretation == kAudioNodeChannelInterpretationSpeakers ||
@@ -224,8 +224,8 @@
}
}
-void AudioNodeInput::FillAudioBus(ShellAudioBus* output_audio_bus,
- bool* silence, bool* all_finished) {
+void AudioNodeInput::FillAudioBus(AudioBus* output_audio_bus, bool* silence,
+ bool* all_finished) {
DCHECK(silence);
DCHECK(all_finished);
@@ -247,7 +247,7 @@
for (std::set<AudioNodeOutput*>::iterator iter = outputs_.begin();
iter != outputs_.end(); ++iter) {
bool finished = false;
- std::unique_ptr<ShellAudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
+ std::unique_ptr<AudioBus> audio_bus = (*iter)->PassAudioBusFromSource(
static_cast<int32>(output_audio_bus->frames()),
output_audio_bus->sample_type(), &finished);
*all_finished &= finished;
diff --git a/src/cobalt/audio/audio_node_input.h b/src/cobalt/audio/audio_node_input.h
index 74aca8b..1c5bee1 100644
--- a/src/cobalt/audio/audio_node_input.h
+++ b/src/cobalt/audio/audio_node_input.h
@@ -20,7 +20,7 @@
#include "base/memory/ref_counted.h"
#include "cobalt/audio/audio_buffer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
namespace cobalt {
namespace audio {
@@ -36,7 +36,7 @@
// number can change depending on the connection(s) made to the input. If the
// input has no connections, then it has one channel which is silent.
class AudioNodeInput : public base::RefCountedThreadSafe<AudioNodeInput> {
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
explicit AudioNodeInput(AudioNode* owner_node) : owner_node_(owner_node) {}
@@ -50,8 +50,7 @@
// For each input, an AudioNode performs a mixing of all connections to that
// input. FillAudioBus() performs that action. In the case of multiple
// connections, it sums the result into |audio_bus|.
- void FillAudioBus(ShellAudioBus* audio_bus, bool* silence,
- bool* all_finished);
+ void FillAudioBus(AudioBus* audio_bus, bool* silence, bool* all_finished);
private:
AudioNode* const owner_node_;
diff --git a/src/cobalt/audio/audio_node_input_output_test.cc b/src/cobalt/audio/audio_node_input_output_test.cc
index 970254a..41a2ca2 100644
--- a/src/cobalt/audio/audio_node_input_output_test.cc
+++ b/src/cobalt/audio/audio_node_input_output_test.cc
@@ -27,18 +27,18 @@
#include "cobalt/script/typed_arrays.h"
#include "testing/gtest/include/gtest/gtest.h"
-// TODO: Consolidate ShellAudioBus creation code
+// TODO: Consolidate AudioBus creation code
namespace cobalt {
namespace audio {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
constexpr int kRenderBufferSizeFrames = 32;
class AudioDestinationNodeMock : public AudioNode,
public AudioDevice::RenderCallback {
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
AudioDestinationNodeMock(script::EnvironmentSettings* settings,
@@ -50,14 +50,15 @@
}
// From AudioNode.
- std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(
- int32 number_of_frames, SampleType sample_type, bool* finished) override {
+ std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type,
+ bool* finished) override {
NOTREACHED();
- return std::unique_ptr<ShellAudioBus>();
+ return std::unique_ptr<AudioBus>();
}
// From AudioDevice::RenderCallback.
- void FillAudioBus(bool all_consumed, ShellAudioBus* audio_bus,
+ void FillAudioBus(bool all_consumed, AudioBus* audio_bus,
bool* silence) override {
AudioLock::AutoLock lock(audio_lock());
@@ -68,9 +69,9 @@
};
void FillAudioBusFromOneSource(
- std::unique_ptr<ShellAudioBus> src_data,
- const AudioNodeChannelInterpretation& interpretation,
- ShellAudioBus* audio_bus, bool* silence) {
+ std::unique_ptr<AudioBus> src_data,
+ const AudioNodeChannelInterpretation& interpretation, AudioBus* audio_bus,
+ bool* silence) {
dom::testing::StubEnvironmentSettings environment_settings;
scoped_refptr<AudioContext> audio_context(
@@ -139,11 +140,11 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -180,12 +181,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -219,12 +220,12 @@
src_data_in_float[i] = 50.0f;
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -254,12 +255,12 @@
src_data_in_float[i] = 50.0f;
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -292,12 +293,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -334,12 +335,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -376,12 +377,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -418,12 +419,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -460,12 +461,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -498,12 +499,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -536,12 +537,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -574,12 +575,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -612,12 +613,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -650,12 +651,12 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames, src_data_in_float));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
FillAudioBusFromOneSource(std::move(src_data), kInterpretation,
@@ -691,8 +692,8 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data_1(new ShellAudioBus(
- kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
+ std::unique_ptr<AudioBus> src_data_1(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames_1, src_data_in_float_1));
scoped_refptr<AudioBufferSourceNode> source_1(
audio_context->CreateBufferSource(environment_settings()));
scoped_refptr<AudioBuffer> buffer_1(
@@ -709,8 +710,8 @@
}
}
- std::unique_ptr<ShellAudioBus> src_data_2(new ShellAudioBus(
- kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
+ std::unique_ptr<AudioBus> src_data_2(
+ new AudioBus(kNumOfSrcChannels, kNumOfFrames_2, src_data_in_float_2));
scoped_refptr<AudioBufferSourceNode> source_2(
audio_context->CreateBufferSource(environment_settings()));
scoped_refptr<AudioBuffer> buffer_2(
@@ -726,9 +727,9 @@
source_1->Start(0, 0, NULL);
source_2->Start(0, 0, NULL);
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kRenderBufferSizeFrames,
+ AudioBus::kFloat32, AudioBus::kPlanar));
audio_bus->ZeroAllFrames();
bool silence = true;
destination->FillAudioBus(true, audio_bus.get(), &silence);
@@ -788,9 +789,9 @@
scoped_refptr<AudioContext> audio_context(
new AudioContext(environment_settings()));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kPlanar));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kFloat32,
+ AudioBus::kPlanar));
audio_bus->ZeroAllFrames();
scoped_refptr<AudioBuffer> buffer(
new AudioBuffer(audio_context->sample_rate(), std::move(audio_bus)));
@@ -828,9 +829,9 @@
scoped_refptr<AudioContext> audio_context(
new AudioContext(environment_settings()));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kFloat32, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kFloat32,
+ AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
scoped_refptr<AudioBuffer> buffer(
new AudioBuffer(audio_context->sample_rate(), std::move(audio_bus)));
@@ -868,9 +869,9 @@
scoped_refptr<AudioContext> audio_context(
new AudioContext(environment_settings()));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kInt16, ShellAudioBus::kPlanar));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kInt16,
+ AudioBus::kPlanar));
audio_bus->ZeroAllFrames();
scoped_refptr<AudioBuffer> buffer(
@@ -911,9 +912,9 @@
scoped_refptr<AudioContext> audio_context(
new AudioContext(environment_settings()));
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfChannels, kRenderBufferSizeFrames,
- ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfChannels, kRenderBufferSizeFrames, AudioBus::kInt16,
+ AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
scoped_refptr<AudioBuffer> buffer(
@@ -966,9 +967,9 @@
for (size_t buffer_sample_rate : kBufferSampleRateArr) {
for (SampleType sample_type : kSampleTypeArr) {
- std::unique_ptr<ShellAudioBus> src_data(
- new ShellAudioBus(kNumOfSrcChannels, kNumOfSrcFrames, sample_type,
- ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> src_data(
+ new AudioBus(kNumOfSrcChannels, kNumOfSrcFrames, sample_type,
+ AudioBus::kInterleaved));
src_data->ZeroAllFrames();
scoped_refptr<AudioBuffer> buffer(
new AudioBuffer(buffer_sample_rate, std::move(src_data)));
@@ -988,9 +989,9 @@
source->Connect(destination, 0, 0, NULL);
source->Start(0, 0, NULL);
- std::unique_ptr<ShellAudioBus> audio_bus(
- new ShellAudioBus(kNumOfDestChannels, kNumOfDestFrames, sample_type,
- ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> audio_bus(
+ new AudioBus(kNumOfDestChannels, kNumOfDestFrames, sample_type,
+ AudioBus::kInterleaved));
audio_bus->ZeroAllFrames();
bool silence = true;
destination->FillAudioBus(true, audio_bus.get(), &silence);
diff --git a/src/cobalt/audio/audio_node_output.cc b/src/cobalt/audio/audio_node_output.cc
index 8ab0f43..ed6c507 100644
--- a/src/cobalt/audio/audio_node_output.cc
+++ b/src/cobalt/audio/audio_node_output.cc
@@ -24,7 +24,7 @@
namespace cobalt {
namespace audio {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
AudioNodeOutput::~AudioNodeOutput() {
owner_node_->audio_lock()->AssertLocked();
@@ -57,7 +57,7 @@
}
}
-std::unique_ptr<ShellAudioBus> AudioNodeOutput::PassAudioBusFromSource(
+std::unique_ptr<AudioBus> AudioNodeOutput::PassAudioBusFromSource(
int32 number_of_frames, SampleType sample_type, bool* finished) {
// This is called by Audio thread.
owner_node_->audio_lock()->AssertLocked();
diff --git a/src/cobalt/audio/audio_node_output.h b/src/cobalt/audio/audio_node_output.h
index 4122390..1774ce4 100644
--- a/src/cobalt/audio/audio_node_output.h
+++ b/src/cobalt/audio/audio_node_output.h
@@ -22,7 +22,7 @@
#include "base/memory/ref_counted.h"
#include "cobalt/audio/audio_buffer.h"
#include "cobalt/audio/audio_helpers.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
namespace cobalt {
namespace audio {
@@ -33,7 +33,7 @@
// This represents the output coming out of the AudioNode.
// It may be connected to one or more AudioNodeInputs.
class AudioNodeOutput : public base::RefCountedThreadSafe<AudioNodeOutput> {
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
public:
explicit AudioNodeOutput(AudioNode* owner_node) : owner_node_(owner_node) {}
@@ -44,9 +44,9 @@
void DisconnectAll();
- std::unique_ptr<ShellAudioBus> PassAudioBusFromSource(int32 number_of_frames,
- SampleType sample_type,
- bool* finished);
+ std::unique_ptr<AudioBus> PassAudioBusFromSource(int32 number_of_frames,
+ SampleType sample_type,
+ bool* finished);
private:
AudioNode* const owner_node_;
diff --git a/src/cobalt/base/localized_strings.cc b/src/cobalt/base/localized_strings.cc
index 1c590a4..3388ca7 100644
--- a/src/cobalt/base/localized_strings.cc
+++ b/src/cobalt/base/localized_strings.cc
@@ -17,8 +17,8 @@
#include <algorithm>
#include "base/logging.h"
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
-#include "starboard/file.h"
#include "starboard/system.h"
#include "starboard/types.h"
diff --git a/src/cobalt/base/path_provider.cc b/src/cobalt/base/path_provider.cc
index ae07949..6dfbbe7 100644
--- a/src/cobalt/base/path_provider.cc
+++ b/src/cobalt/base/path_provider.cc
@@ -31,6 +31,9 @@
base::FilePath directory(path.get());
if (base::PathExists(directory) || base::CreateDirectory(directory)) {
return directory;
+ } else {
+ DLOG(ERROR) << "Attempt to open or create this path failed: " +
+ directory.value();
}
}
return base::FilePath();
diff --git a/src/cobalt/black_box_tests/black_box_tests.py b/src/cobalt/black_box_tests/black_box_tests.py
index 220c342..60f5596 100644
--- a/src/cobalt/black_box_tests/black_box_tests.py
+++ b/src/cobalt/black_box_tests/black_box_tests.py
@@ -30,6 +30,7 @@
from starboard.tools import abstract_launcher
from starboard.tools import build
from starboard.tools import command_line
+from starboard.tools import log_level
_DISABLED_BLACKBOXTEST_CONFIGS = [
'android-arm/devel',
@@ -294,13 +295,7 @@
'specified, all IPs will be allowed to connect.'))
args, _ = parser.parse_known_args()
- # This format matches Cobalt's console log format.
- logging_format = ('[%(process)d:%(asctime)s.%(msecs)03d...:'
- '%(levelname)s:%(filename)s(%(lineno)s)] %(message)s')
- logging.basicConfig(
- level=logging.INFO, format=logging_format, datefmt='%m%d/%H%M%S')
- if args.verbose:
- logging.getLogger().setLevel(logging.DEBUG)
+ log_level.InitializeLogging(args)
test_object = BlackBoxTests(args.server_binding_address, args.proxy_address,
args.proxy_port, args.test_name,
diff --git a/src/cobalt/black_box_tests/testdata/pointer_test.html b/src/cobalt/black_box_tests/testdata/pointer_test.html
index 1f1b275..340e5da 100644
--- a/src/cobalt/black_box_tests/testdata/pointer_test.html
+++ b/src/cobalt/black_box_tests/testdata/pointer_test.html
@@ -98,14 +98,10 @@
['pointerover', 'top', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'top_two', 'at target'],
- ['pointerenter', 'top', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'top_two', 'at target'],
['mouseover', 'top', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'top_two', 'at target'],
- ['mouseenter', 'top', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// actions.move_to_element_with_offset(top_two, 10, 10).pause(_SLEEP_AFTER_MOVE_TIME)
['pointermove', 'top_two', 'at target'],
['pointermove', 'top', 'bubbling'],
@@ -139,14 +135,10 @@
['pointerover', 'top', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'top_one', 'at target'],
- ['pointerenter', 'top', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'top_one', 'at target'],
['mouseover', 'top', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'top_one', 'at target'],
- ['mouseenter', 'top', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// actions.click(top_three)
['pointerout', 'top_one', 'at target'],
['pointerout', 'top', 'bubbling'],
@@ -166,14 +158,10 @@
['pointerover', 'top', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'top_three', 'at target'],
- ['pointerenter', 'top', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'top_three', 'at target'],
['mouseover', 'top', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'top_three', 'at target'],
- ['mouseenter', 'top', 'at target'],
- ['mouseenter', 'outer', 'at target'],
['pointerdown', 'top_three', 'at target'],
['pointerdown', 'top', 'bubbling'],
['pointerdown', 'outer', 'bubbling'],
@@ -208,14 +196,10 @@
['pointerover', 'top', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'top_four', 'at target'],
- ['pointerenter', 'top', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'top_four', 'at target'],
['mouseover', 'top', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'top_four', 'at target'],
- ['mouseenter', 'top', 'at target'],
- ['mouseenter', 'outer', 'at target'],
['pointerdown', 'top_four', 'at target'],
['pointerdown', 'top', 'bubbling'],
['pointerdown', 'outer', 'bubbling'],
@@ -257,14 +241,10 @@
['pointerover', 'top', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'top_six', 'at target'],
- ['pointerenter', 'top', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'top_six', 'at target'],
['mouseover', 'top', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'top_six', 'at target'],
- ['mouseenter', 'top', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// actions.move_to_element(bottom_six).pause(_SLEEP_AFTER_MOVE_TIME)
['pointerout', 'top_six', 'at target'],
['pointerout', 'top', 'bubbling'],
@@ -287,13 +267,11 @@
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'bottom_six', 'at target'],
['pointerenter', 'bottom', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'bottom_six', 'at target'],
['mouseover', 'bottom', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'bottom_six', 'at target'],
['mouseenter', 'bottom', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// actions.click(bottom_five)
['pointerout', 'bottom_six', 'at target'],
['pointerout', 'bottom', 'bubbling'],
@@ -313,14 +291,10 @@
['pointerover', 'bottom', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'bottom_five', 'at target'],
- ['pointerenter', 'bottom', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'bottom_five', 'at target'],
['mouseover', 'bottom', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'bottom_five', 'at target'],
- ['mouseenter', 'bottom', 'at target'],
- ['mouseenter', 'outer', 'at target'],
['pointerdown', 'bottom_five', 'at target'],
['pointerdown', 'bottom', 'bubbling'],
['pointerdown', 'outer', 'bubbling'],
@@ -349,14 +323,10 @@
['pointerover', 'bottom', 'bubbling'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'bottom_four', 'at target'],
- ['pointerenter', 'bottom', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'bottom_four', 'at target'],
['mouseover', 'bottom', 'bubbling'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'bottom_four', 'at target'],
- ['mouseenter', 'bottom', 'at target'],
- ['mouseenter', 'outer', 'at target'],
['pointerdown', 'bottom_four', 'at target'],
['pointerdown', 'bottom', 'bubbling'],
['pointerdown', 'outer', 'bubbling'],
@@ -383,12 +353,8 @@
['mousemove', 'bottom_two', 'at target'],
['pointerover', 'bottom_two', 'at target'],
['pointerenter', 'bottom_two', 'at target'],
- ['pointerenter', 'bottom', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'bottom_two', 'at target'],
['mouseenter', 'bottom_two', 'at target'],
- ['mouseenter', 'bottom', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// actions.move_to_element(bottom_one).pause(_SLEEP_AFTER_MOVE_TIME)
['pointerout', 'bottom_two', 'at target'],
['pointerleave', 'bottom_two', 'at target'],
@@ -398,12 +364,8 @@
['mousemove', 'bottom_one', 'at target'],
['pointerover', 'bottom_one', 'at target'],
['pointerenter', 'bottom_one', 'at target'],
- ['pointerenter', 'bottom', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'bottom_one', 'at target'],
['mouseenter', 'bottom_one', 'at target'],
- ['mouseenter', 'bottom', 'at target'],
- ['mouseenter', 'outer', 'at target'],
// find_element_by_id(runner, 'end').click()
['pointerout', 'bottom_one', 'at target'],
['pointerleave', 'bottom_one', 'at target'],
@@ -418,11 +380,9 @@
['pointerover', 'end', 'at target'],
['pointerover', 'outer', 'bubbling'],
['pointerenter', 'end', 'at target'],
- ['pointerenter', 'outer', 'at target'],
['mouseover', 'end', 'at target'],
['mouseover', 'outer', 'bubbling'],
['mouseenter', 'end', 'at target'],
- ['mouseenter', 'outer', 'at target'],
['pointerdown', 'end', 'at target'],
['pointerdown', 'outer', 'bubbling'],
['mousedown', 'end', 'at target'],
@@ -489,33 +449,72 @@
e.target.setPointerCapture(e.pointerId);
}
- function SetHandlers(event, classname, callback) {
- var elements = document.getElementsByClassName(classname);
+ // If the event type has value 'type', then report an error if the
+ // 'name' property on the event target already has 'value'. Otherwise,
+ // set it to the 'value'. This is used to detect erroneous boundary
+ // events (enter/leave, over/out), and up/down event sequences on an
+ // element.
+ function TrackAndVerifyTargetState(event, type, name, value) {
+ if (event.type == type) {
+ if (event.target[name] == value) {
+ console.log('ERROR: ' + type + 'event received while ' +
+ name + ' == ' + event.target[name]);
+ assertTrue(event.target[name] != value);
+ }
+ event.target[name] = value;
+ }
+ }
+
+ function CheckState(e) {
+ // Check the target element state when the event is 'at target'.
+ if (e.eventPhase == 2) {
+ // Verify that there is not a duplicated or missing event for enter,
+ // leave, over, out, up, or down.
+ TrackAndVerifyTargetState(e, 'mouseenter', 'mouseenter', true);
+ TrackAndVerifyTargetState(e, 'mouseleave', 'mouseenter', false);
+ TrackAndVerifyTargetState(e, 'mouseover', 'mouseover', true);
+ TrackAndVerifyTargetState(e, 'mouseout', 'mouseover', false);
+ TrackAndVerifyTargetState(e, 'mousedown', 'mousedown', true);
+ TrackAndVerifyTargetState(e, 'mouseup', 'mousedown', false);
+
+ TrackAndVerifyTargetState(e, 'pointerenter', 'pointerenter', true);
+ TrackAndVerifyTargetState(e, 'pointerleave', 'pointerenter', false);
+ TrackAndVerifyTargetState(e, 'pointerover', 'pointerover', true);
+ TrackAndVerifyTargetState(e, 'pointerout', 'pointerover', false);
+ TrackAndVerifyTargetState(e, 'pointerdown', 'pointerdown', true);
+ TrackAndVerifyTargetState(e, 'pointerup', 'pointerdown', false);
+ }
+ }
+
+ function SetHandlers(event, selector, callback) {
+ var elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; ++i) {
elements[i].addEventListener(event, callback);
}
}
- function SetAllHandlers(prefix, classname, callback) {
- SetHandlers(prefix + 'enter', classname, callback);
- SetHandlers(prefix + 'leave', classname, callback);
- SetHandlers(prefix + 'over', classname, callback);
- SetHandlers(prefix + 'out', classname, callback);
- SetHandlers(prefix + 'down', classname, callback);
- SetHandlers(prefix + 'up', classname, callback);
- SetHandlers(prefix + 'move', classname, callback);
+ function SetAllHandlers(prefix, selector, callback) {
+ SetHandlers(prefix + 'enter', selector, callback);
+ SetHandlers(prefix + 'leave', selector, callback);
+ SetHandlers(prefix + 'over', selector, callback);
+ SetHandlers(prefix + 'out', selector, callback);
+ SetHandlers(prefix + 'down', selector, callback);
+ SetHandlers(prefix + 'up', selector, callback);
+ SetHandlers(prefix + 'move', selector, callback);
}
window.onload = function() {
- SetAllHandlers('mouse', 'track', LogEvent);
- SetAllHandlers('pointer', 'track', LogEvent);
- SetHandlers('click', 'track', LogEvent);
- SetAllHandlers('mouse', 'cancel', Cancel);
- SetAllHandlers('pointer', 'cancel', Cancel);
- SetAllHandlers('mouse', 'stop', Stop);
- SetAllHandlers('pointer', 'stop', Stop);
- SetHandlers('pointerdown', 'capture', Capture);
- SetHandlers('click', 'end', EndTest);
+ SetAllHandlers('mouse', '.track', LogEvent);
+ SetAllHandlers('pointer', '.track', LogEvent);
+ SetHandlers('click', '.track', LogEvent);
+ SetAllHandlers('mouse', '.cancel', Cancel);
+ SetAllHandlers('pointer', '.cancel', Cancel);
+ SetAllHandlers('mouse', '.stop', Stop);
+ SetAllHandlers('pointer', '.stop', Stop);
+ SetHandlers('pointerdown', '.capture', Capture);
+ SetHandlers('click', '.end', EndTest);
+ SetAllHandlers('mouse', '*', CheckState);
+ SetAllHandlers('pointer', '*', CheckState);
console.log("Setup finished");
setupFinished();
}
diff --git a/src/cobalt/black_box_tests/tests/pointer_test.py b/src/cobalt/black_box_tests/tests/pointer_test.py
index 706bbc5..02e9a00 100644
--- a/src/cobalt/black_box_tests/tests/pointer_test.py
+++ b/src/cobalt/black_box_tests/tests/pointer_test.py
@@ -44,8 +44,7 @@
class PointerTest(black_box_tests.BlackBoxTestCase):
"""Tests pointer and mouse event."""
- def test_simple(self):
-
+ def test_pointer_events(self):
try:
with ThreadedWebServer(
binding_address=self.GetBindingAddress()) as server:
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index f0a72e0..27edd11 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -1381,3 +1381,9 @@
} // namespace browser
} // namespace cobalt
+
+const char* GetCobaltUserAgentString() {
+ static std::string ua = cobalt::browser::CreateUserAgentString(
+ cobalt::browser::GetUserAgentPlatformInfoFromSystem());
+ return ua.c_str();
+}
diff --git a/src/cobalt/browser/application.h b/src/cobalt/browser/application.h
index 2b93e86..6f8ed3c 100644
--- a/src/cobalt/browser/application.h
+++ b/src/cobalt/browser/application.h
@@ -231,4 +231,9 @@
} // namespace browser
} // namespace cobalt
+
+extern "C" {
+SB_IMPORT const char* GetCobaltUserAgentString();
+}
+
#endif // COBALT_BROWSER_APPLICATION_H_
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index 66b62b6..6d14fbe 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -433,7 +433,8 @@
base::Unretained(this)),
network_module_, GetViewportSize(), GetResourceProvider(),
kLayoutMaxRefreshFrequencyInHz,
- base::Bind(&BrowserModule::CreateDebugClient, base::Unretained(this))));
+ base::Bind(&BrowserModule::CreateDebugClient, base::Unretained(this)),
+ base::Bind(&BrowserModule::OnMaybeFreeze, base::Unretained(this))));
lifecycle_observers_.AddObserver(debug_console_.get());
#endif // defined(ENABLE_DEBUGGER)
@@ -563,17 +564,18 @@
DestroySplashScreen(base::TimeDelta());
if (options_.enable_splash_screen_on_reloads ||
main_web_module_generation_ == 1) {
- base::Optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
+ splash_screen_cache_->SetUrl(url);
if (fallback_splash_screen_url_ ||
- (key && splash_screen_cache_->IsSplashScreenCached(*key))) {
+ splash_screen_cache_->IsSplashScreenCached()) {
splash_screen_.reset(new SplashScreen(
application_state_,
base::Bind(&BrowserModule::QueueOnSplashScreenRenderTreeProduced,
base::Unretained(this)),
network_module_, viewport_size, GetResourceProvider(),
- kLayoutMaxRefreshFrequencyInHz, fallback_splash_screen_url_, url,
+ kLayoutMaxRefreshFrequencyInHz, fallback_splash_screen_url_,
splash_screen_cache_.get(),
- base::Bind(&BrowserModule::DestroySplashScreen, weak_this_)));
+ base::Bind(&BrowserModule::DestroySplashScreen, weak_this_),
+ base::Bind(&BrowserModule::OnMaybeFreeze, base::Unretained(this))));
lifecycle_observers_.AddObserver(splash_screen_.get());
}
}
@@ -629,6 +631,10 @@
options.debugger_state = debugger_state.get();
#endif // ENABLE_DEBUGGER
+ // Pass down this callback from to Web module.
+ options_.web_module_options.maybe_freeze_callback =
+ base::Bind(&BrowserModule::OnMaybeFreeze, base::Unretained(this));
+
web_module_.reset(new WebModule(
url, application_state_,
base::Bind(&BrowserModule::QueueOnRenderTreeProduced,
@@ -1466,6 +1472,7 @@
DCHECK(application_state_ == base::kApplicationStateBlurred);
application_state_ = base::kApplicationStateConcealed;
ConcealInternal();
+ OnMaybeFreeze();
}
void BrowserModule::Focus() {
@@ -1795,6 +1802,35 @@
Unfreeze(GetResourceProvider()));
}
+void BrowserModule::OnMaybeFreeze() {
+ TRACE_EVENT0("cobalt::browser", "BrowserModule::MaybeFreeze()");
+ if (base::MessageLoop::current() != self_message_loop_) {
+ self_message_loop_->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserModule::OnMaybeFreeze, base::Unretained(this)));
+ return;
+ }
+
+ bool splash_screen_ready_to_freeze = splash_screen_ ?
+ splash_screen_->IsReadyToFreeze() : true;
+#if defined(ENABLE_DEBUGGER)
+ bool debug_console_ready_to_freeze = debug_console_ ?
+ debug_console_->IsReadyToFreeze() : true;
+#endif // defined(ENABLE_DEBUGGER)
+ bool web_module_ready_to_freeze = web_module_->IsReadyToFreeze();
+ if (splash_screen_ready_to_freeze &&
+#if defined(ENABLE_DEBUGGER)
+ debug_console_ready_to_freeze &&
+#endif // defined(ENABLE_DEBUGGER)
+ web_module_ready_to_freeze) {
+#if SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION || \
+ SB_HAS(CONCEALED_STATE)
+ SbSystemRequestFreeze();
+#endif // SB_API_VERSION >= SB_ADD_CONCEALED_STATE_SUPPORT_VERSION ||
+ // SB_HAS(CONCEALED_STATE)
+ }
+}
+
ViewportSize BrowserModule::GetViewportSize() {
// We trust the renderer module for width and height the most, if it exists.
if (renderer_module_) {
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 194810a..2d9b43f 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -426,6 +426,11 @@
// the app state update.
void UnfreezeInternal();
+ // Check debug console, splash screen and web module if they are
+ // ready to freeze at Concealed state. If so, call SystemRequestFreeze
+ // to freeze Cobalt.
+ void OnMaybeFreeze();
+
// Gets a viewport size to use for now. This may change depending on the
// current application state. While concealed, this returns the requested
// viewport size. If there was no requested viewport size, it returns a
diff --git a/src/cobalt/browser/debug_console.cc b/src/cobalt/browser/debug_console.cc
index 0503509..5716112 100644
--- a/src/cobalt/browser/debug_console.cc
+++ b/src/cobalt/browser/debug_console.cc
@@ -110,7 +110,8 @@
network::NetworkModule* network_module,
const cssom::ViewportSize& window_dimensions,
render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
- const debug::CreateDebugClientCallback& create_debug_client_callback) {
+ const debug::CreateDebugClientCallback& create_debug_client_callback,
+ const base::Closure& maybe_freeze_callback) {
mode_ = GetInitialMode();
WebModule::Options web_module_options;
@@ -134,6 +135,10 @@
base::Bind(&CreateDebugHub,
base::Bind(&DebugConsole::GetMode, base::Unretained(this)),
create_debug_client_callback);
+
+ // Pass down this callback from Browser module to Web module eventually.
+ web_module_options.maybe_freeze_callback = maybe_freeze_callback;
+
web_module_.reset(new WebModule(
GURL(kInitialDebugConsoleUrl), initial_application_state,
render_tree_produced_callback,
diff --git a/src/cobalt/browser/debug_console.h b/src/cobalt/browser/debug_console.h
index 8d573e2..7983443 100644
--- a/src/cobalt/browser/debug_console.h
+++ b/src/cobalt/browser/debug_console.h
@@ -51,7 +51,8 @@
const cssom::ViewportSize& window_dimensions,
render_tree::ResourceProvider* resource_provider,
float layout_refresh_rate,
- const debug::CreateDebugClientCallback& create_debug_client_callback);
+ const debug::CreateDebugClientCallback& create_debug_client_callback,
+ const base::Closure& maybe_freeze_callback);
~DebugConsole();
// Filters a key event.
@@ -109,6 +110,10 @@
void ReduceMemory() { web_module_->ReduceMemory(); }
+ bool IsReadyToFreeze() {
+ return web_module_->IsReadyToFreeze();
+ }
+
private:
void OnError(const GURL& url, const std::string& error) {
LOG(ERROR) << error;
diff --git a/src/cobalt/browser/splash_screen.cc b/src/cobalt/browser/splash_screen.cc
index 962142f..7d18340 100644
--- a/src/cobalt/browser/splash_screen.cc
+++ b/src/cobalt/browser/splash_screen.cc
@@ -57,10 +57,10 @@
const cssom::ViewportSize& window_dimensions,
render_tree::ResourceProvider* resource_provider, float layout_refresh_rate,
const base::Optional<GURL>& fallback_splash_screen_url,
- const GURL& initial_main_web_module_url,
SplashScreenCache* splash_screen_cache,
const base::Callback<void(base::TimeDelta)>&
- on_splash_screen_shutdown_complete)
+ on_splash_screen_shutdown_complete,
+ const base::Closure& maybe_freeze_callback)
: render_tree_produced_callback_(render_tree_produced_callback),
self_message_loop_(base::MessageLoop::current()),
on_splash_screen_shutdown_complete_(on_splash_screen_shutdown_complete),
@@ -76,15 +76,10 @@
base::ThreadPriority::HIGHEST;
base::Optional<GURL> url_to_pass = fallback_splash_screen_url;
- // Use the cached URL rather than the passed in URL if it exists.
- base::Optional<std::string> key =
- SplashScreenCache::GetKeyForStartUrl(initial_main_web_module_url);
DCHECK(fallback_splash_screen_url ||
- (key && splash_screen_cache &&
- splash_screen_cache->IsSplashScreenCached(*key)));
- if (key && splash_screen_cache &&
- splash_screen_cache->IsSplashScreenCached(*key)) {
- url_to_pass = GURL(loader::kCacheScheme + ("://" + *key));
+ (splash_screen_cache && splash_screen_cache->IsSplashScreenCached()));
+ if (splash_screen_cache && splash_screen_cache->IsSplashScreenCached()) {
+ url_to_pass = splash_screen_cache->GetCachedSplashScreenUrl();
web_module_options.can_fetch_cache = true;
web_module_options.splash_screen_cache = splash_screen_cache;
}
@@ -99,6 +94,9 @@
// module contents, make sure blending is enabled for its background.
web_module_options.clear_window_with_background_color = false;
+ // Pass down this callback from Browser module to Web module eventually.
+ web_module_options.maybe_freeze_callback = maybe_freeze_callback;
+
DCHECK(url_to_pass);
web_module_.reset(new WebModule(
*url_to_pass, initial_application_state, render_tree_produced_callback_,
diff --git a/src/cobalt/browser/splash_screen.h b/src/cobalt/browser/splash_screen.h
index d6e08a4..381280f 100644
--- a/src/cobalt/browser/splash_screen.h
+++ b/src/cobalt/browser/splash_screen.h
@@ -42,10 +42,10 @@
render_tree::ResourceProvider* resource_provider,
float layout_refresh_rate,
const base::Optional<GURL>& fallback_splash_screen_url,
- const GURL& initial_main_web_module_url,
cobalt::browser::SplashScreenCache* splash_screen_cache,
const base::Callback<void(base::TimeDelta)>&
- on_splash_screen_shutdown_complete);
+ on_splash_screen_shutdown_complete,
+ const base::Closure& maybe_freeze_callback);
~SplashScreen();
void SetSize(const cssom::ViewportSize& viewport_size) {
@@ -80,6 +80,8 @@
WebModule& web_module() { return *web_module_; }
+ bool IsReadyToFreeze() { return web_module_->IsReadyToFreeze(); }
+
private:
// Run when window.close() is called by the WebModule.
void OnWindowClosed();
diff --git a/src/cobalt/browser/splash_screen_cache.cc b/src/cobalt/browser/splash_screen_cache.cc
index 43cb6f7..5cf8299 100644
--- a/src/cobalt/browser/splash_screen_cache.cc
+++ b/src/cobalt/browser/splash_screen_cache.cc
@@ -23,10 +23,10 @@
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "cobalt/base/get_application_key.h"
+#include "starboard/common/file.h"
#include "starboard/common/string.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
-#include "starboard/file.h"
namespace cobalt {
namespace browser {
@@ -59,10 +59,10 @@
base::AutoLock lock(lock_);
}
-bool SplashScreenCache::CacheSplashScreen(const std::string& key,
- const std::string& content) const {
+bool SplashScreenCache::CacheSplashScreen(const std::string& content) const {
base::AutoLock lock(lock_);
- if (key.empty()) {
+ base::Optional<std::string> key = GetKeyForStartUrl(url_);
+ if (!key) {
return false;
}
@@ -76,10 +76,11 @@
kSbFileMaxPath)) {
return false;
}
- if (!CreateDirsForKey(key)) {
+ if (!CreateDirsForKey(key.value())) {
return false;
}
- std::string full_path = std::string(path.data()) + kSbFileSepString + key;
+ std::string full_path =
+ std::string(path.data()) + kSbFileSepString + key.value();
starboard::ScopedFile cache_file(
full_path.c_str(), kSbFileCreateAlways | kSbFileWrite, NULL, NULL);
@@ -87,15 +88,18 @@
static_cast<int>(content.size())) > 0;
}
-bool SplashScreenCache::IsSplashScreenCached(const std::string& key) const {
+bool SplashScreenCache::IsSplashScreenCached() const {
base::AutoLock lock(lock_);
std::vector<char> path(kSbFileMaxPath, 0);
if (!SbSystemGetPath(kSbSystemPathCacheDirectory, path.data(),
kSbFileMaxPath)) {
return false;
}
- std::string full_path = std::string(path.data()) + kSbFileSepString + key;
- return !key.empty() && SbFileExists(full_path.c_str());
+ base::Optional<std::string> key = GetKeyForStartUrl(url_);
+ if (!key) return false;
+ std::string full_path =
+ std::string(path.data()) + kSbFileSepString + key.value();
+ return SbFileExists(full_path.c_str());
}
int SplashScreenCache::ReadCachedSplashScreen(
diff --git a/src/cobalt/browser/splash_screen_cache.h b/src/cobalt/browser/splash_screen_cache.h
index deacfc8..336d124 100644
--- a/src/cobalt/browser/splash_screen_cache.h
+++ b/src/cobalt/browser/splash_screen_cache.h
@@ -20,6 +20,7 @@
#include "base/optional.h"
#include "base/synchronization/lock.h"
+#include "cobalt/loader/cache_fetcher.h"
#include "url/gurl.h"
namespace cobalt {
@@ -36,25 +37,35 @@
SplashScreenCache();
// Cache the splash screen.
- bool CacheSplashScreen(const std::string& key,
- const std::string& content) const;
+ bool CacheSplashScreen(const std::string& content) const;
// Read the cached the splash screen.
int ReadCachedSplashScreen(const std::string& key,
std::unique_ptr<char[]>* result) const;
- // Determine if a splash screen is cached corresponding to the key.
- bool IsSplashScreenCached(const std::string& key) const;
+ // Determine if a splash screen is cached corresponding to the current url.
+ bool IsSplashScreenCached() const;
+ // Set the URL of the currently requested splash screen.
+ void SetUrl(const GURL& url) { url_ = url; }
+
+ // Get the cache location of the currently requested splash screen.
+ GURL GetCachedSplashScreenUrl() {
+ base::Optional<std::string> key = GetKeyForStartUrl(url_);
+ return GURL(loader::kCacheScheme + ("://" + *key));
+ }
+
+ private:
// Get the key that corresponds to a starting URL. Optionally create
// subdirectories along the path.
static base::Optional<std::string> GetKeyForStartUrl(const GURL& url);
- private:
// Lock to protect access to the cache file.
mutable base::Lock lock_;
// Hash of the last read page contents.
mutable uint32_t last_page_hash_;
+ // Latest url that was navigated to.
+ GURL url_;
};
} // namespace browser
diff --git a/src/cobalt/browser/user_agent_string.cc b/src/cobalt/browser/user_agent_string.cc
index 2e7f57d..8e973b5 100644
--- a/src/cobalt/browser/user_agent_string.cc
+++ b/src/cobalt/browser/user_agent_string.cc
@@ -224,10 +224,15 @@
std::string os_name_and_version = platform_info.os_name_and_version;
#if defined(ENABLE_DEBUG_COMMAND_LINE_SWITCHES)
- base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kUserAgentOsNameVersion)) {
- os_name_and_version =
- command_line->GetSwitchValueASCII(switches::kUserAgentOsNameVersion);
+ // Because we add Cobalt's user agent string to Crashpad before we actually
+ // start Cobalt, the command line won't be initialized when we first try to
+ // get the user agent string.
+ if (base::CommandLine::InitializedForCurrentProcess()) {
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch(switches::kUserAgentOsNameVersion)) {
+ os_name_and_version =
+ command_line->GetSwitchValueASCII(switches::kUserAgentOsNameVersion);
+ }
}
#endif // ENABLE_DEBUG_COMMAND_LINE_SWITCHES
diff --git a/src/cobalt/browser/web_module.cc b/src/cobalt/browser/web_module.cc
index 3f8186d..d852db5 100644
--- a/src/cobalt/browser/web_module.cc
+++ b/src/cobalt/browser/web_module.cc
@@ -86,23 +86,18 @@
// deeper than this could be discarded, and will not be rendered.
const int kDOMMaxElementDepth = 32;
-bool CacheUrlContent(SplashScreenCache* splash_screen_cache, const GURL& url,
+void CacheUrlContent(SplashScreenCache* splash_screen_cache,
const std::string& content) {
- base::Optional<std::string> key = SplashScreenCache::GetKeyForStartUrl(url);
- if (key) {
- return splash_screen_cache->SplashScreenCache::CacheSplashScreen(*key,
- content);
- }
- return false;
+ splash_screen_cache->SplashScreenCache::CacheSplashScreen(content);
}
-base::Callback<bool(const GURL&, const std::string&)> CacheUrlContentCallback(
+base::Callback<void(const std::string&)> CacheUrlContentCallback(
SplashScreenCache* splash_screen_cache) {
// This callback takes in first the url, then the content string.
if (splash_screen_cache) {
return base::Bind(CacheUrlContent, base::Unretained(splash_screen_cache));
} else {
- return base::Callback<bool(const GURL&, const std::string&)>();
+ return base::Callback<void(const std::string&)>();
}
}
@@ -242,6 +237,10 @@
void CancelSynchronousLoads();
+ void IsReadyToFreeze(volatile bool* is_ready_to_freeze) {
+ *is_ready_to_freeze = !media_session_client_->is_active();
+ }
+
private:
class DocumentLoadedObserver;
@@ -593,6 +592,8 @@
media_session_client_ = media_session::MediaSessionClient::Create();
media_session_client_->SetMediaPlayerFactory(data.web_media_player_factory);
+ media_session_client_->SetMaybeFreezeCallback(
+ data.options.maybe_freeze_callback);
system_caption_settings_ = new cobalt::dom::captions::SystemCaptionSettings(
environment_settings_.get());
@@ -1692,5 +1693,16 @@
base::Unretained(impl_.get()), callback));
}
+bool WebModule::IsReadyToFreeze() {
+ DCHECK_NE(base::MessageLoop::current(), message_loop());
+
+ volatile bool is_ready_to_freeze = false;
+ message_loop()->task_runner()->PostBlockingTask(
+ FROM_HERE, base::Bind(&WebModule::Impl::IsReadyToFreeze,
+ base::Unretained(impl_.get()),
+ &is_ready_to_freeze));
+ return is_ready_to_freeze;
+}
+
} // namespace browser
} // namespace cobalt
diff --git a/src/cobalt/browser/web_module.h b/src/cobalt/browser/web_module.h
index cc09c00..afbb6a7 100644
--- a/src/cobalt/browser/web_module.h
+++ b/src/cobalt/browser/web_module.h
@@ -270,6 +270,11 @@
// there is no state to restore.
debug::backend::DebuggerState* debugger_state = nullptr;
#endif // defined(ENABLE_DEBUGGER)
+
+ // This callback is for checking the mediasession actions transitions. When
+ // there is no playback during Concealed state, we should provide a chance
+ // for Cobalt to freeze.
+ base::Closure maybe_freeze_callback;
};
typedef layout::LayoutManager::LayoutResults LayoutResults;
@@ -397,6 +402,9 @@
void RequestJavaScriptHeapStatistics(
const JavaScriptHeapStatisticsCallback& callback);
+ // Indicate the web module is ready to freeze.
+ bool IsReadyToFreeze();
+
private:
// Data required to construct a WebModule, initialized in the constructor and
// passed to |Initialize|.
diff --git a/src/cobalt/build/all.gyp b/src/cobalt/build/all.gyp
index f798b39..79ece6f 100644
--- a/src/cobalt/build/all.gyp
+++ b/src/cobalt/build/all.gyp
@@ -66,6 +66,7 @@
'<(DEPTH)/cobalt/network/network.gyp:*',
'<(DEPTH)/cobalt/overlay_info/overlay_info.gyp:*',
'<(DEPTH)/cobalt/render_tree/render_tree.gyp:*',
+ '<(DEPTH)/cobalt/renderer/backend/backend.gyp:graphics_system_test_deploy',
'<(DEPTH)/cobalt/renderer/renderer.gyp:*',
'<(DEPTH)/cobalt/renderer/sandbox/sandbox.gyp:*',
'<(DEPTH)/cobalt/samples/simple_example/simple_example.gyp:*',
@@ -89,7 +90,7 @@
'<(DEPTH)/net/net.gyp:net_unittests_deploy',
'<(DEPTH)/sql/sql.gyp:sql_unittests_deploy',
'<(DEPTH)/starboard/elf_loader/elf_loader.gyp:elf_loader_test_deploy',
- '<(DEPTH)/starboard/loader_app/loader_app.gyp:loader_app',
+ '<(DEPTH)/starboard/loader_app/loader_app.gyp:loader_app_tests_deploy',
'<(DEPTH)/starboard/nplb/nplb_evergreen_compat_tests/nplb_evergreen_compat_tests.gyp:nplb_evergreen_compat_tests_deploy',
],
'conditions': [
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 28196cb..a485246 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-278102
\ No newline at end of file
+282262
\ No newline at end of file
diff --git a/src/cobalt/configuration/configuration.cc b/src/cobalt/configuration/configuration.cc
index 1502037..9c5087c 100644
--- a/src/cobalt/configuration/configuration.cc
+++ b/src/cobalt/configuration/configuration.cc
@@ -25,7 +25,8 @@
Configuration* Configuration::configuration_ = nullptr;
Configuration* Configuration::GetInstance() {
- return base::Singleton<Configuration>::get();
+ return base::Singleton<Configuration,
+ base::LeakySingletonTraits<Configuration>>::get();
}
Configuration::Configuration() {
diff --git a/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2 b/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
index 9cac5c0..72a6830 100644
--- a/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
+++ b/src/cobalt/content/fonts/font_files/NotoColorEmoji.woff2
Binary files differ
diff --git a/src/cobalt/demos/content/background-mode-demo/background-mode-demo.html b/src/cobalt/demos/content/background-mode-demo/background-mode-demo.html
new file mode 100644
index 0000000..058300b
--- /dev/null
+++ b/src/cobalt/demos/content/background-mode-demo/background-mode-demo.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Loop Playback</title>
+ <style>
+ body {
+ background-color: rgb(255, 255, 255);
+ color: #0047ab;
+ font-size: 50px;
+ }
+ video {
+ transform: translateX(100px) rotate(3deg);
+ }
+ </style>
+</head>
+<body>
+ Loop Playback
+ <script type="text/javascript" src="background-mode-demo.js"></script>
+ <div>Playback Actions:
+ <span id='info' style='white-space: pre; background-color:#00FF00'></span>
+ </div>
+</body>
+</html>
diff --git a/src/cobalt/demos/content/background-mode-demo/background-mode-demo.js b/src/cobalt/demos/content/background-mode-demo/background-mode-demo.js
new file mode 100644
index 0000000..6de9271
--- /dev/null
+++ b/src/cobalt/demos/content/background-mode-demo/background-mode-demo.js
@@ -0,0 +1,188 @@
+// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The page simply plays an audio or a video stream in a loop, it can be used
+// in the following forms:
+// background-mode-demo.html&type=video
+// background-mode-demo.html&type=audio
+// If the stream is adaptive, it has to be fit in memory as this demo will
+// download the whole stream at once.
+
+var video = null;
+var type = null;
+
+let playlist = getPlaylist();
+let index = 0;
+
+var kAdaptiveAudioChunkSize = 720 * 1024;
+
+function downloadAndAppend(url, begin, end, source_buffer, callback) {
+ var xhr = new XMLHttpRequest;
+ xhr.open('GET', url, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.addEventListener('load', function(e) {
+ var onupdateend = function() {
+ source_buffer.removeEventListener('updateend', onupdateend);
+ callback();
+ };
+ source_buffer.addEventListener('updateend', onupdateend);
+ source_buffer.appendBuffer(new Uint8Array(e.target.response));
+ });
+
+ xhr.setRequestHeader('Range', ('bytes=' + begin +'-' + end));
+ xhr.send();
+}
+
+function createVideoElement() {
+ var video = document.createElement('video');
+ video.autoplay = true;
+ video.style.width = '1280px';
+ video.style.height = '720px';
+ document.body.appendChild(video);
+
+ return video;
+}
+
+function onVideoEnded() {
+ startNextVideo();
+}
+
+function startAdaptiveVideo() {
+ video.src = '';
+ video.load();
+ var mediasource = new MediaSource;
+ mediasource.addEventListener('sourceopen', function () {
+ if (type == 'audio') {
+ var audio_source_buffer = mediasource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
+ downloadAndAppend('../media-element-demo/dash-audio.mp4', 0, kAdaptiveAudioChunkSize * 10, audio_source_buffer, function () {
+ mediasource.endOfStream();
+ });
+ }
+
+ if (type == 'video') {
+ var video_source_buffer = mediasource.addSourceBuffer('video/mp4; codecs="avc1.640028"');
+ var audio_source_buffer = mediasource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
+ downloadAndAppend('../media-element-demo/dash-video-1080p.mp4', 0, 15 * 1024 * 1024, video_source_buffer, function () {
+ video_source_buffer.abort();
+ // Append the first two segments of the 240p video so we can see the transition.
+ downloadAndAppend('../media-element-demo/dash-audio.mp4', 0, kAdaptiveAudioChunkSize * 10, audio_source_buffer, function () {
+ mediasource.endOfStream();
+ });
+ });
+ }
+ })
+
+ video.src = window.URL.createObjectURL(mediasource);
+ video.addEventListener('ended', onVideoEnded);
+}
+
+
+function startNextVideo() {
+ startAdaptiveVideo();
+}
+
+function main() {
+ var get_parameters = window.location.search.substr(1).split('&');
+ for (var param of get_parameters) {
+ splitted = param.split('=');
+ if (splitted[0] == 'type') {
+ type = splitted[1];
+ }
+ }
+
+ if (type != 'audio' && type != 'video') {
+ throw "invalid type " + type;
+ }
+
+ video = createVideoElement();
+ startNextVideo();
+ updateMetadata();
+}
+
+main();
+
+// MeidaSession
+function updateMetadata() {
+ let track = playlist[index];
+
+ navigator.mediaSession.metadata = new MediaMetadata({
+ title: track.title,
+ artist: track.artist,
+ //artwork: track.artwork
+ });
+ navigator.mediaSession.playbackState = "playing";
+}
+
+let defaultSkipTime = 10;
+
+navigator.mediaSession.setActionHandler('seekbackward', function(event) {
+ const skipTime = event.seekOffset || defaultSkipTime;
+ video.currentTime = Math.max(video.currentTime - skipTime, 0);
+ updatePositionState();
+});
+
+navigator.mediaSession.setActionHandler('seekforward', function(event) {
+ const skipTime = event.seekOffset || defaultSkipTime;
+ video.currentTime = Math.min(video.currentTime + skipTime, video.duration);
+ updatePositionState();
+});
+
+navigator.mediaSession.setActionHandler('play', function() {
+ log_info('TimeStamp: ' + getTime() + ' seconds' + ' play');
+ video.play();
+ navigator.mediaSession.playbackState = "playing";
+});
+
+navigator.mediaSession.setActionHandler('pause', function() {
+ log_info('TimeStamp: ' + getTime() + ' seconds' + ' pause');
+ video.pause();
+ navigator.mediaSession.playbackState = "paused";
+});
+
+
+try {
+ navigator.mediaSession.setActionHandler('stop', function() {
+ log_info('TimeStamp: ' + getTime() + ' seconds' + ' stop');
+ });
+} catch(error) {
+}
+
+try {
+ navigator.mediaSession.setActionHandler('seekto', function(event) {
+ if (event.fastSeek && ('fastSeek' in video)) {
+ video.fastSeek(event.seekTime);
+ return;
+ }
+ video.currentTime = event.seekTime;
+ updatePositionState();
+ });
+} catch(error) {
+}
+
+function getPlaylist() {
+ return [{
+ title: 'Background mode demo',
+ artist: 'Cobalt',
+ }];
+}
+
+function log_info(message) {
+ console.warn(message);
+ document.getElementById('info').innerHTML += message + '.\n';
+}
+
+function getTime() {
+ return Math.floor(Date.now() / 1000 | 0);
+}
+
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.html b/src/cobalt/demos/content/media-element-demo/key-systems.html
new file mode 100644
index 0000000..7358ca5
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/key-systems.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Media Element Demo</title>
+ <style>
+ .title {
+ background-color: #FFF;
+ color: #0047ab;
+ font-size: 20px;
+ }
+ .query {
+ }
+ .result-success {
+ background-color: rgb(0, 128, 0);
+ }
+ .result-failure {
+ background-color: rgb(128, 0, 0);
+ }
+ body {
+ background-color: #FFF;
+ font-size: 20px;
+ }
+ </style>
+</head>
+<body>
+ <span class="title">Queries on Key Systems</span>
+ <script type="text/javascript" src="key-systems.js"></script>
+</body>
+</html>
diff --git a/src/cobalt/demos/content/media-element-demo/key-systems.js b/src/cobalt/demos/content/media-element-demo/key-systems.js
new file mode 100644
index 0000000..ec8f149
--- /dev/null
+++ b/src/cobalt/demos/content/media-element-demo/key-systems.js
@@ -0,0 +1,111 @@
+// Copyright 2020 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+function getRepresentation(keySystem, audioMime, videoMime, encryptionScheme) {
+ var representation = keySystem;
+ if (typeof audioMime !== 'undefined') {
+ representation += ', ' + audioMime;
+ }
+ if (typeof videoMime !== 'undefined') {
+ representation += ', ' + videoMime;
+ }
+ if (typeof encryptionScheme !== 'undefined') {
+ representation += ', encryptionscheme="' + encryptionScheme + '"';
+ }
+ return representation;
+}
+
+function checkForSupport(keySystem, audioMime, videoMime, encryptionScheme,
+ expectedResult) {
+ var configs = [{
+ initDataTypes: ['cenc', 'sinf', 'webm'],
+ audioCapabilities: [],
+ videoCapabilities: [],
+ }];
+ if (typeof audioMime !== 'undefined') {
+ configs[0].audioCapabilities.push(
+ {contentType: audioMime, encryptionScheme: encryptionScheme});
+ }
+ if (typeof videoMime !== 'undefined') {
+ configs[0].videoCapabilities.push(
+ {contentType: videoMime, encryptionScheme: encryptionScheme});
+ }
+ var representation = getRepresentation(keySystem, audioMime, videoMime,
+ encryptionScheme);
+ navigator.requestMediaKeySystemAccess(keySystem, configs)
+ .then(onKeySystemAccess.bind(this, representation, expectedResult),
+ onFailure.bind(this, representation, expectedResult));
+}
+
+function addQueryResult(query, result, expectedResult) {
+ var row = document.createElement('div');
+
+ var cell = document.createElement('span');
+ cell.className = 'query';
+ cell.textContent = query + ' => ';
+ row.appendChild(cell);
+
+ cell = document.createElement('span');
+ cell.className = result == expectedResult ? 'result-success' :
+ 'result-failure';
+ cell.textContent = result;
+ row.appendChild(cell);
+
+ document.body.appendChild(row);
+}
+
+function onKeySystemAccess(representation, expectedResult, keySystemAccess) {
+ addQueryResult(representation, 'supported', expectedResult);
+}
+
+function onFailure(representation, expectedResult) {
+ addQueryResult(representation, 'not supported', expectedResult);
+}
+
+checkForSupport('com.widevine.alpha.invalid', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, undefined, 'not supported');
+checkForSupport('com.widevine.alpha.invalid', undefined,
+ 'video/webm; codecs="vp9"', undefined, 'not supported');
+
+// 'invalid-scheme' is not a valid scheme.
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, 'invalid-scheme', 'not supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ 'invalid-scheme', 'not supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, undefined, 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ undefined, 'supported');
+
+// Empty string is not a valid scheme.
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, '', 'not supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ '', 'not supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, 'cenc', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ 'cenc', 'supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, 'cbcs', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ 'cbcs', 'supported');
+
+checkForSupport('com.widevine.alpha', 'audio/mp4; codecs="mp4a.40.2"',
+ undefined, 'cbcs-1-9', 'supported');
+checkForSupport('com.widevine.alpha', undefined, 'video/webm; codecs="vp9"',
+ 'cbcs-1-9', 'supported');
diff --git a/src/cobalt/doc/resources/devtools-overlay-console-modes.png b/src/cobalt/doc/resources/devtools-overlay-console-modes.png
new file mode 100644
index 0000000..5103358
--- /dev/null
+++ b/src/cobalt/doc/resources/devtools-overlay-console-modes.png
Binary files differ
diff --git a/src/cobalt/doc/web_debugging.md b/src/cobalt/doc/web_debugging.md
index c4711db..5090e4f 100644
--- a/src/cobalt/doc/web_debugging.md
+++ b/src/cobalt/doc/web_debugging.md
@@ -102,28 +102,146 @@
### Console
-Cobalt has two consoles:
-* Overlay console in Cobalt itself (shown with ctrl-O or F1).
-* Remote console shown in a connected DevTools session.
+Cobalt has two types of consoles:
+
+* Overlay Console: shown at runtime of Cobalt. It has multiple mode that it
+ can cycle between as well:
+ * HUD
+ * HUD & Debug Console
+ * Media Console
+* Remote Console: shown in a connected devtools session.
Both console UIs show messages logged from JavaScript (with `console.log()`,
etc.), and have a command line to evaluate arbitrary JavaScript in the context
of the page being debugged.
+#### Overlay Console
+
The overlay console also shows non-JavaScript logging from Cobalt itself, which
is mostly interesting to Cobalt developers rather than web app developers.
+The various modes of the overlay console are accessed by repeatedly pressing
+"`F1`" or "`Ctrl+O`". They cycle in order between: none, HUD, HUD & Debug, and
+Media. Alternatively, initial console state can be set with the
+`--debug_console=off|hud|debug|media` command-line switch (`--debug_console=on`
+is accepted as a legacy option and maps to "debug" setting).
+
+![Overlay Console mode switching](resources/devtools-overlay-console-flow.png)
+
+##### HUD overlay
+
+This brings up an overlay panel which does not block sending input to the
+underlying Cobalt app. It serves to display real-time statistics (e.g. memory
+usage) and configuration values (e.g. disabled codecs) of the Cobalt app in a
+compact string.
+
+##### Debug Console overlay
+
+This overlay is interactive and it shows messages from Cobalt, along with logs
+from Javacript `console.log()`. While it is active, you cannot interact directly
+with the underlying page.
+
+Additionally, it can act as a JS interpreter that will evaluate arbitrary
+expressions on the page being debugged. The output from these JS commands will
+also be printed to the Debug console.
+
+Finally, it has some special debug commands which can be listed by calling
+`d.help()`. They are provided by a debug helper object and the list of functions
+are invoked by prepending either "`debug`" or "`d`". For example, you can
+disable the vp9 codec manually for all future played videos in this session of
+Cobalt by sending `debug.disable_media_codecs("vp9")` to the console.
+
+Note: you can clear the disabled media codecs by sending
+`debug.disable_media_codecs("")`. The command takes a semicolon separated list
+of codecs as the input list of codecs to disable.
+
+##### Media Console overlay
+
+The media console is a specialized console of the debug overlay system, for
+playback and media related tasks. The current list of implemented features are:
+
+* Reading the play/pause state of the primary video
+* Reading the current time and duration of the primary video
+* Reading the playback rate of the primary video
+* Reading the currently disabled codecs for the player
+* Toggling between playing and pausing the primary video
+* Setting the current playback rate between various presets for the primary
+ video
+* Toggling the enabled/disabled state of the available codecs
+
+While the media console is shown, it is not possible to interact with the page
+below it directly.
+
+Additionally, the console does not show any meaningful information or
+interactions when no video is currently playing (all the readouts are blank or
+undefined). A status message of “No primary video.” indicates there is no valid
+player element on the current page.
+
+In the case of multiple videos playing (such as picture in picture), only the
+primary (fullscreen) video’s information is shown and the controls are only
+enabled for the primary video.
+
+The list of hotkeys and commands are dynamically generated as they are found to
+be available on app startup.
+
+Basic always-enabled commands are (case-sensitive):
+
+* "`p`" Toggle the play/pause state
+* "`]`" Increase the playback rate
+* "`[`" Decrease the playback rate
+
+The above commands will take effect instantly for the currently playing video.
+They have no effect if there is no video playing.
+
+The following commands are dynamically loaded based on the capability of the
+system:
+
+* "`CTRL+NUM`" Enable/disable specific video codec
+* "`ALT+NUM`" Enable/disable specific audio codec
+
+**Important:** Media Console cannot be used to directly select a specific codec for
+playback. See the section below for rough outline of steps to work around this.
+
+The list of available codecs for any video is chosen based on the decoders on
+the platform, and what formats YouTube itself serves. As a result, the only way
+to get a particular codec to play is to disable all the options until the
+desired codec is the one that is picked. Simply do the following procedure:
+
+* Pick the video you want to play.
+* Enable “stats for nerds” (See [help page for
+ instructions](https://support.google.com/youtube/answer/7519898)).
+* Write down the codecs that are chosen when playing the video, without any
+ codecs disabled (one for video, and one for audio).
+* Disable the default codecs.
+* Replay the same video from the browse screen.
+* Repeat until you identify all codecs that are available for the video, until
+ the video is unable to be played.
+* Use the above knowledge to disable the codecs to force the player into
+ choosing a particular codec, by process of elimination.
+
+**Important:** Disabled codecs only take effect when a video starts playing.
+When you play a video, the current list of disabled codecs is used to select an
+arbitrary enabled format. When you seek in the video, the disabled codecs list
+does not take effect. Only when you exit the player and re-enter by playing a
+video will any toggled codecs be affected.
+
+**Important:** Disabled codecs list is persistent for the app-run. If you
+disable “av01”, then until you re-enable it, “av01” formats will never be
+chosen.
+
+**Important:** If you disable all the available codecs, no video codec can be
+selected and an error dialog will be shown. This means that YouTube does not
+have the video in any other formats, outside of the codecs that are disabled.
+The player reports that it cannot play the video in any of the available formats
+so playback will fail here, which is intended.
+
+#### Remote Console
+
The console in DevTools is a richer UI that can show evaluated objects with an
expander so you can dig in to their properties. Logging from JavaScript with
`console.log()` can show objects and exceptions as well, in contrast to the
text-only messages shown in the console overlay.
-> There may be some things (e.g. timers) that still need to be hooked up to the
-> V8 backend, so please file a bug if something isn't working as expected.
-
-> When built with MozJs instead of V8, the functionality of the console is
-> limited to showing only text log messages.
-
Chrome docs:
* https://developers.google.com/web/tools/chrome-devtools/console/
diff --git a/src/cobalt/dom/eme/media_key_system_media_capability.idl b/src/cobalt/dom/eme/media_key_system_media_capability.idl
index 56e86d5..7f1da60 100644
--- a/src/cobalt/dom/eme/media_key_system_media_capability.idl
+++ b/src/cobalt/dom/eme/media_key_system_media_capability.idl
@@ -16,7 +16,11 @@
dictionary MediaKeySystemMediaCapability {
DOMString contentType = "";
+
// TODO: Implement robustness as per
// https://www.w3.org/TR/encrypted-media/#dom-mediakeysystemmediacapability-robustness.
// DOMString robustness = "";
+
+ // https://wicg.github.io/encrypted-media-encryption-scheme/
+ DOMString? encryptionScheme = null;
};
diff --git a/src/cobalt/dom/html_element.cc b/src/cobalt/dom/html_element.cc
index 80f7a7a..ea5606b 100644
--- a/src/cobalt/dom/html_element.cc
+++ b/src/cobalt/dom/html_element.cc
@@ -79,6 +79,11 @@
// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
const int32 kUiNavFocusTabIndexThreshold = -2;
+// Track which HTMLElement is currently focused by UI navigation so that
+// redundant blur / focus events are not fired. Do not de-reference this
+// variable -- it is only meant to identify objects.
+const HTMLElement* g_ui_nav_focus_ = nullptr;
+
struct NonTrivialStaticFields {
NonTrivialStaticFields() {
cssom::PropertyKeyVector computed_style_invalidation_properties;
@@ -1027,10 +1032,7 @@
// themselves still need to have their computed style updated, in case the
// value of display is changed.
if (computed_style()->display() == cssom::KeywordValue::GetNone()) {
- if (ui_nav_item_) {
- ui_nav_item_->SetEnabled(false);
- ui_nav_item_ = nullptr;
- }
+ ReleaseUiNavigationItem();
return;
}
@@ -1167,15 +1169,19 @@
directionality_ = base::nullopt;
}
-void HTMLElement::OnUiNavBlur() { Blur(); }
+void HTMLElement::OnUiNavBlur() {
+ if (g_ui_nav_focus_ == this) {
+ g_ui_nav_focus_ = nullptr;
+ Blur();
+ }
+}
void HTMLElement::OnUiNavFocus() {
- // Ensure the focusing steps do not trigger the UI navigation item to
- // force focus again.
- if (!ui_nav_focusing_) {
- ui_nav_focusing_ = true;
+ // Suppress the focus event if this is already focused -- i.e. the HTMLElement
+ // initiated the focus change that resulted in this call to OnUiNavFocus.
+ if (g_ui_nav_focus_ != this) {
+ g_ui_nav_focus_ = this;
Focus();
- ui_nav_focusing_ = false;
}
}
@@ -1211,12 +1217,7 @@
}
HTMLElement::~HTMLElement() {
- // Disable any associated navigation item to prevent callbacks during
- // destruction.
- if (ui_nav_item_) {
- ui_nav_item_->SetEnabled(false);
- ui_nav_item_ = nullptr;
- }
+ ReleaseUiNavigationItem();
if (IsInDocument()) {
dom_stat_tracker_->OnHtmlElementRemovedFromDocument();
@@ -1255,11 +1256,7 @@
// Node::OnRemovedFromDocument().
ClearRuleMatchingStateInternal(false /*invalidate_descendants*/);
- // Release the associated navigation item as the object is no longer visible.
- if (ui_nav_item_) {
- ui_nav_item_->SetEnabled(false);
- ui_nav_item_ = nullptr;
- }
+ ReleaseUiNavigationItem();
}
void HTMLElement::OnMutation() { InvalidateMatchingRulesRecursively(); }
@@ -1340,6 +1337,45 @@
old_active_element->AsHTMLElement()->RunUnFocusingSteps();
}
+ // Custom, not in any spec.
+ // Set the focus item for the UI navigation system. Search up the DOM tree to
+ // find the nearest ancestor that is a UI navigation item if needed. Do this
+ // step before dispatching events as the event handlers may make UI navigation
+ // changes.
+ for (Node* node = this; node; node = node->parent_node()) {
+ Element* element = node->AsElement();
+ if (!element) {
+ continue;
+ }
+ HTMLElement* html_element = element->AsHTMLElement();
+ if (!html_element) {
+ continue;
+ }
+ if (!html_element->ui_nav_item_ ||
+ html_element->ui_nav_item_->IsContainer()) {
+ continue;
+ }
+ if (g_ui_nav_focus_ == html_element) {
+ // UI navigation is already focused on the correct element.
+ break;
+ }
+ // Updating the g_ui_nav_focus_ has the additional effect of suppressing
+ // the Blur call for the previously focused HTMLElement and the Focus call
+ // for this HTMLElement as a result of OnUiNavBlur / OnUiNavFocus callbacks
+ // that result from initiating the UI navigation focus change.
+ g_ui_nav_focus_ = html_element;
+ // Only navigation items attached to the root container are interactable.
+ // If the item is not registered with a container, then force a layout to
+ // connect items to their containers and eventually to the root container.
+ scoped_refptr<ui_navigation::NavItem> nav_item = html_element->ui_nav_item_;
+ if (!nav_item->GetContainerItem()) {
+ // UI navigation items are updated as part of generating the render tree.
+ node_document()->DoSynchronousLayoutAndGetRenderTree();
+ }
+ nav_item->Focus();
+ break;
+ }
+
// focusin: A user agent MUST dispatch this event when an event target is
// about to receive focus. This event type MUST be dispatched before the
// element is given focus. The event target MUST be the element which is about
@@ -1368,18 +1404,6 @@
// Custom, not in any spec.
ClearRuleMatchingState();
-
- // Set the focus item for the UI navigation system.
- if (ui_nav_item_ && !ui_nav_item_->IsContainer() && !ui_nav_focusing_) {
- // Only navigation items attached to the root container are interactable.
- // If the item is not registered with a container, then force a layout to
- // connect items to their containers and eventually to the root container.
- if (!ui_nav_item_->GetContainerItem()) {
- // UI navigation items are updated as part of generating the render tree.
- node_document()->DoSynchronousLayoutAndGetRenderTree();
- }
- ui_nav_item_->Focus();
- }
}
// Algorithm for RunUnFocusingSteps:
@@ -2011,6 +2035,11 @@
}
}
+ // Update the UI navigation item and invalidate layout boxes if needed.
+ if (!UpdateUiNavigationAndReturnIfLayoutBoxesAreValid()) {
+ invalidation_flags.invalidate_layout_boxes = true;
+ }
+
if (invalidation_flags.mark_descendants_as_display_none) {
MarkNotDisplayedOnDescendants();
}
@@ -2033,9 +2062,6 @@
}
}
- // Update the UI navigation item.
- UpdateUiNavigationType();
-
computed_style_valid_ = true;
pseudo_elements_computed_styles_valid_ = true;
}
@@ -2096,7 +2122,7 @@
computed_style()->visibility() == cssom::KeywordValue::GetVisible();
}
-void HTMLElement::UpdateUiNavigationType() {
+bool HTMLElement::UpdateUiNavigationAndReturnIfLayoutBoxesAreValid() {
base::Optional<ui_navigation::NativeItemType> ui_nav_item_type;
if (computed_style()->overflow() == cssom::KeywordValue::GetAuto() ||
computed_style()->overflow() == cssom::KeywordValue::GetScroll()) {
@@ -2115,14 +2141,19 @@
if (ui_nav_item_->GetType() == *ui_nav_item_type) {
// Keep using the existing navigation item.
ui_nav_item_->SetDir(ui_nav_item_dir);
- return;
+ return true;
}
// The current navigation item isn't of the correct type. Disable it so
// that callbacks won't be invoked for it. The object will be destroyed
// when all references to it are released.
+ if (g_ui_nav_focus_ == this) {
+ g_ui_nav_focus_ = nullptr;
+ ui_nav_item_->UnfocusAll();
+ }
ui_nav_item_->SetEnabled(false);
ui_nav_item_ = nullptr;
}
+
ui_nav_item_ = new ui_navigation::NavItem(
*ui_nav_item_type,
base::Bind(
@@ -2141,8 +2172,36 @@
FROM_HERE,
base::Bind(&HTMLElement::OnUiNavScroll, base::AsWeakPtr(this))));
ui_nav_item_->SetDir(ui_nav_item_dir);
+ return false;
} else if (ui_nav_item_) {
// This navigation item is no longer relevant.
+ if (g_ui_nav_focus_ == this) {
+ g_ui_nav_focus_ = nullptr;
+ ui_nav_item_->UnfocusAll();
+ }
+ ui_nav_item_->SetEnabled(false);
+ ui_nav_item_ = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+void HTMLElement::ReleaseUiNavigationItem() {
+ if (ui_nav_item_) {
+ // Make sure layout updates this element.
+ InvalidateLayoutBoxesOfNodeAndAncestors();
+ if (ui_nav_item_->IsContainer()) {
+ // Make sure layout updates any focus items that may be in this container.
+ InvalidateLayoutBoxesOfDescendants();
+ }
+
+ // Disable the UI navigation item so it won't receive anymore callbacks
+ // while being released.
+ if (g_ui_nav_focus_ == this) {
+ g_ui_nav_focus_ = nullptr;
+ ui_nav_item_->UnfocusAll();
+ }
ui_nav_item_->SetEnabled(false);
ui_nav_item_ = nullptr;
}
diff --git a/src/cobalt/dom/html_element.h b/src/cobalt/dom/html_element.h
index 09211ad..3994f6f 100644
--- a/src/cobalt/dom/html_element.h
+++ b/src/cobalt/dom/html_element.h
@@ -419,7 +419,8 @@
void ClearRuleMatchingStateInternal(bool invalidate_descendants);
// Update the UI navigation item type for this element.
- void UpdateUiNavigationType();
+ bool UpdateUiNavigationAndReturnIfLayoutBoxesAreValid();
+ void ReleaseUiNavigationItem();
// Clear the list of active background images, and notify the animated image
// tracker to stop the animations.
@@ -515,12 +516,6 @@
// boxes without requiring a new layout.
scoped_refptr<ui_navigation::NavItem> ui_nav_item_;
- // This temporary flag is used to avoid a cycle on focus changes. When the
- // HTML element receives focus, it must inform the UI navigation item. When
- // the UI navigation item receives focus (either by calling SetFocus or by an
- // update from the UI engine), it will tell the HTML element it was focused.
- bool ui_nav_focusing_ = false;
-
// HTMLElement is a friend of Animatable so that animatable can insert and
// remove animations into HTMLElement's set of animations.
friend class DOMAnimatable;
diff --git a/src/cobalt/dom/html_media_element.cc b/src/cobalt/dom/html_media_element.cc
index 12061ca..beb04c7 100644
--- a/src/cobalt/dom/html_media_element.cc
+++ b/src/cobalt/dom/html_media_element.cc
@@ -251,10 +251,9 @@
DLOG_IF(ERROR, !key_system.empty())
<< "CanPlayType() only accepts one parameter but (" << key_system
<< ") is passed as a second parameter.";
- const bool kIsProgressive = true;
auto support_type =
- html_element_context()->can_play_type_handler()->CanPlayType(
- mime_type, key_system, kIsProgressive);
+ html_element_context()->can_play_type_handler()->CanPlayProgressive(
+ mime_type);
std::string result = "";
switch (support_type) {
case kSbMediaSupportTypeNotSupported:
diff --git a/src/cobalt/dom/media_source.cc b/src/cobalt/dom/media_source.cc
index e94f4c4..57817f4 100644
--- a/src/cobalt/dom/media_source.cc
+++ b/src/cobalt/dom/media_source.cc
@@ -269,10 +269,8 @@
DOMSettings* dom_settings =
base::polymorphic_downcast<DOMSettings*>(settings);
DCHECK(dom_settings->can_play_type_handler());
- const bool kIsProgressive = false;
SbMediaSupportType support_type =
- dom_settings->can_play_type_handler()->CanPlayType(type.c_str(), "",
- kIsProgressive);
+ dom_settings->can_play_type_handler()->CanPlayAdaptive(type.c_str(), "");
if (support_type == kSbMediaSupportTypeNotSupported) {
LOG(INFO) << "MediaSource::IsTypeSupported(" << type
<< ") -> not supported/false";
diff --git a/src/cobalt/dom/navigator.cc b/src/cobalt/dom/navigator.cc
index c57b886..a6d3ce3 100644
--- a/src/cobalt/dom/navigator.cc
+++ b/src/cobalt/dom/navigator.cc
@@ -31,12 +31,117 @@
using cobalt::media_session::MediaSession;
-namespace {
-const char kLicensesRelativePath[] = "/licenses/licenses_cobalt.txt";
-} // namespace
-
namespace cobalt {
namespace dom {
+namespace {
+
+const char kLicensesRelativePath[] = "/licenses/licenses_cobalt.txt";
+
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+
+std::string ToString(const std::string& str, int indent_level);
+std::string ToString(const eme::MediaKeySystemMediaCapability& capability,
+ int indent_level);
+std::string ToString(const eme::MediaKeySystemConfiguration& configuration,
+ int indent_level);
+
+std::string GetIndent(int indent_level) {
+ std::string indent;
+ while (indent_level > 0) {
+ indent += " ";
+ --indent_level;
+ }
+ return indent;
+}
+
+template <typename T>
+std::string ToString(const script::Sequence<T>& sequence, int indent_level) {
+ std::stringstream ss;
+
+ ss << "{\n";
+
+ for (auto iter = sequence.begin(); iter != sequence.end(); ++iter) {
+ ss << ToString(*iter, indent_level + 1) << ",\n";
+ }
+
+ ss << GetIndent(indent_level) << "}";
+
+ return ss.str();
+}
+
+std::string ToString(const std::string& str, int indent_level) {
+ return GetIndent(indent_level) + str;
+}
+
+std::string ToString(const eme::MediaKeySystemMediaCapability& capability,
+ int indent_level) {
+ return GetIndent(indent_level) + '\'' + capability.content_type() + "'/'" +
+ capability.encryption_scheme().value_or("(null)") + '\'';
+}
+
+std::string ToString(const eme::MediaKeySystemConfiguration& configuration,
+ int indent_level) {
+ DCHECK(configuration.has_label());
+
+ std::stringstream ss;
+
+ ss << GetIndent(indent_level) << "label:'" << configuration.label()
+ << "': {\n";
+ if (configuration.has_init_data_types()) {
+ ss << GetIndent(indent_level + 1) << "init_data_types: "
+ << ToString(configuration.init_data_types(), indent_level + 1) << ",\n";
+ }
+ if (configuration.has_audio_capabilities()) {
+ ss << GetIndent(indent_level + 1) << "audio_capabilities: "
+ << ToString(configuration.audio_capabilities(), indent_level + 1)
+ << ",\n";
+ }
+ if (configuration.has_video_capabilities()) {
+ ss << GetIndent(indent_level + 1) << "video_capabilities: "
+ << ToString(configuration.video_capabilities(), indent_level + 1)
+ << ",\n";
+ }
+ ss << GetIndent(indent_level) << "}";
+
+ return ss.str();
+}
+
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
+
+// This function is used when the underlying SbMediaCanPlayMimeAndKeySystem()
+// implementation doesn't support extra attributes on |key_system|, it makes
+// decision based on the key system itself.
+bool IsEncryptionSchemeSupportedByDefault(
+ const std::string& key_system, const std::string& encryption_scheme) {
+ // 1. Playready only supports "cenc".
+ if (key_system.find("playready") != key_system.npos) {
+ return encryption_scheme == "cenc";
+ }
+ // 2. Fairplay only supports "cbcs" and "cbcs-1-9".
+ if (key_system.find("fairplay") != key_system.npos) {
+ return encryption_scheme == "cbcs" || encryption_scheme == "cbcs-1-9";
+ }
+ // 3. Widevine only supports "cenc", "cbcs" and "cbcs-1-9".
+ if (key_system.find("widevine") != key_system.npos) {
+ return encryption_scheme == "cenc" || encryption_scheme == "cbcs" ||
+ encryption_scheme == "cbcs-1-9";
+ }
+
+ // The key system is unknown, assume only "cenc" is supported.
+ return encryption_scheme == "cenc";
+}
+
+bool CanPlay(const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& content_type, const std::string& key_system) {
+ auto can_play_result = can_play_type_handler.CanPlayAdaptive(
+ content_type.c_str(), key_system.c_str());
+ LOG_IF(INFO, can_play_result == kSbMediaSupportTypeMaybe)
+ << "CanPlayAdaptive() returns \"maybe\".";
+ return can_play_result == kSbMediaSupportTypeProbably ||
+ can_play_result == kSbMediaSupportTypeMaybe;
+}
+
+} // namespace
Navigator::Navigator(
script::EnvironmentSettings* settings, const std::string& user_agent,
@@ -123,17 +228,16 @@
return media_session_;
}
-namespace {
-
+// TODO: Move the following two functions to the bottom of the file, in sync
+// with the order of declaration.
// See
// https://www.w3.org/TR/encrypted-media/#get-supported-capabilities-for-audio-video-type.
base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
-TryGetSupportedCapabilities(
+Navigator::TryGetSupportedCapabilities(
+ const media::CanPlayTypeHandler& can_play_type_handler,
const std::string& key_system,
const script::Sequence<MediaKeySystemMediaCapability>&
- requested_media_capabilities,
- const media::CanPlayTypeHandler* can_play_type_handler) {
- DCHECK(can_play_type_handler);
+ requested_media_capabilities) {
// 2. Let supported media capabilities be an empty sequence of
// MediaKeySystemMediaCapability dictionaries.
script::Sequence<MediaKeySystemMediaCapability> supported_media_capabilities;
@@ -152,17 +256,10 @@
// 3.13. If the user agent and [CDM] implementation definitely support
// playback of encrypted media data for the combination of container,
// media types [...]:
- const bool kIsProgressive = false;
- if (can_play_type_handler->CanPlayType(
- content_type.c_str(), key_system.c_str(), kIsProgressive) ==
- kSbMediaSupportTypeProbably) {
- LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
- << ", " << key_system << ") -> supported";
+ if (CanPlayWithCapability(can_play_type_handler, key_system,
+ requested_media_capability)) {
// 3.13.1. Add requested media capability to supported media capabilities.
supported_media_capabilities.push_back(requested_media_capability);
- } else {
- LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
- << ", " << key_system << ") -> not supported";
}
}
// 4. If supported media capabilities is empty, return null.
@@ -179,10 +276,11 @@
// is always given and go straight to "3.1.1.2 Get Supported Configuration and
// Consent". See
// https://www.w3.org/TR/encrypted-media/#get-supported-configuration-and-consent.
-base::Optional<eme::MediaKeySystemConfiguration> TryGetSupportedConfiguration(
+base::Optional<eme::MediaKeySystemConfiguration>
+Navigator::TryGetSupportedConfiguration(
+ const media::CanPlayTypeHandler& can_play_type_handler,
const std::string& key_system,
- const eme::MediaKeySystemConfiguration& candidate_configuration,
- const media::CanPlayTypeHandler* can_play_type_handler) {
+ const eme::MediaKeySystemConfiguration& candidate_configuration) {
// 1. Let accumulated configuration be a new MediaKeySystemConfiguration
// dictionary.
eme::MediaKeySystemConfiguration accumulated_configuration;
@@ -225,8 +323,8 @@
// Supported Capabilities for Audio/Video Type" algorithm.
base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
maybe_video_capabilities = TryGetSupportedCapabilities(
- key_system, candidate_configuration.video_capabilities(),
- can_play_type_handler);
+ can_play_type_handler, key_system,
+ candidate_configuration.video_capabilities());
// 16.2. If video capabilities is null, return NotSupported.
if (!maybe_video_capabilities) {
return base::nullopt;
@@ -249,8 +347,8 @@
// Supported Capabilities for Audio/Video Type" algorithm.
base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
maybe_audio_capabilities = TryGetSupportedCapabilities(
- key_system, candidate_configuration.audio_capabilities(),
- can_play_type_handler);
+ can_play_type_handler, key_system,
+ candidate_configuration.audio_capabilities());
// 17.2. If audio capabilities is null, return NotSupported.
if (!maybe_audio_capabilities) {
return base::nullopt;
@@ -269,8 +367,6 @@
return accumulated_configuration;
}
-} // namespace
-
// See
// https://www.w3.org/TR/encrypted-media/#dom-navigator-requestmediakeysystemaccess.
script::Handle<Navigator::InterfacePromise>
@@ -286,6 +382,12 @@
script_value_factory_
->CreateInterfacePromise<scoped_refptr<eme::MediaKeySystemAccess>>();
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ LOG(INFO) << "Navigator.RequestMediaKeySystemAccess() called with '"
+ << key_system << "', and\n"
+ << ToString(supported_configurations, 0);
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
+
// 1. If |keySystem| is the empty string, return a promise rejected
// with a newly created TypeError.
// 2. If |supportedConfigurations| is empty, return a promise rejected
@@ -302,14 +404,19 @@
// 6.3.3. If supported configuration is not NotSupported:
base::Optional<eme::MediaKeySystemConfiguration>
maybe_supported_configuration = TryGetSupportedConfiguration(
- key_system, supported_configurations.at(configuration_index),
- dom_settings->can_play_type_handler());
+ *dom_settings->can_play_type_handler(), key_system,
+ supported_configurations.at(configuration_index));
if (maybe_supported_configuration) {
// 6.3.3.1. Let access be a new MediaKeySystemAccess object.
scoped_refptr<eme::MediaKeySystemAccess> media_key_system_access(
new eme::MediaKeySystemAccess(key_system,
*maybe_supported_configuration,
script_value_factory_));
+#if !defined(COBALT_BUILD_TYPE_GOLD)
+ LOG(INFO) << "Navigator.RequestMediaKeySystemAccess() resolved with '"
+ << media_key_system_access->key_system() << "', and\n"
+ << ToString(media_key_system_access->GetConfiguration(), 0);
+#endif // !defined(COBALT_BUILD_TYPE_GOLD)
// 6.3.3.2. Resolve promise.
promise->Resolve(media_key_system_access);
return promise;
@@ -334,5 +441,115 @@
tracer->Trace(system_caption_settings_);
}
+bool Navigator::CanPlayWithCapability(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& key_system,
+ const MediaKeySystemMediaCapability& media_capability) {
+ const std::string& content_type = media_capability.content_type();
+
+ // There is no encryption scheme specified, check directly.
+ if (!media_capability.encryption_scheme().has_value()) {
+ if (CanPlay(can_play_type_handler, content_type, key_system)) {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+ << ", " << key_system << ") -> supported";
+ return true;
+ }
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+ << ", " << key_system << ") -> not supported";
+ return false;
+ }
+
+ if (!key_system_with_attributes_supported_.has_value()) {
+ if (!CanPlay(can_play_type_handler, content_type, key_system)) {
+ // If the check on the basic key system fails, we don't even bother to
+ // check if it supports key system with attributes.
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+ << ", " << key_system << ") -> not supported";
+ return false;
+ }
+ auto key_system_with_invalid_attribute =
+ std::string(key_system) + "; invalid_attributes=\"value\"";
+ // If an implementation supports attributes, it should ignore unknown
+ // attributes and return true, as the key system has been verified to be
+ // supported above.
+ key_system_with_attributes_supported_ = CanPlay(
+ can_play_type_handler, content_type, key_system_with_invalid_attribute);
+ if (key_system_with_attributes_supported_.value()) {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() will use key"
+ << " system with attributes.";
+ } else {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() won't use key"
+ << " system with attributes.";
+ }
+ }
+
+ DCHECK(key_system_with_attributes_supported_.has_value());
+
+ // As a key system with attributes support is optional, and the logic to
+ // determine whether the encryptionScheme is supported can be quite different
+ // depending on whether this is supported, we encapsulate the logic into two
+ // different functions.
+ if (key_system_with_attributes_supported_.value()) {
+ return CanPlayWithAttributes(can_play_type_handler, content_type,
+ key_system,
+ media_capability.encryption_scheme().value());
+ }
+
+ return CanPlayWithoutAttributes(can_play_type_handler, content_type,
+ key_system,
+ media_capability.encryption_scheme().value());
+}
+
+bool Navigator::CanPlayWithoutAttributes(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& content_type, const std::string& key_system,
+ const std::string& encryption_scheme) {
+ DCHECK(key_system_with_attributes_supported_.has_value());
+ DCHECK(!key_system_with_attributes_supported_.value());
+
+ if (!IsEncryptionSchemeSupportedByDefault(key_system, encryption_scheme)) {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess() rejects "
+ << key_system << " because encryptionScheme \""
+ << encryption_scheme << "\" is not supported.";
+ return false;
+ }
+
+ if (CanPlay(can_play_type_handler, content_type, key_system)) {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+ << ", " << key_system << ") with encryptionScheme \""
+ << encryption_scheme << "\" -> supported";
+ return true;
+ }
+
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type << ", "
+ << key_system << ") with encryptionScheme \"" << encryption_scheme
+ << "\" -> not supported";
+ return false;
+}
+
+bool Navigator::CanPlayWithAttributes(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& content_type, const std::string& key_system,
+ const std::string& encryption_scheme) {
+ DCHECK(key_system_with_attributes_supported_.has_value());
+ DCHECK(key_system_with_attributes_supported_.value());
+
+ auto key_system_with_attributes =
+ key_system + "; encryptionscheme=\"" + encryption_scheme + '"';
+
+ if (CanPlay(can_play_type_handler, content_type,
+ key_system_with_attributes)) {
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type
+ << ", " << key_system << ") with encryptionScheme \""
+ << encryption_scheme << "\" -> supported";
+ return true;
+ }
+
+ LOG(INFO) << "Navigator::RequestMediaKeySystemAccess(" << content_type << ", "
+ << key_system << ") with encryptionScheme \"" << encryption_scheme
+ << "\" -> not supported";
+ return false;
+}
+
} // namespace dom
} // namespace cobalt
diff --git a/src/cobalt/dom/navigator.h b/src/cobalt/dom/navigator.h
index 10a417d..1489f78 100644
--- a/src/cobalt/dom/navigator.h
+++ b/src/cobalt/dom/navigator.h
@@ -18,6 +18,7 @@
#include <string>
#include "base/memory/ref_counted.h"
+#include "base/optional.h"
#include "cobalt/dom/captions/system_caption_settings.h"
#include "cobalt/dom/eme/media_key_system_configuration.h"
#include "cobalt/dom/mime_type_array.h"
@@ -26,6 +27,7 @@
#include "cobalt/media_session/media_session.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/script_value_factory.h"
+#include "cobalt/script/sequence.h"
#include "cobalt/script/wrappable.h"
namespace cobalt {
@@ -84,6 +86,33 @@
private:
~Navigator() override {}
+ base::Optional<script::Sequence<MediaKeySystemMediaCapability>>
+ TryGetSupportedCapabilities(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& key_system,
+ const script::Sequence<MediaKeySystemMediaCapability>&
+ requested_media_capabilities);
+
+ base::Optional<eme::MediaKeySystemConfiguration> TryGetSupportedConfiguration(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& key_system,
+ const eme::MediaKeySystemConfiguration& candidate_configuration);
+
+ bool CanPlayWithCapability(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& key_system,
+ const MediaKeySystemMediaCapability& media_capability);
+
+ bool CanPlayWithoutAttributes(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& content_type, const std::string& key_system,
+ const std::string& encryption_scheme);
+
+ bool CanPlayWithAttributes(
+ const media::CanPlayTypeHandler& can_play_type_handler,
+ const std::string& content_type, const std::string& key_system,
+ const std::string& encryption_scheme);
+
std::string user_agent_;
std::string language_;
scoped_refptr<MimeTypeArray> mime_types_;
@@ -93,6 +122,7 @@
scoped_refptr<cobalt::dom::captions::SystemCaptionSettings>
system_caption_settings_;
script::ScriptValueFactory* script_value_factory_;
+ base::Optional<bool> key_system_with_attributes_supported_;
DISALLOW_COPY_AND_ASSIGN(Navigator);
};
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index d4ecd45..a667e9e 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -710,7 +710,7 @@
return;
}
DLOG(INFO) << "Caching splash screen for URL " << location()->url();
- splash_screen_cache_callback_.Run(location()->url(), content);
+ splash_screen_cache_callback_.Run(content);
}
const scoped_refptr<OnScreenKeyboard>& Window::on_screen_keyboard() const {
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index a590389..3b3976f 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -121,7 +121,7 @@
// close() was called.
typedef base::Callback<void(base::TimeDelta)> CloseCallback;
typedef UrlRegistry<MediaSource> MediaSourceRegistry;
- typedef base::Callback<bool(const GURL&, const std::string&)> CacheCallback;
+ typedef base::Callback<void(const std::string&)> CacheCallback;
enum ClockType {
kClockTypeTestRunner,
diff --git a/src/cobalt/extension/media_session.h b/src/cobalt/extension/media_session.h
index efb9f17..dccbd42 100644
--- a/src/cobalt/extension/media_session.h
+++ b/src/cobalt/extension/media_session.h
@@ -25,13 +25,13 @@
#define kCobaltExtensionMediaSessionName "dev.cobalt.extension.MediaSession"
-enum CobaltExtensionPlaybackState {
- kCobaltExtensionPlaying = 0,
- kCobaltExtensionPaused = 1,
- kCobaltExtensionNone = 2
-};
+typedef enum CobaltExtensionMediaSessionPlaybackState {
+ kCobaltExtensionMediaSessionNone = 0,
+ kCobaltExtensionMediaSessionPaused = 1,
+ kCobaltExtensionMediaSessionPlaying = 2
+} CobaltExtensionMediaSessionPlaybackState;
-enum CobaltExtensionMediaSessionAction {
+typedef enum CobaltExtensionMediaSessionAction {
kCobaltExtensionMediaSessionActionPlay,
kCobaltExtensionMediaSessionActionPause,
kCobaltExtensionMediaSessionActionSeekbackward,
@@ -44,7 +44,7 @@
// Not part of spec, but used in Cobalt implementation.
kCobaltExtensionMediaSessionActionNumActions,
-};
+} CobaltExtensionMediaSessionAction;
typedef struct CobaltExtensionMediaImage {
// These fields are null-terminated strings copied over from IDL.
@@ -63,6 +63,20 @@
size_t artwork_count;
} CobaltExtensionMediaMetadata;
+typedef struct CobaltExtensionMediaSessionActionDetails {
+ CobaltExtensionMediaSessionAction action;
+
+ // Seek time/offset are non-negative. Negative value signifies "unset".
+ double seek_offset;
+ double seek_time;
+
+ bool fast_seek;
+} CobaltExtensionMediaSessionActionDetails;
+
+typedef void (*CobaltExtensionMediaSessionUpdatePlatformPlaybackStateCallback)(
+ CobaltExtensionMediaSessionPlaybackState state, void* callback_context);
+typedef void (*CobaltExtensionMediaSessionInvokeActionCallback)(
+ CobaltExtensionMediaSessionActionDetails details, void* callback_context);
// This struct and all its members should only be used for piping data to each
// platform's implementation of OnMediaSessionStateChanged and they are only
@@ -70,11 +84,21 @@
// will be referenced later.
typedef struct CobaltExtensionMediaSessionState {
SbTimeMonotonic duration;
- enum CobaltExtensionPlaybackState actual_playback_state;
+ CobaltExtensionMediaSessionPlaybackState actual_playback_state;
bool available_actions[kCobaltExtensionMediaSessionActionNumActions];
- CobaltExtensionMediaMetadata metadata;
+ CobaltExtensionMediaMetadata* metadata;
double actual_playback_rate;
SbTimeMonotonic current_playback_position;
+
+ // Callback to MediaSessionClient::UpdatePlatformPlaybackState for when the
+ // platform updates state.
+ CobaltExtensionMediaSessionUpdatePlatformPlaybackStateCallback
+ update_platform_playback_state_callback;
+
+ // Callback to MediaSessionClient::InvokeAction for when the platform handles
+ // a new media action.
+ void* callback_context;
+ CobaltExtensionMediaSessionInvokeActionCallback invoke_action_callback;
} CobaltExtensionMediaSessionState;
typedef struct CobaltExtensionMediaSessionApi {
@@ -92,8 +116,17 @@
} CobaltExtensionMediaSessionApi;
+inline void CobaltExtensionMediaSessionActionDetailsInit(
+ CobaltExtensionMediaSessionActionDetails* details,
+ CobaltExtensionMediaSessionAction action) {
+ details->action = action;
+ details->seek_offset = -1.0;
+ details->seek_time = -1.0;
+ details->fast_seek = false;
+}
+
#ifdef __cplusplus
} // extern "C"
#endif
-#endif // COBALT_EXTENSION_MEDIA_SESSION_H_
\ No newline at end of file
+#endif // COBALT_EXTENSION_MEDIA_SESSION_H_
diff --git a/src/cobalt/fetch/embedded_scripts/fetch.js b/src/cobalt/fetch/embedded_scripts/fetch.js
index ac5a7a3..ec89191 100644
--- a/src/cobalt/fetch/embedded_scripts/fetch.js
+++ b/src/cobalt/fetch/embedded_scripts/fetch.js
@@ -1,22 +1,22 @@
-'use strict';(function(c){function E(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new g("Invalid character in header field name");return a.toLowerCase()}function Q(a){"string"!==typeof a&&(a=String(a));var b;var d=0;for(b=a.length;d<b;d++){var e=a.charCodeAt(d);if(9!==e&&10!==e&&13!==e&&32!==e)break}for(b=a.length-1;b>d&&(e=a.charCodeAt(b),9===e||10===e||13===e||32===e);b--);a=a.substring(d,b+1);d=0;for(b=a.length;d<b;d++)if(e=a.charCodeAt(d),256<=e||0===e||
-10===e||13===e)throw new g("Invalid character in header field value");return a}function W(a,b){throw new g("Immutable header cannot be modified");}function X(a,b){return!1}function Y(a,b){a=a.toLowerCase();return-1<Z.indexOf(a)||a.startsWith("proxy-")||a.startsWith("sec-")?!0:!1}function aa(a,b){a=a.toLowerCase();return-1<ba.indexOf(a)||"content-type"===a&&(b=b.split(";")[0].toLowerCase(),-1<ca.indexOf(b))?!1:!0}function L(a,b){return-1<da.indexOf(a.toLowerCase())?!0:!1}function h(a){this[l]=new I;
-void 0===this[v]&&(this[v]=X);if(void 0!==a){if(null===a||"object"!==typeof a)throw new g("Constructing Headers with invalid parameters");a instanceof h?a.forEach(function(a,d){this.append(d,a)},this):F.isArray(a)?a.forEach(function(a){if(2!==a.length)throw new g("Constructing Headers with invalid parameters");this.append(a[0],a[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function G(a,b){var d=ea(h.prototype);d[v]=b;h.call(d,a);return d}function M(a){if(a.bodyUsed)return t.reject(new g("Body was already read"));
-if(null===a.body)return t.resolve(new u(0));if(fa(a.body))return t.reject(new g("ReadableStream was already locked"));var b=a.body.getReader(),d=[],e=0;return b.read().then(function ha(a){if(a.done){if(0===d.length)a=new u(0);else if(1===d.length)a=new u(d[0].buffer);else{a=new u(e);for(var k=0,c=d.length,f=0;k<c;k++)a.set(d[k],f),f+=d[k].length}return a}return a.value instanceof u?(e+=a.value.length,d.push(a.value),b.read().then(ha)):t.reject(new g("Invalid stream data type"))})}function R(){this._initBody=
-function(a){this[N]=!1;this[m]=null===a||void 0===a?null:a instanceof O?a:new O({start:function(b){if(a)if("string"===typeof a)b.enqueue(FetchInternal.encodeToUTF8(a));else if(S.prototype.isPrototypeOf(a))b.enqueue(new u(a.slice(0)));else if(ia(a))b.enqueue(new u(a.buffer.slice(0)));else if(a instanceof Blob)b.enqueue(new u(FetchInternal.blobToArrayBuffer(a)));else throw new g("Unsupported BodyInit type");b.close()}});this[n].get("content-type")||("string"===typeof a?this[n].set("content-type","text/plain;charset=UTF-8"):
-a instanceof Blob&&""!==a.type&&this[n].set("content-type",a.type))};P(this,{body:{get:function(){return this[m]}},bodyUsed:{get:function(){return this[N]?!0:this[m]?!!ja(this[m]):!1}}});this.arrayBuffer=function(){return this[z]?t.reject(new DOMException("Aborted","AbortError")):M(this).then(function(a){return a.buffer})};this.text=function(){return this[z]?t.reject(new DOMException("Aborted","AbortError")):M(this).then(function(a){return FetchInternal.decodeFromUTF8(a)})};this.json=function(){return this[z]?
-t.reject(new DOMException("Aborted","AbortError")):this.text().then(JSON.parse)};return this}function w(a,b){var d=void 0!==b&&null!==b&&void 0===b.cloneBody;b=b||{};var e=b.body||b.cloneBody,c=b.headers,f=new AbortController;this[A]=f.signal;f=null;if(a instanceof w)this[x]=a.url,this[B]=a.cache,this[C]=a.credentials,void 0===c&&(c=a.headers),this[D]=a.integrity,this[y]=a.method,this[p]=a.mode,d&&"navigate"===this[p]&&(this[p]="same-origin"),this[H]=a.redirect,e||null===a.body||(e=a.body,a[N]=!0),
-f=a[A];else{this[x]=String(a);if(!FetchInternal.isUrlValid(this[x],!1))throw new g("Invalid request URL");this[p]="cors";this[C]="same-origin"}if(void 0!==b.window&&null!==b.window)throw new g("Invalid request window");this[B]=b.cache||this[B]||"default";if(-1===ka.indexOf(this[B]))throw new g("Invalid request cache mode");this[C]=b.credentials||this[C]||"same-origin";if(-1===la.indexOf(this[C]))throw new g("Invalid request credentials");void 0!==b.integrity?this[D]=b.integrity:void 0===this[D]&&
-(this[D]="");a=(b.method||this[y]||"GET").toUpperCase();if(-1===ma.indexOf(a))throw new g("Invalid request method");this[y]=a;if(b.mode&&-1===na.indexOf(b.mode))throw new g("Invalid request mode");this[p]=b.mode||this[p]||"no-cors";if("no-cors"===this[p]){if(-1===oa.indexOf(this[y]))throw new g("Invalid request method for no-cors");if(""!==this[D])throw new g("Request integrity data is not allowed with no-cors");}if("same-origin"!==this[p]&&"only-if-cached"===this[B])throw new g("Request mode must be same-origin for only-if-cached");
-this[H]=b.redirect||this[H]||"follow";if(-1===pa.indexOf(this[H]))throw new g("Invalid request redirect mode");this[n]="no-cors"===this[p]?G(c,aa):G(c,Y);if(("GET"===this[y]||"HEAD"===this[y])&&e)throw new g("Request body is not allowed for GET or HEAD");"signal"in b&&(f=b.signal);f&&this[A].follow(f);this._initBody(e)}function qa(a,b){var d=G(void 0,b);a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(a){var b=a.split(":");if(a=b.shift().trim())b=b.join(":").trim(),d.append(a,b)});return d}
-function r(a,b){b||(b={});this[J]="default";this[q]="status"in b?b.status:200;if(200>this[q]||599<this[q])throw new T("Invalid response status");this[U]=200<=this[q]&&300>this[q];if("statusText"in b){var d=b.statusText;for(var e=0,f=d.length,c;e<f;e++)if(c=d.charCodeAt(e),9!==c&&(32>c||255<c||127===c))throw g("Invalid response status text");}else d="OK";this[K]=d;this[n]=G(b.headers,L);this[x]=b.url||"";if(a&&-1<ra.indexOf(this[q]))throw new g("Response body is not allowed with a null body status");
-this[z]=b.is_aborted||!1;this._initBody(a)}if(!c.fetch){var F=c.Array,S=c.ArrayBuffer,ea=c.Object.create,P=c.Object.defineProperties,f=c.Symbol,sa=f.iterator,I=c.Map,T=c.RangeError,g=c.TypeError,u=c.Uint8Array,t=c.Promise,O=c.ReadableStream,V=c.ReadableStreamTee,ja=c.IsReadableStreamDisturbed,fa=c.IsReadableStreamLocked,m=f("body"),N=f("bodyUsed"),B=f("cache"),C=f("credentials"),v=f("guardCallback"),n=f("headers"),D=f("integrity"),l=f("map"),y=f("method"),p=f("mode"),U=f("ok"),H=f("redirect"),q=f("status"),
-K=f("statusText"),J=f("type"),x=f("url"),z=f("is_aborted"),A=f("signal"),Z="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),da=["set-cookie","set-cookie2"],ba=["accept","accept-language","content-language"],ca=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],ka="default no-store reload no-cache force-cache only-if-cached".split(" "),
-la=["omit","same-origin","include"],ma="DELETE GET HEAD OPTIONS POST PUT".split(" "),oa=["GET","HEAD","POST"],na=["same-origin","no-cors","cors"],pa=["follow","error","manual"],ra=[101,204,205,304],ta=[301,302,303,307,308],ua="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),ia=S.isView||function(a){return a&&-1<ua.indexOf(Object.prototype.toString.call(a))};
-h.prototype.append=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to append");a=E(a);b=Q(b);this[v](a,b)||(this[l].has(a)?this[l].set(a,this[l].get(a)+", "+b):this[l].set(a,b))};h.prototype["delete"]=function(a){if(1!==arguments.length)throw g("Invalid parameters to delete");this[v](a,"invalid")||this[l].delete(E(a))};h.prototype.get=function(a){if(1!==arguments.length)throw g("Invalid parameters to get");a=E(a);var b=this[l].get(a);return void 0!==b?b:null};h.prototype.has=function(a){if(1!==
-arguments.length)throw g("Invalid parameters to has");return this[l].has(E(a))};h.prototype.set=function(a,b){if(2!==arguments.length)throw g("Invalid parameters to set");a=E(a);b=Q(b);this[v](a,b)||this[l].set(a,b)};h.prototype.forEach=function(a,b){var d=this;F.from(this[l].entries()).sort().forEach(function(e){a.call(b,e[1],e[0],d)})};h.prototype.keys=function(){return(new I(F.from(this[l].entries()).sort())).keys()};h.prototype.values=function(){return(new I(F.from(this[l].entries()).sort())).values()};
-h.prototype.entries=function(){return(new I(F.from(this[l].entries()).sort())).entries()};h.prototype[sa]=h.prototype.entries;w.prototype.clone=function(){var a=null;null!==this[m]&&(a=V(this[m],!0),this[m]=a[0],a=a[1]);return new w(this,{cloneBody:a,signal:this[A]})};P(w.prototype,{cache:{get:function(){return this[B]}},credentials:{get:function(){return this[C]}},headers:{get:function(){return this[n]}},integrity:{get:function(){return this[D]}},method:{get:function(){return this[y]}},mode:{get:function(){return this[p]}},
-redirect:{get:function(){return this[H]}},url:{get:function(){return this[x]}},signal:{get:function(){return this[A]}}});R.call(w.prototype);R.call(r.prototype);r.prototype.clone=function(){var a=null;null!==this[m]&&(a=V(this[m],!0),this[m]=a[0],a=a[1]);return new r(a,{status:this[q],statusText:this[K],headers:G(this[n],L),url:this[x],is_aborted:this[z]})};P(r.prototype,{headers:{get:function(){return this[n]}},ok:{get:function(){return this[U]}},status:{get:function(){return this[q]}},statusText:{get:function(){return this[K]}},
-type:{get:function(){return this[J]}},url:{get:function(){return this[x]}}});r.error=function(){var a=new r(null);a[n][v]=W;a[J]="error";a[q]=0;a[K]="";return a};r.redirect=function(a,b){if(!FetchInternal.isUrlValid(a,!0))throw new g("Invalid URL for response redirect");void 0===b&&(b=302);if(-1===ta.indexOf(b))throw new T("Invalid status code for response redirect");return new r(null,{status:b,headers:{location:a}})};c.Headers=h;c.Request=w;c.Response=r;c.fetch=function(a,b){return new t(function(d,
-e){var c=!1,f=!1,h=new w(a,b),k=new XMLHttpRequest,l=null;if(h.signal.aborted)return e(new DOMException("Aborted","AbortError"));var m=new O({start:function(a){l=a},cancel:function(a){c=!0;k.abort()}}),p=function(){if(!c){c=!0;m.cancel();if(l)try{ReadableStreamDefaultControllerError(l,new DOMException("Aborted","AbortError"))}catch(va){}setTimeout(function(){try{k.abort()}catch(va){}},0)}};k.onload=function(){l.close()};k.onreadystatechange=function(){if(k.readyState===k.HEADERS_RECEIVED){var a={status:k.status,
-statusText:k.statusText,headers:qa(k.getAllResponseHeaders()||"",L)};a.url="responseURL"in k?k.responseURL:a.headers.get("X-Request-URL");try{var b=new r(m,a);h[A].addEventListener("abort",function(){b[z]=!0;p();e(new DOMException("Aborted","AbortError"))});b[J]=f?"cors":"basic";d(b)}catch(wa){e(wa)}}};k.onerror=function(){l.error(new g("Network request failed"));e(new g("Network request failed"))};k.ontimeout=function(){l.error(new g("Network request failed"));e(new g("Network request failed"))};
-k.open(h.method,h.url,!0);"include"===h.credentials&&(k.withCredentials=!0);h.headers.forEach(function(a,b){k.setRequestHeader(b,a)});var n=function(a){c||l.enqueue(a)},q=function(a){f=a};null===h.body?k.fetch(n,q,null):M(h).then(function(a){k.fetch(n,q,a)})})};c.fetch.polyfill=!0}})(this);
\ No newline at end of file
+'use strict';(function(h){function J(a){"string"!==typeof a&&(a=String(a));if(/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(a))throw new e("Invalid character in header field name");return a.toLowerCase()}function X(a){"string"!==typeof a&&(a=String(a));var b;var c=0;for(b=a.length;c<b;c++){var d=a.charCodeAt(c);if(9!==d&&10!==d&&13!==d&&32!==d)break}for(b=a.length-1;b>c&&(d=a.charCodeAt(b),9===d||10===d||13===d||32===d);b--);a=a.substring(c,b+1);c=0;for(b=a.length;c<b;c++)if(d=a.charCodeAt(c),256<=d||0===d||
+10===d||13===d)throw new e("Invalid character in header field value");return a}function fa(a,b){throw new e("Immutable header cannot be modified");}function ha(a,b){return!1}function ia(a,b){a=a.toLowerCase();return-1<ja.indexOf(a)||a.startsWith("proxy-")||a.startsWith("sec-")?!0:!1}function ka(a,b){a=a.toLowerCase();return-1<la.indexOf(a)||"content-type"===a&&(b=b.split(";")[0].toLowerCase(),-1<ma.indexOf(b))?!1:!0}function S(a,b){return-1<na.indexOf(a.toLowerCase())?!0:!1}function n(a){this[p]=
+new P;void 0===this[A]&&(this[A]=ha);if(void 0!==a){if(null===a||"object"!==typeof a)throw new e("Constructing Headers with invalid parameters");a instanceof n?a.forEach(function(b,c){this.append(c,b)},this):K.isArray(a)?a.forEach(function(b){if(2!==b.length)throw new e("Constructing Headers with invalid parameters");this.append(b[0],b[1])},this):Object.getOwnPropertyNames(a).forEach(function(b){this.append(b,a[b])},this)}}function L(a,b){var c=oa(n.prototype);c[A]=b;n.call(c,a);return c}function T(a){if(a.bodyUsed)return z.reject(new e("Body was already read"));
+if(null===a.body)return z.resolve(new v(0));if(pa(a.body))return z.reject(new e("ReadableStream was already locked"));var b=a.body.getReader(),c=[],d=0;return b.read().then(function q(f){if(f.done){if(0===c.length)f=new v(0);else if(1===c.length)f=new v(c[0].buffer);else{f=new v(d);for(var g=0,w=c.length,M=0;g<w;g++)f.set(c[g],M),M+=c[g].length}return f}return f.value instanceof v?(d+=f.value.length,c.push(f.value),b.read().then(q)):z.reject(new e("Invalid stream data type"))})}function Y(){this._initBody=
+function(a){this[U]=!1;this[r]=null===a||void 0===a?null:a instanceof V?a:new V({start:function(b){if(a)if("string"===typeof a)b.enqueue(FetchInternal.encodeToUTF8(a));else if(Z.prototype.isPrototypeOf(a))b.enqueue(new v(a.slice(0)));else if(qa(a)){var c=new v(a.buffer);c=v.from(c.slice(a.byteOffset,a.byteLength+1));b.enqueue(c)}else if(a instanceof Blob)b.enqueue(new v(FetchInternal.blobToArrayBuffer(a)));else throw new e("Unsupported BodyInit type");b.close()}});this[x].get("content-type")||("string"===
+typeof a?this[x].set("content-type","text/plain;charset=UTF-8"):a instanceof Blob&&""!==a.type&&this[x].set("content-type",a.type))};W(this,{body:{get:function(){return this[r]}},bodyUsed:{get:function(){return this[U]?!0:this[r]?!!ra(this[r]):!1}}});this.arrayBuffer=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):T(this).then(function(a){return a.buffer})};this.text=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):T(this).then(function(a){return FetchInternal.decodeFromUTF8(a)})};
+this.json=function(){return this[E]?z.reject(new DOMException("Aborted","AbortError")):this.text().then(JSON.parse)};return this}function B(a,b){var c=void 0!==b&&null!==b&&void 0===b.cloneBody;b=b||{};var d=b.body||b.cloneBody;""===b.body&&(d="");var l=b.headers,f=new AbortController;this[F]=f.signal;f=null;if(a instanceof B)this[C]=a.url,this[G]=a.cache,this[H]=a.credentials,void 0===l&&(l=a.headers),this[I]=a.integrity,this[D]=a.method,this[t]=a.mode,c&&"navigate"===this[t]&&(this[t]="same-origin"),
+this[N]=a.redirect,d||null===a.body||(d=a.body,a[U]=!0),f=a[F];else{this[C]=String(a);if(!FetchInternal.isUrlValid(this[C],!1))throw new e("Invalid request URL");this[t]="cors";this[H]="same-origin"}if(void 0!==b.window&&null!==b.window)throw new e("Invalid request window");this[G]=b.cache||this[G]||"default";if(-1===sa.indexOf(this[G]))throw new e("Invalid request cache mode");this[H]=b.credentials||this[H]||"same-origin";if(-1===ta.indexOf(this[H]))throw new e("Invalid request credentials");void 0!==
+b.integrity?this[I]=b.integrity:void 0===this[I]&&(this[I]="");a=(b.method||this[D]||"GET").toUpperCase();if(-1===ua.indexOf(a))throw new e("Invalid request method");this[D]=a;if(b.mode&&-1===va.indexOf(b.mode))throw new e("Invalid request mode");this[t]=b.mode||this[t]||"no-cors";if("no-cors"===this[t]){if(-1===wa.indexOf(this[D]))throw new e("Invalid request method for no-cors");if(""!==this[I])throw new e("Request integrity data is not allowed with no-cors");}if("same-origin"!==this[t]&&"only-if-cached"===
+this[G])throw new e("Request mode must be same-origin for only-if-cached");this[N]=b.redirect||this[N]||"follow";if(-1===xa.indexOf(this[N]))throw new e("Invalid request redirect mode");this[x]="no-cors"===this[t]?L(l,ka):L(l,ia);if(("GET"===this[D]||"HEAD"===this[D])&&d)throw new e("Request body is not allowed for GET or HEAD");"signal"in b&&(f=b.signal);f&&this[F].follow(f);this._initBody(d)}function ya(a,b){var c=L(void 0,b);a.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach(function(d){var l=
+d.split(":");if(d=l.shift().trim())l=l.join(":").trim(),c.append(d,l)});return c}function u(a,b){b||(b={});this[Q]="default";this[y]="status"in b?b.status:200;if(200>this[y]||599<this[y])throw new aa("Invalid response status");this[ba]=200<=this[y]&&300>this[y];if("statusText"in b){var c=b.statusText;for(var d=0,l=c.length,f;d<l;d++)if(f=c.charCodeAt(d),9!==f&&(32>f||255<f||127===f))throw e("Invalid response status text");}else c="OK";this[R]=c;this[x]=L(b.headers,S);this[C]=b.url||"";if(a&&-1<za.indexOf(this[y]))throw new e("Response body is not allowed with a null body status");
+this[E]=b.is_aborted||!1;this._initBody(a)}if(!h.fetch){var K=h.Array,Z=h.ArrayBuffer,oa=h.Object.create,W=h.Object.defineProperties,k=h.Symbol,Aa=k.iterator,P=h.Map,aa=h.RangeError,e=h.TypeError,v=h.Uint8Array,z=h.Promise,V=h.ReadableStream,ca=h.ReadableStreamTee,ra=h.IsReadableStreamDisturbed,pa=h.IsReadableStreamLocked,r=k("body"),U=k("bodyUsed"),G=k("cache"),H=k("credentials"),A=k("guardCallback"),x=k("headers"),I=k("integrity"),p=k("map"),D=k("method"),t=k("mode"),ba=k("ok"),N=k("redirect"),
+y=k("status"),R=k("statusText"),Q=k("type"),C=k("url"),E=k("is_aborted"),F=k("signal"),ja="accept-charset accept-encoding access-control-request-headers access-control-request-method connection content-length cookie cookie2 date dnt expect host keep-alive origin referer te trailer transfer-encoding upgrade via".split(" "),na=["set-cookie","set-cookie2"],la=["accept","accept-language","content-language"],ma=["application/x-www-form-urlencoded","multipart/form-data","text/plain"],sa="default no-store reload no-cache force-cache only-if-cached".split(" "),
+ta=["omit","same-origin","include"],ua="DELETE GET HEAD OPTIONS POST PUT".split(" "),wa=["GET","HEAD","POST"],va=["same-origin","no-cors","cors"],xa=["follow","error","manual"],za=[101,204,205,304],Ba=[301,302,303,307,308],Ca="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";"),qa=Z.isView||function(a){return a&&-1<Ca.indexOf(Object.prototype.toString.call(a))};
+n.prototype.append=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to append");a=J(a);b=X(b);this[A](a,b)||(this[p].has(a)?this[p].set(a,this[p].get(a)+", "+b):this[p].set(a,b))};n.prototype["delete"]=function(a){if(1!==arguments.length)throw e("Invalid parameters to delete");this[A](a,"invalid")||this[p].delete(J(a))};n.prototype.get=function(a){if(1!==arguments.length)throw e("Invalid parameters to get");a=J(a);var b=this[p].get(a);return void 0!==b?b:null};n.prototype.has=function(a){if(1!==
+arguments.length)throw e("Invalid parameters to has");return this[p].has(J(a))};n.prototype.set=function(a,b){if(2!==arguments.length)throw e("Invalid parameters to set");a=J(a);b=X(b);this[A](a,b)||this[p].set(a,b)};n.prototype.forEach=function(a,b){var c=this;K.from(this[p].entries()).sort().forEach(function(d){a.call(b,d[1],d[0],c)})};n.prototype.keys=function(){return(new P(K.from(this[p].entries()).sort())).keys()};n.prototype.values=function(){return(new P(K.from(this[p].entries()).sort())).values()};
+n.prototype.entries=function(){return(new P(K.from(this[p].entries()).sort())).entries()};n.prototype[Aa]=n.prototype.entries;B.prototype.clone=function(){var a=null;null!==this[r]&&(a=ca(this[r],!0),this[r]=a[0],a=a[1]);return new B(this,{cloneBody:a,signal:this[F]})};W(B.prototype,{cache:{get:function(){return this[G]}},credentials:{get:function(){return this[H]}},headers:{get:function(){return this[x]}},integrity:{get:function(){return this[I]}},method:{get:function(){return this[D]}},mode:{get:function(){return this[t]}},
+redirect:{get:function(){return this[N]}},url:{get:function(){return this[C]}},signal:{get:function(){return this[F]}}});Y.call(B.prototype);Y.call(u.prototype);u.prototype.clone=function(){var a=null;null!==this[r]&&(a=ca(this[r],!0),this[r]=a[0],a=a[1]);return new u(a,{status:this[y],statusText:this[R],headers:L(this[x],S),url:this[C],is_aborted:this[E]})};W(u.prototype,{headers:{get:function(){return this[x]}},ok:{get:function(){return this[ba]}},status:{get:function(){return this[y]}},statusText:{get:function(){return this[R]}},
+type:{get:function(){return this[Q]}},url:{get:function(){return this[C]}}});u.error=function(){var a=new u(null);a[x][A]=fa;a[Q]="error";a[y]=0;a[R]="";return a};u.redirect=function(a,b){if(!FetchInternal.isUrlValid(a,!0))throw new e("Invalid URL for response redirect");void 0===b&&(b=302);if(-1===Ba.indexOf(b))throw new aa("Invalid status code for response redirect");return new u(null,{status:b,headers:{location:a}})};h.Headers=n;h.Request=B;h.Response=u;h.fetch=function(a,b){return new z(function(c,
+d){var l=!1,f=!1,q=new B(a,b),g=new XMLHttpRequest,w=null;if(q.signal.aborted)return d(new DOMException("Aborted","AbortError"));var M=new V({start:function(m){w=m},cancel:function(m){l=!0;g.abort()}}),Da=function(){if(!l){l=!0;M.cancel();if(w)try{ReadableStreamDefaultControllerError(w,new DOMException("Aborted","AbortError"))}catch(m){}setTimeout(function(){try{g.abort()}catch(m){}},0)}};g.onload=function(){w.close()};g.onreadystatechange=function(){if(g.readyState===g.HEADERS_RECEIVED){var m={status:g.status,
+statusText:g.statusText,headers:ya(g.getAllResponseHeaders()||"",S)};m.url="responseURL"in g?g.responseURL:m.headers.get("X-Request-URL");try{var O=new u(M,m);q[F].addEventListener("abort",function(){O[E]=!0;Da();d(new DOMException("Aborted","AbortError"))});O[Q]=f?"cors":"basic";c(O)}catch(Ea){d(Ea)}}};g.onerror=function(){w.error(new e("Network request failed"));d(new e("Network request failed"))};g.ontimeout=function(){w.error(new e("Network request failed"));d(new e("Network request failed"))};
+g.open(q.method,q.url,!0);"include"===q.credentials&&(g.withCredentials=!0);q.headers.forEach(function(m,O){g.setRequestHeader(O,m)});var da=function(m){l||w.enqueue(m)},ea=function(m){f=m};null===q.body?g.fetch(da,ea,null):T(q).then(function(m){g.fetch(da,ea,m)})})};h.fetch.polyfill=!0}})(this);
\ No newline at end of file
diff --git a/src/cobalt/fetch/fetch.js b/src/cobalt/fetch/fetch.js
index b5de929..8c8162b 100644
--- a/src/cobalt/fetch/fetch.js
+++ b/src/cobalt/fetch/fetch.js
@@ -434,7 +434,11 @@
} else if (ArrayBuffer.prototype.isPrototypeOf(data)) {
controller.enqueue(new Uint8Array(data.slice(0)))
} else if (isArrayBufferView(data)) {
- controller.enqueue(new Uint8Array(data.buffer.slice(0)))
+ // View as bytes
+ const asBytes = new Uint8Array(data.buffer);
+ // slice and copy
+ var byteSlice = Uint8Array.from(asBytes.slice(data.byteOffset, data.byteLength + 1));
+ controller.enqueue(byteSlice);
} else if (data instanceof Blob) {
controller.enqueue(new Uint8Array(FetchInternal.blobToArrayBuffer(data)))
} else {
@@ -529,6 +533,7 @@
init.cloneBody === undefined
init = init || {}
var body = init.body || init.cloneBody
+ if(init.body === '') body = '';
var headersInit = init.headers
// AbortSignal cannot be constructed directly, so create a temporary
diff --git a/src/cobalt/h5vcc/h5vcc_updater.cc b/src/cobalt/h5vcc/h5vcc_updater.cc
index 4b7a171..bebd3b0 100644
--- a/src/cobalt/h5vcc/h5vcc_updater.cc
+++ b/src/cobalt/h5vcc/h5vcc_updater.cc
@@ -33,7 +33,7 @@
if (updater_module_->GetUpdaterChannel().compare(channel) != 0 &&
updater_module_->IsChannelValid(channel)) {
updater_module_->SetUpdaterChannel(channel);
- updater_module_->MarkChannelChanged();
+ updater_module_->CompareAndSwapChannelChanged(0, 1);
updater_module_->RunUpdateCheck();
}
}
diff --git a/src/cobalt/layout/paragraph.cc b/src/cobalt/layout/paragraph.cc
index 55ccd1e..a18aecb 100644
--- a/src/cobalt/layout/paragraph.cc
+++ b/src/cobalt/layout/paragraph.cc
@@ -125,17 +125,22 @@
return start_position;
}
-bool Paragraph::FindBreakPosition(const scoped_refptr<dom::FontList>& used_font,
- int32 start_position, int32 end_position,
- LayoutUnit available_width,
- bool should_collapse_trailing_white_space,
- bool allow_overflow,
- Paragraph::BreakPolicy break_policy,
- int32* break_position,
- LayoutUnit* break_width) {
+bool Paragraph::FindBreakPosition(
+ BaseDirection direction, bool should_attempt_to_wrap,
+ const scoped_refptr<dom::FontList>& used_font, int32 start_position,
+ int32 end_position, LayoutUnit available_width,
+ bool should_collapse_trailing_white_space, bool allow_overflow,
+ Paragraph::BreakPolicy break_policy, int32* break_position,
+ LayoutUnit* break_width) {
DCHECK(is_closed_);
- *break_position = start_position;
+ DCHECK(direction == base_direction_);
+ if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+ should_attempt_to_wrap) {
+ *break_position = start_position;
+ } else {
+ *break_position = end_position;
+ }
*break_width = LayoutUnit();
// If overflow isn't allowed and there is no available width, then there is
@@ -165,9 +170,10 @@
// |break_width| will be updated with the position of the last available
// break position.
FindIteratorBreakPosition(
- used_font, line_break_iterator_, start_position, end_position,
- available_width, should_collapse_trailing_white_space,
- allow_normal_overflow, break_position, break_width);
+ direction, should_attempt_to_wrap, used_font, line_break_iterator_,
+ start_position, end_position, available_width,
+ should_collapse_trailing_white_space, allow_normal_overflow,
+ break_position, break_width);
}
// If break word is the break policy, attempt to break unbreakable "words" at
@@ -177,20 +183,39 @@
if (break_policy == kBreakPolicyBreakWord) {
// Only continue allowing overflow if the break position has not moved from
// start, meaning that no normal break positions were found.
- allow_overflow = allow_overflow && (*break_position == start_position);
+ if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+ should_attempt_to_wrap) {
+ allow_overflow = allow_overflow && (*break_position == start_position);
+ } else {
+ allow_overflow = allow_overflow && (*break_position == end_position);
+ }
// Find the last available break-word break position. |break_position| and
// |break_width| will be updated with the position of the last available
// break position. The search begins at the location of the last normal
// break position that fit within the available width.
- FindIteratorBreakPosition(
- used_font, character_break_iterator_, *break_position, end_position,
- available_width, false, allow_overflow, break_position, break_width);
+ if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+ should_attempt_to_wrap) {
+ FindIteratorBreakPosition(direction, should_attempt_to_wrap, used_font,
+ character_break_iterator_, *break_position,
+ end_position, available_width, false,
+ allow_overflow, break_position, break_width);
+ } else {
+ FindIteratorBreakPosition(direction, should_attempt_to_wrap, used_font,
+ character_break_iterator_, start_position,
+ *break_position, available_width, false,
+ allow_overflow, break_position, break_width);
+ }
}
// No usable break position was found if the break position has not moved
// from the start position.
- return *break_position > start_position;
+ if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+ should_attempt_to_wrap) {
+ return *break_position > start_position;
+ } else {
+ return *break_position < end_position;
+ }
}
int32 Paragraph::GetNextBreakPosition(int32 position,
@@ -268,6 +293,12 @@
return (GetBidiLevel(position) % 2) == 1;
}
+bool Paragraph::AreInlineAndScriptDirectionsTheSame(BaseDirection direction,
+ int32 position) const {
+ return ((direction == kLeftToRightBaseDirection && !IsRTL(position)) ||
+ (direction == kRightToLeftBaseDirection && IsRTL(position)));
+}
+
bool Paragraph::IsCollapsibleWhiteSpace(int32 position) const {
// Only check for the space character. Other collapsible white space
// characters will have already been converted into the space characters and
@@ -338,6 +369,7 @@
}
void Paragraph::FindIteratorBreakPosition(
+ BaseDirection direction, bool should_attempt_to_wrap,
const scoped_refptr<dom::FontList>& used_font,
icu::BreakIterator* const break_iterator, int32 start_position,
int32 end_position, LayoutUnit available_width,
@@ -347,19 +379,37 @@
// position. Continue until TryIncludeSegmentWithinAvailableWidth() returns
// false, indicating that no more segments can be included.
break_iterator->setText(unicode_text_);
- for (int32 segment_end = break_iterator->following(start_position);
- segment_end != icu::BreakIterator::DONE && segment_end < end_position;
- segment_end = break_iterator->next()) {
- if (!TryIncludeSegmentWithinAvailableWidth(
- used_font, *break_position, segment_end, available_width,
- should_collapse_trailing_white_space, &allow_overflow,
- break_position, break_width)) {
- break;
+ if (AreInlineAndScriptDirectionsTheSame(direction, start_position) ||
+ should_attempt_to_wrap) {
+ for (int32 segment_end = break_iterator->following(start_position);
+ segment_end != icu::BreakIterator::DONE && segment_end < end_position;
+ segment_end = break_iterator->next()) {
+ if (!TryIncludeSegmentWithinAvailableWidth(
+ direction, should_attempt_to_wrap, used_font, *break_position,
+ segment_end, available_width,
+ should_collapse_trailing_white_space, &allow_overflow,
+ break_position, break_width)) {
+ break;
+ }
+ }
+ } else {
+ for (int32 segment_begin = break_iterator->preceding(end_position);
+ segment_begin != icu::BreakIterator::DONE &&
+ segment_begin > start_position;
+ segment_begin = break_iterator->previous()) {
+ if (!TryIncludeSegmentWithinAvailableWidth(
+ direction, should_attempt_to_wrap, used_font, segment_begin,
+ *break_position, available_width,
+ should_collapse_trailing_white_space, &allow_overflow,
+ break_position, break_width)) {
+ break;
+ }
}
}
}
bool Paragraph::TryIncludeSegmentWithinAvailableWidth(
+ BaseDirection direction, bool should_attempt_to_wrap,
const scoped_refptr<dom::FontList>& used_font, int32 segment_start,
int32 segment_end, LayoutUnit available_width,
bool should_collapse_trailing_white_space, bool* allow_overflow,
@@ -388,7 +438,12 @@
return false;
}
- *break_position = segment_end;
+ if (AreInlineAndScriptDirectionsTheSame(direction, segment_start) ||
+ should_attempt_to_wrap) {
+ *break_position = segment_end;
+ } else {
+ *break_position = segment_start;
+ }
*break_width += segment_width;
if (*allow_overflow) {
diff --git a/src/cobalt/layout/paragraph.h b/src/cobalt/layout/paragraph.h
index 3feb5d8..d64233c 100644
--- a/src/cobalt/layout/paragraph.h
+++ b/src/cobalt/layout/paragraph.h
@@ -111,7 +111,8 @@
// substring coming before |break_position|.
//
// Returns false if no usable break position was found.
- bool FindBreakPosition(const scoped_refptr<dom::FontList>& used_font,
+ bool FindBreakPosition(BaseDirection direction, bool should_attempt_to_wrap,
+ const scoped_refptr<dom::FontList>& used_font,
int32 start_position, int32 end_position,
LayoutUnit available_width,
bool should_collapse_trailing_white_space,
@@ -136,6 +137,8 @@
int GetBidiLevel(int32 position) const;
bool IsRTL(int32 position) const;
+ bool AreInlineAndScriptDirectionsTheSame(BaseDirection direction,
+ int32 position) const;
bool IsCollapsibleWhiteSpace(int32 position) const;
bool GetNextRunPosition(int32 position, int32* next_run_position) const;
int32 GetTextEndPosition() const;
@@ -174,7 +177,9 @@
// that first overflowing segment will be included. The parameter
// |break_width| indicates the width of the portion of the substring coming
// before |break_position|.
- void FindIteratorBreakPosition(const scoped_refptr<dom::FontList>& used_font,
+ void FindIteratorBreakPosition(BaseDirection direction,
+ bool should_attempt_to_wrap,
+ const scoped_refptr<dom::FontList>& used_font,
icu::BreakIterator* const break_iterator,
int32 start_position, int32 end_position,
LayoutUnit available_width,
@@ -197,6 +202,7 @@
// of false does not guarantee that the segment was not included, but simply
// that no additional segments can be included.
bool TryIncludeSegmentWithinAvailableWidth(
+ BaseDirection direction, bool should_attempt_to_wrap,
const scoped_refptr<dom::FontList>& used_font, int32 start_position,
int32 end_position, LayoutUnit available_width,
bool should_collapse_trailing_white_space, bool* allow_overflow,
diff --git a/src/cobalt/layout/text_box.cc b/src/cobalt/layout/text_box.cc
index 4507080b..f1bd926 100644
--- a/src/cobalt/layout/text_box.cc
+++ b/src/cobalt/layout/text_box.cc
@@ -42,7 +42,9 @@
paragraph_(paragraph),
text_start_position_(text_start_position),
text_end_position_(text_end_position),
+ truncated_text_start_position_(text_start_position),
truncated_text_end_position_(text_end_position),
+ previous_truncated_text_start_position_(text_start_position),
previous_truncated_text_end_position_(text_end_position),
truncated_text_offset_from_left_(0),
used_font_(used_style_provider->GetUsedFontList(
@@ -237,12 +239,16 @@
}
void TextBox::DoPreEllipsisPlacementProcessing() {
+ previous_truncated_text_start_position_ = truncated_text_start_position_;
previous_truncated_text_end_position_ = truncated_text_end_position_;
+ truncated_text_start_position_ = text_start_position_;
truncated_text_end_position_ = text_end_position_;
}
void TextBox::DoPostEllipsisPlacementProcessing() {
- if (previous_truncated_text_end_position_ != truncated_text_end_position_) {
+ if (previous_truncated_text_start_position_ !=
+ truncated_text_start_position_ ||
+ previous_truncated_text_end_position_ != truncated_text_end_position_) {
InvalidateRenderTreeNodesOfBoxAndAncestors();
}
}
@@ -397,7 +403,7 @@
// color is animated, in which case it could become non-transparent.
if (used_color.a() > 0.0f || is_color_animated ||
text_shadow != cssom::KeywordValue::GetNone()) {
- int32 text_start_position = GetNonCollapsedTextStartPosition();
+ int32 text_start_position = GetVisibleTextStartPosition();
int32 text_length = GetVisibleTextLength();
scoped_refptr<render_tree::GlyphBuffer> glyph_buffer =
@@ -481,7 +487,12 @@
// If the ellipsis has already been placed, then the text is fully truncated
// by the ellipsis.
if (*is_placed) {
- truncated_text_end_position_ = text_start_position_;
+ if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+ text_start_position_)) {
+ truncated_text_end_position_ = text_start_position_;
+ } else {
+ truncated_text_start_position_ = text_end_position_;
+ }
return;
}
@@ -510,8 +521,8 @@
// text box. Otherwise, it can only appear after the first character
// (https://www.w3.org/TR/css3-ui/#propdef-text-overflow).
if (paragraph_->FindBreakPosition(
- used_font_, start_position, end_position, desired_content_offset,
- false, !(*is_placement_requirement_met),
+ base_direction, false, used_font_, start_position, end_position,
+ desired_content_offset, false, !(*is_placement_requirement_met),
Paragraph::kBreakPolicyBreakWord, &found_position, &found_offset)) {
// A usable break position was found. Calculate the placed offset using the
// the break position's distance from the content box's start edge. In the
@@ -525,7 +536,12 @@
} else {
*placed_offset = content_box_start_offset + found_offset;
}
- truncated_text_end_position_ = found_position;
+ if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+ start_position)) {
+ truncated_text_end_position_ = found_position;
+ } else {
+ truncated_text_start_position_ = found_position;
+ }
// An acceptable break position was not found. If the placement requirement
// was already met prior to this box, then the ellipsis doesn't require a
// character from this box to appear prior to its position, so simply place
@@ -533,7 +549,12 @@
} else if (is_placement_requirement_met) {
*placed_offset =
GetMarginBoxStartEdgeOffsetFromContainingBlock(base_direction);
- truncated_text_end_position_ = text_start_position_;
+ if (paragraph_->AreInlineAndScriptDirectionsTheSame(base_direction,
+ start_position)) {
+ truncated_text_end_position_ = text_start_position_;
+ } else {
+ truncated_text_start_position_ = text_end_position_;
+ }
// The placement requirement has not already been met. Given that an
// acceptable break position was not found within the text, the ellipsis can
// only be placed at the end edge of the box.
@@ -603,7 +624,8 @@
// fits within the available width. Overflow is never allowed.
LayoutUnit wrap_width;
if (!paragraph_->FindBreakPosition(
- used_font_, start_position, text_end_position_, available_width,
+ paragraph_->base_direction(), true, used_font_, start_position,
+ text_end_position_, available_width,
should_collapse_trailing_white_space, false, break_policy,
&wrap_position, &wrap_width)) {
// If no break position is found, but the line existence is already
@@ -720,19 +742,24 @@
Paragraph::kVisualTextOrder);
}
+int32 TextBox::GetVisibleTextStartPosition() const {
+ return std::max(GetNonCollapsedTextStartPosition(),
+ truncated_text_start_position_);
+}
+
int32 TextBox::GetVisibleTextEndPosition() const {
return std::min(GetNonCollapsedTextEndPosition(),
truncated_text_end_position_);
}
int32 TextBox::GetVisibleTextLength() const {
- return GetVisibleTextEndPosition() - GetNonCollapsedTextStartPosition();
+ return GetVisibleTextEndPosition() - GetVisibleTextStartPosition();
}
bool TextBox::HasVisibleText() const { return GetVisibleTextLength() > 0; }
std::string TextBox::GetVisibleText() const {
- return paragraph_->RetrieveUtf8SubString(GetNonCollapsedTextStartPosition(),
+ return paragraph_->RetrieveUtf8SubString(GetVisibleTextStartPosition(),
GetVisibleTextEndPosition(),
Paragraph::kVisualTextOrder);
}
diff --git a/src/cobalt/layout/text_box.h b/src/cobalt/layout/text_box.h
index c2edcc6..855a0a2 100644
--- a/src/cobalt/layout/text_box.h
+++ b/src/cobalt/layout/text_box.h
@@ -133,6 +133,7 @@
int32 GetNonCollapsibleTextLength() const;
std::string GetNonCollapsibleText() const;
+ int32 GetVisibleTextStartPosition() const;
int32 GetVisibleTextEndPosition() const;
int32 GetVisibleTextLength() const;
bool HasVisibleText() const;
@@ -145,7 +146,7 @@
const scoped_refptr<Paragraph> paragraph_;
// The position within the paragraph where the text contained in this box
// begins.
- const int32 text_start_position_;
+ int32 text_start_position_;
// The position within the paragraph where the text contained in this box
// ends.
int32 text_end_position_;
@@ -154,11 +155,13 @@
// "Implementations must hide characters and atomic inline-level elements at
// the applicable edge(s) of the line as necessary to fit the ellipsis."
// https://www.w3.org/TR/css3-ui/#propdef-text-overflow
+ int32 truncated_text_start_position_;
int32 truncated_text_end_position_;
- // Tracking of the previous value of |truncated_text_end_position_|, which
+ // Tracking of the previous value of the truncated text position, which
// allows for determination of whether or not the value changed during
// ellipsis placement. When this occurs, the cached render tree nodes of this
// box and its ancestors are invalidated.
+ int32 previous_truncated_text_start_position_;
int32 previous_truncated_text_end_position_;
// The horizontal offset to apply to rendered text as a result of an ellipsis
// truncating the text. This value can be non-zero when the text box is in a
diff --git a/src/cobalt/layout/topmost_event_target.cc b/src/cobalt/layout/topmost_event_target.cc
index 6316a6b..df2cd6a 100644
--- a/src/cobalt/layout/topmost_event_target.cc
+++ b/src/cobalt/layout/topmost_event_target.cc
@@ -130,18 +130,45 @@
}
namespace {
+// Return the nearest common ancestor of previous_element and target_element
+scoped_refptr<dom::Element> GetNearestCommonAncestor(
+ scoped_refptr<dom::HTMLElement> previous_element,
+ scoped_refptr<dom::HTMLElement> target_element) {
+ scoped_refptr<dom::Element> nearest_common_ancestor;
+ if (previous_element == target_element) {
+ nearest_common_ancestor = target_element;
+ } else {
+ if (previous_element && target_element) {
+ // Find the nearest common ancestor, if there is any.
+ dom::Document* previous_document = previous_element->node_document();
+ // The elements only have a common ancestor if they are both in the same
+ // document.
+ if (previous_document &&
+ previous_document == target_element->node_document()) {
+ // The nearest ancestor of the target element that is already
+ // designated is the nearest common ancestor of it and the previous
+ // element.
+ nearest_common_ancestor = target_element;
+ while (nearest_common_ancestor &&
+ nearest_common_ancestor->AsHTMLElement() &&
+ !nearest_common_ancestor->AsHTMLElement()->IsDesignated()) {
+ nearest_common_ancestor = nearest_common_ancestor->parent_element();
+ }
+ }
+ }
+ }
+ return nearest_common_ancestor;
+}
+
void SendStateChangeLeaveEvents(
bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
scoped_refptr<dom::HTMLElement> target_element,
+ scoped_refptr<dom::Element> nearest_common_ancestor,
dom::PointerEventInit* event_init) {
// Send enter/leave/over/out (status change) events when needed.
if (previous_element != target_element) {
const scoped_refptr<dom::Window>& view = event_init->view();
- // The enter/leave status change events apply to all ancestors up to the
- // nearest common ancestor between the previous and current element.
- scoped_refptr<dom::Element> nearest_common_ancestor;
-
// Send out and leave events.
if (previous_element) {
// LottiePlayer elements may change playback state.
@@ -149,24 +176,9 @@
previous_element->AsLottiePlayer()->OnUnHover();
}
- event_init->set_related_target(target_element);
- // Find the nearest common ancestor, if there is any.
dom::Document* previous_document = previous_element->node_document();
- if (previous_document) {
- if (target_element &&
- previous_document == target_element->node_document()) {
- // The nearest ancestor of the current element that is already
- // designated is the nearest common ancestor of it and the previous
- // element.
- nearest_common_ancestor = target_element;
- while (nearest_common_ancestor &&
- nearest_common_ancestor->AsHTMLElement() &&
- !nearest_common_ancestor->AsHTMLElement()->IsDesignated()) {
- nearest_common_ancestor = nearest_common_ancestor->parent_element();
- }
- }
- }
+ event_init->set_related_target(target_element);
if (is_pointer_event) {
previous_element->DispatchEvent(new dom::PointerEvent(
base::Tokens::pointerout(), view, *event_init));
@@ -209,15 +221,12 @@
void SendStateChangeEnterEvents(
bool is_pointer_event, scoped_refptr<dom::HTMLElement> previous_element,
scoped_refptr<dom::HTMLElement> target_element,
+ scoped_refptr<dom::Element> nearest_common_ancestor,
dom::PointerEventInit* event_init) {
// Send enter/leave/over/out (status change) events when needed.
if (previous_element != target_element) {
const scoped_refptr<dom::Window>& view = event_init->view();
- // The enter/leave status change events apply to all ancestors up to the
- // nearest common ancestor between the previous and current element.
- scoped_refptr<dom::Element> nearest_common_ancestor;
-
// Send over and enter events.
if (target_element) {
// LottiePlayer elements may change playback state.
@@ -399,8 +408,14 @@
scoped_refptr<dom::HTMLElement> previous_html_element(
previous_html_element_weak_);
+ // The enter/leave status change events apply to all ancestors up to the
+ // nearest common ancestor between the previous and current element.
+ scoped_refptr<dom::Element> nearest_common_ancestor(
+ GetNearestCommonAncestor(previous_html_element, target_element));
+
SendStateChangeLeaveEvents(pointer_event, previous_html_element,
- target_element, &event_init);
+ target_element, nearest_common_ancestor,
+ &event_init);
if (target_element) {
target_element->DispatchEvent(event);
@@ -424,26 +439,41 @@
}
}
- if (target_element && !is_touchpad_event) {
- // Send the click event if needed, which is not prevented by canceling the
- // pointerdown event.
- // https://www.w3.org/TR/uievents/#event-type-click
- // https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
- if (event_init.button() == 0 &&
- ((mouse_event->type() == base::Tokens::pointerup()) ||
- (mouse_event->type() == base::Tokens::mouseup()))) {
+ if (event_init.button() == 0 &&
+ ((mouse_event->type() == base::Tokens::pointerup()) ||
+ (mouse_event->type() == base::Tokens::mouseup()))) {
+ // This is an 'up' event for the last pressed button indicating that no
+ // more buttons are pressed.
+ if (target_element && !is_touchpad_event) {
+ // Send the click event if needed, which is not prevented by canceling
+ // the pointerdown event.
+ // https://www.w3.org/TR/uievents/#event-type-click
+ // https://www.w3.org/TR/pointerevents/#compatibility-mapping-with-mouse-events
target_element->DispatchEvent(
new dom::MouseEvent(base::Tokens::click(), view, event_init));
}
+ if (target_element && (pointer_event->pointer_type() != "mouse")) {
+ // If it's not a mouse event, then releasing the last button means
+ // that there is no longer an indicated element.
+ dom::Document* document = target_element->node_document();
+ if (document) {
+ document->SetIndicatedElement(NULL);
+ target_element = NULL;
+ }
+ }
}
SendStateChangeEnterEvents(pointer_event, previous_html_element,
- target_element, &event_init);
+ target_element, nearest_common_ancestor,
+ &event_init);
if (target_element) {
- dom::Document* document = target_element->node_document();
- if (document) {
- document->SetIndicatedElement(target_element);
+ // Touchpad input never indicates document elements.
+ if (!is_touchpad_event) {
+ dom::Document* document = target_element->node_document();
+ if (document) {
+ document->SetIndicatedElement(target_element);
+ }
}
previous_html_element_weak_ = base::AsWeakPtr(target_element.get());
} else {
diff --git a/src/cobalt/layout_tests/layout_tests.cc b/src/cobalt/layout_tests/layout_tests.cc
index 4cd46c1..17804c8 100644
--- a/src/cobalt/layout_tests/layout_tests.cc
+++ b/src/cobalt/layout_tests/layout_tests.cc
@@ -207,7 +207,7 @@
renderer::RenderTreePixelTester::Options pixel_tester_options;
if (renderer::RenderTreePixelTester::IsReferencePlatform()) {
// Use stricter tolerances on reference platforms.
- pixel_tester_options.gaussian_blur_sigma = 3.0f;
+ pixel_tester_options.gaussian_blur_sigma = 3.5f;
}
RunTest(GetParam(), graphics_context_, pixel_tester_options);
}
diff --git a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
index d0f9074..7e86a49 100644
--- a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
+++ b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
index 71a614f..463fedd 100644
--- a/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
+++ b/src/cobalt/layout_tests/testdata/bidi/bidi-paragraphs-should-maintain-proper-ordering-when-split-across-multiple-lines.html
@@ -30,8 +30,12 @@
لتد2ل3تت4دل5ت6د ط1لتدلنش 123456789 745 ط1لت
987 لتد 654</span>
</div>
+ <div class="containing-block" dir="rtl">
+ <span>12لتدل3ت dدلت4دلتد5ل6تد يabطا32ل Single Word
+ 1234 ط1لتدلنش ab12دلتد34cd ط1aلتد2ل 3تت489
+ 12 ل5ت6745 ط1لتدلنش ط1لaتدلتدتد ط11246 ط1abcd
+ لتد2ل3تت4دل5ت6د ط1لتدلنش 123456789 745 ط1لت
+ 987 لتد 654</span>
+ </div>
</body>
</html>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
index 291576a..bf339a6 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/5-2-use-system-fallback-if-no-matching-family-is-found-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
index 4b3d234..e509aaa 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
index 22e2cab..e1e8f5b 100644
--- a/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
+++ b/src/cobalt/layout_tests/testdata/css3-fonts/color-emojis-should-render-properly.html
@@ -29,7 +29,7 @@
<span>🙈🙉🙊👨👩🏃🐌</span>
<span>🐜💐🌲🍇🥝🍄🍔</span>
<span>🍟🍞🥞🌮🍣🍡🍨</span>
- <span class="bold-span">🌎🌚</span>
+ <span class="bold-span">🌎🌚🥰</span>
</div>
</body>
</html>
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
index 27b72da..0bbe59c 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-clip-when-it-overflows-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
index 164d112..2e89634 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-each-line-that-overflows-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
index 59aa7b9..4db6e7b 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-ellipsize-overflowing-first-word-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
index b311cd1..21d31da 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-matrix-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
index 4cfa868..85a1acb 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-handle-transform-functions-via-rotation-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
index 27b72da..0bbe59c 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-not-ellipsize-first-character-on-line-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
index a88d55a..f71519f 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-properly-position-ellipsis-in-spans-with-margins-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
index ae53279..ac84ae0 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-retain-background-color-of-ellipsized-span-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
index 5d56a8b..e059ac1 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-ellipsis-should-use-style-of-containing-block-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
index 7b2d801..8b20b2a 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-shaped-text-should-accurately-calculate-width-before-ellipsis-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
index bf2da54..7fecf85 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-display-ellipsis-on-overflow-when-overflow-is-hidden-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
index bb3987f..a264063 100644
--- a/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
+++ b/src/cobalt/layout_tests/testdata/css3-ui/5-2-text-overflow-ellipsis-should-not-be-inherited-expected.png
Binary files differ
diff --git a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
index 5c11ed1..72ab7d8 100644
--- a/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
+++ b/src/cobalt/layout_tests/testdata/web-platform-tests/fetch/web_platform_tests.txt
@@ -97,12 +97,8 @@
api/request/request-cache-only-if-cached.html,DISABLE
api/request/request-cache-reload.html,DISABLE
api/request/request-clone.sub.html,PASS
-# Fails because Body only supports text, json, and arrayBuffer. See the
-# corresponding tests for Response which have been customized to test only
-# those types.
-api/request/request-consume.html,DISABLE
-# Fails because blob and formData are not supported.
-api/request/request-consume-empty.html,DISABLE
+api/request/request-consume.html,PASS
+api/request/request-consume-empty.html,PASS
# Disabled due to failing test fixture
api/request/request-disturbed.html,DISABLE
api/request/request-error.html,PASS
diff --git a/src/cobalt/layout_tests/web_platform_test_parser.cc b/src/cobalt/layout_tests/web_platform_test_parser.cc
index 864148b..0e1b721 100644
--- a/src/cobalt/layout_tests/web_platform_test_parser.cc
+++ b/src/cobalt/layout_tests/web_platform_test_parser.cc
@@ -126,7 +126,6 @@
<< "\"" << top_level << "\"";
return std::vector<WebPlatformTestInfo>();
}
- DLOG(INFO) << "[Temporary debugging] javascript engine for parser exiting";
}
base::FilePath test_dir(GetTestInputRootDirectory()
diff --git a/src/cobalt/media/base/shell_audio_bus.cc b/src/cobalt/media/base/audio_bus.cc
similarity index 87%
rename from src/cobalt/media/base/shell_audio_bus.cc
rename to src/cobalt/media/base/audio_bus.cc
index 5140d01..ea63e7a 100644
--- a/src/cobalt/media/base/shell_audio_bus.cc
+++ b/src/cobalt/media/base/audio_bus.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include <algorithm>
#include <limits>
@@ -24,20 +24,17 @@
namespace {
-typedef ShellAudioBus::StorageType StorageType;
-typedef ShellAudioBus::SampleType SampleType;
+typedef AudioBus::StorageType StorageType;
+typedef AudioBus::SampleType SampleType;
const float kFloat32ToInt16Factor = 32768.f;
-inline void ConvertSample(ShellAudioBus::SampleType src_type,
- const uint8* src_ptr,
- ShellAudioBus::SampleType dest_type,
- uint8* dest_ptr) {
+inline void ConvertSample(AudioBus::SampleType src_type, const uint8* src_ptr,
+ AudioBus::SampleType dest_type, uint8* dest_ptr) {
if (src_type == dest_type) {
- SbMemoryCopy(
- dest_ptr, src_ptr,
- src_type == ShellAudioBus::kInt16 ? sizeof(int16) : sizeof(float));
- } else if (src_type == ShellAudioBus::kFloat32) {
+ SbMemoryCopy(dest_ptr, src_ptr,
+ src_type == AudioBus::kInt16 ? sizeof(int16) : sizeof(float));
+ } else if (src_type == AudioBus::kFloat32) {
float sample_in_float = *reinterpret_cast<const float*>(src_ptr);
int32 sample_in_int32 =
static_cast<int32>(sample_in_float * kFloat32ToInt16Factor);
@@ -78,8 +75,8 @@
} // namespace
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames,
- SampleType sample_type, StorageType storage_type)
+AudioBus::AudioBus(size_t channels, size_t frames, SampleType sample_type,
+ StorageType storage_type)
: channels_(channels),
frames_(frames),
sample_type_(sample_type),
@@ -106,7 +103,7 @@
}
}
-ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<float*>& samples)
+AudioBus::AudioBus(size_t frames, const std::vector<float*>& samples)
: channels_(samples.size()),
frames_(frames),
sample_type_(kFloat32),
@@ -119,7 +116,7 @@
}
}
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, float* samples)
+AudioBus::AudioBus(size_t channels, size_t frames, float* samples)
: channels_(channels),
frames_(frames),
sample_type_(kFloat32),
@@ -129,7 +126,7 @@
channel_data_.push_back(reinterpret_cast<uint8*>(samples));
}
-ShellAudioBus::ShellAudioBus(size_t frames, const std::vector<int16*>& samples)
+AudioBus::AudioBus(size_t frames, const std::vector<int16*>& samples)
: channels_(samples.size()),
frames_(frames),
sample_type_(kInt16),
@@ -142,7 +139,7 @@
}
}
-ShellAudioBus::ShellAudioBus(size_t channels, size_t frames, int16* samples)
+AudioBus::AudioBus(size_t channels, size_t frames, int16* samples)
: channels_(channels),
frames_(frames),
sample_type_(kInt16),
@@ -152,7 +149,7 @@
channel_data_.push_back(reinterpret_cast<uint8*>(samples));
}
-size_t ShellAudioBus::GetSampleSizeInBytes() const {
+size_t AudioBus::GetSampleSizeInBytes() const {
if (sample_type_ == kInt16) {
return sizeof(int16);
}
@@ -160,29 +157,29 @@
return sizeof(float);
}
-const uint8* ShellAudioBus::interleaved_data() const {
+const uint8* AudioBus::interleaved_data() const {
DCHECK_EQ(storage_type_, kInterleaved);
return channel_data_[0];
}
-const uint8* ShellAudioBus::planar_data(size_t channel) const {
+const uint8* AudioBus::planar_data(size_t channel) const {
DCHECK_LT(channel, channels_);
DCHECK_EQ(storage_type_, kPlanar);
return channel_data_[channel];
}
-uint8* ShellAudioBus::interleaved_data() {
+uint8* AudioBus::interleaved_data() {
DCHECK_EQ(storage_type_, kInterleaved);
return channel_data_[0];
}
-uint8* ShellAudioBus::planar_data(size_t channel) {
+uint8* AudioBus::planar_data(size_t channel) {
DCHECK_LT(channel, channels_);
DCHECK_EQ(storage_type_, kPlanar);
return channel_data_[channel];
}
-void ShellAudioBus::ZeroFrames(size_t start_frame, size_t end_frame) {
+void AudioBus::ZeroFrames(size_t start_frame, size_t end_frame) {
DCHECK_LE(start_frame, end_frame);
DCHECK_LE(end_frame, frames_);
end_frame = std::min(end_frame, frames_);
@@ -201,7 +198,7 @@
}
}
-void ShellAudioBus::Assign(const ShellAudioBus& source) {
+void AudioBus::Assign(const AudioBus& source) {
DCHECK_EQ(channels_, source.channels_);
if (channels_ != source.channels_) {
ZeroAllFrames();
@@ -232,8 +229,8 @@
}
}
-void ShellAudioBus::Assign(const ShellAudioBus& source,
- const std::vector<float>& matrix) {
+void AudioBus::Assign(const AudioBus& source,
+ const std::vector<float>& matrix) {
DCHECK_EQ(channels() * source.channels(), matrix.size());
DCHECK_EQ(sample_type_, kFloat32);
DCHECK_EQ(source.sample_type_, kFloat32);
@@ -258,7 +255,7 @@
}
template <StorageType SourceStorageType, StorageType DestStorageType>
-void ShellAudioBus::MixFloatSamples(const ShellAudioBus& source) {
+void AudioBus::MixFloatSamples(const AudioBus& source) {
const size_t frames = std::min(frames_, source.frames_);
if (SourceStorageType == DestStorageType) {
@@ -284,7 +281,7 @@
}
template <StorageType SourceStorageType, StorageType DestStorageType>
-void ShellAudioBus::MixInt16Samples(const ShellAudioBus& source) {
+void AudioBus::MixInt16Samples(const AudioBus& source) {
const size_t frames = std::min(frames_, source.frames_);
if (SourceStorageType == DestStorageType) {
@@ -318,7 +315,7 @@
}
}
-void ShellAudioBus::Mix(const ShellAudioBus& source) {
+void AudioBus::Mix(const AudioBus& source) {
DCHECK_EQ(channels_, source.channels_);
DCHECK_EQ(sample_type_, source.sample_type_);
@@ -359,8 +356,7 @@
}
}
-void ShellAudioBus::Mix(const ShellAudioBus& source,
- const std::vector<float>& matrix) {
+void AudioBus::Mix(const AudioBus& source, const std::vector<float>& matrix) {
DCHECK_EQ(channels() * source.channels(), matrix.size());
DCHECK_EQ(sample_type_, source.sample_type_);
@@ -411,7 +407,7 @@
}
}
-uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) {
+uint8* AudioBus::GetSamplePtr(size_t channel, size_t frame) {
DCHECK_LT(channel, channels_);
DCHECK_LT(frame, frames_);
@@ -423,7 +419,7 @@
}
}
-const uint8* ShellAudioBus::GetSamplePtr(size_t channel, size_t frame) const {
+const uint8* AudioBus::GetSamplePtr(size_t channel, size_t frame) const {
DCHECK_LT(channel, channels_);
DCHECK_LT(frame, frames_);
diff --git a/src/cobalt/media/base/shell_audio_bus.h b/src/cobalt/media/base/audio_bus.h
similarity index 85%
rename from src/cobalt/media/base/shell_audio_bus.h
rename to src/cobalt/media/base/audio_bus.h
index cdd4668..2b4c369 100644
--- a/src/cobalt/media/base/shell_audio_bus.h
+++ b/src/cobalt/media/base/audio_bus.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
-#define COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
+#ifndef COBALT_MEDIA_BASE_AUDIO_BUS_H_
+#define COBALT_MEDIA_BASE_AUDIO_BUS_H_
#include <memory>
#include <vector>
@@ -37,11 +37,11 @@
// second of such audio contains 48000 frames (96000 samples).
// Note: This class doesn't do endianness conversions. It assumes that all data
// is in the correct endianness.
-class COBALT_EXPORT ShellAudioBus {
+class COBALT_EXPORT AudioBus {
public:
// Guaranteed alignment of each channel's data; use 64-byte alignment so it
// satisfies all our current platforms. Note that this is only used for
- // buffers that are allocated and owned by the ShellAudioBus. We don't
+ // buffers that are allocated and owned by the AudioBus. We don't
// enforce alignment for the buffers passed in and extra caution should be
// taken if they are used as hardware buffer.
static const size_t kChannelAlignmentInBytes = 64;
@@ -50,12 +50,12 @@
enum StorageType { kInterleaved, kPlanar };
- ShellAudioBus(size_t channels, size_t frames, SampleType sample_type,
- StorageType storage_type);
- ShellAudioBus(size_t frames, const std::vector<float*>& samples);
- ShellAudioBus(size_t channels, size_t frames, float* samples);
- ShellAudioBus(size_t frames, const std::vector<int16*>& samples);
- ShellAudioBus(size_t channels, size_t frames, int16* samples);
+ AudioBus(size_t channels, size_t frames, SampleType sample_type,
+ StorageType storage_type);
+ AudioBus(size_t frames, const std::vector<float*>& samples);
+ AudioBus(size_t channels, size_t frames, float* samples);
+ AudioBus(size_t frames, const std::vector<int16*>& samples);
+ AudioBus(size_t channels, size_t frames, int16* samples);
size_t channels() const { return channels_; }
size_t frames() const { return frames_; }
@@ -84,7 +84,7 @@
// conversion between different sample types and storage types. When source
// has less frames than the destination object, it will only copy these frames
// and will not fill the rest frames in our buffer with 0.
- void Assign(const ShellAudioBus& source);
+ void Assign(const AudioBus& source);
// The same as the above function except that this function also does mixing.
// |matrix| is a |dest.channels()| row * |source.channels()| column matrix in
@@ -96,14 +96,14 @@
// + source.sample[source.channels() - 1][frame] *
// matrix[channels() * source.channels() + source.channels() - 1];
// Note: Both objects must have storage type of kFloat32.
- void Assign(const ShellAudioBus& source, const std::vector<float>& matrix);
+ void Assign(const AudioBus& source, const std::vector<float>& matrix);
// The following functions are the same as the Assign() functions except that
// they add the calculated samples to the target samples instead of replacing
// the target samples with the calculated samples.
// Note: Both objects must have storage type of kFloat32.
- void Mix(const ShellAudioBus& source);
- void Mix(const ShellAudioBus& source, const std::vector<float>& matrix);
+ void Mix(const AudioBus& source);
+ void Mix(const AudioBus& source, const std::vector<float>& matrix);
public:
// The .*ForTypes? functions below are optimized versions that assume what
@@ -134,10 +134,10 @@
}
template <StorageType SourceStorageType, StorageType DestStorageType>
- void MixFloatSamples(const ShellAudioBus& source);
+ void MixFloatSamples(const AudioBus& source);
template <StorageType SourceStorageType, StorageType DestStorageType>
- void MixInt16Samples(const ShellAudioBus& source);
+ void MixInt16Samples(const AudioBus& source);
private:
void SetFloat32Sample(size_t channel, size_t frame, float sample) {
@@ -160,10 +160,10 @@
SampleType sample_type_;
StorageType storage_type_;
- DISALLOW_COPY_AND_ASSIGN(ShellAudioBus);
+ DISALLOW_COPY_AND_ASSIGN(AudioBus);
};
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_BASE_SHELL_AUDIO_BUS_H_
+#endif // COBALT_MEDIA_BASE_AUDIO_BUS_H_
diff --git a/src/cobalt/media/base/pipeline.h b/src/cobalt/media/base/pipeline.h
index f0d37fc..c2bbcfc 100644
--- a/src/cobalt/media/base/pipeline.h
+++ b/src/cobalt/media/base/pipeline.h
@@ -92,7 +92,9 @@
virtual ~Pipeline() {}
virtual void Suspend() {}
- virtual void Resume() {}
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ virtual void Resume(PipelineWindow window) {}
// Build a pipeline to using the given filter collection to construct a filter
// chain, executing |seek_cb| when the initial seek/preroll has completed.
diff --git a/src/cobalt/media/base/sbplayer_pipeline.cc b/src/cobalt/media/base/sbplayer_pipeline.cc
index 4f4f9d1..68abc49 100644
--- a/src/cobalt/media/base/sbplayer_pipeline.cc
+++ b/src/cobalt/media/base/sbplayer_pipeline.cc
@@ -90,7 +90,10 @@
~SbPlayerPipeline() override;
void Suspend() override;
- void Resume() override;
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ void Resume(PipelineWindow window) override;
+
void Start(Demuxer* demuxer,
const SetDrmSystemReadyCB& set_drm_system_ready_cb,
const PipelineStatusCB& ended_cb, const ErrorCB& error_cb,
@@ -175,7 +178,7 @@
void CallSeekCB(PipelineStatus status);
void SuspendTask(base::WaitableEvent* done_event);
- void ResumeTask(base::WaitableEvent* done_event);
+ void ResumeTask(PipelineWindow window, base::WaitableEvent* done_event);
// Store the media time retrieved by GetMediaTime so we can cache it as an
// estimate and avoid calling SbPlayerGetInfo too frequently.
@@ -331,7 +334,7 @@
waitable_event.Wait();
}
-void SbPlayerPipeline::Resume() {
+void SbPlayerPipeline::Resume(PipelineWindow window) {
DCHECK(!task_runner_->BelongsToCurrentThread());
base::WaitableEvent waitable_event(
@@ -339,7 +342,8 @@
base::WaitableEvent::InitialState::NOT_SIGNALED);
task_runner_->PostTask(FROM_HERE,
base::Bind(&SbPlayerPipeline::ResumeTask,
- base::Unretained(this), &waitable_event));
+ base::Unretained(this),
+ window, &waitable_event));
waitable_event.Wait();
}
@@ -1314,7 +1318,8 @@
done_event->Signal();
}
-void SbPlayerPipeline::ResumeTask(base::WaitableEvent* done_event) {
+void SbPlayerPipeline::ResumeTask(PipelineWindow window,
+ base::WaitableEvent* done_event) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(done_event);
DCHECK(suspended_);
@@ -1324,8 +1329,10 @@
return;
}
+ window_ = window;
+
if (player_) {
- player_->Resume();
+ player_->Resume(window);
}
suspended_ = false;
diff --git a/src/cobalt/media/base/starboard_player.cc b/src/cobalt/media/base/starboard_player.cc
index d23cdab..d7eeb2e 100644
--- a/src/cobalt/media/base/starboard_player.cc
+++ b/src/cobalt/media/base/starboard_player.cc
@@ -140,7 +140,7 @@
#if SB_HAS(PLAYER_WITH_URL)
,
is_url_based_(false)
-#endif // SB_HAS(PLAYER_WITH_URL)
+#endif // SB_HAS(PLAYER_WITH_URL
{
DCHECK(!get_decode_target_graphics_context_provider_func_.is_null());
DCHECK(audio_config.IsValidConfig() || video_config.IsValidConfig());
@@ -450,9 +450,11 @@
player_ = kSbPlayerInvalid;
}
-void StarboardPlayer::Resume() {
+void StarboardPlayer::Resume(SbWindow window) {
DCHECK(task_runner_->BelongsToCurrentThread());
+ window_ = window;
+
// Check if the player is already resumed.
if (state_ != kSuspended) {
DCHECK(SbPlayerIsValid(player_));
@@ -540,16 +542,22 @@
TRACE_EVENT0("cobalt::media", "StarboardPlayer::CreatePlayer");
DCHECK(task_runner_->BelongsToCurrentThread());
+bool is_visible = SbWindowIsValid(window_);
#if SB_API_VERSION >= 11
SbMediaAudioCodec audio_codec = audio_sample_info_.codec;
- SbMediaVideoCodec video_codec = video_sample_info_.codec;
+ SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ if (is_visible) {
+ video_codec = video_sample_info_.codec;
+ }
#else // SB_API_VERSION >= 11
SbMediaAudioCodec audio_codec = kSbMediaAudioCodecNone;
if (audio_config_.IsValidConfig()) {
audio_codec = MediaAudioCodecToSbMediaAudioCodec(audio_config_.codec());
}
SbMediaVideoCodec video_codec = kSbMediaVideoCodecNone;
- if (video_config_.IsValidConfig()) {
+ if (video_config_.IsValidConfig() && is_visible) {
video_codec = MediaVideoCodecToSbMediaVideoCodec(video_config_.codec());
}
#endif // SB_API_VERSION >= 11
@@ -562,6 +570,11 @@
creation_param.drm_system = drm_system_;
creation_param.audio_sample_info = audio_sample_info_;
creation_param.video_sample_info = video_sample_info_;
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ if (!is_visible) {
+ creation_param.video_sample_info.codec = kSbMediaVideoCodecNone;
+ }
creation_param.output_mode = output_mode_;
DCHECK_EQ(SbPlayerGetPreferredOutputMode(&creation_param), output_mode_);
player_ = SbPlayerCreate(
@@ -573,6 +586,11 @@
#else // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
DCHECK(SbPlayerOutputModeSupported(output_mode_, video_codec, drm_system_));
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ if (!is_visible) {
+ DCHECK(audio_codec != kSbMediaAudioCodecNone);
+ }
player_ = SbPlayerCreate(
window_, video_codec, audio_codec,
drm_system_, has_audio ? &audio_sample_info_ : NULL,
diff --git a/src/cobalt/media/base/starboard_player.h b/src/cobalt/media/base/starboard_player.h
index 2b00770..b4c1581 100644
--- a/src/cobalt/media/base/starboard_player.h
+++ b/src/cobalt/media/base/starboard_player.h
@@ -114,7 +114,9 @@
#endif // SB_HAS(PLAYER_WITH_URL)
void Suspend();
- void Resume();
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ void Resume(SbWindow window);
SbDecodeTarget GetCurrentSbDecodeTarget();
SbPlayerOutputMode GetSbPlayerOutputMode();
@@ -213,7 +215,7 @@
const GetDecodeTargetGraphicsContextProviderFunc
get_decode_target_graphics_context_provider_func_;
scoped_refptr<CallbackHelper> callback_helper_;
- const SbWindow window_;
+ SbWindow window_;
SbDrmSystem drm_system_ = kSbDrmSystemInvalid;
Host* const host_;
// Consider merge |SbPlayerSetBoundsHelper| into CallbackHelper.
diff --git a/src/cobalt/media/can_play_type_handler.h b/src/cobalt/media/can_play_type_handler.h
index 40d1ea6..00b94f1 100644
--- a/src/cobalt/media/can_play_type_handler.h
+++ b/src/cobalt/media/can_play_type_handler.h
@@ -25,9 +25,10 @@
class CanPlayTypeHandler {
public:
virtual ~CanPlayTypeHandler() {}
- virtual SbMediaSupportType CanPlayType(const std::string& mime_type,
- const std::string& key_system,
- bool is_progressive) const = 0;
+ virtual SbMediaSupportType CanPlayProgressive(
+ const std::string& mime_type) const = 0;
+ virtual SbMediaSupportType CanPlayAdaptive(
+ const std::string& mime_type, const std::string& key_system) const = 0;
virtual void SetDisabledMediaCodecs(const std::string& codecs) = 0;
protected:
diff --git a/src/cobalt/media/fetcher_buffered_data_source.cc b/src/cobalt/media/fetcher_buffered_data_source.cc
index 2409eb8..9eb25be 100644
--- a/src/cobalt/media/fetcher_buffered_data_source.cc
+++ b/src/cobalt/media/fetcher_buffered_data_source.cc
@@ -407,7 +407,7 @@
read_cb.Run(static_cast<int>(bytes_peeked));
// If we have a large buffer size, it could be ideal if we can keep sending
// small requests when the read offset is far from the beginning of the
- // buffer. However as the ShellDemuxer will cache many frames and the
+ // buffer. However as the ProgressiveDemuxer will cache many frames and the
// buffer we are using is usually small, we will just avoid sending requests
// here to make code simple.
return;
diff --git a/src/cobalt/media/filters/source_buffer_stream.cc b/src/cobalt/media/filters/source_buffer_stream.cc
index c4208ba..0d30352 100644
--- a/src/cobalt/media/filters/source_buffer_stream.cc
+++ b/src/cobalt/media/filters/source_buffer_stream.cc
@@ -736,7 +736,7 @@
size_t bytes_to_free = 0;
- int garbage_collection_duration_threshold_in_seconds =
+ int64_t garbage_collection_duration_threshold_in_seconds =
SbMediaGetBufferGarbageCollectionDurationThreshold() / kSbTimeSecond;
// Check if we're under or at the memory/duration limit.
const auto kGcDurationThresholdInMilliseconds =
diff --git a/src/cobalt/media/media.gyp b/src/cobalt/media/media.gyp
index bc1e422..4b31fc0 100644
--- a/src/cobalt/media/media.gyp
+++ b/src/cobalt/media/media.gyp
@@ -29,6 +29,8 @@
'media_module.cc',
'media_module.h',
+ 'base/audio_bus.cc',
+ 'base/audio_bus.h',
'base/audio_codecs.cc',
'base/audio_codecs.h',
'base/audio_decoder_config.cc',
@@ -90,10 +92,6 @@
'base/sbplayer_pipeline.cc',
'base/sbplayer_set_bounds_helper.cc',
'base/sbplayer_set_bounds_helper.h',
- 'base/shell_audio_bus.cc',
- 'base/shell_audio_bus.h',
- 'base/shell_data_source_reader.cc',
- 'base/shell_data_source_reader.h',
'base/starboard_player.cc',
'base/starboard_player.h',
'base/starboard_utils.cc',
@@ -125,20 +123,6 @@
'filters/h264_to_annex_b_bitstream_converter.h',
'filters/h265_parser.cc',
'filters/h265_parser.h',
- 'filters/shell_au.cc',
- 'filters/shell_au.h',
- 'filters/shell_avc_parser.cc',
- 'filters/shell_avc_parser.h',
- 'filters/shell_demuxer.cc',
- 'filters/shell_demuxer.h',
- 'filters/shell_mp4_map.cc',
- 'filters/shell_mp4_map.h',
- 'filters/shell_mp4_parser.cc',
- 'filters/shell_mp4_parser.h',
- 'filters/shell_parser.cc',
- 'filters/shell_parser.h',
- 'filters/shell_rbsp_stream.cc',
- 'filters/shell_rbsp_stream.h',
'filters/source_buffer_range.cc',
'filters/source_buffer_range.h',
'filters/source_buffer_state.cc',
@@ -216,6 +200,22 @@
'player/web_media_player_impl.h',
'player/web_media_player_proxy.cc',
'player/web_media_player_proxy.h',
+ 'progressive/avc_access_unit.cc',
+ 'progressive/avc_access_unit.h',
+ 'progressive/avc_parser.cc',
+ 'progressive/avc_parser.h',
+ 'progressive/data_source_reader.cc',
+ 'progressive/data_source_reader.h',
+ 'progressive/mp4_map.cc',
+ 'progressive/mp4_map.h',
+ 'progressive/mp4_parser.cc',
+ 'progressive/mp4_parser.h',
+ 'progressive/progressive_demuxer.cc',
+ 'progressive/progressive_demuxer.h',
+ 'progressive/progressive_parser.cc',
+ 'progressive/progressive_parser.h',
+ 'progressive/rbsp_stream.cc',
+ 'progressive/rbsp_stream.h',
],
'direct_dependent_settings': {
'include_dirs': [
@@ -240,9 +240,9 @@
'<(DEPTH)/testing/gtest.gyp:gtest',
],
'sources': [
- 'base/mock_shell_data_source_reader.h',
- 'filters/shell_mp4_map_unittest.cc',
- 'filters/shell_rbsp_stream_unittest.cc',
+ 'progressive/mock_data_source_reader.h',
+ 'progressive/mp4_map_unittest.cc',
+ 'progressive/rbsp_stream_unittest.cc',
],
'includes': [ '<(DEPTH)/cobalt/test/test.gypi' ],
},
diff --git a/src/cobalt/media/media_module.cc b/src/cobalt/media/media_module.cc
index e476a6f..9938d41 100644
--- a/src/cobalt/media/media_module.cc
+++ b/src/cobalt/media/media_module.cc
@@ -48,37 +48,27 @@
<< "\" from console/command line.";
}
- SbMediaSupportType CanPlayType(const std::string& mime_type,
- const std::string& key_system,
- bool is_progressive) const override {
- if (is_progressive) {
- // |mime_type| is something like:
- // video/mp4
- // video/webm
- // video/mp4; codecs="avc1.4d401e"
- // video/webm; codecs="vp9"
- // We do a rough pre-filter to ensure that only video/mp4 is supported as
- // progressive.
- if (SbStringFindString(mime_type.c_str(), "video/mp4") == 0 &&
- SbStringFindString(mime_type.c_str(), "application/x-mpegURL") == 0) {
- return kSbMediaSupportTypeNotSupported;
- }
+ SbMediaSupportType CanPlayProgressive(
+ const std::string& mime_type) const override {
+ // |mime_type| is something like:
+ // video/mp4
+ // video/webm
+ // video/mp4; codecs="avc1.4d401e"
+ // video/webm; codecs="vp9"
+ // We do a rough pre-filter to ensure that only video/mp4 is supported as
+ // progressive.
+ if (SbStringFindString(mime_type.c_str(), "video/mp4") == 0 &&
+ SbStringFindString(mime_type.c_str(), "application/x-mpegURL") == 0) {
+ return kSbMediaSupportTypeNotSupported;
}
- if (!disabled_media_codecs_.empty()) {
- auto mime_codecs = ExtractCodecs(mime_type);
- for (auto& disabled_codec : disabled_media_codecs_) {
- for (auto& mime_codec : mime_codecs) {
- if (mime_codec.find(disabled_codec) != std::string::npos) {
- LOG(INFO) << "Codec (" << mime_codec
- << ") is disabled via console/command line.";
- return kSbMediaSupportTypeNotSupported;
- }
- }
- }
- }
- SbMediaSupportType type =
- SbMediaCanPlayMimeAndKeySystem(mime_type.c_str(), key_system.c_str());
- return type;
+
+ return CanPlayType(mime_type, "");
+ }
+
+ SbMediaSupportType CanPlayAdaptive(
+ const std::string& mime_type,
+ const std::string& key_system) const override {
+ return CanPlayType(mime_type, key_system);
}
private:
@@ -107,6 +97,25 @@
return codecs;
}
+ SbMediaSupportType CanPlayType(const std::string& mime_type,
+ const std::string& key_system) const {
+ if (!disabled_media_codecs_.empty()) {
+ auto mime_codecs = ExtractCodecs(mime_type);
+ for (auto& disabled_codec : disabled_media_codecs_) {
+ for (auto& mime_codec : mime_codecs) {
+ if (mime_codec.find(disabled_codec) != std::string::npos) {
+ LOG(INFO) << "Codec (" << mime_codec
+ << ") is disabled via console/command line.";
+ return kSbMediaSupportTypeNotSupported;
+ }
+ }
+ }
+ }
+ SbMediaSupportType type =
+ SbMediaCanPlayMimeAndKeySystem(mime_type.c_str(), key_system.c_str());
+ return type;
+ }
+
// List of disabled media codecs that will be treated as unsupported.
std::vector<std::string> disabled_media_codecs_;
};
@@ -120,6 +129,7 @@
if (system_window_) {
window = system_window_->GetSbWindow();
}
+
return std::unique_ptr<WebMediaPlayer>(new media::WebMediaPlayerImpl(
window,
base::Bind(&MediaModule::GetSbDecodeTargetGraphicsContextProvider,
@@ -149,11 +159,16 @@
resource_provider_ = resource_provider;
+ SbWindow window = kSbWindowInvalid;
+ if (system_window_) {
+ window = system_window_->GetSbWindow();
+ }
+
for (Players::iterator iter = players_.begin(); iter != players_.end();
++iter) {
DCHECK(!iter->second);
if (!iter->second) {
- iter->first->Resume();
+ iter->first->Resume(window);
}
}
diff --git a/src/cobalt/media/player/web_media_player.h b/src/cobalt/media/player/web_media_player.h
index 86c4827..ea66d09 100644
--- a/src/cobalt/media/player/web_media_player.h
+++ b/src/cobalt/media/player/web_media_player.h
@@ -115,7 +115,9 @@
// Suspend/Resume
virtual void Suspend() = 0;
- virtual void Resume() = 0;
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ virtual void Resume(SbWindow window) = 0;
// True if the loaded media has a playable video/audio track.
virtual bool HasVideo() const = 0;
diff --git a/src/cobalt/media/player/web_media_player_impl.cc b/src/cobalt/media/player/web_media_player_impl.cc
index 6f18dc7..7638848 100644
--- a/src/cobalt/media/player/web_media_player_impl.cc
+++ b/src/cobalt/media/player/web_media_player_impl.cc
@@ -24,8 +24,8 @@
#include "cobalt/media/base/limits.h"
#include "cobalt/media/base/media_log.h"
#include "cobalt/media/filters/chunk_demuxer.h"
-#include "cobalt/media/filters/shell_demuxer.h"
#include "cobalt/media/player/web_media_player_proxy.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
#include "starboard/double.h"
#include "starboard/types.h"
@@ -292,8 +292,8 @@
is_local_source_ = !url.SchemeIs("http") && !url.SchemeIs("https");
progressive_demuxer_.reset(
- new ShellDemuxer(pipeline_thread_.task_runner(), buffer_allocator_,
- proxy_->data_source(), media_log_));
+ new ProgressiveDemuxer(pipeline_thread_.task_runner(), buffer_allocator_,
+ proxy_->data_source(), media_log_));
state_.is_progressive = true;
StartPipeline(progressive_demuxer_.get());
@@ -532,7 +532,9 @@
void WebMediaPlayerImpl::Suspend() { pipeline_->Suspend(); }
-void WebMediaPlayerImpl::Resume() { pipeline_->Resume(); }
+void WebMediaPlayerImpl::Resume(PipelineWindow window) {
+ pipeline_->Resume(window);
+}
bool WebMediaPlayerImpl::DidLoadingProgress() const {
DCHECK_EQ(main_loop_, base::MessageLoop::current());
diff --git a/src/cobalt/media/player/web_media_player_impl.h b/src/cobalt/media/player/web_media_player_impl.h
index a1a51be..c77d8b3 100644
--- a/src/cobalt/media/player/web_media_player_impl.h
+++ b/src/cobalt/media/player/web_media_player_impl.h
@@ -140,7 +140,9 @@
// Suspend/Resume
void Suspend() override;
- void Resume() override;
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ void Resume(PipelineWindow window) override;
// True if the loaded media has a playable video/audio track.
bool HasVideo() const override;
diff --git a/src/cobalt/media/filters/shell_au.cc b/src/cobalt/media/progressive/avc_access_unit.cc
similarity index 74%
rename from src/cobalt/media/filters/shell_au.cc
rename to src/cobalt/media/progressive/avc_access_unit.cc
index fadfadb..25ad24e 100644
--- a/src/cobalt/media/filters/shell_au.cc
+++ b/src/cobalt/media/progressive/avc_access_unit.cc
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_au.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
#include <algorithm>
#include "cobalt/media/base/decoder_buffer.h"
#include "cobalt/media/base/endian_util.h"
#include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
namespace cobalt {
namespace media {
@@ -27,7 +27,7 @@
namespace {
bool ReadBytes(uint64 offset, size_t size, uint8* buffer,
- ShellDataSourceReader* reader) {
+ DataSourceReader* reader) {
if (reader->BlockingRead(offset, size, buffer) != size) {
DLOG(ERROR) << "unable to download AU";
return false;
@@ -36,7 +36,7 @@
}
bool ReadBytes(uint64 offset, size_t size, DecoderBuffer* decoder_buffer,
- uint64 decoder_buffer_offset, ShellDataSourceReader* reader) {
+ uint64 decoder_buffer_offset, DataSourceReader* reader) {
size_t buffer_index = 0;
auto& allocations = decoder_buffer->allocations();
while (size > 0) {
@@ -67,17 +67,17 @@
return true;
}
-// ==== ShellEndOfStreamAU ==================================================
+// ==== EndOfStreamAU ==================================================
-class ShellEndOfStreamAU : public ShellAU {
+class EndOfStreamAU : public AvcAccessUnit {
public:
- ShellEndOfStreamAU(Type type, TimeDelta timestamp)
+ EndOfStreamAU(Type type, TimeDelta timestamp)
: type_(type), timestamp_(timestamp), duration_(kInfiniteDuration) {}
private:
bool IsEndOfStream() const override { return true; }
bool IsValid() const override { return true; }
- bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override {
+ bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override {
NOTREACHED();
return false;
}
@@ -102,20 +102,19 @@
TimeDelta duration_;
};
-// ==== ShellAudioAU =======================================================
+// ==== AudioAU =======================================================
-class ShellAudioAU : public ShellAU {
+class AudioAU : public AvcAccessUnit {
public:
- ShellAudioAU(uint64 offset, size_t size, size_t prepend_size,
- bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
- ShellParser* parser);
+ AudioAU(uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
+ TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser);
private:
bool IsEndOfStream() const override { return false; }
bool IsValid() const override {
return offset_ != 0 && size_ != 0 && timestamp_ != kNoTimestamp;
}
- bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override;
+ bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override;
Type GetType() const override { return DemuxerStream::AUDIO; }
bool IsKeyframe() const override { return is_keyframe_; }
bool AddPrepend() const override { return true; }
@@ -132,12 +131,12 @@
bool is_keyframe_;
TimeDelta timestamp_;
TimeDelta duration_;
- ShellParser* parser_;
+ ProgressiveParser* parser_;
};
-ShellAudioAU::ShellAudioAU(uint64 offset, size_t size, size_t prepend_size,
- bool is_keyframe, TimeDelta timestamp,
- TimeDelta duration, ShellParser* parser)
+AudioAU::AudioAU(uint64 offset, size_t size, size_t prepend_size,
+ bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
+ ProgressiveParser* parser)
: offset_(offset),
size_(size),
prepend_size_(prepend_size),
@@ -146,7 +145,7 @@
duration_(duration),
parser_(parser) {}
-bool ShellAudioAU::Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) {
+bool AudioAU::Read(DataSourceReader* reader, DecoderBuffer* buffer) {
DCHECK_LE(size_ + prepend_size_, buffer->data_size());
if (!ReadBytes(offset_, size_, buffer, prepend_size_, reader)) return false;
@@ -158,20 +157,20 @@
return true;
}
-// ==== ShellVideoAU =======================================================
+// ==== VideoAU =======================================================
-class ShellVideoAU : public ShellAU {
+class VideoAU : public AvcAccessUnit {
public:
- ShellVideoAU(uint64 offset, size_t size, size_t prepend_size,
- uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
- TimeDelta duration, ShellParser* parser);
+ VideoAU(uint64 offset, size_t size, size_t prepend_size,
+ uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
+ TimeDelta duration, ProgressiveParser* parser);
private:
bool IsEndOfStream() const override { return false; }
bool IsValid() const override {
return offset_ != 0 && size_ != 0 && timestamp_ != kNoTimestamp;
}
- bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) override;
+ bool Read(DataSourceReader* reader, DecoderBuffer* buffer) override;
Type GetType() const override { return DemuxerStream::VIDEO; }
bool IsKeyframe() const override { return is_keyframe_; }
bool AddPrepend() const override { return is_keyframe_; }
@@ -193,13 +192,13 @@
bool is_keyframe_;
TimeDelta timestamp_;
TimeDelta duration_;
- ShellParser* parser_;
+ ProgressiveParser* parser_;
};
-ShellVideoAU::ShellVideoAU(uint64 offset, size_t size, size_t prepend_size,
- uint8 length_of_nalu_size, bool is_keyframe,
- TimeDelta timestamp, TimeDelta duration,
- ShellParser* parser)
+VideoAU::VideoAU(uint64 offset, size_t size, size_t prepend_size,
+ uint8 length_of_nalu_size, bool is_keyframe,
+ TimeDelta timestamp, TimeDelta duration,
+ ProgressiveParser* parser)
: offset_(offset),
size_(size),
prepend_size_(prepend_size),
@@ -212,7 +211,7 @@
CHECK_NE(length_of_nalu_size_, 3);
}
-bool ShellVideoAU::Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) {
+bool VideoAU::Read(DataSourceReader* reader, DecoderBuffer* buffer) {
size_t au_left = size_; // bytes left in the AU
uint64 au_offset = offset_; // offset to read in the reader
size_t buf_left = buffer->data_size(); // bytes left in the buffer
@@ -280,33 +279,34 @@
} // namespace
-// ==== ShellAU ================================================================
+// ==== AvcAccessUnit
+// ================================================================
-ShellAU::ShellAU() {}
+AvcAccessUnit::AvcAccessUnit() {}
-ShellAU::~ShellAU() {}
+AvcAccessUnit::~AvcAccessUnit() {}
// static
-scoped_refptr<ShellAU> ShellAU::CreateEndOfStreamAU(DemuxerStream::Type type,
- TimeDelta timestamp) {
- return new ShellEndOfStreamAU(type, timestamp);
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateEndOfStreamAU(
+ DemuxerStream::Type type, TimeDelta timestamp) {
+ return new EndOfStreamAU(type, timestamp);
}
// static
-scoped_refptr<ShellAU> ShellAU::CreateAudioAU(
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateAudioAU(
uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
- TimeDelta timestamp, TimeDelta duration, ShellParser* parser) {
- return new ShellAudioAU(offset, size, prepend_size, is_keyframe, timestamp,
- duration, parser);
+ TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser) {
+ return new AudioAU(offset, size, prepend_size, is_keyframe, timestamp,
+ duration, parser);
}
// static
-scoped_refptr<ShellAU> ShellAU::CreateVideoAU(
+scoped_refptr<AvcAccessUnit> AvcAccessUnit::CreateVideoAU(
uint64 offset, size_t size, size_t prepend_size, uint8 length_of_nalu_size,
bool is_keyframe, TimeDelta timestamp, TimeDelta duration,
- ShellParser* parser) {
- return new ShellVideoAU(offset, size, prepend_size, length_of_nalu_size,
- is_keyframe, timestamp, duration, parser);
+ ProgressiveParser* parser) {
+ return new VideoAU(offset, size, prepend_size, length_of_nalu_size,
+ is_keyframe, timestamp, duration, parser);
}
} // namespace media
diff --git a/src/cobalt/media/filters/shell_au.h b/src/cobalt/media/progressive/avc_access_unit.h
similarity index 63%
rename from src/cobalt/media/filters/shell_au.h
rename to src/cobalt/media/progressive/avc_access_unit.h
index d6254e4..7c283e9 100644
--- a/src/cobalt/media/filters/shell_au.h
+++ b/src/cobalt/media/progressive/avc_access_unit.h
@@ -12,44 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_AU_H_
-#define COBALT_MEDIA_FILTERS_SHELL_AU_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
+#define COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
#include "base/memory/ref_counted.h"
#include "cobalt/media/base/demuxer_stream.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
namespace cobalt {
namespace media {
-class ShellParser;
+class ProgressiveParser;
static const int kAnnexBStartCodeSize = 4;
static const uint8_t kAnnexBStartCode[] = {0, 0, 0, 1};
-// The basic unit of currency between ShellDemuxer and ShellParser, the ShellAU
-// defines all needed information for a given AccessUnit (Frame) of encoded
-// media data.
-class ShellAU : public base::RefCountedThreadSafe<ShellAU> {
+// The basic unit of currency between ProgressiveDemuxer and ProgressiveParser,
+// the AvcAccessUnit defines all needed information for a given AvcAccessUnit
+// (Frame) of encoded media data.
+class AvcAccessUnit : public base::RefCountedThreadSafe<AvcAccessUnit> {
public:
typedef base::TimeDelta TimeDelta;
typedef DemuxerStream::Type Type;
- static scoped_refptr<ShellAU> CreateEndOfStreamAU(Type type,
- TimeDelta timestamp);
- static scoped_refptr<ShellAU> CreateAudioAU(
+ static scoped_refptr<AvcAccessUnit> CreateEndOfStreamAU(Type type,
+ TimeDelta timestamp);
+ static scoped_refptr<AvcAccessUnit> CreateAudioAU(
uint64 offset, size_t size, size_t prepend_size, bool is_keyframe,
- TimeDelta timestamp, TimeDelta duration, ShellParser* parser);
- static scoped_refptr<ShellAU> CreateVideoAU(
+ TimeDelta timestamp, TimeDelta duration, ProgressiveParser* parser);
+ static scoped_refptr<AvcAccessUnit> CreateVideoAU(
uint64 offset, size_t size, size_t prepend_size,
uint8 length_of_nalu_size, bool is_keyframe, TimeDelta timestamp,
- TimeDelta duration, ShellParser* parser);
+ TimeDelta duration, ProgressiveParser* parser);
virtual bool IsEndOfStream() const = 0;
virtual bool IsValid() const = 0;
// Read an AU from reader to buffer and also do all the necessary operations
// like prepending head to make it ready to decode.
- virtual bool Read(ShellDataSourceReader* reader, DecoderBuffer* buffer) = 0;
+ virtual bool Read(DataSourceReader* reader, DecoderBuffer* buffer) = 0;
virtual Type GetType() const = 0;
virtual bool IsKeyframe() const = 0;
virtual bool AddPrepend() const = 0;
@@ -63,13 +63,13 @@
virtual void SetTimestamp(TimeDelta timestamp) = 0;
protected:
- friend class base::RefCountedThreadSafe<ShellAU>;
+ friend class base::RefCountedThreadSafe<AvcAccessUnit>;
- ShellAU();
- virtual ~ShellAU();
+ AvcAccessUnit();
+ virtual ~AvcAccessUnit();
};
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_AU_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_ACCESS_UNIT_H_
diff --git a/src/cobalt/media/filters/shell_avc_parser.cc b/src/cobalt/media/progressive/avc_parser.cc
similarity index 92%
rename from src/cobalt/media/filters/shell_avc_parser.cc
rename to src/cobalt/media/progressive/avc_parser.cc
index baca931..e08141a 100644
--- a/src/cobalt/media/filters/shell_avc_parser.cc
+++ b/src/cobalt/media/progressive/avc_parser.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_avc_parser.h"
+#include "cobalt/media/progressive/avc_parser.h"
#include <limits>
#include <vector>
@@ -23,9 +23,9 @@
#include "cobalt/media/base/endian_util.h"
#include "cobalt/media/base/media_util.h"
#include "cobalt/media/base/video_types.h"
-#include "cobalt/media/filters/shell_au.h"
-#include "cobalt/media/filters/shell_rbsp_stream.h"
#include "cobalt/media/formats/mp4/aac.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
#include "starboard/memory.h"
namespace cobalt {
@@ -36,19 +36,19 @@
// lower five bits of first byte in SPS should be 7
static const uint8 kSPSNALType = 7;
-ShellAVCParser::ShellAVCParser(scoped_refptr<ShellDataSourceReader> reader,
- const scoped_refptr<MediaLog>& media_log)
- : ShellParser(reader),
+AVCParser::AVCParser(scoped_refptr<DataSourceReader> reader,
+ const scoped_refptr<MediaLog>& media_log)
+ : ProgressiveParser(reader),
media_log_(media_log),
nal_header_size_(0),
video_prepend_size_(0) {
DCHECK(media_log);
}
-ShellAVCParser::~ShellAVCParser() {}
+AVCParser::~AVCParser() {}
-bool ShellAVCParser::Prepend(scoped_refptr<ShellAU> au,
- scoped_refptr<DecoderBuffer> buffer) {
+bool AVCParser::Prepend(scoped_refptr<AvcAccessUnit> au,
+ scoped_refptr<DecoderBuffer> buffer) {
// sanity-check inputs
if (!au || !buffer) {
NOTREACHED() << "bad input to Prepend()";
@@ -93,8 +93,7 @@
return true;
}
-bool ShellAVCParser::DownloadAndParseAVCConfigRecord(uint64 offset,
- uint32 size) {
+bool AVCParser::DownloadAndParseAVCConfigRecord(uint64 offset, uint32 size) {
if (size == 0) {
return false;
}
@@ -109,8 +108,8 @@
}
// static
-bool ShellAVCParser::ParseSPS(const uint8* sps, size_t sps_size,
- ShellSPSRecord* record_out) {
+bool AVCParser::ParseSPS(const uint8* sps, size_t sps_size,
+ SPSRecord* record_out) {
DCHECK(sps) << "no sps provided";
DCHECK(record_out) << "no output structure provided";
// first byte is NAL type id, check that it is SPS
@@ -119,7 +118,7 @@
return false;
}
// convert SPS NALU to RBSP stream
- ShellRBSPStream sps_rbsp(sps + 1, sps_size - 1);
+ RBSPStream sps_rbsp(sps + 1, sps_size - 1);
uint8 profile_idc = 0;
if (!sps_rbsp.ReadByte(&profile_idc)) {
DLOG(ERROR) << "failure reading profile_idc from sps RBSP";
@@ -294,7 +293,7 @@
return true;
}
-bool ShellAVCParser::ParseAVCConfigRecord(uint8* buffer, uint32 size) {
+bool AVCParser::ParseAVCConfigRecord(uint8* buffer, uint32 size) {
if (size < kAVCConfigMinSize) {
DLOG(ERROR) << base::StringPrintf("AVC config record bad size: %d", size);
return false;
@@ -392,7 +391,7 @@
}
}
// now we parse the valid SPS we extracted from byte stream earlier.
- ShellSPSRecord sps_record;
+ SPSRecord sps_record;
if (!ParseSPS(buffer + usable_sps_offset, usable_sps_size, &sps_record)) {
DLOG(WARNING) << "error parsing SPS";
return false;
@@ -409,8 +408,8 @@
buffer + usable_pps_offset, usable_pps_size);
}
-bool ShellAVCParser::BuildAnnexBPrepend(uint8* sps, uint32 sps_size, uint8* pps,
- uint32 pps_size) {
+bool AVCParser::BuildAnnexBPrepend(uint8* sps, uint32 sps_size, uint8* pps,
+ uint32 pps_size) {
// We will need to attach the sps and pps (if provided) to each keyframe
// video packet, with the AnnexB start code in front of each. Start with
// sps size and start code
@@ -445,7 +444,7 @@
return true;
}
-void ShellAVCParser::ParseAudioSpecificConfig(uint8 b0, uint8 b1) {
+void AVCParser::ParseAudioSpecificConfig(uint8 b0, uint8 b1) {
media::mp4::AAC aac;
std::vector<uint8> aac_config(2);
@@ -472,8 +471,8 @@
Unencrypted(), base::TimeDelta(), 0);
}
-size_t ShellAVCParser::CalculatePrependSize(DemuxerStream::Type type,
- bool is_keyframe) {
+size_t AVCParser::CalculatePrependSize(DemuxerStream::Type type,
+ bool is_keyframe) {
size_t prepend_size = 0;
if (type == DemuxerStream::VIDEO) {
bool needs_prepend = is_keyframe;
diff --git a/src/cobalt/media/filters/shell_avc_parser.h b/src/cobalt/media/progressive/avc_parser.h
similarity index 77%
rename from src/cobalt/media/filters/shell_avc_parser.h
rename to src/cobalt/media/progressive/avc_parser.h
index 758a7a2..e383bdf 100644
--- a/src/cobalt/media/filters/shell_avc_parser.h
+++ b/src/cobalt/media/progressive/avc_parser.h
@@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
#include <vector>
#include "cobalt/media/base/media_log.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
namespace cobalt {
namespace media {
@@ -29,27 +29,27 @@
static const int kAnnexBPrependMaxSize = 1024;
// while not an actual parser, provides shared functionality to both the
-// mp4 and flv parsers which derive from it. Implements part of ShellParser
-// while leaving the rest for its children.
-class ShellAVCParser : public ShellParser {
+// mp4 and flv parsers which derive from it. Implements part of
+// ProgressiveParser while leaving the rest for its children.
+class AVCParser : public ProgressiveParser {
public:
- explicit ShellAVCParser(scoped_refptr<ShellDataSourceReader> reader,
- const scoped_refptr<MediaLog>& media_log);
- virtual ~ShellAVCParser();
+ explicit AVCParser(scoped_refptr<DataSourceReader> reader,
+ const scoped_refptr<MediaLog>& media_log);
+ virtual ~AVCParser();
- struct ShellSPSRecord {
+ struct SPSRecord {
math::Size coded_size;
math::Rect visible_rect;
math::Size natural_size;
uint32 num_ref_frames;
};
static bool ParseSPS(const uint8* sps, size_t sps_size,
- ShellSPSRecord* record_out);
+ SPSRecord* record_out);
// GetNextAU we must pass on to FLV or MP4 children.
- virtual scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) = 0;
+ virtual scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) = 0;
// Prepends are common to all AVC/AAC containers so we can do this one here.
- bool Prepend(scoped_refptr<ShellAU> au,
+ bool Prepend(scoped_refptr<AvcAccessUnit> au,
scoped_refptr<DecoderBuffer> buffer) override;
protected:
@@ -77,4 +77,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_AVC_PARSER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_AVC_PARSER_H_
diff --git a/src/cobalt/media/base/shell_data_source_reader.cc b/src/cobalt/media/progressive/data_source_reader.cc
similarity index 81%
rename from src/cobalt/media/base/shell_data_source_reader.cc
rename to src/cobalt/media/progressive/data_source_reader.cc
index 2c7c38f..fd4ff71 100644
--- a/src/cobalt/media/base/shell_data_source_reader.cc
+++ b/src/cobalt/media/progressive/data_source_reader.cc
@@ -12,16 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
#include "starboard/types.h"
namespace cobalt {
namespace media {
-const int ShellDataSourceReader::kReadError = DataSource::kReadError;
+const int DataSourceReader::kReadError = DataSource::kReadError;
-ShellDataSourceReader::ShellDataSourceReader()
+DataSourceReader::DataSourceReader()
: data_source_(NULL),
blocking_read_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
@@ -29,15 +29,15 @@
read_has_failed_(false),
last_bytes_read_(0) {}
-ShellDataSourceReader::~ShellDataSourceReader() {}
+DataSourceReader::~DataSourceReader() {}
-void ShellDataSourceReader::SetDataSource(DataSource* data_source) {
+void DataSourceReader::SetDataSource(DataSource* data_source) {
DCHECK(data_source);
data_source_ = data_source;
}
// currently only single-threaded reads supported
-int ShellDataSourceReader::BlockingRead(int64 position, int size, uint8* data) {
+int DataSourceReader::BlockingRead(int64 position, int size, uint8* data) {
// read failures are unrecoverable, all subsequent reads will also fail
if (read_has_failed_) {
return kReadError;
@@ -57,7 +57,7 @@
}
data_source_->Read(
position, size, data,
- base::Bind(&ShellDataSourceReader::BlockingReadCompleted, this));
+ base::Bind(&DataSourceReader::BlockingReadCompleted, this));
}
// wait for callback on read completion
@@ -91,7 +91,7 @@
return total_bytes_read;
}
-void ShellDataSourceReader::Stop() {
+void DataSourceReader::Stop() {
if (data_source_) {
data_source_->Stop();
@@ -100,13 +100,13 @@
}
}
-void ShellDataSourceReader::BlockingReadCompleted(int bytes_read) {
+void DataSourceReader::BlockingReadCompleted(int bytes_read) {
last_bytes_read_ = bytes_read;
// wake up blocked thread
blocking_read_event_.Signal();
}
-int64 ShellDataSourceReader::FileSize() {
+int64 DataSourceReader::FileSize() {
if (file_size_ == -1) {
base::AutoLock auto_lock(lock_);
if (data_source_ && !data_source_->GetSize(&file_size_)) {
diff --git a/src/cobalt/media/base/shell_data_source_reader.h b/src/cobalt/media/progressive/data_source_reader.h
similarity index 85%
rename from src/cobalt/media/base/shell_data_source_reader.h
rename to src/cobalt/media/progressive/data_source_reader.h
index 2049a9d..c773083 100644
--- a/src/cobalt/media/base/shell_data_source_reader.h
+++ b/src/cobalt/media/progressive/data_source_reader.h
@@ -12,9 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
-#define COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
-
+#ifndef COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
+#define COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/ref_counted.h"
@@ -32,12 +31,11 @@
// object is also the sole owner of a pointer to DataSource. If we want to add
// asynchronous reading to this object it will need its own thread and a
// callback queue.
-class ShellDataSourceReader
- : public base::RefCountedThreadSafe<ShellDataSourceReader> {
+class DataSourceReader : public base::RefCountedThreadSafe<DataSourceReader> {
public:
static const int kReadError;
- ShellDataSourceReader();
+ DataSourceReader();
virtual void SetDataSource(DataSource* data_source);
// Block the calling thread's message loop until read is complete.
@@ -53,8 +51,8 @@
virtual void Stop();
protected:
- friend class base::RefCountedThreadSafe<ShellDataSourceReader>;
- virtual ~ShellDataSourceReader();
+ friend class base::RefCountedThreadSafe<DataSourceReader>;
+ virtual ~DataSourceReader();
// blocking read callback
virtual void BlockingReadCompleted(int bytes_read);
@@ -69,4 +67,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_BASE_SHELL_DATA_SOURCE_READER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_DATA_SOURCE_READER_H_
diff --git a/src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc b/src/cobalt/media/progressive/demuxer_fuzzer.cc
similarity index 78%
rename from src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc
rename to src/cobalt/media/progressive/demuxer_fuzzer.cc
index 74d1e7e..ebd2766 100644
--- a/src/cobalt/media/sandbox/shell_demuxer_fuzzer.cc
+++ b/src/cobalt/media/progressive/demuxer_fuzzer.cc
@@ -25,7 +25,7 @@
#include "cobalt/media/sandbox/media_sandbox.h"
#include "media/base/bind_to_loop.h"
#include "media/base/pipeline_status.h"
-#include "media/filters/shell_demuxer.h"
+#include "media/progressive/progressive_demuxer.h"
namespace cobalt {
namespace media {
@@ -39,19 +39,20 @@
using ::media::DemuxerHost;
using ::media::DemuxerStream;
using ::media::PipelineStatus;
-using ::media::ShellDemuxer;
+using ::media::ProgressiveDemuxer;
-class ShellDemuxerFuzzer : DemuxerHost {
+class DemuxerFuzzer : DemuxerHost {
public:
- explicit ShellDemuxerFuzzer(const std::vector<uint8>& content)
+ explicit DemuxerFuzzer(const std::vector<uint8>& content)
: error_occurred_(false), eos_count_(0), stopped_(false) {
- demuxer_ = new ShellDemuxer(base::MessageLoop::current()->task_runner(),
- new InMemoryDataSource(content));
+ demuxer_ =
+ new ProgressiveDemuxer(base::MessageLoop::current()->task_runner(),
+ new InMemoryDataSource(content));
}
void Fuzz() {
demuxer_->Initialize(
- this, Bind(&ShellDemuxerFuzzer::InitializeCB, base::Unretained(this)));
+ this, Bind(&DemuxerFuzzer::InitializeCB, base::Unretained(this)));
// Check if there is any error or if both of the audio and video streams
// have reached eos.
@@ -59,7 +60,7 @@
base::RunLoop().RunUntilIdle();
}
- demuxer_->Stop(Bind(&ShellDemuxerFuzzer::StopCB, base::Unretained(this)));
+ demuxer_->Stop(Bind(&DemuxerFuzzer::StopCB, base::Unretained(this)));
while (!stopped_) {
base::RunLoop().RunUntilIdle();
@@ -101,10 +102,10 @@
error_occurred_ = true;
return;
}
- audio_stream->Read(BindToCurrentLoop(Bind(
- &ShellDemuxerFuzzer::ReadCB, base::Unretained(this), audio_stream)));
- video_stream->Read(BindToCurrentLoop(Bind(
- &ShellDemuxerFuzzer::ReadCB, base::Unretained(this), video_stream)));
+ audio_stream->Read(BindToCurrentLoop(
+ Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), audio_stream)));
+ video_stream->Read(BindToCurrentLoop(
+ Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), video_stream)));
}
void StopCB() { stopped_ = true; }
@@ -122,18 +123,18 @@
}
DCHECK(!error_occurred_);
stream->Read(BindToCurrentLoop(
- Bind(&ShellDemuxerFuzzer::ReadCB, base::Unretained(this), stream)));
+ Bind(&DemuxerFuzzer::ReadCB, base::Unretained(this), stream)));
}
bool error_occurred_;
int eos_count_;
bool stopped_;
- scoped_refptr<ShellDemuxer> demuxer_;
+ scoped_refptr<ProgressiveDemuxer> demuxer_;
};
-class ShellDemuxerFuzzerApp : public FuzzerApp {
+class DemuxerFuzzerApp : public FuzzerApp {
public:
- explicit ShellDemuxerFuzzerApp(MediaSandbox* media_sandbox)
+ explicit DemuxerFuzzerApp(MediaSandbox* media_sandbox)
: media_sandbox_(media_sandbox) {}
std::vector<uint8> ParseFileContent(
@@ -149,7 +150,7 @@
void Fuzz(const std::string& file_name,
const std::vector<uint8>& fuzzing_content) override {
- ShellDemuxerFuzzer demuxer_fuzzer(fuzzing_content);
+ DemuxerFuzzer demuxer_fuzzer(fuzzing_content);
demuxer_fuzzer.Fuzz();
}
@@ -159,9 +160,8 @@
int SandboxMain(int argc, char** argv) {
MediaSandbox media_sandbox(
- argc, argv,
- base::FilePath(FILE_PATH_LITERAL("shell_demuxer_fuzzer.json")));
- ShellDemuxerFuzzerApp fuzzer_app(&media_sandbox);
+ argc, argv, base::FilePath(FILE_PATH_LITERAL("demuxer_fuzzer.json")));
+ DemuxerFuzzerApp fuzzer_app(&media_sandbox);
if (fuzzer_app.Init(argc, argv)) {
fuzzer_app.RunFuzzingLoop();
diff --git a/src/cobalt/media/base/mock_shell_data_source_reader.h b/src/cobalt/media/progressive/mock_data_source_reader.h
similarity index 71%
rename from src/cobalt/media/base/mock_shell_data_source_reader.h
rename to src/cobalt/media/progressive/mock_data_source_reader.h
index 08935b2..cf3ddab 100644
--- a/src/cobalt/media/base/mock_shell_data_source_reader.h
+++ b/src/cobalt/media/progressive/mock_data_source_reader.h
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-#ifndef COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
-#define COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
+#define COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
-#include "media/base/shell_data_source_reader.h"
+#include "media/progressive/data_source_reader.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace cobalt {
namespace media {
-class MockShellDataSourceReader : public ShellDataSourceReader {
+class MockDataSourceReader : public DataSourceReader {
public:
- MockShellDataSourceReader() {}
+ MockDataSourceReader() {}
- // ShellDataSourceReader implementation
+ // DataSourceReader implementation
MOCK_METHOD1(SetDataSource, void(DataSource*));
MOCK_METHOD3(BlockingRead, int(int64, int, uint8*));
MOCK_METHOD0(FileSize, int64());
@@ -37,4 +37,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_BASE_MOCK_SHELL_DATA_SOURCE_READER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_MOCK_DATA_SOURCE_READER_H_
diff --git a/src/cobalt/media/filters/shell_mp4_map.cc b/src/cobalt/media/progressive/mp4_map.cc
similarity index 92%
rename from src/cobalt/media/filters/shell_mp4_map.cc
rename to src/cobalt/media/progressive/mp4_map.cc
index 3a5189e..3a65467 100644
--- a/src/cobalt/media/filters/shell_mp4_map.cc
+++ b/src/cobalt/media/progressive/mp4_map.cc
@@ -12,23 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/mp4_map.h"
#include <algorithm>
#include "base/strings/stringprintf.h"
#include "cobalt/media/base/endian_util.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
namespace cobalt {
namespace media {
// ==== TableCache =============================================================
-ShellMP4Map::TableCache::TableCache(uint64 table_offset, uint32 entry_count,
- uint32 entry_size,
- uint32 cache_size_entries,
- scoped_refptr<ShellDataSourceReader> reader)
+MP4Map::TableCache::TableCache(uint64 table_offset, uint32 entry_count,
+ uint32 entry_size, uint32 cache_size_entries,
+ scoped_refptr<DataSourceReader> reader)
: entry_size_(entry_size),
entry_count_(entry_count),
cache_size_entries_(cache_size_entries),
@@ -37,7 +36,7 @@
cache_first_entry_number_(-1),
cache_entry_count_(0) {}
-uint8* ShellMP4Map::TableCache::GetBytesAtEntry(uint32 entry_number) {
+uint8* MP4Map::TableCache::GetBytesAtEntry(uint32 entry_number) {
// don't fetch the unfetchable
if (entry_number >= entry_count_) {
return NULL;
@@ -77,7 +76,7 @@
return &cache_[0] + (cache_offset * entry_size_);
}
-bool ShellMP4Map::TableCache::ReadU32Entry(uint32 entry_number, uint32* entry) {
+bool MP4Map::TableCache::ReadU32Entry(uint32 entry_number, uint32* entry) {
if (uint8* data = GetBytesAtEntry(entry_number)) {
*entry = endian_util::load_uint32_big_endian(data);
return true;
@@ -86,8 +85,8 @@
return false;
}
-bool ShellMP4Map::TableCache::ReadU32PairEntry(uint32 entry_number,
- uint32* first, uint32* second) {
+bool MP4Map::TableCache::ReadU32PairEntry(uint32 entry_number, uint32* first,
+ uint32* second) {
if (uint8* data = GetBytesAtEntry(entry_number)) {
if (first) *first = endian_util::load_uint32_big_endian(data);
if (second) *second = endian_util::load_uint32_big_endian(data + 4);
@@ -97,8 +96,8 @@
return false;
}
-bool ShellMP4Map::TableCache::ReadU32EntryIntoU64(uint32 entry_number,
- uint64* entry) {
+bool MP4Map::TableCache::ReadU32EntryIntoU64(uint32 entry_number,
+ uint64* entry) {
if (uint8* data = GetBytesAtEntry(entry_number)) {
*entry = endian_util::load_uint32_big_endian(data);
return true;
@@ -107,7 +106,7 @@
return false;
}
-bool ShellMP4Map::TableCache::ReadU64Entry(uint32 entry_number, uint64* entry) {
+bool MP4Map::TableCache::ReadU64Entry(uint32 entry_number, uint64* entry) {
if (uint8* data = GetBytesAtEntry(entry_number)) {
*entry = endian_util::load_uint64_big_endian(data);
return true;
@@ -116,7 +115,7 @@
return false;
}
-// ==== ShellMP4Map ============================================================
+// ==== MP4Map ============================================================
// atom | name | size | description, (*) means optional table
// -----+-----------------------+------+----------------------------------------
@@ -128,7 +127,7 @@
// stts | time-to-sample | 8 | run-length sample number to duration
// stsz | sample size | 4 | per-sample list of sample sizes
-ShellMP4Map::ShellMP4Map(scoped_refptr<ShellDataSourceReader> reader)
+MP4Map::MP4Map(scoped_refptr<DataSourceReader> reader)
: reader_(reader),
current_chunk_sample_(0),
next_chunk_sample_(0),
@@ -155,14 +154,14 @@
stts_table_index_(0),
stsz_default_size_(0) {}
-bool ShellMP4Map::IsComplete() {
+bool MP4Map::IsComplete() {
// all required table pointers must be valid for map to function
return (co64_ || stco_) && stsc_ && stts_ && (stsz_ || stsz_default_size_);
}
// The sample size is a lookup in the stsz table, which is indexed per sample
// number.
-bool ShellMP4Map::GetSize(uint32 sample_number, uint32* size_out) {
+bool MP4Map::GetSize(uint32 sample_number, uint32* size_out) {
DCHECK(size_out);
DCHECK(stsz_ || stsz_default_size_);
@@ -184,7 +183,7 @@
// then use the stsz to sum samples to the byte offset with that chunk. The sum
// of the chunk offset and the byte offset within the chunk is the offset of
// the sample.
-bool ShellMP4Map::GetOffset(uint32 sample_number, uint64* offset_out) {
+bool MP4Map::GetOffset(uint32 sample_number, uint64* offset_out) {
DCHECK(offset_out);
DCHECK(stsc_);
DCHECK(stco_ || co64_);
@@ -255,7 +254,7 @@
// durations to find the dts of that sample number. We then integrate sample
// numbers through the ctts to find the composition time offset, which we add to
// the dts to return the pts.
-bool ShellMP4Map::GetTimestamp(uint32 sample_number, uint64* timestamp_out) {
+bool MP4Map::GetTimestamp(uint32 sample_number, uint64* timestamp_out) {
DCHECK(timestamp_out);
if (sample_number > highest_valid_sample_number_) {
return false;
@@ -280,7 +279,7 @@
}
// Sum through the stts to find the duration of the given sample_number.
-bool ShellMP4Map::GetDuration(uint32 sample_number, uint32* duration_out) {
+bool MP4Map::GetDuration(uint32 sample_number, uint32* duration_out) {
DCHECK(duration_out);
if (sample_number > highest_valid_sample_number_) {
return false;
@@ -295,7 +294,7 @@
return true;
}
-bool ShellMP4Map::GetIsKeyframe(uint32 sample_number, bool* is_keyframe_out) {
+bool MP4Map::GetIsKeyframe(uint32 sample_number, bool* is_keyframe_out) {
DCHECK(is_keyframe_out);
if (sample_number > highest_valid_sample_number_) {
return false;
@@ -336,14 +335,14 @@
return true;
}
-bool ShellMP4Map::IsEOS(uint32 sample_number) {
+bool MP4Map::IsEOS(uint32 sample_number) {
return (sample_number > highest_valid_sample_number_);
}
// First look up the sample number for the provided timestamp by integrating
// timestamps through the stts. Then do a binary search on the stss to find the
// keyframe nearest that sample number.
-bool ShellMP4Map::GetKeyframe(uint64 timestamp, uint32* sample_out) {
+bool MP4Map::GetKeyframe(uint64 timestamp, uint32* sample_out) {
DCHECK(sample_out);
// Advance stts to the provided timestamp range
if (!stts_AdvanceToTime(timestamp)) {
@@ -375,8 +374,8 @@
// Set up map state and load first part of table, or entire table if it is small
// enough, for each of the supporated atoms.
-bool ShellMP4Map::SetAtom(uint32 four_cc, uint64 offset, uint64 size,
- uint32 cache_size_entries, const uint8* atom) {
+bool MP4Map::SetAtom(uint32 four_cc, uint64 offset, uint64 size,
+ uint32 cache_size_entries, const uint8* atom) {
// All map atoms are variable-length tables starting with 4 bytes of
// version/flag info followed by a uint32 indicating the number of items in
// table. The stsz atom bucks tradition by putting an optional default value
@@ -463,7 +462,7 @@
return atom_init;
}
-bool ShellMP4Map::co64_Init() {
+bool MP4Map::co64_Init() {
DCHECK(co64_);
// load offset of first chunk into current_chunk_offset_
if (co64_->GetEntryCount() > 0) {
@@ -482,7 +481,7 @@
// uint32 sample count
// uint32 composition offset in ticks
//
-bool ShellMP4Map::ctts_Init() {
+bool MP4Map::ctts_Init() {
DCHECK(ctts_);
// get cache segment vector to reserve table entries in advance
int cache_segments =
@@ -506,7 +505,7 @@
// To find the composition offset of a given sample number we must integrate
// through the ctts to find the range of samples containing sample_number. Note
// that the ctts is an optional table.
-bool ShellMP4Map::ctts_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::ctts_AdvanceToSample(uint32 sample_number) {
// ctts table is optional, so treat not having one as non-fatal
if (!ctts_) {
return true;
@@ -573,8 +572,8 @@
return true;
}
-bool ShellMP4Map::ctts_SlipCacheToSample(uint32 sample_number,
- int starting_cache_index) {
+bool MP4Map::ctts_SlipCacheToSample(uint32 sample_number,
+ int starting_cache_index) {
DCHECK_LT(starting_cache_index, ctts_samples_.size());
int cache_index = starting_cache_index;
for (; cache_index + 1 < ctts_samples_.size(); cache_index++) {
@@ -593,7 +592,7 @@
return true;
}
-bool ShellMP4Map::stco_Init() {
+bool MP4Map::stco_Init() {
DCHECK(stco_);
// load offset of first chunk into current_chunk_offset_
if (stco_->GetEntryCount() > 0) {
@@ -610,7 +609,7 @@
// uint32 first chunk number with this sample count
// uint32 samples-per-chunk
// uint32 sample description id (unused)
-bool ShellMP4Map::stsc_Init() {
+bool MP4Map::stsc_Init() {
DCHECK(stsc_);
// set up vector to correct final size
int cache_segments =
@@ -660,7 +659,7 @@
// to be consumed incrementally and with minimal memory consumption we calculate
// this integration step only when needed, and save results for each cached
// piece of the table, to avoid having to recalculate needed data.
-bool ShellMP4Map::stsc_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::stsc_AdvanceToSample(uint32 sample_number) {
DCHECK(stsc_);
// sample_number could be before first chunk, meaning that we are seeking
// backwards and have left the current chunk. Find the closest part of the
@@ -736,8 +735,8 @@
return true;
}
-bool ShellMP4Map::stsc_SlipCacheToSample(uint32 sample_number,
- int starting_cache_index) {
+bool MP4Map::stsc_SlipCacheToSample(uint32 sample_number,
+ int starting_cache_index) {
DCHECK_LT(starting_cache_index, stsc_sample_sums_.size());
// look through old sample sums for the first entry that exceeds sample
// sample_number, we want the entry right before that
@@ -776,7 +775,7 @@
}
// stss is a list of sample numbers that are keyframes.
-bool ShellMP4Map::stss_Init() {
+bool MP4Map::stss_Init() {
int cache_segments =
(stss_->GetEntryCount() / stss_->GetCacheSizeEntries()) + 1;
stss_keyframes_.reserve(cache_segments);
@@ -800,7 +799,7 @@
}
// advance by one table entry through stss, updating cache if necessary
-bool ShellMP4Map::stss_AdvanceStep() {
+bool MP4Map::stss_AdvanceStep() {
DCHECK(stss_);
stss_last_keyframe_ = stss_next_keyframe_;
stss_table_index_++;
@@ -823,7 +822,7 @@
return true;
}
-bool ShellMP4Map::stss_FindNearestKeyframe(uint32 sample_number) {
+bool MP4Map::stss_FindNearestKeyframe(uint32 sample_number) {
DCHECK(stss_);
// it is assumed that there's at least one cache entry created by
// stss_Init();
@@ -955,7 +954,7 @@
// The stts table has the following per-entry layout:
// uint32 sample count - number of sequential samples with this duration
// uint32 sample duration - duration in ticks of this sample range
-bool ShellMP4Map::stts_Init() {
+bool MP4Map::stts_Init() {
int cache_segments =
(stts_->GetEntryCount() / stts_->GetCacheSizeEntries()) + 1;
stts_samples_.reserve(cache_segments);
@@ -982,7 +981,7 @@
return true;
}
-bool ShellMP4Map::stts_AdvanceToSample(uint32 sample_number) {
+bool MP4Map::stts_AdvanceToSample(uint32 sample_number) {
DCHECK(stts_);
// sample_number could be before our current sample range, in which case
// we skip to the nearest table entry before sample_number and integrate
@@ -1015,8 +1014,8 @@
// Move our integration steps to a previously saved entry in the cache tables.
// Searches linearly through the vector of old cached values, so can accept a
// starting index to do the search from.
-bool ShellMP4Map::stts_SlipCacheToSample(uint32 sample_number,
- int starting_cache_index) {
+bool MP4Map::stts_SlipCacheToSample(uint32 sample_number,
+ int starting_cache_index) {
DCHECK_LT(starting_cache_index, stts_samples_.size());
int cache_index = starting_cache_index;
for (; cache_index + 1 < stts_samples_.size(); cache_index++) {
@@ -1039,7 +1038,7 @@
return true;
}
-bool ShellMP4Map::stts_AdvanceToTime(uint64 timestamp) {
+bool MP4Map::stts_AdvanceToTime(uint64 timestamp) {
DCHECK(stts_);
if (timestamp < stts_first_sample_time_) {
@@ -1067,7 +1066,7 @@
return true;
}
-bool ShellMP4Map::stts_IntegrateStep() {
+bool MP4Map::stts_IntegrateStep() {
// advance time to next sample range
uint32 range_size = stts_next_first_sample_ - stts_first_sample_;
stts_first_sample_time_ += (range_size * stts_sample_duration_);
@@ -1111,8 +1110,7 @@
return true;
}
-bool ShellMP4Map::stts_SlipCacheToTime(uint64 timestamp,
- int starting_cache_index) {
+bool MP4Map::stts_SlipCacheToTime(uint64 timestamp, int starting_cache_index) {
DCHECK_LT(starting_cache_index, stts_timestamps_.size());
int cache_index = starting_cache_index;
for (; cache_index + 1 < stts_timestamps_.size(); cache_index++) {
@@ -1135,7 +1133,7 @@
return true;
}
-bool ShellMP4Map::stsz_Init() { return stsz_->GetBytesAtEntry(0) != NULL; }
+bool MP4Map::stsz_Init() { return stsz_->GetBytesAtEntry(0) != NULL; }
} // namespace media
} // namespace cobalt
diff --git a/src/cobalt/media/filters/shell_mp4_map.h b/src/cobalt/media/progressive/mp4_map.h
similarity index 93%
rename from src/cobalt/media/filters/shell_mp4_map.h
rename to src/cobalt/media/progressive/mp4_map.h
index 88e1319..93bc08f 100644
--- a/src/cobalt/media/filters/shell_mp4_map.h
+++ b/src/cobalt/media/progressive/mp4_map.h
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
-#define COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
+#define COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
#include <vector>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
+#include "cobalt/media/progressive/data_source_reader.h"
namespace cobalt {
namespace media {
@@ -37,9 +37,9 @@
// them to provide byte offsets, sizes, and timestamps of a mp4 atom. The
// caching design benefits from, but does not require, sequential access
// in sample numbers.
-class ShellMP4Map : public base::RefCountedThreadSafe<ShellMP4Map> {
+class MP4Map : public base::RefCountedThreadSafe<MP4Map> {
public:
- explicit ShellMP4Map(scoped_refptr<ShellDataSourceReader> reader);
+ explicit MP4Map(scoped_refptr<DataSourceReader> reader);
bool IsComplete();
@@ -117,7 +117,7 @@
uint32 entry_count, // number of entries in table
uint32 entry_size, // size in bytes of each entry in table
uint32 cache_size_entries, // number of entries to cache in mem
- scoped_refptr<ShellDataSourceReader> reader); // reader to use
+ scoped_refptr<DataSourceReader> reader); // reader to use
// The following Read* functions all read values in big endian.
bool ReadU32Entry(uint32 entry_number, uint32* entry);
@@ -138,7 +138,7 @@
uint32 entry_count_; // size of table in entries
uint32 cache_size_entries_; // max number of entries to fit in memory
uint64 table_offset_; // offset of table in stream
- scoped_refptr<ShellDataSourceReader> reader_; // means to read more table
+ scoped_refptr<DataSourceReader> reader_; // means to read more table
// current cache state
std::vector<uint8> cache_; // the cached part of the table
@@ -146,7 +146,7 @@
uint32 cache_entry_count_; // number of valid entries in cache
};
- scoped_refptr<ShellDataSourceReader> reader_;
+ scoped_refptr<DataSourceReader> reader_;
// current integration state for GetOffset(), we save the sum of sample sizes
// within the current chunk.
@@ -211,4 +211,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_MP4_MAP_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_MP4_MAP_H_
diff --git a/src/cobalt/media/filters/shell_mp4_map_unittest.cc b/src/cobalt/media/progressive/mp4_map_unittest.cc
similarity index 95%
rename from src/cobalt/media/filters/shell_mp4_map_unittest.cc
rename to src/cobalt/media/progressive/mp4_map_unittest.cc
index 1fa0a73..347b7bb 100644
--- a/src/cobalt/media/filters/shell_mp4_map_unittest.cc
+++ b/src/cobalt/media/progressive/mp4_map_unittest.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/mp4_map.h"
#include <stdlib.h> // for rand and srand
@@ -23,8 +23,8 @@
#include <vector>
#include "cobalt/media/base/endian_util.h"
-#include "cobalt/media/base/mock_shell_data_source_reader.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mock_data_source_reader.h"
+#include "cobalt/media/progressive/mp4_parser.h"
#include "starboard/memory.h"
#include "starboard/types.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -48,8 +48,8 @@
using cobalt::media::kEntrySize_stss;
using cobalt::media::kEntrySize_stsz;
using cobalt::media::kEntrySize_stts;
-using cobalt::media::MockShellDataSourceReader;
-using cobalt::media::ShellMP4Map;
+using cobalt::media::MockDataSourceReader;
+using cobalt::media::MP4Map;
using ::testing::_;
using ::testing::AllOf;
@@ -403,16 +403,16 @@
}
};
-class ShellMP4MapTest : public testing::Test {
+class MP4MapTest : public testing::Test {
protected:
- ShellMP4MapTest() {
+ MP4MapTest() {
// make a new mock reader
- reader_ = new ::testing::NiceMock<MockShellDataSourceReader>();
+ reader_ = new ::testing::NiceMock<MockDataSourceReader>();
// make a new map with a mock reader.
- map_ = new ShellMP4Map(reader_);
+ map_ = new MP4Map(reader_);
}
- virtual ~ShellMP4MapTest() {
+ virtual ~MP4MapTest() {
DCHECK(map_->HasOneRef());
map_ = NULL;
@@ -421,7 +421,7 @@
reader_ = NULL;
}
- void ResetMap() { map_ = new ShellMP4Map(reader_); }
+ void ResetMap() { map_ = new MP4Map(reader_); }
void CreateTestSampleTable(unsigned int seed, int num_of_samples,
int min_sample_size, int max_sample_size,
@@ -454,21 +454,21 @@
}
// ==== Test Fixture Members
- scoped_refptr<ShellMP4Map> map_;
- scoped_refptr<MockShellDataSourceReader> reader_;
+ scoped_refptr<MP4Map> map_;
+ scoped_refptr<MockDataSourceReader> reader_;
std::unique_ptr<SampleTable> sample_table_;
};
// ==== SetAtom() Tests ========================================================
/*
-TEST_F(ShellMP4MapTest, SetAtomWithZeroDefaultSize) {
+TEST_F(MP4MapTest, SetAtomWithZeroDefaultSize) {
// SetAtom() should fail with a zero default size on an stsc.
NOTIMPLEMENTED();
}
*/
// ==== GetSize() Tests ========================================================
-TEST_F(ShellMP4MapTest, GetSizeWithDefaultSize) {
+TEST_F(MP4MapTest, GetSizeWithDefaultSize) {
CreateTestSampleTable(100, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
10, 20);
sample_table_->ClearReadStatistics();
@@ -490,7 +490,7 @@
ASSERT_EQ(sample_table_->read_count(), 0);
}
-TEST_F(ShellMP4MapTest, GetSizeIterationWithHugeCache) {
+TEST_F(MP4MapTest, GetSizeIterationWithHugeCache) {
for (int max_sample_size = 10; max_sample_size < 20; ++max_sample_size) {
CreateTestSampleTable(200 + max_sample_size, 1000, 10, max_sample_size, 5,
10, 5, 10, 10, 20, 10, 20);
@@ -517,7 +517,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetSizeIterationTinyCache) {
+TEST_F(MP4MapTest, GetSizeIterationTinyCache) {
for (int max_sample_size = 10; max_sample_size < 20; ++max_sample_size) {
CreateTestSampleTable(300 + max_sample_size, 1000, 10, max_sample_size, 5,
10, 5, 10, 10, 20, 10, 20);
@@ -543,7 +543,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetSizeRandomAccess) {
+TEST_F(MP4MapTest, GetSizeRandomAccess) {
CreateTestSampleTable(101, 2000, 20, 24, 5, 10, 5, 10, 10, 20, 10, 20);
for (int i = 24; i < 27; ++i) {
ResetMap();
@@ -593,7 +593,7 @@
// ==== GetOffset() Tests ======================================================
-TEST_F(ShellMP4MapTest, GetOffsetIterationHugeCache) {
+TEST_F(MP4MapTest, GetOffsetIterationHugeCache) {
for (int coindex = 0; coindex < 2; ++coindex) {
CreateTestSampleTable(102 + coindex, 1000, 20, 25, 5, 10, 5, 10, 10, 20, 10,
20);
@@ -617,7 +617,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetOffsetIterationTinyCache) {
+TEST_F(MP4MapTest, GetOffsetIterationTinyCache) {
for (int coindex = 0; coindex < 2; ++coindex) {
CreateTestSampleTable(103, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
for (int i = 1; i < 12; ++i) {
@@ -644,7 +644,7 @@
// Random access within cache should just result in correct re-integration
// through the stsc.
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessHugeCache) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessHugeCache) {
for (int coindex = 0; coindex < 2; ++coindex) {
CreateTestSampleTable(104, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
@@ -664,7 +664,7 @@
// Random access across cache boundaries should not break computation of
// offsets.
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessTinyCache) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessTinyCache) {
for (int coindex = 0; coindex < 2; ++coindex) {
CreateTestSampleTable(105, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
@@ -718,7 +718,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetOffsetRandomAccessWithDefaultSize) {
+TEST_F(MP4MapTest, GetOffsetRandomAccessWithDefaultSize) {
for (int coindex = 0; coindex < 2; ++coindex) {
CreateTestSampleTable(106, 300, 20, 20, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
@@ -755,7 +755,7 @@
// ==== GetDuration() Tests ====================================================
-TEST_F(ShellMP4MapTest, GetDurationIteration) {
+TEST_F(MP4MapTest, GetDurationIteration) {
CreateTestSampleTable(107, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 2);
@@ -773,7 +773,7 @@
map_->GetDuration(sample_table_->sample_count(), &failed_duration));
}
-TEST_F(ShellMP4MapTest, GetDurationRandomAccess) {
+TEST_F(MP4MapTest, GetDurationRandomAccess) {
CreateTestSampleTable(108, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 3);
@@ -810,7 +810,7 @@
// ==== GetTimestamp() Tests ===================================================
-TEST_F(ShellMP4MapTest, GetTimestampIterationNoCompositionTime) {
+TEST_F(MP4MapTest, GetTimestampIterationNoCompositionTime) {
CreateTestSampleTable(109, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 7);
@@ -828,7 +828,7 @@
map_->GetTimestamp(sample_table_->sample_count(), &failed_timestamp));
}
-TEST_F(ShellMP4MapTest, GetTimestampRandomAccessNoCompositionTime) {
+TEST_F(MP4MapTest, GetTimestampRandomAccessNoCompositionTime) {
CreateTestSampleTable(110, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 10);
@@ -858,7 +858,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetTimestampIteration) {
+TEST_F(MP4MapTest, GetTimestampIteration) {
CreateTestSampleTable(111, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
for (int i = 1; i < 20; ++i) {
ResetMap();
@@ -879,7 +879,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetTimestampRandomAccess) {
+TEST_F(MP4MapTest, GetTimestampRandomAccess) {
CreateTestSampleTable(112, 300, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
for (int i = 1; i < 20; ++i) {
ResetMap();
@@ -904,7 +904,7 @@
// ==== GetIsKeyframe() Tests ==================================================
// the map should consider every valid sample number a keyframe without an stss
-TEST_F(ShellMP4MapTest, GetIsKeyframeNoKeyframeTable) {
+TEST_F(MP4MapTest, GetIsKeyframeNoKeyframeTable) {
ResetMap();
bool is_keyframe_out = false;
ASSERT_TRUE(map_->GetIsKeyframe(100, &is_keyframe_out));
@@ -921,7 +921,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetIsKeyframeIteration) {
+TEST_F(MP4MapTest, GetIsKeyframeIteration) {
CreateTestSampleTable(113, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
10, 20);
ResetMap();
@@ -936,7 +936,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetIsKeyframeRandomAccess) {
+TEST_F(MP4MapTest, GetIsKeyframeRandomAccess) {
CreateTestSampleTable(114, 1000, 0xb0df00d, 0xb0df00d, 5, 10, 5, 10, 10, 20,
10, 20);
ResetMap();
@@ -1005,7 +1005,7 @@
// every frame should be returned as a keyframe. This tests if our computation
// of timestamps => sample numbers is equivalent to sample numbers => timestamps
-TEST_F(ShellMP4MapTest, GetKeyframeNoKeyframeTableIteration) {
+TEST_F(MP4MapTest, GetKeyframeNoKeyframeTableIteration) {
CreateTestSampleTable(115, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 7);
@@ -1022,7 +1022,7 @@
}
}
-TEST_F(ShellMP4MapTest, GetKeyframeNoKeyframeTableRandomAccess) {
+TEST_F(MP4MapTest, GetKeyframeNoKeyframeTableRandomAccess) {
CreateTestSampleTable(116, 30, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stts, 5);
@@ -1056,7 +1056,7 @@
}
// GetKeyframe is not normally called iteratively, so we test random access
-TEST_F(ShellMP4MapTest, GetKeyframe) {
+TEST_F(MP4MapTest, GetKeyframe) {
CreateTestSampleTable(117, 60, 20, 25, 5, 10, 5, 10, 10, 20, 10, 20);
ResetMap();
SetTestTable(kAtomType_stss, 3);
diff --git a/src/cobalt/media/filters/shell_mp4_parser.cc b/src/cobalt/media/progressive/mp4_parser.cc
similarity index 91%
rename from src/cobalt/media/filters/shell_mp4_parser.cc
rename to src/cobalt/media/progressive/mp4_parser.cc
index 7d9efb1..957395d 100644
--- a/src/cobalt/media/filters/shell_mp4_parser.cc
+++ b/src/cobalt/media/progressive/mp4_parser.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
#include <inttypes.h>
#include <limits>
@@ -67,10 +67,10 @@
static const int kMapTableAtomCacheEntries_ctts = 51543 / kEntrySize_ctts;
// static
-PipelineStatus ShellMP4Parser::Construct(
- scoped_refptr<ShellDataSourceReader> reader,
- const uint8* construction_header, scoped_refptr<ShellParser>* parser,
- const scoped_refptr<MediaLog>& media_log) {
+PipelineStatus MP4Parser::Construct(scoped_refptr<DataSourceReader> reader,
+ const uint8* construction_header,
+ scoped_refptr<ProgressiveParser>* parser,
+ const scoped_refptr<MediaLog>& media_log) {
DCHECK(parser);
DCHECK(media_log);
*parser = NULL;
@@ -90,28 +90,28 @@
}
// construct new mp4 parser
- *parser = new ShellMP4Parser(reader, ftyp_atom_size, media_log);
+ *parser = new MP4Parser(reader, ftyp_atom_size, media_log);
return PIPELINE_OK;
}
-ShellMP4Parser::ShellMP4Parser(scoped_refptr<ShellDataSourceReader> reader,
- uint32 ftyp_atom_size,
- const scoped_refptr<MediaLog>& media_log)
- : ShellAVCParser(reader, media_log),
+MP4Parser::MP4Parser(scoped_refptr<DataSourceReader> reader,
+ uint32 ftyp_atom_size,
+ const scoped_refptr<MediaLog>& media_log)
+ : AVCParser(reader, media_log),
atom_offset_(ftyp_atom_size), // start at next atom, skipping over ftyp
current_trak_is_video_(false),
current_trak_is_audio_(false),
current_trak_time_scale_(0),
video_time_scale_hz_(0),
audio_time_scale_hz_(0),
- audio_map_(new ShellMP4Map(reader)),
- video_map_(new ShellMP4Map(reader)),
+ audio_map_(new MP4Map(reader)),
+ video_map_(new MP4Map(reader)),
audio_sample_(0),
video_sample_(0),
first_audio_hole_ticks_(0),
first_audio_hole_(base::TimeDelta::FromSeconds(0)) {}
-ShellMP4Parser::~ShellMP4Parser() {}
+MP4Parser::~MP4Parser() {}
// For MP4 we traverse the file's atom structure attempting to find the audio
// and video configuration information and the locations in the file of the
@@ -119,7 +119,7 @@
// NALUs in the file. As some of the stbl subatoms can be quite large we cache
// a fixed maximum quantity of each stbl subatom and update the cache only on
// miss.
-bool ShellMP4Parser::ParseConfig() {
+bool MP4Parser::ParseConfig() {
while (!IsConfigComplete() || !audio_map_->IsComplete() ||
!video_map_->IsComplete()) {
if (!ParseNextAtom()) {
@@ -129,7 +129,7 @@
return true;
}
-scoped_refptr<ShellAU> ShellMP4Parser::GetNextAU(DemuxerStream::Type type) {
+scoped_refptr<AvcAccessUnit> MP4Parser::GetNextAU(DemuxerStream::Type type) {
uint32 size = 0;
uint32 duration_ticks = 0;
uint64 timestamp_ticks = 0;
@@ -148,8 +148,8 @@
!audio_map_->GetTimestamp(audio_sample_, ×tamp_ticks)) {
// determine if EOS or error
if (audio_map_->IsEOS(audio_sample_)) {
- return ShellAU::CreateEndOfStreamAU(DemuxerStream::AUDIO,
- audio_track_duration_);
+ return AvcAccessUnit::CreateEndOfStreamAU(DemuxerStream::AUDIO,
+ audio_track_duration_);
} else {
DLOG(ERROR) << "parsed bad audio AU";
return NULL;
@@ -195,8 +195,8 @@
!video_map_->GetTimestamp(video_sample_, ×tamp_ticks) ||
!video_map_->GetIsKeyframe(video_sample_, &is_keyframe)) {
if (video_map_->IsEOS(video_sample_)) {
- return ShellAU::CreateEndOfStreamAU(DemuxerStream::VIDEO,
- video_track_duration_);
+ return AvcAccessUnit::CreateEndOfStreamAU(DemuxerStream::VIDEO,
+ video_track_duration_);
} else {
DLOG(ERROR) << "parsed bad video AU";
return NULL;
@@ -219,13 +219,14 @@
size_t prepend_size = CalculatePrependSize(type, is_keyframe);
if (type == DemuxerStream::AUDIO)
- return ShellAU::CreateAudioAU(offset, size, prepend_size, is_keyframe,
- timestamp, duration, this);
- return ShellAU::CreateVideoAU(offset, size, prepend_size, nal_header_size_,
- is_keyframe, timestamp, duration, this);
+ return AvcAccessUnit::CreateAudioAU(offset, size, prepend_size, is_keyframe,
+ timestamp, duration, this);
+ return AvcAccessUnit::CreateVideoAU(offset, size, prepend_size,
+ nal_header_size_, is_keyframe, timestamp,
+ duration, this);
}
-bool ShellMP4Parser::SeekTo(base::TimeDelta timestamp) {
+bool MP4Parser::SeekTo(base::TimeDelta timestamp) {
if (audio_time_scale_hz_ == 0 || video_time_scale_hz_ == 0) {
DLOG_IF(ERROR, audio_time_scale_hz_ == 0)
<< "|audio_time_scale_hz_| cannot be 0.";
@@ -272,7 +273,7 @@
// fourCC code | ASCII | four-byte ASCII code we treat as uint32
// extended size | uint64 | optional size field, only here if atom size is 1
// <--- rest of atom body starts here
-bool ShellMP4Parser::ParseNextAtom() {
+bool MP4Parser::ParseNextAtom() {
uint8 atom[kAtomDownload];
int bytes_read = reader_->BlockingRead(atom_offset_, kAtomDownload, atom);
if (bytes_read < kAtomDownload) {
@@ -461,7 +462,7 @@
return atom_parse_success;
}
-bool ShellMP4Parser::ParseMP4_esds(uint64 atom_data_size) {
+bool MP4Parser::ParseMP4_esds(uint64 atom_data_size) {
if (atom_data_size < kFullBoxHeaderAndFlagSize) {
DLOG(WARNING) << base::StringPrintf(
"esds box should at least be %d bytes but now it is %" PRId64 " bytes",
@@ -501,7 +502,7 @@
return false;
}
-bool ShellMP4Parser::ParseMP4_hdlr(uint64 atom_data_size, uint8* hdlr) {
+bool MP4Parser::ParseMP4_hdlr(uint64 atom_data_size, uint8* hdlr) {
// ensure we're downloading enough of the hdlr to parse
DCHECK_LE(kDesiredBytes_hdlr + 16, kAtomDownload);
// sanity-check for minimum size
@@ -534,7 +535,7 @@
return true;
}
-bool ShellMP4Parser::ParseMP4_mdhd(uint64 atom_data_size, uint8* mdhd) {
+bool MP4Parser::ParseMP4_mdhd(uint64 atom_data_size, uint8* mdhd) {
DCHECK_LE(kDesiredBytes_mdhd + 16, kAtomDownload);
if (atom_data_size < kDesiredBytes_mdhd) {
DLOG(WARNING) << base::StringPrintf("bad size %" PRId64 " on mdhd",
@@ -577,7 +578,7 @@
return true;
}
-bool ShellMP4Parser::ParseMP4_mp4a(uint64 atom_data_size, uint8* mp4a) {
+bool MP4Parser::ParseMP4_mp4a(uint64 atom_data_size, uint8* mp4a) {
DCHECK_LE(kDesiredBytes_mp4a + 16, kAtomDownload);
// we only need the first two bytes of the header, which details the version
// number of this atom, which tells us the size of the rest of the header,
@@ -619,7 +620,7 @@
// 12 | time scale | 4
// 16 | duration: | 4
//
-bool ShellMP4Parser::ParseMP4_mvhd(uint64 atom_data_size, uint8* mvhd) {
+bool MP4Parser::ParseMP4_mvhd(uint64 atom_data_size, uint8* mvhd) {
DCHECK_LE(kDesiredBytes_mvhd + 16, kAtomDownload);
// it should be at least long enough for us to extract the parts we want
if (atom_data_size < kDesiredBytes_mvhd) {
@@ -641,8 +642,7 @@
return true;
}
-base::TimeDelta ShellMP4Parser::TicksToTime(uint64 ticks,
- uint32 time_scale_hz) {
+base::TimeDelta MP4Parser::TicksToTime(uint64 ticks, uint32 time_scale_hz) {
DCHECK_NE(time_scale_hz, 0);
if (time_scale_hz == 0) {
@@ -652,7 +652,7 @@
time_scale_hz);
}
-uint64 ShellMP4Parser::TimeToTicks(base::TimeDelta time, uint32 time_scale_hz) {
+uint64 MP4Parser::TimeToTicks(base::TimeDelta time, uint32 time_scale_hz) {
DCHECK_NE(time_scale_hz, 0);
if (time_scale_hz == 0) {
diff --git a/src/cobalt/media/filters/shell_mp4_parser.h b/src/cobalt/media/progressive/mp4_parser.h
similarity index 82%
rename from src/cobalt/media/filters/shell_mp4_parser.h
rename to src/cobalt/media/progressive/mp4_parser.h
index 6a16b9f..6d0d617 100644
--- a/src/cobalt/media/filters/shell_mp4_parser.h
+++ b/src/cobalt/media/progressive/mp4_parser.h
@@ -12,12 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
#include "cobalt/media/base/media_log.h"
-#include "cobalt/media/filters/shell_avc_parser.h"
-#include "cobalt/media/filters/shell_mp4_map.h"
+#include "cobalt/media/progressive/avc_parser.h"
+#include "cobalt/media/progressive/mp4_map.h"
namespace cobalt {
namespace media {
@@ -27,7 +27,7 @@
// second download (typically), but no larger. This is currently set at 16
// bytes for the 8 byte header + optional 8 byte size extension plus 20 bytes
// for the needed values within an mvhd header. We leave this is the header so
-// that ShellMP4Map can re-use,
+// that MP4Map can re-use,
static const int kAtomDownload = 36;
// mp4 atom fourCC codes as big-endian unsigned ints
@@ -59,24 +59,23 @@
static const uint32 kAtomType_vmhd = 0x766d6864; // skip whole atom
// TODO: mp4v!!
-class ShellMP4Parser : public ShellAVCParser {
+class MP4Parser : public AVCParser {
public:
// Attempts to make sense of the provided bytes of the top of a file as an
// flv, and if it does make sense returns PIPELINE_OK and |*parser| contains a
- // ShellMP4Parser initialized with some basic state. If it doesn't make sense
+ // MP4Parser initialized with some basic state. If it doesn't make sense
// this returns an error status and |*parser| contains NULL.
- static PipelineStatus Construct(scoped_refptr<ShellDataSourceReader> reader,
+ static PipelineStatus Construct(scoped_refptr<DataSourceReader> reader,
const uint8* construction_header,
- scoped_refptr<ShellParser>* parser,
+ scoped_refptr<ProgressiveParser>* parser,
const scoped_refptr<MediaLog>& media_log);
- ShellMP4Parser(scoped_refptr<ShellDataSourceReader> reader,
- uint32 ftyp_atom_size,
- const scoped_refptr<MediaLog>& media_log);
- ~ShellMP4Parser() override;
+ MP4Parser(scoped_refptr<DataSourceReader> reader, uint32 ftyp_atom_size,
+ const scoped_refptr<MediaLog>& media_log);
+ ~MP4Parser() override;
- // === ShellParser implementation
+ // === ProgressiveParser implementation
bool ParseConfig() override;
- scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) override;
+ scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) override;
bool SeekTo(base::TimeDelta timestamp) override;
private:
@@ -99,8 +98,8 @@
uint32 audio_time_scale_hz_;
base::TimeDelta audio_track_duration_;
base::TimeDelta video_track_duration_;
- scoped_refptr<ShellMP4Map> audio_map_;
- scoped_refptr<ShellMP4Map> video_map_;
+ scoped_refptr<MP4Map> audio_map_;
+ scoped_refptr<MP4Map> video_map_;
uint32 audio_sample_;
uint32 video_sample_;
// for keeping buffers continuous across time scales
@@ -111,4 +110,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_MP4_PARSER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_MP4_PARSER_H_
diff --git a/src/cobalt/media/filters/shell_demuxer.cc b/src/cobalt/media/progressive/progressive_demuxer.cc
similarity index 77%
rename from src/cobalt/media/filters/shell_demuxer.cc
rename to src/cobalt/media/progressive/progressive_demuxer.cc
index 3d0a8cf..a396825 100644
--- a/src/cobalt/media/filters/shell_demuxer.cc
+++ b/src/cobalt/media/progressive/progressive_demuxer.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_demuxer.h"
+#include "cobalt/media/progressive/progressive_demuxer.h"
#include <inttypes.h>
@@ -33,14 +33,16 @@
namespace cobalt {
namespace media {
-ShellDemuxerStream::ShellDemuxerStream(ShellDemuxer* demuxer, Type type)
+ProgressiveDemuxerStream::ProgressiveDemuxerStream(ProgressiveDemuxer* demuxer,
+ Type type)
: demuxer_(demuxer), type_(type) {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::ShellDemuxerStream()");
+ TRACE_EVENT0("media_stack",
+ "ProgressiveDemuxerStream::ProgressiveDemuxerStream()");
DCHECK(demuxer_);
}
-void ShellDemuxerStream::Read(const ReadCB& read_cb) {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read()");
+void ProgressiveDemuxerStream::Read(const ReadCB& read_cb) {
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read()");
DCHECK(!read_cb.is_null());
base::AutoLock auto_lock(lock_);
@@ -48,7 +50,7 @@
// Don't accept any additional reads if we've been told to stop.
// The demuxer_ may have been destroyed in the pipleine thread.
if (stopped_) {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() EOS sent.");
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read() EOS sent.");
read_cb.Run(DemuxerStream::kOk,
scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
return;
@@ -61,7 +63,7 @@
// Send the oldest buffer back.
scoped_refptr<DecoderBuffer> buffer = buffer_queue_.front();
if (buffer->end_of_stream()) {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() EOS sent.");
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Read() EOS sent.");
} else {
// Do not pop EOS buffers, so that subsequent read requests also get EOS
total_buffer_size_ -= buffer->data_size();
@@ -70,31 +72,33 @@
}
read_cb.Run(DemuxerStream::kOk, buffer);
} else {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Read() request queued.");
+ TRACE_EVENT0("media_stack",
+ "ProgressiveDemuxerStream::Read() request queued.");
read_queue_.push_back(read_cb);
}
}
-AudioDecoderConfig ShellDemuxerStream::audio_decoder_config() {
+AudioDecoderConfig ProgressiveDemuxerStream::audio_decoder_config() {
return demuxer_->AudioConfig();
}
-VideoDecoderConfig ShellDemuxerStream::video_decoder_config() {
+VideoDecoderConfig ProgressiveDemuxerStream::video_decoder_config() {
return demuxer_->VideoConfig();
}
-Ranges<base::TimeDelta> ShellDemuxerStream::GetBufferedRanges() {
+Ranges<base::TimeDelta> ProgressiveDemuxerStream::GetBufferedRanges() {
base::AutoLock auto_lock(lock_);
return buffered_ranges_;
}
-DemuxerStream::Type ShellDemuxerStream::type() const { return type_; }
+DemuxerStream::Type ProgressiveDemuxerStream::type() const { return type_; }
-void ShellDemuxerStream::EnableBitstreamConverter() { NOTIMPLEMENTED(); }
+void ProgressiveDemuxerStream::EnableBitstreamConverter() { NOTIMPLEMENTED(); }
-void ShellDemuxerStream::EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer) {
+void ProgressiveDemuxerStream::EnqueueBuffer(
+ scoped_refptr<DecoderBuffer> buffer) {
TRACE_EVENT1(
- "media_stack", "ShellDemuxerStream::EnqueueBuffer()", "timestamp",
+ "media_stack", "ProgressiveDemuxerStream::EnqueueBuffer()", "timestamp",
buffer->end_of_stream() ? -1 : buffer->timestamp().InMicroseconds());
base::AutoLock auto_lock(lock_);
if (stopped_) {
@@ -107,7 +111,7 @@
if (buffer->end_of_stream()) {
TRACE_EVENT0("media_stack",
- "ShellDemuxerStream::EnqueueBuffer() EOS received.");
+ "ProgressiveDemuxerStream::EnqueueBuffer() EOS received.");
} else if (buffer->timestamp() != kNoTimestamp) {
if (last_buffer_timestamp_ != kNoTimestamp &&
last_buffer_timestamp_ < buffer->timestamp()) {
@@ -135,23 +139,23 @@
}
}
-base::TimeDelta ShellDemuxerStream::GetLastBufferTimestamp() const {
+base::TimeDelta ProgressiveDemuxerStream::GetLastBufferTimestamp() const {
base::AutoLock auto_lock(lock_);
return last_buffer_timestamp_;
}
-size_t ShellDemuxerStream::GetTotalBufferSize() const {
+size_t ProgressiveDemuxerStream::GetTotalBufferSize() const {
base::AutoLock auto_lock(lock_);
return total_buffer_size_;
}
-size_t ShellDemuxerStream::GetTotalBufferCount() const {
+size_t ProgressiveDemuxerStream::GetTotalBufferCount() const {
base::AutoLock auto_lock(lock_);
return total_buffer_count_;
}
-void ShellDemuxerStream::FlushBuffers() {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::FlushBuffers()");
+void ProgressiveDemuxerStream::FlushBuffers() {
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::FlushBuffers()");
base::AutoLock auto_lock(lock_);
// TODO: Investigate if the following warning is valid.
DLOG_IF(WARNING, !read_queue_.empty()) << "Read requests should be empty";
@@ -161,8 +165,8 @@
last_buffer_timestamp_ = kNoTimestamp;
}
-void ShellDemuxerStream::Stop() {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Stop()");
+void ProgressiveDemuxerStream::Stop() {
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Stop()");
DCHECK(demuxer_->MessageLoopBelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
buffer_queue_.clear();
@@ -172,7 +176,7 @@
// fulfill any pending callbacks with EOS buffers set to end timestamp
for (ReadQueue::iterator it = read_queue_.begin(); it != read_queue_.end();
++it) {
- TRACE_EVENT0("media_stack", "ShellDemuxerStream::Stop() EOS sent.");
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxerStream::Stop() EOS sent.");
it->Run(DemuxerStream::kOk,
scoped_refptr<DecoderBuffer>(DecoderBuffer::CreateEOSBuffer()));
}
@@ -181,16 +185,16 @@
}
//
-// ShellDemuxer
+// ProgressiveDemuxer
//
-ShellDemuxer::ShellDemuxer(
+ProgressiveDemuxer::ProgressiveDemuxer(
const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
DecoderBuffer::Allocator* buffer_allocator, DataSource* data_source,
const scoped_refptr<MediaLog>& media_log)
: message_loop_(message_loop),
buffer_allocator_(buffer_allocator),
host_(NULL),
- blocking_thread_("ShellDemuxerBlk"),
+ blocking_thread_("ProgDemuxerBlk"),
data_source_(data_source),
media_log_(media_log),
stopped_(false),
@@ -201,20 +205,20 @@
DCHECK(buffer_allocator_);
DCHECK(data_source_);
DCHECK(media_log_);
- reader_ = new ShellDataSourceReader();
+ reader_ = new DataSourceReader();
reader_->SetDataSource(data_source_);
}
-ShellDemuxer::~ShellDemuxer() {
+ProgressiveDemuxer::~ProgressiveDemuxer() {
// Explicitly stop |blocking_thread_| to ensure that it stops before the
// destructiing of any other members.
blocking_thread_.Stop();
}
-void ShellDemuxer::Initialize(DemuxerHost* host,
- const PipelineStatusCB& status_cb,
- bool enable_text_tracks) {
- TRACE_EVENT0("media_stack", "ShellDemuxer::Initialize()");
+void ProgressiveDemuxer::Initialize(DemuxerHost* host,
+ const PipelineStatusCB& status_cb,
+ bool enable_text_tracks) {
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxer::Initialize()");
DCHECK(!enable_text_tracks);
DCHECK(MessageLoopBelongsToCurrentThread());
DCHECK(reader_);
@@ -226,9 +230,9 @@
// create audio and video demuxer stream objects
audio_demuxer_stream_.reset(
- new ShellDemuxerStream(this, DemuxerStream::AUDIO));
+ new ProgressiveDemuxerStream(this, DemuxerStream::AUDIO));
video_demuxer_stream_.reset(
- new ShellDemuxerStream(this, DemuxerStream::VIDEO));
+ new ProgressiveDemuxerStream(this, DemuxerStream::VIDEO));
// start the blocking thread and have it download and parse the media config
if (!blocking_thread_.Start()) {
@@ -237,16 +241,18 @@
}
blocking_thread_.task_runner()->PostTask(
- FROM_HERE, base::Bind(&ShellDemuxer::ParseConfigBlocking,
+ FROM_HERE, base::Bind(&ProgressiveDemuxer::ParseConfigBlocking,
base::Unretained(this), status_cb));
}
-void ShellDemuxer::ParseConfigBlocking(const PipelineStatusCB& status_cb) {
+void ProgressiveDemuxer::ParseConfigBlocking(
+ const PipelineStatusCB& status_cb) {
DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
DCHECK(!parser_);
// construct stream parser with error callback
- PipelineStatus status = ShellParser::Construct(reader_, &parser_, media_log_);
+ PipelineStatus status =
+ ProgressiveParser::Construct(reader_, &parser_, media_log_);
// if we can't construct a parser for this stream it's a fatal error, return
// false so ParseConfigDone will notify the caller to Initialize() via
// status_cb.
@@ -286,8 +292,8 @@
ParseConfigDone(status_cb, PIPELINE_OK);
}
-void ShellDemuxer::ParseConfigDone(const PipelineStatusCB& status_cb,
- PipelineStatus status) {
+void ProgressiveDemuxer::ParseConfigDone(const PipelineStatusCB& status_cb,
+ PipelineStatus status) {
DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
if (HasStopCalled()) {
@@ -306,18 +312,18 @@
status_cb.Run(PIPELINE_OK);
}
-void ShellDemuxer::Request(DemuxerStream::Type type) {
+void ProgressiveDemuxer::Request(DemuxerStream::Type type) {
if (!blocking_thread_.task_runner()->BelongsToCurrentThread()) {
blocking_thread_.task_runner()->PostTask(
FROM_HERE,
- base::Bind(&ShellDemuxer::Request, base::Unretained(this), type));
+ base::Bind(&ProgressiveDemuxer::Request, base::Unretained(this), type));
return;
}
DCHECK(!requested_au_) << "overlapping requests not supported!";
flushing_ = false;
// Ask parser for next AU
- scoped_refptr<ShellAU> au = parser_->GetNextAU(type);
+ scoped_refptr<AvcAccessUnit> au = parser_->GetNextAU(type);
// fatal parsing error returns NULL or malformed AU
if (!au || !au->IsValid()) {
if (!HasStopCalled()) {
@@ -332,12 +338,12 @@
const char* ALLOW_UNUSED_TYPE event_type =
type == DemuxerStream::AUDIO ? "audio" : "video";
- TRACE_EVENT2("media_stack", "ShellDemuxer::RequestTask()", "type", event_type,
- "timestamp", au->GetTimestamp().InMicroseconds());
+ TRACE_EVENT2("media_stack", "ProgressiveDemuxer::RequestTask()", "type",
+ event_type, "timestamp", au->GetTimestamp().InMicroseconds());
// don't issue allocation requests for EOS AUs
if (au->IsEndOfStream()) {
- TRACE_EVENT0("media_stack", "ShellDemuxer::RequestTask() EOS sent");
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxer::RequestTask() EOS sent");
// enqueue EOS buffer with correct stream
scoped_refptr<DecoderBuffer> eos_buffer = DecoderBuffer::CreateEOSBuffer();
if (type == DemuxerStream::AUDIO) {
@@ -357,7 +363,7 @@
AllocateBuffer();
}
-void ShellDemuxer::AllocateBuffer() {
+void ProgressiveDemuxer::AllocateBuffer() {
DCHECK(requested_au_);
if (HasStopCalled()) {
@@ -385,7 +391,8 @@
const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(100);
blocking_thread_.message_loop()->task_runner()->PostDelayedTask(
FROM_HERE,
- base::Bind(&ShellDemuxer::AllocateBuffer, base::Unretained(this)),
+ base::Bind(&ProgressiveDemuxer::AllocateBuffer,
+ base::Unretained(this)),
kDelay);
return;
}
@@ -404,13 +411,14 @@
const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(100);
blocking_thread_.message_loop()->task_runner()->PostDelayedTask(
FROM_HERE,
- base::Bind(&ShellDemuxer::AllocateBuffer, base::Unretained(this)),
+ base::Bind(&ProgressiveDemuxer::AllocateBuffer,
+ base::Unretained(this)),
kDelay);
}
}
}
-void ShellDemuxer::Download(scoped_refptr<DecoderBuffer> buffer) {
+void ProgressiveDemuxer::Download(scoped_refptr<DecoderBuffer> buffer) {
DCHECK(blocking_thread_.task_runner()->BelongsToCurrentThread());
// We need a requested_au_ or to have canceled this request and
// are buffering to a new location for this to make sense
@@ -418,8 +426,9 @@
const char* ALLOW_UNUSED_TYPE event_type =
requested_au_->GetType() == DemuxerStream::AUDIO ? "audio" : "video";
- TRACE_EVENT2("media_stack", "ShellDemuxer::Download()", "type", event_type,
- "timestamp", requested_au_->GetTimestamp().InMicroseconds());
+ TRACE_EVENT2("media_stack", "ProgressiveDemuxer::Download()", "type",
+ event_type, "timestamp",
+ requested_au_->GetTimestamp().InMicroseconds());
// do nothing if stopped
if (HasStopCalled()) {
DLOG(INFO) << "aborting download task, stopped";
@@ -466,11 +475,11 @@
host_->OnBufferedTimeRangesChanged(buffered);
blocking_thread_.task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&ShellDemuxer::IssueNextRequest, base::Unretained(this)));
+ FROM_HERE, base::Bind(&ProgressiveDemuxer::IssueNextRequest,
+ base::Unretained(this)));
}
-void ShellDemuxer::IssueNextRequest() {
+void ProgressiveDemuxer::IssueNextRequest() {
DCHECK(!requested_au_);
// if we're stopped don't download anymore
if (HasStopCalled()) {
@@ -519,10 +528,10 @@
// running in a tight loop and seek or stop request has no chance to kick in.
blocking_thread_.task_runner()->PostTask(
FROM_HERE,
- base::Bind(&ShellDemuxer::Request, base::Unretained(this), type));
+ base::Bind(&ProgressiveDemuxer::Request, base::Unretained(this), type));
}
-void ShellDemuxer::Stop() {
+void ProgressiveDemuxer::Stop() {
DCHECK(MessageLoopBelongsToCurrentThread());
// set our internal stop flag, to not treat read failures as
// errors anymore but as a natural part of stopping
@@ -535,8 +544,8 @@
reader_->Stop();
}
-void ShellDemuxer::DataSourceStopped(const base::Closure& callback) {
- TRACE_EVENT0("media_stack", "ShellDemuxer::DataSourceStopped()");
+void ProgressiveDemuxer::DataSourceStopped(const base::Closure& callback) {
+ TRACE_EVENT0("media_stack", "ProgressiveDemuxer::DataSourceStopped()");
DCHECK(MessageLoopBelongsToCurrentThread());
// stop the download thread
blocking_thread_.Stop();
@@ -548,20 +557,23 @@
callback.Run();
}
-bool ShellDemuxer::HasStopCalled() {
+bool ProgressiveDemuxer::HasStopCalled() {
base::AutoLock auto_lock(lock_for_stopped_);
return stopped_;
}
-void ShellDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
+void ProgressiveDemuxer::Seek(base::TimeDelta time,
+ const PipelineStatusCB& cb) {
blocking_thread_.message_loop()->task_runner()->PostTask(
- FROM_HERE, base::Bind(&ShellDemuxer::SeekTask, base::Unretained(this),
- time, BindToCurrentLoop(cb)));
+ FROM_HERE,
+ base::Bind(&ProgressiveDemuxer::SeekTask, base::Unretained(this), time,
+ BindToCurrentLoop(cb)));
}
// runs on blocking thread
-void ShellDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) {
- TRACE_EVENT1("media_stack", "ShellDemuxer::SeekTask()", "timestamp",
+void ProgressiveDemuxer::SeekTask(base::TimeDelta time,
+ const PipelineStatusCB& cb) {
+ TRACE_EVENT1("media_stack", "ProgressiveDemuxer::SeekTask()", "timestamp",
time.InMicroseconds());
DLOG(INFO) << base::StringPrintf("seek to: %" PRId64 " ms",
time.InMilliseconds());
@@ -586,7 +598,7 @@
}
}
-DemuxerStream* ShellDemuxer::GetStream(media::DemuxerStream::Type type) {
+DemuxerStream* ProgressiveDemuxer::GetStream(media::DemuxerStream::Type type) {
if (type == DemuxerStream::AUDIO) {
return audio_demuxer_stream_.get();
} else if (type == DemuxerStream::VIDEO) {
@@ -597,20 +609,20 @@
return NULL;
}
-base::TimeDelta ShellDemuxer::GetStartTime() const {
+base::TimeDelta ProgressiveDemuxer::GetStartTime() const {
// we always assume a start time of 0
return base::TimeDelta();
}
-const AudioDecoderConfig& ShellDemuxer::AudioConfig() {
+const AudioDecoderConfig& ProgressiveDemuxer::AudioConfig() {
return parser_->AudioConfig();
}
-const VideoDecoderConfig& ShellDemuxer::VideoConfig() {
+const VideoDecoderConfig& ProgressiveDemuxer::VideoConfig() {
return parser_->VideoConfig();
}
-bool ShellDemuxer::MessageLoopBelongsToCurrentThread() const {
+bool ProgressiveDemuxer::MessageLoopBelongsToCurrentThread() const {
return message_loop_->BelongsToCurrentThread();
}
diff --git a/src/cobalt/media/filters/shell_demuxer.h b/src/cobalt/media/progressive/progressive_demuxer.h
similarity index 81%
rename from src/cobalt/media/filters/shell_demuxer.h
rename to src/cobalt/media/progressive/progressive_demuxer.h
index 38f3dde..6ecb323 100644
--- a/src/cobalt/media/filters/shell_demuxer.h
+++ b/src/cobalt/media/progressive/progressive_demuxer.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
+#define COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
#include <deque>
#include <memory>
@@ -28,17 +28,17 @@
#include "cobalt/media/base/demuxer_stream.h"
#include "cobalt/media/base/media_log.h"
#include "cobalt/media/base/ranges.h"
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
namespace cobalt {
namespace media {
class DecoderBuffer;
-class ShellDemuxer;
+class ProgressiveDemuxer;
-class ShellDemuxerStream : public DemuxerStream {
+class ProgressiveDemuxerStream : public DemuxerStream {
public:
- ShellDemuxerStream(ShellDemuxer* demuxer, Type type);
+ ProgressiveDemuxerStream(ProgressiveDemuxer* demuxer, Type type);
// DemuxerStream implementation
void Read(const ReadCB& read_cb) override;
@@ -56,7 +56,7 @@
NOTREACHED();
}
- // Functions used by ShellDemuxer
+ // Functions used by ProgressiveDemuxer
Ranges<base::TimeDelta> GetBufferedRanges();
void EnqueueBuffer(scoped_refptr<DecoderBuffer> buffer);
void FlushBuffers();
@@ -72,7 +72,7 @@
void RebuildEnqueuedRanges_Locked();
// non-owning pointer to avoid circular reference
- ShellDemuxer* demuxer_;
+ ProgressiveDemuxer* demuxer_;
Type type_;
// Used to protect everything below.
@@ -97,19 +97,19 @@
size_t total_buffer_size_ = 0;
size_t total_buffer_count_ = 0;
- DISALLOW_COPY_AND_ASSIGN(ShellDemuxerStream);
+ DISALLOW_COPY_AND_ASSIGN(ProgressiveDemuxerStream);
};
-class MEDIA_EXPORT ShellDemuxer : public Demuxer {
+class MEDIA_EXPORT ProgressiveDemuxer : public Demuxer {
public:
- ShellDemuxer(const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
- DecoderBuffer::Allocator* buffer_allocator,
- DataSource* data_source,
- const scoped_refptr<MediaLog>& media_log);
- ~ShellDemuxer() override;
+ ProgressiveDemuxer(
+ const scoped_refptr<base::SingleThreadTaskRunner>& message_loop,
+ DecoderBuffer::Allocator* buffer_allocator, DataSource* data_source,
+ const scoped_refptr<MediaLog>& media_log);
+ ~ProgressiveDemuxer() override;
// Demuxer implementation.
- std::string GetDisplayName() const override { return "ShellDemuxer"; }
+ std::string GetDisplayName() const override { return "ProgressiveDemuxer"; }
void Initialize(DemuxerHost* host, const PipelineStatusCB& status_cb,
bool enable_text_tracks) override;
void AbortPendingReads() override {}
@@ -140,12 +140,12 @@
// in to it, and enqueue the data in the appropriate demuxer stream.
void Request(DemuxerStream::Type type);
- // The DemuxerStream objects ask their parent ShellDemuxer stream class
+ // The DemuxerStream objects ask their parent ProgressiveDemuxer stream class
// for these configuration data rather than duplicating in the child classes
const AudioDecoderConfig& AudioConfig();
const VideoDecoderConfig& VideoConfig();
- // Provide access to ShellDemuxerStream.
+ // Provide access to ProgressiveDemuxerStream.
bool MessageLoopBelongsToCurrentThread() const;
private:
@@ -170,17 +170,17 @@
base::Thread blocking_thread_;
DataSource* data_source_;
scoped_refptr<MediaLog> media_log_;
- scoped_refptr<ShellDataSourceReader> reader_;
+ scoped_refptr<DataSourceReader> reader_;
base::Lock lock_for_stopped_;
bool stopped_;
bool flushing_;
- std::unique_ptr<ShellDemuxerStream> audio_demuxer_stream_;
- std::unique_ptr<ShellDemuxerStream> video_demuxer_stream_;
- scoped_refptr<ShellParser> parser_;
+ std::unique_ptr<ProgressiveDemuxerStream> audio_demuxer_stream_;
+ std::unique_ptr<ProgressiveDemuxerStream> video_demuxer_stream_;
+ scoped_refptr<ProgressiveParser> parser_;
- scoped_refptr<ShellAU> requested_au_;
+ scoped_refptr<AvcAccessUnit> requested_au_;
bool audio_reached_eos_;
bool video_reached_eos_;
};
@@ -188,4 +188,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_DEMUXER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_PROGRESSIVE_DEMUXER_H_
diff --git a/src/cobalt/media/filters/shell_parser.cc b/src/cobalt/media/progressive/progressive_parser.cc
similarity index 71%
rename from src/cobalt/media/filters/shell_parser.cc
rename to src/cobalt/media/progressive/progressive_parser.cc
index 3ff978e..f9ffb84 100644
--- a/src/cobalt/media/filters/shell_parser.cc
+++ b/src/cobalt/media/progressive/progressive_parser.cc
@@ -12,24 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_parser.h"
+#include "cobalt/media/progressive/progressive_parser.h"
#include "base/logging.h"
#include "cobalt/media/base/timestamp_constants.h"
-#include "cobalt/media/filters/shell_mp4_parser.h"
+#include "cobalt/media/progressive/mp4_parser.h"
namespace cobalt {
namespace media {
-// ==== ShellParser ============================================================
+// ==== ProgressiveParser
+// ============================================================
// how many bytes to download of the file to determine type?
-const int ShellParser::kInitialHeaderSize = 9;
+const int ProgressiveParser::kInitialHeaderSize = 9;
// static
-PipelineStatus ShellParser::Construct(
- scoped_refptr<ShellDataSourceReader> reader,
- scoped_refptr<ShellParser>* parser,
+PipelineStatus ProgressiveParser::Construct(
+ scoped_refptr<DataSourceReader> reader,
+ scoped_refptr<ProgressiveParser>* parser,
const scoped_refptr<MediaLog>& media_log) {
DCHECK(parser);
DCHECK(media_log);
@@ -44,15 +45,15 @@
}
// attempt to construct mp4 parser from this header
- return ShellMP4Parser::Construct(reader, header, parser, media_log);
+ return MP4Parser::Construct(reader, header, parser, media_log);
}
-ShellParser::ShellParser(scoped_refptr<ShellDataSourceReader> reader)
+ProgressiveParser::ProgressiveParser(scoped_refptr<DataSourceReader> reader)
: reader_(reader), duration_(kInfiniteDuration), bits_per_second_(0) {}
-ShellParser::~ShellParser() {}
+ProgressiveParser::~ProgressiveParser() {}
-bool ShellParser::IsConfigComplete() {
+bool ProgressiveParser::IsConfigComplete() {
return video_config_.IsValidConfig() && audio_config_.IsValidConfig() &&
duration_ != kInfiniteDuration;
}
diff --git a/src/cobalt/media/filters/shell_parser.h b/src/cobalt/media/progressive/progressive_parser.h
similarity index 77%
rename from src/cobalt/media/filters/shell_parser.h
rename to src/cobalt/media/progressive/progressive_parser.h
index 119a20c..a0e6f41 100644
--- a/src/cobalt/media/filters/shell_parser.h
+++ b/src/cobalt/media/progressive/progressive_parser.h
@@ -12,31 +12,31 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
-#define COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
-
+#ifndef COBALT_MEDIA_PROGRESSIVE_PARSER_H_
+#define COBALT_MEDIA_PROGRESSIVE_PARSER_H_
#include "base/memory/ref_counted.h"
#include "cobalt/media/base/audio_decoder_config.h"
#include "cobalt/media/base/demuxer_stream.h"
#include "cobalt/media/base/media_log.h"
#include "cobalt/media/base/pipeline.h"
-#include "cobalt/media/base/shell_data_source_reader.h"
#include "cobalt/media/base/video_decoder_config.h"
-#include "cobalt/media/filters/shell_au.h"
+#include "cobalt/media/progressive/avc_access_unit.h"
+#include "cobalt/media/progressive/data_source_reader.h"
namespace cobalt {
namespace media {
-// abstract base class to define a stream parser interface used by ShellDemuxer.
-class ShellParser : public base::RefCountedThreadSafe<ShellParser> {
+// abstract base class to define a stream parser interface used by
+// ProgressiveDemuxer.
+class ProgressiveParser : public base::RefCountedThreadSafe<ProgressiveParser> {
public:
static const int kInitialHeaderSize;
// Determine stream type, construct appropriate parser object, and returns
// PIPELINE_OK on success or error code.
- static PipelineStatus Construct(scoped_refptr<ShellDataSourceReader> reader,
- scoped_refptr<ShellParser>* parser,
+ static PipelineStatus Construct(scoped_refptr<DataSourceReader> reader,
+ scoped_refptr<ProgressiveParser>* parser,
const scoped_refptr<MediaLog>& media_log);
- explicit ShellParser(scoped_refptr<ShellDataSourceReader> reader);
+ explicit ProgressiveParser(scoped_refptr<DataSourceReader> reader);
// Seek through the file looking for audio and video configuration info,
// saving as much config state as is possible. Should try to be fast but this
@@ -47,10 +47,10 @@
// downloding and decoding the next access unit in the stream, or NULL on
// fatal error. On success this advances the respective audio or video cursor
// to the next AU.
- virtual scoped_refptr<ShellAU> GetNextAU(DemuxerStream::Type type) = 0;
+ virtual scoped_refptr<AvcAccessUnit> GetNextAU(DemuxerStream::Type type) = 0;
// Write the appropriate prepend header for the supplied au into the supplied
// buffer. Return false on error.
- virtual bool Prepend(scoped_refptr<ShellAU> au,
+ virtual bool Prepend(scoped_refptr<AvcAccessUnit> au,
scoped_refptr<DecoderBuffer> buffer) = 0;
// Advance internal state to provided timestamp. Return false on error.
virtual bool SeekTo(base::TimeDelta timestamp) = 0;
@@ -68,9 +68,9 @@
protected:
// only allow RefCountedThreadSafe to delete us
- friend class base::RefCountedThreadSafe<ShellParser>;
- virtual ~ShellParser();
- scoped_refptr<ShellDataSourceReader> reader_;
+ friend class base::RefCountedThreadSafe<ProgressiveParser>;
+ virtual ~ProgressiveParser();
+ scoped_refptr<DataSourceReader> reader_;
AudioDecoderConfig audio_config_;
VideoDecoderConfig video_config_;
base::TimeDelta duration_;
@@ -80,4 +80,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_PARSER_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_PARSER_H_
diff --git a/src/cobalt/media/filters/shell_rbsp_stream.cc b/src/cobalt/media/progressive/rbsp_stream.cc
similarity index 90%
rename from src/cobalt/media/filters/shell_rbsp_stream.cc
rename to src/cobalt/media/progressive/rbsp_stream.cc
index 6a3fb50..ea90f7a 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream.cc
+++ b/src/cobalt/media/progressive/rbsp_stream.cc
@@ -12,15 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_rbsp_stream.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
#include "base/logging.h"
namespace cobalt {
namespace media {
-ShellRBSPStream::ShellRBSPStream(const uint8* nalu_buffer,
- size_t nalu_buffer_size)
+RBSPStream::RBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size)
: nalu_buffer_(nalu_buffer),
nalu_buffer_size_(nalu_buffer_size),
nalu_buffer_byte_offset_(0),
@@ -29,7 +28,7 @@
rbsp_bit_offset_(0) {}
// read unsigned Exp-Golomb coded integer, ISO 14496-10 Section 9.1
-bool ShellRBSPStream::ReadUEV(uint32* uev_out) {
+bool RBSPStream::ReadUEV(uint32* uev_out) {
DCHECK(uev_out);
int leading_zero_bits = -1;
for (uint8 b = 0; b == 0; leading_zero_bits++) {
@@ -52,7 +51,7 @@
}
// read signed Exp-Golomb coded integer, ISO 14496-10 Section 9.1
-bool ShellRBSPStream::ReadSEV(int32* sev_out) {
+bool RBSPStream::ReadSEV(int32* sev_out) {
DCHECK(sev_out);
// we start off by reading an unsigned Exp-Golomb coded number
uint32 uev = 0;
@@ -71,7 +70,7 @@
// read and return up to 32 bits, filling from the right, meaning that
// ReadBits(17) on a stream of all 1s would return 0x01ffff
-bool ShellRBSPStream::ReadBits(size_t bits, uint32* bits_out) {
+bool RBSPStream::ReadBits(size_t bits, uint32* bits_out) {
DCHECK(bits_out);
if (bits > 32) {
return false;
@@ -105,7 +104,7 @@
}
// jump over bytes in the RBSP stream
-bool ShellRBSPStream::SkipBytes(size_t bytes) {
+bool RBSPStream::SkipBytes(size_t bytes) {
for (int i = 0; i < bytes; ++i) {
if (!ConsumeNALUByte()) {
return false;
@@ -115,7 +114,7 @@
}
// jump over bits in the RBSP stream
-bool ShellRBSPStream::SkipBits(size_t bits) {
+bool RBSPStream::SkipBits(size_t bits) {
// skip bytes first
size_t bytes = bits >> 3;
if (bytes > 0) {
@@ -152,7 +151,7 @@
// advance by one byte through the NALU buffer, respecting the encoding of
// 00 00 03 => 00 00. Updates the state of current_nalu_byte_ to the new value.
-bool ShellRBSPStream::ConsumeNALUByte() {
+bool RBSPStream::ConsumeNALUByte() {
if (nalu_buffer_byte_offset_ >= nalu_buffer_size_) {
return false;
}
@@ -174,7 +173,7 @@
// return single bit in the LSb from the RBSP stream. Bits are read from MSb
// to LSb in the stream.
-bool ShellRBSPStream::ReadRBSPBit(uint8* bit_out) {
+bool RBSPStream::ReadRBSPBit(uint8* bit_out) {
DCHECK(bit_out);
// check to see if we need to consume a fresh byte
if (rbsp_bit_offset_ == 0) {
@@ -190,7 +189,7 @@
return true;
}
-bool ShellRBSPStream::ReadRBSPByte(uint8* byte_out) {
+bool RBSPStream::ReadRBSPByte(uint8* byte_out) {
DCHECK(byte_out);
// fast path for byte-aligned access
if (rbsp_bit_offset_ == 0) {
diff --git a/src/cobalt/media/filters/shell_rbsp_stream.h b/src/cobalt/media/progressive/rbsp_stream.h
similarity index 91%
rename from src/cobalt/media/filters/shell_rbsp_stream.h
rename to src/cobalt/media/progressive/rbsp_stream.h
index 5a46499..0c8d8bc 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream.h
+++ b/src/cobalt/media/progressive/rbsp_stream.h
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
-#define COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
+#ifndef COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
+#define COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
#include "base/basictypes.h"
@@ -25,11 +25,11 @@
// that some other atoms are defined. This class takes a non-owning reference
// to a buffer and extract various types from the stream while silently
// consuming the extra encoding bytes and advancing a bit stream pointer.
-class ShellRBSPStream {
+class RBSPStream {
public:
// NON-OWNING pointer to buffer. It is assumed the client will dispose of
// this buffer.
- ShellRBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size);
+ RBSPStream(const uint8* nalu_buffer, size_t nalu_buffer_size);
// all Read/Skip methods return the value by reference and return true
// on success, false on read error/EOB. Once the object has returned
// false the consistency of the data is not guaranteed.
@@ -70,4 +70,4 @@
} // namespace media
} // namespace cobalt
-#endif // COBALT_MEDIA_FILTERS_SHELL_RBSP_STREAM_H_
+#endif // COBALT_MEDIA_PROGRESSIVE_RBSP_STREAM_H_
diff --git a/src/cobalt/media/filters/shell_rbsp_stream_unittest.cc b/src/cobalt/media/progressive/rbsp_stream_unittest.cc
similarity index 89%
rename from src/cobalt/media/filters/shell_rbsp_stream_unittest.cc
rename to src/cobalt/media/progressive/rbsp_stream_unittest.cc
index 472639b..33669ed 100644
--- a/src/cobalt/media/filters/shell_rbsp_stream_unittest.cc
+++ b/src/cobalt/media/progressive/rbsp_stream_unittest.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "cobalt/media/filters/shell_rbsp_stream.h"
+#include "cobalt/media/progressive/rbsp_stream.h"
#include <list>
#include <memory>
@@ -24,11 +24,11 @@
namespace cobalt {
namespace media {
-class ShellRBSPStreamTest : public testing::Test {
+class RBSPStreamTest : public testing::Test {
protected:
- ShellRBSPStreamTest() {}
+ RBSPStreamTest() {}
- virtual ~ShellRBSPStreamTest() {}
+ virtual ~RBSPStreamTest() {}
// Given num encode the value in signed exp-golomb syntax and push
// the value on the provided bitlist
@@ -76,7 +76,7 @@
}
// after building a bitlist in various fun ways call this method to
- // create a buffer on the heap that can be passed to ShellRBSPStream
+ // create a buffer on the heap that can be passed to RBSPStream
// for deserialization.
std::unique_ptr<uint8[]> SerializeToBuffer(const std::list<bool>& bitlist,
bool add_sequence_bytes,
@@ -150,7 +150,7 @@
}
};
-TEST_F(ShellRBSPStreamTest, ReadUEV) {
+TEST_F(RBSPStreamTest, ReadUEV) {
std::list<bool> fibbits;
// encode first 47 Fibonacci numbers
uint32 f_n_minus_2 = 0;
@@ -171,9 +171,9 @@
size_t fib_buffer_no_sequence_size;
std::unique_ptr<uint8[]> fib_buffer_no_sequence =
SerializeToBuffer(fibbits, false, fib_buffer_no_sequence_size);
- ShellRBSPStream fib_stream(fib_buffer.get(), fib_buffer_size);
- ShellRBSPStream fib_stream_no_sequence(fib_buffer_no_sequence.get(),
- fib_buffer_no_sequence_size);
+ RBSPStream fib_stream(fib_buffer.get(), fib_buffer_size);
+ RBSPStream fib_stream_no_sequence(fib_buffer_no_sequence.get(),
+ fib_buffer_no_sequence_size);
// deserialize the same sequence from both buffers
uint32 uev = 0;
uint32 uev_n = 0;
@@ -203,7 +203,7 @@
ASSERT_FALSE(fib_stream_no_sequence.ReadUEV(&uev_n));
}
-TEST_F(ShellRBSPStreamTest, ReadSEV) {
+TEST_F(RBSPStreamTest, ReadSEV) {
std::list<bool> lucasbits;
// encode first 44 Lucas numbers with alternating sign
int32 l_n_minus_2 = 1;
@@ -227,10 +227,9 @@
size_t lucas_deseq_buffer_size = 0;
std::unique_ptr<uint8[]> lucas_deseq_buffer =
SerializeToBuffer(lucasbits, false, lucas_deseq_buffer_size);
- ShellRBSPStream lucas_seq_stream(lucas_seq_buffer.get(),
- lucas_seq_buffer_size);
- ShellRBSPStream lucas_deseq_stream(lucas_deseq_buffer.get(),
- lucas_deseq_buffer_size);
+ RBSPStream lucas_seq_stream(lucas_seq_buffer.get(), lucas_seq_buffer_size);
+ RBSPStream lucas_deseq_stream(lucas_deseq_buffer.get(),
+ lucas_deseq_buffer_size);
l_n_minus_2 = 1;
l_n_minus_1 = 2;
int32 sev = 0;
@@ -286,10 +285,10 @@
// 1111 111+0 (to complete the byte)
0xfe};
-TEST_F(ShellRBSPStreamTest, ReadUEVTooLarge) {
+TEST_F(RBSPStreamTest, ReadUEVTooLarge) {
// construct a stream from the supplied test data
- ShellRBSPStream uev_too_big(kTestRBSPExpGolombTooBig,
- sizeof(kTestRBSPExpGolombTooBig));
+ RBSPStream uev_too_big(kTestRBSPExpGolombTooBig,
+ sizeof(kTestRBSPExpGolombTooBig));
// first call should succeed
uint32 uev = 0;
ASSERT_TRUE(uev_too_big.ReadUEV(&uev));
@@ -301,10 +300,10 @@
ASSERT_FALSE(uev_too_big.ReadUEV(&uev));
}
-TEST_F(ShellRBSPStreamTest, ReadSEVTooLarge) {
+TEST_F(RBSPStreamTest, ReadSEVTooLarge) {
// construct a stream from the supplied test data
- ShellRBSPStream sev_too_big(kTestRBSPExpGolombTooBig,
- sizeof(kTestRBSPExpGolombTooBig));
+ RBSPStream sev_too_big(kTestRBSPExpGolombTooBig,
+ sizeof(kTestRBSPExpGolombTooBig));
// first call should succeed
int32 sev = 0;
ASSERT_TRUE(sev_too_big.ReadSEV(&sev));
@@ -316,7 +315,7 @@
ASSERT_FALSE(sev_too_big.ReadSEV(&sev));
}
-TEST_F(ShellRBSPStreamTest, ReadBit) {
+TEST_F(RBSPStreamTest, ReadBit) {
std::list<bool> padded_ones;
// build a bitfield of 1 padded by n zeros, for n in range[0, 1024]
for (int i = 0; i < 1024; i++) {
@@ -329,12 +328,12 @@
size_t sequence_buff_size = 0;
std::unique_ptr<uint8[]> sequence_buff =
SerializeToBuffer(padded_ones, true, sequence_buff_size);
- ShellRBSPStream seq_stream(sequence_buff.get(), sequence_buff_size);
+ RBSPStream seq_stream(sequence_buff.get(), sequence_buff_size);
size_t desequence_buff_size = 0;
std::unique_ptr<uint8[]> desequence_buff =
SerializeToBuffer(padded_ones, false, desequence_buff_size);
- ShellRBSPStream deseq_stream(desequence_buff.get(), desequence_buff_size);
+ RBSPStream deseq_stream(desequence_buff.get(), desequence_buff_size);
for (std::list<bool>::iterator it = padded_ones.begin();
it != padded_ones.end(); ++it) {
uint8 bit = 0;
@@ -351,7 +350,7 @@
ASSERT_FALSE(deseq_stream.ReadByte(&fail_byte));
}
-TEST_F(ShellRBSPStreamTest, ReadByte) {
+TEST_F(RBSPStreamTest, ReadByte) {
// build a field of 16 x (0xaa byte followed by 0 bit)
std::list<bool> aa_field;
for (int i = 0; i < 16; ++i) {
@@ -364,7 +363,7 @@
size_t aabuff_size = 0;
std::unique_ptr<uint8[]> aabuff =
SerializeToBuffer(aa_field, true, aabuff_size);
- ShellRBSPStream aa_stream(aabuff.get(), aabuff_size);
+ RBSPStream aa_stream(aabuff.get(), aabuff_size);
for (int i = 0; i < 16; ++i) {
uint8 aa = 0;
ASSERT_TRUE(aa_stream.ReadByte(&aa));
@@ -397,11 +396,11 @@
size_t zseqbuff_size = 0;
std::unique_ptr<uint8[]> zseqbuff =
SerializeToBuffer(zero_field, true, zseqbuff_size);
- ShellRBSPStream zseq_stream(zseqbuff.get(), zseqbuff_size);
+ RBSPStream zseq_stream(zseqbuff.get(), zseqbuff_size);
size_t zdseqbuff_size = 0;
std::unique_ptr<uint8[]> zdseqbuff =
SerializeToBuffer(zero_field, false, zdseqbuff_size);
- ShellRBSPStream zdseq_stream(zdseqbuff.get(), zdseqbuff_size);
+ RBSPStream zdseq_stream(zdseqbuff.get(), zdseqbuff_size);
for (int i = 0; i < 24; ++i) {
// read the leading 1 bit
uint8 seq_bit = 0;
@@ -454,7 +453,7 @@
}
}
-TEST_F(ShellRBSPStreamTest, ReadBits) {
+TEST_F(RBSPStreamTest, ReadBits) {
// test the assertion in the ReadBits comment, as it had a bug :)
std::list<bool> seventeen_ones;
for (int i = 0; i < 17; ++i) {
@@ -463,8 +462,8 @@
size_t seventeen_ones_size = 0;
std::unique_ptr<uint8[]> seventeen_ones_buff =
SerializeToBuffer(seventeen_ones, false, seventeen_ones_size);
- ShellRBSPStream seventeen_ones_stream(seventeen_ones_buff.get(),
- seventeen_ones_size);
+ RBSPStream seventeen_ones_stream(seventeen_ones_buff.get(),
+ seventeen_ones_size);
uint32 seventeen_ones_word = 0;
ASSERT_TRUE(seventeen_ones_stream.ReadBits(17, &seventeen_ones_word));
ASSERT_EQ(seventeen_ones_word, 0x0001ffff);
@@ -479,7 +478,7 @@
}
size_t pows_size = 0;
std::unique_ptr<uint8[]> pows_buff = SerializeToBuffer(pows, true, pows_size);
- ShellRBSPStream pows_stream(pows_buff.get(), pows_size);
+ RBSPStream pows_stream(pows_buff.get(), pows_size);
// ReadBits(0) should succeed and not modify the value of the ref output or
// internal bit iterator
uint32 dont_touch = 0xfeedfeed;
@@ -493,7 +492,7 @@
}
}
-TEST_F(ShellRBSPStreamTest, SkipBytes) {
+TEST_F(RBSPStreamTest, SkipBytes) {
// serialize all nine-bit values from zero to 512
std::list<bool> nines;
for (int i = 0; i < 512; ++i) {
@@ -507,8 +506,8 @@
size_t nines_deseq_size = 0;
std::unique_ptr<uint8[]> nines_deseq_buff =
SerializeToBuffer(nines, false, nines_deseq_size);
- ShellRBSPStream nines_stream(nines_buff.get(), nines_size);
- ShellRBSPStream nines_deseq_stream(nines_deseq_buff.get(), nines_deseq_size);
+ RBSPStream nines_stream(nines_buff.get(), nines_size);
+ RBSPStream nines_deseq_stream(nines_deseq_buff.get(), nines_deseq_size);
// iterate through streams, skipping in one and reading in the other, always
// comparing values.
for (int i = 0; i < 512; ++i) {
@@ -547,9 +546,9 @@
size_t run_length_deseq_size = 0;
std::unique_ptr<uint8[]> run_length_deseq_buff =
SerializeToBuffer(run_length, false, run_length_deseq_size);
- ShellRBSPStream run_length_stream(run_length_buff.get(), run_length_size);
- ShellRBSPStream run_length_deseq_stream(run_length_deseq_buff.get(),
- run_length_deseq_size);
+ RBSPStream run_length_stream(run_length_buff.get(), run_length_size);
+ RBSPStream run_length_deseq_stream(run_length_deseq_buff.get(),
+ run_length_deseq_size);
// read first bit, skip first byte from each stream, read next bit
uint8 bit = 0;
ASSERT_TRUE(run_length_stream.ReadBit(&bit));
@@ -593,7 +592,7 @@
ASSERT_FALSE(run_length_deseq_stream.SkipBytes(1));
}
-TEST_F(ShellRBSPStreamTest, SkipBits) {
+TEST_F(RBSPStreamTest, SkipBits) {
std::list<bool> one_ohs;
// encode one 1, followed by one zero, followed by 2 1s, followed by 2 zeros,
// etc
@@ -611,8 +610,8 @@
size_t skip_ohs_size = 0;
std::unique_ptr<uint8[]> skip_ohs_buff =
SerializeToBuffer(one_ohs, false, skip_ohs_size);
- ShellRBSPStream skip_ones(skip_ones_buff.get(), skip_ones_size);
- ShellRBSPStream skip_ohs(skip_ohs_buff.get(), skip_ohs_size);
+ RBSPStream skip_ones(skip_ones_buff.get(), skip_ones_size);
+ RBSPStream skip_ohs(skip_ohs_buff.get(), skip_ohs_size);
for (int i = 1; i < 64; ++i) {
// skip the ones
ASSERT_TRUE(skip_ones.SkipBits(i));
diff --git a/src/cobalt/media/sandbox/demuxer_helper.cc b/src/cobalt/media/sandbox/demuxer_helper.cc
index fa343fa..c5b2991 100644
--- a/src/cobalt/media/sandbox/demuxer_helper.cc
+++ b/src/cobalt/media/sandbox/demuxer_helper.cc
@@ -24,7 +24,7 @@
#include "media/base/bind_to_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_decoder_config.h"
-#include "media/filters/shell_demuxer.h"
+#include "media/progressive/progressive_demuxer.h"
namespace cobalt {
namespace media {
@@ -263,7 +263,7 @@
fetcher_factory->network_module(),
loader::kNoCORSMode, loader::Origin()));
scoped_refptr<Demuxer> demuxer =
- new ::media::ShellDemuxer(media_message_loop, data_source);
+ new ::media::ProgressiveDemuxer(media_message_loop, data_source);
demuxer->Initialize(
host_, base::Bind(&DemuxerHelper::OnDemuxerReady, base::Unretained(this),
demuxer, demuxer_ready_cb, bytes_to_cache));
diff --git a/src/cobalt/media/sandbox/format_guesstimator.cc b/src/cobalt/media/sandbox/format_guesstimator.cc
index 9d6c3f2..a616d53 100644
--- a/src/cobalt/media/sandbox/format_guesstimator.cc
+++ b/src/cobalt/media/sandbox/format_guesstimator.cc
@@ -34,8 +34,8 @@
#include "cobalt/render_tree/image.h"
#include "net/base/filename_util.h"
#include "net/base/url_util.h"
+#include "starboard/common/file.h"
#include "starboard/common/string.h"
-#include "starboard/file.h"
#include "starboard/memory.h"
#include "starboard/types.h"
diff --git a/src/cobalt/media/sandbox/web_media_player_sandbox.cc b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
index c947020..f97a2b6 100644
--- a/src/cobalt/media/sandbox/web_media_player_sandbox.cc
+++ b/src/cobalt/media/sandbox/web_media_player_sandbox.cc
@@ -30,8 +30,8 @@
#include "cobalt/media/sandbox/media_sandbox.h"
#include "cobalt/media/sandbox/web_media_player_helper.h"
#include "cobalt/render_tree/image.h"
+#include "starboard/common/file.h"
#include "starboard/event.h"
-#include "starboard/file.h"
#include "starboard/log.h"
#include "starboard/system.h"
diff --git a/src/cobalt/media_capture/encoders/audio_encoder.h b/src/cobalt/media_capture/encoders/audio_encoder.h
index 63134e0..0efe09b 100644
--- a/src/cobalt/media_capture/encoders/audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/audio_encoder.h
@@ -20,10 +20,10 @@
#include "base/basictypes.h"
#include "base/strings/string_piece.h"
#include "base/time/time.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media_stream/audio_parameters.h"
#include "starboard/common/mutex.h"
-#include "cobalt/media/base/shell_audio_bus.h"
namespace cobalt {
namespace media_capture {
@@ -31,7 +31,7 @@
class AudioEncoder {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
class Listener {
public:
@@ -44,7 +44,7 @@
virtual ~AudioEncoder() = default;
// Encode raw audio data.
- virtual void Encode(const ShellAudioBus& audio_bus,
+ virtual void Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) = 0;
// Finish encoding.
virtual void Finish(base::TimeTicks timecode) = 0;
diff --git a/src/cobalt/media_capture/encoders/flac_audio_encoder.cc b/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
index 0e45933..7c57c69 100644
--- a/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
+++ b/src/cobalt/media_capture/encoders/flac_audio_encoder.cc
@@ -59,11 +59,11 @@
base::Bind(&FlacAudioEncoder::DestroyEncoder, base::Unretained(this)));
}
-void FlacAudioEncoder::Encode(const ShellAudioBus& audio_bus,
+void FlacAudioEncoder::Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) {
- std::unique_ptr<ShellAudioBus> audio_bus_copy(
- new ShellAudioBus(audio_bus.channels(), audio_bus.frames(),
- audio_bus.sample_type(), audio_bus.storage_type()));
+ std::unique_ptr<AudioBus> audio_bus_copy(
+ new AudioBus(audio_bus.channels(), audio_bus.frames(),
+ audio_bus.sample_type(), audio_bus.storage_type()));
audio_bus_copy->Assign(audio_bus);
// base::Unretained usage is safe here, since we're posting to a thread that
@@ -107,7 +107,7 @@
void FlacAudioEncoder::DestroyEncoder() { flac_encoder_.reset(); }
-void FlacAudioEncoder::DoEncode(std::unique_ptr<ShellAudioBus> audio_bus,
+void FlacAudioEncoder::DoEncode(std::unique_ptr<AudioBus> audio_bus,
base::TimeTicks reference_time) {
DCHECK(flac_encoder_);
flac_encoder_->Encode(audio_bus.get());
diff --git a/src/cobalt/media_capture/encoders/flac_audio_encoder.h b/src/cobalt/media_capture/encoders/flac_audio_encoder.h
index 90d4bd4..a982eaf 100644
--- a/src/cobalt/media_capture/encoders/flac_audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/flac_audio_encoder.h
@@ -36,7 +36,7 @@
// Called from the thread the object is constructed on (usually
// a dedicated audio thread).
- void Encode(const ShellAudioBus& audio_bus,
+ void Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) override;
// This can be called from any thread
@@ -54,7 +54,7 @@
// These functions are called on the encoder thread.
void CreateEncoder(const media_stream::AudioParameters& params);
void DestroyEncoder();
- void DoEncode(std::unique_ptr<ShellAudioBus> audio_bus,
+ void DoEncode(std::unique_ptr<AudioBus> audio_bus,
base::TimeTicks reference_time);
void DoFinish(base::TimeTicks reference_time);
diff --git a/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc b/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
index f18f035..c5148ac 100644
--- a/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
+++ b/src/cobalt/media_capture/encoders/linear16_audio_encoder.cc
@@ -43,9 +43,9 @@
return match_iterator == mime_type_container.begin();
}
-void Linear16AudioEncoder::Encode(const ShellAudioBus& audio_bus,
+void Linear16AudioEncoder::Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) {
- DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+ DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
DCHECK_EQ(audio_bus.channels(), size_t(1));
auto data = audio_bus.interleaved_data();
size_t data_size = audio_bus.GetSampleSizeInBytes() * audio_bus.frames();
diff --git a/src/cobalt/media_capture/encoders/linear16_audio_encoder.h b/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
index bb5f5d7..75040f4 100644
--- a/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
+++ b/src/cobalt/media_capture/encoders/linear16_audio_encoder.h
@@ -29,7 +29,7 @@
static bool IsLinear16MIMEType(const base::StringPiece& mime_type);
Linear16AudioEncoder() = default;
- void Encode(const ShellAudioBus& audio_bus,
+ void Encode(const AudioBus& audio_bus,
base::TimeTicks reference_time) override;
void Finish(base::TimeTicks reference_time) override;
std::string GetMimeType() const override;
diff --git a/src/cobalt/media_capture/media_recorder.cc b/src/cobalt/media_capture/media_recorder.cc
index 3a015f0..6642bfc 100644
--- a/src/cobalt/media_capture/media_recorder.cc
+++ b/src/cobalt/media_capture/media_recorder.cc
@@ -161,10 +161,10 @@
StopRecording();
}
-void MediaRecorder::OnData(const ShellAudioBus& audio_bus,
+void MediaRecorder::OnData(const AudioBus& audio_bus,
base::TimeTicks reference_time) {
// The source is always int16 data from the microphone.
- DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+ DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
DCHECK_EQ(audio_bus.channels(), size_t(1));
DCHECK(audio_encoder_);
audio_encoder_->Encode(audio_bus, reference_time);
diff --git a/src/cobalt/media_capture/media_recorder.h b/src/cobalt/media_capture/media_recorder.h
index a7b1c9d..5883af8 100644
--- a/src/cobalt/media_capture/media_recorder.h
+++ b/src/cobalt/media_capture/media_recorder.h
@@ -118,7 +118,7 @@
RecordingState state() { return recording_state_; }
// MediaStreamAudioSink overrides.
- void OnData(const ShellAudioBus& audio_bus,
+ void OnData(const AudioBus& audio_bus,
base::TimeTicks reference_time) override;
void OnSetFormat(const media_stream::AudioParameters& params) override;
void OnReadyStateChanged(
diff --git a/src/cobalt/media_capture/media_recorder_test.cc b/src/cobalt/media_capture/media_recorder_test.cc
index cfc5e3c..46fab4a 100644
--- a/src/cobalt/media_capture/media_recorder_test.cc
+++ b/src/cobalt/media_capture/media_recorder_test.cc
@@ -52,7 +52,7 @@
frames.push_back(32767);
frames.push_back(1000);
frames.push_back(0);
- cobalt::media_stream::MediaStreamAudioTrack::ShellAudioBus audio_bus(
+ cobalt::media_stream::MediaStreamAudioTrack::AudioBus audio_bus(
1, frames.size(), frames.data());
base::TimeTicks current_time = base::TimeTicks::Now();
media_recorder->OnData(audio_bus, current_time);
@@ -68,9 +68,8 @@
MOCK_METHOD0(EnsureSourceIsStarted, bool());
MOCK_METHOD0(EnsureSourceIsStopped, void());
- void DeliverDataToTracks(
- const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
- base::TimeTicks reference_time) {
+ void DeliverDataToTracks(const MediaStreamAudioTrack::AudioBus& audio_bus,
+ base::TimeTicks reference_time) {
MediaStreamAudioSource::DeliverDataToTracks(audio_bus, reference_time);
}
@@ -191,8 +190,8 @@
frames.push_back(32767);
frames.push_back(1000);
frames.push_back(0);
- media_stream::MediaStreamAudioTrack::ShellAudioBus audio_bus(1, frames.size(),
- frames.data());
+ media_stream::MediaStreamAudioTrack::AudioBus audio_bus(1, frames.size(),
+ frames.data());
base::TimeTicks current_time = base::TimeTicks::Now();
media_recorder_->OnData(audio_bus, current_time);
current_time += base::TimeDelta::FromSecondsD(frames.size() / kSampleRate);
diff --git a/src/cobalt/media_session/media_session_client.cc b/src/cobalt/media_session/media_session_client.cc
index 3263591..4b32aa9 100644
--- a/src/cobalt/media_session/media_session_client.cc
+++ b/src/cobalt/media_session/media_session_client.cc
@@ -64,7 +64,6 @@
session_state->available_actions());
}
}
-
} // namespace
MediaSessionClient::MediaSessionClient(
@@ -171,38 +170,41 @@
return result;
}
-void MediaSessionClient::UpdatePlatformPlaybackState(
- MediaSessionPlaybackState state) {
+void MediaSessionClient::UpdatePlatformCobaltExtensionPlaybackState(
+ CobaltExtensionMediaSessionPlaybackState state) {
DCHECK(media_session_->task_runner_);
if (!media_session_->task_runner_->BelongsToCurrentThread()) {
media_session_->task_runner_->PostTask(
- FROM_HERE, base::Bind(&MediaSessionClient::UpdatePlatformPlaybackState,
- base::Unretained(this), state));
+ FROM_HERE,
+ base::Bind(
+ &MediaSessionClient::UpdatePlatformCobaltExtensionPlaybackState,
+ base::Unretained(this), state));
return;
}
- platform_playback_state_ = state;
+ platform_playback_state_ = ConvertPlaybackState(state);
if (session_state_.actual_playback_state() != ComputeActualPlaybackState()) {
UpdateMediaSessionState();
}
+
+ if (!is_active() && !maybe_freeze_callback_.is_null()) {
+ maybe_freeze_callback_.Run();
+ }
}
void MediaSessionClient::InvokeActionInternal(
- std::unique_ptr<MediaSessionActionDetails> details) {
- DCHECK(details->has_action());
+ CobaltExtensionMediaSessionActionDetails* details) {
+ DCHECK(details->action >= 0 &&
+ details->action < kCobaltExtensionMediaSessionActionNumActions);
// Some fields should only be set for applicable actions.
- DCHECK(!details->has_seek_offset() ||
- details->action() == kMediaSessionActionSeekforward ||
- details->action() == kMediaSessionActionSeekbackward);
- DCHECK(!details->has_seek_time() ||
- details->action() == kMediaSessionActionSeekto);
- DCHECK(!details->has_fast_seek() ||
- details->action() == kMediaSessionActionSeekto);
-
- // Seek times/offsets are non-negative, even for seeking backwards.
- DCHECK(!details->has_seek_time() || details->seek_time() >= 0.0);
- DCHECK(!details->has_seek_offset() || details->seek_offset() >= 0.0);
+ DCHECK(details->seek_offset < 0.0 ||
+ details->action == kCobaltExtensionMediaSessionActionSeekforward ||
+ details->action == kCobaltExtensionMediaSessionActionSeekbackward);
+ DCHECK(details->seek_time < 0.0 ||
+ details->action == kCobaltExtensionMediaSessionActionSeekto);
+ DCHECK(!details->fast_seek ||
+ details->action == kCobaltExtensionMediaSessionActionSeekto);
DCHECK(media_session_->task_runner_);
if (!media_session_->task_runner_->BelongsToCurrentThread()) {
@@ -212,14 +214,16 @@
return;
}
- MediaSession::ActionMap::iterator it =
- media_session_->action_map_.find(details->action());
+ MediaSession::ActionMap::iterator it = media_session_->action_map_.find(
+ ConvertMediaSessionAction(details->action));
if (it == media_session_->action_map_.end()) {
return;
}
- it->second->value().Run(*details);
+ std::unique_ptr<MediaSessionActionDetails> script_details =
+ ConvertActionDetails(details);
+ it->second->value().Run(*script_details);
// Queue a session update to reflect the effects of the action.
if (!media_session_->media_position_state_) {
@@ -270,13 +274,14 @@
const MediaSessionState& session_state) {
if (extension_ && extension_->version >= 1) {
CobaltExtensionMediaSessionState ext_state;
- CobaltExtensionMediaMetadata ext_metadata = {0};
size_t artwork_size = 0;
if (session_state.has_metadata() &&
session_state.metadata().value().has_artwork()) {
artwork_size = session_state.metadata().value().artwork().size();
}
- ext_metadata.artwork_count = artwork_size;
+ std::unique_ptr<CobaltExtensionMediaImage[]> ext_artwork =
+ std::unique_ptr<CobaltExtensionMediaImage[]>(
+ new CobaltExtensionMediaImage[artwork_size]);
ext_state.duration = session_state.duration();
ext_state.actual_playback_rate = session_state.actual_playback_rate();
@@ -286,12 +291,15 @@
ConvertPlaybackState(session_state.actual_playback_state());
ConvertMediaSessionActions(session_state.available_actions(),
ext_state.available_actions);
+ std::string album = "";
+ std::string artist = "";
+ std::string title = "";
if (session_state.has_metadata()) {
const MediaMetadataInit& metadata = session_state.metadata().value();
- ext_metadata.album = metadata.album().c_str();
- ext_metadata.artist = metadata.artist().c_str();
- ext_metadata.title = metadata.title().c_str();
+ album = metadata.album();
+ artist = metadata.artist();
+ title = metadata.title();
if (artwork_size > 0) {
const MediaImageSequence& artwork(metadata.artwork());
for (MediaImageSequence::size_type i = 0; i < artwork_size; i++) {
@@ -300,26 +308,63 @@
ext_image.src = media_image.src().c_str();
ext_image.size = media_image.sizes().c_str();
ext_image.type = media_image.type().c_str();
- ext_metadata.artwork[i] = ext_image;
+ ext_artwork[i] = ext_image;
}
}
- ext_state.metadata = ext_metadata;
}
+ CobaltExtensionMediaMetadata ext_metadata = {
+ album.c_str(), artist.c_str(), title.c_str(), ext_artwork.get(),
+ artwork_size};
+ ext_state.metadata = &ext_metadata;
+
+ ext_state.update_platform_playback_state_callback =
+ &UpdatePlatformPlaybackStateCallback;
+ ext_state.invoke_action_callback = &InvokeActionCallback;
+ ext_state.callback_context = this;
extension_->OnMediaSessionStateChanged(ext_state);
}
}
-CobaltExtensionPlaybackState MediaSessionClient::ConvertPlaybackState(
- MediaSessionPlaybackState in_state) {
- switch (in_state) {
+// static
+void MediaSessionClient::UpdatePlatformPlaybackStateCallback(
+ CobaltExtensionMediaSessionPlaybackState state, void* callback_context) {
+ MediaSessionClient* client =
+ static_cast<MediaSessionClient*>(callback_context);
+ client->UpdatePlatformCobaltExtensionPlaybackState(state);
+}
+
+// static
+void MediaSessionClient::InvokeActionCallback(
+ CobaltExtensionMediaSessionActionDetails details, void* callback_context) {
+ MediaSessionClient* client =
+ static_cast<MediaSessionClient*>(callback_context);
+ client->InvokeCobaltExtensionAction(details);
+}
+
+CobaltExtensionMediaSessionPlaybackState
+MediaSessionClient::ConvertPlaybackState(MediaSessionPlaybackState state) {
+ switch (state) {
case kMediaSessionPlaybackStatePlaying:
- return CobaltExtensionPlaybackState::kCobaltExtensionPlaying;
+ return kCobaltExtensionMediaSessionPlaying;
case kMediaSessionPlaybackStatePaused:
- return CobaltExtensionPlaybackState::kCobaltExtensionPaused;
+ return kCobaltExtensionMediaSessionPaused;
case kMediaSessionPlaybackStateNone:
default:
- return CobaltExtensionPlaybackState::kCobaltExtensionNone;
+ return kCobaltExtensionMediaSessionNone;
+ }
+}
+
+MediaSessionPlaybackState MediaSessionClient::ConvertPlaybackState(
+ CobaltExtensionMediaSessionPlaybackState state) {
+ switch (state) {
+ case kCobaltExtensionMediaSessionPlaying:
+ return kMediaSessionPlaybackStatePlaying;
+ case kCobaltExtensionMediaSessionPaused:
+ return kMediaSessionPlaybackStatePaused;
+ case kCobaltExtensionMediaSessionNone:
+ default:
+ return kMediaSessionPlaybackStateNone;
}
}
@@ -328,32 +373,73 @@
bool result[kCobaltExtensionMediaSessionActionNumActions]) {
for (int i = 0; i < kCobaltExtensionMediaSessionActionNumActions; i++) {
result[i] = false;
- }
- if (actions[kMediaSessionActionPause]) {
- result[kCobaltExtensionMediaSessionActionPause] = true;
- }
- if (actions[kMediaSessionActionPlay]) {
- result[kCobaltExtensionMediaSessionActionPlay] = true;
- }
- if (actions[kMediaSessionActionSeekbackward]) {
- result[kCobaltExtensionMediaSessionActionSeekbackward] = true;
- }
- if (actions[kMediaSessionActionPrevioustrack]) {
- result[kCobaltExtensionMediaSessionActionPrevioustrack] = true;
- }
- if (actions[kMediaSessionActionNexttrack]) {
- result[kCobaltExtensionMediaSessionActionNexttrack] = true;
- }
- if (actions[kMediaSessionActionSeekforward]) {
- result[kCobaltExtensionMediaSessionActionSeekforward] = true;
- }
- if (actions[kMediaSessionActionSeekto]) {
- result[kCobaltExtensionMediaSessionActionSeekto] = true;
- }
- if (actions[kMediaSessionActionStop]) {
- result[kCobaltExtensionMediaSessionActionStop] = true;
+ MediaSessionAction action = static_cast<MediaSessionAction>(i);
+ if (actions[action]) {
+ result[ConvertMediaSessionAction(action)] = true;
+ }
}
}
+std::unique_ptr<MediaSessionActionDetails>
+MediaSessionClient::ConvertActionDetails(
+ CobaltExtensionMediaSessionActionDetails* ext_details) {
+ std::unique_ptr<MediaSessionActionDetails> details(
+ new MediaSessionActionDetails());
+ details->set_action(ConvertMediaSessionAction(ext_details->action));
+ if (ext_details->seek_offset >= 0.0) {
+ details->set_seek_offset(ext_details->seek_offset);
+ }
+ if (ext_details->seek_time >= 0.0) {
+ details->set_seek_time(ext_details->seek_time);
+ }
+ details->set_fast_seek(ext_details->fast_seek);
+ return details;
+}
+
+CobaltExtensionMediaSessionAction MediaSessionClient::ConvertMediaSessionAction(
+ MediaSessionAction action) {
+ switch (action) {
+ case kMediaSessionActionPause:
+ return kCobaltExtensionMediaSessionActionPause;
+ case kMediaSessionActionSeekbackward:
+ return kCobaltExtensionMediaSessionActionSeekbackward;
+ case kMediaSessionActionPrevioustrack:
+ return kCobaltExtensionMediaSessionActionPrevioustrack;
+ case kMediaSessionActionNexttrack:
+ return kCobaltExtensionMediaSessionActionNexttrack;
+ case kMediaSessionActionSeekforward:
+ return kCobaltExtensionMediaSessionActionSeekforward;
+ case kMediaSessionActionSeekto:
+ return kCobaltExtensionMediaSessionActionSeekto;
+ case kMediaSessionActionStop:
+ return kCobaltExtensionMediaSessionActionStop;
+ case kMediaSessionActionPlay:
+ default:
+ return kCobaltExtensionMediaSessionActionPlay;
+ }
+}
+
+MediaSessionAction MediaSessionClient::ConvertMediaSessionAction(
+ CobaltExtensionMediaSessionAction action) {
+ switch (action) {
+ case kCobaltExtensionMediaSessionActionPause:
+ return kMediaSessionActionPause;
+ case kCobaltExtensionMediaSessionActionSeekbackward:
+ return kMediaSessionActionSeekbackward;
+ case kCobaltExtensionMediaSessionActionPrevioustrack:
+ return kMediaSessionActionPrevioustrack;
+ case kCobaltExtensionMediaSessionActionNexttrack:
+ return kMediaSessionActionNexttrack;
+ case kCobaltExtensionMediaSessionActionSeekforward:
+ return kMediaSessionActionSeekforward;
+ case kCobaltExtensionMediaSessionActionSeekto:
+ return kMediaSessionActionSeekto;
+ case kCobaltExtensionMediaSessionActionStop:
+ return kMediaSessionActionStop;
+ case kCobaltExtensionMediaSessionActionPlay:
+ default:
+ return kMediaSessionActionPlay;
+ }
+}
} // namespace media_session
} // namespace cobalt
diff --git a/src/cobalt/media_session/media_session_client.h b/src/cobalt/media_session/media_session_client.h
index 9b5d6fa..d2d5221 100644
--- a/src/cobalt/media_session/media_session_client.h
+++ b/src/cobalt/media_session/media_session_client.h
@@ -58,28 +58,66 @@
// the "guessed playback state"
// https://wicg.github.io/mediasession/#guessed-playback-state
// Can be invoked from any thread.
- void UpdatePlatformPlaybackState(MediaSessionPlaybackState state);
+ void UpdatePlatformCobaltExtensionPlaybackState(
+ CobaltExtensionMediaSessionPlaybackState state);
+
+ // Deprecated - use the alternative
+ // UpdatePlatformCobaltExtensionPlaybackState.
+ // TODO: Delete once platform migrations to CobaltExtensionMediaSessionApi are
+ // complete.
+ void UpdatePlatformPlaybackState(MediaSessionPlaybackState state) {
+ UpdatePlatformCobaltExtensionPlaybackState(ConvertPlaybackState(state));
+ }
// Invokes a given media session action
// https://wicg.github.io/mediasession/#actions-model
// Can be invoked from any thread.
- void InvokeAction(MediaSessionAction action) {
- std::unique_ptr<MediaSessionActionDetails> details(
- new MediaSessionActionDetails());
- details->set_action(action);
- InvokeActionInternal(std::move(details));
+ void InvokeAction(CobaltExtensionMediaSessionAction action) {
+ CobaltExtensionMediaSessionActionDetails details = {};
+ CobaltExtensionMediaSessionActionDetailsInit(&details, action);
+ InvokeActionInternal(std::move(&details));
}
// Invokes a given media session action that takes additional details.
- void InvokeAction(std::unique_ptr<MediaSessionActionDetails> details) {
- InvokeActionInternal(std::move(details));
+ void InvokeCobaltExtensionAction(
+ CobaltExtensionMediaSessionActionDetails details) {
+ InvokeActionInternal(&details);
}
+ // Deprecated - use the alternative InvokeCobaltExtensionAction.
+ // TODO: Delete once platform migrations to CobaltExtensionMediaSessionApi are
+ // complete.
+ void InvokeAction(std::unique_ptr<MediaSessionActionDetails> details) {
+ CobaltExtensionMediaSessionActionDetails* ext_details = {};
+ CobaltExtensionMediaSessionActionDetailsInit(
+ ext_details, ConvertMediaSessionAction(details->action()));
+ if (details->has_seek_offset()) {
+ ext_details->seek_offset = details->seek_offset().value();
+ }
+ if (details->has_seek_time()) {
+ ext_details->seek_time = details->seek_time().value();
+ }
+ if (details->has_fast_seek()) {
+ ext_details->fast_seek = details->fast_seek().value();
+ }
+ InvokeActionInternal(std::move(ext_details));
+ }
// Invoked on the browser thread when any metadata, position state, playback
// state, or supported session actions change.
virtual void OnMediaSessionStateChanged(
const MediaSessionState& session_state);
+ // Indicate the media session client is active or not depending on the
+ // media session playback state.
+ bool is_active() {
+ return platform_playback_state_ != kMediaSessionPlaybackStateNone;
+ }
+
+ // Set maybe freeze callback.
+ void SetMaybeFreezeCallback(const base::Closure& maybe_freeze_callback) {
+ maybe_freeze_callback_ = maybe_freeze_callback;
+ }
+
private:
THREAD_CHECKER(thread_checker_);
scoped_refptr<MediaSession> media_session_;
@@ -91,13 +129,31 @@
void UpdateMediaSessionState();
MediaSessionPlaybackState ComputeActualPlaybackState() const;
MediaSessionState::AvailableActionsSet ComputeAvailableActions() const;
- CobaltExtensionPlaybackState ConvertPlaybackState(
- MediaSessionPlaybackState in_state);
+ void InvokeActionInternal(CobaltExtensionMediaSessionActionDetails* details);
+
void ConvertMediaSessionActions(
const MediaSessionState::AvailableActionsSet& actions,
bool result[kCobaltExtensionMediaSessionActionNumActions]);
+ std::unique_ptr<MediaSessionActionDetails> ConvertActionDetails(
+ CobaltExtensionMediaSessionActionDetails* ext_details);
- void InvokeActionInternal(std::unique_ptr<MediaSessionActionDetails> details);
+ // Static callback wrappers for MediaSessionAPI extension.
+ static void UpdatePlatformPlaybackStateCallback(
+ CobaltExtensionMediaSessionPlaybackState state, void* callback_context);
+ static void InvokeActionCallback(
+ CobaltExtensionMediaSessionActionDetails details, void* callback_context);
+
+ // MediaSessionAPI extension type conversion helpers.
+ CobaltExtensionMediaSessionPlaybackState ConvertPlaybackState(
+ MediaSessionPlaybackState state);
+ MediaSessionPlaybackState ConvertPlaybackState(
+ CobaltExtensionMediaSessionPlaybackState state);
+ CobaltExtensionMediaSessionAction ConvertMediaSessionAction(
+ MediaSessionAction action);
+ MediaSessionAction ConvertMediaSessionAction(
+ CobaltExtensionMediaSessionAction action);
+
+ base::Closure maybe_freeze_callback_;
DISALLOW_COPY_AND_ASSIGN(MediaSessionClient);
};
diff --git a/src/cobalt/media_session/media_session_test.cc b/src/cobalt/media_session/media_session_test.cc
index 687167a..8b2f22e 100644
--- a/src/cobalt/media_session/media_session_test.cc
+++ b/src/cobalt/media_session/media_session_test.cc
@@ -20,6 +20,7 @@
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "cobalt/bindings/testing/script_object_owner.h"
+#include "cobalt/extension/media_session.h"
#include "cobalt/media_session/media_session_client.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_value.h"
@@ -68,6 +69,7 @@
override {
session_state_ = session_state;
++session_change_count_;
+ MediaSessionClient::OnMediaSessionStateChanged(session_state);
}
void WaitForSessionStateChange() {
size_t current_change_count = session_change_count_;
@@ -193,12 +195,12 @@
session->SetActionHandler(kMediaSessionActionPlay, holder);
client.WaitForSessionStateChange();
EXPECT_EQ(1, client.GetMediaSessionState().available_actions().to_ulong());
- client.InvokeAction(kMediaSessionActionPlay);
+ client.InvokeAction(kCobaltExtensionMediaSessionActionPlay);
session->SetActionHandler(kMediaSessionActionPlay, null_holder);
client.WaitForSessionStateChange();
EXPECT_EQ(0, client.GetMediaSessionState().available_actions().to_ulong());
- client.InvokeAction(kMediaSessionActionPlay);
+ client.InvokeAction(kCobaltExtensionMediaSessionActionPlay);
EXPECT_GE(client.GetMediaSessionChangeCount(), 3);
}
@@ -315,7 +317,7 @@
MockCallbackFunction cf;
FakeScriptValue<MediaSession::MediaSessionActionHandler> holder(&cf);
- std::unique_ptr<MediaSessionActionDetails> details;
+ CobaltExtensionMediaSessionActionDetails details;
session->SetActionHandler(kMediaSessionActionSeekto, holder);
session->SetActionHandler(kMediaSessionActionSeekforward, holder);
@@ -323,31 +325,31 @@
EXPECT_CALL(cf, Run(SeekNoOffset(kMediaSessionActionSeekforward)))
.WillOnce(Return(CallbackResult<void>()));
- client.InvokeAction(kMediaSessionActionSeekforward);
+ client.InvokeAction(kCobaltExtensionMediaSessionActionSeekforward);
EXPECT_CALL(cf, Run(SeekNoOffset(kMediaSessionActionSeekbackward)))
.WillOnce(Return(CallbackResult<void>()));
- client.InvokeAction(kMediaSessionActionSeekbackward);
+ client.InvokeAction(kCobaltExtensionMediaSessionActionSeekbackward);
EXPECT_CALL(cf, Run(SeekTime(1.2))).WillOnce(Return(CallbackResult<void>()));
- details.reset(new MediaSessionActionDetails());
- details->set_action(kMediaSessionActionSeekto);
- details->set_seek_time(1.2);
- client.InvokeAction(std::move(details));
+ CobaltExtensionMediaSessionActionDetailsInit(
+ &details, kCobaltExtensionMediaSessionActionSeekto);
+ details.seek_time = 1.2;
+ client.InvokeCobaltExtensionAction(details);
EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekforward, 3.4)))
.WillOnce(Return(CallbackResult<void>()));
- details.reset(new MediaSessionActionDetails());
- details->set_action(kMediaSessionActionSeekforward);
- details->set_seek_offset(3.4);
- client.InvokeAction(std::move(details));
+ CobaltExtensionMediaSessionActionDetailsInit(
+ &details, kCobaltExtensionMediaSessionActionSeekforward);
+ details.seek_offset = 3.4;
+ client.InvokeCobaltExtensionAction(details);
EXPECT_CALL(cf, Run(SeekOffset(kMediaSessionActionSeekbackward, 5.6)))
.WillOnce(Return(CallbackResult<void>()));
- details.reset(new MediaSessionActionDetails());
- details->set_action(kMediaSessionActionSeekbackward);
- details->set_seek_offset(5.6);
- client.InvokeAction(std::move(details));
+ CobaltExtensionMediaSessionActionDetailsInit(
+ &details, kCobaltExtensionMediaSessionActionSeekbackward);
+ details.seek_offset = 5.6;
+ client.InvokeCobaltExtensionAction(details);
client.WaitForSessionStateChange();
EXPECT_GE(client.GetMediaSessionChangeCount(), 0);
diff --git a/src/cobalt/media_stream/media_stream_audio_deliverer.h b/src/cobalt/media_stream/media_stream_audio_deliverer.h
index 40628d4..b942bdd 100644
--- a/src/cobalt/media_stream/media_stream_audio_deliverer.h
+++ b/src/cobalt/media_stream/media_stream_audio_deliverer.h
@@ -26,9 +26,10 @@
#include "base/threading/thread_checker.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
+
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media_stream/audio_parameters.h"
-#include "cobalt/media/base/shell_audio_bus.h"
namespace cobalt {
namespace media_stream {
@@ -119,7 +120,7 @@
// Deliver data to all consumers. This method may be called on any thread.
- void OnData(const media::ShellAudioBus& audio_bus,
+ void OnData(const media::AudioBus& audio_bus,
base::TimeTicks reference_time) {
TRACE_EVENT1("media_stream", "MediaStreamAudioDeliverer::OnData",
"reference time (ms)",
diff --git a/src/cobalt/media_stream/media_stream_audio_sink.h b/src/cobalt/media_stream/media_stream_audio_sink.h
index 680bbd3..034dbec 100644
--- a/src/cobalt/media_stream/media_stream_audio_sink.h
+++ b/src/cobalt/media_stream/media_stream_audio_sink.h
@@ -15,12 +15,13 @@
#ifndef COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_SINK_H_
#define COBALT_MEDIA_STREAM_MEDIA_STREAM_AUDIO_SINK_H_
+#include "cobalt/media/base/audio_bus.h"
+
#include "cobalt/media_stream/audio_parameters.h"
#include "cobalt/media_stream/media_stream_source.h"
#include "cobalt/media_stream/media_stream_track.h"
-#include "cobalt/media/base/shell_audio_bus.h"
namespace cobalt {
namespace media_stream {
@@ -29,11 +30,11 @@
// Note: users of this class will call OnSetFormat is before OnData.
class MediaStreamAudioSink {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
MediaStreamAudioSink() = default;
// These are called on the same thread.
- virtual void OnData(const ShellAudioBus& audio_bus,
+ virtual void OnData(const AudioBus& audio_bus,
base::TimeTicks reference_time) = 0;
virtual void OnSetFormat(const media_stream::AudioParameters& params) = 0;
virtual void OnReadyStateChanged(
diff --git a/src/cobalt/media_stream/media_stream_audio_source.cc b/src/cobalt/media_stream/media_stream_audio_source.cc
index 6c20d49..2d15393 100644
--- a/src/cobalt/media_stream/media_stream_audio_source.cc
+++ b/src/cobalt/media_stream/media_stream_audio_source.cc
@@ -60,7 +60,7 @@
}
void MediaStreamAudioSource::DeliverDataToTracks(
- const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
+ const MediaStreamAudioTrack::AudioBus& audio_bus,
base::TimeTicks reference_time) {
deliverer_.OnData(audio_bus, reference_time);
}
diff --git a/src/cobalt/media_stream/media_stream_audio_source.h b/src/cobalt/media_stream/media_stream_audio_source.h
index 77c00d7..69d789c 100644
--- a/src/cobalt/media_stream/media_stream_audio_source.h
+++ b/src/cobalt/media_stream/media_stream_audio_source.h
@@ -58,9 +58,8 @@
// Subclasses should call these methods. |DeliverDataToTracks| can be
// called from a different thread than where MediaStreamAudioSource
// was created.
- void DeliverDataToTracks(
- const MediaStreamAudioTrack::ShellAudioBus& audio_bus,
- base::TimeTicks reference_time);
+ void DeliverDataToTracks(const MediaStreamAudioTrack::AudioBus& audio_bus,
+ base::TimeTicks reference_time);
void SetFormat(const media_stream::AudioParameters& params) {
DLOG(INFO) << "MediaStreamAudioSource@" << this << "::SetFormat("
diff --git a/src/cobalt/media_stream/media_stream_audio_source_test.cc b/src/cobalt/media_stream/media_stream_audio_source_test.cc
index f526bba..4157d3d 100644
--- a/src/cobalt/media_stream/media_stream_audio_source_test.cc
+++ b/src/cobalt/media_stream/media_stream_audio_source_test.cc
@@ -17,7 +17,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "cobalt/dom/testing/stub_environment_settings.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media_stream/media_stream_audio_track.h"
#include "cobalt/media_stream/testing/mock_media_stream_audio_source.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -35,7 +35,7 @@
namespace cobalt {
namespace media_stream {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
class MediaStreamAudioSourceTest : public testing::Test {
public:
diff --git a/src/cobalt/media_stream/media_stream_audio_track.cc b/src/cobalt/media_stream/media_stream_audio_track.cc
index c6deb82..0823274 100644
--- a/src/cobalt/media_stream/media_stream_audio_track.cc
+++ b/src/cobalt/media_stream/media_stream_audio_track.cc
@@ -69,7 +69,7 @@
stop_callback_ = stop_callback;
}
-void MediaStreamAudioTrack::OnData(const ShellAudioBus& audio_bus,
+void MediaStreamAudioTrack::OnData(const AudioBus& audio_bus,
base::TimeTicks reference_time) {
deliverer_.OnData(audio_bus, reference_time);
}
diff --git a/src/cobalt/media_stream/media_stream_audio_track.h b/src/cobalt/media_stream/media_stream_audio_track.h
index b7d7708..f1b9b7d 100644
--- a/src/cobalt/media_stream/media_stream_audio_track.h
+++ b/src/cobalt/media_stream/media_stream_audio_track.h
@@ -19,7 +19,7 @@
#include "base/strings/string_piece.h"
#include "base/threading/thread_checker.h"
#include "cobalt/dom/event_target.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media_stream/audio_parameters.h"
#include "cobalt/media_stream/media_stream_audio_deliverer.h"
#include "cobalt/media_stream/media_stream_audio_sink.h"
@@ -50,7 +50,7 @@
settings.set_sample_size(parameters.bits_per_sample());
return settings;
}
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
explicit MediaStreamAudioTrack(script::EnvironmentSettings* settings)
: MediaStreamTrack(settings) {}
@@ -81,7 +81,7 @@
void Start(const base::Closure& stop_callback);
// Called by MediaStreamAudioDeliverer.
- void OnData(const ShellAudioBus& audio_bus, base::TimeTicks reference_time);
+ void OnData(const AudioBus& audio_bus, base::TimeTicks reference_time);
void OnSetFormat(const media_stream::AudioParameters& params);
THREAD_CHECKER(thread_checker_);
diff --git a/src/cobalt/media_stream/media_stream_audio_track_test.cc b/src/cobalt/media_stream/media_stream_audio_track_test.cc
index 8fc542a..5512ae2 100644
--- a/src/cobalt/media_stream/media_stream_audio_track_test.cc
+++ b/src/cobalt/media_stream/media_stream_audio_track_test.cc
@@ -16,7 +16,7 @@
#include "base/bind_helpers.h"
#include "cobalt/dom/testing/stub_environment_settings.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/media_stream/media_stream_audio_deliverer.h"
#include "cobalt/media_stream/testing/mock_media_stream_audio_sink.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -36,7 +36,7 @@
namespace cobalt {
namespace media_stream {
-typedef media::ShellAudioBus ShellAudioBus;
+typedef media::AudioBus AudioBus;
// This fixture is created, so it can be added as a friend to
// |MediaStreamAudioTrack|. This enables calling a private method
@@ -71,8 +71,8 @@
deliverer.OnSetFormat(expected_params);
- ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
+ AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+ AudioBus::kInterleaved);
deliverer.OnData(audio_bus, expected_time);
}
@@ -104,8 +104,8 @@
deliverer.OnSetFormat(expected_params);
- ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
+ AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+ AudioBus::kInterleaved);
deliverer.OnData(audio_bus, expected_time);
}
@@ -141,8 +141,8 @@
deliverer.OnSetFormat(expected_params);
- ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
+ AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+ AudioBus::kInterleaved);
deliverer.OnData(audio_bus, expected_time);
}
@@ -166,8 +166,8 @@
deliverer.OnSetFormat(expected_params);
- ShellAudioBus audio_bus(kChannelCount, kFrameCount, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
+ AudioBus audio_bus(kChannelCount, kFrameCount, AudioBus::kInt16,
+ AudioBus::kInterleaved);
track->RemoveSink(&mock_sink);
deliverer.OnData(audio_bus, expected_time);
diff --git a/src/cobalt/media_stream/microphone_audio_source.cc b/src/cobalt/media_stream/microphone_audio_source.cc
index 778223e..74eff37 100644
--- a/src/cobalt/media_stream/microphone_audio_source.cc
+++ b/src/cobalt/media_stream/microphone_audio_source.cc
@@ -100,7 +100,7 @@
base::Unretained(this), options))) {}
void MicrophoneAudioSource::OnDataReceived(
- std::unique_ptr<MediaStreamAudioTrack::ShellAudioBus> audio_bus) {
+ std::unique_ptr<MediaStreamAudioTrack::AudioBus> audio_bus) {
base::TimeTicks now = base::TimeTicks::Now();
DeliverDataToTracks(*audio_bus, now);
}
diff --git a/src/cobalt/media_stream/microphone_audio_source.h b/src/cobalt/media_stream/microphone_audio_source.h
index d3049d7..c930298 100644
--- a/src/cobalt/media_stream/microphone_audio_source.h
+++ b/src/cobalt/media_stream/microphone_audio_source.h
@@ -70,7 +70,7 @@
int buffer_size_bytes);
void OnDataReceived(
- std::unique_ptr<MediaStreamAudioTrack::ShellAudioBus> audio_bus);
+ std::unique_ptr<MediaStreamAudioTrack::AudioBus> audio_bus);
void OnDataCompletion();
void OnMicrophoneOpen();
diff --git a/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h b/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
index bd11714..7fa06b6 100644
--- a/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
+++ b/src/cobalt/media_stream/testing/mock_media_stream_audio_sink.h
@@ -24,8 +24,8 @@
class MockMediaStreamAudioSink : public MediaStreamAudioSink {
public:
- MOCK_METHOD2(OnData, void(const ShellAudioBus& audio_bus,
- base::TimeTicks reference_time));
+ MOCK_METHOD2(OnData,
+ void(const AudioBus& audio_bus, base::TimeTicks reference_time));
MOCK_METHOD1(OnSetFormat, void(const media_stream::AudioParameters& params));
MOCK_METHOD1(OnReadyStateChanged,
void(media_stream::MediaStreamTrack::ReadyState new_state));
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
index 09ce038..9ba721f 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.cc
@@ -96,6 +96,16 @@
SelectAtlasCache(&offscreen_atlases_, &offscreen_cache_);
SelectAtlasCache(&offscreen_atlases_1d_, &offscreen_cache_1d_);
+ // Delete skottie targets that were not used in the previous render frame.
+ for (size_t index = 0; index < skottie_targets_.size();) {
+ if (skottie_targets_[index]->allocations_used == 0) {
+ skottie_targets_.erase(skottie_targets_.begin() + index);
+ } else {
+ skottie_targets_[index]->allocations_used = 0;
+ ++index;
+ }
+ }
+
// Delete uncached targets that were not used in the previous render frame.
for (size_t index = 0; index < uncached_targets_.size();) {
if (uncached_targets_[index]->allocations_used == 0) {
@@ -215,6 +225,33 @@
return false;
}
+bool OffscreenTargetManager::GetCachedTarget(
+ const skia::SkottieAnimation* skottie_animation, const math::SizeF& size,
+ TargetInfo* out_target_info) {
+ // Each SkottieAnimation uses its own render target cache. The cache is keyed
+ // off the SkottieAnimation's size and pointer value. Using the pointer can
+ // be risky as an object may be destroyed and a new one created with the same
+ // pointer value. However, since each SkottieAnimation is created assuming
+ // the render cache is dirty, it is safe to accidentally use the render cache
+ // of an old SkottieAnimation with a new one.
+ int64_t id = reinterpret_cast<int64_t>(skottie_animation);
+ math::RectF target_size(std::ceil(size.width()), std::ceil(size.height()));
+ for (size_t i = 0; i < skottie_targets_.size(); ++i) {
+ OffscreenAtlas* target = skottie_targets_[i].get();
+ auto iter = target->allocation_map.find(id);
+ if (iter != target->allocation_map.end() &&
+ iter->second.error_data == target_size) {
+ target->allocations_used += 1;
+ out_target_info->framebuffer = target->framebuffer.get();
+ out_target_info->skia_canvas = target->skia_surface->getCanvas();
+ out_target_info->region = iter->second.target_region;
+ return true;
+ }
+ }
+
+ return false;
+}
+
void OffscreenTargetManager::AllocateCachedTarget(const render_tree::Node* node,
const math::SizeF& size,
const ErrorData& error_data,
@@ -325,6 +362,34 @@
}
}
+void OffscreenTargetManager::AllocateCachedTarget(
+ const skia::SkottieAnimation* skottie_animation, const math::SizeF& size,
+ TargetInfo* out_target_info) {
+ // Each SkottieAnimation has its own offscreen atlas.
+ math::Size target_size(static_cast<int>(std::ceil(size.width())),
+ static_cast<int>(std::ceil(size.height())));
+ OffscreenAtlas* atlas = CreateOffscreenAtlas(target_size, true).release();
+ if (!atlas) {
+ // If there was an error allocating the offscreen atlas, indicate by
+ // marking framebuffer and skia canvas as null and returning early.
+ out_target_info->framebuffer = nullptr;
+ out_target_info->skia_canvas = nullptr;
+ return;
+ }
+
+ int64_t id = reinterpret_cast<int64_t>(skottie_animation);
+ // |target_rect| must be calculated the same way as in GetCachedTarget().
+ math::RectF target_rect(std::ceil(size.width()), std::ceil(size.height()));
+ atlas->allocation_map.insert(AllocationMap::value_type(
+ id, AllocationMapValue(target_rect, target_rect)));
+ skottie_targets_.emplace_back(atlas);
+
+ atlas->allocations_used = 1;
+ out_target_info->framebuffer = atlas->framebuffer.get();
+ out_target_info->skia_canvas = atlas->skia_surface->getCanvas();
+ out_target_info->region = target_rect;
+}
+
void OffscreenTargetManager::AllocateUncachedTarget(
const math::SizeF& size, TargetInfo* out_target_info) {
// Align up the requested target size to increase the chances that it can
diff --git a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
index ec64a19..69e4bde 100644
--- a/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
+++ b/src/cobalt/renderer/rasterizer/egl/offscreen_target_manager.h
@@ -23,6 +23,7 @@
#include "cobalt/render_tree/node.h"
#include "cobalt/renderer/backend/egl/framebuffer_render_target.h"
#include "cobalt/renderer/backend/egl/graphics_context.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
@@ -85,6 +86,9 @@
bool GetCachedTarget(const render_tree::Node* node,
const CacheErrorFunction1D& error_function,
TargetInfo* out_target_info);
+ bool GetCachedTarget(const skia::SkottieAnimation* skottie_animation,
+ const math::SizeF& size,
+ TargetInfo* out_target_info);
// Allocate a cached offscreen target of the specified size.
// The returned values are only valid until the next call to Update().
@@ -95,6 +99,9 @@
void AllocateCachedTarget(const render_tree::Node* node, float size,
const ErrorData1D& error_data,
TargetInfo* out_target_info);
+ void AllocateCachedTarget(const skia::SkottieAnimation* skottie_animation,
+ const math::SizeF& size,
+ TargetInfo* out_target_info);
// Allocate an uncached render target. The contents of the target cannot be
// reused in subsequent frames. If there was an error allocating the
@@ -122,6 +129,7 @@
std::unique_ptr<OffscreenAtlas> offscreen_cache_1d_;
std::vector<std::unique_ptr<OffscreenAtlas>> uncached_targets_;
+ std::vector<std::unique_ptr<OffscreenAtlas>> skottie_targets_;
// Align offscreen targets to a particular size to more efficiently use the
// offscreen target atlas. Use a power of 2 for the alignment so that a bit
diff --git a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
index 263afa5..ec33f8f 100644
--- a/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
+++ b/src/cobalt/renderer/rasterizer/egl/render_tree_node_visitor.cc
@@ -44,6 +44,7 @@
#include "cobalt/renderer/rasterizer/egl/draw_rrect_color_texture.h"
#include "cobalt/renderer/rasterizer/skia/hardware_image.h"
#include "cobalt/renderer/rasterizer/skia/image.h"
+#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
namespace cobalt {
namespace renderer {
@@ -111,6 +112,17 @@
0, 1);
}
+math::SizeF GetTransformScale(const math::Matrix3F& transform) {
+ math::PointF mapped_origin = transform * math::PointF(0, 0);
+ math::PointF mapped_x = transform * math::PointF(1, 0);
+ math::PointF mapped_y = transform * math::PointF(0, 1);
+ math::Vector2dF mapped_vecx(mapped_x.x() - mapped_origin.x(),
+ mapped_x.y() - mapped_origin.y());
+ math::Vector2dF mapped_vecy(mapped_y.x() - mapped_origin.x(),
+ mapped_y.y() - mapped_origin.y());
+ return math::SizeF(mapped_vecx.Length(), mapped_vecy.Length());
+}
+
bool ImageNodeSupportedNatively(render_tree::ImageNode* image_node) {
// The image node may contain nothing. For example, when it represents a video
// element before any frame is decoded.
@@ -407,17 +419,9 @@
}
draw_state_.opacity = 1.0f;
- math::PointF mapped_origin = draw_state_.transform * math::PointF(0, 0);
- math::PointF mapped_x = draw_state_.transform * math::PointF(1, 0);
- math::PointF mapped_y = draw_state_.transform * math::PointF(0, 1);
- math::Vector2dF mapped_vecx(mapped_x.x() - mapped_origin.x(),
- mapped_x.y() - mapped_origin.y());
- math::Vector2dF mapped_vecy(mapped_y.x() - mapped_origin.x(),
- mapped_y.y() - mapped_origin.y());
- float scale_x = mapped_vecx.Length();
- float scale_y = mapped_vecy.Length();
- draw_state_.transform = math::ScaleMatrix(std::max(1.0f, scale_x),
- std::max(1.0f, scale_y));
+ math::SizeF scale = GetTransformScale(draw_state_.transform);
+ draw_state_.transform = math::ScaleMatrix(
+ std::max(1.0f, scale.width()), std::max(1.0f, scale.height()));
// Don't clip the source since it is in its own local space.
bool limit_to_screen_size = false;
@@ -672,12 +676,63 @@
}
void RenderTreeNodeVisitor::Visit(render_tree::LottieNode* lottie_node) {
- if (!IsVisible(lottie_node->GetBounds())) {
+ const render_tree::LottieNode::Builder& data = lottie_node->data();
+ math::RectF content_rect = data.destination_rect;
+ if (!IsVisible(content_rect)) {
return;
}
- // Use Skottie to render Lottie animations.
- FallbackRasterize(lottie_node);
+ skia::SkottieAnimation* animation =
+ base::polymorphic_downcast<skia::SkottieAnimation*>(data.animation.get());
+ if (animation->GetSize().GetArea() == 0) {
+ return;
+ }
+ animation->SetAnimationTime(data.animation_time);
+
+ // Get an offscreen target to cache the animation. Make the target big enough
+ // to avoid scaling artifacts.
+ math::SizeF scale = GetTransformScale(draw_state_.transform);
+ math::SizeF mapped_size = content_rect.size();
+ // Use a uniform scale to avoid impacting aspect ratio calculations.
+ mapped_size.Scale(std::max(scale.width(), scale.height()));
+
+ OffscreenTargetManager::TargetInfo target_info;
+ if (!offscreen_target_manager_->GetCachedTarget(
+ animation, mapped_size, &target_info)) {
+ // No pre-existing target was found. Allocate a new target.
+ animation->ResetRenderCache();
+ offscreen_target_manager_->AllocateCachedTarget(
+ animation, mapped_size, &target_info);
+ }
+ if (target_info.framebuffer == nullptr) {
+ // Unable to allocate the render target for the animation cache.
+ return;
+ }
+
+ // Add a draw call to update the cache.
+ std::unique_ptr<DrawObject> update_cache(new DrawCallback(
+ base::Bind(&skia::SkottieAnimation::UpdateRenderCache,
+ base::Unretained(animation),
+ base::Unretained(target_info.skia_canvas),
+ target_info.region.size())));
+ draw_object_manager_->AddBatchedExternalDraw(
+ std::move(update_cache), lottie_node->GetTypeId(),
+ target_info.framebuffer, target_info.region);
+
+ // Add a draw call to render the cached animation to the current target.
+ backend::TextureEGL* texture = target_info.framebuffer->GetColorTexture();
+ math::Matrix3F texcoord_transform = GetTexcoordTransform(target_info);
+ if (IsOpaque(draw_state_.opacity)) {
+ std::unique_ptr<DrawObject> draw(
+ new DrawRectTexture(graphics_state_, draw_state_, content_rect, texture,
+ texcoord_transform));
+ AddDraw(std::move(draw), content_rect, DrawObjectManager::kBlendSrcAlpha);
+ } else {
+ std::unique_ptr<DrawObject> draw(new DrawRectColorTexture(
+ graphics_state_, draw_state_, content_rect, kOpaqueWhite, texture,
+ texcoord_transform, false /* clamp_texcoords */));
+ AddDraw(std::move(draw), content_rect, DrawObjectManager::kBlendSrcAlpha);
+ }
}
void RenderTreeNodeVisitor::Visit(
diff --git a/src/cobalt/renderer/rasterizer/pixel_test.cc b/src/cobalt/renderer/rasterizer/pixel_test.cc
index 464ef3d..a751832 100644
--- a/src/cobalt/renderer/rasterizer/pixel_test.cc
+++ b/src/cobalt/renderer/rasterizer/pixel_test.cc
@@ -4538,6 +4538,61 @@
TestTree(lottie_node);
}
+TEST_F(PixelTest, LottiePreserveAspectRatioTooShortAnimationTest) {
+ std::vector<uint8> animation_data =
+ GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+ scoped_refptr<LottieAnimation> animation =
+ GetResourceProvider()->CreateLottieAnimation(
+ reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+ LottieAnimation::LottieProperties lottie_properties;
+ lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+ animation->BeginRenderFrame(lottie_properties);
+
+ LottieNode::Builder node_builder =
+ LottieNode::Builder(animation, RectF(output_surface_size().width(),
+ 100.0f));
+ node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+ scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+ TestTree(lottie_node);
+}
+
+TEST_F(PixelTest, LottiePreserveAspectRatioTooNarrowAnimationTest) {
+ std::vector<uint8> animation_data =
+ GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+ scoped_refptr<LottieAnimation> animation =
+ GetResourceProvider()->CreateLottieAnimation(
+ reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+ LottieAnimation::LottieProperties lottie_properties;
+ lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+ animation->BeginRenderFrame(lottie_properties);
+
+ LottieNode::Builder node_builder =
+ LottieNode::Builder(animation, RectF(100.0f,
+ output_surface_size().height()));
+ node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+ scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+ TestTree(lottie_node);
+}
+
+TEST_F(PixelTest, LottieScaledWideAnimationTest) {
+ std::vector<uint8> animation_data =
+ GetFileData(GetTestFilePath("white_material_wave_loading.json"));
+ scoped_refptr<LottieAnimation> animation =
+ GetResourceProvider()->CreateLottieAnimation(
+ reinterpret_cast<char*>(&animation_data[0]), animation_data.size());
+ LottieAnimation::LottieProperties lottie_properties;
+ lottie_properties.UpdateState(LottieAnimation::LottieState::kPlaying);
+ animation->BeginRenderFrame(lottie_properties);
+
+ LottieNode::Builder node_builder =
+ LottieNode::Builder(animation, RectF(output_surface_size()));
+ node_builder.animation_time = base::TimeDelta::FromSecondsD(0);
+ scoped_refptr<LottieNode> lottie_node = new LottieNode(node_builder);
+ TestTree(new MatrixTransformNode(lottie_node,
+ ScaleMatrix(4.0f, 1.0f) *
+ TranslateMatrix(output_surface_size().width() * -0.5f, 0.0f)));
+}
+
#endif // !SB_HAS(BLITTER)
} // namespace rasterizer
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
index 4fae87f..224b4a9 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.cc
@@ -15,6 +15,7 @@
#include "cobalt/renderer/rasterizer/skia/skottie_animation.h"
#include "base/bind.h"
+#include "third_party/skia/include/core/SkRect.h"
namespace cobalt {
namespace renderer {
@@ -23,6 +24,7 @@
SkottieAnimation::SkottieAnimation(const char* data, size_t length)
: last_updated_animation_time_(base::TimeDelta()) {
+ ResetRenderCache();
skottie::Animation::Builder builder;
skottie_animation_ = builder.make(data, length);
animation_size_ = math::Size(skottie_animation_->size().width(),
@@ -72,6 +74,11 @@
return;
}
+ // Do not update the animation time if it has already reached the last frame.
+ if (is_complete_) {
+ return;
+ }
+
base::TimeDelta current_animation_time = last_updated_animation_time_;
base::TimeDelta time_elapsed =
animate_function_time - last_updated_animate_function_time_;
@@ -124,6 +131,21 @@
animate_function_time);
}
+void SkottieAnimation::UpdateRenderCache(SkCanvas* render_target,
+ const math::SizeF& size) {
+ DCHECK(render_target);
+ if (cached_animation_time_ == last_updated_animation_time_) {
+ // The render cache is already up-to-date.
+ return;
+ }
+
+ cached_animation_time_ = last_updated_animation_time_;
+ SkRect bounding_rect = SkRect::MakeWH(size.width(), size.height());
+ render_target->clear(SK_ColorTRANSPARENT);
+ skottie_animation_->render(render_target, &bounding_rect);
+ render_target->flush();
+}
+
void SkottieAnimation::UpdateAnimationFrameAndAnimateFunctionTimes(
base::TimeDelta current_animation_time,
base::TimeDelta current_animate_function_time) {
diff --git a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
index 3d5531c..0acb854 100644
--- a/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
+++ b/src/cobalt/renderer/rasterizer/skia/skottie_animation.h
@@ -15,7 +15,9 @@
#ifndef COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
#define COBALT_RENDERER_RASTERIZER_SKIA_SKOTTIE_ANIMATION_H_
+#include "cobalt/math/size_f.h"
#include "cobalt/render_tree/lottie_animation.h"
+#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/modules/skottie/include/Skottie.h"
namespace cobalt {
@@ -41,6 +43,12 @@
sk_sp<skottie::Animation> GetSkottieAnimation() { return skottie_animation_; }
+ // Rendering the lottie animation can be CPU-intensive. To minimize the cost,
+ // the animation can be updated in an offscreen render target only as needed,
+ // then the offscreen target rendered to the screen.
+ void ResetRenderCache() { cached_animation_time_ = base::TimeDelta::Min(); }
+ void UpdateRenderCache(SkCanvas* render_target, const math::SizeF& size);
+
private:
void UpdateAnimationFrameAndAnimateFunctionTimes(
base::TimeDelta current_animation_time,
@@ -79,6 +87,9 @@
// The most recently updated frame time for |skottie_animation_|.
base::TimeDelta last_updated_animation_time_;
+
+ // This is the animation time used for the last cache update.
+ base::TimeDelta cached_animation_time_;
};
} // namespace skia
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png
new file mode 100644
index 0000000..803f356
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooNarrowAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png
new file mode 100644
index 0000000..804797d
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottiePreserveAspectRatioTooShortAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png b/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png
new file mode 100644
index 0000000..86784fd
--- /dev/null
+++ b/src/cobalt/renderer/rasterizer/testdata/LottieScaledWideAnimationTest-expected.png
Binary files differ
diff --git a/src/cobalt/renderer/render_tree_pixel_tester.cc b/src/cobalt/renderer/render_tree_pixel_tester.cc
index f617aab..d6e8be3 100644
--- a/src/cobalt/renderer/render_tree_pixel_tester.cc
+++ b/src/cobalt/renderer/render_tree_pixel_tester.cc
@@ -77,6 +77,12 @@
// static
bool RenderTreePixelTester::IsReferencePlatform() {
+#if SB_API_VERSION < 12 && SB_HAS(BLITTER)
+ // The blitter rasterizer often relies on software rendering which may not
+ // produce the same results as GPU-based rendering.
+ return false;
+#endif
+
const char* rasterizer_type =
configuration::Configuration::GetInstance()->CobaltRasterizerType();
const bool is_opengles_rasterizer =
diff --git a/src/cobalt/script/v8c/v8c_global_environment.cc b/src/cobalt/script/v8c/v8c_global_environment.cc
index 010b61d..4d43ade 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.cc
+++ b/src/cobalt/script/v8c/v8c_global_environment.cc
@@ -105,8 +105,6 @@
"V8cGlobalEnvironment::~V8cGlobalEnvironment()");
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
InvalidateWeakPtrs();
- destruction_helper_.global_wrappable_ = global_wrappable_.get();
- destruction_helper_.wrapper_factory_ = wrapper_factory_.get();
}
void V8cGlobalEnvironment::CreateGlobalObject() {
@@ -322,13 +320,6 @@
// Another GC to make sure global object is collected.
isolate_->LowMemoryNotification();
isolate_->SetEmbedderHeapTracer(nullptr);
- if (global_wrappable_ && global_wrappable_->RefCounts() != 1) {
- // At this point only environment should hold the last reference.
- DLOG(INFO) << "[Temporary debugging] more than one ref alive, ref_count: "
- << global_wrappable_->RefCounts();
- DLOG(INFO) << "[Temporary debugging] window's v8 wrapper alive?"
- << wrapper_factory_->HasWrapper(global_wrappable_);
- }
}
// static
diff --git a/src/cobalt/script/v8c/v8c_global_environment.h b/src/cobalt/script/v8c/v8c_global_environment.h
index be1f6aa..c131a8d 100644
--- a/src/cobalt/script/v8c/v8c_global_environment.h
+++ b/src/cobalt/script/v8c/v8c_global_environment.h
@@ -132,9 +132,6 @@
explicit DestructionHelper(v8::Isolate* isolate) : isolate_(isolate) {}
~DestructionHelper();
- script::Wrappable* global_wrappable_;
- WrapperFactory* wrapper_factory_;
-
private:
v8::Isolate* isolate_;
};
@@ -165,10 +162,10 @@
// destruct. Were we to not do this, finalizers can run in the order (e.g.)
// document window, which the Cobalt DOM does not support.
scoped_refptr<Wrappable> global_wrappable_;
- std::unique_ptr<WrapperFactory> wrapper_factory_;
DestructionHelper destruction_helper_;
v8::Global<v8::Context> context_;
+ std::unique_ptr<WrapperFactory> wrapper_factory_;
std::unique_ptr<V8cScriptValueFactory> script_value_factory_;
// Data that is cached on a per-interface basis. Note that we can get to
diff --git a/src/cobalt/script/v8c/wrapper_factory.cc b/src/cobalt/script/v8c/wrapper_factory.cc
index 27c3463..e123d1d 100644
--- a/src/cobalt/script/v8c/wrapper_factory.cc
+++ b/src/cobalt/script/v8c/wrapper_factory.cc
@@ -38,17 +38,6 @@
<< "RegisterWrappableType registered for type more than once.";
}
-bool WrapperFactory::HasWrapper(Wrappable* wrappable) {
- v8::Local<v8::Object> wrapper;
- v8::MaybeLocal<v8::Object> maybe_wrapper =
- V8cWrapperHandle::MaybeGetObject(isolate_, GetCachedWrapper(wrappable));
- if (!maybe_wrapper.ToLocal(&wrapper)) {
- return false;
- } else {
- return true;
- }
-}
-
v8::Local<v8::Object> WrapperFactory::GetWrapper(
const scoped_refptr<Wrappable>& wrappable) {
v8::Local<v8::Object> wrapper;
diff --git a/src/cobalt/script/v8c/wrapper_factory.h b/src/cobalt/script/v8c/wrapper_factory.h
index 986e740..a78a9de 100644
--- a/src/cobalt/script/v8c/wrapper_factory.h
+++ b/src/cobalt/script/v8c/wrapper_factory.h
@@ -50,8 +50,6 @@
v8::Local<v8::Object> GetWrapper(const scoped_refptr<Wrappable>& wrappable);
- // Added temporarily for debugging purpose, will be removed soon.
- bool HasWrapper(Wrappable* wrappable);
// Attempt to get the |WrapperPrivate| associated with |wrappable|. Returns
// |nullptr| if no |WrapperPrivate| was found.
WrapperPrivate* MaybeGetWrapperPrivate(Wrappable* wrappable);
diff --git a/src/cobalt/site/docs/development/setup-android.md b/src/cobalt/site/docs/development/setup-android.md
index 676c2e1..4a6f2b0 100644
--- a/src/cobalt/site/docs/development/setup-android.md
+++ b/src/cobalt/site/docs/development/setup-android.md
@@ -37,6 +37,19 @@
python -m pip install requests
```
+1. Install ccache to support build acceleration. ccache is automatically used
+ when available, otherwise defaults to unaccelerated building:
+
+ ```
+ $ sudo apt-get install ccache
+ ```
+
+ We recommend adjusting the cache size as needed to increase cache hits:
+
+ ```
+ $ ccache --max-size=20G
+ ```
+
1. Download and install [Android Studio](https://developer.android.com/studio/).
1. Run `cobalt/build/gyp_cobalt android-x86` to configure the Cobalt build,
which also installs the SDK and NDK. (This step will have to be repeated
diff --git a/src/cobalt/site/docs/development/setup-linux.md b/src/cobalt/site/docs/development/setup-linux.md
index c0ac49e..3c4d49f 100644
--- a/src/cobalt/site/docs/development/setup-linux.md
+++ b/src/cobalt/site/docs/development/setup-linux.md
@@ -9,6 +9,8 @@
on the machine that you are using to view the client. For example, you cannot
SSH into another machine and run the binary on that machine.
+## Set up your workstation
+
1. Choose where you want to put the `depot_tools` directory, which is used
by the Cobalt code. An easy option is to put them in `~/depot_tools`.
Clone the tools by running the following command:
@@ -48,6 +50,19 @@
&& nvm use default
```
+1. Install ccache to support build acceleration. ccache is automatically used
+ when available, otherwise defaults to unaccelerated building:
+
+ ```
+ $ sudo apt install -qqy --no-install-recommends ccache
+ ```
+
+ We recommend adjusting the cache size as needed to increase cache hits:
+
+ ```
+ $ ccache --max-size=20G
+ ```
+
1. Clone the Cobalt code repository. The following `git` command creates a
`cobalt` directory that contains the repository:
@@ -55,6 +70,8 @@
$ git clone https://cobalt.googlesource.com/cobalt
```
+## Build and Run Cobalt
+
1. Build the code by navigating to the `src` directory in your new
`cobalt` directory and running the following command. You must
specify a platform when running this command. On Ubuntu Linux, the
diff --git a/src/cobalt/site/docs/development/setup-raspi.md b/src/cobalt/site/docs/development/setup-raspi.md
index 13936bb..3e88d8c 100644
--- a/src/cobalt/site/docs/development/setup-raspi.md
+++ b/src/cobalt/site/docs/development/setup-raspi.md
@@ -48,15 +48,27 @@
$ apt-get remove -y --purge --auto-remove libgl1-mesa-dev \
libegl1-mesa-dev libgles2-mesa libgles2-mesa-dev
$ apt-get install -y libpulse-dev libasound2-dev libavformat-dev \
- libavresample-dev
+ libavresample-dev rsync
```
## Set up your workstation
+<aside class="note">
+<b>Note:</b> Before proceeding further, refer to the documentation for ["Set up your environment - Linux"](setup-linux.md). Complete the section **Set up your workstation**, then return and complete the following steps.
+</aside>
+
The following steps install the cross-compiling toolchain on your workstation.
The toolchain runs on an x86 workstation and compiles binaries for your ARM
Raspberry Pi.
+1. Run the following command to install packages needed to build and run
+ Cobalt for Raspberry Pi:
+
+ ```
+ $ sudo apt install -qqy --no-install-recommends g++-multilib \
+ python-requests wget xz-utils
+ ```
+
1. Choose a location for the installed toolchain – e.g. `raspi-tools`
– and set `$RASPI_HOME` to that location.
@@ -94,10 +106,16 @@
## Build, install, and run Cobalt for Raspberry Pi
-1. Run the following commands to build Cobalt:
+1. Build the code by navigating to the src directory in your cobalt directory and run the
+ following command :
```
- $ gyp_cobalt raspi-2
+ $ cobalt/build/gyp_cobalt raspi-2
+ ```
+
+1. Compile the code from the `src/` directory:
+
+ ```
$ ninja -C out/raspi-2_debug cobalt
```
@@ -105,7 +123,7 @@
on the device:
```
- rsync -avzh --exclude="obj*" \
+ rsync -avzLPh --exclude="obj*" --exclude="gen/" \
$COBALT_SRC/out/raspi-2_debug pi@$RASPI_ADDR:~/
```
@@ -129,3 +147,21 @@
Note that you can also exit YouTube on Cobalt by hitting the `[Esc]` key
enough times to bring up the "Do you want to quit YouTube?" dialog and
selecting "yes".
+
+### Improving Cobalt performance on Raspberry Pi
+
+1. You will find that there are some processes installed by default that run on the
+ Raspberry Pi and can take away CPU time from Cobalt. You may wish to consider
+ disabling these processes for maximum (and more consistent) performance, as they
+ have been found to occasionally take >10% of the CPU according to `top`.
+ You can do this by typing:
+
+ ```
+ apt-get remove -y --auto-remove [PACKAGE_NAME, ...]
+ ```
+
+ For example:
+
+ ```
+ apt-get remove -y --auto-remove avahi-daemon
+ ```
diff --git a/src/cobalt/site/docs/reference/starboard/modules/10/file.md b/src/cobalt/site/docs/reference/starboard/modules/10/file.md
index b115501..b9c0e36 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/10/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/10/file.md
@@ -169,7 +169,7 @@
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.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
#### Declaration ####
diff --git a/src/cobalt/site/docs/reference/starboard/modules/10/media.md b/src/cobalt/site/docs/reference/starboard/modules/10/media.md
index 9b83d06..7271b29 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/10/media.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/10/media.md
@@ -665,6 +665,8 @@
outputs. If `true`, then non-protection-capable outputs are expected to be
blanked.
+presubmit: allow sb_export mismatch
+
#### Declaration ####
```
@@ -700,6 +702,8 @@
`enabled`: Indicates whether output protection is enabled (`true`) or disabled.
+presubmit: allow sb_export mismatch
+
#### Declaration ####
```
diff --git a/src/cobalt/site/docs/reference/starboard/modules/11/file.md b/src/cobalt/site/docs/reference/starboard/modules/11/file.md
index ad890dc..f910cb0 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/11/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/11/file.md
@@ -168,7 +168,7 @@
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.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
#### Declaration ####
diff --git a/src/cobalt/site/docs/reference/starboard/modules/11/media.md b/src/cobalt/site/docs/reference/starboard/modules/11/media.md
index 8d94612..377cb2b 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/11/media.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/11/media.md
@@ -659,6 +659,8 @@
outputs. If `true`, then non-protection-capable outputs are expected to be
blanked.
+presubmit: allow sb_export mismatch
+
#### Declaration ####
```
@@ -712,6 +714,8 @@
`enabled`: Indicates whether output protection is enabled (`true`) or disabled.
+presubmit: allow sb_export mismatch
+
#### Declaration ####
```
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md b/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
index 836c4b8..22c2b79 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/condition_variable.md
@@ -40,7 +40,7 @@
#### Definition ####
```
-typedef union SbConditionVariable SbConditionVariable
+typedef union SbConditionVariable SbConditionVariable
```
## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/file.md b/src/cobalt/site/docs/reference/starboard/modules/12/file.md
index c3ba551..e22edeb 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/file.md
@@ -184,7 +184,7 @@
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.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
#### Declaration ####
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md b/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
index 58f1907..a9d2a9a 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/mutex.md
@@ -40,7 +40,7 @@
#### Definition ####
```
-typedef union SbMutex SbMutex
+typedef union SbMutex SbMutex
```
## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/12/once.md b/src/cobalt/site/docs/reference/starboard/modules/12/once.md
index ce3c4d6..61aad5d 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/12/once.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/12/once.md
@@ -22,7 +22,7 @@
#### Definition ####
```
-typedef union SbOnceControl SbOnceControl
+typedef union SbOnceControl SbOnceControl
```
### SbOnceInitRoutine ###
diff --git a/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md b/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
index 836c4b8..22c2b79 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/condition_variable.md
@@ -40,7 +40,7 @@
#### Definition ####
```
-typedef union SbConditionVariable SbConditionVariable
+typedef union SbConditionVariable SbConditionVariable
```
## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/file.md b/src/cobalt/site/docs/reference/starboard/modules/file.md
index c3ba551..e22edeb 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/file.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/file.md
@@ -184,7 +184,7 @@
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.
+`path`: The absolute path of the file, symlink, or directory to be deleted.
#### Declaration ####
diff --git a/src/cobalt/site/docs/reference/starboard/modules/mutex.md b/src/cobalt/site/docs/reference/starboard/modules/mutex.md
index 58f1907..a9d2a9a 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/mutex.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/mutex.md
@@ -40,7 +40,7 @@
#### Definition ####
```
-typedef union SbMutex SbMutex
+typedef union SbMutex SbMutex
```
## Functions ##
diff --git a/src/cobalt/site/docs/reference/starboard/modules/once.md b/src/cobalt/site/docs/reference/starboard/modules/once.md
index ce3c4d6..61aad5d 100644
--- a/src/cobalt/site/docs/reference/starboard/modules/once.md
+++ b/src/cobalt/site/docs/reference/starboard/modules/once.md
@@ -22,7 +22,7 @@
#### Definition ####
```
-typedef union SbOnceControl SbOnceControl
+typedef union SbOnceControl SbOnceControl
```
### SbOnceInitRoutine ###
diff --git a/src/cobalt/speech/audio_encoder_flac.cc b/src/cobalt/speech/audio_encoder_flac.cc
index c6899de..279b5fe 100644
--- a/src/cobalt/speech/audio_encoder_flac.cc
+++ b/src/cobalt/speech/audio_encoder_flac.cc
@@ -56,18 +56,18 @@
FLAC__stream_encoder_delete(encoder_);
}
-void AudioEncoderFlac::Encode(const ShellAudioBus* audio_bus) {
+void AudioEncoderFlac::Encode(const AudioBus* audio_bus) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK_EQ(audio_bus->channels(), size_t(1));
uint32 frames = static_cast<uint32>(audio_bus->frames());
std::unique_ptr<FLAC__int32[]> flac_samples(new FLAC__int32[frames]);
for (uint32 i = 0; i < frames; ++i) {
- if (audio_bus->sample_type() == ShellAudioBus::kFloat32) {
+ if (audio_bus->sample_type() == AudioBus::kFloat32) {
flac_samples[i] = static_cast<FLAC__int32>(
audio_bus->GetFloat32Sample(0, i) * kMaxInt16AsFloat32);
} else {
- DCHECK_EQ(audio_bus->sample_type(), ShellAudioBus::kInt16);
+ DCHECK_EQ(audio_bus->sample_type(), AudioBus::kInt16);
flac_samples[i] =
static_cast<FLAC__int32>(audio_bus->GetInt16Sample(0, i));
}
diff --git a/src/cobalt/speech/audio_encoder_flac.h b/src/cobalt/speech/audio_encoder_flac.h
index 71b1822..3317af9 100644
--- a/src/cobalt/speech/audio_encoder_flac.h
+++ b/src/cobalt/speech/audio_encoder_flac.h
@@ -20,7 +20,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/threading/thread_checker.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "third_party/flac/include/FLAC/stream_encoder.h"
namespace cobalt {
@@ -29,13 +29,13 @@
// Encode raw audio to using FLAC codec.
class AudioEncoderFlac {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
explicit AudioEncoderFlac(int sample_rate);
~AudioEncoderFlac();
// Encode raw audio data.
- void Encode(const ShellAudioBus* audio_bus);
+ void Encode(const AudioBus* audio_bus);
// Finish encoding.
void Finish();
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.cc b/src/cobalt/speech/cobalt_speech_recognizer.cc
index eb9fd8d..0d61e70 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.cc
+++ b/src/cobalt/speech/cobalt_speech_recognizer.cc
@@ -123,7 +123,7 @@
}
void CobaltSpeechRecognizer::OnDataReceived(
- std::unique_ptr<ShellAudioBus> audio_bus) {
+ std::unique_ptr<AudioBus> audio_bus) {
if (endpointer_delegate_.IsFirstTimeSoundStarted(*audio_bus)) {
RunEventCallback(new dom::Event(base::Tokens::soundstart()));
}
@@ -135,8 +135,8 @@
// silence at the end in case encoder had no data already.
size_t dummy_frames =
static_cast<size_t>(kSampleRate * kAudioPacketDurationInSeconds);
- std::unique_ptr<ShellAudioBus> dummy_audio_bus(new ShellAudioBus(
- 1, dummy_frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ std::unique_ptr<AudioBus> dummy_audio_bus(
+ new AudioBus(1, dummy_frames, AudioBus::kInt16, AudioBus::kInterleaved));
dummy_audio_bus->ZeroAllFrames();
service_->RecognizeAudio(std::move(dummy_audio_bus), true);
}
diff --git a/src/cobalt/speech/cobalt_speech_recognizer.h b/src/cobalt/speech/cobalt_speech_recognizer.h
index f9ca9d4..d67ab5e 100644
--- a/src/cobalt/speech/cobalt_speech_recognizer.h
+++ b/src/cobalt/speech/cobalt_speech_recognizer.h
@@ -18,7 +18,7 @@
#include <memory>
#include <string>
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/network/network_module.h"
#include "cobalt/speech/endpointer_delegate.h"
#include "cobalt/speech/google_speech_service.h"
@@ -39,7 +39,7 @@
// from there.
class CobaltSpeechRecognizer : public SpeechRecognizer {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
CobaltSpeechRecognizer(network::NetworkModule* network_module,
const Microphone::Options& microphone_options,
@@ -51,7 +51,7 @@
private:
// Callbacks from mic.
- void OnDataReceived(std::unique_ptr<ShellAudioBus> audio_bus);
+ void OnDataReceived(std::unique_ptr<AudioBus> audio_bus);
void OnDataCompletion();
void OnMicrophoneError(MicrophoneManager::MicrophoneError error,
std::string error_message);
diff --git a/src/cobalt/speech/endpointer_delegate.cc b/src/cobalt/speech/endpointer_delegate.cc
index 6340eea..1fc2658 100644
--- a/src/cobalt/speech/endpointer_delegate.cc
+++ b/src/cobalt/speech/endpointer_delegate.cc
@@ -45,8 +45,7 @@
void EndPointerDelegate::Stop() { endpointer_.EndSession(); }
-bool EndPointerDelegate::IsFirstTimeSoundStarted(
- const ShellAudioBus& audio_bus) {
+bool EndPointerDelegate::IsFirstTimeSoundStarted(const AudioBus& audio_bus) {
if (is_first_time_sound_started_) {
return false;
}
diff --git a/src/cobalt/speech/endpointer_delegate.h b/src/cobalt/speech/endpointer_delegate.h
index fe2f18b..e222f02 100644
--- a/src/cobalt/speech/endpointer_delegate.h
+++ b/src/cobalt/speech/endpointer_delegate.h
@@ -15,8 +15,8 @@
#ifndef COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
#define COBALT_SPEECH_ENDPOINTER_DELEGATE_H_
+#include "cobalt/media/base/audio_bus.h"
#include "content/browser/speech/endpointer/endpointer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
namespace cobalt {
namespace speech {
@@ -25,7 +25,7 @@
// speech session (from start speaking to end of speaking).
class EndPointerDelegate {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
explicit EndPointerDelegate(int sample_rate);
~EndPointerDelegate();
@@ -36,7 +36,7 @@
void Stop();
// Return true if it is the first time that the sound started.
- bool IsFirstTimeSoundStarted(const ShellAudioBus& audio_bus);
+ bool IsFirstTimeSoundStarted(const AudioBus& audio_bus);
private:
// Used for detecting sound start event.
diff --git a/src/cobalt/speech/google_speech_service.cc b/src/cobalt/speech/google_speech_service.cc
index 83ef7ec..0bd11ac 100644
--- a/src/cobalt/speech/google_speech_service.cc
+++ b/src/cobalt/speech/google_speech_service.cc
@@ -220,8 +220,8 @@
base::Bind(&GoogleSpeechService::StopInternal, base::Unretained(this)));
}
-void GoogleSpeechService::RecognizeAudio(
- std::unique_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+void GoogleSpeechService::RecognizeAudio(std::unique_ptr<AudioBus> audio_bus,
+ bool is_last_chunk) {
// Called by the speech recognition manager thread.
thread_.message_loop()->task_runner()->PostTask(
FROM_HERE, base::Bind(&GoogleSpeechService::UploadAudioDataInternal,
@@ -393,7 +393,7 @@
}
void GoogleSpeechService::UploadAudioDataInternal(
- std::unique_ptr<ShellAudioBus> audio_bus, bool is_last_chunk) {
+ std::unique_ptr<AudioBus> audio_bus, bool is_last_chunk) {
DCHECK_EQ(thread_.message_loop(), base::MessageLoop::current());
DCHECK(audio_bus);
diff --git a/src/cobalt/speech/google_speech_service.h b/src/cobalt/speech/google_speech_service.h
index b0002ae..a1ea1e5 100644
--- a/src/cobalt/speech/google_speech_service.h
+++ b/src/cobalt/speech/google_speech_service.h
@@ -21,7 +21,7 @@
#include "base/threading/thread.h"
#include "cobalt/loader/url_fetcher_string_writer.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/network/network_module.h"
#include "cobalt/speech/audio_encoder_flac.h"
#include "cobalt/speech/google_streaming_api.pb.h"
@@ -43,7 +43,7 @@
// manager.
class GoogleSpeechService : public net::URLFetcherDelegate {
public:
- typedef media::ShellAudioBus ShellAudioBus;
+ typedef media::AudioBus AudioBus;
typedef base::Callback<void(const scoped_refptr<dom::Event>&)> EventCallback;
typedef SpeechRecognitionResultList::SpeechRecognitionResults
SpeechRecognitionResults;
@@ -63,8 +63,7 @@
// Stop speech recognizer.
void Stop();
// An encoded audio data is available and ready to be recognized.
- void RecognizeAudio(std::unique_ptr<ShellAudioBus> audio_bus,
- bool is_last_chunk);
+ void RecognizeAudio(std::unique_ptr<AudioBus> audio_bus, bool is_last_chunk);
// net::URLFetcherDelegate interface
void OnURLFetchDownloadProgress(const net::URLFetcher* source,
@@ -81,7 +80,7 @@
void StopInternal();
// This method handles wrappables and should run on the MainWebModule thread.
void ClearFinalResults();
- void UploadAudioDataInternal(std::unique_ptr<ShellAudioBus> audio_bus,
+ void UploadAudioDataInternal(std::unique_ptr<AudioBus> audio_bus,
bool is_last_chunk);
// This method handles wrappables, and so it must run on the MainWebModule.
void ProcessAndFireSuccessEvent(proto::SpeechRecognitionEvent event);
diff --git a/src/cobalt/speech/microphone_fake.cc b/src/cobalt/speech/microphone_fake.cc
index 15c955a..a85c191 100644
--- a/src/cobalt/speech/microphone_fake.cc
+++ b/src/cobalt/speech/microphone_fake.cc
@@ -24,14 +24,14 @@
#include "base/path_service.h"
#include "base/rand_util.h"
#include "cobalt/audio/audio_file_reader.h"
-#include "starboard/file.h"
+#include "starboard/common/file.h"
#include "starboard/memory.h"
#include "starboard/time.h"
namespace cobalt {
namespace speech {
-typedef audio::ShellAudioBus ShellAudioBus;
+typedef audio::AudioBus AudioBus;
namespace {
@@ -89,10 +89,10 @@
} else {
file_length_ = std::min(options.audio_data_size, kMaxBufferSize);
DCHECK_GT(file_length_, 0);
- audio_bus_.reset(new ShellAudioBus(
- kSupportedMonoChannel,
- file_length_ / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
- ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ audio_bus_.reset(
+ new AudioBus(kSupportedMonoChannel,
+ file_length_ / audio::GetSampleTypeSize(AudioBus::kInt16),
+ AudioBus::kInt16, AudioBus::kInterleaved));
SbMemoryCopy(audio_bus_->interleaved_data(), options.external_audio_data,
file_length_);
}
@@ -131,14 +131,14 @@
const float kSupportedSampleRate = 16000.0f;
if (!reader) {
// If it is not a WAV file, read audio data as raw audio.
- audio_bus_.reset(new ShellAudioBus(
+ audio_bus_.reset(new AudioBus(
kSupportedMonoChannel,
- file_buffer_size / audio::GetSampleTypeSize(ShellAudioBus::kInt16),
- ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
+ file_buffer_size / audio::GetSampleTypeSize(AudioBus::kInt16),
+ AudioBus::kInt16, AudioBus::kInterleaved));
SbMemoryCopy(audio_bus_->interleaved_data(), audio_input.get(),
file_buffer_size);
file_length_ = file_buffer_size;
- } else if (reader->sample_type() != ShellAudioBus::kInt16 ||
+ } else if (reader->sample_type() != AudioBus::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
diff --git a/src/cobalt/speech/microphone_fake.h b/src/cobalt/speech/microphone_fake.h
index ac4cf8b..3f6cbbc 100644
--- a/src/cobalt/speech/microphone_fake.h
+++ b/src/cobalt/speech/microphone_fake.h
@@ -52,7 +52,7 @@
bool read_data_from_file_;
std::vector<base::FilePath> file_paths_;
- std::unique_ptr<audio::ShellAudioBus> audio_bus_;
+ std::unique_ptr<audio::AudioBus> audio_bus_;
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 a957f2d..cddf31e 100644
--- a/src/cobalt/speech/microphone_manager.cc
+++ b/src/cobalt/speech/microphone_manager.cc
@@ -145,9 +145,9 @@
// If |read_bytes| is zero, nothing should happen.
if (read_bytes > 0 && read_bytes % sizeof(int16_t) == 0) {
size_t frames = read_bytes / sizeof(int16_t);
- std::unique_ptr<ShellAudioBus> output_audio_bus(new ShellAudioBus(
- 1, frames, ShellAudioBus::kInt16, ShellAudioBus::kInterleaved));
- ShellAudioBus source(1, frames, samples);
+ std::unique_ptr<AudioBus> output_audio_bus(
+ new AudioBus(1, frames, AudioBus::kInt16, AudioBus::kInterleaved));
+ AudioBus source(1, frames, samples);
output_audio_bus->Assign(source);
data_received_callback_.Run(std::move(output_audio_bus));
} else if (read_bytes != 0) {
diff --git a/src/cobalt/speech/microphone_manager.h b/src/cobalt/speech/microphone_manager.h
index 765d2b6..602f6bf 100644
--- a/src/cobalt/speech/microphone_manager.h
+++ b/src/cobalt/speech/microphone_manager.h
@@ -25,7 +25,7 @@
#include "base/threading/thread.h"
#include "base/timer/timer.h"
#include "cobalt/dom/event.h"
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "cobalt/speech/microphone.h"
namespace cobalt {
@@ -39,9 +39,8 @@
kAudioCapture,
kAborted,
};
- typedef media::ShellAudioBus ShellAudioBus;
- typedef base::Callback<void(std::unique_ptr<ShellAudioBus>)>
- DataReceivedCallback;
+ typedef media::AudioBus AudioBus;
+ typedef base::Callback<void(std::unique_ptr<AudioBus>)> DataReceivedCallback;
typedef base::Closure CompletionCallback;
typedef base::Closure SuccessfulOpenCallback;
typedef base::Callback<void(MicrophoneError, std::string)> ErrorCallback;
diff --git a/src/cobalt/ui_navigation/nav_item.cc b/src/cobalt/ui_navigation/nav_item.cc
index 05f49d6..8eaf1f3 100644
--- a/src/cobalt/ui_navigation/nav_item.cc
+++ b/src/cobalt/ui_navigation/nav_item.cc
@@ -31,7 +31,9 @@
onfocus_callback_(onfocus_callback),
onscroll_callback_(onscroll_callback),
nav_item_type_(type),
- nav_item_(GetInterface().create_item(type, &s_callbacks_, this)) {}
+ nav_item_(GetInterface().create_item(type, &s_callbacks_, this)) {
+ SbAtomicNoBarrier_Store8(&enabled_, 0);
+}
NavItem::~NavItem() {
GetInterface().set_item_enabled(nav_item_, false);
diff --git a/src/cobalt/ui_navigation/nav_item.h b/src/cobalt/ui_navigation/nav_item.h
index 64d6aac..c43662f 100644
--- a/src/cobalt/ui_navigation/nav_item.h
+++ b/src/cobalt/ui_navigation/nav_item.h
@@ -18,6 +18,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "cobalt/ui_navigation/interface.h"
+#include "starboard/atomic.h"
namespace cobalt {
namespace ui_navigation {
@@ -39,10 +40,19 @@
}
void Focus() {
- GetInterface().set_focus(nav_item_);
+ if (SbAtomicNoBarrier_Load8(&enabled_)) {
+ GetInterface().set_focus(nav_item_);
+ }
+ }
+
+ void UnfocusAll() {
+#if SB_API_VERSION >= SB_UI_NAVIGATION2_VERSION
+ GetInterface().set_focus(kNativeItemInvalid);
+#endif
}
void SetEnabled(bool enabled) {
+ SbAtomicNoBarrier_Store8(&enabled_, enabled ? 1 : 0);
GetInterface().set_item_enabled(nav_item_, enabled);
}
@@ -104,6 +114,7 @@
NativeItemType nav_item_type_;
NativeItem nav_item_;
+ SbAtomic8 enabled_;
static NativeCallbacks s_callbacks_;
};
diff --git a/src/cobalt/updater/configurator.cc b/src/cobalt/updater/configurator.cc
index d626083..210f088 100644
--- a/src/cobalt/updater/configurator.cc
+++ b/src/cobalt/updater/configurator.cc
@@ -31,6 +31,7 @@
#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
const std::set<std::string> valid_channels = {"dev"};
+const std::string kDefaultUpdaterChannel = "dev";
#elif defined(COBALT_BUILD_TYPE_QA)
// Find more information about these test channels in the Evergreen test plan.
const std::set<std::string> valid_channels = {
@@ -51,8 +52,10 @@
// Test an update that's larger than the available storage on the device
"tistore",
};
+const std::string kDefaultUpdaterChannel = "qa";
#elif defined(COBALT_BUILD_TYPE_GOLD)
const std::set<std::string> valid_channels = {"prod", "dogfood"};
+const std::string kDefaultUpdaterChannel = "prod";
#endif
std::string GetDeviceProperty(SbSystemPropertyId id) {
@@ -74,10 +77,21 @@
Configurator::Configurator(network::NetworkModule* network_module)
: pref_service_(CreatePrefService()),
+ persisted_data_(std::make_unique<update_client::PersistedData>(
+ pref_service_.get(), nullptr)),
+ is_channel_changed_(0),
unzip_factory_(base::MakeRefCounted<UnzipperFactory>()),
network_fetcher_factory_(
base::MakeRefCounted<NetworkFetcherFactoryCobalt>(network_module)),
- patch_factory_(base::MakeRefCounted<PatcherFactory>()) {}
+ patch_factory_(base::MakeRefCounted<PatcherFactory>()) {
+ const std::string persisted_channel =
+ persisted_data_->GetUpdaterChannel(GetAppGuid());
+ if (persisted_channel.empty()) {
+ SetChannel(kDefaultUpdaterChannel);
+ } else {
+ SetChannel(persisted_channel);
+ }
+}
Configurator::~Configurator() = default;
int Configurator::InitialDelay() const { return 0; }
@@ -134,9 +148,9 @@
params.insert(std::make_pair("sbversion", std::to_string(SB_API_VERSION)));
params.insert(std::make_pair(
"jsengine", script::GetJavaScriptEngineNameAndVersion()));
- params.insert(std::make_pair("updaterchannelchanged",
- IsChannelChanged() ? "True" : "False"));
-
+ params.insert(std::make_pair(
+ "updaterchannelchanged",
+ SbAtomicNoBarrier_Load(&is_channel_changed_) == 1 ? "True" : "False"));
// Brand name
params.insert(
std::make_pair("brand", GetDeviceProperty(kSbSystemPropertyBrandName)));
@@ -200,6 +214,10 @@
return {};
}
+void Configurator::CompareAndSwapChannelChanged(int old_value, int new_value) {
+ SbAtomicNoBarrier_CompareAndSwap(&is_channel_changed_, old_value, new_value);
+}
+
// The updater channel is get and set by main web module thread and update
// client thread. The getter and set use a lock to prevent synchronization
// issue.
@@ -211,6 +229,7 @@
void Configurator::SetChannel(const std::string& updater_channel) {
base::AutoLock auto_lock(updater_channel_lock_);
updater_channel_ = updater_channel;
+ persisted_data_->SetUpdaterChannel(GetAppGuid(), updater_channel);
}
bool Configurator::IsChannelValid(const std::string& channel) {
diff --git a/src/cobalt/updater/configurator.h b/src/cobalt/updater/configurator.h
index 8012b2f..e335b4f 100644
--- a/src/cobalt/updater/configurator.h
+++ b/src/cobalt/updater/configurator.h
@@ -17,6 +17,7 @@
#include "base/synchronization/lock.h"
#include "cobalt/network/network_module.h"
#include "components/update_client/configurator.h"
+#include "components/update_client/persisted_data.h"
class GURL;
class PrefService;
@@ -76,8 +77,8 @@
void SetChannel(const std::string& updater_channel) override;
- void MarkChannelChanged() { is_channel_changed = true; }
- bool IsChannelChanged() const override { return is_channel_changed; }
+ void CompareAndSwapChannelChanged(int old_value, int new_value) override;
+
bool IsChannelValid(const std::string& channel);
std::string GetUpdaterStatus() const;
@@ -88,12 +89,13 @@
~Configurator() override;
std::unique_ptr<PrefService> pref_service_;
+ std::unique_ptr<update_client::PersistedData> persisted_data_;
scoped_refptr<update_client::NetworkFetcherFactory> network_fetcher_factory_;
scoped_refptr<update_client::UnzipperFactory> unzip_factory_;
scoped_refptr<update_client::PatcherFactory> patch_factory_;
std::string updater_channel_;
base::Lock updater_channel_lock_;
- bool is_channel_changed = false;
+ SbAtomic32 is_channel_changed_;
std::string updater_status_;
base::Lock updater_status_lock_;
diff --git a/src/cobalt/updater/crash_sandbox.cc b/src/cobalt/updater/crash_sandbox.cc
index 8f69c81..869c589 100644
--- a/src/cobalt/updater/crash_sandbox.cc
+++ b/src/cobalt/updater/crash_sandbox.cc
@@ -18,5 +18,6 @@
#include "starboard/event.h"
void SbEventHandle(const SbEvent* event) {
- SB_CHECK(false);
+ volatile int* a = (int*)(NULL);
+ *a = 1;
}
diff --git a/src/cobalt/updater/updater_module.cc b/src/cobalt/updater/updater_module.cc
index 0a7904e..2f0452b 100644
--- a/src/cobalt/updater/updater_module.cc
+++ b/src/cobalt/updater/updater_module.cc
@@ -246,6 +246,10 @@
base::TimeDelta::FromHours(kNextUpdateCheckHours));
}
+void UpdaterModule::CompareAndSwapChannelChanged(int old_value, int new_value) {
+ updater_configurator_->CompareAndSwapChannelChanged(old_value, new_value);
+}
+
// The following three methods all called by the main web module thread.
std::string UpdaterModule::GetUpdaterChannel() const {
return updater_configurator_->GetChannel();
diff --git a/src/cobalt/updater/updater_module.h b/src/cobalt/updater/updater_module.h
index c441d1d..d275556 100644
--- a/src/cobalt/updater/updater_module.h
+++ b/src/cobalt/updater/updater_module.h
@@ -65,8 +65,7 @@
std::string GetUpdaterChannel() const;
void SetUpdaterChannel(const std::string& updater_channel);
- void MarkChannelChanged() { updater_configurator_->MarkChannelChanged(); }
- bool IsChannelChanged() { return updater_configurator_->IsChannelChanged(); }
+ void CompareAndSwapChannelChanged(int old_value, int new_value);
bool IsChannelValid(const std::string& channel) {
return updater_configurator_->IsChannelValid(channel);
}
diff --git a/src/cobalt/updater/utils.cc b/src/cobalt/updater/utils.cc
index 17f0822..dab4b92 100644
--- a/src/cobalt/updater/utils.cc
+++ b/src/cobalt/updater/utils.cc
@@ -22,6 +22,12 @@
namespace cobalt {
namespace updater {
+namespace {
+// The default manifest version to assume when the actual manifest cannot be
+// parsed for any reason. This should not be used for installation manager
+// errors, or any other error unrelated to parsing the manifest.
+const std::string kDefaultManifestVersion = "1.0.0";
+} // namespace
bool CreateProductDirectory(base::FilePath* path) {
if (!GetProductDirectoryPath(path)) {
@@ -112,6 +118,12 @@
std::string(installation_path.begin(), installation_path.end())));
if (!version.IsValid()) {
+ if (!index) {
+ SB_LOG(ERROR) << "Failed to get the Everegreen version. Defaulting to "
+ << kDefaultManifestVersion << ".";
+ return kDefaultManifestVersion;
+ }
+
SB_LOG(ERROR) << "Failed to get the Everegreen version.";
return "";
}
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.cc b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
index a01a9a8..3b43b8a 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.cc
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.cc
@@ -103,7 +103,8 @@
return copy_of_data_as_string_;
}
-void URLFetcherResponseWriter::Buffer::GetAndReset(std::string* str) {
+void URLFetcherResponseWriter::Buffer::GetAndResetDataAndDownloadProgress(
+ std::string* str) {
DCHECK(str);
ReleaseMemory(str);
@@ -119,9 +120,15 @@
}
data_as_string_.swap(*str);
+
+ // It is important to reset the |download_progress_| and return the data in
+ // one function to avoid potential race condition that may prevent the last
+ // bit of data of Fetcher from being downloaded, because the data download is
+ // guarded by HasProgressSinceLastGetAndReset().
+ download_progress_ = 0;
}
-void URLFetcherResponseWriter::Buffer::GetAndReset(
+void URLFetcherResponseWriter::Buffer::GetAndResetData(
PreallocatedArrayBufferData* data) {
DCHECK(data);
diff --git a/src/cobalt/xhr/url_fetcher_buffer_writer.h b/src/cobalt/xhr/url_fetcher_buffer_writer.h
index c82ce32..8eb9736 100644
--- a/src/cobalt/xhr/url_fetcher_buffer_writer.h
+++ b/src/cobalt/xhr/url_fetcher_buffer_writer.h
@@ -57,8 +57,8 @@
// public member function is called on this object.
const std::string& GetTemporaryReferenceOfString();
- void GetAndReset(std::string* str);
- void GetAndReset(PreallocatedArrayBufferData* data);
+ void GetAndResetDataAndDownloadProgress(std::string* str);
+ void GetAndResetData(PreallocatedArrayBufferData* data);
void MaybePreallocate(int64_t capacity);
void Write(const void* buffer, int num_bytes);
diff --git a/src/cobalt/xhr/xml_http_request.cc b/src/cobalt/xhr/xml_http_request.cc
index 27408e2..0ea3345 100644
--- a/src/cobalt/xhr/xml_http_request.cc
+++ b/src/cobalt/xhr/xml_http_request.cc
@@ -717,7 +717,7 @@
if (fetch_callback_) {
std::string downloaded_data;
- response_body_->GetAndReset(&downloaded_data);
+ response_body_->GetAndResetDataAndDownloadProgress(&downloaded_data);
script::Handle<script::Uint8Array> data =
script::Uint8Array::New(settings_->global_environment(),
downloaded_data.data(), downloaded_data.size());
@@ -729,9 +729,13 @@
const base::TimeDelta elapsed(now - last_progress_time_);
if (elapsed > base::TimeDelta::FromMilliseconds(kProgressPeriodMs)) {
last_progress_time_ = now;
- // TODO: Investigate if we have to fire progress event with 0 loaded bytes
- // when used as Fetch API.
- UpdateProgress(response_body_->GetAndResetDownloadProgress());
+ if (fetch_callback_) {
+ // TODO: Investigate if we have to fire progress event with 0 loaded bytes
+ // when used as Fetch API.
+ UpdateProgress(0);
+ } else {
+ UpdateProgress(response_body_->GetAndResetDownloadProgress());
+ }
}
}
@@ -1025,7 +1029,7 @@
// request is re-opened.
std::unique_ptr<script::PreallocatedArrayBufferData> downloaded_data(
new script::PreallocatedArrayBufferData());
- response_body_->GetAndReset(downloaded_data.get());
+ response_body_->GetAndResetData(downloaded_data.get());
auto array_buffer = script::ArrayBuffer::New(
settings_->global_environment(), std::move(downloaded_data));
response_array_buffer_reference_.reset(
diff --git a/src/components/update_client/component.cc b/src/components/update_client/component.cc
index df190d0..50ec274 100644
--- a/src/components/update_client/component.cc
+++ b/src/components/update_client/component.cc
@@ -195,6 +195,14 @@
#endif
if (result.error != UnpackerError::kNone) {
+#if defined(OS_STARBOARD)
+ // When there is an error unpacking the downloaded CRX, such as a failure to
+ // verify the package, we should remember to clear out any drain files.
+ if (base::DirectoryExists(crx_path.DirName())) {
+ CobaltSlotManagement cobalt_slot_management;
+ cobalt_slot_management.CleanupAllDrainFiles(crx_path.DirName());
+ }
+#endif
main_task_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), ErrorCategory::kUnpack,
diff --git a/src/components/update_client/configurator.h b/src/components/update_client/configurator.h
index 5baa3cc..ab7e01e 100644
--- a/src/components/update_client/configurator.h
+++ b/src/components/update_client/configurator.h
@@ -171,7 +171,9 @@
// parameters.
virtual void SetChannel(const std::string& channel) = 0;
- virtual bool IsChannelChanged() const = 0;
+ // Compare and swap the is_channel_changed flag.
+ virtual void CompareAndSwapChannelChanged(int old_value, int new_value) = 0;
+
#endif
protected:
diff --git a/src/components/update_client/test_configurator.h b/src/components/update_client/test_configurator.h
index f6ede80..c3fbda1 100644
--- a/src/components/update_client/test_configurator.h
+++ b/src/components/update_client/test_configurator.h
@@ -128,7 +128,7 @@
#if defined(STARBOARD)
void SetChannel(const std::string& channel) override {}
- bool IsChannelChanged() const override { return false; }
+ void CompareAndSwapChannelChanged(int old_value, int new_value) override {}
#else
network::TestURLLoaderFactory* test_url_loader_factory() {
return &test_url_loader_factory_;
diff --git a/src/components/update_client/update_checker.cc b/src/components/update_client/update_checker.cc
index c4045f0..3cfa2d5 100644
--- a/src/components/update_client/update_checker.cc
+++ b/src/components/update_client/update_checker.cc
@@ -44,14 +44,6 @@
namespace {
-#if defined(COBALT_BUILD_TYPE_DEBUG) || defined(COBALT_BUILD_TYPE_DEVEL)
-const std::string kDefaultUpdaterChannel = "dev";
-#elif defined(COBALT_BUILD_TYPE_QA)
-const std::string kDefaultUpdaterChannel = "qa";
-#elif defined(COBALT_BUILD_TYPE_GOLD)
-const std::string kDefaultUpdaterChannel = "prod";
-#endif
-
// Returns a sanitized version of the brand or an empty string otherwise.
std::string SanitizeBrand(const std::string& brand) {
return IsValidBrand(brand) ? brand : std::string("");
@@ -249,22 +241,6 @@
MakeProtocolPing(app_id, metadata_)));
}
std::string updater_channel = config_->GetChannel();
-#if defined(OS_STARBOARD)
- // If the updater channel is not set, read from pref store instead.
- if (updater_channel.empty()) {
- // All apps of the update use the same channel.
- updater_channel = GetPersistedData()->GetUpdaterChannel(ids_checked_[0]);
- if (updater_channel.empty()) {
- updater_channel = kDefaultUpdaterChannel;
- }
- // Set the updater channel from the persistent store or to default channel,
- // if it's not set already.
- config_->SetChannel(updater_channel);
- } else {
- // Update the record of updater channel in pref store.
- GetPersistedData()->SetUpdaterChannel(ids_checked_[0], updater_channel);
- }
-#endif
const auto request = MakeProtocolRequest(
session_id, config_->GetProdId(),
@@ -284,6 +260,10 @@
config_->EnabledCupSigning(),
base::BindOnce(&UpdateCheckerImpl::OnRequestSenderComplete,
base::Unretained(this)));
+#if defined(OS_STARBOARD)
+ // Reset is_channel_changed flag to false if it is true
+ config_->CompareAndSwapChannelChanged(1, 0);
+#endif
}
void UpdateCheckerImpl::OnRequestSenderComplete(int error,
diff --git a/src/content/browser/speech/endpointer/endpointer.cc b/src/content/browser/speech/endpointer/endpointer.cc
index 5c19c4a..96d8e6c 100644
--- a/src/content/browser/speech/endpointer/endpointer.cc
+++ b/src/content/browser/speech/endpointer/endpointer.cc
@@ -89,7 +89,7 @@
}
#if defined(STARBOARD)
-EpStatus Endpointer::ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out) {
+EpStatus Endpointer::ProcessAudio(const AudioBus& audio_bus, float* rms_out) {
// TODO[Cobalt]: replace ShellAudioData with AudioChunk and deprecate
// ShellAudioData.
DCHECK_EQ(audio_bus.channels(), 1);
@@ -97,16 +97,16 @@
const size_t num_samples = audio_bus.frames();
const int16_t* audio_data = NULL;
- ShellAudioBus int16_audio_bus(1, num_samples, ShellAudioBus::kInt16,
- ShellAudioBus::kInterleaved);
+ AudioBus int16_audio_bus(1, num_samples, AudioBus::kInt16,
+ AudioBus::kInterleaved);
- if (audio_bus.sample_type() == ShellAudioBus::kFloat32) {
+ if (audio_bus.sample_type() == AudioBus::kFloat32) {
int16_audio_bus.Assign(audio_bus);
- DCHECK_EQ(int16_audio_bus.sample_type(), ShellAudioBus::kInt16);
+ DCHECK_EQ(int16_audio_bus.sample_type(), AudioBus::kInt16);
audio_data =
reinterpret_cast<const int16_t*>(int16_audio_bus.interleaved_data());
} else {
- DCHECK_EQ(audio_bus.sample_type(), ShellAudioBus::kInt16);
+ DCHECK_EQ(audio_bus.sample_type(), AudioBus::kInt16);
audio_data =
reinterpret_cast<const int16_t*>(audio_bus.interleaved_data());
}
diff --git a/src/content/browser/speech/endpointer/endpointer.h b/src/content/browser/speech/endpointer/endpointer.h
index 16bfed6..2264842 100644
--- a/src/content/browser/speech/endpointer/endpointer.h
+++ b/src/content/browser/speech/endpointer/endpointer.h
@@ -7,7 +7,7 @@
#include <stdint.h>
-#include "cobalt/media/base/shell_audio_bus.h"
+#include "cobalt/media/base/audio_bus.h"
#include "content/browser/speech/endpointer/energy_endpointer.h"
#include "content/common/content_export.h"
@@ -47,7 +47,7 @@
// long_speech_input_complete_silence_length.
class CONTENT_EXPORT Endpointer {
public:
- typedef cobalt::media::ShellAudioBus ShellAudioBus;
+ typedef cobalt::media::AudioBus AudioBus;
explicit Endpointer(int sample_rate);
@@ -68,7 +68,7 @@
// Process a segment of audio, which may be more than one frame.
// The status of the last frame will be returned.
#if defined(STARBOARD)
- EpStatus ProcessAudio(const ShellAudioBus& audio_bus, float* rms_out);
+ EpStatus ProcessAudio(const AudioBus& audio_bus, float* rms_out);
#else
EpStatus ProcessAudio(const AudioChunk& raw_audio, float* rms_out);
#endif
diff --git a/src/content/browser/speech/endpointer/endpointer_unittest.cc b/src/content/browser/speech/endpointer/endpointer_unittest.cc
index 632cc37..4b3cbb5 100644
--- a/src/content/browser/speech/endpointer/endpointer_unittest.cc
+++ b/src/content/browser/speech/endpointer/endpointer_unittest.cc
@@ -120,7 +120,7 @@
class EndpointerFrameProcessor : public FrameProcessor {
public:
#if defined(STARBOARD)
- typedef Endpointer::ShellAudioBus ShellAudioBus;
+ typedef Endpointer::AudioBus AudioBus;
#endif
explicit EndpointerFrameProcessor(Endpointer* endpointer)
: endpointer_(endpointer) {}
@@ -129,7 +129,7 @@
int16_t* samples,
int frame_size) override {
#if defined(STARBOARD)
- auto frame = std::make_unique<ShellAudioBus>(1, kFrameSize, samples);
+ auto frame = std::make_unique<AudioBus>(1, kFrameSize, samples);
endpointer_->ProcessAudio(*frame.get(), NULL);
#else
scoped_refptr<AudioChunk> frame(
diff --git a/src/docker-compose.yml b/src/docker-compose.yml
index 30d600f..749a24a 100644
--- a/src/docker-compose.yml
+++ b/src/docker-compose.yml
@@ -31,6 +31,15 @@
dockerfile: base/Dockerfile
image: cobalt-base
+ base-xenial:
+ build:
+ args:
+ - BASE_OS=ubuntu
+ - BASE_OS_TAG=xenial
+ context: ./docker/linux
+ dockerfile: base/Dockerfile
+ image: base-xenial
+
# Define common build container for Linux
build-base:
build:
@@ -40,6 +49,16 @@
depends_on:
- base
+ build-base-xenial:
+ build:
+ context: ./docker/linux
+ dockerfile: base/build/Dockerfile
+ args:
+ - FROM_IMAGE=base-xenial
+ image: build-base-xenial
+ depends_on:
+ - base-xenial
+
stub:
<<: *build-common-definitions
build:
@@ -56,10 +75,36 @@
context: ./docker/linux
dockerfile: linux-x64x11/Dockerfile
image: cobalt-build-linux
+ depends_on: [ build-base ]
environment:
- PLATFORM=linux-x64x11
- CONFIG=${CONFIG:-debug}
+ linux-x64x11-xenial:
+ <<: *common-definitions
+ <<: *build-volumes
+ build:
+ context: ./docker/linux
+ dockerfile: linux-x64x11/Dockerfile
+ args:
+ - FROM_IMAGE=build-base-xenial
+ image: linux-x64x11-xenial
+ depends_on:
+ - build-base-xenial
+
+ linux-x64x11-clang-3-6:
+ <<: *common-definitions
+ <<: *build-volumes
+ build:
+ context: ./docker/linux/
+ dockerfile: clang-3-6/Dockerfile
+ image: cobalt-build-linux-clang-3-6
+ environment:
+ - PLATFORM=linux-x64x11-clang-3-6
+ - CONFIG=${CONFIG:-debug}
+ depends_on:
+ - linux-x64x11-xenial
+
# Define common build container for Android
build-android:
<<: *build-common-definitions
@@ -71,7 +116,7 @@
android-x86:
<<: *build-common-definitions
image: cobalt-build-android
- depends_on: [ build-evergreen ]
+ depends_on: [ build-android ]
environment:
- IS_DOCKER=1
- PLATFORM=android-x86
@@ -80,7 +125,7 @@
android-arm:
<<: *build-common-definitions
image: cobalt-build-android
- depends_on: [ build-evergreen ]
+ depends_on: [ build-android ]
environment:
- IS_DOCKER=1
- PLATFORM=android-arm
@@ -89,7 +134,7 @@
android-arm64:
<<: *build-common-definitions
image: cobalt-build-android
- depends_on: [ build-evergreen ]
+ depends_on: [ build-android ]
environment:
- IS_DOCKER=1
- PLATFORM=android-arm64
@@ -160,8 +205,8 @@
environment:
- PLATFORM=${PLATFORM:-linux-x64x11}
- CONFIG=${CONFIG:-debug}
- - TEST_TARGET=${TEST_TARGET:-eztime_test}
volumes:
- ${COBALT_SRC:-.}/out/${PLATFORM:-linux-x64x11}_${CONFIG:-debug}:/out
# TODO: Get NPLB unittests to run with IPv6 without using the host network.
network_mode: "host"
+ depends_on: [ base ]
diff --git a/src/docker/linux/android/Dockerfile b/src/docker/linux/android/Dockerfile
index d76e0ce..ad34732 100644
--- a/src/docker/linux/android/Dockerfile
+++ b/src/docker/linux/android/Dockerfile
@@ -16,6 +16,8 @@
&& rm -rf /var/lib/{apt,dpkg,cache,log} \
&& echo "Done"
+RUN mkdir -p /root/.android
+
CMD (test -f /root/.android/debug.keystore \
&& echo "Android debug keystore exists." \
|| (keytool -genkey -v \
diff --git a/src/docker/linux/base/Dockerfile b/src/docker/linux/base/Dockerfile
index 4f21c76..2746def 100644
--- a/src/docker/linux/base/Dockerfile
+++ b/src/docker/linux/base/Dockerfile
@@ -1,5 +1,6 @@
ARG BASE_OS
-FROM ${BASE_OS:-gcr.io/cloud-marketplace-containers/google/debian9}
+ARG BASE_OS_TAG
+FROM ${BASE_OS:-gcr.io/cloud-marketplace-containers/google/debian9}:${BASE_OS_TAG:-latest}
ENV PYTHONUNBUFFERED 1
diff --git a/src/docker/linux/base/build/Dockerfile b/src/docker/linux/base/build/Dockerfile
index 416b5ee..545908d 100644
--- a/src/docker/linux/base/build/Dockerfile
+++ b/src/docker/linux/base/build/Dockerfile
@@ -1,4 +1,5 @@
-FROM cobalt-base
+ARG FROM_IMAGE
+FROM ${FROM_IMAGE:-cobalt-base}
# === Get Nodejs pinned LTS version via NVM
ENV NVM_DIR /root/.nvm
@@ -18,17 +19,15 @@
RUN git clone https://cobalt.googlesource.com/depot_tools /depot_tools
# === Configure common env vars
-ENV PATH="${PATH}:/depot_tools:/root/fake_goma" \
+ENV PATH="${PATH}:/depot_tools" \
OUTDIR=out \
DEPOT_TOOLS_UPDATE=0 \
NINJA_STATUS="[%f/%t %c/sec] " \
CCACHE_DIR=/root/ccache \
CCACHE_MAXSIZE=30G
-# == Set up gclient and fake Goma with ccache
-COPY ./files/fake_goma /root/fake_goma
+# == Set up gclient and ccache
RUN cd /tmp && gclient verify || true \
- && chmod +x /root/fake_goma/gomacc /root/fake_goma/goma_ctl.py \
&& mkdir /root/ccache
WORKDIR /code
diff --git a/src/docker/linux/clang-3-6/Dockerfile b/src/docker/linux/clang-3-6/Dockerfile
new file mode 100644
index 0000000..0bf3926
--- /dev/null
+++ b/src/docker/linux/clang-3-6/Dockerfile
@@ -0,0 +1,14 @@
+FROM linux-x64x11-xenial
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -qqy \
+ && apt install -qqy --no-install-recommends clang-3.6 \
+ && apt-get clean autoclean \
+ && apt-get autoremove -y --purge \
+ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
+ && rm -rf /var/lib/{apt,dpkg,cache,log} \
+ && echo "Done"
+
+CMD /code/cobalt/build/gyp_cobalt -v -C ${CONFIG} ${PLATFORM} && \
+ ninja -C ${OUTDIR}/${PLATFORM}_${CONFIG} ${TARGET:-cobalt_deploy}
diff --git a/src/docker/linux/files/fake_goma/goma_ctl.py b/src/docker/linux/files/fake_goma/goma_ctl.py
deleted file mode 100755
index 2f2ceb9..0000000
--- a/src/docker/linux/files/fake_goma/goma_ctl.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-
-print("Faking Goma via ccache")
diff --git a/src/docker/linux/files/fake_goma/gomacc b/src/docker/linux/files/fake_goma/gomacc
deleted file mode 100755
index d64f812..0000000
--- a/src/docker/linux/files/fake_goma/gomacc
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-ccache "$@"
diff --git a/src/docker/linux/linux-x64x11/Dockerfile b/src/docker/linux/linux-x64x11/Dockerfile
index 524f320..29f7d8d 100644
--- a/src/docker/linux/linux-x64x11/Dockerfile
+++ b/src/docker/linux/linux-x64x11/Dockerfile
@@ -1,4 +1,5 @@
-FROM cobalt-build-base
+ARG FROM_IMAGE
+FROM ${FROM_IMAGE:-cobalt-build-base}
RUN apt update -qqy \
&& apt install -qqy --no-install-recommends \
diff --git a/src/docker/linux/unittest/Dockerfile b/src/docker/linux/unittest/Dockerfile
index 32a35a5..e876c8d 100644
--- a/src/docker/linux/unittest/Dockerfile
+++ b/src/docker/linux/unittest/Dockerfile
@@ -7,7 +7,9 @@
libavformat57 \
libavresample3 \
libavutil55 \
+ libegl1-mesa \
libgl1-mesa-dri \
+ libgles2-mesa \
libx11-6 \
libxcomposite1 \
libxrender1 \
@@ -27,7 +29,7 @@
RUN mkdir -p /app_launcher_out
-CMD unzip /out/app_launcher -d /app_launcher_out && \
+CMD unzip -q /out/app_launcher -d /app_launcher_out && \
xvfb-run --server-args="-screen 0 1920x1080x24 +render +extension GLX -noreset" \
python /app_launcher_out/starboard/tools/testing/test_runner.py --run \
- -o /out --platform $PLATFORM --config $CONFIG -t $TEST_TARGET
+ -o /out --platform $PLATFORM --config $CONFIG
diff --git a/src/net/dial/dial_system_config_starboard.cc b/src/net/dial/dial_system_config_starboard.cc
index 316d889..7ec70d0 100644
--- a/src/net/dial/dial_system_config_starboard.cc
+++ b/src/net/dial/dial_system_config_starboard.cc
@@ -14,8 +14,8 @@
#include "net/dial/dial_system_config.h"
+#include "starboard/common/file.h"
#include "starboard/configuration_constants.h"
-#include "starboard/file.h"
#include "starboard/system.h"
namespace {
diff --git a/src/net/ssl/ssl_key_logger_impl.cc b/src/net/ssl/ssl_key_logger_impl.cc
index 550d372..2bad355 100644
--- a/src/net/ssl/ssl_key_logger_impl.cc
+++ b/src/net/ssl/ssl_key_logger_impl.cc
@@ -16,7 +16,10 @@
#include "base/sequenced_task_runner.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
+#if defined(STARBOARD)
+#include "starboard/common/file.h"
#include "starboard/types.h"
+#endif
namespace net {
diff --git a/src/requirements.txt b/src/requirements.txt
new file mode 100644
index 0000000..e9215ea
--- /dev/null
+++ b/src/requirements.txt
@@ -0,0 +1,4 @@
+cpplint==1.5.4
+pre-commit==2.6.0
+pylint==2.6.0
+yapf==0.30.0
diff --git a/src/starboard/CHANGELOG.md b/src/starboard/CHANGELOG.md
index cdd706c..40aab86 100644
--- a/src/starboard/CHANGELOG.md
+++ b/src/starboard/CHANGELOG.md
@@ -261,6 +261,23 @@
when those settings change. For older starboard versions, use
kSbEventTypeAccessiblitySettingsChanged instead.
+### Add extension to SbMediaCanPlayMimeAndKeySystem() for encryptionScheme.
+
+Now the Starboard implementation may choose to support |key_system| with extra
+attributes, in order to selectively support encryption schemes on particular
+containers or codecs.
+The Starboard implementation needn't support |key_system| with extra attributes
+if it meets the requirements for the default implementation of
+`Navigator.requestMediaKeySystemAccess()`, which assumes that:
+1. When the Widevine DRM system is used, all the encryption schemes ('cenc',
+ 'cbcs', 'cbcs-1-9') should be supported across all containers and codecs
+ supported by the platform.
+2. When the PlayReady DRM system is used, only 'cenc' is supported across all
+ containers and codecs supported by the platform.
+
+Please see the comment of `SbMediaCanPlayMimeAndKeySystem()` in `media.h` for
+more details.
+
## Version 11
### Add arguments to `SbMediaIsVideoSupported`.
diff --git a/src/starboard/android/apk/app/build.gradle b/src/starboard/android/apk/app/build.gradle
index fdf3b29..5ab0feb 100644
--- a/src/starboard/android/apk/app/build.gradle
+++ b/src/starboard/android/apk/app/build.gradle
@@ -66,7 +66,7 @@
defaultConfig {
applicationId "dev.cobalt.coat"
minSdkVersion 21
- targetSdkVersion 30
+ targetSdkVersion 29
versionCode 1
versionName "${buildId}"
manifestPlaceholders = [applicationName: "CoAT: ${cobaltTarget}"]
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
index fe988bb..9cee635 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/StarboardBridge.java
@@ -30,7 +30,7 @@
import android.os.Build;
import android.util.Size;
import android.util.SizeF;
-import android.view.Display;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.CaptioningManager;
import androidx.annotation.RequiresApi;
@@ -155,13 +155,17 @@
@SuppressWarnings("unused")
@UsedByNative
protected void beforeSuspend() {
- Log.i(TAG, "Prepare to suspend");
- // We want the MediaSession to be deactivated immediately before suspending so that by the time
- // the launcher is visible our "Now Playing" card is already gone. Then Cobalt and the web app
- // can take their time suspending after that.
- cobaltMediaSession.suspend();
- for (CobaltService service : cobaltServices.values()) {
- service.beforeSuspend();
+ try {
+ Log.i(TAG, "Prepare to suspend");
+ // We want the MediaSession to be deactivated immediately before suspending so that by the time
+ // the launcher is visible our "Now Playing" card is already gone. Then Cobalt and the web app
+ // can take their time suspending after that.
+ cobaltMediaSession.suspend();
+ for (CobaltService service : cobaltServices.values()) {
+ service.beforeSuspend();
+ }
+ } catch (Throwable e) {
+ Log.i(TAG, "Caught exception in beforeSuspend: " + e.getMessage());
}
}
@@ -521,12 +525,18 @@
return false;
}
- Display defaultDisplay = DisplayUtil.getDefaultDisplay(activityHolder.get());
- if (defaultDisplay == null) {
+ Activity activity = activityHolder.get();
+ if (activity == null) {
return false;
}
- int[] supportedHdrTypes = defaultDisplay.getHdrCapabilities().getSupportedHdrTypes();
+ WindowManager windowManager = activity.getWindowManager();
+ if (windowManager == null) {
+ return false;
+ }
+
+ int[] supportedHdrTypes =
+ windowManager.getDefaultDisplay().getHdrCapabilities().getSupportedHdrTypes();
for (int supportedType : supportedHdrTypes) {
if (supportedType == hdrType) {
return true;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
index 42c4e98..faa8742 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java
@@ -69,7 +69,6 @@
private static final String KEY_CROP_TOP = "crop-top";
private static final int BITRATE_ADJUSTMENT_FPS = 30;
- private static final int MAXIMUM_INITIAL_FPS = 30;
private long mNativeMediaCodecBridge;
private MediaCodec mMediaCodec;
@@ -78,6 +77,8 @@
private long mLastPresentationTimeUs;
private final String mMime;
private boolean mAdaptivePlaybackSupported;
+ private double mPlaybackRate = 1.0;
+ private int mFps = 30;
// Functions that require this will be called frequently in a tight loop.
// Only create one of these and reuse it to avoid excessive allocations,
@@ -104,6 +105,45 @@
public static final String VIDEO_AV1 = "video/av01";
}
+ private class FrameRateEstimator {
+ private static final int INVALID_FRAME_RATE = -1;
+ private static final long INVALID_FRAME_TIMESTAMP = -1;
+ private static final int MINIMUN_REQUIRED_FRAMES = 4;
+ private long mLastFrameTimestampUs = INVALID_FRAME_TIMESTAMP;
+ private long mNumberOfFrames = 0;
+ private long mTotalDurationUs = 0;
+
+ public int getEstimatedFrameRate() {
+ if (mTotalDurationUs <= 0 || mNumberOfFrames < MINIMUN_REQUIRED_FRAMES) {
+ return INVALID_FRAME_RATE;
+ }
+ return Math.round((mNumberOfFrames - 1) * 1000000.0f / mTotalDurationUs);
+ }
+
+ public void reset() {
+ mLastFrameTimestampUs = INVALID_FRAME_TIMESTAMP;
+ mNumberOfFrames = 0;
+ mTotalDurationUs = 0;
+ }
+
+ public void onNewFrame(long presentationTimeUs) {
+ mNumberOfFrames++;
+
+ if (mLastFrameTimestampUs == INVALID_FRAME_TIMESTAMP) {
+ mLastFrameTimestampUs = presentationTimeUs;
+ return;
+ }
+ if (presentationTimeUs <= mLastFrameTimestampUs) {
+ Log.v(TAG, String.format("Invalid output presentation timestamp."));
+ return;
+ }
+
+ mTotalDurationUs += presentationTimeUs - mLastFrameTimestampUs;
+ mLastFrameTimestampUs = presentationTimeUs;
+ }
+ }
+
+ private FrameRateEstimator mFrameRateEstimator = null;
private BitrateAdjustmentTypes mBitrateAdjustmentType = BitrateAdjustmentTypes.NO_ADJUSTMENT;
@SuppressWarnings("unused")
@@ -415,6 +455,14 @@
info.offset,
info.presentationTimeUs,
info.size);
+ if (mFrameRateEstimator != null) {
+ mFrameRateEstimator.onNewFrame(info.presentationTimeUs);
+ int fps = mFrameRateEstimator.getEstimatedFrameRate();
+ if (fps != FrameRateEstimator.INVALID_FRAME_RATE && mFps != fps) {
+ mFps = fps;
+ updateOperatingRate();
+ }
+ }
}
}
@@ -599,6 +647,40 @@
@SuppressWarnings("unused")
@UsedByNative
+ private void setPlaybackRate(double playbackRate) {
+ if (mPlaybackRate == playbackRate) {
+ return;
+ }
+ mPlaybackRate = playbackRate;
+ if (mFrameRateEstimator != null) {
+ updateOperatingRate();
+ }
+ }
+
+ private void updateOperatingRate() {
+ // We needn't set operation rate if playback rate is 0 or less.
+ if (Double.compare(mPlaybackRate, 0.0) <= 0) {
+ return;
+ }
+ if (mFps == FrameRateEstimator.INVALID_FRAME_RATE) {
+ return;
+ }
+ if (mFps <= 0) {
+ Log.e(TAG, "Failed to set operating rate with invalid fps " + mFps);
+ return;
+ }
+ double operatingRate = mPlaybackRate * mFps;
+ Bundle b = new Bundle();
+ b.putFloat(MediaFormat.KEY_OPERATING_RATE, (float) operatingRate);
+ try {
+ mMediaCodec.setParameters(b);
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to set MediaCodec operating rate", e);
+ }
+ }
+
+ @SuppressWarnings("unused")
+ @UsedByNative
private int flush() {
try {
mFlushed = true;
@@ -830,17 +912,23 @@
if (mAdaptivePlaybackSupported) {
// Since we haven't passed the properties of the stream we're playing
// down to this level, from our perspective, we could potentially
- // adapt up to 8k at any point. We thus request 8k buffers up front,
+ // adapt up to 8k at any point. We thus request 8k buffers up front,
// unless the decoder claims to not be able to do 8k, in which case
// we're ok, since we would've rejected a 8k stream when canPlayType
// was called, and then use those decoder values instead.
- int maxWidth = Math.min(7680, maxSupportedWidth);
- int maxHeight = Math.min(4320, maxSupportedHeight);
- format.setInteger(MediaFormat.KEY_MAX_WIDTH, maxWidth);
- format.setInteger(MediaFormat.KEY_MAX_HEIGHT, maxHeight);
+ if (Build.VERSION.SDK_INT > 22) {
+ format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(7680, maxSupportedWidth));
+ format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(4320, maxSupportedHeight));
+ } else {
+ // Android 5.0/5.1 seems not support 8K. Fallback to 4K until we get a
+ // better way to get maximum supported resolution.
+ format.setInteger(MediaFormat.KEY_MAX_WIDTH, Math.min(3840, maxSupportedWidth));
+ format.setInteger(MediaFormat.KEY_MAX_HEIGHT, Math.min(2160, maxSupportedHeight));
+ }
}
maybeSetMaxInputSize(format);
mMediaCodec.configure(format, surface, crypto, flags);
+ mFrameRateEstimator = new FrameRateEstimator();
return true;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Cannot configure the video codec with IllegalArgumentException: ", e);
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
index 1e39d63..0274811 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoFrameReleaseTimeHelper.java
@@ -35,9 +35,8 @@
import android.os.Message;
import android.view.Choreographer;
import android.view.Choreographer.FrameCallback;
-import android.view.Display;
+import android.view.WindowManager;
import androidx.annotation.RequiresApi;
-import dev.cobalt.util.DisplayUtil;
import dev.cobalt.util.UsedByNative;
/** Makes a best effort to adjust frame release timestamps for a smoother visual result. */
@@ -222,9 +221,9 @@
}
private static double getDefaultDisplayRefreshRate(Context context) {
- Display defaultDisplay = DisplayUtil.getDefaultDisplay(context);
- return defaultDisplay != null
- ? defaultDisplay.getRefreshRate()
+ WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ return manager.getDefaultDisplay() != null
+ ? manager.getDefaultDisplay().getRefreshRate()
: DISPLAY_REFRESH_RATE_UNKNOWN;
}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
index c7c2208..4b23394 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/util/DisplayUtil.java
@@ -15,14 +15,10 @@
package dev.cobalt.util;
import android.content.Context;
-import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.util.Size;
import android.util.SizeF;
-import android.view.Display;
import android.view.WindowManager;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
/** Utility functions for querying display attributes. */
public class DisplayUtil {
@@ -30,34 +26,6 @@
private DisplayUtil() {}
/**
- * Returns the default display associated with a context.
- */
- @Nullable
- public static Display getDefaultDisplay(Context context) {
- if (context == null) {
- return null;
- }
- if (android.os.Build.VERSION.SDK_INT >= 30) {
- return getDefaultDisplayV30(context);
- } else {
- return getDefaultDisplayDeprecated(context);
- }
- }
-
- @Nullable
- @SuppressWarnings("deprecation")
- private static Display getDefaultDisplayDeprecated(Context context) {
- WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
- return (wm == null) ? null : wm.getDefaultDisplay();
- }
-
- @Nullable
- @RequiresApi(30)
- private static Display getDefaultDisplayV30(Context context) {
- return context.getDisplay();
- }
-
- /**
* Returns the physical pixels per inch of the screen in the X and Y
* dimensions.
*/
@@ -114,10 +82,10 @@
private static DisplayMetrics cachedDisplayMetrics = null;
private static DisplayMetrics getDisplayMetrics(Context context) {
- Resources.getSystem().getDisplayMetrics();
if (cachedDisplayMetrics == null) {
cachedDisplayMetrics = new DisplayMetrics();
- getDefaultDisplay(context).getRealMetrics(cachedDisplayMetrics);
+ ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay().getRealMetrics(cachedDisplayMetrics);
}
return cachedDisplayMetrics;
}
diff --git a/src/starboard/android/apk/build.id b/src/starboard/android/apk/build.id
new file mode 100644
index 0000000..a485246
--- /dev/null
+++ b/src/starboard/android/apk/build.id
@@ -0,0 +1 @@
+282262
\ No newline at end of file
diff --git a/src/starboard/android/shared/android_main.cc b/src/starboard/android/shared/android_main.cc
index 1f6513d..a44497e 100644
--- a/src/starboard/android/shared/android_main.cc
+++ b/src/starboard/android/shared/android_main.cc
@@ -67,6 +67,7 @@
if (j_url) {
start_url = env->GetStringStandardUTFOrAbort(j_url.Get());
}
+ SB_LOG(INFO) << "GetStartDeepLink: " << start_url;
return start_url;
}
diff --git a/src/starboard/android/shared/android_media_session_client.cc b/src/starboard/android/shared/android_media_session_client.cc
new file mode 100644
index 0000000..b822ec1
--- /dev/null
+++ b/src/starboard/android/shared/android_media_session_client.cc
@@ -0,0 +1,297 @@
+// Copyright 2017 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "starboard/android/shared/android_media_session_client.h"
+
+#include "base/time/time.h"
+#include "starboard/android/shared/jni_env_ext.h"
+#include "starboard/android/shared/jni_utils.h"
+#include "starboard/common/log.h"
+#include "starboard/common/mutex.h"
+#include "starboard/once.h"
+
+namespace starboard {
+namespace android {
+namespace shared {
+
+using ::starboard::android::shared::JniEnvExt;
+using ::starboard::android::shared::ScopedLocalJavaRef;
+
+// These constants are from android.media.session.PlaybackState
+const jlong kPlaybackStateActionStop = 1 << 0;
+const jlong kPlaybackStateActionPause = 1 << 1;
+const jlong kPlaybackStateActionPlay = 1 << 2;
+const jlong kPlaybackStateActionRewind = 1 << 3;
+const jlong kPlaybackStateActionSkipToPrevious = 1 << 4;
+const jlong kPlaybackStateActionSkipToNext = 1 << 5;
+const jlong kPlaybackStateActionFastForward = 1 << 6;
+const jlong kPlaybackStateActionSetRating = 1 << 7; // not supported
+const jlong kPlaybackStateActionSeekTo = 1 << 8;
+
+// Converts a MediaSessionClient::AvailableActions bitset into
+// a android.media.session.PlaybackState jlong bitset.
+jlong MediaSessionActionsToPlaybackStateActions(const bool* actions) {
+ jlong result = 0;
+ if (actions[kCobaltExtensionMediaSessionActionPause]) {
+ result |= kPlaybackStateActionPause;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionPlay]) {
+ result |= kPlaybackStateActionPlay;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionSeekbackward]) {
+ result |= kPlaybackStateActionRewind;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionPrevioustrack]) {
+ result |= kPlaybackStateActionSkipToPrevious;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionNexttrack]) {
+ result |= kPlaybackStateActionSkipToNext;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionSeekforward]) {
+ result |= kPlaybackStateActionFastForward;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionSeekto]) {
+ result |= kPlaybackStateActionSeekTo;
+ }
+ if (actions[kCobaltExtensionMediaSessionActionStop]) {
+ result |= kPlaybackStateActionStop;
+ }
+ return result;
+}
+
+PlaybackState CobaltExtensionPlaybackStateToPlaybackState(
+ CobaltExtensionMediaSessionPlaybackState in_state) {
+ switch (in_state) {
+ case kCobaltExtensionMediaSessionPlaying:
+ return kPlaying;
+ case kCobaltExtensionMediaSessionPaused:
+ return kPaused;
+ case kCobaltExtensionMediaSessionNone:
+ return kNone;
+ }
+}
+
+CobaltExtensionMediaSessionAction PlaybackStateActionToMediaSessionAction(
+ jlong action) {
+ CobaltExtensionMediaSessionAction result;
+ switch (action) {
+ case kPlaybackStateActionPause:
+ result = kCobaltExtensionMediaSessionActionPause;
+ break;
+ case kPlaybackStateActionPlay:
+ result = kCobaltExtensionMediaSessionActionPlay;
+ break;
+ case kPlaybackStateActionRewind:
+ result = kCobaltExtensionMediaSessionActionSeekbackward;
+ break;
+ case kPlaybackStateActionSkipToPrevious:
+ result = kCobaltExtensionMediaSessionActionPrevioustrack;
+ break;
+ case kPlaybackStateActionSkipToNext:
+ result = kCobaltExtensionMediaSessionActionNexttrack;
+ break;
+ case kPlaybackStateActionFastForward:
+ result = kCobaltExtensionMediaSessionActionSeekforward;
+ break;
+ case kPlaybackStateActionSeekTo:
+ result = kCobaltExtensionMediaSessionActionSeekto;
+ break;
+ case kPlaybackStateActionStop:
+ result = kCobaltExtensionMediaSessionActionStop;
+ break;
+ default:
+ SB_NOTREACHED() << "Unsupported MediaSessionAction 0x" << std::hex
+ << action;
+ result = static_cast<CobaltExtensionMediaSessionAction>(-1);
+ }
+ return result;
+}
+
+CobaltExtensionMediaSessionPlaybackState
+PlaybackStateToCobaltExtensionPlaybackState(PlaybackState state) {
+ CobaltExtensionMediaSessionPlaybackState result;
+ switch (state) {
+ case kPlaying:
+ result = kCobaltExtensionMediaSessionPlaying;
+ break;
+ case kPaused:
+ result = kCobaltExtensionMediaSessionPaused;
+ break;
+ case kNone:
+ result = kCobaltExtensionMediaSessionNone;
+ break;
+ default:
+ SB_NOTREACHED() << "Unsupported PlaybackState " << state;
+ result = static_cast<CobaltExtensionMediaSessionPlaybackState>(-1);
+ }
+ return result;
+}
+
+SbOnceControl once_flag = SB_ONCE_INITIALIZER;
+SbMutex mutex;
+
+// Callbacks to the last MediaSessionClient to become active, or null.
+// Used to route Java callbacks.
+// In practice, only one MediaSessionClient will become active at a time.
+// Protected by "mutex"
+CobaltExtensionMediaSessionUpdatePlatformPlaybackStateCallback
+ update_platform_playback_state_callback;
+CobaltExtensionMediaSessionInvokeActionCallback invoke_action_callback;
+void* callback_context;
+
+void OnceInit() {
+ SbMutexCreate(&mutex);
+}
+
+void NativeInvokeAction(jlong action, jlong seek_ms) {
+ SbOnce(&once_flag, OnceInit);
+ SbMutexAcquire(&mutex);
+
+ if (invoke_action_callback != NULL && callback_context != NULL) {
+ CobaltExtensionMediaSessionActionDetails details = {};
+ CobaltExtensionMediaSessionActionDetailsInit(
+ &details, PlaybackStateActionToMediaSessionAction(action));
+ // CobaltMediaSession.java only sets seek_ms for SeekTo (not ff/rew).
+ if (details.action == kCobaltExtensionMediaSessionActionSeekto) {
+ details.seek_time = seek_ms / 1000.0;
+ }
+ invoke_action_callback(details, callback_context);
+ }
+
+ SbMutexRelease(&mutex);
+}
+
+void UpdateActiveSessionPlatformPlaybackState(PlaybackState state) {
+ SbOnce(&once_flag, OnceInit);
+ SbMutexAcquire(&mutex);
+
+ CobaltExtensionMediaSessionPlaybackState media_session_state =
+ PlaybackStateToCobaltExtensionPlaybackState(state);
+
+ if (update_platform_playback_state_callback != NULL &&
+ callback_context != NULL) {
+ update_platform_playback_state_callback(media_session_state,
+ callback_context);
+ }
+
+ SbMutexRelease(&mutex);
+}
+
+void OnMediaSessionStateChanged(
+ const CobaltExtensionMediaSessionState session_state) {
+ JniEnvExt* env = JniEnvExt::Get();
+
+ jint playback_state = CobaltExtensionPlaybackStateToPlaybackState(
+ session_state.actual_playback_state);
+ void* media_session_client = session_state.callback_context;
+
+ SbOnce(&once_flag, OnceInit);
+ SbMutexAcquire(&mutex);
+ if (playback_state != kNone) {
+ callback_context = media_session_client;
+ update_platform_playback_state_callback =
+ session_state.update_platform_playback_state_callback;
+ invoke_action_callback = session_state.invoke_action_callback;
+ } else if (callback_context == media_session_client) {
+ callback_context = NULL;
+ }
+ SbMutexRelease(&mutex);
+
+ jlong playback_state_actions = MediaSessionActionsToPlaybackStateActions(
+ session_state.available_actions);
+
+ ScopedLocalJavaRef<jstring> j_title;
+ ScopedLocalJavaRef<jstring> j_artist;
+ ScopedLocalJavaRef<jstring> j_album;
+ ScopedLocalJavaRef<jobjectArray> j_artwork;
+
+ if (session_state.metadata != NULL) {
+ CobaltExtensionMediaMetadata* media_metadata(session_state.metadata);
+
+ j_title.Reset(env->NewStringStandardUTFOrAbort(media_metadata->title));
+ j_artist.Reset(env->NewStringStandardUTFOrAbort(media_metadata->artist));
+ j_album.Reset(env->NewStringStandardUTFOrAbort(media_metadata->album));
+
+ size_t artwork_count = media_metadata->artwork_count;
+ if (artwork_count > 0) {
+ CobaltExtensionMediaImage* artwork(media_metadata->artwork);
+ ScopedLocalJavaRef<jclass> media_image_class(
+ env->FindClassExtOrAbort("dev/cobalt/media/MediaImage"));
+ jmethodID media_image_constructor = env->GetMethodID(
+ media_image_class.Get(), "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ env->AbortOnException();
+
+ j_artwork.Reset(static_cast<jobjectArray>(
+ env->NewObjectArray(artwork_count, media_image_class.Get(), NULL)));
+ env->AbortOnException();
+
+ ScopedLocalJavaRef<jstring> j_src;
+ ScopedLocalJavaRef<jstring> j_sizes;
+ ScopedLocalJavaRef<jstring> j_type;
+ for (size_t i = 0; i < artwork_count; i++) {
+ const CobaltExtensionMediaImage& media_image(artwork[i]);
+ j_src.Reset(env->NewStringStandardUTFOrAbort(media_image.src));
+ j_sizes.Reset(env->NewStringStandardUTFOrAbort(media_image.size));
+ j_type.Reset(env->NewStringStandardUTFOrAbort(media_image.type));
+
+ ScopedLocalJavaRef<jobject> j_media_image(
+ env->NewObject(media_image_class.Get(), media_image_constructor,
+ j_src.Get(), j_sizes.Get(), j_type.Get()));
+
+ env->SetObjectArrayElement(j_artwork.Get(), i, j_media_image.Get());
+ }
+ }
+ }
+
+ jlong durationInMilliseconds;
+ if (session_state.duration == kSbTimeMax) {
+ // Set duration to negative if duration is unknown or infinite, as with live
+ // playback.
+ // https://developer.android.com/reference/android/support/v4/media/MediaMetadataCompat#METADATA_KEY_DURATION
+ durationInMilliseconds = -1;
+ } else {
+ // SbTime is measured in microseconds while Android MediaSession expects
+ // duration in milliseconds.
+ durationInMilliseconds = session_state.duration / kSbTimeMillisecond;
+ }
+
+ env->CallStarboardVoidMethodOrAbort(
+ "updateMediaSession",
+ "(IJJFLjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
+ "[Ldev/cobalt/media/MediaImage;J)V",
+ playback_state, playback_state_actions,
+ session_state.current_playback_position / kSbTimeMillisecond,
+ static_cast<jfloat>(session_state.actual_playback_rate), j_title.Get(),
+ j_artist.Get(), j_album.Get(), j_artwork.Get(), durationInMilliseconds);
+}
+
+const CobaltExtensionMediaSessionApi kMediaSessionApi = {
+ kCobaltExtensionMediaSessionName, 1, &OnMediaSessionStateChanged};
+
+const void* GetMediaSessionApi() {
+ return &kMediaSessionApi;
+}
+
+} // namespace shared
+} // namespace android
+} // namespace starboard
+
+extern "C" SB_EXPORT_PLATFORM void
+Java_dev_cobalt_media_CobaltMediaSession_nativeInvokeAction(JNIEnv* env,
+ jclass unused_clazz,
+ jlong action,
+ jlong seek_ms) {
+ starboard::android::shared::NativeInvokeAction(action, seek_ms);
+}
diff --git a/src/starboard/android/shared/cobalt/android_media_session_client.h b/src/starboard/android/shared/android_media_session_client.h
similarity index 86%
rename from src/starboard/android/shared/cobalt/android_media_session_client.h
rename to src/starboard/android/shared/android_media_session_client.h
index 5fc3f41..83fd0cc 100644
--- a/src/starboard/android/shared/cobalt/android_media_session_client.h
+++ b/src/starboard/android/shared/android_media_session_client.h
@@ -15,17 +15,21 @@
#ifndef STARBOARD_ANDROID_SHARED_COBALT_ANDROID_MEDIA_SESSION_CLIENT_H_
#define STARBOARD_ANDROID_SHARED_COBALT_ANDROID_MEDIA_SESSION_CLIENT_H_
+#include "cobalt/extension/media_session.h"
+
namespace starboard {
namespace android {
namespace shared {
-namespace cobalt {
// Duplicated in CobaltMediaSession.java
enum PlaybackState { kPlaying = 0, kPaused = 1, kNone = 2 };
void UpdateActiveSessionPlatformPlaybackState(PlaybackState state);
-} // namespace cobalt
+void OnMediaSessionStateChanged(
+ const CobaltExtensionMediaSessionState session_state);
+
+const void* GetMediaSessionApi();
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/src/starboard/android/shared/application_android.cc b/src/starboard/android/shared/application_android.cc
index e8fab42..1352a26 100644
--- a/src/starboard/android/shared/application_android.cc
+++ b/src/starboard/android/shared/application_android.cc
@@ -69,6 +69,8 @@
return "WindowFocusGained";
case ApplicationAndroid::AndroidCommand::kWindowFocusLost:
return "WindowFocusLost";
+ case ApplicationAndroid::AndroidCommand::kDeepLink:
+ return "DeepLink";
default:
return "unknown";
}
@@ -298,6 +300,21 @@
case AndroidCommand::kStop:
sync_state = activity_state_ = cmd.type;
break;
+ case AndroidCommand::kDeepLink:
+ char* deep_link = static_cast<char*>(cmd.data);
+ SB_LOG(INFO) << "AndroidCommand::kDeepLink: deep_link=" << deep_link
+ << " state=" << state();
+ if (deep_link != NULL) {
+ if (state() == kStateUnstarted) {
+ SetStartLink(deep_link);
+ SB_LOG(INFO) << "ApplicationAndroid SetStartLink";
+ SbMemoryDeallocate(static_cast<void*>(deep_link));
+ } else {
+ SB_LOG(INFO) << "ApplicationAndroid Inject: kSbEventTypeLink";
+ Inject(new Event(kSbEventTypeLink, deep_link, SbMemoryDeallocate));
+ }
+ }
+ break;
}
// If there's a window, sync the app state to the Activity lifecycle, letting
@@ -337,7 +354,7 @@
// Android main thread. This lets the MediaSession get released now without
// having to wait to bounce between threads.
JniEnvExt* env = JniEnvExt::Get();
- env->CallStarboardVoidMethodOrAbort("beforeSuspend", "()V");
+ env->CallStarboardVoidMethod("beforeSuspend", "()V");
}
AndroidCommand cmd {type, data};
ScopedLock lock(android_command_mutex_);
@@ -538,12 +555,14 @@
}
void ApplicationAndroid::HandleDeepLink(const char* link_url) {
+ SB_LOG(INFO) << "ApplicationAndroid::HandleDeepLink link_url=" << link_url;
if (link_url == NULL || link_url[0] == '\0') {
return;
}
char* deep_link = SbStringDuplicate(link_url);
SB_DCHECK(deep_link);
- Inject(new Event(kSbEventTypeLink, deep_link, SbMemoryDeallocate));
+
+ SendAndroidCommand(AndroidCommand::kDeepLink, deep_link);
}
extern "C" SB_EXPORT_PLATFORM
diff --git a/src/starboard/android/shared/application_android.h b/src/starboard/android/shared/application_android.h
index 3ad4820..6dad099 100644
--- a/src/starboard/android/shared/application_android.h
+++ b/src/starboard/android/shared/application_android.h
@@ -51,6 +51,7 @@
kNativeWindowDestroyed,
kWindowFocusGained,
kWindowFocusLost,
+ kDeepLink,
} CommandType;
CommandType type;
diff --git a/src/starboard/android/shared/audio_track_audio_sink_type.h b/src/starboard/android/shared/audio_track_audio_sink_type.h
index 1a8cb37..43b0c86 100644
--- a/src/starboard/android/shared/audio_track_audio_sink_type.h
+++ b/src/starboard/android/shared/audio_track_audio_sink_type.h
@@ -74,10 +74,10 @@
SbMediaAudioSampleType sample_type,
int sampling_frequency_hz);
- MinRequiredFramesTester min_required_frames_tester_;
Mutex min_required_frames_map_mutex_;
// The minimum frames required to avoid underruns of different frequencies.
std::map<int, int> min_required_frames_map_;
+ MinRequiredFramesTester min_required_frames_tester_;
};
class AudioTrackAudioSink : public SbAudioSinkPrivate {
diff --git a/src/starboard/android/shared/cobalt/android_media_session_client.cc b/src/starboard/android/shared/cobalt/android_media_session_client.cc
deleted file mode 100644
index 30ec630..0000000
--- a/src/starboard/android/shared/cobalt/android_media_session_client.cc
+++ /dev/null
@@ -1,353 +0,0 @@
-// Copyright 2017 The Cobalt Authors. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include "starboard/android/shared/cobalt/android_media_session_client.h"
-
-#include "base/time/time.h"
-#include "cobalt/media_session/media_session_action_details.h"
-#include "cobalt/media_session/media_session_client.h"
-#include "cobalt/script/sequence.h"
-#include "starboard/android/shared/jni_env_ext.h"
-#include "starboard/android/shared/jni_utils.h"
-#include "starboard/common/log.h"
-#include "starboard/common/mutex.h"
-#include "starboard/once.h"
-
-namespace starboard {
-namespace android {
-namespace shared {
-namespace cobalt {
-
-using ::cobalt::media_session::MediaImage;
-using ::cobalt::media_session::MediaMetadataInit;
-using ::cobalt::media_session::MediaSession;
-using ::cobalt::media_session::MediaSessionAction;
-using ::cobalt::media_session::MediaSessionActionDetails;
-using ::cobalt::media_session::MediaSessionClient;
-using ::cobalt::media_session::MediaSessionPlaybackState;
-using ::cobalt::media_session::MediaSessionState;
-using ::cobalt::media_session::kMediaSessionActionPause;
-using ::cobalt::media_session::kMediaSessionActionPlay;
-using ::cobalt::media_session::kMediaSessionActionSeekto;
-using ::cobalt::media_session::kMediaSessionActionSeekbackward;
-using ::cobalt::media_session::kMediaSessionActionSeekforward;
-using ::cobalt::media_session::kMediaSessionActionStop;
-using ::cobalt::media_session::kMediaSessionActionPrevioustrack;
-using ::cobalt::media_session::kMediaSessionActionNexttrack;
-using ::cobalt::media_session::kMediaSessionPlaybackStateNone;
-using ::cobalt::media_session::kMediaSessionPlaybackStatePaused;
-using ::cobalt::media_session::kMediaSessionPlaybackStatePlaying;
-
-using MediaImageSequence = ::cobalt::script::Sequence<MediaImage>;
-
-using ::starboard::android::shared::JniEnvExt;
-using ::starboard::android::shared::ScopedLocalJavaRef;
-
-namespace {
-
-// These constants are from android.media.session.PlaybackState
-const jlong kPlaybackStateActionStop = 1 << 0;
-const jlong kPlaybackStateActionPause = 1 << 1;
-const jlong kPlaybackStateActionPlay = 1 << 2;
-const jlong kPlaybackStateActionRewind = 1 << 3;
-const jlong kPlaybackStateActionSkipToPrevious = 1 << 4;
-const jlong kPlaybackStateActionSkipToNext = 1 << 5;
-const jlong kPlaybackStateActionFastForward = 1 << 6;
-const jlong kPlaybackStateActionSetRating = 1 << 7; // not supported
-const jlong kPlaybackStateActionSeekTo = 1 << 8;
-
-// Converts a MediaSessionClient::AvailableActions bitset into
-// a android.media.session.PlaybackState jlong bitset.
-jlong MediaSessionActionsToPlaybackStateActions(
- const MediaSessionState::AvailableActionsSet& actions) {
- jlong result = 0;
- if (actions[kMediaSessionActionPause]) {
- result |= kPlaybackStateActionPause;
- }
- if (actions[kMediaSessionActionPlay]) {
- result |= kPlaybackStateActionPlay;
- }
- if (actions[kMediaSessionActionSeekbackward]) {
- result |= kPlaybackStateActionRewind;
- }
- if (actions[kMediaSessionActionPrevioustrack]) {
- result |= kPlaybackStateActionSkipToPrevious;
- }
- if (actions[kMediaSessionActionNexttrack]) {
- result |= kPlaybackStateActionSkipToNext;
- }
- if (actions[kMediaSessionActionSeekforward]) {
- result |= kPlaybackStateActionFastForward;
- }
- if (actions[kMediaSessionActionSeekto]) {
- result |= kPlaybackStateActionSeekTo;
- }
- if (actions[kMediaSessionActionStop]) {
- result |= kPlaybackStateActionStop;
- }
- return result;
-}
-
-PlaybackState MediaSessionPlaybackStateToPlaybackState(
- MediaSessionPlaybackState in_state) {
- switch (in_state) {
- case kMediaSessionPlaybackStatePlaying:
- return kPlaying;
- case kMediaSessionPlaybackStatePaused:
- return kPaused;
- case kMediaSessionPlaybackStateNone:
- return kNone;
- }
-}
-
-MediaSessionAction PlaybackStateActionToMediaSessionAction(jlong action) {
- MediaSessionAction result;
- switch (action) {
- case kPlaybackStateActionPause:
- result = kMediaSessionActionPause;
- break;
- case kPlaybackStateActionPlay:
- result = kMediaSessionActionPlay;
- break;
- case kPlaybackStateActionRewind:
- result = kMediaSessionActionSeekbackward;
- break;
- case kPlaybackStateActionSkipToPrevious:
- result = kMediaSessionActionPrevioustrack;
- break;
- case kPlaybackStateActionSkipToNext:
- result = kMediaSessionActionNexttrack;
- break;
- case kPlaybackStateActionFastForward:
- result = kMediaSessionActionSeekforward;
- break;
- case kPlaybackStateActionSeekTo:
- result = kMediaSessionActionSeekto;
- break;
- case kPlaybackStateActionStop:
- result = kMediaSessionActionStop;
- break;
- default:
- SB_NOTREACHED() << "Unsupported MediaSessionAction 0x"
- << std::hex << action;
- result = static_cast<MediaSessionAction>(-1);
- }
- return result;
-}
-
-MediaSessionPlaybackState PlaybackStateToMediaSessionPlaybackState(
- PlaybackState state) {
- MediaSessionPlaybackState result;
- switch (state) {
- case kPlaying:
- result = kMediaSessionPlaybackStatePlaying;
- break;
- case kPaused:
- result = kMediaSessionPlaybackStatePaused;
- break;
- case kNone:
- result = kMediaSessionPlaybackStateNone;
- break;
- default:
- SB_NOTREACHED() << "Unsupported PlaybackState " << state;
- result = static_cast<MediaSessionPlaybackState>(-1);
- }
- return result;
-}
-
-} // namespace
-
-class AndroidMediaSessionClient : public MediaSessionClient {
- static SbOnceControl once_flag;
- static SbMutex mutex;
- // The last MediaSessionClient to become active, or null.
- // Used to route Java callbacks.
- // In practice, only one MediaSessionClient will become active at a time.
- // Protected by "mutex"
- static AndroidMediaSessionClient* active_client;
-
- static void OnceInit() { SbMutexCreate(&mutex); }
-
- public:
- static void NativeInvokeAction(jlong action, jlong seek_ms) {
- SbOnce(&once_flag, OnceInit);
- SbMutexAcquire(&mutex);
-
- if (active_client != NULL) {
- std::unique_ptr<MediaSessionActionDetails> details(
- new MediaSessionActionDetails());
- details->set_action(PlaybackStateActionToMediaSessionAction(action));
- // CobaltMediaSession.java only sets seek_ms for SeekTo (not ff/rew).
- if (details->action() == kMediaSessionActionSeekto) {
- details->set_seek_time(seek_ms / 1000.0);
- }
- active_client->InvokeAction(std::move(details));
- }
-
- SbMutexRelease(&mutex);
- }
-
- static void UpdateActiveSessionPlatformPlaybackState(
- MediaSessionPlaybackState state) {
- SbOnce(&once_flag, OnceInit);
- SbMutexAcquire(&mutex);
-
- if (active_client != NULL) {
- active_client->UpdatePlatformPlaybackState(state);
- }
-
- SbMutexRelease(&mutex);
- }
-
- AndroidMediaSessionClient() {}
-
- virtual ~AndroidMediaSessionClient() {
- SbOnce(&once_flag, OnceInit);
- SbMutexAcquire(&mutex);
- if (active_client == this) {
- active_client = NULL;
- }
- SbMutexRelease(&mutex);
- }
-
- void OnMediaSessionStateChanged(
- const MediaSessionState& session_state) override {
- JniEnvExt* env = JniEnvExt::Get();
-
- jint playback_state = MediaSessionPlaybackStateToPlaybackState(
- session_state.actual_playback_state());
-
- SbOnce(&once_flag, OnceInit);
- SbMutexAcquire(&mutex);
- if (playback_state != kNone) {
- active_client = this;
- } else if (active_client == this) {
- active_client = NULL;
- }
- SbMutexRelease(&mutex);
-
- jlong playback_state_actions = MediaSessionActionsToPlaybackStateActions(
- session_state.available_actions());
-
- ScopedLocalJavaRef<jstring> j_title;
- ScopedLocalJavaRef<jstring> j_artist;
- ScopedLocalJavaRef<jstring> j_album;
- ScopedLocalJavaRef<jobjectArray> j_artwork;
-
- if (session_state.has_metadata()) {
- const MediaMetadataInit& media_metadata(session_state.metadata().value());
-
- j_title.Reset(
- env->NewStringStandardUTFOrAbort(media_metadata.title().c_str()));
- j_artist.Reset(
- env->NewStringStandardUTFOrAbort(media_metadata.artist().c_str()));
- j_album.Reset(
- env->NewStringStandardUTFOrAbort(media_metadata.album().c_str()));
-
- if (media_metadata.has_artwork()) {
- const MediaImageSequence& artwork(media_metadata.artwork());
- ScopedLocalJavaRef<jclass> media_image_class(
- env->FindClassExtOrAbort("dev/cobalt/media/MediaImage"));
- jmethodID media_image_constructor = env->GetMethodID(
- media_image_class.Get(), "<init>",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
- env->AbortOnException();
-
- j_artwork.Reset(static_cast<jobjectArray>(env->NewObjectArray(
- artwork.size(), media_image_class.Get(), NULL)));
- env->AbortOnException();
-
- ScopedLocalJavaRef<jstring> j_src;
- ScopedLocalJavaRef<jstring> j_sizes;
- ScopedLocalJavaRef<jstring> j_type;
- for (MediaImageSequence::size_type i = 0; i < artwork.size(); i++) {
- const MediaImage& media_image(artwork.at(i));
- j_src.Reset(
- env->NewStringStandardUTFOrAbort(media_image.src().c_str()));
- j_sizes.Reset(
- env->NewStringStandardUTFOrAbort(media_image.sizes().c_str()));
- j_type.Reset(
- env->NewStringStandardUTFOrAbort(media_image.type().c_str()));
-
- ScopedLocalJavaRef<jobject> j_media_image(
- env->NewObject(media_image_class.Get(), media_image_constructor,
- j_src.Get(), j_sizes.Get(), j_type.Get()));
-
- env->SetObjectArrayElement(j_artwork.Get(), i, j_media_image.Get());
- }
- }
- }
-
- jlong durationInMilliseconds;
- if (session_state.duration() == kSbTimeMax) {
- // Set duration to negative if duration is unknown or infinite, as with live
- // playback.
- // https://developer.android.com/reference/android/support/v4/media/MediaMetadataCompat#METADATA_KEY_DURATION
- durationInMilliseconds = -1;
- } else {
- // SbTime is measured in microseconds while Android MediaSession expects
- // duration in milliseconds.
- durationInMilliseconds = session_state.duration() / kSbTimeMillisecond;
- }
-
- env->CallStarboardVoidMethodOrAbort(
- "updateMediaSession",
- "(IJJFLjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
- "[Ldev/cobalt/media/MediaImage;J)V",
- playback_state, playback_state_actions,
- session_state.current_playback_position() / kSbTimeMillisecond,
- static_cast<jfloat>(session_state.actual_playback_rate()),
- j_title.Get(), j_artist.Get(), j_album.Get(), j_artwork.Get(),
- durationInMilliseconds);
- }
-};
-
-SbOnceControl AndroidMediaSessionClient::once_flag = SB_ONCE_INITIALIZER;
-SbMutex AndroidMediaSessionClient::mutex;
-AndroidMediaSessionClient* AndroidMediaSessionClient::active_client = NULL;
-
-void UpdateActiveSessionPlatformPlaybackState(PlaybackState state) {
- MediaSessionPlaybackState media_session_state =
- PlaybackStateToMediaSessionPlaybackState(state);
-
- AndroidMediaSessionClient::UpdateActiveSessionPlatformPlaybackState(
- media_session_state);
-}
-
-} // namespace cobalt
-} // namespace shared
-} // namespace android
-} // namespace starboard
-
-using starboard::android::shared::cobalt::AndroidMediaSessionClient;
-
-extern "C" SB_EXPORT_PLATFORM
-void Java_dev_cobalt_media_CobaltMediaSession_nativeInvokeAction(
- JNIEnv* env,
- jclass unused_clazz,
- jlong action,
- jlong seek_ms) {
- AndroidMediaSessionClient::NativeInvokeAction(action, seek_ms);
-}
-
-namespace cobalt {
-namespace media_session {
-
-// static
-std::unique_ptr<MediaSessionClient> MediaSessionClient::Create() {
- return std::unique_ptr<MediaSessionClient>(new AndroidMediaSessionClient());
-}
-
-} // namespace media_session
-} // namespace cobalt
diff --git a/src/starboard/android/shared/cobalt/cobalt_platform.gyp b/src/starboard/android/shared/cobalt/cobalt_platform.gyp
index 6f6379a..2d4ba5b 100644
--- a/src/starboard/android/shared/cobalt/cobalt_platform.gyp
+++ b/src/starboard/android/shared/cobalt/cobalt_platform.gyp
@@ -20,11 +20,10 @@
'sources': [
'android_user_authorizer.h',
'android_user_authorizer.cc',
- 'android_media_session_client.cc',
],
'dependencies': [
- '<(DEPTH)/cobalt/media_session/media_session.gyp:media_session'
+ '<(DEPTH)/cobalt/base/base.gyp:base',
],
- },
+ }
],
}
diff --git a/src/starboard/android/shared/cobalt/configuration.gypi b/src/starboard/android/shared/cobalt/configuration.gypi
index c374599..c9cdba0 100644
--- a/src/starboard/android/shared/cobalt/configuration.gypi
+++ b/src/starboard/android/shared/cobalt/configuration.gypi
@@ -18,7 +18,6 @@
'variables': {
'in_app_dial': 0,
- 'custom_media_session_client': 1,
'enable_account_manager': 1,
# The 'android_system' font package installs only minimal fonts, with a
diff --git a/src/starboard/android/shared/cobalt/configuration.py b/src/starboard/android/shared/cobalt/configuration.py
index 96c11fe..f9cf7d7 100644
--- a/src/starboard/android/shared/cobalt/configuration.py
+++ b/src/starboard/android/shared/cobalt/configuration.py
@@ -66,6 +66,11 @@
# A map of failing or crashing tests per target.
__FILTERED_TESTS = {
+ 'layout_tests': [
+ # Android relies of system fonts and some older Android builds do not
+ # have the update (Emoji 11.0) NotoColorEmoji.ttf installed.
+ 'CSS3FontsLayoutTests/Layout.Test/color_emojis_should_render_properly'
+ ],
'renderer_test': [
# Instead of returning an error when allocating too much texture
# memory, Android instead just terminates the process. Since this
diff --git a/src/starboard/android/shared/gyp_configuration.py b/src/starboard/android/shared/gyp_configuration.py
index f0eba6e..04d5372 100644
--- a/src/starboard/android/shared/gyp_configuration.py
+++ b/src/starboard/android/shared/gyp_configuration.py
@@ -327,6 +327,18 @@
'SbDirectoryGetNextTest.SunnyDayStaticContent',
'SbDirectoryOpenTest.SunnyDayStaticContent',
'SbFileGetPathInfoTest.WorksOnStaticContentDirectories',
+ # Android doesn't currently support specifying a bitrate under 8000
+ 'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
+ # There are issues with playback of heeac format files where the input
+ # |frames_per_channel| is 6912, instead of 12544 (as with other
+ # formats).
+ 'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/6',
+ # These tests are disabled due to not receiving the kEndOfStream
+ # player state update within the specified timeout.
+ 'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/7',
+ 'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/8',
+ 'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/9',
+ 'SbPlayerWriteSampleTests/SbPlayerWriteSampleTest.NoInput/10',
],
}
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index b39a61d..f78c347 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -344,6 +344,11 @@
render_timestamp_ns);
}
+void MediaCodecBridge::SetPlaybackRate(double playback_rate) {
+ JniEnvExt::Get()->CallVoidMethodOrAbort(
+ j_media_codec_bridge_, "setPlaybackRate", "(D)V", playback_rate);
+}
+
jint MediaCodecBridge::Flush() {
return JniEnvExt::Get()->CallIntMethodOrAbort(j_media_codec_bridge_, "flush",
"()I");
diff --git a/src/starboard/android/shared/media_codec_bridge.h b/src/starboard/android/shared/media_codec_bridge.h
index 1fc6618..77452c1 100644
--- a/src/starboard/android/shared/media_codec_bridge.h
+++ b/src/starboard/android/shared/media_codec_bridge.h
@@ -131,6 +131,7 @@
void ReleaseOutputBuffer(jint index, jboolean render);
void ReleaseOutputBufferAtTimestamp(jint index, jlong render_timestamp_ns);
+ void SetPlaybackRate(double playback_rate);
jint Flush();
SurfaceDimensions GetOutputDimensions();
AudioOutputFormatResult GetAudioOutputFormat();
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index d5668d0..fc5fa0d 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -177,6 +177,12 @@
}
}
+void MediaDecoder::SetPlaybackRate(double playback_rate) {
+ SB_DCHECK(media_type_ == kSbMediaTypeVideo);
+ SB_DCHECK(media_codec_bridge_);
+ media_codec_bridge_->SetPlaybackRate(playback_rate);
+}
+
// static
void* MediaDecoder::DecoderThreadEntryPoint(void* context) {
SB_DCHECK(context);
diff --git a/src/starboard/android/shared/media_decoder.h b/src/starboard/android/shared/media_decoder.h
index 41a7a11..80d0b7e 100644
--- a/src/starboard/android/shared/media_decoder.h
+++ b/src/starboard/android/shared/media_decoder.h
@@ -86,6 +86,8 @@
void WriteInputBuffer(const scoped_refptr<InputBuffer>& input_buffer);
void WriteEndOfStream();
+ void SetPlaybackRate(double playback_rate);
+
size_t GetNumberOfPendingTasks() const {
return number_of_pending_tasks_.load();
}
diff --git a/src/starboard/android/shared/media_is_supported.cc b/src/starboard/android/shared/media_is_supported.cc
index 26d1e19..42aa227 100644
--- a/src/starboard/android/shared/media_is_supported.cc
+++ b/src/starboard/android/shared/media_is_supported.cc
@@ -16,12 +16,19 @@
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/media_common.h"
+#include "starboard/string.h"
bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
SbMediaAudioCodec audio_codec,
const char* key_system) {
using starboard::android::shared::IsWidevineL1;
using starboard::android::shared::JniEnvExt;
+
+ if (SbStringFindCharacter(key_system, ';')) {
+ // TODO: Remove this check and enable key system with attributes support.
+ return false;
+ }
+
// Filter anything other then aac as we only support paid content on aac.
// TODO: Add support of Opus if we are going to support software based drm
// systems.
diff --git a/src/starboard/android/shared/player_components_factory.cc b/src/starboard/android/shared/player_components_factory.cc
index 948b85a..6ac984a 100644
--- a/src/starboard/android/shared/player_components_factory.cc
+++ b/src/starboard/android/shared/player_components_factory.cc
@@ -38,6 +38,15 @@
namespace {
+const int kAudioSinkFramesAlignment = 256;
+const int kDefaultAudioSinkMinFramesPerAppend = 1024;
+const int kDefaultAudioSinkMaxCachedFrames =
+ 8 * kDefaultAudioSinkMinFramesPerAppend;
+
+int AlignUp(int value, int alignment) {
+ return (value + alignment - 1) / alignment * alignment;
+}
+
class PlayerComponentsFactory : public PlayerComponents::Factory {
bool CreateSubComponents(
const CreationParameters& creation_parameters,
@@ -94,6 +103,8 @@
creation_parameters.decode_target_graphics_context_provider(),
creation_parameters.max_video_capabilities(), error_message));
if (video_decoder_impl->is_valid()) {
+ video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm(
+ video_decoder_impl.get()));
*video_renderer_sink = video_decoder_impl->GetSink();
video_decoder->reset(video_decoder_impl.release());
} else {
@@ -103,12 +114,35 @@
"Failed to create video decoder with error: " + *error_message;
return false;
}
-
- video_render_algorithm->reset(new android::shared::VideoRenderAlgorithm);
}
return true;
}
+
+ void GetAudioRendererParams(const CreationParameters& creation_parameters,
+ int* max_cached_frames,
+ int* min_frames_per_append) const override {
+ SB_DCHECK(max_cached_frames);
+ SB_DCHECK(min_frames_per_append);
+ SB_DCHECK(kDefaultAudioSinkMinFramesPerAppend % kAudioSinkFramesAlignment ==
+ 0);
+ *min_frames_per_append = kDefaultAudioSinkMinFramesPerAppend;
+
+ // AudioRenderer prefers to use kSbMediaAudioSampleTypeFloat32 and only uses
+ // kSbMediaAudioSampleTypeInt16Deprecated when float32 is not supported.
+ int min_frames_required = SbAudioSinkGetMinBufferSizeInFrames(
+ creation_parameters.audio_sample_info().number_of_channels,
+ SbAudioSinkIsAudioSampleTypeSupported(kSbMediaAudioSampleTypeFloat32)
+ ? kSbMediaAudioSampleTypeFloat32
+ : kSbMediaAudioSampleTypeInt16Deprecated,
+ creation_parameters.audio_sample_info().samples_per_second);
+ // On Android 5.0, the size of audio renderer sink buffer need to be two
+ // times larger than AudioTrack minBufferSize. Otherwise, AudioTrack may
+ // stop working after pause.
+ *max_cached_frames =
+ min_frames_required * 2 + kDefaultAudioSinkMinFramesPerAppend;
+ *max_cached_frames = AlignUp(*max_cached_frames, kAudioSinkFramesAlignment);
+ }
};
} // namespace
diff --git a/src/starboard/android/shared/player_create.cc b/src/starboard/android/shared/player_create.cc
index e2cfb48..a3a9a2a 100644
--- a/src/starboard/android/shared/player_create.cc
+++ b/src/starboard/android/shared/player_create.cc
@@ -14,7 +14,7 @@
#include "starboard/player.h"
-#include "starboard/android/shared/cobalt/android_media_session_client.h"
+#include "starboard/android/shared/android_media_session_client.h"
#include "starboard/android/shared/video_decoder.h"
#include "starboard/android/shared/video_window.h"
#include "starboard/common/log.h"
@@ -28,9 +28,8 @@
using starboard::shared::starboard::player::filter::
FilterBasedPlayerWorkerHandler;
using starboard::shared::starboard::player::PlayerWorker;
-using starboard::android::shared::cobalt::kPlaying;
-using starboard::android::shared::cobalt::
- UpdateActiveSessionPlatformPlaybackState;
+using starboard::android::shared::kPlaying;
+using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
using starboard::android::shared::VideoDecoder;
SbPlayer SbPlayerCreate(SbWindow window,
@@ -130,7 +129,10 @@
UpdateActiveSessionPlatformPlaybackState(kPlaying);
}
- if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture) {
+ if (creation_param->output_mode != kSbPlayerOutputModeDecodeToTexture &&
+ // TODO: This is temporary for supporting background media playback.
+ // Need to be removed with media refactor.
+ video_codec != kSbMediaVideoCodecNone) {
// Check the availability of the video window. As we only support one main
// player, and sub players are in decode to texture mode on Android, a
// single video window should be enough.
diff --git a/src/starboard/android/shared/player_destroy.cc b/src/starboard/android/shared/player_destroy.cc
index b790729..bdfbba3 100644
--- a/src/starboard/android/shared/player_destroy.cc
+++ b/src/starboard/android/shared/player_destroy.cc
@@ -14,12 +14,11 @@
#include "starboard/player.h"
-#include "starboard/android/shared/cobalt/android_media_session_client.h"
+#include "starboard/android/shared/android_media_session_client.h"
#include "starboard/shared/starboard/player/player_internal.h"
-using starboard::android::shared::cobalt::kNone;
-using starboard::android::shared::cobalt::
- UpdateActiveSessionPlatformPlaybackState;
+using starboard::android::shared::kNone;
+using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
void SbPlayerDestroy(SbPlayer player) {
if (!SbPlayerIsValid(player)) {
diff --git a/src/starboard/android/shared/player_set_playback_rate.cc b/src/starboard/android/shared/player_set_playback_rate.cc
index 2a01fcd..233cd92 100644
--- a/src/starboard/android/shared/player_set_playback_rate.cc
+++ b/src/starboard/android/shared/player_set_playback_rate.cc
@@ -14,14 +14,13 @@
#include "starboard/player.h"
-#include "starboard/android/shared/cobalt/android_media_session_client.h"
+#include "starboard/android/shared/android_media_session_client.h"
#include "starboard/common/log.h"
#include "starboard/shared/starboard/player/player_internal.h"
-using starboard::android::shared::cobalt::kPaused;
-using starboard::android::shared::cobalt::kPlaying;
-using starboard::android::shared::cobalt::
- UpdateActiveSessionPlatformPlaybackState;
+using starboard::android::shared::kPaused;
+using starboard::android::shared::kPlaying;
+using starboard::android::shared::UpdateActiveSessionPlatformPlaybackState;
bool SbPlayerSetPlaybackRate(SbPlayer player, double playback_rate) {
if (!SbPlayerIsValid(player)) {
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 2621ee4..65c5044 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -55,6 +55,7 @@
'accessibility_get_text_to_speech_settings.cc',
'accessibility_set_captions_enabled.cc',
'android_main.cc',
+ 'android_media_session_client.cc',
'application_android.cc',
'application_android.h',
'atomic_public.h',
diff --git a/src/starboard/android/shared/system_get_extensions.cc b/src/starboard/android/shared/system_get_extensions.cc
index 869446b..1ffca16 100644
--- a/src/starboard/android/shared/system_get_extensions.cc
+++ b/src/starboard/android/shared/system_get_extensions.cc
@@ -15,7 +15,9 @@
#include "starboard/system.h"
#include "cobalt/extension/configuration.h"
+#include "cobalt/extension/media_session.h"
#include "cobalt/extension/platform_service.h"
+#include "starboard/android/shared/android_media_session_client.h"
#include "starboard/android/shared/configuration.h"
#include "starboard/android/shared/platform_service.h"
#include "starboard/common/log.h"
@@ -28,5 +30,8 @@
if (SbStringCompareAll(name, kCobaltExtensionConfigurationName) == 0) {
return starboard::android::shared::GetConfigurationApi();
}
+ if (SbStringCompareAll(name, kCobaltExtensionMediaSessionName) == 0) {
+ return starboard::android::shared::GetMediaSessionApi();
+ }
return NULL;
}
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index 5a5030e..96a490a 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -377,6 +377,7 @@
media_decoder_->Initialize(
std::bind(&VideoDecoder::ReportError, this, _1, _2));
}
+ media_decoder_->SetPlaybackRate(playback_rate_);
return true;
}
media_decoder_.reset();
@@ -577,6 +578,13 @@
return kSbDecodeTargetInvalid;
}
+void VideoDecoder::SetPlaybackRate(double playback_rate) {
+ playback_rate_ = playback_rate;
+ if (media_decoder_) {
+ media_decoder_->SetPlaybackRate(playback_rate);
+ }
+}
+
void VideoDecoder::OnNewTextureAvailable() {
has_new_texture_available_.store(true);
}
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 8202f57..508817f 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -83,6 +83,8 @@
void Reset() override;
SbDecodeTarget GetCurrentDecodeTarget() override;
+ void SetPlaybackRate(double playback_rate);
+
bool is_valid() const { return media_decoder_ != NULL; }
void OnNewTextureAvailable();
@@ -133,6 +135,8 @@
int32_t frame_width_ = 0;
int32_t frame_height_ = 0;
+ double playback_rate_ = 1.0;
+
// The last enqueued |SbMediaColorMetadata|.
optional<SbMediaColorMetadata> color_metadata_;
diff --git a/src/starboard/android/shared/video_render_algorithm.cc b/src/starboard/android/shared/video_render_algorithm.cc
index 1b7e9aa..b234937 100644
--- a/src/starboard/android/shared/video_render_algorithm.cc
+++ b/src/starboard/android/shared/video_render_algorithm.cc
@@ -18,6 +18,7 @@
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/media_common.h"
+#include "starboard/common/log.h"
namespace starboard {
namespace android {
@@ -36,6 +37,12 @@
} // namespace
+VideoRenderAlgorithm::VideoRenderAlgorithm(VideoDecoder* video_decoder)
+ : video_decoder_(video_decoder) {
+ SB_DCHECK(video_decoder_);
+ video_decoder_->SetPlaybackRate(playback_rate_);
+}
+
void VideoRenderAlgorithm::Render(
MediaTimeProvider* media_time_provider,
std::list<scoped_refptr<VideoFrame>>* frames,
@@ -61,6 +68,10 @@
if (!is_audio_playing) {
break;
}
+ if (playback_rate != playback_rate_) {
+ playback_rate_ = playback_rate;
+ video_decoder_->SetPlaybackRate(playback_rate);
+ }
jlong early_us = frames->front()->timestamp() - playback_time;
diff --git a/src/starboard/android/shared/video_render_algorithm.h b/src/starboard/android/shared/video_render_algorithm.h
index 187a1ca..132e871 100644
--- a/src/starboard/android/shared/video_render_algorithm.h
+++ b/src/starboard/android/shared/video_render_algorithm.h
@@ -18,6 +18,7 @@
#include <list>
#include "starboard/android/shared/jni_env_ext.h"
+#include "starboard/android/shared/video_decoder.h"
#include "starboard/shared/starboard/player/filter/video_render_algorithm.h"
namespace starboard {
@@ -27,6 +28,8 @@
class VideoRenderAlgorithm : public ::starboard::shared::starboard::player::
filter::VideoRenderAlgorithm {
public:
+ explicit VideoRenderAlgorithm(VideoDecoder* video_decoder);
+
void Render(MediaTimeProvider* media_time_provider,
std::list<scoped_refptr<VideoFrame>>* frames,
VideoRendererSink::DrawFrameCB draw_frame_cb) override;
@@ -45,6 +48,8 @@
jobject j_video_frame_release_time_helper_ = nullptr;
};
+ VideoDecoder* video_decoder_ = nullptr;
+ double playback_rate_ = 1.0;
VideoFrameReleaseTimeHelper video_frame_release_time_helper_;
int dropped_frames_ = 0;
};
diff --git a/src/starboard/android/shared/video_window.cc b/src/starboard/android/shared/video_window.cc
index 8329a85..88b7037 100644
--- a/src/starboard/android/shared/video_window.cc
+++ b/src/starboard/android/shared/video_window.cc
@@ -123,6 +123,11 @@
// during painting.
ScopedLock lock(*GetViewSurfaceMutex());
+ if (!g_native_video_window) {
+ SB_LOG(INFO) << "Tried to clear video window when it was null.";
+ return;
+ }
+
if (g_reset_surface_on_clear_window) {
int width = ANativeWindow_getWidth(g_native_video_window);
int height = ANativeWindow_getHeight(g_native_video_window);
@@ -133,11 +138,6 @@
}
}
- if (!g_native_video_window) {
- SB_LOG(INFO) << "Tried to clear video window when it was null.";
- return;
- }
-
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
if (display == EGL_NO_DISPLAY) {
diff --git a/src/starboard/build/platform_configuration.py b/src/starboard/build/platform_configuration.py
index 77d8e2e..ebb054e 100644
--- a/src/starboard/build/platform_configuration.py
+++ b/src/starboard/build/platform_configuration.py
@@ -24,7 +24,6 @@
from starboard.sabi import sabi
from starboard.tools import ccache
from starboard.tools import environment
-from starboard.tools import goma
from starboard.tools import paths
from starboard.tools import platform
from starboard.tools.config import Config
@@ -72,12 +71,8 @@
self._application_configuration = None
self._application_configuration_search_path = [self._directory]
- # Specifies the build accelerator to be used. Default is ccache. Goma can
- # be used but will be deprecated.
- if 'FORCE_GOMA' in os.environ and os.environ['FORCE_GOMA'] == 1:
- build_accelerator = goma.Goma()
- else:
- build_accelerator = ccache.Ccache()
+ # Specifies the build accelerator to be used. Default is ccache.
+ build_accelerator = ccache.Ccache()
if build_accelerator.Use():
self.build_accelerator = build_accelerator.GetName()
logging.info('Using %sbuild accelerator.', self.build_accelerator)
diff --git a/src/starboard/build/toolchain/gcc_toolchain.gni b/src/starboard/build/toolchain/gcc_toolchain.gni
index ef3d025..6fab1ae 100644
--- a/src/starboard/build/toolchain/gcc_toolchain.gni
+++ b/src/starboard/build/toolchain/gcc_toolchain.gni
@@ -17,7 +17,6 @@
# limitations under the License.
import("//starboard/build/toolchain/clang.gni")
-import("//starboard/build/toolchain/goma.gni")
# This template defines a toolchain for something that works like gcc
# (including clang).
@@ -111,29 +110,8 @@
forward_variables_from(invoker_toolchain_args, "*")
}
- # When the invoker has explicitly overridden use_goma or cc_wrapper in the
- # toolchain args, use those values, otherwise default to the global one.
- # This works because the only reasonable override that toolchains might
- # supply for these values are to force-disable them.
- if (defined(toolchain_args.use_goma)) {
- toolchain_uses_goma = toolchain_args.use_goma
- } else {
- toolchain_uses_goma = use_goma
- }
-
- # When the invoker has explicitly overridden use_goma in the
- # toolchain args, use those values, otherwise default to the global one.
- # This works because the only reasonable override that toolchains might
- # supply for these values are to force-disable them.
- if (toolchain_uses_goma) {
- goma_path = "$goma_dir/gomacc"
- compiler_prefix = "${goma_path} "
- } else {
- compiler_prefix = ""
- }
-
- cc = compiler_prefix + invoker.cc
- cxx = compiler_prefix + invoker.cxx
+ cc = invoker.cc
+ cxx = invoker.cxx
ar = invoker.ar
ld = invoker.ld
if (!defined(asm)) {
diff --git a/src/starboard/build/toolchain/goma.gni b/src/starboard/build/toolchain/goma.gni
deleted file mode 100644
index 84914b2..0000000
--- a/src/starboard/build/toolchain/goma.gni
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (c) 2013 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.
-
-# Modifications Copyright 2017 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Defines the configuration of Goma.
-
-# Allow ports to set an alternate default for use_goma
-import("//$starboard_path/configuration.gni")
-
-declare_args() {
- if (!defined(use_goma)) {
- # Set to true to enable distributed compilation using Goma. By default we
- # use Goma for stub and linux.
- use_goma = false
- }
-
- # Set the default value based on the platform.
- if (host_os == "win" || host_os == "winrt_81" ||
- host_os == "winrt_81_phone" || host_os == "winrt_10") {
- # Absolute directory containing the gomacc.exe binary.
- goma_dir = "C:\goma\goma-win64"
- } else {
- # Absolute directory containing the gomacc binary.
- goma_dir = getenv("HOME") + "/goma"
- }
-}
diff --git a/src/starboard/common/file.h b/src/starboard/common/file.h
index a91c2fa..c24cdfe 100644
--- a/src/starboard/common/file.h
+++ b/src/starboard/common/file.h
@@ -36,6 +36,74 @@
// |preserve_root|: Whether or not the root directory should be preserved.
bool SbFileDeleteRecursive(const char* path, bool preserve_root);
+// A class that opens an SbFile in its constructor and closes it in its
+// destructor, so the file is open for the lifetime of the object. Member
+// functions call the corresponding SbFile function.
+class ScopedFile {
+ public:
+ ScopedFile(const char* path,
+ int flags,
+ bool* out_created,
+ SbFileError* out_error)
+ : file_(kSbFileInvalid) {
+ file_ = SbFileOpen(path, flags, out_created, out_error);
+ }
+
+ ScopedFile(const char* path, int flags, bool* out_created)
+ : file_(kSbFileInvalid) {
+ file_ = SbFileOpen(path, flags, out_created, NULL);
+ }
+
+ ScopedFile(const char* path, int flags) : file_(kSbFileInvalid) {
+ file_ = SbFileOpen(path, flags, NULL, NULL);
+ }
+
+ ~ScopedFile() { SbFileClose(file_); }
+
+ SbFile file() const { return file_; }
+
+ bool IsValid() const { return SbFileIsValid(file_); }
+
+ int64_t Seek(SbFileWhence whence, int64_t offset) const {
+ return SbFileSeek(file_, whence, offset);
+ }
+
+ int Read(char* data, int size) const { return SbFileRead(file_, data, size); }
+
+ int ReadAll(char* data, int size) const {
+ return SbFileReadAll(file_, data, size);
+ }
+
+ int Write(const char* data, int size) const {
+ return SbFileWrite(file_, data, size);
+ }
+
+ int WriteAll(const char* data, int size) const {
+ return SbFileWriteAll(file_, data, size);
+ }
+
+ bool Truncate(int64_t length) const { return SbFileTruncate(file_, length); }
+
+ bool Flush() const { return SbFileFlush(file_); }
+
+ bool GetInfo(SbFileInfo* out_info) const {
+ return SbFileGetInfo(file_, out_info);
+ }
+
+ int64_t GetSize() const {
+ SbFileInfo file_info;
+ bool success = GetInfo(&file_info);
+ return (success ? file_info.size : -1);
+ }
+
+ // disallow copy and move operations
+ ScopedFile(const ScopedFile&) = delete;
+ ScopedFile& operator=(const ScopedFile&) = delete;
+
+ private:
+ SbFile file_;
+};
+
} // namespace starboard
#endif // STARBOARD_COMMON_FILE_H_
diff --git a/src/starboard/configuration.h b/src/starboard/configuration.h
index 98a80c9..e6ba5e4 100644
--- a/src/starboard/configuration.h
+++ b/src/starboard/configuration.h
@@ -71,6 +71,9 @@
// Add Concealed state support.
#define SB_ADD_CONCEALED_STATE_SUPPORT_VERSION 14
+// Iteration on UI navigation API.
+#define SB_UI_NAVIGATION2_VERSION SB_EXPERIMENTAL_API_VERSION
+
// --- Release Candidate Feature Defines -------------------------------------
// --- Common Detected Features ----------------------------------------------
diff --git a/src/starboard/doc/evergreen/symbolizing_minidumps.md b/src/starboard/doc/evergreen/symbolizing_minidumps.md
new file mode 100644
index 0000000..4931300
--- /dev/null
+++ b/src/starboard/doc/evergreen/symbolizing_minidumps.md
@@ -0,0 +1,129 @@
+# How to Symbolize Dumps
+
+Evergreen will store the minidumps (`.dmp` files) from the 2 most recent
+crashes on the disk. They are stored under `kSbSystemPathCacheDirectory` in the
+subdirectory `crashpad_database/`. These files can be used along with
+Breakpad's tools to get a full stacktrace of the past crashes. This can help in
+debugging, as these minidumps have the information for the dynamic
+`libcobalt.so` module correctly mapped, which a out-of-the-box dumper could not
+manage.
+
+## Obtaining the Tools to Symbolize Minidumps
+
+Tools for symbolizing these dumps are available through
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/). Breakpad is
+an open source crash reporting library that we use to obtain symbol files
+(`.sym`) from unstripped binaries, and to process the symbol files with the
+minidumps to produce human-readable stacktraces.
+
+
+### Building Breakpad
+
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/) provides
+instructions for building these tools yourself. The
+[Getting Started with Breakpad](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/getting_started_with_breakpad.md)
+guide is a good place to start if you want to go through the docs yourself, but
+below is a brief overview of how to get and build the tools.
+
+Download depot_tools:
+```
+$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+$ export PATH=/path/to/depot_tools:$PATH
+```
+
+Get breakpad:
+```
+$ mkdir breakpad && cd breakpad
+$ fetch breakpad
+$ cd src
+```
+
+Build breakpad:
+```
+$ ./configure && make
+```
+
+This will build the processor (`src/processor/minidump_stackwalk`), and when
+building on Linux it will also build the `dump_syms` tool
+(`src/tools/linux/dump_syms/dump_syms`).
+
+**IMPORTANT:** Once you have fetched Breakpad, you should remove the path to
+depot_tools from your `$PATH` environment variable, as it can conflict with
+Cobalt's depot_tools.
+
+## Symbolizing Minidumps
+
+Now that you have all the tools you need, we can symbolize the dumps. To be
+able to symbolize Cobalt using Evergreen, you need to be get the unstripped
+`libcobalt.so` binary. These will be available as assets in GitHub releases
+[on Cobalt's public GitHub repo](https://github.com/youtube/cobalt/releases).
+
+libcobalt releases will be labeled by the Evergreen version, the architecture,
+the config, and the ELF build id, for example
+"libcobalt_1.0.10_unstripped_armeabi_softfp_qa_ac3132014007df0e.tgz". Here, we
+have:
+* Evergreen Version: 1.0.10
+* Architecture: armeabi_softfp
+* Config: qa
+* ELF Build Id: ac3132014007df0e
+
+Knowing the architecture and config you want, you'll just have to know which
+version of Evergreen you're on or obtain the build id of the library. If you
+need to obtain the ELF build id, you can do so easily by running
+`readelf -n /path/to/libcobalt.so` and look at the hash displayed after "Build
+ID:".
+
+Now you can get the debug symbols from the library using the tools we
+downloaded previously. Unpack libcobalt and dump its symbols into a file:
+
+```
+$ tar xzf /path/to/libcobalt.tgz
+$ /path/to/dump_syms /path/to/unzipped/libcobalt > libcobalt.so.sym
+$ head -n1 libcobalt.so.sym
+MODULE Linux x86_64 6462A5D44C0843D100000000000000000 libcobalt.so
+```
+
+We run `head` on the symbol file to get the debug identifier, the hash
+displayed above (in this case, it's `6462A5D44C0843D100000000000000000`). Now
+we can create the file structure that `minidump_stackwalker` expects and run
+the stackwalker against the minidump:
+
+```
+$ mkdir -p symbols/libcobalt.so/<debug identifier>/
+$ mv libcobalt.so.sym symbols/libcobalt.so/<debug identifier>/
+$ /path/to/minidump_stackwalk /path/to/your/minidump.dmp symbols/
+```
+
+`minidump_stackwalk` produces verbose output on stderr, and the stacktrace on
+stdout, so you may want to redirect stderr.
+
+### Addendum: Adding Other Symbols
+
+We can use the process above to add symbols for any library or executable you
+use, not just `libcobalt.so`. To do this, all you have to do is run the
+`dump_syms` tools on the binary you want symbolized and put that in the
+"symbols/" folder.
+
+```
+$ /path/to/dump_syms /path/to/<your-binary> > <your-binary>.sym
+$ head -n1 <your-binary.sym>
+MODULE Linux x86_64 <debug-identifier> <your-binary>
+$ mkdir -p symbols/<your-binary>/<debug-identifier>
+$ mv <your-binary>.sym symbols/<your-binary>/<debug-identifier>/
+```
+
+Now, `minidump_stackwalk` should symbolize sections within `<your-binary>`. For
+example, if you decided to symbolize the `loader_app`, it would transform the
+stacktrace output from `minidump_stackwalk` from:
+
+```
+9 loader_app + 0x3a31130
+```
+
+to:
+
+```
+9 loader_app!SbEventHandle [sandbox.cc : 44 + 0x8]
+```
+
+Note that the addresses will vary.
diff --git a/src/starboard/elf_loader/elf_loader_impl.cc b/src/starboard/elf_loader/elf_loader_impl.cc
index 3bfc632..cd7a03e 100644
--- a/src/starboard/elf_loader/elf_loader_impl.cc
+++ b/src/starboard/elf_loader/elf_loader_impl.cc
@@ -40,7 +40,7 @@
elf_header_loader_.reset(new ElfHeader());
if (!elf_header_loader_->LoadElfHeader(elf_file_.get())) {
- SB_LOG(ERROR) << "Failed to loaded ELF header";
+ SB_LOG(ERROR) << "Failed to load ELF header";
return false;
}
diff --git a/src/starboard/elf_loader/evergreen_info.h b/src/starboard/elf_loader/evergreen_info.h
index fecd079..2adfe29 100644
--- a/src/starboard/elf_loader/evergreen_info.h
+++ b/src/starboard/elf_loader/evergreen_info.h
@@ -26,6 +26,7 @@
// the starboard implementation.
#define EVERGREEN_FILE_PATH_MAX_SIZE 4096
#define EVERGREEN_BUILD_ID_MAX_SIZE 128
+#define EVERGREEN_USER_AGENT_MAX_SIZE 2048
#define IS_EVERGREEN_ADDRESS(address, evergreen_info) \
(evergreen_info.base_address != 0 && \
@@ -62,6 +63,12 @@
size_t build_id_length;
} EvergreenInfo;
+// Annotations that Evergreen will add to Crashpad for more detailed crash
+// reports.
+typedef struct EvergreenAnnotations {
+ char user_agent_string[EVERGREEN_USER_AGENT_MAX_SIZE];
+} EvergreenAnnotations;
+
// Set the Evergreen information. Should be called only from the
// elf_loader module. Passing NULL clears the currently stored
// information.
diff --git a/src/starboard/elf_loader/sandbox.cc b/src/starboard/elf_loader/sandbox.cc
index 857ab89..9dd2f73 100644
--- a/src/starboard/elf_loader/sandbox.cc
+++ b/src/starboard/elf_loader/sandbox.cc
@@ -21,6 +21,7 @@
#include "starboard/event.h"
#include "starboard/mutex.h"
#include "starboard/shared/starboard/command_line.h"
+#include "starboard/string.h"
#include "starboard/thread_types.h"
#include "third_party/crashpad/wrapper/wrapper.h"
@@ -63,6 +64,20 @@
g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
g_elf_loader.LookupSymbol("SbEventHandle"));
+ auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+ g_elf_loader.LookupSymbol("GetCobaltUserAgentString"));
+ if (!get_user_agent_func) {
+ SB_LOG(ERROR) << "Failed to get user agent string";
+ } else {
+ EvergreenAnnotations cobalt_version_info;
+ SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+ SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+ EVERGREEN_USER_AGENT_MAX_SIZE);
+ third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+ cobalt_version_info);
+ SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+ }
+
if (!g_sb_event_func) {
SB_LOG(ERROR) << "Failed to find SbEventHandle.";
return;
diff --git a/src/starboard/evergreen/arm/shared/gyp_configuration.gypi b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
index 404d3a3..4192b6c 100644
--- a/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm/shared/gyp_configuration.gypi
@@ -24,6 +24,8 @@
# Force char to be signed.
'-fsigned-char',
+ '-ffunction-sections',
+ '-fdata-sections',
],
'linker_flags': [
diff --git a/src/starboard/evergreen/arm64/gyp_configuration.gypi b/src/starboard/evergreen/arm64/gyp_configuration.gypi
index b2a0525..f8843b2 100644
--- a/src/starboard/evergreen/arm64/gyp_configuration.gypi
+++ b/src/starboard/evergreen/arm64/gyp_configuration.gypi
@@ -22,6 +22,8 @@
'compiler_flags': [
'-isystem<(cobalt_repo_root)/third_party/musl/arch/aarch64',
+ '-ffunction-sections',
+ '-fdata-sections',
],
},
diff --git a/src/starboard/evergreen/shared/launcher.py b/src/starboard/evergreen/shared/launcher.py
index 5cfcd55..c8a2787 100644
--- a/src/starboard/evergreen/shared/launcher.py
+++ b/src/starboard/evergreen/shared/launcher.py
@@ -24,7 +24,7 @@
_BASE_STAGING_DIRECTORY = 'evergreen_staging'
_CRASHPAD_TARGET = 'crashpad_handler'
-_LOADER_TARGET = 'elf_loader_sandbox'
+_DEFAULT_LOADER_TARGET = 'elf_loader_sandbox'
class Launcher(abstract_launcher.AbstractLauncher):
@@ -66,6 +66,10 @@
if not self.loader_config:
raise ValueError('|loader_config| cannot be |None|.')
+ self.loader_target = kwargs.get('loader_target')
+ if not self.loader_target:
+ self.loader_target = _DEFAULT_LOADER_TARGET
+
self.loader_out_directory = kwargs.get('loader_out_directory')
if not self.loader_out_directory:
self.loader_out_directory = paths.BuildOutputDirectory(
@@ -99,7 +103,7 @@
self.launcher = abstract_launcher.LauncherFactory(
self.loader_platform,
- _LOADER_TARGET,
+ self.loader_target,
self.loader_config,
device_id,
target_params=target_command_line_params,
@@ -150,7 +154,7 @@
# out/evergreen_staging/linux-x64x11_devel__evergreen-x64_devel/deploy/elf_loader_sandbox
staging_directory_loader = os.path.join(self.staging_directory, 'deploy',
- _LOADER_TARGET)
+ self.loader_target)
# out/evergreen_staging/linux-x64x11_devel__evergreen-x64_devel/deploy/elf_loader_sandbox/content/app/nplb/
staging_directory_evergreen = os.path.join(staging_directory_loader,
@@ -161,7 +165,7 @@
# specified by |staging_directory_loader|. A symbolic link here would cause
# future symbolic links to fall through to the original out-directories.
shutil.copytree(
- os.path.join(self.loader_out_directory, 'deploy', _LOADER_TARGET),
+ os.path.join(self.loader_out_directory, 'deploy', self.loader_target),
staging_directory_loader)
shutil.copy(
os.path.join(self.loader_out_directory, 'deploy', _CRASHPAD_TARGET,
@@ -174,8 +178,8 @@
# TODO: Make the Linux launcher run from the deploy directory, no longer
# create these symlinks, and remove the NOTE from the docstring.
port_symlink.MakeSymLink(
- os.path.join(staging_directory_loader, _LOADER_TARGET),
- os.path.join(self.staging_directory, _LOADER_TARGET))
+ os.path.join(staging_directory_loader, self.loader_target),
+ os.path.join(self.staging_directory, self.loader_target))
port_symlink.MakeSymLink(
os.path.join(staging_directory_loader, _CRASHPAD_TARGET),
os.path.join(self.staging_directory, _CRASHPAD_TARGET))
diff --git a/src/starboard/evergreen/testing/linux/clean_up.sh b/src/starboard/evergreen/testing/linux/clean_up.sh
new file mode 100755
index 0000000..d454e2f
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/clean_up.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function clean_up() {
+ clear_storage
+}
+
diff --git a/src/starboard/evergreen/testing/linux/clear_storage.sh b/src/starboard/evergreen/testing/linux/clear_storage.sh
new file mode 100755
index 0000000..6f14bde
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/clear_storage.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function clear_storage() {
+ echo " Clearing Cobalt storage"
+ eval "find ${STORAGE_DIR}/ -mindepth 1 -maxdepth 1 ! -name 'icu' -exec rm -rf {} +" 1> /dev/null
+}
+
diff --git a/src/starboard/evergreen/testing/linux/create_file.sh b/src/starboard/evergreen/testing/linux/create_file.sh
new file mode 100755
index 0000000..0841fb1
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/create_file.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function create_file() {
+ if [[ $# -ne 1 ]]; then
+ error " create_file only accepts a single argument"
+ return 1
+ fi
+
+ echo " Creating file '${1}'"
+
+ eval "mkdir -p \"\"$(dirname "${1}")\"\"" 1> /dev/null
+ eval "touch \"\"${1}\"\"" 1> /dev/null
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/linux/delete_file.sh b/src/starboard/evergreen/testing/linux/delete_file.sh
new file mode 100755
index 0000000..8e8b376
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/delete_file.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function delete_file() {
+ if [[ $# -ne 1 ]]; then
+ error " delete_file only accepts a single argument"
+ return 1
+ fi
+
+ echo " Deleting file '${1}'"
+
+ eval "rm -f \"\"${1}\"\"" 1> /dev/null
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/linux/deploy_cobalt.sh b/src/starboard/evergreen/testing/linux/deploy_cobalt.sh
new file mode 100755
index 0000000..7f5bb9c
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/deploy_cobalt.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function deploy_cobalt() {
+ if [[ -z "${OUT}" ]]; then
+ info "Please set the environment variable 'OUT'"
+ exit 1
+ fi
+
+ echo " Checking '${OUT}'"
+
+ PATHS=("${OUT}/deploy/loader_app/loader_app" \
+ "${OUT}/deploy/loader_app/content/app/cobalt/lib/libcobalt.so" \
+ "${OUT}/deploy/loader_app/content/app/cobalt/content/")
+
+ for file in "${PATHS[@]}"; do
+ if [[ ! -e "${file}" ]]; then
+ echo " Failed to find '${file}'"
+ exit 1
+ fi
+ done
+
+ echo " Required files were found within '${OUT}'"
+
+ echo " Deploying Cobalt on local device"
+
+ echo " Generating HTML test directory"
+ eval "mkdir -p ${OUT}/deploy/loader_app/content/app/cobalt/content/web/tests/" 1> /dev/null
+
+ echo " Copying HTML test files to HTML test directory"
+ eval "cp ${1}/../tests/*.html ${OUT}/deploy/loader_app/content/app/cobalt/content/web/tests/" 1> /dev/null
+
+ clear_storage
+
+ echo " Successfully deployed!"
+}
+
diff --git a/src/starboard/evergreen/testing/linux/run_command.sh b/src/starboard/evergreen/testing/linux/run_command.sh
new file mode 100755
index 0000000..c299282
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/run_command.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function run_command() {
+ if [[ $# -ne 1 ]]; then
+ error " run_command only accepts a single argument"
+ return 1
+ fi
+
+ # We want to capture the standard output from the command.
+ echo "$(eval "${1}")"
+}
+
diff --git a/src/starboard/evergreen/testing/linux/setup.sh b/src/starboard/evergreen/testing/linux/setup.sh
new file mode 100755
index 0000000..6701ea2
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/setup.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+CONTENT="${OUT}/content/app/cobalt/content"
+STORAGE_DIR="${HOME}/.cobalt_storage"
+STORAGE_DIR_TMPFS="${STORAGE_DIR}.tmpfs"
+
+ID="id"
+TAIL="tail"
+
+# Mounting a temporary filesystem cannot be done on buildbot since it requires
+# sudo. When run locally, check for the temporary filesystem and create and
+# mount it if it does not exist.
+if [[ -z "${IS_BUILDBOT}" ]] || [[ "${IS_BUILDBOT}" -eq 0 ]]; then
+ if ! grep -qs "${STORAGE_DIR_TMPFS}" "/proc/mounts"; then
+ echo " Missing tmpfs mount at ${STORAGE_DIR_TMPFS}"
+
+ if [[ ! -e "${STORAGE_DIR_TMPFS}" ]]; then
+ mkdir -p "${STORAGE_DIR_TMPFS}" 1> /dev/null
+ fi
+
+ if [[ "$(${ID} -u)" -ne 0 ]]; then
+ echo " Not root, trying to mount tmpfs with sudo at ${STORAGE_DIR_TMPFS}"
+ sudo mount -F -t tmpfs -o size=10m "cobalt_storage.tmpfs" "${STORAGE_DIR_TMPFS}" 1> /dev/null
+ else
+ echo " Mounting tmpfs as root at ${STORAGE_DIR_TMPFS}"
+ mount -F -t tmpfs -o size=10m "cobalt_storage.tmpfs" "${STORAGE_DIR_TMPFS}" 1> /dev/null
+ fi
+ fi
+fi
+
diff --git a/src/starboard/evergreen/testing/linux/start_cobalt.sh b/src/starboard/evergreen/testing/linux/start_cobalt.sh
new file mode 100755
index 0000000..99e6e1c
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/start_cobalt.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+TIMEOUT=150
+
+# Runs Cobalt on the desired platform.
+#
+# Globals:
+# CONTENT
+# LOG_PATH
+# TIMEOUT
+#
+# Args:
+# URL, path for logging, pattern to search logs for, extra arguments.
+#
+# Returns:
+# 0 if the provided pattern was found in the logs, otherwise 1.
+function start_cobalt() {
+ if [[ $# -lt 3 ]]; then
+ error " start_cobalt missing args"
+ return 1
+ fi
+
+ URL="${1}"
+ LOG="${2}"
+ PAT="${3}"
+ ARGS="${4}"
+
+ stop_cobalt
+
+ echo " Starting Cobalt with:"
+ echo " --url=${URL}"
+ echo " --content=${CONTENT}"
+
+ for arg in $ARGS; do
+ echo " ${arg}"
+ done
+
+ echo " Logs will be output to '${LOG_PATH}/${LOG}'"
+
+ eval "${OUT}/loader_app --url=\"\"${URL}\"\" --content=${CONTENT} ${ARGS} 2>&1 | tee \"${LOG_PATH}/${LOG}\"" &
+
+ LOADER=$!
+
+ echo " Cobalt process ID is ${LOADER}"
+
+ echo " Waiting up to ${TIMEOUT} seconds for \"${PAT}\" to be logged"
+
+ wait_and_watch "${PAT}" "${LOG_PATH}/${LOG}"
+
+ FOUND=$?
+
+ echo " Finished after ${WAITED} seconds"
+
+ echo " Stopping with 'kill -9 ${LOADER}'"
+
+ kill -9 "${LOADER}" 1> /dev/null
+
+ sleep 1
+
+ if [[ "${FOUND}" -eq 1 ]]; then
+ return 0
+ fi
+
+ return 1
+}
+
diff --git a/src/starboard/evergreen/testing/linux/stop_cobalt.sh b/src/starboard/evergreen/testing/linux/stop_cobalt.sh
new file mode 100755
index 0000000..0028883
--- /dev/null
+++ b/src/starboard/evergreen/testing/linux/stop_cobalt.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function stop_cobalt() {
+ echo " Stopping Cobalt"
+ eval "kill -9 $(pidof "${OUT}/loader_app")" 1> /dev/null
+ sleep 1
+}
+
diff --git a/src/starboard/evergreen/testing/pprint.sh b/src/starboard/evergreen/testing/pprint.sh
new file mode 100755
index 0000000..c44ce31
--- /dev/null
+++ b/src/starboard/evergreen/testing/pprint.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# A set of helper functions to output colored text using ANSI escape codes.
+
+function pprint() {
+ local arg="-e"
+ if [[ "${OSTYPE}" = "*darwin*" ]]; then
+ arg=""
+ fi
+ # This command uses ANSI escape codes to attempt to output colored text. For
+ # more information see https://en.wikipedia.org/wiki/ANSI_escape_code.
+ echo "$arg" "\033[0;${1}m${2}\033[0m" >&2
+}
+
+function info() {
+ pprint 32 "${1}"
+}
+
+function warn() {
+ pprint 33 "${1}"
+}
+
+function error() {
+ pprint 31 "${1}"
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/clean_up.sh b/src/starboard/evergreen/testing/raspi/clean_up.sh
new file mode 100755
index 0000000..50d3792
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/clean_up.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source $1/../linux/clean_up.sh
+
diff --git a/src/starboard/evergreen/testing/raspi/clear_storage.sh b/src/starboard/evergreen/testing/raspi/clear_storage.sh
new file mode 100755
index 0000000..4ddaa4f
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/clear_storage.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function clear_storage() {
+ echo " Clearing Cobalt storage"
+ eval "${SSH}\"find ${STORAGE_DIR}/ -mindepth 1 -maxdepth 1 ! -name 'icu' -exec rm -rf {} +\"" 1> /dev/null
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/create_file.sh b/src/starboard/evergreen/testing/raspi/create_file.sh
new file mode 100755
index 0000000..ec054a7
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/create_file.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function create_file() {
+ if [[ $# -ne 1 ]]; then
+ error " create_file only accepts a single argument"
+ return 1
+ fi
+
+ echo " Creating file '${1}'"
+
+ eval "${SSH}\"mkdir -p \"$(dirname "${1}")\"\"" 1> /dev/null
+ eval "${SSH}\"touch \"${1}\"\"" 1> /dev/null
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/delete_file.sh b/src/starboard/evergreen/testing/raspi/delete_file.sh
new file mode 100755
index 0000000..017d5b0
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/delete_file.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function delete_file() {
+ if [[ $# -ne 1 ]]; then
+ error " delete_file only accepts a single argument"
+ return 1
+ fi
+
+ echo " Deleting file '${1}'"
+
+ eval "${SSH}\"rm -f \"${1}\"\"" 1> /dev/null
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/deploy_cobalt.sh b/src/starboard/evergreen/testing/raspi/deploy_cobalt.sh
new file mode 100755
index 0000000..36da43e
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/deploy_cobalt.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function deploy_cobalt() {
+ if [[ -z "${OUT}" ]]; then
+ info "Please set the environment variable 'OUT'"
+ exit 1
+ fi
+
+ echo " Checking '${OUT}'"
+
+ PATHS=("${OUT}/deploy/loader_app/loader_app" \
+ "${OUT}/deploy/loader_app/content/app/cobalt/lib/libcobalt.so" \
+ "${OUT}/deploy/loader_app/content/app/cobalt/content/")
+
+ for file in "${PATHS[@]}"; do
+ if [[ ! -e "${file}" ]]; then
+ echo " Failed to find '${file}'"
+ exit 1
+ fi
+ done
+
+ echo " Required files were found within '${OUT}'"
+
+ echo " Deploying to the Raspberry Pi 2 at ${RASPI_ADDR}"
+
+ echo " Regenerating Cobalt-on-Evergreen directory"
+ eval "${SSH} rm -rf /home/pi/coeg/" 1> /dev/null
+ eval "${SSH} mkdir /home/pi/coeg/" 1> /dev/null
+
+ echo " Copying loader_app to Cobalt-on-Evergreen directory"
+ eval "${SCP} ${OUT}/deploy/loader_app/loader_app pi@${RASPI_ADDR}:/home/pi/coeg/" 1> /dev/null
+
+ echo " Regenerating system image directory"
+ eval "${SSH} mkdir -p /home/pi/coeg/content/app/cobalt/lib" 1> /dev/null
+
+ echo " Copying cobalt to system image directory"
+ eval "${SCP} ${OUT}/deploy/loader_app/content/app/cobalt/lib/libcobalt.so pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/lib/" 1> /dev/null
+
+ echo " Copying content to system image directory"
+ eval "${SCP} -r ${OUT}/deploy/loader_app/content/app/cobalt/content/ pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/" 1> /dev/null
+
+ echo " Generating HTML test directory"
+ eval "${SSH} mkdir -p /home/pi/coeg/content/app/cobalt/content/web/tests/" 1> /dev/null
+
+ echo " Copying HTML test files to HTML test directory"
+ eval "${SCP} ${1}/../tests/*.html pi@${RASPI_ADDR}:/home/pi/coeg/content/app/cobalt/content/web/tests/" 1> /dev/null
+
+ echo " Successfully deployed!"
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/run_command.sh b/src/starboard/evergreen/testing/raspi/run_command.sh
new file mode 100755
index 0000000..7a92b7e
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/run_command.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function run_command() {
+ if [[ $# -ne 1 ]]; then
+ error " run_command only accepts a single argument"
+ return 1
+ fi
+
+ # We want to capture the standard output from the command.
+ echo "$(eval "${SSH}\"${1}\"")"
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/setup.sh b/src/starboard/evergreen/testing/raspi/setup.sh
new file mode 100755
index 0000000..ab815ef
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/setup.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source $1/../pprint.sh
+source $1/run_command.sh
+
+CONTENT="/home/pi/coeg/content/app/cobalt/content"
+STORAGE_DIR="/home/pi/.cobalt_storage"
+STORAGE_DIR_TMPFS="${STORAGE_DIR}.tmpfs"
+
+ID="id"
+TAIL="tail"
+
+if [[ -z "${RASPI_ADDR}" ]]; then
+ info " Please set the environment variable 'RASPI_ADDR'"
+ exit 1
+fi
+
+KEYPATH="${HOME}/.ssh/raspi"
+
+if [[ ! -f "${KEYPATH}" ]]; then
+ ssh-keygen -t rsa -q -f "${KEYPATH}" -N "" 1> /dev/null
+ ssh-copy-id -i "${KEYPATH}.pub" pi@"${RASPI_ADDR}" 1> /dev/null
+ echo " Generated SSH key-pair for Raspberry Pi 2 at ${KEYPATH}"
+else
+ echo " Re-using existing SSH key-pair for Raspberry Pi 2 at ${KEYPATH}"
+fi
+
+SSH="ssh -i ${KEYPATH} pi@${RASPI_ADDR} "
+SCP="scp -i ${KEYPATH} "
+
+echo " Targeting the Raspberry Pi 2 at ${RASPI_ADDR}"
+
+# Mounting a temporary filesystem cannot be done on buildbot since it requires
+# sudo. When run locally, check for the temporary filesystem and create and
+# mount it if it does not exist.
+if [[ -z "${IS_BUILDBOT}" ]] || [[ "${IS_BUILDBOT}" -eq 0 ]]; then
+ eval "${SSH}\"grep -qs \"${STORAGE_DIR_TMPFS}\" \"/proc/mounts\"\"" 1> /dev/null
+
+ if [[ $? -ne 0 ]]; then
+ if [[ ! -e "${STORAGE_DIR_TMPFS}" ]]; then
+ run_command "mkdir -p \"${STORAGE_DIR_TMPFS}\"" 1> /dev/null
+ fi
+
+ if [[ "$(${SSH} "${ID} -u")" -ne 0 ]]; then
+ echo " Not root, trying to mount tmpfs with sudo at ${STORAGE_DIR_TMPFS}"
+ run_command "sudo mount -F -t tmpfs -o size=10m \"cobalt_storage.tmpfs\" \"${STORAGE_DIR_TMPFS}\"" 1> /dev/null
+ else
+ echo " Mounting tmpfs as root at ${STORAGE_DIR_TMPFS}"
+ run_command "mount -F -t tmpfs -o size=10m \"cobalt_storage.tmpfs\" \"${STORAGE_DIR_TMPFS}\"" 1> /dev/null
+ fi
+ fi
+fi
+
diff --git a/src/starboard/evergreen/testing/raspi/start_cobalt.sh b/src/starboard/evergreen/testing/raspi/start_cobalt.sh
new file mode 100755
index 0000000..d1b879c
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/start_cobalt.sh
@@ -0,0 +1,78 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+TIMEOUT=150
+
+# Runs Cobalt on the desired platform.
+#
+# Globals:
+# CONTENT
+# LOG_PATH
+# TIMEOUT
+#
+# Args:
+# URL, path for logging, pattern to search logs for, extra arguments.
+#
+# Returns:
+# 0 if the provided pattern was found in the logs, otherwise 1.
+function start_cobalt() {
+ if [[ $# -lt 3 ]]; then
+ error " start_cobalt missing args"
+ return 1
+ fi
+
+ URL="${1}"
+ LOG="${2}"
+ PAT="${3}"
+ ARGS="${4}"
+
+ stop_cobalt
+
+ echo " Starting Cobalt with:"
+ echo " --url=${URL}"
+ echo " --content=${CONTENT}"
+
+ for arg in $ARGS; do
+ echo " ${arg}"
+ done
+
+ echo " Logs will be output to '${LOG_PATH}/${LOG}'"
+
+ eval "${SSH}\"/home/pi/coeg/loader_app --url=\"\"${URL}\"\" --content=${CONTENT} ${ARGS} \" 2>&1 | tee \"${LOG_PATH}/${LOG}\"" &
+
+ LOADER=$!
+
+ echo " Cobalt process ID is ${LOADER}"
+ echo " Waiting up to ${TIMEOUT} seconds for \"${PAT}\" to be logged"
+
+ wait_and_watch "${PAT}" "${LOG_PATH}/${LOG}"
+
+ FOUND=$?
+
+ echo " Finished after ${WAITED} seconds"
+ echo " Stopping with 'kill -9 ${LOADER}'"
+
+ kill -9 "${LOADER}" &> /dev/null
+
+ sleep 1
+
+ if [[ "${FOUND}" -eq 1 ]]; then
+ return 0
+ fi
+
+ return 1
+}
+
diff --git a/src/starboard/evergreen/testing/raspi/stop_cobalt.sh b/src/starboard/evergreen/testing/raspi/stop_cobalt.sh
new file mode 100755
index 0000000..ffd5bac
--- /dev/null
+++ b/src/starboard/evergreen/testing/raspi/stop_cobalt.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function stop_cobalt() {
+ echo " Stopping Cobalt"
+ eval "${SSH}\"pidof /home/pi/coeg/loader_app | xargs kill -9\"" 1> /dev/null
+ sleep 1
+}
+
diff --git a/src/starboard/evergreen/testing/run_all_tests.sh b/src/starboard/evergreen/testing/run_all_tests.sh
new file mode 100755
index 0000000..d0602fc
--- /dev/null
+++ b/src/starboard/evergreen/testing/run_all_tests.sh
@@ -0,0 +1,112 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Driver script that sets up the testing environment, collects all of the tests
+# to run, runs them, and outputs the results.
+
+DIR="$(dirname "${0}")"
+
+if [[ ! -f "${DIR}/setup.sh" ]]; then
+ echo "The script 'setup.sh' is required"
+ exit 1
+fi
+
+source $DIR/setup.sh
+
+# Find all of the test files within the 'test' subdirectory.
+TESTS=($(eval "find ${DIR}/tests -maxdepth 1 -name '*_test.sh'"))
+
+COUNT=0
+FAILED=()
+PASSED=()
+SKIPPED=()
+
+info " [==========] Deploying Cobalt."
+
+deploy_cobalt "${DIR}/${1}"
+
+info " [==========] Running ${#TESTS[@]} tests."
+
+# Loop over all of the tests found in the current directory and run them.
+for test in "${TESTS[@]}"; do
+ source $test "${DIR}/${1}"
+
+ info " [ RUN ] ${TEST_NAME}"
+
+ run_test
+
+ RESULT=$?
+
+ if [[ "${RESULT}" -eq 0 ]]; then
+ info " [ PASSED ] ${TEST_NAME}"
+ PASSED+=("${TEST_NAME}")
+ elif [[ "${RESULT}" -eq 1 ]]; then
+ error " [ FAILED ] ${TEST_NAME}"
+ FAILED+=("$TEST_NAME")
+ elif [[ "${RESULT}" -eq 2 ]]; then
+ warn " [ SKIPPED ] ${TEST_NAME}"
+ SKIPPED+=("$TEST_NAME")
+ fi
+
+ stop_cobalt &> /dev/null
+
+ COUNT=$((COUNT + 1))
+done
+
+info " [==========] ${COUNT} tests ran."
+
+# Output the number of passed tests.
+if [[ "${#PASSED[@]}" -eq 1 ]]; then
+ info " [ PASSED ] 1 test."
+elif [[ "${#PASSED[@]}" -gt 1 ]]; then
+ info " [ PASSED ] ${#PASSED[@]} tests."
+fi
+
+# Output the number of skipped tests.
+if [[ "${#SKIPPED[@]}" -eq 1 ]]; then
+ warn " [ SKIPPED ] 1 test, listed below:"
+elif [[ "${#SKIPPED[@]}" -gt 1 ]]; then
+ warn " [ SKIPPED ] ${#SKIPPED[@]} tests, listed below:"
+fi
+
+# Output each of the skipped tests.
+for test in "${SKIPPED[@]}"; do
+ warn " [ SKIPPED ] ${test}"
+done
+
+# Output the number of failed tests.
+if [[ "${#FAILED[@]}" -eq 1 ]]; then
+ error " [ FAILED ] 1 test, listed below:"
+elif [[ "${#FAILED[@]}" -gt 1 ]]; then
+ error " [ FAILED ] ${#FAILED[@]} tests, listed below:"
+fi
+
+# Output each of the failed tests.
+for test in "${FAILED[@]}"; do
+ error " [ FAILED ] ${test}"
+done
+
+info " [==========] Cleaning up."
+
+clean_up
+
+info " [==========] Finished."
+
+if [[ "${#FAILED[@]}" -eq 0 ]]; then
+ exit 0
+fi
+
+exit 1
diff --git a/src/starboard/evergreen/testing/setup.sh b/src/starboard/evergreen/testing/setup.sh
new file mode 100755
index 0000000..1546b28
--- /dev/null
+++ b/src/starboard/evergreen/testing/setup.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if [[ ! -f "${DIR}/pprint.sh" ]]; then
+ echo "The script 'pprint.sh' is required"
+ exit 1
+fi
+
+source $DIR/pprint.sh
+
+info " [==========] Preparing Cobalt."
+
+if [[ $# -ne 1 ]]; then
+ error "A platform must be provided"
+ exit 1
+fi
+
+PLATFORMS=("linux" "raspi")
+
+if [[ ! "${PLATFORMS[@]}" =~ "${1}" ]] && [[ ! -d "${DIR}/${1}" ]]; then
+ error "The platform provided must be one of the following: " "${PLATFORMS[@]}"
+ exit 1
+fi
+
+# List of all required scripts.
+SCRIPTS=("${DIR}/shared/app_key.sh" \
+ "${DIR}/shared/drain_file.sh" \
+ "${DIR}/shared/installation_slot.sh" \
+ "${DIR}/shared/wait_and_watch.sh" \
+
+ # Each of the following scripts must be provided for the targeted
+ # platform. The script 'setup.sh' must be source'd first.
+ "${DIR}/${1}/setup.sh" \
+
+ "${DIR}/${1}/clean_up.sh" \
+ "${DIR}/${1}/clear_storage.sh" \
+ "${DIR}/${1}/create_file.sh" \
+ "${DIR}/${1}/delete_file.sh" \
+ "${DIR}/${1}/deploy_cobalt.sh" \
+ "${DIR}/${1}/run_command.sh" \
+ "${DIR}/${1}/start_cobalt.sh" \
+ "${DIR}/${1}/stop_cobalt.sh")
+
+for script in "${SCRIPTS[@]}"; do
+ if [[ ! -f "${script}" ]]; then
+ error "The script '${script}' is required"
+ exit 1
+ fi
+
+ source $script "${DIR}/${1}"
+done
+
+if [[ ! -d "/tmp/" ]]; then
+ error "The '/tmp/' directory is missing"
+ exit 1
+fi
+
+# A path in the temporary directory to write test log files to.
+LOG_PATH="/tmp/youtube_test_logs/$(date +%s%3N)"
+
+mkdir -p "${LOG_PATH}" &> /dev/null
+
+if [[ ! -d "${LOG_PATH}" ]]; then
+ error "Failed to create directory at '${LOG_PATH}'"
+ exit 1
+fi
+
diff --git a/src/starboard/evergreen/testing/shared/app_key.sh b/src/starboard/evergreen/testing/shared/app_key.sh
new file mode 100755
index 0000000..506fe5d
--- /dev/null
+++ b/src/starboard/evergreen/testing/shared/app_key.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function get_bad_app_key_file_path() {
+ if [[ $# -ne 1 ]]; then
+ error " get_bad_app_key_file_path only accepts a single argument"
+ return 1
+ fi
+
+ # Warning: do not wrap '$TAIL' with double quotes or else it will not actually
+ # resolve to the correct command.
+ echo "$( \
+ sed -n "s/.*bad_app_key_file_path: \(.*app_key_[A-Za-z0-9+/=]\{32,\}\.bad\).*/\1/p" \
+ "${LOG_PATH}/${1}" | ${TAIL} -1)"
+}
+
diff --git a/src/starboard/evergreen/testing/shared/drain_file.sh b/src/starboard/evergreen/testing/shared/drain_file.sh
new file mode 100755
index 0000000..2e66aa1
--- /dev/null
+++ b/src/starboard/evergreen/testing/shared/drain_file.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function get_temporary_drain_file_path() {
+ if [[ $# -ne 1 ]]; then
+ error " get_temporary_drain_file_path only accepts a single argument"
+ return 1
+ fi
+
+ # Warning: do not wrap '$TAIL' with double quotes or else it will not actually
+ # resolve to the correct command.
+ echo "$( \
+ sed -n "s/.*Created drain file at '\(.*d_\)[A-Za-z0-9+/=]\{32,\}\(_[0-9]\{8,\}\).*/\1TEST\2/p" \
+ "${LOG_PATH}/${1}" | ${TAIL} -1)"
+}
+
diff --git a/src/starboard/evergreen/testing/shared/installation_slot.sh b/src/starboard/evergreen/testing/shared/installation_slot.sh
new file mode 100755
index 0000000..0258be3
--- /dev/null
+++ b/src/starboard/evergreen/testing/shared/installation_slot.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function get_current_installation_slot() {
+ if [[ $# -ne 1 ]]; then
+ error " get_current_installation_slot only accepts a single argument"
+ return 1
+ fi
+
+ # Warning: do not wrap '$TAIL' with double quotes or else it will not actually
+ # resolve to the correct command.
+ echo "$( \
+ sed -n "s/.*current_installation=\([0-9]\+\).*/\1/p" \
+ "${LOG_PATH}/${1}" | ${TAIL} -1)"
+}
+
diff --git a/src/starboard/evergreen/testing/shared/wait_and_watch.sh b/src/starboard/evergreen/testing/shared/wait_and_watch.sh
new file mode 100644
index 0000000..f7c39da
--- /dev/null
+++ b/src/starboard/evergreen/testing/shared/wait_and_watch.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function wait_and_watch() {
+ if [[ $# -ne 2 ]]; then
+ error " wait_and_watch requires a pattern and a path"
+ return 1
+ fi
+
+ WAITED=0
+
+ while [[ "${WAITED}" -lt "${TIMEOUT}" ]]; do
+ if grep -Eq "${1}" "${2}"; then
+ return 1
+ fi
+
+ WAITED=$((WAITED + 1))
+ sleep 1
+ done
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh b/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh
new file mode 100755
index 0000000..90f8c1f
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/abort_update_if_already_updating_test.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="AbortUpdateIfAlreadyUpdating"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "Created drain file"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to create a drain file for the test package"
+ return 1
+ fi
+
+ FILENAME="$(get_temporary_drain_file_path "${TEST_NAME}.0.log")"
+
+ clear_storage
+
+ create_file "${FILENAME}"
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "bailing out"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to find 'bailing out' in logs"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/alternative_content_test.sh b/src/starboard/evergreen/testing/tests/alternative_content_test.sh
new file mode 100755
index 0000000..00d6eca
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/alternative_content_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="AlternativeContent"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date" "--content=${CONTENT}"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to find 'App is up to date' indicating failure"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/continuous_updates_test.sh b/src/starboard/evergreen/testing/tests/continuous_updates_test.sh
new file mode 100755
index 0000000..f82d212
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/continuous_updates_test.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="ContinuousUpdates"
+TEST_FILE="tseries.html"
+
+function run_test() {
+ clear_storage
+
+ OLD_TIMEOUT="${TIMEOUT}"
+ TIMEOUT=300
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "continuous updates without restart working"
+
+ TIMEOUT="${OLD_TIMEOUT}"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to validate alternating channels"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/crashing_binary_test.sh b/src/starboard/evergreen/testing/tests/crashing_binary_test.sh
new file mode 100755
index 0000000..040538d
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/crashing_binary_test.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="CrashingBinary"
+TEST_FILE="tcrash.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from tcrash channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the tcrash package"
+ return 1
+ fi
+
+ for i in {1..3}; do
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.${i}.log" "Caught signal: SIG[A-Z]{4}"
+
+ if [[ $? -ne 0 ]]; then
+ error "Binary did not crash on attempt #${i}"
+ return 1
+ fi
+ done
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.4.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to revert to working installation after crashing 3 times"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/disabled_updater_test.sh b/src/starboard/evergreen/testing/tests/disabled_updater_test.sh
new file mode 100755
index 0000000..985f110
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/disabled_updater_test.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="DisabledUpdater"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run downloaded installation"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.2.log" "disable_updates=1" "--disable_updates"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run system installation"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/empty.html b/src/starboard/evergreen/testing/tests/empty.html
new file mode 100644
index 0000000..b8dd5a8
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/empty.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>empty</title>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh b/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh
new file mode 100755
index 0000000..f61736e
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/load_slot_being_updated_test.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="LoadSlotBeingUpdated"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ FILENAME=$(get_temporary_drain_file_path "${TEST_NAME}.0.log")
+
+ create_file "${FILENAME}"
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "Active slot draining"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to abort loading a slot being drained"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh b/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh
new file mode 100755
index 0000000..0f8a427
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/mismatched_architecture_test.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="MismatchedArchitecture"
+TEST_FILE="tmsabi.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from tmsabi channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the tmsabi package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "Failed to load ELF header"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to recognize architecture mismatch"
+ return 1
+ fi
+
+ if ! grep -Eq "RevertBack current_installation=[0-9]+" "${LOG_PATH}/${TEST_NAME}.1.log"; then
+ error "Failed to revert after mismatched download"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/noop_binary_test.sh b/src/starboard/evergreen/testing/tests/noop_binary_test.sh
new file mode 100755
index 0000000..30835a6
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/noop_binary_test.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="NoOpBinary"
+TEST_FILE="tnoop.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from tnoop channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the tnoop package"
+ return 1
+ fi
+
+ for i in {1..3}; do
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.${i}.log" "Load start=0x[a-f0-9]{8,} base_memory_address=0x[a-f0-9]{8,}"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to load binary"
+ return 1
+ fi
+
+ if grep -Eq "Starting application." "${LOG_PATH}/${TEST_NAME}.${i}.log"; then
+ error "Failed to run no-op binary"
+ return 1
+ fi
+ done
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.4.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to revert to working installation after no-oping 3 times"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/out_of_storage_test.sh b/src/starboard/evergreen/testing/tests/out_of_storage_test.sh
new file mode 100755
index 0000000..a970be8
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/out_of_storage_test.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="OutOfStorage"
+TEST_FILE="test.html"
+
+function run_test() {
+ if [[ ! -z "${IS_BUILDBOT}" ]] && [[ "${IS_BUILDBOT}" -eq 1 ]]; then
+ echo " Cannot mount temporary filesystems on builbot, skipping"
+ return 2
+ fi
+
+ clear_storage
+
+ run_command "ln -s \"${STORAGE_DIR_TMPFS}\" \"${STORAGE_DIR}\"" 1> /dev/null
+
+ # We need to explicitly clear the "storage", i.e. the temporary filesystem,
+ # since previous runs might have artifacts leftover.
+ run_command "rm -rf ${STORAGE_DIR}/*" 1> /dev/null
+
+ OLD_TIMEOUT="${TIMEOUT}"
+ TIMEOUT=300
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "Failed to update, error code is 12"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run out of storage"
+ return 1
+ fi
+
+ TIMEOUT="${OLD_TIMEOUT}"
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh b/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh
new file mode 100755
index 0000000..4e15e72
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/quick_roll_forward_test.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="QuickRollForward"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run downloaded installation"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/empty.html" "${TEST_NAME}.2.log" "quick update succeeded"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to adopt downloaded installation on different app"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/racing_updaters_test.sh b/src/starboard/evergreen/testing/tests/racing_updaters_test.sh
new file mode 100755
index 0000000..087b5cd
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/racing_updaters_test.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="RacingUpdaters"
+TEST_FILE="test.html"
+
+function wait_and_force_race_condition() {
+ if [[ $# -ne 3 ]]; then
+ error " wait_and_force_race_condition requires a pattern, a path and a filename"
+ return 1
+ fi
+
+ # The previous logs are available when this is invoked. Wait before beginning
+ # to check the logs.
+ sleep 15
+
+ wait_and_watch "${1}" "${2}"
+
+ FOUND=$?
+
+ if [[ "${FOUND}" -eq 1 ]]; then
+ create_file "${3}"
+ fi
+}
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "Created drain file"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to create a drain file for the test package"
+ return 1
+ fi
+
+ FILENAME="$(get_temporary_drain_file_path "${TEST_NAME}.0.log")"
+
+ clear_storage
+
+ wait_and_force_race_condition "Created drain file" "${LOG_PATH}/${TEST_NAME}.1.log" "${FILENAME}" &
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "failed to lock slot"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to recognize another update is updating slot"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/tcrash.html b/src/starboard/evergreen/testing/tests/tcrash.html
new file mode 100644
index 0000000..e05af63
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/tcrash.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>tcrash</title>
+<script>
+ var changeChannel = setInterval(tryChangeChannel, 500);
+
+ function tryChangeChannel() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "") {
+ return;
+ }
+
+ if (channel == "qa") {
+ if ((status == "App is up to date") || (status == "Update installed, pending restart")) {
+ window.h5vcc.updater.setUpdaterChannel("tcrash");
+ console.log("channel was changed to tcrash");
+ return;
+ }
+ }
+
+ if ((channel == "tcrash") && (status == "Update installed, pending restart")) {
+ console.log("update from tcrash channel installed");
+ clearInterval(changeChannel);
+ }
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/test.html b/src/starboard/evergreen/testing/tests/test.html
new file mode 100644
index 0000000..f404546
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/test.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>test</title>
+<script>
+ var changeChannel = setInterval(tryChangeChannel, 500);
+
+ function tryChangeChannel() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "") {
+ return;
+ }
+
+ if (channel == "qa") {
+ if ((status == "App is up to date") || (status == "Update installed, pending restart")) {
+ window.h5vcc.updater.setUpdaterChannel("test");
+ console.log("channel was changed to test");
+ return;
+ }
+ }
+
+ if ((channel == "test") && (status == "Update installed, pending restart")) {
+ console.log("update from test channel installed");
+ clearInterval(changeChannel);
+ }
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/tfailv.html b/src/starboard/evergreen/testing/tests/tfailv.html
new file mode 100644
index 0000000..c039830
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/tfailv.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>tfailv</title>
+<script>
+ var changeChannel = setInterval(tryChangeChannel, 500);
+
+ function tryChangeChannel() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "") {
+ return;
+ }
+
+ if (channel == "qa") {
+ if ((status == "App is up to date") || (status == "Update installed, pending restart")) {
+ window.h5vcc.updater.setUpdaterChannel("tfailv");
+ console.log("channel was changed to tfailv");
+ return;
+ }
+ }
+
+ if ((channel == "tfailv") && (status == "Update installed, pending restart")) {
+ console.log("update from tfailv channel installed");
+ clearInterval(changeChannel);
+ }
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/tmsabi.html b/src/starboard/evergreen/testing/tests/tmsabi.html
new file mode 100644
index 0000000..ae70b57
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/tmsabi.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>tmsabi</title>
+<script>
+ var changeChannel = setInterval(tryChangeChannel, 500);
+
+ function tryChangeChannel() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "") {
+ return;
+ }
+
+ if (channel == "qa") {
+ if ((status == "App is up to date") || (status == "Update installed, pending restart")) {
+ window.h5vcc.updater.setUpdaterChannel("tmsabi");
+ console.log("channel was changed to tmsabi");
+ return;
+ }
+ }
+
+ if ((channel == "tmsabi") && (status == "Update installed, pending restart")) {
+ console.log("update from tmsabi channel installed");
+ clearInterval(changeChannel);
+ }
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/tnoop.html b/src/starboard/evergreen/testing/tests/tnoop.html
new file mode 100644
index 0000000..ecfa328
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/tnoop.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>tnoop</title>
+<script>
+ var changeChannel = setInterval(tryChangeChannel, 500);
+
+ function tryChangeChannel() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "") {
+ return;
+ }
+
+ if (channel == "qa") {
+ if ((status == "App is up to date") || (status == "Update installed, pending restart")) {
+ window.h5vcc.updater.setUpdaterChannel("tnoop");
+ console.log("channel was changed to tnoop");
+ return;
+ }
+ }
+
+ if ((channel == "tnoop") && (status == "Update installed, pending restart")) {
+ console.log("update from tnoop channel installed");
+ clearInterval(changeChannel);
+ }
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/tseries.html b/src/starboard/evergreen/testing/tests/tseries.html
new file mode 100644
index 0000000..0d3aeff
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/tseries.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<!--
+Copyright 2020 The Cobalt Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<html>
+<meta charset="utf-8">
+<title>ttseries</title>
+<script>
+ var alternatedCount = 0;
+ var alternatingChannels = setInterval(alternateChannels, 500);
+
+ function alternateChannels() {
+ const channel = window.h5vcc.updater.getUpdaterChannel();
+ const status = window.h5vcc.updater.getUpdateStatus();
+
+ if (channel == "qa") {
+ console.log("channel changed from qa to tseries1");
+ window.h5vcc.updater.setUpdaterChannel("tseries1");
+ return;
+ }
+
+ if (alternatedCount < 5) {
+ if (status != "Update installed, pending restart") {
+ return;
+ }
+
+ if (channel == "tseries1") {
+ console.log("channel changed from tseries1 to tseries2");
+ window.h5vcc.updater.setUpdaterChannel("tseries2");
+ } else {
+ console.log("channel changed from " + channel + " to tseries1");
+ window.h5vcc.updater.setUpdaterChannel("tseries1");
+ }
+
+ alternatedCount++;
+ return;
+ }
+
+ console.log("continuous updates without restart working");
+
+ clearInterval(alternatingChannels);
+ }
+</script>
+</html>
diff --git a/src/starboard/evergreen/testing/tests/update_fails_verification_test.sh b/src/starboard/evergreen/testing/tests/update_fails_verification_test.sh
new file mode 100755
index 0000000..bfcbbc9
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/update_fails_verification_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="UpdateFailsVerification"
+TEST_FILE="tfailv.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "Verification failed. Verifier error = [0-9]+"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to fail verifying the downloaded installation"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to revert back to the system image"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh b/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh
new file mode 100755
index 0000000..2574654
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/update_works_for_only_one_app_test.sh
@@ -0,0 +1,62 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="UpdateOnlyWorksForOneApp"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run the downloaded installation"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/empty.html" "${TEST_NAME}.2.log" "quick update succeeded"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to perform a quick update on different app"
+ return 1
+ fi
+
+ FILENAME="$(get_bad_app_key_file_path "${TEST_NAME}.2.log")"
+
+ create_file "${FILENAME}"
+
+ start_cobalt "file:///tests/empty.html" "${TEST_NAME}.3.log" "RevertBack current_installation="
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to revert when the app's bad file exists"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh b/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh
new file mode 100755
index 0000000..6fc33b0
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/valid_slot_overwritten_test.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="ValidSlotOverwritten"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run the downloaded installation"
+ return 1
+ fi
+
+ SLOT="$(get_current_installation_slot "${TEST_NAME}.1.log")"
+
+ # Warning: do not wrap '$TAIL' with double quotes or else it will not actually
+ # resolve to the correct command.
+ delete_file "$(run_command "find ${STORAGE_DIR}/installation_${SLOT} -name app_key_*.good | ${TAIL} -1")"
+
+ create_file "${STORAGE_DIR}/installation_${SLOT}/app_key_TEST.good"
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.2.log" "AdoptInstallation: current_installation=${SLOT}"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to adopt installation"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh b/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh
new file mode 100755
index 0000000..66c4632
--- /dev/null
+++ b/src/starboard/evergreen/testing/tests/verify_qa_channel_update_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Unset the previous test's name and runner function.
+unset TEST_NAME
+unset TEST_FILE
+unset -f run_test
+
+TEST_NAME="VerifyQaChannelUpdate"
+TEST_FILE="test.html"
+
+function run_test() {
+ clear_storage
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.0.log" "update from test channel installed"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to download and install the test package"
+ return 1
+ fi
+
+ start_cobalt "file:///tests/${TEST_FILE}" "${TEST_NAME}.1.log" "App is up to date"
+
+ if [[ $? -ne 0 ]]; then
+ error "Failed to run the downloaded installation"
+ return 1
+ fi
+
+ return 0
+}
+
diff --git a/src/starboard/evergreen/x64/gyp_configuration.gypi b/src/starboard/evergreen/x64/gyp_configuration.gypi
index dcb689e..a9f3eaa 100644
--- a/src/starboard/evergreen/x64/gyp_configuration.gypi
+++ b/src/starboard/evergreen/x64/gyp_configuration.gypi
@@ -22,6 +22,8 @@
'compiler_flags': [
'-isystem<(cobalt_repo_root)/third_party/musl/arch/x86_64',
+ '-ffunction-sections',
+ '-fdata-sections',
],
},
diff --git a/src/starboard/evergreen/x86/gyp_configuration.gypi b/src/starboard/evergreen/x86/gyp_configuration.gypi
index f8a868f..58ebaca 100644
--- a/src/starboard/evergreen/x86/gyp_configuration.gypi
+++ b/src/starboard/evergreen/x86/gyp_configuration.gypi
@@ -22,6 +22,8 @@
'compiler_flags': [
'-isystem<(cobalt_repo_root)/third_party/musl/arch/i386',
+ '-ffunction-sections',
+ '-fdata-sections',
],
},
diff --git a/src/starboard/file.h b/src/starboard/file.h
index 5f90802..1d69e49 100644
--- a/src/starboard/file.h
+++ b/src/starboard/file.h
@@ -330,78 +330,10 @@
} // extern "C"
#endif
-#ifdef __cplusplus
-namespace starboard {
-
-// A class that opens an SbFile in its constructor and closes it in its
-// destructor, so the file is open for the lifetime of the object. Member
-// functions call the corresponding SbFile function.
-class ScopedFile {
- public:
- ScopedFile(const char* path,
- int flags,
- bool* out_created,
- SbFileError* out_error)
- : file_(kSbFileInvalid) {
- file_ = SbFileOpen(path, flags, out_created, out_error);
- }
-
- ScopedFile(const char* path, int flags, bool* out_created)
- : file_(kSbFileInvalid) {
- file_ = SbFileOpen(path, flags, out_created, NULL);
- }
-
- ScopedFile(const char* path, int flags) : file_(kSbFileInvalid) {
- file_ = SbFileOpen(path, flags, NULL, NULL);
- }
-
- ~ScopedFile() { SbFileClose(file_); }
-
- SbFile file() const { return file_; }
-
- bool IsValid() const { return SbFileIsValid(file_); }
-
- int64_t Seek(SbFileWhence whence, int64_t offset) const {
- return SbFileSeek(file_, whence, offset);
- }
-
- int Read(char* data, int size) const { return SbFileRead(file_, data, size); }
-
- int ReadAll(char* data, int size) const {
- return SbFileReadAll(file_, data, size);
- }
-
- int Write(const char* data, int size) const {
- return SbFileWrite(file_, data, size);
- }
-
- int WriteAll(const char* data, int size) const {
- return SbFileWriteAll(file_, data, size);
- }
-
- bool Truncate(int64_t length) const { return SbFileTruncate(file_, length); }
-
- bool Flush() const { return SbFileFlush(file_); }
-
- bool GetInfo(SbFileInfo* out_info) const {
- return SbFileGetInfo(file_, out_info);
- }
-
- int64_t GetSize() const {
- SbFileInfo file_info;
- bool success = GetInfo(&file_info);
- return (success ? file_info.size : -1);
- }
-
- // disallow copy and move operations
- ScopedFile(const ScopedFile&) = delete;
- ScopedFile& operator=(const ScopedFile&) = delete;
-
- private:
- SbFile file_;
-};
-
-} // namespace starboard
-#endif // ifdef __cplusplus
+#if SB_API_VERSION < 13 && defined(__cplusplus)
+extern "C++" {
+#include "starboard/common/file.h"
+} // extern "C++"
+#endif // SB_API_VERSION < 13 && defined(__cplusplus)
#endif // STARBOARD_FILE_H_
diff --git a/src/starboard/linux/shared/compiler_flags.gypi b/src/starboard/linux/shared/compiler_flags.gypi
index 53b3229..af40c10 100644
--- a/src/starboard/linux/shared/compiler_flags.gypi
+++ b/src/starboard/linux/shared/compiler_flags.gypi
@@ -36,9 +36,15 @@
],
'compiler_flags_qa_size': [
'-Os',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_qa_speed': [
'-O2',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_gold': [
'-fno-rtti',
@@ -46,9 +52,15 @@
],
'compiler_flags_gold_size': [
'-Os',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_gold_speed': [
'-O2',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'conditions': [
['clang==1', {
@@ -135,6 +147,8 @@
],
'ldflags': [
'-Wl,-rpath=$ORIGIN/lib',
+ # Cleanup unused sections
+ '-Wl,-gc-sections',
],
'target_conditions': [
['sb_pedantic_warnings==1', {
diff --git a/src/starboard/linux/shared/configuration.gni b/src/starboard/linux/shared/configuration.gni
index c4562c3..49d1b21 100644
--- a/src/starboard/linux/shared/configuration.gni
+++ b/src/starboard/linux/shared/configuration.gni
@@ -39,9 +39,3 @@
# Use ASAN by default when building with Clang.
use_asan_by_default = true
-
-declare_args() {
- # Set to true to enable distributed compilation using Goma. By default we
- # use Goma for stub and linux.
- use_goma = true
-}
diff --git a/src/starboard/linux/shared/gyp_configuration.py b/src/starboard/linux/shared/gyp_configuration.py
index f96c364..0cb799e 100644
--- a/src/starboard/linux/shared/gyp_configuration.py
+++ b/src/starboard/linux/shared/gyp_configuration.py
@@ -18,6 +18,7 @@
from starboard.build import clang
from starboard.build import platform_configuration
from starboard.tools import build
+from starboard.tools import paths
from starboard.tools.testing import test_filter
@@ -88,13 +89,25 @@
def GetTestFilters(self):
filters = super(LinuxConfiguration, self).GetTestFilters()
- for target, tests in self.__FILTERED_TESTS.iteritems():
+
+ has_cdm = os.path.isfile(
+ os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'ce_cdm', 'cdm',
+ 'include', 'cdm.h'))
+
+ if has_cdm:
+ return filters
+
+ # Filter the drm related tests, as ce_cdm is not present.
+ for target, tests in self.__DRM_RELATED_TESTS.iteritems():
filters.extend(test_filter.TestFilter(target, test) for test in tests)
return filters
def GetPathToSabiJsonFile(self):
return self.sabi_json_path
- __FILTERED_TESTS = { # pylint: disable=invalid-name
- 'nplb': ['SbDrmTest.AnySupportedKeySystems',],
+ __DRM_RELATED_TESTS = { # pylint: disable=invalid-name
+ 'nplb': [
+ 'SbDrmTest.AnySupportedKeySystems',
+ 'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+ ],
}
diff --git a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
index b5368e1..845e1df 100644
--- a/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
+++ b/src/starboard/linux/x64x11/clang/3.6/compiler_flags.gypi
@@ -50,8 +50,6 @@
'common_clang_flags': [
'-Werror',
'-fcolor-diagnostics',
- # Point to a gcc toolchain that works with this compiler.
- '--gcc-toolchain=<(GCC_TOOLCHAIN_FOLDER)',
# Default visibility to hidden, to enable dead stripping.
'-fvisibility=hidden',
# Warn for implicit type conversions that may change a value.
@@ -73,18 +71,15 @@
'-Wno-invalid-offsetof',
# Suppress 'implicit conversion changes signedness'
'-Wno-sign-conversion',
-
# shifting a negative signed value is undefined
'-Wno-shift-sign-overflow',
- # Suppress "'&&' within '||'"
- '-Wno-logical-op-parentheses',
# Suppress "comparison may be assumed to always evaluate to false"
'-Wno-tautological-undefined-compare',
# Suppress "comparison of unsigned enum expression < 0 is always false"
'-Wno-tautological-compare',
# Suppress "[type1] has C-linkage specified, but returns user-defined type [type2] which is incompatible with C"
'-Wno-return-type-c-linkage',
-
+ '-Wno-array-bounds',
# Suppress "template argument uses unnamed type"
'-Wno-unnamed-type-template-args',
# 'this' pointer cannot be NULL...pointer may be assumed
@@ -94,19 +89,19 @@
'-Wno-inconsistent-missing-override',
# Triggered by the COMPILE_ASSERT macro.
'-Wno-unused-local-typedef',
- # shifting a negative signed value is undefined
- '-Wno-shift-sign-overflow',
# Suppress "'&&' within '||'"
'-Wno-logical-op-parentheses',
- # Suppress "comparison may be assumed to always evaluate to false"
- '-Wno-tautological-undefined-compare',
- # Suppress "comparison of unsigned enum expression < 0 is always false"
- '-Wno-tautological-compare',
- # Suppress "[type1] has C-linkage specified, but returns user-defined type [type2] which is incompatible with C"
- '-Wno-return-type-c-linkage',
'-Wno-unused-parameter',
# Suppress warnings about unknown pragmas.
'-Wno-unknown-pragmas',
+ # Suppress warnings about equality checks within double parentheses.
+ '-Wno-parentheses-equality',
+ # Suppress warnings about unreachable code due to constexpr conditions
+ '-Wno-unreachable-code',
+ # Suppress warnings about values being written but not read before the next write.
+ '-Wno-unused-value',
+ # Suppress warnings related to unused compilation flags in clang.
+ '-Wno-unused-command-line-argument',
],
}],
['cobalt_fastbuild==0', {
diff --git a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
index 1506d71..8bb0020 100644
--- a/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/clang/3.6/gyp_configuration.py
@@ -18,7 +18,6 @@
import subprocess
from starboard.linux.shared import gyp_configuration as shared_configuration
-from starboard.tools import build
from starboard.tools.toolchain import ar
from starboard.tools.toolchain import bash
from starboard.tools.toolchain import clang
@@ -34,21 +33,15 @@
platform='linux-x64x11-clang-3-6',
asan_enabled_by_default=False,
sabi_json_path='starboard/sabi/default/sabi.json'):
- super(LinuxX64X11Clang36Configuration, self).__init__(
- platform,
- asan_enabled_by_default,
- sabi_json_path)
+ super(LinuxX64X11Clang36Configuration,
+ self).__init__(platform, asan_enabled_by_default, sabi_json_path)
- self.toolchain_top_dir = os.path.join(build.GetToolchainsDir(),
- 'x86_64-linux-gnu-clang-3.6')
- self.toolchain_dir = os.path.join(self.toolchain_top_dir, 'llvm',
- 'Release+Asserts')
+ self.toolchain_dir = '/usr/lib/llvm-3.6'
def SetupPlatformTools(self, build_number):
- script_path = os.path.dirname(os.path.realpath(__file__))
- # Run the script that ensures clang 3.6 is installed.
- subprocess.call(
- os.path.join(script_path, 'download_clang.sh'), cwd=script_path)
+ ret = subprocess.call('/usr/bin/clang-3.6 --version', shell=True)
+ if ret != 0:
+ raise Exception('clang-3.6 is not installed.')
def GetEnvironmentVariables(self):
toolchain_bin_dir = os.path.join(self.toolchain_dir, 'bin')
@@ -56,22 +49,18 @@
env_variables = super(LinuxX64X11Clang36Configuration,
self).GetEnvironmentVariables()
env_variables.update({
- 'CC': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
- 'clang'),
- 'CXX': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
- 'clang++'),
+ 'CC':
+ self.build_accelerator + ' ' +
+ os.path.join(toolchain_bin_dir, 'clang'),
+ 'CXX':
+ self.build_accelerator + ' ' +
+ os.path.join(toolchain_bin_dir, 'clang++'),
})
return env_variables
def GetVariables(self, config_name):
- # A significant amount of code in V8 fails to compile on clang 3.6 using
- # the debug config, due to an internal error in clang.
variables = super(LinuxX64X11Clang36Configuration,
self).GetVariables(config_name)
- variables.update({
- 'GCC_TOOLCHAIN_FOLDER':
- '\"%s\"' % os.path.join(self.toolchain_top_dir, 'libstdc++-7'),
- })
return variables
def GetTargetToolchain(self, **kwargs):
diff --git a/src/starboard/linux/x64x11/gyp_configuration.py b/src/starboard/linux/x64x11/gyp_configuration.py
index 5bf1c11..5f8069a 100644
--- a/src/starboard/linux/x64x11/gyp_configuration.py
+++ b/src/starboard/linux/x64x11/gyp_configuration.py
@@ -13,8 +13,6 @@
# limitations under the License.
"""Starboard Linux X64 X11 platform configuration."""
-import os.path
-
from starboard.linux.shared import gyp_configuration as shared_configuration
from starboard.tools.toolchain import ar
from starboard.tools.toolchain import bash
@@ -22,7 +20,6 @@
from starboard.tools.toolchain import clangxx
from starboard.tools.toolchain import cp
from starboard.tools.toolchain import touch
-from starboard.tools import paths
class LinuxX64X11Configuration(shared_configuration.LinuxConfiguration):
@@ -32,9 +29,8 @@
platform='linux-x64x11',
asan_enabled_by_default=True,
sabi_json_path='starboard/sabi/default/sabi.json'):
- super(LinuxX64X11Configuration, self).__init__(platform,
- asan_enabled_by_default,
- sabi_json_path)
+ super(LinuxX64X11Configuration,
+ self).__init__(platform, asan_enabled_by_default, sabi_json_path)
def GetTargetToolchain(self, **kwargs):
return self.GetHostToolchain(**kwargs)
@@ -57,25 +53,6 @@
bash.Shell(),
]
- def GetTestFilters(self):
- filters = super(LinuxX64X11Configuration, self).GetTestFilters()
- # Remove the exclusion filter on SbDrmTest.AnySupportedKeySystems.
- # Generally, children of linux/shared do not support widevine, but children
- # of linux/x64x11 do, if the content decryption module is present.
-
- has_cdm = os.path.isfile(
- os.path.join(paths.REPOSITORY_ROOT, 'third_party', 'cdm', 'cdm',
- 'include', 'content_decryption_module.h'))
-
- if not has_cdm:
- return filters
-
- for test_filter in filters:
- if (test_filter.target_name == 'nplb' and
- test_filter.test_name == 'SbDrmTest.AnySupportedKeySystems'):
- filters.remove(test_filter)
- return filters
-
def CreatePlatformConfig():
return LinuxX64X11Configuration(
diff --git a/src/starboard/loader_app/app_key_files.cc b/src/starboard/loader_app/app_key_files.cc
index e387987..68631e5 100644
--- a/src/starboard/loader_app/app_key_files.cc
+++ b/src/starboard/loader_app/app_key_files.cc
@@ -16,10 +16,10 @@
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
-#include "starboard/file.h"
#include "starboard/string.h"
namespace starboard {
diff --git a/src/starboard/loader_app/drain_file_helper.cc b/src/starboard/loader_app/drain_file_helper.cc
index 8c3e0b1..0a61a97 100644
--- a/src/starboard/loader_app/drain_file_helper.cc
+++ b/src/starboard/loader_app/drain_file_helper.cc
@@ -14,7 +14,7 @@
#include "starboard/loader_app/drain_file_helper.h"
-#include "starboard/file.h"
+#include "starboard/common/file.h"
#include "starboard/loader_app/drain_file.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/src/starboard/loader_app/drain_file_test.cc b/src/starboard/loader_app/drain_file_test.cc
index afbde87..35ae7cd 100644
--- a/src/starboard/loader_app/drain_file_test.cc
+++ b/src/starboard/loader_app/drain_file_test.cc
@@ -17,10 +17,10 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
-#include "starboard/file.h"
#include "starboard/loader_app/drain_file_helper.h"
#include "starboard/system.h"
#include "starboard/types.h"
diff --git a/src/starboard/loader_app/installation_manager_test.cc b/src/starboard/loader_app/installation_manager_test.cc
index 168241c..da7e8b0 100644
--- a/src/starboard/loader_app/installation_manager_test.cc
+++ b/src/starboard/loader_app/installation_manager_test.cc
@@ -17,6 +17,7 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/configuration_constants.h"
#include "starboard/loader_app/installation_store.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/src/starboard/loader_app/loader_app.cc b/src/starboard/loader_app/loader_app.cc
index c74437f..1c661af 100644
--- a/src/starboard/loader_app/loader_app.cc
+++ b/src/starboard/loader_app/loader_app.cc
@@ -25,6 +25,7 @@
#include "starboard/loader_app/loader_app_switches.h"
#include "starboard/loader_app/slot_management.h"
#include "starboard/loader_app/system_get_extension_shim.h"
+#include "starboard/memory.h"
#include "starboard/mutex.h"
#include "starboard/shared/starboard/command_line.h"
#include "starboard/string.h"
@@ -109,6 +110,20 @@
SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
}
+ auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+ g_elf_loader.LookupSymbol("GetCobaltUserAgentString"));
+ if (!get_user_agent_func) {
+ SB_LOG(ERROR) << "Failed to get user agent string";
+ } else {
+ EvergreenAnnotations cobalt_version_info;
+ SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+ SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+ EVERGREEN_USER_AGENT_MAX_SIZE);
+ third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+ cobalt_version_info);
+ SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+ }
+
g_sb_event_func = reinterpret_cast<void (*)(const SbEvent*)>(
g_elf_loader.LookupSymbol("SbEventHandle"));
diff --git a/src/starboard/loader_app/loader_app.gyp b/src/starboard/loader_app/loader_app.gyp
index 154cb61..e8f730e 100644
--- a/src/starboard/loader_app/loader_app.gyp
+++ b/src/starboard/loader_app/loader_app.gyp
@@ -90,5 +90,17 @@
},
'includes': [ '<(DEPTH)/starboard/build/deploy.gypi' ],
},
+ {
+ 'target_name': 'loader_app_tests_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'loader_app',
+ '<(DEPTH)/starboard/loader_app/app_key.gyp:app_key_test_deploy',
+ '<(DEPTH)/starboard/loader_app/app_key_files.gyp:app_key_files_test_deploy',
+ '<(DEPTH)/starboard/loader_app/drain_file.gyp:drain_file_test_deploy',
+ '<(DEPTH)/starboard/loader_app/installation_manager.gyp:installation_manager_test_deploy',
+ '<(DEPTH)/starboard/loader_app/slot_management.gyp:slot_management_test_deploy',
+ ],
+ },
],
}
diff --git a/src/starboard/loader_app/slot_management.cc b/src/starboard/loader_app/slot_management.cc
index 9e1eadd..5dc1212 100644
--- a/src/starboard/loader_app/slot_management.cc
+++ b/src/starboard/loader_app/slot_management.cc
@@ -23,6 +23,7 @@
#include "starboard/loader_app/app_key_files.h"
#include "starboard/loader_app/drain_file.h"
#include "starboard/loader_app/installation_manager.h"
+#include "starboard/memory.h"
#include "starboard/string.h"
#include "third_party/crashpad/wrapper/wrapper.h"
@@ -243,6 +244,20 @@
SB_LOG(INFO) << "Loaded Cobalt library information into Crashpad.";
}
+ auto get_user_agent_func = reinterpret_cast<const char* (*)()>(
+ library_loader->Resolve("GetCobaltUserAgentString"));
+ if (!get_user_agent_func) {
+ SB_LOG(ERROR) << "Failed to get user agent string";
+ } else {
+ EvergreenAnnotations cobalt_version_info;
+ SbMemorySet(&cobalt_version_info, sizeof(EvergreenAnnotations), 0);
+ SbStringCopy(cobalt_version_info.user_agent_string, get_user_agent_func(),
+ EVERGREEN_USER_AGENT_MAX_SIZE);
+ third_party::crashpad::wrapper::AddAnnotationsToCrashpad(
+ cobalt_version_info);
+ SB_DLOG(INFO) << "Added user agent string to Crashpad.";
+ }
+
SB_DLOG(INFO) << "Successfully loaded Cobalt!\n";
void* p = library_loader->Resolve("SbEventHandle");
if (p != NULL) {
diff --git a/src/starboard/loader_app/slot_management_test.cc b/src/starboard/loader_app/slot_management_test.cc
index 0eeb83c..5ff9e20 100644
--- a/src/starboard/loader_app/slot_management_test.cc
+++ b/src/starboard/loader_app/slot_management_test.cc
@@ -37,6 +37,10 @@
void SbEventFake(const SbEvent*) {}
+const char* GetCobaltUserAgentStringFake() {
+ return "";
+}
+
class MockLibraryLoader : public LibraryLoader {
public:
MOCK_METHOD2(Load,
@@ -132,6 +136,10 @@
Load(testing::EndsWith(lib), testing::EndsWith(content)))
.Times(1)
.WillOnce(testing::Return(true));
+ EXPECT_CALL(library_loader, Resolve("GetCobaltUserAgentString"))
+ .Times(1)
+ .WillOnce(testing::Return(
+ reinterpret_cast<void*>(&GetCobaltUserAgentStringFake)));
EXPECT_CALL(library_loader, Resolve("SbEventHandle"))
.Times(1)
.WillOnce(testing::Return(reinterpret_cast<void*>(&SbEventFake)));
@@ -261,6 +269,10 @@
testing::EndsWith("/foo")))
.Times(1)
.WillOnce(testing::Return(true));
+ EXPECT_CALL(library_loader, Resolve("GetCobaltUserAgentString"))
+ .Times(1)
+ .WillOnce(testing::Return(
+ reinterpret_cast<void*>(&GetCobaltUserAgentStringFake)));
EXPECT_CALL(library_loader, Resolve("SbEventHandle"))
.Times(1)
.WillOnce(testing::Return(reinterpret_cast<void*>(&SbEventFake)));
diff --git a/src/starboard/media.h b/src/starboard/media.h
index bdafbe4..05aaf0d 100644
--- a/src/starboard/media.h
+++ b/src/starboard/media.h
@@ -544,11 +544,44 @@
// or |video/mp4; codecs="avc1.42001E"|. It may include arbitrary parameters
// like "codecs", "channels", etc. Note that the "codecs" parameter may
// contain more than one codec, delimited by comma.
-// |key_system|: A lowercase value in fhe form of "com.example.somesystem"
+// |key_system|: A lowercase value in the 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.
+//
+// An implementation may choose to support |key_system| with extra attributes,
+// separated by ';', like
+// |com.example.somesystem; attribute_name1="value1"; attribute_name2=value1|.
+// If |key_system| with attributes is not supported by an implementation, it
+// should treat |key_system| as if it contains only the key system, and reject
+// any input containing extra attributes, i.e. it can keep using its existing
+// implementation.
+// When an implementation supports |key_system| with attributes, it has to
+// support all attributes defined by the Starboard version the implementation
+// uses.
+// An implementation should ignore any unknown attributes, and make a decision
+// solely based on the key system and the known attributes. For example, if
+// an implementation supports "com.widevine.alpha", it should also return
+// `kSbMediaSupportTypeProbably` when |key_system| is
+// |com.widevine.alpha; invalid_attribute="invalid_value"|.
+// Currently the only attribute has to be supported is |encryptionscheme|. It
+// reflects the value passed to `encryptionScheme` of
+// MediaKeySystemMediaCapability, as defined in
+// https://wicg.github.io/encrypted-media-encryption-scheme/, which can take
+// value "cenc", "cbcs", or "cbcs-1-9".
+// Empty string is not a valid value for |encryptionscheme| and the
+// implementation should return `kSbMediaSupportTypeNotSupported` when
+// |encryptionscheme| is set to "".
+// The implementation should return `kSbMediaSupportTypeNotSupported` for
+// unknown values of known attributes. For example, if an implementation
+// supports "encryptionscheme" with value "cenc", "cbcs", or "cbcs-1-9", then
+// it should return `kSbMediaSupportTypeProbably` when |key_system| is
+// |com.widevine.alpha; encryptionscheme="cenc"|, and return
+// `kSbMediaSupportTypeNotSupported` when |key_system| is
+// |com.widevine.alpha; encryptionscheme="invalid"|.
+// If an implementation supports key system with attributes on one key system,
+// it has to support key system with attributes on all key systems supported.
SB_EXPORT SbMediaSupportType
SbMediaCanPlayMimeAndKeySystem(const char* mime, const char* key_system);
diff --git a/src/starboard/nplb/drm_helpers.h b/src/starboard/nplb/drm_helpers.h
index c24b696..e6b0972 100644
--- a/src/starboard/nplb/drm_helpers.h
+++ b/src/starboard/nplb/drm_helpers.h
@@ -68,6 +68,10 @@
"com.youtube.fairplay",
};
+static const char* kEncryptionSchemes[] = {
+ "cenc", "cbcs", "cbcs-1-9",
+};
+
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/file_delete_test.cc b/src/starboard/nplb/file_delete_test.cc
index ada20d8..cea7b61 100644
--- a/src/starboard/nplb/file_delete_test.cc
+++ b/src/starboard/nplb/file_delete_test.cc
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// SbFileDelete is otherwise tested in all the other unit tests.
-
#include <string>
#include "starboard/file.h"
@@ -24,12 +22,29 @@
namespace nplb {
namespace {
-TEST(SbFileDeleteTest, NonExistentFileErrors) {
- std::string filename = starboard::nplb::GetRandomFilename();
- EXPECT_FALSE(SbFileExists(filename.c_str()));
+TEST(SbFileDeleteTest, SunnyDayDeleteExistingFile) {
+ ScopedRandomFile file;
- bool result = SbFileDelete(filename.c_str());
- EXPECT_FALSE(result);
+ EXPECT_TRUE(SbFileExists(file.filename().c_str()));
+ EXPECT_TRUE(SbFileDelete(file.filename().c_str()));
+}
+
+TEST(SbFileDeleteTest, SunnyDayDeleteExistingDirectory) {
+ ScopedRandomFile file(ScopedRandomFile::kDontCreate);
+
+ const std::string& path = file.filename();
+
+ EXPECT_FALSE(SbFileExists(path.c_str()));
+ EXPECT_TRUE(SbDirectoryCreate(path.c_str()));
+ EXPECT_TRUE(SbDirectoryCanOpen(path.c_str()));
+ EXPECT_TRUE(SbFileDelete(path.c_str()));
+}
+
+TEST(SbFileDeleteTest, RainyDayNonExistentFileErrors) {
+ ScopedRandomFile file(ScopedRandomFile::kDontCreate);
+
+ EXPECT_FALSE(SbFileExists(file.filename().c_str()));
+ EXPECT_TRUE(SbFileDelete(file.filename().c_str()));
}
} // namespace
diff --git a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
index 2e38c86..3003a56 100644
--- a/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
+++ b/src/starboard/nplb/media_can_play_mime_and_key_system_test.cc
@@ -14,14 +14,38 @@
#include "starboard/media.h"
+#include "starboard/common/string.h"
+#include "starboard/nplb/drm_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
-namespace shared {
-namespace starboard {
-namespace media {
+namespace nplb {
namespace {
+bool IsKeySystemWithAttributesSupported() {
+ for (auto key_system : kKeySystems) {
+ if (!SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ key_system)) {
+ continue;
+ }
+
+ // The key system is supported, let's check if the implementation supports
+ // attributes. By definition, when an implementation supports key system
+ // with attributes, it should make the decision without any unknown
+ // attributes. So the following |key_system_with_invalid_attribute| should
+ // be supported on such implementation.
+ std::string key_system_with_invalid_attribute = key_system;
+ key_system_with_invalid_attribute += "; invalid_attribute=\"some_value\"";
+ if (SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"",
+ key_system_with_invalid_attribute.c_str())) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
TEST(SbMediaCanPlayMimeAndKeySystem, SunnyDay) {
// Vp9
SbMediaCanPlayMimeAndKeySystem(
@@ -89,14 +113,121 @@
"audio/mp4; codecs=\"mp4a.40.2\"; channels=99", "");
ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
- // Invalid keysystem
- result = SbMediaCanPlayMimeAndKeySystem(
- "video/mp4; codecs=\"avc1.4d4015\"; width=1920; height=1080;", "abc");
+ // Invalid key system
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "abc");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "widevine");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "com.widevine.alpha.invalid");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "playready");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "com.youtube.playready.invalid");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "fairplay");
+ ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
+ result = SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ "com.youtube.fairplay.invalid");
ASSERT_EQ(result, kSbMediaSupportTypeNotSupported);
}
+TEST(SbMediaCanPlayMimeAndKeySystem, MinimumSupport) {
+ // H.264 Main Profile Level 4.2
+ SbMediaSupportType result = SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
+ "framerate=30;",
+ "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+ result = SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d402a\"; width=1920; height=1080; "
+ "framerate=60;",
+ "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+ // H.264 Main Profile Level 2.1
+ result = SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"; width=432; height=240; "
+ "framerate=15;",
+ "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+ // AAC-LC
+ result = SbMediaCanPlayMimeAndKeySystem(
+ "audio/mp4; codecs=\"mp4a.40.2\"; channels=2; bitrate=256;", "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+
+ // HE-AAC
+ result = SbMediaCanPlayMimeAndKeySystem(
+ "audio/mp4; codecs=\"mp4a.40.5\"; channels=2; bitrate=48;", "");
+ ASSERT_EQ(result, kSbMediaSupportTypeProbably);
+}
+
+TEST(SbMediaCanPlayMimeAndKeySystem, AnySupportedKeySystems) {
+ bool any_supported_key_systems = false;
+ for (auto key_system : kKeySystems) {
+ if (SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ key_system)) {
+ any_supported_key_systems = true;
+ break;
+ }
+ }
+ ASSERT_TRUE(any_supported_key_systems);
+}
+
+TEST(SbMediaCanPlayMimeAndKeySystem, KeySystemWithAttributes) {
+ if (!IsKeySystemWithAttributesSupported()) {
+ SB_LOG(INFO) << "KeySystemWithAttributes test skipped because key system"
+ << " with attribute is not supported.";
+ return;
+ }
+
+ for (auto key_system : kKeySystems) {
+ if (!SbMediaCanPlayMimeAndKeySystem("video/mp4; codecs=\"avc1.4d4015\"",
+ key_system)) {
+ continue;
+ }
+
+ EXPECT_TRUE(SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"",
+ FormatString("%s; %s=\"%s\"", key_system, "invalid_attribute",
+ "some_value")
+ .c_str()));
+
+ // "" is not a valid value for "encryptionscheme".
+ EXPECT_FALSE(SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"",
+ FormatString("%s; %s=\"%s\"", key_system, "encryptionscheme", "")
+ .c_str()));
+
+ bool has_supported_encryption_scheme = false;
+ for (auto encryption_scheme : kEncryptionSchemes) {
+ if (!SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"",
+ FormatString("%s; %s=\"%s\"", key_system, "encryptionscheme",
+ encryption_scheme)
+ .c_str())) {
+ continue;
+ }
+ has_supported_encryption_scheme = true;
+ EXPECT_TRUE(SbMediaCanPlayMimeAndKeySystem(
+ "video/mp4; codecs=\"avc1.4d4015\"",
+ FormatString("%s; %s=\"%s\"; %s=\"%s\"", key_system,
+ "encryptionscheme", encryption_scheme,
+ "invalid_attribute", "some_value")
+ .c_str()));
+ }
+
+ ASSERT_TRUE(has_supported_encryption_scheme);
+ }
+}
+
} // namespace
-} // namespace media
-} // namespace starboard
-} // namespace shared
+} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/mutex_acquire_test.cc b/src/starboard/nplb/mutex_acquire_test.cc
index c37a465..81ad198 100644
--- a/src/starboard/nplb/mutex_acquire_test.cc
+++ b/src/starboard/nplb/mutex_acquire_test.cc
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
#include "starboard/configuration.h"
+#include "starboard/mutex.h"
#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/src/starboard/nplb/mutex_acquire_try_test.cc b/src/starboard/nplb/mutex_acquire_try_test.cc
index ccd7e5b..e38e897 100644
--- a/src/starboard/nplb/mutex_acquire_try_test.cc
+++ b/src/starboard/nplb/mutex_acquire_try_test.cc
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
#include "starboard/configuration.h"
+#include "starboard/mutex.h"
#include "testing/gtest/include/gtest/gtest.h"
#if SB_API_VERSION >= 12
diff --git a/src/starboard/nplb/mutex_create_test.cc b/src/starboard/nplb/mutex_create_test.cc
index 8c11eaa..f52ba08 100644
--- a/src/starboard/nplb/mutex_create_test.cc
+++ b/src/starboard/nplb/mutex_create_test.cc
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
#include "starboard/configuration.h"
+#include "starboard/mutex.h"
#include "testing/gtest/include/gtest/gtest.h"
#if SB_API_VERSION >= 12
diff --git a/src/starboard/nplb/mutex_destroy_test.cc b/src/starboard/nplb/mutex_destroy_test.cc
index e555b70..5df8a3a 100644
--- a/src/starboard/nplb/mutex_destroy_test.cc
+++ b/src/starboard/nplb/mutex_destroy_test.cc
@@ -14,8 +14,8 @@
// Destroy is mostly Sunny Day tested in Create.
-#include "starboard/common/mutex.h"
#include "starboard/configuration.h"
+#include "starboard/mutex.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
diff --git a/src/starboard/nplb/nplb.gyp b/src/starboard/nplb/nplb.gyp
index 41f8971..ab22a51 100644
--- a/src/starboard/nplb/nplb.gyp
+++ b/src/starboard/nplb/nplb.gyp
@@ -129,6 +129,7 @@
'file_can_open_test.cc',
'file_close_test.cc',
'file_delete_recursive_test.cc',
+ 'file_delete_test.cc',
'file_get_info_test.cc',
'file_get_path_info_test.cc',
'file_helpers.cc',
@@ -191,6 +192,7 @@
'player_output_mode_supported_test.cc',
'player_test_util.cc',
'player_test_util.h',
+ 'player_write_sample_test.cc',
'random_helpers.cc',
'recursive_mutex_test.cc',
'rwlock_test.cc',
diff --git a/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc b/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
index 1214d7c..4cd5584 100644
--- a/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
+++ b/src/starboard/nplb/nplb_evergreen_compat_tests/storage_test.cc
@@ -15,9 +15,9 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/configuration.h"
-#include "starboard/file.h"
#include "starboard/memory.h"
#include "starboard/nplb/nplb_evergreen_compat_tests/checks.h"
#include "starboard/system.h"
diff --git a/src/starboard/nplb/player_test_util.cc b/src/starboard/nplb/player_test_util.cc
index 88f8233..151cbdf 100644
--- a/src/starboard/nplb/player_test_util.cc
+++ b/src/starboard/nplb/player_test_util.cc
@@ -14,12 +14,92 @@
#include "starboard/nplb/player_test_util.h"
+#include <functional>
+
+#include "starboard/audio_sink.h"
+#include "starboard/common/string.h"
#include "starboard/directory.h"
#include "starboard/nplb/player_creation_param_helpers.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
namespace starboard {
namespace nplb {
+namespace {
+
+using shared::starboard::player::video_dmp::VideoDmpReader;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using std::placeholders::_3;
+using std::placeholders::_4;
+using testing::FakeGraphicsContextProvider;
+
+void ErrorFunc(SbPlayer player,
+ void* context,
+ SbPlayerError error,
+ const char* message) {
+ atomic_bool* error_occurred = static_cast<atomic_bool*>(context);
+ error_occurred->exchange(true);
+}
+
+} // namespace
+
+std::vector<SbPlayerTestConfig> GetSupportedSbPlayerTestConfigs() {
+ const char* kAudioTestFiles[] = {"beneath_the_canopy_aac_stereo.dmp",
+ "beneath_the_canopy_aac_5_1.dmp",
+ "beneath_the_canopy_aac_mono.dmp",
+ "beneath_the_canopy_opus_5_1.dmp",
+ "beneath_the_canopy_opus_stereo.dmp",
+ "beneath_the_canopy_opus_mono.dmp",
+ "sintel_329_ec3.dmp",
+ "sintel_381_ac3.dmp",
+ "heaac.dmp"};
+
+ const char* kVideoTestFiles[] = {"beneath_the_canopy_137_avc.dmp",
+ "beneath_the_canopy_248_vp9.dmp",
+ "sintel_399_av1.dmp"};
+
+ const SbPlayerOutputMode kOutputModes[] = {kSbPlayerOutputModeDecodeToTexture,
+ kSbPlayerOutputModePunchOut};
+
+ std::vector<SbPlayerTestConfig> test_configs;
+
+ const char* kEmptyName = NULL;
+
+ for (auto audio_filename : kAudioTestFiles) {
+ VideoDmpReader dmp_reader(ResolveTestFileName(audio_filename).c_str());
+ SB_DCHECK(dmp_reader.number_of_audio_buffers() > 0);
+
+ const auto* audio_sample_info = &dmp_reader.audio_sample_info();
+ if (IsMediaConfigSupported(kSbMediaVideoCodecNone, dmp_reader.audio_codec(),
+ kSbDrmSystemInvalid, audio_sample_info,
+ "", /* max_video_capabilities */
+ kSbPlayerOutputModePunchOut)) {
+ test_configs.push_back(std::make_tuple(audio_filename, kEmptyName,
+ kSbPlayerOutputModePunchOut));
+ }
+ }
+
+ for (auto video_filename : kVideoTestFiles) {
+ VideoDmpReader dmp_reader(ResolveTestFileName(video_filename).c_str());
+ SB_DCHECK(dmp_reader.number_of_video_buffers() > 0);
+
+ for (auto output_mode : kOutputModes) {
+ if (IsMediaConfigSupported(dmp_reader.video_codec(),
+ kSbMediaAudioCodecNone, kSbDrmSystemInvalid,
+ NULL, /* audio_sample_info */
+ "", /* max_video_capabilities */
+ output_mode)) {
+ test_configs.push_back(
+ std::make_tuple(kEmptyName, video_filename, output_mode));
+ }
+ }
+ }
+
+ return test_configs;
+}
+
std::string ResolveTestFileName(const char* filename) {
std::vector<char> content_path(kSbFileMaxPath);
SB_CHECK(SbSystemGetPath(kSbSystemPathContentDirectory, content_path.data(),
@@ -45,10 +125,10 @@
SbPlayerDecoderState state,
int ticket) {}
-void DummyStatusFunc(SbPlayer player,
- void* context,
- SbPlayerState state,
- int ticket) {}
+void DummyPlayerStatusFunc(SbPlayer player,
+ void* context,
+ SbPlayerState state,
+ int ticket) {}
void DummyErrorFunc(SbPlayer player,
void* context,
@@ -116,5 +196,30 @@
#endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT)
}
+bool IsMediaConfigSupported(SbMediaVideoCodec video_codec,
+ SbMediaAudioCodec audio_codec,
+ SbDrmSystem drm_system,
+ const SbMediaAudioSampleInfo* audio_sample_info,
+ const char* max_video_capabilities,
+ SbPlayerOutputMode output_mode) {
+ if (audio_codec != kSbMediaAudioCodecNone &&
+ audio_sample_info->number_of_channels > SbAudioSinkGetMaxChannels()) {
+ return false;
+ }
+
+ atomic_bool error_occurred;
+ FakeGraphicsContextProvider fake_graphics_context_provider;
+ SbPlayer player = CallSbPlayerCreate(
+ fake_graphics_context_provider.window(), video_codec, audio_codec,
+ drm_system, audio_sample_info, max_video_capabilities,
+ DummyDeallocateSampleFunc, DummyDecoderStatusFunc, DummyPlayerStatusFunc,
+ ErrorFunc, &error_occurred, output_mode,
+ fake_graphics_context_provider.decoder_target_provider());
+ bool is_valid_player = SbPlayerIsValid(player);
+ SbPlayerDestroy(player);
+
+ return is_valid_player && !error_occurred.load();
+}
+
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/player_test_util.h b/src/starboard/nplb/player_test_util.h
index 335e414..ece5faf 100644
--- a/src/starboard/nplb/player_test_util.h
+++ b/src/starboard/nplb/player_test_util.h
@@ -16,6 +16,7 @@
#define STARBOARD_NPLB_PLAYER_TEST_UTIL_H_
#include <string>
+#include <tuple>
#include <vector>
#include "starboard/configuration_constants.h"
@@ -24,6 +25,13 @@
namespace starboard {
namespace nplb {
+typedef std::tuple<const char* /* audio_filename */,
+ const char* /* video_filename */,
+ SbPlayerOutputMode /* output_mode */>
+ SbPlayerTestConfig;
+
+std::vector<SbPlayerTestConfig> GetSupportedSbPlayerTestConfigs();
+
std::string ResolveTestFileName(const char* filename);
void DummyDeallocateSampleFunc(SbPlayer player,
@@ -64,6 +72,13 @@
bool IsOutputModeSupported(SbPlayerOutputMode output_mode,
SbMediaVideoCodec codec);
+bool IsMediaConfigSupported(SbMediaVideoCodec video_codec,
+ SbMediaAudioCodec audio_codec,
+ SbDrmSystem drm_system,
+ const SbMediaAudioSampleInfo* audio_sample_info,
+ const char* max_video_capabilities,
+ SbPlayerOutputMode output_mode);
+
} // namespace nplb
} // namespace starboard
diff --git a/src/starboard/nplb/player_write_sample_test.cc b/src/starboard/nplb/player_write_sample_test.cc
new file mode 100644
index 0000000..13977ee
--- /dev/null
+++ b/src/starboard/nplb/player_write_sample_test.cc
@@ -0,0 +1,520 @@
+// Copyright 2020 The Cobalt Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <algorithm>
+#include <deque>
+#include <functional>
+
+#include "starboard/atomic.h"
+#include "starboard/common/optional.h"
+#include "starboard/common/queue.h"
+#include "starboard/common/scoped_ptr.h"
+#include "starboard/common/string.h"
+#include "starboard/nplb/player_test_util.h"
+#include "starboard/shared/starboard/player/video_dmp_reader.h"
+#include "starboard/string.h"
+#include "starboard/testing/fake_graphics_context_provider.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace starboard {
+namespace nplb {
+namespace {
+
+using shared::starboard::player::video_dmp::VideoDmpReader;
+using testing::FakeGraphicsContextProvider;
+using ::testing::ValuesIn;
+
+const SbTimeMonotonic kDefaultWaitForDecoderStateNeedsDataTimeout =
+ 5 * kSbTimeSecond;
+const SbTimeMonotonic kDefaultWaitForPlayerStateTimeout = 5 * kSbTimeSecond;
+const SbTimeMonotonic kDefaultWaitForCallbackEventTimeout =
+ 15 * kSbTimeMillisecond;
+
+class SbPlayerWriteSampleTest
+ : public ::testing::TestWithParam<SbPlayerTestConfig> {
+ public:
+ SbPlayerWriteSampleTest();
+
+ void SetUp() override;
+ void TearDown() override;
+
+ protected:
+ struct CallbackEvent {
+ CallbackEvent() {}
+
+ CallbackEvent(SbPlayer player, SbPlayerState state, int ticket)
+ : player(player), player_state(state), ticket(ticket) {}
+
+ CallbackEvent(SbPlayer player, SbPlayerDecoderState state, int ticket)
+ : player(player), decoder_state(state), ticket(ticket) {}
+
+ bool HasStateUpdate() const {
+ return player_state.has_engaged() || decoder_state.has_engaged();
+ }
+
+ SbPlayer player = kSbPlayerInvalid;
+ optional<SbPlayerState> player_state;
+ optional<SbPlayerDecoderState> decoder_state;
+ int ticket = SB_PLAYER_INITIAL_TICKET;
+ };
+
+ static void DecoderStatusCallback(SbPlayer player,
+ void* context,
+ SbMediaType type,
+ SbPlayerDecoderState state,
+ int ticket);
+
+ static void PlayerStatusCallback(SbPlayer player,
+ void* context,
+ SbPlayerState state,
+ int ticket);
+
+ static void ErrorCallback(SbPlayer player,
+ void* context,
+ SbPlayerError error,
+ const char* message);
+
+ void InitializePlayer();
+
+ void PrepareForSeek();
+
+ void Seek(const SbTime time);
+
+ // When the |output_mode| is decoding to texture, then this method is used to
+ // advance the decoded frames.
+ void GetDecodeTargetWhenSupported();
+
+ // Callback methods from the underlying player.
+ void OnDecoderState(SbPlayer player,
+ SbMediaType media_type,
+ SbPlayerDecoderState state,
+ int ticket);
+ void OnPlayerState(SbPlayer player, SbPlayerState state, int ticket);
+ void OnError(SbPlayer player, SbPlayerError error, const char* message);
+
+ // Waits for |kSbPlayerDecoderStateNeedsData| to be sent.
+ void WaitForDecoderStateNeedsData(
+ const SbTime timeout = kDefaultWaitForDecoderStateNeedsDataTimeout);
+
+ // Waits for desired player state update to be sent.
+ void WaitForPlayerState(
+ const SbPlayerState desired_state,
+ const SbTime timeout = kDefaultWaitForPlayerStateTimeout);
+
+ // Player and Decoder methods for driving input and output.
+ void WriteSingleInput(size_t index);
+ void WriteEndOfStream();
+ void WriteMultipleInputs(size_t start_index, size_t num_inputs_to_write);
+ void DrainOutputs();
+
+ int GetNumBuffers() const;
+
+ SbMediaType GetTestMediaType() const { return test_media_type_; }
+
+ // Determine if the the current event is valid based on previously received
+ // player state updates, or other inputs to the player.
+ void AssertPlayerStateIsValid(SbPlayerState state) const;
+
+ bool HasReceivedPlayerState(SbPlayerState state) const {
+ return player_state_set_.find(state) != player_state_set_.end();
+ }
+
+ // Checks if there are pending callback events and, if so, logs the received
+ // state update in the appropriate tracking container:
+ // * |decoder_state_queue_| for SbPlayerDecoderState updates.
+ // * |player_state_set_| for SbPlayerState updates.
+ // Executes a blocking wait for any new CallbackEvents to be enqueued.
+ void TryProcessCallbackEvents(SbTime timeout);
+
+ // Queue of events from the underlying player.
+ Queue<CallbackEvent> callback_event_queue_;
+
+ // Ordered queue of pending decoder state updates.
+ std::deque<SbPlayerDecoderState> decoder_state_queue_;
+
+ // Set of received player state updates from the underlying player. This is
+ // used to check that the state updates occur in a valid order during normal
+ // playback.
+ std::set<SbPlayerState> player_state_set_;
+
+ // Test instance specific configuration.
+ std::string dmp_filename_;
+ SbMediaType test_media_type_;
+ SbPlayerOutputMode output_mode_;
+ scoped_ptr<VideoDmpReader> dmp_reader_;
+
+ FakeGraphicsContextProvider fake_graphics_context_provider_;
+ SbPlayer player_ = kSbPlayerInvalid;
+
+ bool destroy_player_called_ = false;
+ bool end_of_stream_written_ = false;
+ atomic_bool error_occurred_;
+ int ticket_ = SB_PLAYER_INITIAL_TICKET;
+};
+
+SbPlayerWriteSampleTest::SbPlayerWriteSampleTest() {
+ const char* audio_filename = std::get<0>(GetParam());
+ const char* video_filename = std::get<1>(GetParam());
+ output_mode_ = std::get<2>(GetParam());
+
+ SB_DCHECK(output_mode_ != kSbPlayerOutputModeInvalid);
+
+ if (audio_filename != NULL) {
+ SB_DCHECK(video_filename == NULL);
+
+ dmp_filename_ = audio_filename;
+ test_media_type_ = kSbMediaTypeAudio;
+ } else {
+ SB_DCHECK(video_filename != NULL);
+
+ dmp_filename_ = video_filename;
+ test_media_type_ = kSbMediaTypeVideo;
+ }
+ dmp_reader_.reset(
+ new VideoDmpReader(ResolveTestFileName(dmp_filename_.c_str()).c_str()));
+
+ SB_LOG(INFO) << FormatString(
+ "Initialize SbPlayerWriteSampleTest with dmp file '%s' and with output "
+ "mode '%s'.",
+ dmp_filename_.c_str(),
+ output_mode_ == kSbPlayerOutputModeDecodeToTexture ? "Decode To Texture"
+ : "Punchout");
+}
+
+void SbPlayerWriteSampleTest::SetUp() {
+ SbMediaVideoCodec video_codec = dmp_reader_->video_codec();
+ SbMediaAudioCodec audio_codec = dmp_reader_->audio_codec();
+ const SbMediaAudioSampleInfo* audio_sample_info = NULL;
+
+ if (test_media_type_ == kSbMediaTypeAudio) {
+ SB_DCHECK(audio_codec != kSbMediaAudioCodecNone);
+
+ audio_sample_info = &dmp_reader_->audio_sample_info();
+ video_codec = kSbMediaVideoCodecNone;
+ } else {
+ SB_DCHECK(video_codec != kSbMediaVideoCodecNone);
+
+ audio_codec = kSbMediaAudioCodecNone;
+ }
+
+ player_ = CallSbPlayerCreate(
+ fake_graphics_context_provider_.window(), video_codec, audio_codec,
+ kSbDrmSystemInvalid, audio_sample_info, "", DummyDeallocateSampleFunc,
+ DecoderStatusCallback, PlayerStatusCallback, ErrorCallback, this,
+ output_mode_, fake_graphics_context_provider_.decoder_target_provider());
+
+ ASSERT_TRUE(SbPlayerIsValid(player_));
+
+ InitializePlayer();
+}
+
+void SbPlayerWriteSampleTest::TearDown() {
+ SB_DCHECK(SbPlayerIsValid(player_));
+
+ ASSERT_FALSE(destroy_player_called_);
+ destroy_player_called_ = true;
+ SbPlayerDestroy(player_);
+
+ // We expect the event to be sent already.
+ ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateDestroyed, 0));
+ ASSERT_FALSE(error_occurred_.load());
+}
+
+// static
+void SbPlayerWriteSampleTest::DecoderStatusCallback(SbPlayer player,
+ void* context,
+ SbMediaType type,
+ SbPlayerDecoderState state,
+ int ticket) {
+ SbPlayerWriteSampleTest* sb_player_write_sample_test =
+ static_cast<SbPlayerWriteSampleTest*>(context);
+ sb_player_write_sample_test->OnDecoderState(player, type, state, ticket);
+}
+
+// static
+void SbPlayerWriteSampleTest::PlayerStatusCallback(SbPlayer player,
+ void* context,
+ SbPlayerState state,
+ int ticket) {
+ SbPlayerWriteSampleTest* sb_player_write_sample_test =
+ static_cast<SbPlayerWriteSampleTest*>(context);
+ sb_player_write_sample_test->OnPlayerState(player, state, ticket);
+}
+
+// static
+void SbPlayerWriteSampleTest::ErrorCallback(SbPlayer player,
+ void* context,
+ SbPlayerError error,
+ const char* message) {
+ SbPlayerWriteSampleTest* sb_player_write_sample_test =
+ static_cast<SbPlayerWriteSampleTest*>(context);
+ sb_player_write_sample_test->OnError(player, error, message);
+}
+
+void SbPlayerWriteSampleTest::InitializePlayer() {
+ ASSERT_FALSE(destroy_player_called_);
+ ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateInitialized));
+ Seek(0);
+ SbPlayerSetPlaybackRate(player_, 1.0);
+ SbPlayerSetVolume(player_, 1.0);
+}
+
+void SbPlayerWriteSampleTest::PrepareForSeek() {
+ ASSERT_FALSE(destroy_player_called_);
+ ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+ ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+ player_state_set_.clear();
+ player_state_set_.insert(kSbPlayerStateInitialized);
+ ticket_++;
+}
+
+void SbPlayerWriteSampleTest::Seek(const SbTime time) {
+ PrepareForSeek();
+ SbPlayerSeek2(player_, time, ticket_);
+ ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+ ASSERT_TRUE(decoder_state_queue_.empty());
+}
+
+void SbPlayerWriteSampleTest::GetDecodeTargetWhenSupported() {
+ if (destroy_player_called_) {
+ return;
+ }
+#if SB_HAS(GLES2)
+ fake_graphics_context_provider_.RunOnGlesContextThread([&]() {
+ ASSERT_TRUE(SbPlayerIsValid(player_));
+ if (output_mode_ != kSbPlayerOutputModeDecodeToTexture) {
+#if SB_API_VERSION >= 12
+ ASSERT_EQ(SbPlayerGetCurrentFrame(player_), kSbDecodeTargetInvalid);
+#endif // SB_API_VERSION >= 12
+ return;
+ }
+ ASSERT_EQ(output_mode_, kSbPlayerOutputModeDecodeToTexture);
+ SbDecodeTarget frame = SbPlayerGetCurrentFrame(player_);
+ if (SbDecodeTargetIsValid(frame)) {
+ SbDecodeTargetRelease(frame);
+ }
+ });
+#endif // SB_HAS(GLES2)
+}
+
+void SbPlayerWriteSampleTest::OnDecoderState(SbPlayer player,
+ SbMediaType type,
+ SbPlayerDecoderState state,
+ int ticket) {
+ switch (state) {
+ case kSbPlayerDecoderStateNeedsData:
+ callback_event_queue_.Put(CallbackEvent(player, state, ticket));
+ break;
+#if SB_API_VERSION < 12
+ // Note: we do not add these events to the queue since these states are not
+ // used in Cobalt and are being deprecated.
+ case kSbPlayerDecoderStateBufferFull:
+ case kSbPlayerDecoderStateDestroyed:
+ break;
+#endif // SB_API_VERSION < 12
+ }
+}
+
+void SbPlayerWriteSampleTest::OnPlayerState(SbPlayer player,
+ SbPlayerState state,
+ int ticket) {
+ callback_event_queue_.Put(CallbackEvent(player, state, ticket));
+}
+
+void SbPlayerWriteSampleTest::OnError(SbPlayer player,
+ SbPlayerError error,
+ const char* message) {
+ SB_LOG(ERROR) << FormatString("Got SbPlayerError %d with message '%s'", error,
+ message != NULL ? message : "");
+ error_occurred_.exchange(true);
+}
+
+void SbPlayerWriteSampleTest::WaitForDecoderStateNeedsData(
+ const SbTime timeout) {
+ optional<SbPlayerDecoderState> received_state;
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ do {
+ ASSERT_FALSE(error_occurred_.load());
+ GetDecodeTargetWhenSupported();
+ ASSERT_NO_FATAL_FAILURE(TryProcessCallbackEvents(
+ std::min(timeout, kDefaultWaitForCallbackEventTimeout)));
+ if (decoder_state_queue_.empty()) {
+ continue;
+ }
+ received_state = decoder_state_queue_.front();
+ decoder_state_queue_.pop_front();
+ if (received_state.value() == kSbPlayerDecoderStateNeedsData) {
+ break;
+ }
+ } while (SbTimeGetMonotonicNow() - start < timeout);
+
+ ASSERT_TRUE(received_state.has_engaged()) << "Did not receive any states.";
+ ASSERT_EQ(kSbPlayerDecoderStateNeedsData, received_state.value())
+ << "Did not receive expected state.";
+}
+
+void SbPlayerWriteSampleTest::WaitForPlayerState(
+ const SbPlayerState desired_state,
+ const SbTime timeout) {
+ SbTimeMonotonic start = SbTimeGetMonotonicNow();
+ do {
+ ASSERT_FALSE(error_occurred_.load());
+ GetDecodeTargetWhenSupported();
+ ASSERT_NO_FATAL_FAILURE(TryProcessCallbackEvents(
+ std::min(timeout, kDefaultWaitForCallbackEventTimeout)));
+ if (HasReceivedPlayerState(desired_state)) {
+ break;
+ }
+ } while (SbTimeGetMonotonicNow() - start < timeout);
+ ASSERT_TRUE(HasReceivedPlayerState(desired_state))
+ << "Did not received expected state.";
+}
+
+void SbPlayerWriteSampleTest::WriteSingleInput(size_t index) {
+ ASSERT_FALSE(destroy_player_called_);
+ ASSERT_LT(index, GetNumBuffers());
+ SbPlayerSampleInfo sample_info =
+ dmp_reader_->GetPlayerSampleInfo(test_media_type_, index);
+ SbPlayerWriteSample2(player_, test_media_type_, &sample_info, 1);
+}
+
+void SbPlayerWriteSampleTest::WriteEndOfStream() {
+ ASSERT_FALSE(destroy_player_called_);
+ ASSERT_FALSE(end_of_stream_written_);
+ end_of_stream_written_ = true;
+ SbPlayerWriteEndOfStream(player_, test_media_type_);
+}
+
+void SbPlayerWriteSampleTest::WriteMultipleInputs(size_t start_index,
+ size_t num_inputs_to_write) {
+ SB_DCHECK(num_inputs_to_write > 0);
+ SB_DCHECK(start_index < GetNumBuffers());
+
+ ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
+ ++start_index;
+ --num_inputs_to_write;
+
+ while (num_inputs_to_write > 0 && start_index < GetNumBuffers()) {
+ ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+ ASSERT_NO_FATAL_FAILURE(WriteSingleInput(start_index));
+ ++start_index;
+ --num_inputs_to_write;
+ }
+}
+
+void SbPlayerWriteSampleTest::DrainOutputs() {
+ ASSERT_TRUE(end_of_stream_written_);
+ ASSERT_NO_FATAL_FAILURE(WaitForPlayerState(kSbPlayerStateEndOfStream));
+ // We should not get any new decoder events after end of stream.
+ ASSERT_TRUE(decoder_state_queue_.empty());
+}
+
+int SbPlayerWriteSampleTest::GetNumBuffers() const {
+ return test_media_type_ == kSbMediaTypeAudio
+ ? dmp_reader_->number_of_audio_buffers()
+ : dmp_reader_->number_of_video_buffers();
+}
+
+void SbPlayerWriteSampleTest::AssertPlayerStateIsValid(
+ SbPlayerState state) const {
+ // Note: it is possible to receive the same state that has been previously
+ // received in the case of multiple Seek() calls. Prior to any Seek commands
+ // issued in this test, we should reset the |player_state_set_| member.
+ ASSERT_FALSE(HasReceivedPlayerState(state));
+
+ switch (state) {
+ case kSbPlayerStateInitialized:
+ // No other states have been received before getting Initialized.
+ ASSERT_TRUE(player_state_set_.empty());
+ break;
+ case kSbPlayerStatePrerolling:
+ ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+ ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+ break;
+ case kSbPlayerStatePresenting:
+ ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStatePrerolling));
+ ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+ break;
+ case kSbPlayerStateEndOfStream:
+ ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStateInitialized));
+ ASSERT_TRUE(HasReceivedPlayerState(kSbPlayerStatePrerolling));
+ ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+ break;
+ case kSbPlayerStateDestroyed:
+ // Nothing stops the user of the player from destroying the player during
+ // any of the previous states.
+ ASSERT_TRUE(destroy_player_called_);
+ break;
+ }
+}
+
+void SbPlayerWriteSampleTest::TryProcessCallbackEvents(SbTime timeout) {
+ for (;;) {
+ auto event = callback_event_queue_.GetTimed(timeout);
+ if (!event.HasStateUpdate()) {
+ break;
+ }
+ if (event.ticket != ticket_) {
+ continue;
+ }
+ ASSERT_EQ(event.player, player_);
+ SB_DCHECK(event.decoder_state.has_engaged() ^
+ event.player_state.has_engaged());
+ if (event.decoder_state.has_engaged()) {
+ // Callbacks may be in-flight at the time that the player is destroyed by
+ // a call to |SbPlayerDestroy|. In this case, the callbacks are ignored.
+ // However no new callbacks are expected after receiving the player status
+ // |kSbPlayerStateDestroyed|.
+ ASSERT_FALSE(HasReceivedPlayerState(kSbPlayerStateDestroyed));
+ decoder_state_queue_.push_back(event.decoder_state.value());
+ continue;
+ }
+ ASSERT_NO_FATAL_FAILURE(
+ AssertPlayerStateIsValid(event.player_state.value()));
+ player_state_set_.insert(event.player_state.value());
+ }
+}
+
+TEST_P(SbPlayerWriteSampleTest, SeekAndDestroy) {
+ PrepareForSeek();
+ SbPlayerSeek2(player_, 1 * kSbTimeSecond, ticket_);
+}
+
+TEST_P(SbPlayerWriteSampleTest, NoInput) {
+ WriteEndOfStream();
+ DrainOutputs();
+}
+
+TEST_P(SbPlayerWriteSampleTest, SingleInput) {
+ ASSERT_NO_FATAL_FAILURE(WriteSingleInput(0));
+ ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+ WriteEndOfStream();
+ DrainOutputs();
+}
+
+TEST_P(SbPlayerWriteSampleTest, MultipleInputs) {
+ ASSERT_NO_FATAL_FAILURE(
+ WriteMultipleInputs(0, std::min<size_t>(10, GetNumBuffers())));
+ ASSERT_NO_FATAL_FAILURE(WaitForDecoderStateNeedsData());
+ WriteEndOfStream();
+ DrainOutputs();
+}
+
+INSTANTIATE_TEST_CASE_P(SbPlayerWriteSampleTests,
+ SbPlayerWriteSampleTest,
+ ValuesIn(GetSupportedSbPlayerTestConfigs()));
+
+} // namespace
+} // namespace nplb
+} // namespace starboard
diff --git a/src/starboard/nplb/system_get_path_test.cc b/src/starboard/nplb/system_get_path_test.cc
index 7231d01..55ca991 100644
--- a/src/starboard/nplb/system_get_path_test.cc
+++ b/src/starboard/nplb/system_get_path_test.cc
@@ -16,9 +16,9 @@
#include <algorithm>
+#include "starboard/common/file.h"
#include "starboard/common/string.h"
#include "starboard/configuration_constants.h"
-#include "starboard/file.h"
#include "starboard/memory.h"
#include "starboard/nplb/file_helpers.h"
#include "starboard/system.h"
diff --git a/src/starboard/raspi/shared/gyp_configuration.gypi b/src/starboard/raspi/shared/gyp_configuration.gypi
index deb191c..d2dd46b 100644
--- a/src/starboard/raspi/shared/gyp_configuration.gypi
+++ b/src/starboard/raspi/shared/gyp_configuration.gypi
@@ -76,7 +76,8 @@
# libraries.
'-L<(sysroot)/opt/vc/lib',
'-Wl,-rpath=<(sysroot)/opt/vc/lib',
-
+ # Cleanup unused sections
+ '-Wl,-gc-sections',
# We don't wrap these symbols, but this ensures that they aren't
# linked in.
'-Wl,--wrap=malloc',
@@ -107,9 +108,15 @@
],
'compiler_flags_qa_size': [
'-Os',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_qa_speed': [
'-O2',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_cc_qa': [
'-fno-rtti',
@@ -119,9 +126,15 @@
],
'compiler_flags_gold_size': [
'-Os',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_gold_speed': [
'-O2',
+ # Compile symbols in separate sections
+ '-ffunction-sections',
+ '-fdata-sections',
],
'compiler_flags_cc_gold': [
'-fno-rtti',
diff --git a/src/starboard/raspi/shared/gyp_configuration.py b/src/starboard/raspi/shared/gyp_configuration.py
index b13b62e..faacb40 100644
--- a/src/starboard/raspi/shared/gyp_configuration.py
+++ b/src/starboard/raspi/shared/gyp_configuration.py
@@ -72,18 +72,22 @@
self.host_compiler_environment = build.GetHostCompilerEnvironment(
clang_specification.GetClangSpecification(), self.build_accelerator)
- toolchain = os.path.realpath(os.path.join(
- self.raspi_home,
- 'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
+ toolchain = os.path.realpath(
+ os.path.join(
+ self.raspi_home,
+ 'tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64'))
toolchain_bin_dir = os.path.join(toolchain, 'bin')
env_variables = self.host_compiler_environment
env_variables.update({
- 'CC': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
- 'arm-linux-gnueabihf-gcc'),
- 'CXX': self.build_accelerator + ' ' + os.path.join(toolchain_bin_dir,
- 'arm-linux-gnueabihf-g++'),
- 'STRIP': os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-strip'),
+ 'CC':
+ self.build_accelerator + ' ' +
+ os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-gcc'),
+ 'CXX':
+ self.build_accelerator + ' ' +
+ os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-g++'),
+ 'STRIP':
+ os.path.join(toolchain_bin_dir, 'arm-linux-gnueabihf-strip'),
})
return env_variables
@@ -152,6 +156,9 @@
__FILTERED_TESTS = { # pylint: disable=invalid-name
'nplb': [
'SbDrmTest.AnySupportedKeySystems',
+ 'SbMediaCanPlayMimeAndKeySystem.AnySupportedKeySystems',
+ 'SbMediaCanPlayMimeAndKeySystem.KeySystemWithAttributes',
+ 'SbMediaCanPlayMimeAndKeySystem.MinimumSupport',
],
'player_filter_tests': [
# The implementations for the raspberry pi (0 and 2) are incomplete
diff --git a/src/starboard/raspi/shared/system_get_property.cc b/src/starboard/raspi/shared/system_get_property.cc
index 6fa8f5d..ac31872 100644
--- a/src/starboard/raspi/shared/system_get_property.cc
+++ b/src/starboard/raspi/shared/system_get_property.cc
@@ -154,16 +154,8 @@
return CopyStringAndTestIfSuccess(out_value, value_length, kFriendlyName);
case kSbSystemPropertyPlatformName: {
- // Example output: "Raspian Linux armv7l".
- utsname name;
-
- if (uname(&name) == -1)
- return false;
-
- std::string temp =
- starboard::FormatString("Raspian %s %s", name.sysname, name.machine);
-
- return CopyStringAndTestIfSuccess(out_value, value_length, temp.c_str());
+ return CopyStringAndTestIfSuccess(out_value, value_length,
+ "X11; Linux armv7l");
}
default:
diff --git a/src/starboard/shared/linux/system_get_used_cpu_memory.cc b/src/starboard/shared/linux/system_get_used_cpu_memory.cc
index 5205355..3a7ad2e 100644
--- a/src/starboard/shared/linux/system_get_used_cpu_memory.cc
+++ b/src/starboard/shared/linux/system_get_used_cpu_memory.cc
@@ -21,9 +21,9 @@
#include <sys/types.h>
#include <unistd.h>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
-#include "starboard/file.h"
// We find the current amount of used memory on Linux by opening
// '/proc/self/status' and scan the file for its "VmRSS" and "VmSwap" entries.
diff --git a/src/starboard/shared/pthread/mutex_acquire.cc b/src/starboard/shared/pthread/mutex_acquire.cc
index 67f26b4..6183f7d 100644
--- a/src/starboard/shared/pthread/mutex_acquire.cc
+++ b/src/starboard/shared/pthread/mutex_acquire.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
#include <pthread.h>
diff --git a/src/starboard/shared/pthread/mutex_acquire_try.cc b/src/starboard/shared/pthread/mutex_acquire_try.cc
index 4c216a5..8c5c241 100644
--- a/src/starboard/shared/pthread/mutex_acquire_try.cc
+++ b/src/starboard/shared/pthread/mutex_acquire_try.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
#include <pthread.h>
diff --git a/src/starboard/shared/pthread/mutex_create.cc b/src/starboard/shared/pthread/mutex_create.cc
index e72884d..55b6a94 100644
--- a/src/starboard/shared/pthread/mutex_create.cc
+++ b/src/starboard/shared/pthread/mutex_create.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
#include <pthread.h>
diff --git a/src/starboard/shared/pthread/mutex_destroy.cc b/src/starboard/shared/pthread/mutex_destroy.cc
index c131595..f2d0b15 100644
--- a/src/starboard/shared/pthread/mutex_destroy.cc
+++ b/src/starboard/shared/pthread/mutex_destroy.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
#include <pthread.h>
diff --git a/src/starboard/shared/pthread/mutex_release.cc b/src/starboard/shared/pthread/mutex_release.cc
index fcd7de7..d2ce183 100644
--- a/src/starboard/shared/pthread/mutex_release.cc
+++ b/src/starboard/shared/pthread/mutex_release.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
#include <pthread.h>
diff --git a/src/starboard/shared/starboard/link_receiver.cc b/src/starboard/shared/starboard/link_receiver.cc
index bb97f5d..d5ddadd 100644
--- a/src/starboard/shared/starboard/link_receiver.cc
+++ b/src/starboard/shared/starboard/link_receiver.cc
@@ -18,13 +18,13 @@
#include <unordered_map>
#include "starboard/atomic.h"
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/common/semaphore.h"
#include "starboard/common/socket.h"
#include "starboard/common/string.h"
#include "starboard/configuration_constants.h"
-#include "starboard/file.h"
#include "starboard/shared/starboard/application.h"
#include "starboard/socket_waiter.h"
#include "starboard/system.h"
diff --git a/src/starboard/shared/starboard/localized_strings.cc b/src/starboard/shared/starboard/localized_strings.cc
index 2d7fb02..838c4e4 100644
--- a/src/starboard/shared/starboard/localized_strings.cc
+++ b/src/starboard/shared/starboard/localized_strings.cc
@@ -16,8 +16,8 @@
#include <algorithm>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
-#include "starboard/file.h"
#include "starboard/system.h"
#include "starboard/types.h"
diff --git a/src/starboard/shared/starboard/media/codec_util.cc b/src/starboard/shared/starboard/media/codec_util.cc
index 64a649b..7d192fd 100644
--- a/src/starboard/shared/starboard/media/codec_util.cc
+++ b/src/starboard/shared/starboard/media/codec_util.cc
@@ -499,10 +499,10 @@
return true;
}
- // 6. Parse chroma subsampling, which we only support 01.
+ // 6. Parse chroma subsampling, which we only support 00 and 01.
// Note that this value is not returned.
int chroma;
- if (!ReadTwoDigitDecimal(codec + 14, &chroma) || chroma != 1) {
+ if (!ReadTwoDigitDecimal(codec + 14, &chroma) || (chroma != 0 && chroma != 1)) {
return false;
}
diff --git a/src/starboard/shared/starboard/media/media_util.cc b/src/starboard/shared/starboard/media/media_util.cc
index 9b721cb..3269d04 100644
--- a/src/starboard/shared/starboard/media/media_util.cc
+++ b/src/starboard/shared/starboard/media/media_util.cc
@@ -373,8 +373,11 @@
}
if (codecs.size() == 0) {
- // This is a progressive query. We only support "video/mp4" in this case.
- if (mime_type.type() == "video" && mime_type.subtype() == "mp4") {
+ // This happens when the H5 player is either querying for progressive
+ // playback support, or probing for generic mp4 support without specific
+ // codecs. We only support "audio/mp4" and "video/mp4" for these cases.
+ if ((mime_type.type() == "audio" || mime_type.type() == "video") &&
+ mime_type.subtype() == "mp4") {
return kSbMediaSupportTypeMaybe;
}
return kSbMediaSupportTypeNotSupported;
diff --git a/src/starboard/shared/starboard/player/file_cache_reader.h b/src/starboard/shared/starboard/player/file_cache_reader.h
index c9e2a08..a32b222 100644
--- a/src/starboard/shared/starboard/player/file_cache_reader.h
+++ b/src/starboard/shared/starboard/player/file_cache_reader.h
@@ -18,8 +18,8 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/common/scoped_ptr.h"
-#include "starboard/file.h"
namespace starboard {
namespace shared {
diff --git a/src/starboard/shared/starboard/player/filter/player_components.h b/src/starboard/shared/starboard/player/filter/player_components.h
index 567c4a1..e4c0eef 100644
--- a/src/starboard/shared/starboard/player/filter/player_components.h
+++ b/src/starboard/shared/starboard/player/filter/player_components.h
@@ -197,9 +197,10 @@
scoped_refptr<VideoRendererSink>* video_renderer_sink);
// Check AudioRenderer ctor for more details on the parameters.
- void GetAudioRendererParams(const CreationParameters& creation_parameters,
- int* max_cached_frames,
- int* min_frames_per_append) const;
+ virtual void GetAudioRendererParams(
+ const CreationParameters& creation_parameters,
+ int* max_cached_frames,
+ int* min_frames_per_append) const;
private:
SB_DISALLOW_COPY_AND_ASSIGN(Factory);
diff --git a/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc b/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
index 8506b7a..c8ac858 100644
--- a/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
+++ b/src/starboard/shared/starboard/player/filter/testing/file_cache_reader_test.cc
@@ -18,8 +18,8 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/configuration_constants.h"
-#include "starboard/file.h"
#include "starboard/memory.h"
#include "starboard/shared/starboard/player/filter/testing/test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
index 1450b58..49dbd09 100644
--- a/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
+++ b/src/starboard/shared/starboard/player/filter/testing/video_decoder_test_fixture.h
@@ -86,7 +86,11 @@
SbPlayerOutputMode output_mode,
bool using_stub_decoder);
- ~VideoDecoderTestFixture() { video_decoder_->Reset(); }
+ ~VideoDecoderTestFixture() {
+ if (video_decoder_) {
+ video_decoder_->Reset();
+ }
+ }
void Initialize();
diff --git a/src/starboard/shared/starboard/player/video_dmp_reader.h b/src/starboard/shared/starboard/player/video_dmp_reader.h
index 218384d..6770123 100644
--- a/src/starboard/shared/starboard/player/video_dmp_reader.h
+++ b/src/starboard/shared/starboard/player/video_dmp_reader.h
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/common/mutex.h"
#include "starboard/common/optional.h"
diff --git a/src/starboard/shared/stub/mutex_acquire.cc b/src/starboard/shared/stub/mutex_acquire.cc
index cede4d1..423202e 100644
--- a/src/starboard/shared/stub/mutex_acquire.cc
+++ b/src/starboard/shared/stub/mutex_acquire.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
SbMutexResult SbMutexAcquire(SbMutex* mutex) {
return kSbMutexDestroyed;
diff --git a/src/starboard/shared/stub/mutex_acquire_try.cc b/src/starboard/shared/stub/mutex_acquire_try.cc
index a9f1ba8..efc87b6 100644
--- a/src/starboard/shared/stub/mutex_acquire_try.cc
+++ b/src/starboard/shared/stub/mutex_acquire_try.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
SbMutexResult SbMutexAcquireTry(SbMutex* mutex) {
return kSbMutexDestroyed;
diff --git a/src/starboard/shared/stub/mutex_create.cc b/src/starboard/shared/stub/mutex_create.cc
index 11586de..4439ee0 100644
--- a/src/starboard/shared/stub/mutex_create.cc
+++ b/src/starboard/shared/stub/mutex_create.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
bool SbMutexCreate(SbMutex* mutex) {
return false;
diff --git a/src/starboard/shared/stub/mutex_destroy.cc b/src/starboard/shared/stub/mutex_destroy.cc
index 7922ddd..b88ae22 100644
--- a/src/starboard/shared/stub/mutex_destroy.cc
+++ b/src/starboard/shared/stub/mutex_destroy.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
bool SbMutexDestroy(SbMutex* mutex) {
return false;
diff --git a/src/starboard/shared/stub/mutex_release.cc b/src/starboard/shared/stub/mutex_release.cc
index 88e6071..207b642 100644
--- a/src/starboard/shared/stub/mutex_release.cc
+++ b/src/starboard/shared/stub/mutex_release.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "starboard/common/mutex.h"
+#include "starboard/mutex.h"
bool SbMutexRelease(SbMutex* mutex) {
return false;
diff --git a/src/starboard/shared/widevine/drm_system_widevine.cc b/src/starboard/shared/widevine/drm_system_widevine.cc
index c3910d3..4a8eaf1 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.cc
+++ b/src/starboard/shared/widevine/drm_system_widevine.cc
@@ -26,6 +26,7 @@
#include "starboard/memory.h"
#include "starboard/once.h"
#include "starboard/shared/starboard/application.h"
+#include "starboard/shared/starboard/media/mime_type.h"
#include "starboard/shared/widevine/widevine_storage.h"
#include "starboard/shared/widevine/widevine_timer.h"
#include "starboard/time.h"
@@ -40,7 +41,7 @@
namespace {
const int kInitializationVectorSize = 16;
-const char* kWidevineKeySystem[] = {"com.widevine", "com.widevine.alpha"};
+const char* kWidevineKeySystems[] = {"com.widevine", "com.widevine.alpha"};
const char kWidevineStorageFileName[] = "wvcdm.dat";
// Key usage may be blocked due to incomplete HDCP authentication which could
@@ -264,8 +265,28 @@
// static
bool DrmSystemWidevine::IsKeySystemSupported(const char* key_system) {
- for (auto wv_key_system : kWidevineKeySystem) {
- if (SbStringCompareAll(key_system, wv_key_system) == 0) {
+ SB_DCHECK(key_system);
+
+ // It is possible that the |key_system| comes with extra attributes, like
+ // `com.widevine.alpha; encryptionscheme="cenc"`. We prepend "key_system/"
+ // to it, so it can be parsed by MimeType.
+ starboard::media::MimeType mime_type(std::string("key_system/") + key_system);
+
+ if (!mime_type.is_valid()) {
+ return false;
+ }
+ SB_DCHECK(mime_type.type() == "key_system");
+
+ for (auto wv_key_system : kWidevineKeySystems) {
+ if (mime_type.subtype() == wv_key_system) {
+ for (int i = 0; i < mime_type.GetParamCount(); ++i) {
+ if (mime_type.GetParamName(i) == "encryptionscheme") {
+ auto value = mime_type.GetParamStringValue(i);
+ if (value != "cenc" && value != "cbcs" && value != "cbcs-1-9") {
+ return false;
+ }
+ }
+ }
return true;
}
}
diff --git a/src/starboard/shared/widevine/widevine_storage.cc b/src/starboard/shared/widevine/widevine_storage.cc
index 91819e7..d6c8a3d 100644
--- a/src/starboard/shared/widevine/widevine_storage.cc
+++ b/src/starboard/shared/widevine/widevine_storage.cc
@@ -14,8 +14,8 @@
#include "starboard/shared/widevine/widevine_storage.h"
+#include "starboard/common/file.h"
#include "starboard/common/log.h"
-#include "starboard/file.h"
#include "starboard/shared/widevine/widevine_keybox_hash.h"
#include "starboard/types.h"
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 5e78b9b..030a2ed 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -41,6 +41,10 @@
#include "starboard/shared/x11/window_internal.h"
#include "starboard/time.h"
+namespace {
+const char kTouchscreenPointerSwitch[] = "touchscreen_pointer";
+}
+
namespace starboard {
namespace shared {
namespace x11 {
@@ -720,6 +724,7 @@
// evdev input will be sent to the first created window only.
dev_input_.reset(DevInput::Create(window, ConnectionNumber(display_)));
}
+ touchscreen_pointer_ = GetCommandLine()->HasSwitch(kTouchscreenPointerSwitch);
return window;
}
@@ -1273,7 +1278,8 @@
data->key = XButtonEventToSbKey(x_button_event);
data->type =
is_press_event ? kSbInputEventTypePress : kSbInputEventTypeUnpress;
- data->device_type = kSbInputDeviceTypeMouse;
+ data->device_type = touchscreen_pointer_ ? kSbInputDeviceTypeTouchScreen
+ : kSbInputDeviceTypeMouse;
if (is_wheel_event) {
data->pressure = NAN;
data->size = {NAN, NAN};
@@ -1299,11 +1305,17 @@
data->size = {NAN, NAN};
data->tilt = {NAN, NAN};
data->type = kSbInputEventTypeMove;
- data->device_type = kSbInputDeviceTypeMouse;
+ data->device_type = touchscreen_pointer_ ? kSbInputDeviceTypeTouchScreen
+ : kSbInputDeviceTypeMouse;
data->device_id = kMouseDeviceId;
data->key_modifiers = XEventStateToSbKeyModifiers(x_motion_event->state);
data->position.x = x_motion_event->x;
data->position.y = x_motion_event->y;
+ if (touchscreen_pointer_ && !data->key_modifiers) {
+ // For touch screens, only report motion events when a button is
+ // pressed.
+ return NULL;
+ }
return new Event(kSbEventTypeInput, data.release(),
&DeleteDestructor<SbInputData>);
}
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index 75934d3..875dd20 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -155,6 +155,9 @@
// The /dev/input input handler. Only set when there is an open window.
scoped_ptr<::starboard::shared::dev_input::DevInput> dev_input_;
+
+ // Indicates whether pointer input is from a touchscreen.
+ bool touchscreen_pointer_;
};
} // namespace x11
diff --git a/src/starboard/stub/configuration.gni b/src/starboard/stub/configuration.gni
index 9babbcb..774ab8a 100644
--- a/src/starboard/stub/configuration.gni
+++ b/src/starboard/stub/configuration.gni
@@ -18,9 +18,3 @@
# Use media source extension implementation that is conformed to the
# Candidate Recommendation of July 5th 2016.
cobalt_use_media_source_2016 = true
-
-declare_args() {
- # Set to true to enable distributed compilation using Goma. By default we
- # use Goma for stub and linux.
- use_goma = true
-}
diff --git a/src/starboard/tools/command_line.py b/src/starboard/tools/command_line.py
index cecccb5..ddb4b32 100644
--- a/src/starboard/tools/command_line.py
+++ b/src/starboard/tools/command_line.py
@@ -23,8 +23,18 @@
import starboard.tools.platform
+def AddLoggingArguments(arg_parser):
+ arg_parser.add_argument(
+ '--log_level',
+ choices=['debug', 'warning', 'error', 'critical'],
+ default='info',
+ help='The minimum level a log statement must be to be output. This value '
+ "is used to initialize the 'logging' module log level.")
+
+
def AddPlatformConfigArguments(arg_parser):
"""Adds the platform configuration arguments required for building."""
+ AddLoggingArguments(arg_parser)
default_config, default_platform = build.GetDefaultConfigAndPlatform()
arg_parser.add_argument(
'-p',
diff --git a/src/starboard/tools/goma.py b/src/starboard/tools/goma.py
deleted file mode 100644
index 183ff3f..0000000
--- a/src/starboard/tools/goma.py
+++ /dev/null
@@ -1,130 +0,0 @@
-#
-# Copyright 2019 The Cobalt Authors. All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-"""Goma specific definitions and helper functions.
-
-Goma is used for faster builds. Install goma and add directory to PATH.
-Provides common functions to setup goma across platform builds.
-It checks that goma is enabled and installed, and starts the
-goma proxy.
-
-Override settings using environment variables.
-USE_GOMA To enable/disable goma. Default is None.
-"""
-
-import logging
-import os
-import subprocess
-import sys
-import util
-
-from starboard.tools import build_accelerator
-
-
-class Goma(build_accelerator.BuildAccelerator):
- """Goma is a distributed build accelerator."""
-
- def GetName(self):
- return 'gomacc'
-
- def Use(self):
- return FindAndStartGoma()
-
-
-def _GomaEnabledFromEnv():
- """Enable goma if USE_GOMA is defined.
-
- Returns:
- True to enable goma, defaults to False.
- """
- if 'USE_GOMA' in os.environ:
- return os.environ['USE_GOMA'] == '1'
- return False
-
-
-def _GetGomaFromPath():
- """Returns goma directory from PATH, otherwise is None."""
- gomacc_path = util.Which('gomacc')
- if gomacc_path is not None:
- return os.path.dirname(gomacc_path)
- return None
-
-
-def _GomaInstalled(goma_dir):
- """Returns True if goma is installed, otherwise is False."""
- if goma_dir and os.path.isdir(goma_dir):
- if os.path.isfile(GomaControlFile(goma_dir)):
- logging.error('Using Goma installed at: %s', goma_dir)
- return True
- logging.error('Failed to find goma dir. Check PATH location: %s', goma_dir)
- return False
-
-
-def GomaControlFile(goma_dir):
- """Returns path to goma control script."""
- return os.path.join(goma_dir, 'goma_ctl.py')
-
-
-def _GomaEnsureStart(goma_dir):
- """Starts the goma proxy.
-
- Checks the proxy status and tries to start if not running.
-
- Args:
- goma_dir: goma install directory
-
- Returns:
- True if goma started, otherwise False.
- """
- if not _GomaInstalled(goma_dir):
- return False
-
- goma_ctl = GomaControlFile(goma_dir)
- command = [goma_ctl, 'ensure_start']
- logging.error('starting goma proxy...')
-
- try:
- subprocess.check_call(command)
- return True
- except subprocess.CalledProcessError as e:
- logging.error('Goma proxy failed to start.\nCommand: %s\n%s',
- ' '.join(e.cmd), e.output)
- return False
-
-
-def FindAndStartGoma(enable_in_path=True, exit_on_failed_start=False):
- """Uses goma if installed and proxy is running.
-
- Args:
- enable_in_path: If True, enable goma if found in PATH. Otherwise,
- it enables it when USE_GOMA=1 is set.
- exit_on_failed_start: Boolean to exit if goma is enabled, but wasn't
- started.
- Returns:
- True if goma is enabled and running, otherwise False.
- """
- if enable_in_path or _GomaEnabledFromEnv():
- goma_dir = _GetGomaFromPath()
- if _GomaEnsureStart(goma_dir):
- return True
- else:
- logging.critical('goma was enabled, but failed to start.')
- if exit_on_failed_start:
- sys.exit(1)
- return False
- else:
- logging.info('Goma is disabled. To enable, check for gomacc in PATH '
- 'and/or set USE_GOMA=1.')
- return False
diff --git a/src/starboard/tools/log_level.py b/src/starboard/tools/log_level.py
new file mode 100644
index 0000000..6bb05e0
--- /dev/null
+++ b/src/starboard/tools/log_level.py
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Provides a consistent mechanism for the initialize of logging."""
+
+import logging
+import _env # pylint: disable=unused-import
+
+_NAME_TO_LEVEL = {
+ 'info': logging.INFO,
+ 'debug': logging.DEBUG,
+ 'warning': logging.WARNING,
+ 'error': logging.ERROR,
+ 'critical': logging.CRITICAL,
+}
+
+
+def InitializeLogging(args):
+ """Parses the provided argparse.Namespace to determine a logging level."""
+ log_level = logging.INFO
+
+ if args:
+ # Allow a 'verbose' flag to force |logging.DEBUG|.
+ if 'verbose' in args and args.verbose:
+ log_level = logging.DEBUG
+ elif 'log_level' in args:
+ log_level = _NAME_TO_LEVEL[args.log_level]
+
+ InitializeLoggingWithLevel(log_level)
+
+
+def InitializeLoggingWithLevel(log_level):
+ logging.basicConfig(
+ level=log_level,
+ format=('[%(process)d:%(asctime)s.%(msecs)03d...:'
+ '%(levelname)s:%(filename)s(%(lineno)s)] %(message)s'),
+ datefmt='%m-%d %H:%M')
diff --git a/src/starboard/tools/log_level_test.py b/src/starboard/tools/log_level_test.py
new file mode 100755
index 0000000..8167c14
--- /dev/null
+++ b/src/starboard/tools/log_level_test.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# Copyright 2020 The Cobalt Authors. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Tests the log_level module."""
+
+import argparse
+import logging
+import mock
+import unittest
+import _env # pylint: disable=unused-import
+from starboard.tools import log_level
+
+_INITIALIZE_LOGGING_MOCK = 'starboard.tools.log_level.InitializeLoggingWithLevel'
+
+
+class LogLevelTest(unittest.TestCase):
+
+ @mock.patch(_INITIALIZE_LOGGING_MOCK)
+ def testRainyDayNoArgs(self, initialize_logging_mock):
+ log_level.InitializeLogging(None)
+ initialize_logging_mock.assert_called_with(logging.INFO)
+
+ @mock.patch(_INITIALIZE_LOGGING_MOCK)
+ def testRainyDayEmptyArgs(self, initialize_logging_mock):
+ log_level.InitializeLogging(argparse.Namespace())
+ initialize_logging_mock.assert_called_with(logging.INFO)
+
+ @mock.patch(_INITIALIZE_LOGGING_MOCK)
+ def testSunnyDayCorrectLevels(self, initialize_logging_mock):
+ for name, level in log_level._NAME_TO_LEVEL.items():
+ args = argparse.Namespace()
+ args.log_level = name
+ log_level.InitializeLogging(args)
+ initialize_logging_mock.assert_called_with(level)
+
+ @mock.patch(_INITIALIZE_LOGGING_MOCK)
+ def testSunnyDayVerboseOverride(self, initialize_logging_mock):
+ args = argparse.Namespace()
+ args.verbose = True
+ log_level.InitializeLogging(args)
+ initialize_logging_mock.assert_called_with(logging.DEBUG)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/starboard/tools/toolchain/evergreen_linker.py b/src/starboard/tools/toolchain/evergreen_linker.py
index a7bf4d7..04fdc19 100644
--- a/src/starboard/tools/toolchain/evergreen_linker.py
+++ b/src/starboard/tools/toolchain/evergreen_linker.py
@@ -28,6 +28,7 @@
return shell.And('{0} '
'--build-id '
+ '-gc-sections '
'-X '
'-v '
'--eh-frame-hdr '
diff --git a/src/starboard/ui_navigation.h b/src/starboard/ui_navigation.h
index adb1193..6ff4137 100644
--- a/src/starboard/ui_navigation.h
+++ b/src/starboard/ui_navigation.h
@@ -143,6 +143,11 @@
// kSbUiNavItemTypeFocus. Any previously focused navigation item should
// receive the blur event. If the item is not transitively a content of the
// root item, then this does nothing.
+#if SB_API_VERSION >= SB_UI_NAVIGATION2_VERSION
+ //
+ // Specifying kSbUiNavItemInvalid should remove focus from the UI navigation
+ // system.
+#endif
void (*set_focus)(SbUiNavItem item);
// This is used to enable or disable user interaction with the specified
@@ -193,6 +198,11 @@
// the specified window. Navigation items are only interactable if they are
// transitively attached to a window.
//
+#if SB_API_VERSION >= SB_UI_NAVIGATION2_VERSION
+ // The native UI engine should never change this navigation item's content
+ // offset. It is assumed to be used as a proxy for the system window.
+ //
+#endif
// A navigation item may only have a SbUiNavItem or SbWindow as its direct
// container. The navigation item hierarchy is established using
// set_item_container_item() with the root container attached to a SbWindow
@@ -242,6 +252,13 @@
// Retrieve the current content offset for the navigation item. If |item| is
// not a container, then the content offset is (0,0).
+#if SB_API_VERSION >= SB_UI_NAVIGATION2_VERSION
+ //
+ // The native UI engine should not change the content offset of a container
+ // unless one of its contents (possibly recursively) is focused. This is to
+ // allow seemlessly disabling then re-enabling focus items without having
+ // their containers change offsets.
+#endif
void (*get_item_content_offset)(SbUiNavItem item,
float* out_content_offset_x,
float* out_content_offset_y);
diff --git a/src/testing/gtest/src/gtest.cc b/src/testing/gtest/src/gtest.cc
index 7f0193b..50717b7 100644
--- a/src/testing/gtest/src/gtest.cc
+++ b/src/testing/gtest/src/gtest.cc
@@ -37,6 +37,7 @@
#if GTEST_OS_STARBOARD
#include "starboard/client_porting/eztime/eztime.h"
+#include "starboard/common/file.h"
#include "starboard/system.h"
#else
#include <ctype.h>
diff --git a/src/third_party/aom_includes/METATDATA b/src/third_party/aom_includes/METADATA
similarity index 100%
rename from src/third_party/aom_includes/METATDATA
rename to src/third_party/aom_includes/METADATA
diff --git a/src/third_party/crashpad/client/crash_report_database.h b/src/third_party/crashpad/client/crash_report_database.h
index cc4b8df..0739a9c 100644
--- a/src/third_party/crashpad/client/crash_report_database.h
+++ b/src/third_party/crashpad/client/crash_report_database.h
@@ -404,6 +404,15 @@
//! \return The number of reports cleaned.
virtual int CleanDatabase(time_t lockfile_ttl) { return 0; }
+ //! \brief Deletes the oldest crash reports and their associated metadata,
+ //! leaving only num_reports_to_keep left in the database.
+ //!
+ //! \param[in] num_reports_to_keep To number of most recent reports to leave
+ //! in the database.
+ //!
+ //! \return The operation status code.
+ virtual OperationStatus RemoveOldReports(int num_reports_to_keep) = 0;
+
protected:
CrashReportDatabase() {}
diff --git a/src/third_party/crashpad/client/crash_report_database_generic.cc b/src/third_party/crashpad/client/crash_report_database_generic.cc
index e90bfa6..e807f17 100644
--- a/src/third_party/crashpad/client/crash_report_database_generic.cc
+++ b/src/third_party/crashpad/client/crash_report_database_generic.cc
@@ -18,7 +18,9 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <algorithm>
#include <utility>
+#include <vector>
#include "base/logging.h"
#include "build/build_config.h"
@@ -191,6 +193,11 @@
}
}
+bool WasCreatedSooner(CrashReportDatabase::Report a,
+ CrashReportDatabase::Report b) {
+ return a.creation_time < b.creation_time;
+}
+
} // namespace
class CrashReportDatabaseGeneric : public CrashReportDatabase {
@@ -218,6 +225,7 @@
OperationStatus DeleteReport(const UUID& uuid) override;
OperationStatus RequestUpload(const UUID& uuid) override;
int CleanDatabase(time_t lockfile_ttl) override;
+ OperationStatus RemoveOldReports(int num_reports_to_keep) override;
// Build a filepath for the directory for the report to hold attachments.
base::FilePath AttachmentsPath(const UUID& uuid);
@@ -640,6 +648,32 @@
return removed;
}
+OperationStatus CrashReportDatabaseGeneric::RemoveOldReports(
+ int num_reports_to_keep) {
+ std::vector<CrashReportDatabase::Report> pending_reports;
+ std::vector<CrashReportDatabase::Report> completed_reports;
+ std::vector<CrashReportDatabase::Report> all_reports;
+
+ GetPendingReports(&pending_reports);
+ GetCompletedReports(&completed_reports);
+
+ all_reports.insert(
+ all_reports.end(), pending_reports.begin(), pending_reports.end());
+ all_reports.insert(
+ all_reports.end(), completed_reports.begin(), completed_reports.end());
+ std::sort(all_reports.begin(), all_reports.end(), WasCreatedSooner);
+
+ while (all_reports.size() > num_reports_to_keep) {
+ OperationStatus os = DeleteReport((*all_reports.begin()).uuid);
+ if (os != kNoError) {
+ return os;
+ }
+ all_reports.erase(all_reports.begin());
+ }
+
+ return kNoError;
+}
+
OperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt(
UploadReport* report,
bool successful,
diff --git a/src/third_party/crashpad/client/crashpad_client.h b/src/third_party/crashpad/client/crashpad_client.h
index e381b41..147b974 100644
--- a/src/third_party/crashpad/client/crashpad_client.h
+++ b/src/third_party/crashpad/client/crashpad_client.h
@@ -388,6 +388,15 @@
//!
//! \return `true` on success, `false` on failure with a message logged.
static bool SendEvergreenInfoToHandler(EvergreenInfo evergreen_info);
+
+ //! \brief Sends mapping info to the handler
+ //!
+ //! A handler must have already been installed before calling this method.
+ //! \param[in] annotations A EvergreenAnnotations struct, whose information
+ //! was created on Evergreen startup.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ static bool SendAnnotationsToHandler(EvergreenAnnotations annotations);
#endif
//! \brief Requests that the handler capture a dump even though there hasn't
diff --git a/src/third_party/crashpad/client/crashpad_client_linux.cc b/src/third_party/crashpad/client/crashpad_client_linux.cc
index 02a4e57..2435153 100644
--- a/src/third_party/crashpad/client/crashpad_client_linux.cc
+++ b/src/third_party/crashpad/client/crashpad_client_linux.cc
@@ -141,6 +141,11 @@
evergreen_info_ = evergreen_info;
return SendEvergreenInfoImpl();
}
+
+ bool SendAnnotations(EvergreenAnnotations annotations) {
+ annotations_ = annotations;
+ return SendAnnotationsImpl();
+ }
#endif
// The base implementation for all signal handlers, suitable for calling
@@ -181,6 +186,7 @@
#if defined(STARBOARD)
const EvergreenInfo& GetEvergreenInfo() { return evergreen_info_; }
+ const EvergreenAnnotations& GetAnnotations() { return annotations_; }
#endif
const ExceptionInformation& GetExceptionInfo() {
@@ -189,6 +195,7 @@
#if defined(STARBOARD)
virtual bool SendEvergreenInfoImpl() = 0;
+ virtual bool SendAnnotationsImpl() = 0;
#endif
virtual void HandleCrashImpl() = 0;
@@ -211,6 +218,7 @@
#if defined(STARBOARD)
EvergreenInfo evergreen_info_;
+ EvergreenAnnotations annotations_;
#endif
static SignalHandler* handler_;
@@ -250,6 +258,7 @@
#if defined(STARBOARD)
bool SendEvergreenInfoImpl() override { return false; }
+ bool SendAnnotationsImpl() override { return false; }
#endif
void HandleCrashImpl() override {
@@ -353,6 +362,14 @@
client.SendEvergreenInfo(info);
return true;
}
+
+ bool SendAnnotationsImpl() override {
+ ExceptionHandlerClient client(sock_to_handler_.get(), true);
+ ExceptionHandlerProtocol::ClientInformation info = {};
+ info.annotations_address = FromPointerCast<VMAddress>(&GetAnnotations());
+ client.SendAnnotations(info);
+ return true;
+ }
#endif
void HandleCrashImpl() override {
@@ -576,6 +593,16 @@
return SignalHandler::Get()->SendEvergreenInfo(evergreen_info);
}
+
+bool CrashpadClient::SendAnnotationsToHandler(
+ EvergreenAnnotations annotations) {
+ if (!SignalHandler::Get()) {
+ DLOG(ERROR) << "Crashpad isn't enabled";
+ return false;
+ }
+
+ return SignalHandler::Get()->SendAnnotations(annotations);
+}
#endif
// static
diff --git a/src/third_party/crashpad/handler/crash_report_upload_thread.cc b/src/third_party/crashpad/handler/crash_report_upload_thread.cc
index ac0f7c1..db960b1 100644
--- a/src/third_party/crashpad/handler/crash_report_upload_thread.cc
+++ b/src/third_party/crashpad/handler/crash_report_upload_thread.cc
@@ -242,7 +242,7 @@
break;
}
#if defined(STARBOARD)
- database_->DeleteReport(report.uuid);
+ database_->RemoveOldReports(/*num_reports_to_save=*/2);
#endif
}
diff --git a/src/third_party/crashpad/handler/linux/capture_snapshot.cc b/src/third_party/crashpad/handler/linux/capture_snapshot.cc
index d792945..488bbba 100644
--- a/src/third_party/crashpad/handler/linux/capture_snapshot.cc
+++ b/src/third_party/crashpad/handler/linux/capture_snapshot.cc
@@ -34,14 +34,15 @@
std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
#if defined(STARBOARD)
,
- VMAddress evergreen_information_address
+ VMAddress evergreen_information_address,
+ VMAddress annotations_address
#endif
) {
std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
new ProcessSnapshotLinux());
#if defined(STARBOARD)
- if (!process_snapshot->Initialize(connection,
- evergreen_information_address)) {
+ if (!process_snapshot->Initialize(
+ connection, evergreen_information_address, annotations_address)) {
#else
if (!process_snapshot->Initialize(connection)) {
#endif
diff --git a/src/third_party/crashpad/handler/linux/capture_snapshot.h b/src/third_party/crashpad/handler/linux/capture_snapshot.h
index d191b8b..51eca35 100644
--- a/src/third_party/crashpad/handler/linux/capture_snapshot.h
+++ b/src/third_party/crashpad/handler/linux/capture_snapshot.h
@@ -67,7 +67,8 @@
std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot
#if defined(STARBOARD)
,
- VMAddress evergreen_information_address
+ VMAddress evergreen_information_address,
+ VMAddress annotations_address
#endif
);
diff --git a/src/third_party/crashpad/handler/linux/crash_report_exception_handler.cc b/src/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
index 3a59dc7..b0f68ce 100644
--- a/src/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
+++ b/src/third_party/crashpad/handler/linux/crash_report_exception_handler.cc
@@ -86,6 +86,12 @@
evergreen_info_ = info.evergreen_information_address;
return true;
}
+
+bool CrashReportExceptionHandler::AddAnnotations(
+ const ExceptionHandlerProtocol::ClientInformation& info) {
+ annotations_address_ = info.annotations_address;
+ return true;
+}
#endif
bool CrashReportExceptionHandler::HandleException(
@@ -150,7 +156,8 @@
&sanitized_snapshot
#if defined(STARBOARD)
,
- evergreen_info_
+ evergreen_info_,
+ annotations_address_
#endif
)) {
return false;
diff --git a/src/third_party/crashpad/handler/linux/crash_report_exception_handler.h b/src/third_party/crashpad/handler/linux/crash_report_exception_handler.h
index b5ea3f7..5cca2ad 100644
--- a/src/third_party/crashpad/handler/linux/crash_report_exception_handler.h
+++ b/src/third_party/crashpad/handler/linux/crash_report_exception_handler.h
@@ -87,6 +87,8 @@
#if defined(STARBOARD)
bool AddEvergreenInfo(
const ExceptionHandlerProtocol::ClientInformation& info) override;
+ bool AddAnnotations(
+ const ExceptionHandlerProtocol::ClientInformation& info) override;
#endif
bool HandleExceptionWithBroker(
@@ -120,6 +122,7 @@
const UserStreamDataSources* user_stream_data_sources_; // weak
#if defined(STARBOARD)
VMAddress evergreen_info_;
+ VMAddress annotations_address_;
#endif
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
diff --git a/src/third_party/crashpad/handler/linux/exception_handler_server.cc b/src/third_party/crashpad/handler/linux/exception_handler_server.cc
index d49cc1c..63215d5 100644
--- a/src/third_party/crashpad/handler/linux/exception_handler_server.cc
+++ b/src/third_party/crashpad/handler/linux/exception_handler_server.cc
@@ -442,6 +442,8 @@
#if defined(STARBOARD)
case ExceptionHandlerProtocol::ClientToServerMessage::kTypeAddEvergreenInfo:
return HandleAddEvergreenInfoRequest(creds, message.client_info);
+ case ExceptionHandlerProtocol::ClientToServerMessage::kTypeAddAnnotations:
+ return HandleAddAnnotationsRequest(creds, message.client_info);
#endif
}
@@ -456,6 +458,12 @@
const ExceptionHandlerProtocol::ClientInformation& client_info) {
return delegate_->AddEvergreenInfo(client_info);
}
+
+bool ExceptionHandlerServer::HandleAddAnnotationsRequest(
+ const ucred& creds,
+ const ExceptionHandlerProtocol::ClientInformation& client_info) {
+ return delegate_->AddAnnotations(client_info);
+}
#endif
bool ExceptionHandlerServer::HandleCrashDumpRequest(
diff --git a/src/third_party/crashpad/handler/linux/exception_handler_server.h b/src/third_party/crashpad/handler/linux/exception_handler_server.h
index 0f0e73c..1e87157 100644
--- a/src/third_party/crashpad/handler/linux/exception_handler_server.h
+++ b/src/third_party/crashpad/handler/linux/exception_handler_server.h
@@ -102,6 +102,13 @@
//! \return `true` on success. `false` on failure with a message logged.
virtual bool AddEvergreenInfo(
const ExceptionHandlerProtocol::ClientInformation& info) = 0;
+
+ //! \brief Called on receipt of a request to add Evergreen Annotations.
+ //!
+ //! \param[in] info Information on the client.
+ //! \return `true` on success. `false` on failure with a message logged.
+ virtual bool AddAnnotations(
+ const ExceptionHandlerProtocol::ClientInformation& info) = 0;
#endif
//! \brief Called on the receipt of a crash dump request from a client for a
@@ -193,6 +200,9 @@
bool HandleAddEvergreenInfoRequest(
const ucred& creds,
const ExceptionHandlerProtocol::ClientInformation& client_info);
+ bool HandleAddAnnotationsRequest(
+ const ucred& creds,
+ const ExceptionHandlerProtocol::ClientInformation& client_info);
#endif
std::unordered_map<int, std::unique_ptr<Event>> clients_;
diff --git a/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc b/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
index 32ee101..a731187 100644
--- a/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
+++ b/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.cc
@@ -55,7 +55,8 @@
#if defined(STARBOARD)
bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection,
- VMAddress evergreen_information_address) {
+ VMAddress evergreen_information_address,
+ VMAddress annotations_address) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
if (gettimeofday(&snapshot_time_, nullptr) != 0) {
@@ -69,6 +70,15 @@
return false;
}
+ EvergreenAnnotations annotations;
+ if (!memory_range_.Read(
+ annotations_address, sizeof(EvergreenAnnotations), &annotations)) {
+ LOG(ERROR) << "Could not read annotations";
+ } else {
+ AddAnnotation("user_agent_string",
+ std::string(annotations.user_agent_string));
+ }
+
system_.Initialize(&process_reader_, &snapshot_time_);
InitializeThreads();
diff --git a/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.h b/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
index 6c5d7a8..c78a709 100644
--- a/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
+++ b/src/third_party/crashpad/snapshot/linux/process_snapshot_linux.h
@@ -70,11 +70,14 @@
//! \param[in] connection A connection to the process to snapshot.
//! \param[in] evergreen_information_address An address sent to the handler
//! server that points to a populated EvergreenInfo struct.
+ //! \param[in] annotations_address An address sent to the handler server that
+ //! that points to a populated EvergreenAnnotations struct.
//!
//! \return `true` if the snapshot could be created, `false` otherwise with
//! an appropriate message logged.
bool Initialize(PtraceConnection* connnection,
- VMAddress evergreen_information_address);
+ VMAddress evergreen_information_address,
+ VMAddress annotations_address);
#endif
//! \brief Finds the thread whose stack contains \a stack_address.
diff --git a/src/third_party/crashpad/util/linux/exception_handler_client.cc b/src/third_party/crashpad/util/linux/exception_handler_client.cc
index b1df219..de27275 100644
--- a/src/third_party/crashpad/util/linux/exception_handler_client.cc
+++ b/src/third_party/crashpad/util/linux/exception_handler_client.cc
@@ -89,6 +89,11 @@
const ExceptionHandlerProtocol::ClientInformation& info) {
return SendEvergreenInfoRequest(info);
}
+
+bool ExceptionHandlerClient::SendAnnotations(
+ const ExceptionHandlerProtocol::ClientInformation& info) {
+ return SendAddAnnotationsRequest(info);
+}
#endif
int ExceptionHandlerClient::RequestCrashDump(
@@ -161,6 +166,17 @@
UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message));
return true;
}
+
+bool ExceptionHandlerClient::SendAddAnnotationsRequest(
+ const ExceptionHandlerProtocol::ClientInformation& info) {
+ ExceptionHandlerProtocol::ClientToServerMessage message;
+ message.type =
+ ExceptionHandlerProtocol::ClientToServerMessage::kTypeAddAnnotations;
+ message.client_info = info;
+
+ UnixCredentialSocket::SendMsg(server_sock_, &message, sizeof(message));
+ return true;
+}
#endif
int ExceptionHandlerClient::SendCrashDumpRequest(
diff --git a/src/third_party/crashpad/util/linux/exception_handler_client.h b/src/third_party/crashpad/util/linux/exception_handler_client.h
index 3a27739..e2f6398 100644
--- a/src/third_party/crashpad/util/linux/exception_handler_client.h
+++ b/src/third_party/crashpad/util/linux/exception_handler_client.h
@@ -57,6 +57,12 @@
//! \return `true` on success or `false` on failure.
bool SendEvergreenInfo(
const ExceptionHandlerProtocol::ClientInformation& info);
+
+ //! \brief Sends EvergreenAnnotations to the ExceptionHandlerServer.
+ //!
+ //! \param[in] info Information to about this client.
+ //! \return `true` on success or `false` on failure.
+ bool SendAnnotations(const ExceptionHandlerProtocol::ClientInformation& info);
#endif
//! \brief Request a crash dump from the ExceptionHandlerServer.
@@ -83,6 +89,9 @@
#if defined(STARBOARD)
bool SendEvergreenInfoRequest(
const ExceptionHandlerProtocol::ClientInformation& info);
+
+ bool SendAddAnnotationsRequest(
+ const ExceptionHandlerProtocol::ClientInformation& info);
#endif
int SendCrashDumpRequest(
const ExceptionHandlerProtocol::ClientInformation& info,
diff --git a/src/third_party/crashpad/util/linux/exception_handler_protocol.cc b/src/third_party/crashpad/util/linux/exception_handler_protocol.cc
index 2220ecf..b139017 100644
--- a/src/third_party/crashpad/util/linux/exception_handler_protocol.cc
+++ b/src/third_party/crashpad/util/linux/exception_handler_protocol.cc
@@ -25,7 +25,8 @@
#endif // OS_LINUX
#if defined(STARBOARD)
,
- evergreen_information_address(0)
+ evergreen_information_address(0),
+ annotations_address(0)
#endif
{
}
diff --git a/src/third_party/crashpad/util/linux/exception_handler_protocol.h b/src/third_party/crashpad/util/linux/exception_handler_protocol.h
index 9edd82a..dbfaeca 100644
--- a/src/third_party/crashpad/util/linux/exception_handler_protocol.h
+++ b/src/third_party/crashpad/util/linux/exception_handler_protocol.h
@@ -56,6 +56,10 @@
//! \brief The address in the client's address space of an EvergreenInfo
//! struct, or 0 if there is no such struct.
VMAddress evergreen_information_address;
+
+ //! \brief The address in the client's address space of an
+ //! EvergreenAnnotations struct, or 0 if there is no such struct.
+ VMAddress annotations_address;
#endif
#if defined(OS_LINUX)
@@ -93,7 +97,8 @@
#if defined(STARBOARD)
//! \brief Used to store Evergreen mapping info in the handler for use at
//! time of crash.
- kTypeAddEvergreenInfo
+ kTypeAddEvergreenInfo,
+ kTypeAddAnnotations,
#endif
};
diff --git a/src/third_party/crashpad/wrapper/wrapper.cc b/src/third_party/crashpad/wrapper/wrapper.cc
index 8b8a18d..0ab09e5 100644
--- a/src/third_party/crashpad/wrapper/wrapper.cc
+++ b/src/third_party/crashpad/wrapper/wrapper.cc
@@ -206,6 +206,11 @@
return client->SendEvergreenInfoToHandler(evergreen_info);
}
+bool AddAnnotationsToCrashpad(EvergreenAnnotations annotations) {
+ ::crashpad::CrashpadClient* client = GetCrashpadClient();
+ return client->SendAnnotationsToHandler(annotations);
+}
+
} // namespace wrapper
} // namespace crashpad
} // namespace third_party
diff --git a/src/third_party/crashpad/wrapper/wrapper.h b/src/third_party/crashpad/wrapper/wrapper.h
index c2b366f..57ab399 100644
--- a/src/third_party/crashpad/wrapper/wrapper.h
+++ b/src/third_party/crashpad/wrapper/wrapper.h
@@ -25,6 +25,8 @@
bool AddEvergreenInfoToCrashpad(EvergreenInfo evergreen_info);
+bool AddAnnotationsToCrashpad(EvergreenAnnotations annotations);
+
} // namespace wrapper
} // namespace crashpad
} // namespace third_party
diff --git a/src/third_party/crashpad/wrapper/wrapper_stub.cc b/src/third_party/crashpad/wrapper/wrapper_stub.cc
index 6158445..f2aeb8a 100644
--- a/src/third_party/crashpad/wrapper/wrapper_stub.cc
+++ b/src/third_party/crashpad/wrapper/wrapper_stub.cc
@@ -24,6 +24,10 @@
return false;
}
+bool AddAnnotationsToCrashpad(EvergreenAnnotations annotations) {
+ return false;
+}
+
} // namespace wrapper
} // namespace crashpad
} // namespace third_party
\ No newline at end of file
diff --git a/src/third_party/libdav1d/METATDATA b/src/third_party/libdav1d/METADATA
similarity index 100%
rename from src/third_party/libdav1d/METATDATA
rename to src/third_party/libdav1d/METADATA
diff --git a/src/third_party/precommit-hooks/clang-format_wrapper.py b/src/third_party/precommit-hooks/clang-format_wrapper.py
new file mode 100755
index 0000000..b11325c
--- /dev/null
+++ b/src/third_party/precommit-hooks/clang-format_wrapper.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python3
+
+import os
+import platform
+import subprocess
+import sys
+
+if __name__ == '__main__':
+ clang_format_args = sys.argv[1:]
+ path_to_clang_format = [os.getcwd(), 'buildtools']
+
+ system = platform.system()
+ if system == 'Linux':
+ path_to_clang_format += ['linux64', 'clang-format']
+ elif system == 'Darwin':
+ path_to_clang_format += ['win', 'clang-format.exe']
+ elif system == 'Windows':
+ path_to_clang_format += ['mac', 'clang-format']
+ else:
+ sys.exit(1)
+
+ clang_format_executable = os.path.join(*path_to_clang_format)
+ sys.exit(subprocess.call([clang_format_executable] + clang_format_args))
diff --git a/src/third_party/precommit-hooks/gcheckstyle_wrapper.py b/src/third_party/precommit-hooks/gcheckstyle_wrapper.py
new file mode 100755
index 0000000..0275960
--- /dev/null
+++ b/src/third_party/precommit-hooks/gcheckstyle_wrapper.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python3
+
+import platform
+import subprocess
+import sys
+
+try:
+ from internal_tools_paths import checkstyle_path
+except ImportError:
+ checkstyle_path = None
+
+if __name__ == '__main__':
+ gcheckstyle_args = sys.argv[1:]
+ try:
+ sys.exit(subprocess.call([checkstyle_path] + gcheckstyle_args))
+ except OSError:
+ print('Checkstyle not found, skipping.')
+ sys.exit(0)
diff --git a/src/third_party/precommit-hooks/google-java-format_wrapper.py b/src/third_party/precommit-hooks/google-java-format_wrapper.py
new file mode 100755
index 0000000..f244fa3
--- /dev/null
+++ b/src/third_party/precommit-hooks/google-java-format_wrapper.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import platform
+import subprocess
+import sys
+
+if __name__ == '__main__':
+ if platform.system() != 'Linux':
+ sys.exit(0)
+
+ google_java_format_args = sys.argv[1:]
+ sys.exit(subprocess.call(['google-java-format'] + google_java_format_args))
diff --git a/src/third_party/web_platform_tests/fetch/api/request/request-consume-empty.html b/src/third_party/web_platform_tests/fetch/api/request/request-consume-empty.html
index c3ca838..a5bc141 100644
--- a/src/third_party/web_platform_tests/fetch/api/request/request-consume-empty.html
+++ b/src/third_party/web_platform_tests/fetch/api/request/request-consume-empty.html
@@ -69,12 +69,18 @@
}, "Consume request's body as " + bodyType);
}
+/* Cobalt doesn't support FormData request body
var formData = new FormData();
+*/
checkRequestWithNoBody("text", checkBodyText);
+/* Cobalt doesn't support Blob request body
checkRequestWithNoBody("blob", checkBodyBlob);
+*/
checkRequestWithNoBody("arrayBuffer", checkBodyArrayBuffer);
checkRequestWithNoBody("json", checkBodyJSON);
+/* Cobalt doesn't support FormData request body
checkRequestWithNoBody("formData", checkBodyFormData);
+*/
function checkRequestWithEmptyBody(bodyType, body, asText) {
promise_test(function(test) {
@@ -98,9 +104,11 @@
checkRequestWithEmptyBody("text", "", false);
checkRequestWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), true);
checkRequestWithEmptyBody("text", "", true);
+/* Cobalt doesn't support URLSearchParams and FormData request body
checkRequestWithEmptyBody("URLSearchParams", new URLSearchParams(""), true);
// FIXME: This test assumes that the empty string be returned but it is not clear whether that is right. See https://github.com/w3c/web-platform-tests/pull/3950.
checkRequestWithEmptyBody("FormData", new FormData(), true);
+*/
checkRequestWithEmptyBody("ArrayBuffer", new ArrayBuffer(), true);
</script>
</body>
diff --git a/src/third_party/web_platform_tests/fetch/api/request/request-consume.html b/src/third_party/web_platform_tests/fetch/api/request/request-consume.html
index 9ac7041..880f471 100644
--- a/src/third_party/web_platform_tests/fetch/api/request/request-consume.html
+++ b/src/third_party/web_platform_tests/fetch/api/request/request-consume.html
@@ -69,11 +69,13 @@
assert_false(request.bodyUsed, "bodyUsed is false at init");
return checkBodyText(request, expected);
}, "Consume " + bodyType + " request's body as text");
+/* Cobalt doesn't support Blob request body
promise_test(function(test) {
var request = new Request("", {"method": "POST", "body": body });
assert_false(request.bodyUsed, "bodyUsed is false at init");
return checkBodyBlob(request, expected);
}, "Consume " + bodyType + " request's body as blob");
+*/
promise_test(function(test) {
var request = new Request("", {"method": "POST", "body": body });
assert_false(request.bodyUsed, "bodyUsed is false at init");
@@ -114,6 +116,7 @@
checkRequestBody(new Float32Array(getArrayBuffer()), string, "Float32Array");
checkRequestBody(new DataView(getArrayBufferWithZeros(), 1, 8), string, "DataView");
+/* Cobalt doesn't support FormData request body
promise_test(function(test) {
var formData = new FormData();
formData.append("name", "value")
@@ -121,6 +124,7 @@
assert_false(request.bodyUsed, "bodyUsed is false at init");
return checkBodyFormData(request, formData);
}, "Consume FormData request's body as FormData");
+*/
function checkBlobResponseBody(blobBody, blobData, bodyType, checkFunction) {
promise_test(function(test) {
@@ -130,11 +134,15 @@
}, "Consume blob response's body as " + bodyType);
}
+/* Cobalt doesn't support Blob request body
checkBlobResponseBody(blob, textData, "blob", checkBodyBlob);
+*/
checkBlobResponseBody(blob, textData, "text", checkBodyText);
checkBlobResponseBody(blob, textData, "json", checkBodyJSON);
checkBlobResponseBody(blob, textData, "arrayBuffer", checkBodyArrayBuffer);
+/* Cobalt doesn't support Blob request body
checkBlobResponseBody(new Blob([""]), "", "blob (empty blob as input)", checkBodyBlob);
+*/
var goodJSONValues = ["null", "1", "true", "\"string\""];
goodJSONValues.forEach(function(value) {
diff --git a/src/third_party/zlib/contrib/optimizations/chunkcopy.h b/src/third_party/zlib/contrib/optimizations/chunkcopy.h
index 38ba0ed..c856b6a 100644
--- a/src/third_party/zlib/contrib/optimizations/chunkcopy.h
+++ b/src/third_party/zlib/contrib/optimizations/chunkcopy.h
@@ -406,6 +406,26 @@
return chunkcopy_lapped_relaxed(out, dist, len);
}
+/* TODO(cavalcanti): see crbug.com/1110083. */
+static inline unsigned char FAR* chunkcopy_safe_ugly(unsigned char FAR* out,
+ unsigned dist,
+ unsigned len,
+ unsigned char FAR* limit) {
+#if defined(__GNUC__) && !defined(__clang__)
+ /* Speed is the same as using chunkcopy_safe
+ w/ GCC on ARM (tested gcc 6.3 and 7.5) and avoids
+ undefined behavior.
+ */
+ return chunkcopy_core_safe(out, out - dist, len, limit);
+#elif defined(__clang__) && defined(ARMV8_OS_ANDROID) && !defined(__aarch64__)
+ /* Seems to perform better on 32bit (i.e. Android). */
+ return chunkcopy_core_safe(out, out - dist, len, limit);
+#else
+ /* Seems to perform better on 64bit. */
+ return chunkcopy_lapped_safe(out, dist, len, limit);
+#endif
+}
+
/*
* The chunk-copy code above deals with writing the decoded DEFLATE data to
* the output with SIMD methods to increase decode speed. Reading the input
diff --git a/src/third_party/zlib/contrib/optimizations/inffast_chunk.c b/src/third_party/zlib/contrib/optimizations/inffast_chunk.c
index 4099edf..4bacbc4 100644
--- a/src/third_party/zlib/contrib/optimizations/inffast_chunk.c
+++ b/src/third_party/zlib/contrib/optimizations/inffast_chunk.c
@@ -276,7 +276,7 @@
the main copy is near the end.
*/
out = chunkunroll_relaxed(out, &dist, &len);
- out = chunkcopy_safe(out, out - dist, len, limit);
+ out = chunkcopy_safe_ugly(out, dist, len, limit);
} else {
/* from points to window, so there is no risk of
overlapping pointers requiring memset-like behaviour
diff --git a/src/v8/METADATA b/src/v8/METADATA
new file mode 100644
index 0000000..8522c17
--- /dev/null
+++ b/src/v8/METADATA
@@ -0,0 +1,22 @@
+name: "v8"
+description:
+ "Subtree at v8."
+ "v8 is Google's JavaScript engine developed for The Chromium Project. Cobalt "
+ "uses v8 as its primary JavaScript engine. v8 works closely with Cobalt's "
+ "script interface and templated bindings code."
+third_party {
+ url {
+ type: LOCAL_SOURCE
+ value: "/v8_mirror"
+ }
+ url {
+ type: GIT
+ value: "https://chromium.googlesource.com/v8/v8"
+ }
+ version: "1e6ebba9def991e536159fa658bf5564c054733f"
+ last_upgrade_date {
+ year: 2019
+ month: 8
+ day: 26
+ }
+}
diff --git a/src/v8/include/v8config.h b/src/v8/include/v8config.h
index dd065a0..766ca5d 100644
--- a/src/v8/include/v8config.h
+++ b/src/v8/include/v8config.h
@@ -56,7 +56,6 @@
// -----------------------------------------------------------------------------
// Operating system detection
//
-// V8_OS_STARBOARD - Starboard (platform abstraction layer for the Cobalt project)
// V8_OS_ANDROID - Android
// V8_OS_BSD - BSDish (Mac OS X, Net/Free/Open/DragonFlyBSD)
// V8_OS_CYGWIN - Cygwin
@@ -71,6 +70,7 @@
// V8_OS_POSIX - POSIX compatible (mostly everything except Windows)
// V8_OS_QNX - QNX Neutrino
// V8_OS_SOLARIS - Sun Solaris and OpenSolaris
+// V8_OS_STARBOARD - Starboard (platform abstraction layer for the Cobalt project)
// V8_OS_AIX - AIX
// V8_OS_WIN - Microsoft Windows
diff --git a/src/v8/src/base/atomicops.h b/src/v8/src/base/atomicops.h
index 18c99a2..09acdc2 100644
--- a/src/v8/src/base/atomicops.h
+++ b/src/v8/src/base/atomicops.h
@@ -72,9 +72,9 @@
// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
// Atomic64 routines below, depending on your architecture.
#if defined(V8_OS_STARBOARD)
-typedef SbAtomicPtr AtomicWord;
+using AtomicWord = SbAtomicPtr;
#else
-typedef intptr_t AtomicWord;
+using AtomicWord = intptr_t;
#endif
// Atomically execute:
diff --git a/src/v8/src/base/cpu.cc b/src/v8/src/base/cpu.cc
index a13c13e..46daeed 100644
--- a/src/v8/src/base/cpu.cc
+++ b/src/v8/src/base/cpu.cc
@@ -5,15 +5,9 @@
#include "src/base/cpu.h"
#if defined(STARBOARD)
-#include "starboard/client_porting/poem/stdlib_poem.h"
#include "starboard/cpu_features.h"
#endif
-#if V8_HOST_ARCH_MIPS || V8_HOST_ARCH_MIPS64
-// Assume that if we're on a MIPS platform, we're on Linux.
-#define V8_OS_LINUX 1
-#endif
-
#if V8_LIBC_MSVCRT
#include <intrin.h> // __cpuid()
#endif
@@ -43,13 +37,11 @@
#endif
#include <ctype.h>
-#if !V8_OS_STARBOARD
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
-#endif
#include "src/base/logging.h"
#if V8_OS_WIN
diff --git a/src/v8/src/base/once.cc b/src/v8/src/base/once.cc
index 7378b54..0222687 100644
--- a/src/v8/src/base/once.cc
+++ b/src/v8/src/base/once.cc
@@ -6,6 +6,8 @@
#ifdef _WIN32
#include <windows.h>
+#elif defined(V8_OS_STARBOARD)
+#include "starboard/thread.h"
#else
#include <sched.h>
#endif
@@ -49,6 +51,8 @@
ONCE_STATE_EXECUTING_FUNCTION) {
#ifdef _WIN32
::Sleep(0);
+#elif defined(V8_OS_STARBOARD)
+ SbThreadYield();
#else
sched_yield();
#endif
diff --git a/src/v8/src/base/platform/condition-variable.cc b/src/v8/src/base/platform/condition-variable.cc
index b319793..04ea291 100644
--- a/src/v8/src/base/platform/condition-variable.cc
+++ b/src/v8/src/base/platform/condition-variable.cc
@@ -165,7 +165,6 @@
SbConditionVariableCreate(&native_handle_, nullptr);
}
-
ConditionVariable::~ConditionVariable() {
SbConditionVariableDestroy(&native_handle_);
}
@@ -178,12 +177,10 @@
SbConditionVariableBroadcast(&native_handle_);
}
-
void ConditionVariable::Wait(Mutex* mutex) {
SbConditionVariableWait(&native_handle_, &mutex->native_handle());
}
-
bool ConditionVariable::WaitFor(Mutex* mutex, const TimeDelta& rel_time) {
SbTime microseconds = static_cast<SbTime>(rel_time.InMicroseconds());
SbConditionVariableResult result = SbConditionVariableWaitTimed(
@@ -192,7 +189,7 @@
return result == kSbConditionVariableSignaled;
}
-#endif // V8_OS_POSIX
+#endif // V8_OS_STARBOARD
} // namespace base
-} // namespace v8
\ No newline at end of file
+} // namespace v8
diff --git a/src/v8/src/base/platform/mutex.cc b/src/v8/src/base/platform/mutex.cc
index 68f5132..88d9ba8 100644
--- a/src/v8/src/base/platform/mutex.cc
+++ b/src/v8/src/base/platform/mutex.cc
@@ -4,9 +4,7 @@
#include "src/base/platform/mutex.h"
-#if !V8_OS_STARBOARD
#include <errno.h>
-#endif
namespace v8 {
namespace base {
diff --git a/src/v8/src/base/platform/platform-starboard.cc b/src/v8/src/base/platform/platform-starboard.cc
index 3cd69ec..a9a0118 100644
--- a/src/v8/src/base/platform/platform-starboard.cc
+++ b/src/v8/src/base/platform/platform-starboard.cc
@@ -1,25 +1,17 @@
-// Copyright 2018 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.
+// Copyright 2020 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Platform-specific code for Starboard goes here. Starboard is the platform
+// abstraction layer for Cobalt, an HTML5 container used mainly by YouTube
+// LivingRoom products.
#include "src/base/lazy-instance.h"
#include "src/base/macros.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/time.h"
-#include "src/base/utils/random-number-generator.h"
-
#include "src/base/timezone-cache.h"
-
+#include "src/base/utils/random-number-generator.h"
#include "starboard/common/condition_variable.h"
#include "starboard/common/log.h"
#include "starboard/common/string.h"
@@ -102,9 +94,7 @@
#endif
}
-double OS::TimeCurrentMillis() {
- return Time::Now().ToJsTime();
-}
+double OS::TimeCurrentMillis() { return Time::Now().ToJsTime(); }
int OS::ActivationFrameAlignment() {
#if V8_TARGET_ARCH_ARM
@@ -148,7 +138,8 @@
break;
default:
SB_LOG(ERROR) << "The requested memory allocation access is not"
- " implemented for Starboard: " << static_cast<int>(access);
+ " implemented for Starboard: "
+ << static_cast<int>(access);
return nullptr;
}
void* result = SbMemoryMap(size, sb_flags, "v8::Base::Allocate");
@@ -247,10 +238,10 @@
break;
case OS::MemoryPermission::kReadExecute:
#if SB_CAN(MAP_EXECUTABLE_MEMORY)
- new_protection = SbMemoryMapFlags(kSbMemoryMapProtectRead |
- kSbMemoryMapProtectExec);
+ new_protection =
+ SbMemoryMapFlags(kSbMemoryMapProtectRead | kSbMemoryMapProtectExec);
#else
- CHECK(false);
+ UNREACHABLE();
#endif
break;
default:
@@ -417,9 +408,7 @@
set_name(options.name());
}
-Thread::~Thread() {
- delete data_;
-}
+Thread::~Thread() { delete data_; }
static void SetThreadName(const char* name) { SbThreadSetName(name); }
diff --git a/src/v8/src/base/platform/semaphore.cc b/src/v8/src/base/platform/semaphore.cc
index f37801a..3545bab 100644
--- a/src/v8/src/base/platform/semaphore.cc
+++ b/src/v8/src/base/platform/semaphore.cc
@@ -190,21 +190,13 @@
#elif V8_OS_STARBOARD
-Semaphore::Semaphore(int count) : native_handle_(count) {
- DCHECK_GE(count, 0);
-}
+Semaphore::Semaphore(int count) : native_handle_(count) { DCHECK_GE(count, 0); }
-Semaphore::~Semaphore() {
-}
+Semaphore::~Semaphore() {}
-void Semaphore::Signal() {
- native_handle_.Put();
-}
+void Semaphore::Signal() { native_handle_.Put(); }
-
-void Semaphore::Wait() {
- native_handle_.Take();
-}
+void Semaphore::Wait() { native_handle_.Take(); }
bool Semaphore::WaitFor(const TimeDelta& rel_time) {
SbTime microseconds = rel_time.InMicroseconds();
diff --git a/src/v8/src/base/platform/time.cc b/src/v8/src/base/platform/time.cc
index 5aa6dcd..ff1fcd8 100644
--- a/src/v8/src/base/platform/time.cc
+++ b/src/v8/src/base/platform/time.cc
@@ -458,15 +458,11 @@
#elif V8_OS_STARBOARD
-Time Time::Now() {
- return Time(SbTimeToPosix(SbTimeGetNow()));
-}
+Time Time::Now() { return Time(SbTimeToPosix(SbTimeGetNow())); }
-Time Time::NowFromSystemTime() {
- return Now();
-}
+Time Time::NowFromSystemTime() { return Now(); }
-#endif // V8_OS_WIN
+#endif // V8_OS_STARBOARD
// static
TimeTicks TimeTicks::HighResolutionNow() {
diff --git a/src/venv/.gitkeep b/src/venv/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/venv/.gitkeep