I'm using ExoPlayer 2.3.1 for playing the list of videos. I'm using code from sample:
MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0]
: new ConcatenatingMediaSource(mediaSources);
player.prepare(mediaSource, !haveResumePosition, false);
It's working fine, but I want to select video to play first.
For example I have 3 videos, when I clicks on second it should play and prev/next buttons in player should be available. Now it starts playing from the first mediaSource in array, and I can't find a way to set index of initial track or something like that.
Please check this out seekTo(windowIndex, positionMs). It says
Seeks to a position specified in milliseconds in the specified window.
Example:
player.seekTo(3, C.TIME_UNSET);
player.setPlayWhenReady(true);
Related
I am designing an android video editor app and one of the feature is to trim video, selected from gallery. I can give an option to select the range using the RangeSlider, displayed at the bottom of the VideoView, to the user and then use FFMPEG library to trim the video.
But i am not able to show the progress of the video being played, within the selected range, on the RangeSlider.
Not sure if i am approaching properly, hence please provide me a solution to achieve this.
When you change the bounds of the RangeSlider, you need to calculate the startTime and endTime of the video. Once you are able to calculate the startTime and endTime, you need to create a ClippingMediaSource instance.
public ClippingMediaSource(MediaSource mediaSource, long startPositionUs, long endPositionUs)
ClippingMediaSource takes three paramters:
MediaSource
startPositionUs
endPositionUs
You can create media source by following the below snippet:
fun getMediaSource(file: String): MediaSource {
return ProgressiveMediaSource.Factory(DefaultDataSourceFactory(context, userAgent))
.createMediaSource(MediaItem.fromUri(Uri.parse(file)))
}
After creating the MediaSource you can pass on the values for start and end time you had calculated.
Note: startPositionUs and endPositionUs are in micro-seconds.
Once done, you can pass this media source to your ExoPlayer and it will play only the selected/trimmed part of your video.
I'm working on an app that streams a list of mp3 files, to do this I've used ExoPlayer with a ConcatenatingMediaSource as this:
private fun createMediaSource(
tracks: List<Track>
): MediaSource = ConcatenatingMediaSource(true).apply {
tracks.forEach { track ->
val mediaSource = ProgressiveMediaSource
.Factory(DefaultDataSourceFactory(context))
.createMediaSource(MediaItem.fromUri(track.getFullUri()))
addMediaSource(mediaSource)
}
}
This works great, the files play as list with no errors at all, however what's required from me is to play all these streams as a single stream, where I show the total length of all streams on the seek bar, and the user would seek seamlessly between them.
Of course I'm not using the VideoPlayer provided by ExoPlayer because I need the seekbar to span all media sources, which apparently this is not possible to do with ExoPlayerUi.
So this is the logic I've used when the user tries to seek:
exoPlayer.apply {
var previousTracksLength = 0L
var windowIndex = 0
var currentItemLength = 0L
run loop#{
tracksList.forEachIndexed { index, track ->
currentItemLength = track.getLengthMillis()
previousTracksLength += currentItemLength
if (newPositionMillis < previousTracksLength) {
windowIndex = index
return#loop
}
}
}
val positionForCurrentTrack = (newPositionMillis - (previousTracksLength - currentItemLength))
pause()
if (windowIndex == currentWindowIndex) {
seekTo(positionForCurrentTrack)
} else {
seekTo(windowIndex, positionForCurrentTrack)
}
play()
}
This works amazingly well when the ConcatenatingMediaSource has only 3 or less media sources, but if it's bigger than that, weird behavior starts showing up, I might just want to seek 10 seconds forward the player would move more than 2 minutes instead.
After debugging it was obvious for me that when I call: seekTo(windowIndex, positionForCurrentTrack) exoPlayer is seeking to a window that's not mapped with a specific media source in the ConcatenatingMediaSource !
And here comes my questions:
Does ExoPlayer create a single window for each mediaSource in the ConcatenatingMediaSource or not ?
and If not is there a way to force it to do that ?
This is not really an answer but the explanation to why when I called seekTo(windowIndex, position) the player seemed like it was ignoring the windowIndex and actually seek to a completely unexpected position is because the media type was mp3 !
Apparently many devs have suffered the same issue where the player seek position is out of sync with the real position of the media that's being played when it's an mp3.
More details for anyone having weird issues when playing mp3 using ExoPlayer
https://github.com/google/ExoPlayer/issues/6787#issuecomment-568180969
I am trying add an intro video to my actual video. I am planning to achieve this by using ConcatenatingMediaSource. Below is the source code
DataSource.Factory dataSourceFactory = new CacheDataSourceFactory(VideoCache.getInstance(this), new DefaultDataSourceFactory(this, "test"));
MediaSource firstSource = new ExtractorMediaSource(Uri.parse("path1.mp4"),
dataSourceFactory, new DefaultExtractorsFactory(), null, null);
MediaSource secondSource = new ExtractorMediaSource(Uri.parse("path2.mp4"),
mediaDataSourceFactory, extractorsFactory, null, null);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
new ConcatenatingMediaSource(firstSource, secondSource);
player.prepare(concatenatedSource);
I need to know when the intro video stops playing so I can make some UI changes as well as start showing the controller layout for the video. One way I have tried, is to set a CountDownTimer with a hardcoded value which does the necessary changes once onFinish is called. I was wondering if there are any listeners which will help me get a callback for when a source ends. Is onTracksChanged a proper callback to consider?
In your case, both onTrackChanged() and onPositionDiscontinuity() callbacks will be called when the second video is start to play.
onPositionDiscontinuity() will also be invoked during a seek operation. You can get the newly changed window index by calling player.getCurrentWindowIndex() inside it. On the other hand, calling player.getCurrentWindowIndex() inside onTrackChanged() will not be guaranteed to get the right index.
UPDATE:
there is a section in the documentation explained how to detect playback transitions.
There are three types of events that may be called when the current playback item changes:
EventListener.onPositionDiscontinuity with reason = Player.DISCONTINUITY_REASON_PERIOD_TRANSITION.
This happens when playback automatically transitions from one item to the next.
EventListener.onPositionDiscontinuity with reason = Player.DISCONTINUITY_REASON_SEEK.
This happens when the current playback item changes as part of a seek operation, for example when calling Player.next.
EventListener.onTimelineChanged with reason = Player.TIMELINE_CHANGE_REASON_DYNAMIC.
This happens when the playlist changes, e.g. if items are added, moved, or removed.
In all cases, when your application code receives the event, you can query the player to determine which item in the playlist is now being played. This can be done using methods such as Player.getCurrentWindowIndex and Player.getCurrentTag. If you only want to detect playlist item changes, then it’s necessary to compare against the last known window index or tag, because the mentioned events may be triggered for other reasons.
I want to change current playing video to another seamlessly. But there is a little bit delay before the next video will be playing. I don't know what the next video will be. How I can do this?
You can use playlists with ExoPlayer. This way the playback transitions at the end of item 1 to 2 without buffering.
DynamicConcatenatingMediaSource mainSource = new DynamicConcatenatingMediaSource();
mainSource.addMediaSource(mediaSource1);
player.prepare(mainSource);
// later...
mainSource.addMediaSource(mediaSource2);
As soon as you know the second media you can add it while the player is playing.
https://medium.com/google-exoplayer/dynamic-playlists-with-exoplayer-6f53e54a56c0
I am working on an Android project that involves the use of Google's ExoPlayer.
I have a list of video sources which I build a playlist from using the following code:
for (int i = 0; i < vidList.length(); i++) {
MediaSource source = new ExtractorMediaSource(Uri.parse(vidList.getJSONObject(i).getString("url")),
buildDataSourceFactory(bandwidthMeter), extractorsFactory, mainHandler, HomeFragment.this);
mediaSources.add(source);
captions.add(vidList.getJSONObject(i).getString("caption"));
}
mediaSource = new ConcatenatingMediaSource(mediaSources.toArray(new MediaSource[mediaSources.size()]));
I then call
exoplayer.prepare(mediasource, false, false)
and the videos play in succession fine. I would like to display the caption of the currently playing video in a textView and so I have a separate list that holds the "caption" values for each video.
From scouring through the code I see that I can get the currently playing video in the playlist like this;
exoPlayer.getCurrentPeriodIndex()
Which seems to work and returns the index except for one problem. It returns the value of 0 twice as playback starts. That is video at index 0 returns period 0 as well as video at index 1. This only occurs at indexes 0 and 1 and thereafter everything else looks fine except that the getCurrentPeriodIndex() will return theAccurateIndex - 1.
I see this also happening in the demo Exoplayer application.
Is there a better way to determine what track is currently playing in the playlist?
Thanks.
To find the currently playing track, you need to reference currentWindowIndex exoPlayer field. Looks like this in Java...
exoPlayer.getCurrentWindowIndex()
I'm not sure what getCurrentPeriodIndex() does, and the docs don't elaborate, and I don't like speculating.
exoPlayer.getCurrentWindowIndex() is Deprecated.
Use exoPlayer.getCurrentMediaItemIndex() instead.