feat: implemented onStreamedFrameAvailable
diff --git a/packages/camera/camera_web/lib/src/camera.dart b/packages/camera/camera_web/lib/src/camera.dart
index 210a0df..be01768 100644
--- a/packages/camera/camera_web/lib/src/camera.dart
+++ b/packages/camera/camera_web/lib/src/camera.dart
@@ -230,20 +230,17 @@
 
   /// Captures a picture and returns the saved file in a JPEG format.
   ///
-  /// Enables the camera flash (torch mode) for a period of taking a picture
-  /// if the flash mode is either [FlashMode.auto] or [FlashMode.always].
-  Future<XFile> takePicture() async {
-    final bool shouldEnableTorchMode =
-        flashMode == FlashMode.auto || flashMode == FlashMode.always;
-
-    if (shouldEnableTorchMode) {
-      _setTorchMode(enabled: true);
-    }
-
+  /// The picture might be mirrored horizontally depending on the chosen camera.
+  ///
+  /// **NOTE**: It doesn't enable the camera flash (torch mode) when taking the picture.
+  /// If you want so, consider using [takePicture].
+  Future<html.Blob> _takePicture() async {
     final int videoWidth = videoElement.videoWidth;
     final int videoHeight = videoElement.videoHeight;
-    final html.CanvasElement canvas =
-        html.CanvasElement(width: videoWidth, height: videoHeight);
+    final html.CanvasElement canvas = html.CanvasElement(
+      width: videoWidth,
+      height: videoHeight,
+    );
     final bool isBackCamera = getLensDirection() == CameraLensDirection.back;
 
     // Flip the picture horizontally if it is not taken from a back camera.
@@ -255,8 +252,22 @@
 
     canvas.context2D
         .drawImageScaled(videoElement, 0, 0, videoWidth, videoHeight);
+    return await canvas.toBlob('image/jpeg');
+  }
 
-    final html.Blob blob = await canvas.toBlob('image/jpeg');
+  /// Captures a picture and returns the saved file in a JPEG format.
+  ///
+  /// Enables the camera flash (torch mode) for a period of taking a picture
+  /// if the flash mode is either [FlashMode.auto] or [FlashMode.always].
+  Future<XFile> takePicture() async {
+    final bool shouldEnableTorchMode =
+        flashMode == FlashMode.auto || flashMode == FlashMode.always;
+
+    if (shouldEnableTorchMode) {
+      _setTorchMode(enabled: true);
+    }
+
+    final html.Blob blob = await _takePicture();
 
     if (shouldEnableTorchMode) {
       _setTorchMode(enabled: false);
@@ -578,6 +589,36 @@
     return _videoAvailableCompleter!.future;
   }
 
+  late final StreamController<CameraImageData> _cameraFrameStreamController =
+      StreamController<CameraImageData>.broadcast();
+
+  /// Returns a stream of camera frames.
+  ///
+  /// To stop listening to new animation frames close all listening streams.
+  Stream<CameraImageData> cameraFrameStream({
+    CameraImageStreamOptions? options,
+  }) {
+    final Stream<CameraImageData> stream = _cameraFrameStreamController.stream;
+    window!.requestAnimationFrame(_onAnimationFrame);
+    return stream;
+  }
+
+  /// Called when a new animation frame is available.
+  Future<void> _onAnimationFrame([num? _]) async {
+    final html.Blob picture = await _takePicture();
+    print('picture taken at ${DateTime.now()}');
+    final CameraImageData cameraImageData =
+        _cameraService.getCameraImageDataFromBlob(
+      picture,
+      width: videoElement.videoWidth,
+      height: videoElement.videoHeight,
+    );
+    _cameraFrameStreamController.add(cameraImageData);
+
+    if (_cameraFrameStreamController.hasListener)
+      window!.requestAnimationFrame(_onAnimationFrame);
+  }
+
   /// Disposes the camera by stopping the camera stream,
   /// the video recording and reloading the camera source.
   Future<void> dispose() async {
diff --git a/packages/camera/camera_web/lib/src/camera_service.dart b/packages/camera/camera_web/lib/src/camera_service.dart
index 5f4a5fd..ed24101 100644
--- a/packages/camera/camera_web/lib/src/camera_service.dart
+++ b/packages/camera/camera_web/lib/src/camera_service.dart
@@ -336,4 +336,20 @@
         return DeviceOrientation.portraitUp;
     }
   }
+
+  CameraImageData getCameraImageDataFromBlob(
+    html.Blob blob, {
+    required int height,
+    required int width,
+  }) {
+    return CameraImageData(
+      format: const CameraImageFormat(
+        ImageFormatGroup.jpeg,
+        raw: '',
+      ),
+      planes: [],
+      height: height,
+      width: width,
+    );
+  }
 }
diff --git a/packages/camera/camera_web/lib/src/camera_web.dart b/packages/camera/camera_web/lib/src/camera_web.dart
index 26f965d..a6ec84c 100644
--- a/packages/camera/camera_web/lib/src/camera_web.dart
+++ b/packages/camera/camera_web/lib/src/camera_web.dart
@@ -689,4 +689,11 @@
       ),
     );
   }
+
+  @override
+  Stream<CameraImageData> onStreamedFrameAvailable(
+    int cameraId, {
+    CameraImageStreamOptions? options,
+  }) =>
+      getCamera(cameraId).cameraFrameStream(options: options);
 }