feat: add more basic functionality (#5)

This commit is contained in:
Krzysztof Moch
2025-04-16 22:28:25 +02:00
committed by GitHub
parent 950dde6a7d
commit 047d74de38
15 changed files with 506 additions and 29 deletions
@@ -263,14 +263,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-frameworks.sh\"\n";
@@ -306,14 +302,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-VideoExample/Pods-VideoExample-resources.sh\"\n";
@@ -523,7 +515,10 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -592,7 +587,10 @@
"-DFOLLY_CFG_NO_COROUTINES=1",
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
);
OTHER_LDFLAGS = "$(inherited) ";
OTHER_LDFLAGS = (
"$(inherited)",
" ",
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../../node_modules/react-native";
SDKROOT = iphoneos;
USE_HERMES = true;
+8 -2
View File
@@ -12,7 +12,12 @@ import { VideoView, createSource, useVideoPlayer } from 'react-native-video';
const VideoDemo = () => {
const [show, setShow] = React.useState(false);
const player = useVideoPlayer('https://www.w3schools.com/html/mov_bbb.mp4');
const player = useVideoPlayer(
'https://www.w3schools.com/html/mov_bbb.mp4',
(_player) => {
_player.loop = true;
}
);
return (
<View style={styles.container}>
@@ -27,7 +32,8 @@ const VideoDemo = () => {
</View>
<Button title="Play" onPress={() => player.play()} />
<Button title="Pause" onPress={() => player.pause()} />
<Button title="Seek to 3sec" onPress={() => (player.currentTime = 3)} />
<Button title="Seek to 0sec" onPress={() => player.seekTo(0)} />
<Button title="Seek by 1sec" onPress={() => player.seekBy(1)} />
<Button
title={show ? 'Hide VideoView' : 'Show VideoView'}
onPress={() => setShow((prev) => !prev)}
@@ -12,9 +12,10 @@ import com.margelo.nitro.NitroModules
import com.margelo.nitro.core.Promise
import com.video.core.LibraryError
import com.video.core.PlayerError
import com.video.core.SourceError
import com.video.core.utils.Threading.runOnMainThread
import com.video.core.utils.Threading.runOnMainThreadSync
import kotlin.math.max
import kotlin.math.min
@UnstableApi
@DoNotStrip
@@ -51,9 +52,15 @@ class HybridVideoPlayer() : HybridVideoPlayerSpec() {
get() = runOnMainThreadSync { return@runOnMainThreadSync playerPointer.currentPosition.toDouble() / 1000.0 }
set(value) = runOnMainThread { playerPointer.seekTo((value * 1000).toLong()) }
// volume defined by user
var userVolume: Double = 1.0
override var volume: Double
get() = runOnMainThreadSync { return@runOnMainThreadSync playerPointer.volume.toDouble() }
set(value) = runOnMainThread { playerPointer.volume = value.toFloat() }
set(value) = runOnMainThread {
userVolume = value
playerPointer.volume = value.toFloat()
}
override val duration: Double
get() {
@@ -61,6 +68,36 @@ class HybridVideoPlayer() : HybridVideoPlayerSpec() {
return if (duration == C.TIME_UNSET) Double.NaN else duration.toDouble() / 1000.0
}
override var loop: Boolean
get() {
val repeatMode = runOnMainThreadSync { return@runOnMainThreadSync playerPointer.repeatMode }
return repeatMode == Player.REPEAT_MODE_ONE
}
set(value) = runOnMainThread {
playerPointer.repeatMode = if (value) Player.REPEAT_MODE_ONE else Player.REPEAT_MODE_OFF
}
override var muted: Boolean
get() = runOnMainThreadSync {
val playerVolume = playerPointer.volume.toDouble()
val isMuted = playerVolume == 0.0
return@runOnMainThreadSync isMuted
}
set(value) = runOnMainThread {
if (value) {
userVolume = volume
playerPointer.volume = 0f
} else {
playerPointer.volume = userVolume.toFloat()
}
}
override var rate: Double
get() = runOnMainThreadSync { return@runOnMainThreadSync playerPointer.playbackParameters.speed.toDouble() }
set(value) = runOnMainThread {
playerPointer.playbackParameters = playerPointer.playbackParameters.withSpeed(value.toFloat())
}
private fun initializePlayer() {
if (NitroModules.applicationContext == null) {
throw LibraryError.ApplicationContextNotFound
@@ -101,6 +138,14 @@ class HybridVideoPlayer() : HybridVideoPlayerSpec() {
}
}
override fun seekBy(time: Double) {
currentTime = (currentTime + time).coerceIn(0.0, duration)
}
override fun seekTo(time: Double) {
currentTime = time.coerceIn(0.0, duration)
}
override fun replaceSourceAsync(source: HybridVideoPlayerSourceSpec): Promise<Unit> {
return Promise.async {
val hybridSource = source as? HybridVideoPlayerSource ?: throw PlayerError.InvalidSource
@@ -0,0 +1,91 @@
//
// VideoPlayerObserver.swift
// ReactNativeVideo
//
// Created by Krzysztof Moch on 15/04/2025.
//
import Foundation
import AVFoundation
protocol VideoPlayerObserverDelegate: AnyObject {
func onPlayedToEnd(player: AVPlayer)
func onPlayerItemChange(player: AVPlayer, playerItem: AVPlayerItem?)
}
extension VideoPlayerObserverDelegate {
func onPlayedToEnd(player: AVPlayer) {}
func onPlayerItemChange(player: AVPlayer, playerItem: AVPlayerItem?) {}
}
class VideoPlayerObserver {
private weak var delegate: HybridVideoPlayer?
var player: AVPlayer? {
delegate?.player
}
// Player observers
var playerCurrentItemObserver: NSKeyValueObservation?
// Player item observers
var playbackEndedObserver: NSObjectProtocol?
init(delegate: HybridVideoPlayer) {
self.delegate = delegate
}
deinit {
removeObservers()
invalidatePlayerItemObservers()
}
public func updatePlayerObservers() {
invalidatePlayerItemObservers()
removeObservers()
initializePlayerObservers()
}
private func initializePlayerObservers() {
guard let player else {
return
}
playerCurrentItemObserver = player.observe(\.currentItem, options: [.new, .old]) { [weak self] _, change in
self?.onPlayerCurrentItemChanged(player: player, change: change)
}
}
private func initializePlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) {
playbackEndedObserver = NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
object: playerItem,
queue: nil
) { [weak self] notification in
self?.delegate?.onPlayedToEnd(player: player)
}
}
private func invalidatePlayerItemObservers() {
NotificationCenter.default.removeObserver(playbackEndedObserver as Any)
}
private func removeObservers() {
playerCurrentItemObserver?.invalidate()
}
func onPlayerCurrentItemChanged(player: AVPlayer, change: NSKeyValueObservedChange<AVPlayerItem?>) {
let newPlayerItem = change.newValue?.flatMap { $0 }
// Remove observers for old player item
invalidatePlayerItemObservers()
if let playerItem = newPlayerItem {
// Initialize observers for new player item
initializePlayerItemObservers(player: player, playerItem: playerItem)
delegate?.onPlayerItemChange(player: player, playerItem: playerItem)
}
}
}
@@ -9,11 +9,15 @@ import Foundation
import NitroModules
import AVFoundation
class HybridVideoPlayer: HybridVideoPlayerSpec {
class HybridVideoPlayer: HybridVideoPlayerSpec, VideoPlayerObserverDelegate {
/**
* This in general should not be used directly, use `playerPointer` instead. This should be set only from within the playerQueue.
*/
private var player: AVPlayer?
var player: AVPlayer? {
didSet {
playerObserver?.updatePlayerObservers()
}
}
/**
* The player queue is used to synchronize player initialization.
@@ -35,9 +39,12 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
do {
let item = try initializePlayerItem()
player?.replaceCurrentItem(with: item)
playerItem = item
player = AVPlayer(playerItem: playerItem)
// We need to intialize empty player first so player observers are set
// before we set the player item
player = AVPlayer()
player?.replaceCurrentItem(with: item)
} catch {
playerItem = nil
player = AVPlayer()
@@ -54,6 +61,20 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
}
var playerItem: AVPlayerItem?
var playerObserver: VideoPlayerObserver?
init(source: (any HybridVideoPlayerSourceSpec)) throws {
self.source = source
super.init()
self.playerObserver = VideoPlayerObserver(delegate: self)
}
deinit {
release()
}
// MARK: - Hybrid Impl
var source: any HybridVideoPlayerSourceSpec
@@ -66,6 +87,15 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
}
}
var muted: Bool {
set {
playerPointer.isMuted = newValue
}
get {
return playerPointer.isMuted
}
}
var currentTime: Double {
set {
playerPointer.seek(
@@ -83,14 +113,20 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
Double(playerPointer.currentItem?.duration.seconds ?? Double.nan)
}
init(source: (any HybridVideoPlayerSourceSpec)) throws {
self.source = source
super.init()
var rate: Double {
set {
if #available(iOS 16.0, tvOS 16.0, *) {
playerPointer.defaultRate = Float(newValue)
}
playerPointer.rate = Float(newValue)
}
get {
return Double(playerPointer.rate)
}
}
deinit {
release()
}
var loop: Bool = false
func clean() throws {
release()
@@ -106,6 +142,9 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
if let source = self.source as? HybridVideoPlayerSource {
source.releaseAsset()
}
// Clear player observer
self.playerObserver = nil
}
}
@@ -130,6 +169,26 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
playerPointer.pause()
}
func seekBy(time: Double) throws {
guard let currentItem = playerPointer.currentItem else {
throw PlayerError.notIntilaized.error()
}
let currentItemTime = currentItem.currentTime()
// Duration is NaN for live streams
let fixedDurration = duration.isNaN ? Double.infinity : duration
// Clap by <0, duration>
let newTime = max(0, min(currentItemTime.seconds + time, fixedDurration))
currentTime = newTime
}
func seekTo(time: Double) {
currentTime = time
}
func replaceSourceAsync(source: (any HybridVideoPlayerSourceSpec)) throws -> Promise<Void> {
return Promise.parallel { [weak self] in
guard let self else {
@@ -156,6 +215,8 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
}
}
// MARK: - Internal Methods
private func initializePlayerItem() throws -> AVPlayerItem {
guard let _source = source as? HybridVideoPlayerSource else {
throw PlayerError.invalidSource.error()
@@ -170,6 +231,19 @@ class HybridVideoPlayer: HybridVideoPlayerSpec {
return AVPlayerItem(asset: asset)
}
// MARK: - VideoPlayerObserverDelegate
func onPlayedToEnd(player: AVPlayer) {
// TODO: Notify listeners once we implement callbacks
if loop {
currentTime = 0
try? play()
}
}
// MARK: - Memory Management
var memorySize: Int {
var size = 0
@@ -63,6 +63,33 @@ namespace margelo::nitro::video {
static const auto method = javaClassStatic()->getMethod<void(double /* currentTime */)>("setCurrentTime");
method(_javaPart, currentTime);
}
bool JHybridVideoPlayerSpec::getMuted() {
static const auto method = javaClassStatic()->getMethod<jboolean()>("getMuted");
auto __result = method(_javaPart);
return static_cast<bool>(__result);
}
void JHybridVideoPlayerSpec::setMuted(bool muted) {
static const auto method = javaClassStatic()->getMethod<void(jboolean /* muted */)>("setMuted");
method(_javaPart, muted);
}
bool JHybridVideoPlayerSpec::getLoop() {
static const auto method = javaClassStatic()->getMethod<jboolean()>("getLoop");
auto __result = method(_javaPart);
return static_cast<bool>(__result);
}
void JHybridVideoPlayerSpec::setLoop(bool loop) {
static const auto method = javaClassStatic()->getMethod<void(jboolean /* loop */)>("setLoop");
method(_javaPart, loop);
}
double JHybridVideoPlayerSpec::getRate() {
static const auto method = javaClassStatic()->getMethod<double()>("getRate");
auto __result = method(_javaPart);
return __result;
}
void JHybridVideoPlayerSpec::setRate(double rate) {
static const auto method = javaClassStatic()->getMethod<void(double /* rate */)>("setRate");
method(_javaPart, rate);
}
// Methods
std::shared_ptr<Promise<void>> JHybridVideoPlayerSpec::replaceSourceAsync(const std::shared_ptr<margelo::nitro::video::HybridVideoPlayerSourceSpec>& source) {
@@ -107,5 +134,13 @@ namespace margelo::nitro::video {
static const auto method = javaClassStatic()->getMethod<void()>("pause");
method(_javaPart);
}
void JHybridVideoPlayerSpec::seekBy(double time) {
static const auto method = javaClassStatic()->getMethod<void(double /* time */)>("seekBy");
method(_javaPart, time);
}
void JHybridVideoPlayerSpec::seekTo(double time) {
static const auto method = javaClassStatic()->getMethod<void(double /* time */)>("seekTo");
method(_javaPart, time);
}
} // namespace margelo::nitro::video
@@ -53,6 +53,12 @@ namespace margelo::nitro::video {
void setVolume(double volume) override;
double getCurrentTime() override;
void setCurrentTime(double currentTime) override;
bool getMuted() override;
void setMuted(bool muted) override;
bool getLoop() override;
void setLoop(bool loop) override;
double getRate() override;
void setRate(double rate) override;
public:
// Methods
@@ -61,6 +67,8 @@ namespace margelo::nitro::video {
std::shared_ptr<Promise<void>> preload() override;
void play() override;
void pause() override;
void seekBy(double time) override;
void seekTo(double time) override;
private:
friend HybridBase;
@@ -56,6 +56,24 @@ abstract class HybridVideoPlayerSpec: HybridObject() {
@set:DoNotStrip
@set:Keep
abstract var currentTime: Double
@get:DoNotStrip
@get:Keep
@set:DoNotStrip
@set:Keep
abstract var muted: Boolean
@get:DoNotStrip
@get:Keep
@set:DoNotStrip
@set:Keep
abstract var loop: Boolean
@get:DoNotStrip
@get:Keep
@set:DoNotStrip
@set:Keep
abstract var rate: Double
// Methods
@DoNotStrip
@@ -77,6 +95,14 @@ abstract class HybridVideoPlayerSpec: HybridObject() {
@DoNotStrip
@Keep
abstract fun pause(): Unit
@DoNotStrip
@Keep
abstract fun seekBy(time: Double): Unit
@DoNotStrip
@Keep
abstract fun seekTo(time: Double): Unit
private external fun initHybrid(): HybridData
@@ -73,6 +73,24 @@ namespace margelo::nitro::video {
inline void setCurrentTime(double currentTime) noexcept override {
_swiftPart.setCurrentTime(std::forward<decltype(currentTime)>(currentTime));
}
inline bool getMuted() noexcept override {
return _swiftPart.getMuted();
}
inline void setMuted(bool muted) noexcept override {
_swiftPart.setMuted(std::forward<decltype(muted)>(muted));
}
inline bool getLoop() noexcept override {
return _swiftPart.getLoop();
}
inline void setLoop(bool loop) noexcept override {
_swiftPart.setLoop(std::forward<decltype(loop)>(loop));
}
inline double getRate() noexcept override {
return _swiftPart.getRate();
}
inline void setRate(double rate) noexcept override {
_swiftPart.setRate(std::forward<decltype(rate)>(rate));
}
public:
// Methods
@@ -110,6 +128,18 @@ namespace margelo::nitro::video {
std::rethrow_exception(__result.error());
}
}
inline void seekBy(double time) override {
auto __result = _swiftPart.seekBy(std::forward<decltype(time)>(time));
if (__result.hasError()) [[unlikely]] {
std::rethrow_exception(__result.error());
}
}
inline void seekTo(double time) override {
auto __result = _swiftPart.seekTo(std::forward<decltype(time)>(time));
if (__result.hasError()) [[unlikely]] {
std::rethrow_exception(__result.error());
}
}
private:
ReactNativeVideo::HybridVideoPlayerSpec_cxx _swiftPart;
@@ -15,6 +15,9 @@ public protocol HybridVideoPlayerSpec_protocol: HybridObject {
var duration: Double { get }
var volume: Double { get set }
var currentTime: Double { get set }
var muted: Bool { get set }
var loop: Bool { get set }
var rate: Double { get set }
// Methods
func replaceSourceAsync(source: (any HybridVideoPlayerSourceSpec)) throws -> Promise<Void>
@@ -22,6 +25,8 @@ public protocol HybridVideoPlayerSpec_protocol: HybridObject {
func preload() throws -> Promise<Void>
func play() throws -> Void
func pause() throws -> Void
func seekBy(time: Double) throws -> Void
func seekTo(time: Double) throws -> Void
}
/// See ``HybridVideoPlayerSpec``
@@ -135,6 +135,39 @@ public class HybridVideoPlayerSpec_cxx {
self.__implementation.currentTime = newValue
}
}
public final var muted: Bool {
@inline(__always)
get {
return self.__implementation.muted
}
@inline(__always)
set {
self.__implementation.muted = newValue
}
}
public final var loop: Bool {
@inline(__always)
get {
return self.__implementation.loop
}
@inline(__always)
set {
self.__implementation.loop = newValue
}
}
public final var rate: Double {
@inline(__always)
get {
return self.__implementation.rate
}
@inline(__always)
set {
self.__implementation.rate = newValue
}
}
// Methods
@inline(__always)
@@ -211,4 +244,26 @@ public class HybridVideoPlayerSpec_cxx {
return bridge.create_Result_void_(__exceptionPtr)
}
}
@inline(__always)
public final func seekBy(time: Double) -> bridge.Result_void_ {
do {
try self.__implementation.seekBy(time: time)
return bridge.create_Result_void_()
} catch (let __error) {
let __exceptionPtr = __error.toCpp()
return bridge.create_Result_void_(__exceptionPtr)
}
}
@inline(__always)
public final func seekTo(time: Double) -> bridge.Result_void_ {
do {
try self.__implementation.seekTo(time: time)
return bridge.create_Result_void_()
} catch (let __error) {
let __exceptionPtr = __error.toCpp()
return bridge.create_Result_void_(__exceptionPtr)
}
}
}
@@ -20,15 +20,24 @@ namespace margelo::nitro::video {
prototype.registerHybridSetter("volume", &HybridVideoPlayerSpec::setVolume);
prototype.registerHybridGetter("currentTime", &HybridVideoPlayerSpec::getCurrentTime);
prototype.registerHybridSetter("currentTime", &HybridVideoPlayerSpec::setCurrentTime);
prototype.registerHybridGetter("muted", &HybridVideoPlayerSpec::getMuted);
prototype.registerHybridSetter("muted", &HybridVideoPlayerSpec::setMuted);
prototype.registerHybridGetter("loop", &HybridVideoPlayerSpec::getLoop);
prototype.registerHybridSetter("loop", &HybridVideoPlayerSpec::setLoop);
prototype.registerHybridGetter("rate", &HybridVideoPlayerSpec::getRate);
prototype.registerHybridSetter("rate", &HybridVideoPlayerSpec::setRate);
prototype.registerHybridMethod("replaceSourceAsync", &HybridVideoPlayerSpec::replaceSourceAsync);
prototype.registerHybridMethod("clean", &HybridVideoPlayerSpec::clean);
prototype.registerHybridMethod("preload", &HybridVideoPlayerSpec::preload);
prototype.registerHybridMethod("play", &HybridVideoPlayerSpec::play);
prototype.registerHybridMethod("pause", &HybridVideoPlayerSpec::pause);
prototype.registerHybridMethod("seekBy", &HybridVideoPlayerSpec::seekBy);
prototype.registerHybridMethod("seekTo", &HybridVideoPlayerSpec::seekTo);
});
}
void HybridVideoPlayerSpec::dispose() {
clean();
HybridObject::dispose();
}
} // namespace margelo::nitro::video
@@ -53,6 +53,12 @@ namespace margelo::nitro::video {
virtual void setVolume(double volume) = 0;
virtual double getCurrentTime() = 0;
virtual void setCurrentTime(double currentTime) = 0;
virtual bool getMuted() = 0;
virtual void setMuted(bool muted) = 0;
virtual bool getLoop() = 0;
virtual void setLoop(bool loop) = 0;
virtual double getRate() = 0;
virtual void setRate(double rate) = 0;
public:
// Methods
@@ -61,6 +67,8 @@ namespace margelo::nitro::video {
virtual std::shared_ptr<Promise<void>> preload() = 0;
virtual void play() = 0;
virtual void pause() = 0;
virtual void seekBy(double time) = 0;
virtual void seekTo(double time) = 0;
protected:
// Hybrid Setup
@@ -1,3 +1,4 @@
import { NitroModules } from 'react-native-nitro-modules';
import { type VideoPlayer as VideoPlayerImpl } from '../spec/nitro/VideoPlayer.nitro';
import type { VideoPlayerSource } from '../spec/nitro/VideoPlayerSource.nitro';
import type { VideoConfig, VideoSource } from './types/VideoConfig';
@@ -61,18 +62,23 @@ class VideoPlayer implements VideoPlayerBase {
*/
private wrapPromise<T>(promise: Promise<T>) {
return new Promise<T>((resolve, reject) => {
promise.then(resolve).catch(reject);
promise.then(resolve).catch((error) => {
reject(this.throwError(error));
});
});
}
// Source
get source(): VideoPlayerSource {
return this.player.source;
}
// Duration
get duration(): number {
return this.player.duration;
}
// Volume
get volume(): number {
return this.player.volume;
}
@@ -81,6 +87,7 @@ class VideoPlayer implements VideoPlayerBase {
this.player.volume = value;
}
// Current Time
get currentTime(): number {
return this.player.currentTime;
}
@@ -89,8 +96,37 @@ class VideoPlayer implements VideoPlayerBase {
this.player.currentTime = value;
}
preload(): Promise<void> {
return this.wrapPromise(this.player.preload());
// Muted
get muted(): boolean {
return this.player.muted;
}
set muted(value: boolean) {
this.player.muted = value;
}
// Loop
get loop(): boolean {
return this.player.loop;
}
set loop(value: boolean) {
this.player.loop = value;
}
// Rate
get rate(): number {
return this.player.rate;
}
set rate(value: number) {
this.player.rate = value;
}
async preload(): Promise<void> {
await this.wrapPromise(this.player.preload());
NitroModules.updateMemorySize(this.player);
}
play(): void {
@@ -109,12 +145,30 @@ class VideoPlayer implements VideoPlayerBase {
}
}
replaceSourceAsync(
seekBy(time: number): void {
try {
this.player.seekBy(time);
} catch (error) {
this.throwError(error);
}
}
seekTo(time: number): void {
try {
this.player.seekTo(time);
} catch (error) {
this.throwError(error);
}
}
async replaceSourceAsync(
source: VideoSource | VideoConfig | VideoPlayerSource
): Promise<void> {
return this.wrapPromise(
await this.wrapPromise(
this.player.replaceSourceAsync(createSource(source))
);
NitroModules.updateMemorySize(this.player);
}
}
@@ -16,6 +16,7 @@ export interface VideoPlayerBase {
/**
* The volume of the video (0.0 = 0%, 1.0 = 100%).
* @note If the player is {@link muted}, the volume will be 0.0.
*/
volume: number;
@@ -25,6 +26,22 @@ export interface VideoPlayerBase {
*/
currentTime: number;
/**
* Whether the player is muted.
*/
muted: boolean;
/**
* Whether the player is looped.
*/
loop: boolean;
/**
* Controls the speed at which the player should play.
* @note if rate is = 0, it will pause video.
*/
rate: number;
/**
* Preload the video.
* This is useful to avoid delay when the user plays the video.
@@ -42,6 +59,22 @@ export interface VideoPlayerBase {
*/
pause(): void;
/**
* Seek by given time.
* If the time is negative, it will seek backward.
* time will be clamped if it is out of range (0 ~ {@link duration}).
* @param time - The time to seek from current time in seconds.
*/
seekBy(time: number): void;
/**
* Seek to a specific time in the video.
* @param time - The time to seek to in seconds.
* @note This have same effect as {@link currentTime} setter.
* @note time will be clamped if it is out of range (0 ~ {@link duration}).
*/
seekTo(time: number): void;
/**
* Replace the current source of the player.
* @param source - The new source of the video.