How can i get remote streams on react-native-webrtc from a Kurento Media Server - react-native

I am trying to connect a Kurento Media Server to React Native on iOS for a group video call.
The server is running on Nodejs and Docker, and they already have a TURN server set up.
The app is already running in the web browser via kurento-utils, but I can't stream the web video to RN, instead, the RN video looks just fine on the web.
I'm not usig kurento-utils on client side of React Native.
This is my code:
Video Screen (emmiter and receptor)
import React, { useState, useEffect } from 'react';
import {
View,
StyleSheet,
Text,
} from 'react-native';
import Display from 'react-native-display';
import InCallManager from 'react-native-incall-manager';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { socket } from '../../screens/User';
import ReceiveScreen from './ReceiveScreen';
import {
RTCView,
RTCIceCandidate,
} from 'react-native-webrtc';
import {
startCommunication,
receiveVideo,
addIceCandidate,
ProcessAnswer,
ViewPCArray
} from '../../utils/webrtc-utils';
const participants = {};
function sendMessage(message) {
if (socket) {
socket.emit('message', message);
}
}
const VideoScreen = () => {
const [videoURL, setVideoURL] = useState(null)
const [remoteURL, setRemoteURL] = useState(null)
const [userName, setUserName] = useState(socket.id)
const [roomName, setRoomName] = useState('9b33737f-737f-4a3d-a323-e1cd3f4b68b2')
useEffect(() => {
if(socket) {
var message = {
event: 'joinRoom',
userName: userName,
roomName: roomName
}
sendMessage(message);
}
socket.on('message', message => {
messageProcessHandler(message);
});
return () => {
socket.close();
}
}, [])
/**
*
* #param {*} msg
*/
const messageProcessHandler = (msg) => {
// console.log(`MessageProcessHandler: ${msg.event}`);
switch (msg.event) {
case 'existingParticipants':
console.log('LOG:176 onExistingParticipants= ' + JSON.stringify(msg));
startCommunication(sendMessage, userName, (stream) => {
setVideoURL(stream.toURL())
msg.existingUsers.forEach((object) => {
participants[object.name] = object.id;
console.log("participants:" + JSON.stringify(participants));
receiveVideo(sendMessage, object.name, (pc) => {
console.log('getRemoteStreams ', pc);
pc.getRemoteStreams().forEach(track => {
// console.log('TRACK: ', track);
console.log('STREAM', track);
// pc.addTrack(track, stream);
setRemoteURL(track.toURL())
})
});
});
});
break;
case 'newParticipantArrived':
participants[msg.name] = msg.name;
if (remoteURL == null || remoteURL === '') {
receiveVideo(sendMessage, msg.userName, (pc) => {
pc.getRemoteStreams().forEach(track => {
console.log('STREAM', track);
setRemoteURL(track.toURL())
})
});
}
break;
case 'participantLeft':
participantLeft(msg.name);
break;
case 'receiveVideoAnswer':
ProcessAnswer(msg.senderid, msg.sdpAnswer, (err) => {
if (err) {
console.log('the error: ' + err);
}
});
break;
case 'candidate':
addIceCandidate(msg.userid, new RTCIceCandidate(msg.candidate));
break;
default:
console.error('Unrecognized message', msg.message);
}
}
/**
*
* #param {*} name
*/
const participantLeft = (name) => {
if (participants[name]) {
delete participants[name];
}
if (Object.keys(participants).length == 0) {
setRemoteURL(null)
}
}
return (
<View style={styles.container}>
<View style={{position: "absolute", top: 40, right: 20, zIndex: 200}}>
<Text style={{fontSize: 20, fontWeight: "bold", color: "#fff"}}>{userName}</Text>
</View>
<RTCView zOrder={0} objectFit='cover' style={styles.videoContainer} streamURL={videoURL} />
<Display enable={remoteURL != null}>
<View style={styles.floatView}>
<ReceiveScreen videoURL={remoteURL} />
</View>
</Display>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1
},
videoContainer: {
flex: 1
},
floatView: {
position: 'absolute',
width: 250,
height: 210,
bottom: 15,
right: 20,
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: 15
}
});
export default VideoScreen
Display (where the browser video is supposed to be)
import React, { Component } from 'react';
import config from "../../config/app.js";
import {
View,
Text,
ScrollView,
StyleSheet
} from 'react-native';
import { RTCView } from 'react-native-webrtc';
const styles = StyleSheet.create({
root: {
flex: 1
},
titleContainer: {
flex: 0.1,
paddingVertical: 2,
paddingLeft: 10
},
title: {
fontSize: 15,
color: 'rgba(255, 255, 255, 0.5)'
},
contentContainer: {
flex: 1,
backgroundColor: 'transparent'
}
});
const ReceiveScreen = (props) => {
return (
<View style={styles.root}>
<View style={styles.titleContainer}>
<Text style={styles.title}>Nuevo miembro</Text>
</View>
<RTCView objectFit='cover' zOrder={1} style={styles.contentContainer} streamURL={props.videoURL} mirror={true} />
</View>
)
}
export default ReceiveScreen;
WebRtc Utils
import {
getUserMedia,
MediaStreamTrack,
mediaDevices,
RTCPeerConnection,
RTCSessionDescription,
} from 'react-native-webrtc';
import { PERMISSIONS, request, checkMultiple } from 'react-native-permissions';
import { socket } from '../screens/User';
let pcArray = {};
let isFront = true;
let isEnableAudio = true;
let isEnableVideo = true;
let localstream = null;
const ICE_CONFIG = {
'iceServers': [
{
'urls': 'turn:xxx.xxx.xxx.xxx:3478',
'username': 'xxxxxxxx',
'credential': 'xxxxxxxxxxxxxxxxxxxxxxx',
'credentialType': 'password'
}
]
};
/**
* Obtenga elementos multimedia locales (transmisiones de video)
*
* #param {*} _sendMessage
* #param {*} _name
* #param {*} callback
*/
export function startCommunication(_sendMessage, _name, callback) {
getStream(true, stream => {
localstream = stream;
let options = {
audio: true,
video : {
mandatory : {
maxWidth : 320,
maxFrameRate : 15,
minFrameRate : 1
}
}
};
let pc = createPC(_sendMessage, _name, true, options);
pcArray[_name] = pc;
callback(stream);
});
}
/**
* Obtener transmisión de video remota
*
* #param {*} _sendMessae
* #param {*} _name
* #param {*} callback
*/
export function receiveVideo(_sendMessae, _name, callback) {
console.log("receiveVideo:", _sendMessae, _name, callback);
let options = {
audio: true,
video : {
mandatory : {
// maxWidth : 320,
// maxFrameRate : 15,
// minFrameRate : 15
maxWidth : 120,
maxHeight: 80,
minWidth : 80,
minHeight: 60,
maxFrameRate : 10,
minFrameRate : 10
}
}
};
let pc = createPC(_sendMessae, _name, true, options);
// console.log(`PC CREATED FOR ${_name}: ${JSON.stringify(pc)}`);
pcArray[_name] = pc;
// callback(pc);
callback(pcArray);
}
/**
* Encender/apagar el micrófono
*/
export function toggleAudio() {
if (localstream) {
isEnableAudio = !isEnableAudio;
localstream.getAudioTracks().forEach((track) => {
track.enabled = isEnableAudio;
});
} else {
console.log('in toggleAudio(), localstream is empty');
}
return isEnableAudio;
}
/**
* Activar/desactivar vídeo
*/
export function toggleVideo() {
if (localstream) {
isEnableVideo = !isEnableVideo;
localstream.getVideoTracks().forEach((track) => {
track.enabled = isEnableVideo;
});
} else {
console.log('in toggleVideo(), localstream is empty');
}
return isEnableVideo;
}
/**
* cambiar de cámara
*
*/
export function switchVideoType() {
if (localstream) {
localstream.getVideoTracks().forEach(track => {
track._switchCamera();
});
} else {
console.log('error');
}
}
/**
* Crear una transmisión de video local
*
* #param {*} isFront
* #param {*} callback
*/
export function getStream(isFront, callback) {
mediaDevices.enumerateDevices().then(sourceInfos => {
//console.log('Log: '+ JSON.stringify(sourceInfos)); // -> Es un objecto de 3
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (sourceInfo.kind === 'videoinput' && sourceInfo.facing === (isFront ? 'front' : 'back')) {
// if (sourceInfo.kind === 'videoinput' && sourceInfo.facing === (isFront ? 'front' : 'back')) { Was video only, not videoinput
videoSourceId = sourceInfo.id;
}
}
request(PERMISSIONS.IOS.CAMERA).then((response) => {
if (response === 'granted') {
request(PERMISSIONS.IOS.CAMERA).then((response) => {
if (response === 'granted') {
mediaDevices.getUserMedia({
audio: true,
video: {
width: 640,
height: 480,
frameRate: 30,
facingMode: (isFront ? "user" : "environment"),
deviceId: videoSourceId
}
})
.then(stream => {
callback(stream)
})
.catch(error => {
// Log error
console.log(error)
});
}
})
}
})
});
}
/**
*
* Crear una conexión WebRTC
*
* #param {*} sendMessage
* #param {*} name
* #param {*} isOffer
* #param {*} options
*/
export function createPC(sendMessage, name, isOffer, options) {
let pc = new RTCPeerConnection(ICE_CONFIG);
pc.onnegotiationneeded = () => {
console.log('onnegotiationneeded');
if (isOffer) {
isOffer = false;
createOffer();
}
};
pc.onicecandidate = (event) => {
console.log('onicecandidate');
if (event.candidate) {
let msg = {
event: 'candidate',
// event: 'onIceCandidate',
userid: socket.id,
roomName: '9b33737f-737f-4a3d-a323-e1cd3f4b68b2',
candidate: event.candidate
};
sendMessage(msg);
}
};
pc.oniceconnectionstatechange = (event) => {
// console.log('oniceconnectionstatechange');
if (event.target.iceConnectionState === 'disconnected') {
//localstream.release();
// localstream = null;
if (pc !== null) {
pc.close();
pc = null;
}
}
};
pc.onsignalingstatechange = (event) => {
console.log('onsignalingstatechange: ', event.target.signalingState);
};
// send local stream
pc.addStream(localstream);
function createOffer() {
// console.log('createOffer');
pc.createOffer()
.then(function (desc) {
// console.log('...createOffer...');
pc.setLocalDescription(desc);
// something to do
let msg = {
event: 'receiveVideoFrom',
userid: socket.id,
roomName: '9b33737f-737f-4a3d-a323-e1cd3f4b68b2',
// sdpOffer: desc.sdp,
sdpOffer: desc.sdp,
};
sendMessage(msg);
})
.catch(err => console.error(err));
}
return pc;
}
/**
* Adición incremental de iceCandidate
*
* #param {*} name
* #param {*} candidate
*/
export function addIceCandidate(name, candidate) {
let pc = pcArray[name];
if (pc) {
pc.addIceCandidate(candidate);
} else {
console.log('pc.addIceCandidate failed : pc not exists');
}
}
/**
* Proceso SdpAnswer
*
* #param {*} name
* #param {*} sdp
* #param {*} callback
*/
export async function ProcessAnswer(name, sdp, callback) {
let pc = pcArray[name];
if (pc) {
let answer = {
type: 'answer',
sdp: sdp,
};
if (pc) {
// const remoteDesc = new RTCSessionDescription(answer);
// await pc.setRemoteDescription(remoteDesc);
await pc.setRemoteDescription(new RTCSessionDescription(answer)).then(function () {
console.log('LOG:252 Answer = ' + answer);
callback();
})
.catch(function (reason) {
callback(reason);
console.log('ERROR REASON:', reason);
});
}
} else {
console.log('ProcessAnswer failed : pc not exists');
}
}
/**
*
* Cierra la conexión y libera el streamer local.
*
*/
export function ViewPCArray (username) {
// console.log(pcArray[username].getRemoteStreams()[0]);
return pcArray
}
export function ReleaseMeidaSource() {
if (localstream) {
localstream.release();
localstream = null;
}
if (pcArray !== null) {
for (let mem in pcArray) {
pcArray[mem].close();
delete pcArray[mem];
}
}
}
function logError(error) {
console.log('logError', error);
}
NodeJs Kurento
module.exports = function(io, socket) {
// variables
const kurento = require('kurento-client');
const minimist = require('minimist');
var kurentoClient = null;
var iceCandidateQueues = {};
// constants
var argv = minimist(process.argv.slice(2), {
default: {
// as_uri: 'https://localhost:3000/',
// ws_uri: 'ws://localhost:8888/kurento'
as_uri: 'https://localhost:4000/',
ws_uri: 'ws://localhost:8888/kurento'
}
});
socket.on('message', function (message) {
console.log('Message received: ', message.event);
switch (message.event) {
case 'joinRoom':
joinRoom(socket, message.userName, message.roomName, err => {
if (err) {
console.log(err);
}
});
break;
case 'receiveVideoFrom':
receiveVideoFrom(socket, message.userid, message.roomName, message.sdpOffer, err => {
if (err) {
console.log(err);
}
});
break;
case 'candidate':
addIceCandidate(socket, message.userid, message.roomName, message.candidate, err => {
if (err) {
console.log(err);
}
});
break;
}
socket.on('disconnect', () => {
leaveRoom(socket, message.roomName, err => {
if (err) {
console.log(err);
};
});
})
});
// signaling functions
function joinRoom(socket, username, roomname, callback) {
console.log(('Smeone in the Room').green + socket + " " + username + " " + roomname)
getRoom(socket, roomname, (err, myRoom) => {
if (err) {
return callback(err);
}
myRoom.pipeline.create('WebRtcEndpoint', (err, outgoingMedia) => {
outgoingMedia.setMaxVideoSendBandwidth(100);
if (err) {
return callback(err);
}
var user = {
id: socket.id,
name: username,
outgoingMedia: outgoingMedia,
incomingMedia: {}
}
let iceCandidateQueue = iceCandidateQueues[user.id];
if (iceCandidateQueue) {
while (iceCandidateQueue.length) {
let ice = iceCandidateQueue.shift();
console.error(`user: ${user.name} collect candidate for outgoing media`);
user.outgoingMedia.addIceCandidate(ice.candidate);
}
}
user.outgoingMedia.on('OnIceCandidate', event => {
let candidate = kurento.register.complexTypes.IceCandidate(event.candidate);
socket.emit('message', {
event: 'candidate',
userid: user.id,
candidate: candidate
});
});
socket.to(roomname).emit('message', {
event: 'newParticipantArrived',
userid: user.id,
username: user.name
});
let existingUsers = [];
for (let i in myRoom.participants) {
if (myRoom.participants[i].id != user.id) {
existingUsers.push({
id: myRoom.participants[i].id,
name: myRoom.participants[i].name
});
}
}
socket.emit('message', {
event: 'existingParticipants',
existingUsers: existingUsers,
userid: user.id
});
myRoom.participants[user.id] = user;
});
});
}
// Disconnect from room
function leaveRoom(socket, roomname, callback) {
if (io.sockets.adapter.rooms[roomname] == null) {
// ROOMS
// console.log((io.sockets.adapter.rooms))
return;
}
var userSession = io.sockets.adapter.rooms[roomname].participants[socket.id];
var myRoom = io.sockets.adapter.rooms[roomname] || { length: 0 };
// MY ROOM
// console.log(myRoom);
if (!userSession) {
return;
}
var room = io.sockets.adapter.rooms[roomname];
if(!room){
return;
}
console.log('notify all user that ' + userSession.name + ' is leaving the room ' + roomname);
var usersInRoom = room.participants;
delete usersInRoom[userSession.id];
userSession.outgoingMedia.release();
// release incoming media for the leaving user
for (var i in userSession.incomingMedia) {
userSession.incomingMedia[i].release();
delete userSession.incomingMedia[i];
}
for (var i in usersInRoom) {
var user = usersInRoom[i];
// release viewer from this
user.incomingMedia[userSession.id].release();
delete user.incomingMedia[userSession.id];
// notify all user in the room
io.emit('message', {
event: 'participantLeft',
name: userSession.id
});
console.log('Mensaje emitido')
}
// Release pipeline and delete room when room is empty
if (Object.keys(room.participants).length == 0) {
room.pipeline.release();
delete rooms[userSession.roomName];
}
delete userSession.roomName;
// console.log(myRoom);
callback(null);
}
function receiveVideoFrom(socket, userid, roomname, sdpOffer, callback) {
getEndpointForUser(socket, roomname, userid, (err, endpoint) => {
if (err) {
return callback(err);
}
endpoint.processOffer(sdpOffer, (err, sdpAnswer) => {
if (err) {
return callback(err);
}
socket.emit('message', {
event: 'receiveVideoAnswer',
senderid: userid,
sdpAnswer: sdpAnswer
});
endpoint.gatherCandidates(err => {
if (err) {
return callback(err);
}
});
});
});
}
function addIceCandidate(socket, senderid, roomname, iceCandidate, callback) {
// console.log(io.sockets.adapter.rooms);
let user = io.sockets.adapter.rooms[roomname].participants[socket.id];
if (user != null) {
let candidate = kurento.register.complexTypes.IceCandidate(iceCandidate);
if (senderid == user.id) {
if (user.outgoingMedia) {
user.outgoingMedia.addIceCandidate(candidate);
} else {
iceCandidateQueues[user.id].push({ candidate: candidate });
}
} else {
if (user.incomingMedia[senderid]) {
user.incomingMedia[senderid].addIceCandidate(candidate);
} else {
if (!iceCandidateQueues[senderid]) {
iceCandidateQueues[senderid] = [];
}
iceCandidateQueues[senderid].push({ candidate: candidate });
}
}
callback(null);
} else {
callback(new Error("addIceCandidate failed"));
}
}
// useful functions
function getRoom(socket, roomname, callback) {
var myRoom = io.sockets.adapter.rooms[roomname] || { length: 0 };
var numClients = myRoom.length;
console.log(roomname, ' has ', numClients, ' clients');
if (numClients == 0) {
socket.join(roomname, () => {
myRoom = io.sockets.adapter.rooms[roomname];
getKurentoClient((error, kurento) => {
kurento.create('MediaPipeline', (err, pipeline) => {
if (error) {
return callback(err);
}
myRoom.pipeline = pipeline;
myRoom.participants = {};
callback(null, myRoom);
});
});
});
} else {
socket.join(roomname);
callback(null, myRoom);
}
}
function getEndpointForUser(socket, roomname, senderid, callback) {
var myRoom = io.sockets.adapter.rooms[roomname];
var asker = myRoom.participants[socket.id];
var sender = myRoom.participants[senderid];
if (asker.id === sender.id) {
return callback(null, asker.outgoingMedia);
}
if (asker.incomingMedia[sender.id]) {
sender.outgoingMedia.connect(asker.incomingMedia[sender.id], err => {
if (err) {
return callback(err);
}
callback(null, asker.incomingMedia[sender.id]);
});
} else {
myRoom.pipeline.create('WebRtcEndpoint', (err, incoming) => {
incoming.setMaxVideoSendBandwidth(100);
if (err) {
return callback(err);
}
asker.incomingMedia[sender.id] = incoming;
let iceCandidateQueue = iceCandidateQueues[sender.id];
if (iceCandidateQueue) {
while (iceCandidateQueue.length) {
let ice = iceCandidateQueue.shift();
console.error(`user: ${sender.name} collect candidate for outgoing media`);
incoming.addIceCandidate(ice.candidate);
}
}
incoming.on('OnIceCandidate', event => {
let candidate = kurento.register.complexTypes.IceCandidate(event.candidate);
console.log("CANDIDATE: ", event.candidate);
socket.emit('message', {
event: 'candidate',
userid: sender.id,
candidate: candidate
});
});
sender.outgoingMedia.connect(incoming, err => {
if (err) {
return callback(err);
}
callback(null, incoming);
});
});
}
}
function getKurentoClient(callback) {
if (kurentoClient !== null) {
return callback(null, kurentoClient);
}
kurento(argv.ws_uri, function (error, _kurentoClient) {
if (error) {
console.log("Could not find media server at address " + argv.ws_uri);
return callback("Could not find media server at address" + argv.ws_uri
+ ". Exiting with error " + error);
}
kurentoClient = _kurentoClient;
callback(null, kurentoClient);
});
}
}

