From 6a49cba273fa0a47e106f4abb8caeb4ab6dbe4c8 Mon Sep 17 00:00:00 2001 From: Olivier Bouillet <62574056+freeboub@users.noreply.github.com> Date: Thu, 1 Feb 2024 08:56:00 +0100 Subject: [PATCH] feat: implement onAudioTracks and onTextTracks on ios (#3503) * feat: implement onAudioTracks and onTextTracks on ios * chore: lint code * fix: rework previous fix to fix linter and be more aligned with architecture --------- Co-authored-by: olivier --- docs/pages/component/events.md | 4 ++-- ios/Video/Features/RCTPlayerObserver.swift | 10 ++++++++++ ios/Video/RCTVideo.swift | 11 +++++++++++ ios/Video/RCTVideoManager.m | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/docs/pages/component/events.md b/docs/pages/component/events.md index 0b814728..f45a4477 100644 --- a/docs/pages/component/events.md +++ b/docs/pages/component/events.md @@ -6,7 +6,7 @@ This page shows the list of available callbacks to handle player notifications |-------------------------------------------------------------------------------------------------|---------------------------| | [onAudioBecomingNoisy](#onaudiobecomingnoisy) | Android, iOS | | [onAudioFocusChanged](#onaudiofocuschanged) | Android | -| [onAudioTracks](#onaudiotracks) | Android | +| [onAudioTracks](#onaudiotracks) | Android, iOS | | [onBandwidthUpdate](#onbandwidthupdate) | Android | | [onBuffer](#onbuffer) | Android, iOS | | [onEnd](#onend) | All | @@ -27,7 +27,7 @@ This page shows the list of available callbacks to handle player notifications | [onRestoreUserInterfaceForPictureInPictureStop](#onrestoreuserinterfaceforpictureinpicturestop) | iOS, visionOS | | [onSeek](#onseek) | All | | [onTimedMetadata](#ontimedmetadata) | Android, iOS, visionOS | -| [onTextTracks](#ontexttracks) | Android | +| [onTextTracks](#ontexttracks) | Android, iOS | | [onVideoTracks](#onvideotracks) | Android | | [onVolumeChange](#onvolumechange) | Android, iOS, visionOS | diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index 00227feb..dfdd8320 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -25,6 +25,7 @@ protocol RCTPlayerObserverHandler: RCTPlayerObserverHandlerObjc { func handleVolumeChange(player: AVPlayer, change: NSKeyValueObservedChange) func handleExternalPlaybackActiveChange(player: AVPlayer, change: NSKeyValueObservedChange) func handleViewControllerOverlayViewFrameChange(overlayView: UIView, change: NSKeyValueObservedChange) + func handleTracksChange(playerItem: AVPlayerItem, change: NSKeyValueObservedChange<[AVPlayerItemTrack]>) } // MARK: - RCTPlayerObserver @@ -96,6 +97,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate { private var _playerViewControllerReadyForDisplayObserver: NSKeyValueObservation? private var _playerLayerReadyForDisplayObserver: NSKeyValueObservation? private var _playerViewControllerOverlayFrameObserver: NSKeyValueObservation? + private var _playerTracksObserver: NSKeyValueObservation? deinit { if let _handlers { @@ -141,6 +143,13 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate { options: [.new, .old], changeHandler: _handlers.handlePlaybackLikelyToKeepUp ) + + // observe tracks update + _playerTracksObserver = playerItem.observe( + \.tracks, + options: [.new, .old], + changeHandler: _handlers.handleTracksChange + ) } func removePlayerItemObservers() { @@ -148,6 +157,7 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate { _playerPlaybackBufferEmptyObserver?.invalidate() _playerPlaybackLikelyToKeepUpObserver?.invalidate() _playerTimedMetadataObserver?.invalidate() + _playerTracksObserver?.invalidate() } func addPlayerViewControllerObservers() { diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index d227e89e..70ae6e81 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -116,6 +116,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc var onRestoreUserInterfaceForPictureInPictureStop: RCTDirectEventBlock? @objc var onGetLicense: RCTDirectEventBlock? @objc var onReceiveAdEvent: RCTDirectEventBlock? + @objc var onTextTracks: RCTDirectEventBlock? + @objc var onAudioTracks: RCTDirectEventBlock? @objc func _onPictureInPictureStatusChanged() { @@ -367,7 +369,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } self._player = self._player ?? AVPlayer() + self._player?.replaceCurrentItem(with: playerItem) + self._playerObserver.player = self._player self.applyModifiers() self._player?.actionAtItemEnd = .none @@ -1371,4 +1375,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH onVideoBandwidthUpdate?(["bitrate": lastEvent.observedBitrate, "target": reactTag]) } + + func handleTracksChange(playerItem _: AVPlayerItem, change _: NSKeyValueObservedChange<[AVPlayerItemTrack]>) { + all(RCTVideoUtils.getAudioTrackInfo(self._player), RCTVideoUtils.getTextTrackInfo(self._player)).then { audioTracks, textTracks in + self.onTextTracks?(["textTracks": textTracks]) + self.onAudioTracks?(["audioTracks": audioTracks]) + } + } } diff --git a/ios/Video/RCTVideoManager.m b/ios/Video/RCTVideoManager.m index 0a02595b..f7b5bcb5 100644 --- a/ios/Video/RCTVideoManager.m +++ b/ios/Video/RCTVideoManager.m @@ -64,6 +64,8 @@ RCT_EXPORT_VIEW_PROPERTY(onGetLicense, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onPictureInPictureStatusChanged, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onRestoreUserInterfaceForPictureInPictureStop, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onReceiveAdEvent, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onTextTracks, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onAudioTracks, RCTDirectEventBlock); RCT_EXTERN_METHOD(save : (NSDictionary*)options reactTag