Skip to content

Commit 4775008

Browse files
committed
Better preloading mechanism (using pcm_offset)
1 parent c035b0c commit 4775008

4 files changed

Lines changed: 41 additions & 51 deletions

File tree

core/src/main/java/xyz/gianlu/librespot/player/Player.java

Lines changed: 9 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.apache.log4j.Logger;
77
import org.jetbrains.annotations.NotNull;
88
import xyz.gianlu.librespot.common.Utils;
9-
import xyz.gianlu.librespot.common.proto.Metadata;
109
import xyz.gianlu.librespot.common.proto.Spirc;
1110
import xyz.gianlu.librespot.core.Session;
1211
import xyz.gianlu.librespot.mercury.MercuryClient;
@@ -20,22 +19,18 @@
2019
import java.util.Collections;
2120
import java.util.Iterator;
2221
import java.util.List;
23-
import java.util.concurrent.Executors;
24-
import java.util.concurrent.ScheduledExecutorService;
25-
import java.util.concurrent.TimeUnit;
2622

2723
/**
2824
* @author Gianlu
2925
*/
3026
public class Player implements FrameListener, TrackHandler.Listener {
3127
private static final Logger LOGGER = Logger.getLogger(Player.class);
32-
private static final long TRACK_PRELOAD_THRESHOLD = TimeUnit.SECONDS.toMillis(10);
28+
3329
private final Session session;
3430
private final SpotifyIrc spirc;
3531
private final Spirc.State.Builder state;
3632
private final PlayerConfiguration conf;
3733
private final CacheManager cacheManager;
38-
private final PreloadScheduler scheduler = new PreloadScheduler();
3934
private TrackHandler trackHandler;
4035
private TrackHandler preloadTrackHandler;
4136

@@ -234,8 +229,6 @@ private void handleSeek(int pos) {
234229
state.setPositionMeasuredAt(System.currentTimeMillis());
235230
if (trackHandler != null) trackHandler.sendSeek(pos);
236231
stateUpdated();
237-
238-
scheduler.reschedule();
239232
}
240233

241234
private void updatedTracks(@NotNull Spirc.Frame frame) {
@@ -278,12 +271,15 @@ public void endOfTrack(@NotNull TrackHandler handler) {
278271
}
279272
}
280273

281-
private void preloadNextTrack() {
282-
Spirc.TrackRef next = state.getTrack(getQueuedTrack(false));
274+
@Override
275+
public void preloadNextTrack(@NotNull TrackHandler handler) {
276+
if (handler == trackHandler) {
277+
Spirc.TrackRef next = state.getTrack(getQueuedTrack(false));
283278

284-
preloadTrackHandler = new TrackHandler(session, cacheManager, conf, this);
285-
preloadTrackHandler.sendLoad(next, false, 0);
286-
LOGGER.trace("Started next track preload, gid: " + Utils.bytesToHex(next.getGid()));
279+
preloadTrackHandler = new TrackHandler(session, cacheManager, conf, this);
280+
preloadTrackHandler.sendLoad(next, false, 0);
281+
LOGGER.trace("Started next track preload, gid: " + Utils.bytesToHex(next.getGid()));
282+
}
287283
}
288284

289285
private void handleLoad(@NotNull Spirc.Frame frame) {
@@ -328,8 +324,6 @@ private void loadTrack(boolean play) {
328324
}
329325

330326
stateUpdated();
331-
332-
scheduler.reschedule();
333327
}
334328

335329
private void handlePlay() {
@@ -338,8 +332,6 @@ private void handlePlay() {
338332
state.setStatus(Spirc.PlayStatus.kPlayStatusPlay);
339333
state.setPositionMeasuredAt(System.currentTimeMillis());
340334
stateUpdated();
341-
342-
scheduler.reschedule();
343335
}
344336
}
345337

@@ -354,8 +346,6 @@ private void handlePause() {
354346
state.setPositionMs(pos + diff);
355347
state.setPositionMeasuredAt(now);
356348
stateUpdated();
357-
358-
scheduler.reschedule();
359349
}
360350
}
361351

@@ -405,8 +395,6 @@ private void handlePrev() {
405395
state.setPositionMeasuredAt(System.currentTimeMillis());
406396
if (trackHandler != null) trackHandler.sendSeek(0);
407397
stateUpdated();
408-
409-
scheduler.reschedule();
410398
}
411399
}
412400

