diff --git a/src/Makefile.am b/src/Makefile.am index 08858b83..2587ab6e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,7 @@ SUBDIRS=converters analysers bin_PROGRAMS=MistBuffer MistController MistConnRAW MistConnRTMP MistConnHTTP MistConnHTTPProgressive MistConnHTTPDynamic MistConnHTTPSmooth MistPlayer MistBuffer_SOURCES=buffer.cpp buffer_user.h buffer_user.cpp buffer_stream.h buffer_stream.cpp tinythread.cpp tinythread.h ../VERSION MistBuffer_LDADD=$(MIST_LIBS) -lpthread -MistController_SOURCES=controller.cpp controller_connectors.h controller_connectors.cpp controller_storage.h controller_storage.cpp ../VERSION ./server.html.h +MistController_SOURCES=controller.cpp controller_connectors.h controller_connectors.cpp controller_storage.h controller_storage.cpp controller_streams.h controller_streams.cpp controller_capabilities.h controller_capabilities.cpp ../VERSION ./server.html.h MistConnRAW_SOURCES=conn_raw.cpp ../VERSION MistConnRTMP_SOURCES=conn_rtmp.cpp ../VERSION MistConnHTTP_SOURCES=conn_http.cpp tinythread.cpp tinythread.h ../VERSION ./embed.js.h diff --git a/src/analysers/amf_analyser.cpp b/src/analysers/amf_analyser.cpp index e98ed595..0cb66fe2 100644 --- a/src/analysers/amf_analyser.cpp +++ b/src/analysers/amf_analyser.cpp @@ -11,14 +11,16 @@ /// Debugging tool for AMF data. /// Expects AMF data through stdin, outputs human-readable information to stderr. -int main(int argc, char ** argv) { +int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); conf.parseArgs(argc, argv); std::string temp; - while (std::cin.good()){temp += std::cin.get();}//read all of std::cin to temp - temp.erase(temp.size()-1, 1);//strip the invalid last character - AMF::Object amfdata = AMF::parse(temp);//parse temp into an AMF::Object - amfdata.Print();//pretty-print the object + while (std::cin.good()){ + temp += std::cin.get(); + } //read all of std::cin to temp + temp.erase(temp.size() - 1, 1); //strip the invalid last character + AMF::Object amfdata = AMF::parse(temp); //parse temp into an AMF::Object + amfdata.Print(); //pretty-print the object return 0; } diff --git a/src/analysers/dtsc_analyser.cpp b/src/analysers/dtsc_analyser.cpp index a0f5a21a..88c10042 100644 --- a/src/analysers/dtsc_analyser.cpp +++ b/src/analysers/dtsc_analyser.cpp @@ -36,15 +36,21 @@ int main(int argc, char ** argv){ long long unsigned int bps = 0; F.seekNext(); - while (!F.getJSON().isNull()){ + while ( !F.getJSON().isNull()){ std::cout << F.getJSON().toPrettyString() << std::endl; nowpack = F.getJSON()["time"].asInt(); - if (firstpack == 0){firstpack = nowpack;} + if (firstpack == 0){ + firstpack = nowpack; + } if (F.getJSON()["datatype"].asString() == "audio"){ if (lastaudio != 0 && (nowpack - lastaudio) != 0){ bps = F.getJSON()["data"].asString().size() / (nowpack - lastaudio); - if (bps < aud_min){aud_min = bps;} - if (bps > aud_max){aud_max = bps;} + if (bps < aud_min){ + aud_min = bps; + } + if (bps > aud_max){ + aud_max = bps; + } } totalaudio += F.getJSON()["data"].asString().size(); lastaudio = nowpack; @@ -52,22 +58,34 @@ int main(int argc, char ** argv){ if (F.getJSON()["datatype"].asString() == "video"){ if (lastvideo != 0 && (nowpack - lastvideo) != 0){ bps = F.getJSON()["data"].asString().size() / (nowpack - lastvideo); - if (bps < vid_min){vid_min = bps;} - if (bps > vid_max){vid_max = bps;} + if (bps < vid_min){ + vid_min = bps; + } + if (bps > vid_max){ + vid_max = bps; + } } if (F.getJSON()["keyframe"].asInt() != 0){ if (lastkey != 0){ bps = nowpack - lastkey; - if (bps < key_min){key_min = bps;} - if (bps > key_max){key_max = bps;} + if (bps < key_min){ + key_min = bps; + } + if (bps > key_max){ + key_max = bps; + } } keyframes++; lastkey = nowpack; } if (F.getJSON()["offset"].asInt() != 0){ bps = F.getJSON()["offset"].asInt(); - if (bps < bfrm_min){bfrm_min = bps;} - if (bps > bfrm_max){bfrm_max = bps;} + if (bps < bfrm_min){ + bfrm_min = bps; + } + if (bps > bfrm_max){ + bfrm_max = bps; + } } totalvideo += F.getJSON()["data"].asString().size(); lastvideo = nowpack; @@ -76,7 +94,7 @@ int main(int argc, char ** argv){ } std::cout << std::endl << "Summary:" << std::endl; - meta["length"] = (long long int)((nowpack - firstpack)/1000); + meta["length"] = (long long int)((nowpack - firstpack) / 1000); if (meta.isMember("audio")){ meta["audio"]["bps"] = (long long int)(totalaudio / ((lastaudio - firstpack) / 1000)); std::cout << " Audio: " << meta["audio"]["codec"].asString() << std::endl; @@ -96,4 +114,4 @@ int main(int argc, char ** argv){ std::cout << " B-frames: " << bfrm_min << " - " << bfrm_max << std::endl; } return 0; -}//main +} //main diff --git a/src/analysers/flv_analyser.cpp b/src/analysers/flv_analyser.cpp index 00583338..27cb534d 100644 --- a/src/analysers/flv_analyser.cpp +++ b/src/analysers/flv_analyser.cpp @@ -15,20 +15,20 @@ #include /// Reads FLV from stdin and outputs human-readable information to stderr. -int main(int argc, char ** argv) { +int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); conf.parseArgs(argc, argv); FLV::Tag FLV_in; // Temporary storage for incoming FLV data. - std::ofstream vData( "vData" ); - std::ofstream aData( "aData" ); - while (!feof(stdin)){ + std::ofstream vData("vData"); + std::ofstream aData("aData"); + while ( !feof(stdin)){ if (FLV_in.FileLoader(stdin)){ std::cout << "Tag: " << FLV_in.tagType() << "\n\tTime: " << FLV_in.tagTime() << std::endl; - if( FLV_in.data[0] == 0x08 ) {//Audio - aData.write( FLV_in.data + 13, FLV_in.len - 17 ); + if (FLV_in.data[0] == 0x08){ //Audio + aData.write(FLV_in.data + 13, FLV_in.len - 17); } - if( FLV_in.data[0] == 0x09 ) {//Video - vData.write( FLV_in.data + 16, FLV_in.len - 20 ); + if (FLV_in.data[0] == 0x09){ //Video + vData.write(FLV_in.data + 16, FLV_in.len - 20); } } } diff --git a/src/analysers/mp4_analyser.cpp b/src/analysers/mp4_analyser.cpp index b8d3bfea..41537370 100644 --- a/src/analysers/mp4_analyser.cpp +++ b/src/analysers/mp4_analyser.cpp @@ -12,21 +12,23 @@ /// Debugging tool for MP4 data. /// Expects MP4 data through stdin, outputs human-readable information to stderr. -int main(int argc, char ** argv) { +int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); conf.parseArgs(argc, argv); std::string temp; - while (std::cin.good()){temp += std::cin.get();}//read all of std::cin to temp - temp.erase(temp.size()-1, 1);//strip the invalid last character + while (std::cin.good()){ + temp += std::cin.get(); + } //read all of std::cin to temp + temp.erase(temp.size() - 1, 1); //strip the invalid last character MP4::Box mp4data; while (mp4data.read(temp)){ std::cerr << mp4data.toPrettyString(0) << std::endl; - if( mp4data.isType( "mdat" ) ) { + if (mp4data.isType("mdat")){ std::ofstream oFile; - oFile.open( "mdat" ); - oFile << std::string( mp4data.payload(), mp4data.payloadSize() ); + oFile.open("mdat"); + oFile << std::string(mp4data.payload(), mp4data.payloadSize()); oFile.close(); } } diff --git a/src/analysers/rtmp_analyser.cpp b/src/analysers/rtmp_analyser.cpp index 6dd8364a..86d27591 100644 --- a/src/analysers/rtmp_analyser.cpp +++ b/src/analysers/rtmp_analyser.cpp @@ -30,9 +30,11 @@ int Detail = 0; /// Automatically skips 3073 bytes of handshake data. int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); - conf.addOption("detail", JSON::fromString("{\"arg_num\":1, \"arg\":\"integer\", \"default\":0, \"help\":\"Bitmask, 1 = Reconstruct, 2 = Explicit media info, 4 = Verbose chunks\"}")); + conf.addOption("detail", + JSON::fromString( + "{\"arg_num\":1, \"arg\":\"integer\", \"default\":0, \"help\":\"Bitmask, 1 = Reconstruct, 2 = Explicit media info, 4 = Verbose chunks\"}")); conf.parseArgs(argc, argv); - + Detail = conf.getInteger("detail"); if (Detail > 0){ fprintf(stderr, "Detail level set:\n"); @@ -49,36 +51,38 @@ int main(int argc, char ** argv){ } std::string inbuffer; - while (std::cin.good()){inbuffer += std::cin.get();}//read all of std::cin to temp - inbuffer.erase(0, 3073);//strip the handshake part + while (std::cin.good()){ + inbuffer += std::cin.get(); + } //read all of std::cin to temp + inbuffer.erase(0, 3073); //strip the handshake part RTMPStream::Chunk next; - FLV::Tag F;//FLV holder + FLV::Tag F; //FLV holder AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER); AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER); - while (next.Parse(inbuffer)){ if ((Detail & DETAIL_VERBOSE) == DETAIL_VERBOSE){ - fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp, next.len, next.msg_type_id, next.msg_stream_id); + fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp, + next.len, next.msg_type_id, next.msg_stream_id); } switch (next.msg_type_id){ - case 0://does not exist + case 0: //does not exist fprintf(stderr, "Error chunk - %i, %i, %i, %i, %i\n", next.cs_id, next.timestamp, next.real_len, next.len_left, next.msg_stream_id); //return 0; - break;//happens when connection breaks unexpectedly - case 1://set chunk size + break; //happens when connection breaks unexpectedly + case 1: //set chunk size RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str()); fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max); break; - case 2://abort message - we ignore this one + case 2: //abort message - we ignore this one fprintf(stderr, "CTRL: Abort message: %i\n", ntohl(*(int*)next.data.c_str())); //4 bytes of stream id to drop break; - case 3://ack + case 3: //ack RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str()); fprintf(stderr, "CTRL: Acknowledgement: %i\n", RTMPStream::snd_window_at); break; - case 4:{ + case 4: { short int ucmtype = ntohs(*(short int*)next.data.c_str()); switch (ucmtype){ case 0: @@ -106,8 +110,9 @@ int main(int argc, char ** argv){ fprintf(stderr, "CTRL: User control message: UNKNOWN %hu - %u\n", ucmtype, ntohl(*(unsigned int*)(next.data.c_str()+2))); break; } - } break; - case 5://window size of other end + } + break; + case 5: //window size of other end RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str()); RTMPStream::rec_window_at = RTMPStream::rec_cnt; fprintf(stderr, "CTRL: Window size: %i\n", RTMPStream::rec_window_size); @@ -147,19 +152,20 @@ int main(int argc, char ** argv){ case 16: fprintf(stderr, "Received AFM3 shared object\n"); break; - case 17:{ + case 17: { fprintf(stderr, "Received AFM3 command message:\n"); - char soort = next.data[0]; - next.data = next.data.substr(1); - if (soort == 0){ - amfdata = AMF::parse(next.data); - std::cerr << amfdata.Print() << std::endl; - }else{ - amf3data = AMF::parse3(next.data); - amf3data.Print(); - } - } break; - case 18:{ + char soort = next.data[0]; + next.data = next.data.substr(1); + if (soort == 0){ + amfdata = AMF::parse(next.data); + std::cerr << amfdata.Print() << std::endl; + }else{ + amf3data = AMF::parse3(next.data); + amf3data.Print(); + } + } + break; + case 18: { fprintf(stderr, "Received AFM0 data message (metadata):\n"); amfdata = AMF::parse(next.data); amfdata.Print(); @@ -167,15 +173,17 @@ int main(int argc, char ** argv){ F.ChunkLoader(next); std::cout.write(F.data, F.len); } - } break; + } + break; case 19: fprintf(stderr, "Received AFM0 shared object\n"); break; - case 20:{//AMF0 command message + case 20: { //AMF0 command message fprintf(stderr, "Received AFM0 command message:\n"); amfdata = AMF::parse(next.data); std::cerr << amfdata.Print() << std::endl; - } break; + } + break; case 22: fprintf(stderr, "Received aggregate message\n"); break; @@ -183,8 +191,8 @@ int main(int argc, char ** argv){ fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n"); return 1; break; - }//switch for type of chunk - }//while chunk parsed + } //switch for type of chunk + } //while chunk parsed fprintf(stderr, "No more readable data\n"); return 0; -}//main \ No newline at end of file +} //main diff --git a/src/buffer.cpp b/src/buffer.cpp index cd3888c0..cdc22e5f 100644 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -17,7 +17,7 @@ #include /// Holds all code unique to the Buffer. -namespace Buffer{ +namespace Buffer { volatile bool buffer_running = true; ///< Set to false when shutting down. Stream * thisStream = 0; @@ -26,19 +26,20 @@ namespace Buffer{ /// Gets the current system time in milliseconds. long long int getNowMS(){ timeval t; - gettimeofday(&t, 0); - return t.tv_sec * 1000 + t.tv_usec/1000; - }//getNowMS - + gettimeofday( &t, 0); + return t.tv_sec * 1000 + t.tv_usec / 1000; + } //getNowMS void handleStats(void * empty){ - if (empty != 0){return;} + if (empty != 0){ + return; + } std::string double_newline = "\n\n"; Socket::Connection StatsSocket = Socket::Connection("/tmp/mist/statistics", true); while (buffer_running){ usleep(1000000); //sleep one second Stream::get()->cleanUsers(); - if (!StatsSocket.connected()){ + if ( !StatsSocket.connected()){ StatsSocket = Socket::Connection("/tmp/mist/statistics", true); } if (StatsSocket.connected()){ @@ -52,9 +53,9 @@ namespace Buffer{ void handleUser(void * v_usr){ user * usr = (user*)v_usr; - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Thread launched for user " << usr->MyStr << ", socket number " << usr->S.getSocket() << std::endl; - #endif +#endif usr->myRing = thisStream->getRing(); if (thisStream->getHeader().size() > 0){ @@ -66,19 +67,19 @@ namespace Buffer{ usr->Send(); if (usr->S.spool() && usr->S.Received().size()){ //delete anything that doesn't end with a newline - if (!usr->S.Received().get().empty() && *(usr->S.Received().get().rbegin()) != '\n'){ + if ( !usr->S.Received().get().empty() && *(usr->S.Received().get().rbegin()) != '\n'){ usr->S.Received().get().clear(); continue; } usr->S.Received().get().resize(usr->S.Received().get().size() - 1); - if (!usr->S.Received().get().empty()){ + if ( !usr->S.Received().get().empty()){ switch (usr->S.Received().get()[0]){ - case 'P':{ //Push + case 'P': { //Push std::cout << "Push attempt from IP " << usr->S.Received().get().substr(2) << std::endl; if (thisStream->checkWaitingIP(usr->S.Received().get().substr(2))){ if (thisStream->setInput(usr->S)){ std::cout << "Push accepted!" << std::endl; - usr->S = Socket::Connection(-1); + usr->S = Socket::Connection( -1); return; }else{ usr->Disconnect("Push denied - push already in progress!"); @@ -86,31 +87,40 @@ namespace Buffer{ }else{ usr->Disconnect("Push denied - invalid IP address!"); } - } break; - case 'S':{ //Stats + } + break; + case 'S': { //Stats usr->tmpStats = Stats(usr->S.Received().get().substr(2)); unsigned int secs = usr->tmpStats.conntime - usr->lastStats.conntime; - if (secs < 1){secs = 1;} + if (secs < 1){ + secs = 1; + } usr->curr_up = (usr->tmpStats.up - usr->lastStats.up) / secs; usr->curr_down = (usr->tmpStats.down - usr->lastStats.down) / secs; usr->lastStats = usr->tmpStats; thisStream->saveStats(usr->MyStr, usr->tmpStats); - } break; - case 's':{ //second-seek + } + break; + case 's': { //second-seek //ignored for now - } break; - case 'f':{ //frame-seek + } + break; + case 'f': { //frame-seek //ignored for now - } break; - case 'p':{ //play + } + break; + case 'p': { //play //ignored for now - } break; - case 'o':{ //once-play + } + break; + case 'o': { //once-play //ignored for now - } break; - case 'q':{ //quit-playing + } + break; + case 'q': { //quit-playing //ignored for now - } break; + } + break; } } } @@ -120,11 +130,13 @@ namespace Buffer{ /// Loop reading DTSC data from stdin and processing it at the correct speed. void handleStdin(void * empty){ - if (empty != 0){return;} - long long int timeDiff = 0;//difference between local time and stream time - unsigned int lastPacket = 0;//last parsed packet timestamp + if (empty != 0){ + return; + } + long long int timeDiff = 0; //difference between local time and stream time + unsigned int lastPacket = 0; //last parsed packet timestamp std::string inBuffer; - char charBuffer[1024*10]; + char charBuffer[1024 * 10]; unsigned int charCount; long long int now; @@ -142,7 +154,7 @@ namespace Buffer{ thisStream->dropWriteLock(true); }else{ thisStream->dropWriteLock(false); - std::cin.read(charBuffer, 1024*10); + std::cin.read(charBuffer, 1024 * 10); charCount = std::cin.gcount(); inBuffer.append(charBuffer, charCount); } @@ -157,7 +169,9 @@ namespace Buffer{ /// Loop reading DTSC data from an IP push address. /// No changes to the speed are made. void handlePushin(void * empty){ - if (empty != 0){return;} + if (empty != 0){ + return; + } while (buffer_running){ if (thisStream->getIPInput().connected()){ if (thisStream->getIPInput().spool()){ @@ -167,30 +181,34 @@ namespace Buffer{ thisStream->dropWriteLock(true); }else{ thisStream->dropWriteLock(false); - usleep(1000);//1ms wait + usleep(1000); //1ms wait } }else{ - usleep(1000);//1ms wait + usleep(1000); //1ms wait } }else{ - usleep(1000000);//1s wait + usleep(1000000); //1s wait } } SS.close(); } /// Starts a loop, waiting for connections to send data to. - int Start(int argc, char ** argv) { + int Start(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); - conf.addOption("stream_name", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Name of the stream this buffer will be providing.\"}")); - conf.addOption("awaiting_ip", JSON::fromString("{\"arg_num\":2, \"arg\":\"string\", \"default\":\"\", \"help\":\"IP address to expect incoming data from. This will completely disable reading from standard input if used.\"}")); - conf.addOption("reportstats", JSON::fromString("{\"default\":0, \"help\":\"Report stats to a controller process.\", \"short\":\"s\", \"long\":\"reportstats\"}")); + conf.addOption("stream_name", + JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Name of the stream this buffer will be providing.\"}")); + conf.addOption("awaiting_ip", + JSON::fromString( + "{\"arg_num\":2, \"arg\":\"string\", \"default\":\"\", \"help\":\"IP address to expect incoming data from. This will completely disable reading from standard input if used.\"}")); + conf.addOption("reportstats", + JSON::fromString("{\"default\":0, \"help\":\"Report stats to a controller process.\", \"short\":\"s\", \"long\":\"reportstats\"}")); conf.parseArgs(argc, argv); std::string name = conf.getString("stream_name"); SS = Util::Stream::makeLive(name); - if (!SS.connected()) { + if ( !SS.connected()){ perror("Could not create stream socket"); return 1; } @@ -201,7 +219,9 @@ namespace Buffer{ Socket::Connection std_input(fileno(stdin)); tthread::thread * StatsThread = 0; - if (conf.getBool("reportstats")){StatsThread = new tthread::thread(handleStats, 0);} + if (conf.getBool("reportstats")){ + StatsThread = new tthread::thread(handleStats, 0); + } tthread::thread * StdinThread = 0; std::string await_ip = conf.getString("awaiting_ip"); if (await_ip == ""){ @@ -220,7 +240,7 @@ namespace Buffer{ thisStream->addUser(usr_ptr); usr_ptr->Thread = new tthread::thread(handleUser, (void *)usr_ptr); } - }//main loop + } //main loop // disconnect listener buffer_running = false; @@ -230,16 +250,20 @@ namespace Buffer{ StatsThread->join(); delete StatsThread; } - if (thisStream->getIPInput().connected()){thisStream->getIPInput().close();} + if (thisStream->getIPInput().connected()){ + thisStream->getIPInput().close(); + } StdinThread->join(); delete StdinThread; delete thisStream; return 0; } -};//Buffer namespace +} +; +//Buffer namespace /// Entry point for Buffer, simply calls Buffer::Start(). int main(int argc, char ** argv){ return Buffer::Start(argc, argv); -}//main +} //main diff --git a/src/buffer_stream.cpp b/src/buffer_stream.cpp index 8f56327b..ef88a2f8 100644 --- a/src/buffer_stream.cpp +++ b/src/buffer_stream.cpp @@ -13,7 +13,9 @@ Buffer::Stream * Buffer::Stream::get(){ if (ref == 0){ //prevent creating two at the same time creator.lock(); - if (ref == 0){ref = new Stream();} + if (ref == 0){ + ref = new Stream(); + } creator.unlock(); } return ref; @@ -31,9 +33,9 @@ Buffer::Stream::~Stream(){ while (users.size() > 0){ stats_mutex.lock(); for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - if ((**usersIt).S.connected()){ - (**usersIt).S.close(); - printf("Closing user %s\n", (**usersIt).MyStr.c_str()); + if (( * *usersIt).S.connected()){ + ( * *usersIt).S.close(); + printf("Closing user %s\n", ( * *usersIt).MyStr.c_str()); } } stats_mutex.unlock(); @@ -51,8 +53,8 @@ std::string & Buffer::Stream::getStats(){ stats_mutex.lock(); if (users.size() > 0){ for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - tot_down += (**usersIt).curr_down; - tot_up += (**usersIt).curr_up; + tot_down += ( * *usersIt).curr_down; + tot_up += ( * *usersIt).curr_up; tot_count++; } } @@ -62,8 +64,12 @@ std::string & Buffer::Stream::getStats(){ Storage["totals"]["now"] = now; Storage["buffer"] = name; Storage["meta"] = Strm->metadata; - if (Storage["meta"].isMember("audio")){Storage["meta"]["audio"].removeMember("init");} - if (Storage["meta"].isMember("video")){Storage["meta"]["video"].removeMember("init");} + if (Storage["meta"].isMember("audio")){ + Storage["meta"]["audio"].removeMember("init"); + } + if (Storage["meta"].isMember("video")){ + Storage["meta"]["video"].removeMember("init"); + } ret = Storage.toString(); Storage["log"].null(); stats_mutex.unlock(); @@ -92,7 +98,7 @@ void Buffer::Stream::setWaitingIP(std::string ip){ /// Check if this is the IP address to accept push data from. bool Buffer::Stream::checkWaitingIP(std::string ip){ - if (ip == waiting_ip || ip == "::ffff:"+waiting_ip){ + if (ip == waiting_ip || ip == "::ffff:" + waiting_ip){ return true; }else{ std::cout << ip << " != " << waiting_ip << std::endl; @@ -115,7 +121,6 @@ Socket::Connection & Buffer::Stream::getIPInput(){ return ip_input; } - /// Stores intermediate statistics. void Buffer::Stream::saveStats(std::string username, Stats & stats){ stats_mutex.lock(); @@ -133,9 +138,10 @@ void Buffer::Stream::clearStats(std::string username, Stats & stats, std::string stats_mutex.lock(); if (Storage["curr"].isMember(username)){ Storage["curr"].removeMember(username); - #if DEBUG >= 4 - std::cout << "Disconnected user " << username << ": " << reason << ". " << stats.connector << " transferred " << stats.up << " up and " << stats.down << " down in " << stats.conntime << " seconds to " << stats.host << std::endl; - #endif +#if DEBUG >= 4 + std::cout << "Disconnected user " << username << ": " << reason << ". " << stats.connector << " transferred " << stats.up << " up and " + << stats.down << " down in " << stats.conntime << " seconds to " << stats.host << std::endl; +#endif } Storage["log"][username]["connector"] = stats.connector; Storage["log"][username]["up"] = stats.up; @@ -154,23 +160,23 @@ void Buffer::Stream::cleanUsers(){ repeat = false; if (users.size() > 0){ for (usersIt = users.begin(); usersIt != users.end(); usersIt++){ - if ((**usersIt).Thread == 0 && !(**usersIt).S.connected()){ + if (( * *usersIt).Thread == 0 && !( * *usersIt).S.connected()){ delete *usersIt; users.erase(usersIt); repeat = true; break; }else{ - if (!(**usersIt).S.connected()){ - if ((**usersIt).Thread->joinable()){ - (**usersIt).Thread->join(); - delete (**usersIt).Thread; - (**usersIt).Thread = 0; + if ( !( * *usersIt).S.connected()){ + if (( * *usersIt).Thread->joinable()){ + ( * *usersIt).Thread->join(); + delete ( * *usersIt).Thread; + ( * *usersIt).Thread = 0; } } } } } - }while(repeat); + }while (repeat); stats_mutex.unlock(); } @@ -190,7 +196,9 @@ void Buffer::Stream::dropWriteLock(bool newpackets_available){ writers--; rw_mutex.unlock(); rw_change.notify_all(); - if (newpackets_available){moreData.notify_all();} + if (newpackets_available){ + moreData.notify_all(); + } } /// Blocks until reading is safe. diff --git a/src/buffer_stream.h b/src/buffer_stream.h index 81784d39..e0f58133 100644 --- a/src/buffer_stream.h +++ b/src/buffer_stream.h @@ -3,11 +3,13 @@ #pragma once #include +#include #include +#include #include "tinythread.h" #include "buffer_user.h" -namespace Buffer{ +namespace Buffer { /// Keeps track of a single streams inputs and outputs, taking care of thread safety and all other related issues. class Stream{ public: @@ -53,9 +55,9 @@ namespace Buffer{ void waitForData(); /// Cleanup function ~Stream(); - private: - volatile int readers;///< Current count of active readers; - volatile int writers;///< Current count of waiting/active writers. + private: + volatile int readers; ///< Current count of active readers; + volatile int writers; ///< Current count of waiting/active writers. tthread::mutex rw_mutex; ///< Mutex for read/write locking. tthread::condition_variable rw_change; ///< Triggered when reader/writer count changes. static Stream * ref; @@ -70,4 +72,5 @@ namespace Buffer{ std::string name; ///< Name for this buffer. tthread::condition_variable moreData; ///< Triggered when more data becomes available. }; -}; +} +; diff --git a/src/buffer_user.cpp b/src/buffer_user.cpp index 6d52be68..e67a04dc 100644 --- a/src/buffer_user.cpp +++ b/src/buffer_user.cpp @@ -5,7 +5,6 @@ #include "buffer_stream.h" #include #include //for atoi and friends - int Buffer::user::UserCount = 0; /// Creates a new user from a newly connected socket. @@ -21,42 +20,54 @@ Buffer::user::user(Socket::Connection fd){ currsend = 0; myRing = 0; Thread = 0; -}//constructor + gotproperaudio = false; + lastpointer = 0; +} //constructor /// Drops held DTSC::Ring class, if one is held. Buffer::user::~user(){ Stream::get()->dropRing(myRing); -}//destructor +} //destructor /// Disconnects the current user. Doesn't do anything if already disconnected. /// Prints "Disconnected user" to stdout if disconnect took place. -void Buffer::user::Disconnect(std::string reason) { - if (S.connected()){S.close();} +void Buffer::user::Disconnect(std::string reason){ + if (S.connected()){ + S.close(); + } Stream::get()->clearStats(MyStr, lastStats, reason); -}//Disconnect +} //Disconnect /// Tries to send the current buffer, returns true if success, false otherwise. /// Has a side effect of dropping the connection if send will never complete. bool Buffer::user::doSend(const char * ptr, int len){ - if (!len){return false;}//do not do empty sends - int r = S.iwrite(ptr+currsend, len-currsend); + if ( !len){ + return false; + } //do not do empty sends + int r = S.iwrite(ptr + currsend, len - currsend); if (r <= 0){ - if (errno == EWOULDBLOCK){return false;} + if (errno == EWOULDBLOCK){ + return false; + } Disconnect(S.getError()); return false; } currsend += r; return (currsend == len); -}//doSend +} //doSend /// Try to send data to this user. Disconnects if any problems occur. void Buffer::user::Send(){ - if (!myRing){return;}//no ring! - if (!S.connected()){return;}//cancel if not connected + if ( !myRing){ + return; + } //no ring! + if ( !S.connected()){ + return; + } //cancel if not connected if (myRing->waiting){ Stream::get()->waitForData(); return; - }//still waiting for next buffer? + } //still waiting for next buffer? if (myRing->starved){ //if corrupt data, warn and get new DTSC::Ring std::cout << "Warning: User " << MyNum << " was send corrupt video data and send to the next keyframe!" << std::endl; @@ -69,11 +80,14 @@ void Buffer::user::Send(){ if (doSend(Stream::get()->getStream()->outPacket(myRing->b).c_str(), Stream::get()->getStream()->outPacket(myRing->b).length())){ //switch to next buffer currsend = 0; - if (myRing->b <= 0){myRing->waiting = true; return;}//no next buffer? go in waiting mode. + if (myRing->b <= 0){ + myRing->waiting = true; + return; + } //no next buffer? go in waiting mode. myRing->b--; - }//completed a send + } //completed a send Stream::get()->dropReadLock(); -}//send +} //send /// Default constructor - should not be in use. Buffer::Stats::Stats(){ @@ -87,22 +101,22 @@ Buffer::Stats::Stats(std::string s){ size_t f = s.find(' '); if (f != std::string::npos){ host = s.substr(0, f); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ connector = s.substr(0, f); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ conntime = atoi(s.substr(0, f).c_str()); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ up = atoi(s.substr(0, f).c_str()); - s.erase(0, f+1); + s.erase(0, f + 1); down = atoi(s.c_str()); } } diff --git a/src/buffer_user.h b/src/buffer_user.h index dd85ce39..a58acb45 100644 --- a/src/buffer_user.h +++ b/src/buffer_user.h @@ -7,17 +7,17 @@ #include #include "tinythread.h" -namespace Buffer{ +namespace Buffer { /// Converts a stats line to up, down, host, connector and conntime values. class Stats{ - public: - unsigned int up; - unsigned int down; - std::string host; - std::string connector; - unsigned int conntime; - Stats(); - Stats(std::string s); + public: + unsigned int up; + unsigned int down; + std::string host; + std::string connector; + unsigned int conntime; + Stats(); + Stats(std::string s); }; /// Holds connected users. diff --git a/src/conn_http.cpp b/src/conn_http.cpp index 37419a89..535a41a1 100644 --- a/src/conn_http.cpp +++ b/src/conn_http.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,7 +21,7 @@ #include "embed.js.h" /// Holds everything unique to HTTP Connector. -namespace Connector_HTTP{ +namespace Connector_HTTP { /// Class for keeping track of connections to connectors. class ConnConn{ @@ -34,12 +33,14 @@ namespace Connector_HTTP{ ConnConn(){ conn = 0; lastuse = 0; - }; + } + ; /// Constructor that sets lastuse to 0, but socket to s. ConnConn(Socket::Connection * s){ conn = s; lastuse = 0; - }; + } + ; /// Destructor that deletes the socket if non-null. ~ConnConn(){ if (conn){ @@ -47,7 +48,8 @@ namespace Connector_HTTP{ delete conn; } conn = 0; - }; + } + ; }; std::map connconn; ///< Connections to connectors @@ -59,7 +61,7 @@ namespace Connector_HTTP{ tthread::thread * timeouter = 0; ///< Thread that times out connections to connectors. void Timeout_Thread(void * n){ - n = 0;//prevent unused variable warning + n = 0; //prevent unused variable warning tthread::lock_guard guard(timeout_mutex); while (true){ { @@ -69,19 +71,21 @@ namespace Connector_HTTP{ } std::map::iterator it; for (it = connconn.begin(); it != connconn.end(); it++){ - if (!it->second->conn->connected() || it->second->lastuse++ > 15){ + if ( !it->second->conn->connected() || it->second->lastuse++ > 15){ if (it->second->in_use.try_lock()){ it->second->in_use.unlock(); delete it->second; connconn.erase(it); - it = connconn.begin();//get a valid iterator - if (it == connconn.end()){return;} + it = connconn.begin(); //get a valid iterator + if (it == connconn.end()){ + return; + } } } } conn_mutex.unlock(); } - usleep(1000000);//sleep 1 second and re-check + usleep(1000000); //sleep 1 second and re-check } } @@ -89,14 +93,16 @@ namespace Connector_HTTP{ void Handle_None(HTTP::Parser & H, Socket::Connection * conn){ H.Clean(); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody("Unsupported Media Type

