private MediaSource buildMediaSource(Uri uri, String overrideExtension) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { case C.TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null); case C.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null); case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null); case C.TYPE_OTHER: return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, null); default: { throw new IllegalStateException("Unsupported type: " + type); } } }
@Override public long open(DataSpec dataSpec) throws IOException { Assertions.checkState(dataSource == null); String scheme = dataSpec.uri.getScheme(); if (Util.isLocalFileUri(dataSpec.uri)) { if (dataSpec.uri.getPath().startsWith("/android_asset/")) { dataSource = getAssetDataSource(); } else { dataSource = getFileDataSource(); } } else if (SCHEME_ASSET.equals(scheme)) { dataSource = getAssetDataSource(); } else if (SCHEME_CONTENT.equals(scheme)) { dataSource = getContentDataSource(); } else if (SCHEME_RTMP.equals(scheme)) { dataSource = getRtmpDataSource(); } else { dataSource = baseDataSource; } // Open the source and return. return dataSource.open(dataSpec); }
private MediaSource buildMediaSource(Uri uri, String overrideExtension) { DataSource.Factory mediaDataSourceFactory = buildDataSourceFactory(true); int type = TextUtils.isEmpty(overrideExtension) ? Util.inferContentType(uri) : Util.inferContentType("." + overrideExtension); switch (type) { case C.TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, null); case C.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, null); case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null); case C.TYPE_OTHER: return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, null); default: { throw new IllegalStateException("Unsupported type: " + type); } } }
@TargetApi(21) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(com.google.android.exoplayer2.ui.DefaultTimeBar.class.getCanonicalName()); info.setContentDescription(getProgressText()); if (duration <= 0) { return; } if (Util.SDK_INT >= 21) { info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); } else if (Util.SDK_INT >= 16) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); } }
private void drawPlayhead(Canvas canvas) { if (duration <= 0) { return; } int playheadX = Util.constrainValue(scrubberBar.right, scrubberBar.left, progressBar.right); int playheadY = scrubberBar.centerY(); if (scrubberDrawable == null) { int scrubberSize = (scrubbing || isFocused()) ? scrubberDraggedSize : (isEnabled() ? scrubberEnabledSize : scrubberDisabledSize); int playheadRadius = scrubberSize / 2; canvas.drawCircle(playheadX, playheadY, playheadRadius, scrubberPaint); } else { int scrubberDrawableWidth = scrubberDrawable.getIntrinsicWidth(); int scrubberDrawableHeight = scrubberDrawable.getIntrinsicHeight(); scrubberDrawable.setBounds( playheadX - scrubberDrawableWidth / 2, playheadY - scrubberDrawableHeight / 2, playheadX + scrubberDrawableWidth / 2, playheadY + scrubberDrawableHeight / 2); scrubberDrawable.draw(canvas); } }
/** * Incrementally scrubs the position by {@code positionChange}. * * @param positionChange The change in the scrubber position, in milliseconds. May be negative. * @return Returns whether the scrubber position changed. */ private boolean scrubIncrementally(long positionChange) { if (duration <= 0) { return false; } long scrubberPosition = getScrubberPosition(); scrubPosition = Util.constrainValue(scrubberPosition + positionChange, 0, duration); if (scrubPosition == scrubberPosition) { return false; } if (!scrubbing) { startScrubbing(); } for (OnScrubListener listener : listeners) { listener.onScrubMove(this, scrubPosition); } update(); return true; }
@Override protected List<SampleGroup> doInBackground(String... uris) { List<SampleGroup> result = new ArrayList<>(); Context context = getApplicationContext(); String userAgent = Util.getUserAgent(context, "ExoPlayerDemo"); DataSource dataSource = new DefaultDataSource(context, null, userAgent, false); for (String uri : uris) { DataSpec dataSpec = new DataSpec(Uri.parse(uri)); InputStream inputStream = new DataSourceInputStream(dataSource, dataSpec); try { readSampleGroups(new JsonReader(new InputStreamReader(inputStream, "UTF-8")), result); } catch (Exception e) { Log.e(TAG, "Error loading sample list: " + uri, e); sawError = true; } finally { Util.closeQuietly(dataSource); } } return result; }
private MediaSource buildMediaSource(Uri uri, String overrideExtension) { int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension : uri.getLastPathSegment()); switch (type) { case C.TYPE_SS: return new SsMediaSource(uri, buildDataSourceFactory(false), new DefaultSsChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case C.TYPE_DASH: return new DashMediaSource(uri, buildDataSourceFactory(false), new DefaultDashChunkSource.Factory(mediaDataSourceFactory), mainHandler, eventLogger); case C.TYPE_HLS: return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, eventLogger); case C.TYPE_OTHER: return new ExtractorMediaSource(uri, mediaDataSourceFactory, new DefaultExtractorsFactory(), mainHandler, eventLogger); default: { throw new IllegalStateException("Unsupported type: " + type); } } }
private SimpleExoPlayer setupPlayer(){ Log.d(TAG,"Setting up Player."); videoView.setKeepScreenOn(true); mUserAgent = Util.getUserAgent(getContext(), getResources().getString(R.string.app_name)); AdaptiveTrackSelection.Factory trackSelectorFactory = new AdaptiveTrackSelection.Factory(BANDWIDTH_METER); DefaultTrackSelector trackSelector = new DefaultTrackSelector(trackSelectorFactory); LoadControl loadControl = new DefaultLoadControl(); DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(getContext(), null, DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); exoPlayer = ExoPlayerFactory.newSimpleInstance(renderersFactory, trackSelector, loadControl); MyListener listener = new MyListener(exoPlayer, this); exoPlayer.addVideoListener(listener); exoPlayer.addListener(listener); exoPlayer.setPlayWhenReady(mPlaybackState); exoPlayer.prepare(buildMediaSource(Uri.parse(mRecording.getRecordingUrl()),"")); return exoPlayer; }
@Override public final void load() throws IOException, InterruptedException { try { dataSource.open(dataSpec); limit = 0; int bytesRead = 0; while (bytesRead != C.RESULT_END_OF_INPUT && !loadCanceled) { maybeExpandData(); bytesRead = dataSource.read(data, limit, READ_GRANULARITY); if (bytesRead != -1) { limit += bytesRead; } } if (!loadCanceled) { consume(data, limit); } } finally { Util.closeQuietly(dataSource); } }
/** * Given viewport dimensions and video dimensions, computes the maximum size of the video as it * will be rendered to fit inside of the viewport. */ private static Point getMaxVideoSizeInViewport(boolean orientationMayChange, int viewportWidth, int viewportHeight, int videoWidth, int videoHeight) { if (orientationMayChange && (videoWidth > videoHeight) != (viewportWidth > viewportHeight)) { // Rotation is allowed, and the video will be larger in the rotated viewport. int tempViewportWidth = viewportWidth; viewportWidth = viewportHeight; viewportHeight = tempViewportWidth; } if (videoWidth * viewportHeight >= videoHeight * viewportWidth) { // Horizontal letter-boxing along top and bottom. return new Point(viewportWidth, Util.ceilDivide(viewportWidth * videoHeight, videoWidth)); } else { // Vertical letter-boxing along edges. return new Point(Util.ceilDivide(viewportHeight * videoWidth, videoHeight), viewportHeight); } }
private void initializePlayer(Uri videoUri){ if (mExoPlayer == null){ TrackSelector trackSelector = new DefaultTrackSelector(); LoadControl loadControl = new DefaultLoadControl(); mExoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, loadControl); mExoPlayer.addListener(this); mExoPlayer.seekTo(currentPosition); mPlayerView.setPlayer(mExoPlayer); String userAgent = Util.getUserAgent(getContext(), "Tasty"); MediaSource mediaSource = new ExtractorMediaSource(videoUri, new DefaultDataSourceFactory( getContext(), userAgent), new DefaultExtractorsFactory(), null, null); mExoPlayer.prepare(mediaSource); if (playWhenReady) mExoPlayer.setPlayWhenReady(true); else mExoPlayer.setPlayWhenReady(false); } }
@Override public long getTimeUs(long position) { if (!isSeekable() || position < firstFramePosition) { return 0L; } double offsetByte = 256.0 * (position - firstFramePosition) / sizeBytes; int previousTocPosition = Util.binarySearchFloor(tableOfContents, (long) offsetByte, true, false) + 1; long previousTime = getTimeUsForTocPosition(previousTocPosition); // Linearly interpolate the time taking into account the next entry. long previousByte = previousTocPosition == 0 ? 0 : tableOfContents[previousTocPosition - 1]; long nextByte = previousTocPosition == 99 ? 256 : tableOfContents[previousTocPosition]; long nextTime = getTimeUsForTocPosition(previousTocPosition + 1); long timeOffset = nextByte == previousByte ? 0 : (long) ((nextTime - previousTime) * (offsetByte - previousByte) / (nextByte - previousByte)); return previousTime + timeOffset; }
public void testWidevineOfflineLicense() throws Exception { if (Util.SDK_INT < 22) { // Pass. return; } String streamName = "test_widevine_h264_fixed_offline"; DashHostedTestEncParameters parameters = newDashHostedTestEncParameters( WIDEVINE_H264_MANIFEST_PREFIX, true, MimeTypes.VIDEO_H264); TestOfflineLicenseHelper helper = new TestOfflineLicenseHelper(parameters); try { byte[] keySetId = helper.downloadLicense(); testDashPlayback(getActivity(), streamName, null, true, parameters, WIDEVINE_AAC_AUDIO_REPRESENTATION_ID, false, keySetId, WIDEVINE_H264_CDD_FIXED); helper.renewLicense(); } finally { helper.releaseResources(); } }
public void testWidevineOfflineLicenseExpiresOnPause() throws Exception { if (Util.SDK_INT < 22) { // Pass. return; } String streamName = "test_widevine_h264_fixed_offline"; DashHostedTestEncParameters parameters = newDashHostedTestEncParameters( WIDEVINE_H264_MANIFEST_PREFIX, true, MimeTypes.VIDEO_H264); TestOfflineLicenseHelper helper = new TestOfflineLicenseHelper(parameters); try { byte[] keySetId = helper.downloadLicense(); // During playback pause until the license expires then continue playback Pair<Long, Long> licenseDurationRemainingSec = helper.getLicenseDurationRemainingSec(); long licenseDuration = licenseDurationRemainingSec.first; assertTrue("License duration should be less than 30 sec. " + "Server settings might have changed.", licenseDuration < 30); ActionSchedule schedule = new ActionSchedule.Builder(TAG) .delay(3000).pause().delay(licenseDuration * 1000 + 2000).play().build(); // DefaultDrmSessionManager should renew the license and stream play fine testDashPlayback(getActivity(), streamName, schedule, true, parameters, WIDEVINE_AAC_AUDIO_REPRESENTATION_ID, false, keySetId, WIDEVINE_H264_CDD_FIXED); } finally { helper.releaseResources(); } }
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_playback); ButterKnife.bind(this); mSurfaceView.setKeepScreenOn(true); mPlaybackControllFragment = (OverlayFragment) getFragmentManager().findFragmentById(R.id.playback_controls_fragment); mUserAgent = Util.getUserAgent(this, getResources().getString(R.string.app_name)); synchronized (this) { if (player == null) { setupPlayer(); } } }
/** * Infer content type int. * * @param fileName the file name * @return the int */ @C.ContentType public static int inferContentType(String fileName) { fileName = Util.toLowerInvariant(fileName); if (fileName.matches(".*m3u8.*")) { return C.TYPE_HLS; } else if (fileName.matches(".*mpd.*")) { return C.TYPE_DASH; } else if (fileName.matches(".*\\.ism(l)?(/manifest(\\(.+\\))?)?")) { return C.TYPE_SS; } else { return C.TYPE_OTHER; } }
private void initExoPlayer() { DefaultBandwidthMeter bwMeter = new DefaultBandwidthMeter(); AdaptiveTrackSelection.Factory trackFactory = new AdaptiveTrackSelection.Factory(bwMeter); DefaultTrackSelector trackSelector = new DefaultTrackSelector(trackFactory); player = ExoPlayerFactory.newSimpleInstance(this, trackSelector); videoView.setPlayer(player); DataSource.Factory dsFactory = new DefaultDataSourceFactory(getBaseContext(), Util.getUserAgent(this, "Traxy"), bwMeter); ExtractorsFactory exFactory = new DefaultExtractorsFactory(); Uri mediaUri = Uri.parse(entry.getUrl()); MediaSource videoSource = new ExtractorMediaSource(mediaUri, dsFactory, exFactory, null, null); player.prepare(videoSource); }
/** * Ensures {@code peekBuffer} is large enough to store at least {@code length} bytes from the * current peek position. */ private void ensureSpaceForPeek(int length) { int requiredLength = peekBufferPosition + length; if (requiredLength > peekBuffer.length) { int newPeekCapacity = Util.constrainValue(peekBuffer.length * 2, requiredLength + PEEK_MIN_FREE_SPACE_AFTER_RESIZE, requiredLength + PEEK_MAX_FREE_SPACE); peekBuffer = Arrays.copyOf(peekBuffer, newPeekCapacity); } }
@Override public boolean equals(Object obj) { if (!(obj instanceof SchemeData)) { return false; } if (obj == this) { return true; } SchemeData other = (SchemeData) obj; return mimeType.equals(other.mimeType) && Util.areEqual(uuid, other.uuid) && Arrays.equals(data, other.data); }
private void drawTimeBar(Canvas canvas) { int progressBarHeight = progressBar.height(); int barTop = progressBar.centerY() - progressBarHeight / 2; int barBottom = barTop + progressBarHeight; if (duration <= 0) { canvas.drawRect(progressBar.left, barTop, progressBar.right, barBottom, unplayedPaint); return; } int bufferedLeft = bufferedBar.left; int bufferedRight = bufferedBar.right; int progressLeft = Math.max(Math.max(progressBar.left, bufferedRight), scrubberBar.right); if (progressLeft < progressBar.right) { canvas.drawRect(progressLeft, barTop, progressBar.right, barBottom, unplayedPaint); } bufferedLeft = Math.max(bufferedLeft, scrubberBar.right); if (bufferedRight > bufferedLeft) { canvas.drawRect(bufferedLeft, barTop, bufferedRight, barBottom, bufferedPaint); } if (scrubberBar.width() > 0) { canvas.drawRect(scrubberBar.left, barTop, scrubberBar.right, barBottom, playedPaint); } int adMarkerOffset = adMarkerWidth / 2; for (int i = 0; i < adGroupCount; i++) { long adGroupTimeMs = Util.constrainValue(adGroupTimesMs[i], 0, duration); int markerPositionOffset = (int) (progressBar.width() * adGroupTimeMs / duration) - adMarkerOffset; int markerLeft = progressBar.left + Math.min(progressBar.width() - adMarkerWidth, Math.max(0, markerPositionOffset)); Paint paint = playedAdGroups[i] ? playedAdMarkerPaint : adMarkerPaint; canvas.drawRect(markerLeft, barTop, markerLeft + adMarkerWidth, barBottom, paint); } }
/** * @param audioCapabilities The audio capabilities for playback on this device. May be null if the * default capabilities (no encoded audio passthrough support) should be assumed. * @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio before * output. May be empty. * @param listener Listener for audio track events. */ public AudioTrack(AudioCapabilities audioCapabilities, AudioProcessor[] audioProcessors, Listener listener) { this.audioCapabilities = audioCapabilities; this.listener = listener; releasingConditionVariable = new ConditionVariable(true); if (Util.SDK_INT >= 18) { try { getLatencyMethod = android.media.AudioTrack.class.getMethod("getLatency", (Class<?>[]) null); } catch (NoSuchMethodException e) { // There's no guarantee this method exists. Do nothing. } } if (Util.SDK_INT >= 19) { audioTrackUtil = new AudioTrackUtilV19(); } else { audioTrackUtil = new AudioTrackUtil(); } channelMappingAudioProcessor = new ChannelMappingAudioProcessor(); sonicAudioProcessor = new SonicAudioProcessor(); availableAudioProcessors = new AudioProcessor[3 + audioProcessors.length]; availableAudioProcessors[0] = new ResamplingAudioProcessor(); availableAudioProcessors[1] = channelMappingAudioProcessor; System.arraycopy(audioProcessors, 0, availableAudioProcessors, 2, audioProcessors.length); availableAudioProcessors[2 + audioProcessors.length] = sonicAudioProcessor; playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT]; volume = 1.0f; startMediaTimeState = START_NOT_SET; streamType = C.STREAM_TYPE_DEFAULT; audioSessionId = C.AUDIO_SESSION_ID_UNSET; playbackParameters = PlaybackParameters.DEFAULT; drainingAudioProcessorIndex = C.INDEX_UNSET; this.audioProcessors = new AudioProcessor[0]; outputBuffers = new ByteBuffer[0]; playbackParametersCheckpoints = new LinkedList<>(); }
/** * Returns whether {@code brand} is an ftyp atom brand that is compatible with the MP4 extractors. */ private static boolean isCompatibleBrand(int brand) { // Accept all brands starting '3gp'. if (brand >>> 8 == Util.getIntegerCodeForString("3gp")) { return true; } for (int compatibleBrand : COMPATIBLE_BRANDS) { if (compatibleBrand == brand) { return true; } } return false; }
/** * Returns a {@link XingSeeker} for seeking in the stream, if required information is present. * Returns {@code null} if not. On returning, {@code frame}'s position is not specified so the * caller should reset it. * * @param mpegAudioHeader The MPEG audio header associated with the frame. * @param frame The data in this audio frame, with its position set to immediately after the * 'Xing' or 'Info' tag. * @param position The position (byte offset) of the start of this frame in the stream. * @param inputLength The length of the stream in bytes. * @return A {@link XingSeeker} for seeking in the stream, or {@code null} if the required * information is not present. */ public static XingSeeker create(MpegAudioHeader mpegAudioHeader, ParsableByteArray frame, long position, long inputLength) { int samplesPerFrame = mpegAudioHeader.samplesPerFrame; int sampleRate = mpegAudioHeader.sampleRate; long firstFramePosition = position + mpegAudioHeader.frameSize; int flags = frame.readInt(); int frameCount; if ((flags & 0x01) != 0x01 || (frameCount = frame.readUnsignedIntToInt()) == 0) { // If the frame count is missing/invalid, the header can't be used to determine the duration. return null; } long durationUs = Util.scaleLargeTimestamp(frameCount, samplesPerFrame * C.MICROS_PER_SECOND, sampleRate); if ((flags & 0x06) != 0x06) { // If the size in bytes or table of contents is missing, the stream is not seekable. return new XingSeeker(firstFramePosition, durationUs, inputLength); } long sizeBytes = frame.readUnsignedIntToInt(); frame.skipBytes(1); long[] tableOfContents = new long[99]; for (int i = 0; i < 99; i++) { tableOfContents[i] = frame.readUnsignedByte(); } // TODO: Handle encoder delay and padding in 3 bytes offset by xingBase + 213 bytes: // delay = (frame.readUnsignedByte() << 4) + (frame.readUnsignedByte() >> 4); // padding = ((frame.readUnsignedByte() & 0x0F) << 8) + frame.readUnsignedByte(); return new XingSeeker(firstFramePosition, durationUs, inputLength, tableOfContents, sizeBytes, mpegAudioHeader.frameSize); }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } PrivFrame other = (PrivFrame) obj; return Util.areEqual(owner, other.owner) && Arrays.equals(privateData, other.privateData); }
@Override public boolean evaluate(String contentType) { contentType = Util.toLowerInvariant(contentType); return !TextUtils.isEmpty(contentType) && (!contentType.contains("text") || contentType.contains("text/vtt")) && !contentType.contains("html") && !contentType.contains("xml"); }
private void initializePlayer(Uri mediaUri) { if (mExoPlayer == null) { // Create an instance of the ExoPlayer. TrackSelector trackSelector = new DefaultTrackSelector(); LoadControl loadControl = new DefaultLoadControl(); mExoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector, loadControl); binding.exoStepFragmentPlayerView.setPlayer(mExoPlayer); mExoPlayer.addListener(this); String userAgent = Util.getUserAgent(getContext(), "RecipeStepVideo"); MediaSource mediaSource = new ExtractorMediaSource(mediaUri, new DefaultDataSourceFactory( getContext(), userAgent), new DefaultExtractorsFactory(), null, null); mExoPlayer.prepare(mediaSource); mExoPlayer.setPlayWhenReady(true); } }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } TextInformationFrame other = (TextInformationFrame) obj; return id.equals(other.id) && Util.areEqual(description, other.description) && Util.areEqual(value, other.value); }
@Override public void onResume() { super.onResume(); if ((Util.SDK_INT <= 23 || mTubiExoPlayer == null)) { setupExo(); } }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } EventMessage other = (EventMessage) obj; return durationMs == other.durationMs && id == other.id && Util.areEqual(schemeIdUri, other.schemeIdUri) && Util.areEqual(value, other.value) && Arrays.equals(messageData, other.messageData); }
@Override protected void configureCodec(MediaCodecInfo codecInfo, MediaCodec codec, Format format, MediaCrypto crypto) throws DecoderQueryException { codecMaxValues = getCodecMaxValues(codecInfo, format, streamFormats); MediaFormat mediaFormat = getMediaFormat(format, codecMaxValues, deviceNeedsAutoFrcWorkaround, tunnelingAudioSessionId); codec.configure(mediaFormat, surface, crypto, 0); if (Util.SDK_INT >= 23 && tunneling) { tunnelingOnFrameRenderedListener = new OnFrameRenderedListenerV23(codec); } }
@Override public void onStop() { super.onStop(); if (Util.SDK_INT > 23) { releasePlayer(); } }
@Override public void onStart() { super.onStart(); if (Util.SDK_INT > 23) { initializePlayer(); } }
@Override public void onResume() { super.onResume(); if ((Util.SDK_INT <= 23 || player == null)) { initializePlayer(); } }
public void setPattern(int patternBlocksToEncrypt, int patternBlocksToSkip) { this.patternBlocksToEncrypt = patternBlocksToEncrypt; this.patternBlocksToSkip = patternBlocksToSkip; if (Util.SDK_INT >= 24) { patternHolder.set(patternBlocksToEncrypt, patternBlocksToSkip); } }
/** * Returns the underlying audio track {@code positionUs} with any applicable speedup applied. */ private long applySpeedup(long positionUs) { while (!playbackParametersCheckpoints.isEmpty() && positionUs >= playbackParametersCheckpoints.getFirst().positionUs) { // We are playing (or about to play) media with the new playback parameters, so update them. PlaybackParametersCheckpoint checkpoint = playbackParametersCheckpoints.remove(); playbackParameters = checkpoint.playbackParameters; playbackParametersPositionUs = checkpoint.positionUs; playbackParametersOffsetUs = checkpoint.mediaTimeUs - startMediaTimeUs; } if (playbackParameters.speed == 1f) { return positionUs + playbackParametersOffsetUs - playbackParametersPositionUs; } if (playbackParametersCheckpoints.isEmpty() && sonicAudioProcessor.getOutputByteCount() >= SONIC_MIN_BYTES_FOR_SPEEDUP) { return playbackParametersOffsetUs + Util.scaleLargeTimestamp(positionUs - playbackParametersPositionUs, sonicAudioProcessor.getInputByteCount(), sonicAudioProcessor.getOutputByteCount()); } // We are playing drained data at a previous playback speed, or don't have enough bytes to // calculate an accurate speedup, so fall back to multiplying by the speed. return playbackParametersOffsetUs + (long) ((double) playbackParameters.speed * (positionUs - playbackParametersPositionUs)); }