@@ -428,33 +416,4 @@ public interface PlayerConfiguration {
428416

429417
float normalisationPregain();
430418
}
431-
432-
private class PreloadScheduler {
433-
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
434-
private Task activeTask;
435-
436-
void reschedule() {
437-
if (activeTask != null) activeTask.abort();
438-
439-
if (!conf.preloadEnabled() || state.getStatus() != Spirc.PlayStatus.kPlayStatusPlay) return;
440-
Metadata.Track track = trackHandler != null ? trackHandler.track() : null;
441-
if (track == null) return;
442-
443-
activeTask = new Task();
444-
executor.schedule(activeTask, (track.getDuration() - getPosition()) - TRACK_PRELOAD_THRESHOLD, TimeUnit.MILLISECONDS);
445-
}
446-
447-
private class Task implements Runnable {
448-
private volatile boolean aborted = false;
449-
450-
@Override
451-
public void run() {
452-
if (!aborted) preloadNextTrack();
453-
}
454-
455-
private void abort() {
456-
aborted = true;
457-
}
458-
}
459-
}
460419
}

core/src/main/java/xyz/gianlu/librespot/player/PlayerRunner.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class PlayerRunner implements Runnable {
2626
private static final int BUFFER_SIZE = 2048;
2727
private static final int CONVERTED_BUFFER_SIZE = BUFFER_SIZE * 2;
2828
private static final Logger LOGGER = Logger.getLogger(PlayerRunner.class);
29+
private static final long TRACK_PRELOAD_THRESHOLD = 10; // sec
2930
private final SyncState joggSyncState = new SyncState();
3031
private final InputStream audioIn;
3132
private final Listener listener;
@@ -49,6 +50,8 @@ public class PlayerRunner implements Runnable {
4950
private int[] pcmIndex;
5051
private volatile boolean playing = false;
5152
private volatile boolean stopped = false;
53+
private long pcm_offset;
54+
private boolean calledPreload = false;
5255

5356
PlayerRunner(@NotNull AudioFileStreaming audioFile, @NotNull NormalizationData normalizationData,
5457
@NotNull Player.PlayerConfiguration configuration, @NotNull Listener listener, int duration) throws IOException, PlayerException {
@@ -204,6 +207,8 @@ private void readBody() throws PlayerException, IOException {
204207
}
205208

206209
private void decodeCurrentPacket() {
210+
long granulepos = joggPacket.granulepos;
211+
207212
if (jorbisBlock.synthesis(joggPacket) == 0)
208213
jorbisDspState.synthesis_blockin(jorbisBlock);
209214

@@ -232,9 +237,26 @@ private void decodeCurrentPacket() {
232237

233238
outputLine.write(convertedBuffer, 0, 2 * jorbisInfo.channels * range);
234239
jorbisDspState.synthesis_read(range);
240+
241+
if (granulepos != -1 && joggPacket.e_o_s == 0) {
242+
granulepos -= samples;
243+
pcm_offset = granulepos;
244+
checkPreload();
245+
}
235246
}
236247
}
237248

249+
private void checkPreload() {
250+
if (!calledPreload && (duration / 1000) - time() <= TRACK_PRELOAD_THRESHOLD) {
251+
calledPreload = true;
252+
listener.preloadNextTrack();
253+
}
254+
}
255+
256+
public int time() {
257+
return (int) (pcm_offset / jorbisInfo.rate);
258+
}
259+
238260
private void cleanup() {
239261
joggStreamState.clear();
240262
jorbisBlock.clear();
@@ -298,6 +320,8 @@ public interface Listener {
298320
void endOfTrack();
299321

300322
void playbackError(@NotNull Exception ex);
323+
324+
void preloadNextTrack();
301325
}
302326

303327
public static class Controller {

core/src/main/java/xyz/gianlu/librespot/player/TrackHandler.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ public void playbackError(@NotNull Exception ex) {
145145
LOGGER.fatal("Playback failed!", ex);
146146
}
147147

148+
@Override
149+
public void preloadNextTrack() {
150+
listener.preloadNextTrack(this);
151+
}
152+
148153
@Nullable
149154
PlayerRunner.Controller controller() {
150155
return playerRunner == null ? null : playerRunner.controller();
@@ -218,6 +223,8 @@ public interface Listener {
218223
void loadingError(@NotNull TrackHandler handler, @NotNull Exception ex);
219224

220225
void endOfTrack(@NotNull TrackHandler handler);
226+
227+
void preloadNextTrack(@NotNull TrackHandler handler);
221228
}
222229

223230
private class Looper implements Runnable {

mdnsjava

0 commit comments

Comments
 (0)