Related

How to call APi Service in component of Vue3

I am working on an application where I have created service js which I need to consume in different components of vue3. Here is my service code
const base_url = "https://localhost:7005/";
var apiObject = {
data: function() {
return {
response : undefined
};
},
methods: {
fetchContent: function(apiEndpoint) {
axios
.get(`${base_url}${apiEndpoint}`)
.then(res => {
this.response = res
})
.catch(e => {
this.errors.push(e);
});
}
}
};
Here is my component code. It is not working it gives me the error show in image below
<script>
import {fetchContent} from "../service/apiService";
export default {
data() {
return {
// url_base: "https://localhost:7005/api/weather/",
weather: undefined,
error : false,
errormessage : "",
searchHistory : []
};
},
methods : {
async fetchWeather(e) {
if (e.key == "Enter" && this.query) {
let {response} =await fetchContent(`api/weather/forecast?city=${query}`) //get(query,`${weather_url}forecast?city=`); //await axios.get(`${this.url_base}forecast?city=${this.query}`);
this.setResults(response.data);
}else if (e.key == "Enter" && !this.query){
this.error = true;
this.errormessage = 'Please enter name to search!';
}
},
setResults(res) {
if(res.isSuccessful === true){
this.error = false;
this.weather = res.response;
this.saveData(res.response)
}else{
this.weather = undefined;
this.errormessage = res.response;
this.error = true;
}
},
saveData(res){
this.searchHistory = JSON.parse(localStorage.getItem("SearchHistory"));
if(this.searchHistory == null){this.searchHistory = [];}
res.forEach(x => {
this.searchHistory.push(x);
});
localStorage.setItem("SearchHistory",JSON.stringify(this.searchHistory));
}
},
};
</script>
Image

