Added onTimedMetadata callback for iOS player and Exoplayer (#487)
* added listener for timedMetadata event. * added callback in RCTVideo for the timed metadata * exposing onTimedMetadata to JS * added forgotten method declaration * returning array of string values * added metadata type to the array * added onMetadata method * overridden onMetadata method on exoplayer2 * added format of return value from onMetadata * added function reference in README file
This commit is contained in:
		
				
					committed by
					
						 Matt Apperson
						Matt Apperson
					
				
			
			
				
	
			
			
			
						parent
						
							07ac819a46
						
					
				
				
					commit
					d792427ce1
				
			| @@ -151,6 +151,7 @@ using System.Collections.Generic; | |||||||
|        onEnd={this.onEnd}                      // Callback when playback finishes |        onEnd={this.onEnd}                      // Callback when playback finishes | ||||||
|        onError={this.videoError}               // Callback when video cannot be loaded |        onError={this.videoError}               // Callback when video cannot be loaded | ||||||
|        onBuffer={this.onBuffer}                // Callback when remote video is buffering |        onBuffer={this.onBuffer}                // Callback when remote video is buffering | ||||||
|  |        onTimedMetadata={this.onTimedMetadata}  // Callback when the stream receive some metadata | ||||||
|        style={styles.backgroundVideo} /> |        style={styles.backgroundVideo} /> | ||||||
|  |  | ||||||
| // Later to trigger fullscreen | // Later to trigger fullscreen | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								Video.js
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								Video.js
									
									
									
									
									
								
							| @@ -79,6 +79,12 @@ export default class Video extends Component { | |||||||
|     } |     } | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   _onTimedMetadata = (event) => { | ||||||
|  |     if (this.props.onTimedMetadata) { | ||||||
|  |       this.props.onTimedMetadata(event.nativeEvent); | ||||||
|  |     } | ||||||
|  |   }; | ||||||
|  |  | ||||||
|   _onFullscreenPlayerWillPresent = (event) => { |   _onFullscreenPlayerWillPresent = (event) => { | ||||||
|     if (this.props.onFullscreenPlayerWillPresent) { |     if (this.props.onFullscreenPlayerWillPresent) { | ||||||
|       this.props.onFullscreenPlayerWillPresent(event.nativeEvent); |       this.props.onFullscreenPlayerWillPresent(event.nativeEvent); | ||||||
| @@ -191,6 +197,7 @@ export default class Video extends Component { | |||||||
|       onVideoSeek: this._onSeek, |       onVideoSeek: this._onSeek, | ||||||
|       onVideoEnd: this._onEnd, |       onVideoEnd: this._onEnd, | ||||||
|       onVideoBuffer: this._onBuffer, |       onVideoBuffer: this._onBuffer, | ||||||
|  |       onTimedMetadata: this._onTimedMetadata, | ||||||
|       onVideoFullscreenPlayerWillPresent: this._onFullscreenPlayerWillPresent, |       onVideoFullscreenPlayerWillPresent: this._onFullscreenPlayerWillPresent, | ||||||
|       onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent, |       onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent, | ||||||
|       onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss, |       onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss, | ||||||
| @@ -248,6 +255,7 @@ Video.propTypes = { | |||||||
|   onVideoProgress: PropTypes.func, |   onVideoProgress: PropTypes.func, | ||||||
|   onVideoSeek: PropTypes.func, |   onVideoSeek: PropTypes.func, | ||||||
|   onVideoEnd: PropTypes.func, |   onVideoEnd: PropTypes.func, | ||||||
|  |   onTimedMetadata: PropTypes.func, | ||||||
|   onVideoFullscreenPlayerWillPresent: PropTypes.func, |   onVideoFullscreenPlayerWillPresent: PropTypes.func, | ||||||
|   onVideoFullscreenPlayerDidPresent: PropTypes.func, |   onVideoFullscreenPlayerDidPresent: PropTypes.func, | ||||||
|   onVideoFullscreenPlayerWillDismiss: PropTypes.func, |   onVideoFullscreenPlayerWillDismiss: PropTypes.func, | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import android.annotation.TargetApi; | |||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.support.v4.content.ContextCompat; | import android.support.v4.content.ContextCompat; | ||||||
| import android.util.AttributeSet; | import android.util.AttributeSet; | ||||||
|  | import android.util.Log; | ||||||
| import android.view.Gravity; | import android.view.Gravity; | ||||||
| import android.view.SurfaceView; | import android.view.SurfaceView; | ||||||
| import android.view.TextureView; | import android.view.TextureView; | ||||||
| @@ -16,6 +17,8 @@ import com.google.android.exoplayer2.ExoPlaybackException; | |||||||
| import com.google.android.exoplayer2.ExoPlayer; | import com.google.android.exoplayer2.ExoPlayer; | ||||||
| import com.google.android.exoplayer2.SimpleExoPlayer; | import com.google.android.exoplayer2.SimpleExoPlayer; | ||||||
| import com.google.android.exoplayer2.Timeline; | import com.google.android.exoplayer2.Timeline; | ||||||
|  | import com.google.android.exoplayer2.metadata.Metadata; | ||||||
|  | import com.google.android.exoplayer2.metadata.MetadataRenderer; | ||||||
| import com.google.android.exoplayer2.source.TrackGroupArray; | import com.google.android.exoplayer2.source.TrackGroupArray; | ||||||
| import com.google.android.exoplayer2.text.Cue; | import com.google.android.exoplayer2.text.Cue; | ||||||
| import com.google.android.exoplayer2.text.TextRenderer; | import com.google.android.exoplayer2.text.TextRenderer; | ||||||
| @@ -96,6 +99,7 @@ public final class ExoPlayerView extends FrameLayout { | |||||||
|             this.player.setVideoListener(null); |             this.player.setVideoListener(null); | ||||||
|             this.player.removeListener(componentListener); |             this.player.removeListener(componentListener); | ||||||
|             this.player.setVideoSurface(null); |             this.player.setVideoSurface(null); | ||||||
|  |             this.player.setMetadataOutput(componentListener); | ||||||
|         } |         } | ||||||
|         this.player = player; |         this.player = player; | ||||||
|         shutterView.setVisibility(VISIBLE); |         shutterView.setVisibility(VISIBLE); | ||||||
| @@ -108,6 +112,7 @@ public final class ExoPlayerView extends FrameLayout { | |||||||
|             player.setVideoListener(componentListener); |             player.setVideoListener(componentListener); | ||||||
|             player.addListener(componentListener); |             player.addListener(componentListener); | ||||||
|             player.setTextOutput(componentListener); |             player.setTextOutput(componentListener); | ||||||
|  |             player.setMetadataOutput(componentListener); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -161,7 +166,7 @@ public final class ExoPlayerView extends FrameLayout { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     private final class ComponentListener implements SimpleExoPlayer.VideoListener, |     private final class ComponentListener implements SimpleExoPlayer.VideoListener, | ||||||
|             TextRenderer.Output, ExoPlayer.EventListener { |             TextRenderer.Output, ExoPlayer.EventListener, MetadataRenderer.Output { | ||||||
|  |  | ||||||
|         // TextRenderer.Output implementation |         // TextRenderer.Output implementation | ||||||
|  |  | ||||||
| @@ -220,6 +225,10 @@ public final class ExoPlayerView extends FrameLayout { | |||||||
|             updateForCurrentTrackSelections(); |             updateForCurrentTrackSelections(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         @Override | ||||||
|  |         public void onMetadata(Metadata metadata) { | ||||||
|  |             Log.d("onMetadata", "onMetadata"); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -26,6 +26,8 @@ import com.google.android.exoplayer2.Timeline; | |||||||
| import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; | import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; | ||||||
| import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; | import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; | ||||||
| import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; | import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; | ||||||
|  | import com.google.android.exoplayer2.metadata.Metadata; | ||||||
|  | import com.google.android.exoplayer2.metadata.MetadataRenderer; | ||||||
| import com.google.android.exoplayer2.source.ExtractorMediaSource; | import com.google.android.exoplayer2.source.ExtractorMediaSource; | ||||||
| import com.google.android.exoplayer2.source.LoopingMediaSource; | import com.google.android.exoplayer2.source.LoopingMediaSource; | ||||||
| import com.google.android.exoplayer2.source.MediaSource; | import com.google.android.exoplayer2.source.MediaSource; | ||||||
| @@ -49,11 +51,14 @@ import java.net.CookieManager; | |||||||
| import java.net.CookiePolicy; | import java.net.CookiePolicy; | ||||||
|  |  | ||||||
| @SuppressLint("ViewConstructor") | @SuppressLint("ViewConstructor") | ||||||
|  |  | ||||||
| class ReactExoplayerView extends FrameLayout implements | class ReactExoplayerView extends FrameLayout implements | ||||||
|         LifecycleEventListener, |         LifecycleEventListener, | ||||||
|         ExoPlayer.EventListener, |         ExoPlayer.EventListener, | ||||||
|         BecomingNoisyListener, |         BecomingNoisyListener, | ||||||
|         AudioManager.OnAudioFocusChangeListener { |         AudioManager.OnAudioFocusChangeListener, | ||||||
|  |         MetadataRenderer.Output | ||||||
|  |     { | ||||||
|  |  | ||||||
|     private static final String TAG = "ReactExoplayerView"; |     private static final String TAG = "ReactExoplayerView"; | ||||||
|  |  | ||||||
| @@ -192,6 +197,7 @@ class ReactExoplayerView extends FrameLayout implements | |||||||
|             trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); |             trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); | ||||||
|             player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, new DefaultLoadControl()); |             player = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, new DefaultLoadControl()); | ||||||
|             player.addListener(this); |             player.addListener(this); | ||||||
|  |             player.setMetadataOutput(this); | ||||||
|             exoPlayerView.setPlayer(player); |             exoPlayerView.setPlayer(player); | ||||||
|             if (isTimelineStatic) { |             if (isTimelineStatic) { | ||||||
|                 if (playerPosition == C.TIME_UNSET) { |                 if (playerPosition == C.TIME_UNSET) { | ||||||
| @@ -247,6 +253,7 @@ class ReactExoplayerView extends FrameLayout implements | |||||||
|                 playerPosition = player.getCurrentPosition(); |                 playerPosition = player.getCurrentPosition(); | ||||||
|             } |             } | ||||||
|             player.release(); |             player.release(); | ||||||
|  |             player.setMetadataOutput(null); | ||||||
|             player = null; |             player = null; | ||||||
|             trackSelector = null; |             trackSelector = null; | ||||||
|         } |         } | ||||||
| @@ -480,6 +487,11 @@ class ReactExoplayerView extends FrameLayout implements | |||||||
|         playerNeedsSource = true; |         playerNeedsSource = true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public void onMetadata(Metadata metadata) { | ||||||
|  |         eventEmitter.timedMetadata(metadata); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // ReactExoplayerViewManager public api |     // ReactExoplayerViewManager public api | ||||||
|  |  | ||||||
|     public void setSrc(final Uri uri, final String extension) { |     public void setSrc(final Uri uri, final String extension) { | ||||||
|   | |||||||
| @@ -5,11 +5,18 @@ import android.view.View; | |||||||
|  |  | ||||||
| import com.facebook.react.bridge.Arguments; | import com.facebook.react.bridge.Arguments; | ||||||
| import com.facebook.react.bridge.ReactContext; | import com.facebook.react.bridge.ReactContext; | ||||||
|  | import com.facebook.react.bridge.WritableArray; | ||||||
| import com.facebook.react.bridge.WritableMap; | import com.facebook.react.bridge.WritableMap; | ||||||
| import com.facebook.react.uimanager.events.RCTEventEmitter; | import com.facebook.react.uimanager.events.RCTEventEmitter; | ||||||
|  | import com.google.android.exoplayer2.metadata.Metadata; | ||||||
|  | import com.google.android.exoplayer2.metadata.id3.BinaryFrame; | ||||||
|  | import com.google.android.exoplayer2.metadata.id3.Id3Frame; | ||||||
|  | import com.google.android.exoplayer2.metadata.id3.TxxxFrame; | ||||||
|  |  | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.util.HashMap; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
| class VideoEventEmitter { | class VideoEventEmitter { | ||||||
|  |  | ||||||
| @@ -32,6 +39,7 @@ class VideoEventEmitter { | |||||||
|     private static final String EVENT_READY = "onReadyForDisplay"; |     private static final String EVENT_READY = "onReadyForDisplay"; | ||||||
|     private static final String EVENT_BUFFER = "onVideoBuffer"; |     private static final String EVENT_BUFFER = "onVideoBuffer"; | ||||||
|     private static final String EVENT_IDLE = "onVideoIdle"; |     private static final String EVENT_IDLE = "onVideoIdle"; | ||||||
|  |     private static final String EVENT_TIMED_METADATA = "onTimedMetadata"; | ||||||
|     private static final String EVENT_AUDIO_BECOMING_NOISY = "onAudioBecomingNoisy"; |     private static final String EVENT_AUDIO_BECOMING_NOISY = "onAudioBecomingNoisy"; | ||||||
|     private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged"; |     private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged"; | ||||||
|  |  | ||||||
| @@ -47,6 +55,7 @@ class VideoEventEmitter { | |||||||
|             EVENT_READY, |             EVENT_READY, | ||||||
|             EVENT_BUFFER, |             EVENT_BUFFER, | ||||||
|             EVENT_IDLE, |             EVENT_IDLE, | ||||||
|  |             EVENT_TIMED_METADATA, | ||||||
|             EVENT_AUDIO_BECOMING_NOISY, |             EVENT_AUDIO_BECOMING_NOISY, | ||||||
|             EVENT_AUDIO_FOCUS_CHANGE, |             EVENT_AUDIO_FOCUS_CHANGE, | ||||||
|     }; |     }; | ||||||
| @@ -64,6 +73,7 @@ class VideoEventEmitter { | |||||||
|             EVENT_READY, |             EVENT_READY, | ||||||
|             EVENT_BUFFER, |             EVENT_BUFFER, | ||||||
|             EVENT_IDLE, |             EVENT_IDLE, | ||||||
|  |             EVENT_TIMED_METADATA, | ||||||
|             EVENT_AUDIO_BECOMING_NOISY, |             EVENT_AUDIO_BECOMING_NOISY, | ||||||
|             EVENT_AUDIO_FOCUS_CHANGE, |             EVENT_AUDIO_FOCUS_CHANGE, | ||||||
|     }) |     }) | ||||||
| @@ -92,6 +102,9 @@ class VideoEventEmitter { | |||||||
|     private static final String EVENT_PROP_ERROR_STRING = "errorString"; |     private static final String EVENT_PROP_ERROR_STRING = "errorString"; | ||||||
|     private static final String EVENT_PROP_ERROR_EXCEPTION = ""; |     private static final String EVENT_PROP_ERROR_EXCEPTION = ""; | ||||||
|  |  | ||||||
|  |     private static final String EVENT_PROP_TIMED_METADATA = "metadata"; | ||||||
|  |  | ||||||
|  |  | ||||||
|     void setViewId(int viewId) { |     void setViewId(int viewId) { | ||||||
|         this.viewId = viewId; |         this.viewId = viewId; | ||||||
|     } |     } | ||||||
| @@ -168,6 +181,36 @@ class VideoEventEmitter { | |||||||
|         receiveEvent(EVENT_ERROR, event); |         receiveEvent(EVENT_ERROR, event); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     void timedMetadata(Metadata metadata) { | ||||||
|  |         WritableArray metadataArray = Arguments.createArray(); | ||||||
|  |  | ||||||
|  |         for (int i = 0; i < metadata.length(); i++) { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             Id3Frame frame = (Id3Frame) metadata.get(i); | ||||||
|  |  | ||||||
|  |             String value = ""; | ||||||
|  |  | ||||||
|  |             if (frame instanceof TxxxFrame) { | ||||||
|  |                 TxxxFrame txxxFrame = (TxxxFrame) frame; | ||||||
|  |                 value = txxxFrame.value; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             String identifier = frame.id; | ||||||
|  |  | ||||||
|  |             WritableMap map = Arguments.createMap(); | ||||||
|  |             map.putString("identifier", identifier); | ||||||
|  |             map.putString("value", value); | ||||||
|  |  | ||||||
|  |             metadataArray.pushMap(map); | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         WritableMap event = Arguments.createMap(); | ||||||
|  |         event.putArray(EVENT_PROP_TIMED_METADATA, metadataArray); | ||||||
|  |         receiveEvent(EVENT_TIMED_METADATA, event); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     void audioFocusChanged(boolean hasFocus) { |     void audioFocusChanged(boolean hasFocus) { | ||||||
|         WritableMap map = Arguments.createMap(); |         WritableMap map = Arguments.createMap(); | ||||||
|         map.putBoolean(EVENT_PROP_HAS_AUDIO_FOCUS, hasFocus); |         map.putBoolean(EVENT_PROP_HAS_AUDIO_FOCUS, hasFocus); | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoProgress; | ||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoSeek; | ||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoEnd; | ||||||
|  | @property (nonatomic, copy) RCTBubblingEventBlock onTimedMetadata; | ||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillPresent; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillPresent; | ||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidPresent; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerDidPresent; | ||||||
| @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillDismiss; | @property (nonatomic, copy) RCTBubblingEventBlock onVideoFullscreenPlayerWillDismiss; | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ static NSString *const playbackLikelyToKeepUpKeyPath = @"playbackLikelyToKeepUp" | |||||||
| static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; | static NSString *const playbackBufferEmptyKeyPath = @"playbackBufferEmpty"; | ||||||
| static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; | static NSString *const readyForDisplayKeyPath = @"readyForDisplay"; | ||||||
| static NSString *const playbackRate = @"rate"; | static NSString *const playbackRate = @"rate"; | ||||||
|  | static NSString *const timedMetadata = @"timedMetadata"; | ||||||
|  |  | ||||||
| @implementation RCTVideo | @implementation RCTVideo | ||||||
| { | { | ||||||
| @@ -226,6 +227,7 @@ static NSString *const playbackRate = @"rate"; | |||||||
|   [_playerItem addObserver:self forKeyPath:statusKeyPath options:0 context:nil]; |   [_playerItem addObserver:self forKeyPath:statusKeyPath options:0 context:nil]; | ||||||
|   [_playerItem addObserver:self forKeyPath:playbackBufferEmptyKeyPath options:0 context:nil]; |   [_playerItem addObserver:self forKeyPath:playbackBufferEmptyKeyPath options:0 context:nil]; | ||||||
|   [_playerItem addObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath options:0 context:nil]; |   [_playerItem addObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath options:0 context:nil]; | ||||||
|  |   [_playerItem addObserver:self forKeyPath:timedMetadata options:NSKeyValueObservingOptionNew context:nil]; | ||||||
|   _playerItemObserversSet = YES; |   _playerItemObserversSet = YES; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -238,6 +240,7 @@ static NSString *const playbackRate = @"rate"; | |||||||
|     [_playerItem removeObserver:self forKeyPath:statusKeyPath]; |     [_playerItem removeObserver:self forKeyPath:statusKeyPath]; | ||||||
|     [_playerItem removeObserver:self forKeyPath:playbackBufferEmptyKeyPath]; |     [_playerItem removeObserver:self forKeyPath:playbackBufferEmptyKeyPath]; | ||||||
|     [_playerItem removeObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath]; |     [_playerItem removeObserver:self forKeyPath:playbackLikelyToKeepUpKeyPath]; | ||||||
|  |     [_playerItem removeObserver:self forKeyPath:timedMetadata]; | ||||||
|     _playerItemObserversSet = NO; |     _playerItemObserversSet = NO; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -318,6 +321,34 @@ static NSString *const playbackRate = @"rate"; | |||||||
| { | { | ||||||
|    if (object == _playerItem) { |    if (object == _playerItem) { | ||||||
|  |  | ||||||
|  |     // When timeMetadata is read the event onTimedMetadata is triggered | ||||||
|  |     if ([keyPath isEqualToString: timedMetadata]) | ||||||
|  |     { | ||||||
|  |  | ||||||
|  |          | ||||||
|  |         NSArray<AVMetadataItem *> *items = [change objectForKey:@"new"]; | ||||||
|  |         if (items && ![items isEqual:[NSNull null]] && items.count > 0) { | ||||||
|  |              | ||||||
|  |             NSMutableArray *array = [NSMutableArray new]; | ||||||
|  |             for (AVMetadataItem *item in items) { | ||||||
|  |                  | ||||||
|  |                 NSString *value = item.value; | ||||||
|  |                 NSString *identifier = item.identifier; | ||||||
|  |                  | ||||||
|  |                 if (![value isEqual: [NSNull null]]) { | ||||||
|  |                     NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:@[value, identifier] forKeys:@[@"value", @"identifier"]]; | ||||||
|  |                      | ||||||
|  |                     [array addObject:dictionary]; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             self.onTimedMetadata(@{ | ||||||
|  |                                    @"target": self.reactTag, | ||||||
|  |                                    @"metadata": array | ||||||
|  |                                    }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if ([keyPath isEqualToString:statusKeyPath]) { |     if ([keyPath isEqualToString:statusKeyPath]) { | ||||||
|       // Handle player item status change. |       // Handle player item status change. | ||||||
|       if (_playerItem.status == AVPlayerItemStatusReadyToPlay) { |       if (_playerItem.status == AVPlayerItemStatusReadyToPlay) { | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(onVideoError, RCTBubblingEventBlock); | |||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoProgress, RCTBubblingEventBlock); | ||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoSeek, RCTBubblingEventBlock); | ||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoEnd, RCTBubblingEventBlock); | ||||||
|  | RCT_EXPORT_VIEW_PROPERTY(onTimedMetadata, RCTBubblingEventBlock); | ||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillPresent, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillPresent, RCTBubblingEventBlock); | ||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidPresent, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerDidPresent, RCTBubblingEventBlock); | ||||||
| RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillDismiss, RCTBubblingEventBlock); | RCT_EXPORT_VIEW_PROPERTY(onVideoFullscreenPlayerWillDismiss, RCTBubblingEventBlock); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user