Refactoring of the HTTP_Connector namespace, starting to add documentation as well.
This commit is contained in:
parent
2e1296cf5e
commit
570aa95315
7 changed files with 233 additions and 159 deletions
12
Doxyfile
12
Doxyfile
|
@ -26,7 +26,7 @@ DOXYFILE_ENCODING = UTF-8
|
||||||
# identify the project. Note that if you do not use Doxywizard you need
|
# identify the project. Note that if you do not use Doxywizard you need
|
||||||
# to put quotes around the project name if it contains spaces.
|
# to put quotes around the project name if it contains spaces.
|
||||||
|
|
||||||
PROJECT_NAME = DDVTECHStreamingServer
|
PROJECT_NAME = MistServer
|
||||||
|
|
||||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
|
||||||
# This could be handy for archiving the generated documentation or
|
# This could be handy for archiving the generated documentation or
|
||||||
|
@ -106,7 +106,7 @@ ABBREVIATE_BRIEF =
|
||||||
# Doxygen will generate a detailed section even if there is only a brief
|
# Doxygen will generate a detailed section even if there is only a brief
|
||||||
# description.
|
# description.
|
||||||
|
|
||||||
ALWAYS_DETAILED_SEC = NO
|
ALWAYS_DETAILED_SEC = YES
|
||||||
|
|
||||||
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
|
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
|
||||||
# inherited members of a class in the documentation of that class as if those
|
# inherited members of a class in the documentation of that class as if those
|
||||||
|
@ -151,7 +151,7 @@ SHORT_NAMES = NO
|
||||||
# comments will behave just like regular Qt-style comments
|
# comments will behave just like regular Qt-style comments
|
||||||
# (thus requiring an explicit @brief command for a brief description.)
|
# (thus requiring an explicit @brief command for a brief description.)
|
||||||
|
|
||||||
JAVADOC_AUTOBRIEF = YES
|
JAVADOC_AUTOBRIEF = NO
|
||||||
|
|
||||||
# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
|
# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
|
||||||
# interpret the first line (until the first dot) of a Qt-style
|
# interpret the first line (until the first dot) of a Qt-style
|
||||||
|
@ -471,7 +471,7 @@ SORT_MEMBER_DOCS = YES
|
||||||
# by member name. If set to NO (the default) the members will appear in
|
# by member name. If set to NO (the default) the members will appear in
|
||||||
# declaration order.
|
# declaration order.
|
||||||
|
|
||||||
SORT_BRIEF_DOCS = NO
|
SORT_BRIEF_DOCS = YES
|
||||||
|
|
||||||
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
|
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
|
||||||
# will sort the (brief and detailed) documentation of class members so that
|
# will sort the (brief and detailed) documentation of class members so that
|
||||||
|
@ -481,7 +481,7 @@ SORT_BRIEF_DOCS = NO
|
||||||
# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
|
# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
|
||||||
# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
|
# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
|
||||||
|
|
||||||
SORT_MEMBERS_CTORS_1ST = NO
|
SORT_MEMBERS_CTORS_1ST = YES
|
||||||
|
|
||||||
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
|
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
|
||||||
# hierarchy of group names into alphabetical order. If set to NO (the default)
|
# hierarchy of group names into alphabetical order. If set to NO (the default)
|
||||||
|
@ -629,7 +629,7 @@ WARN_IF_DOC_ERROR = YES
|
||||||
# wrong or incomplete parameter documentation, but not about the absence of
|
# wrong or incomplete parameter documentation, but not about the absence of
|
||||||
# documentation.
|
# documentation.
|
||||||
|
|
||||||
WARN_NO_PARAMDOC = NO
|
WARN_NO_PARAMDOC = YES
|
||||||
|
|
||||||
# The WARN_FORMAT tag determines the format of the warning messages that
|
# The WARN_FORMAT tag determines the format of the warning messages that
|
||||||
# doxygen can produce. The string should contain the $file, $line, and $text
|
# doxygen can produce. The string should contain the $file, $line, and $text
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -10,17 +12,18 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <set>
|
|
||||||
#include <mist/socket.h>
|
#include <mist/socket.h>
|
||||||
#include <mist/http_parser.h>
|
#include <mist/http_parser.h>
|
||||||
#include <mist/config.h>
|
#include <mist/config.h>
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/timing.h>
|
#include <mist/timing.h>
|
||||||
#include <mist/auth.h>
|
#include <mist/auth.h>
|
||||||
|
|
||||||
#include "tinythread.h"
|
#include "tinythread.h"
|
||||||
#include "embed.js.h"
|
#include "embed.js.h"
|
||||||
|
|
||||||
/// Holds everything unique to HTTP Connector.
|
/// Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
|
|
||||||
/// Class for keeping track of connections to connectors.
|
/// Class for keeping track of connections to connectors.
|
||||||
|
@ -60,7 +63,7 @@ namespace Connector_HTTP {
|
||||||
tthread::mutex timeout_mutex; ///< Mutex for timeout thread.
|
tthread::mutex timeout_mutex; ///< Mutex for timeout thread.
|
||||||
tthread::thread * timeouter = 0; ///< Thread that times out connections to connectors.
|
tthread::thread * timeouter = 0; ///< Thread that times out connections to connectors.
|
||||||
|
|
||||||
void Timeout_Thread(void * n){
|
void proxyTimeoutThread(void * n){
|
||||||
n = 0; //prevent unused variable warning
|
n = 0; //prevent unused variable warning
|
||||||
tthread::lock_guard<tthread::mutex> guard(timeout_mutex);
|
tthread::lock_guard<tthread::mutex> guard(timeout_mutex);
|
||||||
while (true){
|
while (true){
|
||||||
|
@ -90,7 +93,7 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles requests without associated handler, displaying a nice friendly error message.
|
/// Handles requests without associated handler, displaying a nice friendly error message.
|
||||||
long long int Handle_None(HTTP::Parser & H, Socket::Connection * conn){
|
long long int proxyHandleUnsupported(HTTP::Parser & H, Socket::Connection * conn){
|
||||||
H.Clean();
|
H.Clean();
|
||||||
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);
|
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);
|
||||||
H.SetBody(
|
H.SetBody(
|
||||||
|
@ -100,7 +103,7 @@ namespace Connector_HTTP {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long int Handle_Timeout(HTTP::Parser & H, Socket::Connection * conn){
|
long long int proxyHandleTimeout(HTTP::Parser & H, Socket::Connection * conn){
|
||||||
H.Clean();
|
H.Clean();
|
||||||
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);
|
H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver);
|
||||||
H.SetBody(
|
H.SetBody(
|
||||||
|
@ -111,7 +114,7 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles internal requests.
|
/// Handles internal requests.
|
||||||
long long int Handle_Internal(HTTP::Parser & H, Socket::Connection * conn){
|
long long int proxyHandleInternal(HTTP::Parser & H, Socket::Connection * conn){
|
||||||
|
|
||||||
std::string url = H.getUrl();
|
std::string url = H.getUrl();
|
||||||
|
|
||||||
|
@ -257,11 +260,11 @@ namespace Connector_HTTP {
|
||||||
return ret;
|
return ret;
|
||||||
} //embed code generator
|
} //embed code generator
|
||||||
|
|
||||||
return Handle_None(H, conn); //anything else doesn't get handled
|
return proxyHandleUnsupported(H, conn); //anything else doesn't get handled
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles requests without associated handler, displaying a nice friendly error message.
|
/// Handles requests without associated handler, displaying a nice friendly error message.
|
||||||
long long int Handle_Through_Connector(HTTP::Parser & H, Socket::Connection * conn, std::string & connector){
|
long long int proxyHandleThroughConnector(HTTP::Parser & H, Socket::Connection * conn, std::string & connector){
|
||||||
//create a unique ID based on a hash of the user agent and host, followed by the stream name and connector
|
//create a unique ID based on a hash of the user agent and host, followed by the stream name and connector
|
||||||
std::string uid = Secure::md5(H.GetHeader("User-Agent") + conn->getHost()) + "_" + H.GetVar("stream") + "_" + connector;
|
std::string uid = Secure::md5(H.GetHeader("User-Agent") + conn->getHost()) + "_" + H.GetVar("stream") + "_" + connector;
|
||||||
H.SetHeader("X-Stream", H.GetVar("stream"));
|
H.SetHeader("X-Stream", H.GetVar("stream"));
|
||||||
|
@ -293,7 +296,7 @@ namespace Connector_HTTP {
|
||||||
timeouter->join();
|
timeouter->join();
|
||||||
delete timeouter;
|
delete timeouter;
|
||||||
}
|
}
|
||||||
timeouter = new tthread::thread(Connector_HTTP::Timeout_Thread, 0);
|
timeouter = new tthread::thread(Connector_HTTP::proxyTimeoutThread, 0);
|
||||||
timeout_mutex.unlock();
|
timeout_mutex.unlock();
|
||||||
}
|
}
|
||||||
conn_mutex.unlock();
|
conn_mutex.unlock();
|
||||||
|
@ -303,7 +306,7 @@ namespace Connector_HTTP {
|
||||||
//if the server connection is dead, handle as timeout.
|
//if the server connection is dead, handle as timeout.
|
||||||
if ( !connconn.count(uid) || !connconn[uid]->conn->connected()){
|
if ( !connconn.count(uid) || !connconn[uid]->conn->connected()){
|
||||||
connconn[uid]->conn->close();
|
connconn[uid]->conn->close();
|
||||||
return Handle_Timeout(H, conn);
|
return proxyHandleTimeout(H, conn);
|
||||||
}
|
}
|
||||||
//forward the original request
|
//forward the original request
|
||||||
connconn[uid]->conn->SendNow(request);
|
connconn[uid]->conn->SendNow(request);
|
||||||
|
@ -332,7 +335,7 @@ namespace Connector_HTTP {
|
||||||
if (retries >= 5){
|
if (retries >= 5){
|
||||||
std::cout << "[5 retry-laters, cancelled]" << std::endl;
|
std::cout << "[5 retry-laters, cancelled]" << std::endl;
|
||||||
connconn[uid]->conn->close();
|
connconn[uid]->conn->close();
|
||||||
return Handle_Timeout(H, conn);
|
return proxyHandleTimeout(H, conn);
|
||||||
}
|
}
|
||||||
connconn[uid]->lastuse = 0;
|
connconn[uid]->lastuse = 0;
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
|
@ -348,7 +351,7 @@ namespace Connector_HTTP {
|
||||||
if (timeout++ > 4000){
|
if (timeout++ > 4000){
|
||||||
std::cout << "[20s timeout triggered]" << std::endl;
|
std::cout << "[20s timeout triggered]" << std::endl;
|
||||||
connconn[uid]->conn->close();
|
connconn[uid]->conn->close();
|
||||||
return Handle_Timeout(H, conn);
|
return proxyHandleTimeout(H, conn);
|
||||||
}else{
|
}else{
|
||||||
Util::sleep(5);
|
Util::sleep(5);
|
||||||
}
|
}
|
||||||
|
@ -357,7 +360,7 @@ namespace Connector_HTTP {
|
||||||
if ( !connconn.count(uid) || !connconn[uid]->conn->connected() || !conn->connected()){
|
if ( !connconn.count(uid) || !connconn[uid]->conn->connected() || !conn->connected()){
|
||||||
//failure, disconnect and sent error to user
|
//failure, disconnect and sent error to user
|
||||||
connconn[uid]->conn->close();
|
connconn[uid]->conn->close();
|
||||||
return Handle_Timeout(H, conn);
|
return proxyHandleTimeout(H, conn);
|
||||||
}else{
|
}else{
|
||||||
long long int ret = Util::getMS();
|
long long int ret = Util::getMS();
|
||||||
//success, check type of response
|
//success, check type of response
|
||||||
|
@ -399,7 +402,7 @@ namespace Connector_HTTP {
|
||||||
/// - internal (request fed from information internal to this connector)
|
/// - internal (request fed from information internal to this connector)
|
||||||
/// - dynamic (request fed from http_dynamic connector)
|
/// - dynamic (request fed from http_dynamic connector)
|
||||||
/// - progressive (request fed from http_progressive connector)
|
/// - progressive (request fed from http_progressive connector)
|
||||||
std::string getHTTPType(HTTP::Parser & H){
|
std::string proxyGetHandleType(HTTP::Parser & H){
|
||||||
std::string url = H.getUrl();
|
std::string url = H.getUrl();
|
||||||
if (url.find("/dynamic/") != std::string::npos){
|
if (url.find("/dynamic/") != std::string::npos){
|
||||||
std::string streamname = url.substr(9, url.find("/", 9) - 9);
|
std::string streamname = url.substr(9, url.find("/", 9) - 9);
|
||||||
|
@ -447,7 +450,7 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Thread for handling a single HTTP connection
|
/// Thread for handling a single HTTP connection
|
||||||
void Handle_HTTP_Connection(void * pointer){
|
void proxyHandleHTTPConnection(void * pointer){
|
||||||
Socket::Connection * conn = (Socket::Connection *)pointer;
|
Socket::Connection * conn = (Socket::Connection *)pointer;
|
||||||
conn->setBlocking(false); //do not block on conn.spool() when no data is available
|
conn->setBlocking(false); //do not block on conn.spool() when no data is available
|
||||||
HTTP::Parser Client;
|
HTTP::Parser Client;
|
||||||
|
@ -464,7 +467,7 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Client.Read(conn->Received().get())){
|
if (Client.Read(conn->Received().get())){
|
||||||
std::string handler = getHTTPType(Client);
|
std::string handler = proxyGetHandleType(Client);
|
||||||
#if DEBUG >= 4
|
#if DEBUG >= 4
|
||||||
std::cout << "Received request: " << Client.getUrl() << " (" << conn->getSocket() << ") => " << handler << " (" << Client.GetVar("stream")
|
std::cout << "Received request: " << Client.getUrl() << " (" << conn->getSocket() << ") => " << handler << " (" << Client.GetVar("stream")
|
||||||
<< ")" << std::endl;
|
<< ")" << std::endl;
|
||||||
|
@ -478,12 +481,12 @@ namespace Connector_HTTP {
|
||||||
|
|
||||||
if (handler == "none" || handler == "internal"){
|
if (handler == "none" || handler == "internal"){
|
||||||
if (handler == "internal"){
|
if (handler == "internal"){
|
||||||
midms = Handle_Internal(Client, conn);
|
midms = proxyHandleInternal(Client, conn);
|
||||||
}else{
|
}else{
|
||||||
midms = Handle_None(Client, conn);
|
midms = proxyHandleUnsupported(Client, conn);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
midms = Handle_Through_Connector(Client, conn, handler);
|
midms = proxyHandleThroughConnector(Client, conn, handler);
|
||||||
}
|
}
|
||||||
#if DEBUG >= 4
|
#if DEBUG >= 4
|
||||||
long long int nowms = Util::getMS();
|
long long int nowms = Util::getMS();
|
||||||
|
@ -531,7 +534,7 @@ int main(int argc, char ** argv){
|
||||||
if (S.connected()){ //check if the new connection is valid
|
if (S.connected()){ //check if the new connection is valid
|
||||||
//lock the thread mutex and spawn a new thread for this connection
|
//lock the thread mutex and spawn a new thread for this connection
|
||||||
Connector_HTTP::thread_mutex.lock();
|
Connector_HTTP::thread_mutex.lock();
|
||||||
tthread::thread * T = new tthread::thread(Connector_HTTP::Handle_HTTP_Connection, (void *)(new Socket::Connection(S)));
|
tthread::thread * T = new tthread::thread(Connector_HTTP::proxyHandleHTTPConnection, (void *)(new Socket::Connection(S)));
|
||||||
Connector_HTTP::active_threads.insert(T);
|
Connector_HTTP::active_threads.insert(T);
|
||||||
//clean up any threads that may have finished
|
//clean up any threads that may have finished
|
||||||
while ( !Connector_HTTP::done_threads.empty()){
|
while ( !Connector_HTTP::done_threads.empty()){
|
||||||
|
|
|
@ -23,10 +23,14 @@
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/timing.h>
|
#include <mist/timing.h>
|
||||||
|
|
||||||
/// Holds everything unique to HTTP Dynamic Connector.
|
/// Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
|
///\brief Builds a bootstrap for use in HTTP Dynamic streaming.
|
||||||
std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum = 0){
|
///\param MovieId The name of the movie.
|
||||||
|
///\param metadata The current metadata, used to generate the index.
|
||||||
|
///\param fragnum The index of the current fragment
|
||||||
|
///\return The generated bootstrap.
|
||||||
|
std::string dynamicBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum = 0){
|
||||||
std::string empty;
|
std::string empty;
|
||||||
|
|
||||||
MP4::ASRT asrt;
|
MP4::ASRT asrt;
|
||||||
|
@ -86,8 +90,11 @@ namespace Connector_HTTP {
|
||||||
return std::string((char*)abst.asBox(), (int)abst.boxedSize());
|
return std::string((char*)abst.asBox(), (int)abst.boxedSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a F4M-format manifest file
|
///\brief Builds an index file for HTTP Dynamic streaming.
|
||||||
std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
|
///\param MovieId The name of the movie.
|
||||||
|
///\param metadata The current metadata, used to generate the index.
|
||||||
|
///\return The index file for HTTP Dynamic Streaming.
|
||||||
|
std::string dynamicIndex(std::string & MovieId, JSON::Value & metadata){
|
||||||
std::string Result;
|
std::string Result;
|
||||||
if (metadata.isMember("vod")){
|
if (metadata.isMember("vod")){
|
||||||
Result =
|
Result =
|
||||||
|
@ -100,7 +107,7 @@ namespace Connector_HTTP {
|
||||||
"<mimeType>video/mp4</mimeType>\n"
|
"<mimeType>video/mp4</mimeType>\n"
|
||||||
"<streamType>recorded</streamType>\n"
|
"<streamType>recorded</streamType>\n"
|
||||||
"<deliveryType>streaming</deliveryType>\n"
|
"<deliveryType>streaming</deliveryType>\n"
|
||||||
"<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(GenerateBootstrap(MovieId, metadata)) + "</bootstrapInfo>\n"
|
"<bootstrapInfo profile=\"named\" id=\"bootstrap1\">" + Base64::encode(dynamicBootstrap(MovieId, metadata)) + "</bootstrapInfo>\n"
|
||||||
"<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\">\n"
|
"<media streamId=\"1\" bootstrapInfoId=\"bootstrap1\" url=\"" + MovieId + "/\">\n"
|
||||||
"<metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>\n"
|
"<metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>\n"
|
||||||
"</media>\n"
|
"</media>\n"
|
||||||
|
@ -126,8 +133,10 @@ namespace Connector_HTTP {
|
||||||
return Result;
|
return Result;
|
||||||
} //BuildManifest
|
} //BuildManifest
|
||||||
|
|
||||||
/// Main function for Connector_HTTP_Dynamic
|
///\brief Main function for the HTTP Dynamic Connector
|
||||||
int Connector_HTTP_Dynamic(Socket::Connection conn){
|
///\param conn A socket describing the connection the client.
|
||||||
|
///\return The exit code of the connector.
|
||||||
|
int dynamicConnector(Socket::Connection conn){
|
||||||
std::deque<std::string> FlashBuf;
|
std::deque<std::string> FlashBuf;
|
||||||
int FlashBufSize = 0;
|
int FlashBufSize = 0;
|
||||||
long long int FlashBufTime = 0;
|
long long int FlashBufTime = 0;
|
||||||
|
@ -184,7 +193,7 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
if (HTTP_R.url.find(".abst") != std::string::npos){
|
if (HTTP_R.url.find(".abst") != std::string::npos){
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetBody(GenerateBootstrap(streamname, Strm.metadata));
|
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.metadata));
|
||||||
HTTP_S.SetHeader("Content-Type", "binary/octet");
|
HTTP_S.SetHeader("Content-Type", "binary/octet");
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
|
@ -232,7 +241,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetHeader("Content-Type", "text/xml");
|
HTTP_S.SetHeader("Content-Type", "text/xml");
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
std::string manifest = BuildManifest(streamname, Strm.metadata);
|
std::string manifest = dynamicIndex(streamname, Strm.metadata);
|
||||||
HTTP_S.SetBody(manifest);
|
HTTP_S.SetBody(manifest);
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
}
|
}
|
||||||
|
@ -254,7 +263,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||||
HTTP_S.SetBody("");
|
HTTP_S.SetBody("");
|
||||||
std::string new_strap = GenerateBootstrap(streamname, Strm.metadata, ReqFragment);
|
std::string new_strap = dynamicBootstrap(streamname, Strm.metadata, ReqFragment);
|
||||||
HTTP_S.SetHeader("Content-Length", FlashBufSize + 8 + new_strap.size()); //32+33+btstrp.size());
|
HTTP_S.SetHeader("Content-Length", FlashBufSize + 8 + new_strap.size()); //32+33+btstrp.size());
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
conn.SendNow(new_strap);
|
conn.SendNow(new_strap);
|
||||||
|
@ -305,6 +314,7 @@ namespace Connector_HTTP {
|
||||||
|
|
||||||
} //Connector_HTTP_Dynamic namespace
|
} //Connector_HTTP_Dynamic namespace
|
||||||
|
|
||||||
|
///\brief The standard process-spawning main function.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char ** argv){
|
||||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||||
conf.addConnectorOptions(1935);
|
conf.addConnectorOptions(1935);
|
||||||
|
@ -320,7 +330,7 @@ int main(int argc, char ** argv){
|
||||||
if (S.connected()){ //check if the new connection is valid
|
if (S.connected()){ //check if the new connection is valid
|
||||||
pid_t myid = fork();
|
pid_t myid = fork();
|
||||||
if (myid == 0){ //if new child, start MAINHANDLER
|
if (myid == 0){ //if new child, start MAINHANDLER
|
||||||
return Connector_HTTP::Connector_HTTP_Dynamic(S);
|
return Connector_HTTP::dynamicConnector(S);
|
||||||
}else{ //otherwise, do nothing or output debugging text
|
}else{ //otherwise, do nothing or output debugging text
|
||||||
#if DEBUG >= 3
|
#if DEBUG >= 3
|
||||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||||
|
|
|
@ -25,9 +25,11 @@
|
||||||
|
|
||||||
/// Holds everything unique to HTTP Connectors.
|
/// Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
|
///\brief Builds an index file for HTTP Live streaming.
|
||||||
/// Returns a m3u or m3u8 index file
|
///\param MovieId The name of the movie.
|
||||||
std::string BuildIndex(std::string & MovieId, JSON::Value & metadata){
|
///\param metadata The current metadata, used to generate the index.
|
||||||
|
///\return The index file for HTTP Live Streaming.
|
||||||
|
std::string liveIndex(std::string & MovieId, JSON::Value & metadata){
|
||||||
std::stringstream Result;
|
std::stringstream Result;
|
||||||
if ( !metadata.isMember("live")){
|
if ( !metadata.isMember("live")){
|
||||||
int longestFragment = 0;
|
int longestFragment = 0;
|
||||||
|
@ -58,10 +60,12 @@ namespace Connector_HTTP {
|
||||||
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
||||||
#endif
|
#endif
|
||||||
return Result.str();
|
return Result.str();
|
||||||
} //BuildIndex
|
} //liveIndex
|
||||||
|
|
||||||
/// Main function for Connector_HTTP_Live
|
///\brief Main function for the HTTP HLS Connector
|
||||||
int Connector_HTTP_Live(Socket::Connection conn){
|
///\param conn A socket describing the connection the client.
|
||||||
|
///\return The exit code of the connector.
|
||||||
|
int liveConnector(Socket::Connection conn){
|
||||||
std::stringstream TSBuf;
|
std::stringstream TSBuf;
|
||||||
long long int TSBufTime = 0;
|
long long int TSBufTime = 0;
|
||||||
|
|
||||||
|
@ -171,7 +175,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetHeader("Content-Type", manifestType);
|
HTTP_S.SetHeader("Content-Type", manifestType);
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
std::string manifest = BuildIndex(streamname, Strm.metadata);
|
std::string manifest = liveIndex(streamname, Strm.metadata);
|
||||||
HTTP_S.SetBody(manifest);
|
HTTP_S.SetBody(manifest);
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
}
|
}
|
||||||
|
@ -292,10 +296,11 @@ namespace Connector_HTTP {
|
||||||
fprintf(stderr, "HLS: User %i disconnected.\n", conn.getSocket());
|
fprintf(stderr, "HLS: User %i disconnected.\n", conn.getSocket());
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
} //Connector_HTTP_Dynamic main function
|
} //HLS_Connector main function
|
||||||
|
|
||||||
} //Connector_HTTP_Dynamic namespace
|
} //Connector_HTTP namespace
|
||||||
|
|
||||||
|
///\brief The standard process-spawning main function.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char ** argv){
|
||||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||||
conf.addConnectorOptions(1935);
|
conf.addConnectorOptions(1935);
|
||||||
|
@ -311,7 +316,7 @@ int main(int argc, char ** argv){
|
||||||
if (S.connected()){ //check if the new connection is valid
|
if (S.connected()){ //check if the new connection is valid
|
||||||
pid_t myid = fork();
|
pid_t myid = fork();
|
||||||
if (myid == 0){ //if new child, start MAINHANDLER
|
if (myid == 0){ //if new child, start MAINHANDLER
|
||||||
return Connector_HTTP::Connector_HTTP_Live(S);
|
return Connector_HTTP::liveConnector(S);
|
||||||
}else{ //otherwise, do nothing or output debugging text
|
}else{ //otherwise, do nothing or output debugging text
|
||||||
#if DEBUG >= 5
|
#if DEBUG >= 5
|
||||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
/// \file conn_http_progressive.cpp
|
///\file conn_http_progressive.cpp
|
||||||
/// Contains the main code for the HTTP Progressive Connector
|
///\brief Contains the main code for the HTTP Progressive Connector
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -10,7 +12,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <sstream>
|
|
||||||
#include <mist/socket.h>
|
#include <mist/socket.h>
|
||||||
#include <mist/http_parser.h>
|
#include <mist/http_parser.h>
|
||||||
#include <mist/dtsc.h>
|
#include <mist/dtsc.h>
|
||||||
|
@ -20,28 +22,29 @@
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/timing.h>
|
#include <mist/timing.h>
|
||||||
|
|
||||||
/// Holds everything unique to HTTP Progressive Connector.
|
///\brief Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
|
///\brief Main function for the HTTP Progressive Connector
|
||||||
|
///\param conn A socket describing the connection the client.
|
||||||
|
///\return The exit code of the connector.
|
||||||
|
int progressiveConnector(Socket::Connection conn){
|
||||||
|
bool progressive_has_sent_header = false;//Indicates whether we have sent a header.
|
||||||
|
bool ready4data = false; //Set to true when streaming is to begin.
|
||||||
|
DTSC::Stream Strm; //Incoming stream buffer.
|
||||||
|
HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
|
||||||
|
bool inited = false;//Whether the stream is initialized
|
||||||
|
Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
|
||||||
|
std::string streamname;//Will contain the name of the stream.
|
||||||
|
FLV::Tag tag;//Temporary tag buffer.
|
||||||
|
|
||||||
/// Main function for Connector_HTTP_Progressive
|
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
||||||
int Connector_HTTP_Progressive(Socket::Connection conn){
|
unsigned int seek_sec = 0;//Seek position in ms
|
||||||
bool progressive_has_sent_header = false;
|
unsigned int seek_byte = 0;//Seek position in bytes
|
||||||
bool ready4data = false; ///< Set to true when streaming is to begin.
|
|
||||||
DTSC::Stream Strm; ///< Incoming stream buffer.
|
|
||||||
HTTP::Parser HTTP_R, HTTP_S; ///<HTTP Receiver en HTTP Sender.
|
|
||||||
bool inited = false;
|
|
||||||
Socket::Connection ss( -1);
|
|
||||||
std::string streamname;
|
|
||||||
FLV::Tag tag; ///< Temporary tag buffer.
|
|
||||||
|
|
||||||
unsigned int lastStats = 0;
|
|
||||||
unsigned int seek_sec = 0; //seek position in ms
|
|
||||||
unsigned int seek_byte = 0; //seek position in bytes
|
|
||||||
|
|
||||||
bool isMP3 = false;
|
bool isMP3 = false;//Indicates whether the request is audio-only mp3.
|
||||||
|
|
||||||
while (conn.connected()){
|
while (conn.connected()){
|
||||||
//only parse input if available or not yet init'ed
|
//Only attempt to parse input when not yet init'ed.
|
||||||
if ( !inited){
|
if ( !inited){
|
||||||
if (conn.Received().size() || conn.spool()){
|
if (conn.Received().size() || conn.spool()){
|
||||||
//make sure it ends in a \n
|
//make sure it ends in a \n
|
||||||
|
@ -192,10 +195,11 @@ namespace Connector_HTTP {
|
||||||
ss.SendNow(conn.getStats("HTTP_Dynamic").c_str());
|
ss.SendNow(conn.getStats("HTTP_Dynamic").c_str());
|
||||||
ss.close();
|
ss.close();
|
||||||
return 0;
|
return 0;
|
||||||
} //Connector_HTTP main function
|
} //Progressive_Connector main function
|
||||||
|
|
||||||
} //Connector_HTTP namespace
|
} //Connector_HTTP namespace
|
||||||
|
|
||||||
|
///\brief The standard process-spawning main function.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char ** argv){
|
||||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||||
conf.addConnectorOptions(1935);
|
conf.addConnectorOptions(1935);
|
||||||
|
@ -211,7 +215,7 @@ int main(int argc, char ** argv){
|
||||||
if (S.connected()){ //check if the new connection is valid
|
if (S.connected()){ //check if the new connection is valid
|
||||||
pid_t myid = fork();
|
pid_t myid = fork();
|
||||||
if (myid == 0){ //if new child, start MAINHANDLER
|
if (myid == 0){ //if new child, start MAINHANDLER
|
||||||
return Connector_HTTP::Connector_HTTP_Progressive(S);
|
return Connector_HTTP::progressiveConnector(S);
|
||||||
}else{ //otherwise, do nothing or output debugging text
|
}else{ //otherwise, do nothing or output debugging text
|
||||||
#if DEBUG >= 5
|
#if DEBUG >= 5
|
||||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
/// \file conn_http_dynamic.cpp
|
///\file conn_http_smooth.cpp
|
||||||
/// Contains the main code for the HTTP Dynamic Connector
|
///\brief Contains the main code for the HTTP Smooth Connector
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -11,6 +13,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
#include <mist/socket.h>
|
#include <mist/socket.h>
|
||||||
#include <mist/http_parser.h>
|
#include <mist/http_parser.h>
|
||||||
#include <mist/json.h>
|
#include <mist/json.h>
|
||||||
|
@ -19,36 +22,58 @@
|
||||||
#include <mist/amf.h>
|
#include <mist/amf.h>
|
||||||
#include <mist/mp4.h>
|
#include <mist/mp4.h>
|
||||||
#include <mist/config.h>
|
#include <mist/config.h>
|
||||||
#include <sstream>
|
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/timing.h>
|
#include <mist/timing.h>
|
||||||
|
|
||||||
/// Holds everything unique to HTTP Connectors.
|
///\brief Holds everything unique to HTTP Connectors.
|
||||||
namespace Connector_HTTP {
|
namespace Connector_HTTP {
|
||||||
/// Returns a Smooth-format manifest file
|
///\brief Builds an index file for HTTP Smooth streaming.
|
||||||
std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){
|
///\param MovieId The name of the movie.
|
||||||
|
///\param metadata The current metadata, used to generate the index.
|
||||||
|
///\return The index file for HTTP Smooth Streaming.
|
||||||
|
std::string smoothIndex(std::string & MovieId, JSON::Value & metadata){
|
||||||
std::stringstream Result;
|
std::stringstream Result;
|
||||||
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||||
Result << "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" TimeScale=\"10000000\" ";
|
Result << "<SmoothStreamingMedia "
|
||||||
|
"MajorVersion=\"2\" "
|
||||||
|
"MinorVersion=\"0\" "
|
||||||
|
"TimeScale=\"10000000\" ";
|
||||||
if (metadata.isMember("vod")){
|
if (metadata.isMember("vod")){
|
||||||
Result << "Duration=\"" << metadata["lastms"].asInt() << "0000\"";
|
Result << "Duration=\"" << metadata["lastms"].asInt() << "0000\"";
|
||||||
}else{
|
}else{
|
||||||
Result << "Duration=\"0\" IsLive=\"TRUE\" LookAheadFragmentCount=\"2\" DVRWindowLength=\"" + metadata["buffer_window"].asString() + "0000\" CanSeek=\"TRUE\" CanPause=\"TRUE\" ";
|
Result << "Duration=\"0\" "
|
||||||
|
"IsLive=\"TRUE\" "
|
||||||
|
"LookAheadFragmentCount=\"2\" "
|
||||||
|
"DVRWindowLength=\"" + metadata["buffer_window"].asString() + "0000\" "
|
||||||
|
"CanSeek=\"TRUE\" "
|
||||||
|
"CanPause=\"TRUE\" ";
|
||||||
}
|
}
|
||||||
Result << ">\n";
|
Result << ">\n";
|
||||||
|
//Add audio entries
|
||||||
if (metadata.isMember("audio") && metadata["audio"]["codec"].asString() == "AAC"){
|
if (metadata.isMember("audio") && metadata["audio"]["codec"].asString() == "AAC"){
|
||||||
Result << " <StreamIndex Type=\"audio\" QualityLevels=\"1\" Name=\"audio\" Chunks=\"" << metadata["keytime"].size()
|
Result << "<StreamIndex "
|
||||||
<< "\" Url=\"Q({bitrate})/A({start time})\">\n";
|
"Type=\"audio\" "
|
||||||
Result << " <QualityLevel Index=\"0\" Bitrate=\"" << metadata["audio"]["bps"].asInt() * 8 << "\" CodecPrivateData=\"";
|
"QualityLevels=\"1\" "
|
||||||
Result << std::hex;
|
"Name=\"audio\" "
|
||||||
|
"Chunks=\"" << metadata["keytime"].size() << "\" "
|
||||||
|
"Url=\"Q({bitrate})/A({start time})\">\n";
|
||||||
|
//Add audio qualities
|
||||||
|
Result << "<QualityLevel "
|
||||||
|
"Index=\"0\" "
|
||||||
|
"Bitrate=\"" << metadata["audio"]["bps"].asInt() * 8 << "\" "
|
||||||
|
"CodecPrivateData=\"" << std::hex;
|
||||||
for (int i = 0; i < metadata["audio"]["init"].asString().size(); i++){
|
for (int i = 0; i < metadata["audio"]["init"].asString().size(); i++){
|
||||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)metadata["audio"]["init"].asString()[i];
|
Result << std::setfill('0') << std::setw(2) << std::right << (int)metadata["audio"]["init"].asString()[i];
|
||||||
}
|
}
|
||||||
Result << std::dec;
|
Result << std::dec << "\" "
|
||||||
Result << "\" SamplingRate=\"" << metadata["audio"]["rate"].asInt()
|
"SamplingRate=\"" << metadata["audio"]["rate"].asInt() << "\" "
|
||||||
<< "\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" FourCC=\"AACL\" />\n";
|
"Channels=\"2\" "
|
||||||
|
"BitsPerSample=\"16\" "
|
||||||
|
"PacketSize=\"4\" "
|
||||||
|
"AudioTag=\"255\" "
|
||||||
|
"FourCC=\"AACL\" />\n";
|
||||||
for (unsigned int i = 0; i < metadata["keylen"].size(); i++){
|
for (unsigned int i = 0; i < metadata["keylen"].size(); i++){
|
||||||
Result << " <c ";
|
Result << "<c ";
|
||||||
if (i == 0){
|
if (i == 0){
|
||||||
Result << "t=\"" << metadata["keytime"][i].asInt() * 10000 << "\" ";
|
Result << "t=\"" << metadata["keytime"][i].asInt() * 10000 << "\" ";
|
||||||
}
|
}
|
||||||
|
@ -56,30 +81,41 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
Result << " </StreamIndex>\n";
|
Result << " </StreamIndex>\n";
|
||||||
}
|
}
|
||||||
|
//Add video entries
|
||||||
if (metadata.isMember("video") && metadata["video"]["codec"].asString() == "H264"){
|
if (metadata.isMember("video") && metadata["video"]["codec"].asString() == "H264"){
|
||||||
Result << " <StreamIndex Type=\"video\" QualityLevels=\"1\" Name=\"video\" Chunks=\"" << metadata["keytime"].size()
|
Result << "<StreamIndex "
|
||||||
<< "\" Url=\"Q({bitrate})/V({start time})\" MaxWidth=\"" << metadata["video"]["width"].asInt() << "\" MaxHeight=\""
|
"Type=\"video\" "
|
||||||
<< metadata["video"]["height"].asInt() << "\" DisplayWidth=\"" << metadata["video"]["width"].asInt() << "\" DisplayHeight=\""
|
"QualityLevels=\"1\" "
|
||||||
<< metadata["video"]["height"].asInt() << "\">\n";
|
"Name=\"video\" "
|
||||||
Result << " <QualityLevel Index=\"0\" Bitrate=\"" << metadata["video"]["bps"].asInt() * 8 << "\" CodecPrivateData=\"";
|
"Chunks=\"" << metadata["keytime"].size() << "\" "
|
||||||
|
"Url=\"Q({bitrate})/V({start time})\" "
|
||||||
|
"MaxWidth=\"" << metadata["video"]["width"].asInt() << "\" "
|
||||||
|
"MaxHeight=\"" << metadata["video"]["height"].asInt() << "\" "
|
||||||
|
"DisplayWidth=\"" << metadata["video"]["width"].asInt() << "\" "
|
||||||
|
"DisplayHeight=\"" << metadata["video"]["height"].asInt() << "\">\n";
|
||||||
|
//Add video qualities
|
||||||
|
Result << "<QualityLevel "
|
||||||
|
"Index=\"0\" "
|
||||||
|
"Bitrate=\"" << metadata["video"]["bps"].asInt() * 8 << "\" "
|
||||||
|
"CodecPrivateData=\"" << std::hex;
|
||||||
MP4::AVCC avccbox;
|
MP4::AVCC avccbox;
|
||||||
avccbox.setPayload(metadata["video"]["init"].asString());
|
avccbox.setPayload(metadata["video"]["init"].asString());
|
||||||
std::string tmpString = avccbox.asAnnexB();
|
std::string tmpString = avccbox.asAnnexB();
|
||||||
Result << std::hex;
|
|
||||||
for (int i = 0; i < tmpString.size(); i++){
|
for (int i = 0; i < tmpString.size(); i++){
|
||||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
|
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
|
||||||
}
|
}
|
||||||
Result << std::dec;
|
Result << std::dec << "\" "
|
||||||
Result << "\" MaxWidth=\"" << metadata["video"]["width"].asInt() << "\" MaxHeight=\"" << metadata["video"]["height"].asInt()
|
"MaxWidth=\"" << metadata["video"]["width"].asInt() << "\" "
|
||||||
<< "\" FourCC=\"AVC1\" />\n";
|
"MaxHeight=\"" << metadata["video"]["height"].asInt() << "\" "
|
||||||
|
"FourCC=\"AVC1\" />\n";
|
||||||
for (unsigned int i = 0; i < metadata["keylen"].size(); i++){
|
for (unsigned int i = 0; i < metadata["keylen"].size(); i++){
|
||||||
Result << " <c ";
|
Result << "<c ";
|
||||||
if (i == 0){
|
if (i == 0){
|
||||||
Result << "t=\"" << metadata["keytime"][i].asInt() * 10000 << "\" ";
|
Result << "t=\"" << metadata["keytime"][i].asInt() * 10000 << "\" ";
|
||||||
}
|
}
|
||||||
Result << "d=\"" << metadata["keylen"][i].asInt() * 10000 << "\" />\n";
|
Result << "d=\"" << metadata["keylen"][i].asInt() * 10000 << "\" />\n";
|
||||||
}
|
}
|
||||||
Result << " </StreamIndex>\n";
|
Result << "</StreamIndex>\n";
|
||||||
}
|
}
|
||||||
Result << "</SmoothStreamingMedia>\n";
|
Result << "</SmoothStreamingMedia>\n";
|
||||||
|
|
||||||
|
@ -87,32 +123,35 @@ namespace Connector_HTTP {
|
||||||
std::cerr << "Sending this manifest:" << std::endl << Result << std::endl;
|
std::cerr << "Sending this manifest:" << std::endl << Result << std::endl;
|
||||||
#endif
|
#endif
|
||||||
return Result.str();
|
return Result.str();
|
||||||
} //BuildManifest
|
} //smoothIndex
|
||||||
|
|
||||||
/// Main function for Connector_HTTP_Dynamic
|
///\brief Main function for the HTTP Smooth Connector
|
||||||
int Connector_HTTP_Dynamic(Socket::Connection conn){
|
///\param conn A socket describing the connection the client.
|
||||||
std::deque<std::string> FlashBuf;
|
///\return The exit code of the connector.
|
||||||
int FlashBufSize = 0;
|
int smoothConnector(Socket::Connection conn){
|
||||||
|
std::deque<std::string> dataBuffer;//A buffer for the data that needs to be sent to the client.
|
||||||
|
int dataSize = 0;//The amount of bytes in the dataBuffer
|
||||||
|
|
||||||
DTSC::Stream Strm; //Incoming stream buffer.
|
DTSC::Stream Strm;//Incoming stream buffer.
|
||||||
HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver and HTTP Sender.
|
HTTP::Parser HTTP_R;//HTTP Receiver
|
||||||
|
HTTP::Parser HTTP_S;//HTTP Sender.
|
||||||
|
|
||||||
bool ready4data = false; //Set to true when streaming is to begin.
|
bool ready4data = false;//Set to true when streaming is to begin.
|
||||||
Socket::Connection ss( -1);
|
Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
|
||||||
std::string streamname;
|
std::string streamname;//Will contain the name of the stream.
|
||||||
|
|
||||||
bool wantsVideo = false;
|
bool wantsVideo = false;//Indicates whether this request is a video request.
|
||||||
bool wantsAudio = false;
|
bool wantsAudio = false;//Indicates whether this request is an audio request.
|
||||||
|
|
||||||
std::string Quality;
|
std::string Quality;//Indicates the request quality of the movie.
|
||||||
long long int ReqFragment = -1;
|
long long int requestedTime = -1;//Indicates the fragment requested.
|
||||||
std::string tempStr;
|
std::string parseString;//A string used for parsing different aspects of the request.
|
||||||
unsigned int lastStats = 0;
|
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
||||||
conn.setBlocking(false); //do not block on conn.spool() when no data is available
|
conn.setBlocking(false);//Set the client socket to non-blocking
|
||||||
|
|
||||||
while (conn.connected()){
|
while (conn.connected()){
|
||||||
if (conn.spool() || conn.Received().size()){
|
if (conn.spool() || conn.Received().size()){
|
||||||
//make sure it ends in a \n
|
//Make sure the received data ends in a newline (\n).
|
||||||
if ( *(conn.Received().get().rbegin()) != '\n'){
|
if ( *(conn.Received().get().rbegin()) != '\n'){
|
||||||
std::string tmp = conn.Received().get();
|
std::string tmp = conn.Received().get();
|
||||||
conn.Received().get().clear();
|
conn.Received().get().clear();
|
||||||
|
@ -126,9 +165,11 @@ namespace Connector_HTTP {
|
||||||
#if DEBUG >= 5
|
#if DEBUG >= 5
|
||||||
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
|
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
//Get data set by the proxy.
|
||||||
conn.setHost(HTTP_R.GetHeader("X-Origin"));
|
conn.setHost(HTTP_R.GetHeader("X-Origin"));
|
||||||
streamname = HTTP_R.GetHeader("X-Stream");
|
streamname = HTTP_R.GetHeader("X-Stream");
|
||||||
if ( !ss){
|
if ( !ss){
|
||||||
|
//initiate Stream Socket
|
||||||
ss = Util::Stream::getStream(streamname);
|
ss = Util::Stream::getStream(streamname);
|
||||||
if ( !ss.connected()){
|
if ( !ss.connected()){
|
||||||
#if DEBUG >= 1
|
#if DEBUG >= 1
|
||||||
|
@ -141,7 +182,7 @@ namespace Connector_HTTP {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ss.setBlocking(false);
|
ss.setBlocking(false);
|
||||||
//make sure metadata is received
|
//Do nothing until metadata has been received.
|
||||||
while ( !Strm.metadata){
|
while ( !Strm.metadata){
|
||||||
if (ss.spool()){
|
if (ss.spool()){
|
||||||
while (Strm.parsePacket(ss.Received())){
|
while (Strm.parsePacket(ss.Received())){
|
||||||
|
@ -151,25 +192,26 @@ namespace Connector_HTTP {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HTTP_R.url.find("Manifest") == std::string::npos){
|
if (HTTP_R.url.find("Manifest") == std::string::npos){
|
||||||
|
//We have a non-manifest request, parse it.
|
||||||
Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3);
|
Quality = HTTP_R.url.substr(HTTP_R.url.find("/Q(", 8) + 3);
|
||||||
Quality = Quality.substr(0, Quality.find(")"));
|
Quality = Quality.substr(0, Quality.find(")"));
|
||||||
tempStr = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2);
|
parseString = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2);
|
||||||
wantsAudio = false;
|
wantsAudio = false;
|
||||||
wantsVideo = false;
|
wantsVideo = false;
|
||||||
if (tempStr[0] == 'A'){
|
if (parseString[0] == 'A'){
|
||||||
wantsAudio = true;
|
wantsAudio = true;
|
||||||
}
|
}
|
||||||
if (tempStr[0] == 'V'){
|
if (parseString[0] == 'V'){
|
||||||
wantsVideo = true;
|
wantsVideo = true;
|
||||||
}
|
}
|
||||||
tempStr = tempStr.substr(tempStr.find("(") + 1);
|
parseString = parseString.substr(parseString.find("(") + 1);
|
||||||
ReqFragment = atoll(tempStr.substr(0, tempStr.find(")")).c_str());
|
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.isMember("live")){
|
||||||
int seekable = Strm.canSeekms(ReqFragment / 10000);
|
int seekable = Strm.canSeekms(requestedTime / 10000);
|
||||||
if (seekable == 0){
|
if (seekable == 0){
|
||||||
// iff the fragment in question is available, check if the next is available too
|
// iff the fragment in question is available, check if the next is available too
|
||||||
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
||||||
if (Strm.metadata["keytime"][i].asInt() >= (ReqFragment / 10000)){
|
if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){
|
||||||
if (i + 1 == Strm.metadata["keytime"].size()){
|
if (i + 1 == Strm.metadata["keytime"].size()){
|
||||||
seekable = 1;
|
seekable = 1;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +224,7 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||||
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
|
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
std::cout << "Fragment @ " << ReqFragment / 10000 << "ms too old (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
|
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (seekable > 0){
|
if (seekable > 0){
|
||||||
|
@ -190,73 +232,79 @@ namespace Connector_HTTP {
|
||||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
HTTP_R.Clean(); //clean for any possible next requests
|
||||||
std::cout << "Fragment @ " << ReqFragment / 10000 << "ms not available yet (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
|
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << Strm.metadata["keytime"][0u].asInt() << " - " << Strm.metadata["keytime"][Strm.metadata["keytime"].size() - 1].asInt() << " ms)" << std::endl;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Seek to the right place and send a play-once for a single fragment.
|
||||||
std::stringstream sstream;
|
std::stringstream sstream;
|
||||||
sstream << "s " << (ReqFragment / 10000) << "\no \n";
|
sstream << "s " << (requestedTime / 10000) << "\no \n";
|
||||||
ss.SendNow(sstream.str().c_str());
|
ss.SendNow(sstream.str().c_str());
|
||||||
}else{
|
}else{
|
||||||
|
//We have a request for a Manifest, generate and send it.
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetHeader("Content-Type", "text/xml");
|
HTTP_S.SetHeader("Content-Type", "text/xml");
|
||||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||||
std::string manifest = BuildManifest(streamname, Strm.metadata);
|
std::string manifest = smoothIndex(streamname, Strm.metadata);
|
||||||
HTTP_S.SetBody(manifest);
|
HTTP_S.SetBody(manifest);
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
}
|
}
|
||||||
ready4data = true;
|
ready4data = true;
|
||||||
HTTP_R.Clean(); //clean for any possible next requests
|
//Clean for any possible next requests
|
||||||
|
HTTP_R.Clean();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
//Wait 1 second before checking for new data.
|
||||||
Util::sleep(1);
|
Util::sleep(1);
|
||||||
}
|
}
|
||||||
if (ready4data){
|
if (ready4data){
|
||||||
unsigned int now = Util::epoch();
|
unsigned int now = Util::epoch();
|
||||||
if (now != lastStats){
|
if (now != lastStats){
|
||||||
|
//Send new stats.
|
||||||
lastStats = now;
|
lastStats = now;
|
||||||
ss.SendNow(conn.getStats("HTTP_Smooth").c_str());
|
ss.SendNow(conn.getStats("HTTP_Smooth").c_str());
|
||||||
}
|
}
|
||||||
if (ss.spool()){
|
if (ss.spool()){
|
||||||
while (Strm.parsePacket(ss.Received())){
|
while (Strm.parsePacket(ss.Received())){
|
||||||
if (Strm.lastType() == DTSC::PAUSEMARK){
|
if (Strm.lastType() == DTSC::PAUSEMARK){
|
||||||
if (FlashBufSize){
|
//Send the current buffer
|
||||||
|
if (dataSize){
|
||||||
HTTP_S.Clean();
|
HTTP_S.Clean();
|
||||||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||||
HTTP_S.SetBody("");
|
HTTP_S.SetBody("");
|
||||||
|
|
||||||
unsigned int myDuration;
|
unsigned int myDuration;
|
||||||
|
|
||||||
|
//Wrap everything in mp4 boxes
|
||||||
MP4::MFHD mfhd_box;
|
MP4::MFHD mfhd_box;
|
||||||
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
||||||
if (Strm.metadata["keytime"][i].asInt() >= (ReqFragment / 10000)){
|
if (Strm.metadata["keytime"][i].asInt() >= (requestedTime / 10000)){
|
||||||
mfhd_box.setSequenceNumber(Strm.metadata["keynum"][i].asInt());
|
mfhd_box.setSequenceNumber(Strm.metadata["keynum"][i].asInt());
|
||||||
myDuration = Strm.metadata["keylen"][i].asInt() * 10000;
|
myDuration = Strm.metadata["keylen"][i].asInt() * 10000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MP4::TFHD tfhd_box;
|
MP4::TFHD tfhd_box;
|
||||||
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
||||||
tfhd_box.setTrackID(1);
|
tfhd_box.setTrackID(1);
|
||||||
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
||||||
|
|
||||||
MP4::TRUN trun_box;
|
MP4::TRUN trun_box;
|
||||||
//maybe reinsert dataOffset
|
|
||||||
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
|
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
|
||||||
trun_box.setDataOffset(42);
|
trun_box.setDataOffset(42);
|
||||||
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
|
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
|
||||||
for (int i = 0; i < FlashBuf.size(); i++){
|
for (int i = 0; i < dataBuffer.size(); i++){
|
||||||
MP4::trunSampleInformation trunSample;
|
MP4::trunSampleInformation trunSample;
|
||||||
trunSample.sampleSize = FlashBuf[i].size();
|
trunSample.sampleSize = dataBuffer[i].size();
|
||||||
trunSample.sampleDuration = (((double)myDuration / FlashBuf.size()) * i) - (((double)myDuration / FlashBuf.size()) * (i - 1));
|
trunSample.sampleDuration = (((double)myDuration / dataBuffer.size()) * i) - (((double)myDuration / dataBuffer.size()) * (i - 1));
|
||||||
trun_box.setSampleInformation(trunSample, i);
|
trun_box.setSampleInformation(trunSample, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
MP4::SDTP sdtp_box;
|
MP4::SDTP sdtp_box;
|
||||||
sdtp_box.setVersion(0);
|
sdtp_box.setVersion(0);
|
||||||
sdtp_box.setValue(0x24, 4);
|
sdtp_box.setValue(0x24, 4);
|
||||||
for (int i = 1; i < FlashBuf.size(); i++){
|
for (int i = 1; i < dataBuffer.size(); i++){
|
||||||
sdtp_box.setValue(0x14, 4 + i);
|
sdtp_box.setValue(0x14, 4 + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,14 +313,14 @@ namespace Connector_HTTP {
|
||||||
traf_box.setContent(trun_box, 1);
|
traf_box.setContent(trun_box, 1);
|
||||||
traf_box.setContent(sdtp_box, 2);
|
traf_box.setContent(sdtp_box, 2);
|
||||||
|
|
||||||
// if live, send fragref box if we can
|
//If the stream is live, we want to have a fragref box if possible
|
||||||
if (Strm.metadata.isMember("live")){
|
if (Strm.metadata.isMember("live")){
|
||||||
MP4::UUID_TrackFragmentReference fragref_box;
|
MP4::UUID_TrackFragmentReference fragref_box;
|
||||||
fragref_box.setVersion(1);
|
fragref_box.setVersion(1);
|
||||||
fragref_box.setFragmentCount(0);
|
fragref_box.setFragmentCount(0);
|
||||||
int fragCount = 0;
|
int fragCount = 0;
|
||||||
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
for (int i = 0; i < Strm.metadata["keytime"].size(); i++){
|
||||||
if (Strm.metadata["keytime"][i].asInt() > (ReqFragment / 10000)){
|
if (Strm.metadata["keytime"][i].asInt() > (requestedTime / 10000)){
|
||||||
fragref_box.setTime(fragCount, Strm.metadata["keytime"][i].asInt() * 10000);
|
fragref_box.setTime(fragCount, Strm.metadata["keytime"][i].asInt() * 10000);
|
||||||
fragref_box.setDuration(fragCount, Strm.metadata["keylen"][i].asInt() * 10000);
|
fragref_box.setDuration(fragCount, Strm.metadata["keylen"][i].asInt() * 10000);
|
||||||
fragref_box.setFragmentCount(++fragCount);
|
fragref_box.setFragmentCount(++fragCount);
|
||||||
|
@ -285,32 +333,31 @@ namespace Connector_HTTP {
|
||||||
moof_box.setContent(mfhd_box, 0);
|
moof_box.setContent(mfhd_box, 0);
|
||||||
moof_box.setContent(traf_box, 1);
|
moof_box.setContent(traf_box, 1);
|
||||||
|
|
||||||
//setting tha offsets!
|
//Setting the correct offsets.
|
||||||
trun_box.setDataOffset(moof_box.boxedSize() + 8);
|
trun_box.setDataOffset(moof_box.boxedSize() + 8);
|
||||||
traf_box.setContent(trun_box, 1);
|
traf_box.setContent(trun_box, 1);
|
||||||
moof_box.setContent(traf_box, 1);
|
moof_box.setContent(traf_box, 1);
|
||||||
|
|
||||||
//std::cerr << "\t[encoded] = " << ((MP4::TRUN&)(((MP4::TRAF&)(moof_box.getContent(1))).getContent(1))).getDataOffset() << std::endl;
|
//Send the complete message
|
||||||
|
HTTP_S.SetHeader("Content-Length", dataSize + 8 + moof_box.boxedSize());
|
||||||
HTTP_S.SetHeader("Content-Length", FlashBufSize + 8 + moof_box.boxedSize()); //32+33+btstrp.size());
|
|
||||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||||
|
|
||||||
conn.SendNow(moof_box.asBox(), moof_box.boxedSize());
|
conn.SendNow(moof_box.asBox(), moof_box.boxedSize());
|
||||||
|
|
||||||
unsigned long size = htonl(FlashBufSize+8);
|
unsigned long size = htonl(dataSize + 8);
|
||||||
conn.SendNow((char*) &size, 4);
|
conn.SendNow((char*) &size, 4);
|
||||||
conn.SendNow("mdat", 4);
|
conn.SendNow("mdat", 4);
|
||||||
while (FlashBuf.size() > 0){
|
while (dataBuffer.size() > 0){
|
||||||
conn.SendNow(FlashBuf.front());
|
conn.SendNow(dataBuffer.front());
|
||||||
FlashBuf.pop_front();
|
dataBuffer.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FlashBuf.clear();
|
dataBuffer.clear();
|
||||||
FlashBufSize = 0;
|
dataSize = 0;
|
||||||
}
|
}
|
||||||
if ((wantsAudio && Strm.lastType() == DTSC::AUDIO) || (wantsVideo && Strm.lastType() == DTSC::VIDEO)){
|
if ((wantsAudio && Strm.lastType() == DTSC::AUDIO) || (wantsVideo && Strm.lastType() == DTSC::VIDEO)){
|
||||||
FlashBuf.push_back(Strm.lastData());
|
//Select only the data that the client has requested.
|
||||||
FlashBufSize += Strm.lastData().size();
|
dataBuffer.push_back(Strm.lastData());
|
||||||
|
dataSize += Strm.lastData().size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,10 +370,11 @@ namespace Connector_HTTP {
|
||||||
ss.SendNow(conn.getStats("HTTP_Smooth").c_str());
|
ss.SendNow(conn.getStats("HTTP_Smooth").c_str());
|
||||||
ss.close();
|
ss.close();
|
||||||
return 0;
|
return 0;
|
||||||
} //Connector_HTTP_Smooth main function
|
}//Smooth_Connector main function
|
||||||
|
|
||||||
} //Connector_HTTP_Smooth namespace
|
}//Connector_HTTP namespace
|
||||||
|
|
||||||
|
///\brief The standard process-spawning main function.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char ** argv){
|
||||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||||
conf.addConnectorOptions(1935);
|
conf.addConnectorOptions(1935);
|
||||||
|
@ -342,7 +390,7 @@ int main(int argc, char ** argv){
|
||||||
if (S.connected()){ //check if the new connection is valid
|
if (S.connected()){ //check if the new connection is valid
|
||||||
pid_t myid = fork();
|
pid_t myid = fork();
|
||||||
if (myid == 0){ //if new child, start MAINHANDLER
|
if (myid == 0){ //if new child, start MAINHANDLER
|
||||||
return Connector_HTTP::Connector_HTTP_Dynamic(S);
|
return Connector_HTTP::smoothConnector(S);
|
||||||
}else{ //otherwise, do nothing or output debugging text
|
}else{ //otherwise, do nothing or output debugging text
|
||||||
#if DEBUG >= 5
|
#if DEBUG >= 5
|
||||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||||
|
|
|
@ -45,8 +45,10 @@ namespace Connector_RTMP {
|
||||||
int Connector_RTMP(Socket::Connection conn);
|
int Connector_RTMP(Socket::Connection conn);
|
||||||
} //Connector_RTMP namespace;
|
} //Connector_RTMP namespace;
|
||||||
|
|
||||||
/// Main Connector_RTMP function
|
///\brief Main Connector_RTMP function
|
||||||
int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
|
///\param conn A socket describing the connection the client.
|
||||||
|
///\return The exit code of the connector.
|
||||||
|
int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
|
||||||
Socket = conn;
|
Socket = conn;
|
||||||
Socket.setBlocking(false);
|
Socket.setBlocking(false);
|
||||||
FLV::Tag tag, init_tag;
|
FLV::Tag tag, init_tag;
|
||||||
|
@ -179,7 +181,8 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){
|
||||||
return 0;
|
return 0;
|
||||||
} //Connector_RTMP
|
} //Connector_RTMP
|
||||||
|
|
||||||
/// Tries to get and parse one RTMP chunk at a time.
|
///\brief Tries to get and parse one RTMP chunk at a time.
|
||||||
|
///\param inbuffer A buffer filled with chunk data.
|
||||||
void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){
|
void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){
|
||||||
//for DTSC conversion
|
//for DTSC conversion
|
||||||
static JSON::Value meta_out;
|
static JSON::Value meta_out;
|
||||||
|
@ -597,6 +600,7 @@ void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int
|
||||||
#endif
|
#endif
|
||||||
} //parseAMFCommand
|
} //parseAMFCommand
|
||||||
|
|
||||||
|
///\brief The standard process-spawning main function.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char ** argv){
|
||||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||||
conf.addConnectorOptions(1935);
|
conf.addConnectorOptions(1935);
|
||||||
|
|
Loading…
Add table
Reference in a new issue