Ionic 4 not print in thermal bluetooth

I tried to print text in a thermal printer "https://www.amazon.it/gp/product/B096KQ99K1/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1"
but nothing appens.
I uded a plugin ble Bluetooth Low Energy.
The connection ok, list device ok and other method ok and write method return "OK".
But don't print nothing.
What Can I solve?
This is my code: - details page .ts
detailsPage.html
import { Component, OnInit, NgZone } from '#angular/core';
import {AlertController, LoadingController, ToastController} from '#ionic/angular';
import { ActivatedRoute, Router } from '#angular/router';
import { BLE } from '#awesome-cordova-plugins/ble/ngx';
// Bluetooth UUIDs
let BLE_SERVICE = ''; //= '180A';
let BLE_CHARACTERISTIC = ''; // '2A29';
#Component({
selector: 'app-details',
templateUrl: './details.page.html',
styleUrls: ['./details.page.scss']
})
export class DetailsPage implements OnInit {
dispositivo;
peripheral: any = {};
statusMessage: string;
public dataFromDevice: any;
constructor(public route: ActivatedRoute,
public router: Router,
private ble: BLE,
private toastCtrl: ToastController,
private alertCtrl: AlertController,
private loadingCtrl: LoadingController,
private ngZone: NgZone) {
this.route.queryParams.subscribe(params => {
if (params && params.special) {
const device = JSON.parse(params.special);
this.dispositivo = device;
}
});
}
async presentLoadingText(macAddress) {
await this.loadingCtrl.create({
message: 'Please wait...'
}).then((res) => {
res.present();
});
this.bleConnect(this.dispositivo);
}
bleConnect(device) {
this.ble.connect(device.id).subscribe(
peripheral => this.onConnected(peripheral),
peripheral => this.onDeviceDisconnected(peripheral)
);
}
bleDisconnect() {
this.ble.disconnect(this.peripheral.id).then(
() => console.log('Disconnected ' + JSON.stringify(this.peripheral)),
() => console.log('ERROR disconnecting ' + JSON.stringify(this.peripheral)));
}
bleWrite() {
const inputdata = new Uint8Array(3);
inputdata[0] = 0x53; // S
inputdata[1] = 0x54; // T
inputdata[2] = 0x0a; // LF
this.ble
.write(
this.peripheral.id,
BLE_SERVICE,
BLE_CHARACTERISTIC,
inputdata.buffer
)
.then(
(data) => {
this.subscribe();
},
err => {
console.log(err);
}
);
}
subscribe() {
console.log('Entro?');
this.ble
.startNotification(this.dispositivo.id, "fff0", "fff2")
.subscribe(
(data) => {
this.onValueChange(data);
},
(err) =>
this.showAlert(
'Unexpected Error',
err
).then(() =>{
this.bleDisconnect();
}),
);
}
onValueChange(buffer: ArrayBuffer) {
console.log('Che fa sto metodo?');
this.ngZone.run(() => {
try {
if (this.dataFromDevice === undefined){
console.log('Dati indefiniti?');
this.dataFromDevice = this.bytesToString(buffer).replace(/\s+/g, ' ');
} else {
console.log('Dati DEFINITI? ' +this.dataFromDevice);
this.dataFromDevice += '<br />' + this.bytesToString(buffer).replace(/\s+/g, ' ');
}
} catch (e) {
console.log(e);
}
});
}
bytesToString(buffer) {
return String.fromCharCode.apply(null, new Uint8Array(buffer));
}
onConnected(peripheral) {
this.loadingCtrl.dismiss();
this.ngZone.run(() => {
this.setStatus('');
this.peripheral = peripheral;
const characteristics = peripheral.characteristics;
BLE_SERVICE = characteristics[5].service;
BLE_CHARACTERISTIC = characteristics[5].characteristic;
this.bleWrite();
});
}
async onDeviceDisconnected(peripheral) {
const toast = await this.toastCtrl.create({
message: 'The peripheral unexpectedly disconnected',
duration: 3000,
position: 'middle'
});
toast.present();
}
setStatus(message) {
console.log(message);
this.ngZone.run(() => {
this.statusMessage = message;
});
}
async showAlert(title, message) {
const alert = await this.alertCtrl.create({
header: title,
message: message,
buttons: ['OK']
});
alert.present();
}
// ASCII only
stringToBytes(str) {
const array = new Uint8Array(str.length);
let i;
let l;
for (i = 0, l = str.length; i < l; i++) {
array[i] = str.charCodeAt(i);
}
return array.buffer;
}
}
HTML
<ion-header>
<ion-toolbar>
<ion-title>{{ peripheral.name || 'Device' }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="padding">
<ion-card>
<ion-card-header>
{{ dispositivo.name || 'Unnamed' }}
</ion-card-header>
<ion-card-content (click)="connectToBluetoothPrinter(dispositivo.id)">
{{ dispositivo.id }}
</ion-card-content>
</ion-card>
</ion-content>

React-native: How to change the audio speed in expo-av

I'm having trouble changing the prop: 'rate' to change the speed of the audio being played.
I'm using expo-av (https://docs.expo.dev/versions/latest/sdk/av/).
Here's my code:
import {Text, View, Alert } from 'react-native'
import * as MediaLibrary from 'expo-media-library';
import { DataProvider } from 'recyclerlistview';
import {Audio} from 'expo-av';
import { play, pause, resume, playNext } from "../misc/AudioController";
export const AudioContext = createContext()
export class AudioProvider extends Component {
constructor(props) {
super(props);
this.state = {
audioFiles: [],
permissionError: false,
dataProvider: new DataProvider((r1, r2) => r1 !== r2),
playbackObj: null,
soundObj: null,
currentAudio: {},
isPlaying: false,
currentAudioIndex: null,
playbackPosition: null,
playbackDuration: null,
rate: 2.0,
};
this.totalAudioCount = 0;
}
permissionAlert = () => {
Alert.alert("Permission Required", "This app needs to read audio files", [
{ text: "I am ready", onPress: () => this.getPermission() },
{
text: "cancel",
onPress: () => this.permissionAlert(),
},
]);
};
getAudioFiles = async () => {
const { dataProvider, audioFiles } = this.state;
let media = await MediaLibrary.getAssetsAsync({
mediaType: "audio",
});
media = await MediaLibrary.getAssetsAsync({
mediaType: "audio",
first: media.totalCount,
});
this.totalAudioCount = media.totalCount;
this.setState({
...this.state,
dataProvider: dataProvider.cloneWithRows([
...audioFiles,
...media.assets,
]),
audioFiles: [...audioFiles, ...media.assets],
});
};
loadPreviousAudio = async () => {
let previousAudio = await AsyncStorageLib.getItem("previousAudio");
let currentAudio;
let currentAudioIndex;
if (previousAudio === null) {
currentAudio = this.state.audioFiles[0];
currentAudioIndex = 0;
} else {
previousAudio = JSON.parse(previousAudio);
currentAudio = previousAudio.audio;
currentAudioIndex = previousAudio.index;
}
this.setState({ ...this.state, currentAudio, currentAudio });
};
getPermission = async () => {
// {
// "canAskAgain": true,
// "expires": "never",
// "granted": false,
// "status": "undetermined",
// }
const permission = await MediaLibrary.getPermissionsAsync();
if (permission.granted) {
this.getAudioFiles();
}
if (!permission.canAskAgain && !permission.granted) {
this.setState({ ...this.state, permissionError: true });
}
if (!permission.granted && permission.canAskAgain) {
const { status, canAskAgain } =
await MediaLibrary.requestPermissionsAsync();
if (status === "denied" && canAskAgain) {
this.permissionAlert();
}
if (status === "granted") {
this.getAudioFiles();
}
if (status === "denied" && !canAskAgain) {
this.setState({ ...this.state, permissionError: true });
}
}
};
onPlaybackStatusUpdate = async (playbackStatus) => {
console.log("hier");
if (playbackStatus.isLoaded && playbackStatus.isPlaying) {
this.updateState(this, {
playbackPosition: playbackStatus.positionMillis,
playbackDuration: playbackStatus.durationMillis,
});
}
if (playbackStatus.didJustFinish) {
const nextAudioIndex = this.state.currentAudioIndex + 1;
if (nextAudioIndex >= this.totalAudioCount) {
this.state.playbackObj.unloadAsync();
this.updateState(this, {
soundObj: null,
currentAudio: this.state.audioFiles[0],
isPlaying: false,
currentAudioIndex: 0,
playbackPosition: null,
playbackDuration: null,
});
}
const audio = this.state.audioFiles[nextAudioIndex];
const status = await playNext(this.state.playbackObj, audio.uri);
this.updateState(this, {
soundObj: status,
currentAudio: audio,
isPlaying: true,
currentAudioIndex: nextAudioIndex,
});
}
};
componentDidMount() {
this.getPermission();
if (this.state.playbackObj === null) {
this.setState({ ...this.state, playbackObj: new Audio.Sound(), });
}
}
updateState = (prevState, newState = {}) => {
this.setState({ ...prevState, ...newState });
};
render() {
const {
audioFiles,
dataProvider,
permissionError,
playbackObj,
soundObj,
currentAudio,
isPlaying,
currentAudioIndex,
playbackPosition,
playbackDuration,
rate,
} = this.state;
if (permissionError)
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>It looks like you haven't accepted the permission</Text>
</View>
);
return (
<AudioContext.Provider
value={{
audioFiles,
dataProvider,
playbackObj,
soundObj,
currentAudio,
isPlaying,
currentAudioIndex,
totalAudioCount: this.totalAudioCount,
playbackPosition,
playbackDuration,
rate,
updateState: this.updateState,
loadPreviousAudio: this.loadPreviousAudio,
onPlaybackStatusUpdate: this.onPlaybackStatusUpdate
}}
>
{this.props.children}
</AudioContext.Provider>
);
}
}
import {Component} from 'react';
import AsyncStorageLib from '#react-native-async-storage/async-storage';
export default AudioProvider;
and here's some more:
// play audio
// Import the react-native-sound module
import { PitchCorrectionQuality,shouldCorrectPitch, rate } from "expo-av/build/AV.types";
export const play = async (playbackObj, uri,) => {
try {
return await playbackObj.loadAsync(
{uri},
{shouldPlay: true},
);
} catch (error) {
console.log('error inside play helper method', error.message)
}
};
//pause
export const pause = async playbackObj => {
try {
// playbackObj.setRateAsync(rate = 2.0, shouldCorrectPitch = true, PitchCorrectionQuality= High);
return await playbackObj.setStatusAsync({
shouldPlay: false},
);
} catch (error) {
console.log('error inside pause helper method', error.message)
}
};
//resume
export const resume = async playbackObj => {
try {
return await playbackObj.playAsync(
);
} catch (error) {
console.log('error inside pause resume method', error.message)
}
};
//select next
export const playNext = async (playbackObj, uri) => {
try {
await playbackObj.stopAsync()
await playbackObj.unloadAsync();
return await play(playbackObj, uri);
} catch (error) {
console.log('error inside playNext helper method')
}
}
I've tried including 'rate: 2.0' inside this.state{audioFiles: [],
permissionError: false, etc.} but it didn't work.
Also I've tried doing: await playbackObj.setRateAsync() in the 2nd code snippet.
Any suggestions?
Nvm, I found the solution. Here's my updated code:
// play audio
// Import the react-native-sound module
import { PitchCorrectionQuality,shouldCorrectPitch, rate } from "expo-av/build/AV.types";
export const play = async (playbackObj, uri,) => {
try {
await playbackObj.loadAsync(
{uri},
{shouldPlay: true},
);
return await playbackObj.setStatusAsync({ rate: 0.9749090909 });
} catch (error) {
console.log('error inside play helper method', error.message)
}
};
//pause
export const pause = async playbackObj => {
try {
return await playbackObj.setStatusAsync({
shouldPlay: false,
rate: 0.9749090909,
});
} catch (error) {
console.log('error inside pause helper method', error.message)
}
};
//resume
export const resume = async playbackObj => {
try {
return await playbackObj.playAsync(
);
} catch (error) {
console.log('error inside pause resume method', error.message)
}
};
//select next
export const playNext = async (playbackObj, uri) => {
try {
await playbackObj.stopAsync()
await playbackObj.unloadAsync();
return await play(playbackObj, uri);
} catch (error) {
console.log('error inside playNext helper method')
}
}

