Refactor #6.HLS.0

Mechanical step to create a new HlsSource in the library.
Note that naming is now confusing. This will be fixed in
the next CL, when:

HlsSource -> HlsSampleSource
HlsSampleSource+HlsOutput -> HlsOutput
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=122054800
This commit is contained in:
olly 2016-05-11 08:07:38 -07:00 committed by Oliver Woodman
parent 9ee3030b79
commit 5ca9f62753
5 changed files with 290 additions and 129 deletions

View file

@ -24,18 +24,24 @@ import com.google.android.exoplayer.MediaCodecTrackRenderer.DecoderInitializatio
import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.dash.DashSampleSource;
import com.google.android.exoplayer.demo.player.DemoPlayer;
import com.google.android.exoplayer.demo.player.SourceBuilder;
import com.google.android.exoplayer.demo.ui.TrackSelectionHelper;
import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.hls.HlsSource;
import com.google.android.exoplayer.metadata.id3.GeobFrame;
import com.google.android.exoplayer.metadata.id3.Id3Frame;
import com.google.android.exoplayer.metadata.id3.PrivFrame;
import com.google.android.exoplayer.metadata.id3.TxxxFrame;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingSampleSource;
import com.google.android.exoplayer.text.CaptionStyleCompat;
import com.google.android.exoplayer.text.Cue;
import com.google.android.exoplayer.text.SubtitleLayout;
import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer.util.DebugTextViewHelper;
import com.google.android.exoplayer.util.Util;
@ -260,18 +266,25 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
// Internal methods
@SuppressWarnings("unused")
private SampleSource buildSource(Uri uri, String id, String provider, int type) {
switch (type) {
case Util.TYPE_SS:
return SourceBuilder.buildSmoothStreamingSource(player, dataSourceFactory, uri,
new SmoothStreamingTestMediaDrmCallback());
// TODO: Bring back DRM support. new SmoothStreamingTestMediaDrmCallback()
return new SmoothStreamingSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_DASH:
return SourceBuilder.buildDashSource(player, dataSourceFactory, uri,
new WidevineTestMediaDrmCallback(id, provider));
// TODO: Bring back DRM support. new WidevineTestMediaDrmCallback(id, provider)
return new DashSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_HLS:
return SourceBuilder.buildHlsSource(player, dataSourceFactory, uri);
return new HlsSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
case Util.TYPE_OTHER:
return SourceBuilder.buildExtractorSource(player, dataSourceFactory, uri);
Allocator allocator = new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE);
DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter());
return new ExtractorSampleSource(uri, dataSource, allocator,
C.DEFAULT_MUXED_BUFFER_SIZE, player.getMainHandler(), player, 0);
default:
throw new IllegalStateException("Unsupported type: " + type);
}

View file

