boolean proceed = true; if (checkPendingSeek(seekExecutor) == SeekResult.EXTERNAL_SEEK) { return; if (Thread.interrupted() && !handlePlaybackInterrupt(null, seekExecutor)) { break; setInterruptibleForSeek(true); readExecutor.performRead(); setInterruptibleForSeek(false); waitOnEnd(); } catch (Exception e) { setInterruptibleForSeek(false); InterruptedException interruption = findInterrupt(e); proceed = handlePlaybackInterrupt(interruption, seekExecutor); } else { throw ExceptionTools.wrapUnfriendlyExceptions("Something went wrong when decoding the track.", FAULT, e);
private boolean handlePlaybackInterrupt(InterruptedException interruption, SeekExecutor seekExecutor) { Thread.interrupted(); if (checkStopped()) { markerTracker.trigger(STOPPED); return false; } SeekResult seekResult = checkPendingSeek(seekExecutor); if (seekResult != SeekResult.NO_SEEK) { // Double-check, might have received a stop request while seeking if (checkStopped()) { markerTracker.trigger(STOPPED); return false; } else { return seekResult == SeekResult.INTERNAL_SEEK; } } else if (interruption != null) { Thread.currentThread().interrupt(); throw new FriendlyException("The track was unexpectedly terminated.", SUSPICIOUS, interruption); } else { return true; } }
@Override public void process(LocalAudioTrackExecutor localExecutor) throws Exception { AdtsStreamProvider provider = new AdtsStreamProvider(inputStream, localExecutor.getProcessingContext()); try { log.debug("Starting to play ADTS stream {}", getIdentifier()); localExecutor.executeProcessingLoop(provider::provideFrames, null); } finally { provider.close(); } } }
} catch (Throwable e) { interrupt = findInterrupt(e); if (interrupt != null && checkStopped()) { log.debug("Track {} was interrupted outside of execution loop.", audioTrack.getIdentifier()); } else { interrupt = interrupt != null ? interrupt : findInterrupt(null);
@Override public void process(LocalAudioTrackExecutor localExecutor) { localExecutor.executeProcessingLoop(() -> { execute(localExecutor); }, null); }
@Override public void setMarker(TrackMarker marker) { markerTracker.set(marker, getPosition()); }
@Override public void setPosition(long timecode) { if (!audioTrack.isSeekable()) { return; } synchronized (actionSynchronizer) { if (timecode < 0) { timecode = 0; } queuedSeek.set(timecode); if (!useSeekGhosting) { frameBuffer.clear(); } interruptForSeek(); } }
private AudioTrackExecutor createExecutorForTrack(InternalAudioTrack track, AudioConfiguration configuration, AudioPlayerOptions playerOptions) { AudioSourceManager sourceManager = track.getSourceManager(); if (remoteNodeManager.isEnabled() && sourceManager != null && sourceManager.isTrackEncodable(track)) { return new RemoteAudioTrackExecutor(track, configuration, remoteNodeManager, playerOptions.volumeLevel); } else { AudioTrackExecutor customExecutor = track.createLocalExecutor(this); if (customExecutor != null) { return customExecutor; } else { int bufferDuration = Optional.ofNullable(playerOptions.frameBufferDuration.get()).orElse(frameBufferDuration); return new LocalAudioTrackExecutor(track, configuration, playerOptions, useSeekGhosting, bufferDuration); } } }
/** * Performs a seek if it scheduled. * @param seekExecutor Callback for performing a seek on the track * @return True if a seek was performed */ private SeekResult checkPendingSeek(SeekExecutor seekExecutor) { if (!audioTrack.isSeekable()) { return SeekResult.NO_SEEK; } long seekPosition; synchronized (actionSynchronizer) { seekPosition = queuedSeek.get(); if (seekPosition == -1) { return SeekResult.NO_SEEK; } log.debug("Track {} interrupted for seeking to {}.", audioTrack.getIdentifier(), seekPosition); applySeekState(seekPosition); } if (seekExecutor != null) { try { seekExecutor.performSeek(seekPosition); } catch (Exception e) { throw ExceptionTools.wrapUnfriendlyExceptions("Something went wrong when seeking to a position.", FAULT, e); } return SeekResult.INTERNAL_SEEK; } else { return SeekResult.EXTERNAL_SEEK; } }
private void processNextSegment(LocalAudioTrackExecutor localExecutor, TrackState state) throws InterruptedException { URI segmentUrl = getNextSegmentUrl(state); try (YoutubePersistentHttpStream stream = new YoutubePersistentHttpStream(httpInterface, segmentUrl, Long.MAX_VALUE)) { if (stream.checkStatusCode() == HttpStatus.SC_NO_CONTENT || stream.getContentLength() == 0) { state.finished = true; return; } // If we were redirected, use that URL as a base for the next segment URL. Otherwise we will likely get redirected // again on every other request, which is inefficient (redirects across domains, the original URL is always // closing the connection, whereas the final URL is keep-alive). state.baseUrl = httpInterface.getFinalLocation(); processSegmentStream(stream, localExecutor.getProcessingContext(), state); stream.releaseConnection(); } catch (IOException e) { throw new RuntimeException(e); } }
@Override public void process(LocalAudioTrackExecutor localExecutor) throws Exception { Mp3TrackProvider provider = new Mp3TrackProvider(localExecutor.getProcessingContext(), inputStream); try { provider.parseHeaders(); log.debug("Starting to play MP3 track {}", getIdentifier()); localExecutor.executeProcessingLoop(provider::provideFrames, provider::seekToTimecode); } finally { provider.close(); } } }
@Override public void process(LocalAudioTrackExecutor localExecutor) throws Exception { WavTrackProvider trackProvider = new WavFileLoader(inputStream).loadTrack(localExecutor.getProcessingContext()); try { log.debug("Starting to play WAV track {}", getIdentifier()); localExecutor.executeProcessingLoop(trackProvider::provideFrames, trackProvider::seekToTimecode); } finally { trackProvider.close(); } } }
@Override public void process(LocalAudioTrackExecutor localExecutor) throws Exception { FlacFileLoader file = new FlacFileLoader(inputStream); FlacTrackProvider trackProvider = file.loadTrack(localExecutor.getProcessingContext()); try { log.debug("Starting to play FLAC track {}", getIdentifier()); localExecutor.executeProcessingLoop(trackProvider::provideFrames, trackProvider::seekToTimecode); } finally { trackProvider.close(); } } }
@Override public void process(final LocalAudioTrackExecutor localExecutor) { OggPacketInputStream packetInputStream = new OggPacketInputStream(inputStream); log.debug("Starting to play an OGG stream track {}", getIdentifier()); localExecutor.executeProcessingLoop(() -> { try { processTrackLoop(packetInputStream, localExecutor.getProcessingContext()); } catch (IOException e) { throw new FriendlyException("Stream broke when playing OGG track.", SUSPICIOUS, e); } }, null); }
@Override public void process(LocalAudioTrackExecutor localExecutor) { MatroskaStreamingFile file = loadMatroskaFile(); MatroskaTrackConsumer trackConsumer = loadAudioTrack(file, localExecutor.getProcessingContext()); try { localExecutor.executeProcessingLoop(() -> { file.provideFrames(trackConsumer); }, position -> { file.seekToTimecode(trackConsumer.getTrack().index, position); }); } finally { trackConsumer.close(); } }
@Override public void process(LocalAudioTrackExecutor localExecutor) { MpegFileLoader file = new MpegFileLoader(inputStream); file.parseHeaders(); MpegTrackConsumer trackConsumer = loadAudioTrack(file, localExecutor.getProcessingContext()); try { MpegFileTrackProvider fileReader = file.loadReader(trackConsumer); if (fileReader == null) { throw new FriendlyException("Unknown MP4 format.", SUSPICIOUS, null); } accurateDuration.set(fileReader.getDuration()); localExecutor.executeProcessingLoop(fileReader::provideFrames, fileReader::seekToTimecode); } finally { trackConsumer.close(); } }