Unsupported Media Type

The server isn't quite sure what you wanted to receive from it."); + H.SetBody( + "Unsupported Media Type

Unsupported Media Type

The server isn't quite sure what you wanted to receive from it."); conn->SendNow(H.BuildResponse("415", "Unsupported Media Type")); } void Handle_Timeout(HTTP::Parser & H, Socket::Connection * conn){ H.Clean(); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody("Gateway timeout

Gateway timeout

Though the server understood your request and attempted to handle it, somehow handling it took longer than it should. Your request has been cancelled - please try again later."); + H.SetBody( + "Gateway timeout

Gateway timeout

Though the server understood your request and attempted to handle it, somehow handling it took longer than it should. Your request has been cancelled - please try again later."); conn->SendNow(H.BuildResponse("504", "Gateway Timeout")); } @@ -109,21 +115,24 @@ namespace Connector_HTTP{ H.Clean(); H.SetHeader("Content-Type", "text/xml"); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody(""); + H.SetBody( + ""); conn->SendNow(H.BuildResponse("200", "OK")); return; - }//crossdomain.xml + } //crossdomain.xml if (url == "/clientaccesspolicy.xml"){ H.Clean(); H.SetHeader("Content-Type", "text/xml"); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); - H.SetBody(""); + H.SetBody( + ""); conn->SendNow(H.BuildResponse("200", "OK")); return; - }//clientaccesspolicy.xml + } //clientaccesspolicy.xml - if ((url.length() > 9 && url.substr(0, 6) == "/info_" && url.substr(url.length() - 3, 3) == ".js") || (url.length() > 10 && url.substr(0, 7) == "/embed_" && url.substr(url.length() - 3, 3) == ".js")){ + if ((url.length() > 9 && url.substr(0, 6) == "/info_" && url.substr(url.length() - 3, 3) == ".js") + || (url.length() > 10 && url.substr(0, 7) == "/embed_" && url.substr(url.length() - 3, 3) == ".js")){ std::string streamname; if (url.substr(0, 6) == "/info_"){ streamname = url.substr(6, url.length() - 9); @@ -134,7 +143,9 @@ namespace Connector_HTTP{ JSON::Value ServConf = JSON::fromFile("/tmp/mist/streamlist"); std::string response; std::string host = H.GetHeader("Host"); - if (host.find(':')){host.resize(host.find(':'));} + if (host.find(':')){ + host.resize(host.find(':')); + } H.Clean(); H.SetHeader("Server", "mistserver/" PACKAGE_VERSION "/" + Util::Config::libver); H.SetHeader("Content-Type", "application/javascript"); @@ -145,72 +156,74 @@ namespace Connector_HTTP{ json_resp["height"] = ServConf["streams"][streamname]["meta"]["video"]["height"].asInt(); //first, see if we have RTMP working and output all the RTMP. for (JSON::ArrIter it = ServConf["config"]["protocols"].ArrBegin(); it != ServConf["config"]["protocols"].ArrEnd(); it++){ - if ((*it)["connector"].asString() == "RTMP"){ + if (( *it)["connector"].asString() == "RTMP"){ JSON::Value tmp; tmp["type"] = "rtmp"; - tmp["url"] = "rtmp://" + host + ":" + (*it)["port"].asString() + "/play/" + streamname; + tmp["url"] = "rtmp://" + host + ":" + ( *it)["port"].asString() + "/play/" + streamname; json_resp["source"].append(tmp); } } //then, see if we have HTTP working and output all the dynamic. for (JSON::ArrIter it = ServConf["config"]["protocols"].ArrBegin(); it != ServConf["config"]["protocols"].ArrEnd(); it++){ - if ((*it)["connector"].asString() == "HTTP"){ + if (( *it)["connector"].asString() == "HTTP"){ JSON::Value tmp; tmp["type"] = "f4v"; - tmp["url"] = "http://" + host + ":" + (*it)["port"].asString() + "/"+streamname+"/manifest.f4m"; + tmp["url"] = "http://" + host + ":" + ( *it)["port"].asString() + "/" + streamname + "/manifest.f4m"; json_resp["source"].append(tmp); } } //and all the progressive. for (JSON::ArrIter it = ServConf["config"]["protocols"].ArrBegin(); it != ServConf["config"]["protocols"].ArrEnd(); it++){ - if ((*it)["connector"].asString() == "HTTP"){ + if (( *it)["connector"].asString() == "HTTP"){ JSON::Value tmp; tmp["type"] = "flv"; - tmp["url"] = "http://" + host + ":" + (*it)["port"].asString() + "/"+streamname+".flv"; + tmp["url"] = "http://" + host + ":" + ( *it)["port"].asString() + "/" + streamname + ".flv"; json_resp["source"].append(tmp); } } }else{ json_resp["error"] = "The specified stream is not available on this server."; - json_resp["bbq"] = "sauce";//for legacy purposes ^_^ + json_resp["bbq"] = "sauce"; //for legacy purposes ^_^ } - response += "mistvideo['" + streamname + "'] = "+json_resp.toString()+";\n"; + response += "mistvideo['" + streamname + "'] = " + json_resp.toString() + ";\n"; if (url.substr(0, 6) != "/info_" && !json_resp.isMember("error")){ response.append("\n("); - response.append((char*)embed_js, (size_t)embed_js_len-2);//remove trailing ";\n" from xxd conversion + response.append((char*)embed_js, (size_t)embed_js_len - 2); //remove trailing ";\n" from xxd conversion response.append("(\"" + streamname + "\"));\n"); } H.SetBody(response); conn->SendNow(H.BuildResponse("200", "OK")); return; - }//embed code generator + } //embed code generator - Handle_None(H, conn);//anything else doesn't get handled + Handle_None(H, conn); //anything else doesn't get handled } /// Handles requests without associated handler, displaying a nice friendly error message. void Handle_Through_Connector(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 - std::string uid = Secure::md5(H.GetHeader("User-Agent")+conn->getHost())+"_"+H.GetVar("stream")+"_"+connector; - H.SetHeader("X-UID", uid);//add the UID to the headers before copying - H.SetHeader("X-Origin", conn->getHost());//add the UID to the headers before copying - std::string request = H.BuildRequest();//copy the request for later forwarding to the connector + std::string uid = Secure::md5(H.GetHeader("User-Agent") + conn->getHost()) + "_" + H.GetVar("stream") + "_" + connector; + H.SetHeader("X-UID", uid); //add the UID to the headers before copying + H.SetHeader("X-Origin", conn->getHost()); //add the UID to the headers before copying + std::string request = H.BuildRequest(); //copy the request for later forwarding to the connector std::string orig_url = H.getUrl(); H.Clean(); //check if a connection exists, and if not create one conn_mutex.lock(); - if (!connconn.count(uid) || !connconn[uid]->conn->connected()){ - if (connconn.count(uid)){connconn.erase(uid);} - connconn[uid] = new ConnConn(new Socket::Connection("/tmp/mist/http_"+connector)); - connconn[uid]->conn->setBlocking(false);//do not block on spool() with no data - #if DEBUG >= 4 + if ( !connconn.count(uid) || !connconn[uid]->conn->connected()){ + if (connconn.count(uid)){ + connconn.erase(uid); + } + connconn[uid] = new ConnConn(new Socket::Connection("/tmp/mist/http_" + connector)); + connconn[uid]->conn->setBlocking(false); //do not block on spool() with no data +#if DEBUG >= 4 std::cout << "Created new connection " << uid << std::endl; - #endif +#endif }else{ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cout << "Re-using connection " << uid << std::endl; - #endif +#endif } //start a new timeout thread, if neccesary if (timeout_mutex.try_lock()){ @@ -226,7 +239,7 @@ namespace Connector_HTTP{ //lock the mutex for this connection, and handle the request tthread::lock_guard guard(connconn[uid]->in_use); //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()){ Handle_Timeout(H, conn); return; } @@ -239,7 +252,7 @@ namespace Connector_HTTP{ conn->spool(); if (connconn[uid]->conn->Received().size() || connconn[uid]->conn->spool()){ //make sure we end in a \n - if (*(connconn[uid]->conn->Received().get().rbegin()) != '\n'){ + if ( *(connconn[uid]->conn->Received().get().rbegin()) != '\n'){ std::string tmp = connconn[uid]->conn->Received().get(); connconn[uid]->conn->Received().get().clear(); if (connconn[uid]->conn->Received().size()){ @@ -250,7 +263,7 @@ namespace Connector_HTTP{ } //check if the whole response was received if (H.Read(connconn[uid]->conn->Received().get())){ - break;//continue down below this while loop + break; //continue down below this while loop } }else{ //keep trying unless the timeout triggers @@ -263,7 +276,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 Handle_Timeout(H, conn); return; @@ -310,13 +323,13 @@ namespace Connector_HTTP{ std::string getHTTPType(HTTP::Parser & H){ std::string url = H.getUrl(); if ((url.find("f4m") != std::string::npos) || ((url.find("Seg") != std::string::npos) && (url.find("Frag") != std::string::npos))){ - std::string streamname = url.substr(1,url.find("/",1)-1); + std::string streamname = url.substr(1, url.find("/", 1) - 1); Util::Stream::sanitizeName(streamname); H.SetVar("stream", streamname); return "dynamic"; } - if (url.find("/smooth/") != std::string::npos && url.find(".ism") != std::string::npos ) { - std::string streamname = url.substr(8,url.find("/",8)-12); + if (url.find("/smooth/") != std::string::npos && url.find(".ism") != std::string::npos){ + std::string streamname = url.substr(8, url.find("/", 8) - 12); Util::Stream::sanitizeName(streamname); H.SetVar("stream", streamname); return "smooth"; @@ -324,28 +337,36 @@ namespace Connector_HTTP{ if (url.length() > 4){ std::string ext = url.substr(url.length() - 4, 4); if (ext == ".flv" || ext == ".mp3"){ - std::string streamname = url.substr(1,url.length() - 5); + std::string streamname = url.substr(1, url.length() - 5); Util::Stream::sanitizeName(streamname); H.SetVar("stream", streamname); return "progressive"; } } - if (url == "/crossdomain.xml"){return "internal";} - if (url == "/clientaccesspolicy.xml"){return "internal";} - if (url.length() > 10 && url.substr(0, 7) == "/embed_" && url.substr(url.length() - 3, 3) == ".js"){return "internal";} - if (url.length() > 9 && url.substr(0, 6) == "/info_" && url.substr(url.length() - 3, 3) == ".js"){return "internal";} + if (url == "/crossdomain.xml"){ + return "internal"; + } + if (url == "/clientaccesspolicy.xml"){ + return "internal"; + } + if (url.length() > 10 && url.substr(0, 7) == "/embed_" && url.substr(url.length() - 3, 3) == ".js"){ + return "internal"; + } + if (url.length() > 9 && url.substr(0, 6) == "/info_" && url.substr(url.length() - 3, 3) == ".js"){ + return "internal"; + } return "none"; } /// Thread for handling a single HTTP connection void Handle_HTTP_Connection(void * 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; while (conn->connected()){ if (conn->spool() || conn->Received().size()){ //make sure it ends in a \n - if (*(conn->Received().get().rbegin()) != '\n'){ + if ( *(conn->Received().get().rbegin()) != '\n'){ std::string tmp = conn->Received().get(); conn->Received().get().clear(); if (conn->Received().size()){ @@ -357,9 +378,10 @@ namespace Connector_HTTP{ if (Client.Read(conn->Received().get())){ std::string handler = getHTTPType(Client); long long int startms = Util::getMS(); - #if DEBUG >= 4 - std::cout << "Received request: " << Client.getUrl() << " (" << conn->getSocket() << ") => " << handler << " (" << Client.GetVar("stream") << ")" << std::endl; - #endif +#if DEBUG >= 4 + std::cout << "Received request: " << Client.getUrl() << " (" << conn->getSocket() << ") => " << handler << " (" << Client.GetVar("stream") + << ")" << std::endl; +#endif if (handler == "none" || handler == "internal"){ if (handler == "internal"){ Handle_Internal(Client, conn); @@ -369,13 +391,13 @@ namespace Connector_HTTP{ }else{ Handle_Through_Connector(Client, conn, handler); } - #if DEBUG >= 4 +#if DEBUG >= 4 std::cout << "Completed request (" << conn->getSocket() << ") " << handler << " in " << (Util::getMS() - startms) << " ms" << std::endl; - #endif +#endif Client.Clean(); //clean for any possible next requests } }else{ - usleep(10000);//sleep 10ms + usleep(10000); //sleep 10ms } } //close and remove the connection @@ -384,8 +406,8 @@ namespace Connector_HTTP{ //remove this thread from active_threads and add it to done_threads. thread_mutex.lock(); for (std::set::iterator it = active_threads.begin(); it != active_threads.end(); it++){ - if ((*it)->get_id() == tthread::this_thread::get_id()){ - tthread::thread * T = (*it); + if (( *it)->get_id() == tthread::this_thread::get_id()){ + tthread::thread * T = ( *it); active_threads.erase(T); done_threads.insert(T); break; @@ -394,26 +416,27 @@ namespace Connector_HTTP{ thread_mutex.unlock(); } -};//Connector_HTTP namespace - +} //Connector_HTTP namespace int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addConnectorOptions(8080); conf.parseArgs(argc, argv); Socket::Server server_socket = Socket::Server(conf.getInteger("listen_port"), conf.getString("listen_interface")); - if (!server_socket.connected()){return 1;} + if ( !server_socket.connected()){ + return 1; + } conf.activate(); while (server_socket.connected() && conf.is_active){ Socket::Connection S = server_socket.accept(); - 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 Connector_HTTP::thread_mutex.lock(); tthread::thread * T = new tthread::thread(Connector_HTTP::Handle_HTTP_Connection, (void *)(new Socket::Connection(S))); Connector_HTTP::active_threads.insert(T); //clean up any threads that may have finished - while (!Connector_HTTP::done_threads.empty()){ + while ( !Connector_HTTP::done_threads.empty()){ T = *Connector_HTTP::done_threads.begin(); T->join(); Connector_HTTP::done_threads.erase(T); @@ -421,10 +444,28 @@ int main(int argc, char ** argv){ } Connector_HTTP::thread_mutex.unlock(); }else{ - usleep(100000);//sleep 100ms + usleep(100000); //sleep 100ms } - }//while connected and not requested to stop + } //while connected and not requested to stop server_socket.close(); - Util::Procs::StopAll(); + + //wait for existing connections to drop + bool repeat = true; + while (repeat){ + Connector_HTTP::thread_mutex.lock(); + repeat = !Connector_HTTP::active_threads.empty(); + if (repeat){ + usleep(100000); //sleep 100ms + } + //clean up any threads that may have finished + while ( !Connector_HTTP::done_threads.empty()){ + tthread::thread * T = *Connector_HTTP::done_threads.begin(); + T->join(); + Connector_HTTP::done_threads.erase(T); + delete T; + } + Connector_HTTP::thread_mutex.unlock(); + } + return 0; -}//main +} //main diff --git a/src/conn_http_dynamic.cpp b/src/conn_http_dynamic.cpp index c92d15e8..baabd185 100644 --- a/src/conn_http_dynamic.cpp +++ b/src/conn_http_dynamic.cpp @@ -24,7 +24,7 @@ #include /// Holds everything unique to HTTP Dynamic Connector. -namespace Connector_HTTP{ +namespace Connector_HTTP { std::string GenerateBootstrap(std::string & MovieId, JSON::Value & metadata, int fragnum, int starttime, int endtime){ std::string empty; @@ -37,13 +37,12 @@ namespace Connector_HTTP{ } asrt.setVersion(1); asrt.setQualityEntry(empty, 0); - if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){ + if ( !metadata.isMember("keytime") || metadata["keytime"].size() == 0){ asrt.setSegmentRun(1, 20000, 0); }else{ asrt.setSegmentRun(1, metadata["keytime"].size(), 0); } - MP4::AFRT afrt; if (starttime == 0){ afrt.setUpdate(false); @@ -54,10 +53,10 @@ namespace Connector_HTTP{ afrt.setTimeScale(1000); afrt.setQualityEntry(empty, 0); MP4::afrt_runtable afrtrun; - if (!metadata.isMember("keytime") || metadata["keytime"].size() == 0){ + if ( !metadata.isMember("keytime") || metadata["keytime"].size() == 0){ afrtrun.firstFragment = 1; afrtrun.firstTimestamp = 0; - if (!metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){ + if ( !metadata.isMember("video") || !metadata["video"].isMember("keyms") || metadata["video"]["keyms"].asInt() == 0){ afrtrun.duration = 2000; }else{ afrtrun.duration = metadata["video"]["keyms"].asInt(); @@ -65,21 +64,21 @@ namespace Connector_HTTP{ afrt.setFragmentRun(afrtrun, 0); }else{ for (int i = 0; i < metadata["keytime"].size(); i++){ - afrtrun.firstFragment = i+1; + afrtrun.firstFragment = i + 1; afrtrun.firstTimestamp = metadata["keytime"][i].asInt(); - if (i+1 < metadata["keytime"].size()){ - afrtrun.duration = metadata["keytime"][i+1].asInt() - metadata["keytime"][i].asInt(); + if (i + 1 < metadata["keytime"].size()){ + afrtrun.duration = metadata["keytime"][i + 1].asInt() - metadata["keytime"][i].asInt(); }else{ if (metadata["lastms"].asInt()){ afrtrun.duration = metadata["lastms"].asInt() - metadata["keytime"][i].asInt(); }else{ - afrtrun.duration = 3000;//guess 3 seconds if unknown + afrtrun.duration = 3000; //guess 3 seconds if unknown } } afrt.setFragmentRun(afrtrun, i); } } - + MP4::ABST abst; abst.setVersion(1); abst.setBootstrapinfoVersion(1); @@ -95,7 +94,7 @@ namespace Connector_HTTP{ if (metadata["lastms"].asInt()){ abst.setCurrentMediaTime(metadata["lastms"].asInt()); }else{ - abst.setCurrentMediaTime(1000*metadata["length"].asInt()); + abst.setCurrentMediaTime(1000 * metadata["length"].asInt()); } }else{ abst.setLive(true); @@ -110,63 +109,65 @@ namespace Connector_HTTP{ abst.setSegmentRunTable(asrt, 0); abst.setFragmentRunTable(afrt, 0); - #if DEBUG >= 8 +#if DEBUG >= 8 std::cout << "Sending bootstrap:" << std::endl << abst.toPrettyString(0) << std::endl; - #endif +#endif return std::string((char*)abst.asBox(), (int)abst.boxedSize()); } - /// Returns a F4M-format manifest file std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){ std::string Result; if (metadata.isMember("length") && metadata["length"].asInt() > 0){ - Result="\n" - "\n" - "" + MovieId + "\n" - "" + metadata["video"]["width"].asString() + "\n" - "" + metadata["video"]["height"].asString() + "\n" - "" + metadata["length"].asString() + ".000\n" - "video/mp4\n" - "recorded\n" - "streaming\n" - "" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "\n" - "\n" - "AgAKb25NZXRhRGF0YQgAAAAAAAl0cmFja2luZm8KAAAAAgMACXRpbWVzY2FsZQBA+GoAAAAAAAAGbGVuZ3RoAEGMcHoQAAAAAAhsYW5ndWFnZQIAA2VuZwARc2FtcGxlZGVzY3JpcHRpb24KAAAAAQMACnNhbXBsZXR5cGUCAARhdmMxAAAJAAAJAwAJdGltZXNjYWxlAEDncAAAAAAAAAZsZW5ndGgAQXtNvTAAAAAACGxhbmd1YWdlAgADZW5nABFzYW1wbGVkZXNjcmlwdGlvbgoAAAABAwAKc2FtcGxldHlwZQIABG1wNGEAAAkAAAkADWF1ZGlvY2hhbm5lbHMAQAAAAAAAAAAAD2F1ZGlvc2FtcGxlcmF0ZQBA53AAAAAAAAAOdmlkZW9mcmFtZXJhdGUAQDf/gi5SciUABmFhY2FvdABAAAAAAAAAAAAIYXZjbGV2ZWwAQD8AAAAAAAAACmF2Y3Byb2ZpbGUAQFNAAAAAAAAADGF1ZGlvY29kZWNpZAIABG1wNGEADHZpZGVvY29kZWNpZAIABGF2YzEABXdpZHRoAECQ4AAAAAAAAAZoZWlnaHQAQIMAAAAAAAAACmZyYW1lV2lkdGgAQJDgAAAAAAAAC2ZyYW1lSGVpZ2h0AECDAAAAAAAAAAxkaXNwbGF5V2lkdGgAQJDgAAAAAAAADWRpc3BsYXlIZWlnaHQAQIMAAAAAAAAADG1vb3Zwb3NpdGlvbgBBmxq2uAAAAAAIZHVyYXRpb24AQIKjqW3oyhIAAAk=\n" - "\n" - "\n"; + Result = + "\n" + "\n" + "" + MovieId + "\n" + "" + metadata["video"]["width"].asString() + "\n" + "" + metadata["video"]["height"].asString() + "\n" + "" + metadata["length"].asString() + ".000\n" + "video/mp4\n" + "recorded\n" + "streaming\n" + "" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + + "\n" + "\n" + "AgAKb25NZXRhRGF0YQgAAAAAAAl0cmFja2luZm8KAAAAAgMACXRpbWVzY2FsZQBA+GoAAAAAAAAGbGVuZ3RoAEGMcHoQAAAAAAhsYW5ndWFnZQIAA2VuZwARc2FtcGxlZGVzY3JpcHRpb24KAAAAAQMACnNhbXBsZXR5cGUCAARhdmMxAAAJAAAJAwAJdGltZXNjYWxlAEDncAAAAAAAAAZsZW5ndGgAQXtNvTAAAAAACGxhbmd1YWdlAgADZW5nABFzYW1wbGVkZXNjcmlwdGlvbgoAAAABAwAKc2FtcGxldHlwZQIABG1wNGEAAAkAAAkADWF1ZGlvY2hhbm5lbHMAQAAAAAAAAAAAD2F1ZGlvc2FtcGxlcmF0ZQBA53AAAAAAAAAOdmlkZW9mcmFtZXJhdGUAQDf/gi5SciUABmFhY2FvdABAAAAAAAAAAAAIYXZjbGV2ZWwAQD8AAAAAAAAACmF2Y3Byb2ZpbGUAQFNAAAAAAAAADGF1ZGlvY29kZWNpZAIABG1wNGEADHZpZGVvY29kZWNpZAIABGF2YzEABXdpZHRoAECQ4AAAAAAAAAZoZWlnaHQAQIMAAAAAAAAACmZyYW1lV2lkdGgAQJDgAAAAAAAAC2ZyYW1lSGVpZ2h0AECDAAAAAAAAAAxkaXNwbGF5V2lkdGgAQJDgAAAAAAAADWRpc3BsYXlIZWlnaHQAQIMAAAAAAAAADG1vb3Zwb3NpdGlvbgBBmxq2uAAAAAAIZHVyYXRpb24AQIKjqW3oyhIAAAk=\n" + "\n" + "\n"; }else{ - Result="\n" - "\n" - "" + MovieId + "\n" - "video/mp4\n" - "live\n" - "streaming\n" - "" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "\n" - "\n" - "\n"; + Result = "\n" + "\n" + "" + MovieId + "\n" + "video/mp4\n" + "live\n" + "streaming\n" + "" + Base64::encode(GenerateBootstrap(MovieId, metadata, 1, 0, 0)) + "\n" + "\n" + "\n"; } - #if DEBUG >= 8 +#if DEBUG >= 8 std::cerr << "Sending this manifest:" << std::endl << Result << std::endl; - #endif +#endif return Result; - }//BuildManifest + } //BuildManifest /// Main function for Connector_HTTP_Dynamic int Connector_HTTP_Dynamic(Socket::Connection conn){ std::deque FlashBuf; int FlashBufSize = 0; long long int FlashBufTime = 0; - FLV::Tag tmp;//temporary tag + FLV::Tag tmp; //temporary tag - DTSC::Stream Strm;//Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. + DTSC::Stream Strm; //Incoming stream buffer. + HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender. - bool ready4data = false;//Set to true when streaming is to begin. + bool ready4data = false; //Set to true when streaming is to begin. bool pending_manifest = false; - bool receive_marks = false;//when set to true, this stream will ignore keyframes and instead use pause marks + bool receive_marks = false; //when set to true, this stream will ignore keyframes and instead use pause marks bool inited = false; - Socket::Connection ss(-1); + Socket::Connection ss( -1); std::string streamname; std::string recBuffer = ""; @@ -176,12 +177,12 @@ namespace Connector_HTTP{ int temp; int Flash_RequestPending = 0; unsigned int lastStats = 0; - 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 while (conn.connected()){ if (conn.spool() || conn.Received().size()){ //make sure it ends in a \n - if (*(conn.Received().get().rbegin()) != '\n'){ + if ( *(conn.Received().get().rbegin()) != '\n'){ std::string tmp = conn.Received().get(); conn.Received().get().clear(); if (conn.Received().size()){ @@ -191,18 +192,18 @@ namespace Connector_HTTP{ } } if (HTTP_R.Read(conn.Received().get())){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif +#endif conn.setHost(HTTP_R.GetHeader("X-Origin")); if (HTTP_R.url.find("f4m") == std::string::npos){ - streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1); - if (!ss){ + streamname = HTTP_R.url.substr(1, HTTP_R.url.find("/", 1) - 1); + if ( !ss){ ss = Util::Stream::getStream(streamname); - if (!ss.connected()){ - #if DEBUG >= 1 + if ( !ss.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif +#endif ss.close(); HTTP_S.Clean(); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); @@ -213,32 +214,34 @@ namespace Connector_HTTP{ ss.setBlocking(false); inited = true; } - Quality = HTTP_R.url.substr( HTTP_R.url.find("/",1)+1 ); + Quality = HTTP_R.url.substr(HTTP_R.url.find("/", 1) + 1); Quality = Quality.substr(0, Quality.find("Seg")); temp = HTTP_R.url.find("Seg") + 3; - Segment = atoi( HTTP_R.url.substr(temp,HTTP_R.url.find("-",temp)-temp).c_str()); + Segment = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find("-", temp) - temp).c_str()); temp = HTTP_R.url.find("Frag") + 4; - ReqFragment = atoi( HTTP_R.url.substr(temp).c_str() ); - #if DEBUG >= 4 - printf( "Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment); - #endif + ReqFragment = atoi(HTTP_R.url.substr(temp).c_str()); +#if DEBUG >= 4 + printf("Quality: %s, Seg %d Frag %d\n", Quality.c_str(), Segment, ReqFragment); +#endif std::stringstream sstream; sstream << "f " << ReqFragment << "\no \n"; ss.SendNow(sstream.str().c_str()); Flash_RequestPending++; }else{ - streamname = HTTP_R.url.substr(1,HTTP_R.url.find("/",1)-1); - if (!Strm.metadata.isNull()){ + streamname = HTTP_R.url.substr(1, HTTP_R.url.find("/", 1) - 1); + if ( !Strm.metadata.isNull()){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; }else{ pending_manifest = true; @@ -249,19 +252,19 @@ namespace Connector_HTTP{ } }else{ if (Flash_RequestPending){ - usleep(1000);//sleep 1ms + usleep(1000); //sleep 1ms }else{ - usleep(10000);//sleep 10ms + usleep(10000); //sleep 10ms } } if (ready4data){ - if (!inited){ + if ( !inited){ //we are ready, connect the socket! ss = Util::Stream::getStream(streamname); - if (!ss.connected()){ - #if DEBUG >= 1 + if ( !ss.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif +#endif ss.close(); HTTP_S.Clean(); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); @@ -270,9 +273,9 @@ namespace Connector_HTTP{ continue; } ss.setBlocking(false); - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Everything connected, starting to send video data...\n"); - #endif +#endif inited = true; } unsigned int now = Util::epoch(); @@ -283,10 +286,10 @@ namespace Connector_HTTP{ if (ss.spool()){ while (Strm.parsePacket(ss.Received())){ if (Strm.getPacket(0).isMember("time")){ - if (!Strm.metadata.isMember("firsttime")){ + if ( !Strm.metadata.isMember("firsttime")){ Strm.metadata["firsttime"] = Strm.getPacket(0)["time"]; }else{ - if (!Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){ + if ( !Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){ Strm.getPacket(0)["time"] = Strm.getPacket(0)["time"].asInt() - Strm.metadata["firsttime"].asInt(); } } @@ -294,32 +297,36 @@ namespace Connector_HTTP{ } if (pending_manifest){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; } - if (!receive_marks && Strm.metadata.isMember("length")){receive_marks = true;} + if ( !receive_marks && Strm.metadata.isMember("length")){ + receive_marks = true; + } if ((Strm.getPacket(0).isMember("keyframe") && !receive_marks) || Strm.lastType() == DTSC::PAUSEMARK){ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received a %s fragment of %i bytes.\n", Strm.getPacket(0)["datatype"].asString().c_str(), FlashBufSize); - #endif +#endif if (Flash_RequestPending > 0 && FlashBufSize){ - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Sending a fragment..."); - #endif +#endif //static std::string btstrp; //btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime, Strm.getPacket(0)["time"]); HTTP_S.Clean(); HTTP_S.SetHeader("Content-Type", "video/mp4"); HTTP_S.SetBody(""); - HTTP_S.SetHeader("Content-Length", FlashBufSize+8);//32+33+btstrp.size()); + HTTP_S.SetHeader("Content-Length", FlashBufSize + 8); //32+33+btstrp.size()); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //conn.SendNow("\x00\x00\x00\x21" "afra\x00\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x01", 21); //unsigned long tmptime = htonl(FlashBufTime << 32); @@ -335,16 +342,16 @@ namespace Connector_HTTP{ //unsigned long fragno = htonl(ReqFragment); //conn.SendNow((char*)&fragno, 4); unsigned long size = htonl(FlashBufSize+8); - conn.SendNow((char*)&size, 4); + conn.SendNow((char*) &size, 4); conn.SendNow("mdat", 4); while (FlashBuf.size() > 0){ conn.SendNow(FlashBuf.front()); FlashBuf.pop_front(); } Flash_RequestPending--; - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Done\n"); - #endif +#endif } FlashBuf.clear(); FlashBufSize = 0; @@ -373,26 +380,32 @@ namespace Connector_HTTP{ } if (pending_manifest && !Strm.metadata.isNull()){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; } } - if (!ss.connected()){break;} + if ( !ss.connected()){ + break; + } } } conn.close(); ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); ss.close(); - #if DEBUG >= 1 - if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());} +#if DEBUG >= 1 + if (FLV::Parse_Error){ + fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str()); + } fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); if (inited){ fprintf(stderr, "Status was: inited\n"); @@ -403,33 +416,35 @@ namespace Connector_HTTP{ fprintf(stderr, "Status was: connected\n"); } } - #endif +#endif return 0; - }//Connector_HTTP_Dynamic main function + } //Connector_HTTP_Dynamic main function -};//Connector_HTTP_Dynamic namespace +} //Connector_HTTP_Dynamic namespace int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addConnectorOptions(1935); conf.parseArgs(argc, argv); Socket::Server server_socket = Socket::Server("/tmp/mist/http_dynamic"); - if (!server_socket.connected()){return 1;} + if ( !server_socket.connected()){ + return 1; + } conf.activate(); - + while (server_socket.connected() && conf.is_active){ Socket::Connection S = server_socket.accept(); - if (S.connected()){//check if the new connection is valid + if (S.connected()){ //check if the new connection is valid 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); - }else{//otherwise, do nothing or output debugging text - #if DEBUG >= 3 + }else{ //otherwise, do nothing or output debugging text +#if DEBUG >= 3 fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); - #endif +#endif } } - }//while connected + } //while connected server_socket.close(); return 0; -}//main +} //main diff --git a/src/conn_http_progressive.cpp b/src/conn_http_progressive.cpp index bfc97c36..8e2b4c4a 100644 --- a/src/conn_http_progressive.cpp +++ b/src/conn_http_progressive.cpp @@ -21,29 +21,29 @@ #include /// Holds everything unique to HTTP Progressive Connector. -namespace Connector_HTTP{ +namespace Connector_HTTP { /// Main function for Connector_HTTP_Progressive int Connector_HTTP_Progressive(Socket::Connection conn){ bool progressive_has_sent_header = false; - bool ready4data = false;///< Set to true when streaming is to begin. - DTSC::Stream Strm;///< Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;///= 4 +#if DEBUG >= 4 std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif +#endif conn.setHost(HTTP_R.GetHeader("X-Origin")); //we assume the URL is the stream name with a 3 letter extension streamname = HTTP_R.getUrl().substr(1); size_t extDot = streamname.rfind('.'); - if (extDot != std::string::npos){streamname.resize(extDot);};//strip the extension + if (extDot != std::string::npos){ + streamname.resize(extDot); + }; //strip the extension int start = 0; - if (!HTTP_R.GetVar("start").empty()){ + if ( !HTTP_R.GetVar("start").empty()){ start = atoi(HTTP_R.GetVar("start").c_str()); } - if (!HTTP_R.GetVar("starttime").empty()){ + if ( !HTTP_R.GetVar("starttime").empty()){ start = atoi(HTTP_R.GetVar("starttime").c_str()); } - if (!HTTP_R.GetVar("apstart").empty()){ + if ( !HTTP_R.GetVar("apstart").empty()){ start = atoi(HTTP_R.GetVar("apstart").c_str()); } - if (!HTTP_R.GetVar("ec_seek").empty()){ + if ( !HTTP_R.GetVar("ec_seek").empty()){ start = atoi(HTTP_R.GetVar("ec_seek").c_str()); } - if (!HTTP_R.GetVar("fs").empty()){ + if ( !HTTP_R.GetVar("fs").empty()){ start = atoi(HTTP_R.GetVar("fs").c_str()); } //under 3 hours we assume seconds, otherwise byte position if (start < 10800){ - seek_sec = start*1000;//ms, not s + seek_sec = start * 1000; //ms, not s }else{ - seek_byte = start;//divide by 1mbit, then *1000 for ms. + seek_byte = start; //divide by 1mbit, then *1000 for ms. } ready4data = true; HTTP_R.Clean(); //clean for any possible next requests @@ -89,13 +91,13 @@ namespace Connector_HTTP{ } } if (ready4data){ - if (!inited){ + if ( !inited){ //we are ready, connect the socket! ss = Util::Stream::getStream(streamname); - if (!ss.connected()){ - #if DEBUG >= 1 + if ( !ss.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); - #endif +#endif ss.close(); HTTP_S.Clean(); HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); @@ -105,9 +107,9 @@ namespace Connector_HTTP{ } if (seek_byte){ //wait until we have a header - while (!Strm.metadata){ + while ( !Strm.metadata){ if (ss.spool()){ - Strm.parsePacket(ss.Received());//read the metadata + Strm.parsePacket(ss.Received()); //read the metadata }else{ Util::sleep(5); } @@ -126,9 +128,9 @@ namespace Connector_HTTP{ cmd << "s " << seek_sec << "\n"; ss.SendNow(cmd.str().c_str()); } - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Everything connected, starting to send video data...\n"); - #endif +#endif ss.SendNow("p\n"); inited = true; } @@ -139,13 +141,13 @@ namespace Connector_HTTP{ } if (ss.spool()){ while (Strm.parsePacket(ss.Received())){ - if (!progressive_has_sent_header){ - HTTP_S.Clean();//make sure no parts of old requests are left in any buffers - HTTP_S.SetHeader("Content-Type", "video/x-flv");//Send the correct content-type for FLV files + if ( !progressive_has_sent_header){ + HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers + HTTP_S.SetHeader("Content-Type", "video/x-flv"); //Send the correct content-type for FLV files //HTTP_S.SetHeader("Transfer-Encoding", "chunked"); HTTP_S.protocol = "HTTP/1.0"; - conn.SendNow(HTTP_S.BuildResponse("200", "OK"));//no SetBody = unknown length - this is intentional, we will stream the entire file - conn.SendNow(FLV::Header, 13);//write FLV header + conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file + conn.SendNow(FLV::Header, 13); //write FLV header //write metadata tag.DTSCMetaInit(Strm); conn.SendNow(tag.data, tag.len); @@ -160,24 +162,28 @@ namespace Connector_HTTP{ conn.SendNow(tag.data, tag.len); } progressive_has_sent_header = true; - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Sent progressive FLV header\n"); - #endif +#endif } tag.DTSCLoader(Strm); - conn.SendNow(tag.data, tag.len);//write the tag contents + conn.SendNow(tag.data, tag.len); //write the tag contents } }else{ Util::sleep(1); } - if (!ss.connected()){break;} + if ( !ss.connected()){ + break; + } } } conn.close(); ss.SendNow(conn.getStats("HTTP_Dynamic").c_str()); ss.close(); - #if DEBUG >= 1 - if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());} +#if DEBUG >= 1 + if (FLV::Parse_Error){ + fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str()); + } fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); if (inited){ fprintf(stderr, "Status was: inited\n"); @@ -188,33 +194,35 @@ namespace Connector_HTTP{ fprintf(stderr, "Status was: connected\n"); } } - #endif +#endif return 0; - }//Connector_HTTP main function + } //Connector_HTTP main function -};//Connector_HTTP namespace +} //Connector_HTTP namespace int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addConnectorOptions(1935); conf.parseArgs(argc, argv); Socket::Server server_socket = Socket::Server("/tmp/mist/http_progressive"); - if (!server_socket.connected()){return 1;} + if ( !server_socket.connected()){ + return 1; + } conf.activate(); - + while (server_socket.connected() && conf.is_active){ Socket::Connection S = server_socket.accept(); - if (S.connected()){//check if the new connection is valid + if (S.connected()){ //check if the new connection is valid 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); - }else{//otherwise, do nothing or output debugging text - #if DEBUG >= 3 + }else{ //otherwise, do nothing or output debugging text +#if DEBUG >= 3 fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); - #endif +#endif } } - }//while connected + } //while connected server_socket.close(); return 0; -}//main +} //main diff --git a/src/conn_http_smooth.cpp b/src/conn_http_smooth.cpp index 4aca8660..4805db67 100644 --- a/src/conn_http_smooth.cpp +++ b/src/conn_http_smooth.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -25,56 +24,67 @@ #include /// Holds everything unique to HTTP Dynamic Connector. -namespace Connector_HTTP{ +namespace Connector_HTTP { /// Returns a Smooth-format manifest file std::string BuildManifest(std::string & MovieId, JSON::Value & metadata){ std::stringstream Result; Result << "\n"; - Result << "\n"; - if( metadata.isMember( "audio" ) ) { - Result << " \n"; - Result << " \n"; + if (metadata.isMember("audio")){ + Result << " \n"; + Result << " \n"; - for( int i = 0; i < metadata["keytime"].size()-1; i++ ) { + Result << "\" SamplingRate=\"" << metadata["audio"]["rate"].asInt() + << "\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" FourCC=\"AACL\" />\n"; + for (int i = 0; i < metadata["keytime"].size() - 1; i++){ Result << " \n"; + if (i == 0){ + Result << "t=\"0\" "; + } + Result << "d=\"" << 10000 * (metadata["keytime"][i + 1].asInt() - metadata["keytime"][i].asInt()) << "\" />\n"; } - Result << " \n"; + Result << " \n"; Result << " \n"; } - if( metadata.isMember( "video" ) ) { - Result << " \n"; - Result << " \n"; + Result << " \n"; - for( int i = 0; i < metadata["keytime"].size()-1; i++ ) { + Result << "\" MaxWidth=\"" << metadata["video"]["width"].asInt() << "\" MaxHeight=\"" << metadata["video"]["height"].asInt() + << "\" FourCC=\"AVC1\" />\n"; + for (int i = 0; i < metadata["keytime"].size() - 1; i++){ Result << " \n"; + if (i == 0){ + Result << "t=\"0\" "; + } + Result << "d=\"" << 10000 * (metadata["keytime"][i + 1].asInt() - metadata["keytime"][i].asInt()) << "\" />\n"; } - Result << " \n"; + Result << " \n"; Result << " \n"; } Result << "\n"; - - #if DEBUG >= 8 + +#if DEBUG >= 8 std::cerr << "Sending this manifest:" << std::endl << Result << std::endl; - #endif +#endif return Result.str(); - }//BuildManifest + } //BuildManifest /// Main function for Connector_HTTP_Dynamic int Connector_HTTP_Dynamic(Socket::Connection conn){ @@ -82,22 +92,21 @@ namespace Connector_HTTP{ std::vector Timestamps; int FlashBufSize = 0; long long int FlashBufTime = 0; - FLV::Tag tmp;//temporary tag - DTSC::Stream Strm;//Incoming stream buffer. - HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender. + DTSC::Stream Strm; //Incoming stream buffer. + HTTP::Parser HTTP_R, HTTP_S; //HTTP Receiver en HTTP Sender. - bool ready4data = false;//Set to true when streaming is to begin. + bool ready4data = false; //Set to true when streaming is to begin. bool pending_manifest = false; - bool receive_marks = false;//when set to true, this stream will ignore keyframes and instead use pause marks + bool receive_marks = false; //when set to true, this stream will ignore keyframes and instead use pause marks bool inited = false; - Socket::Connection ss(-1); + Socket::Connection ss( -1); std::string streamname; std::string recBuffer = ""; bool wantsVideo = false; bool wantsAudio = false; - + std::string Quality; int Segment = -1; long long int ReqFragment = -1; @@ -105,12 +114,12 @@ namespace Connector_HTTP{ std::string tempStr; int Flash_RequestPending = 0; unsigned int lastStats = 0; - 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 while (conn.connected()){ if (conn.spool() || conn.Received().size()){ //make sure it ends in a \n - if (*(conn.Received().get().rbegin()) != '\n'){ + if ( *(conn.Received().get().rbegin()) != '\n'){ std::string tmp = conn.Received().get(); conn.Received().get().clear(); if (conn.Received().size()){ @@ -120,18 +129,18 @@ namespace Connector_HTTP{ } } if (HTTP_R.Read(conn.Received().get())){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; - #endif +#endif conn.setHost(HTTP_R.GetHeader("X-Origin")); if (HTTP_R.url.find("Manifest") == std::string::npos){ - streamname = HTTP_R.url.substr(8,HTTP_R.url.find("/",8)-12); - if (!ss){ + streamname = HTTP_R.url.substr(8, HTTP_R.url.find("/", 8) - 12); + if ( !ss){ ss = Util::Stream::getStream(streamname); - if (!ss.connected()){ - #if DEBUG >= 1 + if ( !ss.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif +#endif ss.close(); HTTP_S.Clean(); HTTP_S.SetBody("No such stream " + streamname + " is available on the system. Please try again.\n"); @@ -142,35 +151,41 @@ namespace Connector_HTTP{ ss.setBlocking(false); inited = true; } - 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(")")); - tempStr = HTTP_R.url.substr( HTTP_R.url.find(")/") + 2 ); + tempStr = HTTP_R.url.substr(HTTP_R.url.find(")/") + 2); wantsAudio = false; wantsVideo = false; - if( tempStr[0] == 'A' ) { wantsAudio = true; } - if( tempStr[0] == 'V' ) { wantsVideo = true; } - tempStr = tempStr.substr( tempStr.find("(") + 1 ); - ReqFragment = atoll( tempStr.substr(0,tempStr.find(")")).c_str() ); - #if DEBUG >= 4 - printf( "Quality: %s, Frag %d\n", Quality.c_str(), ( ReqFragment / 10000 ) ); - #endif + if (tempStr[0] == 'A'){ + wantsAudio = true; + } + if (tempStr[0] == 'V'){ + wantsVideo = true; + } + tempStr = tempStr.substr(tempStr.find("(") + 1); + ReqFragment = atoll(tempStr.substr(0, tempStr.find(")")).c_str()); +#if DEBUG >= 4 + printf("Quality: %s, Frag %d\n", Quality.c_str(), (ReqFragment / 10000)); +#endif std::stringstream sstream; - sstream << "s " << ( ReqFragment / 10000 ) << "\no \n"; + sstream << "s " << (ReqFragment / 10000) << "\no \n"; ss.SendNow(sstream.str().c_str()); Flash_RequestPending++; }else{ - streamname = HTTP_R.url.substr(8,HTTP_R.url.find("/",8)-12); - if (!Strm.metadata.isNull()){ + streamname = HTTP_R.url.substr(8, HTTP_R.url.find("/", 8) - 12); + if ( !Strm.metadata.isNull()){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; }else{ pending_manifest = true; @@ -181,19 +196,19 @@ namespace Connector_HTTP{ } }else{ if (Flash_RequestPending){ - usleep(1000);//sleep 1ms + usleep(1000); //sleep 1ms }else{ - usleep(10000);//sleep 10ms + usleep(10000); //sleep 10ms } } if (ready4data){ - if (!inited){ + if ( !inited){ //we are ready, connect the socket! ss = Util::Stream::getStream(streamname); - if (!ss.connected()){ - #if DEBUG >= 1 + if ( !ss.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif +#endif ss.close(); HTTP_S.Clean(); HTTP_S.SetBody("No such stream " + streamname + " is available on the system. Please try again.\n"); @@ -202,9 +217,9 @@ namespace Connector_HTTP{ continue; } ss.setBlocking(false); - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Everything connected, starting to send video data...\n"); - #endif +#endif inited = true; } unsigned int now = Util::epoch(); @@ -215,10 +230,10 @@ namespace Connector_HTTP{ if (ss.spool()){ while (Strm.parsePacket(ss.Received())){ if (Strm.getPacket(0).isMember("time")){ - if (!Strm.metadata.isMember("firsttime")){ + if ( !Strm.metadata.isMember("firsttime")){ Strm.metadata["firsttime"] = Strm.getPacket(0)["time"]; }else{ - if (!Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){ + if ( !Strm.metadata.isMember("length") || Strm.metadata["length"].asInt() == 0){ Strm.getPacket(0)["time"] = Strm.getPacket(0)["time"].asInt() - Strm.metadata["firsttime"].asInt(); } } @@ -226,144 +241,149 @@ namespace Connector_HTTP{ } if (pending_manifest){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; } - if (!receive_marks && Strm.metadata.isMember("length")){receive_marks = true;} - if ( Strm.lastType() == DTSC::PAUSEMARK ) { - Timestamps.push_back( Strm.getPacket(0)["time"].asInt() ); + if ( !receive_marks && Strm.metadata.isMember("length")){ + receive_marks = true; + } + if (Strm.lastType() == DTSC::PAUSEMARK){ + Timestamps.push_back(Strm.getPacket(0)["time"].asInt()); } if ((Strm.getPacket(0).isMember("keyframe") && !receive_marks) || Strm.lastType() == DTSC::PAUSEMARK){ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received a %s fragment of %i bytes.\n", Strm.getPacket(0)["datatype"].asString().c_str(), FlashBufSize); - #endif +#endif if (Flash_RequestPending > 0 && FlashBufSize){ - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Sending a fragment..."); - #endif +#endif //static std::string btstrp; //btstrp = GenerateBootstrap(streamname, Strm.metadata, ReqFragment, FlashBufTime, Strm.getPacket(0)["time"]); HTTP_S.Clean(); HTTP_S.SetHeader("Content-Type", "video/mp4"); HTTP_S.SetBody(""); - + int myDuration; - + MP4::MFHD mfhd_box; - for( int i = 0; i < Strm.metadata["keytime"].size(); i++ ) { - if( Strm.metadata["keytime"][i].asInt() >= ( ReqFragment / 10000 ) ) { - std::cerr << "Sequence Number: " << i+1 << std::endl; - mfhd_box.setSequenceNumber( i+1 ); - if( i != Strm.metadata["keytime"].size() ) { - myDuration = Strm.metadata["keytime"][i+1].asInt() - Strm.metadata["keytime"][i].asInt(); - } else { + for (int i = 0; i < Strm.metadata["keytime"].size(); i++){ + if (Strm.metadata["keytime"][i].asInt() >= (ReqFragment / 10000)){ + std::cerr << "Sequence Number: " << i + 1 << std::endl; + mfhd_box.setSequenceNumber(i + 1); + if (i != Strm.metadata["keytime"].size()){ + myDuration = Strm.metadata["keytime"][i + 1].asInt() - Strm.metadata["keytime"][i].asInt(); + }else{ myDuration = Strm.metadata["lastms"].asInt() - Strm.metadata["keytime"][i].asInt(); } myDuration = myDuration * 10000; break; } } - + MP4::TFHD tfhd_box; - tfhd_box.setFlags( MP4::tfhdSampleFlag ); - tfhd_box.setTrackID( 1 ); - tfhd_box.setDefaultSampleFlags( 0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample ); - + tfhd_box.setFlags(MP4::tfhdSampleFlag); + tfhd_box.setTrackID(1); + tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample); + MP4::TRUN trun_box; //maybe reinsert dataOffset - std::cerr << "Setting Flags: " << (MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize) << std::endl; - trun_box.setFlags( MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize ); - trun_box.setDataOffset( 42 ); - trun_box.setFirstSampleFlags( 0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample ); - for( int i = 0; i < FlashBuf.size(); i++ ) { + std::cerr << "Setting Flags: " << (MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize) + << std::endl; + trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize); + trun_box.setDataOffset(42); + trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample); + for (int i = 0; i < FlashBuf.size(); i++){ MP4::trunSampleInformation trunSample; trunSample.sampleSize = FlashBuf[i].size(); //trunSample.sampleDuration = (Timestamps[i+1]-Timestamps[i]) * 10000; - trunSample.sampleDuration = (((double)myDuration / FlashBuf.size()) * i) - (((double)myDuration / FlashBuf.size()) * (i-1)); - trun_box.setSampleInformation( trunSample, i ); + trunSample.sampleDuration = (((double)myDuration / FlashBuf.size()) * i) - (((double)myDuration / FlashBuf.size()) * (i - 1)); + trun_box.setSampleInformation(trunSample, i); } MP4::SDTP sdtp_box; - sdtp_box.setVersion( 0 ); - sdtp_box.setValue( 0x24, 4 ); - for( int i = 1; i < FlashBuf.size(); i++ ) { - sdtp_box.setValue( 0x14, 4+i ); + sdtp_box.setVersion(0); + sdtp_box.setValue(0x24, 4); + for (int i = 1; i < FlashBuf.size(); i++){ + sdtp_box.setValue(0x14, 4 + i); } - + MP4::TRAF traf_box; - traf_box.setContent( tfhd_box, 0 ); - traf_box.setContent( trun_box, 1 ); - traf_box.setContent( sdtp_box, 2 ); - + traf_box.setContent(tfhd_box, 0); + traf_box.setContent(trun_box, 1); + traf_box.setContent(sdtp_box, 2); + MP4::MOOF moof_box; - moof_box.setContent( mfhd_box, 0 ); - moof_box.setContent( traf_box, 1 ); - + moof_box.setContent(mfhd_box, 0); + moof_box.setContent(traf_box, 1); + //setting tha offsets! trun_box.setDataOffset(moof_box.boxedSize() + 8); - traf_box.setContent( trun_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; - - - HTTP_S.SetHeader("Content-Length", FlashBufSize+8+moof_box.boxedSize());//32+33+btstrp.size()); - conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - + traf_box.setContent(trun_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; + + HTTP_S.SetHeader("Content-Length", FlashBufSize + 8 + moof_box.boxedSize()); //32+33+btstrp.size()); + 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); - conn.SendNow((char*)&size, 4); + conn.SendNow((char*) &size, 4); conn.SendNow("mdat", 4); while (FlashBuf.size() > 0){ conn.SendNow(FlashBuf.front()); FlashBuf.pop_front(); } Flash_RequestPending--; - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Done\n"); - #endif +#endif } FlashBuf.clear(); FlashBufSize = 0; } - if ( ( wantsAudio && Strm.lastType() == DTSC::AUDIO ) || ( wantsVideo && Strm.lastType() == DTSC::VIDEO ) ) { - FlashBuf.push_back( Strm.lastData() ); + if ((wantsAudio && Strm.lastType() == DTSC::AUDIO) || (wantsVideo && Strm.lastType() == DTSC::VIDEO)){ + FlashBuf.push_back(Strm.lastData()); FlashBufSize += Strm.lastData().size(); - Timestamps.push_back( Strm.getPacket(0)["time"].asInt() ); + Timestamps.push_back(Strm.getPacket(0)["time"].asInt()); } } if (pending_manifest && !Strm.metadata.isNull()){ HTTP_S.Clean(); - HTTP_S.SetHeader("Content-Type","text/xml"); - HTTP_S.SetHeader("Cache-Control","no-cache"); - if (Strm.metadata.isMember("length")){receive_marks = true;} + HTTP_S.SetHeader("Content-Type", "text/xml"); + HTTP_S.SetHeader("Cache-Control", "no-cache"); + if (Strm.metadata.isMember("length")){ + receive_marks = true; + } std::string manifest = BuildManifest(streamname, Strm.metadata); HTTP_S.SetBody(manifest); conn.SendNow(HTTP_S.BuildResponse("200", "OK")); - #if DEBUG >= 3 +#if DEBUG >= 3 printf("Sent manifest\n"); - #endif +#endif pending_manifest = false; } } - if (!ss.connected()){break;} + if ( !ss.connected()){ + break; + } } } conn.close(); ss.SendNow(conn.getStats("HTTP_Smooth").c_str()); ss.close(); - #if DEBUG >= 1 - if (FLV::Parse_Error){fprintf(stderr, "FLV Parser Error: %s\n", FLV::Error_Str.c_str());} +#if DEBUG >= 1 fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); if (inited){ fprintf(stderr, "Status was: inited\n"); @@ -374,33 +394,35 @@ namespace Connector_HTTP{ fprintf(stderr, "Status was: connected\n"); } } - #endif +#endif return 0; - }//Connector_HTTP_Smooth main function + } //Connector_HTTP_Smooth main function -};//Connector_HTTP_Smooth namespace +} //Connector_HTTP_Smooth namespace int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addConnectorOptions(1935); conf.parseArgs(argc, argv); Socket::Server server_socket = Socket::Server("/tmp/mist/http_smooth"); - if (!server_socket.connected()){return 1;} + if ( !server_socket.connected()){ + return 1; + } conf.activate(); - + while (server_socket.connected() && conf.is_active){ Socket::Connection S = server_socket.accept(); - if (S.connected()){//check if the new connection is valid + if (S.connected()){ //check if the new connection is valid 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); - }else{//otherwise, do nothing or output debugging text - #if DEBUG >= 3 + }else{ //otherwise, do nothing or output debugging text +#if DEBUG >= 3 fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); - #endif +#endif } } - }//while connected + } //while connected server_socket.close(); return 0; -}//main +} //main diff --git a/src/conn_raw.cpp b/src/conn_raw.cpp index 6aa0a1e7..480d9910 100644 --- a/src/conn_raw.cpp +++ b/src/conn_raw.cpp @@ -11,7 +11,7 @@ /// Contains the main code for the RAW connector. /// Expects a single commandline argument telling it which stream to connect to, /// then outputs the raw stream to stdout. -int main(int argc, char ** argv) { +int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addOption("stream_name", JSON::fromString("{\"arg_num\":1, \"help\":\"Name of the stream to write to stdout.\"}")); conf.parseArgs(argc, argv); @@ -19,20 +19,20 @@ int main(int argc, char ** argv) { //connect to the proper stream Socket::Connection S = Util::Stream::getStream(conf.getString("stream_name")); S.setBlocking(false); - if (!S.connected()){ + if ( !S.connected()){ std::cout << "Could not open stream " << conf.getString("stream_name") << std::endl; return 1; } long long int lastStats = 0; long long int started = Util::epoch(); - while(std::cout.good()){ + while (std::cout.good()){ if (S.spool()){ while (S.Received().size()){ - std::cout.write(S.Received().get().c_str(),S.Received().get().size()); + std::cout.write(S.Received().get().c_str(), S.Received().get().size()); S.Received().get().clear(); } }else{ - Util::sleep(10);//sleep 10ms if no data + Util::sleep(10); //sleep 10ms if no data } unsigned int now = Util::epoch(); if (now != lastStats){ diff --git a/src/conn_rtmp.cpp b/src/conn_rtmp.cpp index 7329af5a..bb2eeb22 100644 --- a/src/conn_rtmp.cpp +++ b/src/conn_rtmp.cpp @@ -20,7 +20,7 @@ #include /// Holds all functions and data unique to the RTMP Connector -namespace Connector_RTMP{ +namespace Connector_RTMP { //for connection to server bool ready4data = false; ///< Set to true when streaming starts. @@ -34,17 +34,16 @@ namespace Connector_RTMP{ int play_msgtype = -1; //generic state keeping - bool stream_inited = false;///true if init data for audio/video was sent + bool stream_inited = false; ///true if init data for audio/video was sent Socket::Connection Socket; ///< Socket connected to user Socket::Connection SS; ///< Socket connected to server std::string streamname; ///< Stream that will be opened - void parseChunk(Socket::Buffer & buffer);///< Parses a single RTMP chunk. - void sendCommand(AMF::Object & amfreply, int messagetype, int stream_id);///< Sends a RTMP command either in AMF or AMF3 mode. - void parseAMFCommand(AMF::Object & amfdata, int messagetype, int stream_id);///< Parses a single AMF command message. + void parseChunk(Socket::Buffer & buffer); ///< Parses a single RTMP chunk. + void sendCommand(AMF::Object & amfreply, int messagetype, int stream_id); ///< Sends a RTMP command either in AMF or AMF3 mode. + void parseAMFCommand(AMF::Object & amfdata, int messagetype, int stream_id); ///< Parses a single AMF command message. int Connector_RTMP(Socket::Connection conn); -};//Connector_RTMP namespace; - +} //Connector_RTMP namespace; /// Main Connector_RTMP function int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ @@ -53,22 +52,28 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ FLV::Tag tag, init_tag; DTSC::Stream Strm; - while (!Socket.Received().available(1537) && Socket.connected()){Socket.spool(); Util::sleep(5);} + while ( !Socket.Received().available(1537) && Socket.connected()){ + Socket.spool(); + Util::sleep(5); + } RTMPStream::handshake_in = Socket.Received().remove(1537); RTMPStream::rec_cnt += 1537; if (RTMPStream::doHandshake()){ Socket.SendNow(RTMPStream::handshake_out); - while (!Socket.Received().available(1536) && Socket.connected()){Socket.spool(); Util::sleep(5);} + while ( !Socket.Received().available(1536) && Socket.connected()){ + Socket.spool(); + Util::sleep(5); + } Socket.Received().remove(1536); RTMPStream::rec_cnt += 1536; - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Handshake succcess!\n"); - #endif +#endif }else{ - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Handshake fail!\n"); - #endif +#endif return 0; } @@ -80,23 +85,23 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ parseChunk(Socket.Received()); firsttime = false; }else{ - Util::sleep(1);//sleep 1ms to prevent high CPU usage + Util::sleep(1); //sleep 1ms to prevent high CPU usage } if (ready4data){ - if (!inited){ + if ( !inited){ //we are ready, connect the socket! SS = Util::Stream::getStream(streamname); - if (!SS.connected()){ - #if DEBUG >= 1 + if ( !SS.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif - Socket.close();//disconnect user +#endif + Socket.close(); //disconnect user break; } SS.setBlocking(false); - #if DEBUG >= 3 +#if DEBUG >= 3 fprintf(stderr, "Everything connected, starting to send video data...\n"); - #endif +#endif SS.SendNow("p\n"); inited = true; } @@ -112,10 +117,10 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ if (play_trans != -1){ //send a status reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(AMF::Object("", (double)play_trans));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)play_trans)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Reset")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing and resetting...")); @@ -124,31 +129,31 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ sendCommand(amfreply, play_msgtype, play_streamid); //send streamisrecorded if stream, well, is recorded. if (Strm.metadata.isMember("length") && Strm.metadata["length"].asInt() > 0){ - Socket.Send(RTMPStream::SendUSR(4, 1));//send UCM StreamIsRecorded (4), stream 1 + Socket.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1 } //send streambegin - Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1 + Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 //and more reply amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(AMF::Object("", (double)play_trans));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", (double)play_trans)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Play.Start")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Playing!")); amfreply.getContentP(3)->addContent(AMF::Object("details", "DDV")); amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); sendCommand(amfreply, play_msgtype, play_streamid); - RTMPStream::chunk_snd_max = 102400;//100KiB - Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max));//send chunk size max (msg 1) + RTMPStream::chunk_snd_max = 102400; //100KiB + Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) //send dunno? - Socket.Send(RTMPStream::SendUSR(32, 1));//send UCM no clue?, stream 1 + Socket.Send(RTMPStream::SendUSR(32, 1)); //send UCM no clue?, stream 1 play_trans = -1; } //sent init data if needed - if (!stream_inited){ + if ( !stream_inited){ init_tag.DTSCMetaInit(Strm); Socket.SendNow(RTMPStream::SendMedia(init_tag)); if (Strm.metadata.isMember("audio") && Strm.metadata["audio"].isMember("init")){ @@ -164,9 +169,9 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ //sent a tag tag.DTSCLoader(Strm); Socket.SendNow(RTMPStream::SendMedia(tag)); - #if DEBUG >= 8 +#if DEBUG >= 8 fprintf(stderr, "Sent tag to %i: [%u] %s\n", Socket.getSocket(), tag.tagTime(), tag.tagType().c_str()); - #endif +#endif } } } @@ -174,8 +179,10 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ Socket.close(); SS.SendNow(Socket.getStats("RTMP").c_str()); SS.close(); - #if DEBUG >= 1 - if (FLV::Parse_Error){fprintf(stderr, "FLV Parse Error: %s\n", FLV::Error_Str.c_str());} +#if DEBUG >= 1 + if (FLV::Parse_Error){ + fprintf(stderr, "FLV Parse Error: %s\n", FLV::Error_Str.c_str()); + } fprintf(stderr, "User %i disconnected.\n", conn.getSocket()); if (inited){ fprintf(stderr, "Status was: inited\n"); @@ -186,9 +193,9 @@ int Connector_RTMP::Connector_RTMP(Socket::Connection conn){ fprintf(stderr, "Status was: connected\n"); } } - #endif +#endif return 0; -}//Connector_RTMP +} //Connector_RTMP /// Tries to get and parse one RTMP chunk at a time. void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){ @@ -210,35 +217,35 @@ void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){ //send ACK if we received a whole window if ((RTMPStream::rec_cnt - RTMPStream::rec_window_at > RTMPStream::rec_window_size)){ RTMPStream::rec_window_at = RTMPStream::rec_cnt; - Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3) + Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) } switch (next.msg_type_id){ - case 0://does not exist - #if DEBUG >= 2 + case 0: //does not exist +#if DEBUG >= 2 fprintf(stderr, "UNKN: Received a zero-type message. This is an error.\n"); - #endif - break;//happens when connection breaks unexpectedly - case 1://set chunk size +#endif + break; //happens when connection breaks unexpectedly + case 1: //set chunk size RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str()); - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max); - #endif +#endif break; - case 2://abort message - we ignore this one - #if DEBUG >= 4 + case 2: //abort message - we ignore this one +#if DEBUG >= 4 fprintf(stderr, "CTRL: Abort message\n"); - #endif +#endif //4 bytes of stream id to drop break; - case 3://ack - #if DEBUG >= 4 + case 3: //ack +#if DEBUG >= 4 fprintf(stderr, "CTRL: Acknowledgement\n"); - #endif +#endif RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str()); RTMPStream::snd_window_at = RTMPStream::snd_cnt; break; - case 4:{ + case 4: { //2 bytes event type, rest = event data //types: //0 = stream begin, 4 bytes ID @@ -249,50 +256,67 @@ void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){ //6 = pingrequest, 4 bytes data //7 = pingresponse, 4 bytes data //we don't need to process this - #if DEBUG >= 4 +#if DEBUG >= 4 short int ucmtype = ntohs(*(short int*)next.data.c_str()); switch (ucmtype){ - case 0: fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - case 1: fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - case 2: fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - case 3: fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int*)(next.data.c_str()+2))), ntohl(*((int*)(next.data.c_str()+6)))); break; - case 4: fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - case 6: fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - case 7: fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int*)(next.data.c_str()+2)))); break; - default: fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype); break; + case 0: + fprintf(stderr, "CTRL: UCM StreamBegin %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + case 1: + fprintf(stderr, "CTRL: UCM StreamEOF %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + case 2: + fprintf(stderr, "CTRL: UCM StreamDry %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + case 3: + fprintf(stderr, "CTRL: UCM SetBufferLength %i %i\n", ntohl(*((int*)(next.data.c_str()+2))), ntohl(*((int*)(next.data.c_str()+6)))); + break; + case 4: + fprintf(stderr, "CTRL: UCM StreamIsRecorded %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + case 6: + fprintf(stderr, "CTRL: UCM PingRequest %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + case 7: + fprintf(stderr, "CTRL: UCM PingResponse %i\n", ntohl(*((int*)(next.data.c_str()+2)))); + break; + default: + fprintf(stderr, "CTRL: UCM Unknown (%hi)\n", ucmtype); + break; } - #endif - } break; - case 5://window size of other end - #if DEBUG >= 4 +#endif + } + break; + case 5: //window size of other end +#if DEBUG >= 4 fprintf(stderr, "CTRL: Window size\n"); - #endif +#endif RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str()); RTMPStream::rec_window_at = RTMPStream::rec_cnt; - Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt));//send ack (msg 3) + Socket.Send(RTMPStream::SendCTL(3, RTMPStream::rec_cnt)); //send ack (msg 3) break; case 6: - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "CTRL: Set peer bandwidth\n"); - #endif +#endif //4 bytes window size, 1 byte limit type (ignored) RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str()); - Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5) + Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) break; - case 8://audio data - case 9://video data - case 18://meta data + case 8: //audio data + case 9: //video data + case 18: //meta data if (SS.connected()){ F.ChunkLoader(next); JSON::Value pack_out = F.toJSON(meta_out); - if (!pack_out.isNull()){ - if (!sending){ + if ( !pack_out.isNull()){ + if ( !sending){ counter++; if (counter > 8){ sending = true; SS.SendNow(meta_out.toNetPacked()); - SS.SendNow(prebuffer.str().c_str(), prebuffer.str().size());//write buffer - prebuffer.str("");//clear buffer + SS.SendNow(prebuffer.str().c_str(), prebuffer.str().size()); //write buffer + prebuffer.str(""); //clear buffer SS.SendNow(pack_out.toNetPacked()); }else{ prebuffer << pack_out.toNetPacked(); @@ -302,115 +326,125 @@ void Connector_RTMP::parseChunk(Socket::Buffer & inbuffer){ } } }else{ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received useless media data\n"); - #endif +#endif Socket.close(); } break; case 15: - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received AFM3 data message\n"); - #endif +#endif break; case 16: - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received AFM3 shared object\n"); - #endif +#endif break; - case 17:{ - #if DEBUG >= 4 + case 17: { +#if DEBUG >= 4 fprintf(stderr, "Received AFM3 command message\n"); - #endif +#endif if (next.data[0] != 0){ next.data = next.data.substr(1); amf3data = AMF::parse3(next.data); - #if DEBUG >= 4 +#if DEBUG >= 4 amf3data.Print(); - #endif +#endif }else{ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received AFM3-0 command message\n"); - #endif +#endif next.data = next.data.substr(1); amfdata = AMF::parse(next.data); parseAMFCommand(amfdata, 17, next.msg_stream_id); - }//parsing AMF0-style - } break; - case 19: - #if DEBUG >= 4 - fprintf(stderr, "Received AFM0 shared object\n"); - #endif + } //parsing AMF0-style + } break; - case 20:{//AMF0 command message + case 19: +#if DEBUG >= 4 + fprintf(stderr, "Received AFM0 shared object\n"); +#endif + break; + case 20: { //AMF0 command message amfdata = AMF::parse(next.data); parseAMFCommand(amfdata, 20, next.msg_stream_id); - } break; + } + break; case 22: - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received aggregate message\n"); - #endif +#endif break; default: - #if DEBUG >= 1 +#if DEBUG >= 1 fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n"); - #endif +#endif Connector_RTMP::stopparsing = true; break; } } -}//parseChunk +} //parseChunk void Connector_RTMP::sendCommand(AMF::Object & amfreply, int messagetype, int stream_id){ - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << amfreply.Print() << std::endl; - #endif +#endif if (messagetype == 17){ - Socket.SendNow(RTMPStream::SendChunk(3, messagetype, stream_id, (char)0+amfreply.Pack())); + Socket.SendNow(RTMPStream::SendChunk(3, messagetype, stream_id, (char)0 + amfreply.Pack())); }else{ Socket.SendNow(RTMPStream::SendChunk(3, messagetype, stream_id, amfreply.Pack())); } -}//sendCommand +} //sendCommand void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int stream_id){ - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Received command: %s\n", amfdata.Print().c_str()); - #endif - #if DEBUG >= 3 +#endif +#if DEBUG >= 3 fprintf(stderr, "AMF0 command: %s\n", amfdata.getContentP(0)->StrValue().c_str()); - #endif +#endif if (amfdata.getContentP(0)->StrValue() == "connect"){ double objencoding = 0; if (amfdata.getContentP(2)->getContentP("objectEncoding")){ objencoding = amfdata.getContentP(2)->getContentP("objectEncoding")->NumValue(); } - #if DEBUG >= 4 +#if DEBUG >= 4 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 (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");} + if (tmpint & 0x04){ + fprintf(stderr, "MP3 audio support detected\n"); + } + if (tmpint & 0x400){ + fprintf(stderr, "AAC audio support detected\n"); + } } - #endif +#endif RTMPStream::chunk_snd_max = 4096; - Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max));//send chunk size max (msg 1) - Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size));//send window acknowledgement size (msg 5) - Socket.Send(RTMPStream::SendCTL(6, RTMPStream::rec_window_size));//send rec window acknowledgement size (msg 6) - Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1 + Socket.Send(RTMPStream::SendCTL(1, RTMPStream::chunk_snd_max)); //send chunk size max (msg 1) + Socket.Send(RTMPStream::SendCTL(5, RTMPStream::snd_window_size)); //send window acknowledgement size (msg 5) + Socket.Send(RTMPStream::SendCTL(6, RTMPStream::rec_window_size)); //send rec window acknowledgement size (msg 6) + Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 //send a _result reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "_result"));//result success - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object(""));//server properties + amfreply.addContent(AMF::Object("", "_result")); //result success + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("")); //server properties amfreply.getContentP(2)->addContent(AMF::Object("fmsVer", "FMS/3,5,5,2004")); amfreply.getContentP(2)->addContent(AMF::Object("capabilities", (double)31)); amfreply.getContentP(2)->addContent(AMF::Object("mode", (double)1)); - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetConnection.Connect.Success")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Connection succeeded.")); @@ -426,104 +460,106 @@ void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int //amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null //sendCommand(amfreply, messagetype, stream_id); return; - }//connect + } //connect if (amfdata.getContentP(0)->StrValue() == "createStream"){ //send a _result reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "_result"));//result success - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object("", (double)1));//stream ID - we use 1 + amfreply.addContent(AMF::Object("", "_result")); //result success + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("", (double)1)); //stream ID - we use 1 sendCommand(amfreply, messagetype, stream_id); - Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1 + Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 return; - }//createStream + } //createStream if ((amfdata.getContentP(0)->StrValue() == "closeStream") || (amfdata.getContentP(0)->StrValue() == "deleteStream")){ - if (SS.connected()){SS.close();} + if (SS.connected()){ + SS.close(); + } return; } if ((amfdata.getContentP(0)->StrValue() == "getStreamLength") || (amfdata.getContentP(0)->StrValue() == "getMovLen")){ //send a _result reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "_result"));//result success - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object("", (double)0));//zero length + amfreply.addContent(AMF::Object("", "_result")); //result success + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("", (double)0)); //zero length sendCommand(amfreply, messagetype, stream_id); return; - }//getStreamLength + } //getStreamLength if ((amfdata.getContentP(0)->StrValue() == "publish")){ if (amfdata.getContentP(3)){ streamname = amfdata.getContentP(3)->StrValue(); /// \todo implement push for MistPlayer or restrict and change to getLive SS = Util::Stream::getStream(streamname); - if (!SS.connected()){ - #if DEBUG >= 1 + if ( !SS.connected()){ +#if DEBUG >= 1 fprintf(stderr, "Could not connect to server!\n"); - #endif - Socket.close();//disconnect user +#endif + Socket.close(); //disconnect user return; } SS.Send("P "); SS.Send(Socket.getHost().c_str()); SS.Send("\n"); nostats = true; - #if DEBUG >= 4 +#if DEBUG >= 4 fprintf(stderr, "Connected to buffer, starting to send data...\n"); - #endif +#endif } //send a _result reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "_result"));//result success - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object("", 1, AMF::AMF0_BOOL));//publish success? + amfreply.addContent(AMF::Object("", "_result")); //result success + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("", 1, AMF::AMF0_BOOL)); //publish success? sendCommand(amfreply, messagetype, stream_id); - Socket.Send(RTMPStream::SendUSR(0, 1));//send UCM StreamBegin (0), stream 1 + Socket.Send(RTMPStream::SendUSR(0, 1)); //send UCM StreamBegin (0), stream 1 //send a status reply amfreply = AMF::Object("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(AMF::Object("", 0, AMF::AMF0_NUMBER)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Publish.Start")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Stream is now published!")); amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); sendCommand(amfreply, messagetype, stream_id); return; - }//getStreamLength + } //getStreamLength if (amfdata.getContentP(0)->StrValue() == "checkBandwidth"){ //send a _result reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "_result"));//result success - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info + amfreply.addContent(AMF::Object("", "_result")); //result success + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info sendCommand(amfreply, messagetype, stream_id); return; - }//checkBandwidth + } //checkBandwidth if ((amfdata.getContentP(0)->StrValue() == "play") || (amfdata.getContentP(0)->StrValue() == "play2")){ //set reply number and stream name, actual reply is sent up in the SS.spool() handler play_trans = amfdata.getContentP(1)->NumValue(); play_msgtype = messagetype; play_streamid = stream_id; streamname = amfdata.getContentP(3)->StrValue(); - Connector_RTMP::ready4data = true;//start sending video data! + Connector_RTMP::ready4data = true; //start sending video data! return; - }//play + } //play if ((amfdata.getContentP(0)->StrValue() == "seek")){ //set reply number and stream name, actual reply is sent up in the SS.spool() handler play_trans = amfdata.getContentP(1)->NumValue(); play_msgtype = messagetype; play_streamid = stream_id; stream_inited = false; - + AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Seek.Notify")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Seeking to the specified time")); @@ -534,16 +570,16 @@ void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int SS.Send(JSON::Value((long long int)amfdata.getContentP(3)->NumValue()).asString().c_str()); SS.Send("\n"); return; - }//seek + } //seek if ((amfdata.getContentP(0)->StrValue() == "pauseRaw") || (amfdata.getContentP(0)->StrValue() == "pause")){ if (amfdata.getContentP(3)->NumValue()){ - SS.Send("q\n");//quit playing + SS.Send("q\n"); //quit playing //send a status reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Pause.Notify")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Pausing playback")); @@ -551,13 +587,13 @@ void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337)); sendCommand(amfreply, play_msgtype, play_streamid); }else{ - SS.Send("p\n");//start playing + SS.Send("p\n"); //start playing //send a status reply AMF::Object amfreply("container", AMF::AMF0_DDV_CONTAINER); - amfreply.addContent(AMF::Object("", "onStatus"));//status reply - amfreply.addContent(amfdata.getContent(1));//same transaction ID - amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL));//null - command info - amfreply.addContent(AMF::Object(""));//info + amfreply.addContent(AMF::Object("", "onStatus")); //status reply + amfreply.addContent(amfdata.getContent(1)); //same transaction ID + amfreply.addContent(AMF::Object("", (double)0, AMF::AMF0_NULL)); //null - command info + amfreply.addContent(AMF::Object("")); //info amfreply.getContentP(3)->addContent(AMF::Object("level", "status")); amfreply.getContentP(3)->addContent(AMF::Object("code", "NetStream.Unpause.Notify")); amfreply.getContentP(3)->addContent(AMF::Object("description", "Resuming playback")); @@ -566,34 +602,36 @@ void Connector_RTMP::parseAMFCommand(AMF::Object & amfdata, int messagetype, int sendCommand(amfreply, play_msgtype, play_streamid); } return; - }//seek - - #if DEBUG >= 2 + } //seek + +#if DEBUG >= 2 fprintf(stderr, "AMF0 command not processed! :(\n"); - #endif -}//parseAMFCommand +#endif +} //parseAMFCommand int main(int argc, char ** argv){ Util::Config conf(argv[0], PACKAGE_VERSION); conf.addConnectorOptions(1935); conf.parseArgs(argc, argv); Socket::Server server_socket = Socket::Server(conf.getInteger("listen_port"), conf.getString("listen_interface")); - if (!server_socket.connected()){return 1;} + if ( !server_socket.connected()){ + return 1; + } conf.activate(); - + while (server_socket.connected() && conf.is_active){ Socket::Connection S = server_socket.accept(); - if (S.connected()){//check if the new connection is valid + if (S.connected()){ //check if the new connection is valid pid_t myid = fork(); - if (myid == 0){//if new child, start MAINHANDLER + if (myid == 0){ //if new child, start MAINHANDLER return Connector_RTMP::Connector_RTMP(S); - }else{//otherwise, do nothing or output debugging text - #if DEBUG >= 3 + }else{ //otherwise, do nothing or output debugging text +#if DEBUG >= 3 fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket()); - #endif +#endif } } - }//while connected + } //while connected server_socket.close(); return 0; -}//main +} //main diff --git a/src/controller.cpp b/src/controller.cpp index 6c6dbe8e..d6003c44 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -2,26 +2,8 @@ /// Contains all code for the controller executable. #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include #include #include @@ -30,343 +12,142 @@ #include #include "controller_storage.h" #include "controller_connectors.h" +#include "controller_streams.h" +#include "controller_capabilities.h" #include "server.html.h" #define UPLINK_INTERVAL 30 #define COMPILED_USERNAME "" #define COMPILED_PASSWORD "" -namespace Controller{ +namespace Controller { -std::map lastBuffer; ///< Last moment of contact with all buffers. -Secure::Auth keychecker; ///< Checks key authorization. + Secure::Auth keychecker; ///< Checks key authorization. - -void WriteFile( std::string Filename, std::string contents ) { - std::ofstream File; - File.open( Filename.c_str( ) ); - File << contents << std::endl; - File.close( ); -} - -class ConnectedUser{ - public: - Socket::Connection C; - HTTP::Parser H; - bool Authorized; - bool clientMode; - int logins; - std::string Username; - ConnectedUser(Socket::Connection c){ - C = c; - H.Clean(); - logins = 0; - Authorized = false; - clientMode = false; - } -}; - -void Authorize( JSON::Value & Request, JSON::Value & Response, ConnectedUser & conn ) { - time_t Time = time(0); - tm * TimeInfo = localtime(&Time); - std::stringstream Date; - std::string retval; - Date << TimeInfo->tm_mday << "-" << TimeInfo->tm_mon << "-" << TimeInfo->tm_year + 1900; - std::string Challenge = Secure::md5( Date.str().c_str() + conn.C.getHost() ); - if( Request.isMember( "authorize" ) ) { - std::string UserID = Request["authorize"]["username"]; - if (Storage["account"].isMember(UserID)){ - if (Secure::md5(Storage["account"][UserID]["password"].asString() + Challenge) == Request["authorize"]["password"].asString()){ - Response["authorize"]["status"] = "OK"; - conn.Username = UserID; - conn.Authorized = true; - return; + class ConnectedUser{ + public: + Socket::Connection C; + HTTP::Parser H; + bool Authorized; + bool clientMode; + int logins; + std::string Username; + ConnectedUser(Socket::Connection c){ + C = c; + H.Clean(); + logins = 0; + Authorized = false; + clientMode = false; } - } - if (UserID != ""){ - if (Request["authorize"]["password"].asString() != "" && Secure::md5(Storage["account"][UserID]["password"].asString()) != Request["authorize"]["password"].asString()){ - Log("AUTH", "Failed login attempt "+UserID+" @ "+conn.C.getHost()); - } - } - conn.logins++; - } - conn.Username = ""; - conn.Authorized = false; - Response["authorize"]["status"] = "CHALL"; - Response["authorize"]["challenge"] = Challenge; - return; -} + }; -void CheckConfig(JSON::Value & in, JSON::Value & out){ - for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ - if (out.isMember(jit->first)){ - if (jit->second != out[jit->first]){ - if (jit->first != "time"){ - Log("CONF", std::string("Updated configuration value ")+jit->first); + void Authorize(JSON::Value & Request, JSON::Value & Response, ConnectedUser & conn){ + time_t Time = time(0); + tm * TimeInfo = localtime( &Time); + std::stringstream Date; + std::string retval; + Date << TimeInfo->tm_mday << "-" << TimeInfo->tm_mon << "-" << TimeInfo->tm_year + 1900; + std::string Challenge = Secure::md5(Date.str().c_str() + conn.C.getHost()); + if (Request.isMember("authorize")){ + std::string UserID = Request["authorize"]["username"]; + if (Storage["account"].isMember(UserID)){ + if (Secure::md5(Storage["account"][UserID]["password"].asString() + Challenge) == Request["authorize"]["password"].asString()){ + Response["authorize"]["status"] = "OK"; + conn.Username = UserID; + conn.Authorized = true; + return; } } - }else{ - Log("CONF", std::string("New configuration value ")+jit->first); - } - } - for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){ - if (!in.isMember(jit->first)){ - Log("CONF", std::string("Deleted configuration value ")+jit->first); - } - } - out = in; -} - -bool streamsEqual(JSON::Value & one, JSON::Value & two){ - if (one["channel"]["URL"] != two["channel"]["URL"]){return false;} - if (one["preset"]["cmd"] != two["preset"]["cmd"]){return false;} - return true; -} - -void startStream(std::string name, JSON::Value & data){ - std::string URL = data["channel"]["URL"]; - std::string preset = data["preset"]["cmd"]; - std::string cmd1, cmd2, cmd3; - if (URL.substr(0, 4) == "push"){ - std::string pusher = URL.substr(7); - cmd2 = "MistBuffer -s "+name+" "+pusher; - Util::Procs::Start(name, Util::getMyPath() + cmd2); - Log("BUFF", "(re)starting stream buffer "+name+" for push data from "+pusher); - }else{ - if (URL.substr(0, 1) == "/"){ - struct stat fileinfo; - if (stat(URL.c_str(), &fileinfo) != 0 || S_ISDIR(fileinfo.st_mode)){ - Log("BUFF", "Warning for VoD stream "+name+"! File not found: "+URL); - data["error"] = "Not found: "+URL; - return; + if (UserID != ""){ + if (Request["authorize"]["password"].asString() != "" + && Secure::md5(Storage["account"][UserID]["password"].asString()) != Request["authorize"]["password"].asString()){ + Log("AUTH", "Failed login attempt " + UserID + " @ " + conn.C.getHost()); + } } - cmd1 = "cat "+URL; - data["error"] = "Available"; - return; //MistPlayer handles VoD - }else{ - cmd1 = "ffmpeg -re -async 2 -i "+URL+" "+preset+" -f flv -"; - cmd2 = "MistFLV2DTSC"; - } - cmd3 = "MistBuffer -s "+name; - if (cmd2 != ""){ - Util::Procs::Start(name, cmd1, Util::getMyPath() + cmd2, Util::getMyPath() + cmd3); - Log("BUFF", "(re)starting stream buffer "+name+" for ffmpeg data: "+cmd1); - }else{ - Util::Procs::Start(name, cmd1, Util::getMyPath() + cmd3); - Log("BUFF", "(re)starting stream buffer "+name+" using input file "+URL); + conn.logins++; } + conn.Username = ""; + conn.Authorized = false; + Response["authorize"]["status"] = "CHALL"; + Response["authorize"]["challenge"] = Challenge; + return; } -} -void CheckStats(JSON::Value & stats){ - long long int currTime = Util::epoch(); - for (JSON::ObjIter jit = stats.ObjBegin(); jit != stats.ObjEnd(); jit++){ - if (currTime - lastBuffer[jit->first] > 120){ - stats.removeMember(jit->first); - return; - }else{ - if (jit->second.isMember("curr") && jit->second["curr"].size() > 0){ - for (JSON::ObjIter u_it = jit->second["curr"].ObjBegin(); u_it != jit->second["curr"].ObjEnd(); ++u_it){ - if (u_it->second.isMember("now") && u_it->second["now"].asInt() < currTime - 3){ - jit->second["log"].append(u_it->second); - jit->second["curr"].removeMember(u_it->first); - if (!jit->second["curr"].size()){break;} - u_it = jit->second["curr"].ObjBegin(); + void CheckConfig(JSON::Value & in, JSON::Value & out){ + for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ + if (out.isMember(jit->first)){ + if (jit->second != out[jit->first]){ + if (jit->first != "time"){ + Log("CONF", std::string("Updated configuration value ") + jit->first); + } + } + }else{ + Log("CONF", std::string("New configuration value ") + jit->first); + } + } + for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){ + if ( !in.isMember(jit->first)){ + Log("CONF", std::string("Deleted configuration value ") + jit->first); + } + } + out = in; + } + + void CheckStats(JSON::Value & stats){ + long long int currTime = Util::epoch(); + for (JSON::ObjIter jit = stats.ObjBegin(); jit != stats.ObjEnd(); jit++){ + if (currTime - lastBuffer[jit->first] > 120){ + stats.removeMember(jit->first); + return; + }else{ + if (jit->second.isMember("curr") && jit->second["curr"].size() > 0){ + for (JSON::ObjIter u_it = jit->second["curr"].ObjBegin(); u_it != jit->second["curr"].ObjEnd(); ++u_it){ + if (u_it->second.isMember("now") && u_it->second["now"].asInt() < currTime - 3){ + jit->second["log"].append(u_it->second); + jit->second["curr"].removeMember(u_it->first); + if ( !jit->second["curr"].size()){ + break; + } + u_it = jit->second["curr"].ObjBegin(); + } } } } } } -} -class cpudata { - public: - std::string model; - int cores; - int threads; - int mhz; - int id; - cpudata(){ - model = "Unknown"; - cores = 1; - threads = 1; - mhz = 0; - id = 0; - }; - void fill(char * data){ - int i; - i = 0; - if (sscanf(data, "model name : %n", &i) != EOF && i > 0){model = (data+i);} - if (sscanf(data, "cpu cores : %d", &i) == 1){cores = i;} - if (sscanf(data, "siblings : %d", &i) == 1){threads = i;} - if (sscanf(data, "physical id : %d", &i) == 1){id = i;} - if (sscanf(data, "cpu MHz : %d", &i) == 1){mhz = i;} - }; -}; - -void checkCapable(JSON::Value & capa){ - capa.null(); - std::ifstream cpuinfo("/proc/cpuinfo"); - if (cpuinfo){ - std::map cpus; - char line[300]; - int proccount = -1; - while (cpuinfo.good()){ - cpuinfo.getline(line, 300); - if (cpuinfo.fail()){ - //empty lines? ignore them, clear flags, continue - if (!cpuinfo.eof()){ - cpuinfo.ignore(); - cpuinfo.clear(); - } - continue; - } - if (memcmp(line, "processor", 9) == 0){proccount++;} - cpus[proccount].fill(line); - } - //fix wrong core counts - std::map corecounts; - for (int i = 0; i <= proccount; ++i){ - corecounts[cpus[i].id]++; - } - //remove double physical IDs - we only want real CPUs. - std::set used_physids; - int total_speed = 0; - int total_threads = 0; - for (int i = 0; i <= proccount; ++i){ - if (!used_physids.count(cpus[i].id)){ - used_physids.insert(cpus[i].id); - JSON::Value thiscpu; - thiscpu["model"] = cpus[i].model; - thiscpu["cores"] = cpus[i].cores; - if (cpus[i].cores < 2 && corecounts[cpus[i].id] > cpus[i].cores){ - thiscpu["cores"] = corecounts[cpus[i].id]; - } - thiscpu["threads"] = cpus[i].threads; - if (thiscpu["cores"].asInt() > thiscpu["threads"].asInt()){ - thiscpu["threads"] = thiscpu["cores"]; - } - thiscpu["mhz"] = cpus[i].mhz; - capa["cpu"].append(thiscpu); - total_speed += cpus[i].cores * cpus[i].mhz; - total_threads += cpus[i].threads; - } - } - capa["speed"] = total_speed; - capa["threads"] = total_threads; - } - std::ifstream meminfo("/proc/meminfo"); - if (meminfo){ - char line[300]; - int bufcache = 0; - while (meminfo.good()){ - meminfo.getline(line, 300); - if (meminfo.fail()){ - //empty lines? ignore them, clear flags, continue - if (!meminfo.eof()){ - meminfo.ignore(); - meminfo.clear(); - } - continue; - } - long long int i; - if (sscanf(line, "MemTotal : %Li kB", &i) == 1){capa["mem"]["total"] = i/1024;} - if (sscanf(line, "MemFree : %Li kB", &i) == 1){capa["mem"]["free"] = i/1024;} - if (sscanf(line, "SwapTotal : %Li kB", &i) == 1){capa["mem"]["swaptotal"] = i/1024;} - if (sscanf(line, "SwapFree : %Li kB", &i) == 1){capa["mem"]["swapfree"] = i/1024;} - if (sscanf(line, "Buffers : %Li kB", &i) == 1){bufcache += i/1024;} - if (sscanf(line, "Cached : %Li kB", &i) == 1){bufcache += i/1024;} - } - capa["mem"]["used"] = capa["mem"]["total"].asInt() - capa["mem"]["free"].asInt() - bufcache; - capa["mem"]["cached"] = bufcache; - capa["load"]["memory"] = ((capa["mem"]["used"].asInt() + (capa["mem"]["swaptotal"].asInt() - capa["mem"]["swapfree"].asInt())) * 100) / capa["mem"]["total"].asInt(); - } - std::ifstream loadavg("/proc/loadavg"); - if (loadavg){ - char line[300]; - int bufcache = 0; - loadavg.getline(line, 300); - //parse lines here - float onemin, fivemin, fifteenmin; - if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){ - capa["load"]["one"] = (long long int)(onemin * 100); - capa["load"]["five"] = (long long int)(onemin * 100); - capa["load"]["fifteen"] = (long long int)(onemin * 100); - } - } -} - -void CheckAllStreams(JSON::Value & data){ - long long int currTime = Util::epoch(); - for (JSON::ObjIter jit = data.ObjBegin(); jit != data.ObjEnd(); jit++){ - if (!Util::Procs::isActive(jit->first)){ - startStream(jit->first, jit->second); - } - if (currTime - lastBuffer[jit->first] > 5){ - if (jit->second.isMember("error") && jit->second["error"].asString() != ""){ - jit->second["online"] = jit->second["error"]; - }else{ - jit->second["online"] = 0; - } - }else{ - jit->second["online"] = 1; - } - } - static JSON::Value strlist; - bool changed = false; - if (strlist["config"] != Storage["config"]){ - strlist["config"] = Storage["config"]; - changed = true; - } - if (strlist["streams"] != Storage["streams"]){ - strlist["streams"] = Storage["streams"]; - changed = true; - } - if (changed){WriteFile("/tmp/mist/streamlist", strlist.toString());} -} - -void CheckStreams(JSON::Value & in, JSON::Value & out){ - bool changed = false; - for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ - if (out.isMember(jit->first)){ - if (!streamsEqual(jit->second, out[jit->first])){ - Log("STRM", std::string("Updated stream ")+jit->first); - Util::Procs::Stop(jit->first); - startStream(jit->first, jit->second); - } - }else{ - Log("STRM", std::string("New stream ")+jit->first); - startStream(jit->first, jit->second); - } - } - for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){ - if (!in.isMember(jit->first)){ - Log("STRM", std::string("Deleted stream ")+jit->first); - Util::Procs::Stop(jit->first); - } - } - out = in; -} - -}; //Connector namespace +} //Controller namespace int main(int argc, char ** argv){ Controller::Storage = JSON::fromFile("config.json"); JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}"); stored_port["default"] = Controller::Storage["config"]["controller"]["port"]; - if (!stored_port["default"]){stored_port["default"] = 4242;} - JSON::Value stored_interface = JSON::fromString("{\"long\":\"interface\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}"); + if ( !stored_port["default"]){ + stored_port["default"] = 4242; + } + JSON::Value stored_interface = + JSON::fromString( + "{\"long\":\"interface\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}"); stored_interface["default"] = Controller::Storage["config"]["controller"]["interface"]; - if (!stored_interface["default"]){stored_interface["default"] = "0.0.0.0";} - JSON::Value stored_user = JSON::fromString("{\"long\":\"username\", \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}"); + if ( !stored_interface["default"]){ + stored_interface["default"] = "0.0.0.0"; + } + JSON::Value stored_user = JSON::fromString( + "{\"long\":\"username\", \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to drop privileges to, or root to not drop provileges.\"}"); stored_user["default"] = Controller::Storage["config"]["controller"]["username"]; - if (!stored_user["default"]){stored_user["default"] = "root";} - Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); + if ( !stored_user["default"]){ + stored_user["default"] = "root"; + } + Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION " / " RELEASE); conf.addOption("listen_port", stored_port); conf.addOption("listen_interface", stored_interface); conf.addOption("username", stored_user); - conf.addOption("daemonize", JSON::fromString("{\"long\":\"daemon\", \"short\":\"d\", \"default\":1, \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); - conf.addOption("account", JSON::fromString("{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" \"default\":\"\", \"help\":\"A username:password string to create a new account with.\"}")); + conf.addOption("daemonize", + JSON::fromString( + "{\"long\":\"daemon\", \"short\":\"d\", \"default\":1, \"long_off\":\"nodaemon\", \"short_off\":\"n\", \"help\":\"Whether or not to daemonize the process after starting.\"}")); + conf.addOption("account", + JSON::fromString( + "{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" \"default\":\"\", \"help\":\"A username:password string to create a new account with.\"}")); conf.addOption("uplink", JSON::fromString("{\"default\":0, \"help\":\"Enable MistSteward uplink.\", \"short\":\"U\", \"long\":\"uplink\"}")); conf.parseArgs(argc, argv); @@ -376,18 +157,18 @@ int main(int argc, char ** argv){ if (colon != std::string::npos && colon != 0 && colon != account.size()){ std::string uname = account.substr(0, colon); std::string pword = account.substr(colon + 1, std::string::npos); - Controller::Log("CONF", "Created account "+uname+" through commandline option"); + Controller::Log("CONF", "Created account " + uname + " through commandline option"); Controller::Storage["account"][uname]["password"] = Secure::md5(pword); } } time_t lastuplink = 0; time_t processchecker = 0; Socket::Server API_Socket = Socket::Server(conf.getInteger("listen_port"), conf.getString("listen_interface"), true); - mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO);//attempt to create /tmp/mist/ - ignore failures + mkdir("/tmp/mist", S_IRWXU | S_IRWXG | S_IRWXO); //attempt to create /tmp/mist/ - ignore failures Socket::Server Stats_Socket = Socket::Server("/tmp/mist/statistics", true); conf.activate(); Socket::Connection Incoming; - std::vector< Controller::ConnectedUser > users; + std::vector users; std::vector buffers; JSON::Value Request; JSON::Value Response; @@ -408,19 +189,22 @@ int main(int argc, char ** argv){ lastuplink = Util::epoch(); bool gotUplink = false; if (users.size() > 0){ - for( std::vector< Controller::ConnectedUser >::iterator it = users.end() - 1; it >= users.begin(); it--) { - if (!it->C.connected()){ + for (std::vector::iterator it = users.end() - 1; it >= users.begin(); it--){ + if ( !it->C.connected()){ it->C.close(); users.erase(it); break; } - if (it->clientMode){uplink = &*it; gotUplink = true;} + if (it->clientMode){ + uplink = & *it; + gotUplink = true; + } } } - if (!gotUplink){ + if ( !gotUplink){ Incoming = Socket::Connection("gearbox.ddvtech.com", 4242, true); if (Incoming.connected()){ - users.push_back(Incoming); + users.push_back((Controller::ConnectedUser)Incoming); users.back().clientMode = true; uplink = &users.back(); gotUplink = true; @@ -434,7 +218,7 @@ int main(int argc, char ** argv){ Response["statistics"] = Controller::Storage["statistics"]; Response["now"] = (unsigned int)lastuplink; uplink->H.Clean(); - uplink->H.SetBody("command="+HTTP::Parser::urlencode(Response.toString())); + uplink->H.SetBody("command=" + HTTP::Parser::urlencode(Response.toString())); uplink->H.BuildRequest(); uplink->C.Send(uplink->H.BuildResponse("200", "OK")); uplink->H.Clean(); @@ -445,12 +229,16 @@ int main(int argc, char ** argv){ } Incoming = API_Socket.accept(true); - if (Incoming.connected()){users.push_back(Incoming);} + if (Incoming.connected()){ + users.push_back((Controller::ConnectedUser)Incoming); + } Incoming = Stats_Socket.accept(true); - if (Incoming.connected()){buffers.push_back(Incoming);} + if (Incoming.connected()){ + buffers.push_back(Incoming); + } if (buffers.size() > 0){ - for( std::vector< Socket::Connection >::iterator it = buffers.begin(); it != buffers.end(); it++) { - if (!it->connected()){ + for (std::vector::iterator it = buffers.begin(); it != buffers.end(); it++){ + if ( !it->connected()){ it->close(); buffers.erase(it); break; @@ -472,10 +260,10 @@ int main(int argc, char ** argv){ std::string nowstr = Request["totals"]["now"].asString(); Controller::Storage["statistics"][thisbuffer]["totals"][nowstr] = Request["totals"]; Controller::Storage["statistics"][thisbuffer]["totals"][nowstr].removeMember("now"); - Controller::Storage["statistics"][thisbuffer]["totals"].shrink(600);//limit to 10 minutes of data + Controller::Storage["statistics"][thisbuffer]["totals"].shrink(600); //limit to 10 minutes of data for (JSON::ObjIter jit = Request["log"].ObjBegin(); jit != Request["log"].ObjEnd(); jit++){ Controller::Storage["statistics"][thisbuffer]["log"].append(jit->second); - Controller::Storage["statistics"][thisbuffer]["log"].shrink(1000);//limit to 1000 users per buffer + Controller::Storage["statistics"][thisbuffer]["log"].shrink(1000); //limit to 1000 users per buffer } } } @@ -492,7 +280,8 @@ int main(int argc, char ** argv){ Controller::Storage["statistics"][oit->first]["curr"][sockit.asString()] = Request["vod"]; Controller::Storage["statistics"][oit->first]["curr"][sockit.asString()].removeMember("meta"); JSON::Value nowtotal; - for (JSON::ObjIter u_it = Controller::Storage["statistics"][oit->first]["curr"].ObjBegin(); u_it != Controller::Storage["statistics"][oit->first]["curr"].ObjEnd(); ++u_it){ + for (JSON::ObjIter u_it = Controller::Storage["statistics"][oit->first]["curr"].ObjBegin(); + u_it != Controller::Storage["statistics"][oit->first]["curr"].ObjEnd(); ++u_it){ nowtotal["up"] = nowtotal["up"].asInt() + u_it->second["up"].asInt(); nowtotal["down"] = nowtotal["down"].asInt() + u_it->second["down"].asInt(); nowtotal["count"] = nowtotal["count"].asInt() + 1; @@ -507,14 +296,14 @@ int main(int argc, char ** argv){ } } if (users.size() > 0){ - for( std::vector< Controller::ConnectedUser >::iterator it = users.begin(); it != users.end(); it++) { - if (!it->C.connected() || it->logins > 3){ + for (std::vector::iterator it = users.begin(); it != users.end(); it++){ + if ( !it->C.connected() || it->logins > 3){ it->C.close(); users.erase(it); break; } if (it->C.spool() || it->C.Received().size()){ - if (*(it->C.Received().get().rbegin()) != '\n'){ + if ( *(it->C.Received().get().rbegin()) != '\n'){ std::string tmp = it->C.Received().get(); it->C.Received().get().clear(); if (it->C.Received().size()){ @@ -547,7 +336,7 @@ int main(int argc, char ** argv){ Controller::Log("UPLK", "Responding to login challenge: " + Request["authorize"]["challenge"].asString()); Response["authorize"]["password"] = Secure::md5(COMPILED_PASSWORD + Request["authorize"]["challenge"].asString()); it->H.Clean(); - it->H.SetBody("command="+HTTP::Parser::urlencode(Response.toString())); + it->H.SetBody("command=" + HTTP::Parser::urlencode(Response.toString())); it->H.BuildRequest(); it->C.Send(it->H.BuildResponse("200", "OK")); it->H.Clean(); @@ -555,8 +344,12 @@ int main(int argc, char ** argv){ } } }else{ - if (Request.isMember("config")){Controller::CheckConfig(Request["config"], Controller::Storage["config"]);} - if (Request.isMember("streams")){Controller::CheckStreams(Request["streams"], Controller::Storage["streams"]);} + if (Request.isMember("config")){ + Controller::CheckConfig(Request["config"], Controller::Storage["config"]); + } + if (Request.isMember("streams")){ + Controller::CheckStreams(Request["streams"], Controller::Storage["streams"]); + } if (Request.isMember("clearstatlogs")){ Controller::Storage["log"].null(); Controller::Storage["statistics"].null(); @@ -564,7 +357,7 @@ int main(int argc, char ** argv){ } }else{ Request = JSON::fromString(it->H.GetVar("command")); - if (!Request.isObject() && it->H.url != "/api"){ + if ( !Request.isObject() && it->H.url != "/api"){ it->H.Clean(); it->H.SetHeader("Content-Type", "text/html"); it->H.SetHeader("X-Info", "To force an API response, request the file /api"); @@ -573,11 +366,15 @@ int main(int argc, char ** argv){ it->C.Send(it->H.BuildResponse("200", "OK")); it->H.Clean(); }else{ - Authorize(Request, Response, (*it)); + Authorize(Request, Response, ( *it)); if (it->Authorized){ //Parse config and streams from the request. - if (Request.isMember("config")){Controller::CheckConfig(Request["config"], Controller::Storage["config"]);} - if (Request.isMember("streams")){Controller::CheckStreams(Request["streams"], Controller::Storage["streams"]);} + if (Request.isMember("config")){ + Controller::CheckConfig(Request["config"], Controller::Storage["config"]); + } + if (Request.isMember("streams")){ + Controller::CheckStreams(Request["streams"], Controller::Storage["streams"]); + } if (Request.isMember("save")){ Controller::WriteFile("config.json", Controller::Storage.toString()); Controller::Log("CONF", "Config written to file on request through API"); @@ -589,7 +386,9 @@ int main(int argc, char ** argv){ Response["streams"] = Controller::Storage["streams"]; //add required data to the current unix time to the config, for syncing reasons Response["config"]["time"] = Util::epoch(); - if (!Response["config"].isMember("serverid")){Response["config"]["serverid"] = "";} + if ( !Response["config"].isMember("serverid")){ + Response["config"]["serverid"] = ""; + } //sent any available logs and statistics Response["log"] = Controller::Storage["log"]; Response["statistics"] = Controller::Storage["statistics"]; @@ -600,14 +399,18 @@ int main(int argc, char ** argv){ } } jsonp = ""; - if (it->H.GetVar("callback") != ""){jsonp = it->H.GetVar("callback");} - if (it->H.GetVar("jsonp") != ""){jsonp = it->H.GetVar("jsonp");} + if (it->H.GetVar("callback") != ""){ + jsonp = it->H.GetVar("callback"); + } + if (it->H.GetVar("jsonp") != ""){ + jsonp = it->H.GetVar("jsonp"); + } it->H.Clean(); it->H.SetHeader("Content-Type", "text/javascript"); if (jsonp == ""){ - it->H.SetBody(Response.toString()+"\n\n"); + it->H.SetBody(Response.toString() + "\n\n"); }else{ - it->H.SetBody(jsonp+"("+Response.toString()+");\n\n"); + it->H.SetBody(jsonp + "(" + Response.toString() + ");\n\n"); } it->C.Send(it->H.BuildResponse("200", "OK")); it->H.Clean(); diff --git a/src/controller_capabilities.cpp b/src/controller_capabilities.cpp new file mode 100644 index 00000000..d63d046a --- /dev/null +++ b/src/controller_capabilities.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +#include +#include "controller_capabilities.h" + +namespace Controller { + + class cpudata{ + public: + std::string model; + int cores; + int threads; + int mhz; + int id; + cpudata(){ + model = "Unknown"; + cores = 1; + threads = 1; + mhz = 0; + id = 0; + } + ; + void fill(char * data){ + int i; + i = 0; + if (sscanf(data, "model name : %n", &i) != EOF && i > 0){ + model = (data + i); + } + if (sscanf(data, "cpu cores : %d", &i) == 1){ + cores = i; + } + if (sscanf(data, "siblings : %d", &i) == 1){ + threads = i; + } + if (sscanf(data, "physical id : %d", &i) == 1){ + id = i; + } + if (sscanf(data, "cpu MHz : %d", &i) == 1){ + mhz = i; + } + } + ; + }; + + void checkCapable(JSON::Value & capa){ + capa.null(); + std::ifstream cpuinfo("/proc/cpuinfo"); + if (cpuinfo){ + std::map cpus; + char line[300]; + int proccount = -1; + while (cpuinfo.good()){ + cpuinfo.getline(line, 300); + if (cpuinfo.fail()){ + //empty lines? ignore them, clear flags, continue + if ( !cpuinfo.eof()){ + cpuinfo.ignore(); + cpuinfo.clear(); + } + continue; + } + if (memcmp(line, "processor", 9) == 0){ + proccount++; + } + cpus[proccount].fill(line); + } + //fix wrong core counts + std::map corecounts; + for (int i = 0; i <= proccount; ++i){ + corecounts[cpus[i].id]++; + } + //remove double physical IDs - we only want real CPUs. + std::set used_physids; + int total_speed = 0; + int total_threads = 0; + for (int i = 0; i <= proccount; ++i){ + if ( !used_physids.count(cpus[i].id)){ + used_physids.insert(cpus[i].id); + JSON::Value thiscpu; + thiscpu["model"] = cpus[i].model; + thiscpu["cores"] = cpus[i].cores; + if (cpus[i].cores < 2 && corecounts[cpus[i].id] > cpus[i].cores){ + thiscpu["cores"] = corecounts[cpus[i].id]; + } + thiscpu["threads"] = cpus[i].threads; + if (thiscpu["cores"].asInt() > thiscpu["threads"].asInt()){ + thiscpu["threads"] = thiscpu["cores"]; + } + thiscpu["mhz"] = cpus[i].mhz; + capa["cpu"].append(thiscpu); + total_speed += cpus[i].cores * cpus[i].mhz; + total_threads += cpus[i].threads; + } + } + capa["speed"] = total_speed; + capa["threads"] = total_threads; + } + std::ifstream meminfo("/proc/meminfo"); + if (meminfo){ + char line[300]; + int bufcache = 0; + while (meminfo.good()){ + meminfo.getline(line, 300); + if (meminfo.fail()){ + //empty lines? ignore them, clear flags, continue + if ( !meminfo.eof()){ + meminfo.ignore(); + meminfo.clear(); + } + continue; + } + long long int i; + if (sscanf(line, "MemTotal : %Li kB", &i) == 1){ + capa["mem"]["total"] = i / 1024; + } + if (sscanf(line, "MemFree : %Li kB", &i) == 1){ + capa["mem"]["free"] = i / 1024; + } + if (sscanf(line, "SwapTotal : %Li kB", &i) == 1){ + capa["mem"]["swaptotal"] = i / 1024; + } + if (sscanf(line, "SwapFree : %Li kB", &i) == 1){ + capa["mem"]["swapfree"] = i / 1024; + } + if (sscanf(line, "Buffers : %Li kB", &i) == 1){ + bufcache += i / 1024; + } + if (sscanf(line, "Cached : %Li kB", &i) == 1){ + bufcache += i / 1024; + } + } + capa["mem"]["used"] = capa["mem"]["total"].asInt() - capa["mem"]["free"].asInt() - bufcache; + capa["mem"]["cached"] = bufcache; + capa["load"]["memory"] = ((capa["mem"]["used"].asInt() + (capa["mem"]["swaptotal"].asInt() - capa["mem"]["swapfree"].asInt())) * 100) + / capa["mem"]["total"].asInt(); + } + std::ifstream loadavg("/proc/loadavg"); + if (loadavg){ + char line[300]; + int bufcache = 0; + loadavg.getline(line, 300); + //parse lines here + float onemin, fivemin, fifteenmin; + if (sscanf(line, "%f %f %f", &onemin, &fivemin, &fifteenmin) == 3){ + capa["load"]["one"] = (long long int)(onemin * 100); + capa["load"]["five"] = (long long int)(onemin * 100); + capa["load"]["fifteen"] = (long long int)(onemin * 100); + } + } + } + +} + diff --git a/src/controller_capabilities.h b/src/controller_capabilities.h new file mode 100644 index 00000000..726be634 --- /dev/null +++ b/src/controller_capabilities.h @@ -0,0 +1,5 @@ +#include + +namespace Controller { + void checkCapable(JSON::Value & capa); +} diff --git a/src/controller_connectors.cpp b/src/controller_connectors.cpp index 64de467c..9e6e94bd 100644 --- a/src/controller_connectors.cpp +++ b/src/controller_connectors.cpp @@ -2,11 +2,35 @@ #include #include #include "controller_storage.h" +#include "controller_connectors.h" -namespace Controller{ - +namespace Controller { + + static std::map current_connectors; + + /// Checks if the binary mentioned in the protocol argument is currently active, if so, restarts it. + void UpdateProtocol(std::string protocol){ + std::map::iterator iter; + for (iter = current_connectors.begin(); iter != current_connectors.end(); iter++){ + if (iter->second.substr(0, protocol.size()) == protocol){ + Log("CONF", "Restarting connector for update: " + iter->second); + Util::Procs::Stop(iter->first); + int i = 0; + while (Util::Procs::isActive(iter->first) && i < 30){ + Util::sleep(100); + } + if (i >= 30){ + Log("WARN", "Connector still active 3 seconds after shutdown - delaying restart."); + }else{ + Util::Procs::Start(iter->first, Util::getMyPath() + iter->second); + } + return; + } + } + } + + /// Checks current protocol configuration, updates state of enabled connectors if neccesary. void CheckProtocols(JSON::Value & p){ - static std::map current_connectors; std::map new_connectors; std::map::iterator iter; bool haveHTTPgeneric = false; @@ -16,35 +40,40 @@ namespace Controller{ JSON::Value counter = (long long int)0; for (JSON::ArrIter ait = p.ArrBegin(); ait != p.ArrEnd(); ait++){ - if (!(*ait).isMember("connector") || (*ait)["connector"].asString() == ""){continue;} - - tmp = std::string("MistConn") + (*ait)["connector"].asString() + std::string(" -n"); - if ((*ait)["connector"].asString() == "HTTP"){haveHTTPgeneric = true;} - if ((*ait)["connector"].asString() != "HTTP" && (*ait)["connector"].asString().substr(0, 4) == "HTTP"){haveHTTPspecific = true;} - - if ((*ait).isMember("port") && (*ait)["port"].asInt() != 0){ - tmp += std::string(" -p ") + (*ait)["port"].asString(); - } - - if ((*ait).isMember("interface") && (*ait)["interface"].asString() != "" && (*ait)["interface"].asString() != "0.0.0.0"){ - tmp += std::string(" -i ") + (*ait)["interface"].asString(); + if ( !( *ait).isMember("connector") || ( *ait)["connector"].asString() == ""){ + continue; } - if ((*ait).isMember("username") && (*ait)["username"].asString() != "" && (*ait)["username"].asString() != "root"){ - tmp += std::string(" -u ") + (*ait)["username"].asString(); + tmp = std::string("MistConn") + ( *ait)["connector"].asString() + std::string(" -n"); + if (( *ait)["connector"].asString() == "HTTP"){ + haveHTTPgeneric = true; + } + if (( *ait)["connector"].asString() != "HTTP" && ( *ait)["connector"].asString().substr(0, 4) == "HTTP"){ + haveHTTPspecific = true; } - if ((*ait).isMember("args") && (*ait)["args"].asString() != ""){ - tmp += std::string(" ") + (*ait)["args"].asString(); + if (( *ait).isMember("port") && ( *ait)["port"].asInt() != 0){ + tmp += std::string(" -p ") + ( *ait)["port"].asString(); } + if (( *ait).isMember("interface") && ( *ait)["interface"].asString() != "" && ( *ait)["interface"].asString() != "0.0.0.0"){ + tmp += std::string(" -i ") + ( *ait)["interface"].asString(); + } + + if (( *ait).isMember("username") && ( *ait)["username"].asString() != "" && ( *ait)["username"].asString() != "root"){ + tmp += std::string(" -u ") + ( *ait)["username"].asString(); + } + + if (( *ait).isMember("args") && ( *ait)["args"].asString() != ""){ + tmp += std::string(" ") + ( *ait)["args"].asString(); + } counter = counter.asInt() + 1; - new_connectors[std::string("Conn")+counter.asString()] = tmp; - if (Util::Procs::isActive(std::string("Conn")+counter.asString())){ - (*ait)["online"] = 1; + new_connectors[std::string("Conn") + counter.asString()] = tmp; + if (Util::Procs::isActive(std::string("Conn") + counter.asString())){ + ( *ait)["online"] = 1; }else{ - (*ait)["online"] = 0; + ( *ait)["online"] = 0; } } @@ -67,7 +96,7 @@ namespace Controller{ if (haveHTTPgeneric && !haveHTTPspecific){ Log("WARN", "HTTP Connector is enabled but no HTTP-based protocols are active!"); } - if (!haveHTTPgeneric && haveHTTPspecific){ + if ( !haveHTTPgeneric && haveHTTPspecific){ Log("WARN", "HTTP-based protocols will not work without the generic HTTP connector!"); } @@ -75,5 +104,4 @@ namespace Controller{ current_connectors = new_connectors; } - } diff --git a/src/controller_connectors.h b/src/controller_connectors.h index d225d7d4..bb0ad3c8 100644 --- a/src/controller_connectors.h +++ b/src/controller_connectors.h @@ -1,5 +1,11 @@ +#include +namespace Controller { -namespace Controller{ + /// Checks if the binary mentioned in the protocol argument is currently active, if so, restarts it. + void UpdateProtocol(std::string protocol); + + /// Checks current protocol configuration, updates state of enabled connectors if neccesary. void CheckProtocols(JSON::Value & p); + } diff --git a/src/controller_storage.cpp b/src/controller_storage.cpp index 8d8d74de..e35af8c0 100644 --- a/src/controller_storage.cpp +++ b/src/controller_storage.cpp @@ -1,9 +1,10 @@ #include +#include #include #include "controller_storage.h" -namespace Controller{ - +namespace Controller { + JSON::Value Storage; ///< Global storage of data. /// Store and print a log message. @@ -11,15 +12,25 @@ namespace Controller{ //if last log message equals this one, do not log. if (Storage["log"].size() > 0){ JSON::ArrIter it = Storage["log"].ArrEnd() - 1; - if ((*it)[2] == message){return;} + if (( *it)[2] == message){ + return; + } } JSON::Value m; m.append(Util::epoch()); m.append(kind); m.append(message); Storage["log"].append(m); - Storage["log"].shrink(100);//limit to 100 log messages + Storage["log"].shrink(100); //limit to 100 log messages std::cout << "[" << kind << "] " << message << std::endl; } + /// Write contents to Filename + void WriteFile(std::string Filename, std::string contents){ + std::ofstream File; + File.open(Filename.c_str()); + File << contents << std::endl; + File.close(); + } + } diff --git a/src/controller_storage.h b/src/controller_storage.h index fa485098..d6b5d814 100644 --- a/src/controller_storage.h +++ b/src/controller_storage.h @@ -1,11 +1,14 @@ #include #include -namespace Controller{ +namespace Controller { extern JSON::Value Storage; ///< Global storage of data. /// Store and print a log message. void Log(std::string kind, std::string message); + /// Write contents to Filename. + void WriteFile(std::string Filename, std::string contents); + } diff --git a/src/controller_streams.cpp b/src/controller_streams.cpp new file mode 100644 index 00000000..e00de2d0 --- /dev/null +++ b/src/controller_streams.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include "controller_streams.h" +#include "controller_storage.h" +#include + +namespace Controller { + + std::map lastBuffer; ///< Last moment of contact with all buffers. + + bool streamsEqual(JSON::Value & one, JSON::Value & two){ + if (one["channel"]["URL"] != two["channel"]["URL"]){ + return false; + } + if (one["preset"]["cmd"] != two["preset"]["cmd"]){ + return false; + } + return true; + } + + void startStream(std::string name, JSON::Value & data){ + std::string URL = data["channel"]["URL"]; + std::string preset = data["preset"]["cmd"]; + std::string cmd1, cmd2, cmd3; + if (URL.substr(0, 4) == "push"){ + std::string pusher = URL.substr(7); + cmd2 = "MistBuffer -s " + name + " " + pusher; + Util::Procs::Start(name, Util::getMyPath() + cmd2); + Log("BUFF", "(re)starting stream buffer " + name + " for push data from " + pusher); + }else{ + if (URL.substr(0, 1) == "/"){ + struct stat fileinfo; + if (stat(URL.c_str(), &fileinfo) != 0 || S_ISDIR(fileinfo.st_mode)){ + Log("BUFF", "Warning for VoD stream " + name + "! File not found: " + URL); + data["error"] = "Not found: " + URL; + return; + } + cmd1 = "cat " + URL; + data["error"] = "Available"; + return; //MistPlayer handles VoD + }else{ + cmd1 = "ffmpeg -re -async 2 -i " + URL + " " + preset + " -f flv -"; + cmd2 = "MistFLV2DTSC"; + } + cmd3 = "MistBuffer -s " + name; + if (cmd2 != ""){ + Util::Procs::Start(name, cmd1, Util::getMyPath() + cmd2, Util::getMyPath() + cmd3); + Log("BUFF", "(re)starting stream buffer " + name + " for ffmpeg data: " + cmd1); + }else{ + Util::Procs::Start(name, cmd1, Util::getMyPath() + cmd3); + Log("BUFF", "(re)starting stream buffer " + name + " using input file " + URL); + } + } + } + + void CheckAllStreams(JSON::Value & data){ + long long int currTime = Util::epoch(); + for (JSON::ObjIter jit = data.ObjBegin(); jit != data.ObjEnd(); jit++){ + if ( !Util::Procs::isActive(jit->first)){ + startStream(jit->first, jit->second); + } + if (currTime - lastBuffer[jit->first] > 5){ + if (jit->second.isMember("error") && jit->second["error"].asString() != ""){ + jit->second["online"] = jit->second["error"]; + }else{ + jit->second["online"] = 0; + } + }else{ + jit->second["online"] = 1; + } + } + static JSON::Value strlist; + bool changed = false; + if (strlist["config"] != Storage["config"]){ + strlist["config"] = Storage["config"]; + changed = true; + } + if (strlist["streams"] != Storage["streams"]){ + strlist["streams"] = Storage["streams"]; + changed = true; + } + if (changed){ + WriteFile("/tmp/mist/streamlist", strlist.toString()); + } + } + + void CheckStreams(JSON::Value & in, JSON::Value & out){ + bool changed = false; + for (JSON::ObjIter jit = in.ObjBegin(); jit != in.ObjEnd(); jit++){ + if (out.isMember(jit->first)){ + if ( !streamsEqual(jit->second, out[jit->first])){ + Log("STRM", std::string("Updated stream ") + jit->first); + Util::Procs::Stop(jit->first); + startStream(jit->first, jit->second); + } + }else{ + Log("STRM", std::string("New stream ") + jit->first); + startStream(jit->first, jit->second); + } + } + for (JSON::ObjIter jit = out.ObjBegin(); jit != out.ObjEnd(); jit++){ + if ( !in.isMember(jit->first)){ + Log("STRM", std::string("Deleted stream ") + jit->first); + Util::Procs::Stop(jit->first); + } + } + out = in; + } + +} //Controller namespace diff --git a/src/controller_streams.h b/src/controller_streams.h new file mode 100644 index 00000000..5af081b1 --- /dev/null +++ b/src/controller_streams.h @@ -0,0 +1,10 @@ +#include + +namespace Controller { + extern std::map lastBuffer; ///< Last moment of contact with all buffers. + + bool streamsEqual(JSON::Value & one, JSON::Value & two); + void startStream(std::string name, JSON::Value & data); + void CheckAllStreams(JSON::Value & data); + void CheckStreams(JSON::Value & in, JSON::Value & out); +} //Controller namespace diff --git a/src/converters/dtsc2flv.cpp b/src/converters/dtsc2flv.cpp index a4d6ab1a..02e63287 100644 --- a/src/converters/dtsc2flv.cpp +++ b/src/converters/dtsc2flv.cpp @@ -16,20 +16,20 @@ #include /// Holds all code that converts filetypes to/from DTSC. -namespace Converters{ - +namespace Converters { + /// Reads DTSC from STDIN, outputs FLV to STDOUT. - int DTSC2FLV() { + int DTSC2FLV(){ FLV::Tag FLV_out; // Temporary storage for outgoing FLV data. DTSC::Stream Strm; std::string inBuffer; - char charBuffer[1024*10]; + char charBuffer[1024 * 10]; unsigned int charCount; bool doneheader = false; while (std::cin.good()){ if (Strm.parsePacket(inBuffer)){ - if (!doneheader){ + if ( !doneheader){ doneheader = true; std::cout.write(FLV::Header, 13); FLV_out.DTSCMetaInit(Strm); @@ -47,22 +47,22 @@ namespace Converters{ std::cout.write(FLV_out.data, FLV_out.len); } }else{ - std::cin.read(charBuffer, 1024*10); + std::cin.read(charBuffer, 1024 * 10); charCount = std::cin.gcount(); inBuffer.append(charBuffer, charCount); } } std::cerr << "Done!" << std::endl; - - return 0; - }//FLV2DTSC -};//Converter namespace + return 0; + } //FLV2DTSC + +} //Converter namespace /// Entry point for DTSC2FLV, simply calls Converters::DTSC2FLV(). int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); conf.parseArgs(argc, argv); return Converters::DTSC2FLV(); -}//main +} //main diff --git a/src/converters/dtscfix.cpp b/src/converters/dtscfix.cpp index 29c6c49d..f2128e2a 100644 --- a/src/converters/dtscfix.cpp +++ b/src/converters/dtscfix.cpp @@ -7,16 +7,16 @@ #include /// Holds all code that converts filetypes to/from to DTSC. -namespace Converters{ +namespace Converters { /// Reads an DTSC file and attempts to fix the metadata in it. - int DTSCFix(Util::Config & conf) { + int DTSCFix(Util::Config & conf){ DTSC::File F(conf.getString("filename")); JSON::Value oriheader = F.getMeta(); JSON::Value meta = oriheader; JSON::Value pack; - if (!oriheader.isMember("moreheader")){ + if ( !oriheader.isMember("moreheader")){ std::cerr << "This file is not DTSCFix'able. Please re-convert and try again." << std::endl; return 1; } @@ -46,14 +46,20 @@ namespace Converters{ long long unsigned int bps = 0; F.seekNext(); - while (!F.getJSON().isNull()){ + while ( !F.getJSON().isNull()){ nowpack = F.getJSON()["time"].asInt(); - if (firstpack == 0){firstpack = nowpack;} + if (firstpack == 0){ + firstpack = nowpack; + } if (F.getJSON()["datatype"].asString() == "audio"){ if (lastaudio != 0 && (nowpack - lastaudio) != 0){ bps = F.getJSON()["data"].asString().size() / (nowpack - lastaudio); - if (bps < aud_min){aud_min = bps;} - if (bps > aud_max){aud_max = bps;} + if (bps < aud_min){ + aud_min = bps; + } + if (bps > aud_max){ + aud_max = bps; + } } totalaudio += F.getJSON()["data"].asString().size(); lastaudio = nowpack; @@ -61,24 +67,36 @@ namespace Converters{ if (F.getJSON()["datatype"].asString() == "video"){ if (lastvideo != 0 && (nowpack - lastvideo) != 0){ bps = F.getJSON()["data"].asString().size() / (nowpack - lastvideo); - if (bps < vid_min){vid_min = bps;} - if (bps > vid_max){vid_max = bps;} + if (bps < vid_min){ + vid_min = bps; + } + if (bps > vid_max){ + vid_max = bps; + } } if (F.getJSON()["keyframe"].asInt() != 0){ meta["keytime"].append(F.getJSON()["time"]); meta["keybpos"].append(F.getLastReadPos()); if (lastkey != 0){ bps = nowpack - lastkey; - if (bps < key_min){key_min = bps;} - if (bps > key_max){key_max = bps;} + if (bps < key_min){ + key_min = bps; + } + if (bps > key_max){ + key_max = bps; + } } keyframes++; lastkey = nowpack; } if (F.getJSON()["offset"].asInt() != 0){ bps = F.getJSON()["offset"].asInt(); - if (bps < bfrm_min){bfrm_min = bps;} - if (bps > bfrm_max){bfrm_max = bps;} + if (bps < bfrm_min){ + bfrm_min = bps; + } + if (bps > bfrm_max){ + bfrm_max = bps; + } } totalvideo += F.getJSON()["data"].asString().size(); lastvideo = nowpack; @@ -86,7 +104,7 @@ namespace Converters{ F.seekNext(); } - meta["length"] = (long long int)((nowpack - firstpack)/1000); + meta["length"] = (long long int)((nowpack - firstpack) / 1000); meta["lastms"] = (long long int)nowpack; if (meta.isMember("audio")){ meta["audio"]["bps"] = (long long int)(totalaudio / ((lastaudio - firstpack) / 1000)); @@ -104,7 +122,7 @@ namespace Converters{ std::cerr << "Appending new header..." << std::endl; std::string loader = meta.toPacked(); long long int newHPos = F.addHeader(loader); - if (!newHPos){ + if ( !newHPos){ std::cerr << "Failure appending new header. Cancelling." << std::endl; return 1; } @@ -117,9 +135,9 @@ namespace Converters{ }else{ return -1; } - }//DTSCFix + } //DTSCFix -}; +} /// Entry point for FLV2DTSC, simply calls Converters::FLV2DTSC(). int main(int argc, char ** argv){ @@ -127,4 +145,4 @@ int main(int argc, char ** argv){ conf.addOption("filename", JSON::fromString("{\"arg_num\":1, \"arg\":\"string\", \"help\":\"Filename of the file to attempt to fix.\"}")); conf.parseArgs(argc, argv); return Converters::DTSCFix(conf); -}//main +} //main diff --git a/src/converters/flv2dtsc.cpp b/src/converters/flv2dtsc.cpp index efea24fe..b8f90daa 100644 --- a/src/converters/flv2dtsc.cpp +++ b/src/converters/flv2dtsc.cpp @@ -17,65 +17,67 @@ #include /// Holds all code that converts filetypes to/from to DTSC. -namespace Converters{ +namespace Converters { /// Reads FLV from STDIN, outputs DTSC to STDOUT. - int FLV2DTSC() { + int FLV2DTSC(){ FLV::Tag FLV_in; // Temporary storage for incoming FLV data. JSON::Value meta_out; // Storage for outgoing header data. JSON::Value pack_out; // Storage for outgoing data. std::stringstream prebuffer; // Temporary buffer before sending real data bool sending = false; unsigned int counter = 0; - - while (!feof(stdin)){ + + while ( !feof(stdin)){ if (FLV_in.FileLoader(stdin)){ pack_out = FLV_in.toJSON(meta_out); - if (pack_out.isNull()){continue;} - if (!sending){ + if (pack_out.isNull()){ + continue; + } + if ( !sending){ counter++; if (counter > 8){ sending = true; meta_out["moreheader"] = 0LL; std::string packed_header = meta_out.toPacked(); unsigned int size = htonl(packed_header.size()); - std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; + std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*) &size, 4) << packed_header; std::cout << prebuffer.rdbuf(); prebuffer.str(""); std::cerr << "Buffer done, starting real-time output..." << std::endl; }else{ std::string packed_out = pack_out.toPacked(); unsigned int size = htonl(packed_out.size()); - prebuffer << std::string(DTSC::Magic_Packet, 4) << std::string((char*)&size, 4) << packed_out; - continue;//don't also write + prebuffer << std::string(DTSC::Magic_Packet, 4) << std::string((char*) &size, 4) << packed_out; + continue; //don't also write } } //simply write std::string packed_out = pack_out.toPacked(); unsigned int size = htonl(packed_out.size()); - std::cout << std::string(DTSC::Magic_Packet, 4) << std::string((char*)&size, 4) << packed_out; + std::cout << std::string(DTSC::Magic_Packet, 4) << std::string((char*) &size, 4) << packed_out; } } // if the FLV input is very short, do output it correctly... - if (!sending){ + if ( !sending){ std::cerr << "EOF - outputting buffer..." << std::endl; meta_out["moreheader"] = 0LL; std::string packed_header = meta_out.toPacked(); unsigned int size = htonl(packed_header.size()); - std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*)&size, 4) << packed_header; + std::cout << std::string(DTSC::Magic_Header, 4) << std::string((char*) &size, 4) << packed_header; std::cout << prebuffer.rdbuf(); } std::cerr << "Done! If you output this data to a file, don't forget to run MistDTSCFix next." << std::endl; - - return 0; - }//FLV2DTSC -};//Buffer namespace + return 0; + } //FLV2DTSC + +} /// Entry point for FLV2DTSC, simply calls Converters::FLV2DTSC(). int main(int argc, char ** argv){ Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION); conf.parseArgs(argc, argv); return Converters::FLV2DTSC(); -}//main +} //main diff --git a/src/player.cpp b/src/player.cpp index 36b195da..a73ac43f 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -32,32 +32,35 @@ class Stats{ std::string connector; unsigned int conntime; Stats(){ - up = 0; down = 0; conntime = 0; - }; + up = 0; + down = 0; + conntime = 0; + } + ; /// Reads a stats string and parses it to the internal representation. Stats(std::string s){ size_t f = s.find(' '); if (f != std::string::npos){ host = s.substr(0, f); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ connector = s.substr(0, f); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ conntime = atoi(s.substr(0, f).c_str()); - s.erase(0, f+1); + s.erase(0, f + 1); } f = s.find(' '); if (f != std::string::npos){ up = atoi(s.substr(0, f).c_str()); - s.erase(0, f+1); + s.erase(0, f + 1); down = atoi(s.c_str()); } - }; + } }; int main(int argc, char** argv){ @@ -75,7 +78,7 @@ int main(int argc, char** argv){ pausemark["time"] = (long long int)0; Socket::Connection StatsSocket = Socket::Connection("/tmp/mist/statistics", true); - int lasttime = Util::epoch();//time last packet was sent + int lasttime = Util::epoch(); //time last packet was sent //send the header std::string meta_str = meta.toNetPacked(); @@ -87,8 +90,8 @@ int main(int argc, char** argv){ JSON::Value last_pack; bool meta_sent = false; - long long now, lastTime = 0;//for timing of sending packets - long long bench = 0;//for benchmarking + long long now, lastTime = 0; //for timing of sending packets + long long bench = 0; //for benchmarking Stats sts; CYG_DEFI @@ -97,21 +100,22 @@ int main(int argc, char** argv){ if (CYG_LOOP in_out.spool()){ while (in_out.Received().size()){ //delete anything that doesn't end with a newline - if (*(in_out.Received().get().rbegin()) != '\n'){ + if ( *(in_out.Received().get().rbegin()) != '\n'){ in_out.Received().get().clear(); continue; } in_out.Received().get().resize(in_out.Received().get().size() - 1); - if (!in_out.Received().get().empty()){ + if ( !in_out.Received().get().empty()){ switch (in_out.Received().get()[0]){ - case 'P':{ //Push - #if DEBUG >= 4 + case 'P': { //Push +#if DEBUG >= 4 std::cerr << "Received push - ignoring (" << in_out.Received().get() << ")" << std::endl; - #endif - in_out.close();//pushing to VoD makes no sense - } break; - case 'S':{ //Stats - if (!StatsSocket.connected()){ +#endif + in_out.close(); //pushing to VoD makes no sense + } + break; + case 'S': { //Stats + if ( !StatsSocket.connected()){ StatsSocket = Socket::Connection("/tmp/mist/statistics", true); } if (StatsSocket.connected()){ @@ -125,7 +129,7 @@ int main(int argc, char** argv){ json_sts["vod"]["filename"] = conf.getString("filename"); json_sts["vod"]["now"] = Util::epoch(); json_sts["vod"]["start"] = Util::epoch() - sts.conntime; - if (!meta_sent){ + if ( !meta_sent){ json_sts["vod"]["meta"] = meta; json_sts["vod"]["meta"]["audio"].removeMember("init"); json_sts["vod"]["meta"]["video"].removeMember("init"); @@ -137,34 +141,42 @@ int main(int argc, char** argv){ StatsSocket.Send("\n\n"); StatsSocket.flush(); } - } break; - case 's':{ //second-seek + } + break; + case 's': { //second-seek int ms = JSON::Value(in_out.Received().get().substr(2)).asInt(); bool ret = source.seek_time(ms); lastTime = 0; - } break; - case 'f':{ //frame-seek + } + break; + case 'f': { //frame-seek bool ret = source.seek_frame(JSON::Value(in_out.Received().get().substr(2)).asInt()); lastTime = 0; - } break; - case 'p':{ //play + } + break; + case 'p': { //play playing = -1; lastTime = 0; in_out.setBlocking(false); - } break; - case 'o':{ //once-play - if (playing <= 0){playing = 1;} + } + break; + case 'o': { //once-play + if (playing <= 0){ + playing = 1; + } ++playing; in_out.setBlocking(false); - #if DEBUG >= 4 +#if DEBUG >= 4 std::cerr << "Playing one keyframe" << std::endl; - #endif +#endif bench = Util::getMS(); - } break; - case 'q':{ //quit-playing + } + break; + case 'q': { //quit-playing playing = 0; in_out.setBlocking(true); - } break; + } + break; } in_out.Received().get().clear(); } @@ -172,44 +184,48 @@ int main(int argc, char** argv){ } if (playing != 0){ now = Util::getMS(); - source.seekNext(); - if (!source.getJSON()){playing = 0;} - if (source.getJSON().isMember("keyframe")){ - if (playing == -1 && meta["video"]["keyms"].asInt() > now-lastTime) { - Util::sleep(meta["video"]["keyms"].asInt()-(now-lastTime)); - } - lastTime = now; - if (playing > 0){--playing;} + source.seekNext(); + if ( !source.getJSON()){ + playing = 0; + } + if (source.getJSON().isMember("keyframe")){ + if (playing == -1 && meta["video"]["keyms"].asInt() > now - lastTime){ + Util::sleep(meta["video"]["keyms"].asInt() - (now - lastTime)); } - if (playing == 0){ - #if DEBUG >= 4 - std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl; - #endif - pausemark["time"] = source.getJSON()["time"]; - pausemark.toPacked(); - in_out.SendNow(pausemark.toNetPacked()); - in_out.setBlocking(true); - }else{ - lasttime = Util::epoch(); - //insert proper header for this type of data - in_out.Send("DTPD"); - //insert the packet length - unsigned int size = htonl(source.getPacket().size()); - in_out.Send((char*)&size, 4); - in_out.SendNow(source.getPacket()); + lastTime = now; + if (playing > 0){ + --playing; } + } + if (playing == 0){ +#if DEBUG >= 4 + std::cerr << "Sending pause_marker (" << (Util::getMS() - bench) << "ms)" << std::endl; +#endif + pausemark["time"] = source.getJSON()["time"]; + pausemark.toPacked(); + in_out.SendNow(pausemark.toNetPacked()); + in_out.setBlocking(true); + }else{ + lasttime = Util::epoch(); + //insert proper header for this type of data + in_out.Send("DTPD"); + //insert the packet length + unsigned int size = htonl(source.getPacket().size()); + in_out.Send((char*) &size, 4); + in_out.SendNow(source.getPacket()); + } }else{ Util::sleep(10); } } StatsSocket.close(); in_out.close(); - #if DEBUG >= 4 +#if DEBUG >= 4 if (Util::epoch() - lasttime < 60){ std::cerr << "Player exited (disconnect)." << std::endl; }else{ std::cerr << "Player exited (timeout)." << std::endl; } - #endif +#endif return 0; }