package com.kaltura.playersdk.tracks; import com.kaltura.playersdk.players.KPlayer; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import static com.kaltura.playersdk.utils.LogUtils.LOGD; import static com.kaltura.playersdk.utils.LogUtils.LOGE; /** * Created by gilad.nadav on 25/05/2016. */ public class KTracksManager implements KTrackActions { public static String TAG = "TracksManager"; private KPlayer player; private KTracksContainer tracksContainer; private KTrackActions.VideoTrackEventListener videoTrackEventListener = null; private KTrackActions.AudioTrackEventListener audioTrackEventListener = null; private KTrackActions.TextTrackEventListener textTrackEventListener = null; public KTracksManager(KPlayer player) { this.player = player; initTrackManagerLists(); } public void setVideoTrackEventListener(KTrackActions.VideoTrackEventListener videoTrackEventListener) { this.videoTrackEventListener = videoTrackEventListener; } public void setAudioTrackEventListener(KTrackActions.AudioTrackEventListener audioTrackEventListener) { this.audioTrackEventListener = audioTrackEventListener; } public void setTextTrackEventListener(KTrackActions.TextTrackEventListener textTrackEventListener) { this.textTrackEventListener = textTrackEventListener; } public void removeVideoTrackEventListener() { this.videoTrackEventListener = null; } public void removeAudioTrackEventListener() { this.audioTrackEventListener = null; } public void removeTextTrackEventListener() { this.textTrackEventListener = null; } @Override public void switchTrack(TrackType trackType, int newIndex) { if (isAvailableTracksRelevant(trackType)) { LOGD(TAG, "switchTrack for " + trackType.name() + " newIndex = " + newIndex); player.switchTrack(trackType, newIndex); switch (trackType) { case VIDEO: if (videoTrackEventListener != null) { videoTrackEventListener.onVideoTrackChanged(newIndex); } break; case AUDIO: if (audioTrackEventListener != null) { audioTrackEventListener.onAudioTrackChanged(newIndex); } break; case TEXT: if (textTrackEventListener != null) { textTrackEventListener.onTextTrackChanged(newIndex); } break; } } else { LOGD(TAG, "switchTrack " + trackType.name() + "skipped Reason: track count < 2"); } } @Override public void switchTrackByBitrate(TrackType trackType, final int preferredBitrateKBit) { LOGD(TAG, "switchTrackByBitrate : " + trackType.name() + " preferredBitrateKBit : " + preferredBitrateKBit); if (TrackType.TEXT.equals(trackType)){ return; } List<TrackFormat> tracksList = null; if (TrackType.AUDIO.equals(trackType)){ tracksList = getAudioTrackList(); } else if (TrackType.VIDEO.equals(trackType)){ tracksList = getVideoTrackList(); } else { //unsupported track type return; } if (tracksList == null) { return; } if (tracksList.size() <= 2) { LOGD(TAG, "Skip switchTrackByBitrate, tracksList.size() <= 2"); return; } TrackFormat autoTrackFormat = null; if (tracksList.get(0).bitrate == -1) { autoTrackFormat = tracksList.get(0); tracksList.remove(0); } Comparator <TrackFormat> tracksComperator = new Comparator<TrackFormat>() { @Override public int compare(TrackFormat track1, TrackFormat track2) { if (Math.abs(track1.bitrate - preferredBitrateKBit*1000) > Math.abs(track2.bitrate - preferredBitrateKBit*1000)) { return 1; } else { return -1; } } }; SortedSet<TrackFormat> bitrateSet = new TreeSet<TrackFormat>(tracksComperator); bitrateSet.addAll(tracksList); LOGD(TAG, "preferred bitrate selected = " + bitrateSet.first()); switchTrack(trackType, bitrateSet.first().index); //adding Auto again after removing it for comperator ignorance if (autoTrackFormat != null) { tracksList.add(0, autoTrackFormat); } } @Override public TrackFormat getCurrentTrack(TrackType trackType) { int currnetTrackIndex = player.getCurrentTrackIndex(trackType); if (currnetTrackIndex == -1){ return TrackFormat.getDefaultTrackFormat(trackType); } List<TrackFormat> tracksList = getTracksList(trackType); LOGD(TAG, "getCurrentTrack " + trackType.name() + " tracksList size = " + tracksList.size() + " currentIndex = " + currnetTrackIndex); if (tracksList != null && currnetTrackIndex <= (tracksList.size() - 1)) { return tracksList.get(currnetTrackIndex); } return null; } @Override public List<TrackFormat> getAudioTrackList() { return tracksContainer.getAudioTrackList(); } @Override public List<TrackFormat> getTextTrackList() { return tracksContainer.getTextTrackList(); } @Override public List<TrackFormat> getVideoTrackList() { return tracksContainer.getVideoTrackList(); } private void initTrackManagerLists() { tracksContainer = new KTracksContainer(getTracksList(TrackType.AUDIO),getTracksList(TrackType.TEXT),getTracksList(TrackType.VIDEO)); } private void clearTrackManagerLists() { tracksContainer.getAudioTrackList().clear(); tracksContainer.setAudioTrackList(null); tracksContainer.getTextTrackList().clear(); tracksContainer.setTextTrackList(null); tracksContainer.getVideoTrackList().clear(); tracksContainer.setVideoTrackList(null); } public int getTracksCount(TrackType trackType) { return getTracksList(trackType).size(); } public JSONObject getTrackListAsJson(TrackType trackType) { JSONObject resultJsonObj = new JSONObject(); JSONArray tracksJsonArray = new JSONArray(); int trackCount = getTracksCount(trackType); String resultJsonKey = getJsonRootKey(trackType); List<TrackFormat> tracksList = getTracksList(trackType); for (TrackFormat tf : tracksList) { // for webView we filter the -1 bitrate since it is added automatically in the web layer if (TrackType.VIDEO.equals(trackType) && tf.bitrate == -1) { continue; } tracksJsonArray.put(tf.toJSONObject()); } try { resultJsonObj.put(resultJsonKey, tracksJsonArray); } catch (JSONException e) { e.printStackTrace(); } LOGD(TAG, "Track/Lang JSON Result " + resultJsonObj.toString()); return resultJsonObj; } public String getTrackName(TrackFormat trackFormat) { if (trackFormat != null) { if (trackFormat.adaptive) { return "auto"; } return trackFormat.trackLabel; } return ""; } private List<TrackFormat> getTrackFormatsByType(TrackType trackType) { List<TrackFormat> trackList = new ArrayList<>(); int trackCount = player.getTrackCount(trackType); for (int i = 0; i < trackCount; i++) { trackList.add(player.getTrackFormat(trackType, i)); } return trackList; } private boolean isAvailableTracksRelevant(TrackType trackType) { int tracksCount = getTracksCount(trackType); if (tracksCount > 1) { return true; } else if (tracksCount == 1) { if (!getTracksList(trackType).get(0).trackLabel.contains("auto") && !getTracksList(trackType).get(0).trackLabel.contains("Auto")) { return true; } } return false; } private String getJsonRootKey(TrackType trackType) { if (TrackType.VIDEO.equals(trackType)) { return "tracks"; } else if (TrackType.AUDIO.equals(trackType) || TrackType.TEXT.equals(trackType)) { return "languages"; } return ""; } private List<TrackFormat> getTracksList(TrackType trackType) { List<TrackFormat> tracksList = null; switch (trackType) { case AUDIO: tracksList = getTrackFormatsByType(TrackType.AUDIO); break; case TEXT: tracksList = getTrackFormatsByType(TrackType.TEXT); break; case VIDEO: tracksList = getTrackFormatsByType(TrackType.VIDEO); break; default: break; } return tracksList; } }