protected synchronized void processDelegate(InternalAudioTrack delegate, LocalAudioTrackExecutor localExecutor) throws Exception { if (this.delegate != null) { throw new IllegalStateException("Cannot assign delegate twice."); } this.delegate = delegate; delegate.assignExecutor(localExecutor, false); delegate.process(localExecutor); }
@Override public boolean isPlayingTrack(AudioTrack track) { AudioTrackExecutor executor = ((InternalAudioTrack) track).getActiveExecutor(); if (executor instanceof RemoteAudioTrackExecutor) { return playingTracks.containsKey(((RemoteAudioTrackExecutor) executor).getExecutorId()); } return false; }
@Override public long getDuration() { if (delegate != null) { return delegate.getDuration(); } else { synchronized (this) { if (delegate != null) { return delegate.getDuration(); } else { return super.getDuration(); } } } } }
log.debug("Starting to play track {} locally with listener {}", audioTrack.getInfo().identifier, listener); audioTrack.process(this); log.debug("Playing track {} finished or was stopped.", audioTrack.getIdentifier()); } catch (Throwable e) { log.debug("Track {} was interrupted outside of execution loop.", audioTrack.getIdentifier()); } else { frameBuffer.setTerminateOnEmpty(); ExceptionTools.log(log, exception, "playback of " + audioTrack.getIdentifier()); log.warn("Tried to start an already playing track {}", audioTrack.getIdentifier());
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 boolean provideShadowFrame(MutableAudioFrame targetFrame) { InternalAudioTrack shadow = shadowTrack; if (shadow != null && shadow.provide(targetFrame)) { if (targetFrame.isTerminator()) { shadowTrack = null; return false; } return true; } return false; }
@Override public void stop() { synchronized (actionSynchronizer) { Thread thread = playingThread.get(); if (thread != null) { log.debug("Requesting stop for track {}", audioTrack.getIdentifier()); queuedStop.compareAndSet(false, true); thread.interrupt(); } else { log.debug("Tried to stop track {} which is not playing.", audioTrack.getIdentifier()); } } }
/** * Executes an audio track with the given player and volume. * @param listener A listener for track state events * @param track The audio track to execute * @param configuration The audio configuration to use for executing * @param playerOptions Options of the audio player */ public void executeTrack(TrackStateListener listener, InternalAudioTrack track, AudioConfiguration configuration, AudioPlayerOptions playerOptions) { final AudioTrackExecutor executor = createExecutorForTrack(track, configuration, playerOptions); track.assignExecutor(executor, true); trackPlaybackExecutorService.execute(() -> executor.execute(listener)); }
@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 AudioFrame provideShadowFrame() { InternalAudioTrack shadow = shadowTrack; AudioFrame frame = null; if (shadow != null) { frame = shadow.provide(); if (frame != null && frame.isTerminator()) { shadowTrack = null; frame = null; } } return frame; }
private void interruptForSeek() { boolean interrupted = false; synchronized (actionSynchronizer) { if (interruptibleForSeek) { interruptibleForSeek = false; Thread thread = playingThread.get(); if (thread != null) { thread.interrupt(); interrupted = true; } } } if (interrupted) { log.debug("Interrupting playing thread to perform a seek {}", audioTrack.getIdentifier()); } else { log.debug("Seeking on track {} while not in playback loop.", audioTrack.getIdentifier()); } }
@Override public AudioFrame provide(long timeout, TimeUnit unit) throws TimeoutException, InterruptedException { InternalAudioTrack track; lastRequestTime = System.currentTimeMillis(); if (timeout == 0 && paused.get()) { return null; } while ((track = activeTrack) != null) { AudioFrame frame = timeout > 0 ? track.provide(timeout, unit) : track.provide(); if (frame != null) { lastReceiveTime = System.nanoTime(); shadowTrack = null; if (frame.isTerminator()) { handleTerminator(track); continue; } } else if (timeout == 0) { checkStuck(track); frame = provideShadowFrame(); } return frame; } return null; }
@Override public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) { AudioTrackExecutor executor = ((InternalAudioTrack) track).getActiveExecutor(); if (endReason != AudioTrackEndReason.FINISHED && executor instanceof RemoteAudioTrackExecutor) { for (RemoteNodeProcessor processor : activeProcessors) { processor.trackEnded((RemoteAudioTrackExecutor) executor, true); } } }
@Override public boolean provide(MutableAudioFrame targetFrame, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException { InternalAudioTrack track; lastRequestTime = System.currentTimeMillis(); if (timeout == 0 && paused.get()) { return false; } while ((track = activeTrack) != null) { if (timeout > 0 ? track.provide(targetFrame, timeout, unit) : track.provide(targetFrame)) { lastReceiveTime = System.nanoTime(); shadowTrack = null; if (targetFrame.isTerminator()) { handleTerminator(track); continue; } return true; } else if (timeout == 0) { checkStuck(track); return provideShadowFrame(targetFrame); } else { return false; } } return false; }
private void handleTerminator(InternalAudioTrack track) { synchronized (trackSwitchLock) { if (activeTrack == track) { activeTrack = null; dispatchEvent(new TrackEndEvent(this, track, track.getActiveExecutor().failedBeforeLoad() ? LOAD_FAILED : FINISHED)); } } }