mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-16 04:52:39 +02:00
Further work.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class ITagUtils {
|
||||
public final static String AUDIO_68K_WEBM = "249";
|
||||
public final static String AUDIO_89K_WEBM = "250";
|
||||
public final static String AUDIO_133K_WEBM = "171";
|
||||
public final static String AUDIO_156K_WEBM = "251";
|
||||
public final static String AUDIO_48K_AAC = "139";
|
||||
public final static String AUDIO_128K_AAC = "140";
|
||||
public final static String VIDEO_144P_WEBM = "278";
|
||||
public final static String VIDEO_144P_AVC = "160";
|
||||
public final static String VIDEO_240P_WEBM = "242";
|
||||
public final static String VIDEO_240P_AVC = "133";
|
||||
public final static String VIDEO_360P_WEBM = "243";
|
||||
public final static String VIDEO_360P_AVC = "134";
|
||||
public final static String VIDEO_480P_WEBM = "244";
|
||||
public final static String VIDEO_480P_AVC = "135";
|
||||
public final static String VIDEO_720P_WEBM = "247";
|
||||
public final static String VIDEO_720P_WEBM_60FPS_HDR = "334";
|
||||
public final static String VIDEO_720P_AVC = "136";
|
||||
public final static String VIDEO_720P_AVC_60FPS = "298";
|
||||
public final static String VIDEO_1080P_WEBM = "248";
|
||||
public final static String VIDEO_1080P_WEBM_60FPS_HDR = "335";
|
||||
public final static String VIDEO_1080P_AVC = "137";
|
||||
public final static String VIDEO_1080P_AVC_60FPS = "299";
|
||||
public final static String VIDEO_1440P_WEBM = "271";
|
||||
public final static String VIDEO_1440P_WEBM_60FPS_HDR = "336";
|
||||
public final static String VIDEO_1440P_WEBM_60FPS = "308";
|
||||
public final static String VIDEO_1440P_AVC = "264";
|
||||
public final static String VIDEO_2160P_WEBM = "313";
|
||||
public final static String VIDEO_2160P_WEBM_60FPS_HDR = "337";
|
||||
public final static String VIDEO_2160P_WEBM_60FPS = "315";
|
||||
public final static String VIDEO_2160P_AVC = "266";
|
||||
public final static String VIDEO_2160P_AVC_HQ = "138";
|
||||
|
||||
public final static String MUXED_360P_WEBM = "43";
|
||||
public final static String MUXED_360P_AVC = "18";
|
||||
public final static String MUXED_720P_AVC = "22";
|
||||
|
||||
private final static List<String> sOrderedITagsAVC = Arrays.asList(
|
||||
MUXED_360P_AVC, MUXED_720P_AVC,
|
||||
AUDIO_48K_AAC, AUDIO_128K_AAC,
|
||||
VIDEO_144P_AVC, VIDEO_240P_AVC,
|
||||
VIDEO_360P_AVC, VIDEO_480P_AVC, VIDEO_720P_AVC, VIDEO_720P_AVC_60FPS,
|
||||
VIDEO_1080P_AVC, VIDEO_1080P_AVC_60FPS, VIDEO_1440P_AVC, VIDEO_2160P_AVC, VIDEO_2160P_AVC_HQ);
|
||||
|
||||
private final static List<String> sOrderedITagsWEBM = Arrays.asList(
|
||||
MUXED_360P_WEBM,
|
||||
AUDIO_68K_WEBM, AUDIO_89K_WEBM, AUDIO_133K_WEBM, AUDIO_156K_WEBM,
|
||||
VIDEO_144P_WEBM, VIDEO_240P_WEBM,
|
||||
VIDEO_360P_WEBM, VIDEO_480P_WEBM, VIDEO_720P_WEBM, VIDEO_720P_WEBM_60FPS_HDR,
|
||||
VIDEO_1080P_WEBM, VIDEO_1080P_WEBM_60FPS_HDR, VIDEO_1440P_WEBM, VIDEO_1440P_WEBM_60FPS_HDR, VIDEO_1440P_WEBM_60FPS,
|
||||
VIDEO_2160P_WEBM, VIDEO_2160P_WEBM_60FPS_HDR, VIDEO_2160P_WEBM_60FPS);
|
||||
|
||||
private final static List<List<String>> sITagsContainer = Arrays.asList(sOrderedITagsAVC, sOrderedITagsWEBM);
|
||||
public static final String AVC = "AVC";
|
||||
public static final String WEBM = "VP9";
|
||||
|
||||
public static int compare(String leftITag, String rightITag) {
|
||||
for (List<String> iTags : sITagsContainer) {
|
||||
int left = iTags.indexOf(leftITag);
|
||||
int right = iTags.indexOf(rightITag);
|
||||
if (left != -1 && right != -1) {
|
||||
return left - right;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: we can't be here
|
||||
return 99;
|
||||
}
|
||||
|
||||
public static boolean belongsToType(String type, String iTag) {
|
||||
String realType = getRealType(iTag);
|
||||
return type.equals(realType);
|
||||
}
|
||||
|
||||
public static boolean belongsToType(String type, int iTag) {
|
||||
String realType = getRealType(String.valueOf(iTag));
|
||||
return type.equals(realType);
|
||||
}
|
||||
|
||||
private static String getRealType(String iTag) {
|
||||
if (sOrderedITagsAVC.contains(iTag)) {
|
||||
return AVC;
|
||||
}
|
||||
return WEBM;
|
||||
}
|
||||
|
||||
public static String getAudioRateByTag(String iTag) {
|
||||
switch (iTag) {
|
||||
case AUDIO_128K_AAC:
|
||||
return "44100";
|
||||
case AUDIO_48K_AAC:
|
||||
return "22050";
|
||||
case AUDIO_156K_WEBM:
|
||||
return "48000";
|
||||
case AUDIO_133K_WEBM:
|
||||
return "44100";
|
||||
case AUDIO_89K_WEBM:
|
||||
return "48000";
|
||||
case AUDIO_68K_WEBM:
|
||||
return "48000";
|
||||
}
|
||||
return "44100";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MediaFormat extends Comparable<MediaFormat> {
|
||||
int FORMAT_TYPE_DASH = 0;
|
||||
int FORMAT_TYPE_REGULAR = 1;
|
||||
int FORMAT_TYPE_SABR = 2;
|
||||
// Common
|
||||
int getFormatType();
|
||||
String getUrl();
|
||||
String getMimeType();
|
||||
String getITag();
|
||||
boolean isDrc();
|
||||
|
||||
// DASH
|
||||
String getClen();
|
||||
String getBitrate();
|
||||
String getProjectionType();
|
||||
String getXtags();
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
String getIndex();
|
||||
String getInit();
|
||||
String getFps();
|
||||
String getLmt();
|
||||
String getQualityLabel();
|
||||
String getFormat();
|
||||
boolean isOtf();
|
||||
String getOtfInitUrl();
|
||||
String getOtfTemplateUrl();
|
||||
String getLanguage();
|
||||
// DASH LIVE
|
||||
String getTargetDurationSec();
|
||||
String getLastModified();
|
||||
String getMaxDvrDurationSec();
|
||||
|
||||
// Other/Regular
|
||||
String getQuality();
|
||||
String getSignature();
|
||||
String getAudioSamplingRate();
|
||||
String getSourceUrl();
|
||||
List<String> getSegmentUrlList();
|
||||
List<String> getGlobalSegmentList();
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class MediaFormatComparator implements Comparator<MediaFormat> {
|
||||
public static final int ORDER_DESCENDANT = 0;
|
||||
public static final int ORDER_ASCENDANT = 1;
|
||||
private int mOrderType = ORDER_DESCENDANT;
|
||||
|
||||
public MediaFormatComparator() {
|
||||
|
||||
}
|
||||
|
||||
public MediaFormatComparator(int orderType) {
|
||||
mOrderType = orderType;
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: Descendant sorting (better on top). High quality playback on external player.
|
||||
*/
|
||||
@Override
|
||||
public int compare(MediaFormat leftItem, MediaFormat rightItem) {
|
||||
if (leftItem.getGlobalSegmentList() != null ||
|
||||
rightItem.getGlobalSegmentList() != null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (mOrderType == ORDER_ASCENDANT) {
|
||||
MediaFormat tmpItem = leftItem;
|
||||
leftItem = rightItem;
|
||||
rightItem = tmpItem;
|
||||
}
|
||||
|
||||
int leftItemBitrate = leftItem.getBitrate() == null ? 0 : parseInt(leftItem.getBitrate());
|
||||
int rightItemBitrate = rightItem.getBitrate() == null ? 0 : parseInt(rightItem.getBitrate());
|
||||
|
||||
int leftItemHeight = leftItem.getHeight();
|
||||
int rightItemHeight = rightItem.getHeight();
|
||||
|
||||
int delta = rightItemHeight - leftItemHeight;
|
||||
|
||||
if (delta == 0) {
|
||||
delta = rightItemBitrate - leftItemBitrate;
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
public static boolean isNumeric(String s) {
|
||||
return s != null && s.matches("^[-+]?\\d*\\.?\\d+$");
|
||||
}
|
||||
|
||||
private int parseInt(String num) {
|
||||
if (!isNumeric(num)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Integer.parseInt(num);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MediaFormatUtils {
|
||||
public static final String MIME_WEBM_AUDIO = "audio/webm";
|
||||
public static final String MIME_WEBM_VIDEO = "video/webm";
|
||||
public static final String MIME_MP4_AUDIO = "audio/mp4";
|
||||
public static final String MIME_MP4_VIDEO = "video/mp4";
|
||||
private static final Pattern CODECS_PATTERN = Pattern.compile(".*codecs=\\\"(.*)\\\"");
|
||||
|
||||
public static boolean isNumeric(String s) {
|
||||
return s != null && s.matches("^[-+]?\\d*\\.?\\d+$");
|
||||
}
|
||||
|
||||
public static boolean isDash(String id) {
|
||||
if (!isNumeric(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int maxRegularITag = 50;
|
||||
int itag = Integer.parseInt(id);
|
||||
|
||||
return itag > maxRegularITag;
|
||||
}
|
||||
|
||||
public static boolean isDash(MediaFormat format) {
|
||||
if (format.getITag() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format.getGlobalSegmentList() != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String id = format.getITag();
|
||||
|
||||
return isDash(id);
|
||||
}
|
||||
|
||||
public static boolean checkMediaUrl(MediaFormat format) {
|
||||
return format != null && format.getUrl() != null;
|
||||
}
|
||||
|
||||
public static String extractMimeType(MediaFormat format) {
|
||||
if (format.getGlobalSegmentList() != null) {
|
||||
return format.getMimeType();
|
||||
}
|
||||
|
||||
String codecs = extractCodecs(format);
|
||||
|
||||
if (codecs.startsWith("vorbis") ||
|
||||
codecs.startsWith("opus")) {
|
||||
return MIME_WEBM_AUDIO;
|
||||
}
|
||||
|
||||
if (codecs.startsWith("vp9") ||
|
||||
codecs.startsWith("vp09")) {
|
||||
return MIME_WEBM_VIDEO;
|
||||
}
|
||||
|
||||
if (codecs.startsWith("mp4a") ||
|
||||
codecs.startsWith("ec-3") ||
|
||||
codecs.startsWith("ac-3")) {
|
||||
return MIME_MP4_AUDIO;
|
||||
}
|
||||
|
||||
if (codecs.startsWith("avc") ||
|
||||
codecs.startsWith("av01")) {
|
||||
return MIME_MP4_VIDEO;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String extractCodecs(MediaFormat format) {
|
||||
// input example: video/mp4;+codecs="avc1.640033"
|
||||
Matcher matcher = CODECS_PATTERN.matcher(format.getMimeType());
|
||||
matcher.find();
|
||||
return matcher.group(1);
|
||||
}
|
||||
|
||||
public static boolean isLiveMedia(MediaFormat format) {
|
||||
boolean isLive =
|
||||
format.getUrl().contains("live=1") ||
|
||||
format.getUrl().contains("yt_live_broadcast");
|
||||
|
||||
return isLive;
|
||||
}
|
||||
|
||||
private static String normalize(String word) {
|
||||
if (word == null || word.isEmpty()) {
|
||||
return word;
|
||||
}
|
||||
|
||||
return word.toLowerCase().replace("ё", "е");
|
||||
}
|
||||
|
||||
public static boolean startsWith(String word, String prefix) {
|
||||
if (word == null && prefix == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (word == null || prefix == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
word = normalize(word);
|
||||
prefix = normalize(prefix);
|
||||
|
||||
return word.startsWith(prefix);
|
||||
}
|
||||
|
||||
public static boolean isAudio(String mimeType) {
|
||||
return startsWith(mimeType, "audio");
|
||||
}
|
||||
|
||||
public static boolean isVideo(String mimeType) {
|
||||
return startsWith(mimeType, "video");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
public interface MediaItemFormatInfo {
|
||||
List<MediaFormat> getAdaptiveFormats();
|
||||
List<MediaFormat> getUrlFormats();
|
||||
List<MediaSubtitle> getSubtitles();
|
||||
String getHlsManifestUrl();
|
||||
String getDashManifestUrl();
|
||||
// video metadata
|
||||
String getLengthSeconds();
|
||||
String getTitle();
|
||||
String getAuthor();
|
||||
String getViewCount();
|
||||
String getDescription();
|
||||
String getVideoId();
|
||||
String getChannelId();
|
||||
boolean isLive();
|
||||
boolean isLiveContent();
|
||||
boolean containsMedia();
|
||||
boolean containsSabrFormats();
|
||||
boolean containsDashFormats();
|
||||
boolean containsHlsUrl();
|
||||
boolean containsDashUrl();
|
||||
boolean containsUrlFormats();
|
||||
boolean hasExtendedHlsFormats();
|
||||
float getVolumeLevel();
|
||||
InputStream createMpdStream();
|
||||
//Observable<InputStream> createMpdStreamObservable();
|
||||
List<String> createUrlList();
|
||||
MediaItemStoryboard createStoryboard();
|
||||
boolean isUnplayable();
|
||||
boolean isUnknownError();
|
||||
String getPlayabilityStatus();
|
||||
boolean isStreamSeekable();
|
||||
/**
|
||||
* Stream start time in UTC (!!!).<br/>
|
||||
* E.g.: <b>2021-10-06T13:36:25+00:00</b>
|
||||
*/
|
||||
String getStartTimestamp();
|
||||
String getUploadDate();
|
||||
/**
|
||||
* Stream start time in UNIX format.<br/>
|
||||
*/
|
||||
long getStartTimeMs();
|
||||
/**
|
||||
* Number of the stream first segment
|
||||
*/
|
||||
int getStartSegmentNum();
|
||||
/**
|
||||
* Precise segment duration.<br/>
|
||||
* Used inside live streams
|
||||
*/
|
||||
int getSegmentDurationUs();
|
||||
String getPaidContentText();
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
public interface MediaItemStoryboard {
|
||||
int getGroupDurationMS();
|
||||
Size getGroupSize();
|
||||
String getGroupUrl(int imgNum);
|
||||
interface Size {
|
||||
int getDurationEachMS();
|
||||
int getStartNum();
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
int getRowCount();
|
||||
int getColCount();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.futo.platformplayer.sabr;
|
||||
|
||||
public interface MediaSubtitle {
|
||||
String getBaseUrl();
|
||||
void setBaseUrl(String baseUrl);
|
||||
boolean isTranslatable();
|
||||
void setTranslatable(boolean translatable);
|
||||
String getLanguageCode();
|
||||
void setLanguageCode(String languageCode);
|
||||
String getVssId();
|
||||
void setVssId(String vssId);
|
||||
String getName();
|
||||
void setName(String name);
|
||||
String getMimeType();
|
||||
void setMimeType(String mimeType);
|
||||
String getCodecs();
|
||||
void setCodecs(String codecs);
|
||||
String getType();
|
||||
void setType(String type);
|
||||
}
|
||||
@@ -9,7 +9,10 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.LoadingInfo;
|
||||
import androidx.media3.exoplayer.SeekParameters;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionManager;
|
||||
import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory;
|
||||
import androidx.media3.exoplayer.source.EmptySampleStream;
|
||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||
@@ -72,6 +75,8 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
private int periodIndex;
|
||||
private List<EventStream> eventStreams;
|
||||
private boolean notifiedReadingStarted;
|
||||
private final DrmSessionManager drmSessionManager;
|
||||
private final DrmSessionEventListener.EventDispatcher drmEventDispatcher;
|
||||
|
||||
public SabrMediaPeriod(
|
||||
int id,
|
||||
@@ -107,6 +112,13 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
Pair<TrackGroupArray, TrackGroupInfo[]> result = buildTrackGroups(period.adaptationSets);
|
||||
trackGroups = result.first;
|
||||
trackGroupInfos = result.second;
|
||||
this.drmSessionManager = DrmSessionManager.DRM_UNSUPPORTED;
|
||||
this.drmEventDispatcher = new DrmSessionEventListener.EventDispatcher();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoading() {
|
||||
return compositeSequenceableLoader.isLoading();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,7 +182,6 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
@Override
|
||||
public long readDiscontinuity() {
|
||||
if (!notifiedReadingStarted) {
|
||||
eventDispatcher.readingStarted();
|
||||
notifiedReadingStarted = true;
|
||||
}
|
||||
return C.TIME_UNSET;
|
||||
@@ -208,8 +219,8 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean continueLoading(long positionUs) {
|
||||
return compositeSequenceableLoader.continueLoading(positionUs);
|
||||
public boolean continueLoading(LoadingInfo loadingInfo) {
|
||||
return compositeSequenceableLoader.continueLoading(loadingInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -255,9 +266,9 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
sampleStream.release(this);
|
||||
}
|
||||
callback = null;
|
||||
eventDispatcher.mediaPeriodReleased();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static ChunkSampleStream<SabrChunkSource>[] newSampleStreamArray(int length) {
|
||||
return new ChunkSampleStream[length];
|
||||
@@ -385,8 +396,11 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
eventMessageTrackGroupIndex,
|
||||
cea608TrackGroupIndex);
|
||||
if (eventMessageTrackGroupIndex != C.INDEX_UNSET) {
|
||||
Format format = Format.createSampleFormat(firstAdaptationSet.id + ":emsg",
|
||||
MimeTypes.APPLICATION_EMSG, null, Format.NO_VALUE, null);
|
||||
Format format =
|
||||
new Format.Builder()
|
||||
.setId(firstAdaptationSet.id + ":emsg")
|
||||
.setSampleMimeType(MimeTypes.APPLICATION_EMSG)
|
||||
.build();
|
||||
trackGroups[eventMessageTrackGroupIndex] = new TrackGroup(format);
|
||||
trackGroupInfos[eventMessageTrackGroupIndex] =
|
||||
TrackGroupInfo.embeddedEmsgTrack(adaptationSetIndices, primaryTrackGroupIndex);
|
||||
@@ -526,7 +540,11 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
}
|
||||
}
|
||||
|
||||
private ChunkSampleStream<SabrChunkSource> buildSampleStream(TrackGroupInfo trackGroupInfo, ExoTrackSelection selection, long positionUs) {
|
||||
private ChunkSampleStream<SabrChunkSource> buildSampleStream(
|
||||
TrackGroupInfo trackGroupInfo,
|
||||
ExoTrackSelection selection,
|
||||
long positionUs) {
|
||||
|
||||
int embeddedTrackCount = 0;
|
||||
boolean enableEventMessageTrack =
|
||||
trackGroupInfo.embeddedEventMessageTrackGroupIndex != C.INDEX_UNSET;
|
||||
@@ -536,34 +554,72 @@ final class SabrMediaPeriod implements MediaPeriod, SequenceableLoader.Callback<
|
||||
trackGroups.get(trackGroupInfo.embeddedEventMessageTrackGroupIndex);
|
||||
embeddedTrackCount++;
|
||||
}
|
||||
boolean enableCea608Tracks = trackGroupInfo.embeddedCea608TrackGroupIndex != C.INDEX_UNSET;
|
||||
boolean enableCea608Tracks =
|
||||
trackGroupInfo.embeddedCea608TrackGroupIndex != C.INDEX_UNSET;
|
||||
TrackGroup embeddedCea608TrackGroup = null;
|
||||
if (enableCea608Tracks) {
|
||||
embeddedCea608TrackGroup = trackGroups.get(trackGroupInfo.embeddedCea608TrackGroupIndex);
|
||||
embeddedCea608TrackGroup =
|
||||
trackGroups.get(trackGroupInfo.embeddedCea608TrackGroupIndex);
|
||||
embeddedTrackCount += embeddedCea608TrackGroup.length;
|
||||
}
|
||||
|
||||
Format[] embeddedTrackFormats = new Format[embeddedTrackCount];
|
||||
int[] embeddedTrackTypes = new int[embeddedTrackCount];
|
||||
embeddedTrackCount = 0;
|
||||
|
||||
if (enableEventMessageTrack) {
|
||||
embeddedTrackFormats[embeddedTrackCount] = embeddedEventMessageTrackGroup.getFormat(0);
|
||||
embeddedTrackFormats[embeddedTrackCount] =
|
||||
embeddedEventMessageTrackGroup.getFormat(0);
|
||||
embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_METADATA;
|
||||
embeddedTrackCount++;
|
||||
}
|
||||
|
||||
List<Format> embeddedCea608TrackFormats = new ArrayList<>();
|
||||
if (enableCea608Tracks) {
|
||||
for (int i = 0; i < embeddedCea608TrackGroup.length; i++) {
|
||||
embeddedTrackFormats[embeddedTrackCount] = embeddedCea608TrackGroup.getFormat(i);
|
||||
embeddedTrackFormats[embeddedTrackCount] =
|
||||
embeddedCea608TrackGroup.getFormat(i);
|
||||
embeddedTrackTypes[embeddedTrackCount] = C.TRACK_TYPE_TEXT;
|
||||
embeddedCea608TrackFormats.add(embeddedTrackFormats[embeddedTrackCount]);
|
||||
embeddedTrackCount++;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerTrackEmsgHandler trackPlayerEmsgHandler = manifest.dynamic && enableEventMessageTrack ? playerEmsgHandler.newPlayerTrackEmsgHandler() : null;
|
||||
SabrChunkSource chunkSource = chunkSourceFactory.createSabrChunkSource(manifestLoaderErrorThrower, manifest, periodIndex, trackGroupInfo.adaptationSetIndices, selection, trackGroupInfo.trackType, elapsedRealtimeOffsetMs, enableEventMessageTrack, embeddedCea608TrackFormats, trackPlayerEmsgHandler, transferListener);
|
||||
ChunkSampleStream<SabrChunkSource> stream = new ChunkSampleStream<>(trackGroupInfo.trackType, embeddedTrackTypes, embeddedTrackFormats, chunkSource, this, allocator, positionUs, loadErrorHandlingPolicy, eventDispatcher);
|
||||
PlayerTrackEmsgHandler trackPlayerEmsgHandler =
|
||||
manifest.dynamic && enableEventMessageTrack
|
||||
? playerEmsgHandler.newPlayerTrackEmsgHandler()
|
||||
: null;
|
||||
|
||||
SabrChunkSource chunkSource =
|
||||
chunkSourceFactory.createSabrChunkSource(
|
||||
manifestLoaderErrorThrower,
|
||||
manifest,
|
||||
periodIndex,
|
||||
trackGroupInfo.adaptationSetIndices,
|
||||
selection,
|
||||
trackGroupInfo.trackType,
|
||||
elapsedRealtimeOffsetMs,
|
||||
enableEventMessageTrack,
|
||||
embeddedCea608TrackFormats,
|
||||
trackPlayerEmsgHandler,
|
||||
transferListener);
|
||||
|
||||
ChunkSampleStream<SabrChunkSource> stream =
|
||||
new ChunkSampleStream<>(
|
||||
trackGroupInfo.trackType,
|
||||
embeddedTrackTypes,
|
||||
embeddedTrackFormats,
|
||||
chunkSource,
|
||||
/* callback= */ this,
|
||||
allocator,
|
||||
positionUs,
|
||||
drmSessionManager,
|
||||
drmEventDispatcher,
|
||||
loadErrorHandlingPolicy,
|
||||
eventDispatcher,
|
||||
/* canReportInitialDiscontinuity= */ true,
|
||||
/* downloadExecutor= */ null);
|
||||
|
||||
synchronized (this) {
|
||||
// The map is also accessed on the loading thread so synchronize access.
|
||||
trackEmsgHandlerBySampleStream.put(stream, trackPlayerEmsgHandler);
|
||||
|
||||
@@ -8,8 +8,10 @@ import android.util.SparseArray;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Timeline;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.drm.DrmSessionManagerProvider;
|
||||
import androidx.media3.exoplayer.source.BaseMediaSource;
|
||||
import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory;
|
||||
import androidx.media3.exoplayer.source.DefaultCompositeSequenceableLoaderFactory;
|
||||
@@ -34,17 +36,14 @@ import java.io.IOException;
|
||||
|
||||
@UnstableApi
|
||||
public final class SabrMediaSource extends BaseMediaSource {
|
||||
/**
|
||||
* The interval in milliseconds between invocations of {@link
|
||||
* SourceInfoRefreshListener#onSourceInfoRefreshed(MediaSource, Timeline, Object)} when the
|
||||
* source's {@link Timeline} is changing dynamically (for example, for incomplete live streams).
|
||||
*/
|
||||
|
||||
private static final int NOTIFY_MANIFEST_INTERVAL_MS = 5000;
|
||||
/**
|
||||
* The minimum default start position for live streams, relative to the start of the live window.
|
||||
*/
|
||||
private static final long MIN_LIVE_DEFAULT_START_POSITION_US = 5000000;
|
||||
private final SabrManifest manifest;
|
||||
private final MediaItem mediaItem;
|
||||
private final SabrChunkSource.Factory chunkSourceFactory;
|
||||
private final CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
|
||||
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
|
||||
@@ -68,6 +67,7 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
|
||||
private SabrMediaSource(
|
||||
SabrManifest manifest,
|
||||
MediaItem mediaItem,
|
||||
SabrChunkSource.Factory chunkSourceFactory,
|
||||
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
|
||||
LoadErrorHandlingPolicy loadErrorHandlingPolicy,
|
||||
@@ -76,6 +76,7 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
@Nullable Object tag
|
||||
) {
|
||||
this.manifest = manifest;
|
||||
this.mediaItem = mediaItem;
|
||||
this.chunkSourceFactory = chunkSourceFactory;
|
||||
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
|
||||
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
|
||||
@@ -87,6 +88,11 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
manifestLoadErrorThrower = new ManifestLoadErrorThrower();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaItem getMediaItem() {
|
||||
return mediaItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
|
||||
this.mediaTransferListener = mediaTransferListener;
|
||||
@@ -217,7 +223,7 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
windowDefaultStartPositionUs,
|
||||
manifest,
|
||||
tag);
|
||||
refreshSourceInfo(timeline, manifest);
|
||||
refreshSourceInfo(timeline);
|
||||
}
|
||||
|
||||
private long getNowUnixTimeUs() {
|
||||
@@ -231,8 +237,10 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
public static final class Factory implements MediaSource.Factory {
|
||||
private final SabrChunkSource.Factory chunkSourceFactory;
|
||||
@Nullable private final DataSource.Factory manifestDataSourceFactory;
|
||||
private final DefaultLoadErrorHandlingPolicy loadErrorHandlingPolicy;
|
||||
private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
|
||||
private final DefaultCompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
|
||||
@Nullable private DrmSessionManagerProvider drmSessionManagerProvider;
|
||||
|
||||
private long livePresentationDelayMs;
|
||||
private boolean livePresentationDelayOverridesManifest;
|
||||
private boolean isCreateCalled;
|
||||
@@ -258,8 +266,35 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaSource createMediaSource(Uri uri) {
|
||||
return null;
|
||||
public Factory setDrmSessionManagerProvider(DrmSessionManagerProvider drmSessionManagerProvider) {
|
||||
Assertions.checkState(!isCreateCalled);
|
||||
this.drmSessionManagerProvider = drmSessionManagerProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SabrMediaSource createMediaSource(MediaItem mediaItem) {
|
||||
Assertions.checkNotNull(mediaItem);
|
||||
MediaItem.LocalConfiguration localConfiguration = mediaItem.localConfiguration;
|
||||
Assertions.checkNotNull(localConfiguration, "MediaItem must have a local configuration");
|
||||
Object localTag = localConfiguration.tag;
|
||||
Assertions.checkArgument(
|
||||
localTag instanceof SabrManifest,
|
||||
"MediaItem.localConfiguration.tag must be a SabrManifest"
|
||||
);
|
||||
SabrManifest manifest = (SabrManifest) localTag;
|
||||
|
||||
isCreateCalled = true;
|
||||
return new SabrMediaSource(
|
||||
manifest,
|
||||
mediaItem,
|
||||
chunkSourceFactory,
|
||||
compositeSequenceableLoaderFactory,
|
||||
loadErrorHandlingPolicy,
|
||||
livePresentationDelayMs,
|
||||
livePresentationDelayOverridesManifest,
|
||||
tag
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -271,8 +306,15 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
*/
|
||||
public SabrMediaSource createMediaSource(SabrManifest manifest) {
|
||||
isCreateCalled = true;
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId("sabr:" + manifest.hashCode())
|
||||
.setTag(manifest)
|
||||
.build();
|
||||
|
||||
return new SabrMediaSource(
|
||||
manifest,
|
||||
mediaItem,
|
||||
chunkSourceFactory,
|
||||
compositeSequenceableLoaderFactory,
|
||||
loadErrorHandlingPolicy,
|
||||
@@ -301,7 +343,7 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
|
||||
@Override
|
||||
public int[] getSupportedTypes() {
|
||||
return new int[0];
|
||||
return new int[] { C.CONTENT_TYPE_OTHER };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,9 +356,10 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
* @return This factory, for convenience.
|
||||
* @throws IllegalStateException If one of the {@code create} methods has already been called.
|
||||
*/
|
||||
@Override
|
||||
public Factory setLoadErrorHandlingPolicy(LoadErrorHandlingPolicy loadErrorHandlingPolicy) {
|
||||
//Assertions.checkState(!isCreateCalled);
|
||||
//this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
|
||||
Assertions.checkState(!isCreateCalled);
|
||||
this.loadErrorHandlingPolicy = loadErrorHandlingPolicy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -338,15 +381,6 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
return setLoadErrorHandlingPolicy(new DefaultLoadErrorHandlingPolicy(minLoadableRetryCount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a tag for the media source which will be published in the {@link
|
||||
* androidx.media3.Timeline} of the source as {@link
|
||||
* androidx.media3.Timeline.Window#tag}.
|
||||
*
|
||||
* @param tag A tag for the media source.
|
||||
* @return This factory, for convenience.
|
||||
* @throws IllegalStateException If one of the {@code create} methods has already been called.
|
||||
*/
|
||||
public Factory setTag(Object tag) {
|
||||
Assertions.checkState(!isCreateCalled);
|
||||
this.tag = tag;
|
||||
@@ -466,26 +500,32 @@ public final class SabrMediaSource extends BaseMediaSource {
|
||||
|
||||
@Override
|
||||
public Window getWindow(
|
||||
int windowIndex, Window window, boolean setTag, long defaultPositionProjectionUs) {
|
||||
int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||
Assertions.checkIndex(windowIndex, 0, 1);
|
||||
long windowDefaultStartPositionUs = getAdjustedWindowDefaultStartPositionUs(
|
||||
defaultPositionProjectionUs);
|
||||
Object tag = setTag ? windowTag : null;
|
||||
|
||||
long windowDefaultStartPositionUs =
|
||||
getAdjustedWindowDefaultStartPositionUs(defaultPositionProjectionUs);
|
||||
|
||||
boolean isDynamic =
|
||||
manifest.dynamic
|
||||
&& manifest.minUpdatePeriodMs != C.TIME_UNSET
|
||||
&& manifest.durationMs == C.TIME_UNSET;
|
||||
|
||||
return window.set(
|
||||
tag,
|
||||
presentationStartTimeMs,
|
||||
windowStartTimeMs,
|
||||
/* uid= */ Window.SINGLE_WINDOW_UID,
|
||||
/* mediaItem= */ null,
|
||||
/* manifest= */ manifest,
|
||||
/* presentationStartTimeMs= */ presentationStartTimeMs,
|
||||
/* windowStartTimeMs= */ windowStartTimeMs,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
/* isSeekable= */ true,
|
||||
isDynamic,
|
||||
windowDefaultStartPositionUs,
|
||||
windowDurationUs,
|
||||
/* isDynamic= */ isDynamic,
|
||||
/* liveConfiguration= */ null,
|
||||
/* defaultPositionUs= */ windowDefaultStartPositionUs,
|
||||
/* durationUs= */ windowDurationUs,
|
||||
/* firstPeriodIndex= */ 0,
|
||||
/* lastPeriodIndex= */ getPeriodCount() - 1,
|
||||
offsetInFirstPeriodUs);
|
||||
/* positionInFirstPeriodUs= */ offsetInFirstPeriodUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
package com.futo.platformplayer.sabr.manifest;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.DrmInitData;
|
||||
import androidx.media3.common.DrmInitData.SchemeData;
|
||||
|
||||
import com.futo.platformplayer.sabr.ITagUtils;
|
||||
import com.futo.platformplayer.sabr.MediaFormat;
|
||||
import com.futo.platformplayer.sabr.MediaFormatComparator;
|
||||
import com.futo.platformplayer.sabr.MediaFormatUtils;
|
||||
import com.futo.platformplayer.sabr.MediaItemFormatInfo;
|
||||
import com.futo.platformplayer.sabr.MediaSubtitle;
|
||||
import com.futo.platformplayer.sabr.manifest.SegmentBase.SegmentList;
|
||||
import com.futo.platformplayer.sabr.manifest.SegmentBase.SegmentTemplate;
|
||||
import com.futo.platformplayer.sabr.manifest.SegmentBase.SegmentTimelineElement;
|
||||
import com.futo.platformplayer.sabr.manifest.SegmentBase.SingleSegmentBase;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import com.liskovsoft.mediaserviceinterfaces.data.MediaFormat;
|
||||
import com.liskovsoft.mediaserviceinterfaces.data.MediaItemFormatInfo;
|
||||
import com.liskovsoft.mediaserviceinterfaces.data.MediaSubtitle;
|
||||
import com.liskovsoft.sharedutils.helpers.Helpers;
|
||||
import com.liskovsoft.sharedutils.mylogger.Log;
|
||||
import com.liskovsoft.youtubeapi.formatbuilders.mpdbuilder.MediaFormatComparator;
|
||||
import com.liskovsoft.youtubeapi.formatbuilders.utils.ITagUtils;
|
||||
import com.liskovsoft.youtubeapi.formatbuilders.utils.MediaFormatUtils;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -31,6 +34,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@UnstableApi
|
||||
public class SabrManifestParser {
|
||||
private static final String TAG = SabrManifestParser.class.getSimpleName();
|
||||
private int mId;
|
||||
@@ -55,6 +59,50 @@ public class SabrManifestParser {
|
||||
return parseSabrManifest(formatInfo);
|
||||
}
|
||||
|
||||
public static boolean isInteger(String s) {
|
||||
return s != null && s.matches("^[-+]?\\d+$");
|
||||
}
|
||||
|
||||
public static boolean isNumeric(String s) {
|
||||
return s != null && s.matches("^[-+]?\\d*\\.?\\d+$");
|
||||
}
|
||||
|
||||
public static int parseInt(String numString) {
|
||||
return parseInt(numString, -1);
|
||||
}
|
||||
|
||||
public static int parseInt(String numString, int defaultValue) {
|
||||
if (!isInteger(numString)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Integer.parseInt(numString);
|
||||
}
|
||||
|
||||
public static long parseLong(String numString) {
|
||||
return parseLong(numString, -1);
|
||||
}
|
||||
|
||||
public static long parseLong(String numString, long defaultValue) {
|
||||
if (!isInteger(numString)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Long.parseLong(numString);
|
||||
}
|
||||
|
||||
public static float parseFloat(String numString) {
|
||||
return parseFloat(numString, -1);
|
||||
}
|
||||
|
||||
public static float parseFloat(String numString, float defaultValue) {
|
||||
if (!isNumeric(numString)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return Float.parseFloat(numString);
|
||||
}
|
||||
|
||||
private SabrManifest parseSabrManifest(MediaItemFormatInfo formatInfo) {
|
||||
long availabilityStartTime = C.TIME_UNSET;
|
||||
long durationMs = getDurationMs(formatInfo);
|
||||
@@ -86,7 +134,7 @@ public class SabrManifestParser {
|
||||
}
|
||||
|
||||
private static long getDurationMs(MediaItemFormatInfo formatInfo) {
|
||||
long lenSeconds = Helpers.parseLong(formatInfo.getLengthSeconds());
|
||||
long lenSeconds = parseLong(formatInfo.getLengthSeconds());
|
||||
return lenSeconds > 0 ? lenSeconds * 1_000 : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
@@ -284,7 +332,7 @@ public class SabrManifestParser {
|
||||
|
||||
// SegmentURL tag
|
||||
for (String segment : format.getGlobalSegmentList()) {
|
||||
long duration = Helpers.parseLong(segment, C.TIME_UNSET);
|
||||
long duration = parseLong(segment, C.TIME_UNSET);
|
||||
int count = 1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
timeline.add(new SegmentTimelineElement(elapsedTime, duration));
|
||||
@@ -347,14 +395,14 @@ public class SabrManifestParser {
|
||||
int roleFlags = C.ROLE_FLAG_MAIN;
|
||||
int selectionFlags = C.SELECTION_FLAG_DEFAULT;
|
||||
String id = mediaFormat.isDrc() ? mediaFormat.getITag() + "-drc" : mediaFormat.getITag();
|
||||
int bandwidth = Helpers.parseInt(mediaFormat.getBitrate(), Format.NO_VALUE);
|
||||
int bandwidth = parseInt(mediaFormat.getBitrate(), Format.NO_VALUE);
|
||||
String mimeType = MediaFormatUtils.extractMimeType(mediaFormat);
|
||||
String codecs = MediaFormatUtils.extractCodecs(mediaFormat);
|
||||
int width = mediaFormat.getWidth();
|
||||
int height = mediaFormat.getHeight();
|
||||
float frameRate = Helpers.parseFloat(mediaFormat.getFps(), Format.NO_VALUE);
|
||||
float frameRate = parseFloat(mediaFormat.getFps(), Format.NO_VALUE);
|
||||
int audioChannels = Format.NO_VALUE;
|
||||
int audioSamplingRate = Helpers.parseInt(ITagUtils.getAudioRateByTag(mediaFormat.getITag()), Format.NO_VALUE);
|
||||
int audioSamplingRate = parseInt(ITagUtils.getAudioRateByTag(mediaFormat.getITag()), Format.NO_VALUE);
|
||||
String language = mediaFormat.getLanguage();
|
||||
String baseUrl = mediaFormat.getUrl();
|
||||
String label = null;
|
||||
@@ -432,22 +480,42 @@ public class SabrManifestParser {
|
||||
|
||||
protected Representation buildRepresentation(
|
||||
RepresentationInfo representationInfo,
|
||||
String label,
|
||||
String extraDrmSchemeType,
|
||||
@Nullable String label,
|
||||
@Nullable String extraDrmSchemeType,
|
||||
ArrayList<SchemeData> extraDrmSchemeDatas) {
|
||||
Format format = representationInfo.format;
|
||||
|
||||
// Start from the existing format
|
||||
Format.Builder formatBuilder = representationInfo.format.buildUpon();
|
||||
|
||||
// copyWithLabel(label)
|
||||
if (label != null) {
|
||||
format = format.copyWithLabel(label);
|
||||
formatBuilder.setLabel(label);
|
||||
}
|
||||
String drmSchemeType = representationInfo.drmSchemeType != null
|
||||
? representationInfo.drmSchemeType : extraDrmSchemeType;
|
||||
|
||||
// Decide scheme type: representationInfo.drmSchemeType wins over extraDrmSchemeType
|
||||
String drmSchemeType =
|
||||
representationInfo.drmSchemeType != null
|
||||
? representationInfo.drmSchemeType
|
||||
: extraDrmSchemeType;
|
||||
|
||||
// Accumulate DRM scheme datas (same as your old code)
|
||||
ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas;
|
||||
drmSchemeDatas.addAll(extraDrmSchemeDatas);
|
||||
if (extraDrmSchemeDatas != null && !extraDrmSchemeDatas.isEmpty()) {
|
||||
drmSchemeDatas.addAll(extraDrmSchemeDatas);
|
||||
}
|
||||
|
||||
if (!drmSchemeDatas.isEmpty()) {
|
||||
filterRedundantIncompleteSchemeDatas(drmSchemeDatas);
|
||||
|
||||
DrmInitData drmInitData = new DrmInitData(drmSchemeType, drmSchemeDatas);
|
||||
format = format.copyWithDrmInitData(drmInitData);
|
||||
|
||||
// copyWithDrmInitData(drmInitData)
|
||||
formatBuilder.setDrmInitData(drmInitData);
|
||||
}
|
||||
|
||||
Format format = formatBuilder.build();
|
||||
|
||||
// Representation.newInstance(...) still exists with this signature in Media3.:contentReference[oaicite:1]{index=1}
|
||||
return Representation.newInstance(
|
||||
representationInfo.revisionId,
|
||||
format,
|
||||
@@ -455,6 +523,7 @@ public class SabrManifestParser {
|
||||
representationInfo.segmentBase);
|
||||
}
|
||||
|
||||
|
||||
protected Format buildFormat(
|
||||
String id,
|
||||
String containerMimeType,
|
||||
@@ -468,62 +537,44 @@ public class SabrManifestParser {
|
||||
@C.RoleFlags int roleFlags,
|
||||
@C.SelectionFlags int selectionFlags,
|
||||
String codecs) {
|
||||
|
||||
String sampleMimeType = getSampleMimeType(containerMimeType, codecs);
|
||||
|
||||
// Base builder: fields common to all track types
|
||||
Format.Builder builder = new Format.Builder()
|
||||
.setId(id)
|
||||
.setContainerMimeType(containerMimeType)
|
||||
.setSampleMimeType(sampleMimeType)
|
||||
.setCodecs(codecs)
|
||||
.setAverageBitrate(bitrate) // same semantics as old "bitrate" arg
|
||||
.setSelectionFlags(selectionFlags)
|
||||
.setRoleFlags(roleFlags)
|
||||
.setLanguage(language);
|
||||
|
||||
if (sampleMimeType != null) {
|
||||
if (MimeTypes.isVideo(sampleMimeType)) {
|
||||
return Format.createVideoContainerFormat(
|
||||
id,
|
||||
/* label= */ null,
|
||||
containerMimeType,
|
||||
sampleMimeType,
|
||||
codecs,
|
||||
/* metadata= */ null,
|
||||
bitrate,
|
||||
width,
|
||||
height,
|
||||
frameRate,
|
||||
/* initializationData= */ null,
|
||||
selectionFlags,
|
||||
roleFlags);
|
||||
// Replacement for createVideoContainerFormat(...)
|
||||
builder
|
||||
.setWidth(width)
|
||||
.setHeight(height)
|
||||
.setFrameRate(frameRate);
|
||||
|
||||
} else if (MimeTypes.isAudio(sampleMimeType)) {
|
||||
return Format.createAudioContainerFormat(
|
||||
id,
|
||||
/* label= */ null,
|
||||
containerMimeType,
|
||||
sampleMimeType,
|
||||
codecs,
|
||||
/* metadata= */ null,
|
||||
bitrate,
|
||||
audioChannels,
|
||||
audioSamplingRate,
|
||||
/* initializationData= */ null,
|
||||
selectionFlags,
|
||||
roleFlags,
|
||||
language);
|
||||
// Replacement for createAudioContainerFormat(...)
|
||||
builder
|
||||
.setChannelCount(audioChannels)
|
||||
.setSampleRate(audioSamplingRate);
|
||||
|
||||
} else if (mimeTypeIsRawText(sampleMimeType)) {
|
||||
return Format.createTextContainerFormat(
|
||||
id,
|
||||
/* label= */ null,
|
||||
containerMimeType,
|
||||
sampleMimeType,
|
||||
codecs,
|
||||
bitrate,
|
||||
selectionFlags,
|
||||
roleFlags,
|
||||
language,
|
||||
Format.NO_VALUE);
|
||||
// Replacement for createTextContainerFormat(...)
|
||||
// You passed Format.NO_VALUE for accessibilityChannel before,
|
||||
// which is already the default, but we can be explicit:
|
||||
builder.setAccessibilityChannel(Format.NO_VALUE);
|
||||
}
|
||||
}
|
||||
return Format.createContainerFormat(
|
||||
id,
|
||||
/* label= */ null,
|
||||
containerMimeType,
|
||||
sampleMimeType,
|
||||
codecs,
|
||||
bitrate,
|
||||
selectionFlags,
|
||||
roleFlags,
|
||||
language);
|
||||
|
||||
// Replacement for createContainerFormat(...) when no specialized type matched
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user