Skip to content

Commit

Permalink
Add timeoutMillis parameter to Uri/Stream AudioSources (including And…
Browse files Browse the repository at this point in the history
…roid impl change to respect it)
  • Loading branch information
Cris committed Dec 26, 2024
1 parent e0e2b19 commit 21fcb63
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 24 deletions.
5 changes: 5 additions & 0 deletions just_audio/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.9.44

* Add timeoutMillis parameter to Uri/LockCaching/Stream AudioSources (@ctedgar).
* Android platform implementation completed

## 0.9.43

* Fix NPE in load on iOS/macOS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -615,20 +615,20 @@ private MediaSource decodeAudioSource(final Object json) {
String id = (String)map.get("id");
switch ((String)map.get("type")) {
case "progressive":
return new ProgressiveMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers")), buildExtractorsFactory(mapGet(map, "options")))
return new ProgressiveMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis")), buildExtractorsFactory(mapGet(map, "options")))
.createMediaSource(new MediaItem.Builder()
.setUri(Uri.parse((String)map.get("uri")))
.setTag(id)
.build());
case "dash":
return new DashMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers")))
return new DashMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis")))
.createMediaSource(new MediaItem.Builder()
.setUri(Uri.parse((String)map.get("uri")))
.setMimeType(MimeTypes.APPLICATION_MPD)
.setTag(id)
.build());
case "hls":
return new HlsMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers")))
return new HlsMediaSource.Factory(buildDataSourceFactory(mapGet(map, "headers"), mapGet(map, "timeoutMillis")))
.createMediaSource(new MediaItem.Builder()
.setUri(Uri.parse((String)map.get("uri")))
.setMimeType(MimeTypes.APPLICATION_M3U8)
Expand Down Expand Up @@ -709,7 +709,7 @@ private void clearAudioEffects() {
audioEffectsMap.clear();
}

private DataSource.Factory buildDataSourceFactory(Map<?, ?> headers) {
private DataSource.Factory buildDataSourceFactory(Map<?, ?> headers, Integer timeoutMillis) {
final Map<String, String> stringHeaders = castToStringMap(headers);
String userAgent = null;
if (stringHeaders != null) {
Expand All @@ -727,6 +727,11 @@ private DataSource.Factory buildDataSourceFactory(Map<?, ?> headers) {
if (stringHeaders != null && stringHeaders.size() > 0) {
httpDataSourceFactory.setDefaultRequestProperties(stringHeaders);
}
//Default for both timeout setting is 8 seconds, so using timeoutMillis to control both
if(timeoutMillis!=null){
httpDataSourceFactory.setConnectTimeoutMs(timeoutMillis);
httpDataSourceFactory.setReadTimeoutMs(timeoutMillis);
}
return new DefaultDataSource.Factory(context, httpDataSourceFactory);
}

Expand Down
63 changes: 45 additions & 18 deletions just_audio/lib/just_audio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -681,20 +681,21 @@ class AudioPlayer {
/// This is equivalent to:
///
/// ```
/// setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers, tag: tag),
/// setAudioSource(AudioSource.uri(Uri.parse(url), headers: headers, timeoutMillis: timeoutMillis, tag: tag),
/// initialPosition: Duration.zero, preload: true);
/// ```
///
/// See [setAudioSource] for a detailed explanation of the options.
Future<Duration?> setUrl(
String url, {
Map<String, String>? headers,
int? timeoutMillis,
Duration? initialPosition,
bool preload = true,
dynamic tag,
}) =>
setAudioSource(
AudioSource.uri(Uri.parse(url), headers: headers, tag: tag),
AudioSource.uri(Uri.parse(url), headers: headers, timeoutMillis: timeoutMillis, tag: tag),
initialPosition: initialPosition,
preload: preload);

Expand Down Expand Up @@ -2095,6 +2096,7 @@ class _ProxyHttpServer {
uri,
headers: headers,
userAgent: source._player?._userAgent,
timeoutMillis: source.timeoutMillis
);
return uri.replace(
scheme: 'http',
Expand Down Expand Up @@ -2232,17 +2234,20 @@ abstract class AudioSource {
/// provided by that package. If you wish to have more control over the tag
/// for background audio purposes, consider using the plugin audio_service
/// instead of just_audio_background.
///
/// [timeoutMillis] can be used to adjust http connection timeout settings
/// (currently only supported for Android)
static UriAudioSource uri(Uri uri,
{Map<String, String>? headers, dynamic tag}) {
{Map<String, String>? headers, int? timeoutMillis, dynamic tag}) {
bool hasExtension(Uri uri, String extension) =>
uri.path.toLowerCase().endsWith('.$extension') ||
uri.fragment.toLowerCase().endsWith('.$extension');
if (hasExtension(uri, 'mpd')) {
return DashAudioSource(uri, headers: headers, tag: tag);
return DashAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag);
} else if (hasExtension(uri, 'm3u8')) {
return HlsAudioSource(uri, headers: headers, tag: tag);
return HlsAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag);
} else {
return ProgressiveAudioSource(uri, headers: headers, tag: tag);
return ProgressiveAudioSource(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag);
}
}

Expand Down Expand Up @@ -2329,9 +2334,10 @@ abstract class IndexedAudioSource extends AudioSource {
abstract class UriAudioSource extends IndexedAudioSource {
final Uri uri;
final Map<String, String>? headers;
final int? timeoutMillis;
Uri? _overrideUri;

UriAudioSource(this.uri, {this.headers, dynamic tag, Duration? duration})
UriAudioSource(this.uri, {this.headers, this.timeoutMillis, dynamic tag, Duration? duration})
: super(tag: tag, duration: duration);

/// If [uri] points to an asset, this gives us [_overrideUri] which is the URI
Expand Down Expand Up @@ -2423,12 +2429,16 @@ abstract class UriAudioSource extends IndexedAudioSource {
///
/// If headers are set, just_audio will create a cleartext local HTTP proxy on
/// your device to forward HTTP requests with headers included.
///
/// [timeoutMillis] can be used to adjust http connection timeout settings
/// (currently only supported for Android)
class ProgressiveAudioSource extends UriAudioSource {
final ProgressiveAudioSourceOptions? options;

ProgressiveAudioSource(
super.uri, {
super.headers,
super.timeoutMillis,
super.tag,
super.duration,
this.options,
Expand All @@ -2439,6 +2449,7 @@ class ProgressiveAudioSource extends UriAudioSource {
id: _id,
uri: _effectiveUri.toString(),
headers: _mergedHeaders,
timeoutMillis: timeoutMillis,
tag: tag,
options: options?._toMessage(),
);
Expand All @@ -2458,16 +2469,20 @@ class ProgressiveAudioSource extends UriAudioSource {
///
/// If headers are set, just_audio will create a cleartext local HTTP proxy on
/// your device to forward HTTP requests with headers included.
///
/// [timeoutMillis] can be used to adjust http connection timeout settings
/// (currently only supported for Android)
class DashAudioSource extends UriAudioSource {
DashAudioSource(Uri uri,
{Map<String, String>? headers, dynamic tag, Duration? duration})
: super(uri, headers: headers, tag: tag, duration: duration);
{Map<String, String>? headers, int? timeoutMillis, dynamic tag, Duration? duration})
: super(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag, duration: duration);

@override
AudioSourceMessage _toMessage() => DashAudioSourceMessage(
id: _id,
uri: _effectiveUri.toString(),
headers: _mergedHeaders,
timeoutMillis: timeoutMillis,
tag: tag,
);
}
Expand All @@ -2485,16 +2500,20 @@ class DashAudioSource extends UriAudioSource {
///
/// If headers are set, just_audio will create a cleartext local HTTP proxy on
/// your device to forward HTTP requests with headers included.
///
/// [timeoutMillis] can be used to adjust http connection timeout settings
/// (currently only supported for Android)
class HlsAudioSource extends UriAudioSource {
HlsAudioSource(Uri uri,
{Map<String, String>? headers, dynamic tag, Duration? duration})
: super(uri, headers: headers, tag: tag, duration: duration);
{Map<String, String>? headers, int? timeoutMillis, dynamic tag, Duration? duration})
: super(uri, headers: headers, timeoutMillis: timeoutMillis, tag: tag, duration: duration);

@override
AudioSourceMessage _toMessage() => HlsAudioSourceMessage(
id: _id,
uri: _effectiveUri.toString(),
headers: _mergedHeaders,
timeoutMillis: timeoutMillis,
tag: tag,
);
}
Expand Down Expand Up @@ -2815,7 +2834,8 @@ Uri _encodeDataUrl(String base64Data, String mimeType) =>
@experimental
abstract class StreamAudioSource extends IndexedAudioSource {
Uri? _uri;
StreamAudioSource({dynamic tag}) : super(tag: tag);
int? timeoutMillis;
StreamAudioSource({this.timeoutMillis, dynamic tag}) : super(tag: tag);

@override
Future<void> _setup(AudioPlayer player) async {
Expand All @@ -2839,7 +2859,7 @@ abstract class StreamAudioSource extends IndexedAudioSource {

@override
AudioSourceMessage _toMessage() => ProgressiveAudioSourceMessage(
id: _id, uri: _uri.toString(), headers: null, tag: tag);
id: _id, uri: _uri.toString(), headers: null, timeoutMillis: timeoutMillis, tag: tag);
}

/// The response for a [StreamAudioSource]. This API is experimental.
Expand Down Expand Up @@ -2903,11 +2923,12 @@ class LockCachingAudioSource extends StreamAudioSource {
LockCachingAudioSource(
this.uri, {
this.headers,
int? timeoutMillis,
File? cacheFile,
dynamic tag,
}) : cacheFile =
cacheFile != null ? Future.value(cacheFile) : _getCacheFile(uri),
super(tag: tag) {
super(tag: tag, timeoutMillis: timeoutMillis) {
_init();
}

Expand Down Expand Up @@ -2991,7 +3012,7 @@ class LockCachingAudioSource extends StreamAudioSource {
File getEffectiveCacheFile() =>
partialCacheFile.existsSync() ? partialCacheFile : cacheFile;

final httpClient = _createHttpClient(userAgent: _player?._userAgent);
final httpClient = _createHttpClient(userAgent: _player?._userAgent, timeoutMillis: timeoutMillis);
final httpRequest = await _getUrl(httpClient, uri, headers: headers);
final response = await httpRequest.close();
if (response.statusCode != 200) {
Expand Down Expand Up @@ -3106,7 +3127,7 @@ class LockCachingAudioSource extends StreamAudioSource {
_requests.remove(request);
final start = request.start!;
final end = request.end ?? sourceLength;
final httpClient = _createHttpClient(userAgent: _player?._userAgent);
final httpClient = _createHttpClient(userAgent: _player?._userAgent, timeoutMillis: timeoutMillis);

final rangeRequest = _HttpRangeRequest(start, end);
_getUrl(httpClient, uri, headers: {
Expand Down Expand Up @@ -3331,11 +3352,12 @@ _ProxyHandler _proxyHandlerForUri(
Uri uri, {
Map<String, String>? headers,
String? userAgent,
int? timeoutMillis,
}) {
// Keep redirected [Uri] to speed-up requests
Uri? redirectedUri;
Future<void> handler(_ProxyHttpServer server, HttpRequest request) async {
final client = _createHttpClient(userAgent: userAgent);
final client = _createHttpClient(userAgent: userAgent, timeoutMillis: timeoutMillis);
// Try to make normal request
String? host;
try {
Expand Down Expand Up @@ -4022,10 +4044,15 @@ Future<HttpClientRequest> _getUrl(HttpClient client, Uri uri,
return request;
}

HttpClient _createHttpClient({String? userAgent}) {
HttpClient _createHttpClient({String? userAgent, int? timeoutMillis}) {
final client = HttpClient();
if (userAgent != null) {
client.userAgent = userAgent;
}
if (timeoutMillis!=null){
var connectivityTimeouts = Duration(milliseconds: timeoutMillis);
client.idleTimeout = connectivityTimeouts;
client.connectionTimeout = connectivityTimeouts;
}
return client;
}
4 changes: 2 additions & 2 deletions just_audio/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: just_audio
description: A feature-rich audio player for Flutter. Loop, clip and concatenate any sound from any source (asset/file/URL/stream) in a variety of audio formats with gapless playback.
version: 0.9.43
version: 0.9.44
repository: https://github.com/ryanheise/just_audio/tree/minor/just_audio
issue_tracker: https://github.com/ryanheise/just_audio/issues
topics:
Expand All @@ -14,7 +14,7 @@ environment:
flutter: ">=3.10.0"

dependencies:
just_audio_platform_interface: ^4.3.0
just_audio_platform_interface: ^4.3.1
# just_audio_platform_interface:
# path: ../just_audio_platform_interface
just_audio_web: ^0.4.11
Expand Down
18 changes: 18 additions & 0 deletions just_audio/test/just_audio_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,20 @@ void runTests() {
exception = e;
}
expect(exception != null, equals(true));
try {
await player.setUrl('https://foo.foo/timeout.mp3');
exception = null;
} catch (e) {
exception = e;
}
expect(exception != null, equals(true));
try {
await player.setUrl('https://foo.foo/timeout.mp3', timeoutMillis: 11);
exception = null;
} catch (e) {
exception = e;
}
expect(exception == null, equals(true));
await player.dispose();
});

Expand Down Expand Up @@ -1479,6 +1493,10 @@ class MockAudioPlayer extends AudioPlayerPlatform {
throw PlatformException(code: '404', message: 'Not found');
} else if (audioSource.uri.contains('error')) {
throw PlatformException(code: 'error', message: 'Unknown error');
} else if (audioSource.uri.contains('timeout')){
if ((audioSource.timeoutMillis ?? 0) < 10) {
throw PlatformException(code: 'timeout', message: 'Timed out (because timeoutMillis setting is less than 10)');
}
}
_duration = audioSourceDuration;
} else if (audioSource is ClippingAudioSourceMessage) {
Expand Down

0 comments on commit 21fcb63

Please sign in to comment.