Exoplayer: How to get the entire content Duration of a MediaItem, not just the clip length?

Created on 29 Oct 2020  路  9Comments  路  Source: google/ExoPlayer

[REQUIRED] Question

I have a list of MediaItems with different start positions.

MediaItem mediaItem = new MediaItem.Builder()
    .setUri(trackUri)
    .setClipStartPositionMs(startPos)
    .setClipEndPositionMs(C.TIME_END_OF_SOURCE)
    .build();

When a particular MediaItem is being played, the Player is just returning the duration of the clip, not the total length of the track.
Is there a way where I can get the total length of the track, not just the clip length?

duplicate question

All 9 comments

Hi

I have to do quite a bit of work if the Player can't give the entire duration of the MediaItem. And I'm not sure if this is a bug or the intended behavior.
Can I get an update here? Do I need to provide some more details for this question?

Thanks

If you're only changing the start clip position then I think you can retrieve the entire content duration like:

@Override
public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {
  if (!timeline.isEmpty()) {
    long durationMs = timeline.getPeriod(0, new Period()).getDurationMs();
  }
}

Note that you can also retrieve the timeline from Player.getCurrentTimeline. @pavan245 - Does that work for you?

The same trick does not work if you set an end clip position, however. This may not be relevant to your use case, but is something we should think about. @tonihei - Do you understand why that's not the case? If both start and end are clipped, I think I'd expect the period and window to look like:

<----------- original content ----------->
<-------------- period ------------------>
            <----- window ----->

where-as it seems they look like:

<----------- original content ----------->
<-------------- period -------->
            <----- window ----->

I tried changing ClippingTimeline.getPeriod to do what I expected, but then playback doesn't transition to the ended state once the clip end point is reached. Which also seems unexpected to me.

If both start and end are clipped, I think I'd expect the period and window to look like ... where-as it seems they look like ...

This is because the clipping is not properly integrated in every MediaPeriod and thus the player (without knowing ClippingMediaSource) has no concept of a period ending early. That's why we need to shorten the period so that the player knows when it ends and that it can transition to the next item.

The pending work for #3163 will change this by making clipping a first-class citizen in MediaPeriod and Timeline.Period. This will solve this issue, but also #3163 that is currently blocked on correctly discarding buffer when the end clip position changes.

@pavan245 - For only changing the start clip position, my solution posted above will work. For the end position, marking this as a duplicate of #3163 as per Toni's response above.

@ojw28 Thanks for your solution. For the time being, I'm only using the start clip position so your solution works.

Is there any way I can get the current playing position in the window from the Player/Timeline APIs?
I tried Period.getPositionInWindowMs and it's returning a negative of startPosition.
I can add the startPosition and Player.getCurrentPosition, but prefer to rely on the Player.

Player.getCurrentPosition returns the position in the window actually. If you want to convert to a period position you can use Timeline.getPeriodPosition(window, period, windowIndex, windowPositionUs) that gives you both the periodUid and the period position. The uid is only relevant for cases where you have multiple periods per window. As ClippingMediaSource doesn't support multi-period windows at all, you can probably ignore this part of the return value.

Hi @tonihei
If the MediaItem's setClipStartPositionMs is 300000ms, I need the Player.getCurrentPosition to start from 300000. Is this the expected behaviour?

From the below source code, I can see Player.getCurrentPosition internally calling periodPositionUsToWindowPositionMs method. But as mentioned in the above comment, Period.getPositionInWindowMs() is returning -300000


@Override
public long getCurrentPosition() {
    if (playbackInfo.timeline.isEmpty()) {
      return maskingWindowPositionMs;
    } else if (playbackInfo.periodId.isAd()) {
      return C.usToMs(playbackInfo.positionUs);
    } else {
      return periodPositionUsToWindowPositionMs(playbackInfo.periodId, playbackInfo.positionUs);
    }
 }

private long periodPositionUsToWindowPositionMs(MediaPeriodId periodId, long positionUs) {
    long positionMs = C.usToMs(positionUs);
    playbackInfo.timeline.getPeriodByUid(periodId.periodUid, period);
    positionMs += period.getPositionInWindowMs();
    return positionMs;
}

If you want the position to start at 300000, then you want to know the period position if I understand you correctly. Have you tried using Timeline.getPeriodPosition as proposed above?

Sorry, you're right. I need the period position.
This is how I got it working:

    public long getCurrentPosition() {

        if (player == null || player.getPlaybackState() == Player.STATE_IDLE)
            return 0;

        Timeline timeline = player.getCurrentTimeline();
        if (timeline == null || timeline.isEmpty())
            return 0;

        Timeline.Period period = timeline.getPeriod(0, new Timeline.Period());

        Timeline.Window window = timeline.getWindow(period.windowIndex, new Timeline.Window());

        long windowPosition = player.getCurrentPosition();

        Pair<Object, Long> periodPosition = timeline.getPeriodPosition(window, period, period.windowIndex, C.msToUs(windowPosition));

        return periodPosition != null ? C.usToMs(periodPosition.second) : 0;
    }
Was this page helpful?
0 / 5 - 0 ratings