@ -279,7 +279,7 @@ public class DemoPlayer implements ExoPlayer.Listener, DefaultTrackSelector.Even
return player.getPlayWhenReady();
}
/* package */ Handler getMainHandler() {
public Handler getMainHandler() {
return mainHandler;
}

View file

@ -1,109 +0,0 @@
/*
* 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.demo.player;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MultiSampleSource;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.dash.DashSampleSource;
import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.hls.HlsChunkSource;
import com.google.android.exoplayer.hls.HlsSampleSource;
import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider;
import com.google.android.exoplayer.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingSampleSource;
import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.util.ManifestFetcher;
import android.net.Uri;
import android.os.Handler;
/**
* A utility class for building {@link SampleSource} instances.
*/
public class SourceBuilder {
private SourceBuilder() {}
// TODO[REFACTOR]: Bring back DASH DRM support.
// TODO[REFACTOR]: Bring back DASH UTC timing element support.
public static SampleSource buildDashSource(DemoPlayer player, DataSourceFactory dataSourceFactory,
Uri uri, MediaDrmCallback drmCallback) {
return new DashSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
}
// TODO[REFACTOR]: Bring back DRM support.
public static SampleSource buildSmoothStreamingSource(DemoPlayer player,
DataSourceFactory dataSourceFactory, Uri uri, MediaDrmCallback drmCallback) {
return new SmoothStreamingSampleSource(uri, dataSourceFactory, player.getBandwidthMeter(),
player.getMainHandler(), player);
}
public static SampleSource buildHlsSource(DemoPlayer player, DataSourceFactory dataSourceFactory,
Uri uri) {
HlsPlaylistParser parser = new HlsPlaylistParser();
DataSource manifestDataSource = dataSourceFactory.createDataSource();
// TODO[REFACTOR]: This needs releasing.
ManifestFetcher<HlsPlaylist> manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource,
parser);
Handler mainHandler = player.getMainHandler();
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
LoadControl loadControl = new DefaultLoadControl(
new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_DEFAULT,
defaultDataSource, timestampAdjusterProvider,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
HlsSampleSource defaultSampleSource = new HlsSampleSource(defaultChunkSource, loadControl,
C.DEFAULT_MUXED_BUFFER_SIZE, mainHandler, player, C.TRACK_TYPE_VIDEO);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource audioChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_AUDIO,
audioDataSource, timestampAdjusterProvider, null);
HlsSampleSource audioSampleSource = new HlsSampleSource(audioChunkSource, loadControl,
C.DEFAULT_AUDIO_BUFFER_SIZE, mainHandler, player, C.TRACK_TYPE_AUDIO);
DataSource subtitleDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource subtitleChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT,
subtitleDataSource, timestampAdjusterProvider, null);
HlsSampleSource subtitleSampleSource = new HlsSampleSource(subtitleChunkSource, loadControl,
C.DEFAULT_TEXT_BUFFER_SIZE, mainHandler, player, C.TRACK_TYPE_TEXT);
return new MultiSampleSource(defaultSampleSource, audioSampleSource, subtitleSampleSource);
}
public static SampleSource buildExtractorSource(DemoPlayer player,
DataSourceFactory dataSourceFactory, Uri uri) {
Allocator allocator = new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE);
DataSource dataSource = dataSourceFactory.createDataSource(player.getBandwidthMeter());
return new ExtractorSampleSource(uri, dataSource, allocator,
C.DEFAULT_MUXED_BUFFER_SIZE, player.getMainHandler(), player, 0);
}
}

View file

