RTMPPublisher able to get stream ingestion URL and path/key from a web service. Any services.config file (broadcaster settings page) that has a http(s) URL for the stream is assumed to be pointing to a Web API that will return an actual RTMP ingestion URL and path/key.
parent
1b93554034
commit
f764a646d0
|
@ -894,7 +894,13 @@ bool XConfig::ReadFileData2(XElement *curElement, int level, TSTR &lpTemp, bool
|
|||
}
|
||||
}
|
||||
|
||||
++lpTemp;
|
||||
// A ++lpTemp above can step off the end of the string causing
|
||||
// the condition on while to go a bit crazy.
|
||||
// Making sure we preserve the end of string.
|
||||
if (*lpTemp != 0)
|
||||
{
|
||||
++lpTemp;
|
||||
}
|
||||
}
|
||||
|
||||
return (curElement == RootElement);
|
||||
|
@ -1000,6 +1006,27 @@ void XConfig::WriteFileData(XFile &file, int indent, XElement *curElement)
|
|||
}
|
||||
}
|
||||
|
||||
// Basically the same as Open (and in fact Open could/should call ParseString to do its thing)
|
||||
// But ParseString allows chunks of JSON type strings to be parse into the XConfig structure.
|
||||
bool XConfig::ParseString(const String& config)
|
||||
{
|
||||
String safe_copy = config;
|
||||
TSTR lpTemp = safe_copy;
|
||||
|
||||
RootElement = new XElement(this, NULL, TEXT("Root"));
|
||||
|
||||
if(!ReadFileData2(RootElement, 0, lpTemp, true))
|
||||
{
|
||||
for(DWORD i=0; i<RootElement->SubItems.Num(); i++)
|
||||
delete RootElement->SubItems[i];
|
||||
|
||||
CrashError(TEXT("Error parsing X string '%s'"), config.Array());
|
||||
|
||||
Close(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XConfig::Open(CTSTR lpFile)
|
||||
{
|
||||
|
|
|
@ -228,6 +228,7 @@ public:
|
|||
inline ~XConfig() {Close();}
|
||||
|
||||
bool Open(CTSTR lpFile);
|
||||
bool ParseString(const String& config);
|
||||
void Close(bool bSave=false);
|
||||
void Save();
|
||||
|
||||
|
|
|
@ -130,6 +130,111 @@ failure:
|
|||
return ret;
|
||||
}
|
||||
|
||||
String HTTPGetString (CTSTR url, CTSTR extraHeaders, int *responseCode)
|
||||
{
|
||||
HINTERNET hSession = NULL;
|
||||
HINTERNET hConnect = NULL;
|
||||
HINTERNET hRequest = NULL;
|
||||
URL_COMPONENTS urlComponents;
|
||||
BOOL secure = FALSE;
|
||||
String result = "";
|
||||
|
||||
String hostName, path;
|
||||
|
||||
const TCHAR *acceptTypes[] = {
|
||||
TEXT("*/*"),
|
||||
NULL
|
||||
};
|
||||
|
||||
hostName.SetLength(256);
|
||||
path.SetLength(1024);
|
||||
|
||||
zero(&urlComponents, sizeof(urlComponents));
|
||||
|
||||
urlComponents.dwStructSize = sizeof(urlComponents);
|
||||
|
||||
urlComponents.lpszHostName = hostName;
|
||||
urlComponents.dwHostNameLength = hostName.Length();
|
||||
|
||||
urlComponents.lpszUrlPath = path;
|
||||
urlComponents.dwUrlPathLength = path.Length();
|
||||
|
||||
WinHttpCrackUrl(url, 0, 0, &urlComponents);
|
||||
|
||||
if (urlComponents.nPort == 443)
|
||||
secure = TRUE;
|
||||
|
||||
hSession = WinHttpOpen(OBS_VERSION_STRING, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
|
||||
if (!hSession)
|
||||
goto failure;
|
||||
|
||||
hConnect = WinHttpConnect(hSession, hostName, secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT, 0);
|
||||
if (!hConnect)
|
||||
goto failure;
|
||||
|
||||
hRequest = WinHttpOpenRequest(hConnect, TEXT("GET"), path, NULL, WINHTTP_NO_REFERER, acceptTypes, secure ? WINHTTP_FLAG_SECURE|WINHTTP_FLAG_REFRESH : WINHTTP_FLAG_REFRESH);
|
||||
if (!hRequest)
|
||||
goto failure;
|
||||
|
||||
BOOL bResults = WinHttpSendRequest(hRequest, extraHeaders, extraHeaders ? -1 : 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
|
||||
|
||||
// End the request.
|
||||
if (bResults)
|
||||
bResults = WinHttpReceiveResponse(hRequest, NULL);
|
||||
else
|
||||
goto failure;
|
||||
|
||||
TCHAR statusCode[8];
|
||||
DWORD statusCodeLen;
|
||||
|
||||
statusCodeLen = sizeof(statusCode);
|
||||
if (!WinHttpQueryHeaders (hRequest, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, &statusCode, &statusCodeLen, WINHTTP_NO_HEADER_INDEX))
|
||||
goto failure;
|
||||
|
||||
*responseCode = wcstoul(statusCode, NULL, 10);
|
||||
|
||||
if (bResults && *responseCode == 200)
|
||||
{
|
||||
CHAR buffer[16384];
|
||||
DWORD dwSize, dwOutSize;
|
||||
|
||||
do
|
||||
{
|
||||
// Check for available data.
|
||||
dwSize = 0;
|
||||
if (!WinHttpQueryDataAvailable(hRequest, &dwSize))
|
||||
goto failure;
|
||||
|
||||
if (!WinHttpReadData(hRequest, (LPVOID)buffer, dwSize, &dwOutSize))
|
||||
{
|
||||
goto failure;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!dwOutSize)
|
||||
break;
|
||||
|
||||
// Ensure the string is terminated.
|
||||
buffer[dwOutSize] = 0;
|
||||
|
||||
String b = String((LPCSTR)buffer);
|
||||
result.AppendString(b);
|
||||
}
|
||||
} while (dwSize > 0);
|
||||
}
|
||||
|
||||
failure:
|
||||
if (hSession)
|
||||
WinHttpCloseHandle(hSession);
|
||||
if (hConnect)
|
||||
WinHttpCloseHandle(hConnect);
|
||||
if (hRequest)
|
||||
WinHttpCloseHandle(hRequest);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
String CreateHTTPURL(String host, String path, String extra, bool secure)
|
||||
{
|
||||
URL_COMPONENTS components = {
|
||||
|
|
|
@ -21,4 +21,6 @@
|
|||
|
||||
BOOL HTTPGetFile (CTSTR url, CTSTR outputPath, CTSTR extraHeaders, int *responseCode);
|
||||
|
||||
String HTTPGetString (CTSTR url, CTSTR extraHeaders, int *responseCode);
|
||||
|
||||
String CreateHTTPURL(String host, String path, String extra=String(), bool secure=false);
|
|
@ -824,6 +824,7 @@ DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
|
|||
goto end;
|
||||
}
|
||||
|
||||
// A service ID implies the settings have come from the xconfig file.
|
||||
if(serviceID != 0)
|
||||
{
|
||||
XConfig serverData;
|
||||
|
@ -847,6 +848,7 @@ DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
|
|||
XElement *curService = services->GetElementByID(i);
|
||||
if(curService->GetInt(TEXT("id")) == serviceID)
|
||||
{
|
||||
// Found the service in the xconfig file.
|
||||
service = curService;
|
||||
break;
|
||||
}
|
||||
|
@ -858,6 +860,7 @@ DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
|
|||
goto end;
|
||||
}
|
||||
|
||||
// Each service can have many ingestion servers. Look up a server for a particular service.
|
||||
XElement *servers = service->GetElement(TEXT("servers"));
|
||||
if(!servers)
|
||||
{
|
||||
|
@ -865,12 +868,78 @@ DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
|
|||
goto end;
|
||||
}
|
||||
|
||||
// Got the server node now so can look up the ingestion URL.
|
||||
XDataItem *item = servers->GetDataItem(strURL);
|
||||
if(!item)
|
||||
item = servers->GetDataItemByID(0);
|
||||
|
||||
strURL = item->GetData();
|
||||
|
||||
// Stream urls start with RTMP. If there's an HTTP(S) then assume this is a web API call
|
||||
// to get the proper data.
|
||||
if ((strURL.Left(5).MakeLower() == "https") || (strURL.Left(4).MakeLower() == "http"))
|
||||
{
|
||||
// Query the web API for stream details
|
||||
String web_url = strURL + strPlayPath;
|
||||
|
||||
int responseCode;
|
||||
TCHAR extraHeaders[256];
|
||||
|
||||
extraHeaders[0] = 0;
|
||||
|
||||
String response = HTTPGetString(web_url, extraHeaders, &responseCode);
|
||||
|
||||
if (responseCode != 200 && responseCode != 304)
|
||||
{
|
||||
failReason = TEXT("Webserver failed to respond with valid stream details.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
XConfig apiData;
|
||||
|
||||
// Expecting a response from the web API to look like this:
|
||||
// {"data":{"stream_url":"rtmp://some_url", "stream_name": "some-name"}}
|
||||
// A nice bit of JSON which is basically the same as the structure for XConfig.
|
||||
if(!apiData.ParseString(response))
|
||||
{
|
||||
failReason = TEXT("Could not understand response from webserver.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
// We could have read an error string back from the server.
|
||||
// So we need to trap any missing bits of data.
|
||||
|
||||
XElement *p_data = apiData.GetElement(TEXT("data"));
|
||||
|
||||
if (p_data == NULL)
|
||||
{
|
||||
failReason = TEXT("No valid data returned from web server.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
XDataItem *p_stream_url_data = p_data->GetDataItem(TEXT("stream_url"));
|
||||
|
||||
if (p_stream_url_data == NULL)
|
||||
{
|
||||
failReason = TEXT("No valid broadcast stream URL returned from web server.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
strURL = p_stream_url_data->GetData();
|
||||
|
||||
XDataItem *p_stream_name_data = p_data->GetDataItem(TEXT("stream_name"));
|
||||
|
||||
if (p_stream_name_data == NULL)
|
||||
{
|
||||
failReason = TEXT("No valid stream name/path returned from web server.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
strPlayPath = p_stream_name_data->GetData();
|
||||
|
||||
Log(TEXT("Web API returned URL: %s"), strURL.Array());
|
||||
}
|
||||
|
||||
Log(TEXT("Using RTMP service: %s"), service->GetName());
|
||||
Log(TEXT(" Server selection: %s"), strURL.Array());
|
||||
}
|
||||
|
@ -903,6 +972,8 @@ DWORD WINAPI RTMPPublisher::CreateConnectionThread(RTMPPublisher *publisher)
|
|||
goto end;
|
||||
}
|
||||
|
||||
// A user name and password can be kept in the .ini file
|
||||
// If there's some credentials there then they'll be used in the RTMP channel
|
||||
char *rtmpUser = AppConfig->GetString(TEXT("Publish"), TEXT("Username")).CreateUTF8String();
|
||||
char *rtmpPass = AppConfig->GetString(TEXT("Publish"), TEXT("Password")).CreateUTF8String();
|
||||
|
||||
|
|
Loading…
Reference in New Issue