Skip to content

webrtc p2p connection video freezes #1437

@azazadev

Description

@azazadev

in p2p connection when I'm using RTCVideoView for both local and remote user it's working fine.

Now I have the scenario when I want to replace the RTCVideoView local widget by the CameraPreview ( needed for Machine learning scenario ), the p2p connection is established but local video is freezes

any idea how we can override the RTCVideoView local video by my own camera view widget ?

This is a full main.dart example when we can reproduce issue easy :

How to reproduce :

  1. run main on Device 1
  2. run main on Device 2
  3. copy the connection ID of Device 1 on the TextField of Device 2
  4. click on connect

Observed : Remote video working fine but local video freezes

import 'dart:math';

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart' as flutter_webrtc;
import 'package:peerdart/peerdart.dart';

late List<CameraDescription> _cameras;

Future<void> main() async {
  try {
    WidgetsFlutterBinding.ensureInitialized();
    _cameras = await availableCameras();
  } on CameraException catch (e) {}
  runApp(MaterialApp(
    home: WebRTCP2PDemo(cameras: _cameras),
  ));
}

class WebRTCP2PDemo extends StatefulWidget {
  final List<CameraDescription>? cameras;
  const WebRTCP2PDemo({Key? key, required this.cameras}) : super(key: key);

  @override
  _WebRTCP2PDemoState createState() => _WebRTCP2PDemoState();
}

class _WebRTCP2PDemoState extends State<WebRTCP2PDemo> {
  final TextEditingController _controller = TextEditingController();
  late Peer peer = Peer(options: PeerOptions(debug: LogLevel.All), id: GeneratePeerId());
  final _remoteRenderer = flutter_webrtc.RTCVideoRenderer();
  bool inCall = false;
  CameraController? _cameraController;
  String? peerId;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          SizedBox(
            height: 50,
          ),
          _renderState(),
          const Text(
            'Connection ID:',
          ),
          SelectableText(
            peerId ?? '',
            style: TextStyle(fontSize: 48),
          ),
          TextField(
            textAlign: TextAlign.center,
            keyboardType: TextInputType.number,
            controller: _controller,
            style: TextStyle(fontSize: 48),
          ),
          ElevatedButton(onPressed: _connect, child: const Text("connect")),
          inCall
              ? Expanded(
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(width: 3.0, color: Colors.black38),
                        color: Colors.transparent,
                        borderRadius: BorderRadius.all(Radius.circular(5.0)),
                      ),
                      child: SizedBox(
                        height: MediaQuery.of(context).size.height / 4,
                        width: MediaQuery.of(context).size.width,
                        child: flutter_webrtc.RTCVideoView(
                          _remoteRenderer,
                        ),
                      ),
                    ),
                  ),
                )
              : Expanded(
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: Container(
                      decoration: BoxDecoration(
                        border: Border.all(width: 3.0, color: Colors.black38),
                        color: Colors.transparent,
                        borderRadius: BorderRadius.all(Radius.circular(5.0)),
                      ),
                      child: SizedBox(
                        height: MediaQuery.of(context).size.height / 4,
                        width: MediaQuery.of(context).size.width,
                        child: Center(
                          child: Text(
                            'waiting for remote user ...',
                            textAlign: TextAlign.center,
                            style: TextStyle(
                              fontSize: 28,
                            ),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Container(
                decoration: BoxDecoration(
                  border: Border.all(width: 3.0, color: Colors.black38),
                  color: Colors.transparent,
                  borderRadius: BorderRadius.all(Radius.circular(5.0)),
                ),
                child: SizedBox(
                  height: MediaQuery.of(context).size.height / 3,
                  width: MediaQuery.of(context).size.width,
                  child: CameraPreview(_cameraController!),
                ),
              ),
            ),
          ),
        ],
      )),
    );
  }

  Future initCamera(CameraDescription cameraDescription) async {
    _cameraController = CameraController(cameraDescription, ResolutionPreset.high);
    try {
      await _cameraController?.initialize().then((_) {
        if (!mounted) return;
        setState(() {});
      });
    } on CameraException catch (e) {
      debugPrint("camera error $e");
    }
  }

  Widget _renderState() {
    Color bgColor = inCall ? Colors.green : Colors.grey;
    Color txtColor = Colors.white;
    String txt = inCall ? "Connected" : "Standby";
    return Container(
      decoration: BoxDecoration(color: bgColor),
      child: Text(
        txt,
        style: Theme.of(context).textTheme.titleLarge?.copyWith(color: txtColor),
      ),
    );
  }

  String GeneratePeerId() {
    Random random = Random();
    int min = 100000;
    int max = 999999;
    int randomNumber = min + random.nextInt(max - min);
    return randomNumber.toString();
  }

  @override
  void initState() {
    super.initState();
    initCamera(widget.cameras![1]);
    peer.on("open").listen((id) {
      setState(() {
        peerId = peer.id;
      });
    });
    _remoteRenderer.initialize();
    peer.on<MediaConnection>("call").listen(
      (call) async {
        final mediaStream = await flutter_webrtc.navigator.mediaDevices.getUserMedia({
          'video': {
            'facingMode': 'user',
          },
          "audio": false
        });
        call.answer(mediaStream);
        call.on("close").listen((event) {
          setState(() {
            inCall = false;
          });
        });
        call.on<flutter_webrtc.MediaStream>("stream").listen((event) {
          _remoteRenderer.srcObject = event;
          setState(() {
            inCall = true;
          });
        });
      },
    );
  }

  void _connect() async {
    FocusManager.instance.primaryFocus?.unfocus();
    final mediaStream = await flutter_webrtc.navigator.mediaDevices.getUserMedia({
      'video': {
        'facingMode': 'user',
      },
      "audio": false
    });
    final conn = peer.call(_controller.text, mediaStream);

    conn.on("close").listen((event) {
      setState(() {
        inCall = false;
      });
    });

    conn.on<flutter_webrtc.MediaStream>("stream").listen((event) {
      _remoteRenderer.srcObject = event;
      setState(() {
        inCall = true;
      });
    });
  }

  @override
  void dispose() {
    peer.dispose();
    _cameraController?.dispose();
    _remoteRenderer.dispose();
    super.dispose();
  }
}

(base) ➜ ~ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.13.1, on macOS 13.1 22C65 darwin-arm64, locale en-FR)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 4.0)
[✓] Android Studio (version 2021.1)
[✓] IntelliJ IDEA Community Edition (version 2022.3.1)
[✓] VS Code (version 1.79.2)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions