HLS: Fill primary sample formats with track format info (e.g. bitrate)
Issue: #3297 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=204732266
This commit is contained in:
parent
7b2da629ea
commit
b25a124239
@ -47,6 +47,8 @@
|
|||||||
* DASH: Exclude text streams from duration calculations
|
* DASH: Exclude text streams from duration calculations
|
||||||
([#4029](https://github.com/google/ExoPlayer/issues/4029)).
|
([#4029](https://github.com/google/ExoPlayer/issues/4029)).
|
||||||
* HLS:
|
* HLS:
|
||||||
|
* Set the bitrate on primary track sample formats
|
||||||
|
([#3297](https://github.com/google/ExoPlayer/issues/3297)).
|
||||||
* Pass HTTP response headers to `HlsExtractorFactory.createExtractor`.
|
* Pass HTTP response headers to `HlsExtractorFactory.createExtractor`.
|
||||||
* Add support for EXT-X-INDEPENDENT-SEGMENTS in the master playlist.
|
* Add support for EXT-X-INDEPENDENT-SEGMENTS in the master playlist.
|
||||||
* Support load error handling customization
|
* Support load error handling customization
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
package com.google.android.exoplayer2.source.hls;
|
package com.google.android.exoplayer2.source.hls;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.IntDef;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
@ -45,8 +44,6 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -83,15 +80,6 @@ import java.util.List;
|
|||||||
public static final int SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL = -2;
|
public static final int SAMPLE_QUEUE_INDEX_NO_MAPPING_FATAL = -2;
|
||||||
public static final int SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL = -3;
|
public static final int SAMPLE_QUEUE_INDEX_NO_MAPPING_NON_FATAL = -3;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@IntDef({PRIMARY_TYPE_NONE, PRIMARY_TYPE_TEXT, PRIMARY_TYPE_AUDIO, PRIMARY_TYPE_VIDEO})
|
|
||||||
private @interface PrimaryTrackType {}
|
|
||||||
|
|
||||||
private static final int PRIMARY_TYPE_NONE = 0;
|
|
||||||
private static final int PRIMARY_TYPE_TEXT = 1;
|
|
||||||
private static final int PRIMARY_TYPE_AUDIO = 2;
|
|
||||||
private static final int PRIMARY_TYPE_VIDEO = 3;
|
|
||||||
|
|
||||||
private final int trackType;
|
private final int trackType;
|
||||||
private final Callback callback;
|
private final Callback callback;
|
||||||
private final HlsChunkSource chunkSource;
|
private final HlsChunkSource chunkSource;
|
||||||
@ -115,9 +103,12 @@ import java.util.List;
|
|||||||
private int audioSampleQueueIndex;
|
private int audioSampleQueueIndex;
|
||||||
private boolean videoSampleQueueMappingDone;
|
private boolean videoSampleQueueMappingDone;
|
||||||
private int videoSampleQueueIndex;
|
private int videoSampleQueueIndex;
|
||||||
|
private int primarySampleQueueType;
|
||||||
|
private int primarySampleQueueIndex;
|
||||||
private boolean sampleQueuesBuilt;
|
private boolean sampleQueuesBuilt;
|
||||||
private boolean prepared;
|
private boolean prepared;
|
||||||
private int enabledTrackGroupCount;
|
private int enabledTrackGroupCount;
|
||||||
|
private Format upstreamTrackFormat;
|
||||||
private Format downstreamTrackFormat;
|
private Format downstreamTrackFormat;
|
||||||
private boolean released;
|
private boolean released;
|
||||||
|
|
||||||
@ -471,8 +462,23 @@ import java.util.List;
|
|||||||
downstreamTrackFormat = trackFormat;
|
downstreamTrackFormat = trackFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sampleQueues[sampleQueueIndex].read(formatHolder, buffer, requireFormat, loadingFinished,
|
int result =
|
||||||
lastSeekPositionUs);
|
sampleQueues[sampleQueueIndex].read(
|
||||||
|
formatHolder, buffer, requireFormat, loadingFinished, lastSeekPositionUs);
|
||||||
|
if (result == C.RESULT_FORMAT_READ && sampleQueueIndex == primarySampleQueueIndex) {
|
||||||
|
// Fill in primary sample format with information from the track format.
|
||||||
|
int chunkUid = sampleQueues[sampleQueueIndex].peekSourceId();
|
||||||
|
int chunkIndex = 0;
|
||||||
|
while (chunkIndex < mediaChunks.size() && mediaChunks.get(chunkIndex).uid != chunkUid) {
|
||||||
|
chunkIndex++;
|
||||||
|
}
|
||||||
|
Format trackFormat =
|
||||||
|
chunkIndex < mediaChunks.size()
|
||||||
|
? mediaChunks.get(chunkIndex).trackFormat
|
||||||
|
: upstreamTrackFormat;
|
||||||
|
formatHolder.format = formatHolder.format.copyWithManifestFormatInfo(trackFormat);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int skipData(int sampleQueueIndex, long positionUs) {
|
public int skipData(int sampleQueueIndex, long positionUs) {
|
||||||
@ -563,6 +569,7 @@ import java.util.List;
|
|||||||
HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
|
HlsMediaChunk mediaChunk = (HlsMediaChunk) loadable;
|
||||||
mediaChunk.init(this);
|
mediaChunk.init(this);
|
||||||
mediaChunks.add(mediaChunk);
|
mediaChunks.add(mediaChunk);
|
||||||
|
upstreamTrackFormat = mediaChunk.trackFormat;
|
||||||
}
|
}
|
||||||
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
|
long elapsedRealtimeMs = loader.startLoading(loadable, this, minLoadableRetryCount);
|
||||||
eventDispatcher.loadStarted(
|
eventDispatcher.loadStarted(
|
||||||
@ -770,8 +777,8 @@ import java.util.List;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SampleQueue trackOutput = new SampleQueue(allocator);
|
SampleQueue trackOutput = new SampleQueue(allocator);
|
||||||
trackOutput.sourceId(chunkUid);
|
|
||||||
trackOutput.setSampleOffsetUs(sampleOffsetUs);
|
trackOutput.setSampleOffsetUs(sampleOffsetUs);
|
||||||
|
trackOutput.sourceId(chunkUid);
|
||||||
trackOutput.setUpstreamFormatChangeListener(this);
|
trackOutput.setUpstreamFormatChangeListener(this);
|
||||||
sampleQueueTrackIds = Arrays.copyOf(sampleQueueTrackIds, trackCount + 1);
|
sampleQueueTrackIds = Arrays.copyOf(sampleQueueTrackIds, trackCount + 1);
|
||||||
sampleQueueTrackIds[trackCount] = id;
|
sampleQueueTrackIds[trackCount] = id;
|
||||||
@ -788,6 +795,10 @@ import java.util.List;
|
|||||||
videoSampleQueueMappingDone = true;
|
videoSampleQueueMappingDone = true;
|
||||||
videoSampleQueueIndex = trackCount;
|
videoSampleQueueIndex = trackCount;
|
||||||
}
|
}
|
||||||
|
if (getTrackTypeScore(type) > getTrackTypeScore(primarySampleQueueType)) {
|
||||||
|
primarySampleQueueIndex = trackCount;
|
||||||
|
primarySampleQueueType = type;
|
||||||
|
}
|
||||||
sampleQueuesEnabledStates = Arrays.copyOf(sampleQueuesEnabledStates, trackCount + 1);
|
sampleQueuesEnabledStates = Arrays.copyOf(sampleQueuesEnabledStates, trackCount + 1);
|
||||||
return trackOutput;
|
return trackOutput;
|
||||||
}
|
}
|
||||||
@ -925,22 +936,22 @@ import java.util.List;
|
|||||||
private void buildTracksFromSampleStreams() {
|
private void buildTracksFromSampleStreams() {
|
||||||
// Iterate through the extractor tracks to discover the "primary" track type, and the index
|
// Iterate through the extractor tracks to discover the "primary" track type, and the index
|
||||||
// of the single track of this type.
|
// of the single track of this type.
|
||||||
@PrimaryTrackType int primaryExtractorTrackType = PRIMARY_TYPE_NONE;
|
int primaryExtractorTrackType = C.TRACK_TYPE_NONE;
|
||||||
int primaryExtractorTrackIndex = C.INDEX_UNSET;
|
int primaryExtractorTrackIndex = C.INDEX_UNSET;
|
||||||
int extractorTrackCount = sampleQueues.length;
|
int extractorTrackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < extractorTrackCount; i++) {
|
for (int i = 0; i < extractorTrackCount; i++) {
|
||||||
String sampleMimeType = sampleQueues[i].getUpstreamFormat().sampleMimeType;
|
String sampleMimeType = sampleQueues[i].getUpstreamFormat().sampleMimeType;
|
||||||
@PrimaryTrackType int trackType;
|
int trackType;
|
||||||
if (MimeTypes.isVideo(sampleMimeType)) {
|
if (MimeTypes.isVideo(sampleMimeType)) {
|
||||||
trackType = PRIMARY_TYPE_VIDEO;
|
trackType = C.TRACK_TYPE_VIDEO;
|
||||||
} else if (MimeTypes.isAudio(sampleMimeType)) {
|
} else if (MimeTypes.isAudio(sampleMimeType)) {
|
||||||
trackType = PRIMARY_TYPE_AUDIO;
|
trackType = C.TRACK_TYPE_AUDIO;
|
||||||
} else if (MimeTypes.isText(sampleMimeType)) {
|
} else if (MimeTypes.isText(sampleMimeType)) {
|
||||||
trackType = PRIMARY_TYPE_TEXT;
|
trackType = C.TRACK_TYPE_TEXT;
|
||||||
} else {
|
} else {
|
||||||
trackType = PRIMARY_TYPE_NONE;
|
trackType = C.TRACK_TYPE_NONE;
|
||||||
}
|
}
|
||||||
if (trackType > primaryExtractorTrackType) {
|
if (getTrackTypeScore(trackType) > getTrackTypeScore(primaryExtractorTrackType)) {
|
||||||
primaryExtractorTrackType = trackType;
|
primaryExtractorTrackType = trackType;
|
||||||
primaryExtractorTrackIndex = i;
|
primaryExtractorTrackIndex = i;
|
||||||
} else if (trackType == primaryExtractorTrackType
|
} else if (trackType == primaryExtractorTrackType
|
||||||
@ -977,8 +988,11 @@ import java.util.List;
|
|||||||
trackGroups[i] = new TrackGroup(formats);
|
trackGroups[i] = new TrackGroup(formats);
|
||||||
primaryTrackGroupIndex = i;
|
primaryTrackGroupIndex = i;
|
||||||
} else {
|
} else {
|
||||||
Format trackFormat = primaryExtractorTrackType == PRIMARY_TYPE_VIDEO
|
Format trackFormat =
|
||||||
&& MimeTypes.isAudio(sampleFormat.sampleMimeType) ? muxedAudioFormat : null;
|
primaryExtractorTrackType == C.TRACK_TYPE_VIDEO
|
||||||
|
&& MimeTypes.isAudio(sampleFormat.sampleMimeType)
|
||||||
|
? muxedAudioFormat
|
||||||
|
: null;
|
||||||
trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat, false));
|
trackGroups[i] = new TrackGroup(deriveFormat(trackFormat, sampleFormat, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1019,6 +1033,26 @@ import java.util.List;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scores a track type. Where multiple tracks are muxed into a container, the track with the
|
||||||
|
* highest score is the primary track.
|
||||||
|
*
|
||||||
|
* @param trackType The track type.
|
||||||
|
* @return The score.
|
||||||
|
*/
|
||||||
|
private static int getTrackTypeScore(int trackType) {
|
||||||
|
switch (trackType) {
|
||||||
|
case C.TRACK_TYPE_VIDEO:
|
||||||
|
return 3;
|
||||||
|
case C.TRACK_TYPE_AUDIO:
|
||||||
|
return 2;
|
||||||
|
case C.TRACK_TYPE_TEXT:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a track sample format from the corresponding format in the master playlist, and a
|
* Derives a track sample format from the corresponding format in the master playlist, and a
|
||||||
* sample format that may have been obtained from a chunk belonging to a different track.
|
* sample format that may have been obtained from a chunk belonging to a different track.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user