Migrate SmoothStreaming to Format.Builder

Bitrates in SmoothStreaming manifests are average bitrates, as per
the ref'd issue.

Issue: #5978
PiperOrigin-RevId: 296467667
This commit is contained in:
olly 2020-02-21 18:51:16 +00:00 committed by Oliver Woodman
parent bc8fd2ca12
commit d6650e6514
2 changed files with 49 additions and 78 deletions

View File

@ -288,7 +288,8 @@ public final class CodecSpecificDataUtil {
* @return The individual NAL units, or null if the input did not consist of NAL start code
* delimited units.
*/
public static @Nullable byte[][] splitNalUnits(byte[] data) {
@Nullable
public static byte[][] splitNalUnits(byte[] data) {
if (!isNalStartCode(data, 0)) {
// data does not consist of NAL start code delimited units.
return null;

View File

@ -663,96 +663,65 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
@Override
public void parseStartTag(XmlPullParser parser) throws ParserException {
int type = (Integer) getNormalizedAttribute(KEY_TYPE);
String id = parser.getAttributeValue(null, KEY_INDEX);
String name = (String) getNormalizedAttribute(KEY_NAME);
int bitrate = parseRequiredInt(parser, KEY_BITRATE);
String sampleMimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
Format.Builder formatBuilder = new Format.Builder();
@Nullable String sampleMimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
int type = (Integer) getNormalizedAttribute(KEY_TYPE);
if (type == C.TRACK_TYPE_VIDEO) {
int width = parseRequiredInt(parser, KEY_MAX_WIDTH);
int height = parseRequiredInt(parser, KEY_MAX_HEIGHT);
List<byte[]> codecSpecificData = buildCodecSpecificData(
parser.getAttributeValue(null, KEY_CODEC_PRIVATE_DATA));
format =
Format.createVideoContainerFormat(
id,
name,
MimeTypes.VIDEO_MP4,
sampleMimeType,
/* codecs= */ null,
/* metadata= */ null,
bitrate,
width,
height,
/* frameRate= */ Format.NO_VALUE,
codecSpecificData,
/* selectionFlags= */ 0,
/* roleFlags= */ 0);
formatBuilder
.setContainerMimeType(MimeTypes.VIDEO_MP4)
.setWidth(parseRequiredInt(parser, KEY_MAX_WIDTH))
.setHeight(parseRequiredInt(parser, KEY_MAX_HEIGHT))
.setInitializationData(codecSpecificData);
} else if (type == C.TRACK_TYPE_AUDIO) {
sampleMimeType = sampleMimeType == null ? MimeTypes.AUDIO_AAC : sampleMimeType;
int channels = parseRequiredInt(parser, KEY_CHANNELS);
int samplingRate = parseRequiredInt(parser, KEY_SAMPLING_RATE);
if (sampleMimeType == null) {
// If we don't know the MIME type, assume AAC.
sampleMimeType = MimeTypes.AUDIO_AAC;
}
int channelCount = parseRequiredInt(parser, KEY_CHANNELS);
int sampleRate = parseRequiredInt(parser, KEY_SAMPLING_RATE);
List<byte[]> codecSpecificData = buildCodecSpecificData(
parser.getAttributeValue(null, KEY_CODEC_PRIVATE_DATA));
if (codecSpecificData.isEmpty() && MimeTypes.AUDIO_AAC.equals(sampleMimeType)) {
codecSpecificData = Collections.singletonList(
CodecSpecificDataUtil.buildAacLcAudioSpecificConfig(samplingRate, channels));
codecSpecificData =
Collections.singletonList(
CodecSpecificDataUtil.buildAacLcAudioSpecificConfig(sampleRate, channelCount));
}
String language = (String) getNormalizedAttribute(KEY_LANGUAGE);
format =
Format.createAudioContainerFormat(
id,
name,
MimeTypes.AUDIO_MP4,
sampleMimeType,
/* codecs= */ null,
/* metadata= */ null,
bitrate,
channels,
samplingRate,
codecSpecificData,
/* selectionFlags= */ 0,
/* roleFlags= */ 0,
language);
formatBuilder
.setContainerMimeType(MimeTypes.AUDIO_MP4)
.setChannelCount(channelCount)
.setSampleRate(sampleRate)
.setInitializationData(codecSpecificData);
} else if (type == C.TRACK_TYPE_TEXT) {
String subType = (String) getNormalizedAttribute(KEY_SUB_TYPE);
@C.RoleFlags int roleFlags = 0;
switch (subType) {
case "CAPT":
roleFlags = C.ROLE_FLAG_CAPTION;
break;
case "DESC":
roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND;
break;
default:
break;
@Nullable String subType = (String) getNormalizedAttribute(KEY_SUB_TYPE);
if (subType != null) {
switch (subType) {
case "CAPT":
roleFlags = C.ROLE_FLAG_CAPTION;
break;
case "DESC":
roleFlags = C.ROLE_FLAG_DESCRIBES_MUSIC_AND_SOUND;
break;
default:
break;
}
}
String language = (String) getNormalizedAttribute(KEY_LANGUAGE);
format =
Format.createTextContainerFormat(
id,
name,
MimeTypes.APPLICATION_MP4,
sampleMimeType,
/* codecs= */ null,
bitrate,
/* selectionFlags= */ 0,
roleFlags,
language);
formatBuilder.setContainerMimeType(MimeTypes.APPLICATION_MP4).setRoleFlags(roleFlags);
} else {
format =
Format.createContainerFormat(
id,
name,
MimeTypes.APPLICATION_MP4,
sampleMimeType,
/* codecs= */ null,
bitrate,
/* selectionFlags= */ 0,
/* roleFlags= */ 0,
/* language= */ null);
formatBuilder.setContainerMimeType(MimeTypes.APPLICATION_MP4);
}
format =
formatBuilder
.setId(parser.getAttributeValue(null, KEY_INDEX))
.setLabel((String) getNormalizedAttribute(KEY_NAME))
.setSampleMimeType(sampleMimeType)
.setAverageBitrate(parseRequiredInt(parser, KEY_BITRATE))
.setLanguage((String) getNormalizedAttribute(KEY_LANGUAGE))
.build();
}
@Override
@ -764,7 +733,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
ArrayList<byte[]> csd = new ArrayList<>();
if (!TextUtils.isEmpty(codecSpecificDataString)) {
byte[] codecPrivateData = Util.getBytesFromHexString(codecSpecificDataString);
byte[][] split = CodecSpecificDataUtil.splitNalUnits(codecPrivateData);
@Nullable byte[][] split = CodecSpecificDataUtil.splitNalUnits(codecPrivateData);
if (split == null) {
csd.add(codecPrivateData);
} else {
@ -774,6 +743,7 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
return csd;
}
@Nullable
private static String fourCCToMimeType(String fourCC) {
if (fourCC.equalsIgnoreCase("H264") || fourCC.equalsIgnoreCase("X264")
|| fourCC.equalsIgnoreCase("AVC1") || fourCC.equalsIgnoreCase("DAVC")) {