Commit fcc2138e by ojw28

Merge pull request #374 from google/dev

dev -> dev-webm-vp9-opus
parents d27b6de1 acd1b9ac
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
buildscript { buildscript {
repositories { repositories {
mavenCentral() mavenCentral()
jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.0.0' classpath 'com.android.tools.build:gradle:1.0.0'
classpath 'com.novoda:bintray-release:0.2.7'
} }
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'bintray-release'
android { android {
compileSdkVersion 21 compileSdkVersion 21
...@@ -47,3 +48,13 @@ android.libraryVariants.all { variant -> ...@@ -47,3 +48,13 @@ android.libraryVariants.all { variant ->
task.from variant.javaCompile.destinationDir task.from variant.javaCompile.destinationDir
artifacts.add('archives', task); artifacts.add('archives', task);
} }
publish {
repoName = 'exoplayer'
userOrg = 'google'
groupId = 'com.google.android.exoplayer'
artifactId = 'exoplayer'
version = 'r1.2.3'
description = 'The ExoPlayer library.'
website = 'https://github.com/google/ExoPlayer'
}
...@@ -15,6 +15,10 @@ ...@@ -15,6 +15,10 @@
*/ */
package com.google.android.exoplayer.dash.mpd; package com.google.android.exoplayer.dash.mpd;
import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.Util;
import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
/** /**
...@@ -43,9 +47,38 @@ public class ContentProtection { ...@@ -43,9 +47,38 @@ public class ContentProtection {
* @param data Protection scheme specific initialization data. May be null. * @param data Protection scheme specific initialization data. May be null.
*/ */
public ContentProtection(String schemeUriId, UUID uuid, byte[] data) { public ContentProtection(String schemeUriId, UUID uuid, byte[] data) {
this.schemeUriId = schemeUriId; this.schemeUriId = Assertions.checkNotNull(schemeUriId);
this.uuid = uuid; this.uuid = uuid;
this.data = data; this.data = data;
} }
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ContentProtection)) {
return false;
}
if (obj == this) {
return true;
}
ContentProtection other = (ContentProtection) obj;
return schemeUriId.equals(other.schemeUriId)
&& Util.areEqual(uuid, other.uuid)
&& Arrays.equals(data, other.data);
}
@Override
public int hashCode() {
int hashCode = 1;
hashCode = hashCode * 37 + schemeUriId.hashCode();
if (uuid != null) {
hashCode = hashCode * 37 + uuid.hashCode();
}
if (data != null) {
hashCode = hashCode * 37 + Arrays.hashCode(data);
}
return hashCode;
}
} }
...@@ -38,6 +38,8 @@ import java.io.IOException; ...@@ -38,6 +38,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
/** /**
...@@ -178,24 +180,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -178,24 +180,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
int contentType = parseAdaptationSetTypeFromMimeType(mimeType); int contentType = parseAdaptationSetTypeFromMimeType(mimeType);
int id = -1; int id = -1;
List<ContentProtection> contentProtections = null; ContentProtectionsBuilder contentProtectionsBuilder = new ContentProtectionsBuilder();
List<Representation> representations = new ArrayList<Representation>(); List<Representation> representations = new ArrayList<Representation>();
do { do {
xpp.next(); xpp.next();
if (isStartTag(xpp, "BaseURL")) { if (isStartTag(xpp, "BaseURL")) {
baseUrl = parseBaseUrl(xpp, baseUrl); baseUrl = parseBaseUrl(xpp, baseUrl);
} else if (isStartTag(xpp, "ContentProtection")) { } else if (isStartTag(xpp, "ContentProtection")) {
if (contentProtections == null) { contentProtectionsBuilder.addAdaptationSetProtection(parseContentProtection(xpp));
contentProtections = new ArrayList<ContentProtection>();
}
contentProtections.add(parseContentProtection(xpp));
} else if (isStartTag(xpp, "ContentComponent")) { } else if (isStartTag(xpp, "ContentComponent")) {
id = Integer.parseInt(xpp.getAttributeValue(null, "id")); id = Integer.parseInt(xpp.getAttributeValue(null, "id"));
contentType = checkAdaptationSetTypeConsistency(contentType, contentType = checkAdaptationSetTypeConsistency(contentType,
parseAdaptationSetType(xpp.getAttributeValue(null, "contentType"))); parseAdaptationSetType(xpp.getAttributeValue(null, "contentType")));
} else if (isStartTag(xpp, "Representation")) { } else if (isStartTag(xpp, "Representation")) {
Representation representation = parseRepresentation(xpp, baseUrl, periodStartMs, Representation representation = parseRepresentation(xpp, baseUrl, periodStartMs,
periodDurationMs, mimeType, language, segmentBase); periodDurationMs, mimeType, language, segmentBase, contentProtectionsBuilder);
contentProtectionsBuilder.endRepresentation();
contentType = checkAdaptationSetTypeConsistency(contentType, contentType = checkAdaptationSetTypeConsistency(contentType,
parseAdaptationSetTypeFromMimeType(representation.format.mimeType)); parseAdaptationSetTypeFromMimeType(representation.format.mimeType));
representations.add(representation); representations.add(representation);
...@@ -211,7 +211,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -211,7 +211,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
} while (!isEndTag(xpp, "AdaptationSet")); } while (!isEndTag(xpp, "AdaptationSet"));
return buildAdaptationSet(id, contentType, representations, contentProtections); return buildAdaptationSet(id, contentType, representations, contentProtectionsBuilder.build());
} }
protected AdaptationSet buildAdaptationSet(int id, int contentType, protected AdaptationSet buildAdaptationSet(int id, int contentType,
...@@ -289,7 +289,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -289,7 +289,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl, protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl,
long periodStartMs, long periodDurationMs, String mimeType, String language, long periodStartMs, long periodDurationMs, String mimeType, String language,
SegmentBase segmentBase) throws XmlPullParserException, IOException { SegmentBase segmentBase, ContentProtectionsBuilder contentProtectionsBuilder)
throws XmlPullParserException, IOException {
String id = xpp.getAttributeValue(null, "id"); String id = xpp.getAttributeValue(null, "id");
int bandwidth = parseInt(xpp, "bandwidth"); int bandwidth = parseInt(xpp, "bandwidth");
int audioSamplingRate = parseInt(xpp, "audioSamplingRate"); int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
...@@ -312,6 +313,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -312,6 +313,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} else if (isStartTag(xpp, "SegmentTemplate")) { } else if (isStartTag(xpp, "SegmentTemplate")) {
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase, segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase,
periodDurationMs); periodDurationMs);
} else if (isStartTag(xpp, "ContentProtection")) {
contentProtectionsBuilder.addRepresentationProtection(parseContentProtection(xpp));
} }
} while (!isEndTag(xpp, "Representation")); } while (!isEndTag(xpp, "Representation"));
...@@ -577,4 +580,120 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -577,4 +580,120 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
return value == null ? defaultValue : value; return value == null ? defaultValue : value;
} }
/**
* Builds a list of {@link ContentProtection} elements for an {@link AdaptationSet}.
* <p>
* If child Representation elements contain ContentProtection elements, then it is required that
* they all define the same ones. If they do, the ContentProtection elements are bubbled up to the
* AdaptationSet. Child Representation elements defining different ContentProtection elements is
* considered an error.
*/
protected static final class ContentProtectionsBuilder implements Comparator<ContentProtection> {
private ArrayList<ContentProtection> adaptationSetProtections;
private ArrayList<ContentProtection> representationProtections;
private ArrayList<ContentProtection> currentRepresentationProtections;
private boolean representationProtectionsSet;
/**
* Adds a {@link ContentProtection} found in the AdaptationSet element.
*
* @param contentProtection The {@link ContentProtection} to add.
*/
public void addAdaptationSetProtection(ContentProtection contentProtection) {
if (adaptationSetProtections == null) {
adaptationSetProtections = new ArrayList<ContentProtection>();
}
maybeAddContentProtection(adaptationSetProtections, contentProtection);
}
/**
* Adds a {@link ContentProtection} found in a child Representation element.
*
* @param contentProtection The {@link ContentProtection} to add.
*/
public void addRepresentationProtection(ContentProtection contentProtection) {
if (currentRepresentationProtections == null) {
currentRepresentationProtections = new ArrayList<ContentProtection>();
}
maybeAddContentProtection(currentRepresentationProtections, contentProtection);
}
/**
* Should be invoked after processing each child Representation element, in order to apply
* consistency checks.
*/
public void endRepresentation() {
if (!representationProtectionsSet) {
if (currentRepresentationProtections != null) {
Collections.sort(currentRepresentationProtections, this);
}
representationProtections = currentRepresentationProtections;
representationProtectionsSet = true;
} else {
// Assert that each Representation element defines the same ContentProtection elements.
if (currentRepresentationProtections == null) {
Assertions.checkState(representationProtections == null);
} else {
Collections.sort(currentRepresentationProtections, this);
Assertions.checkState(currentRepresentationProtections.equals(representationProtections));
}
}
currentRepresentationProtections = null;
}
/**
* Returns the final list of consistent {@link ContentProtection} elements.
*/
public ArrayList<ContentProtection> build() {
if (adaptationSetProtections == null) {
return representationProtections;
} else if (representationProtections == null) {
return adaptationSetProtections;
} else {
// Bubble up ContentProtection elements found in the child Representation elements.
for (int i = 0; i < representationProtections.size(); i++) {
maybeAddContentProtection(adaptationSetProtections, representationProtections.get(i));
}
return adaptationSetProtections;
}
}
/**
* Checks a ContentProtection for consistency with the given list, adding it if necessary.
* <ul>
* <li>If the new ContentProtection matches another in the list, it's consistent and is not
* added to the list.
* <li>If the new ContentProtection has the same schemeUriId as another ContentProtection in the
* list, but its other attributes do not match, then it's inconsistent and an
* {@link IllegalStateException} is thrown.
* <li>Else the new ContentProtection has a unique schemeUriId, it's consistent and is added.
* </ul>
*
* @param contentProtections The list of ContentProtection elements currently known.
* @param contentProtection The ContentProtection to add.
*/
private void maybeAddContentProtection(List<ContentProtection> contentProtections,
ContentProtection contentProtection) {
if (!contentProtections.contains(contentProtection)) {
for (int i = 0; i < contentProtections.size(); i++) {
// If contains returned false (no complete match), but find a matching schemeUriId, then
// the MPD contains inconsistent ContentProtection data.
Assertions.checkState(
!contentProtections.get(i).schemeUriId.equals(contentProtection.schemeUriId));
}
contentProtections.add(contentProtection);
}
}
// Comparator implementation.
@Override
public int compare(ContentProtection first, ContentProtection second) {
return first.schemeUriId.compareTo(second.schemeUriId);
}
}
} }
...@@ -23,9 +23,10 @@ import java.util.regex.Pattern; ...@@ -23,9 +23,10 @@ import java.util.regex.Pattern;
/** /**
* Utility methods for HLS manifest parsing. * Utility methods for HLS manifest parsing.
*/ */
/* package */ class HlsParserUtil { /* package */ final class HlsParserUtil {
private static final String BOOLEAN_YES = "YES"; private static final String BOOLEAN_YES = "YES";
private static final String BOOLEAN_NO = "NO";
private HlsParserUtil() {} private HlsParserUtil() {}
...@@ -56,7 +57,7 @@ import java.util.regex.Pattern; ...@@ -56,7 +57,7 @@ import java.util.regex.Pattern;
return null; return null;
} }
public static boolean parseOptionalBoolAttr(String line, Pattern pattern) { public static boolean parseOptionalBooleanAttr(String line, Pattern pattern) {
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (matcher.find() && matcher.groupCount() == 1) { if (matcher.find() && matcher.groupCount() == 1) {
return BOOLEAN_YES.equals(matcher.group(1)); return BOOLEAN_YES.equals(matcher.group(1));
...@@ -64,4 +65,8 @@ import java.util.regex.Pattern; ...@@ -64,4 +65,8 @@ import java.util.regex.Pattern;
return false; return false;
} }
public static Pattern compileBooleanAttrPattern(String attrName) {
return Pattern.compile(attrName + "=(" + BOOLEAN_YES + "|" + BOOLEAN_NO + ")");
}
} }
...@@ -98,9 +98,9 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -98,9 +98,9 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
private static final Pattern NAME_ATTR_REGEX = private static final Pattern NAME_ATTR_REGEX =
Pattern.compile(NAME_ATTR + "=\"(.+?)\""); Pattern.compile(NAME_ATTR + "=\"(.+?)\"");
private static final Pattern AUTOSELECT_ATTR_REGEX = private static final Pattern AUTOSELECT_ATTR_REGEX =
Pattern.compile(AUTOSELECT_ATTR + "=\"(.+?)\""); HlsParserUtil.compileBooleanAttrPattern(AUTOSELECT_ATTR);
private static final Pattern DEFAULT_ATTR_REGEX = private static final Pattern DEFAULT_ATTR_REGEX =
Pattern.compile(DEFAULT_ATTR + "=\"(.+?)\""); HlsParserUtil.compileBooleanAttrPattern(DEFAULT_ATTR);
@Override @Override
public HlsPlaylist parse(String connectionUrl, InputStream inputStream) public HlsPlaylist parse(String connectionUrl, InputStream inputStream)
...@@ -156,8 +156,8 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli ...@@ -156,8 +156,8 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
String name = HlsParserUtil.parseStringAttr(line, NAME_ATTR_REGEX, NAME_ATTR); String name = HlsParserUtil.parseStringAttr(line, NAME_ATTR_REGEX, NAME_ATTR);
String uri = HlsParserUtil.parseStringAttr(line, URI_ATTR_REGEX, URI_ATTR); String uri = HlsParserUtil.parseStringAttr(line, URI_ATTR_REGEX, URI_ATTR);
String language = HlsParserUtil.parseOptionalStringAttr(line, LANGUAGE_ATTR_REGEX); String language = HlsParserUtil.parseOptionalStringAttr(line, LANGUAGE_ATTR_REGEX);
boolean isDefault = HlsParserUtil.parseOptionalBoolAttr(line, DEFAULT_ATTR_REGEX); boolean isDefault = HlsParserUtil.parseOptionalBooleanAttr(line, DEFAULT_ATTR_REGEX);
boolean autoSelect = HlsParserUtil.parseOptionalBoolAttr(line, AUTOSELECT_ATTR_REGEX); boolean autoSelect = HlsParserUtil.parseOptionalBooleanAttr(line, AUTOSELECT_ATTR_REGEX);
subtitles.add(new Subtitle(name, uri, language, isDefault, autoSelect)); subtitles.add(new Subtitle(name, uri, language, isDefault, autoSelect));
} else { } else {
// TODO: Support other types of media tag. // TODO: Support other types of media tag.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment