diff --git a/CHANGELOG.md b/CHANGELOG.md index ee5dcc938..5d199c927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Added + +- Added `abr` property to `TypedSource`, supporting `preferredVideoCodecs` and `preferredAudioCodecs` on Android and Web. + +### Fixed + +- Fixed an issue on Android where `preferredKeySystems` in the content protection configuration was ignored. + ## [11.4.1] - 26-07-22 ### Changed diff --git a/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt b/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt index 56b6b45bf..610a6c563 100644 --- a/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt +++ b/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt @@ -48,6 +48,7 @@ const val PROP_MULTISESSION: String = "multiSession" const val PROP_KEYS: String = "keys" const val PROP_ID: String = "id" const val PROP_VALUE: String = "value" +const val PROP_PREFERRED_KEY_SYSTEMS: String = "preferredKeySystems" object ContentProtectionAdapter { @@ -108,6 +109,12 @@ object ContentProtectionAdapter { if (jsonConfig.has(PROP_MULTISESSION)) { multiSession(jsonConfig.getBoolean(PROP_MULTISESSION)) } + if (jsonConfig.has(PROP_PREFERRED_KEY_SYSTEMS)) { + val jsonKeySystems = jsonConfig.getJSONArray(PROP_PREFERRED_KEY_SYSTEMS) + preferredKeySystems((0 until jsonKeySystems.length()).mapNotNull { + KeySystemAdapter.fromString(jsonKeySystems.getString(it)) + }) + } queryParameters(fromJSONObjectToMap(jsonConfig.optJSONObject(PROP_QUERY_PARAMETERS))) }.build() } diff --git a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt index 303c02564..bd2893d4c 100644 --- a/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +++ b/android/src/main/java/com/theoplayer/source/SourceAdapter.kt @@ -8,6 +8,7 @@ import com.theoplayer.android.api.ads.theoads.TheoAdDescription import com.theoplayer.android.api.error.THEOplayerException import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap +import com.theoplayer.android.api.source.SourceAbrConfiguration import com.theoplayer.android.api.source.SourceDescription import com.theoplayer.android.api.source.TypedSource import com.theoplayer.android.api.source.metadata.MetadataDescription @@ -41,6 +42,9 @@ import java.util.ArrayList import java.util.HashMap private const val TAG = "SourceAdapter" +private const val PROP_ABR = "abr" +private const val PROP_PREFERRED_VIDEO_CODECS = "preferredVideoCodecs" +private const val PROP_PREFERRED_AUDIO_CODECS = "preferredAudioCodecs" private const val PROP_CONTENT_PROTECTION = "contentProtection" private const val PROP_LIVE_OFFSET = "liveOffset" private const val PROP_HLS_DATERANGE = "hlsDateRange" @@ -179,6 +183,9 @@ class SourceAdapter { .poster(poster) .ads(*ads.toTypedArray()) .textTracks(*sideLoadedTextTracks.toTypedArray()) + parseAbrConfigurationFromSources(jsonSources, jsonSourceObject.optJSONObject(PROP_SOURCES))?.let { + builder.abr(it) + } if (metadataDescription != null) { builder.metadata(metadataDescription) } @@ -194,6 +201,22 @@ class SourceAdapter { return null } + private fun parseAbrConfigurationFromSources(jsonSources: JSONArray?, jsonSource: JSONObject?): SourceAbrConfiguration? { + // The SDK applies the ABR configuration per source description: take it from the first typed source specifying one. + val jsonAbr = if (jsonSources != null) { + (0 until jsonSources.length()).firstNotNullOfOrNull { (jsonSources[it] as JSONObject).optJSONObject(PROP_ABR) } + } else { + jsonSource?.optJSONObject(PROP_ABR) + } ?: return null + return SourceAbrConfiguration( + maxBitrate = null, + preferredVideoCodecs = jsonAbr.optJSONArray(PROP_PREFERRED_VIDEO_CODECS)?.toStringList(), + preferredAudioCodecs = jsonAbr.optJSONArray(PROP_PREFERRED_AUDIO_CODECS)?.toStringList() + ) + } + + private fun JSONArray.toStringList(): List = (0 until length()).map { getString(it) } + private fun parseTheoLiveSource(jsonTypedSource: JSONObject): TheoLiveSource { return TheoLiveSource( src=jsonTypedSource.optString(PROP_SRC), diff --git a/src/api/source/SourceDescription.ts b/src/api/source/SourceDescription.ts index 475d33aaf..a4388ec71 100644 --- a/src/api/source/SourceDescription.ts +++ b/src/api/source/SourceDescription.ts @@ -16,6 +16,7 @@ import type { ServerSideAdInsertionConfiguration } from './ads/ssai/ServerSideAd import type { AnalyticsDescription } from './analytics/AnalyticsDescription'; import { CmcdSourceConfiguration } from '../cmcd/CmcdConfiguration'; import { SourceLatencyConfiguration } from './latency/SourceLatencyConfiguration'; +import type { SourceAbrConfiguration } from './abr/SourceAbrConfiguration'; /** * A type alias for a {@link TypedSource} media resource. @@ -344,6 +345,13 @@ export interface BaseSource { *
- Ignored for non-HLS streams. */ hls?: HlsPlaybackConfiguration; + + /** + * The source's ABR configuration. + * + * @platform web,android + */ + abr?: SourceAbrConfiguration; } /** diff --git a/src/api/source/abr/SourceAbrConfiguration.ts b/src/api/source/abr/SourceAbrConfiguration.ts new file mode 100644 index 000000000..a970f1849 --- /dev/null +++ b/src/api/source/abr/SourceAbrConfiguration.ts @@ -0,0 +1,27 @@ +/** + * Describes the ABR configuration for a specific source. + * + * @category Source + * @public + */ +export interface SourceAbrConfiguration { + /** + * A list of preferred audio codecs which will be used by the ABR algorithm for track selection, if the codec is supported. + * + * @platform web,android + * + * @remarks + *
- Codecs are specified by their RFC 6381 name, e.g. `"ec-3"` or `"mp4a.40.2"`. + */ + preferredAudioCodecs?: string[]; + + /** + * A list of preferred video codecs which will be used by the ABR algorithm for track selection, if the codec is supported. + * + * @platform web,android + * + * @remarks + *
- Codecs are specified by their RFC 6381 name, e.g. `"hvc1"`, `"dvh1"` or `"avc1"`. + */ + preferredVideoCodecs?: string[]; +} diff --git a/src/api/source/abr/barrel.ts b/src/api/source/abr/barrel.ts new file mode 100644 index 000000000..8b7a54ef0 --- /dev/null +++ b/src/api/source/abr/barrel.ts @@ -0,0 +1 @@ +export * from './SourceAbrConfiguration'; diff --git a/src/api/source/barrel.ts b/src/api/source/barrel.ts index bcabdd48e..bed164870 100644 --- a/src/api/source/barrel.ts +++ b/src/api/source/barrel.ts @@ -1,3 +1,4 @@ +export * from './abr/barrel'; export * from './ads/barrel'; export * from './analytics/barrel'; export * from './drm/barrel';