Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement BufferAPI via Player.buffer #298

Merged
merged 54 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
033b227
feat(docs): update docs to use jsdoc references
123mpozzi Oct 17, 2023
90e0e4c
feat(bufferapi): implement bufferApi interface and types
123mpozzi Oct 17, 2023
1204768
feat(bufferapi): fix bufferapi imports
123mpozzi Oct 17, 2023
c3dbb47
feat(docs): update and fix docs formatting and referencing
123mpozzi Oct 17, 2023
7a961e9
feat(bufferapi): add buffer module for android
123mpozzi Oct 18, 2023
2c73d75
feat(bufferapi): add buffer api
123mpozzi Oct 18, 2023
0981e22
feat(formatting): apply ktlint rules
123mpozzi Oct 18, 2023
f65ecc7
feat(docs): fix typedocs references
123mpozzi Oct 18, 2023
acd6b8e
docs(bufferapi): add missing docs and improve formatting
123mpozzi Oct 18, 2023
68182ed
feat(bufferapi): add buffer module for ios
123mpozzi Oct 19, 2023
0c1660d
docs(bufferapi): fix docs in bufferApi.ts
123mpozzi Oct 19, 2023
18f5b05
feat(bufferapi): codestyle improvements
123mpozzi Oct 19, 2023
cde574d
feat(docs): remove docs changes
123mpozzi Oct 19, 2023
fb5b4b4
feat(docs): remove docs changes
123mpozzi Oct 19, 2023
ebcacca
feat(docs): remove docs changes
123mpozzi Oct 19, 2023
2ea57b4
docs(bufferapi): fix documentation
123mpozzi Oct 19, 2023
5e50a0e
feat(merge): merge commit 'c68292bf05b4f30c4a6146ba4db4401dbaec6827' …
123mpozzi Oct 19, 2023
5c7f68e
feat(bufferapi): fix swiftlint warnings
123mpozzi Oct 19, 2023
a33b50c
feat(bufferapi): add CHANGELOG entry
123mpozzi Oct 19, 2023
5cae0fe
feat(bufferapi): fix buffer module location
123mpozzi Oct 19, 2023
b0ca817
feat(bufferapi): fix `setTargetLevel` on iOS
123mpozzi Oct 19, 2023
d17254f
feat(bufferapi): merge commit 'cc32c2ee92b8f2b882118574dd3dcfdebe3f2c…
123mpozzi Oct 19, 2023
f578105
feat(bufferapi): move RN only types to the bottom of the Kotlin file
123mpozzi Oct 20, 2023
fb87736
feat(bufferapi): move RN-only types on the bottom of `RCTConvert+Bitm…
123mpozzi Oct 20, 2023
6e920fe
feat(bufferapi): fix docs
123mpozzi Oct 20, 2023
46d5e93
feat(bufferapi): nit docs
123mpozzi Oct 20, 2023
64ff2b6
feat(bufferapi): update docs on specific iOS behaviour
123mpozzi Oct 20, 2023
f270ddd
feat(bufferapi): add tvOS in BufferMediaTypeConfig docs
123mpozzi Oct 20, 2023
b94cbb6
feat(bufferapi): update docs with tvOS too
123mpozzi Oct 20, 2023
5781d6f
feat(bufferapi): merge commit 'c432d32c333281765d30db8eeced884b011c22…
123mpozzi Oct 20, 2023
5c0d1c3
feat(bufferapi): move RNBufferLevels to correct place
123mpozzi Oct 20, 2023
ed8ab2f
feat(bufferapi): fix `RNBufferLevels` imports
123mpozzi Oct 20, 2023
eeda724
feat(bufferapi): make getter one-line
123mpozzi Oct 20, 2023
cabdb30
feat(bufferapi): remove docs from module `getName` as it is explicit …
123mpozzi Oct 20, 2023
e24d3f7
feat(bufferapi): fix docs
123mpozzi Oct 20, 2023
0800385
feat(bufferapi): make getter one-line
123mpozzi Oct 20, 2023
b54a7ac
feat(bufferapi): remove docs from `RNBufferLevels`
123mpozzi Oct 20, 2023
de6762a
feat(bufferapi): simplify entry on CHANGELOG
123mpozzi Oct 20, 2023
6e7ca13
feat(bufferapi): map bufferType to Strings instead of Int
123mpozzi Oct 23, 2023
5b8f449
feat(bufferapi): update bufferApi with bufferMedia mapping using Str…
123mpozzi Oct 23, 2023
a0b00ce
feat(bufferapi): remove optionals when not needed on swift
123mpozzi Oct 23, 2023
24f6205
feat(bufferapi): add `toMediaType` on Android
123mpozzi Oct 23, 2023
245d413
feat(bufferapi): remove optionals when not needed on Android
123mpozzi Oct 23, 2023
f868d9d
feat(bufferapi): fix swiftlint warnings
123mpozzi Oct 23, 2023
1aa5620
feat(bufferapi): fix doc return value
123mpozzi Oct 23, 2023
eb22eed
feat(bufferapi): fix unnecessary check on safe call on Android
123mpozzi Oct 23, 2023
a9af0b6
feat(bufferapi): add new docs to BufferLevels
123mpozzi Oct 23, 2023
96d27e2
feat(bufferapi): update docs of `BufferLevels`
123mpozzi Oct 23, 2023
8f4340e
feat(bufferapi): remove nullable in `fromMediaType`
123mpozzi Oct 24, 2023
d2ff9ea
feat(bufferapi): make `toMediaType` and `toBufferType` nullable
123mpozzi Oct 24, 2023
0ada2f8
feat(bufferapi): fix docs
123mpozzi Oct 24, 2023
18ecdcb
feat(bufferapi): make `toBufferType` nullable in Swift
123mpozzi Oct 24, 2023
539e8e8
Merge commit 'c965354c328fbed58b55dae8702fd6c1955a4da9' into bufferap…
123mpozzi Oct 25, 2023
6f38a5b
feat(bufferapi): remove nullable in `fromBufferType`
123mpozzi Oct 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- `PlayerView.isPictureInPictureRequested` to programatically create a Picture in Picture request
- `PlayerView.scalingMode` to allow changing the video scaling mode
- `PlayerViewConfig` with a `UiConfig` to the `PlayerView` to enable configuration of the visual presentation and behaviour
- `Player.buffer` namespace to control buffer preferences and to query the current buffer state.
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.bitmovin.player.reactnative

import com.bitmovin.player.api.buffer.BufferLevel
import com.bitmovin.player.api.media.MediaType
import com.bitmovin.player.reactnative.converter.JsonConverter
import com.facebook.react.bridge.*
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.UIManagerModule

private const val MODULE_NAME = "BufferModule"

@ReactModule(name = MODULE_NAME)
class BufferModule(private val context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
/**
* Collection of [BufferLevel] objects
* @param audio [BufferLevel] for [MediaType.Audio].
* @param video [BufferLevel] for [MediaType.Video].
*/
data class RNBufferLevels(val audio: BufferLevel, val video: BufferLevel)
rolandkakonyi marked this conversation as resolved.
Show resolved Hide resolved

/**
* JS exported module name.
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
*/
override fun getName() = MODULE_NAME

/**
* Gets the [buffer level][BufferLevel] from the Player
* @param nativeId Target player id.
* @param type The [type of buffer][JsonConverter.toBufferType] to return the level for.
* @param promise JS promise object.
*/
@ReactMethod
fun getLevel(nativeId: NativeId, type: Int, promise: Promise) {
uiManager()?.addUIBlock { _ ->
val player = playerModule()?.getPlayer(nativeId) ?: return@addUIBlock
val bufferType = JsonConverter.toBufferType(type)
val bufferLevels = RNBufferLevels(
player.buffer.getLevel(bufferType, MediaType.Audio),
player.buffer.getLevel(bufferType, MediaType.Video),
)
JsonConverter.fromRNBufferLevels(bufferLevels)?.let {
promise.resolve(it)
}
}
}

/**
* Sets the target buffer level for the chosen buffer type across all media types.
* @param nativeId Target player id.
* @param type The type of the buffer to set the target level for.
* @param value The value to set.
*/
@ReactMethod
fun setTargetLevel(nativeId: NativeId, type: Int, value: Double) {
uiManager()?.addUIBlock { _ ->
val player = playerModule()?.getPlayer(nativeId) ?: return@addUIBlock
val bufferType = JsonConverter.toBufferType(type)
player.buffer.setTargetLevel(bufferType, value)
}
}

/**
* Helper function that gets the instantiated `UIManagerModule` from modules registry.
*/
private fun uiManager(): UIManagerModule? =
context.getNativeModule(UIManagerModule::class.java)
123mpozzi marked this conversation as resolved.
Show resolved Hide resolved

/**
* Helper function that gets the instantiated `PlayerModule` from modules registry.
*/
private fun playerModule(): PlayerModule? =
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
context.getNativeModule(PlayerModule::class.java)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class RNPlayerViewPackage : ReactPackage {
FullscreenHandlerModule(reactContext),
CustomMessageHandlerModule(reactContext),
BitmovinCastManagerModule(reactContext),
BufferModule(reactContext),
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@ import com.bitmovin.player.api.advertising.AdSource
import com.bitmovin.player.api.advertising.AdSourceType
import com.bitmovin.player.api.advertising.AdvertisingConfig
import com.bitmovin.player.api.buffer.BufferConfig
import com.bitmovin.player.api.buffer.BufferLevel
import com.bitmovin.player.api.buffer.BufferMediaTypeConfig
import com.bitmovin.player.api.buffer.BufferType
import com.bitmovin.player.api.casting.RemoteControlConfig
import com.bitmovin.player.api.drm.WidevineConfig
import com.bitmovin.player.api.event.PlayerEvent
import com.bitmovin.player.api.event.SourceEvent
import com.bitmovin.player.api.event.data.CastPayload
import com.bitmovin.player.api.event.data.SeekPosition
import com.bitmovin.player.api.media.AdaptationConfig
import com.bitmovin.player.api.media.MediaType
import com.bitmovin.player.api.media.audio.AudioTrack
import com.bitmovin.player.api.media.subtitle.SubtitleTrack
import com.bitmovin.player.api.media.thumbnail.Thumbnail
Expand All @@ -44,6 +47,7 @@ import com.bitmovin.player.api.ui.ScalingMode
import com.bitmovin.player.api.ui.StyleConfig
import com.bitmovin.player.api.ui.UiConfig
import com.bitmovin.player.reactnative.BitmovinCastManagerOptions
import com.bitmovin.player.reactnative.BufferModule
import com.bitmovin.player.reactnative.extensions.getBooleanOrNull
import com.bitmovin.player.reactnative.extensions.getName
import com.bitmovin.player.reactnative.extensions.getOrDefault
Expand Down Expand Up @@ -1160,6 +1164,59 @@ class JsonConverter {
?: true,
),
)

@JvmStatic
fun fromBufferLevel(bufferLevel: BufferLevel?): WritableMap? {
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
if (bufferLevel == null) {
return null
}
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved

return Arguments.createMap().apply {
putDouble("level", bufferLevel.level)
putDouble("targetLevel", bufferLevel.targetLevel)
putInt(
"media",
when (bufferLevel.media) {
MediaType.Audio -> 0
MediaType.Video -> 1
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
},
)
putInt(
"type",
when (bufferLevel.type) {
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
BufferType.ForwardDuration -> 0
BufferType.BackwardDuration -> 1
},
)
}
}

@JvmStatic
fun fromRNBufferLevels(bufferLevels: BufferModule.RNBufferLevels?): WritableMap? {
if (bufferLevels == null) {
return null
}

return Arguments.createMap().apply {
putMap("audio", fromBufferLevel(bufferLevels.audio))
putMap("video", fromBufferLevel(bufferLevels.video))
}
}

/**
* Maps an integer value into the corresponding [BufferType] value.
*
* Currently `0` is mapped to [BufferType.ForwardDuration], and `1` to [BufferType.BackwardDuration].
* Other values fall back to [BufferType.ForwardDuration]
* @param value Integer value representing the [BufferType].
* @return The [BufferType] corresponding to [value].
*/
zigavehovec marked this conversation as resolved.
Show resolved Hide resolved
@JvmStatic
fun toBufferType(value: Int?): BufferType = when (value) {
0 -> BufferType.ForwardDuration
1 -> BufferType.BackwardDuration
else -> BufferType.ForwardDuration
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions ios/BufferModule.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_REMAP_MODULE(BufferModule, BufferModule, NSObject)

RCT_EXTERN_METHOD(getLevel:(NSString *)nativeId type:(nonnull NSNumber *)type resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(setTargetLevel:(NSString *)nativeId type:(nonnull NSNumber *)type value:(nonnull NSNumber *)value)

@end
89 changes: 89 additions & 0 deletions ios/BufferModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import BitmovinPlayer

@objc(BufferModule)
public class BufferModule: NSObject, RCTBridgeModule {
/**
* Collection of `BufferLevel` objects
* - Parameter audio: `BufferLevel` for `MediaType.Audio`.
* - Parameter video: `BufferLevel` for `MediaType.Video`.
*/
internal struct RNBufferLevels {
let audio: BufferLevel
let video: BufferLevel
}
rolandkakonyi marked this conversation as resolved.
Show resolved Hide resolved

// swiftlint:disable:next implicitly_unwrapped_optional
@objc public var bridge: RCTBridge!

/// PlayerModule instance fetched from the bridge's registry
@objc var playerModule: PlayerModule? {
bridge.module(for: PlayerModule.self) as? PlayerModule
}

// swiftlint:disable:next implicitly_unwrapped_optional
public static func moduleName() -> String! {
"BufferModule"
}

/// Module requires main thread initialization.
public static func requiresMainQueueSetup() -> Bool {
true
}

// swiftlint:disable:next implicitly_unwrapped_optional
public var methodQueue: DispatchQueue! {
bridge.uiManager.methodQueue
}

/**
- Gets the `BufferLevel` from the Player
- Parameter nativeId: Native Id of the the player instance.
- Parameter type: The type of buffer to return the level for.
- Parameter resolver: JS promise resolver.
- Parameter rejecter: JS promise rejecter.
*/
@objc(getLevel:type:resolver:rejecter:)
func getLevel(
_ playerId: NativeId,
type: NSNumber,
resolver resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock
) {
bridge.uiManager.addUIBlock { [weak self] _, _ in
guard let bufferApi = self?.playerModule?.retrieve(playerId)?.buffer else {
reject("[BufferModule]", "Could not find player with ID (\(playerId))", nil)
return
}
guard let bufferType = RCTConvert.bufferType(type) else {
reject("[BufferModule]", "Invalid buffer type", nil)
return
}

let level = bufferApi.getLevel(bufferType)
let bufferLevels = RNBufferLevels(audio: level, video: level)
resolve(RCTConvert.toJson(bufferLevels: bufferLevels))
}
}

/**
* Sets the target level in seconds for the forward buffer.
- Parameter nativeId: Target player id.
- Parameter type: The type of the buffer to set the target level for.
- Parameter value: The value to set.
*/
@objc(setTargetLevel:type:value:)
func setTargetLevel(_ playerId: NativeId, type: NSNumber, value: NSNumber) {
let targetLevel = value.doubleValue
bridge.uiManager.addUIBlock { [weak self] _, _ in
guard
let bufferApi = self?.playerModule?.retrieve(playerId)?.buffer,
let bufferType = RCTConvert.bufferType(type),
bufferType == .forwardDuration
else {
return
}

bufferApi.setTargetLevel(targetLevel)
}
}
}
65 changes: 65 additions & 0 deletions ios/RCTConvert+BitmovinPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1138,6 +1138,71 @@ extension RCTConvert {
)
)
}

/**
* Utility method to instantiate a `BufferType` from an Integer.
* Currently `0` is mapped to `BufferType.forwardDuration`, and `1` to `BufferType.backwardDuration`.
* Other values fall back to `BufferType.forwardDuration`
* - Parameter value: Integer value representing the `BufferType`.
* - Returns: The `BufferType` corresponding to `value`.
*/
static func bufferType(_ value: NSNumber?) -> BufferType? {
guard let value = value as? Int else {
return nil
}

switch value {
case 0:
return .forwardDuration
case 1:
return .backwardDuration
default:
return .forwardDuration
}
}

/**
Utility method to get a json dictionary value from a `BufferLevel` object.
- Parameter bufferLevel: The `BufferLevel` to convert to json format.
- Parameter mediaType: The `MediaType` value to pass through.
- Returns: The generated json dictionary.
*/
static func toJson(bufferLevel: BufferLevel?, mediaType: Int) -> [String: Any]? {
guard let bufferLevel else {
return nil
}

let type: Int
switch bufferLevel.type {
case .forwardDuration:
type = 0
case .backwardDuration:
type = 1
}

return [
"level": bufferLevel.level,
"targetLevel": bufferLevel.targetLevel,
"media": mediaType,
"type": type
]
}

/**
Utility method to get a json dictionary value from a `BufferModule.RNBufferLevels` object.
- Parameter bufferLevels: The `BufferModule.RNBufferLevels` to convert to json format.
- Returns: The generated json dictionary.
*/
static func toJson(bufferLevels: BufferModule.RNBufferLevels?) -> [String: Any]? {
guard let bufferLevels else {
return nil
}

return [
"audio": toJson(bufferLevel: bufferLevels.audio, mediaType: 0),
"video": toJson(bufferLevel: bufferLevels.video, mediaType: 1),
]
}
}
/**
* React native specific PlayerViewConfig.
Expand Down
Loading