Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
556 changes: 278 additions & 278 deletions example/ios/Podfile.lock

Large diffs are not rendered by default.

30 changes: 17 additions & 13 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import Slider from '@react-native-community/slider';
import React from 'react';
import { Alert, SafeAreaView, ScrollView, Text, View } from 'react-native';
import { styles } from './styles';
import {
ActionButton,
ControlButton,
SwitchControl,
ToggleButton,
} from './components/Controls';
import Slider from '@react-native-community/slider';
import {
type VideoConfig,
type VideoPlayerStatus,
VideoView,
type VideoViewRef,
type onLoadData,
type onProgressData,
type VideoPlayerStatus,
type onVolumeChangeData,
useVideoPlayer,
useEvent,
VideoView,
type VideoConfig,
useVideoPlayer,
} from 'react-native-video';
import {
ActionButton,
ControlButton,
SwitchControl,
ToggleButton,
} from './components/Controls';
import TextTrackManager from './components/TextTrackManager';
import { styles } from './styles';
import { type VideoSettings, defaultSettings } from './types/videoSettings';
import { formatTime } from './utils/time';
import { getVideoSource } from './utils/videoSource';
Expand Down Expand Up @@ -213,7 +213,11 @@ const VideoDemo = () => {
active={settings.videoType === mode}
onPress={async () => {
updateSetting('videoType', mode);
await player.replaceSourceAsync(getVideoSource(mode));
try {
await player.replaceSourceAsync(getVideoSource(mode));
} catch (error) {
console.error('Error replacing source:', error);
}
}}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ sealed class PlayerError(code: String, message: String) : VideoError(code, messa
"player/invalid-source",
"Invalid source passed to player"
)

object Cancelled : PlayerError(
"player/cancelled",
"Operation was cancelled"
)
}

// Source related errors
Expand Down Expand Up @@ -73,6 +78,11 @@ sealed class SourceError(code: String, message: String) : VideoError(code, messa
"source/unsupported-content-type",
"type of content (${uri}) is not supported"
)

object Cancelled : SourceError(
"source/cancelled",
"Operation was cancelled"
)
}

// View related errors
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.twg.video.core.utils

import com.twg.video.core.SourceError
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicReference

