feat(android): Support Common Media Client Data (CMCD) (#4034)
* feat(VideoNativeComponent.ts): add support for cmcd configuration in VideoSrc type to enable cmcd feature on android feat(video.ts): introduce CmcdMode enum and CmcdConfiguration type to define cmcd configuration options * feat(Video.tsx): add support for CMCD configuration in Video component to handle Content Management and Delivery (CMCD) headers for Android platform. * feat(CMCDProps.kt): add CMCDProps class to handle CMCD related properties and parsing logic for React Native module * feat(CMCDConfig.kt): add CMCDConfig class to handle CMCD configuration for ExoPlayer with support for custom data and configuration options. * feat(ReactExoplayerViewManager.java): add support for CMCD configuration in ReactExoplayerViewManager to enable Content Management and Control Data (CMCD) for better video playback optimization. * feat(ReactExoplayerView.java): add support for setting CmcdConfiguration.Factory to customize CMCD configuration for media playback * feat(Source.kt): add support for CMCD properties linked to the source to enhance functionality and data handling * docs(props.mdx): add documentation for configuring CMCD parameters in the component, including usage examples and default values * refactor(ReactExoplayerViewManager.java): remove unused PROP_CMCD and prevCmcdConfig variables to clean up code and improve readability * refactor(Video.tsx): simplify cmcd configuration logic for better readability and maintainability * docs(props.mdx): improve props documentation for clarity and consistency feat(props.mdx): add definitions for CmcdMode enum and CmcdData type to enhance understanding of CMCD data structure and usage * refactor(CMCDProps.kt): refactor CMCDProps class to data class for improved readability and immutability - update CMCDProps class to use List instead of Array for properties * refactor(Video.tsx): refactor createCmcdHeader function to improve code readability and reduce duplication * fix(CMCDProps.kt): remove import statement for CmcdConfiguration * feat(ReactExoplayerView.java): add support for CMCD configuration in ReactExoplayerView component feat(ReactExoplayerViewManager.java): remove redundant CMCD configuration logic from ReactExoplayerViewManager to simplify code and improve maintainability * fix(Video.tsx): merge _cmcd memo into src memo for optimization
This commit is contained in:
parent
65faba312d
commit
ca795f298a
51
android/src/main/java/com/brentvatne/common/api/CMCDProps.kt
Normal file
51
android/src/main/java/com/brentvatne/common/api/CMCDProps.kt
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package com.brentvatne.common.api
|
||||||
|
|
||||||
|
import com.brentvatne.common.toolbox.ReactBridgeUtils.safeGetInt
|
||||||
|
import com.facebook.react.bridge.ReadableArray
|
||||||
|
import com.facebook.react.bridge.ReadableMap
|
||||||
|
import com.facebook.react.bridge.ReadableType
|
||||||
|
|
||||||
|
data class CMCDProps(
|
||||||
|
val cmcdObject: List<Pair<String, Any>> = emptyList(),
|
||||||
|
val cmcdRequest: List<Pair<String, Any>> = emptyList(),
|
||||||
|
val cmcdSession: List<Pair<String, Any>> = emptyList(),
|
||||||
|
val cmcdStatus: List<Pair<String, Any>> = emptyList(),
|
||||||
|
val mode: Int = 1
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private const val PROP_CMCD_OBJECT = "object"
|
||||||
|
private const val PROP_CMCD_REQUEST = "request"
|
||||||
|
private const val PROP_CMCD_SESSION = "session"
|
||||||
|
private const val PROP_CMCD_STATUS = "status"
|
||||||
|
private const val PROP_CMCD_MODE = "mode"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun parse(src: ReadableMap?): CMCDProps? {
|
||||||
|
if (src == null) return null
|
||||||
|
|
||||||
|
return CMCDProps(
|
||||||
|
cmcdObject = parseKeyValuePairs(src.getArray(PROP_CMCD_OBJECT)),
|
||||||
|
cmcdRequest = parseKeyValuePairs(src.getArray(PROP_CMCD_REQUEST)),
|
||||||
|
cmcdSession = parseKeyValuePairs(src.getArray(PROP_CMCD_SESSION)),
|
||||||
|
cmcdStatus = parseKeyValuePairs(src.getArray(PROP_CMCD_STATUS)),
|
||||||
|
mode = safeGetInt(src, PROP_CMCD_MODE, 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseKeyValuePairs(array: ReadableArray?): List<Pair<String, Any>> {
|
||||||
|
if (array == null) return emptyList()
|
||||||
|
|
||||||
|
return (0 until array.size()).mapNotNull { i ->
|
||||||
|
val item = array.getMap(i)
|
||||||
|
val key = item?.getString("key")
|
||||||
|
val value = when (item?.getType("value")) {
|
||||||
|
ReadableType.Number -> item.getDouble("value")
|
||||||
|
ReadableType.String -> item.getString("value")
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != null && value != null) Pair(key, value) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -57,6 +57,11 @@ class Source {
|
|||||||
*/
|
*/
|
||||||
var textTracksAllowChunklessPreparation: Boolean = false
|
var textTracksAllowChunklessPreparation: Boolean = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CMCD properties linked to the source
|
||||||
|
*/
|
||||||
|
var cmcdProps: CMCDProps? = null
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(uriString, uri, startPositionMs, cropStartMs, cropEndMs, extension, metadata, headers)
|
override fun hashCode(): Int = Objects.hash(uriString, uri, startPositionMs, cropStartMs, cropEndMs, extension, metadata, headers)
|
||||||
|
|
||||||
/** return true if this and src are equals */
|
/** return true if this and src are equals */
|
||||||
@ -68,7 +73,8 @@ class Source {
|
|||||||
cropEndMs == other.cropEndMs &&
|
cropEndMs == other.cropEndMs &&
|
||||||
startPositionMs == other.startPositionMs &&
|
startPositionMs == other.startPositionMs &&
|
||||||
extension == other.extension &&
|
extension == other.extension &&
|
||||||
drmProps == other.drmProps
|
drmProps == other.drmProps &&
|
||||||
|
cmcdProps == other.cmcdProps
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +137,7 @@ class Source {
|
|||||||
private const val PROP_SRC_METADATA = "metadata"
|
private const val PROP_SRC_METADATA = "metadata"
|
||||||
private const val PROP_SRC_HEADERS = "requestHeaders"
|
private const val PROP_SRC_HEADERS = "requestHeaders"
|
||||||
private const val PROP_SRC_DRM = "drm"
|
private const val PROP_SRC_DRM = "drm"
|
||||||
|
private const val PROP_SRC_CMCD = "cmcd"
|
||||||
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"
|
private const val PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION = "textTracksAllowChunklessPreparation"
|
||||||
|
|
||||||
@SuppressLint("DiscouragedApi")
|
@SuppressLint("DiscouragedApi")
|
||||||
@ -189,6 +196,7 @@ class Source {
|
|||||||
source.cropEndMs = safeGetInt(src, PROP_SRC_CROP_END, -1)
|
source.cropEndMs = safeGetInt(src, PROP_SRC_CROP_END, -1)
|
||||||
source.extension = safeGetString(src, PROP_SRC_TYPE, null)
|
source.extension = safeGetString(src, PROP_SRC_TYPE, null)
|
||||||
source.drmProps = parse(safeGetMap(src, PROP_SRC_DRM))
|
source.drmProps = parse(safeGetMap(src, PROP_SRC_DRM))
|
||||||
|
source.cmcdProps = CMCDProps.parse(safeGetMap(src, PROP_SRC_CMCD))
|
||||||
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)
|
source.textTracksAllowChunklessPreparation = safeGetBool(src, PROP_SRC_TEXT_TRACKS_ALLOW_CHUNKLESS_PREPARATION, true)
|
||||||
|
|
||||||
val propSrcHeadersArray = safeGetArray(src, PROP_SRC_HEADERS)
|
val propSrcHeadersArray = safeGetArray(src, PROP_SRC_HEADERS)
|
||||||
|
41
android/src/main/java/com/brentvatne/exoplayer/CMCDConfig.kt
Normal file
41
android/src/main/java/com/brentvatne/exoplayer/CMCDConfig.kt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package com.brentvatne.exoplayer
|
||||||
|
|
||||||
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.exoplayer.upstream.CmcdConfiguration
|
||||||
|
import com.brentvatne.common.api.CMCDProps
|
||||||
|
import com.google.common.collect.ImmutableListMultimap
|
||||||
|
|
||||||
|
class CMCDConfig(private val props: CMCDProps) {
|
||||||
|
fun toCmcdConfigurationFactory(): CmcdConfiguration.Factory = CmcdConfiguration.Factory(::createCmcdConfiguration)
|
||||||
|
|
||||||
|
private fun createCmcdConfiguration(mediaItem: MediaItem): CmcdConfiguration =
|
||||||
|
CmcdConfiguration(
|
||||||
|
java.util.UUID.randomUUID().toString(),
|
||||||
|
mediaItem.mediaId,
|
||||||
|
object : CmcdConfiguration.RequestConfig {
|
||||||
|
override fun getCustomData(): ImmutableListMultimap<String, String> = buildCustomData()
|
||||||
|
},
|
||||||
|
props.mode
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun buildCustomData(): ImmutableListMultimap<String, String> =
|
||||||
|
ImmutableListMultimap.builder<String, String>().apply {
|
||||||
|
addFormattedData(this, CmcdConfiguration.KEY_CMCD_OBJECT, props.cmcdObject)
|
||||||
|
addFormattedData(this, CmcdConfiguration.KEY_CMCD_REQUEST, props.cmcdRequest)
|
||||||
|
addFormattedData(this, CmcdConfiguration.KEY_CMCD_SESSION, props.cmcdSession)
|
||||||
|
addFormattedData(this, CmcdConfiguration.KEY_CMCD_STATUS, props.cmcdStatus)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
private fun addFormattedData(builder: ImmutableListMultimap.Builder<String, String>, key: String, dataList: List<Pair<String, Any>>) {
|
||||||
|
dataList.forEach { (dataKey, dataValue) ->
|
||||||
|
builder.put(key, formatKeyValue(dataKey, dataValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatKeyValue(key: String, value: Any): String =
|
||||||
|
when (value) {
|
||||||
|
is String -> "$key=\"$value\""
|
||||||
|
is Number -> "$key=$value"
|
||||||
|
else -> throw IllegalArgumentException("Unsupported value type: ${value::class.java}")
|
||||||
|
}
|
||||||
|
}
|
@ -96,6 +96,7 @@ import androidx.media3.exoplayer.trackselection.MappingTrackSelector;
|
|||||||
import androidx.media3.exoplayer.trackselection.TrackSelection;
|
import androidx.media3.exoplayer.trackselection.TrackSelection;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
|
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
import androidx.media3.exoplayer.util.EventLogger;
|
import androidx.media3.exoplayer.util.EventLogger;
|
||||||
@ -269,6 +270,12 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
|
|
||||||
private String instanceId = String.valueOf(UUID.randomUUID());
|
private String instanceId = String.valueOf(UUID.randomUUID());
|
||||||
|
|
||||||
|
private CmcdConfiguration.Factory cmcdConfigurationFactory;
|
||||||
|
|
||||||
|
public void setCmcdConfigurationFactory(CmcdConfiguration.Factory factory) {
|
||||||
|
this.cmcdConfigurationFactory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateProgress() {
|
private void updateProgress() {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (playerControlView != null && isPlayingAd() && controls) {
|
if (playerControlView != null && isPlayingAd() && controls) {
|
||||||
@ -1103,6 +1110,12 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmcdConfigurationFactory != null) {
|
||||||
|
mediaSourceFactory = mediaSourceFactory.setCmcdConfigurationFactory(
|
||||||
|
cmcdConfigurationFactory::createCmcdConfiguration
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
MediaItem mediaItem = mediaItemBuilder.setStreamKeys(streamKeys).build();
|
MediaItem mediaItem = mediaItemBuilder.setStreamKeys(streamKeys).build();
|
||||||
MediaSource mediaSource = mediaSourceFactory
|
MediaSource mediaSource = mediaSourceFactory
|
||||||
.setDrmSessionManagerProvider(drmProvider)
|
.setDrmSessionManagerProvider(drmProvider)
|
||||||
@ -1800,6 +1813,14 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
DataSourceUtil.getDefaultDataSourceFactory(this.themedReactContext, bandwidthMeter,
|
||||||
source.getHeaders());
|
source.getHeaders());
|
||||||
|
|
||||||
|
if (source.getCmcdProps() != null) {
|
||||||
|
CMCDConfig cmcdConfig = new CMCDConfig(source.getCmcdProps());
|
||||||
|
CmcdConfiguration.Factory factory = cmcdConfig.toCmcdConfigurationFactory();
|
||||||
|
this.setCmcdConfigurationFactory(factory);
|
||||||
|
} else {
|
||||||
|
this.setCmcdConfigurationFactory(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isSourceEqual) {
|
if (!isSourceEqual) {
|
||||||
reloadSource();
|
reloadSource();
|
||||||
}
|
}
|
||||||
|
@ -1000,3 +1000,68 @@ Adjust the volume.
|
|||||||
- **1.0 (default)** - Play at full volume
|
- **1.0 (default)** - Play at full volume
|
||||||
- **0.0** - Mute the audio
|
- **0.0** - Mute the audio
|
||||||
- **Other values** - Reduce volume
|
- **Other values** - Reduce volume
|
||||||
|
|
||||||
|
### `cmcd`
|
||||||
|
|
||||||
|
<PlatformsList types={['Android']} />
|
||||||
|
|
||||||
|
Configure CMCD (Common Media Client Data) parameters. CMCD is a standard for conveying client-side metrics and capabilities to servers, which can help improve streaming quality and performance.
|
||||||
|
|
||||||
|
For detailed information about CMCD, please refer to the [CTA-5004 Final Specification](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).
|
||||||
|
|
||||||
|
- **false (default)** - Don't use CMCD
|
||||||
|
- **true** - Use default CMCD configuration
|
||||||
|
- **object** - Use custom CMCD configuration
|
||||||
|
|
||||||
|
When providing an object, you can configure the following properties:
|
||||||
|
|
||||||
|
| Property | Type | Description |
|
||||||
|
|----------|-------------------------|----------------------------------------------------|
|
||||||
|
| `mode` | `CmcdMode` | The mode for sending CMCD data |
|
||||||
|
| `request` | `CmcdData` | Custom key-value pairs for the request object |
|
||||||
|
| `session` | `CmcdData` | Custom key-value pairs for the session object |
|
||||||
|
| `object` | `CmcdData` | Custom key-value pairs for the object metadata |
|
||||||
|
| `status` | `CmcdData` | Custom key-value pairs for the status information |
|
||||||
|
|
||||||
|
Note: The `mode` property defaults to `CmcdMode.MODE_QUERY_PARAMETER` if not specified.
|
||||||
|
|
||||||
|
#### `CmcdMode`
|
||||||
|
CmcdMode is an enum that defines how CMCD data should be sent:
|
||||||
|
- `CmcdMode.MODE_REQUEST_HEADER` (0) - Send CMCD data in the HTTP request headers.
|
||||||
|
- `CmcdMode.MODE_QUERY_PARAMETER` (1) - Send CMCD data as query parameters in the URL.
|
||||||
|
|
||||||
|
#### `CmcdData`
|
||||||
|
CmcdData is a type representing custom key-value pairs for CMCD data. It's defined as:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
type CmcdData = Record<`${string}-${string}`, string | number>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Custom key names MUST include a hyphenated prefix to prevent namespace collisions. It's recommended to use a reverse-DNS syntax for custom prefixes.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
<Video
|
||||||
|
source={{
|
||||||
|
uri: 'https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8',
|
||||||
|
cmcd: {
|
||||||
|
mode: CmcdMode.MODE_QUERY_PARAMETER,
|
||||||
|
request: {
|
||||||
|
'com-custom-key': 'custom-value'
|
||||||
|
},
|
||||||
|
session: {
|
||||||
|
sid: 'session-id'
|
||||||
|
},
|
||||||
|
object: {
|
||||||
|
br: '3000',
|
||||||
|
d: '4000'
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
rtp: '1200'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
// or other video props
|
||||||
|
/>
|
||||||
|
```
|
@ -16,7 +16,9 @@ import type {
|
|||||||
ImageResizeMode,
|
ImageResizeMode,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import NativeVideoComponent from './specs/VideoNativeComponent';
|
import NativeVideoComponent, {
|
||||||
|
NativeCmcdConfiguration,
|
||||||
|
} from './specs/VideoNativeComponent';
|
||||||
import type {
|
import type {
|
||||||
OnAudioFocusChangedData,
|
OnAudioFocusChangedData,
|
||||||
OnAudioTracksData,
|
OnAudioTracksData,
|
||||||
@ -44,12 +46,13 @@ import {
|
|||||||
} from './utils';
|
} from './utils';
|
||||||
import NativeVideoManager from './specs/NativeVideoManager';
|
import NativeVideoManager from './specs/NativeVideoManager';
|
||||||
import type {VideoSaveData} from './specs/NativeVideoManager';
|
import type {VideoSaveData} from './specs/NativeVideoManager';
|
||||||
import {ViewType} from './types';
|
import {CmcdMode, ViewType} from './types';
|
||||||
import type {
|
import type {
|
||||||
OnLoadData,
|
OnLoadData,
|
||||||
OnTextTracksData,
|
OnTextTracksData,
|
||||||
OnReceiveAdEventData,
|
OnReceiveAdEventData,
|
||||||
ReactVideoProps,
|
ReactVideoProps,
|
||||||
|
CmcdData,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export interface VideoRef {
|
export interface VideoRef {
|
||||||
@ -176,6 +179,30 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
multiDrm: selectedDrm.multiDrm,
|
multiDrm: selectedDrm.multiDrm,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let _cmcd: NativeCmcdConfiguration | undefined;
|
||||||
|
if (Platform.OS === 'android' && source?.cmcd) {
|
||||||
|
const cmcd = source.cmcd;
|
||||||
|
|
||||||
|
if (typeof cmcd === 'boolean') {
|
||||||
|
_cmcd = cmcd ? {mode: CmcdMode.MODE_QUERY_PARAMETER} : undefined;
|
||||||
|
} else if (typeof cmcd === 'object' && !Array.isArray(cmcd)) {
|
||||||
|
const createCmcdHeader = (property?: CmcdData) =>
|
||||||
|
property ? generateHeaderForNative(property) : undefined;
|
||||||
|
|
||||||
|
_cmcd = {
|
||||||
|
mode: cmcd.mode ?? CmcdMode.MODE_QUERY_PARAMETER,
|
||||||
|
request: createCmcdHeader(cmcd.request),
|
||||||
|
session: createCmcdHeader(cmcd.session),
|
||||||
|
object: createCmcdHeader(cmcd.object),
|
||||||
|
status: createCmcdHeader(cmcd.status),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
'Invalid CMCD configuration: Expected a boolean or an object.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
uri,
|
uri,
|
||||||
isNetwork,
|
isNetwork,
|
||||||
@ -190,6 +217,7 @@ const Video = forwardRef<VideoRef, ReactVideoProps>(
|
|||||||
cropEnd: resolvedSource.cropEnd,
|
cropEnd: resolvedSource.cropEnd,
|
||||||
metadata: resolvedSource.metadata,
|
metadata: resolvedSource.metadata,
|
||||||
drm: _drm,
|
drm: _drm,
|
||||||
|
cmcd: _cmcd,
|
||||||
textTracksAllowChunklessPreparation:
|
textTracksAllowChunklessPreparation:
|
||||||
resolvedSource.textTracksAllowChunklessPreparation,
|
resolvedSource.textTracksAllowChunklessPreparation,
|
||||||
};
|
};
|
||||||
|
@ -40,6 +40,7 @@ export type VideoSrc = Readonly<{
|
|||||||
cropEnd?: Float;
|
cropEnd?: Float;
|
||||||
metadata?: VideoMetadata;
|
metadata?: VideoMetadata;
|
||||||
drm?: Drm;
|
drm?: Drm;
|
||||||
|
cmcd?: NativeCmcdConfiguration; // android
|
||||||
textTracksAllowChunklessPreparation?: boolean; // android
|
textTracksAllowChunklessPreparation?: boolean; // android
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
@ -61,6 +62,15 @@ type Drm = Readonly<{
|
|||||||
multiDrm?: WithDefault<boolean, false>; // android
|
multiDrm?: WithDefault<boolean, false>; // android
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
type CmcdMode = WithDefault<Int32, 1>;
|
||||||
|
export type NativeCmcdConfiguration = Readonly<{
|
||||||
|
mode?: CmcdMode; // default: MODE_QUERY_PARAMETER
|
||||||
|
request?: Headers;
|
||||||
|
session?: Headers;
|
||||||
|
object?: Headers;
|
||||||
|
status?: Headers;
|
||||||
|
}>;
|
||||||
|
|
||||||
type TextTracks = ReadonlyArray<
|
type TextTracks = ReadonlyArray<
|
||||||
Readonly<{
|
Readonly<{
|
||||||
title: string;
|
title: string;
|
||||||
|
@ -33,6 +33,7 @@ export type ReactVideoSourceProperties = {
|
|||||||
cropEnd?: number;
|
cropEnd?: number;
|
||||||
metadata?: VideoMetadata;
|
metadata?: VideoMetadata;
|
||||||
drm?: Drm;
|
drm?: Drm;
|
||||||
|
cmcd?: Cmcd; // android
|
||||||
textTracksAllowChunklessPreparation?: boolean;
|
textTracksAllowChunklessPreparation?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,6 +88,27 @@ export type Drm = Readonly<{
|
|||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export enum CmcdMode {
|
||||||
|
MODE_REQUEST_HEADER = 0,
|
||||||
|
MODE_QUERY_PARAMETER = 1,
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Custom key names MUST carry a hyphenated prefix to ensure that there will not be a
|
||||||
|
* namespace collision with future revisions to this specification. Clients SHOULD
|
||||||
|
* use a reverse-DNS syntax when defining their own prefix.
|
||||||
|
*
|
||||||
|
* @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf CTA-5004 Specification (Page 6, Section 3.1)
|
||||||
|
*/
|
||||||
|
export type CmcdData = Record<`${string}-${string}`, string | number>;
|
||||||
|
export type CmcdConfiguration = Readonly<{
|
||||||
|
mode?: CmcdMode; // default: MODE_QUERY_PARAMETER
|
||||||
|
request?: CmcdData;
|
||||||
|
session?: CmcdData;
|
||||||
|
object?: CmcdData;
|
||||||
|
status?: CmcdData;
|
||||||
|
}>;
|
||||||
|
export type Cmcd = boolean | CmcdConfiguration;
|
||||||
|
|
||||||
export enum BufferingStrategyType {
|
export enum BufferingStrategyType {
|
||||||
DEFAULT = 'Default',
|
DEFAULT = 'Default',
|
||||||
DISABLE_BUFFERING = 'DisableBuffering',
|
DISABLE_BUFFERING = 'DisableBuffering',
|
||||||
|
Loading…
Reference in New Issue
Block a user