diff --git a/library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java b/library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java index 7248f1cdeb..1245ea6454 100644 --- a/library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java +++ b/library/src/main/java/com/google/android/exoplayer/SmoothFrameReleaseTimeHelper.java @@ -107,7 +107,7 @@ public class SmoothFrameReleaseTimeHelper implements FrameReleaseTimeHelper, Fra if (frameCount >= MIN_FRAMES_FOR_ADJUSTMENT) { // We're synced and have waited the required number of frames to apply an adjustment. // Calculate the average frame time across all the frames we've seen since the last sync. - // This will typically give us a framerate at a finer granularity than the frame times + // This will typically give us a frame rate at a finer granularity than the frame times // themselves (which often only have millisecond granularity). long averageFrameTimeNs = (unadjustedFrameTimeNs - syncFrameTimeNs) / frameCount; // Project the adjusted frame time forward using the average. diff --git a/library/src/main/java/com/google/android/exoplayer/chunk/Format.java b/library/src/main/java/com/google/android/exoplayer/chunk/Format.java index b5f36fa96c..0c2f404426 100644 --- a/library/src/main/java/com/google/android/exoplayer/chunk/Format.java +++ b/library/src/main/java/com/google/android/exoplayer/chunk/Format.java @@ -47,34 +47,39 @@ public class Format { public final String mimeType; /** - * The codecs used to decode the format, or {@code null} if they are not specified. + * The average bandwidth in bits per second. */ - public final String codecs; + public final int bitrate; /** - * The width of the video in pixels, or -1 for non-video formats. + * The width of the video in pixels, or -1 if unknown or not applicable. */ public final int width; /** - * The height of the video in pixels, or -1 for non-video formats. + * The height of the video in pixels, or -1 if unknown or not applicable. */ public final int height; /** - * The number of audio channels, or -1 for non-audio formats. + * The video frame rate in frames per second, or -1 if unknown or not applicable. + */ + public final float frameRate; + + /** + * The number of audio channels, or -1 if unknown or not applicable. */ public final int numChannels; /** - * The audio sampling rate in Hz, or -1 for non-audio formats. + * The audio sampling rate in Hz, or -1 if unknown or not applicable. */ public final int audioSamplingRate; /** - * The average bandwidth in bits per second. + * The codecs used to decode the format. Can be {@code null} if unknown. */ - public final int bitrate; + public final String codecs; /** * The language of the format. Can be null if unknown. @@ -87,50 +92,58 @@ public class Format { /** * @param id The format identifier. * @param mimeType The format mime type. - * @param width The width of the video in pixels, or -1 for non-video formats. - * @param height The height of the video in pixels, or -1 for non-video formats. - * @param numChannels The number of audio channels, or -1 for non-audio formats. - * @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. + * @param width The width of the video in pixels, or -1 if unknown or not applicable. + * @param height The height of the video in pixels, or -1 if unknown or not applicable. + * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not + * applicable. + * @param numChannels The number of audio channels, or -1 if unknown or not applicable. + * @param audioSamplingRate The audio sampling rate in Hz, or -1 if unknown or not applicable. * @param bitrate The average bandwidth of the format in bits per second. */ - public Format(String id, String mimeType, int width, int height, int numChannels, + public Format(String id, String mimeType, int width, int height, float frameRate, int numChannels, int audioSamplingRate, int bitrate) { - this(id, mimeType, width, height, numChannels, audioSamplingRate, bitrate, null, null); + this(id, mimeType, width, height, frameRate, numChannels, audioSamplingRate, bitrate, null); } /** * @param id The format identifier. * @param mimeType The format mime type. - * @param width The width of the video in pixels, or -1 for non-video formats. - * @param height The height of the video in pixels, or -1 for non-video formats. - * @param numChannels The number of audio channels, or -1 for non-audio formats. - * @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. + * @param width The width of the video in pixels, or -1 if unknown or not applicable. + * @param height The height of the video in pixels, or -1 if unknown or not applicable. + * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not + * applicable. + * @param numChannels The number of audio channels, or -1 if unknown or not applicable. + * @param audioSamplingRate The audio sampling rate in Hz, or -1 if unknown or not applicable. * @param bitrate The average bandwidth of the format in bits per second. * @param language The language of the format. */ - public Format(String id, String mimeType, int width, int height, int numChannels, + public Format(String id, String mimeType, int width, int height, float frameRate, int numChannels, int audioSamplingRate, int bitrate, String language) { - this(id, mimeType, width, height, numChannels, audioSamplingRate, bitrate, language, null); + this(id, mimeType, width, height, frameRate, numChannels, audioSamplingRate, bitrate, language, + null); } /** * @param id The format identifier. * @param mimeType The format mime type. - * @param width The width of the video in pixels, or -1 for non-video formats. - * @param height The height of the video in pixels, or -1 for non-video formats. - * @param numChannels The number of audio channels, or -1 for non-audio formats. - * @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. + * @param width The width of the video in pixels, or -1 if unknown or not applicable. + * @param height The height of the video in pixels, or -1 if unknown or not applicable. + * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not + * applicable. + * @param numChannels The number of audio channels, or -1 if unknown or not applicable. + * @param audioSamplingRate The audio sampling rate in Hz, or -1 if unknown or not applicable. * @param bitrate The average bandwidth of the format in bits per second. * @param language The language of the format. * @param codecs The codecs used to decode the format. */ - public Format(String id, String mimeType, int width, int height, int numChannels, + public Format(String id, String mimeType, int width, int height, float frameRate, int numChannels, int audioSamplingRate, int bitrate, String language, String codecs) { this.id = Assertions.checkNotNull(id); this.mimeType = mimeType; this.width = width; this.height = height; + this.frameRate = frameRate; this.numChannels = numChannels; this.audioSamplingRate = audioSamplingRate; this.bitrate = bitrate; diff --git a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java index 7767fa8810..9d4b1afd6f 100644 --- a/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java +++ b/library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java @@ -41,6 +41,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * A parser of media presentation description files. @@ -48,6 +50,8 @@ import java.util.List; public class MediaPresentationDescriptionParser extends DefaultHandler implements NetworkLoadable.Parser { + private static final Pattern FRAME_RATE_PATTERN = Pattern.compile("(\\d+)(?:/(\\d+))??"); + private final String contentId; private final XmlPullParserFactory xmlParserFactory; @@ -296,6 +300,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler int audioSamplingRate = parseInt(xpp, "audioSamplingRate"); int width = parseInt(xpp, "width"); int height = parseInt(xpp, "height"); + + float frameRate = -1; + String frameRateAttribute = xpp.getAttributeValue(null, "frameRate"); + if (frameRateAttribute != null) { + Matcher frameRateMatcher = FRAME_RATE_PATTERN.matcher(frameRateAttribute); + if (frameRateMatcher.matches()) { + int numerator = Integer.parseInt(frameRateMatcher.group(1)); + String denominatorString = frameRateMatcher.group(2); + if (!TextUtils.isEmpty(denominatorString)) { + frameRate = (float) numerator / Integer.parseInt(denominatorString); + } else { + frameRate = numerator; + } + } + } + mimeType = parseString(xpp, "mimeType", mimeType); String codecs = parseString(xpp, "codecs", null); @@ -318,16 +338,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler } } while (!isEndTag(xpp, "Representation")); - Format format = buildFormat(id, mimeType, width, height, numChannels, audioSamplingRate, - bandwidth, language, codecs); + Format format = buildFormat(id, mimeType, width, height, frameRate, numChannels, + audioSamplingRate, bandwidth, language, codecs); return buildRepresentation(periodStartMs, periodDurationMs, contentId, -1, format, segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl)); } - protected Format buildFormat(String id, String mimeType, int width, int height, int numChannels, - int audioSamplingRate, int bandwidth, String language, String codecs) { - return new Format(id, mimeType, width, height, numChannels, audioSamplingRate, bandwidth, - language, codecs); + protected Format buildFormat(String id, String mimeType, int width, int height, float frameRate, + int numChannels, int audioSamplingRate, int bandwidth, String language, String codecs) { + return new Format(id, mimeType, width, height, frameRate, numChannels, audioSamplingRate, + bandwidth, language, codecs); } protected Representation buildRepresentation(long periodStartMs, long periodDurationMs, diff --git a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java index eed62a66c5..91fa3a947c 100644 --- a/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/hls/HlsChunkSource.java @@ -676,7 +676,7 @@ public class HlsChunkSource { public HlsFormat(String id, int width, int height, int bitrate, String codecs, int variantIndex) { - super(id, MimeTypes.APPLICATION_M3U8, width, height, -1, -1, bitrate, null, codecs); + super(id, MimeTypes.APPLICATION_M3U8, width, height, -1, -1, -1, bitrate, null, codecs); this.variantIndex = variantIndex; } diff --git a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java index 5949643c14..6503fc0dd1 100644 --- a/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer/smoothstreaming/SmoothStreamingChunkSource.java @@ -417,7 +417,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { public SmoothStreamingFormat(String id, String mimeType, int width, int height, int numChannels, int audioSamplingRate, int bitrate, int trackIndex) { - super(id, mimeType, width, height, numChannels, audioSamplingRate, bitrate); + super(id, mimeType, width, height, -1, numChannels, audioSamplingRate, bitrate); this.trackIndex = trackIndex; } diff --git a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java index 758a0e9b38..46fd26cd01 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java +++ b/library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java @@ -232,8 +232,8 @@ public class TtmlParser implements SubtitleParser { * timeExpression * * @param time A string that includes the time expression. - * @param frameRate The framerate of the stream. - * @param subframeRate The sub-framerate of the stream + * @param frameRate The frame rate of the stream. + * @param subframeRate The sub-frame rate of the stream * @param tickRate The tick rate of the stream. * @return The parsed timestamp in microseconds. * @throws ParserException If the given string does not contain a valid time expression. diff --git a/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java b/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java index f1b573c39b..fc343bbc41 100644 --- a/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java +++ b/library/src/test/java/com/google/android/exoplayer/dash/DashChunkSourceTest.java @@ -65,11 +65,12 @@ public class DashChunkSourceTest extends InstrumentationTestCase { private static final int TALL_HEIGHT = 200; private static final int WIDE_WIDTH = 400; - private static final Format REGULAR_VIDEO = new Format("1", "video/mp4", 480, 240, -1, -1, 1000); - private static final Format TALL_VIDEO = new Format("2", "video/mp4", 100, TALL_HEIGHT, -1, -1, - 1000); - private static final Format WIDE_VIDEO = new Format("3", "video/mp4", WIDE_WIDTH, 50, -1, -1, - 1000); + private static final Format REGULAR_VIDEO = + new Format("1", "video/mp4", 480, 240, -1, -1, -1, 1000); + private static final Format TALL_VIDEO = + new Format("2", "video/mp4", 100, TALL_HEIGHT, -1, -1, -1, 1000); + private static final Format WIDE_VIDEO = + new Format("3", "video/mp4", WIDE_WIDTH, 50, -1, -1, -1, 1000); @Mock private DataSource mockDataSource; @Mock private ManifestFetcher mockManifestFetcher; diff --git a/library/src/test/java/com/google/android/exoplayer/dash/mpd/RepresentationTest.java b/library/src/test/java/com/google/android/exoplayer/dash/mpd/RepresentationTest.java index 19d2226014..dc0ca5047b 100644 --- a/library/src/test/java/com/google/android/exoplayer/dash/mpd/RepresentationTest.java +++ b/library/src/test/java/com/google/android/exoplayer/dash/mpd/RepresentationTest.java @@ -29,12 +29,12 @@ public class RepresentationTest extends TestCase { public void testGetCacheKey() { String uri = "http://www.google.com"; SegmentBase base = new SingleSegmentBase(new RangedUri(uri, null, 0, 1), 1, 0, uri, 1, 1); - Format format = new Format("0", MimeTypes.VIDEO_MP4, 1920, 1080, 0, 0, 2500000); + Format format = new Format("0", MimeTypes.VIDEO_MP4, 1920, 1080, -1, 0, 0, 2500000); Representation representation = Representation.newInstance(-1, -1, "test_stream_1", 3, format, base); assertEquals("test_stream_1.0.3", representation.getCacheKey()); - format = new Format("150", MimeTypes.VIDEO_MP4, 1920, 1080, 0, 0, 2500000); + format = new Format("150", MimeTypes.VIDEO_MP4, 1920, 1080, -1, 0, 0, 2500000); representation = Representation.newInstance(-1, -1, "test_stream_1", -1, format, base); assertEquals("test_stream_1.150.-1", representation.getCacheKey()); }