class SourceLoader {
private val operationCounter = AtomicInteger(0)
private val currentOperationRef = AtomicReference<OperationHolder<*>?>(null)

fun <T> load(operation: () -> T): T {
cancel()

val currentOp = operationCounter.incrementAndGet()
val holder = OperationHolder<T>(currentOp)
currentOperationRef.set(holder)

return try {
val result = operation()
if (holder.operationId != operationCounter.get()) {
throw SourceError.Cancelled
}
result
} catch (e: SourceError.Cancelled) {
throw e
} catch (e: Exception) {
if (holder.operationId != operationCounter.get()) {
throw SourceError.Cancelled
}
throw e
} finally {
if (holder.operationId == operationCounter.get()) {
currentOperationRef.set(null)
}
}
}

fun cancel() {
val current = currentOperationRef.get()
if (current != null) {
operationCounter.incrementAndGet()
currentOperationRef.set(null)
}
}

private class OperationHolder<T>(val operationId: Int)
}

Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ class HybridVideoPlayer() : HybridVideoPlayerSpec() {

val hybridSource = source as? HybridVideoPlayerSource ?: throw PlayerError.InvalidSource

val oldSource = this.source as? HybridVideoPlayerSource
oldSource?.sourceLoader?.cancel()

runOnMainThreadSync {
// Update source
this.source = source
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.twg.video.core.player.DRMManagerSpec
import com.twg.video.core.player.buildMediaSource
import com.twg.video.core.player.createMediaItemFromVideoConfig
import com.twg.video.core.plugins.PluginsRegistry
import com.twg.video.core.utils.SourceLoader
import com.twg.video.core.utils.VideoInformationUtils

class HybridVideoPlayerSource(): HybridVideoPlayerSourceSpec() {
Expand All @@ -25,6 +26,8 @@ class HybridVideoPlayerSource(): HybridVideoPlayerSourceSpec() {
@UnstableApi
var drmSessionManager: DrmSessionManager? = null

internal val sourceLoader = SourceLoader()

constructor(config: NativeVideoConfig) : this() {
this.uri = config.uri
this.config = config
Expand Down Expand Up @@ -53,7 +56,9 @@ class HybridVideoPlayerSource(): HybridVideoPlayerSourceSpec() {

override fun getAssetInformationAsync(): Promise<VideoInformation> {
return Promise.async {
return@async VideoInformationUtils.fromUri(uri, config.headers ?: emptyMap())
return@async sourceLoader.load {
VideoInformationUtils.fromUri(uri, config.headers ?: emptyMap())
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,46 @@
//

import AVFoundation
import Foundation

extension AVAsset {
var estimatedMemoryUsage: Int {
var size = 0

// Get enabled video tracks
let enabledVideoTrack = tracks(withMediaType: .video)
.filter { $0.isEnabled }

// Calculate memory usage for video tracks
for track in enabledVideoTrack {
let dimensions = track.naturalSize
let pixelCount = Int(dimensions.width * dimensions.height)
let frameSize = pixelCount * 4 // RGBA
let frames = 30 * 15 // 30 FPS * 15 seconds of buffer

size += frameSize * frames
if let urlAsset = self as? AVURLAsset, urlAsset.url.isFileURL {
let resourceValues = try? urlAsset.url.resourceValues(forKeys: [.fileSizeKey])
if let fileSize = resourceValues?.fileSize, fileSize > 0 {
return fileSize
}
}

// Get enabled audio tracks
let enabledAudioTrack = tracks(withMediaType: .audio)
.filter { $0.isEnabled }

// Estimate memory usage for audio tracks
for _ in enabledAudioTrack {
let frameSize = 44100 * 2 * 2 // 44.1kHz * 2 channels * 2 seconds
size += frameSize

var estimatedSize = 0

let videoTracks = tracks(withMediaType: .video)
let audioTracks = tracks(withMediaType: .audio)

for track in videoTracks {
let size = track.naturalSize
let pixelCount = size.width * size.height
let duration = CMTimeGetSeconds(track.timeRange.duration)

if duration > 0 && !duration.isNaN && !duration.isInfinite {
let bitrate = track.estimatedDataRate > 0 ? track.estimatedDataRate : 2_000_000
estimatedSize += Int((bitrate * Float(duration)) / 8)
} else {
estimatedSize += Int(pixelCount * 4)
}
}

return size

for track in audioTracks {
let duration = CMTimeGetSeconds(track.timeRange.duration)
let bitrate = track.estimatedDataRate > 0 ? track.estimatedDataRate : 128_000

if duration > 0 && !duration.isNaN && !duration.isInfinite {
estimatedSize += Int((bitrate * Float(duration)) / 8)
} else {
estimatedSize += 1_000_000
}
}

return estimatedSize > 0 ? estimatedSize : 0
}
}
}
10 changes: 10 additions & 0 deletions packages/react-native-video/ios/core/VideoError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum PlayerError: VideoError {
case assetNotInitialized
case invalidSource
case invalidTrackUrl(url: String)
case cancelled

var code: String {
switch self {
Expand All @@ -49,6 +50,8 @@ enum PlayerError: VideoError {
return "player/invalid-source"
case .invalidTrackUrl:
return "player/invalid-track-url"
case .cancelled:
return "player/cancelled"
}
}

Expand All @@ -62,6 +65,8 @@ enum PlayerError: VideoError {
return "Invalid source passed to player"
case let .invalidTrackUrl(url: url):
return "Invalid track URL: \(url)"
case .cancelled:
return "Operation was cancelled"
}
}
}
Expand All @@ -73,6 +78,7 @@ enum SourceError: VideoError {
case fileDoesNotExist(uri: String)
case failedToInitializeAsset
case unsupportedContentType(uri: String)
case cancelled

var code: String {
switch self {
Expand All @@ -86,6 +92,8 @@ enum SourceError: VideoError {
return "source/failed-to-initialize-asset"
case .unsupportedContentType:
return "source/unsupported-content-type"
case .cancelled:
return "source/cancelled"
}
}

Expand All @@ -101,6 +109,8 @@ enum SourceError: VideoError {
return "Failed to initialize asset"
case let .unsupportedContentType(uri: uri):
return "type of content (\(uri)) is not supported"
case .cancelled:
return "Operation was cancelled"
}
}
}
Expand Down
Loading