Expose empty CEA-608 and EMSG tracks for DASH

This change exposes declared CEA-608 and EMSG tracks. The
tracks currently provide no samples.

Issue: #2362
Issue: #2176

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148390849
This commit is contained in:
olly 2017-02-23 14:20:34 -08:00 committed by Oliver Woodman
parent d99cb28e6a
commit 88fc337db0
2 changed files with 103 additions and 28 deletions

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2017 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.exoplayer2.source;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import java.io.IOException;
/**
* An empty {@link SampleStream}.
*/
public final class EmptySampleStream implements SampleStream {
@Override
public boolean isReady() {
return true;
}
@Override
public void maybeThrowError() throws IOException {
// Do nothing.
}
@Override
public int readData(FormatHolder formatHolder, DecoderInputBuffer buffer,
boolean formatRequired) {
buffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM);
return C.RESULT_BUFFER_READ;
}
@Override
public void skipToKeyframeBefore(long timeUs) {
// Do nothing.
}
}

View File

@ -19,6 +19,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.CompositeSequenceableLoader; import com.google.android.exoplayer2.source.CompositeSequenceableLoader;
import com.google.android.exoplayer2.source.EmptySampleStream;
import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.SampleStream; import com.google.android.exoplayer2.source.SampleStream;
import com.google.android.exoplayer2.source.SequenceableLoader; import com.google.android.exoplayer2.source.SequenceableLoader;
@ -27,12 +28,12 @@ import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.chunk.ChunkSampleStream; import com.google.android.exoplayer2.source.chunk.ChunkSampleStream;
import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet; import com.google.android.exoplayer2.source.dash.manifest.AdaptationSet;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest; import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.source.dash.manifest.Period;
import com.google.android.exoplayer2.source.dash.manifest.Representation; import com.google.android.exoplayer2.source.dash.manifest.Representation;
import com.google.android.exoplayer2.source.dash.manifest.SchemeValuePair; import com.google.android.exoplayer2.source.dash.manifest.SchemeValuePair;
import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.LoaderErrorThrower; import com.google.android.exoplayer2.upstream.LoaderErrorThrower;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -56,16 +57,16 @@ import java.util.List;
private ChunkSampleStream<DashChunkSource>[] sampleStreams; private ChunkSampleStream<DashChunkSource>[] sampleStreams;
private CompositeSequenceableLoader sequenceableLoader; private CompositeSequenceableLoader sequenceableLoader;
private DashManifest manifest; private DashManifest manifest;
private int index; private int periodIndex;
private Period period; private List<AdaptationSet> adaptationSets;
public DashMediaPeriod(int id, DashManifest manifest, int index, public DashMediaPeriod(int id, DashManifest manifest, int periodIndex,
DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount, DashChunkSource.Factory chunkSourceFactory, int minLoadableRetryCount,
EventDispatcher eventDispatcher, long elapsedRealtimeOffset, EventDispatcher eventDispatcher, long elapsedRealtimeOffset,
LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) { LoaderErrorThrower manifestLoaderErrorThrower, Allocator allocator) {
this.id = id; this.id = id;
this.manifest = manifest; this.manifest = manifest;
this.index = index; this.periodIndex = periodIndex;
this.chunkSourceFactory = chunkSourceFactory; this.chunkSourceFactory = chunkSourceFactory;
this.minLoadableRetryCount = minLoadableRetryCount; this.minLoadableRetryCount = minLoadableRetryCount;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
@ -74,17 +75,17 @@ import java.util.List;
this.allocator = allocator; this.allocator = allocator;
sampleStreams = newSampleStreamArray(0); sampleStreams = newSampleStreamArray(0);
sequenceableLoader = new CompositeSequenceableLoader(sampleStreams); sequenceableLoader = new CompositeSequenceableLoader(sampleStreams);
period = manifest.getPeriod(index); adaptationSets = manifest.getPeriod(periodIndex).adaptationSets;
trackGroups = buildTrackGroups(period); trackGroups = buildTrackGroups(adaptationSets);
} }
public void updateManifest(DashManifest manifest, int index) { public void updateManifest(DashManifest manifest, int periodIndex) {
this.manifest = manifest; this.manifest = manifest;
this.index = index; this.periodIndex = periodIndex;
period = manifest.getPeriod(index); adaptationSets = manifest.getPeriod(periodIndex).adaptationSets;
if (sampleStreams != null) { if (sampleStreams != null) {
for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) { for (ChunkSampleStream<DashChunkSource> sampleStream : sampleStreams) {
sampleStream.getChunkSource().updateManifest(manifest, index); sampleStream.getChunkSource().updateManifest(manifest, periodIndex);
} }
callback.onContinueLoadingRequested(this); callback.onContinueLoadingRequested(this);
} }
@ -117,7 +118,7 @@ import java.util.List;
SampleStream[] streams, boolean[] streamResetFlags, long positionUs) { SampleStream[] streams, boolean[] streamResetFlags, long positionUs) {
ArrayList<ChunkSampleStream<DashChunkSource>> sampleStreamsList = new ArrayList<>(); ArrayList<ChunkSampleStream<DashChunkSource>> sampleStreamsList = new ArrayList<>();
for (int i = 0; i < selections.length; i++) { for (int i = 0; i < selections.length; i++) {
if (streams[i] != null) { if (streams[i] instanceof ChunkSampleStream) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ChunkSampleStream<DashChunkSource> stream = (ChunkSampleStream<DashChunkSource>) streams[i]; ChunkSampleStream<DashChunkSource> stream = (ChunkSampleStream<DashChunkSource>) streams[i];
if (selections[i] == null || !mayRetainStreamFlags[i]) { if (selections[i] == null || !mayRetainStreamFlags[i]) {
@ -126,11 +127,21 @@ import java.util.List;
} else { } else {
sampleStreamsList.add(stream); sampleStreamsList.add(stream);
} }
} else if (streams[i] instanceof EmptySampleStream && selections[i] == null) {
// TODO: Release streams for cea-608 and emsg tracks.
streams[i] = null;
} }
if (streams[i] == null && selections[i] != null) { if (streams[i] == null && selections[i] != null) {
ChunkSampleStream<DashChunkSource> stream = buildSampleStream(selections[i], positionUs); int adaptationSetIndex = trackGroups.indexOf(selections[i].getTrackGroup());
sampleStreamsList.add(stream); if (adaptationSetIndex < adaptationSets.size()) {
streams[i] = stream; ChunkSampleStream<DashChunkSource> stream = buildSampleStream(adaptationSetIndex,
selections[i], positionUs);
sampleStreamsList.add(stream);
streams[i] = stream;
} else {
// TODO: Output streams for cea-608 and emsg tracks.
streams[i] = new EmptySampleStream();
}
streamResetFlags[i] = true; streamResetFlags[i] = true;
} }
} }
@ -184,35 +195,50 @@ import java.util.List;
// Internal methods. // Internal methods.
private static TrackGroupArray buildTrackGroups(Period period) { private static TrackGroupArray buildTrackGroups(List<AdaptationSet> adaptationSets) {
TrackGroup[] trackGroupArray = new TrackGroup[period.adaptationSets.size()]; int adaptationSetCount = adaptationSets.size();
for (int i = 0; i < period.adaptationSets.size(); i++) { int eventMessageTrackCount = getEventMessageTrackCount(adaptationSets);
AdaptationSet adaptationSet = period.adaptationSets.get(i); int cea608TrackCount = getCea608TrackCount(adaptationSets);
TrackGroup[] trackGroupArray = new TrackGroup[adaptationSetCount + eventMessageTrackCount
+ cea608TrackCount];
int eventMessageTrackIndex = 0;
int cea608TrackIndex = 0;
for (int i = 0; i < adaptationSetCount; i++) {
AdaptationSet adaptationSet = adaptationSets.get(i);
List<Representation> representations = adaptationSet.representations; List<Representation> representations = adaptationSet.representations;
Format[] formats = new Format[representations.size()]; Format[] formats = new Format[representations.size()];
for (int j = 0; j < formats.length; j++) { for (int j = 0; j < formats.length; j++) {
formats[j] = representations.get(j).format; formats[j] = representations.get(j).format;
} }
trackGroupArray[i] = new TrackGroup(formats); trackGroupArray[i] = new TrackGroup(formats);
if (hasEventMessageTrack(adaptationSet)) {
Format format = Format.createSampleFormat(adaptationSet.id + ":emsg",
MimeTypes.APPLICATION_EMSG, null, Format.NO_VALUE, null);
trackGroupArray[adaptationSetCount + eventMessageTrackIndex++] = new TrackGroup(format);
}
if (hasCea608Track(adaptationSet)) {
Format format = Format.createTextSampleFormat(adaptationSet.id + ":cea608",
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null);
trackGroupArray[adaptationSetCount + eventMessageTrackCount + cea608TrackIndex++] =
new TrackGroup(format);
}
} }
return new TrackGroupArray(trackGroupArray); return new TrackGroupArray(trackGroupArray);
} }
private ChunkSampleStream<DashChunkSource> buildSampleStream(TrackSelection selection, private ChunkSampleStream<DashChunkSource> buildSampleStream(int adaptationSetIndex,
long positionUs) { TrackSelection selection, long positionUs) {
int adaptationSetIndex = trackGroups.indexOf(selection.getTrackGroup()); AdaptationSet adaptationSet = adaptationSets.get(adaptationSetIndex);
AdaptationSet adaptationSet = period.adaptationSets.get(adaptationSetIndex);
boolean enableEventMessageTrack = hasEventMessageTrack(adaptationSet); boolean enableEventMessageTrack = hasEventMessageTrack(adaptationSet);
boolean enableCea608Track = hasCea608Track(adaptationSet); boolean enableCea608Track = hasCea608Track(adaptationSet);
DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource( DashChunkSource chunkSource = chunkSourceFactory.createDashChunkSource(
manifestLoaderErrorThrower, manifest, index, adaptationSetIndex, selection, manifestLoaderErrorThrower, manifest, periodIndex, adaptationSetIndex, selection,
elapsedRealtimeOffset, enableEventMessageTrack, enableCea608Track); elapsedRealtimeOffset, enableEventMessageTrack, enableCea608Track);
return new ChunkSampleStream<>(adaptationSet.type, chunkSource, this, allocator, positionUs, return new ChunkSampleStream<>(adaptationSet.type, chunkSource, this, allocator, positionUs,
minLoadableRetryCount, eventDispatcher); minLoadableRetryCount, eventDispatcher);
} }
private static int getEventMessageTrackCount(Period period) { private static int getEventMessageTrackCount(List<AdaptationSet> adaptationSets) {
List<AdaptationSet> adaptationSets = period.adaptationSets;
int inbandEventStreamTrackCount = 0; int inbandEventStreamTrackCount = 0;
for (int i = 0; i < adaptationSets.size(); i++) { for (int i = 0; i < adaptationSets.size(); i++) {
if (hasEventMessageTrack(adaptationSets.get(i))) { if (hasEventMessageTrack(adaptationSets.get(i))) {
@ -233,8 +259,7 @@ import java.util.List;
return false; return false;
} }
private static int getCea608TrackCount(Period period) { private static int getCea608TrackCount(List<AdaptationSet> adaptationSets) {
List<AdaptationSet> adaptationSets = period.adaptationSets;
int cea608TrackCount = 0; int cea608TrackCount = 0;
for (int i = 0; i < adaptationSets.size(); i++) { for (int i = 0; i < adaptationSets.size(); i++) {
if (hasCea608Track(adaptationSets.get(i))) { if (hasCea608Track(adaptationSets.get(i))) {