diff --git a/android/src/main/java/com/theoplayer/latency/LatencyConfiguration.kt b/android/src/main/java/com/theoplayer/latency/LatencyConfiguration.kt new file mode 100644 index 000000000..da9ce66b4 --- /dev/null +++ b/android/src/main/java/com/theoplayer/latency/LatencyConfiguration.kt @@ -0,0 +1,22 @@ +package com.theoplayer.latency + +import com.theoplayer.android.api.latency.LatencyConfiguration +import org.json.JSONObject + +private const val PROP_MINIMUM_OFFSET = "minimumOffset" +private const val PROP_MAXIMUM_OFFSET = "maximumOffset" +private const val PROP_TARGET_OFFSET = "targetOffset" +private const val PROP_FORCE_SEEK_OFFSET = "forceSeekOffset" +private const val PROP_MINIMUM_PLAYBACK_RATE = "minimumPlaybackRate" +private const val PROP_MAXIMUM_PLAYBACK_RATE = "maximumPlaybackRate" + +fun parseLatencyConfiguration(jsonLatency: JSONObject): LatencyConfiguration { + return LatencyConfiguration.Builder().apply { + setMinimumOffset(jsonLatency.optDouble(PROP_MINIMUM_OFFSET)) + setMaximumOffset(jsonLatency.optDouble(PROP_MAXIMUM_OFFSET)) + setTargetOffset(jsonLatency.optDouble(PROP_TARGET_OFFSET)) + setForceSeekOffset(jsonLatency.optDouble(PROP_FORCE_SEEK_OFFSET)) + setMinimumPlaybackRate(jsonLatency.optDouble(PROP_MINIMUM_PLAYBACK_RATE)) + setMaximumPlaybackRate(jsonLatency.optDouble(PROP_MAXIMUM_PLAYBACK_RATE)) + }.build() +} diff --git a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt index 2dde5d0c2..a7d083e56 100644 --- a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +++ b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt @@ -22,7 +22,9 @@ import com.theoplayer.android.api.ads.theoads.TheoAdsLayoutOverride import com.theoplayer.android.api.error.ErrorCode import com.theoplayer.android.api.source.AdIntegration import com.theoplayer.android.api.source.dash.DashPlaybackConfiguration +import com.theoplayer.android.api.theolive.TheoLiveSource import com.theoplayer.drm.ContentProtectionAdapter +import com.theoplayer.latency.parseLatencyConfiguration import com.theoplayer.util.BridgeUtils import org.json.JSONArray import org.json.JSONException @@ -61,6 +63,7 @@ private const val PROP_CUSTOM_ASSET_KEY = "customAssetKey" private const val PROP_OVERRIDE_LAYOUT = "overrideLayout" private const val PROP_NETWORK_CODE = "networkCode" private const val PROP_USE_ID3 = "useId3" +private const val PROP_LATENCY_CONFIGURATION = "latencyConfiguration" private const val ERROR_IMA_NOT_ENABLED = "Google IMA support not enabled." private const val ERROR_THEOADS_NOT_ENABLED = "THEOads support not enabled." @@ -69,6 +72,8 @@ private const val ERROR_MISSING_CSAI_INTEGRATION = "Missing CSAI integration" private const val PROP_SSAI_INTEGRATION_GOOGLE_DAI = "google-dai" +private const val INTEGRATION_THEOLIVE = "theolive" + class SourceAdapter { private val gson = Gson() @@ -148,27 +153,43 @@ class SourceAdapter { return null } + private fun parseTheoLiveSource(jsonTypedSource: JSONObject): TheoLiveSource { + return TheoLiveSource( + src=jsonTypedSource.optString(PROP_SRC), + headers=jsonTypedSource.optJSONObject(PROP_HEADERS)?.let { + BridgeUtils.fromJSONObjectToMap(it) + }, + latencyConfiguration=jsonTypedSource.optJSONObject(PROP_LATENCY_CONFIGURATION)?.let { + parseLatencyConfiguration(it) + } + ) + } + @Throws(THEOplayerException::class) private fun parseTypedSource(jsonTypedSource: JSONObject): TypedSource { + // Some integrations do not support the Builder pattern + return when (jsonTypedSource.optString(PROP_INTEGRATION)) { + INTEGRATION_THEOLIVE -> parseTheoLiveSource(jsonTypedSource) + else -> parseTypedSourceFromBuilder(jsonTypedSource) + } + } + + @Throws(THEOplayerException::class) + private fun parseTypedSourceFromBuilder(jsonTypedSource: JSONObject): TypedSource { try { var tsBuilder = TypedSource.Builder(jsonTypedSource.optString(PROP_SRC)) val sourceType = parseSourceType(jsonTypedSource) - if (jsonTypedSource.has(PROP_SSAI)) { - val ssaiJson = jsonTypedSource.getJSONObject(PROP_SSAI) + jsonTypedSource.optJSONObject(PROP_SSAI)?.let { ssaiJson -> tsBuilder = SSAIAdapterRegistry.typedSourceBuilderFromJson(ssaiJson, tsBuilder, sourceType) } if (sourceType != null) { tsBuilder.type(sourceType) } - if (jsonTypedSource.has(PROP_DASH)) { - tsBuilder.dash(parseDashConfig(jsonTypedSource.getJSONObject(PROP_DASH))) + jsonTypedSource.optJSONObject(PROP_DASH)?.let { dashJson -> + tsBuilder.dash(parseDashConfig(dashJson)) } jsonTypedSource.optJSONObject(PROP_HEADERS)?.let { headersJson -> - tsBuilder.headers(mutableMapOf().apply { - headersJson.keys().forEach { key -> - put(key, headersJson.getString(key)) - } - }) + tsBuilder.headers(BridgeUtils.fromJSONObjectToMap(headersJson)) } if (jsonTypedSource.has(PROP_LIVE_OFFSET)) { tsBuilder.liveOffset(jsonTypedSource.getDouble(PROP_LIVE_OFFSET)) diff --git a/android/src/main/java/com/theoplayer/util/BridgeUtils.kt b/android/src/main/java/com/theoplayer/util/BridgeUtils.kt index 4bb0aa2b8..72a45c9b6 100644 --- a/android/src/main/java/com/theoplayer/util/BridgeUtils.kt +++ b/android/src/main/java/com/theoplayer/util/BridgeUtils.kt @@ -8,6 +8,14 @@ import org.json.JSONException import org.json.JSONObject object BridgeUtils { + fun fromJSONObjectToMap(json: JSONObject): Map { + return mutableMapOf().apply { + json.keys().forEach { key -> + put(key, json.getString(key)) + } + } + } + /** * Convert a JSONObject to bridge data. */