@ -45,7 +45,7 @@ import java.util.List;
/**
* A {@link SampleSource} for HLS streams.
*/
public final class HlsSampleSource implements SampleSource, Loader.Callback {
public final class HlsSampleSource implements Loader.Callback {
/**
* The default minimum number of times to retry loading data prior to failing.
@ -139,9 +139,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
pendingResetPositionUs = C.UNSET_TIME_US;
}
// SampleSource implementation.
@Override
public boolean prepare(long positionUs) throws IOException {
if (prepared) {
return true;
@ -173,17 +170,14 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
return false;
}
@Override
public long getDurationUs() {
return chunkSource.getDurationUs();
}
@Override
public TrackGroupArray getTrackGroups() {
return trackGroups;
}
@Override
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) {
Assertions.checkState(prepared);
@ -233,7 +227,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
return newStreams;
}
@Override
public void continueBuffering(long playbackPositionUs) {
downstreamPositionUs = playbackPositionUs;
discardSamplesForDisabledTracks();
@ -242,7 +235,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
}
}
@Override
public long readReset() {
if (notifyReset) {
notifyReset = false;
@ -251,7 +243,6 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
return C.UNSET_TIME_US;
}
@Override
public long getBufferedPositionUs() {
if (loadingFinished) {
return C.END_OF_SOURCE_US;
@ -273,12 +264,10 @@ public final class HlsSampleSource implements SampleSource, Loader.Callback {
}
}
@Override
public void seekToUs(long positionUs) {
seekToInternal(positionUs);
}
@Override
public void release() {
if (enabledTrackCount > 0) {
loadControl.unregister(this);

View file

@ -0,0 +1,268 @@
/*
* 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.hls;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackGroup;
import com.google.android.exoplayer.TrackGroupArray;
import com.google.android.exoplayer.TrackSelection;
import com.google.android.exoplayer.TrackStream;
import com.google.android.exoplayer.chunk.ChunkTrackStreamEventListener;
import com.google.android.exoplayer.chunk.FormatEvaluator;
import com.google.android.exoplayer.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer.hls.playlist.HlsPlaylistParser;
import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSourceFactory;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.ManifestFetcher;
import android.net.Uri;
import android.os.Handler;
import android.util.Pair;
import java.io.IOException;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
/**
* A {@link SampleSource} for HLS streams.
*/
public final class HlsSource implements SampleSource {
private final ManifestFetcher<HlsPlaylist> manifestFetcher;
private final HlsSampleSource[] sources;
private final IdentityHashMap<TrackStream, HlsSampleSource> trackStreamSources;
private final int[] selectedTrackCounts;
private boolean prepared;
private boolean seenFirstTrackSelection;
private long durationUs;
private TrackGroupArray trackGroups;
private HlsSampleSource[] enabledSources;
public HlsSource(Uri uri, DataSourceFactory dataSourceFactory, BandwidthMeter bandwidthMeter,
Handler eventHandler, ChunkTrackStreamEventListener eventListener) {
HlsPlaylistParser parser = new HlsPlaylistParser();
DataSource manifestDataSource = dataSourceFactory.createDataSource();
manifestFetcher = new ManifestFetcher<>(uri, manifestDataSource, parser);
LoadControl loadControl = new DefaultLoadControl(
new DefaultAllocator(C.DEFAULT_BUFFER_SEGMENT_SIZE));
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
DataSource defaultDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource defaultChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_DEFAULT,
defaultDataSource, timestampAdjusterProvider,
new FormatEvaluator.AdaptiveEvaluator(bandwidthMeter));
HlsSampleSource defaultSampleSource = new HlsSampleSource(defaultChunkSource, loadControl,
C.DEFAULT_MUXED_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_VIDEO);
DataSource audioDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource audioChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_AUDIO,
audioDataSource, timestampAdjusterProvider, null);
HlsSampleSource audioSampleSource = new HlsSampleSource(audioChunkSource, loadControl,
C.DEFAULT_AUDIO_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_AUDIO);
DataSource subtitleDataSource = dataSourceFactory.createDataSource(bandwidthMeter);
HlsChunkSource subtitleChunkSource = new HlsChunkSource(manifestFetcher, C.TRACK_TYPE_TEXT,
subtitleDataSource, timestampAdjusterProvider, null);
HlsSampleSource subtitleSampleSource = new HlsSampleSource(subtitleChunkSource, loadControl,
C.DEFAULT_TEXT_BUFFER_SIZE, eventHandler, eventListener, C.TRACK_TYPE_TEXT);
sources = new HlsSampleSource[] {defaultSampleSource, audioSampleSource, subtitleSampleSource};
selectedTrackCounts = new int[sources.length];
trackStreamSources = new IdentityHashMap<>();
}
@Override
public boolean prepare(long positionUs) throws IOException {
if (prepared) {
return true;
}
boolean sourcesPrepared = true;
for (HlsSampleSource source : sources) {
sourcesPrepared &= source.prepare(positionUs);
}
if (!sourcesPrepared) {
return false;
}
durationUs = 0;
int totalTrackGroupCount = 0;
for (HlsSampleSource source : sources) {
totalTrackGroupCount += source.getTrackGroups().length;
if (durationUs != C.UNSET_TIME_US) {
long sourceDurationUs = source.getDurationUs();
durationUs = sourceDurationUs == C.UNSET_TIME_US
? C.UNSET_TIME_US : Math.max(durationUs, sourceDurationUs);
}
}
TrackGroup[] trackGroupArray = new TrackGroup[totalTrackGroupCount];
int trackGroupIndex = 0;
for (HlsSampleSource source : sources) {
int sourceTrackGroupCount = source.getTrackGroups().length;
for (int j = 0; j < sourceTrackGroupCount; j++) {
trackGroupArray[trackGroupIndex++] = source.getTrackGroups().get(j);
}
}
trackGroups = new TrackGroupArray(trackGroupArray);
prepared = true;
return true;
}
@Override
public long getDurationUs() {
return durationUs;
}
@Override
public TrackGroupArray getTrackGroups() {
return trackGroups;
}
@Override
public TrackStream[] selectTracks(List<TrackStream> oldStreams,
List<TrackSelection> newSelections, long positionUs) {
Assertions.checkState(prepared);
TrackStream[] newStreams = new TrackStream[newSelections.size()];
// Select tracks for each source.
int enabledSourceCount = 0;
for (int i = 0; i < sources.length; i++) {
selectedTrackCounts[i] += selectTracks(sources[i], oldStreams, newSelections, positionUs,
newStreams);
if (selectedTrackCounts[i] > 0) {
enabledSourceCount++;
}
}
// Update the enabled sources.
enabledSources = new HlsSampleSource[enabledSourceCount];
enabledSourceCount = 0;
for (int i = 0; i < sources.length; i++) {
if (selectedTrackCounts[i] > 0) {
enabledSources[enabledSourceCount++] = sources[i];
}
}
seenFirstTrackSelection = true;
return newStreams;
}
@Override
public void continueBuffering(long positionUs) {
for (HlsSampleSource source : enabledSources) {
source.continueBuffering(positionUs);
}
}
@Override
public long readReset() {
long resetPositionUs = C.UNSET_TIME_US;
for (HlsSampleSource source : enabledSources) {
long childResetPositionUs = source.readReset();
if (resetPositionUs == C.UNSET_TIME_US) {
resetPositionUs = childResetPositionUs;
} else if (childResetPositionUs != C.UNSET_TIME_US) {
resetPositionUs = Math.min(resetPositionUs, childResetPositionUs);
}
}
return resetPositionUs;
}
@Override
public long getBufferedPositionUs() {
long bufferedPositionUs = durationUs != C.UNSET_TIME_US ? durationUs : Long.MAX_VALUE;
for (HlsSampleSource source : enabledSources) {
long rendererBufferedPositionUs = source.getBufferedPositionUs();
if (rendererBufferedPositionUs == C.UNSET_TIME_US) {
return C.UNSET_TIME_US;
} else if (rendererBufferedPositionUs == C.END_OF_SOURCE_US) {
// This source is fully buffered.
} else {
bufferedPositionUs = Math.min(bufferedPositionUs, rendererBufferedPositionUs);
}
}
return bufferedPositionUs == Long.MAX_VALUE ? C.UNSET_TIME_US : bufferedPositionUs;
}
@Override
public void seekToUs(long positionUs) {
for (HlsSampleSource source : enabledSources) {
source.seekToUs(positionUs);
}
}
@Override
public void release() {
manifestFetcher.release();
for (HlsSampleSource source : sources) {
source.release();
}
}
// Internal methods.
private int selectTracks(HlsSampleSource source, List<TrackStream> allOldStreams,
List<TrackSelection> allNewSelections, long positionUs, TrackStream[] allNewStreams) {
// Get the subset of the old streams for the source.
ArrayList<TrackStream> oldStreams = new ArrayList<>();
for (int i = 0; i < allOldStreams.size(); i++) {
TrackStream stream = allOldStreams.get(i);
if (trackStreamSources.get(stream) == source) {
trackStreamSources.remove(stream);
oldStreams.add(stream);
}
}
// Get the subset of the new selections for the source.
ArrayList<TrackSelection> newSelections = new ArrayList<>();
int[] newSelectionOriginalIndices = new int[allNewSelections.size()];
for (int i = 0; i < allNewSelections.size(); i++) {
TrackSelection selection = allNewSelections.get(i);
Pair<HlsSampleSource, Integer> sourceAndGroup = getSourceAndGroup(selection.group);
if (sourceAndGroup.first == source) {
newSelectionOriginalIndices[newSelections.size()] = i;
newSelections.add(new TrackSelection(sourceAndGroup.second, selection.getTracks()));
}
}
// Do nothing if nothing has changed, except during the first selection.
if (seenFirstTrackSelection && oldStreams.isEmpty() && newSelections.isEmpty()) {
return 0;
}
// Perform the selection.
TrackStream[] newStreams = source.selectTracks(oldStreams, newSelections, positionUs);
for (int j = 0; j < newStreams.length; j++) {
allNewStreams[newSelectionOriginalIndices[j]] = newStreams[j];
trackStreamSources.put(newStreams[j], source);
}
return newSelections.size() - oldStreams.size();
}
private Pair<HlsSampleSource, Integer> getSourceAndGroup(int group) {
int totalTrackGroupCount = 0;
for (HlsSampleSource source : sources) {
int sourceTrackGroupCount = source.getTrackGroups().length;
if (group < totalTrackGroupCount + sourceTrackGroupCount) {
return Pair.create(source, group - totalTrackGroupCount);
}
totalTrackGroupCount += sourceTrackGroupCount;
}
throw new IndexOutOfBoundsException();
}
}