More flexible mimeType handling in mpd parser.
- Allow the content type of an adaptation set to be inferred from the mimeTypes of the contained representations. - Ensure the contained mimeTypes are consistent with one another, and with the adaptation set. Ref: Issue #2
This commit is contained in:
parent
686ac2a6f5
commit
16fe6a809e
@ -18,9 +18,11 @@ package com.google.android.exoplayer.dash.mpd;
|
||||
import com.google.android.exoplayer.ParserException;
|
||||
import com.google.android.exoplayer.chunk.Format;
|
||||
import com.google.android.exoplayer.upstream.DataSpec;
|
||||
import com.google.android.exoplayer.util.Assertions;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@ -163,43 +165,32 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
||||
throws XmlPullParserException, IOException {
|
||||
Uri baseUrl = parentBaseUrl;
|
||||
int id = -1;
|
||||
int contentType = AdaptationSet.TYPE_UNKNOWN;
|
||||
|
||||
// TODO: Correctly handle other common attributes and elements. See 23009-1 Table 9.
|
||||
String mimeType = xpp.getAttributeValue(null, "mimeType");
|
||||
if (mimeType != null) {
|
||||
if (MimeTypes.isAudio(mimeType)) {
|
||||
contentType = AdaptationSet.TYPE_AUDIO;
|
||||
} else if (MimeTypes.isVideo(mimeType)) {
|
||||
contentType = AdaptationSet.TYPE_VIDEO;
|
||||
} else if (MimeTypes.isText(mimeType)
|
||||
|| mimeType.equalsIgnoreCase(MimeTypes.APPLICATION_TTML)) {
|
||||
contentType = AdaptationSet.TYPE_TEXT;
|
||||
}
|
||||
}
|
||||
int contentType = parseAdaptationSetTypeFromMimeType(mimeType);
|
||||
|
||||
List<ContentProtection> contentProtections = null;
|
||||
List<Representation> representations = new ArrayList<Representation>();
|
||||
do {
|
||||
xpp.next();
|
||||
if (contentType != AdaptationSet.TYPE_UNKNOWN) {
|
||||
if (isStartTag(xpp, "BaseURL")) {
|
||||
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||
} else if (isStartTag(xpp, "ContentProtection")) {
|
||||
if (contentProtections == null) {
|
||||
contentProtections = new ArrayList<ContentProtection>();
|
||||
}
|
||||
contentProtections.add(parseContentProtection(xpp));
|
||||
} else if (isStartTag(xpp, "ContentComponent")) {
|
||||
id = Integer.parseInt(xpp.getAttributeValue(null, "id"));
|
||||
String contentTypeString = xpp.getAttributeValue(null, "contentType");
|
||||
contentType = "video".equals(contentTypeString) ? AdaptationSet.TYPE_VIDEO
|
||||
: "audio".equals(contentTypeString) ? AdaptationSet.TYPE_AUDIO
|
||||
: AdaptationSet.TYPE_UNKNOWN;
|
||||
} else if (isStartTag(xpp, "Representation")) {
|
||||
representations.add(parseRepresentation(xpp, contentId, baseUrl, periodStart,
|
||||
periodDuration, mimeType, segmentTimelineList));
|
||||
if (isStartTag(xpp, "BaseURL")) {
|
||||
baseUrl = parseBaseUrl(xpp, parentBaseUrl);
|
||||
} else if (isStartTag(xpp, "ContentProtection")) {
|
||||
if (contentProtections == null) {
|
||||
contentProtections = new ArrayList<ContentProtection>();
|
||||
}
|
||||
contentProtections.add(parseContentProtection(xpp));
|
||||
} else if (isStartTag(xpp, "ContentComponent")) {
|
||||
id = Integer.parseInt(xpp.getAttributeValue(null, "id"));
|
||||
contentType = checkAdaptationSetTypeConsistency(contentType,
|
||||
parseAdaptationSetType(xpp.getAttributeValue(null, "contentType")));
|
||||
} else if (isStartTag(xpp, "Representation")) {
|
||||
Representation representation = parseRepresentation(xpp, contentId, baseUrl, periodStart,
|
||||
periodDuration, mimeType, segmentTimelineList);
|
||||
contentType = checkAdaptationSetTypeConsistency(contentType,
|
||||
parseAdaptationSetTypeFromMimeType(representation.format.mimeType));
|
||||
representations.add(representation);
|
||||
}
|
||||
} while (!isEndTag(xpp, "AdaptationSet"));
|
||||
|
||||
@ -360,4 +351,42 @@ public class MediaPresentationDescriptionParser extends DefaultHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static int parseAdaptationSetType(String contentType) {
|
||||
return TextUtils.isEmpty(contentType) ? AdaptationSet.TYPE_UNKNOWN
|
||||
: MimeTypes.BASE_TYPE_AUDIO.equals(contentType) ? AdaptationSet.TYPE_AUDIO
|
||||
: MimeTypes.BASE_TYPE_VIDEO.equals(contentType) ? AdaptationSet.TYPE_VIDEO
|
||||
: MimeTypes.BASE_TYPE_TEXT.equals(contentType) ? AdaptationSet.TYPE_TEXT
|
||||
: AdaptationSet.TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
private static int parseAdaptationSetTypeFromMimeType(String mimeType) {
|
||||
return TextUtils.isEmpty(mimeType) ? AdaptationSet.TYPE_UNKNOWN
|
||||
: MimeTypes.isAudio(mimeType) ? AdaptationSet.TYPE_AUDIO
|
||||
: MimeTypes.isVideo(mimeType) ? AdaptationSet.TYPE_VIDEO
|
||||
: MimeTypes.isText(mimeType) || MimeTypes.isTtml(mimeType) ? AdaptationSet.TYPE_TEXT
|
||||
: AdaptationSet.TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks two adaptation set types for consistency, returning the consistent type, or throwing an
|
||||
* {@link IllegalStateException} if the types are inconsistent.
|
||||
* <p>
|
||||
* Two types are consistent if they are equal, or if one is {@link AdaptationSet#TYPE_UNKNOWN}.
|
||||
* Where one of the types is {@link AdaptationSet#TYPE_UNKNOWN}, the other is returned.
|
||||
*
|
||||
* @param firstType The first type.
|
||||
* @param secondType The second type.
|
||||
* @return The consistent type.
|
||||
*/
|
||||
private static int checkAdaptationSetTypeConsistency(int firstType, int secondType) {
|
||||
if (firstType == AdaptationSet.TYPE_UNKNOWN) {
|
||||
return secondType;
|
||||
} else if (secondType == AdaptationSet.TYPE_UNKNOWN) {
|
||||
return firstType;
|
||||
} else {
|
||||
Assertions.checkState(firstType == secondType);
|
||||
return firstType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,17 +20,39 @@ package com.google.android.exoplayer.util;
|
||||
*/
|
||||
public class MimeTypes {
|
||||
|
||||
public static final String VIDEO_MP4 = "video/mp4";
|
||||
public static final String VIDEO_WEBM = "video/webm";
|
||||
public static final String VIDEO_H264 = "video/avc";
|
||||
public static final String VIDEO_VP9 = "video/x-vnd.on2.vp9";
|
||||
public static final String AUDIO_MP4 = "audio/mp4";
|
||||
public static final String AUDIO_AAC = "audio/mp4a-latm";
|
||||
public static final String TEXT_VTT = "text/vtt";
|
||||
public static final String APPLICATION_TTML = "application/ttml+xml";
|
||||
public static final String BASE_TYPE_VIDEO = "video";
|
||||
public static final String BASE_TYPE_AUDIO = "audio";
|
||||
public static final String BASE_TYPE_TEXT = "text";
|
||||
public static final String BASE_TYPE_APPLICATION = "application";
|
||||
|
||||
public static final String VIDEO_MP4 = BASE_TYPE_VIDEO + "/mp4";
|
||||
public static final String VIDEO_WEBM = BASE_TYPE_VIDEO + "/webm";
|
||||
public static final String VIDEO_H264 = BASE_TYPE_VIDEO + "/avc";
|
||||
public static final String VIDEO_VP9 = BASE_TYPE_VIDEO + "/x-vnd.on2.vp9";
|
||||
|
||||
public static final String AUDIO_MP4 = BASE_TYPE_AUDIO + "/mp4";
|
||||
public static final String AUDIO_AAC = BASE_TYPE_AUDIO + "/mp4a-latm";
|
||||
|
||||
public static final String TEXT_VTT = BASE_TYPE_TEXT + "/vtt";
|
||||
|
||||
public static final String APPLICATION_TTML = BASE_TYPE_APPLICATION + "/ttml+xml";
|
||||
|
||||
private MimeTypes() {}
|
||||
|
||||
/**
|
||||
* Returns the top-level type of {@code mimeType}.
|
||||
*
|
||||
* @param mimeType The mimeType whose top-level type is required.
|
||||
* @return The top-level type.
|
||||
*/
|
||||
public static String getTopLevelType(String mimeType) {
|
||||
int indexOfSlash = mimeType.indexOf('/');
|
||||
if (indexOfSlash == -1) {
|
||||
throw new IllegalArgumentException("Invalid mime type: " + mimeType);
|
||||
}
|
||||
return mimeType.substring(0, indexOfSlash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the top-level type of {@code mimeType} is audio.
|
||||
*
|
||||
@ -38,7 +60,7 @@ public class MimeTypes {
|
||||
* @return Whether the top level type is audio.
|
||||
*/
|
||||
public static boolean isAudio(String mimeType) {
|
||||
return mimeType.startsWith("audio/");
|
||||
return getTopLevelType(mimeType).equals(BASE_TYPE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +70,7 @@ public class MimeTypes {
|
||||
* @return Whether the top level type is video.
|
||||
*/
|
||||
public static boolean isVideo(String mimeType) {
|
||||
return mimeType.startsWith("video/");
|
||||
return getTopLevelType(mimeType).equals(BASE_TYPE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,7 +80,27 @@ public class MimeTypes {
|
||||
* @return Whether the top level type is text.
|
||||
*/
|
||||
public static boolean isText(String mimeType) {
|
||||
return mimeType.startsWith("text/");
|
||||
return getTopLevelType(mimeType).equals(BASE_TYPE_TEXT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the top-level type of {@code mimeType} is application.
|
||||
*
|
||||
* @param mimeType The mimeType to test.
|
||||
* @return Whether the top level type is application.
|
||||
*/
|
||||
public static boolean isApplication(String mimeType) {
|
||||
return getTopLevelType(mimeType).equals(BASE_TYPE_APPLICATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the mimeType is {@link #APPLICATION_TTML}.
|
||||
*
|
||||
* @param mimeType The mimeType to test.
|
||||
* @return Whether the mimeType is {@link #APPLICATION_TTML}.
|
||||
*/
|
||||
public static boolean isTtml(String mimeType) {
|
||||
return mimeType.equals(APPLICATION_TTML);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user