Import Cobalt 19.android.1.209838
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index bae6060..1aa0d38 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-203995
\ No newline at end of file
+209838
\ No newline at end of file
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 9a3f6f8..ce25ff2 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -35,6 +35,6 @@
// release is cut.
//.
-#define COBALT_VERSION "19.android.1"
+#define COBALT_VERSION "19.android.2"
#endif // COBALT_VERSION_H_
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
index ecc84c8..09ece25 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltActivity.java
@@ -25,7 +25,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewTreeObserver;
+import android.view.ViewParent;
import android.widget.FrameLayout;
import dev.cobalt.media.VideoSurfaceView;
import dev.cobalt.util.Log;
@@ -58,7 +58,7 @@
private VideoSurfaceView videoSurfaceView;
private KeyboardEditor keyboardEditor;
- private ViewTreeObserver.OnGlobalLayoutListener videoSurfaceLayoutListener;
+ private boolean forceCreateNewVideoSurfaceView = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -87,19 +87,11 @@
addContentView(
videoSurfaceView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
- videoSurfaceLayoutListener =
- new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- VideoSurfaceView.nativeOnGlobalLayout();
- }
- };
- ViewTreeObserver observer = getWindow().getDecorView().getViewTreeObserver();
- observer.addOnGlobalLayoutListener(videoSurfaceLayoutListener);
-
- keyboardEditor = new KeyboardEditor(this);
- addContentView(
- keyboardEditor, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ if (KeyboardInputConnection.nativeHasOnScreenKeyboard()) {
+ keyboardEditor = new KeyboardEditor(this);
+ addContentView(
+ keyboardEditor, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ }
}
/**
@@ -115,6 +107,11 @@
@Override
protected void onStart() {
+ if (forceCreateNewVideoSurfaceView) {
+ Log.w(TAG, "Force to create a new video surface.");
+ createNewSurfaceView();
+ }
+
getStarboardBridge().onActivityStart(this, keyboardEditor);
super.onStart();
}
@@ -123,13 +120,14 @@
protected void onStop() {
getStarboardBridge().onActivityStop(this);
super.onStop();
+
+ if (VideoSurfaceView.getCurrentSurface() != null) {
+ forceCreateNewVideoSurfaceView = true;
+ }
}
@Override
protected void onDestroy() {
- ViewTreeObserver observer = getWindow().getDecorView().getViewTreeObserver();
- observer.removeOnGlobalLayoutListener(videoSurfaceLayoutListener);
-
super.onDestroy();
getStarboardBridge().onActivityDestroy(this);
}
@@ -231,16 +229,10 @@
}
public void setVideoSurfaceBounds(final int x, final int y, final int width, final int height) {
- if (!videoSurfaceView.updateVideoBounds(x, y, width, height)) {
- return;
- }
-
- VideoSurfaceView.nativeOnLayoutNeeded();
runOnUiThread(
new Runnable() {
@Override
public void run() {
- VideoSurfaceView.nativeOnLayoutScheduled();
LayoutParams layoutParams = videoSurfaceView.getLayoutParams();
// Since videoSurfaceView is added directly to the Activity's content view, which is a
// FrameLayout, we expect its layout params to become FrameLayout.LayoutParams.
@@ -263,4 +255,21 @@
}
});
}
+
+ private void createNewSurfaceView() {
+ ViewParent parent = videoSurfaceView.getParent();
+ if (parent instanceof FrameLayout) {
+ FrameLayout frameLayout = (FrameLayout) parent;
+ int index = frameLayout.indexOfChild(videoSurfaceView);
+ frameLayout.removeView(videoSurfaceView);
+ videoSurfaceView = new VideoSurfaceView(this);
+ a11yHelper = new CobaltA11yHelper(videoSurfaceView);
+ frameLayout.addView(
+ videoSurfaceView,
+ index,
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ } else {
+ Log.w(TAG, "Unexpected surface view parent class " + parent.getClass().getName());
+ }
+ }
}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
index 591f63c..65e6dc8 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/CobaltService.java
@@ -57,6 +57,8 @@
public abstract ResponseToClient receiveFromClient(byte[] data);
/** Close the service. */
+ @SuppressWarnings("unused")
+ @UsedByNative
public abstract void close();
/** Send data from the service to the client. */
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/KeyboardInputConnection.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/KeyboardInputConnection.java
index 54a5a46..ce049b9 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/KeyboardInputConnection.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/coat/KeyboardInputConnection.java
@@ -174,5 +174,7 @@
return true;
}
+ public static native boolean nativeHasOnScreenKeyboard();
+
public static native void nativeSendText(CharSequence text, boolean isComposing);
}
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
index 5f59062..494f57b 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/AudioTrackBridge.java
@@ -197,7 +197,7 @@
// This conversion is safe, as only the lower bits will be set, since we
// called |getTimestamp| without a timebase.
// https://developer.android.com/reference/android/media/AudioTimestamp.html#framePosition
- audioTimestamp.framePosition = (int) audioTimestamp.framePosition;
+ audioTimestamp.framePosition &= 0x7FFFFFFF;
} else {
// Time stamps haven't been updated yet, assume playback hasn't started.
audioTimestamp.framePosition = 0;
diff --git a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoSurfaceView.java b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoSurfaceView.java
index 2478b97..1367aa7 100644
--- a/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoSurfaceView.java
+++ b/src/starboard/android/apk/app/src/main/java/dev/cobalt/media/VideoSurfaceView.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.graphics.Color;
-import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Surface;
import android.view.SurfaceHolder;
@@ -31,13 +30,7 @@
*/
public class VideoSurfaceView extends SurfaceView {
- public static native void nativeOnLayoutNeeded();
-
- public static native void nativeOnLayoutScheduled();
-
- public static native void nativeOnGlobalLayout();
-
- private Rect videoBounds;
+ private static Surface currentSurface = null;
public VideoSurfaceView(Context context) {
super(context);
@@ -60,7 +53,6 @@
}
private void initialize(Context context) {
- videoBounds = new Rect();
setBackgroundColor(Color.TRANSPARENT);
getHolder().addCallback(new SurfaceHolderCallback());
@@ -69,17 +61,6 @@
// punch-out video when the position / size is animated.
}
- public boolean updateVideoBounds(final int x, final int y, final int width, final int height) {
- if (videoBounds.left != x
- || videoBounds.top != y
- || videoBounds.right != x + width
- || videoBounds.bottom != y + height) {
- videoBounds.set(x, y, x + width, y + height);
- return true;
- }
- return false;
- }
-
private native void nativeOnVideoSurfaceChanged(Surface surface);
private class SurfaceHolderCallback implements SurfaceHolder.Callback {
@@ -88,7 +69,8 @@
@Override
public void surfaceCreated(SurfaceHolder holder) {
- nativeOnVideoSurfaceChanged(holder.getSurface());
+ currentSurface = holder.getSurface();
+ nativeOnVideoSurfaceChanged(currentSurface);
}
@Override
@@ -102,7 +84,12 @@
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- nativeOnVideoSurfaceChanged(null);
+ currentSurface = null;
+ nativeOnVideoSurfaceChanged(currentSurface);
}
}
+
+ public static Surface getCurrentSurface() {
+ return currentSurface;
+ }
}
diff --git a/src/starboard/android/shared/application_android.cc b/src/starboard/android/shared/application_android.cc
index a51dddd..1f013a8 100644
--- a/src/starboard/android/shared/application_android.cc
+++ b/src/starboard/android/shared/application_android.cc
@@ -395,6 +395,17 @@
ApplicationAndroid::Get()->SendKeyboardInject(static_cast<SbKey>(key));
}
+extern "C" SB_EXPORT_PLATFORM jboolean
+Java_dev_cobalt_coat_KeyboardInputConnection_nativeHasOnScreenKeyboard(
+ JniEnvExt* env,
+ jobject unused_this) {
+#if SB_HAS(ON_SCREEN_KEYBOARD)
+ return JNI_TRUE;
+#else // SB_HAS(ON_SCREEN_KEYBOARD)
+ return JNI_FALSE;
+#endif // SB_HAS(ON_SCREEN_KEYBOARD)
+}
+
#if SB_HAS(ON_SCREEN_KEYBOARD)
void ApplicationAndroid::SbWindowShowOnScreenKeyboard(SbWindow window,
diff --git a/src/starboard/android/shared/drm_create_system.cc b/src/starboard/android/shared/drm_create_system.cc
index e92d6a1..1e97b89 100644
--- a/src/starboard/android/shared/drm_create_system.cc
+++ b/src/starboard/android/shared/drm_create_system.cc
@@ -25,7 +25,7 @@
SbDrmServerCertificateUpdatedFunc server_certificate_updated_callback,
SbDrmSessionClosedFunc session_closed_callback) {
using starboard::android::shared::DrmSystem;
- using starboard::android::shared::IsWidevine;
+ using starboard::android::shared::IsWidevineL1;
if (!update_request_callback || !session_updated_callback ||
!key_statuses_changed_callback || !server_certificate_updated_callback ||
@@ -33,7 +33,7 @@
return kSbDrmSystemInvalid;
}
- if (!IsWidevine(key_system)) {
+ if (!IsWidevineL1(key_system)) {
return kSbDrmSystemInvalid;
}
diff --git a/src/starboard/android/shared/drm_system.h b/src/starboard/android/shared/drm_system.h
index cec052b..b0a6cb7 100644
--- a/src/starboard/android/shared/drm_system.h
+++ b/src/starboard/android/shared/drm_system.h
@@ -49,7 +49,8 @@
int session_id_size);
void CloseSession(const void* session_id, int session_id_size) override;
DecryptStatus Decrypt(InputBuffer* buffer) override;
-#if SB_API_VERSION >= 10
+
+ bool IsServerCertificateUpdatable() override { return false; }
void UpdateServerCertificate(int ticket,
const void* certificate,
int certificate_size) override {
@@ -57,7 +58,6 @@
SB_UNREFERENCED_PARAMETER(certificate);
SB_UNREFERENCED_PARAMETER(certificate_size);
}
-#endif // SB_API_VERSION >= 10
jobject GetMediaCrypto() const { return j_media_crypto_; }
void CallUpdateRequestCallback(int ticket,
diff --git a/src/starboard/android/shared/gyp_configuration.gypi b/src/starboard/android/shared/gyp_configuration.gypi
index 10f6c0c..5fc63d9 100644
--- a/src/starboard/android/shared/gyp_configuration.gypi
+++ b/src/starboard/android/shared/gyp_configuration.gypi
@@ -20,6 +20,7 @@
'target_os': 'android',
'final_executable_type': 'shared_library',
'gtest_target_type': 'shared_library',
+ 'sb_widevine_platform' : 'android',
'gl_type': 'system_gles2',
'enable_remote_debugging': 0,
diff --git a/src/starboard/android/shared/media_codec_bridge.cc b/src/starboard/android/shared/media_codec_bridge.cc
index 51c3586..2a3bcc2 100644
--- a/src/starboard/android/shared/media_codec_bridge.cc
+++ b/src/starboard/android/shared/media_codec_bridge.cc
@@ -230,11 +230,13 @@
}
MediaCodecBridge::~MediaCodecBridge() {
+ if (!j_media_codec_bridge_) {
+ return;
+ }
+
JniEnvExt* env = JniEnvExt::Get();
env->CallVoidMethodOrAbort(j_media_codec_bridge_, "stop", "()V");
-
- SB_DCHECK(j_media_codec_bridge_);
env->CallVoidMethodOrAbort(j_media_codec_bridge_, "release", "()V");
env->DeleteGlobalRef(j_media_codec_bridge_);
j_media_codec_bridge_ = NULL;
diff --git a/src/starboard/android/shared/media_common.h b/src/starboard/android/shared/media_common.h
index cbc2668..d899307 100644
--- a/src/starboard/android/shared/media_common.h
+++ b/src/starboard/android/shared/media_common.h
@@ -32,11 +32,15 @@
const int64_t kSecondInMicroseconds = 1000 * 1000;
-inline bool IsWidevine(const char* key_system) {
+inline bool IsWidevineL1(const char* key_system) {
return SbStringCompareAll(key_system, "com.widevine") == 0 ||
SbStringCompareAll(key_system, "com.widevine.alpha") == 0;
}
+inline bool IsWidevineL3(const char* key_system) {
+ return SbStringCompareAll(key_system, "com.youtube.widevine.l3") == 0;
+}
+
// Map a supported |SbMediaAudioCodec| into its corresponding mime type
// string. Returns |NULL| if |audio_codec| is not supported.
inline const char* SupportedAudioCodecToMimeType(
diff --git a/src/starboard/android/shared/media_decoder.cc b/src/starboard/android/shared/media_decoder.cc
index 1bee654..3c7ae4f 100644
--- a/src/starboard/android/shared/media_decoder.cc
+++ b/src/starboard/android/shared/media_decoder.cc
@@ -301,12 +301,15 @@
decoder_thread_ = kSbThreadInvalid;
}
- host_->OnFlushing();
- jint status = media_codec_bridge_->Flush();
- if (status != MEDIA_CODEC_OK) {
- SB_LOG(ERROR) << "Failed to flush media codec.";
+ if (is_valid()) {
+ host_->OnFlushing();
+
+ jint status = media_codec_bridge_->Flush();
+ if (status != MEDIA_CODEC_OK) {
+ SB_LOG(ERROR) << "Failed to flush media codec.";
+ }
+ host_ = NULL;
}
- host_ = NULL;
}
void MediaDecoder::CollectPendingData_Locked(
diff --git a/src/starboard/android/shared/media_is_supported.cc b/src/starboard/android/shared/media_is_supported.cc
index 4750d03..4dd0d82 100644
--- a/src/starboard/android/shared/media_is_supported.cc
+++ b/src/starboard/android/shared/media_is_supported.cc
@@ -20,7 +20,7 @@
SB_EXPORT bool SbMediaIsSupported(SbMediaVideoCodec video_codec,
SbMediaAudioCodec audio_codec,
const char* key_system) {
- using starboard::android::shared::IsWidevine;
+ using starboard::android::shared::IsWidevineL1;
using starboard::android::shared::JniEnvExt;
// 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
@@ -29,7 +29,7 @@
audio_codec != kSbMediaAudioCodecAac) {
return false;
}
- if (!IsWidevine(key_system)) {
+ if (!IsWidevineL1(key_system)) {
return false;
}
return JniEnvExt::Get()->CallStaticBooleanMethodOrAbort(
diff --git a/src/starboard/android/shared/sdk_utils.py b/src/starboard/android/shared/sdk_utils.py
index 025e8df..bed171b 100644
--- a/src/starboard/android/shared/sdk_utils.py
+++ b/src/starboard/android/shared/sdk_utils.py
@@ -39,6 +39,7 @@
_ANDROID_NDK_API_LEVEL = '21'
# Packages to install in the Android SDK.
+# We download ndk-bundle separately, so it's not in this list.
# Get available packages from "sdkmanager --list --verbose"
_ANDROID_SDK_PACKAGES = [
'build-tools;28.0.3',
@@ -47,7 +48,6 @@
'extras;android;m2repository',
'extras;google;m2repository',
'lldb;3.1',
- 'ndk-bundle',
'patcher;v4',
'platforms;android-28',
'platform-tools',
@@ -61,6 +61,12 @@
# see https://developer.android.com/studio/index.html#command-tools
_SDK_URL = 'https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip'
+# Location from which to download the Android NDK.
+# see https://developer.android.com/ndk/downloads (perhaps in "NDK archives")
+_NDK_ZIP_REVISION = 'android-ndk-r19c'
+_NDK_ZIP_FILE = _NDK_ZIP_REVISION + '-linux-x86_64.zip'
+_NDK_URL = 'https://dl.google.com/android/repository/' + _NDK_ZIP_FILE
+
_STARBOARD_TOOLCHAINS_DIR = build.GetToolchainsDir()
# The path to the Android SDK, if placed inside of starboard-toolchains.
@@ -198,18 +204,33 @@
logging.warning('Checking Android SDK.')
_DownloadInstallOrUpdateSdk()
+ ndk_path = GetNdkPath()
if _ANDROID_NDK_HOME:
- logging.warning('Warning: Using Android NDK in ANDROID_NDK_HOME,'
- ' which is not automatically updated')
- ndk_revision = _GetInstalledNdkRevision()
- logging.warning('Using Android NDK version %s', ndk_revision)
+ logging.warning('Warning: ANDROID_NDK_HOME references NDK %s in %s,'
+ ' which is not automatically updated.',
+ _GetInstalledNdkRevision(), ndk_path)
if _ANDROID_HOME or _ANDROID_NDK_HOME:
reply = raw_input(
- 'Do you want to continue using your custom Android tools? [yN]')
+ 'Do you want to continue using your custom Android tools? [y/N]')
if reply.upper() != 'Y':
sys.exit(1)
+ elif not _CheckStamp(ndk_path):
+ logging.warning('Downloading NDK from %s to %s', _NDK_URL, ndk_path)
+ if os.path.exists(ndk_path):
+ shutil.rmtree(ndk_path)
+ # Download the NDK into _STARBOARD_TOOLCHAINS_DIR and move the top
+ # _NDK_ZIP_REVISION directory that is in the zip to 'ndk-bundle'.
+ ndk_unzip_path = os.path.join(_STARBOARD_TOOLCHAINS_DIR,
+ _NDK_ZIP_REVISION)
+ if os.path.exists(ndk_unzip_path):
+ shutil.rmtree(ndk_unzip_path)
+ _DownloadAndUnzipFile(_NDK_URL, _STARBOARD_TOOLCHAINS_DIR)
+ # Move NDK into its proper final place.
+ os.rename(ndk_unzip_path, ndk_path)
+ _UpdateStamp(ndk_path)
+ logging.warning('Using Android NDK version %s', _GetInstalledNdkRevision())
finally:
fcntl.flock(toolchains_dir_fd, fcntl.LOCK_UN)
os.close(toolchains_dir_fd)
diff --git a/src/starboard/android/shared/starboard_platform.gypi b/src/starboard/android/shared/starboard_platform.gypi
index 7e2d83b..1609871 100644
--- a/src/starboard/android/shared/starboard_platform.gypi
+++ b/src/starboard/android/shared/starboard_platform.gypi
@@ -14,6 +14,7 @@
{
'variables': {
'has_input_events_filter' : '<!(python ../../../build/file_exists.py <(DEPTH)/starboard/android/shared/input_events_filter.cc)',
+ 'has_drm_system_extension%': '<!(test -e <(DEPTH)/starboard/android/shared/drm_system_extension/drm_system_extension.gyp && echo 1 || echo 0)',
},
'includes': [
'<(DEPTH)/starboard/shared/starboard/player/filter/player_filter.gypi',
@@ -84,7 +85,6 @@
'directory_get_next.cc',
'directory_internal.h',
'directory_open.cc',
- 'drm_create_system.cc',
'drm_system.cc',
'drm_system.h',
'egl_swap_buffers.cc',
@@ -126,11 +126,9 @@
'media_get_max_buffer_capacity.cc',
'media_is_audio_supported.cc',
'media_is_output_protected.cc',
- 'media_is_supported.cc',
'media_is_video_supported.cc',
'media_set_output_protection.cc',
'microphone_impl.cc',
- 'player_components_impl.cc',
'player_create.cc',
'player_destroy.cc',
'player_set_bounds.cc',
@@ -346,7 +344,9 @@
'<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_update_session.cc',
'<(DEPTH)/starboard/shared/starboard/event_cancel.cc',
'<(DEPTH)/starboard/shared/starboard/event_schedule.cc',
@@ -429,8 +429,6 @@
'<(DEPTH)/starboard/shared/stub/cryptography_set_authenticated_data.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_set_initialization_vector.cc',
'<(DEPTH)/starboard/shared/stub/cryptography_transform.cc',
- '<(DEPTH)/starboard/shared/stub/drm_is_server_certificate_updatable.cc',
- '<(DEPTH)/starboard/shared/stub/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/stub/image_decode.cc',
'<(DEPTH)/starboard/shared/stub/image_is_decode_supported.cc',
'<(DEPTH)/starboard/shared/stub/system_get_total_gpu_memory.cc',
@@ -446,17 +444,6 @@
'<(DEPTH)/starboard/shared/stub/thread_sampler_thaw.cc',
'<(DEPTH)/starboard/shared/stub/window_get_diagonal_size_in_inches.cc',
],
- 'conditions': [
- ['has_input_events_filter=="True"', {
- 'sources': [
- 'input_events_filter.cc',
- 'input_events_filter.h',
- ],
- 'defines': [
- 'STARBOARD_INPUT_EVENTS_FILTER',
- ],
- }],
- ],
'defines': [
# This must be defined when building Starboard, and must not when
# building Starboard client code.
@@ -468,6 +455,28 @@
'<(DEPTH)/third_party/opus/opus.gyp:opus',
'starboard_base_symbolize',
],
+ 'conditions': [
+ ['has_input_events_filter=="True"', {
+ 'sources': [
+ 'input_events_filter.cc',
+ 'input_events_filter.h',
+ ],
+ 'defines': [
+ 'STARBOARD_INPUT_EVENTS_FILTER',
+ ],
+ }],
+ ['has_drm_system_extension==1', {
+ 'dependencies': [
+ '<(DEPTH)/starboard/android/shared/drm_system_extension/drm_system_extension.gyp:drm_system_extension',
+ ],
+ }, {
+ 'sources': [
+ 'drm_create_system.cc',
+ 'media_is_supported.cc',
+ 'player_components_impl.cc',
+ ],
+ }],
+ ],
},
],
}
diff --git a/src/starboard/android/shared/video_decoder.cc b/src/starboard/android/shared/video_decoder.cc
index 9baffbd..e1808bf 100644
--- a/src/starboard/android/shared/video_decoder.cc
+++ b/src/starboard/android/shared/video_decoder.cc
@@ -25,7 +25,6 @@
#include "starboard/android/shared/jni_env_ext.h"
#include "starboard/android/shared/jni_utils.h"
#include "starboard/android/shared/media_common.h"
-#include "starboard/android/shared/video_window.h"
#include "starboard/android/shared/window_internal.h"
#include "starboard/configuration.h"
#include "starboard/decode_target.h"
@@ -170,7 +169,8 @@
decode_target_(kSbDecodeTargetInvalid),
frame_width_(0),
frame_height_(0),
- first_buffer_received_(false) {
+ first_buffer_received_(false),
+ surface_condition_variable_(surface_destroy_mutex_) {
if (!InitializeCodec()) {
SB_LOG(ERROR) << "Failed to initialize video decoder.";
TeardownCodec();
@@ -241,12 +241,16 @@
// because we need to change the color metadata.
if (media_decoder_ == NULL) {
if (!InitializeCodec()) {
- // TODO: Communicate this failure to our clients somehow.
SB_LOG(ERROR) << "Failed to reinitialize codec.";
+ TeardownCodec();
+ error_cb_(kSbPlayerErrorDecode, "Cannot initialize codec.");
}
}
}
+ if (!is_valid()) {
+ return;
+ }
media_decoder_->WriteInputBuffer(input_buffer);
if (number_of_frames_being_decoded_.increment() < kMaxPendingWorkSize) {
decoder_status_cb_(kNeedMoreInput, NULL);
@@ -261,6 +265,9 @@
first_buffer_timestamp_ = 0;
}
+ if (!is_valid()) {
+ return;
+ }
media_decoder_->WriteEndOfStream();
}
@@ -271,6 +278,7 @@
}
bool VideoDecoder::InitializeCodec() {
+ SB_DCHECK(BelongsToCurrentThread());
// Setup the output surface object. If we are in punch-out mode, target
// the passed in Android video surface. If we are in decode-to-texture
// mode, create a surface from a new texture target and use that as the
@@ -278,7 +286,10 @@
jobject j_output_surface = NULL;
switch (output_mode_) {
case kSbPlayerOutputModePunchOut: {
- j_output_surface = GetVideoSurface();
+ j_output_surface = AcquireVideoSurface();
+ if (j_output_surface) {
+ owns_video_surface_ = true;
+ }
} break;
case kSbPlayerOutputModeDecodeToTexture: {
// A width and height of (0, 0) is provided here because Android doesn't
@@ -306,14 +317,12 @@
return false;
}
- ANativeWindow* video_window = GetVideoWindow();
- if (!video_window) {
+ int width, height;
+ if (!GetVideoWindowSize(&width, &height)) {
SB_LOG(ERROR)
<< "Can't initialize the codec since we don't have a video window.";
return false;
}
- int width = ANativeWindow_getWidth(video_window);
- int height = ANativeWindow_getHeight(video_window);
jobject j_media_crypto = drm_system_ ? drm_system_->GetMediaCrypto() : NULL;
SB_DCHECK(!drm_system_ || j_media_crypto);
@@ -331,6 +340,11 @@
}
void VideoDecoder::TeardownCodec() {
+ SB_DCHECK(BelongsToCurrentThread());
+ if (owns_video_surface_) {
+ ReleaseVideoSurface();
+ owns_video_surface_ = false;
+ }
media_decoder_.reset();
color_metadata_ = starboard::nullopt;
@@ -494,6 +508,21 @@
}
}
+void VideoDecoder::OnSurfaceDestroyed() {
+ if (!BelongsToCurrentThread()) {
+ // Wait until codec is stoped.
+ ScopedLock lock(surface_destroy_mutex_);
+ Schedule(std::bind(&VideoDecoder::OnSurfaceDestroyed, this));
+ surface_condition_variable_.WaitTimed(kSbTimeSecond);
+ return;
+ }
+ // When this function is called, the decoder no longer owns the surface.
+ owns_video_surface_ = false;
+ TeardownCodec();
+ ScopedLock lock(surface_destroy_mutex_);
+ surface_condition_variable_.Signal();
+}
+
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/src/starboard/android/shared/video_decoder.h b/src/starboard/android/shared/video_decoder.h
index 0a4bd13..99aba66 100644
--- a/src/starboard/android/shared/video_decoder.h
+++ b/src/starboard/android/shared/video_decoder.h
@@ -20,9 +20,11 @@
#include "starboard/android/shared/drm_system.h"
#include "starboard/android/shared/media_codec_bridge.h"
#include "starboard/android/shared/media_decoder.h"
+#include "starboard/android/shared/video_window.h"
#include "starboard/atomic.h"
#include "starboard/common/optional.h"
#include "starboard/common/ref_counted.h"
+#include "starboard/condition_variable.h"
#include "starboard/decode_target.h"
#include "starboard/media.h"
#include "starboard/player.h"
@@ -30,6 +32,7 @@
#include "starboard/shared/starboard/player/filter/video_decoder_internal.h"
#include "starboard/shared/starboard/player/filter/video_renderer_sink.h"
#include "starboard/shared/starboard/player/input_buffer_internal.h"
+#include "starboard/shared/starboard/player/job_queue.h"
namespace starboard {
namespace android {
@@ -37,7 +40,9 @@
class VideoDecoder
: public ::starboard::shared::starboard::player::filter::VideoDecoder,
- private MediaDecoder::Host {
+ private MediaDecoder::Host,
+ private ::starboard::shared::starboard::player::JobQueue::JobOwner,
+ private VideoSurfaceHolder {
public:
typedef ::starboard::shared::starboard::player::filter::VideoRendererSink
VideoRendererSink;
@@ -79,6 +84,8 @@
bool Tick(MediaCodecBridge* media_codec_bridge) override;
void OnFlushing() override;
+ void OnSurfaceDestroyed() override;
+
// These variables will be initialized inside ctor or Initialize() and will
// not be changed during the life time of this class.
const SbMediaVideoCodec video_codec_;
@@ -115,6 +122,13 @@
bool first_buffer_received_;
volatile SbTime first_buffer_timestamp_;
+
+ // Use |owns_video_surface_| only on decoder thread, to avoid unnecessary
+ // invocation of ReleaseVideoSurface(), though ReleaseVideoSurface() would
+ // do nothing if not own the surface.
+ bool owns_video_surface_ = false;
+ starboard::Mutex surface_destroy_mutex_;
+ starboard::ConditionVariable surface_condition_variable_;
};
} // namespace shared
diff --git a/src/starboard/android/shared/video_window.cc b/src/starboard/android/shared/video_window.cc
index da4bbf0..518674d 100644
--- a/src/starboard/android/shared/video_window.cc
+++ b/src/starboard/android/shared/video_window.cc
@@ -21,9 +21,10 @@
#include <jni.h>
#include "starboard/android/shared/jni_env_ext.h"
-#include "starboard/condition_variable.h"
#include "starboard/configuration.h"
#include "starboard/log.h"
+#include "starboard/mutex.h"
+#include "starboard/once.h"
#include "starboard/shared/gles/gl_call.h"
namespace starboard {
@@ -32,17 +33,14 @@
namespace {
+// Global video surface pointer mutex.
+SB_ONCE_INITIALIZE_FUNCTION(Mutex, GetViewSurfaceMutex);
// Global pointer to the single video surface.
jobject g_j_video_surface = NULL;
// Global pointer to the single video window.
ANativeWindow* g_native_video_window = NULL;
-
-// Facilitate synchronization of punch-out videos.
-SbMutex g_bounds_updates_mutex = SB_MUTEX_INITIALIZER;
-SbConditionVariable g_bounds_updates_condition =
- SB_CONDITION_VARIABLE_INITIALIZER;
-int g_bounds_updates_needed = 0;
-int g_bounds_updates_scheduled = 0;
+// Global video surface pointer holder.
+VideoSurfaceHolder* g_video_surface_holder = NULL;
} // namespace
@@ -51,58 +49,61 @@
JNIEnv* env,
jobject unused_this,
jobject surface) {
+ ScopedLock lock(*GetViewSurfaceMutex());
+ if (g_video_surface_holder) {
+ g_video_surface_holder->OnSurfaceDestroyed();
+ g_video_surface_holder = NULL;
+ }
if (g_j_video_surface) {
- // TODO: Ensure that the decoder isn't still using the surface.
env->DeleteGlobalRef(g_j_video_surface);
+ g_j_video_surface = NULL;
}
if (g_native_video_window) {
- // TODO: Ensure that the decoder isn't still using the window.
ANativeWindow_release(g_native_video_window);
+ g_native_video_window = NULL;
}
if (surface) {
g_j_video_surface = env->NewGlobalRef(surface);
g_native_video_window = ANativeWindow_fromSurface(env, surface);
- } else {
- g_j_video_surface = NULL;
- g_native_video_window = NULL;
}
}
-extern "C" SB_EXPORT_PLATFORM void
-Java_dev_cobalt_media_VideoSurfaceView_nativeOnLayoutNeeded() {
- SbMutexAcquire(&g_bounds_updates_mutex);
- ++g_bounds_updates_needed;
- SbMutexRelease(&g_bounds_updates_mutex);
-}
-
-extern "C" SB_EXPORT_PLATFORM void
-Java_dev_cobalt_media_VideoSurfaceView_nativeOnLayoutScheduled() {
- SbMutexAcquire(&g_bounds_updates_mutex);
- ++g_bounds_updates_scheduled;
- SbMutexRelease(&g_bounds_updates_mutex);
-}
-
-extern "C" SB_EXPORT_PLATFORM void
-Java_dev_cobalt_media_VideoSurfaceView_nativeOnGlobalLayout() {
- SbMutexAcquire(&g_bounds_updates_mutex);
- g_bounds_updates_needed -= g_bounds_updates_scheduled;
- g_bounds_updates_scheduled = 0;
- if (g_bounds_updates_needed <= 0) {
- g_bounds_updates_needed = 0;
- SbConditionVariableSignal(&g_bounds_updates_condition);
+jobject VideoSurfaceHolder::AcquireVideoSurface() {
+ ScopedLock lock(*GetViewSurfaceMutex());
+ SB_DCHECK(g_video_surface_holder == NULL);
+ if (g_video_surface_holder != NULL) {
+ return NULL;
}
- SbMutexRelease(&g_bounds_updates_mutex);
-}
-
-jobject GetVideoSurface() {
+ if (!g_j_video_surface) {
+ return NULL;
+ }
+ g_video_surface_holder = this;
return g_j_video_surface;
}
-ANativeWindow* GetVideoWindow() {
- return g_native_video_window;
+void VideoSurfaceHolder::ReleaseVideoSurface() {
+ ScopedLock lock(*GetViewSurfaceMutex());
+ if (g_video_surface_holder == this) {
+ g_video_surface_holder = NULL;
+ }
}
-void ClearVideoWindow() {
+bool VideoSurfaceHolder::GetVideoWindowSize(int* width, int* height) {
+ ScopedLock lock(*GetViewSurfaceMutex());
+ if (g_native_video_window == NULL) {
+ return false;
+ } else {
+ *width = ANativeWindow_getWidth(g_native_video_window);
+ *height = ANativeWindow_getHeight(g_native_video_window);
+ return true;
+ }
+}
+
+void VideoSurfaceHolder::ClearVideoWindow() {
+ // Lock *GetViewSurfaceMutex() here, to avoid releasing g_native_video_window
+ // during painting.
+ ScopedLock lock(*GetViewSurfaceMutex());
+
if (!g_native_video_window) {
SB_LOG(INFO) << "Tried to clear video window when it was null.";
return;
@@ -188,20 +189,6 @@
EGL_CALL(eglTerminate(display));
}
-void WaitForVideoBoundsUpdate() {
- SbMutexAcquire(&g_bounds_updates_mutex);
- if (g_bounds_updates_needed > 0) {
- // Use a timed wait to deal with possible race conditions in which
- // suspend occurs in the middle of a bounds update.
- SbConditionVariableWaitTimed(&g_bounds_updates_condition,
- &g_bounds_updates_mutex,
- 100 * kSbTimeMillisecond);
- g_bounds_updates_needed = 0;
- g_bounds_updates_scheduled = 0;
- }
- SbMutexRelease(&g_bounds_updates_mutex);
-}
-
} // namespace shared
} // namespace android
} // namespace starboard
diff --git a/src/starboard/android/shared/video_window.h b/src/starboard/android/shared/video_window.h
index 39cb33f..908b567 100644
--- a/src/starboard/android/shared/video_window.h
+++ b/src/starboard/android/shared/video_window.h
@@ -22,19 +22,31 @@
namespace android {
namespace shared {
-// Returns the surface which video should be rendered. This is the surface
-// that owns the native window returned by |GetVideoWindow|.
-jobject GetVideoSurface();
+class VideoSurfaceHolder {
+ public:
+ // OnSurfaceDestroyed() will be invoked when surface is destroyed. When this
+ // function is called, the decoder no longer owns the surface. Calling
+ // AcquireVideoSurface(), ReleaseVideoSurface(), GetVideoWindowSize() or
+ // ClearVideoWindow() in this function may cause dead lock.
+ virtual void OnSurfaceDestroyed() = 0;
-// Returns the native window into which video should be rendered.
-ANativeWindow* GetVideoWindow();
+ protected:
+ ~VideoSurfaceHolder() {}
-// Clear the video window by painting it Black. This function is safe to call
-// regardless of whether the video window has been initialized or not.
-void ClearVideoWindow();
+ // Returns the surface which video should be rendered. Surface cannot be
+ // acquired before last holder release the surface.
+ jobject AcquireVideoSurface();
-// Wait for all outstanding adjustments of video bounds before returning.
-void WaitForVideoBoundsUpdate();
+ // Release the surface to make the surface available for other holder.
+ void ReleaseVideoSurface();
+
+ // Get the native window size. Return false if don't have available native
+ // window.
+ bool GetVideoWindowSize(int* width, int* height);
+
+ // Clear the video window by painting it Black.
+ void ClearVideoWindow();
+};
} // namespace shared
} // namespace android
diff --git a/src/starboard/linux/shared/starboard_platform.gypi b/src/starboard/linux/shared/starboard_platform.gypi
index 5eed6f1..0a2cedb 100644
--- a/src/starboard/linux/shared/starboard_platform.gypi
+++ b/src/starboard/linux/shared/starboard_platform.gypi
@@ -368,13 +368,13 @@
'<(DEPTH)/starboard/shared/starboard/drm/drm_close_session.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_destroy_system.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_generate_session_update_request.cc',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_system_internal.h',
+ '<(DEPTH)/starboard/shared/starboard/drm/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/starboard/drm/drm_update_session.cc',
- '<(DEPTH)/starboard/shared/widevine/drm_is_server_certificate_updatable.cc',
'<(DEPTH)/starboard/shared/widevine/drm_system_widevine.cc',
'<(DEPTH)/starboard/shared/widevine/drm_system_widevine.h',
- '<(DEPTH)/starboard/shared/widevine/drm_update_server_certificate.cc',
'<(DEPTH)/starboard/shared/widevine/media_is_supported.cc',
'<(DEPTH)/starboard/shared/widevine/widevine_storage.cc',
'<(DEPTH)/starboard/shared/widevine/widevine_storage.h',
diff --git a/src/starboard/shared/widevine/drm_is_server_certificate_updatable.cc b/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
similarity index 69%
rename from src/starboard/shared/widevine/drm_is_server_certificate_updatable.cc
rename to src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
index 91d17dd..57afcd5 100644
--- a/src/starboard/shared/widevine/drm_is_server_certificate_updatable.cc
+++ b/src/starboard/shared/starboard/drm/drm_is_server_certificate_updatable.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
@@ -14,12 +14,18 @@
#include "starboard/drm.h"
+#include "starboard/log.h"
+#include "starboard/shared/starboard/drm/drm_system_internal.h"
+
#if SB_API_VERSION >= 10
bool SbDrmIsServerCertificateUpdatable(SbDrmSystem drm_system) {
- SB_UNREFERENCED_PARAMETER(drm_system);
+ if (!SbDrmSystemIsValid(drm_system)) {
+ SB_DLOG(ERROR) << "Invalid DRM system.";
+ return false;
+ }
- return true;
+ return drm_system->IsServerCertificateUpdatable();
}
#endif // SB_API_VERSION >= 10
diff --git a/src/starboard/shared/starboard/drm/drm_system_internal.h b/src/starboard/shared/starboard/drm/drm_system_internal.h
index 696855b..5cf70e5 100644
--- a/src/starboard/shared/starboard/drm/drm_system_internal.h
+++ b/src/starboard/shared/starboard/drm/drm_system_internal.h
@@ -42,6 +42,8 @@
virtual DecryptStatus Decrypt(InputBuffer* buffer) = 0;
#if SB_API_VERSION >= 10
+ virtual bool IsServerCertificateUpdatable() = 0;
+
virtual void UpdateServerCertificate(int ticket,
const void* certificate,
int certificate_size) = 0;
diff --git a/src/starboard/shared/widevine/drm_update_server_certificate.cc b/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
similarity index 95%
rename from src/starboard/shared/widevine/drm_update_server_certificate.cc
rename to src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
index b9f29d4..c39cd11 100644
--- a/src/starboard/shared/widevine/drm_update_server_certificate.cc
+++ b/src/starboard/shared/starboard/drm/drm_update_server_certificate.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The Cobalt Authors. All Rights Reserved.
+// 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.
diff --git a/src/starboard/shared/widevine/drm_system_widevine.cc b/src/starboard/shared/widevine/drm_system_widevine.cc
index 471b163..3a0f961 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.cc
+++ b/src/starboard/shared/widevine/drm_system_widevine.cc
@@ -14,6 +14,7 @@
#include "starboard/shared/widevine/drm_system_widevine.h"
+#include <algorithm>
#include <vector>
#include "starboard/character.h"
@@ -47,6 +48,38 @@
}
};
+class Registry {
+ public:
+ void Register(SbDrmSystem drm_system) {
+ SB_DCHECK(SbDrmSystemIsValid(drm_system));
+ ScopedLock scoped_lock(mutex_);
+ auto iter = std::find(drm_systems_.begin(), drm_systems_.end(), drm_system);
+ SB_DCHECK(iter == drm_systems_.end());
+ drm_systems_.push_back(drm_system);
+ }
+
+ void Unregister(SbDrmSystem drm_system) {
+ ScopedLock scoped_lock(mutex_);
+ auto iter = std::find(drm_systems_.begin(), drm_systems_.end(), drm_system);
+ SB_DCHECK(iter != drm_systems_.end());
+ drm_systems_.erase(iter);
+ }
+
+ bool Contains(SbDrmSystem drm_system) const {
+ ScopedLock scoped_lock(mutex_);
+ auto iter = std::find(drm_systems_.begin(), drm_systems_.end(), drm_system);
+ return iter != drm_systems_.end();
+ }
+
+ private:
+ Mutex mutex_;
+ // Use std::vector<> as usually there is only one active instance of widevine
+ // drm system.
+ std::vector<SbDrmSystem> drm_systems_;
+};
+
+SB_ONCE_INITIALIZE_FUNCTION(Registry, GetRegistry);
+
std::string GetWidevineStoragePath() {
char path[SB_FILE_MAX_PATH + 1] = {0};
auto path_size = SB_ARRAY_SIZE_INT(path);
@@ -212,9 +245,13 @@
#endif // SB_API_VERSION >= 10
cdm_.reset(wv3cdm::create(this, NULL, kEnablePrivacyMode));
SB_DCHECK(cdm_);
+
+ GetRegistry()->Register(this);
}
-DrmSystemWidevine::~DrmSystemWidevine() {}
+DrmSystemWidevine::~DrmSystemWidevine() {
+ GetRegistry()->Unregister(this);
+}
// static
bool DrmSystemWidevine::IsKeySystemSupported(const char* key_system) {
@@ -226,6 +263,11 @@
return false;
}
+// static
+bool DrmSystemWidevine::IsDrmSystemWidevine(SbDrmSystem drm_system) {
+ return GetRegistry()->Contains(drm_system);
+}
+
void DrmSystemWidevine::GenerateSessionUpdateRequest(
int ticket,
const char* type,
diff --git a/src/starboard/shared/widevine/drm_system_widevine.h b/src/starboard/shared/widevine/drm_system_widevine.h
index d9873a4..ddc8ab2 100644
--- a/src/starboard/shared/widevine/drm_system_widevine.h
+++ b/src/starboard/shared/widevine/drm_system_widevine.h
@@ -61,6 +61,7 @@
~DrmSystemWidevine() override;
static bool IsKeySystemSupported(const char* key_system);
+ static bool IsDrmSystemWidevine(SbDrmSystem drm_system);
// From |SbDrmSystemPrivate|.
void GenerateSessionUpdateRequest(int ticket,
@@ -80,6 +81,8 @@
DecryptStatus Decrypt(InputBuffer* buffer) override;
#if SB_API_VERSION >= 10
+ bool IsServerCertificateUpdatable() override { return true; }
+
// This function is called by the app to explicitly set the server
// certificate. For an app that supports this feature, it should call this
// function before calling any other functions like
diff --git a/src/third_party/boringssl/boringssl.gyp b/src/third_party/boringssl/boringssl.gyp
index 81abcd8..ac3d496 100644
--- a/src/third_party/boringssl/boringssl.gyp
+++ b/src/third_party/boringssl/boringssl.gyp
@@ -211,7 +211,7 @@
'.',
'<@(openssl_config_path)',
],
- 'direct_dependent_settings': {
+ 'all_dependent_settings': {
'include_dirs': [
'<@(openssl_config_path)',
],
@@ -231,7 +231,7 @@
'include_dirs': [
'<(boringssl_root)/include',
],
- 'direct_dependent_settings': {
+ 'all_dependent_settings': {
'include_dirs': [
'<(boringssl_root)/include',
],
@@ -242,7 +242,6 @@
# of assembly language files for the current OS and CPU architecture, or
# it will turn off assembly language files entirely if the
# |asm_target_arch| has been set to "none".
-
['target_os not in ["linux", "android", "tvos"] or asm_target_arch not in ["x86", "x64", "arm", "arm64"]', {
# please read comments for |target_os=="win"| condition below
'defines': [