react-native-twilio-video-webrtc icon indicating copy to clipboard operation
react-native-twilio-video-webrtc copied to clipboard

setLocalVideoEnabled disables the sound

Open wasiquehaider opened this issue 4 years ago • 12 comments

When ever I run setLocalVideoEnabled the audio also disables and on enabling the video the audio remains disabled where as setLocalAudioEnabled works perfectly.

_onMuteVideoButtonPress = () => { this.twilioRef .setLocalVideoEnabled(!this.state.isVideoEnabled) .then((isEnabled) => { this.setState({ isVideoEnabled: isEnabled }, () => { console.log("video mute", this.state.isVideoEnabled); console.log("audio mute", this.state.isAudioEnabled); }); }); };

wasiquehaider avatar Jul 24 '20 14:07 wasiquehaider

Which platform(s) have you observed this behavior?

slycoder avatar Jul 24 '20 19:07 slycoder

I'm experiencing this on Android. I literally tried everything but no luck.

wasiquehaider avatar Jul 24 '20 19:07 wasiquehaider

I wonder if this is due one of the android bugs mentioned here: https://www.twilio.com/docs/video/changelog-twilio-video-android. If so it seems like the solution will be to upgrade to the latest version of the android library. Could you try upgrading the android version in the gradle file: https://github.com/blackuy/react-native-twilio-video-webrtc/blob/ad03d0106cfa5bcc75157f8ddb31a85ba02e0640/android/build.gradle#L52

slycoder avatar Aug 03 '20 02:08 slycoder

I wonder if this is due one of the android bugs mentioned here: https://www.twilio.com/docs/video/changelog-twilio-video-android. If so it seems like the solution will be to upgrade to the latest version of the android library. Could you try upgrading the android version in the gradle file:

https://github.com/blackuy/react-native-twilio-video-webrtc/blob/ad03d0106cfa5bcc75157f8ddb31a85ba02e0640/android/build.gradle#L52

did you update this package because I did not get any success after changing the library version.

wasiquehaider avatar Aug 29 '20 19:08 wasiquehaider

On master it is now set to 5.10.0.

slycoder avatar Aug 30 '20 01:08 slycoder

Hello @slycoder i have started facing this issue on IOs now the setLocalVideoEnabled disables the video as well as the audio and does not return back to normal state after enabling the video again.

wasiquehaider avatar Sep 25 '20 12:09 wasiquehaider

Interesting. We haven't had any other reports of this issue. Is there any code you could share to reproduce the issue? Does it occur on the Example app?

slycoder avatar Oct 04 '20 03:10 slycoder

There's a definition of isVideoEnabled in the Example app but you did not use it, have a look at my code

import React, { Component } from "react";
import {
  StyleSheet,
  Text,
  TextInput,
  View,
  Button,
  Alert,
  TouchableOpacity,
  Platform,
  PermissionsAndroid,
  Image,
} from "react-native";
import styles from "./styles";
import {
  TwilioVideoLocalView,
  TwilioVideoParticipantView,
  TwilioVideo,
} from "react-native-twilio-video-webrtc";
import { connect } from "react-redux";
import _ from "lodash";
import { request } from "../../../actions/GetRoomAndToken";
import SessionHelper from "../../../helpers/SessionHelper";
import { Images, Colors } from "../../../theme";
import { ButtonView, Loading } from "../../../components";

const requestCameraPermission = async () => {
  try {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.CAMERA,
      {
        title: "App Camera Permission",
        message: "App needs access to your camera",
        buttonNegative: "Cancel",
        buttonPositive: "OK",
      }
    );
    if (granted === PermissionsAndroid.RESULTS.GRANTED) {
      console.log("You can use the camera");
    } else {
      console.log("Camera permission denied");
    }
  } catch (err) {
    console.warn(err);
  }
};

class CallModule extends Component {
  state = {
    participantJoined: false,
    isAudioEnabled: true,
    isVideoEnabled: true,
    showVideoBox: true,
    status: "disconnected",
    participants: new Map(),
    videoTracks: new Map(),
    roomName: "",
    token: "",
    isBtnDisabled: true,
  };

  componentDidMount() {
    requestCameraPermission();
    const payload = {
      user_id: this.props.user.data.user_id,
      token: SessionHelper.getAccessToken(),
      booking_id: this.props.bookingId,
    };
    this.props.request(payload);
  }
  componentDidUpdate(prevProps) {
    if (
      !_.isEqual(
        prevProps.getroomandtoken.data,
        this.props.getroomandtoken.data
      )
    ) {
      this.setState({
        roomName: this.props.getroomandtoken.data.room,
        token: this.props.getroomandtoken.data.token,
      });
    }
  }

