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 * @return The individual NAL units, or null if the input did not consist of NAL start code
* delimited units. * delimited units.
*/ */
public static @Nullable byte[][] splitNalUnits(byte[] data) { @Nullable
public static byte[][] splitNalUnits(byte[] data) {
if (!isNalStartCode(data, 0)) { if (!isNalStartCode(data, 0)) {
// data does not consist of NAL start code delimited units. // data does not consist of NAL start code delimited units.
return null; return null;

View File

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