Implemented RTMP push output.
This commit is contained in:
parent
8ddfb000df
commit
fcdf788596
4 changed files with 189 additions and 102 deletions
|
@ -7,10 +7,6 @@
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
|
||||||
#ifndef FILLER_DATA
|
|
||||||
#define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::string RTMPStream::handshake_in; ///< Input for the handshake.
|
std::string RTMPStream::handshake_in; ///< Input for the handshake.
|
||||||
std::string RTMPStream::handshake_out; ///< Output for the handshake.
|
std::string RTMPStream::handshake_out; ///< Output for the handshake.
|
||||||
|
|
||||||
|
@ -205,7 +201,7 @@ RTMPStream::Chunk::Chunk() {
|
||||||
std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data) {
|
std::string & RTMPStream::SendChunk(unsigned int cs_id, unsigned char msg_type_id, unsigned int msg_stream_id, std::string data) {
|
||||||
static RTMPStream::Chunk ch;
|
static RTMPStream::Chunk ch;
|
||||||
ch.cs_id = cs_id;
|
ch.cs_id = cs_id;
|
||||||
ch.timestamp = Util::getMS();
|
ch.timestamp = 0;
|
||||||
ch.len = data.size();
|
ch.len = data.size();
|
||||||
ch.real_len = data.size();
|
ch.real_len = data.size();
|
||||||
ch.len_left = 0;
|
ch.len_left = 0;
|
||||||
|
|
|
@ -10,6 +10,10 @@
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
|
|
||||||
|
#ifndef FILLER_DATA
|
||||||
|
#define FILLER_DATA "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo vulputate urna eu commodo. Cras tempor velit nec nulla placerat volutpat. Proin eleifend blandit quam sit amet suscipit. Pellentesque vitae tristique lorem. Maecenas facilisis consequat neque, vitae iaculis eros vulputate ut. Suspendisse ut arcu non eros vestibulum pulvinar id sed erat. Nam dictum tellus vel tellus rhoncus ut mollis tellus fermentum. Fusce volutpat consectetur ante, in mollis nisi euismod vulputate. Curabitur vitae facilisis ligula. Sed sed gravida dolor. Integer eu eros a dolor lobortis ullamcorper. Mauris interdum elit non neque interdum dictum. Suspendisse imperdiet eros sed sapien cursus pulvinar. Vestibulum ut dolor lectus, id commodo elit. Cras convallis varius leo eu porta. Duis luctus sapien nec dui adipiscing quis interdum nunc congue. Morbi pharetra aliquet mauris vitae tristique. Etiam feugiat sapien quis augue elementum id ultricies magna vulputate. Phasellus luctus, leo id egestas consequat, eros tortor commodo neque, vitae hendrerit nunc sem ut odio."
|
||||||
|
#endif
|
||||||
|
|
||||||
//forward declaration of FLV::Tag to avoid circular dependencies.
|
//forward declaration of FLV::Tag to avoid circular dependencies.
|
||||||
namespace FLV {
|
namespace FLV {
|
||||||
class Tag;
|
class Tag;
|
||||||
|
|
|
@ -10,11 +10,119 @@
|
||||||
|
|
||||||
namespace Mist {
|
namespace Mist {
|
||||||
OutRTMP::OutRTMP(Socket::Connection & conn) : Output(conn) {
|
OutRTMP::OutRTMP(Socket::Connection & conn) : Output(conn) {
|
||||||
|
if (config->getString("target").size() && config->getString("target").substr(0, 7) == "rtmp://"){
|
||||||
|
streamName = config->getString("streamname");
|
||||||
|
std::string pushStr= config->getString("target");
|
||||||
|
pushStr = pushStr.substr(7);
|
||||||
|
std::string host, app = "default", streamOut = "default";
|
||||||
|
int port = 1935;
|
||||||
|
|
||||||
|
size_t slash = pushStr.find('/');
|
||||||
|
if (slash != std::string::npos){
|
||||||
|
app = pushStr.substr(slash+1, std::string::npos);
|
||||||
|
host = pushStr.substr(0, slash);
|
||||||
|
}else{
|
||||||
|
host = pushStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t colon = host.find(':');
|
||||||
|
if (colon != std::string::npos && colon != 0 && colon != host.size()) {
|
||||||
|
port = atoi(host.substr(colon + 1, std::string::npos).c_str());
|
||||||
|
host = host.substr(0, colon);
|
||||||
|
}
|
||||||
|
|
||||||
|
slash = app.find('/');
|
||||||
|
if (slash != std::string::npos){
|
||||||
|
streamOut = app.substr(slash+1, std::string::npos);
|
||||||
|
app = app.substr(0, slash);
|
||||||
|
}else{
|
||||||
|
streamOut = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
INFO_MSG("About to push stream %s out. Host: %s, port: %d, app: %s, stream: %s", streamName.c_str(), host.c_str(), port, app.c_str(), streamOut.c_str());
|
||||||
|
myConn = Socket::Connection(host, port, false);
|
||||||
|
if (!myConn){
|
||||||
|
FAIL_MSG("Could not connect to %s:%d!", host.c_str(), port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//do handshake
|
||||||
|
myConn.SendNow("\003", 1);//protocol version. Always 3
|
||||||
|
char * temp = (char*)malloc(3072);
|
||||||
|
if (!temp){
|
||||||
|
myConn.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*((uint32_t *)temp) = 0; //time zero
|
||||||
|
*(((uint32_t *)(temp + 4))) = htonl(0x01020304); //version 1 2 3 4
|
||||||
|
for (int i = 8; i < 3072; ++i) {
|
||||||
|
temp[i] = FILLER_DATA[i % sizeof(FILLER_DATA)];
|
||||||
|
} //"random" data
|
||||||
|
myConn.SendNow(temp, 3072);
|
||||||
|
free(temp);
|
||||||
setBlocking(true);
|
setBlocking(true);
|
||||||
while (!conn.Received().available(1537) && conn.connected()) {
|
while (!myConn.Received().available(3073) && myConn.connected() && config->is_active) {
|
||||||
|
myConn.spool();
|
||||||
|
}
|
||||||
|
if (!myConn || !config->is_active){return;}
|
||||||
|
conn.Received().remove(3073);
|
||||||
|
RTMPStream::rec_cnt += 3073;
|
||||||
|
RTMPStream::snd_cnt += 3073;
|
||||||
|
setBlocking(false);
|
||||||
|
INFO_MSG("Push out handshake completed");
|
||||||
|
|
||||||
|
{
|
||||||
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
|
amfReply.addContent(AMF::Object("", "connect")); //command
|
||||||
|
amfReply.addContent(AMF::Object("", (double)1)); //transaction ID
|
||||||
|
amfReply.addContent(AMF::Object("")); //options
|
||||||
|
amfReply.getContentP(2)->addContent(AMF::Object("app", app));
|
||||||
|
amfReply.getContentP(2)->addContent(AMF::Object("type", "nonprivate"));
|
||||||
|
amfReply.getContentP(2)->addContent(AMF::Object("flashVer", "FMLE/3.0 (compatible; MistServer/" PACKAGE_VERSION "/" RELEASE ")"));
|
||||||
|
amfReply.getContentP(2)->addContent(AMF::Object("tcUrl", "rtmp://" + host + "/" + app));
|
||||||
|
sendCommand(amfReply, 20, 0);
|
||||||
|
}
|
||||||
|
RTMPStream::chunk_snd_max = 4096;
|
||||||
|
myConn.SendNow(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1)
|
||||||
|
{
|
||||||
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
|
amfReply.addContent(AMF::Object("", "releaseStream")); //command
|
||||||
|
amfReply.addContent(AMF::Object("", (double)2)); //transaction ID
|
||||||
|
amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //options
|
||||||
|
amfReply.addContent(AMF::Object("", streamOut)); //stream name
|
||||||
|
sendCommand(amfReply, 20, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
|
amfReply.addContent(AMF::Object("", "FCPublish")); //command
|
||||||
|
amfReply.addContent(AMF::Object("", (double)3)); //transaction ID
|
||||||
|
amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //options
|
||||||
|
amfReply.addContent(AMF::Object("", streamOut)); //stream name
|
||||||
|
sendCommand(amfReply, 20, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
|
amfReply.addContent(AMF::Object("", "createStream")); //command
|
||||||
|
amfReply.addContent(AMF::Object("", (double)4)); //transaction ID
|
||||||
|
amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //options
|
||||||
|
sendCommand(amfReply, 20, 0);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
|
amfReply.addContent(AMF::Object("", "publish")); //command
|
||||||
|
amfReply.addContent(AMF::Object("", (double)5)); //transaction ID
|
||||||
|
amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //options
|
||||||
|
amfReply.addContent(AMF::Object("", streamOut)); //stream name
|
||||||
|
amfReply.addContent(AMF::Object("", "live")); //stream name
|
||||||
|
sendCommand(amfReply, 20, 1);
|
||||||
|
}
|
||||||
|
INFO_MSG("Publish starting");
|
||||||
|
parseData = true;
|
||||||
|
}else{
|
||||||
|
setBlocking(true);
|
||||||
|
while (!conn.Received().available(1537) && conn.connected() && config->is_active) {
|
||||||
conn.spool();
|
conn.spool();
|
||||||
}
|
}
|
||||||
if (!conn){
|
if (!conn || !config->is_active){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RTMPStream::handshake_in.append(conn.Received().remove(1537));
|
RTMPStream::handshake_in.append(conn.Received().remove(1537));
|
||||||
|
@ -22,16 +130,18 @@ namespace Mist {
|
||||||
|
|
||||||
if (RTMPStream::doHandshake()) {
|
if (RTMPStream::doHandshake()) {
|
||||||
conn.SendNow(RTMPStream::handshake_out);
|
conn.SendNow(RTMPStream::handshake_out);
|
||||||
while (!conn.Received().available(1536) && conn.connected()) {
|
while (!conn.Received().available(1536) && conn.connected() && config->is_active) {
|
||||||
conn.spool();
|
conn.spool();
|
||||||
}
|
}
|
||||||
conn.Received().remove(1536);
|
conn.Received().remove(1536);
|
||||||
RTMPStream::rec_cnt += 1536;
|
RTMPStream::rec_cnt += 1536;
|
||||||
DEBUG_MSG(DLVL_HIGH, "Handshake success!");
|
HIGH_MSG("Handshake success!");
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG(DLVL_DEVEL, "Handshake fail!");
|
MEDIUM_MSG("Handshake fail!");
|
||||||
}
|
}
|
||||||
setBlocking(false);
|
setBlocking(false);
|
||||||
|
}
|
||||||
|
|
||||||
maxSkipAhead = 1500;
|
maxSkipAhead = 1500;
|
||||||
minSkipAhead = 500;
|
minSkipAhead = 500;
|
||||||
isPushing = false;
|
isPushing = false;
|
||||||
|
@ -39,6 +149,10 @@ namespace Mist {
|
||||||
|
|
||||||
OutRTMP::~OutRTMP() {}
|
OutRTMP::~OutRTMP() {}
|
||||||
|
|
||||||
|
bool OutRTMP::listenMode(){
|
||||||
|
return !(config->getString("target").size());
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int OutRTMP::needsPlayableKeys(){
|
unsigned int OutRTMP::needsPlayableKeys(){
|
||||||
if (isPushing){
|
if (isPushing){
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -128,6 +242,15 @@ namespace Mist {
|
||||||
capa["methods"][0u]["player_url"] = "/flashplayer.swf";
|
capa["methods"][0u]["player_url"] = "/flashplayer.swf";
|
||||||
cfg->addConnectorOptions(1935, capa);
|
cfg->addConnectorOptions(1935, capa);
|
||||||
config = cfg;
|
config = cfg;
|
||||||
|
capa["push_urls"].append("rtmp://*");
|
||||||
|
|
||||||
|
JSON::Value opt;
|
||||||
|
opt["arg"] = "string";
|
||||||
|
opt["default"] = "";
|
||||||
|
opt["arg_num"] = 1ll;
|
||||||
|
opt["help"] = "Target rtmp:// URL to push out towards.";
|
||||||
|
cfg->addOption("target", opt);
|
||||||
|
cfg->addOption("streamname", JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream to push out, when pushing out.\"}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutRTMP::sendNext() {
|
void OutRTMP::sendNext() {
|
||||||
|
@ -322,9 +445,7 @@ namespace Mist {
|
||||||
///\param messageType The type of message.
|
///\param messageType The type of message.
|
||||||
///\param streamId The ID of the AMF stream.
|
///\param streamId The ID of the AMF stream.
|
||||||
void OutRTMP::sendCommand(AMF::Object & amfReply, int messageType, int streamId) {
|
void OutRTMP::sendCommand(AMF::Object & amfReply, int messageType, int streamId) {
|
||||||
#if DEBUG >= 8
|
HIGH_MSG("Sending: %s", amfReply.Print().c_str());
|
||||||
std::cerr << amfReply.Print() << std::endl;
|
|
||||||
#endif
|
|
||||||
if (messageType == 17) {
|
if (messageType == 17) {
|
||||||
myConn.SendNow(RTMPStream::SendChunk(3, messageType, streamId, (char)0 + amfReply.Pack()));
|
myConn.SendNow(RTMPStream::SendChunk(3, messageType, streamId, (char)0 + amfReply.Pack()));
|
||||||
} else {
|
} else {
|
||||||
|
@ -352,38 +473,13 @@ namespace Mist {
|
||||||
/// connected client host
|
/// connected client host
|
||||||
/// ~~~~~~~~~~~~~~~
|
/// ~~~~~~~~~~~~~~~
|
||||||
void OutRTMP::parseAMFCommand(AMF::Object & amfData, int messageType, int streamId) {
|
void OutRTMP::parseAMFCommand(AMF::Object & amfData, int messageType, int streamId) {
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("Received command: %s", amfData.Print().c_str());
|
||||||
fprintf(stderr, "Received command: %s\n", amfData.Print().c_str());
|
HIGH_MSG("AMF0 command: %s", amfData.getContentP(0)->StrValue().c_str());
|
||||||
#endif
|
|
||||||
#if DEBUG >= 8
|
|
||||||
fprintf(stderr, "AMF0 command: %s\n", amfData.getContentP(0)->StrValue().c_str());
|
|
||||||
#endif
|
|
||||||
if (amfData.getContentP(0)->StrValue() == "connect") {
|
if (amfData.getContentP(0)->StrValue() == "connect") {
|
||||||
double objencoding = 0;
|
double objencoding = 0;
|
||||||
if (amfData.getContentP(2)->getContentP("objectEncoding")) {
|
if (amfData.getContentP(2)->getContentP("objectEncoding")) {
|
||||||
objencoding = amfData.getContentP(2)->getContentP("objectEncoding")->NumValue();
|
objencoding = amfData.getContentP(2)->getContentP("objectEncoding")->NumValue();
|
||||||
}
|
}
|
||||||
#if DEBUG >= 6
|
|
||||||
int tmpint;
|
|
||||||
if (amfData.getContentP(2)->getContentP("videoCodecs")) {
|
|
||||||
tmpint = (int)amfData.getContentP(2)->getContentP("videoCodecs")->NumValue();
|
|
||||||
if (tmpint & 0x04) {
|
|
||||||
fprintf(stderr, "Sorensen video support detected\n");
|
|
||||||
}
|
|
||||||
if (tmpint & 0x80) {
|
|
||||||
fprintf(stderr, "H264 video support detected\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (amfData.getContentP(2)->getContentP("audioCodecs")) {
|
|
||||||
tmpint = (int)amfData.getContentP(2)->getContentP("audioCodecs")->NumValue();
|
|
||||||
if (tmpint & 0x04) {
|
|
||||||
fprintf(stderr, "MP3 audio support detected\n");
|
|
||||||
}
|
|
||||||
if (tmpint & 0x400) {
|
|
||||||
fprintf(stderr, "AAC audio support detected\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
app_name = amfData.getContentP(2)->getContentP("tcUrl")->StrValue();
|
app_name = amfData.getContentP(2)->getContentP("tcUrl")->StrValue();
|
||||||
reqUrl = app_name;//LTS
|
reqUrl = app_name;//LTS
|
||||||
app_name = app_name.substr(app_name.find('/', 7) + 1);
|
app_name = app_name.substr(app_name.find('/', 7) + 1);
|
||||||
|
@ -538,7 +634,7 @@ namespace Mist {
|
||||||
DTSC::Scan streamCfg = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("streams").getMember(smp);
|
DTSC::Scan streamCfg = DTSC::Scan(serverCfg.mapped, serverCfg.len).getMember("streams").getMember(smp);
|
||||||
if (streamCfg){
|
if (streamCfg){
|
||||||
if (streamCfg.getMember("source").asString().substr(0, 7) != "push://"){
|
if (streamCfg.getMember("source").asString().substr(0, 7) != "push://"){
|
||||||
DEBUG_MSG(DLVL_FAIL, "Push rejected - stream %s not a push-able stream. (%s != push://*)", streamName.c_str(), streamCfg.getMember("source").asString().c_str());
|
FAIL_MSG("Push rejected - stream %s not a push-able stream. (%s != push://*)", streamName.c_str(), streamCfg.getMember("source").asString().c_str());
|
||||||
myConn.close();
|
myConn.close();
|
||||||
}else{
|
}else{
|
||||||
std::string source = streamCfg.getMember("source").asString().substr(7);
|
std::string source = streamCfg.getMember("source").asString().substr(7);
|
||||||
|
@ -549,10 +645,10 @@ namespace Mist {
|
||||||
password = source.substr(source.find('@')+1);
|
password = source.substr(source.find('@')+1);
|
||||||
if (password != ""){
|
if (password != ""){
|
||||||
if (password == app_name){
|
if (password == app_name){
|
||||||
DEBUG_MSG(DLVL_DEVEL, "Password accepted - ignoring IP settings.");
|
INFO_MSG("Password accepted - ignoring IP settings.");
|
||||||
IP = "";
|
IP = "";
|
||||||
}else{
|
}else{
|
||||||
DEBUG_MSG(DLVL_DEVEL, "Password rejected - checking IP.");
|
INFO_MSG("Password rejected - checking IP.");
|
||||||
if (IP == ""){
|
if (IP == ""){
|
||||||
IP = "deny-all.invalid";
|
IP = "deny-all.invalid";
|
||||||
}
|
}
|
||||||
|
@ -562,7 +658,7 @@ namespace Mist {
|
||||||
if(Triggers::shouldTrigger("STREAM_PUSH", smp)){
|
if(Triggers::shouldTrigger("STREAM_PUSH", smp)){
|
||||||
std::string payload = streamName+"\n" + getConnectedHost() +"\n"+capa["name"].asStringRef()+"\n"+reqUrl;
|
std::string payload = streamName+"\n" + getConnectedHost() +"\n"+capa["name"].asStringRef()+"\n"+reqUrl;
|
||||||
if (!Triggers::doTrigger("STREAM_PUSH", payload, smp)){
|
if (!Triggers::doTrigger("STREAM_PUSH", payload, smp)){
|
||||||
DEBUG_MSG(DLVL_FAIL, "Push from %s to %s rejected - STREAM_PUSH trigger denied the push", getConnectedHost().c_str(), streamName.c_str());
|
FAIL_MSG("Push from %s to %s rejected - STREAM_PUSH trigger denied the push", getConnectedHost().c_str(), streamName.c_str());
|
||||||
myConn.close();
|
myConn.close();
|
||||||
configLock.post();
|
configLock.post();
|
||||||
configLock.close();
|
configLock.close();
|
||||||
|
@ -572,13 +668,13 @@ namespace Mist {
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
if (IP != ""){
|
if (IP != ""){
|
||||||
if (!myConn.isAddress(IP)){
|
if (!myConn.isAddress(IP)){
|
||||||
DEBUG_MSG(DLVL_FAIL, "Push from %s to %s rejected - source host not whitelisted", getConnectedHost().c_str(), streamName.c_str());
|
FAIL_MSG("Push from %s to %s rejected - source host not whitelisted", getConnectedHost().c_str(), streamName.c_str());
|
||||||
myConn.close();
|
myConn.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
DEBUG_MSG(DLVL_FAIL, "Push from %s rejected - stream '%s' not configured.", getConnectedHost().c_str(), streamName.c_str());
|
FAIL_MSG("Push from %s rejected - stream '%s' not configured.", getConnectedHost().c_str(), streamName.c_str());
|
||||||
myConn.close();
|
myConn.close();
|
||||||
}
|
}
|
||||||
configLock.post();
|
configLock.post();
|
||||||
|
@ -773,16 +869,22 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} //seek
|
} //seek
|
||||||
|
if (amfData.getContentP(0)->StrValue() == "_error") {
|
||||||
|
WARN_MSG("Received error response: %s", amfData.Print().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((amfData.getContentP(0)->StrValue() == "_result") || (amfData.getContentP(0)->StrValue() == "onFCPublish") || (amfData.getContentP(0)->StrValue() == "onStatus")) {
|
||||||
|
//Results are ignored. We don't really care.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG >= 2
|
WARN_MSG("AMF0 command not processed: %s", amfData.Print().c_str());
|
||||||
fprintf(stderr, "AMF0 command not processed!\n%s\n", amfData.Print().c_str());
|
|
||||||
#endif
|
|
||||||
//send a _result reply
|
//send a _result reply
|
||||||
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
AMF::Object amfReply("container", AMF::AMF0_DDV_CONTAINER);
|
||||||
amfReply.addContent(AMF::Object("", "_error")); //result success
|
amfReply.addContent(AMF::Object("", "_error")); //result success
|
||||||
amfReply.addContent(amfData.getContent(1)); //same transaction ID
|
amfReply.addContent(amfData.getContent(1)); //same transaction ID
|
||||||
amfReply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info
|
amfReply.addContent(AMF::Object("", amfData.getContentP(0)->StrValue())); //null - command info
|
||||||
amfReply.addContent(AMF::Object("Command not implemented or recognized", "")); //stream ID?
|
amfReply.addContent(AMF::Object("", "Command not implemented or recognized")); //stream ID?
|
||||||
sendCommand(amfReply, messageType, streamId);
|
sendCommand(amfReply, messageType, streamId);
|
||||||
} //parseAMFCommand
|
} //parseAMFCommand
|
||||||
|
|
||||||
|
@ -809,9 +911,7 @@ namespace Mist {
|
||||||
|
|
||||||
switch (next.msg_type_id) {
|
switch (next.msg_type_id) {
|
||||||
case 0: //does not exist
|
case 0: //does not exist
|
||||||
#if DEBUG >= 2
|
WARN_MSG("UNKN: Received a zero-type message. Possible data corruption? Aborting!");
|
||||||
fprintf(stderr, "UNKN: Received a zero-type message. Possible data corruption? Aborting!\n");
|
|
||||||
#endif
|
|
||||||
while (inputBuffer.size()) {
|
while (inputBuffer.size()) {
|
||||||
inputBuffer.get().clear();
|
inputBuffer.get().clear();
|
||||||
}
|
}
|
||||||
|
@ -820,20 +920,14 @@ namespace Mist {
|
||||||
break; //happens when connection breaks unexpectedly
|
break; //happens when connection breaks unexpectedly
|
||||||
case 1: //set chunk size
|
case 1: //set chunk size
|
||||||
RTMPStream::chunk_rec_max = ntohl(*(int *)next.data.c_str());
|
RTMPStream::chunk_rec_max = ntohl(*(int *)next.data.c_str());
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("CTRL: Set chunk size: %i", RTMPStream::chunk_rec_max);
|
||||||
fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case 2: //abort message - we ignore this one
|
case 2: //abort message - we ignore this one
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("CTRL: Abort message");
|
||||||
fprintf(stderr, "CTRL: Abort message\n");
|
|
||||||
#endif
|
|
||||||
//4 bytes of stream id to drop
|
//4 bytes of stream id to drop
|
||||||
break;
|
break;
|
||||||
case 3: //ack
|
case 3: //ack
|
||||||
#if DEBUG >= 8
|
VERYHIGH_MSG("CTRL: Acknowledgement");
|
||||||
fprintf(stderr, "CTRL: Acknowledgement\n");
|
|
||||||
#endif
|
|
||||||
RTMPStream::snd_window_at = ntohl(*(int *)next.data.c_str());
|
RTMPStream::snd_window_at = ntohl(*(int *)next.data.c_str());
|
||||||
RTMPStream::snd_window_at = RTMPStream::snd_cnt;
|
RTMPStream::snd_window_at = RTMPStream::snd_cnt;
|
||||||
break;
|
break;
|
||||||
|
@ -848,49 +942,43 @@ namespace Mist {
|
||||||
//6 = pingrequest, 4 bytes data
|
//6 = pingrequest, 4 bytes data
|
||||||
//7 = pingresponse, 4 bytes data
|
//7 = pingresponse, 4 bytes data
|
||||||
//we don't need to process this
|
//we don't need to process this
|
||||||
#if DEBUG >= 5
|
|
||||||
short int ucmtype = ntohs(*(short int *)next.data.c_str());
|
short int ucmtype = ntohs(*(short int *)next.data.c_str());
|
||||||
switch (ucmtype) {
|
switch (ucmtype) {
|
||||||
case 0:
|
case 0:
|
||||||
fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM StreamBegin %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM StreamEOF %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM StreamDry %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int *)(next.data.c_str() + 2))), ntohl(*((int *)(next.data.c_str() + 6))));
|
MEDIUM_MSG("CTRL: UCM SetBufferLength %i %i", ntohl(*((int *)(next.data.c_str() + 2))), ntohl(*((int *)(next.data.c_str() + 6))));
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM StreamIsRecorded %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM PingRequest %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int *)(next.data.c_str() + 2))));
|
MEDIUM_MSG("CTRL: UCM PingResponse %i", ntohl(*((int *)(next.data.c_str() + 2))));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype);
|
MEDIUM_MSG("CTRL: UCM Unknown (%hi)", ucmtype);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5: //window size of other end
|
case 5: //window size of other end
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("CTRL: Window size");
|
||||||
fprintf(stderr, "CTRL: Window size\n");
|
|
||||||
#endif
|
|
||||||
RTMPStream::rec_window_size = ntohl(*(int *)next.data.c_str());
|
RTMPStream::rec_window_size = ntohl(*(int *)next.data.c_str());
|
||||||
RTMPStream::rec_window_at = RTMPStream::rec_cnt;
|
RTMPStream::rec_window_at = RTMPStream::rec_cnt;
|
||||||
myConn.SendNow(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3)
|
myConn.SendNow(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3)
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("CTRL: Set peer bandwidth");
|
||||||
fprintf(stderr, "CTRL: Set peer bandwidth\n");
|
|
||||||
#endif
|
|
||||||
//4 bytes window size, 1 byte limit type (ignored)
|
//4 bytes window size, 1 byte limit type (ignored)
|
||||||
RTMPStream::snd_window_size = ntohl(*(int *)next.data.c_str());
|
RTMPStream::snd_window_size = ntohl(*(int *)next.data.c_str());
|
||||||
myConn.SendNow(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5)
|
myConn.SendNow(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5)
|
||||||
|
@ -925,21 +1013,19 @@ namespace Mist {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 15:
|
case 15:
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 data message");
|
MEDIUM_MSG("Received AMF3 data message");
|
||||||
break;
|
break;
|
||||||
case 16:
|
case 16:
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 shared object");
|
MEDIUM_MSG("Received AMF3 shared object");
|
||||||
break;
|
break;
|
||||||
case 17: {
|
case 17: {
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received AMF3 command message");
|
MEDIUM_MSG("Received AMF3 command message");
|
||||||
if (next.data[0] != 0) {
|
if (next.data[0] != 0) {
|
||||||
next.data = next.data.substr(1);
|
next.data = next.data.substr(1);
|
||||||
amf3data = AMF::parse3(next.data);
|
amf3data = AMF::parse3(next.data);
|
||||||
#if DEBUG >= 5
|
MEDIUM_MSG("AMF3: %s", amf3data.Print().c_str());
|
||||||
amf3data.Print();
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received AMF3-0 command message");
|
MEDIUM_MSG("Received AMF3-0 command message");
|
||||||
next.data = next.data.substr(1);
|
next.data = next.data.substr(1);
|
||||||
amfdata = AMF::parse(next.data);
|
amfdata = AMF::parse(next.data);
|
||||||
parseAMFCommand(amfdata, 17, next.msg_stream_id);
|
parseAMFCommand(amfdata, 17, next.msg_stream_id);
|
||||||
|
@ -947,7 +1033,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 19:
|
case 19:
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received AMF0 shared object");
|
MEDIUM_MSG("Received AMF0 shared object");
|
||||||
break;
|
break;
|
||||||
case 20: { //AMF0 command message
|
case 20: { //AMF0 command message
|
||||||
amfdata = AMF::parse(next.data);
|
amfdata = AMF::parse(next.data);
|
||||||
|
@ -955,10 +1041,10 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 22:
|
case 22:
|
||||||
DEBUG_MSG(DLVL_MEDIUM, "Received aggregate message");
|
MEDIUM_MSG("Received aggregate message");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
DEBUG_MSG(DLVL_FAIL, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.");
|
FAIL_MSG("Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ namespace Mist {
|
||||||
void sendNext();
|
void sendNext();
|
||||||
void sendHeader();
|
void sendHeader();
|
||||||
unsigned int needsPlayableKeys();
|
unsigned int needsPlayableKeys();
|
||||||
|
static bool listenMode();
|
||||||
protected:
|
protected:
|
||||||
bool isPushing;
|
bool isPushing;
|
||||||
void parseVars(std::string data);
|
void parseVars(std::string data);
|
||||||
|
|
Loading…
Add table
Reference in a new issue