apivideo / api.video-flutter-live-stream

Flutter RTMP live stream client. Made with ♥ by api.video
MIT License
62 stars 37 forks source link

PlatformException(failed_to_create_live_stream, java.security.InvalidParameterException: Failed to find a single FPS range that contains 30, null, null) #21

Closed Saleque474 closed 1 year ago

Saleque474 commented 1 year ago

Here is the code


import 'package:apivideo_live_stream/apivideo_live_stream.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class Stream extends StatefulWidget {
  const Stream({super.key});

  @override
  State<Stream> createState() => _StreamState();
}

class _StreamState extends State<Stream> with WidgetsBindingObserver {
  final LiveStreamController controller = LiveStreamController();
  late final Future<int> textureId;

  @override
  void initState() {
    WidgetsBinding.instance.addObserver(this);
    initStream();
    super.initState();
  }

  initStream() async {
    textureId = controller.create(
        initialAudioConfig: AudioConfig(),
        initialVideoConfig: VideoConfig.withDefaultBitrate(
            resolution: Resolution.RESOLUTION_480));
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.inactive) {
      controller.stop();
    } else if (state == AppLifecycleState.resumed) {
      controller.startPreview();
    }
  }

  @override
  void dispose() {
    controller.stop();
    super.dispose();
  }

  final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey<ScaffoldState>();

  // a() async {
  //   await controller.startStreaming(
  //     streamKey: "",
  //     url: "rtmp://10.0.2.2:1935/mypush",
  //   );
  // }

  Widget _controlRowWidget() {
    final LiveStreamController? liveStreamController = controller;

    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      mainAxisSize: MainAxisSize.max,
      children: <Widget>[
        IconButton(
          icon: const Icon(Icons.cameraswitch),
          color: Colors.orange,
          onPressed:
              liveStreamController != null ? onSwitchCameraButtonPressed : null,
        ),
        IconButton(
          icon: const Icon(Icons.mic_off),
          color: Colors.orange,
          onPressed: liveStreamController != null
              ? onToggleMicrophoneButtonPressed
              : null,
        ),
        IconButton(
          icon: const Icon(Icons.fiber_manual_record),
          color: Colors.red,
          onPressed:
              liveStreamController != null && !liveStreamController.isStreaming
                  ? onStartStreamingButtonPressed
                  : null,
        ),
        IconButton(
          icon: const Icon(Icons.stop),
          color: Colors.red,
          onPressed:
              liveStreamController != null && liveStreamController.isStreaming
                  ? onStopStreamingButtonPressed
                  : null,
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        key: scaffoldKey,
        floatingActionButton: FloatingActionButton(onPressed: () async {}),
        appBar: AppBar(
          title: const Text("Stream"),
        ),
        body: Column(
          children: [
            _controlRowWidget(),
            Expanded(
              child: FutureBuilder<int>(
                  future: textureId,
                  builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
                    if (snapshot.hasError) {
                      print("Error 454545");
                      print(snapshot.error);
                    }
                    if (!snapshot.hasData) {
                      // while data is loading:
                      return Center(
                        child: CircularProgressIndicator(),
                      );
                    } else {
                      return CameraPreview(controller: controller);
                    }
                  }),
            ),
          ],
        ));
  }

  void showInSnackBar(String message) {
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text(message)));
  }

  Future<void> switchCamera() async {
    final LiveStreamController? liveStreamController = controller;

    if (liveStreamController == null) {
      showInSnackBar('Error: create a camera controller first.');
      return;
    }

    try {
      liveStreamController.switchCamera();
    } catch (error) {
      if (error is PlatformException) {
        _showDialog(
            context, "Error", "Failed to switch camera: ${error.message}");
      } else {
        _showDialog(context, "Error", "Failed to switch camera: $error");
      }
    }
  }

  Future<void> toggleMicrophone() async {
    final LiveStreamController? liveStreamController = controller;

    if (liveStreamController == null) {
      showInSnackBar('Error: create a camera controller first.');
      return;
    }

    try {
      liveStreamController.toggleMute();
    } catch (error) {
      if (error is PlatformException) {
        _showDialog(
            context, "Error", "Failed to toggle mute: ${error.message}");
      } else {
        _showDialog(context, "Error", "Failed to toggle mute: $error");
      }
    }
  }

  Future<void> startStreaming() async {
    final LiveStreamController? liveStreamController = controller;

    if (liveStreamController == null) {
      showInSnackBar('Error: create a camera controller first.');
      return;
    }

    try {
      await liveStreamController.startStreaming(
          streamKey: "", url: "rtmp://10.0.2.2/live/");
    } catch (error) {
      if (error is PlatformException) {
        print("Error: failed to start stream: ${error.message}");
      } else {
        print("Error: failed to start stream: $error");
      }
    }
  }

  Future<void> stopStreaming() async {
    final LiveStreamController? liveStreamController = controller;

    if (liveStreamController == null) {
      showInSnackBar('Error: create a camera controller first.');
      return;
    }

    try {
      liveStreamController.stopStreaming();
    } catch (error) {
      if (error is PlatformException) {
        _showDialog(
            context, "Error", "Failed to stop stream: ${error.message}");
      } else {
        _showDialog(context, "Error", "Failed to stop stream: $error");
      }
    }
  }

  void onSwitchCameraButtonPressed() {
    switchCamera().then((_) {
      if (mounted) {
        setState(() {});
      }
    });
  }

  void onToggleMicrophoneButtonPressed() {
    toggleMicrophone().then((_) {
      if (mounted) {
        setState(() {});
      }
    });
  }

  void onStartStreamingButtonPressed() {
    startStreaming().then((_) {
      if (mounted) {
        setState(() {});
      }
    });
  }

  void onStopStreamingButtonPressed() {
    stopStreaming().then((_) {
      if (mounted) {
        setState(() {});
      }
    });
  }

  Future<void> _showDialog(
      BuildContext context, String title, String description) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false, // user must tap button!
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text(title),
          content: SingleChildScrollView(
            child: Text(description),
          ),
          actions: <Widget>[
            TextButton(
              child: const Text('Dismiss'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }
}
ThibaultBee commented 1 year ago

Hi,

This issue says that your camera does not support 30 fps. In your VideoConfig(), you should changefps field. On which camera do you test? which devices? In the adb logcat, you will find what the fps ranges that your camera supports. Could you provide the adb logcat?

And there is currently no way to get what is supported by each camera/device.

ThibaultBee commented 1 year ago

Waiting for new info. Reopen the issue if needed