/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.exoplayer; import com.google.android.exoplayer.SampleSource.SampleSourceReader; import java.io.IOException; import java.util.Arrays; /** * Base class for {@link TrackRenderer} implementations that render samples obtained from a * {@link SampleSource}. */ public abstract class SampleSourceTrackRenderer extends TrackRenderer { private final SampleSourceReader[] sources; private int[] handledSourceIndices; private int[] handledSourceTrackIndices; private SampleSourceReader enabledSource; private int enabledSourceTrackIndex; private long durationUs; /** * @param sources One or more upstream sources from which the renderer can obtain samples. */ public SampleSourceTrackRenderer(SampleSource... sources) { this.sources = new SampleSourceReader[sources.length]; for (int i = 0; i < sources.length; i++) { this.sources[i] = sources[i].register(); } } @Override protected boolean doPrepare(long positionUs) throws ExoPlaybackException { boolean allSourcesPrepared = true; for (int i = 0; i < sources.length; i++) { allSourcesPrepared &= sources[i].prepare(positionUs); } if (!allSourcesPrepared) { return false; } // The sources are all prepared. int totalSourceTrackCount = 0; for (int i = 0; i < sources.length; i++) { totalSourceTrackCount += sources[i].getTrackCount(); } long durationUs = 0; int handledTrackCount = 0; int[] handledSourceIndices = new int[totalSourceTrackCount]; int[] handledTrackIndices = new int[totalSourceTrackCount]; int sourceCount = sources.length; for (int sourceIndex = 0; sourceIndex < sourceCount; sourceIndex++) { SampleSourceReader source = sources[sourceIndex]; int sourceTrackCount = source.getTrackCount(); for (int trackIndex = 0; trackIndex < sourceTrackCount; trackIndex++) { MediaFormat format = source.getFormat(trackIndex); if (handlesTrack(format)) { handledSourceIndices[handledTrackCount] = sourceIndex; handledTrackIndices[handledTrackCount] = trackIndex; handledTrackCount++; if (durationUs == TrackRenderer.UNKNOWN_TIME_US) { // We've already encountered a track for which the duration is unknown, so the media // duration is unknown regardless of the duration of this track. } else { long trackDurationUs = format.durationUs; if (trackDurationUs == TrackRenderer.UNKNOWN_TIME_US) { durationUs = TrackRenderer.UNKNOWN_TIME_US; } else if (trackDurationUs == TrackRenderer.MATCH_LONGEST_US) { // Do nothing. } else { durationUs = Math.max(durationUs, trackDurationUs); } } } } } this.durationUs = durationUs; this.handledSourceIndices = Arrays.copyOf(handledSourceIndices, handledTrackCount); this.handledSourceTrackIndices = Arrays.copyOf(handledTrackIndices, handledTrackCount); return true; } /** * Returns whether this renderer is capable of handling the provided track. * * @param mediaFormat The format of the track. * @return True if the renderer can handle the track, false otherwise. */ protected abstract boolean handlesTrack(MediaFormat mediaFormat); @Override protected void onEnabled(int track, long positionUs, boolean joining) throws ExoPlaybackException { enabledSource = sources[handledSourceIndices[track]]; enabledSourceTrackIndex = handledSourceTrackIndices[track]; enabledSource.enable(enabledSourceTrackIndex, positionUs); } @Override protected void seekTo(long positionUs) throws ExoPlaybackException { enabledSource.seekToUs(positionUs); } @Override protected long getBufferedPositionUs() { return enabledSource.getBufferedPositionUs(); } @Override protected long getDurationUs() { return durationUs; } @Override protected void maybeThrowError() throws ExoPlaybackException { if (enabledSource != null) { maybeThrowError(enabledSource); } else { int sourceCount = sources.length; for (int i = 0; i < sourceCount; i++) { maybeThrowError(sources[i]); } } } private void maybeThrowError(SampleSourceReader source) throws ExoPlaybackException { try { source.maybeThrowError(); } catch (IOException e) { throw new ExoPlaybackException(e); } } @Override protected void onDisabled() throws ExoPlaybackException { enabledSource.disable(enabledSourceTrackIndex); enabledSource = null; } @Override protected void onReleased() throws ExoPlaybackException { int sourceCount = sources.length; for (int i = 0; i < sourceCount; i++) { sources[i].release(); } } protected final boolean continueBufferingSource(long positionUs) { return enabledSource.continueBuffering(enabledSourceTrackIndex, positionUs); } protected final int readSource(long positionUs, MediaFormatHolder formatHolder, SampleHolder sampleHolder, boolean onlyReadDiscontinuity) { return enabledSource.readData(enabledSourceTrackIndex, positionUs, formatHolder, sampleHolder, onlyReadDiscontinuity); } @Override protected final int getTrackCount() { return handledSourceTrackIndices.length; } @Override protected final MediaFormat getFormat(int track) { SampleSourceReader source = sources[handledSourceIndices[track]]; return source.getFormat(handledSourceTrackIndices[track]); } }