@Override public int version(RemoteMessage message) { // Backwards compatibility with older nodes. if (message instanceof TrackStartRequestMessage) { if (((TrackStartRequestMessage) message).position != 0) { return VERSION_WITH_POSITION; } AudioDataFormat format = ((TrackStartRequestMessage) message).configuration.getOutputFormat(); if (!format.equals(StandardAudioDataFormats.DISCORD_OPUS)) { return VERSION_WITH_FORMAT; } return VERSION_INITIAL; } return VERSION_WITH_POSITION; }
/** * @return The expected timecode of the next frame to receive from the remote node. */ public long getNextInputTimecode() { boolean dataReceived = hasReceivedData; long frameDuration = configuration.getOutputFormat().frameDuration(); if (dataReceived) { Long lastBufferTimecode = frameBuffer.getLastInputTimecode(); if (lastBufferTimecode != null) { return lastBufferTimecode + frameDuration; } } long seekPosition = pendingSeek.get(); if (seekPosition != NO_SEEK) { return seekPosition; } return dataReceived ? lastFrameTimecode.get() + frameDuration : lastFrameTimecode.get(); }
@Override public void encode(DataOutput out, TrackStartRequestMessage message) throws IOException { int version = version(message); out.writeLong(message.executorId); out.writeUTF(message.trackInfo.title); out.writeUTF(message.trackInfo.author); out.writeLong(message.trackInfo.length); out.writeUTF(message.trackInfo.identifier); out.writeBoolean(message.trackInfo.isStream); out.writeInt(message.encodedTrack.length); out.write(message.encodedTrack); out.writeInt(message.volume); out.writeUTF(message.configuration.getResamplingQuality().name()); out.writeInt(message.configuration.getOpusEncodingQuality()); if (version >= VERSION_WITH_FORMAT) { AudioDataFormat format = message.configuration.getOutputFormat(); out.writeInt(format.channelCount); out.writeInt(format.sampleRate); out.writeInt(format.chunkSampleCount); out.writeUTF(format.codecName()); } if (version >= VERSION_WITH_POSITION) { out.writeLong(message.position); } }
/** * @param track Audio track to play * @param configuration Configuration for audio processing * @param remoteNodeManager Manager of remote nodes * @param volumeLevel Mutable volume level */ public RemoteAudioTrackExecutor(AudioTrack track, AudioConfiguration configuration, RemoteNodeManager remoteNodeManager, AtomicInteger volumeLevel) { this.track = track; this.configuration = configuration.copy(); this.remoteNodeManager = remoteNodeManager; this.volumeLevel = volumeLevel; this.executorId = System.nanoTime(); this.frameBuffer = configuration.getFrameBufferFactory().create(BUFFER_DURATION_MS, configuration.getOutputFormat(), null); }
/** * @param audioTrack The audio track that this executor executes * @param configuration Configuration to use for audio processing * @param playerOptions Mutable player options (for example volume). * @param useSeekGhosting Whether to keep providing old frames continuing from the previous position during a seek * until frames from the new position arrive. * @param bufferDuration The size of the frame buffer in milliseconds */ public LocalAudioTrackExecutor(InternalAudioTrack audioTrack, AudioConfiguration configuration, AudioPlayerOptions playerOptions, boolean useSeekGhosting, int bufferDuration) { this.audioTrack = audioTrack; AudioDataFormat currentFormat = configuration.getOutputFormat(); this.frameBuffer = configuration.getFrameBufferFactory().create(bufferDuration, currentFormat, queuedStop); this.processingContext = new AudioProcessingContext(configuration, frameBuffer, playerOptions, currentFormat); this.useSeekGhosting = useSeekGhosting; }
private void handleTrackFrameData(TrackFrameDataMessage message) throws Exception { RemoteAudioTrackExecutor executor = playingTracks.get(message.executorId); if (executor != null) { if (message.seekedPosition >= 0) { executor.clearSeek(message.seekedPosition); } AudioFrameBuffer buffer = executor.getAudioBuffer(); executor.receivedData(); AudioDataFormat format = executor.getConfiguration().getOutputFormat(); for (AudioFrame frame : message.frames) { buffer.consume(new ImmutableAudioFrame(frame.getTimecode(), frame.getData(), frame.getVolume(), format)); } if (message.finished) { buffer.setTerminateOnEmpty(); trackEnded(executor, false); } } }