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

This commit is contained in:
Oliver Woodman 2015-04-22 16:16:42 +01:00
parent a7e2b719c5
commit 1a9bf018a4
8 changed files with 77 additions and 43 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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<MediaPresentationDescription> {
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,

View file

@ -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;
}

View file

@ -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;
}

View file

@ -232,8 +232,8 @@ public class TtmlParser implements SubtitleParser {
* <a href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
*
* @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.

View file

@ -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<MediaPresentationDescription> mockManifestFetcher;

View file

@ -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());
}