diff --git a/API.md b/API.md index 9ac52616..e15ed199 100644 --- a/API.md +++ b/API.md @@ -316,6 +316,7 @@ var styles = StyleSheet.create({ | Name |Plateforms Support | |--|--| |[onAudioBecomingNoisy](#onaudiobecomingnoisy)|Android, iOS| +|[onAudioTracks](#onAudioTracks)|Android| |[onBandwidthUpdate](#onbandwidthupdate)|Android| |[onBuffer](#onbuffer)|Android, iOS| |[onEnd](#onend)|All| @@ -333,6 +334,8 @@ var styles = StyleSheet.create({ |[onSeek](#onseek)|Android, iOS, Windows UWP| |[onRestoreUserInterfaceForPictureInPictureStop](#onrestoreuserinterfaceforpictureinpicturestop)|iOS| |[onTimedMetadata](#ontimedmetadata)|Android, iOS| +|[onTextTracks](#onTextTracks)|Android| +|[onVideoTracks](#onVideoTracks)|Android| ### Methods @@ -652,6 +655,34 @@ Determine whether to repeat the video when the end is reached Platforms: all + +#### onAudioTracks +Callback function that is called when audio tracks change + +Payload: + +Property | Type | Description +--- | --- | --- +index | number | Internal track ID +title | string | Descriptive name for the track +language | string | 2 letter [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) representing the language +bitrate | number | bitrate of track +type | string | Mime type of track +selected | boolean | true if track is playing + +Example: +``` +{ + audioTracks: [ + { language: 'es', title: 'Spanish', type: 'audio/mpeg', index: 0, selected: true }, + { language: 'en', title: 'English', type: 'audio/mpeg', index: 1 } + ], +} +``` + + +Platforms: Android + #### reportBandwidth Determine whether to generate onBandwidthUpdate events. This is needed due to the high frequency of these events on ExoPlayer. @@ -1228,6 +1259,69 @@ Example: Platforms: Android, iOS +#### onTextTracks +Callback function that is called when text tracks change + +Payload: + +Property | Type | Description +--- | --- | --- +index | number | Internal track ID +title | string | Descriptive name for the track +language | string | 2 letter [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) representing the language +type | string | Mime type of the track
* TextTrackType.SRT - SubRip (.srt)
* TextTrackType.TTML - TTML (.ttml)
* TextTrackType.VTT - WebVTT (.vtt)
iOS only supports VTT, Android supports all 3 +selected | boolean | true if track is playing + + +Example: +``` +{ + textTracks: [ + { + index: 0, + title: 'Any Time You Like', + type: 'srt', + selected: true + } + ] +} +``` + +Platforms: Android + +#### onVideoTracks +Callback function that is called when video tracks change + +Payload: + +Property | Type | Description +--- | --- | --- +trackId | number | Internal track ID +codecs | string | MimeType of codec used for this track +width | number | Track width +height | number | Track height +bitrate | number | Bitrate in bps +selected | boolean | true if track is selected for playing + + +Example: +``` +{ + videoTracks: [ + { + trackId: 0, + codecs: 'video/mp4', + width: 1920, + height: 1080, + bitrate: 10000, + selected: true + } + ] +} +``` + +Platforms: Android + ### Methods Methods operate on a ref to the Video element. You can create a ref using code like: ``` diff --git a/Video.js b/Video.js index dad262b9..a0f47867 100644 --- a/Video.js +++ b/Video.js @@ -116,6 +116,24 @@ export default class Video extends Component { } }; + _onAudioTracks = (event) => { + if (this.props.onAudioTracks) { + this.props.onAudioTracks(event.nativeEvent); + } + }; + + _onTextTracks = (event) => { + if (this.props.onTextTracks) { + this.props.onTextTracks(event.nativeEvent); + } + }; + + _onVideoTracks = (event) => { + if (this.props.onVideoTracks) { + this.props.onVideoTracks(event.nativeEvent); + } + }; + _onError = (event) => { if (this.props.onError) { this.props.onError(event.nativeEvent); @@ -316,6 +334,9 @@ export default class Video extends Component { onVideoLoadStart: this._onLoadStart, onVideoPlaybackStateChanged: this._onPlaybackStateChanged, onVideoLoad: this._onLoad, + onAudioTracks: this._onAudioTracks, + onTextTracks: this._onTextTracks, + onVideoTracks: this._onVideoTracks, onVideoError: this._onError, onVideoProgress: this._onProgress, onVideoSeek: this._onSeek, @@ -495,6 +516,9 @@ Video.propTypes = { onLoadStart: PropTypes.func, onPlaybackStateChanged: PropTypes.func, onLoad: PropTypes.func, + onAudioTracks: PropTypes.func, + onTextTracks: PropTypes.func, + onVideoTracks: PropTypes.func, onBuffer: PropTypes.func, onError: PropTypes.func, onProgress: PropTypes.func, diff --git a/android/src/main/java/com/brentvatne/common/Track.java b/android/src/main/java/com/brentvatne/common/Track.java new file mode 100644 index 00000000..198f9ec7 --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/Track.java @@ -0,0 +1,13 @@ +package com.brentvatne.common; +import android.net.Uri; + +public class Track +{ + public String m_title; + public Uri m_uri; + public String m_mimeType; + public String m_language; + public boolean m_isSelected; + public int m_bitrate; + public int m_index; +} diff --git a/android/src/main/java/com/brentvatne/common/VideoTrack.java b/android/src/main/java/com/brentvatne/common/VideoTrack.java new file mode 100644 index 00000000..6eccd983 --- /dev/null +++ b/android/src/main/java/com/brentvatne/common/VideoTrack.java @@ -0,0 +1,12 @@ +package com.brentvatne.common; + +public class VideoTrack +{ + public int m_width = 0; + public int m_height = 0; + public int m_bitrate = 0; + public String m_codecs = ""; + public int m_id = -1; + public String m_trackId = ""; + public boolean m_isSelected = false; +} diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index 43d4a6ee..1ba9bb6f 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -17,24 +17,21 @@ import android.view.accessibility.CaptioningManager; import android.widget.FrameLayout; import android.widget.ImageButton; +import androidx.annotation.WorkerThread; + +import com.brentvatne.common.Track; +import com.brentvatne.common.VideoTrack; import com.brentvatne.react.R; import com.brentvatne.receiver.AudioBecomingNoisyReceiver; import com.brentvatne.receiver.BecomingNoisyListener; -import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Dynamic; import com.facebook.react.bridge.LifecycleEventListener; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableArray; -import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.ThemedReactContext; -import com.facebook.react.util.RNLog; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlaybackException; -import com.google.android.exoplayer2.drm.MediaDrmCallbackException; -import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.PlaybackException; @@ -47,16 +44,12 @@ import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManagerProvider; -import com.google.android.exoplayer2.drm.ExoMediaDrm; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; -import com.google.android.exoplayer2.drm.MediaDrmCallbackException; import com.google.android.exoplayer2.drm.UnsupportedDrmException; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; -import com.google.android.exoplayer2.mediacodec.MediaCodecRenderer; import com.google.android.exoplayer2.mediacodec.MediaCodecUtil; import com.google.android.exoplayer2.metadata.Metadata; -import com.google.android.exoplayer2.source.BehindLiveWindowException; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; import com.google.android.exoplayer2.source.ProgressiveMediaSource; @@ -72,6 +65,7 @@ import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; import com.google.android.exoplayer2.trackselection.ExoTrackSelection; +import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides; import com.google.android.exoplayer2.trackselection.TrackSelectionOverrides.TrackSelectionOverride; import com.google.android.exoplayer2.ui.PlayerControlView; @@ -80,7 +74,6 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultAllocator; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.source.dash.DashUtil; @@ -88,7 +81,6 @@ import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.Period; import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; import com.google.android.exoplayer2.source.dash.manifest.Representation; -import com.google.android.exoplayer2.source.dash.manifest.Descriptor; import java.net.CookieHandler; import java.net.CookieManager; @@ -98,9 +90,6 @@ import java.util.List; import java.util.Locale; import java.util.UUID; import java.util.Map; -import java.util.Timer; -import java.util.TimerTask; -import java.util.List; import java.lang.Thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -1043,25 +1032,42 @@ class ReactExoplayerView extends FrameLayout implements // Properties that must be accessed on the main thread long duration = player.getDuration(); long currentPosition = player.getCurrentPosition(); - WritableArray audioTrackInfo = getAudioTrackInfo(); - WritableArray textTrackInfo = getTextTrackInfo(); - int trackRendererIndex = getTrackRendererIndex(C.TRACK_TYPE_VIDEO); + ArrayList audioTracks = getAudioTrackInfo(); + ArrayList textTracks = getTextTrackInfo(); - ExecutorService es = Executors.newSingleThreadExecutor(); - es.execute(new Runnable() { - @Override - public void run() { - // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done - eventEmitter.load(duration, currentPosition, width, height, - audioTrackInfo, textTrackInfo, getVideoTrackInfo(trackRendererIndex), trackId); - } - }); + if (this.contentStartTime != -1L) { + ExecutorService es = Executors.newSingleThreadExecutor(); + es.execute(new Runnable() { + @Override + public void run() { + // To prevent ANRs caused by getVideoTrackInfo we run this on a different thread and notify the player only when we're done + ArrayList videoTracks = getVideoTrackInfoFromManifest(); + if (videoTracks != null) { + isUsingContentResolution = true; + } + eventEmitter.load(duration, currentPosition, width, height, + audioTracks, textTracks, videoTracks, trackId ); + + } + }); + return; + } + + ArrayList videoTracks = getVideoTrackInfo(); + + eventEmitter.load(duration, currentPosition, width, height, + audioTracks, textTracks, videoTracks, trackId); } } - private WritableArray getAudioTrackInfo() { - WritableArray audioTracks = Arguments.createArray(); + private static boolean isTrackSelected(TrackSelection selection, TrackGroup group, + int trackIndex){ + return selection != null && selection.getTrackGroup() == group + && selection.indexOf( trackIndex ) != C.INDEX_UNSET; + } + private ArrayList getAudioTrackInfo() { + ArrayList audioTracks = new ArrayList<>(); if (trackSelector == null) { // Likely player is unmounting so no audio tracks are available anymore return audioTracks; @@ -1072,78 +1078,76 @@ class ReactExoplayerView extends FrameLayout implements if (info == null || index == C.INDEX_UNSET) { return audioTracks; } - TrackGroupArray groups = info.getTrackGroups(index); + TrackSelectionArray selectionArray = player.getCurrentTrackSelections(); + TrackSelection selection = selectionArray.get( C.TRACK_TYPE_AUDIO ); + for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - WritableMap audioTrack = Arguments.createMap(); - audioTrack.putInt("index", i); - audioTrack.putString("title", format.id != null ? format.id : ""); - audioTrack.putString("type", format.sampleMimeType); - audioTrack.putString("language", format.language != null ? format.language : ""); - audioTrack.putString("bitrate", format.bitrate == Format.NO_VALUE ? "" - : String.format(Locale.US, "%.2fMbps", format.bitrate / 1000000f)); - audioTracks.pushMap(audioTrack); + TrackGroup group = groups.get(i); + Format format = group.getFormat(0); + Track audioTrack = new Track(); + audioTrack.m_index = i; + audioTrack.m_title = format.id != null ? format.id : ""; + audioTrack.m_mimeType = format.sampleMimeType; + audioTrack.m_language = format.language != null ? format.language : ""; + audioTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate; + audioTrack.m_isSelected = isTrackSelected(selection, group, 0 ); + audioTracks.add(audioTrack); } return audioTracks; } - private WritableArray getVideoTrackInfo(int trackRendererIndex) { - if (this.contentStartTime != -1L) { - WritableArray contentVideoTracks = this.getVideoTrackInfoFromManifest(); - if (contentVideoTracks != null) { - isUsingContentResolution = true; - return contentVideoTracks; - } + private ArrayList getVideoTrackInfo() { + ArrayList videoTracks = new ArrayList<>(); + if (trackSelector == null) { + // Likely player is unmounting so no audio tracks are available anymore + return videoTracks; } - - WritableArray videoTracks = Arguments.createArray(); - MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); - - if (info == null || trackRendererIndex == C.INDEX_UNSET) { + int index = getTrackRendererIndex(C.TRACK_TYPE_VIDEO); + if (info == null || index == C.INDEX_UNSET) { return videoTracks; } - TrackGroupArray groups = info.getTrackGroups(trackRendererIndex); + TrackGroupArray groups = info.getTrackGroups(index); for (int i = 0; i < groups.length; ++i) { TrackGroup group = groups.get(i); for (int trackIndex = 0; trackIndex < group.length; trackIndex++) { Format format = group.getFormat(trackIndex); if (isFormatSupported(format)) { - WritableMap videoTrack = Arguments.createMap(); - videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); - videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); - videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); - videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); - videoTrack.putString("trackId", format.id == null ? String.valueOf(trackIndex) : format.id); - videoTracks.pushMap(videoTrack); + VideoTrack videoTrack = new VideoTrack(); + videoTrack.m_width = format.width == Format.NO_VALUE ? 0 : format.width; + videoTrack.m_height = format.height == Format.NO_VALUE ? 0 : format.height; + videoTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate; + videoTrack.m_codecs = format.codecs != null ? format.codecs : ""; + videoTrack.m_trackId = format.id == null ? String.valueOf(trackIndex) : format.id; + videoTracks.add(videoTrack); } } } - return videoTracks; } - private WritableArray getVideoTrackInfoFromManifest() { + private ArrayList getVideoTrackInfoFromManifest() { return this.getVideoTrackInfoFromManifest(0); } // We need retry count to in case where minefest request fails from poor network conditions - private WritableArray getVideoTrackInfoFromManifest(int retryCount) { + @WorkerThread + private ArrayList getVideoTrackInfoFromManifest(int retryCount) { ExecutorService es = Executors.newSingleThreadExecutor(); final DataSource dataSource = this.mediaDataSourceFactory.createDataSource(); final Uri sourceUri = this.srcUri; final long startTime = this.contentStartTime * 1000 - 100; // s -> ms with 100ms offset - Future result = es.submit(new Callable() { + Future> result = es.submit(new Callable>() { DataSource ds = dataSource; Uri uri = sourceUri; long startTimeUs = startTime * 1000; // ms -> us - public WritableArray call() throws Exception { - WritableArray videoTracks = Arguments.createArray(); + public ArrayList call() throws Exception { + ArrayList videoTracks = new ArrayList<>(); try { DashManifest manifest = DashUtil.loadManifest(this.ds, this.uri); int periodCount = manifest.getPeriodCount(); @@ -1158,19 +1162,18 @@ class ReactExoplayerView extends FrameLayout implements for (int representationIndex = 0; representationIndex < adaptation.representations.size(); representationIndex++) { Representation representation = adaptation.representations.get(representationIndex); Format format = representation.format; - if (representation.presentationTimeOffsetUs <= startTimeUs) { - break; - } - hasFoundContentPeriod = true; - WritableMap videoTrack = Arguments.createMap(); - videoTrack.putInt("width", format.width == Format.NO_VALUE ? 0 : format.width); - videoTrack.putInt("height",format.height == Format.NO_VALUE ? 0 : format.height); - videoTrack.putInt("bitrate", format.bitrate == Format.NO_VALUE ? 0 : format.bitrate); - videoTrack.putString("codecs", format.codecs != null ? format.codecs : ""); - videoTrack.putString("trackId", - format.id == null ? String.valueOf(representationIndex) : format.id); if (isFormatSupported(format)) { - videoTracks.pushMap(videoTrack); + if (representation.presentationTimeOffsetUs <= startTimeUs) { + break; + } + hasFoundContentPeriod = true; + VideoTrack videoTrack = new VideoTrack(); + videoTrack.m_width = format.width == Format.NO_VALUE ? 0 : format.width; + videoTrack.m_height = format.height == Format.NO_VALUE ? 0 : format.height; + videoTrack.m_bitrate = format.bitrate == Format.NO_VALUE ? 0 : format.bitrate; + videoTrack.m_codecs = format.codecs != null ? format.codecs : ""; + videoTrack.m_trackId = format.id == null ? String.valueOf(representationIndex) : format.id; + videoTracks.add(videoTrack); } } if (hasFoundContentPeriod) { @@ -1184,7 +1187,7 @@ class ReactExoplayerView extends FrameLayout implements }); try { - WritableArray results = result.get(3000, TimeUnit.MILLISECONDS); + ArrayList results = result.get(3000, TimeUnit.MILLISECONDS); if (results == null && retryCount < 1) { return this.getVideoTrackInfoFromManifest(++retryCount); } @@ -1195,24 +1198,31 @@ class ReactExoplayerView extends FrameLayout implements return null; } - private WritableArray getTextTrackInfo() { - WritableArray textTracks = Arguments.createArray(); - + private ArrayList getTextTrackInfo() { + ArrayList textTracks = new ArrayList<>(); + if (trackSelector == null) { + return textTracks; + } MappingTrackSelector.MappedTrackInfo info = trackSelector.getCurrentMappedTrackInfo(); int index = getTrackRendererIndex(C.TRACK_TYPE_TEXT); if (info == null || index == C.INDEX_UNSET) { return textTracks; } - + TrackSelectionArray selectionArray = player.getCurrentTrackSelections(); + TrackSelection selection = selectionArray.get( C.TRACK_TYPE_VIDEO ); TrackGroupArray groups = info.getTrackGroups(index); + for (int i = 0; i < groups.length; ++i) { - Format format = groups.get(i).getFormat(0); - WritableMap textTrack = Arguments.createMap(); - textTrack.putInt("index", i); - textTrack.putString("title", format.id != null ? format.id : ""); - textTrack.putString("type", format.sampleMimeType); - textTrack.putString("language", format.language != null ? format.language : ""); - textTracks.pushMap(textTrack); + TrackGroup group = groups.get(i); + Format format = group.getFormat(0); + + Track textTrack = new Track(); + textTrack.m_index = i; + textTrack.m_title = format.id != null ? format.id : ""; + textTrack.m_mimeType = format.sampleMimeType; + textTrack.m_language = format.language != null ? format.language : ""; + textTrack.m_isSelected = isTrackSelected(selection, group, 0 ); + textTracks.add(textTrack); } return textTracks; } @@ -1281,7 +1291,10 @@ class ReactExoplayerView extends FrameLayout implements @Override public void onTracksInfoChanged(TracksInfo tracksInfo) { - // Do nothing. + eventEmitter.textTracks(getTextTrackInfo()); + eventEmitter.audioTracks(getAudioTrackInfo()); + eventEmitter.videoTracks(getVideoTrackInfo()); + } @Override diff --git a/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java index a86edf1c..8de497aa 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java +++ b/android/src/main/java/com/brentvatne/exoplayer/VideoEventEmitter.java @@ -3,6 +3,8 @@ package com.brentvatne.exoplayer; import androidx.annotation.StringDef; import android.view.View; +import com.brentvatne.common.Track; +import com.brentvatne.common.VideoTrack; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableArray; @@ -17,6 +19,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.io.StringWriter; import java.io.PrintWriter; +import java.util.ArrayList; class VideoEventEmitter { @@ -50,6 +53,9 @@ class VideoEventEmitter { private static final String EVENT_AUDIO_BECOMING_NOISY = "onVideoAudioBecomingNoisy"; private static final String EVENT_AUDIO_FOCUS_CHANGE = "onAudioFocusChanged"; private static final String EVENT_PLAYBACK_RATE_CHANGE = "onPlaybackRateChange"; + private static final String EVENT_AUDIO_TRACKS = "onAudioTracks"; + private static final String EVENT_TEXT_TRACKS = "onTextTracks"; + private static final String EVENT_VIDEO_TRACKS = "onVideoTracks"; static final String[] Events = { EVENT_LOAD_START, @@ -72,6 +78,9 @@ class VideoEventEmitter { EVENT_AUDIO_BECOMING_NOISY, EVENT_AUDIO_FOCUS_CHANGE, EVENT_PLAYBACK_RATE_CHANGE, + EVENT_AUDIO_TRACKS, + EVENT_TEXT_TRACKS, + EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, }; @@ -97,6 +106,9 @@ class VideoEventEmitter { EVENT_AUDIO_BECOMING_NOISY, EVENT_AUDIO_FOCUS_CHANGE, EVENT_PLAYBACK_RATE_CHANGE, + EVENT_AUDIO_TRACKS, + EVENT_TEXT_TRACKS, + EVENT_VIDEO_TRACKS, EVENT_BANDWIDTH, }) @interface VideoEvents { @@ -149,12 +161,7 @@ class VideoEventEmitter { receiveEvent(EVENT_LOAD_START, null); } - void load(double duration, double currentPosition, int videoWidth, int videoHeight, - WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) { - WritableMap event = Arguments.createMap(); - event.putDouble(EVENT_PROP_DURATION, duration / 1000D); - event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D); - + WritableMap aspectRatioToNaturalSize(int videoWidth, int videoHeight) { WritableMap naturalSize = Arguments.createMap(); naturalSize.putInt(EVENT_PROP_WIDTH, videoWidth); naturalSize.putInt(EVENT_PROP_HEIGHT, videoHeight); @@ -165,7 +172,79 @@ class VideoEventEmitter { } else { naturalSize.putString(EVENT_PROP_ORIENTATION, "square"); } + return naturalSize; + } + + WritableArray audioTracksToArray(ArrayList audioTracks) { + WritableArray waAudioTracks = Arguments.createArray(); + if( audioTracks != null ){ + for (int i = 0; i < audioTracks.size(); ++i) { + Track format = audioTracks.get(i); + WritableMap audioTrack = Arguments.createMap(); + audioTrack.putInt("index", i); + audioTrack.putString("title", format.m_title != null ? format.m_title : ""); + audioTrack.putString("type", format.m_mimeType != null ? format.m_mimeType : ""); + audioTrack.putString("language", format.m_language != null ? format.m_language : ""); + audioTrack.putInt("bitrate", format.m_bitrate); + audioTrack.putBoolean("selected", format.m_isSelected); + waAudioTracks.pushMap(audioTrack); + } } + return waAudioTracks; + } + + WritableArray videoTracksToArray(ArrayList videoTracks) { + WritableArray waVideoTracks = Arguments.createArray(); + if( videoTracks != null ){ + for (int i = 0; i < videoTracks.size(); ++i) { + VideoTrack vTrack = videoTracks.get(i); + WritableMap videoTrack = Arguments.createMap(); + videoTrack.putInt("width", vTrack.m_width); + videoTrack.putInt("height",vTrack.m_height); + videoTrack.putInt("bitrate", vTrack.m_bitrate); + videoTrack.putString("codecs", vTrack.m_codecs); + videoTrack.putInt("trackId",vTrack.m_id); + videoTrack.putBoolean("selected", vTrack.m_isSelected); + waVideoTracks.pushMap(videoTrack); + } + } + return waVideoTracks; + } + + WritableArray textTracksToArray(ArrayList textTracks) { + WritableArray waTextTracks = Arguments.createArray(); + if (textTracks != null) { + for (int i = 0; i < textTracks.size(); ++i) { + Track format = textTracks.get(i); + WritableMap textTrack = Arguments.createMap(); + textTrack.putInt("index", i); + textTrack.putString("title", format.m_title != null ? format.m_title : ""); + textTrack.putString("type", format.m_mimeType != null ? format.m_mimeType : ""); + textTrack.putString("language", format.m_language != null ? format.m_language : ""); + textTrack.putBoolean("selected", format.m_isSelected); + waTextTracks.pushMap(textTrack); + } + } + return waTextTracks; + } + + public void load(double duration, double currentPosition, int videoWidth, int videoHeight, + ArrayList audioTracks, ArrayList textTracks, ArrayList videoTracks, String trackId){ + WritableArray waAudioTracks = audioTracksToArray(audioTracks); + WritableArray waVideoTracks = videoTracksToArray(videoTracks); + WritableArray waTextTracks = textTracksToArray(textTracks); + + load( duration, currentPosition, videoWidth, videoHeight, waAudioTracks, waTextTracks, waVideoTracks, trackId); + } + + + private void load(double duration, double currentPosition, int videoWidth, int videoHeight, + WritableArray audioTracks, WritableArray textTracks, WritableArray videoTracks, String trackId) { + WritableMap event = Arguments.createMap(); + event.putDouble(EVENT_PROP_DURATION, duration / 1000D); + event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D); + + WritableMap naturalSize = aspectRatioToNaturalSize(videoWidth, videoHeight); event.putMap(EVENT_PROP_NATURAL_SIZE, naturalSize); event.putString(EVENT_PROP_TRACK_ID, trackId); event.putArray(EVENT_PROP_VIDEO_TRACKS, videoTracks); @@ -184,6 +263,26 @@ class VideoEventEmitter { receiveEvent(EVENT_LOAD, event); } + + + WritableMap arrayToObject(String field, WritableArray array) { + WritableMap event = Arguments.createMap(); + event.putArray(field, array); + return event; + } + + public void audioTracks(ArrayList audioTracks){ + receiveEvent(EVENT_AUDIO_TRACKS, arrayToObject(EVENT_PROP_AUDIO_TRACKS, audioTracksToArray(audioTracks))); + } + + public void textTracks(ArrayList textTracks){ + receiveEvent(EVENT_TEXT_TRACKS, arrayToObject(EVENT_PROP_TEXT_TRACKS, textTracksToArray(textTracks))); + } + + public void videoTracks(ArrayList videoTracks){ + receiveEvent(EVENT_VIDEO_TRACKS, arrayToObject(EVENT_PROP_VIDEO_TRACKS, videoTracksToArray(videoTracks))); + } + void progressChanged(double currentPosition, double bufferedDuration, double seekableDuration, double currentPlaybackTime) { WritableMap event = Arguments.createMap(); event.putDouble(EVENT_PROP_CURRENT_TIME, currentPosition / 1000D);