can't set response from api to messages array of GiftedChat

I am new to react native. I am currently developing a messaging app.
I have used npm-giftedChat for UI & functionalities. The problem is I need to get the response from api & set it to the messages array of giftedchat. I receive data from API and while I set it to messages array it loops over data and renders only the last data in that array.
Any help would be appreciated.I have added my code here
Please find where I am going wrong?
componentWillMount() {
var arrMsg = [];
var data = params.data
for(let i = 0; i < data.Replies.length ; i++){
var obj = {
_id: data.To._id,
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: data.From._id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj)
}
this.setState({messages: arrMsg})
}
Sample output
My self also facing same issues..
setting is very important in gifted chat..
so try to use following in ur code,i have edited same like your code.if any queries let me know thanks.
for (let i = 0; i < data.Replies.length; i++) {
console.log(data.Replies[i].CreatedDate);
debugger
var id = data.From._id
if (data.To.id == UserID) {
id = this.state.userID
}
const obj = {
_id: Math.round(Math.random() * 1000000),
text: data.Replies[i].Reply,
createdAt: data.Replies[i].CreatedDate,
user: {
_id: id,
name: 'React Native',
avatar: data.From.Profile.DisplayPicture
},
image: '',
}
arrMsg.push(obj);
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, arrMsg)
};
});
I wrote a gist here on how to add a web socket listening to a rails channel to a react native chat screen + Gifted Chat
// chat.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
TouchableHighlight,
Dimensions,
AppState,
AsyncStorage,
Alert
} from 'react-native';
import {
GiftedChat,
Actions,
Bubble,
SystemMessage
} from 'react-native-gifted-chat';
import axios from 'axios';
import ActionCable from 'react-native-actioncable';
import { yourRootUrl, websocketUrl } from '../config/constants';
class Chat extends Component {
state = {
messages: [],
client: '',
accessToken: '',
expiry: '',
uid: '',
userId: ''
}
componentDidMount() {
AsyncStorage.multiGet(
['client', 'expiry',
'access_token', 'uid',
'account_balance', 'userId'
]
)
.then((result) => {
this.setState({
client: result[0][1],
expiry: result[1][1],
accessToken: result[2][1],
uid: result[3][1],
userId: result[5][1]
});
})
.then(() => {
this.getPreviousMessages();
})
.then(() => {
this.createSocket();
})
.catch(() => {
//error logic
});
}
getPreviousMessages() {
//when we open the chat page we should load previous messages
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
const url = yourRootUrl + '/chats/' + chatId;
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
axios.get(url, { headers })
.then((response) => {
/*
lets construct our messages to be in
same format as expected by GiftedChat
*/
const allMessages = [];
response.data.included.forEach((x) => {
if (x.attributes.system) {
const sysMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
system: true
};
allMessages.push(sysMessage);
} else {
const userMessage = {
_id: x.id,
text: x.attributes['message-text'],
createdAt: new Date(x.attributes['created-at']),
user: {
_id: x.attributes['sender-id'],
avatar: x.attributes['sender-avatar'],
},
image: x.attributes.image,
};
allMessages.push(userMessage);
}
});
if (allMessages.length === response.data.included.length) {
//lets sort messages according to date created
const sortAllMessages = allMessages.sort((a, b) =>
b.createdAt - a.createdAt
);
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sortAllMessages)
};
});
}
})
}
createSocket() {
//assuming you have set up your chatchannel in your rails backend
const { client, accessToken, uid, userId } = this.state;
const { chatId } = this.props.navigation.state.params; //using react-navigation
const WEBSOCKET_HOST = websocketUrl +
'access-token=' + accessToken + '&client=' +
client + '&uid=' + uid;
const cable = ActionCable.createConsumer(WEBSOCKET_HOST);
this.channel = cable.subscriptions.create(
{
channel: 'ChatChannel',
id: chatId
}, {
received: (data) => {
console.log('Received Data:', data);
if ((data.message.sender_id !== parseInt(userId))
|| (data.message.image !== null)) {
//ensuring you do not pick up your own messages
if (data.message.system === true) {
const sysMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
system: true
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, sysMessage)
};
});
} else {
const userMessage = {
_id: data.message.id,
text: data.message.message_text,
createdAt: new Date(data.message.created_at),
user: {
_id: data.message.sender_id,
avatar: data.message.sender_avatar,
},
image: data.message.image,
};
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, userMessage)
};
});
}
}
},
connected: () => {
console.log(`Connected ${chatId}`);
},
disconnected: () => {
console.warn(`${chatId} was disconnected.`);
},
rejected: () => {
console.warn('connection rejected');
},
});
}
onSend(messages = []) {
const { chatId } = this.props.navigation.state.params;
const { client, accessToken, uid, userId } = this.state;
this.setState((previousState) => {
return {
messages: GiftedChat.append(previousState.messages, messages)
};
});
messages.forEach((x) => {
const url = yourRootUrl + '/messages';
const headers = {
'access-token': accessToken,
client,
expiry,
uid
};
const data = {
chat_id: chatId,
sender_id: userId,
sender_name: name,
message_text: x.text,
image: x.image
};
/*
send the message to your rails app backend
hopefully you have a callback in your model like
after_create :broadcast_message
then broadcast to the chat channel from your rails backend
*/
axios.post(url, data, { headers })
.then(response => console.log(response));
});
}
renderBubble(props) {
return (
<Bubble
{...props}
wrapperStyle={{
left: {
backgroundColor: '#f9f9f9',
}
}}
/>
);
}
renderSystemMessage(props) {
return (
<SystemMessage
{...props}
containerStyle={{
marginBottom: 15,
}}
textStyle={{
fontSize: 14,
textAlign: 'center'
}}
/>
);
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={message => this.onSend(message)}
user={{
_id: parseInt(userId)
}}
renderBubble={this.renderBubble}
renderSystemMessage={this.renderSystemMessage}
/>
);
}
}

Right way to setItem in AsyncStorage

I want to use AsyncStorage.setItem inside AsyncStorage.getItem. How to do that in right way?
My code is as follows:
createVehicle: function (vehicle, cb) {
AsyncStorage.getItem('vehicle')
.then(json => {
let vehicles = [];
if (json) {
vehicles = JSON.parse(json);
let o_vehicle = filter(vehicles, {autralis_id: vehicle.autralis_id});
if (o_vehicle.length > 0) {
cb(o_vehicle[0].id);
return;
} else {
vehicles.push(vehicle);
}
} else {
vehicles.push(vehicle);
}
AsyncStorage.setItem('vehicle', JSON.stringify(vehicles), () => {
cb(vehicle.id + 1)
});
}).done();
},
Is that the right way to do it?
I have created a service for Storage which can be used in the entire project as and when required by passing the required params. Have a look :
export default {
async setItem(key, value) {
try {
return await AsyncStorage.setItem(key, JSON.stringify(value));
} catch (error) {
// console.error('AsyncStorage#setItem error: ' + error.message);
}
},
async getItem(key) {
return await AsyncStorage.getItem(key)
.then((result) => {
if (result) {
try {
result = JSON.parse(result);
} catch (e) {
// console.error('AsyncStorage#getItem error deserializing JSON for key: ' + key, e.message);
}
}
return result;
});
},
async removeItem(key) {
return await AsyncStorage.removeItem(key);
}
}
This is by far the best practice I have come across till the date. You should use it too.
Please refer to this official document, it uses getItem inside setItem, So I think you could also use setItem inside getItem, because the return value is just a Promise for both getItem and setItem.
AsyncStorage.setItem('UID123', JSON.stringify(UID123_object), () => {
AsyncStorage.mergeItem('UID123', JSON.stringify(UID123_delta), () => {
AsyncStorage.getItem('UID123', (err, result) => {
console.log(result);
});
});
});
import React, { Component } from 'react'
import { StatusBar } from 'react-native'
import { AsyncStorage, Text, View, TextInput, StyleSheet } from 'react-native'
class AsyncStorageExample extends Component {
state = {
'name': ''
}
componentDidMount = () => AsyncStorage.getItem('name').then((value) => this.setState({
'name': value }))
setName = (value) => {
AsyncStorage.setItem('name', value);
this.setState({ 'name': value });
}
render() {
return (
<View style = {styles.container}>
<TextInput style = {styles.textInput} autoCapitalize = 'none'
onChangeText = {this.setName}/>
<Text>
{this.state.name}
</Text>
</View>
)
}
}
export default AsyncStorageExample
const styles = StyleSheet.create ({
container: {
flex: 1,
alignItems: 'center',
marginTop: 50
},
textInput: {
margin: 5,
height: 100,
borderWidth: 1,
backgroundColor: '#7685ed'
}
})
I don't know if I come too late, but I wrote this for myself :
import { Base64 } from 'js-base64';
import { AsyncStorage } from 'react-native';
export async function storeItem(key: string, item: string, isJson: boolean) {
try {
return new Promise(async resolve => {
let stringObject = '';
if (isJson) {
stringObject = JSON.stringify(item);
} else {
stringObject = item.toString();
}
let base64Object = await Base64.encode(stringObject);
await AsyncStorage.setItem(key, base64Object);
resolve();
});
} catch (e) {
console.log(e);
}
}
export async function retrieveItem(key: string, isJson: boolean) {
try {
return new Promise(async resolve => {
let base64Item = await AsyncStorage.getItem(key);
if (base64Item === null) {
resolve(null);
}
let item = await Base64.decode(base64Item);
resolve(isJson ? JSON.parse(item) : item);
});
} catch (e) {
console.log(e);
}
}
export async function removeItem(key: string) {
try {
return new Promise(async resolve => {
await AsyncStorage.removeItem(key);
resolve();
});
} catch (e) {}
}
I use base64 because of the special characters.
Hope it could helps :)