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': [