Closed snobear closed 5 years ago
From what I understand, the above code should unmount OTPublisher
and mount OTSubscriber
if you were to toggle this.state.isPerformer
from true to false. But after the toggle, the publisher-turned-subscriber still seems to be publishing. Or at least the mic has stayed on and is causing audio feedback.
Let me know if you have any thoughts on this. I will see if I can provide a sample git repo on the issue so you can reproduce.
@msach22 in general, if I wanted to switch from a publisher to a subscriber in the same session, would I have to totally disconnect the current session as a publisher (with a publisher token) and reconnect as a subscriber (with a subscriber token)?
@snobear Apologies for the delay, you can pass in publisher event handlers to the OTPublisher
component to know when the publisher stream has been destroyed:
this.pubEvents = {
streamCreated: event => {
console.log('pub streamCreated', event);
},
streamDestroyed: event => {
console.log('pub streamDestroyed', event);
}
}
You can then mount the subscriber component when you know the publisher stream has been destroyed. There was a bug in the library where the streamDestroyed
event was not firing for the publisher, but this has been fixed as of v0.9.4
. Please use the latest library to check out the fix.
You don't have to disconnect
the client because then you would have to reconnect them to make them a subscriber. I recommend trying out the approach above - the unmounting of the publisher should unpublish the publisher.
if that doesn't work out, I'd love to get on a call with you and think of a better solution then having to disconnect and reconnect.
Thanks for the reply. I’ve actually tried switching pub/sub type while still connected and by attempting to terminate the entire OTSession first then reconnecting as the new type, but neither works.
I added some logging statements in src/OTPublisher.js
and can confirm the componentWillUnmount is properly called and the publisher destroy routine is called without error. But when attempting to reconnect as a subscriber the stream never comes though. I’m still digging into it to get a better idea.
Will try to reproduce this in a simpler example using a clone of the basic video chat sample late next week, and then maybe we can hop on a call to discuss.
@snobear Thanks for the update. Are you using the playground tool as the remote person?
No I haven’t tried the playground tool, thanks for pointing me to that. I’ve been testing with a directly connected device and the iOS simulator in tandem.
Happy to share - the playground tool will make it easier to test because you can test all sorts of features (archiving, broadcasting, etc)
Hi @msach22,
I was able to reproduce what I'm seeing with a simple example, as seen below. Perhaps my understanding or expectations of OT are incorrect, so please give it a shot and we can hop on a call if you'd like to go through it.
Add your settings to the CHANGE ME
section. I specifically generated and used subscriber and publisher type tokens for this.subscriberToken
and this.publisherToken
, respectively. I tested only on iOS in the simulator and Opentok playground tool for this example, but I saw the same on physical iOS devices when working in my own project.
# App.js
import React, { Component } from 'react';
import { View, Button } from 'react-native';
import { OTSession, OTPublisher, OTSubscriber } from 'opentok-react-native';
export default class App extends Component {
constructor(props) {
super(props);
//**** CHANGE ME
this.apiKey = '';
this.sessionId = '';
this.subscriberToken = '';
this.publisherToken = '';
//****
this.state = {
isPublisher: false,
token: this.subscriberToken
};
// Opentok handlers
this.sessionEventHandlers = {
error: error => {
console.log('Opentok session error',error);
},
sessionConnected: () => {
console.log('Opentok session connected');
},
sessionReconnected: () => {
console.log('Opentok session sessionReconnected');
},
sessionReconnecting: () => {
console.log('Opentok session sessionReconnecting...');
},
sessionDisconnected: () => {
console.log('Opentok session disconnected');
},
streamCreated: event => {
console.log('Opentok session stream created',event);
},
streamDestroyed: event => {
console.log('Opentok session stream streamDestroyed',event);
},
connectionCreated: event => {
console.log('Opentok session connectionCreated',event);
},
connectionDestroyed: event => {
console.log('Opentok session connectionCreated',event);
},
connectionCreated: () => {
console.log('Opentok session connectionCreated');
},
connectionCreated: () => {
console.log('Opentok session connectionCreated');
},
connectionCreated: () => {
console.log('Opentok session connectionCreated');
},
signal: event => {
console.log('Opentok session signal',event);
},
};
this.subscriberEventHandlers = {
error: (error) => {
console.log('Subscriber error',error);
}
};
this.publisherEventHandlers = {
error: (error) => {
console.log('Publisher error',error);
},
streamCreated: event => {
console.log('Publisher stream created!', event);
},
streamDestroyed: event => {
console.log('Publisher stream destroyed!', event);
}
};
}
togglePubSub = () => {
let _token = null;
if (this.state.isPublisher) {
_token = this.subscriberToken;
} else {
_token = this.publisherToken;
}
this.setState({
isPublisher: !this.state.isPublisher,
token: _token
});
}
render() {
if (this.state.isPublisher) {
console.log('I am currently a Publisher');
} else {
console.log('I am currently a Subscriber');
}
return (
<View style={{ flex: 1, flexDirection: 'row' }}>
<OTSession
apiKey={this.apiKey}
sessionId={this.sessionId}
token={this.state.token}
eventHandlers={this.sessionEventHandlers}>
{ this.state.isPublisher
? <OTPublisher style={{ width: 300, height: 300 }} eventHandlers={this.publisherEventHandlers} />
: <OTSubscriber style={{ width: 300, height: 300 }} eventHandlers={this.subscriberEventHandlers} />
}
</OTSession>
<View style={{ position: 'absolute', bottom: 100, left:100, borderWidth: 1, borderColor: '#000' }}>
<Button
onPress={this.togglePubSub}
title={ this.state.isPublisher ? 'Switch to subscriber' : 'Switch to publisher' }
color="#000"
/>
</View>
</View>
);
}
}
Before you start, join the session in the playground tool in your browser and publish your stream to help troubleshoot. We'll refer to this as "the playground user".
Attempt 1: Start as a subscriber
isPublisher: false
and use the subscriberToken in the constructor, and it should start as a subscriber with only OTSubscriber
being mounted. You should see the playground user's stream.Switch to publisher
button...and here is where it breaks down. Expected Results when clicking the switch button:
Actual results:
Attempt 2: Start as a publisher
isPublisher: true
and use the publisherToken in the constructor, and it should start as a publisher with only OTPublisher
being mounted. You should see your stream (teapot).Switch to subscriber
button...and here is where it breaks down...Expected Results when clicking the switch button:
Actual results:
TLDR; try the above App.js
and see if it works as you'd expect :). @msach22 lets hop on a call and discuss further if you need help understanding the above book.
OK, I added some event handlers and just did some testing without any additional users, i.e. not using playground. When initializing the above example app as a subscriber, then switching to publisher, I get the following OTPublisher error
:
code: 1500
message: "Unable to Publish."
According to the docs, error code 1500 is:
Unable to Publish. The client's token does not have the role set to publish or moderator. Once the client has connected to the session, the capabilities property of the Session object lists the client's capabilities.
Even though the App.js component rerenders (after this.state.token
and this.state.isPublisher
is updated to publisher settings), it doesn't appear that OTSession is ever updated to use the new token.
I just tested having all users initialize the app component with a publisher token, and this has the switching working successfully 👍. Any downside to using a publisher token for everyone? In our application, anyone can become a publisher at any time, so I guess this is OK. I just figured having a user be a subscriber when they are not publishing anything is a safer method so we're not accidentally publishing audio/video for them.
Pretty sure I had tested using pub tokens for all users in our app, but was still hitting the issue of a subscriber's audio feeding back because the mic was still on. I'll do some debugging and hopefully figure out the (dumb) mistake. Keep you posted.
@snobear Thanks for the detailed sample app, reproduction steps, and what you tried. One key thing to note is that when you change the token from publisher to subscriber token, the library is not creating a new session with the updated token. Think of it as an instance that cannot change, the only way to change the token is to unmount the OTSession
component and mount it again with the new token.
Using the publisher token does not have any implications even if they are not publishing. The tokens are just used for define "permissions". In this case, the users will be able to publish and subscribe only if the code allows it. The token itself doesn't have the ability to do that.
Closing, as using the publisher token for everyone solved my main issue.
Re: the mic feedback, I have only had it happen a few times, so if it happens again in a way that I can reproduce I'll file a new issue. Thanks @msach22 !
It was great chatting with you @snobear!
@msach22 great talking with you as well! Glad we hopped on a call, it was extremely helpful!
i am sorry, i have a question for that, i want to make app in android with react native 1 to many like a bigo app, example : publisher can make 1 room to start live streaming and other user in other android can click and join to that room, the question is how we make the room with session id opentok ? or you have any idea for this ? thank you, sorry for my bad english i hope you understand what i mean
@andikare the sample App.js I posted above ^ should get you started.
To get an API key you need to sign up on tokbox.com. Then you can create sessions and tokens from the toxbox.com account page for testing.
In my app, I have a session ID for each "room". Users get new tokens whenever they join a room. As mentioned, you can generate session IDs and tokens via tokbox.com, but you'll eventually need to roll your own server-side code to generate these for your project. See these docs:
https://tokbox.com/developer/guides/create-session/ https://tokbox.com/developer/guides/create-token/
We use Firebase Cloud Functions (nodejs) for that, but you can use any of the language SDKs mentioned in the docs.
Hope that helps.
@snobear Thank you sir, i was create and make session with node.js, i have question again, Do subcriber tokens and publisher tokens have to be changed every time they enter the room or not? and in https://tokbox.com/developer/guides/create-session/ it is token for subcriber or token publisher ? because just show 1 token in there,
thank you
You will want to reuse the session ID that you create for the room. For our application, we store the session ID along with the room data in our database. Users must connect to the same session ID, so its important that all users use and reference the same session ID. There is no relation for subscriber vs publisher when you are talking about session ID.
However, for subscriber and publisher tokens, Opentok recommends that you issue a new token every time a user connects to the room/session:
Tokens are cheap to generate. They are generated just with a hashing function and your secret. There is no API call to our servers used when generating a token. We recommend:
- Generating a new token for every user at the time they try to connect. Tokens have an expiration time, which by default is 24 hours after the token is created. After the expiration time, you cannot use the token to connect to the session.
- Not storing tokens or trying to reuse them.
- Using connection data to identify users. Connection data is a secure way to store information about your users (such as a user ID, which can help you identify your users in your application).
thankyou sirr @snobear i have session id with nodejs and look like this { "apiKey": "**", "sessionId": "2_MX40NjI1Mzk3Mn5-MTU0OTQ0**", "token": "T1==cGFydG5lcl9pZD00NjI1Mzk3MiZzaWc9YjMwMjIxNTAzNjNkNzBkMTlhMDA1YjNhODhhMDA3NTQzNDQwMmI4ZTpzZXNzaW9uX2lkPTJfTVg0ME5qSTFNemszTW41LU1UVTBPVFEwTnpJek1UUTBOWDVVVVhrM1VtZzRTVlpTZEhObU9VUnVlVUZUUTA1RGIyaC1mZyZjcmVhdGVfdGltZT0xNTQ5NDQ3MjMyJm5vbmNlPTAuODk3Nzc4NjM1Mjg4MzU0OCZyb2xlPXB1Ymxpc2hlciZleHBpcmVfdGltZT0xNTQ5NTMzNjMyJmluaXRpYWxfbGF5b3V0X2NsYXNzX2xpc3Q9" }
The text json above shows a token, question is what token it is ? its publisher token or subcriber token ? thank you for your time
Hi @andikare, I have no idea just looking at it. But don’t waste your time trying to figure it out; instead, you should just generate new tokens. You will specify publisher or subscriber when generating the token, so the token type is whatever you’ve requested. I believe “subscriber” is the default if you don’t explicitly specify the type. I would recommend reading up on the docs linked above first. Also, please open a new Github issue so we can keep this one specific to the original question. Thanks 🙂
@snobear thank you sirr, i was trying to make that token to subcriber and publisher and still work , thank you for your answer , if i have the question again can i email you ?
thank you again, you are so kind 👍
Hi, I'm building a broadcast one-to-many style app. At any given time, a user can become a "performer" aka publisher in Opentok terms, and everyone else a subscriber. There is a queue of performers, so once a performer is done, they become a subscriber and the next person in the queue becomes the sole performer.
The issue I'm having is that when a user goes from publisher to subscriber, there is a bunch of feedback from the microphone because they are still "seen" as a publisher. I assumed at first that with the following code, the
OTPublisher
would be unmounted andOTSubscriber
mounted in its place:Per the docs, this is not the case:
So that makes sense as to the issue I'm seeing. So my question is, how can I "switch" the user from publisher to subscriber? Is there a way to unpublish a user first?
The only thing I could think of was to have some setState that first hides then shows
OTSession
, but that could get ugly. Any ideas? Ideally, the switch should be as seamless as possible.