With clustering, we need to determine which Red5 Pro instance the client will use. The other examples used a static configuration address 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.
To enable Adaptive Bitrate (ABR) control of a stream being played back by a consumer, you need to POST a provision to the Stream Manager detailing the variants at which you will be broadcasting.
For scenarios in which the broadcaster does not have the capability of publishing the variants of the provision, the broadcaster can request that the server does the Transcoding to the variants.
To do so - once the provision has been provided to the Stream Manager - the broadcast must be streamed to the root GUID defined on the provision. The Transcoder will that generate the additional variants for consumption.
In order to publish using the transcoder, you first need to provide the Stream Manager with a provision manifest detailing the variants desired to be generated by the transcoder. This is a simple POST request that can be done outside of the actual publishing client - such as using cURL - but for the purposes of this example, the provision is posted prior to the broadcast. Additionally, the POST of the provision is an authenticated request and requires an authorization token first access from the Stream Manager.
The following is an example of the schema for the provision to be posted:
[
{
"streamGuid": "live/test",
"streams": [
{
"streamGuid": "live/test_3",
"abrLevel": 3,
"videoParams": {
"videoWidth": 320,
"videoHeight": 180,
"videoBitRate": 500000
}
},
{
"streamGuid": "live/test_2",
"abrLevel": 2,
"videoParams": {
"videoWidth": 640,
"videoHeight": 360,
"videoBitRate": 1000000
}
},
{
"streamGuid": "live/test_1",
"abrLevel": 1,
"videoParams": {
"videoWidth": 1280,
"videoHeight": 720,
"videoBitRate": 2000000
}
}
]
}
]To request an authorization token:
String host = TestContent.GetPropertyString("host");
String username = TestContent.GetPropertyString("sm_username");
String password = TestContent.GetPropertyString("sm_password");
String url = String.format("https://%s/as/v1/auth/login", host);
String creds = String.format("%s:%s", username, password);
String token = String.format("Basic %s", Base64.getEncoder().encodeToString(creds.getBytes()));
URL url1 = new URL(url);
HttpURLConnection httpURLConnection = (HttpURLConnection) url1.openConnection();
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
httpURLConnection.setChunkedStreamingMode(0);
httpURLConnection.setRequestProperty("Authorization", token);
httpURLConnection.setRequestProperty("Accept", "application/json");
httpURLConnection.setRequestProperty("Content-type", "application/json");
httpURLConnection.setRequestMethod("PUT");
InputStream in = new BufferedInputStream(httpURLConnection.getInputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
StringBuilder result = new StringBuilder();
String line = null;
try {
while ((line = reader.readLine()) != null) {
result.append(line + "\n");
}
final JSONObject jsonObject = new JSONObject(result.toString());
postProvisions(jsonObject.getString("token"), data);
} catch (Exception e) {
throw e;
}Once an authorization token has been received, the provision listing the stream variants can be POSTed:
String host = TestContent.GetPropertyString("host");
String version = TestContent.GetPropertyString("sm_version");
String nodeGroup = TestContent.GetPropertyString("sm_nodegroup");
String url = String.format("https://%s/as/%s/streams/provision/%s", host, version, nodeGroup);
HttpURLConnection conn = null;
try {
URL url1 = new URL(url);
conn = (HttpURLConnection) url1.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setChunkedStreamingMode(0);
conn.setRequestProperty("Authorization", "Bearer " + authToken);
conn.setRequestProperty("Content-type", "application/json");
conn.setRequestMethod("POST");
OutputStream out = conn.getOutputStream();
out.write(json.getBytes());
out.flush();
out.close();
} catch (Exception e) {
throw e;
}In order to publish using the transcoder, you need to request to endpoint for the transcoder from the Stream Manager just as you would in accessing the Origin endpoint in a normal Stream Manager request for broadcast and you will need to do so using the streamGuid from the root of the submitted provision - for the purposes of this example, that is live/test.
String host = TestContent.GetPropertyString("host");
String version = TestContent.GetPropertyString("sm_version");
String nodeGroup = TestContent.GetPropertyString("sm_nodegroup");
String url = String.format("https://%s/as/%s/streams/stream/%s/publish/%s",
host,
version,
nodeGroup,
streamGuid);
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(new HttpGet(url));
StatusLine statusLine = response.getStatusLine();
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
response.getEntity().writeTo(out);
String responseString = out.toString();
out.close();
JSONArray origins = new JSONArray(responseString);
JSONObject data = origins.getJSONObject(0);
final String outURL = data.getString("serverAddress");
if( !outURL.isEmpty() ){
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
publishToManager(outURL, variant);
}
});
}
else {
System.out.println("Server address not returned");
}
}With the Transcoder node to broadcast to, you will most likely begin your broadcast with the settings from the highest variant of the provision - seeing as the transcoder only down-samples to the variants defined.
Providing the top-level stream variant from the provision, you will then request to broadcast with the streamGuid defined within; for the purposes of this example, that would be live/test_1:
private void publishToManager(String url, PublishTranscoderData.StreamVariant variant) {
PublishTranscoderData.VideoParams videoParams = variant.videoParams;
List<String> paths = Arrays.asList(variant.streamGuid.split("/"));
String streamName = paths.get(paths.size() - 1);
paths = paths.subList(0, paths.size() - 1);
String context = String.join("/", paths);
int port = TestContent.GetPropertyInt("port");
R5Configuration config = new R5Configuration(R5StreamProtocol.RTSP,
url,
port,
context,
TestContent.GetPropertyFloat("publish_buffer_time"));
config.setLicenseKey(TestContent.GetPropertyString("license_key"));
config.setBundleID(getActivity().getPackageName());
config.setParameters("transcode=true;");
...
}After successfully starting a broadcast usign the top-level streamGuid as the context and streamName, that stream and the other variant streams will be available to subscribe to.
It should be noted that when starting a subscriber to a stream variant, the server will determine which variant level to deliver to the subscriber client based on bandwidth estimation on the subscriber side.