Commit 1a9bf018 by Oliver Woodman

Add frame rate to Format for use in format selection (when known).

parent a7e2b719
...@@ -107,7 +107,7 @@ public class SmoothFrameReleaseTimeHelper implements FrameReleaseTimeHelper, Fra ...@@ -107,7 +107,7 @@ public class SmoothFrameReleaseTimeHelper implements FrameReleaseTimeHelper, Fra
if (frameCount >= MIN_FRAMES_FOR_ADJUSTMENT) { if (frameCount >= MIN_FRAMES_FOR_ADJUSTMENT) {
// We're synced and have waited the required number of frames to apply an 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. // 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). // themselves (which often only have millisecond granularity).
long averageFrameTimeNs = (unadjustedFrameTimeNs - syncFrameTimeNs) / frameCount; long averageFrameTimeNs = (unadjustedFrameTimeNs - syncFrameTimeNs) / frameCount;
// Project the adjusted frame time forward using the average. // Project the adjusted frame time forward using the average.
......
...@@ -47,34 +47,39 @@ public class Format { ...@@ -47,34 +47,39 @@ public class Format {
public final String mimeType; 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; 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; 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; 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; 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. * The language of the format. Can be null if unknown.
...@@ -87,50 +92,58 @@ public class Format { ...@@ -87,50 +92,58 @@ public class Format {
/** /**
* @param id The format identifier. * @param id The format identifier.
* @param mimeType The format mime type. * @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video 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 for non-video formats. * @param height The height of the video in pixels, or -1 if unknown or not applicable.
* @param numChannels The number of audio channels, or -1 for non-audio formats. * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. * 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 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) { 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 id The format identifier.
* @param mimeType The format mime type. * @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video 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 for non-video formats. * @param height The height of the video in pixels, or -1 if unknown or not applicable.
* @param numChannels The number of audio channels, or -1 for non-audio formats. * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. * 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 bitrate The average bandwidth of the format in bits per second.
* @param language The language of the format. * @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) { 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 id The format identifier.
* @param mimeType The format mime type. * @param mimeType The format mime type.
* @param width The width of the video in pixels, or -1 for non-video 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 for non-video formats. * @param height The height of the video in pixels, or -1 if unknown or not applicable.
* @param numChannels The number of audio channels, or -1 for non-audio formats. * @param frameRate The frame rate of the video in frames per second, or -1 if unknown or not
* @param audioSamplingRate The audio sampling rate in Hz, or -1 for non-audio formats. * 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 bitrate The average bandwidth of the format in bits per second.
* @param language The language of the format. * @param language The language of the format.
* @param codecs The codecs used to decode 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) { int audioSamplingRate, int bitrate, String language, String codecs) {
this.id = Assertions.checkNotNull(id); this.id = Assertions.checkNotNull(id);
this.mimeType = mimeType; this.mimeType = mimeType;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.frameRate = frameRate;
this.numChannels = numChannels; this.numChannels = numChannels;
this.audioSamplingRate = audioSamplingRate; this.audioSamplingRate = audioSamplingRate;
this.bitrate = bitrate; this.bitrate = bitrate;
......
...@@ -41,6 +41,8 @@ import java.util.ArrayList; ...@@ -41,6 +41,8 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* A parser of media presentation description files. * A parser of media presentation description files.
...@@ -48,6 +50,8 @@ import java.util.List; ...@@ -48,6 +50,8 @@ import java.util.List;
public class MediaPresentationDescriptionParser extends DefaultHandler public class MediaPresentationDescriptionParser extends DefaultHandler
implements NetworkLoadable.Parser<MediaPresentationDescription> { implements NetworkLoadable.Parser<MediaPresentationDescription> {
private static final Pattern FRAME_RATE_PATTERN = Pattern.compile("(\\d+)(?:/(\\d+))??");
private final String contentId; private final String contentId;
private final XmlPullParserFactory xmlParserFactory; private final XmlPullParserFactory xmlParserFactory;
...@@ -296,6 +300,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -296,6 +300,22 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
int audioSamplingRate = parseInt(xpp, "audioSamplingRate"); int audioSamplingRate = parseInt(xpp, "audioSamplingRate");
int width = parseInt(xpp, "width"); int width = parseInt(xpp, "width");
int height = parseInt(xpp, "height"); 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); mimeType = parseString(xpp, "mimeType", mimeType);
String codecs = parseString(xpp, "codecs", null); String codecs = parseString(xpp, "codecs", null);
...@@ -318,16 +338,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler ...@@ -318,16 +338,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
} }
} while (!isEndTag(xpp, "Representation")); } while (!isEndTag(xpp, "Representation"));
Format format = buildFormat(id, mimeType, width, height, numChannels, audioSamplingRate, Format format = buildFormat(id, mimeType, width, height, frameRate, numChannels,
bandwidth, language, codecs); audioSamplingRate, bandwidth, language, codecs);
return buildRepresentation(periodStartMs, periodDurationMs, contentId, -1, format, return buildRepresentation(periodStartMs, periodDurationMs, contentId, -1, format,
segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl)); segmentBase != null ? segmentBase : new SingleSegmentBase(baseUrl));
} }
protected Format buildFormat(String id, String mimeType, int width, int height, int numChannels, protected Format buildFormat(String id, String mimeType, int width, int height, float frameRate,
int audioSamplingRate, int bandwidth, String language, String codecs) { int numChannels, int audioSamplingRate, int bandwidth, String language, String codecs) {
return new Format(id, mimeType, width, height, numChannels, audioSamplingRate, bandwidth, return new Format(id, mimeType, width, height, frameRate, numChannels, audioSamplingRate,
language, codecs); bandwidth, language, codecs);
} }
protected Representation buildRepresentation(long periodStartMs, long periodDurationMs, protected Representation buildRepresentation(long periodStartMs, long periodDurationMs,
......
...@@ -676,7 +676,7 @@ public class HlsChunkSource { ...@@ -676,7 +676,7 @@ public class HlsChunkSource {
public HlsFormat(String id, int width, int height, int bitrate, String codecs, public HlsFormat(String id, int width, int height, int bitrate, String codecs,
int variantIndex) { 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; this.variantIndex = variantIndex;
} }
......
...@@ -417,7 +417,7 @@ public class SmoothStreamingChunkSource implements ChunkSource { ...@@ -417,7 +417,7 @@ public class SmoothStreamingChunkSource implements ChunkSource {
public SmoothStreamingFormat(String id, String mimeType, int width, int height, public SmoothStreamingFormat(String id, String mimeType, int width, int height,
int numChannels, int audioSamplingRate, int bitrate, int trackIndex) { 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; this.trackIndex = trackIndex;
} }
......
...@@ -232,8 +232,8 @@ public class TtmlParser implements SubtitleParser { ...@@ -232,8 +232,8 @@ public class TtmlParser implements SubtitleParser {
* <a href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a> * <a href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
* *
* @param time A string that includes the time expression. * @param time A string that includes the time expression.
* @param frameRate The framerate of the stream. * @param frameRate The frame rate of the stream.
* @param subframeRate The sub-framerate of the stream * @param subframeRate The sub-frame rate of the stream
* @param tickRate The tick rate of the stream. * @param tickRate The tick rate of the stream.
* @return The parsed timestamp in microseconds. * @return The parsed timestamp in microseconds.
* @throws ParserException If the given string does not contain a valid time expression. * @throws ParserException If the given string does not contain a valid time expression.
......
...@@ -65,11 +65,12 @@ public class DashChunkSourceTest extends InstrumentationTestCase { ...@@ -65,11 +65,12 @@ public class DashChunkSourceTest extends InstrumentationTestCase {
private static final int TALL_HEIGHT = 200; private static final int TALL_HEIGHT = 200;
private static final int WIDE_WIDTH = 400; 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 REGULAR_VIDEO =
private static final Format TALL_VIDEO = new Format("2", "video/mp4", 100, TALL_HEIGHT, -1, -1, new Format("1", "video/mp4", 480, 240, -1, -1, -1, 1000);
1000); private static final Format TALL_VIDEO =
private static final Format WIDE_VIDEO = new Format("3", "video/mp4", WIDE_WIDTH, 50, -1, -1, new Format("2", "video/mp4", 100, TALL_HEIGHT, -1, -1, -1, 1000);
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 DataSource mockDataSource;
@Mock private ManifestFetcher<MediaPresentationDescription> mockManifestFetcher; @Mock private ManifestFetcher<MediaPresentationDescription> mockManifestFetcher;
......
...@@ -29,12 +29,12 @@ public class RepresentationTest extends TestCase { ...@@ -29,12 +29,12 @@ public class RepresentationTest extends TestCase {
public void testGetCacheKey() { public void testGetCacheKey() {
String uri = "http://www.google.com"; String uri = "http://www.google.com";
SegmentBase base = new SingleSegmentBase(new RangedUri(uri, null, 0, 1), 1, 0, uri, 1, 1); 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, Representation representation = Representation.newInstance(-1, -1, "test_stream_1", 3,
format, base); format, base);
assertEquals("test_stream_1.0.3", representation.getCacheKey()); 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); representation = Representation.newInstance(-1, -1, "test_stream_1", -1, format, base);
assertEquals("test_stream_1.150.-1", representation.getCacheKey()); assertEquals("test_stream_1.150.-1", representation.getCacheKey());
} }
......
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