use of com.google.android.exoplayer2.util.Assertions.checkNotNull in project ExoPlayer by google.
the class RepeatedTranscodeTransformationTest method repeatedTranscodeNoAudio_givesConsistentLengthOutput.
@Test
public void repeatedTranscodeNoAudio_givesConsistentLengthOutput() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
Matrix transformationMatrix = new Matrix();
transformationMatrix.postTranslate((float) 0.1, (float) 0.1);
Transformer transformer = new Transformer.Builder(context).setRemoveAudio(true).setTransformationRequest(new TransformationRequest.Builder().setVideoMimeType(MimeTypes.VIDEO_H265).setTransformationMatrix(transformationMatrix).build()).build();
Set<Long> differentOutputSizesBytes = new HashSet<>();
for (int i = 0; i < TRANSCODE_COUNT; i++) {
// Use a long video in case an error occurs a while after the start of the video.
AndroidTestUtil.TransformationResult result = runTransformer(context, /* testId= */
"repeatedTranscodeNoAudio_givesConsistentLengthOutput_" + i, transformer, AndroidTestUtil.REMOTE_MP4_10_SECONDS_URI_STRING, /* timeoutSeconds= */
120);
differentOutputSizesBytes.add(Assertions.checkNotNull(result.fileSizeBytes));
}
assertWithMessage("Different transcoding output sizes detected. Sizes: " + differentOutputSizesBytes).that(differentOutputSizesBytes.size()).isEqualTo(1);
}
use of com.google.android.exoplayer2.util.Assertions.checkNotNull in project ExoPlayer by google.
the class TtmlDecoder method parseFontSize.
private static void parseFontSize(String expression, TtmlStyle out) throws SubtitleDecoderException {
String[] expressions = Util.split(expression, "\\s+");
Matcher matcher;
if (expressions.length == 1) {
matcher = FONT_SIZE.matcher(expression);
} else if (expressions.length == 2) {
matcher = FONT_SIZE.matcher(expressions[1]);
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font" + " size and ignoring the first.");
} else {
throw new SubtitleDecoderException("Invalid number of entries for fontSize: " + expressions.length + ".");
}
if (matcher.matches()) {
String unit = Assertions.checkNotNull(matcher.group(3));
switch(unit) {
case "px":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
break;
case "em":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_EM);
break;
case "%":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break;
default:
throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
}
out.setFontSize(Float.parseFloat(Assertions.checkNotNull(matcher.group(1))));
} else {
throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
}
}
use of com.google.android.exoplayer2.util.Assertions.checkNotNull in project ExoPlayer by google.
the class TtmlDecoder method decode.
@Override
protected Subtitle decode(byte[] bytes, int length, boolean reset) throws SubtitleDecoderException {
try {
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
Map<String, TtmlStyle> globalStyles = new HashMap<>();
Map<String, TtmlRegion> regionMap = new HashMap<>();
Map<String, String> imageMap = new HashMap<>();
regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(TtmlNode.ANONYMOUS_REGION_ID));
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
xmlParser.setInput(inputStream, null);
@Nullable TtmlSubtitle ttmlSubtitle = null;
ArrayDeque<TtmlNode> nodeStack = new ArrayDeque<>();
int unsupportedNodeDepth = 0;
int eventType = xmlParser.getEventType();
FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE;
CellResolution cellResolution = DEFAULT_CELL_RESOLUTION;
@Nullable TtsExtent ttsExtent = null;
while (eventType != XmlPullParser.END_DOCUMENT) {
@Nullable TtmlNode parent = nodeStack.peek();
if (unsupportedNodeDepth == 0) {
String name = xmlParser.getName();
if (eventType == XmlPullParser.START_TAG) {
if (TtmlNode.TAG_TT.equals(name)) {
frameAndTickRate = parseFrameAndTickRates(xmlParser);
cellResolution = parseCellResolution(xmlParser, DEFAULT_CELL_RESOLUTION);
ttsExtent = parseTtsExtent(xmlParser);
}
if (!isSupportedTag(name)) {
Log.i(TAG, "Ignoring unsupported tag: " + xmlParser.getName());
unsupportedNodeDepth++;
} else if (TtmlNode.TAG_HEAD.equals(name)) {
parseHeader(xmlParser, globalStyles, cellResolution, ttsExtent, regionMap, imageMap);
} else {
try {
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
nodeStack.push(node);
if (parent != null) {
parent.addChild(node);
}
} catch (SubtitleDecoderException e) {
Log.w(TAG, "Suppressing parser error", e);
// Treat the node (and by extension, all of its children) as unsupported.
unsupportedNodeDepth++;
}
}
} else if (eventType == XmlPullParser.TEXT) {
Assertions.checkNotNull(parent).addChild(TtmlNode.buildTextNode(xmlParser.getText()));
} else if (eventType == XmlPullParser.END_TAG) {
if (xmlParser.getName().equals(TtmlNode.TAG_TT)) {
ttmlSubtitle = new TtmlSubtitle(Assertions.checkNotNull(nodeStack.peek()), globalStyles, regionMap, imageMap);
}
nodeStack.pop();
}
} else {
if (eventType == XmlPullParser.START_TAG) {
unsupportedNodeDepth++;
} else if (eventType == XmlPullParser.END_TAG) {
unsupportedNodeDepth--;
}
}
xmlParser.next();
eventType = xmlParser.getEventType();
}
if (ttmlSubtitle != null) {
return ttmlSubtitle;
} else {
throw new SubtitleDecoderException("No TTML subtitles found");
}
} catch (XmlPullParserException xppe) {
throw new SubtitleDecoderException("Unable to decode source", xppe);
} catch (IOException e) {
throw new IllegalStateException("Unexpected error when reading input.", e);
}
}
use of com.google.android.exoplayer2.util.Assertions.checkNotNull in project ExoPlayer by google.
the class TtmlDecoder method parseTimeExpression.
/**
* Parses a time expression, returning the parsed timestamp.
*
* <p>For the format of a time expression, see: <a
* href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
*
* @param time A string that includes the time expression.
* @param frameAndTickRate The effective frame and tick rates of the stream.
* @return The parsed timestamp in microseconds.
* @throws SubtitleDecoderException If the given string does not contain a valid time expression.
*/
private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate) throws SubtitleDecoderException {
Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) {
String hours = Assertions.checkNotNull(matcher.group(1));
double durationSeconds = Long.parseLong(hours) * 3600;
String minutes = Assertions.checkNotNull(matcher.group(2));
durationSeconds += Long.parseLong(minutes) * 60;
String seconds = Assertions.checkNotNull(matcher.group(3));
durationSeconds += Long.parseLong(seconds);
@Nullable String fraction = matcher.group(4);
durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0;
@Nullable String frames = matcher.group(5);
durationSeconds += (frames != null) ? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0;
@Nullable String subframes = matcher.group(6);
durationSeconds += (subframes != null) ? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate / frameAndTickRate.effectiveFrameRate : 0;
return (long) (durationSeconds * C.MICROS_PER_SECOND);
}
matcher = OFFSET_TIME.matcher(time);
if (matcher.matches()) {
String timeValue = Assertions.checkNotNull(matcher.group(1));
double offsetSeconds = Double.parseDouble(timeValue);
String unit = Assertions.checkNotNull(matcher.group(2));
switch(unit) {
case "h":
offsetSeconds *= 3600;
break;
case "m":
offsetSeconds *= 60;
break;
case "s":
// Do nothing.
break;
case "ms":
offsetSeconds /= 1000;
break;
case "f":
offsetSeconds /= frameAndTickRate.effectiveFrameRate;
break;
case "t":
offsetSeconds /= frameAndTickRate.tickRate;
break;
}
return (long) (offsetSeconds * C.MICROS_PER_SECOND);
}
throw new SubtitleDecoderException("Malformed time expression: " + time);
}
use of com.google.android.exoplayer2.util.Assertions.checkNotNull in project ExoPlayer by google.
the class HlsMediaChunk method createInstance.
/**
* Creates a new instance.
*
* @param extractorFactory A {@link HlsExtractorFactory} from which the {@link
* HlsMediaChunkExtractor} is obtained.
* @param dataSource The source from which the data should be loaded.
* @param format The chunk format.
* @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
* @param mediaPlaylist The media playlist from which this chunk was obtained.
* @param segmentBaseHolder The segment holder.
* @param playlistUrl The url of the playlist from which this chunk was obtained.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the multivariant playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
* @param timestampAdjusterProvider The provider from which to obtain the {@link
* TimestampAdjuster}.
* @param previousChunk The {@link HlsMediaChunk} that preceded this one. May be null.
* @param mediaSegmentKey The media segment decryption key, if fully encrypted. Null otherwise.
* @param initSegmentKey The initialization segment decryption key, if fully encrypted. Null
* otherwise.
* @param shouldSpliceIn Whether samples for this chunk should be spliced into existing samples.
*/
public static HlsMediaChunk createInstance(HlsExtractorFactory extractorFactory, DataSource dataSource, Format format, long startOfPlaylistInPeriodUs, HlsMediaPlaylist mediaPlaylist, HlsChunkSource.SegmentBaseHolder segmentBaseHolder, Uri playlistUrl, @Nullable List<Format> muxedCaptionFormats, @C.SelectionReason int trackSelectionReason, @Nullable Object trackSelectionData, boolean isMasterTimestampSource, TimestampAdjusterProvider timestampAdjusterProvider, @Nullable HlsMediaChunk previousChunk, @Nullable byte[] mediaSegmentKey, @Nullable byte[] initSegmentKey, boolean shouldSpliceIn, PlayerId playerId) {
// Media segment.
HlsMediaPlaylist.SegmentBase mediaSegment = segmentBaseHolder.segmentBase;
DataSpec dataSpec = new DataSpec.Builder().setUri(UriUtil.resolveToUri(mediaPlaylist.baseUri, mediaSegment.url)).setPosition(mediaSegment.byteRangeOffset).setLength(mediaSegment.byteRangeLength).setFlags(segmentBaseHolder.isPreload ? FLAG_MIGHT_NOT_USE_FULL_NETWORK_SPEED : 0).build();
boolean mediaSegmentEncrypted = mediaSegmentKey != null;
@Nullable byte[] mediaSegmentIv = mediaSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(mediaSegment.encryptionIV)) : null;
DataSource mediaDataSource = buildDataSource(dataSource, mediaSegmentKey, mediaSegmentIv);
// Init segment.
HlsMediaPlaylist.Segment initSegment = mediaSegment.initializationSegment;
DataSpec initDataSpec = null;
boolean initSegmentEncrypted = false;
@Nullable DataSource initDataSource = null;
if (initSegment != null) {
initSegmentEncrypted = initSegmentKey != null;
@Nullable byte[] initSegmentIv = initSegmentEncrypted ? getEncryptionIvArray(Assertions.checkNotNull(initSegment.encryptionIV)) : null;
Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url);
initDataSpec = new DataSpec(initSegmentUri, initSegment.byteRangeOffset, initSegment.byteRangeLength);
initDataSource = buildDataSource(dataSource, initSegmentKey, initSegmentIv);
}
long segmentStartTimeInPeriodUs = startOfPlaylistInPeriodUs + mediaSegment.relativeStartTimeUs;
long segmentEndTimeInPeriodUs = segmentStartTimeInPeriodUs + mediaSegment.durationUs;
int discontinuitySequenceNumber = mediaPlaylist.discontinuitySequence + mediaSegment.relativeDiscontinuitySequence;
@Nullable HlsMediaChunkExtractor previousExtractor = null;
Id3Decoder id3Decoder;
ParsableByteArray scratchId3Data;
if (previousChunk != null) {
boolean isSameInitData = initDataSpec == previousChunk.initDataSpec || (initDataSpec != null && previousChunk.initDataSpec != null && initDataSpec.uri.equals(previousChunk.initDataSpec.uri) && initDataSpec.position == previousChunk.initDataSpec.position);
boolean isFollowingChunk = playlistUrl.equals(previousChunk.playlistUrl) && previousChunk.loadCompleted;
id3Decoder = previousChunk.id3Decoder;
scratchId3Data = previousChunk.scratchId3Data;
previousExtractor = isSameInitData && isFollowingChunk && !previousChunk.extractorInvalidated && previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber ? previousChunk.extractor : null;
} else {
id3Decoder = new Id3Decoder();
scratchId3Data = new ParsableByteArray(Id3Decoder.ID3_HEADER_LENGTH);
}
return new HlsMediaChunk(extractorFactory, mediaDataSource, dataSpec, format, mediaSegmentEncrypted, initDataSource, initDataSpec, initSegmentEncrypted, playlistUrl, muxedCaptionFormats, trackSelectionReason, trackSelectionData, segmentStartTimeInPeriodUs, segmentEndTimeInPeriodUs, segmentBaseHolder.mediaSequence, segmentBaseHolder.partIndex, /* isPublished= */
!segmentBaseHolder.isPreload, discontinuitySequenceNumber, mediaSegment.hasGapTag, isMasterTimestampSource, /* timestampAdjuster= */
timestampAdjusterProvider.getAdjuster(discontinuitySequenceNumber), mediaSegment.drmInitData, previousExtractor, id3Decoder, scratchId3Data, shouldSpliceIn, playerId);
}
Aggregations