diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index e805ca0815..657e26c47b 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -52,6 +52,16 @@ body: value: "A bug happened!" validations: required: true + + - type: input + id: reproduction-repo + attributes: + label: Reproduction + description: Provide a link to a repository with a reproduction of the bug, this is optional but it will make us to fix the bug faster + placeholder: Reproduction Repository + value: "repository link" + validations: + required: false - type: textarea id: reproduction diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index e131e408eb..185ddc2709 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -20,12 +20,12 @@ runs: uses: actions/cache@v3 with: path: | - **/node_modules + ${{ inputs.working-directory }}/node_modules .yarn/install-state.gz - key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} + key: ${{ runner.os }}-yarn-${{ inputs.working-directory }}-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} restore-keys: | - ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - ${{ runner.os }}-yarn- + ${{ runner.os }}-yarn-${{ inputs.working-directory }}-${{ hashFiles('yarn.lock') }}- + ${{ runner.os }}-yarn-${{ inputs.working-directory }} - name: Install dependencies working-directory: ${{ inputs.working-directory }} if: steps.yarn-cache.outputs.cache-hit != 'true' diff --git a/.github/workflows/test-build-docs.yml b/.github/workflows/test-build-docs.yml new file mode 100644 index 0000000000..825df994ae --- /dev/null +++ b/.github/workflows/test-build-docs.yml @@ -0,0 +1,35 @@ + +name: Test Docs build +on: + workflow_dispatch: + pull_request: + paths: + - '.github/workflows/test-build-docs.yml' + - 'docs/**' + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup + uses: ./.github/actions/setup-bun + with: + working-directory: ./docs + + - name: Cache build + uses: actions/cache@v3 + with: + path: | + docs/.next/cache + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }}-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/bun.lockb') }} + ${{ runner.os }}-nextjs- + + - name: Build docs + run: | + bun --cwd docs build + touch docs/out/.nojekyll \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index f1b47d25eb..8fd931c422 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ +# [6.0.0-beta.6](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.5...v6.0.0-beta.6) (2024-03-18) + + +### Bug Fixes + +* add missing node_modules paths to metro.config.js of basic example app ([#3555](https://github.com/react-native-video/react-native-video/issues/3555)) ([d505de5](https://github.com/react-native-video/react-native-video/commit/d505de5910a22ab9a0d7429e6b88a81cd2594b9c)) +* add missing shutterColor type ([#3561](https://github.com/react-native-video/react-native-video/issues/3561)) ([ba00881](https://github.com/react-native-video/react-native-video/commit/ba00881ddcd53c2f5a4e1fc6e30cb5eb7ef674a3)) +* **android:** check disableFocus when state is ready ([#3494](https://github.com/react-native-video/react-native-video/issues/3494)) ([366c841](https://github.com/react-native-video/react-native-video/commit/366c841c0b960fd461ae7dcfdcb76a928fadf2b8)) +* **android:** enableDecoderFallback to decrease DECODER_ERROR issue ([#3416](https://github.com/react-native-video/react-native-video/issues/3416)) ([eaa72c6](https://github.com/react-native-video/react-native-video/commit/eaa72c66659b9e2a22af9ff9d43013521f6a66e3)) +* **android:** onSeek called instantly ([#3530](https://github.com/react-native-video/react-native-video/issues/3530)) ([af6aea8](https://github.com/react-native-video/react-native-video/commit/af6aea8934e19467e1ed8e21808b2dbddb6f6356)) +* **android:** suppress lint `PrivateResource` ([#3531](https://github.com/react-native-video/react-native-video/issues/3531)) ([38e3625](https://github.com/react-native-video/react-native-video/commit/38e3625541753340e912e474b753e0f4fac4e9c1)) +* **docs/ci:** add typescript ([#3572](https://github.com/react-native-video/react-native-video/issues/3572)) ([0f31271](https://github.com/react-native-video/react-native-video/commit/0f31271dcf2bfe2f4429e22040660025be8a6a3c)) +* **docs:** fix build ([#3571](https://github.com/react-native-video/react-native-video/issues/3571)) ([4fc7d27](https://github.com/react-native-video/react-native-video/commit/4fc7d2788b4d01c581a31cc3ac733c3948b65a3a)) +* **ios:** add text tracks only if we successfully insertTimeRage ([#3557](https://github.com/react-native-video/react-native-video/issues/3557)) ([b73baad](https://github.com/react-native-video/react-native-video/commit/b73baad2c2c0c6ea701d865eee32d4e94ae58178)) +* **ios:** apply `cropStart` when in repeat mode ([#3525](https://github.com/react-native-video/react-native-video/issues/3525)) ([2c0e009](https://github.com/react-native-video/react-native-video/commit/2c0e00987685875f9603ae2084ae23b3c1aebce7)) +* **ios:** current release volume change observer ([#3565](https://github.com/react-native-video/react-native-video/issues/3565)) ([16f3cdb](https://github.com/react-native-video/react-native-video/commit/16f3cdbd9a7864206feaeef29344c09792d66d56)) +* **ios:** Do not crash when accessLog return nil ([#3549](https://github.com/react-native-video/react-native-video/issues/3549)) ([4d4b56c](https://github.com/react-native-video/react-native-video/commit/4d4b56c05dd3c09fce5ddc38f56b0391c357ac85)) +* **ios:** don't crop video when in repeat mode ([#3575](https://github.com/react-native-video/react-native-video/issues/3575)) ([90b31af](https://github.com/react-native-video/react-native-video/commit/90b31af2c969b6d6d57877c71ef3a4830a76aedc)) +* **ios:** ensure playback stopped in background ([#3587](https://github.com/react-native-video/react-native-video/issues/3587)) ([41c6785](https://github.com/react-native-video/react-native-video/commit/41c6785ee8c667ebe9c6c464223f6485473d94f8)) +* **ios:** fix missing bridge in bridgeless mode ([#3570](https://github.com/react-native-video/react-native-video/issues/3570)) ([46c8c49](https://github.com/react-native-video/react-native-video/commit/46c8c498c474600a0b35ebaf744306aefa42905f)) +* **ios:** fix tvOS build ([#3524](https://github.com/react-native-video/react-native-video/issues/3524)) ([9306d9a](https://github.com/react-native-video/react-native-video/commit/9306d9a15d281a60492f6d4166598a389a56f652)) +* **ios:** split licenseUrl and loadedLicenseUrl ([#3578](https://github.com/react-native-video/react-native-video/issues/3578)) ([7c4d19f](https://github.com/react-native-video/react-native-video/commit/7c4d19fa72a35449dd11ec59278b2ea11ec629fc)) + + +### Features + +* **android:** add subtitle event ([#3566](https://github.com/react-native-video/react-native-video/issues/3566)) ([6184c10](https://github.com/react-native-video/react-native-video/commit/6184c10acc90defd63cd55af51458864dfe112d5)) +* implement opacity to control visibility of subtitles ([#3583](https://github.com/react-native-video/react-native-video/issues/3583)) ([f4cce2e](https://github.com/react-native-video/react-native-video/commit/f4cce2ecdba0668c3ecf74d2fd7956df4dd8489d)) +* **ios:** Add ios support for accessing WebVTT Subtitle Content ([#3541](https://github.com/react-native-video/react-native-video/issues/3541)) ([253ffb5](https://github.com/react-native-video/react-native-video/commit/253ffb595633a4b18221339278f73c8416225f56)) +* move require (local files) to `source.uri` ([#3535](https://github.com/react-native-video/react-native-video/issues/3535)) ([41ac781](https://github.com/react-native-video/react-native-video/commit/41ac7814121fc70a123fa4585dc9b1bd96e9629f)) + # [6.0.0-beta.5](https://github.com/react-native-video/react-native-video/compare/v6.0.0-beta.4...v6.0.0-beta.5) (2024-02-02) diff --git a/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt b/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt index 1e32c77ada..efb4da0a81 100644 --- a/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt +++ b/android/src/main/java/com/brentvatne/common/api/SubtitleStyle.kt @@ -17,6 +17,8 @@ class SubtitleStyle private constructor() { private set var paddingBottom = 0 private set + var opacity = 1f + private set companion object { private const val PROP_FONT_SIZE_TRACK = "fontSize" @@ -24,6 +26,7 @@ class SubtitleStyle private constructor() { private const val PROP_PADDING_TOP = "paddingTop" private const val PROP_PADDING_LEFT = "paddingLeft" private const val PROP_PADDING_RIGHT = "paddingRight" + private const val PROP_OPACITY = "opacity" @JvmStatic fun parse(src: ReadableMap?): SubtitleStyle { @@ -33,6 +36,7 @@ class SubtitleStyle private constructor() { subtitleStyle.paddingTop = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_TOP, 0) subtitleStyle.paddingLeft = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_LEFT, 0) subtitleStyle.paddingRight = ReactBridgeUtils.safeGetInt(src, PROP_PADDING_RIGHT, 0) + subtitleStyle.opacity = ReactBridgeUtils.safeGetFloat(src, PROP_OPACITY, 1f) return subtitleStyle } } diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java index ac2b0f5061..7ddd2ca1d1 100644 --- a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java +++ b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java @@ -59,6 +59,8 @@ public VideoEventEmitter(ReactContext reactContext) { private static final String EVENT_VOLUME_CHANGE = "onVolumeChange"; private static final String EVENT_AUDIO_TRACKS = "onAudioTracks"; private static final String EVENT_TEXT_TRACKS = "onTextTracks"; + + private static final String EVENT_TEXT_TRACK_DATA_CHANGED = "onTextTrackDataChanged"; private static final String EVENT_VIDEO_TRACKS = "onVideoTracks"; private static final String EVENT_ON_RECEIVE_AD_EVENT = "onReceiveAdEvent"; private static final String EVENT_PICTURE_IN_PICTURE_STATUS_CHANGED = "onPictureInPictureStatusChanged"; @@ -87,6 +89,7 @@ public VideoEventEmitter(ReactContext reactContext) { EVENT_VOLUME_CHANGE, EVENT_AUDIO_TRACKS, EVENT_TEXT_TRACKS, + EVENT_TEXT_TRACK_DATA_CHANGED, EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, EVENT_ON_RECEIVE_AD_EVENT, @@ -118,6 +121,7 @@ public VideoEventEmitter(ReactContext reactContext) { EVENT_VOLUME_CHANGE, EVENT_AUDIO_TRACKS, EVENT_TEXT_TRACKS, + EVENT_TEXT_TRACK_DATA_CHANGED, EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, EVENT_ON_RECEIVE_AD_EVENT, @@ -147,6 +151,7 @@ public VideoEventEmitter(ReactContext reactContext) { private static final String EVENT_PROP_VIDEO_TRACKS = "videoTracks"; private static final String EVENT_PROP_AUDIO_TRACKS = "audioTracks"; private static final String EVENT_PROP_TEXT_TRACKS = "textTracks"; + private static final String EVENT_PROP_TEXT_TRACK_DATA = "subtitleTracks"; private static final String EVENT_PROP_HAS_AUDIO_FOCUS = "hasAudioFocus"; private static final String EVENT_PROP_IS_BUFFERING = "isBuffering"; private static final String EVENT_PROP_PLAYBACK_RATE = "playbackRate"; @@ -288,6 +293,12 @@ public void textTracks(ArrayList textTracks){ receiveEvent(EVENT_TEXT_TRACKS, arrayToObject(EVENT_PROP_TEXT_TRACKS, textTracksToArray(textTracks))); } + public void textTrackDataChanged(String textTrackData){ + WritableMap event = Arguments.createMap(); + event.putString(EVENT_PROP_TEXT_TRACK_DATA, textTrackData); + receiveEvent(EVENT_TEXT_TRACK_DATA_CHANGED, event); + } + public void videoTracks(ArrayList videoTracks){ receiveEvent(EVENT_VIDEO_TRACKS, arrayToObject(EVENT_PROP_VIDEO_TRACKS, videoTracksToArray(videoTracks))); } diff --git a/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt b/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt index 65dc57a5b4..c2ee24921a 100644 --- a/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt +++ b/android/src/main/java/com/brentvatne/common/toolbox/ReactBridgeUtils.kt @@ -66,6 +66,14 @@ object ReactBridgeUtils { return safeGetDouble(map, key, 0.0) } + @JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?, fallback: Float): Float { + return if (map != null && map.hasKey(key!!) && !map.isNull(key)) map.getDouble(key).toFloat() else fallback + } + + @JvmStatic fun safeGetFloat(map: ReadableMap?, key: String?): Float { + return safeGetFloat(map, key, 0.0f) + } + /** * toStringMap converts a [ReadableMap] into a HashMap. * diff --git a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index b00b4e9564..ebe05d16a9 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -117,6 +117,13 @@ public void setSubtitleStyle(SubtitleStyle style) { subtitleLayout.setFixedTextSize(TypedValue.COMPLEX_UNIT_SP, style.getFontSize()); } subtitleLayout.setPadding(style.getPaddingLeft(), style.getPaddingTop(), style.getPaddingRight(), style.getPaddingBottom()); + if (style.getOpacity() != 0) { + subtitleLayout.setAlpha(style.getOpacity()); + subtitleLayout.setVisibility(View.VISIBLE); + } else { + subtitleLayout.setVisibility(View.GONE); + } + } public void setShutterColor(Integer color) { diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index d9b0771098..e47218ad9f 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -45,6 +45,7 @@ import androidx.media3.common.TrackGroup; import androidx.media3.common.TrackSelectionOverride; import androidx.media3.common.Tracks; +import androidx.media3.common.text.CueGroup; import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSource; import androidx.media3.datasource.DataSpec; @@ -664,7 +665,8 @@ private void initializePlayerCore(ReactExoplayerView self) { ); DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext()) - .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); + .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF) + .setEnableDecoderFallback(true); // Create an AdsLoader. adsLoader = new ImaAdsLoader @@ -1054,9 +1056,7 @@ private void startPlayback() { } else { initializePlayer(); } - if (!disableFocus) { - setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); - } + setKeepScreenOn(preventsDisplaySleepDuringVideoPlayback); } private void pausePlayback() { @@ -1563,6 +1563,13 @@ public void onMetadata(@NonNull Metadata metadata) { eventEmitter.timedMetadata(metadataArray); } + public void onCues(CueGroup cueGroup) { + if (!cueGroup.cues.isEmpty() && cueGroup.cues.get(0).text != null) { + String subtitleText = cueGroup.cues.get(0).text.toString(); + eventEmitter.textTrackDataChanged(subtitleText); + } + } + // ReactExoplayerViewManager public api public void setSrc(final Uri uri, final long startPositionMs, final long cropStartMs, final long cropEndMs, final String extension, Map headers) { diff --git a/docs/pages/component/drm.mdx b/docs/pages/component/drm.mdx index 33bcdedcfb..fe02ed3383 100644 --- a/docs/pages/component/drm.mdx +++ b/docs/pages/component/drm.mdx @@ -43,14 +43,18 @@ Rather than setting the `licenseServer` url to get the license, you can manually your `contentId` + the provided certificate via `objc [loadingRequest streamingContentKeyRequestDataForApp:certificateData contentIdentifier:contentIdData options:nil error:&spcError]; ` -Also, you will receive the `contentId` and a `licenseUrl` URL defined as `loadingRequest.request.URL.absoluteString ` or as the `licenseServer` prop if it's passed. +Also, you will receive following parameter of getLicense: +* `contentId` contentId if passed to `drm` object or loadingRequest.request.url?.host +* `loadedLicenseUrl` URL defined as `loadingRequest.request.URL.absoluteString`, this url starts with `skd://` or `clearkey://` +* `licenseServer` prop if prop is passed to `drm` object. +* `spcString` the SPC used to validate playback with drm server You should return on this method a `CKC` in Base64, either by just returning it or returning a `Promise` that resolves with the `CKC`. With this prop you can override the license acquisition flow, as an example: ```js -getLicense: (spcString, contentId, licenseUrl) => { +getLicense: (spcString, contentId, licenseUrl, loadedLicenseUrl) => { const base64spc = Base64.encode(spcString); const formData = new FormData(); formData.append('spc', base64spc); diff --git a/docs/pages/component/events.mdx b/docs/pages/component/events.mdx index bb6a55190f..01e25eb43f 100644 --- a/docs/pages/component/events.mdx +++ b/docs/pages/component/events.mdx @@ -516,7 +516,7 @@ Example: ### `onTextTrackDataChanged` - + Callback function that is called when new subtitle data is available. It provides the actual subtitle content for the current selected text track, if available (mainly WebVTT). @@ -534,6 +534,8 @@ Example: } ``` +For details on how to control the visibility of subtitles, see the [subtitleStyle](./props.mdx#subtitleStyle) section. + ### `onVideoTracks` diff --git a/docs/pages/component/props.mdx b/docs/pages/component/props.mdx index 0657980d7a..0100c67069 100644 --- a/docs/pages/component/props.mdx +++ b/docs/pages/component/props.mdx @@ -634,12 +634,21 @@ The docs for this prop are incomplete and will be updated as each option is inve Example: +Pass directly the asset to play (deprecated) + ```javascript const sintel = require('./sintel.mp4'); +source={ sintel }; +``` + +Or by using an uri (starting from 6.0.0-beta.6) -source = {sintel}; +```javascript +const sintel = require('./sintel.mp4'); +source={{ uri: sintel }} ``` + #### URI string A number of URI schemes are supported by passing an object with a `uri` attribute. @@ -751,11 +760,12 @@ source={{ | paddingBottom | Adjust the bottom padding of the subtitles. Default: 0 | Android | | paddingLeft | Adjust the left padding of the subtitles. Default: 0 | Android | | paddingRight | Adjust the right padding of the subtitles. Default: 0 | Android | +| opacity | Adjust the visibility of subtitles with 0 hiding and 1 fully showing them. Android supports float values between 0 and 1 for varying opacity levels, whereas iOS supports only 0 or 1. Default: 1. | Android, iOS | Example: ```javascript -subtitleStyle={{ paddingBottom: 50, fontSize: 20 }} +subtitleStyle={{ paddingBottom: 50, fontSize: 20, opacity: 0 }} ``` ### `textTracks` diff --git a/examples/basic/ios/Podfile.lock b/examples/basic/ios/Podfile.lock index df02ef57ed..68e85c7390 100644 --- a/examples/basic/ios/Podfile.lock +++ b/examples/basic/ios/Podfile.lock @@ -1368,7 +1368,7 @@ SPEC CHECKSUMS: PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RCT-Folly: 045d6ecaa59d826c5736dfba0b2f4083ff8d79df - RCTDeprecation: 6cc9677aab2e9af79e75f2eeea9be22adbc657d9 + RCTDeprecation: a6392cb4842ae80e6f13ea460517170be529c301 RCTRequired: 1c308285f5125d3e65fd864d1ffbd51c84f19be0 RCTTypeSafety: c66cf0ffe89d4c3a6f74c38a8b1396d9a06bd5f2 React: 525ec60174ace50c7cc60fe92982e580b0b26cfb @@ -1397,9 +1397,9 @@ SPEC CHECKSUMS: React-perflogger: bc4cdc9440a5ba3b462813447a5a6633010b1cc8 React-RCTActionSheet: 8b27811da488f196167ca1756453cc1feacbed8f React-RCTAnimation: aba9dcc5c6c0213cd1e747076300261aa8d23845 - React-RCTAppDelegate: bf765fc52328ba350794f1ca16d48e757278d2aa + React-RCTAppDelegate: f8d6afca137f95bac028b631c89c9648ae2e864d React-RCTBlob: 843bb240adc3bc28cf8893a5fad4b4d41c0b89ad - React-RCTFabric: e79eaae738addada21b4b728fd77f95a3255c2af + React-RCTFabric: 176ba2d8c621a38090b7759eafc046e8f76d081f React-RCTImage: 67ea59e43e222cde321675e49416e21b5adc52e6 React-RCTLinking: 73f14ccf9ce161d5f7b61c4d18eb01876897fe17 React-RCTNetwork: 157786ee39743278756977b8161631646bcf9770 @@ -1421,4 +1421,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: bbe1bd5f521b6b9366288dbaf8af1cec85c0a95a -COCOAPODS: 1.13.0 +COCOAPODS: 1.12.0 diff --git a/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj b/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj index 08a7b9b128..65de7e2a6d 100644 --- a/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj +++ b/examples/basic/ios/videoplayer.xcodeproj/project.pbxproj @@ -8,12 +8,12 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* videoplayerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* videoplayerTests.m */; }; + 11C6209C7B72C624AC36CAD1 /* Pods_videoplayer_videoplayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 2395E922397FC6A6C2EF111E /* Pods_videoplayer_videoplayerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B635AA1A8380D1027D65879C /* Pods_videoplayer_videoplayerTests.framework */; }; - 786F172127660DA79AA1A40E /* Pods_videoplayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73BD19CF9308E0D19FBB9651 /* Pods_videoplayer.framework */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + A64041D5CF85945B698F6FD0 /* Pods_videoplayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,13 +36,13 @@ 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = videoplayer/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = videoplayer/Info.plist; sourceTree = ""; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = videoplayer/main.m; sourceTree = ""; }; - 683CFCFD33A7C6BFFC446604 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.debug.xcconfig"; sourceTree = ""; }; - 70576FE834C7A5574E4E1C55 /* Pods-videoplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.release.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.release.xcconfig"; sourceTree = ""; }; - 73BD19CF9308E0D19FBB9651 /* Pods_videoplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.release.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.release.xcconfig"; sourceTree = ""; }; + 4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.release.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.release.xcconfig"; sourceTree = ""; }; + 594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer_videoplayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.debug.xcconfig"; sourceTree = ""; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = videoplayer/LaunchScreen.storyboard; sourceTree = ""; }; - 898A15A5BC01E9AED05EA40A /* Pods-videoplayer-videoplayerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.release.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.release.xcconfig"; sourceTree = ""; }; - B635AA1A8380D1027D65879C /* Pods_videoplayer_videoplayerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_videoplayer_videoplayerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - C2294C9F98CE00118BC70E0C /* Pods-videoplayer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer/Pods-videoplayer.debug.xcconfig"; sourceTree = ""; }; + CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-videoplayer-videoplayerTests.debug.xcconfig"; path = "Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests.debug.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -51,7 +51,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 2395E922397FC6A6C2EF111E /* Pods_videoplayer_videoplayerTests.framework in Frameworks */, + 11C6209C7B72C624AC36CAD1 /* Pods_videoplayer_videoplayerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -59,7 +59,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 786F172127660DA79AA1A40E /* Pods_videoplayer.framework in Frameworks */, + A64041D5CF85945B698F6FD0 /* Pods_videoplayer.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -100,8 +100,8 @@ isa = PBXGroup; children = ( ED297162215061F000B7C4FE /* JavaScriptCore.framework */, - 73BD19CF9308E0D19FBB9651 /* Pods_videoplayer.framework */, - B635AA1A8380D1027D65879C /* Pods_videoplayer_videoplayerTests.framework */, + 4610D42AC113C0F184AAF5BC /* Pods_videoplayer.framework */, + 594390EE3512A597F6D2CFC8 /* Pods_videoplayer_videoplayerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -140,10 +140,10 @@ BBD78D7AC51CEA395F1C20DB /* Pods */ = { isa = PBXGroup; children = ( - C2294C9F98CE00118BC70E0C /* Pods-videoplayer.debug.xcconfig */, - 70576FE834C7A5574E4E1C55 /* Pods-videoplayer.release.xcconfig */, - 683CFCFD33A7C6BFFC446604 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */, - 898A15A5BC01E9AED05EA40A /* Pods-videoplayer-videoplayerTests.release.xcconfig */, + 6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */, + 327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */, + CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */, + 5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -155,12 +155,12 @@ isa = PBXNativeTarget; buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "videoplayerTests" */; buildPhases = ( - F102B4F37B9B875C963CC540 /* [CP] Check Pods Manifest.lock */, + C0C880E0881EE98C1792E57D /* [CP] Check Pods Manifest.lock */, 00E356EA1AD99517003FC87E /* Sources */, 00E356EB1AD99517003FC87E /* Frameworks */, 00E356EC1AD99517003FC87E /* Resources */, - 8DD7997A551817281D5EF9FE /* [CP] Embed Pods Frameworks */, - DB2182CF036650761ADDB488 /* [CP] Copy Pods Resources */, + E3461B2529EDAA05BCA42787 /* [CP] Embed Pods Frameworks */, + 0765D094F5C79DBE6CC6386B /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -176,14 +176,14 @@ isa = PBXNativeTarget; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "videoplayer" */; buildPhases = ( - 71542B065A5C697B0C5F4B62 /* [CP] Check Pods Manifest.lock */, + 66FA7E36D064B737784DDFB7 /* [CP] Check Pods Manifest.lock */, FD10A7F022414F080027D42C /* Start Packager */, 13B07F871A680F5B00A75B9A /* Sources */, 13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8E1A680F5B00A75B9A /* Resources */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, - 89F9A997AA9C0BD33946880C /* [CP] Embed Pods Frameworks */, - F63B411E9BE4F0BD28A10E5E /* [CP] Copy Pods Resources */, + 189A4605ED0B2F88CF1DCFA8 /* [CP] Embed Pods Frameworks */, + F467D0E16E9AD72917847B43 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -211,7 +211,7 @@ }; }; }; - buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "videoplayer" */; compatibilityVersion = "Xcode 12.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -266,29 +266,24 @@ shellPath = /bin/sh; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; }; - 71542B065A5C697B0C5F4B62 /* [CP] Check Pods Manifest.lock */ = { + 0765D094F5C79DBE6CC6386B /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-videoplayer-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 89F9A997AA9C0BD33946880C /* [CP] Embed Pods Frameworks */ = { + 189A4605ED0B2F88CF1DCFA8 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -305,63 +300,68 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer/Pods-videoplayer-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 8DD7997A551817281D5EF9FE /* [CP] Embed Pods Frameworks */ = { + 66FA7E36D064B737784DDFB7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-videoplayer-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - DB2182CF036650761ADDB488 /* [CP] Copy Pods Resources */ = { + C0C880E0881EE98C1792E57D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-videoplayer-videoplayerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-resources.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - F102B4F37B9B875C963CC540 /* [CP] Check Pods Manifest.lock */ = { + E3461B2529EDAA05BCA42787 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-videoplayer-videoplayerTests-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-videoplayer-videoplayerTests/Pods-videoplayer-videoplayerTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - F63B411E9BE4F0BD28A10E5E /* [CP] Copy Pods Resources */ = { + F467D0E16E9AD72917847B43 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -430,7 +430,7 @@ /* Begin XCBuildConfiguration section */ 00E356F61AD99517003FC87E /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 683CFCFD33A7C6BFFC446604 /* Pods-videoplayer-videoplayerTests.debug.xcconfig */; + baseConfigurationReference = CBE734469FBE698BF938A7EF /* Pods-videoplayer-videoplayerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -457,7 +457,7 @@ }; 00E356F71AD99517003FC87E /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 898A15A5BC01E9AED05EA40A /* Pods-videoplayer-videoplayerTests.release.xcconfig */; + baseConfigurationReference = 5112808AA45F803BD0D3F411 /* Pods-videoplayer-videoplayerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; @@ -481,7 +481,7 @@ }; 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C2294C9F98CE00118BC70E0C /* Pods-videoplayer.debug.xcconfig */; + baseConfigurationReference = 6E6DE29C8B861F4D5A4BBDEB /* Pods-videoplayer.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -508,7 +508,7 @@ }; 13B07F951A680F5B00A75B9A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 70576FE834C7A5574E4E1C55 /* Pods-videoplayer.release.xcconfig */; + baseConfigurationReference = 327740C386721461467B91B2 /* Pods-videoplayer.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; @@ -612,6 +612,9 @@ " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -716,6 +719,9 @@ " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + " ${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", ); IPHONEOS_DEPLOYMENT_TARGET = 12.4; LD_RUNPATH_SEARCH_PATHS = ( @@ -764,7 +770,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoPlayer" */ = { + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "videoplayer" */ = { isa = XCConfigurationList; buildConfigurations = ( 83CBBA201A601CBA00E9B192 /* Debug */, diff --git a/examples/basic/src/VideoPlayer.tsx b/examples/basic/src/VideoPlayer.tsx index ed9f06f511..4b657c1b22 100644 --- a/examples/basic/src/VideoPlayer.tsx +++ b/examples/basic/src/VideoPlayer.tsx @@ -97,7 +97,10 @@ class VideoPlayer extends Component { seekerWidth = 0; srcAllPlatformList = [ - require('./broadchurch.mp4'), + { + description: 'local file', + uri: require('./broadchurch.mp4'), + }, { description: '(hls|live) red bull tv', uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8', @@ -120,15 +123,14 @@ class VideoPlayer extends Component { description: 'another bunny (can be saved)', uri: 'https://rawgit.com/mediaelement/mediaelement-files/master/big_buck_bunny.mp4', }, - ]; - - srcIosList = [ { description: 'sintel with subtitles', uri: 'https://bitmovin-a.akamaihd.net/content/sintel/hls/playlist.m3u8', }, ]; + srcIosList = []; + srcAndroidList = [ { description: 'Another live sample', @@ -691,6 +693,9 @@ class VideoPlayer extends Component { }); }}> {this.state.audioTracks.map(track => { + if (!track) { + return; + } return ( - {this.state.textTracks.map(track => ( - - ))} + {this.state.textTracks.map(track => { + if (!track) { + return; + } + return ( + + ); + })} )} diff --git a/examples/basic/tsconfig.json b/examples/basic/tsconfig.json index a2371834a5..36999c7e1d 100644 --- a/examples/basic/tsconfig.json +++ b/examples/basic/tsconfig.json @@ -2,7 +2,8 @@ "extends": "@react-native/typescript-config/tsconfig.json", "compilerOptions": { "paths": { - "react-native-video": ["../../src/index"] + "react-native-video": ["../../src/index"], + "react": ["./node_modules/@types/react"] } }, "include": ["src", "**/*.js"], diff --git a/ios/Video/DataStructures/SubtitleStyle.swift b/ios/Video/DataStructures/SubtitleStyle.swift new file mode 100644 index 0000000000..76bd8632c8 --- /dev/null +++ b/ios/Video/DataStructures/SubtitleStyle.swift @@ -0,0 +1,17 @@ +struct SubtitleStyle { + // Extend with more style properties as needed. + private(set) var opacity: CGFloat + + enum SubtitleStyleKeys { + static let opacity = "opacity" + } + + init(opacity: CGFloat = 1) { + self.opacity = opacity + } + + static func parse(from dictionary: [String: Any]?) -> SubtitleStyle { + let opacity = dictionary?[SubtitleStyleKeys.opacity] as? CGFloat ?? 1 + return SubtitleStyle(opacity: opacity) + } +} diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index c1c93262f2..22db23a8fe 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -47,6 +47,8 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla } } + var subtitleStyle: SubtitleStyle? + var playerItem: AVPlayerItem? { willSet { removePlayerItemObservers() @@ -63,6 +65,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate, AVPla playerItem.add(legibleOutput) metadataOutput.setDelegate(self, queue: .main) legibleOutput.setDelegate(self, queue: .main) + legibleOutput.suppressesPlayerRendering = subtitleStyle?.opacity == 0 ? true : false } } diff --git a/ios/Video/Features/RCTResourceLoaderDelegate.swift b/ios/Video/Features/RCTResourceLoaderDelegate.swift index 48b2859c50..047151faca 100644 --- a/ios/Video/Features/RCTResourceLoaderDelegate.swift +++ b/ios/Video/Features/RCTResourceLoaderDelegate.swift @@ -153,7 +153,8 @@ class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSes base64Certificate: _drm.base64Certificate ).then { spcData in self._requestingCertificate = true - self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? loadingRequest.request.url?.absoluteString ?? "", + self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? "", + "loadedLicenseUrl": loadingRequest.request.url?.absoluteString ?? "", "contentId": contentId ?? "", "spcBase64": spcData.base64EncodedString(options: []), "target": self._reactTag]) diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 9fd6bec82d..9d4e6f66a8 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -364,7 +364,7 @@ enum RCTVideoUtils { static func delay(seconds: Int = 0) -> Promise { return Promise(on: .global()) { fulfill, _ in - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(seconds)) / Double(NSEC_PER_SEC)) { + DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(seconds)) { fulfill(()) } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index fb465b67a6..a1d34063fa 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -13,7 +13,6 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH private var _player: AVPlayer? private var _playerItem: AVPlayerItem? private var _source: VideoSource? - private var _playerBufferEmpty = true private var _playerLayer: AVPlayerLayer? private var _chapters: [Chapter]? @@ -300,14 +299,38 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } } + var isSetSourceOngoing = false + var nextSource: NSDictionary? + + func applyNextSource() { + if self.nextSource != nil { + DebugLog("apply next source") + self.isSetSourceOngoing = false + let nextSrc = self.nextSource + self.nextSource = nil + self.setSrc(nextSrc) + } + } + // MARK: - Player and source @objc func setSrc(_ source: NSDictionary!) { + if self.isSetSourceOngoing || self.nextSource != nil { + DebugLog("setSrc buffer request") + self._player?.replaceCurrentItem(with: nil) + nextSource = source + return + } + self.isSetSourceOngoing = true + let dispatchClosure = { self._source = VideoSource(source) if self._source?.uri == nil || self._source?.uri == "" { self._player?.replaceCurrentItem(with: nil) + self.isSetSourceOngoing = false + self.applyNextSource() + DebugLog("setSrc Stopping playback") return } self.removePlayerLayer() @@ -321,9 +344,13 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH guard let self else { throw NSError(domain: "", code: 0, userInfo: nil) } guard let source = self._source else { DebugLog("The source not exist") + self.isSetSourceOngoing = false + self.applyNextSource() throw NSError(domain: "", code: 0, userInfo: nil) } if let uri = source.uri, uri.starts(with: "ph://") { + self.isSetSourceOngoing = false + self.applyNextSource() return Promise { RCTVideoUtils.preparePHAsset(uri: uri).then { asset in return self.playerItemPrepareText(asset: asset, assetOptions: nil, uri: source.uri ?? "") @@ -334,6 +361,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH let asset = assetResult.asset, let assetOptions = assetResult.assetOptions else { DebugLog("Could not find video URL in source '\(String(describing: self._source))'") + self.isSetSourceOngoing = false + self.applyNextSource() throw NSError(domain: "", code: 0, userInfo: nil) } @@ -361,7 +390,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH return self.playerItemPrepareText(asset: asset, assetOptions: assetOptions, uri: source.uri ?? "") }.then { [weak self] (playerItem: AVPlayerItem!) in guard let self else { throw NSError(domain: "", code: 0, userInfo: nil) } - + if !self.isSetSourceOngoing { + DebugLog("setSrc has been canceled last step") + return + } self._player?.pause() self._playerItem = playerItem self._playerObserver.playerItem = self._playerItem @@ -402,8 +434,16 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH "drm": self._drm?.json ?? NSNull(), "target": self.reactTag, ]) - }.catch { _ in } + self.isSetSourceOngoing = false + self.applyNextSource() + }.catch { error in + DebugLog("An error occurred: \(error.localizedDescription)") + self.onVideoError?(["error": error.localizedDescription]) + self.isSetSourceOngoing = false + self.applyNextSource() + } self._videoLoadStarted = true + self.applyNextSource() } DispatchQueue.global(qos: .default).async(execute: dispatchClosure) } @@ -953,6 +993,12 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _playerObserver.playerLayer = nil } + @objc + func setSubtitleStyle(_ style: [String: Any]) { + let subtitleStyle = SubtitleStyle.parse(from: style) + _playerObserver.subtitleStyle = subtitleStyle + } + // MARK: - RCTVideoPlayerViewControllerDelegate func videoPlayerViewControllerWillDismiss(playerViewController: AVPlayerViewController) { @@ -1118,6 +1164,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } func handleReadyForDisplay(changeObject _: Any, change _: NSKeyValueObservedChange) { + onVideoBuffer?(["isBuffering": false, "target": reactTag as Any]) onReadyForDisplay?([ "target": reactTag, ]) @@ -1248,16 +1295,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } func handlePlaybackBufferKeyEmpty(playerItem _: AVPlayerItem, change _: NSKeyValueObservedChange) { - _playerBufferEmpty = true onVideoBuffer?(["isBuffering": true, "target": reactTag as Any]) } // Continue playing (or not if paused) after being paused due to hitting an unbuffered zone. func handlePlaybackLikelyToKeepUp(playerItem _: AVPlayerItem, change _: NSKeyValueObservedChange) { - if (!(_controls || _fullscreenPlayerPresented) || _playerBufferEmpty) && ((_playerItem?.isPlaybackLikelyToKeepUp) == true) { - setPaused(_paused) - } - _playerBufferEmpty = false onVideoBuffer?(["isBuffering": false, "target": reactTag as Any]) } @@ -1365,10 +1407,16 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH if _repeat { let item: AVPlayerItem! = notification.object as? AVPlayerItem - item.seek(to: _source?.cropStart != nil ? CMTime(value: _source!.cropStart!, timescale: 1000) : CMTime.zero, completionHandler: nil) - self.applyModifiers() + item.seek( + to: _source?.cropStart != nil ? CMTime(value: _source!.cropStart!, timescale: 1000) : CMTime.zero, + toleranceBefore: CMTime.zero, + toleranceAfter: CMTime.zero, + completionHandler: { [weak self] _ in + guard let self else { return } + self.applyModifiers() + } + ) } else { - self.setPaused(true) _playerObserver.removePlayerTimeObserver() } } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index e30f6c9cb5..de1140ef16 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -37,7 +37,7 @@ @interface RCT_EXTERN_MODULE (RCTVideoManager, RCTViewManager) RCT_EXPORT_VIEW_PROPERTY(progressUpdateInterval, float); RCT_EXPORT_VIEW_PROPERTY(restoreUserInterfaceForPIPStopCompletionHandler, BOOL); RCT_EXPORT_VIEW_PROPERTY(localSourceEncryptionKeyScheme, NSString); - +RCT_EXPORT_VIEW_PROPERTY(subtitleStyle, NSDictionary); /* Should support: onLoadStart, onLoad, and onError to stay consistent with Image */ RCT_EXPORT_VIEW_PROPERTY(onVideoLoadStart, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onVideoLoad, RCTDirectEventBlock); diff --git a/package.json b/package.json index f15c017e62..ff0ae78dc6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-video", - "version": "6.0.0-beta.5", + "version": "6.0.0-beta.6", "description": "A