  _onConnectButtonPress = () => {
    const { roomName, token } = this.state;

    if (roomName === "" || token === "") {
      Alert.alert("Enter both Room name and token.");
    } else {
      try {
        this.twilioRef.connect({
          roomName: this.state.roomName,
          accessToken: this.state.token,
        });
      } catch (error) {
        console.log(error);
      }
      this.setState({ status: "connecting" });
    }
  };

  _onEndButtonPress = () => {
    this.twilioRef.disconnect();
    this.setState({ status: "disconnected" });
  };

  _onMuteButtonPress = () => {
    this.twilioRef
      .setLocalAudioEnabled(!this.state.isAudioEnabled)
      .then((isEnabled) =>
        this.setState({ isAudioEnabled: isEnabled }, () => {
          console.log("video mute", this.state.isVideoEnabled);
          console.log("audio mute", this.state.isAudioEnabled);
        })
      );
  };
  _onMuteVideoButtonPress = () => {
    this.twilioRef
      .setLocalVideoEnabled(!this.state.isVideoEnabled)
      .then((isEnabled) => {
        this.setState({ isVideoEnabled: isEnabled }, () => {
          console.log("video mute", this.state.isVideoEnabled);
          console.log("audio mute", this.state.isAudioEnabled);
        });
      });
  };

  onMute = () => {
    const { videoTracks } = this.state;
    videoTracks.forEach(function (track) {
      track.disable();
    });
    this.setState({ isVideoEnabled: false });
  };
  onUnMute = () => {
    const { videoTracks } = this.state;
    videoTracks.forEach(function (track) {
      track.enable();
    });
    this.setState({ isVideoEnabled: true });
  };

  _onFlipButtonPress = () => {
    this.twilioRef.flipCamera();
  };

  _onRoomDidConnect = () => {
    this.setState({ status: "connected", isBtnDisabled: false });
  };
  _onVideoChanged = () => {
    this.setState({ showVideoBox: !this.state.showVideoBox });
  };
  _onAudioChanged = () => {
    console.log("audio toggled");
  };
  _onRoomDidConnect = () => {
    this.setState({ status: "connected", isBtnDisabled: false });
  };

  _onRoomDidDisconnect = ({ roomName, error }) => {
    console.log("ERROR: ", error);

    this.setState({ status: "disconnected" });
  };

  _onRoomDidFailToConnect = (error) => {
    console.log("ERROR: ", error);

    this.setState({ status: "disconnected" });
  };

  _onParticipantAddedVideoTrack = ({ participant, track }) => {
    console.log("onParticipantAddedVideoTrack: ", participant, track);

    this.setState({
      participantJoined: true,
      videoTracks: new Map([
        ...this.state.videoTracks,
        [
          track.trackSid,
          { participantSid: participant.sid, videoTrackSid: track.trackSid },
        ],
      ]),
    });
  };

  _onParticipantRemovedVideoTrack = ({ participant, track }) => {
    console.log("onParticipantRemovedVideoTrack: ", participant, track);

    const videoTracks = this.state.videoTracks;
    videoTracks.delete(track.trackSid);

    this.setState({
      participantJoined: false,
      videoTracks: new Map([...videoTracks]),
    });
  };

  setTwilioRef = (ref) => {
    this.twilioRef = ref;
  };

