@capgo/native-audio 8.2.11 → 8.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -34
- package/android/build.gradle +1 -1
- package/android/src/main/java/ee/forgr/audio/AudioAsset.java +352 -74
- package/android/src/main/java/ee/forgr/audio/AudioDispatcher.java +24 -3
- package/android/src/main/java/ee/forgr/audio/Constant.java +9 -1
- package/android/src/main/java/ee/forgr/audio/Logger.java +55 -0
- package/android/src/main/java/ee/forgr/audio/NativeAudio.java +336 -57
- package/android/src/main/java/ee/forgr/audio/RemoteAudioAsset.java +307 -98
- package/android/src/main/java/ee/forgr/audio/StreamAudioAsset.java +285 -96
- package/dist/docs.json +307 -41
- package/dist/esm/definitions.d.ts +116 -38
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +52 -41
- package/dist/esm/web.js +386 -41
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +386 -41
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +386 -41
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/NativeAudioPlugin/AudioAsset+Fade.swift +104 -0
- package/ios/Sources/NativeAudioPlugin/AudioAsset.swift +168 -324
- package/ios/Sources/NativeAudioPlugin/Constant.swift +17 -4
- package/ios/Sources/NativeAudioPlugin/Logger.swift +43 -0
- package/ios/Sources/NativeAudioPlugin/Plugin.swift +176 -87
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset+Fade.swift +110 -0
- package/ios/Sources/NativeAudioPlugin/RemoteAudioAsset.swift +117 -273
- package/ios/Tests/NativeAudioPluginTests/PluginTests.swift +47 -72
- package/package.json +1 -1
|
@@ -24,8 +24,6 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
24
24
|
private float volume;
|
|
25
25
|
private boolean isPrepared = false;
|
|
26
26
|
private final float initialVolume;
|
|
27
|
-
private static final float FADE_STEP = 0.05f;
|
|
28
|
-
private static final int FADE_DELAY_MS = 80; // 80ms between steps
|
|
29
27
|
private static final long LIVE_OFFSET_MS = 5000; // 5 seconds behind live
|
|
30
28
|
private final java.util.Map<String, String> headers;
|
|
31
29
|
|
|
@@ -68,7 +66,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
68
66
|
}
|
|
69
67
|
|
|
70
68
|
private void initializePlayer() {
|
|
71
|
-
|
|
69
|
+
logger.debug("Initializing stream player with volume: " + volume);
|
|
72
70
|
|
|
73
71
|
// Configure HLS source with better settings for live streaming
|
|
74
72
|
DefaultHttpDataSource.Factory httpDataSourceFactory = new DefaultHttpDataSource.Factory()
|
|
@@ -95,7 +93,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
95
93
|
new Player.Listener() {
|
|
96
94
|
@Override
|
|
97
95
|
public void onPlaybackStateChanged(int state) {
|
|
98
|
-
|
|
96
|
+
logger.debug("Stream state changed to: " + getStateString(state));
|
|
99
97
|
if (state == Player.STATE_READY && !isPrepared) {
|
|
100
98
|
isPrepared = true;
|
|
101
99
|
if (player.isCurrentMediaItemLive()) {
|
|
@@ -106,17 +104,17 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
106
104
|
|
|
107
105
|
@Override
|
|
108
106
|
public void onIsLoadingChanged(boolean isLoading) {
|
|
109
|
-
|
|
107
|
+
logger.debug("Loading state changed: " + isLoading);
|
|
110
108
|
}
|
|
111
109
|
|
|
112
110
|
@Override
|
|
113
111
|
public void onIsPlayingChanged(boolean isPlaying) {
|
|
114
|
-
|
|
112
|
+
logger.debug("Playing state changed: " + isPlaying);
|
|
115
113
|
}
|
|
116
114
|
|
|
117
115
|
@Override
|
|
118
116
|
public void onPlayerError(PlaybackException error) {
|
|
119
|
-
|
|
117
|
+
logger.error("Player error: " + error.getMessage());
|
|
120
118
|
isPrepared = false;
|
|
121
119
|
// Try to recover by recreating the player
|
|
122
120
|
owner
|
|
@@ -146,8 +144,8 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
146
144
|
}
|
|
147
145
|
|
|
148
146
|
@Override
|
|
149
|
-
public void play(
|
|
150
|
-
|
|
147
|
+
public void play(double time, float volume) throws Exception {
|
|
148
|
+
logger.debug("Play called with time: " + time + ", isPrepared: " + isPrepared);
|
|
151
149
|
owner
|
|
152
150
|
.getActivity()
|
|
153
151
|
.runOnUiThread(() -> {
|
|
@@ -157,23 +155,24 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
157
155
|
new Player.Listener() {
|
|
158
156
|
@Override
|
|
159
157
|
public void onPlaybackStateChanged(int state) {
|
|
160
|
-
|
|
158
|
+
logger.debug("Play-wait state changed to: " + getStateString(state));
|
|
161
159
|
if (state == Player.STATE_READY) {
|
|
162
|
-
startPlayback(time);
|
|
160
|
+
startPlayback(time, volume);
|
|
161
|
+
startCurrentTimeUpdates();
|
|
163
162
|
player.removeListener(this);
|
|
164
163
|
}
|
|
165
164
|
}
|
|
166
165
|
}
|
|
167
166
|
);
|
|
168
167
|
} else {
|
|
169
|
-
startPlayback(time);
|
|
168
|
+
startPlayback(time, volume);
|
|
170
169
|
}
|
|
171
170
|
});
|
|
172
171
|
}
|
|
173
172
|
|
|
174
|
-
private void startPlayback(
|
|
175
|
-
|
|
176
|
-
if (time !=
|
|
173
|
+
private void startPlayback(double time, float volume) {
|
|
174
|
+
logger.debug("Starting playback with time: " + time);
|
|
175
|
+
if (time != 0) {
|
|
177
176
|
player.seekTo(Math.round(time * 1000));
|
|
178
177
|
} else if (player.isCurrentMediaItemLive()) {
|
|
179
178
|
player.seekToDefaultPosition();
|
|
@@ -181,6 +180,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
181
180
|
player.setPlaybackParameters(new PlaybackParameters(1.0f));
|
|
182
181
|
player.setVolume(volume);
|
|
183
182
|
player.setPlayWhenReady(true);
|
|
183
|
+
startCurrentTimeUpdates();
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
@Override
|
|
@@ -189,8 +189,10 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
189
189
|
owner
|
|
190
190
|
.getActivity()
|
|
191
191
|
.runOnUiThread(() -> {
|
|
192
|
-
|
|
192
|
+
cancelFade();
|
|
193
|
+
if (player != null && player.isPlaying()) {
|
|
193
194
|
player.setPlayWhenReady(false);
|
|
195
|
+
stopCurrentTimeUpdates();
|
|
194
196
|
wasPlaying[0] = true;
|
|
195
197
|
}
|
|
196
198
|
});
|
|
@@ -203,6 +205,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
203
205
|
.getActivity()
|
|
204
206
|
.runOnUiThread(() -> {
|
|
205
207
|
player.setPlayWhenReady(true);
|
|
208
|
+
startCurrentTimeUpdates();
|
|
206
209
|
});
|
|
207
210
|
}
|
|
208
211
|
|
|
@@ -211,6 +214,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
211
214
|
owner
|
|
212
215
|
.getActivity()
|
|
213
216
|
.runOnUiThread(() -> {
|
|
217
|
+
cancelFade();
|
|
214
218
|
// First stop playback
|
|
215
219
|
player.stop();
|
|
216
220
|
// Reset player state
|
|
@@ -243,7 +247,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
243
247
|
new Player.Listener() {
|
|
244
248
|
@Override
|
|
245
249
|
public void onPlaybackStateChanged(int state) {
|
|
246
|
-
|
|
250
|
+
logger.debug("Stop-reinit state changed to: " + getStateString(state));
|
|
247
251
|
if (state == Player.STATE_READY) {
|
|
248
252
|
isPrepared = true;
|
|
249
253
|
player.removeListener(this);
|
|
@@ -264,6 +268,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
264
268
|
.runOnUiThread(() -> {
|
|
265
269
|
player.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
266
270
|
player.setPlayWhenReady(true);
|
|
271
|
+
startCurrentTimeUpdates();
|
|
267
272
|
});
|
|
268
273
|
}
|
|
269
274
|
|
|
@@ -272,24 +277,58 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
272
277
|
owner
|
|
273
278
|
.getActivity()
|
|
274
279
|
.runOnUiThread(() -> {
|
|
280
|
+
cancelFade();
|
|
275
281
|
player.stop();
|
|
276
282
|
player.clearMediaItems();
|
|
277
283
|
player.release();
|
|
278
284
|
isPrepared = false;
|
|
285
|
+
close(); // Ensure fadeExecutor is shutdown
|
|
279
286
|
});
|
|
280
287
|
}
|
|
281
288
|
|
|
282
289
|
@Override
|
|
283
|
-
public void
|
|
290
|
+
public void close() {
|
|
291
|
+
if (fadeExecutor != null && !fadeExecutor.isShutdown()) {
|
|
292
|
+
fadeExecutor.shutdown();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@Override
|
|
297
|
+
protected void finalize() throws Throwable {
|
|
298
|
+
try {
|
|
299
|
+
close();
|
|
300
|
+
} finally {
|
|
301
|
+
super.finalize();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@Override
|
|
306
|
+
public void setVolume(float volume, double duration) throws Exception {
|
|
284
307
|
this.volume = volume;
|
|
285
308
|
owner
|
|
286
309
|
.getActivity()
|
|
287
310
|
.runOnUiThread(() -> {
|
|
288
|
-
|
|
289
|
-
|
|
311
|
+
cancelFade();
|
|
312
|
+
try {
|
|
313
|
+
if (this.isPlaying() && duration > 0) {
|
|
314
|
+
fadeTo(duration, volume);
|
|
315
|
+
} else {
|
|
316
|
+
player.setVolume(volume);
|
|
317
|
+
}
|
|
318
|
+
} catch (Exception e) {
|
|
319
|
+
logger.error("Error setting volume", e);
|
|
320
|
+
}
|
|
290
321
|
});
|
|
291
322
|
}
|
|
292
323
|
|
|
324
|
+
@Override
|
|
325
|
+
public float getVolume() throws Exception {
|
|
326
|
+
if (player != null) {
|
|
327
|
+
return player.getVolume();
|
|
328
|
+
}
|
|
329
|
+
return 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
293
332
|
@Override
|
|
294
333
|
public boolean isPlaying() throws Exception {
|
|
295
334
|
return player != null && player.isPlaying();
|
|
@@ -340,8 +379,8 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
340
379
|
}
|
|
341
380
|
|
|
342
381
|
@Override
|
|
343
|
-
public void
|
|
344
|
-
|
|
382
|
+
public void playWithFadeIn(double time, float volume, double fadeInDurationMs) throws Exception {
|
|
383
|
+
logger.debug("playWithFadeIn called with time: " + time);
|
|
345
384
|
owner
|
|
346
385
|
.getActivity()
|
|
347
386
|
.runOnUiThread(() -> {
|
|
@@ -352,19 +391,19 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
352
391
|
@Override
|
|
353
392
|
public void onPlaybackStateChanged(int state) {
|
|
354
393
|
if (state == Player.STATE_READY) {
|
|
355
|
-
startPlaybackWithFade(time);
|
|
394
|
+
startPlaybackWithFade(time, volume, fadeInDurationMs);
|
|
356
395
|
player.removeListener(this);
|
|
357
396
|
}
|
|
358
397
|
}
|
|
359
398
|
}
|
|
360
399
|
);
|
|
361
400
|
} else {
|
|
362
|
-
startPlaybackWithFade(time);
|
|
401
|
+
startPlaybackWithFade(time, volume, fadeInDurationMs);
|
|
363
402
|
}
|
|
364
403
|
});
|
|
365
404
|
}
|
|
366
405
|
|
|
367
|
-
private void startPlaybackWithFade(Double time) {
|
|
406
|
+
private void startPlaybackWithFade(Double time, float targetVolume, double fadeInDurationMs) {
|
|
368
407
|
if (!player.isPlayingAd()) {
|
|
369
408
|
// Make sure we're not in an ad
|
|
370
409
|
if (time != null) {
|
|
@@ -388,8 +427,9 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
388
427
|
// Start with volume 0
|
|
389
428
|
player.setVolume(0);
|
|
390
429
|
player.setPlayWhenReady(true);
|
|
430
|
+
startCurrentTimeUpdates();
|
|
391
431
|
// Start fade after ensuring we're actually playing
|
|
392
|
-
checkAndStartFade();
|
|
432
|
+
checkAndStartFade(fadeInDurationMs, targetVolume);
|
|
393
433
|
}
|
|
394
434
|
}
|
|
395
435
|
}
|
|
@@ -397,7 +437,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
397
437
|
}
|
|
398
438
|
}
|
|
399
439
|
|
|
400
|
-
private void checkAndStartFade() {
|
|
440
|
+
private void checkAndStartFade(double fadeInDurationMs, float volume) {
|
|
401
441
|
final Handler handler = new Handler(Looper.getMainLooper());
|
|
402
442
|
handler.postDelayed(
|
|
403
443
|
new Runnable() {
|
|
@@ -406,7 +446,7 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
406
446
|
@Override
|
|
407
447
|
public void run() {
|
|
408
448
|
if (player.isPlaying()) {
|
|
409
|
-
fadeIn();
|
|
449
|
+
fadeIn(fadeInDurationMs, volume);
|
|
410
450
|
} else if (attempts < 10) {
|
|
411
451
|
// Try for 5 seconds (10 * 500ms)
|
|
412
452
|
attempts++;
|
|
@@ -418,93 +458,166 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
418
458
|
);
|
|
419
459
|
}
|
|
420
460
|
|
|
421
|
-
private void fadeIn() {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
float currentVolume = 0;
|
|
461
|
+
private void fadeIn(double fadeInDurationMs, float targetVolume) {
|
|
462
|
+
cancelFade();
|
|
463
|
+
fadeState = FadeState.FADE_IN;
|
|
425
464
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
465
|
+
final int steps = Math.max(1, (int) (fadeInDurationMs / FADE_DELAY_MS));
|
|
466
|
+
final float fadeStep = targetVolume / steps;
|
|
467
|
+
|
|
468
|
+
fadeTask = fadeExecutor.scheduleWithFixedDelay(
|
|
469
|
+
new Runnable() {
|
|
470
|
+
float currentVolume = 0;
|
|
471
|
+
|
|
472
|
+
@Override
|
|
473
|
+
public void run() {
|
|
474
|
+
if (fadeState != FadeState.FADE_IN || player == null || !player.isPlaying() || currentVolume >= targetVolume) {
|
|
475
|
+
fadeState = FadeState.NONE;
|
|
476
|
+
cancelFade();
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
final float nextVolume = Math.min(currentVolume + fadeStep, targetVolume);
|
|
481
|
+
owner
|
|
482
|
+
.getActivity()
|
|
483
|
+
.runOnUiThread(() -> {
|
|
484
|
+
if (player != null && player.isPlaying()) {
|
|
485
|
+
player.setVolume(nextVolume);
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
currentVolume = nextVolume;
|
|
434
489
|
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
|
|
490
|
+
},
|
|
491
|
+
0,
|
|
492
|
+
FADE_DELAY_MS,
|
|
493
|
+
java.util.concurrent.TimeUnit.MILLISECONDS
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
private void fadeTo(double fadeDurationMs, float targetVolume) {
|
|
498
|
+
cancelFade();
|
|
499
|
+
fadeState = FadeState.FADE_TO;
|
|
500
|
+
|
|
501
|
+
if (player == null) return;
|
|
502
|
+
|
|
503
|
+
final int steps = Math.max(1, (int) (fadeDurationMs / FADE_DELAY_MS));
|
|
504
|
+
final float minVolume = zeroVolume;
|
|
505
|
+
final float initialVolume = Math.max(player.getVolume(), minVolume);
|
|
506
|
+
final float finalTargetVolume = Math.max(targetVolume, minVolume);
|
|
507
|
+
final double ratio = Math.pow(finalTargetVolume / initialVolume, 1.0 / steps);
|
|
508
|
+
if (Double.isNaN(ratio) || Double.isInfinite(ratio)) {
|
|
509
|
+
player.setVolume(finalTargetVolume);
|
|
510
|
+
fadeState = FadeState.NONE;
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
fadeTask = fadeExecutor.scheduleWithFixedDelay(
|
|
515
|
+
new Runnable() {
|
|
516
|
+
int currentStep = 0;
|
|
517
|
+
float currentVolume = initialVolume;
|
|
518
|
+
|
|
519
|
+
@Override
|
|
520
|
+
public void run() {
|
|
521
|
+
if (fadeState != FadeState.FADE_TO || player == null || !player.isPlaying() || currentStep >= steps) {
|
|
522
|
+
fadeState = FadeState.NONE;
|
|
523
|
+
cancelFade();
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
currentVolume *= (float) ratio;
|
|
528
|
+
final float nextVolume = Math.min(Math.max(currentVolume, minVolume), maxVolume);
|
|
529
|
+
owner
|
|
530
|
+
.getActivity()
|
|
531
|
+
.runOnUiThread(() -> {
|
|
532
|
+
if (player != null && player.isPlaying()) {
|
|
533
|
+
player.setVolume(nextVolume);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
currentStep++;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
0,
|
|
540
|
+
FADE_DELAY_MS,
|
|
541
|
+
java.util.concurrent.TimeUnit.MILLISECONDS
|
|
542
|
+
);
|
|
438
543
|
}
|
|
439
544
|
|
|
440
545
|
@Override
|
|
441
|
-
public void stopWithFade() throws Exception {
|
|
546
|
+
public void stopWithFade(double fadeOutDurationMs, boolean toPause) throws Exception {
|
|
442
547
|
owner
|
|
443
548
|
.getActivity()
|
|
444
549
|
.runOnUiThread(() -> {
|
|
445
|
-
if (player.isPlaying()) {
|
|
446
|
-
fadeOut();
|
|
550
|
+
if (player != null && player.isPlaying()) {
|
|
551
|
+
fadeOut(fadeOutDurationMs, toPause);
|
|
552
|
+
} else if (!toPause) {
|
|
553
|
+
try {
|
|
554
|
+
stop();
|
|
555
|
+
} catch (Exception e) {
|
|
556
|
+
logger.error("Error stopping stream asset", e);
|
|
557
|
+
}
|
|
447
558
|
}
|
|
448
559
|
});
|
|
449
560
|
}
|
|
450
561
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
@Override
|
|
457
|
-
public void run() {
|
|
458
|
-
if (currentVolume > FADE_STEP) {
|
|
459
|
-
currentVolume -= FADE_STEP;
|
|
460
|
-
player.setVolume(currentVolume);
|
|
461
|
-
Log.d(TAG, "Fading out: volume = " + currentVolume);
|
|
462
|
-
handler.postDelayed(this, FADE_DELAY_MS);
|
|
463
|
-
} else {
|
|
464
|
-
player.setVolume(0);
|
|
465
|
-
// Stop and reset player
|
|
466
|
-
player.stop();
|
|
467
|
-
player.clearMediaItems();
|
|
468
|
-
isPrepared = false;
|
|
562
|
+
@Override
|
|
563
|
+
public void stopWithFade() throws Exception {
|
|
564
|
+
stopWithFade(DEFAULT_FADE_DURATION_MS, false);
|
|
565
|
+
}
|
|
469
566
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
.setConnectTimeoutMs(15000)
|
|
474
|
-
.setReadTimeoutMs(15000)
|
|
475
|
-
.setUserAgent("ExoPlayer");
|
|
567
|
+
private void fadeOut(double fadeOutDurationMs, boolean toPause) {
|
|
568
|
+
cancelFade();
|
|
569
|
+
fadeState = FadeState.FADE_OUT;
|
|
476
570
|
|
|
477
|
-
|
|
478
|
-
if (headers != null && !headers.isEmpty()) {
|
|
479
|
-
httpDataSourceFactory.setDefaultRequestProperties(headers);
|
|
480
|
-
}
|
|
571
|
+
if (player == null) return;
|
|
481
572
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
.createMediaSource(MediaItem.fromUri(uri));
|
|
573
|
+
final int steps = Math.max(1, (int) (fadeOutDurationMs / FADE_DELAY_MS));
|
|
574
|
+
final float initialVolume = player.getVolume();
|
|
575
|
+
final float fadeStep = initialVolume / steps;
|
|
486
576
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
577
|
+
fadeTask = fadeExecutor.scheduleWithFixedDelay(
|
|
578
|
+
new Runnable() {
|
|
579
|
+
float currentVolume = initialVolume;
|
|
490
580
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
581
|
+
@Override
|
|
582
|
+
public void run() {
|
|
583
|
+
if (fadeState != FadeState.FADE_OUT || player == null || currentVolume <= 0) {
|
|
584
|
+
fadeState = FadeState.NONE;
|
|
585
|
+
cancelFade();
|
|
586
|
+
owner
|
|
587
|
+
.getActivity()
|
|
588
|
+
.runOnUiThread(() -> {
|
|
589
|
+
if (player == null) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (toPause) {
|
|
593
|
+
player.setPlayWhenReady(false);
|
|
594
|
+
stopCurrentTimeUpdates();
|
|
595
|
+
} else {
|
|
596
|
+
try {
|
|
597
|
+
stop();
|
|
598
|
+
} catch (Exception e) {
|
|
599
|
+
logger.error("Error stopping stream asset after fade out", e);
|
|
600
|
+
}
|
|
500
601
|
}
|
|
602
|
+
});
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
final float nextVolume = Math.max(currentVolume - fadeStep, 0f);
|
|
607
|
+
owner
|
|
608
|
+
.getActivity()
|
|
609
|
+
.runOnUiThread(() -> {
|
|
610
|
+
if (player != null) {
|
|
611
|
+
player.setVolume(nextVolume);
|
|
501
612
|
}
|
|
502
|
-
}
|
|
503
|
-
|
|
613
|
+
});
|
|
614
|
+
currentVolume = nextVolume;
|
|
504
615
|
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
|
|
616
|
+
},
|
|
617
|
+
0,
|
|
618
|
+
FADE_DELAY_MS,
|
|
619
|
+
java.util.concurrent.TimeUnit.MILLISECONDS
|
|
620
|
+
);
|
|
508
621
|
}
|
|
509
622
|
|
|
510
623
|
@Override
|
|
@@ -512,8 +625,84 @@ public class StreamAudioAsset extends AudioAsset {
|
|
|
512
625
|
owner
|
|
513
626
|
.getActivity()
|
|
514
627
|
.runOnUiThread(() -> {
|
|
515
|
-
|
|
628
|
+
logger.debug("Setting playback rate to: " + rate);
|
|
516
629
|
player.setPlaybackParameters(new PlaybackParameters(rate));
|
|
517
630
|
});
|
|
518
631
|
}
|
|
632
|
+
|
|
633
|
+
@Override
|
|
634
|
+
protected void startCurrentTimeUpdates() {
|
|
635
|
+
logger.debug("Starting timer updates");
|
|
636
|
+
if (currentTimeHandler == null) {
|
|
637
|
+
currentTimeHandler = new Handler(Looper.getMainLooper());
|
|
638
|
+
}
|
|
639
|
+
// Reset completion status for this assetId
|
|
640
|
+
dispatchedCompleteMap.put(assetId, false);
|
|
641
|
+
|
|
642
|
+
// Wait for player to be truly ready
|
|
643
|
+
currentTimeHandler.postDelayed(
|
|
644
|
+
new Runnable() {
|
|
645
|
+
@Override
|
|
646
|
+
public void run() {
|
|
647
|
+
if (player != null && player.getPlaybackState() == Player.STATE_READY) {
|
|
648
|
+
startTimeUpdateLoop();
|
|
649
|
+
} else {
|
|
650
|
+
// Check again in 100ms
|
|
651
|
+
currentTimeHandler.postDelayed(this, 100);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
100
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
private void startTimeUpdateLoop() {
|
|
660
|
+
currentTimeRunnable = new Runnable() {
|
|
661
|
+
@Override
|
|
662
|
+
public void run() {
|
|
663
|
+
try {
|
|
664
|
+
boolean isPaused = false;
|
|
665
|
+
if (player != null && player.getPlaybackState() == Player.STATE_READY) {
|
|
666
|
+
if (player.isPlaying()) {
|
|
667
|
+
double currentTime = player.getCurrentPosition() / 1000.0; // Get time directly
|
|
668
|
+
logger.debug("Play timer update: currentTime = " + currentTime);
|
|
669
|
+
if (owner != null) owner.notifyCurrentTime(assetId, currentTime);
|
|
670
|
+
currentTimeHandler.postDelayed(this, 100);
|
|
671
|
+
return;
|
|
672
|
+
} else if (!player.getPlayWhenReady()) {
|
|
673
|
+
isPaused = true;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
logger.debug("Stopping play timer - not playing or not ready");
|
|
677
|
+
stopCurrentTimeUpdates();
|
|
678
|
+
if (isPaused) {
|
|
679
|
+
logger.verbose("Playback is paused, not dispatching complete");
|
|
680
|
+
} else {
|
|
681
|
+
logger.verbose("Playback is stopped, dispatching complete");
|
|
682
|
+
dispatchComplete();
|
|
683
|
+
}
|
|
684
|
+
} catch (Exception e) {
|
|
685
|
+
logger.error("Error getting current time", e);
|
|
686
|
+
stopCurrentTimeUpdates();
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
try {
|
|
691
|
+
if (currentTimeHandler == null) {
|
|
692
|
+
currentTimeHandler = new Handler(Looper.getMainLooper());
|
|
693
|
+
}
|
|
694
|
+
currentTimeHandler.post(currentTimeRunnable);
|
|
695
|
+
} catch (Exception e) {
|
|
696
|
+
logger.error("Error starting current time updates", e);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
@Override
|
|
701
|
+
void stopCurrentTimeUpdates() {
|
|
702
|
+
logger.debug("Stopping play timer updates");
|
|
703
|
+
if (currentTimeHandler != null) {
|
|
704
|
+
currentTimeHandler.removeCallbacks(currentTimeRunnable);
|
|
705
|
+
currentTimeHandler = null;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
519
708
|
}
|