The application that i m developing is mixture of both multiparty and broadcasting. There will be a single particular admin/host who's stream is subscribed by all the subscribers(Broadcasting). Both the host/publisher as well as all the subscribers will be able to see all the participants in the session(sort of multiparty). On event from the host/publisher such as button click etc, the publisher will now stop publishing his/her stream and all the subscriber will view the stream from a particular subscriber in the session that the host selects. Since my application uses android SDK and does not have moderator token. How can i succeed creating the application i desire.(
(note: I have already completed Basic audio,video chat )
Can you provide me with necessary methods and sample code to pull it off.
I used the following codes:
private HashMap<String, Stream> mStringStreams = new HashMap<String, Stream>();
#Override
public void onStreamReceived(Session session, final Stream stream) {
Log.d(LOG_TAG, "onStreamReceived: New Stream Received " + stream.getStreamId() + " in session: " + session.getSessionId()+" Stream name: "+stream.getName());
if(stream.getName().equalsIgnoreCase("Main_stream"))
{
mSubscriber = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mSession.subscribe(mSubscriber);
mSubscriberViewContainer.addView(mSubscriber.getView());
}
if (mSubscribers.size() + 1 > MAX_NUM_SUBSCRIBERS ) {
Toast.makeText(this, "New subscriber ignored. MAX_NUM_SUBSCRIBERS limit reached.", Toast.LENGTH_LONG).show();
return;
}
if(stream.getName().compareTo("Main_stream")!=0)
{
final Subscriber subscriber = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mStringStreams.put(stream.getStreamId().toString(), stream);
mSession.subscribe(subscriber);
mSubscribers.add(subscriber);
mSubscriberStreams.put(stream,subscriber);
int position = mSubscribers.size() - 1;
final int id = getResources().getIdentifier("subscriberview" + (new Integer(position)).toString(), "id", SuscriberActivity.this.getPackageName());
FrameLayout subscriberViewContainer = (FrameLayout) findViewById(id);
subscriber.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
subscriberViewContainer.addView(subscriber.getView());
}
#Override
public void onSignalReceived(Session session, String s, String s1, Connection connection) {
stream = mSubscriberStreams.get(s1).getStream();
Stream stream= mStringStreams.get(s1);
Subscriber mSubscriber1 = new Subscriber.Builder(SuscriberActivity.this, stream).build();
mSubscriber1.setStyle(BaseVideoRenderer.STYLE_VIDEO_SCALE, BaseVideoRenderer.STYLE_VIDEO_FILL);
mSubscriberViewContainer.removeView(mPublisher.getView());
mSubscriberViewContainer.addView(mSubscriber1.getView().getRootView());
}
}
If I understand correctly, you're trying to enable participants to subscribe to a different stream when the publisher(main host) clicks on a button. To accomplish this, you can use the OpenTok Signaling API to send a signal with the streamId as the data to all participants so they can subscribe to that specific user.
Related
I have developed a voice calling app with android ConnectionServices and telecom framework.
I can show native call screen when receive incoming call notification from firebase and everything is fine.
When I start an outgoing call I just want to report this call to Connection service so that if any call from gsm received it should report busy to caller. In callkit if you call reporting Outgoing call native callkit screen doesnt appears on my app screen but android shows native call.
is it possible to make native screen invisible for outgoing calls?
my function to report outgoing call is below;
public void startCall(String number, String uuidString) {
UUID uuid = null;
if (uuidString != null) {
uuid = UUID.fromString(uuidString);
}
//TODO: allow name passed in as well
Log.d(TAG, "startCall number: $number");
Bundle extras = new Bundle();
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
Bundle callExtras = new Bundle();
callExtras.putString(Constants.EXTRA_CALL_NUMBER, number);
if (uuid != null) {
callExtras.putString(Constants.EXTRA_CALL_UUID, uuid.toString());
}
extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, voipUtilties.handle);
extras.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras);
if (ActivityCompat.checkSelfPermission(Application.context, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
Log.d("VOIP_pLuGIN", "Yetki Yok");
return;
}else{
voipUtilties.telecomManager.placeCall(uri, extras);
}
}
and overrided outgoingConnection in Service class is like below.
#Override
public Connection onCreateOutgoingConnection(PhoneAccountHandle
connectionManagerPhoneAccount, ConnectionRequest request) {
Bundle extras = request.getExtras();
Connection outgoingCallConnection = null;
String number = request.getAddress().getSchemeSpecificPart();
String extrasNumber = extras.getString(EXTRA_CALL_NUMBER);
String displayName = extras.getString(EXTRA_CALLER_NAME);
boolean isForeground = VoipConnectionService.isRunning(this.getApplicationContext());
Log.d(TAG, "makeOutgoingCall:, number: " + number + ", displayName:" + displayName);
// Wakeup application if needed
if (!isForeground) {
Log.d(TAG, "onCreateOutgoingConnection: Waking up application");
//TODO:
}
outgoingCallConnection = createConnection(request);
outgoingCallConnection.setDialing();
outgoingCallConnection.setAudioModeIsVoip(true);
outgoingCallConnection.setCallerDisplayName(displayName, TelecomManager.PRESENTATION_ALLOWED);
// ️Weirdly on some Samsung phones (A50, S9...) using `setInitialized` will not display the native UI ...
// when making a call from the native Phone application. The call will still be displayed correctly without it.
if (!Build.MANUFACTURER.equalsIgnoreCase("Samsung")) {
outgoingCallConnection.setInitialized();
}
Log.d(TAG, "onCreateOutgoingConnection: calling");
final String uuid = outgoingCallConnection.getExtras().getString(EXTRA_CALL_UUID);
setAllOthersOnHold(uuid);
final String address = outgoingCallConnection.getExtras().getString(EXTRA_CALL_NUMBER);
Log.d(TAG,"Created call's uuid" + uuid);
return outgoingCallConnection;
}
any idea will be appriciated.
I was using this package for my project. In VoipUtilities.kt Changing PhoneAccount capability to CAPABILITY_SELF_MANAGED instead of CAPABILITY_CALL_PROVIDER and in VoipconnectionService.java I added this code chunk to createConnection function solved my problem.
if((phoneAccount.getCapabilities() & PhoneAccount.CAPABILITY_SELF_MANAGED) == PhoneAccount.CAPABILITY_SELF_MANAGED) {
Log.d(TAG, "[VoiceConnectionService] PhoneAccount is SELF_MANAGED, so connection will be too");
connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED);
}
I am trying to subscribe events from IBM IoT platform.
The example I tested is from an IBM blog: https://www.ibm.com/developerworks/library/iot-mobile-phone-iot-device-bluemix-apps-trs/. In this link, the codes can be downloaded.
With this example, the Android app can successfully connect and publish events to the IBM IoT platform, but when I tried "subscribe", I always receive the error "Connection lost". What causes this problem? Thank you!
The relevant codes are:
public IMqttToken subscribeToEvent(String deviceType, String deviceId, String event, String format, int qos, Object userContext, IMqttActionListener listener) throws MqttException {
String eventTopic = "iot-2/type/" + deviceType + "/id/" + deviceId + "/evt/" + event + "/fmt/"+format;
return subscribe(eventTopic, qos, userContext, listener);
}
public static void subscribeEvent(Context context,String event) {
Log.v(TAG, ".subscribeEvent() entered")
try {
MyIoTActionListener listener = new MyIoTActionListener(context, Constants.ActionStateStatus.SUBSCRIBE);
IoTClient iotClient = IoTClient.getInstance(context);
String deviceType = "Android";
String deviceId = "...";
iotClient.subscribeToEvent(deviceType, deviceId, event, "json", 0, context,listener);
} catch (MqttException e) {
Log.d(TAG, ".SubscribeEvent() received exception on SubscribeEvent()");
}
}
The connection URL is:
String connectionURI = "tcp://" + this.getOrganization() + ".messaging.internetofthings.ibmcloud.com:1883";
ssl somehow cannot work.
This problem has been solved.
Two concepts have to be clarified: device client and application client.
A device client can only publish events and subscribe commands. In the question, I subscribe events with a device client, the IoT platform cannot response and returns the error "Connection Lost". The correct way to code is:
public IMqttToken subscribeToCommand(String command, String format, int qos, Object userContext, IMqttActionListener listener) throws MqttException {
String commandTopic = getCommandTopic(command, format);
return subscribe(commandTopic, qos, userContext, listener);
}
public static String getCommandTopic(String command, String format) {
return "iot-2/cmd/" + command + "/fmt/json";
}
public static void subscribeCommand(Context context, String command) {
try {
MyIoTActionListener listener = new MyIoTActionListener(context, Constants.ActionStateStatus.SUBSCRIBE);
IoTClient iotClient = IoTClient.getInstance(context);
iotClient.subscribeToCommand(command, "json", 0, context,listener);
} catch (MqttException e) {
Log.d(TAG, ".SubscribeCommand() received exception on SubscribeEvent()");
}
}
The example provided by IBM contains functions like subscribeEvent and publishCommand for a device client. These functions make me confused.
If you want to publish commands and subscribe events, an application client need to be created. The method to create an application client is:
import com.ibm.iotf.client.app.ApplicationClient;
Properties appProperties = new Properties();
appProperties.put("id",applicationId);
appProperties.put("Organization-ID",organizationId);
appProperties.put("Authentication-Method","apikey");
appProperties.put("API-Key",apiKey);
appProperties.put("Authentication-Token",token);
appClient = new ApplicationClient(appProperties);
appClient.connect();
See more about application client at https://github.com/ibm-watson-iot/iot-java/blob/master/src/main/java/com/ibm/iotf/client/app/ApplicationClient.java
I have a python server and about 10 android clients, using sockets. It is really important that when the server sends a message, all clients receive it at the same time (say 1/10th of a second of difference).
But the connection is over Wifi, and some devices get the message later than others, which gives a very messy result. I don't want to get the latency of every device because this is a very unreliable approach. I want something as accurate as possible.
For example, in FPS games, it is common to have a countdown at the start of the round, and every player can start playing at the same time. What kind of logic lies behind this?
As for what my code currently looks like:
I use a BufferedReader in android to read every line sent by the server. The server is a console application in which you can type a message, and when you press enter, every listed client receives it with a new thread for every client.
java method receiving messages:
private void readMessage() throws IOException {
String data;
while ((data = mBufferedReader.readLine()) != null) {
data = data.toUpperCase();
if (data.startsWith("POSITION")) {
String[] splitData = data.split("/");
Log.d(Constants.TAG, splitData[1]);
mMainActivity.setDevicePosition(Integer.parseInt(splitData[1]));
} else {
String message = data.substring(data.indexOf('/') + 1, data.length());
int devices = Integer.parseInt(data.substring(0, data.indexOf('/')));
if (message.length() >= devices) {
message += " ";
} else {
int difference = devices - message.length();
for (int i = 0; i < difference; i++) {
message += " ";
}
}
mMainActivity.printMessage(message);
}
}
}
python line :
for cl in clients_list:
start_new_thread(send_message_thread, (cl, message,))
I'm wondering if anybody can help me figure out what is causing the data I am sending to become corrupt.
My setup is currently an Arduino pro mini with a HM-10 bluetooth module connected (I have also tried HM-11 Module too) and an Android application to receive the bluetooth data.
Module setup: http://letsmakerobots.com/node/38009
If I send data with big enough intervals then the data is fine, but if I send the data continuously I see messages getting mixed up and lost. To test this I send "$0.1,0.2,0.3,0.4,0.5" to the Android application from the Arduino, sometimes the stream of data appears to send fine but other times it is really quite scrambled. Please see the below graphs that demonstrate this:
Good case:
Bad case:
Arduino code:
String inputString = ""; //Hold the incoming data.
boolean stringComplete = false; //Determines if the string is complete.
boolean realtime = false;
void setup()
{
Serial.begin(9600);
delay(500);
Serial.print("AT+START");
delay(500);
}
void loop()
{
if(stringComplete)
{
if(inputString.equals("rStart"))
{
Serial.println("$startACK");
realtime = true;
}
else if(inputString.equals("stop"))
{
Serial.println("$stopACK");
realtime = false;
}
else{
Serial.print(inputString);
}
inputString = "";
stringComplete = false;
}
if(realtime)
{
Serial.println("$0.1,0.2,0.3,0.4,0.5,0.6");
delay(10);
}
}
void serialEvent() {
while (Serial.available())
{
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n')
{
stringComplete = true;
}
else
{
inputString += inChar;
}
}
}
The Android side just receives the data and then parses it in an IntentService:
#Override
protected void onHandleIntent(Intent intent) {
//Incoming command.
String rawData = intent.getStringExtra(DataProcessingIntentService.REQUEST);
//Append our new data to our data helper.
Log.i(this.getClass().getName(), "Previous Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
DataProcessingHelper.getInstance().appendData(rawData);
Log.i(this.getClass().getName(), "New Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
commandStartIndex = DataProcessingHelper.getInstance().getData().indexOf("$");
commandEndIndex = DataProcessingHelper.getInstance().getData().indexOf("\n");
//Set this as the data starting point.
if(commandStartIndex != -1){
DataProcessingHelper.getInstance().offsetData(commandStartIndex);
}
//Ensure that a command has been found and that the end index is after the starting index.
if(commandStartIndex != -1 && commandEndIndex > commandStartIndex){
//Remove the command structure from the command.
command = DataProcessingHelper.getInstance().getData().substring(commandStartIndex+1, commandEndIndex-1); //Remove the \r\n end command.
DataProcessingHelper.getInstance().offsetData(commandEndIndex+1);
if(command.length() > 1){
//Split the data out of the comand.
splitData = command.split(",");
Log.i(this.getClass().getName(), "Broadcasting the processed data. (" + command + ")");
//Broadcast data.
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(DataProcessingIntentService.RESPONSE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(DataProcessingIntentService.RESPONSE, splitData);
sendBroadcast(broadcastIntent);
}else{
Log.e(this.getClass().getName(), "Command is less than 1 character long!");
}
}
}
Thank you for any help!
I have now figured out what was causing this problem. It appears that BLE only supports a maximum of 20 bytes per a transaction. The time between these transactions is different depending on what you are using. I'm currently using notifications which means that I can send 20 bytes every 7.5 milliseconds maximum. I have opted for 10 milliseconds to be safe. I will now need to look into breaking up packets into 20 bytes maximum to ensure no data corruption.
I am creating a generic Chromecast remote control app. Most of the guts of the app are already created and I've managed to get Chromecast volume control working (by connecting to a Chromecast device along side another app that is casting - YouTube for example).
What I've having difficult with is performing other media commands such as play, pause, seek, etc.
Use case example:
1. User opens YouTube on their android device and starts casting a video.
2. User opens my app and connects to the same Chromecast device.
3. Volume control from my app (works now)
4. Media control (play, pause, etc) (does not yet work)
I found the Cast api reference that explains that you can sendMessage(ApiClient, namespace, message) with media commands; however the "message" (JSON) requires the sessionId of the current application (Youtube in this case). I have tried the following, but the connection to the current application always fails; status.isSuccess() is always false:
Cast.CastApi
.joinApplication(mApiClient)
.setResultCallback(
new ResultCallback<Cast.ApplicationConnectionResult>() {
#Override
public void onResult(
Cast.ApplicationConnectionResult result) {
Status status = result.getStatus();
if (status.isSuccess()) {
ApplicationMetadata applicationMetadata = result
.getApplicationMetadata();
sessionId = result.getSessionId();
String applicationStatus = result
.getApplicationStatus();
boolean wasLaunched = result
.getWasLaunched();
Log.i(TAG,
"Joined Application with sessionId: "
+ sessionId
+ " Application Status: "
+ applicationStatus);
} else {
// teardown();
Log.e(TAG,
"Could not join application: "
+ status.toString());
}
}
});
Is is possible to get the sessionId of an already running cast application from a generic remote control app (like the one I am creating)? If so, am I right in my assumption that I can then perform media commands on the connected Chromecast device using something like this:
JSONObject message = new JSONObject();
message.put("mediaSessionId", sessionId);
message.put("requestId", 9999);
message.put("type", "PAUSE");
Cast.CastApi.sendMessage(mApiClient,
"urn:x-cast:com.google.cast.media", message.toString());
Update:
I have tried the recommendations provided by #Ali Naddaf but unfortunately they are not working. After creating mRemoteMediaPlayer in onCreate, I also do requestStatus(mApiClient) in the onConnected callback (in the ConnectionCallbacks). When I try to .play(mApiClient) I get an IllegalStateException stating that there is no current media session. Also, I tried doing joinApplication and in the callback performed result.getSessionId; which returns null.
A few comments and answers:
You can get the sessionId from the callback of launchApplication or joinApplication; in the "onResult(result)", you can get the sessionId from: result.getSessionId()
YouTube is still not on the official SDK so YMMV, for apps using official SDK, you should be able to use the above approach (most of it)
Why are you trying to set up a message yourself? Why not building a RemoteMediaPlayer and using play/pause that is provided there? Whenever you are working with the media playback through the official channel, always use the RemoteMediaPlayer (don't forget to call requestStatus() on it after creating it).
Yes it is possible , First you have to save sesionId and CastDevice device id
and when remove app from background and again open app please check is there sessionId then call bello line.
Cast.CastApi.joinApplication(apiClient, APP_ID,sid).setResultCallback(connectionResultCallback);
if you get success result then need to implement further process in connectionResultCallback listener.
//Get selected device which you selected before
#Override
public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
// Log.d("Route Added", "onRouteAdded");
/* if (router.getRoutes().size() > 1)
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
else
Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes(), Toast.LENGTH_SHORT).show();*/
if (router != null && router.getRoutes() != null && router.getRoutes().size() > 1) {
// Show the button when a device is discovered.
// Toast.makeText(homeScreenActivity, "'onRouteAdded :: " + router.getRoutes().size() + " -- " + router.getRoutes().get(1).isSelected(), Toast.LENGTH_SHORT).show();
mMediaRouteButton.setVisibility(View.VISIBLE);
titleLayout.setVisibility(View.GONE);
castName.setVisibility(View.VISIBLE);
selectedDevice = CastDevice.getFromBundle(route.getExtras());
routeInfoArrayList = router.getRoutes();
titleLayout.setVisibility(View.GONE);
if (!isCastConnected) {
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
for (int i = 0; i < routeInfoArrayList.size(); i++) {
if (routeInfoArrayList.get(i).getExtras() != null && CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras()).getDeviceId().equalsIgnoreCase(deid)) {
selectedDevice = CastDevice.getFromBundle(routeInfoArrayList.get(i).getExtras());
routeInfoArrayList.get(i).select();
ReSelectedDevice(selectedDevice, routeInfoArrayList.get(i).getName());
break;
}
}
}
}
}
//Reconnect google Api Client
public void reConnectGoogleApiClient() {
if (apiClient == null) {
Cast.CastOptions apiOptions = new
Cast.CastOptions.Builder(selectedDevice, castClientListener).build();
apiClient = new GoogleApiClient.Builder(this)
.addApi(Cast.API, apiOptions)
.addConnectionCallbacks(reconnectionCallback)
.addOnConnectionFailedListener(connectionFailedListener)
.build();
apiClient.connect();
}
}
// join Application
private final GoogleApiClient.ConnectionCallbacks reconnectionCallback = new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
// Toast.makeText(homeScreenActivity, "" + isDeviceSelected(), Toast.LENGTH_SHORT).show();
try {
String sid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_SESSION_ID);
String deid = MyPref.getInstance(homeScreenActivity).readPrefs(MyPref.CAST_DEVICE_ID);
if (sid != null && deid != null && sid.length() > 0 && deid.length() > 0)
Cast.CastApi.joinApplication(apiClient, APP_ID, sid).setResultCallback(connectionResultCallback);
isApiConnected = true;
} catch (Exception e) {
}
}
#Override
public void onConnectionSuspended(int i) {
isCastConnected = false;
isApiConnected = false;
}
};