mirror of
https://github.com/samsonjs/media.git
synced 2026-03-31 10:25:48 +00:00
commit
fcc2138e9f
6 changed files with 185 additions and 15 deletions
|
|
@ -17,9 +17,11 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.0.0'
|
||||
classpath 'com.novoda:bintray-release:0.2.7'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'bintray-release'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
|
|
@ -47,3 +48,13 @@ android.libraryVariants.all { variant ->
|
|||
task.from variant.javaCompile.destinationDir
|
||||
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 @@
|
|||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
|
|
@ -43,9 +47,38 @@ public class ContentProtection {
|
|||
* @param data Protection scheme specific initialization data. May be null.
|
||||
*/
|
||||
public ContentProtection(String schemeUriId, UUID uuid, byte[] data) {
|
||||
this.schemeUriId = schemeUriId;
|
||||
this.schemeUriId = Assertions.checkNotNull(schemeUriId);
|
||||
this.uuid = uuid;
|
||||
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;
|
|||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
|
@ -178,24 +180,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
int contentType = parseAdaptationSetTypeFromMimeType(mimeType);
|
||||
|
||||
int id = -1;
|
||||
List<ContentProtection> contentProtections = null;
|
||||
ContentProtectionsBuilder contentProtectionsBuilder = new ContentProtectionsBuilder();
|
||||
List<Representation> representations = new ArrayList<Representation>();
|
||||
do {
|
||||
xpp.next();
|
||||
if (isStartTag(xpp, "BaseURL")) {
|
||||
baseUrl = parseBaseUrl(xpp, baseUrl);
|
||||
} else if (isStartTag(xpp, "ContentProtection")) {
|
||||
if (contentProtections == null) {
|
||||
contentProtections = new ArrayList<ContentProtection>();
|
||||
}
|
||||
contentProtections.add(parseContentProtection(xpp));
|
||||
contentProtectionsBuilder.addAdaptationSetProtection(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, baseUrl, periodStartMs,
|
||||
periodDurationMs, mimeType, language, segmentBase);
|
||||
periodDurationMs, mimeType, language, segmentBase, contentProtectionsBuilder);
|
||||
contentProtectionsBuilder.endRepresentation();
|
||||
contentType = checkAdaptationSetTypeConsistency(contentType,
|
||||
parseAdaptationSetTypeFromMimeType(representation.format.mimeType));
|
||||
representations.add(representation);
|
||||
|
|
@ -211,7 +211,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
}
|
||||
} while (!isEndTag(xpp, "AdaptationSet"));
|
||||
|
||||
return buildAdaptationSet(id, contentType, representations, contentProtections);
|
||||
return buildAdaptationSet(id, contentType, representations, contentProtectionsBuilder.build());
|
||||
}
|
||||
|
||||
protected AdaptationSet buildAdaptationSet(int id, int contentType,
|
||||
|
|
@ -289,7 +289,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
|
||||
protected Representation parseRepresentation(XmlPullParser xpp, String baseUrl,
|
||||
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");
|
||||
int bandwidth = parseInt(xpp, "bandwidth");
|
||||
int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
|
||||
|
|
@ -312,6 +313,8 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
} else if (isStartTag(xpp, "SegmentTemplate")) {
|
||||
segmentBase = parseSegmentTemplate(xpp, baseUrl, (SegmentTemplate) segmentBase,
|
||||
periodDurationMs);
|
||||
} else if (isStartTag(xpp, "ContentProtection")) {
|
||||
contentProtectionsBuilder.addRepresentationProtection(parseContentProtection(xpp));
|
||||
}
|
||||
} while (!isEndTag(xpp, "Representation"));
|
||||
|
||||
|
|
@ -577,4 +580,120 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
|
|||
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;
|
|||
/**
|
||||
* 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_NO = "NO";
|
||||
|
||||
private HlsParserUtil() {}
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ import java.util.regex.Pattern;
|
|||
return null;
|
||||
}
|
||||
|
||||
public static boolean parseOptionalBoolAttr(String line, Pattern pattern) {
|
||||
public static boolean parseOptionalBooleanAttr(String line, Pattern pattern) {
|
||||
Matcher matcher = pattern.matcher(line);
|
||||
if (matcher.find() && matcher.groupCount() == 1) {
|
||||
return BOOLEAN_YES.equals(matcher.group(1));
|
||||
|
|
@ -64,4 +65,8 @@ import java.util.regex.Pattern;
|
|||
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
|
|||
private static final Pattern NAME_ATTR_REGEX =
|
||||
Pattern.compile(NAME_ATTR + "=\"(.+?)\"");
|
||||
private static final Pattern AUTOSELECT_ATTR_REGEX =
|
||||
Pattern.compile(AUTOSELECT_ATTR + "=\"(.+?)\"");
|
||||
HlsParserUtil.compileBooleanAttrPattern(AUTOSELECT_ATTR);
|
||||
private static final Pattern DEFAULT_ATTR_REGEX =
|
||||
Pattern.compile(DEFAULT_ATTR + "=\"(.+?)\"");
|
||||
HlsParserUtil.compileBooleanAttrPattern(DEFAULT_ATTR);
|
||||
|
||||
@Override
|
||||
public HlsPlaylist parse(String connectionUrl, InputStream inputStream)
|
||||
|
|
@ -156,8 +156,8 @@ public final class HlsPlaylistParser implements NetworkLoadable.Parser<HlsPlayli
|
|||
String name = HlsParserUtil.parseStringAttr(line, NAME_ATTR_REGEX, NAME_ATTR);
|
||||
String uri = HlsParserUtil.parseStringAttr(line, URI_ATTR_REGEX, URI_ATTR);
|
||||
String language = HlsParserUtil.parseOptionalStringAttr(line, LANGUAGE_ATTR_REGEX);
|
||||
boolean isDefault = HlsParserUtil.parseOptionalBoolAttr(line, DEFAULT_ATTR_REGEX);
|
||||
boolean autoSelect = HlsParserUtil.parseOptionalBoolAttr(line, AUTOSELECT_ATTR_REGEX);
|
||||
boolean isDefault = HlsParserUtil.parseOptionalBooleanAttr(line, DEFAULT_ATTR_REGEX);
|
||||
boolean autoSelect = HlsParserUtil.parseOptionalBooleanAttr(line, AUTOSELECT_ATTR_REGEX);
|
||||
subtitles.add(new Subtitle(name, uri, language, isDefault, autoSelect));
|
||||
} else {
|
||||
// TODO: Support other types of media tag.
|
||||
|
|
|
|||
Loading…
Reference in a new issue