Private
Public Access
Recover from playback stalls; cap quality over Bluetooth
On the Galaxy Mini, Wi-Fi and Bluetooth audio share one radio: streams starve and the media player then sits at the same position forever while showing playing. After 10 s without progress, recover like a network failure (reconnect and resume, then download first, then skip). New setting: quality over Bluetooth (default 192 kbps) to leave the radio airtime for the speaker. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a610b090f0
commit
9440536fa1
5 files changed
+38
-18
No files matched your search
@@ -199,7 +199,7 @@
|
||||
<string name="sort_played">Most played</string>
|
||||
<string name="sort_title">Title</string>
|
||||
<string name="sort_year">Year (newest)</string>
|
||||
<string name="stalled">Playback is stuck. Try pausing and playing again.</string>
|
||||
<string name="stalled">Playback got stuck: the network isn\'t keeping up.</string>
|
||||
<string name="starting_x">Starting %1$s…</string>
|
||||
<string name="stream_interrupted">Stream interrupted</string>
|
||||
<string name="surprise_me">Surprise me</string>
|
||||
@@ -236,4 +236,6 @@
|
||||
<string name="unloved">Removed from favourites</string>
|
||||
<string name="up_next">Queue</string>
|
||||
<string name="you">you</string>
|
||||
<string name="quality_bt_title">Quality over Bluetooth</string>
|
||||
<string name="set_quality_bt">Quality over Bluetooth: %1$s</string>
|
||||
</resources>
|
||||
@@ -199,7 +199,7 @@
|
||||
<string name="sort_played">Máis escoitados</string>
|
||||
<string name="sort_title">Título</string>
|
||||
<string name="sort_year">Ano (máis recentes)</string>
|
||||
<string name="stalled">A reprodución quedou parada. Proba a pausar e reproducir de novo.</string>
|
||||
<string name="stalled">A reprodución quedou parada: a rede non dá abasto.</string>
|
||||
<string name="starting_x">Iniciando %1$s…</string>
|
||||
<string name="stream_interrupted">Interrompeuse a transmisión</string>
|
||||
<string name="surprise_me">Sorpréndeme</string>
|
||||
@@ -236,4 +236,6 @@
|
||||
<string name="unloved">Quitada de favoritas</string>
|
||||
<string name="up_next">Cola</string>
|
||||
<string name="you">ti</string>
|
||||
<string name="quality_bt_title">Calidade por Bluetooth</string>
|
||||
<string name="set_quality_bt">Calidade por Bluetooth: %1$s</string>
|
||||
</resources>
|
||||
@@ -296,12 +296,20 @@ public class App extends Application {
|
||||
|
||||
public int qualityHome() { return prefs.getInt("qHome", 0); }
|
||||
public int qualityAway() { return prefs.getInt("qAway", 192); }
|
||||
/** Cap while the sound goes to a Bluetooth speaker (0 = none). Wi-Fi and Bluetooth share this phone's radio:
|
||||
* less to download leaves the Bluetooth link the airtime it needs, so it doesn't stutter. */
|
||||
public int qualityBluetooth() { return prefs.getInt("qBt", 192); }
|
||||
|
||||
/** Max kbps for the current situation; 0 = original. */
|
||||
public int currentMaxKbps() {
|
||||
boolean away = server == null || !server.isLocal() || onMobileData();
|
||||
int q = away ? qualityAway() : qualityHome();
|
||||
if (server != null && server.isRelay() && (q == 0 || q > 192)) q = 192; // relay is bandwidth-capped
|
||||
int bt = qualityBluetooth();
|
||||
if (bt > 0 && (q == 0 || q > bt)) {
|
||||
android.media.AudioManager am = (android.media.AudioManager) getSystemService(AUDIO_SERVICE);
|
||||
if (am != null && am.isBluetoothA2dpOn()) q = bt;
|
||||
}
|
||||
return q;
|
||||
}
|
||||
|
||||
|
||||
@@ -435,6 +435,7 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
final String[] rows = {
|
||||
str(R.string.set_quality_home, qualityLabel(app.qualityHome())),
|
||||
str(R.string.set_quality_away, qualityLabel(app.qualityAway())),
|
||||
str(R.string.set_quality_bt, qualityLabel(app.qualityBluetooth())),
|
||||
str(R.string.set_dl_quality, dlQualityLabel(Downloads.get().quality())),
|
||||
str(R.string.set_delete_downloads, Downloads.size(Downloads.get().bytesOnDisk())),
|
||||
str(R.string.set_download_first, str(app.downloadFirst() ? R.string.on : R.string.off)),
|
||||
@@ -454,21 +455,22 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
switch (which) {
|
||||
case 0: pickQuality("qHome", app.qualityHome(), str(R.string.quality_home_title)); break;
|
||||
case 1: pickQuality("qAway", app.qualityAway(), str(R.string.quality_away_title)); break;
|
||||
case 2: pickDownloadQuality(); break;
|
||||
case 3: confirmDeleteDownloads(); break;
|
||||
case 4:
|
||||
case 2: pickQuality("qBt", app.qualityBluetooth(), str(R.string.quality_bt_title)); break;
|
||||
case 3: pickDownloadQuality(); break;
|
||||
case 4: confirmDeleteDownloads(); break;
|
||||
case 5:
|
||||
app.prefs.edit().putBoolean("downloadFirst", !app.downloadFirst()).commit();
|
||||
toast(str(R.string.download_first_toast, str(app.downloadFirst() ? R.string.on : R.string.off)));
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
Server.preferSecureAtHome = !Server.preferSecureAtHome;
|
||||
app.prefs.edit().putBoolean("secureHome", Server.preferSecureAtHome).commit();
|
||||
if (app.server != null) app.server.invalidate();
|
||||
toast(str(Server.preferSecureAtHome ? R.string.https_home_on : R.string.https_home_off));
|
||||
break;
|
||||
case 6: startActivity(new Intent(BaseActivity.this, SetupActivity.class).putExtra(SetupActivity.EXTRA_MODE, SetupActivity.MODE_LIBRARY)); break;
|
||||
case 7: startActivity(new Intent(BaseActivity.this, SetupActivity.class).putExtra(SetupActivity.EXTRA_MODE, SetupActivity.MODE_SERVER)); break;
|
||||
case 8:
|
||||
case 7: startActivity(new Intent(BaseActivity.this, SetupActivity.class).putExtra(SetupActivity.EXTRA_MODE, SetupActivity.MODE_LIBRARY)); break;
|
||||
case 8: startActivity(new Intent(BaseActivity.this, SetupActivity.class).putExtra(SetupActivity.EXTRA_MODE, SetupActivity.MODE_SERVER)); break;
|
||||
case 9:
|
||||
if (app.server != null) {
|
||||
app.server.invalidate();
|
||||
toast(str(R.string.reconnecting));
|
||||
@@ -480,10 +482,10 @@ public abstract class BaseActivity extends Activity implements PlayerService.Lis
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 9: showDiagnostics(); break;
|
||||
case 10: pickLanguage(); break;
|
||||
case 11: pickSize(); break;
|
||||
case 12: confirmSignOut(); break;
|
||||
case 10: showDiagnostics(); break;
|
||||
case 11: pickLanguage(); break;
|
||||
case 12: pickSize(); break;
|
||||
case 13: confirmSignOut(); break;
|
||||
}
|
||||
}
|
||||
}).show();
|
||||
|
||||
@@ -146,7 +146,8 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
private boolean pausedByCall;
|
||||
private boolean pausedByRoute; // the Bluetooth speaker dropped: resume if it's back soon
|
||||
private long routeLostAt;
|
||||
private boolean btOn; // audio was going to Bluetooth (A2DP), as last seen while playing
|
||||
private boolean btOn;
|
||||
private long failedAtPos = -1; // where the last failure/stall was recovered from // audio was going to Bluetooth (A2DP), as last seen while playing
|
||||
private WifiManager.WifiLock wifiLock;
|
||||
private boolean noisyRegistered;
|
||||
private boolean foreground;
|
||||
@@ -489,6 +490,7 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
}
|
||||
|
||||
private void trackFailed(String msg) {
|
||||
failedAtPos = Math.max(currentPosition(), lastPos);
|
||||
com.minamp.net.DiagLog.log("track failed: " + msg + (refusal != null ? " / " + refusal : "") + " (retry " + (retries + 1) + ")");
|
||||
long pos = Math.max(currentPosition(), lastPos);
|
||||
if (refusal != null) msg = refusal; // the server's own reason beats "Playback error (1/-1004)"
|
||||
@@ -627,15 +629,19 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList
|
||||
if (state == PLAYING && track != null) {
|
||||
long pos = currentPosition();
|
||||
lastPos = pos;
|
||||
// "playing" but not moving for 12 s: say so instead of staying silent
|
||||
// "playing" but not moving: the stream starved (Wi-Fi stalled, often because Bluetooth audio
|
||||
// shares the radio) and the media player waits forever. Recover like a network error:
|
||||
// reconnect and resume here; then download the track first; then move on.
|
||||
if (pos != stallPos) {
|
||||
stallPos = pos;
|
||||
stallSince = now;
|
||||
stallWarned = false;
|
||||
} else if (!stallWarned && now - stallSince > 12000) {
|
||||
// playing smoothly again for a while: a later stall starts the recovery steps afresh
|
||||
if (retries > 0 && failedAtPos >= 0 && pos - failedAtPos > 30000) { retries = 0; failedAtPos = -1; }
|
||||
} else if (!stallWarned && now - stallSince > 10000) {
|
||||
stallWarned = true;
|
||||
com.minamp.net.DiagLog.log("playback stalled at " + pos / 1000 + "s");
|
||||
toast(getString(R.string.stalled));
|
||||
com.minamp.net.DiagLog.log("playback stalled at " + pos / 1000 + "s" + (btOn ? " (Bluetooth)" : "") + ": recovering");
|
||||
trackFailed(getString(R.string.stalled));
|
||||
}
|
||||
if (!scrobbled && track.duration > 0 && pos > track.duration * 9 / 10) scrobble(track);
|
||||
if (now - lastTimeline > 10000) report(false);
|
||||
|
||||
Reference in new issue
Block a user