Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for frameTimeout option #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Parameter | Description
--- | ---
`isLive` | Whether or not the stream should be loaded continuously
`timeout` | HTTP Timeout when fetching the MJPEG stream
`frameTimeout` | If no frame is received for this duration, the stream will be reloaded. This should only be used together with `isLive` as there's no point in waiting for new frames in a stream that's not live.
`width` | Force width
`height` | Force height
`error` | Error builder used when an error occurred
Expand Down
43 changes: 39 additions & 4 deletions lib/src/mjpeg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class Mjpeg extends HookWidget {
final double? height;
final bool isLive;
final Duration timeout;
final Duration? frameTimeout;
final WidgetBuilder? loading;
final Client? httpClient;
final Widget Function(BuildContext contet, dynamic error, dynamic stack)?
Expand All @@ -55,6 +56,7 @@ class Mjpeg extends HookWidget {
this.isLive = false,
this.width,
this.timeout = const Duration(seconds: 5),
this.frameTimeout,
this.height,
this.fit,
required this.stream,
Expand All @@ -78,6 +80,7 @@ class Mjpeg extends HookWidget {
isLive && visible.visible,
headers,
timeout,
frameTimeout,
httpClient ?? Client(),
preprocessor ?? MjpegPreprocessor(),
isMounted,
Expand All @@ -87,9 +90,10 @@ class Mjpeg extends HookWidget {
isLive,
visible.visible,
timeout,
frameTimeout,
httpClient,
preprocessor,
isMounted
isMounted,
]);
final key = useMemoized(() => UniqueKey(), [manager]);

Expand Down Expand Up @@ -153,26 +157,55 @@ class _StreamManager {
final String stream;
final bool isLive;
final Duration _timeout;
final Duration? _frameTimeout;
final Map<String, String> headers;
final Client _httpClient;
final MjpegPreprocessor _preprocessor;
final bool Function() _mounted;
// ignore: cancel_subscriptions
StreamSubscription? _subscription;
Timer? _frameTimeoutTimer;

_StreamManager(this.stream, this.isLive, this.headers, this._timeout,
this._httpClient, this._preprocessor, this._mounted);
_StreamManager(
this.stream,
this.isLive,
this.headers,
this._timeout,
this._frameTimeout,
this._httpClient,
this._preprocessor,
this._mounted,
);

Future<void> dispose() async {
if (_subscription != null) {
await _subscription!.cancel();
_subscription = null;
}
_frameTimeoutTimer?.cancel();
_httpClient.close();
}

void _resetFrameTimeoutTimer(
BuildContext context,
ValueNotifier<MemoryImage?> image,
ValueNotifier<List<dynamic>?> errorState,
) {
if (_frameTimeout == null) return;

_frameTimeoutTimer?.cancel();
_frameTimeoutTimer = Timer(_frameTimeout!, () {
errorState.value = null;
updateStream(context, image, errorState);
});
}

void _sendImage(BuildContext context, ValueNotifier<MemoryImage?> image,
ValueNotifier<dynamic> errorState, List<int> chunks) async {
ValueNotifier<List<dynamic>?> errorState, List<int> chunks) async {
// If frame timeout timer is active and we are expecting new frames,
// reset the timer on each frame received.
_resetFrameTimeoutTimer(context, image, errorState);

// pass image through preprocessor sending to [Image] for rendering
final List<int>? imageData = _preprocessor.process(chunks);
if (imageData == null) return;
Expand Down Expand Up @@ -258,6 +291,8 @@ class _StreamManager {
image.value = null;
}
}
} finally {
_resetFrameTimeoutTimer(context, image, errorState);
}
}
}