I want to add an audio stream obtained via getusermedia () to the canvas stream and send it to the remote peer...
So I looked up, and there was addTrack ().
But addTrack () does not seem to work properly. I am running on chrome.
var audioTracks;
navigator.getUserMedia({ "audio": true, "video": false }, function (stream)
{
audioTracks = stream.getAudioTracks()[0];
}, function(error) { console.log(error);});
.
.
.
var sharestream = canvas2.captureStream(25); // 25 FPS
peerConn.addTrack(audioTracks,sharestream);
peerConn.addStream(sharestream);
It's part of my code. What's wrong?
My webrtc full source code works fine, but it does not work with addTrack ().
Updated on 10-29-2018 to replace getAudioTracks with getTracks:
var canvasStream = canvas2d.captureStream(25); // parameter is optional
// get first audio track
// var audioTrack = audioStream.getAudioTracks()[0];
var audioTrack = audioStream.getTracks().filter(function(track) {
return track.kind === 'audio'
})[0];
// append audio track into Canvas2D stream
canvasStream.addTrack( audioTrack );
// now canvas2D stream has both audio and video tracks
// peerConnection.addStream( canvasStream );
canvasStream.getTracks().forEach(function(track) {
peerConnection.Track( track, canvasStream );
});
// create offer or answer descriptions
peerConnection.createOffer(hints).then(success).catch(failure);
Related
I'm trying to make a live stream app and I want the functionality of switching cameras in smartphones. I've followed this article from MDN but I couldn't get the expected results.
The following are the results I'm getting:
Chrome mobile results: Camera switching from front to back but not switching the other way around.
Firefox mobile results: Camera starting the stream with the rear camera(which is not expected) and not switching to the front camera.
I'm using RTCMultiConnection and RecordRTC for this app.
Code:
function switchCamera(stream){
connection.replaceTrack(stream)
video.pause();
video.srcObject = stream;
video.play();
}
var front = true;
$('#switch').on('click', function(e){
e.preventDefault();
e.stopImmediatePropagation();
front = !front;
var constraints = { video: { facingMode: (front? "user" : "environment") } };
var internalRecorder = recorder.getInternalRecorder();
connection.streamEvents.selectFirst({local: true}).stream.getVideoTracks().forEach(function(track){
track.stop();
});
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
if(internalRecorder instanceof MultiStreamRecorder){
internalRecorder.resetVideoStreams(stream)
}
switchCamera(stream)
});
I am trying to record media using the mediaRecorder API. Here is my code (just the relevant part). I expect to get a console log in saveChunks but it appears that the ondataavailable event is never triggered.I am able to see the video in the video element.
recordedChunks = [];
navigator.mediaDevices.getUserMedia({video:true, audio:true})
.then(function(stream) {
myVideoMedia = document.getElementById("vid1");
myVideoMedia.srcObject = stream;
myVideoMedia.onloadedmetadata = function(e) {
myVideoMedia.play();
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.ondataavailable = saveChunks;
mediaRecorder.start();
console.log(mediaRecorder);
};
})
function saveChunks(event) {
console.log("Data recorded...");
//...
};
}
The console log of mediaRecorder.state is 'recording'
I did try by passing a timeslice of 1000 to start() and its working now! If no timeslice is passed, the function is called once at the end.
I'm following the example here: https://www.w3.org/TR/webrtc/#simple-peer-to-peer-example
I've modified the code because I only need one-way streaming:
var configuration = null; //{ "iceServers": [{ "urls": "stuns:stun.example.org" }] };
var peerConnection;
var outboundPeerStream = null;
var outboundPeerStreamSessionId = null;
var createPeerConnection = function () {
if (peerConnection)
return;
peerConnection = new RTCPeerConnection(configuration);
// send any ice candidates to the other peer
peerConnection.onicecandidate = function (event) {
signalrModule.sendClientNotification(JSON.stringify({ "candidate": event.candidate }));
};
// let the "negotiationneeded" event trigger offer generation
peerConnection.onnegotiationneeded = peerStreamingModule.sendOfferToPeers;
// once remote track arrives, show it in the remote video element
peerConnection.ontrack = function (event) {
var inboundPeerStream = event.streams[0];
remoteStreamHelper.pushStreamToDom(inboundPeerStream, foo);
}
}
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer from the stream originator
peerStreamingModule.sendOfferToPeers = function () {
peerConnection.createOffer().then(function (offer) {
return peerConnection.setLocalDescription(offer);
}).then(function () {
// send the offer to the other peer
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription}));
}).catch(logger.internalLog);
};
// this gets called by the stream originator when the stream is available to initiate streaming to peers
peerStreamingModule.initializeWithStream = function (outboundStream, sessionId) {
outboundPeerStream = outboundStream;
outboundPeerStreamSessionId = sessionId;
createPeerConnection();
peerConnection.addStream(outboundStream);
//peerStreamingModule.sendOfferToPeers(); I don't think I need this...
}
peerStreamingModule.handleP2PEvent = function (notification) {
if (!peerConnection)
createPeerConnection();
if (notification.desc) {
var desc = notification.desc;
// if we get an offer, we need to reply with an answer
if (desc.type == "offer") {
peerConnection.setRemoteDescription(desc).then(function () {
return peerConnection.createAnswer();
}).then(function (answer) {
return peerConnection.setLocalDescription(answer);
}).then(function () {
signalrModule.sendClientNotification(JSON.stringify({ "desc": peerConnection.localDescription, "sessionId": sessionManager.thisSession().deviceSessionId() }), app.username());
}).catch(logger.internalLog);
} else if (desc.type == "answer") {
peerConnection.setRemoteDescription(desc).catch(logger.internalLog);
} else {
logger.internalLog("Unsupported SDP type. Your code may differ here.");
}
} else
pc.addIceCandidate(notification.candidate).catch(logger.internalLog);
}
This seems to be working, but I'm stumped with two parts:
1) WebRTC - Failed to set remote answer sdp: Called in wrong state: STATE_INPROGRESS - this is appearing in my logs from time to time - am I doing something wrong in the above that is causing this?
2) Am I correctly implementing sendOfferToPeers and initializeWithStream? I'm afraid that the sendOfferToPeers getting triggered on interval from the originator isn't how the spec is intended to be used; my goal is to ensure that all peers eventually receive an offer no matter when they join or whether or not they're facing connectivity issues that drop the original offer / negotiation.
// this gets called either on negotiationNeeded and every 30s to ensure all peers have the offer
You can't send the same offer to multiple peers. It's peer-to-peer, not peer-to-peers. One-to-many requires at minimum a connection per participant, and probably a media server to scale.
Also, SDP is not for discovery. The offer/answer exchange is a fragile state-machine negotiation between two end-points only, to set up a single connection.
You should solve who's connecting with whom ahead of establishing the WebRTC connection.
I have an embedded Google Map using API V3 but I cannot get it default Zoom to anything other than 1.
My JS in the head is:
var map1;
var src1 = 'https://latitude.google.com/latitude/apps/badge/api?user=8963899225283336226&type=kml';
function initialize1() {
map1 = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 7,
mapTypeId: google.maps.MapTypeId.TERRAIN
});
loadKmlLayer1(src1, map1);
}
google.maps.event.addDomListener(window, 'load', initialize1);
function loadKmlLayer1(src1, map1) {
var kmlLayer1 = new google.maps.KmlLayer(src1, {
suppressInfoWindows: false,
clickable: true,
preserveViewport: false,
map: map1
});
}
The HTML is just the map-canvas div, nothing else. Looking at some of the threads on here it look like its something to do with detecting the viewport and resetting the bounds.
I found a thread that suggested adding something like:
google.maps.event.addListener(kmlLayer1, 'defaultviewport_changed', function() {
var bounds = kmlLayer1.getDefaultViewport();
map.setCenter(bounds.getCenter());
})
but it made no difference. I'm by no means a JS expert and whilst I mostly understand what is going on in most of the code above, I'm not advanced enough to improvise or even understand where it should be placed.
Thanks Molle.
I enhanced to this and it works:
google.maps.event.addListener(kmlLayer, 'status_changed', function () {
console.log('kml loaded:');
google.maps.event.addListenerOnce(map, 'zoom_changed', function () {
console.log('zoom_changed:');
map.setZoom(7);
map.setCenter(new google.maps.LatLng(0, 0));
});
});
The API will set the viewport to contain all KML-features, what will override the zoom-settings.
Reset the zoom once the zoom has changed(as it does when the KML-Layer has been loaded)
google.maps.event.addListenerOnce(map1, 'zoom_changed', function() {
this.setZoom(7);
})
I'm having problems to understand how to implement new WP media uploader into my theme options page. Is there a documentation on how to do this or some explanation what-so-ever? I have seen couple of samples of how to do this but none of them has any good explanation about their code. Is there list of options how to customize media uploader frame? I mean wouldn't it be good if you can do something like this (See // Create the media frame.):
// Uploading files
var file_frame;
jQuery('.upload_image_button').live('click', function() {
// If the media frame already exists, reopen it.
if ( file_frame ) {
file_frame.open();
return;
}
// Create the media frame.
file_frame = wp.media.frames.file_frame = wp.media({
title: 'My frame title',
button: {
text: 'My button text',
},
id: 'logo-frame',
multiple: false,
editing_sidebar: false, // Just added for example
default_tab: 'upload', // Just added for example
tabs: 'upload, library', // Just added for example
returned_image_size: 'thumbnail' // Just added for example
});
// When an image is selected, run a callback.
file_frame.on( 'select', function() {
var attachment;
// We set multiple to false so only get one image from the uploader
attachment = file_frame.state().get('selection').first().toJSON();
// Do something with attachment.id and/or attachment.url here
});
// Finally, open the modal
file_frame.open();
return false
});
For WP 3.5, you can use the new media uploader. I'll be brief in the hopes that you know what you're doing. The idea is to call the wp_enqueue_script (this only works on WP >= 3.5 btw). Once the script is called, you can manipulate the javascript object. You'll have to do some inspecting to see your full set of options.
First you have to enqueue the script:
add_action( 'wp_enqueue_scripts', 'front_upload_enqueues' );
function front_upload_enqueues() {
wp_register_script('uploads',
// path to upload script
get_template_directory_uri().'/lib/js/media-upload.js'
);
wp_enqueue_script('uploads');
if ( function_exists('wp_enqueue_media') ) {
// this enqueues all the media upload stuff
wp_enqueue_media();
}
}
Then you have to add the javascript (jQuery in my case):
jQuery(document).ready(function($){
var frame;
/*
* Upload button click event, which builds the choose-from-library frame.
*
*/
$('.form-table').on('click', '.member-upload-field .btn-upload', function( event ) {
var $el = $(this);
event.preventDefault();
// Create the media frame.
frame = wp.media.frames.customHeader = wp.media({
title: $el.data('choose'),
library: { // remove these to show all
type: 'image', // specific mime
author: userSettings.uid // specific user-posted attachment
},
button: {
text: $el.data('update'), // button text
close: true // whether click closes
}
});
// When an image is selected, run a callback.
frame.on( 'select', function() {
// Grab the selected attachment.
var attachment = frame.state().get('selection').first(),
link = $el.data('updateLink');
$el.prev('input').val( attachment.attributes.id );
$el.parent().prev().find('img').attr('src', attachment.attributes.url );
});
frame.open();
});
});