  render() {
    let loading = this.props.getroomandtoken.isFetching;

    const { status } = this.state;
    if (loading) {
      return (
        <View
          style={{ flex: 1, alignItems: "center", justifyContent: "center" }}
        >
          <Loading loading={loading} />
          <Text>Preparing Room</Text>
        </View>
      );
    }
    return (
      <View
        style={[
          styles.container,
          {
            backgroundColor: status === "disconnected" ? "#FFFFFF" : "#000000",
          },
        ]}
      >
        {this.state.status === "disconnected" && (
          <View
            style={{ flex: 1, justifyContent: "center", alignItems: "center" }}
          >
            <ButtonView
              onPress={this._onConnectButtonPress}
              style={{
                backgroundColor: "#FFFFFF",
                width: 150,
                height: 150,
                justifyContent: "center",
                alignItems: "center",
                borderRadius: 150 / 2,
                backgroundColor: "#FFF",
                shadowColor: "#000",
                shadowOffset: {
                  width: 0,
                  height: 5,
                },
                shadowOpacity: 0.22,
                shadowRadius: 2.22,
                elevation: 6,
              }}
            >
              <Image
                source={Images.video_chat_icon}
                style={{ width: 100, height: 100 }}
                resizeMode="contain"
              />
            </ButtonView>
            <Text style={styles.welcome}>Push button to initiate the call</Text>
          </View>
        )}

        {this.state.status === "connected" ||
        this.state.status === "connecting" ? (
          <View style={styles.callContainer}>
            {this.state.status === "connected" && (
              <View style={styles.remoteGrid}>
                {this.state.participantJoined ? (
                  Array.from(
                    this.state.videoTracks,
                    ([trackSid, trackIdentifier]) => {
                      return (
                        <TwilioVideoParticipantView
                          style={styles.remoteVideo}
                          key={trackSid}
                          trackIdentifier={trackIdentifier}
                        />
                      );
                    }
                  )
                ) : (
                  <View
                    style={{
                      flex: 1,
                      justifyContent: "center",
                      alignItems: "center",
                    }}
                  >
                    <Text style={{ color: "#FFFFFF", fontSize: 20 }}>
                      Participant has not joined the call yet
                    </Text>
                  </View>
                )}
              </View>
            )}
            <View style={styles.optionsContainer}>
              <TouchableOpacity
                disabled={this.state.isBtnDisabled}
                style={[styles.optionButton]}
                onPress={this._onEndButtonPress}
              >
                <Image
                  source={Images.disconnect_call}
                  style={{ width: 60, height: 60 }}
                  resizeMode="contain"
                />
              </TouchableOpacity>
              <TouchableOpacity
                disabled={this.state.isBtnDisabled}
                style={[styles.optionButton, { backgroundColor: "#a0c6ed" }]}
                onPress={this._onMuteButtonPress}
              >
                <Image
                  source={
                    this.state.isAudioEnabled
                      ? Images.mute_mic
                      : Images.unmute_mic
                  }
                  style={{ width: 30, height: 30, tintColor: "#FFFFFF" }}
                  resizeMode="contain"
                />
              </TouchableOpacity>
              <TouchableOpacity
                disabled={this.state.isBtnDisabled}
                style={[styles.optionButton, { backgroundColor: "#a0c6ed" }]}
                onPress={() => this._onMuteVideoButtonPress()}
              >
                <Image
                  source={
                    this.state.isVideoEnabled
                      ? Images.mute_video
                      : Images.unmute_video
                  }
                  style={{ width: 30, height: 30, tintColor: "#FFFFFF" }}
                  resizeMode="contain"
                />
              </TouchableOpacity>

              <TouchableOpacity
                disabled={this.state.isBtnDisabled}
                style={[styles.optionButton, { backgroundColor: "#a0c6ed" }]}
                onPress={this._onFlipButtonPress}
              >
                <Image
                  source={Images.flip_camera}
                  style={{ width: 30, height: 30, tintColor: "#FFFFFF" }}
                  resizeMode="contain"
                />
              </TouchableOpacity>

              <View />
            </View>
            {this.state.showVideoBox ? (
              <TwilioVideoLocalView
                enabled={this.state.showVideoBox}
                style={styles.localVideo}
              />
            ) : (
              <View style={styles.localVideo}></View>
            )}
          </View>
        ) : null}

        <TwilioVideo
          ref={this.setTwilioRef}
          onParticipantDisabledVideoTrack={(res, ind) => console.log(res, ind)}
          onVideoChanged={this._onVideoChanged}
          onAudioChanged={this._onAudioChanged}
          onRoomDidConnect={this._onRoomDidConnect}
          onRoomDidDisconnect={this._onRoomDidDisconnect}
          onRoomDidFailToConnect={this._onRoomDidFailToConnect}
          onParticipantAddedVideoTrack={this._onParticipantAddedVideoTrack}
          onParticipantRemovedVideoTrack={this._onParticipantRemovedVideoTrack}
        />
      </View>
    );
  }
}

const mapStateToProps = ({ user, getroomandtoken }) => ({
  user,
  getroomandtoken,
});

const actions = { request };

export default connect(mapStateToProps, actions)(CallModule);

plus the onVideoChanged and onAudioChanged does not trigger on IOS

wasiquehaider avatar Oct 04 '20 11:10 wasiquehaider

Looks like there's an inconsistency here. On iOS the result of the muting is returned via the promise whereas on android it's returned via the onVideoChanged callback. If you use only the latter for Android, do you get the correct events returned to you?

slycoder avatar Oct 04 '20 23:10 slycoder

Yes on android it triggers the events but lets not forget what issue we are facing here, the video disabling the sound one. :|

wasiquehaider avatar Oct 05 '20 06:10 wasiquehaider

Hi, this issue still persists on ios on latest library verion. Any solutions on how to fix it?

Andriiklymiuk avatar Aug 02 '21 09:08 Andriiklymiuk

I am having this issue on ios, weird. Any help?

Andriiklymiuk avatar Aug 13 '21 13:08 Andriiklymiuk