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

Error: TypeError: Instance of 'LinkedMap<String, dynamic>': type 'LinkedMap<String, dynamic>' is not a subtype of type 'JSObject' #1564

Open
tolotrasamuel opened this issue Apr 13, 2024 · 8 comments

Comments

@tolotrasamuel
Copy link

tolotrasamuel commented Apr 13, 2024

[Describe the bug

Create PeerConnection with configuration: {iceServers: [{urls: [stun:stun1.l.google.com:19302, stun:stun2.l.google.com:19302]}]}
Error: TypeError: Instance of 'LinkedMap<String, dynamic>': type 'LinkedMap<String, dynamic>' is not a subtype of type 'JSObject'
dart-sdk/lib/internal/js_dev_runtime/private/ddc_runtime/errors.dart 297:3 throw
dart-sdk/lib/_internal/js_shared/lib/rti.dart 1385:3 _failedAsCheck
dart-sdk/lib/_internal/js_shared/lib/rti.dart 1363:3 _generalAsCheckImplementation
packages/dart_webrtc/src/factory_impl.dart 45:39

Error: TypeError: Instance of 'LinkedMap<String, dynamic>': type 'LinkedMap<String, dynamic>' is not a subtype of type 'JSObject'

Error trace back to this line

final jsRtcPc = web.RTCPeerConnection(
        {...constr, ...configuration} as web.RTCConfiguration);

To Reproduce

This happens when createPeerConnection is called


import 'dart:convert';

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';

typedef void StreamStateCallback(MediaStream stream);

class Signaling {
  Map<String, dynamic> configuration = {
    'iceServers': [
      {
        'urls': [
          'stun:stun1.l.google.com:19302',
          'stun:stun2.l.google.com:19302'
        ]
      }
    ]
  };

  RTCPeerConnection? peerConnection;
  MediaStream? localStream;
  MediaStream? remoteStream;
  String? roomId;
  String? currentRoomText;
  StreamStateCallback? onAddRemoteStream;

  Future<String> createRoom(RTCVideoRenderer remoteRenderer) async {
    FirebaseFirestore db = FirebaseFirestore.instance;
    DocumentReference roomRef = db.collection('rooms').doc();

    print('Create PeerConnection with configuration: $configuration');

    peerConnection = await createPeerConnection(configuration);

    registerPeerConnectionListeners();

    localStream?.getTracks().forEach((track) {
      peerConnection?.addTrack(track, localStream!);
    });

    // Code for collecting ICE candidates below
    var callerCandidatesCollection = roomRef.collection('callerCandidates');

    peerConnection?.onIceCandidate = (RTCIceCandidate candidate) {
      print('Got candidate: ${candidate.toMap()}');
      callerCandidatesCollection.add(candidate.toMap());
    };
    // Finish Code for collecting ICE candidate

    // Add code for creating a room
    RTCSessionDescription offer = await peerConnection!.createOffer();
    await peerConnection!.setLocalDescription(offer);
    print('Created offer: $offer');

    Map<String, dynamic> roomWithOffer = {'offer': offer.toMap()};

    await roomRef.set(roomWithOffer);
    var roomId = roomRef.id;
    print('New room created with SDK offer. Room ID: $roomId');
    currentRoomText = 'Current room is $roomId - You are the caller!';
    // Created a Room

    peerConnection?.onTrack = (RTCTrackEvent event) {
      print('Got remote track: ${event.streams[0]}');

      event.streams[0].getTracks().forEach((track) {
        print('Add a track to the remoteStream $track');
        remoteStream?.addTrack(track);
      });
    };

    // Listening for remote session description below
    roomRef.snapshots().listen((snapshot) async {
      print('Got updated room: ${snapshot.data()}');

      Map<String, dynamic> data = snapshot.data() as Map<String, dynamic>;
      if (peerConnection?.getRemoteDescription() != null &&
          data['answer'] != null) {
        var answer = RTCSessionDescription(
          data['answer']['sdp'],
          data['answer']['type'],
        );

        print("Someone tried to connect");
        await peerConnection?.setRemoteDescription(answer);
      }
    });
    // Listening for remote session description above

    // Listen for remote Ice candidates below
    roomRef.collection('calleeCandidates').snapshots().listen((snapshot) {
      snapshot.docChanges.forEach((change) {
        if (change.type == DocumentChangeType.added) {
          Map<String, dynamic> data = change.doc.data() as Map<String, dynamic>;
          print('Got new remote ICE candidate: ${jsonEncode(data)}');
          peerConnection!.addCandidate(
            RTCIceCandidate(
              data['candidate'],
              data['sdpMid'],
              data['sdpMLineIndex'],
            ),
          );
        }
      });
    });
    // Listen for remote ICE candidates above

    return roomId;
  }

  Future<void> joinRoom(String roomId, RTCVideoRenderer remoteVideo) async {
    FirebaseFirestore db = FirebaseFirestore.instance;
    print(roomId);
    DocumentReference roomRef = db.collection('rooms').doc('$roomId');
    var roomSnapshot = await roomRef.get();
    print('Got room ${roomSnapshot.exists}');

    if (roomSnapshot.exists) {
      print('Create PeerConnection with configuration: $configuration');
      peerConnection = await createPeerConnection(configuration);

      registerPeerConnectionListeners();

      localStream?.getTracks().forEach((track) {
        peerConnection?.addTrack(track, localStream!);
      });

      // Code for collecting ICE candidates below
      var calleeCandidatesCollection = roomRef.collection('calleeCandidates');
      peerConnection!.onIceCandidate = (RTCIceCandidate? candidate) {
        if (candidate == null) {
          print('onIceCandidate: complete!');
          return;
        }
        print('onIceCandidate: ${candidate.toMap()}');
        calleeCandidatesCollection.add(candidate.toMap());
      };
      // Code for collecting ICE candidate above

      peerConnection?.onTrack = (RTCTrackEvent event) {
        print('Got remote track: ${event.streams[0]}');
        event.streams[0].getTracks().forEach((track) {
          print('Add a track to the remoteStream: $track');
          remoteStream?.addTrack(track);
        });
      };

      // Code for creating SDP answer below
      var data = roomSnapshot.data() as Map<String, dynamic>;
      print('Got offer $data');
      var offer = data['offer'];
      await peerConnection?.setRemoteDescription(
        RTCSessionDescription(offer['sdp'], offer['type']),
      );
      var answer = await peerConnection!.createAnswer();
      print('Created Answer $answer');

      await peerConnection!.setLocalDescription(answer);

      Map<String, dynamic> roomWithAnswer = {
        'answer': {'type': answer.type, 'sdp': answer.sdp}
      };

      await roomRef.update(roomWithAnswer);
      // Finished creating SDP answer

      // Listening for remote ICE candidates below
      roomRef.collection('callerCandidates').snapshots().listen((snapshot) {
        snapshot.docChanges.forEach((document) {
          var data = document.doc.data() as Map<String, dynamic>;
          print(data);
          print('Got new remote ICE candidate: $data');
          peerConnection!.addCandidate(
            RTCIceCandidate(
              data['candidate'],
              data['sdpMid'],
              data['sdpMLineIndex'],
            ),
          );
        });
      });
    }
  }

  Future<void> openUserMedia(
      RTCVideoRenderer localVideo,
      RTCVideoRenderer remoteVideo,
      ) async {
    var stream = await navigator.mediaDevices
        .getUserMedia({'video': true, 'audio': false});

    localVideo.srcObject = stream;
    localStream = stream;

    remoteVideo.srcObject = await createLocalMediaStream('key');
  }

  Future<void> hangUp(RTCVideoRenderer localVideo) async {
    List<MediaStreamTrack> tracks = localVideo.srcObject!.getTracks();
    tracks.forEach((track) {
      track.stop();
    });

    if (remoteStream != null) {
      remoteStream!.getTracks().forEach((track) => track.stop());
    }
    if (peerConnection != null) peerConnection!.close();

    if (roomId != null) {
      var db = FirebaseFirestore.instance;
      var roomRef = db.collection('rooms').doc(roomId);
      var calleeCandidates = await roomRef.collection('calleeCandidates').get();
      calleeCandidates.docs.forEach((document) => document.reference.delete());

      var callerCandidates = await roomRef.collection('callerCandidates').get();
      callerCandidates.docs.forEach((document) => document.reference.delete());

      await roomRef.delete();
    }

    localStream!.dispose();
    remoteStream?.dispose();
  }

  void registerPeerConnectionListeners() {
    peerConnection?.onIceGatheringState = (RTCIceGatheringState state) {
      print('ICE gathering state changed: $state');
    };

    peerConnection?.onConnectionState = (RTCPeerConnectionState state) {
      print('Connection state change: $state');
    };

    peerConnection?.onSignalingState = (RTCSignalingState state) {
      print('Signaling state change: $state');
    };

    peerConnection?.onIceGatheringState = (RTCIceGatheringState state) {
      print('ICE connection state change: $state');
    };

    peerConnection?.onAddStream = (MediaStream stream) {
      print("Add remote stream");
      onAddRemoteStream?.call(stream);
      remoteStream = stream;
    };
  }
}

Expected behavior

It works with the exact same code in version 0.9.27

Platform information

  • Flutter version:
    3.19.3
    [✓] Flutter (Channel stable, 3.19.3, on macOS 14.0 23A344 darwin-arm64, locale en-US)
    [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    [✓] Xcode - develop for iOS and macOS (Xcode 15.0.1)
    [✓] Chrome - develop for the web
    [✓] Android Studio (version 2023.1)
    [✓] Connected device (3 available)
    [✓] Network resources

  • Plugin version:

flutter_webrtc: ^0.10.3

  • OS:
    Web
  • OS version:
    Macos
@cloudwebrtc
Copy link
Member

It has been fixed in dart-webrtc 1.4.1. Please delete pubspec.lock under your project and re-run flutter pub get. This issue will be fixed.

https://github.com/flutter-webrtc/dart-webrtc/releases/tag/1.4.1

@Thasln
Copy link

Thasln commented Apr 13, 2024

It has been fixed in dart-webrtc 1.4.1. Please delete pubspec.lock under your project and re-run flutter pub get. This issue will be fixed.

https://github.com/flutter-webrtc/dart-webrtc/releases/tag/1.4.1

This issue same as mine. But the code using flutter_webrtc instead of dart_webrtc. I have done what you've said but it still don't working. Also when im using dart_webrtc, i cant use some methods like RTCVideoRenderer. I dont know what should i do. Can you help.

@cloudwebrtc
Copy link
Member

Hey, you don't need to change to dart-webrtc. In fact, flutter-webrtc depends on dart 1.4.0 and above, but when you run flutter pub get for the first time, pubspec.lock should be locked to 1.4.0, but when You delete pubspec.lock and run flutter pub get again, it should be updated to 1.4.1

https://github.com/flutter-webrtc/flutter-webrtc/blob/main/pubspec.yaml#L11

@mitch2na
Copy link

I am getting this same error when running dart-webrtc 1.4.1

Looks like you reverted the mapper in 1.4.0 to this:

final jsRtcPc = web.RTCPeerConnection(
        {...constr, ...configuration} as web.RTCConfiguration);

but back in dart-webrtc 1.3.0 it was this:

final jsRtcPc = web.RTCPeerConnection(
        jsify({...constr, ...configuration}) as web.RTCConfiguration);

@SaloxiddinTursunaliev
Copy link

cloudwebrtc

This didn't work

@lingguox
Copy link

我也遇到了,需要帮忙解决,谢谢。
errors.dart:297 Uncaught (in promise) Error: TypeError: Instance of 'LinkedMap<String, dynamic>': type 'LinkedMap<String, dynamic>' is not a subtype of type 'JSObject'
at Object.throw_ [as throw] (errors.dart:297:3)
at Object._failedAsCheck (rti.dart:1385:3)
at dart_rti.Rti.new._generalAsCheckImplementation (rti.dart:1363:3)
at createPeerConnection (factory_impl.dart:45:39)
at createPeerConnection.next ()
at runBody (async_patch.dart:84:54)
at Object._async [as async] (async_patch.dart:127:5)
at factory_impl.RTCFactoryWeb._internal.createPeerConnection (factory_impl.dart:33:49)
at Object.createPeerConnection (factory_impl.dart:90:8)

@lingguox
Copy link

lingguox commented Apr 18, 2024

fixed!已经搞定!
不要替换换PUB_HOSTED_URL环境变量,要继续使用https://pub.dev。是版本兼容问题,flutter 3.19.5升级dart-webrtc到1.4.3

pubspec.yaml:
name: flutter_hello
description: "A new Flutter project."

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 1.0.0+1

environment:
sdk: '>=3.3.3 <4.0.0'

dependencies:
flutter:
sdk: flutter

cupertino_icons: ^1.0.2
http: ^1.2.1
flutter_webrtc: ^0.10.3
path_provider: ^2.0.2
web_socket_channel: ^2.2.0
fluttertoast: ^8.2.4
permission_handler: ^10.2.0
sdp_transform: ^0.3.2

dev_dependencies:
flutter_test:
sdk: flutter
pedantic: ^1.11.0
flutter_lints: ^3.0.0

flutter:

uses-material-design: true

@lingguox
Copy link

pub get后自动升级了dart_webrtc
pubspec.lock:

dart_webrtc:
dependency: transitive
description:
name: dart_webrtc
sha256: ef3ba75d13e8b8d9949bf4389de4d5dc5d9914faae8004f59b44fa0c15889d9e
url: "https://pub.dev"
source: hosted
version: "1.4.3"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants