Skip to content

Commit

Permalink
More flexible mimeType handling in mpd parser.
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ojw28 committed Jul 10, 2014
1 parent 686ac2a commit 16fe6a8
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
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;
Expand Down Expand Up @@ -163,43 +165,32 @@ private AdaptationSet parseAdaptationSet(XmlPullParser xpp, String contentId, Ur
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"));

Expand Down Expand Up @@ -360,4 +351,42 @@ private static Uri parseBaseUrl(XmlPullParser xpp, Uri parentBaseUrl)
}
}

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;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,47 @@
*/
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.
*
* @param mimeType The mimeType to test.
* @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);
}

/**
Expand All @@ -48,7 +70,7 @@ public static boolean isAudio(String mimeType) {
* @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);
}

/**
Expand All @@ -58,7 +80,27 @@ public static boolean isVideo(String mimeType) {
* @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);
}

}

0 comments on commit 16fe6a8

Please sign in to comment.