use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerImplInternal method setForegroundModeInternal.
private void setForegroundModeInternal(boolean foregroundMode, @Nullable AtomicBoolean processedFlag) {
if (this.foregroundMode != foregroundMode) {
this.foregroundMode = foregroundMode;
if (!foregroundMode) {
for (Renderer renderer : renderers) {
if (!isRendererEnabled(renderer) && renderersToReset.remove(renderer)) {
renderer.reset();
}
}
}
}
if (processedFlag != null) {
synchronized (this) {
processedFlag.set(true);
notifyAll();
}
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class MediaPeriodQueue method getFollowingMediaPeriodInfo.
/**
* Returns the {@link MediaPeriodInfo} for the media period following {@code mediaPeriodHolder}'s
* media period.
*
* @param timeline The current timeline.
* @param mediaPeriodHolder The media period holder.
* @param rendererPositionUs The current renderer position in microseconds.
* @return The following media period's info, or {@code null} if it is not yet possible to get the
* next media period info.
*/
@Nullable
private MediaPeriodInfo getFollowingMediaPeriodInfo(Timeline timeline, MediaPeriodHolder mediaPeriodHolder, long rendererPositionUs) {
// TODO: This method is called repeatedly from ExoPlayerImplInternal.maybeUpdateLoadingPeriod
// but if the timeline is not ready to provide the next period it can't return a non-null value
// until the timeline is updated. Store whether the next timeline period is ready when the
// timeline is updated, to avoid repeatedly checking the same timeline.
MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info;
// The expected delay until playback transitions to the new period is equal the duration of
// media that's currently buffered (assuming no interruptions). This is used to project forward
// the start position for transitions to new windows.
long bufferedDurationUs = mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs;
if (mediaPeriodInfo.isLastInTimelinePeriod) {
int currentPeriodIndex = timeline.getIndexOfPeriod(mediaPeriodInfo.id.periodUid);
int nextPeriodIndex = timeline.getNextPeriodIndex(currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
if (nextPeriodIndex == C.INDEX_UNSET) {
// We can't create a next period yet.
return null;
}
// We either start a new period in the same window or the first period in the next window.
long startPositionUs = 0;
long contentPositionUs = 0;
int nextWindowIndex = timeline.getPeriod(nextPeriodIndex, period, /* setIds= */
true).windowIndex;
Object nextPeriodUid = checkNotNull(period.uid);
long windowSequenceNumber = mediaPeriodInfo.id.windowSequenceNumber;
if (timeline.getWindow(nextWindowIndex, window).firstPeriodIndex == nextPeriodIndex) {
// We're starting to buffer a new window. When playback transitions to this window we'll
// want it to be from its default start position, so project the default start position
// forward by the duration of the buffer, and start buffering from this point.
contentPositionUs = C.TIME_UNSET;
@Nullable Pair<Object, Long> defaultPositionUs = timeline.getPeriodPositionUs(window, period, nextWindowIndex, /* windowPositionUs= */
C.TIME_UNSET, /* defaultPositionProjectionUs= */
max(0, bufferedDurationUs));
if (defaultPositionUs == null) {
return null;
}
nextPeriodUid = defaultPositionUs.first;
startPositionUs = defaultPositionUs.second;
@Nullable MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext();
if (nextMediaPeriodHolder != null && nextMediaPeriodHolder.uid.equals(nextPeriodUid)) {
windowSequenceNumber = nextMediaPeriodHolder.info.id.windowSequenceNumber;
} else {
windowSequenceNumber = nextWindowSequenceNumber++;
}
}
@Nullable MediaPeriodId periodId = resolveMediaPeriodIdForAds(timeline, nextPeriodUid, startPositionUs, windowSequenceNumber, window, period);
if (contentPositionUs != C.TIME_UNSET && mediaPeriodInfo.requestedContentPositionUs != C.TIME_UNSET) {
boolean isPrecedingPeriodAnAd = timeline.getPeriodByUid(mediaPeriodInfo.id.periodUid, period).getAdGroupCount() > 0 && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount());
// Handle the requested content position for period transitions within the same window.
if (periodId.isAd() && isPrecedingPeriodAnAd) {
// Propagate the requested position to the following ad period in the same window.
contentPositionUs = mediaPeriodInfo.requestedContentPositionUs;
} else if (isPrecedingPeriodAnAd) {
// Use the requested content position of the preceding ad period as the start position.
startPositionUs = mediaPeriodInfo.requestedContentPositionUs;
}
}
return getMediaPeriodInfo(timeline, periodId, contentPositionUs, startPositionUs);
}
MediaPeriodId currentPeriodId = mediaPeriodInfo.id;
timeline.getPeriodByUid(currentPeriodId.periodUid, period);
if (currentPeriodId.isAd()) {
int adGroupIndex = currentPeriodId.adGroupIndex;
int adCountInCurrentAdGroup = period.getAdCountInAdGroup(adGroupIndex);
if (adCountInCurrentAdGroup == C.LENGTH_UNSET) {
return null;
}
int nextAdIndexInAdGroup = period.getNextAdIndexToPlay(adGroupIndex, currentPeriodId.adIndexInAdGroup);
if (nextAdIndexInAdGroup < adCountInCurrentAdGroup) {
// Play the next ad in the ad group if it's available.
return getMediaPeriodInfoForAd(timeline, currentPeriodId.periodUid, adGroupIndex, nextAdIndexInAdGroup, mediaPeriodInfo.requestedContentPositionUs, currentPeriodId.windowSequenceNumber);
} else {
// Play content from the ad group position.
long startPositionUs = mediaPeriodInfo.requestedContentPositionUs;
if (startPositionUs == C.TIME_UNSET) {
// If we're transitioning from an ad group to content starting from its default position,
// project the start position forward as if this were a transition to a new window.
@Nullable Pair<Object, Long> defaultPositionUs = timeline.getPeriodPositionUs(window, period, period.windowIndex, /* windowPositionUs= */
C.TIME_UNSET, /* defaultPositionProjectionUs= */
max(0, bufferedDurationUs));
if (defaultPositionUs == null) {
return null;
}
startPositionUs = defaultPositionUs.second;
}
long minStartPositionUs = getMinStartPositionAfterAdGroupUs(timeline, currentPeriodId.periodUid, currentPeriodId.adGroupIndex);
return getMediaPeriodInfoForContent(timeline, currentPeriodId.periodUid, max(minStartPositionUs, startPositionUs), mediaPeriodInfo.requestedContentPositionUs, currentPeriodId.windowSequenceNumber);
}
} else {
// Play the next ad group if it's still available.
int adIndexInAdGroup = period.getFirstAdIndexToPlay(currentPeriodId.nextAdGroupIndex);
boolean isPlayedServerSideInsertedAd = period.isServerSideInsertedAdGroup(currentPeriodId.nextAdGroupIndex) && period.getAdState(currentPeriodId.nextAdGroupIndex, adIndexInAdGroup) == AdPlaybackState.AD_STATE_PLAYED;
if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex) || isPlayedServerSideInsertedAd) {
// The next ad group has no ads left to play or is a played SSAI ad group. Play content from
// the end position instead.
long startPositionUs = getMinStartPositionAfterAdGroupUs(timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex);
return getMediaPeriodInfoForContent(timeline, currentPeriodId.periodUid, startPositionUs, /* requestedContentPositionUs= */
mediaPeriodInfo.durationUs, currentPeriodId.windowSequenceNumber);
}
return getMediaPeriodInfoForAd(timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex, adIndexInAdGroup, /* contentPositionUs= */
mediaPeriodInfo.durationUs, currentPeriodId.windowSequenceNumber);
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class DefaultRenderersFactory method buildVideoRenderers.
/**
* Builds video renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param extensionRendererMode The extension renderer mode.
* @param mediaCodecSelector A decoder selector.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param eventHandler A handler associated with the main thread's looper.
* @param eventListener An event listener.
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
* seamlessly join an ongoing playback, in milliseconds.
* @param out An array to which the built renderers should be appended.
*/
protected void buildVideoRenderers(Context context, @ExtensionRendererMode int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, Handler eventHandler, VideoRendererEventListener eventListener, long allowedVideoJoiningTimeMs, ArrayList<Renderer> out) {
MediaCodecVideoRenderer videoRenderer = new MediaCodecVideoRenderer(context, getCodecAdapterFactory(), mediaCodecSelector, allowedVideoJoiningTimeMs, enableDecoderFallback, eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(videoRenderer);
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.vp9.LibvpxVideoRenderer");
Constructor<?> constructor = clazz.getConstructor(long.class, android.os.Handler.class, com.google.android.exoplayer2.video.VideoRendererEventListener.class, int.class);
Renderer renderer = (Renderer) constructor.newInstance(allowedVideoJoiningTimeMs, eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibvpxVideoRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
// The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating VP9 extension", e);
}
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.av1.Libgav1VideoRenderer");
Constructor<?> constructor = clazz.getConstructor(long.class, android.os.Handler.class, com.google.android.exoplayer2.video.VideoRendererEventListener.class, int.class);
Renderer renderer = (Renderer) constructor.newInstance(allowedVideoJoiningTimeMs, eventHandler, eventListener, MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded Libgav1VideoRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
// The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating AV1 extension", e);
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class DefaultRenderersFactory method buildAudioRenderers.
/**
* Builds audio renderers for use by the player.
*
* @param context The {@link Context} associated with the player.
* @param extensionRendererMode The extension renderer mode.
* @param mediaCodecSelector A decoder selector.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param audioSink A sink to which the renderers will output.
* @param eventHandler A handler to use when invoking event listeners and outputs.
* @param eventListener An event listener.
* @param out An array to which the built renderers should be appended.
*/
protected void buildAudioRenderers(Context context, @ExtensionRendererMode int extensionRendererMode, MediaCodecSelector mediaCodecSelector, boolean enableDecoderFallback, AudioSink audioSink, Handler eventHandler, AudioRendererEventListener eventListener, ArrayList<Renderer> out) {
MediaCodecAudioRenderer audioRenderer = new MediaCodecAudioRenderer(context, getCodecAdapterFactory(), mediaCodecSelector, enableDecoderFallback, eventHandler, eventListener, audioSink);
out.add(audioRenderer);
if (extensionRendererMode == EXTENSION_RENDERER_MODE_OFF) {
return;
}
int extensionRendererIndex = out.size();
if (extensionRendererMode == EXTENSION_RENDERER_MODE_PREFER) {
extensionRendererIndex--;
}
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.opus.LibopusAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(android.os.Handler.class, com.google.android.exoplayer2.audio.AudioRendererEventListener.class, com.google.android.exoplayer2.audio.AudioSink.class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibopusAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
// The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating Opus extension", e);
}
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.flac.LibflacAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(android.os.Handler.class, com.google.android.exoplayer2.audio.AudioRendererEventListener.class, com.google.android.exoplayer2.audio.AudioSink.class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded LibflacAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
// The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating FLAC extension", e);
}
try {
// Full class names used for constructor args so the LINT rule triggers if any of them move.
Class<?> clazz = Class.forName("com.google.android.exoplayer2.ext.ffmpeg.FfmpegAudioRenderer");
Constructor<?> constructor = clazz.getConstructor(android.os.Handler.class, com.google.android.exoplayer2.audio.AudioRendererEventListener.class, com.google.android.exoplayer2.audio.AudioSink.class);
Renderer renderer = (Renderer) constructor.newInstance(eventHandler, eventListener, audioSink);
out.add(extensionRendererIndex++, renderer);
Log.i(TAG, "Loaded FfmpegAudioRenderer.");
} catch (ClassNotFoundException e) {
// Expected if the app was built without the extension.
} catch (Exception e) {
// The extension is present, but instantiation failed.
throw new RuntimeException("Error instantiating FFmpeg extension", e);
}
}
use of com.google.android.exoplayer2.Renderer in project ExoPlayer by google.
the class ExoPlayerTest method setAndSwitchSurface.
@Test
public void setAndSwitchSurface() throws Exception {
final List<Integer> rendererMessages = new ArrayList<>();
Renderer videoRenderer = new FakeRenderer(C.TRACK_TYPE_VIDEO) {
@Override
public void handleMessage(@MessageType int messageType, @Nullable Object message) throws ExoPlaybackException {
super.handleMessage(messageType, message);
rendererMessages.add(messageType);
}
};
ActionSchedule actionSchedule = addSurfaceSwitch(new ActionSchedule.Builder(TAG)).build();
new ExoPlayerTestRunner.Builder(context).setRenderers(videoRenderer).setActionSchedule(actionSchedule).build().start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
assertThat(Collections.frequency(rendererMessages, Renderer.MSG_SET_VIDEO_OUTPUT)).isEqualTo(2);
}
Aggregations