Fix SmoothStreaming where audio FourCC is missing.
This commit is contained in:
parent
b2fc944af1
commit
8378019839
@ -16,7 +16,6 @@
|
|||||||
package com.google.android.exoplayer.smoothstreaming;
|
package com.google.android.exoplayer.smoothstreaming;
|
||||||
|
|
||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -103,12 +102,9 @@ public class SmoothStreamingManifest {
|
|||||||
public final int bitrate;
|
public final int bitrate;
|
||||||
|
|
||||||
// Audio-video
|
// Audio-video
|
||||||
public final String fourCC;
|
|
||||||
public final byte[][] csd;
|
public final byte[][] csd;
|
||||||
public final int profile;
|
public final int profile;
|
||||||
public final int level;
|
public final int level;
|
||||||
|
|
||||||
// Audio-video (derived)
|
|
||||||
public final String mimeType;
|
public final String mimeType;
|
||||||
|
|
||||||
// Video-only
|
// Video-only
|
||||||
@ -125,12 +121,12 @@ public class SmoothStreamingManifest {
|
|||||||
public final int nalUnitLengthField;
|
public final int nalUnitLengthField;
|
||||||
public final String content;
|
public final String content;
|
||||||
|
|
||||||
public TrackElement(int index, int bitrate, String fourCC, byte[][] csd, int profile, int level,
|
public TrackElement(int index, int bitrate, String mimeType, byte[][] csd, int profile,
|
||||||
int maxWidth, int maxHeight, int sampleRate, int channels, int packetSize, int audioTag,
|
int level, int maxWidth, int maxHeight, int sampleRate, int channels, int packetSize,
|
||||||
int bitPerSample, int nalUnitLengthField, String content) {
|
int audioTag, int bitPerSample, int nalUnitLengthField, String content) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.bitrate = bitrate;
|
this.bitrate = bitrate;
|
||||||
this.fourCC = fourCC;
|
this.mimeType = mimeType;
|
||||||
this.csd = csd;
|
this.csd = csd;
|
||||||
this.profile = profile;
|
this.profile = profile;
|
||||||
this.level = level;
|
this.level = level;
|
||||||
@ -143,19 +139,6 @@ public class SmoothStreamingManifest {
|
|||||||
this.bitPerSample = bitPerSample;
|
this.bitPerSample = bitPerSample;
|
||||||
this.nalUnitLengthField = nalUnitLengthField;
|
this.nalUnitLengthField = nalUnitLengthField;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
this.mimeType = fourCCToMimeType(fourCC);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String fourCCToMimeType(String fourCC) {
|
|
||||||
if (fourCC.equalsIgnoreCase("H264") || fourCC.equalsIgnoreCase("AVC1")
|
|
||||||
|| fourCC.equalsIgnoreCase("DAVC")) {
|
|
||||||
return MimeTypes.VIDEO_H264;
|
|
||||||
} else if (fourCC.equalsIgnoreCase("AACL") || fourCC.equalsIgnoreCase("AACH")) {
|
|
||||||
return MimeTypes.AUDIO_AAC;
|
|
||||||
} else if (fourCC.equalsIgnoreCase("TTML")) {
|
|
||||||
return MimeTypes.APPLICATION_TTML;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.Trac
|
|||||||
import com.google.android.exoplayer.util.Assertions;
|
import com.google.android.exoplayer.util.Assertions;
|
||||||
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
||||||
import com.google.android.exoplayer.util.ManifestParser;
|
import com.google.android.exoplayer.util.ManifestParser;
|
||||||
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
@ -586,7 +587,7 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
|
|||||||
|
|
||||||
private int index;
|
private int index;
|
||||||
private int bitrate;
|
private int bitrate;
|
||||||
private String fourCC;
|
private String mimeType;
|
||||||
private int profile;
|
private int profile;
|
||||||
private int level;
|
private int level;
|
||||||
private int maxWidth;
|
private int maxWidth;
|
||||||
@ -618,11 +619,14 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
|
|||||||
if (type == StreamElement.TYPE_VIDEO) {
|
if (type == StreamElement.TYPE_VIDEO) {
|
||||||
maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT);
|
maxHeight = parseRequiredInt(parser, KEY_MAX_HEIGHT);
|
||||||
maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH);
|
maxWidth = parseRequiredInt(parser, KEY_MAX_WIDTH);
|
||||||
fourCC = parseRequiredString(parser, KEY_FOUR_CC);
|
mimeType = fourCCToMimeType(parseRequiredString(parser, KEY_FOUR_CC));
|
||||||
} else {
|
} else {
|
||||||
maxHeight = -1;
|
maxHeight = -1;
|
||||||
maxWidth = -1;
|
maxWidth = -1;
|
||||||
fourCC = parser.getAttributeValue(null, KEY_FOUR_CC);
|
String fourCC = parser.getAttributeValue(null, KEY_FOUR_CC);
|
||||||
|
// If fourCC is missing and the stream type is audio, we assume AAC.
|
||||||
|
mimeType = fourCC != null ? fourCCToMimeType(fourCC)
|
||||||
|
: type == StreamElement.TYPE_AUDIO ? MimeTypes.AUDIO_AAC : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == StreamElement.TYPE_AUDIO) {
|
if (type == StreamElement.TYPE_AUDIO) {
|
||||||
@ -658,17 +662,6 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] hexStringToByteArray(String hexString) {
|
|
||||||
int length = hexString.length();
|
|
||||||
byte[] data = new byte[length / 2];
|
|
||||||
for (int i = 0; i < data.length; i++) {
|
|
||||||
int stringOffset = i * 2;
|
|
||||||
data[i] = (byte) ((Character.digit(hexString.charAt(stringOffset), 16) << 4)
|
|
||||||
+ Character.digit(hexString.charAt(stringOffset + 1), 16));
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseText(XmlPullParser parser) {
|
public void parseText(XmlPullParser parser) {
|
||||||
content = parser.getText();
|
content = parser.getText();
|
||||||
@ -681,8 +674,33 @@ public class SmoothStreamingManifestParser implements ManifestParser<SmoothStrea
|
|||||||
csdArray = new byte[csd.size()][];
|
csdArray = new byte[csd.size()][];
|
||||||
csd.toArray(csdArray);
|
csd.toArray(csdArray);
|
||||||
}
|
}
|
||||||
return new TrackElement(index, bitrate, fourCC, csdArray, profile, level, maxWidth, maxHeight,
|
return new TrackElement(index, bitrate, mimeType, csdArray, profile, level, maxWidth,
|
||||||
samplingRate, channels, packetSize, audioTag, bitPerSample, nalUnitLengthField, content);
|
maxHeight, samplingRate, channels, packetSize, audioTag, bitPerSample, nalUnitLengthField,
|
||||||
|
content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String fourCCToMimeType(String fourCC) {
|
||||||
|
if (fourCC.equalsIgnoreCase("H264") || fourCC.equalsIgnoreCase("X264")
|
||||||
|
|| fourCC.equalsIgnoreCase("AVC1") || fourCC.equalsIgnoreCase("DAVC")) {
|
||||||
|
return MimeTypes.VIDEO_H264;
|
||||||
|
} else if (fourCC.equalsIgnoreCase("AAC") || fourCC.equalsIgnoreCase("AACL")
|
||||||
|
|| fourCC.equalsIgnoreCase("AACH") || fourCC.equalsIgnoreCase("AACP")) {
|
||||||
|
return MimeTypes.AUDIO_AAC;
|
||||||
|
} else if (fourCC.equalsIgnoreCase("TTML")) {
|
||||||
|
return MimeTypes.APPLICATION_TTML;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] hexStringToByteArray(String hexString) {
|
||||||
|
int length = hexString.length();
|
||||||
|
byte[] data = new byte[length / 2];
|
||||||
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
int stringOffset = i * 2;
|
||||||
|
data[i] = (byte) ((Character.digit(hexString.charAt(stringOffset), 16) << 4)
|
||||||
|
+ Character.digit(hexString.charAt(stringOffset + 1), 16));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 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.exoplayer.smoothstreaming;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer.MediaFormat;
|
|
||||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.StreamElement;
|
|
||||||
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest.TrackElement;
|
|
||||||
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
|
||||||
|
|
||||||
import android.util.Base64;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SmoothStreamingUtil {
|
|
||||||
|
|
||||||
private SmoothStreamingUtil() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a {@link MediaFormat} for the specified track of the specified {@link StreamElement}.
|
|
||||||
*
|
|
||||||
* @param element The stream element.
|
|
||||||
* @param track The index of the track for which to build the format.
|
|
||||||
* @return The format.
|
|
||||||
*/
|
|
||||||
public static MediaFormat getMediaFormat(StreamElement element, int track) {
|
|
||||||
TrackElement trackElement = element.tracks[track];
|
|
||||||
String mimeType = trackElement.mimeType;
|
|
||||||
if (element.type == StreamElement.TYPE_VIDEO) {
|
|
||||||
MediaFormat format = MediaFormat.createVideoFormat(mimeType, -1, trackElement.maxWidth,
|
|
||||||
trackElement.maxHeight, Arrays.asList(trackElement.csd));
|
|
||||||
format.setMaxVideoDimensions(element.maxWidth, element.maxHeight);
|
|
||||||
return format;
|
|
||||||
} else if (element.type == StreamElement.TYPE_AUDIO) {
|
|
||||||
List<byte[]> csd;
|
|
||||||
if (trackElement.csd != null) {
|
|
||||||
csd = Arrays.asList(trackElement.csd);
|
|
||||||
} else {
|
|
||||||
csd = Collections.singletonList(CodecSpecificDataUtil.buildAudioSpecificConfig(
|
|
||||||
trackElement.sampleRate, trackElement.numChannels));
|
|
||||||
}
|
|
||||||
MediaFormat format = MediaFormat.createAudioFormat(mimeType, -1, trackElement.numChannels,
|
|
||||||
trackElement.sampleRate, csd);
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
// TODO: Do subtitles need a format? MediaFormat supports KEY_LANGUAGE.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] getKeyId(byte[] initData) {
|
|
||||||
StringBuilder initDataStringBuilder = new StringBuilder();
|
|
||||||
for (int i = 0; i < initData.length; i += 2) {
|
|
||||||
initDataStringBuilder.append((char) initData[i]);
|
|
||||||
}
|
|
||||||
String initDataString = initDataStringBuilder.toString();
|
|
||||||
String keyIdString = initDataString.substring(
|
|
||||||
initDataString.indexOf("<KID>") + 5, initDataString.indexOf("</KID>"));
|
|
||||||
byte[] keyId = Base64.decode(keyIdString, Base64.DEFAULT);
|
|
||||||
swap(keyId, 0, 3);
|
|
||||||
swap(keyId, 1, 2);
|
|
||||||
swap(keyId, 4, 5);
|
|
||||||
swap(keyId, 6, 7);
|
|
||||||
return keyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void swap(byte[] data, int firstPosition, int secondPosition) {
|
|
||||||
byte temp = data[firstPosition];
|
|
||||||
data[firstPosition] = data[secondPosition];
|
|
||||||
data[secondPosition] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user