diff --git a/library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java b/library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java index c9d2caf953..9950aecd2a 100644 --- a/library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer/parser/mp4/FragmentedMp4Extractor.java @@ -462,7 +462,8 @@ public final class FragmentedMp4Extractor implements Extractor { /** * Parses a tkhd atom (defined in 14496-12). * - * @return A {@link Pair} consisting of the track id and duration. + * @return A {@link Pair} consisting of the track id and duration (in the timescale indicated in + * the movie header box). The duration is set to -1 if the duration is unspecified. */ private static Pair parseTkhd(ParsableByteArray tkhd) { tkhd.setPosition(ATOM_HEADER_SIZE); @@ -473,7 +474,23 @@ public final class FragmentedMp4Extractor implements Extractor { int trackId = tkhd.readInt(); tkhd.skip(4); - long duration = version == 0 ? tkhd.readUnsignedInt() : tkhd.readUnsignedLongToLong(); + + boolean durationUnknown = true; + int durationPosition = tkhd.getPosition(); + int durationByteCount = version == 0 ? 4 : 8; + for (int i = 0; i < durationByteCount; i++) { + if (tkhd.data[durationPosition + i] != -1) { + durationUnknown = false; + break; + } + } + long duration; + if (durationUnknown) { + tkhd.skip(durationByteCount); + duration = -1; + } else { + duration = version == 0 ? tkhd.readUnsignedInt() : tkhd.readUnsignedLongToLong(); + } return Pair.create(trackId, duration); } diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java b/library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java index f9d3bf8f1a..164820654c 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/HttpDataSource.java @@ -376,7 +376,7 @@ public class HttpDataSource implements DataSource { connection.setReadTimeout(readTimeoutMillis); connection.setDoOutput(false); synchronized (requestProperties) { - for (HashMap.Entry property : requestProperties.entrySet()) { + for (Map.Entry property : requestProperties.entrySet()) { connection.setRequestProperty(property.getKey(), property.getValue()); } } diff --git a/library/src/main/java/com/google/android/exoplayer/util/Util.java b/library/src/main/java/com/google/android/exoplayer/util/Util.java index 95f9576d41..4d51fda9b0 100644 --- a/library/src/main/java/com/google/android/exoplayer/util/Util.java +++ b/library/src/main/java/com/google/android/exoplayer/util/Util.java @@ -55,7 +55,7 @@ public final class Util { + "([Zz]|((\\+|\\-)(\\d\\d):(\\d\\d)))?"); private static final Pattern XS_DURATION_PATTERN = - Pattern.compile("^P(([0-9]*)Y)?(([0-9]*)M)?(([0-9]*)D)?" + Pattern.compile("^(-)?P(([0-9]*)Y)?(([0-9]*)M)?(([0-9]*)D)?" + "(T(([0-9]*)H)?(([0-9]*)M)?(([0-9.]*)S)?)?$"); private Util() {} @@ -275,21 +275,23 @@ public final class Util { public static long parseXsDuration(String value) { Matcher matcher = XS_DURATION_PATTERN.matcher(value); if (matcher.matches()) { + boolean negated = !TextUtils.isEmpty(matcher.group(1)); // Durations containing years and months aren't completely defined. We assume there are // 30.4368 days in a month, and 365.242 days in a year. - String years = matcher.group(2); + String years = matcher.group(3); double durationSeconds = (years != null) ? Double.parseDouble(years) * 31556908 : 0; - String months = matcher.group(4); + String months = matcher.group(5); durationSeconds += (months != null) ? Double.parseDouble(months) * 2629739 : 0; - String days = matcher.group(6); + String days = matcher.group(7); durationSeconds += (days != null) ? Double.parseDouble(days) * 86400 : 0; - String hours = matcher.group(9); + String hours = matcher.group(10); durationSeconds += (hours != null) ? Double.parseDouble(hours) * 3600 : 0; - String minutes = matcher.group(11); + String minutes = matcher.group(12); durationSeconds += (minutes != null) ? Double.parseDouble(minutes) * 60 : 0; - String seconds = matcher.group(13); + String seconds = matcher.group(14); durationSeconds += (seconds != null) ? Double.parseDouble(seconds) : 0; - return (long) (durationSeconds * 1000); + long durationMillis = (long) (durationSeconds * 1000); + return negated ? -durationMillis : durationMillis; } else { return (long) (Double.parseDouble(value) * 3600 * 1000); }