How do I set the CORS headers for M3U8 file streaming in Chromecast? In my sender (Android) I am setting the Metadata and MediaInfo like this:
metaData = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
metaData.putString(MediaMetadata.KEY_TITLE, "Demo Video");
MediaInfo mediaInfo = new MediaInfo.Builder(
"http://playertest.longtailvideo.com/adaptive/bbbfull/bbbfull.m3u8")
.setContentType("application/vnd.apple.mpegurl")
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
.setMetadata(metaData)
.build();
player.load(client, mediaInfo, true)
.setResultCallback(new ResultCallback<RemoteMediaPlayer.MediaChannelResult>() {
#Override
public void onResult(RemoteMediaPlayer.MediaChannelResult mediaChannelResult) {
Status status = mediaChannelResult.getStatus();
if (status.isSuccess()) {
}
}
});
My onLoad method is set up like this:
mediaManager.onLoad = function(event) {
console.log("### Media Manager - LOAD: " + JSON.stringify(event));
if(mediaPlayer !== null) {
mediaPlayer.unload(); // Ensure unload before loading again
}
if (event.data['media'] && event.data['media']['contentId']) {
var url = event.data['media']['contentId'];
mediaHost = new cast.player.api.Host({
'mediaElement': mediaElement,
'url': url
});
mediaHost.onError = function (errorCode) {
console.error('### HOST ERROR - Fatal Error: code = ' + errorCode);
if (mediaPlayer !== null) {
mediaPlayer.unload();
}
}
var initialTimeIndexSeconds = event.data['media']['currentTime'] || 0;
// TODO: real code would know what content it was going to access and this would not be here.
var protocol = null;
var parser = document.createElement('a');
parser.href = url;
var ext = ext = parser.pathname.split('.').pop();
if (ext === 'm3u8') {
protocol = cast.player.api.CreateHlsStreamingProtocol(mediaHost);
} else if (ext === 'mpd') {
protocol = cast.player.api.CreateDashStreamingProtocol(mediaHost);
} else if (ext === 'ism/') {
protocol = cast.player.api.CreateSmoothStreamingProtocol(mediaHost);
}
console.log('### Media Protocol Identified as ' + ext);
if (protocol === null) {
mediaManager['onLoadOrig'](event); // Call on the original callback
} else {
mediaPlayer = new cast.player.api.Player(mediaHost);
mediaPlayer.load(protocol, initialTimeIndexSeconds);
}
}
}
However, I am getting this error:
XMLHttpRequest cannot load http://playertest.longtailvideo.com/adaptive/bbbfull/bbbfull.m3u8. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '----' is therefore not allowed access.
For Chromecast, how do I set the CORS headers for Chromecast?
Probably too late, but just came across with the same issue and get it done by the approach mentioned below.
I didn't get a way to add headers on sender app side so, sharing my own experience. I get the CORS issue fixed upon firstly confirming that my server is supporting CORS. And then for playing the media on chromecast, i was needed to add gstatic.com and in my case another one as well as allowed domains on server, which is in-fact the whole idea of CORS, that each domain should be known to our server. And that's it.
Note: Be very much sure to go through this official documentation. But for being a beginner it may appear a bit tricky to grab all the stuff from here. So shared own experience as well.
Related
I'm developing a video application with HLS streams.
These streams can only be played if I send in the request https custom headers.
On iOS I do like this:
NSMutableDictionary* headers = [NSMutableDictionary dictionary];
[headers setObject:#"MY_VALUE" forKey:#"MY_KEY"];
AVURLAsset* asset = [AVURLAsset URLAssetWithURL:videoTempURL options:#{#"AVURLAssetHTTPHeaderFieldsKey": headers}];
AVPlayerItem *myNewitem = [[AVPlayerItem alloc] initWithAsset:asset];
and on android like this:
DefaultHttpDataSource.Factory MGSource = new DefaultHttpDataSourceFactory(Util.getUserAgent( MainActivity.getContext(), "MY_USER_AGENT"), BANDWIDTH_METER);
MGSource.getDefaultRequestProperties().set("MY_KEY", "MY_VALUE");
and these methods work very well.
And I want to send these feeds on a ChromeCast.
So I look at how to do on Google Doc and they say this in receiver :
in this function :
sampleplayer.CastPlayer.prototype.loadVideo_ = function(info) {
this.log_('loadVideo_');
var self = this;
var protocolFunc = null;
var url = info.message.media.contentId;
...
host.updateSegmentRequestInfo = function(requestInfo) {
// example of setting CORS withCredentials
requestInfo.withCredentials = true;
// example of setting headers
//requestInfo.headers = {};
//requestInfo.headers['content-type'] = 'text/xml;charset=utf-8';
requestInfo.headers['MY_KEY'] = 'MY_VALUE';
console.log("################# SENDING HEADERS");
};
host.updateManifestRequestInfo = function(requestInfo) {
if (!requestInfo.url) {
requestInfo.url = this.url;
}
requestInfo.withCredentials = true;
};
host.updateLicenseRequestInfo = function(requestInfo) {
requestInfo.withCredentials = true;
};
But that does not work, can someone tell me how I can send custom headers in a URL to a ChromeCast.
Either in the Android sender or in the receiver.
thank you so much
I am very new to webrtc and coding, so apologies if this is not a clear question.
I have followed the Shane Tully example here and amended it to run on my AWS server. Its running find but it only allows me one connection at a time. I would like to have users enter my URL followed by a room name in order to connect to a room.
e.g. www.myurl.com/apple where apple is the room that will be created. Here is an example - if you add /apppl at the end of this URL it will create a room. (The code for this example is rather complex and uses socket.io. where I use ws for Node to create the websockets)
Does anyone have any advice on this? My overall aim is to create an Android App which incorporates video calling functionality, and uses WebView to display the calling feature, which is why I need different URLs for each pair of devices so they both access the same room.
Thank you in advance!
Claire
Server Code:
const HTTPS_PORT = 443;
const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');
const WebSocketServer = WebSocket.Server;
const serverConfig = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
};
// ----------------------------------------------------------------------------------------
// Create a server for the client html page
var handleRequest = function(request, response) {
// Render the single client html file for any request the HTTP server receives
console.log('request received: ' + request.url);
if(request.url === '/') {
response.writeHead(200, {'Content-Type': 'text/html'});
response.end(fs.readFileSync('client/index.html'));
} else if(request.url === '/webrtc.js') {
response.writeHead(200, {'Content-Type': 'application/javascript'});
response.end(fs.readFileSync('client/webrtc.js'));
}
};
var httpsServer = https.createServer(serverConfig, handleRequest);
httpsServer.listen(HTTPS_PORT, '0.0.0.0');
// ----------------------------------------------------------------------------------------
// Create a server for handling websocket calls
var wss = new WebSocketServer({server: httpsServer});
wss.on('connection', function(ws) {
ws.on('message', function(message) {
// Broadcast any received message to all clients
console.log('received: %s', message);
wss.broadcast(message);
});
});
wss.broadcast = function(data) {
this.clients.forEach(function(client) {
if(client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
};
console.log('Server running. Visit https://localhost:' + HTTPS_PORT + ' in Firefox/Chrome (note the HTTPS; there is no HTTP -> HTTPS redirect!)');
//console.log("TEST TEST TEST" + JSON.stringify(room));
Client Code:
var localVideo;
var remoteVideo;
var peerConnection;
var uuid;
var constraints = {
video: true,
audio: true,
};
var peerConnectionConfig = {
'iceServers': [
{'urls': 'stun:stun.services.mozilla.com'},
{'urls': 'stun:stun.l.google.com:19302'},
]
};
function pageReady() {
uuid = uuid();
localVideo = document.getElementById('localVideo');
remoteVideo = document.getElementById('remoteVideo');
serverConnection = new WebSocket('wss://' + window.location.hostname + ':443');
serverConnection.onmessage = gotMessageFromServer;
if(navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia(constraints).then(getUserMediaSuccess).catch(errorHandler);
} else {
alert('Your browser does not support getUserMedia API');
}
}
//CB if it is possible to run gerUserMedia then gets the local video stream
function getUserMediaSuccess(stream) {
localStream = stream;
localVideo.src = window.URL.createObjectURL(stream); //Depreciated
//localVideo.srcObject = stream;
}
//CB this function starts the call
function start(isCaller) {
peerConnection = new RTCPeerConnection(peerConnectionConfig);
peerConnection.onicecandidate = gotIceCandidate;
peerConnection.onaddstream = gotRemoteStream;
//peerConnection.ontrack = gotRemoteStream;
peerConnection.addStream(localStream);
if(isCaller) {
peerConnection.createOffer().then(createdDescription).catch(errorHandler);
}
}
function gotMessageFromServer(message) {
if(!peerConnection) start(false);
var signal = JSON.parse(message.data);
// Ignore messages from ourself
if(signal.uuid == uuid) return;
if(signal.sdp) {
peerConnection.setRemoteDescription(new RTCSessionDescription(signal.sdp)).then(function() {
// Only create answers in response to offers
if(signal.sdp.type == 'offer') {
peerConnection.createAnswer().then(createdDescription).catch(errorHandler);
}
}).catch(errorHandler);
} else if(signal.ice) {
peerConnection.addIceCandidate(new RTCIceCandidate(signal.ice)).catch(errorHandler);
}
}
function gotIceCandidate(event) {
if(event.candidate != null) {
serverConnection.send(JSON.stringify({'ice': event.candidate, 'uuid': uuid}));
}
}
function createdDescription(description) {
console.log('got description');
peerConnection.setLocalDescription(description).then(function() {
serverConnection.send(JSON.stringify({'sdp': peerConnection.localDescription, 'uuid': uuid}));
}).catch(errorHandler);
}
function gotRemoteStream(event) {
console.log('got remote stream');
remoteVideo.src = window.URL.createObjectURL(event.stream);
//remoteVideo.src = event.stream;
}
function errorHandler(error) {
console.log(error);
}
// Taken from http://stackoverflow.com/a/105074/515584
// Strictly speaking, it's not a real UUID, but it gets the job done here
function uuid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}
You need to change the server's code in the example. Right now it is just broadcasting all the messages to all other clients. What you need to do is right a logic at server side to send messages to only those clients with same room id.
I have written a webRTC based p2p scalable broadcast following Shane Tully's example. Click here to see
You can follow from here and get an idea on what i mean by sending signalling messages to specific clients only. In your case message broadcasting should occur only for those clients having the same room id. I hope this helps !!
I'm building an iOS app that Stream's video from Google drive the videos link looks like this
https://drive.google.com/file/d/0B2Kri7-TaAFJSlJ4UTJuSElGamM/preview
The only way to get the Stream Link from the URL above is by Decoding the webView HTML
Code:
let myURLString = "https://drive.google.com/file/d/0B2Kri7-TaAFJSlJ4UTJuSElGamM/preview"
if let myURL = NSURL(string: myURLString) {
do {
let myHTMLString = try String(contentsOfURL: myURL, encoding: NSUTF8StringEncoding)
print("HTML : \(myHTMLString)")
} catch {
print("Error : \(error)")
}
} else {
print("Error: \(myURLString) doesn't URL")
}
after doing that i get the HTML for the webView
The problem is :
What I'm looking for in the HTML is this "fmt_stream_map" this contain is all the Streaming Links that I need too Stream the video but I don't know how to access it and get the Steam Links from it.
PS: I'm Working with an Android Developer and he told me he used this method to solve the issue but he couldn't explain it to me and I don't know java
I fixed the problem using the method :
let myURLString = "https://drive.google.com/file/d/0B1XhqDeOfqG7UWZSaG1ZbFFhSzQ/preview"
if let myURL = NSURL(string: myURLString) {
do {
let myHTMLString = try String(contentsOfURL: myURL, encoding: NSUTF8StringEncoding)
let t = myHTMLString
if let rangeOfZero = t.rangeOfString("plid", options: NSStringCompareOptions.BackwardsSearch) {
let suffix = String(t.characters.suffixFrom(rangeOfZero.endIndex))
// print(suffix)
let input = "\(suffix)"
let detector = try! NSDataDetector(types: NSTextCheckingType.Link.rawValue)
let matches = detector.matchesInString(input, options: [], range: NSRange(location: 0, length: input.utf8.count))
// print(matches)
for match in matches {
let url = (input as NSString).substringWithRange(match.range)
linksA.append(url)
}
theLink()
} else {
print("noooo")
}
} catch {
print("Error : \(error)")
}
} else {
print("Error: \(myURLString) doesn't URL")
}
afther that you need to decode the streaming link using this method :
func theLink() {
/// /[u]00../g
let firstElement = linksA.first
let t = firstElement!.stringByReplacingOccurrencesOfString(",35", withString: "")
let deUrl = t.characters.split{$0 == "|"}.map(String.init)
let link = deUrl[0]
// the link needs to be decoded
let i = link.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
let p = i!.stringByReplacingOccurrencesOfString("%5Cu", withString: "")
// you can see how the link should look like here :
// http://ddecode.com/hexdecoder/?results=d82d4e564eccc1a6b96ee7c5c1e1c3b2
// %252C : ,
// 003d : =
// 0026 : &
let re = p.stringByReplacingOccurrencesOfString("003d", withString: "=")
let y = re.stringByReplacingOccurrencesOfString("0026", withString: "&")
let c = y.stringByReplacingOccurrencesOfString("%252C", withString: ",")
print(c) }
you can see all the code Here
I've been reading the MDN docs about nsicookieservice and I'm interested in the notifications of this service: cookie-changed and cookie-rejected. Does that mean that we can get a list of the changed or rejected cookies? Would that be possible by doing somthing like this:
var cookieSvc = Components.classes["#mozilla.org/cookieService;1"]
.getService(Components.interfaces.nsICookieService);
var cookies=cookieSvc.cookie-changed;
Thank you for the explanation.
I reread the documentation(I really did) and searched in the source codes of many extensions. The problem that the extension that i'm building is bootstarpped for firefox for android but I'm not using SDK. Based on what I understood I wrote this:
var cookieMonster = {
//cookieManager: null,
current: null,
observerService: null,
cookieService: null,
init: function() {
this.observerService = Components.classes["#mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
this.observerService.addObserver(this, "cookie-changed", false);
this.cookieService = Components.classes["#mozilla.org/cookieService;1"].getService(Components.interfaces.nsICookieService);
// this.cookieManager = Components.classes["#mozilla.org/cookiemanager;1"].getService(Components.interfaces.nsICookieManager);
},
uninit: function() {
//var observerService = Components.classes["#mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
this.observerService.removeObserver(this, "cookie-changed", false);
//this.cookieManager = Components.classes["#mozilla.org/cookiemanager;1"].getService(Components.interfaces.nsICookieManager);
},
observe: function(subject, topic, data) {
if (topic != 'cookie-changed') { this.current= "no cookie changed"; return; }
try {
// if (!ghostery.prefs.cookieProtect) { return; }
this.current="there is a chnged cookie!!!";
if ( (data == 'added') || (data == 'changed') ) {
var cookie = subject.QueryInterface(Components.interfaces.nsICookie2);
this.current = "name "+cookie.name+" host "+cookie.host;
}
} catch (err) {}
}
};
Then I do this to add the observer when I load the page:
let addListener = function() {
window.BrowserApp.deck.addEventListener("load", cookieMonster.init, false);
window.NativeWindow.toast.show(cookieMonster.current, "long");
};
if(window.BrowserApp.deck) {
// BrowserApp.deck (and maybe whole BrowserApp?) has been initialized
addListener();
}
else {
// use the chrome window to wait for BrowserApp to initialize
window.BrowserApp.deck.addEventListener("UIReady", addListener);
}
That doesn't work and I don't find any error in the logcat which confuse me more.I'm really stuck and I need your help.
Yes, your add-on can receive notifications about cookies, but you don't receive them via the cookies manager, but the general purpose nsIObserverService.
XUL overlay add-ons should use the nsIObserverService directly.
SDK add-ons will want to use the system/events module instead, which is a bit higher level and does clean up after itself.
There is also a list of the most commonly used notification types.
I have a really weird scenario that I'm stuck on. I have a ASP.Net MVC 4 app where I'm authenticating a user and creating an authCookie and adding it to the response's cookies then redirecting them to the target page:
if (ModelState.IsValid)
{
var userAuthenticated = UserInfo.AuthenticateUser(model.UserName, model.Password);
if (userAuthenticated)
{
var userInfo = UserInfo.FindByUserName(model.UserName);
//SERIALIZE AUTHENTICATED USER
var serializer = new JavaScriptSerializer();
var serializedUser = serializer.Serialize(userInfo);
var ticket = new FormsAuthenticationTicket(1, model.UserName, DateTime.Now, DateTime.Now.AddMinutes(30), false, serializedUser);
var hash = FormsAuthentication.Encrypt(ticket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, hash) {Expires = ticket.Expiration};
Response.Cookies.Add(authCookie);
if (Url.IsLocalUrl(model.ReturnUrl) && model.ReturnUrl.Length > 1 && model.ReturnUrl.StartsWith("/") && !model.ReturnUrl.StartsWith("//") && !model.ReturnUrl.StartsWith("/\\"))
{
return Redirect(model.ReturnUrl);
}
var url = Url.Action("Index", "Course");
return Redirect(url);
}
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
This is working just fine in all browsers. I can login and access the secure pages in my app.
My client is requesting an android version of this app. So, I'm trying to figure out how to convert this app into an APK file. My first attempt is to create a simple index.html page with an iframe that targets the application. This works just fine in Firefox and IE 9. However, when accessing the index.html page that contains the iframe that points to the app via Chrome, I get past the login code above and the user gets redirected to the secure controller, but the secure controller has a custom attribute to make sure the user is authenticated:
public class RequiresAuthenticationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated) return;
if (filterContext.HttpContext.Request.Url == null) return;
var returnUrl = filterContext.HttpContext.Request.Url.AbsolutePath;
if (!filterContext.HttpContext.Request.Browser.IsMobileDevice)
{
filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + string.Format("?ReturnUrl={0}", returnUrl), true);
}
else
{
filterContext.HttpContext.Response.Redirect("/Home/Home", true);
}
}
}
My app is failing on: filterContext.HttpContext.User.Identity.IsAuthenticated. IsAuthenticated is always false, even though the user was authenticated in the code above.
Keep in mind this only happens when accessing the app via iframe in Chrome. If I access the app directly instead of via iframe, then everything works just fine.
Any ideas?
UPDATE:
My controller extends SecureController. In the constructor of SecureController I have the code that deserializes the user:
public SecureController()
{
var context = new HttpContextWrapper(System.Web.HttpContext.Current);
if (context.Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
var serializer = new JavaScriptSerializer();
var cookie = context.Request.Cookies[FormsAuthentication.FormsCookieName].Value;
var ticket = FormsAuthentication.Decrypt(cookie);
CurrentUser = serializer.Deserialize<UserInfo>(ticket.UserData);
}
else
{
CurrentUser = new UserInfo();
}
//if ajax request and session has expired, then force re-login
if (context.Request.IsAjaxRequest() && context.Request.IsAuthenticated == false)
{
context.Response.Clear();
context.Response.StatusCode = 401;
context.Response.Flush();
}
}
First, you should be deriving from AuthorizeAttribute, not an ActionFilterAttribute. Authorization attributes execute before the method is even called at a higher level of the pipeline, while ActionFilters execute much further down, and other attributes can execute before yours.
Secondly, you aren't showing the code you use to decrypt the ticket and set the IPrincipal and IIdentity. Since that's where the problem is, it's odd that you didn't include it.