Started on mp4 progressive
This commit is contained in:
parent
d6f91d17d2
commit
a304d017d7
3 changed files with 250 additions and 4 deletions
|
@ -18,6 +18,7 @@ bin_PROGRAMS+=MistConnRTMP
|
|||
bin_PROGRAMS+=MistConnHTTP
|
||||
bin_PROGRAMS+=MistConnHTTPProgressiveFLV
|
||||
bin_PROGRAMS+=MistConnHTTPProgressiveMP3
|
||||
bin_PROGRAMS+=MistConnHTTPProgressiveMP4
|
||||
bin_PROGRAMS+=MistConnHTTPProgressiveOGG
|
||||
bin_PROGRAMS+=MistConnHTTPDynamic
|
||||
bin_PROGRAMS+=MistConnHTTPSmooth
|
||||
|
@ -57,6 +58,7 @@ MistConnHTTP_SOURCES=connectors/conn_http.cpp tinythread.cpp tinythread.h ../VER
|
|||
MistConnHTTP_LDADD=$(MIST_LIBS) -lpthread
|
||||
MistConnHTTPProgressiveFLV_SOURCES=connectors/conn_http_progressive_flv.cpp ../VERSION
|
||||
MistConnHTTPProgressiveMP3_SOURCES=connectors/conn_http_progressive_mp3.cpp ../VERSION
|
||||
MistConnHTTPProgressiveMP4_SOURCES=connectors/conn_http_progressive_mp4.cpp ../VERSION
|
||||
MistConnHTTPProgressiveOGG_SOURCES=connectors/conn_http_progressive_ogg.cpp ../VERSION
|
||||
MistConnHTTPDynamic_SOURCES=connectors/conn_http_dynamic.cpp ../VERSION
|
||||
MistConnHTTPSmooth_SOURCES=connectors/conn_http_smooth.cpp ../VERSION
|
||||
|
|
232
src/connectors/conn_http_progressive_mp4.cpp
Normal file
232
src/connectors/conn_http_progressive_mp4.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
///\file conn_http_progressive.cpp
|
||||
///\brief Contains the main code for the HTTP Progressive Connector
|
||||
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <cmath>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <mist/socket.h>
|
||||
#include <mist/http_parser.h>
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/ogg.h>
|
||||
#include <mist/amf.h>
|
||||
#include <mist/config.h>
|
||||
#include <mist/stream.h>
|
||||
#include <mist/timing.h>
|
||||
|
||||
///\brief Holds everything unique to HTTP Connectors.
|
||||
namespace Connector_HTTP {
|
||||
///\brief Main function for the HTTP Progressive Connector
|
||||
///\param conn A socket describing the connection the client.
|
||||
///\return The exit code of the connector.
|
||||
int progressiveConnector(Socket::Connection conn){
|
||||
bool progressive_has_sent_header = false;//Indicates whether we have sent a header.
|
||||
bool ready4data = false; //Set to true when streaming is to begin.
|
||||
DTSC::Stream Strm; //Incoming stream buffer.
|
||||
HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
|
||||
bool inited = false;//Whether the stream is initialized
|
||||
Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
|
||||
std::string streamname;//Will contain the name of the stream.
|
||||
|
||||
//MP4 specific variables
|
||||
|
||||
//OGG specific variables
|
||||
/*OGG::headerPages oggMeta;
|
||||
OGG::Page curOggPage;
|
||||
std::map <long long unsigned int, std::vector<JSON::Value> > DTSCBuffer;
|
||||
std::map <long long unsigned int, long long unsigned int> prevGran;*/
|
||||
|
||||
|
||||
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
||||
unsigned int seek_sec = 0;//Seek position in ms
|
||||
unsigned int seek_byte = 0;//Seek position in bytes
|
||||
|
||||
int videoID = -1;
|
||||
int audioID = -1;
|
||||
|
||||
while (conn.connected()){
|
||||
//Only attempt to parse input when not yet init'ed.
|
||||
if ( !inited){
|
||||
if (conn.Received().size() || conn.spool()){
|
||||
//make sure it ends in a \n
|
||||
if ( *(conn.Received().get().rbegin()) != '\n'){
|
||||
std::string tmp = conn.Received().get();
|
||||
conn.Received().get().clear();
|
||||
if (conn.Received().size()){
|
||||
conn.Received().get().insert(0, tmp);
|
||||
}else{
|
||||
conn.Received().append(tmp);
|
||||
}
|
||||
}
|
||||
if (HTTP_R.Read(conn.Received().get())){
|
||||
#if DEBUG >= 5
|
||||
std::cout << "Received request: " << HTTP_R.getUrl() << std::endl;
|
||||
#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
|
||||
int start = 0;
|
||||
if ( !HTTP_R.GetVar("start").empty()){
|
||||
start = atoi(HTTP_R.GetVar("start").c_str());
|
||||
}
|
||||
if ( !HTTP_R.GetVar("starttime").empty()){
|
||||
start = atoi(HTTP_R.GetVar("starttime").c_str());
|
||||
}
|
||||
if ( !HTTP_R.GetVar("apstart").empty()){
|
||||
start = atoi(HTTP_R.GetVar("apstart").c_str());
|
||||
}
|
||||
if ( !HTTP_R.GetVar("ec_seek").empty()){
|
||||
start = atoi(HTTP_R.GetVar("ec_seek").c_str());
|
||||
}
|
||||
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
|
||||
}else{
|
||||
seek_byte = start; //divide by 1mbit, then *1000 for ms.
|
||||
}
|
||||
ready4data = true;
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ready4data){
|
||||
if ( !inited){
|
||||
//we are ready, connect the socket!
|
||||
ss = Util::Stream::getStream(streamname);
|
||||
if ( !ss.connected()){
|
||||
#if DEBUG >= 1
|
||||
fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str());
|
||||
#endif
|
||||
ss.close();
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("No such stream is available on the system. Please try again.\n");
|
||||
conn.SendNow(HTTP_S.BuildResponse("404", "Not found"));
|
||||
ready4data = false;
|
||||
continue;
|
||||
}
|
||||
//wait until we have a header
|
||||
while ( !Strm.metadata && ss.connected()){
|
||||
if (ss.spool()){
|
||||
Strm.parsePacket(ss.Received()); //read the metadata
|
||||
}else{
|
||||
Util::sleep(5);
|
||||
}
|
||||
}
|
||||
int byterate = 0;
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (videoID == -1 && objIt->second["type"].asString() == "video"){
|
||||
videoID = objIt->second["trackid"].asInt();
|
||||
}
|
||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
}
|
||||
}
|
||||
if (videoID != -1){
|
||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
||||
}
|
||||
if (audioID != -1){
|
||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
||||
}
|
||||
if ( !byterate){byterate = 1;}
|
||||
seek_sec = (seek_byte / byterate) * 1000;
|
||||
std::stringstream cmd;
|
||||
cmd << "t";
|
||||
if (videoID != -1){
|
||||
cmd << " " << videoID;
|
||||
}
|
||||
if (audioID != -1){
|
||||
cmd << " " << audioID;
|
||||
}
|
||||
cmd << "\ns " << seek_sec << "\np\n";
|
||||
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
||||
inited = true;
|
||||
}
|
||||
unsigned int now = Util::epoch();
|
||||
if (now != lastStats){
|
||||
lastStats = now;
|
||||
ss.SendNow(conn.getStats("HTTP_Progressive_Ogg").c_str());
|
||||
}
|
||||
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/ogg"); //Send the correct content-type for FLV files
|
||||
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
|
||||
//Fill in header here
|
||||
progressive_has_sent_header = true;
|
||||
}
|
||||
//parse DTSC to MP4 here
|
||||
}
|
||||
}else{
|
||||
Util::sleep(1);
|
||||
}
|
||||
if ( !ss.connected()){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
conn.close();
|
||||
ss.SendNow(conn.getStats("HTTP_Dynamic").c_str());
|
||||
ss.close();
|
||||
return 0;
|
||||
} //Progressive_Connector main function
|
||||
|
||||
} //Connector_HTTP namespace
|
||||
|
||||
///\brief The standard process-spawning main function.
|
||||
int main(int argc, char ** argv){
|
||||
Util::Config conf(argv[0], PACKAGE_VERSION);
|
||||
JSON::Value capa;
|
||||
capa["desc"] = "Enables HTTP protocol progressive streaming.";
|
||||
capa["deps"] = "HTTP";
|
||||
capa["url_rel"] = "/$.mp4";
|
||||
capa["url_match"] = "/$.mp4";
|
||||
capa["url_handler"] = "http";
|
||||
capa["url_type"] = "mp4";
|
||||
capa["socket"] = "http_progressive_mp4";
|
||||
conf.addBasicConnectorOptions(capa);
|
||||
conf.parseArgs(argc, argv);
|
||||
|
||||
if (conf.getBool("json")){
|
||||
std::cout << capa.toString() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Socket::Server server_socket = Socket::Server("/tmp/mist/http_progressive_mp4");
|
||||
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
|
||||
pid_t myid = fork();
|
||||
if (myid == 0){ //if new child, start MAINHANDLER
|
||||
return Connector_HTTP::progressiveConnector(S);
|
||||
}else{ //otherwise, do nothing or output debugging text
|
||||
#if DEBUG >= 5
|
||||
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} //while connected
|
||||
server_socket.close();
|
||||
return 0;
|
||||
} //main
|
|
@ -46,6 +46,7 @@ namespace Converters {
|
|||
std::cout << std::string(ftypBox.asBox(),ftypBox.boxedSize());
|
||||
|
||||
uint64_t mdatSize = 0;
|
||||
//std::vector<uint64_t> stcoOffsets;
|
||||
//moov box
|
||||
MP4::MOOV moovBox;
|
||||
MP4::MVHD mvhdBox;
|
||||
|
@ -262,7 +263,7 @@ namespace Converters {
|
|||
stblBox.setContent(stszBox,3 + offset);
|
||||
|
||||
MP4::STCO stcoBox;
|
||||
stcoBox.setVersion(0);
|
||||
stcoBox.setVersion(1);
|
||||
total = 0;
|
||||
uint64_t totalByteOffset = 0;
|
||||
//Inserting wrong values on purpose here, will be fixed later.
|
||||
|
@ -278,9 +279,17 @@ namespace Converters {
|
|||
totalByteOffset += keyParts[i].size;
|
||||
}
|
||||
}
|
||||
//calculating the offset where the STCO box will be in the main MOOV box
|
||||
//needed for probable optimise
|
||||
/*stcoOffsets.push_back(
|
||||
moovBox.payloadSize() +
|
||||
trakBox.boxedSize() +
|
||||
mdiaBox.boxedSize() +
|
||||
minfBox.boxedSize() +
|
||||
stblBox.boxedSize()
|
||||
);*/
|
||||
mdatSize = totalByteOffset;
|
||||
stblBox.setContent(stcoBox,4 + offset);
|
||||
|
||||
minfBox.setContent(stblBox,2);
|
||||
mdiaBox.setContent(minfBox, 2);
|
||||
trakBox.setContent(mdiaBox, 1);
|
||||
|
@ -288,10 +297,10 @@ namespace Converters {
|
|||
boxOffset++;
|
||||
}
|
||||
//end arbitrary
|
||||
//initial offset lengte ftyp, length moov + 8
|
||||
//initial offset length ftyp, length moov + 8
|
||||
unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8;
|
||||
//update all STCO
|
||||
std::map <int, MP4::STCO&> STCOFix;
|
||||
//std::map <int, MP4::STCO&> STCOFix;
|
||||
//for tracks
|
||||
for (unsigned int i = 1; i < moovBox.getContentCount(); i++){
|
||||
//10 lines to get the STCO box.
|
||||
|
@ -326,6 +335,9 @@ namespace Converters {
|
|||
break;
|
||||
}
|
||||
}
|
||||
/*MP4::Box temp = MP4::Box((moovBox.payload()+stcoOffsets[i]),false);
|
||||
MP4::STCO & checkStcoBox = *((MP4::STCO*)(&temp));
|
||||
std::cerr << checkStcoBox.toPrettyString() << std::endl;*/
|
||||
//got the STCO box, fixing values with MP4 header offset
|
||||
for (int j = 0; j < checkStcoBox.getEntryCount(); j++){
|
||||
checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j);
|
||||
|
|
Loading…
Add table
Reference in a new issue