Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #137 from red5pro/feature/two-way-cluster
Added a version of the Two Way test for clustering
- Loading branch information
Showing
5 changed files
with
248 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#Two Way with the Stream Manager | ||
|
||
With clustering, we need to determine which red5 pro instance the client will use. The other examples used a static configuration ip for streaming endpoints. Basic clustering uses more than one stream endpoint for subscribers. Advanced clustering uses more than one endpoint for publishers also. | ||
|
||
With the Stream Manager, our configuration ip will be used similarly for publishers and subscribers. Both publishers and subscribers will call a web service to receive the ip that should be used. | ||
|
||
###Example Code | ||
- ***[BaseTest.swift](../BaseTest.swift)*** | ||
- ***[TwoWayStreamManagerTest.swift](TwoWayStreamManagerTest.swift)*** | ||
|
||
###Running the Example | ||
Like the other Two Way example, you need two devices running it, and the second will need to hit `swap streams` in the home settings, so that they're publishing and subscribing to each other. You will also need to have pointed the app to a properly deployed cluster origin server. | ||
|
||
###Setup | ||
In order to stream, you first need to connect to the origin server's Stream Manager. The Stream Manager will know which edges are active and provide the one that needs to be published to. For the publisher we add the action `broadcast` to the web call, while we send `subscribe` for the subscribers. | ||
|
||
```Swift | ||
let originURI = "http://" + (Testbed.getParameter(param: "host") as! String) + portURI + "/streammanager/api/2.0/event/" + | ||
(Testbed.getParameter(param: "context") as! String) + "/" + streamName + "?action=" + action | ||
|
||
NSURLConnection.sendAsynchronousRequest( | ||
NSURLRequest( url: NSURL(string: originURI)! as URL ) as URLRequest, | ||
queue: OperationQueue(), | ||
completionHandler:{ (response: URLResponse?, data: Data?, error: Error?) -> Void in | ||
``` | ||
<sup> | ||
[TwoWayStreamManagerTest.swift #30](TwoWayStreamManagerTest.swift#L30) | ||
</sup> | ||
|
||
The service returns a json object with the information needed to connect to publish. | ||
|
||
```Swift | ||
do{ | ||
json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as! [String: AnyObject] | ||
}catch{ | ||
print(error) | ||
return | ||
} | ||
|
||
if let ip = json["serverAddress"] as? String { | ||
``` | ||
<sup> | ||
[TwoWayStreamManagerTest.swift #45](TwoWayStreamManagerTest.swift#L45) | ||
</sup> | ||
|
||
###Knowing When to Subscribe | ||
Like with any stream, you can't subscribe to a stream until it's been published. To know what streams are available to subscribe to with clustering, use the `list` function of the Stream Manager api. | ||
|
||
```Swift | ||
let url = "http://" + domain + ":5080/streammanager/api/2.0/event/list" | ||
let request = URLRequest.init(url: URL.init(string: url)!) | ||
|
||
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.init(), completionHandler: { (response: URLResponse?, data: Data?, error: Error?) -> Void in | ||
``` | ||
<sup> | ||
[TwoWayStreamManagerTest.swift #69](TwoWayStreamManagerTest.swift#L69) | ||
</sup> | ||
|
||
Like using `streams.jsp` on a solo server, on success this returns a JSON array of dictionaries. For our purposes, the only property we care about in the dictionary is `name` - as we need to compare it against the name we've set up to subscribe to. | ||
|
||
```Swift | ||
let list = try JSONSerialization.jsonObject(with: data!) as! Array<Dictionary<String, String>>; | ||
|
||
for dict:Dictionary<String, String> in list { | ||
if(dict["name"] == (Testbed.getParameter(param: "stream2") as! String)){ | ||
``` | ||
<sup> | ||
[TwoWayStreamManagerTest.swift #80](TwoWayStreamManagerTest.swift#L80) | ||
</sup> | ||
|
||
For more information on this and other parts of the Stream Manager API, see our dcumentation [here](https://www.red5pro.com/docs/autoscale/streammanagerapi-v2.html) |
159 changes: 159 additions & 0 deletions
159
R5ProTestbed/Tests/TwoWayStreamManager/TwoWayStreamManagerTest.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
// | ||
// TwoWayStreamManagerTest.swift | ||
// R5ProTestbed | ||
// | ||
// Created by David Heimann on 7/2/18. | ||
// Copyright © 2018 Infrared5. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
import R5Streaming | ||
|
||
@objc(TwoWayStreamManagerTest) | ||
class TwoWayStreamManagerTest: BaseTest { | ||
var publishView : R5VideoViewController? = nil | ||
var timer : Timer? = nil | ||
|
||
override func viewDidAppear(_ animated: Bool) { | ||
super.viewDidAppear(animated) | ||
|
||
requestServer(Testbed.getParameter(param: "stream1") as! String, action: "broadcast", resolve: { (url) in | ||
self.publishTo(url: url) | ||
}) | ||
callForStreamList() | ||
} | ||
|
||
func requestServer(_ streamName: String, action: String, resolve: @escaping (_ ip: String) -> Void) { | ||
|
||
let port = (Testbed.getParameter(param: "server_port") as! String) | ||
let portURI = port == "80" ? "" : ":" + port | ||
let originURI = "http://" + (Testbed.getParameter(param: "host") as! String) + portURI + "/streammanager/api/2.0/event/" + | ||
(Testbed.getParameter(param: "context") as! String) + "/" + streamName + "?action=" + action | ||
|
||
NSURLConnection.sendAsynchronousRequest( | ||
NSURLRequest( url: NSURL(string: originURI)! as URL ) as URLRequest, | ||
queue: OperationQueue(), | ||
completionHandler:{ (response: URLResponse?, data: Data?, error: Error?) -> Void in | ||
|
||
if ((error) != nil) { | ||
print(error!) | ||
return | ||
} | ||
|
||
// The string above is in JSON format, we specifically need the serverAddress value | ||
var json: [String: AnyObject] | ||
do{ | ||
json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as! [String: AnyObject] | ||
}catch{ | ||
print(error) | ||
return | ||
} | ||
|
||
if let ip = json["serverAddress"] as? String { | ||
resolve(ip) | ||
} | ||
else if let errorMessage = json["errorMessage"] as? String { | ||
print(AccessError.error(message: errorMessage)) | ||
} | ||
|
||
}) | ||
} | ||
|
||
func delayCallForList() { | ||
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(callForStreamList), userInfo: nil, repeats: false) | ||
} | ||
|
||
func callForStreamList(){ | ||
|
||
let domain = Testbed.getParameter(param: "host") as! String | ||
let url = "http://" + domain + ":5080/streammanager/api/2.0/event/list" | ||
let request = URLRequest.init(url: URL.init(string: url)!) | ||
|
||
NSURLConnection.sendAsynchronousRequest(request, queue: OperationQueue.init(), completionHandler: { (response: URLResponse?, data: Data?, error: Error?) -> Void in | ||
|
||
//parse the response | ||
if (error != nil) { | ||
NSLog("Error, %@", error!.localizedDescription) | ||
} else { | ||
|
||
do{ | ||
let list = try JSONSerialization.jsonObject(with: data!) as! Array<Dictionary<String, String>>; | ||
|
||
for dict:Dictionary<String, String> in list { | ||
if(dict["name"] == (Testbed.getParameter(param: "stream2") as! String)){ | ||
self.requestServer(Testbed.getParameter(param: "stream2") as! String, action: "subscribe", resolve: { (url) in | ||
self.subscribeTo(url: url) | ||
}) | ||
return | ||
} | ||
} | ||
|
||
|
||
}catch let error as NSError { | ||
print(error) | ||
} | ||
} | ||
|
||
self.delayCallForList() | ||
}) | ||
} | ||
|
||
func publishTo( url: String ){ | ||
let config = getConfig() | ||
config.host = url | ||
|
||
// Create a new connection using the configuration above | ||
let connection = R5Connection(config: config) | ||
|
||
// UI updates must be on the main queue | ||
DispatchQueue.main.async(execute: { | ||
// Create our new stream that will utilize that connection | ||
self.setupPublisher(connection: connection!) | ||
|
||
// show preview and debug info | ||
self.publishView!.attach(self.publishStream!) | ||
|
||
self.publishStream!.publish(Testbed.getParameter(param: "stream1") as! String, type: R5RecordTypeLive) | ||
|
||
let label = UILabel(frame: CGRect(x: 0, y: self.view.frame.height-24, width: self.view.frame.width, height: 24)) | ||
label.textAlignment = NSTextAlignment.left | ||
label.backgroundColor = UIColor.lightGray | ||
label.text = "Pub Connected to: " + url | ||
self.view.addSubview(label) | ||
}) | ||
} | ||
|
||
func subscribeTo( url: String ) { | ||
let config = getConfig() | ||
config.host = url | ||
|
||
// Create a new connection using the configuration above | ||
let connection = R5Connection(config: config) | ||
|
||
// UI updates must be on the main queue | ||
DispatchQueue.main.async(execute: { | ||
// Create our new stream that will utilize that connection | ||
self.subscribeStream = R5Stream(connection: connection) | ||
|
||
// show preview and debug info | ||
self.currentView?.attach(self.subscribeStream!) | ||
|
||
self.subscribeStream!.play(Testbed.getParameter(param: "stream2") as! String) | ||
|
||
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 24)) | ||
label.textAlignment = NSTextAlignment.left | ||
label.backgroundColor = UIColor.lightGray | ||
label.text = "Sub Connected to: " + url | ||
self.view.addSubview(label) | ||
}) | ||
} | ||
|
||
override func closeTest() { | ||
|
||
if( self.timer != nil ){ | ||
self.timer!.invalidate(); | ||
} | ||
|
||
super.closeTest() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters