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+=MistConnHTTP
|
||||||
bin_PROGRAMS+=MistConnHTTPProgressiveFLV
|
bin_PROGRAMS+=MistConnHTTPProgressiveFLV
|
||||||
bin_PROGRAMS+=MistConnHTTPProgressiveMP3
|
bin_PROGRAMS+=MistConnHTTPProgressiveMP3
|
||||||
|
bin_PROGRAMS+=MistConnHTTPProgressiveMP4
|
||||||
bin_PROGRAMS+=MistConnHTTPProgressiveOGG
|
bin_PROGRAMS+=MistConnHTTPProgressiveOGG
|
||||||
bin_PROGRAMS+=MistConnHTTPDynamic
|
bin_PROGRAMS+=MistConnHTTPDynamic
|
||||||
bin_PROGRAMS+=MistConnHTTPSmooth
|
bin_PROGRAMS+=MistConnHTTPSmooth
|
||||||
|
@ -57,6 +58,7 @@ MistConnHTTP_SOURCES=connectors/conn_http.cpp tinythread.cpp tinythread.h ../VER
|
||||||
MistConnHTTP_LDADD=$(MIST_LIBS) -lpthread
|
MistConnHTTP_LDADD=$(MIST_LIBS) -lpthread
|
||||||
MistConnHTTPProgressiveFLV_SOURCES=connectors/conn_http_progressive_flv.cpp ../VERSION
|
MistConnHTTPProgressiveFLV_SOURCES=connectors/conn_http_progressive_flv.cpp ../VERSION
|
||||||
MistConnHTTPProgressiveMP3_SOURCES=connectors/conn_http_progressive_mp3.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
|
MistConnHTTPProgressiveOGG_SOURCES=connectors/conn_http_progressive_ogg.cpp ../VERSION
|
||||||
MistConnHTTPDynamic_SOURCES=connectors/conn_http_dynamic.cpp ../VERSION
|
MistConnHTTPDynamic_SOURCES=connectors/conn_http_dynamic.cpp ../VERSION
|
||||||
MistConnHTTPSmooth_SOURCES=connectors/conn_http_smooth.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());
|
std::cout << std::string(ftypBox.asBox(),ftypBox.boxedSize());
|
||||||
|
|
||||||
uint64_t mdatSize = 0;
|
uint64_t mdatSize = 0;
|
||||||
|
//std::vector<uint64_t> stcoOffsets;
|
||||||
//moov box
|
//moov box
|
||||||
MP4::MOOV moovBox;
|
MP4::MOOV moovBox;
|
||||||
MP4::MVHD mvhdBox;
|
MP4::MVHD mvhdBox;
|
||||||
|
@ -262,7 +263,7 @@ namespace Converters {
|
||||||
stblBox.setContent(stszBox,3 + offset);
|
stblBox.setContent(stszBox,3 + offset);
|
||||||
|
|
||||||
MP4::STCO stcoBox;
|
MP4::STCO stcoBox;
|
||||||
stcoBox.setVersion(0);
|
stcoBox.setVersion(1);
|
||||||
total = 0;
|
total = 0;
|
||||||
uint64_t totalByteOffset = 0;
|
uint64_t totalByteOffset = 0;
|
||||||
//Inserting wrong values on purpose here, will be fixed later.
|
//Inserting wrong values on purpose here, will be fixed later.
|
||||||
|
@ -278,9 +279,17 @@ namespace Converters {
|
||||||
totalByteOffset += keyParts[i].size;
|
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;
|
mdatSize = totalByteOffset;
|
||||||
stblBox.setContent(stcoBox,4 + offset);
|
stblBox.setContent(stcoBox,4 + offset);
|
||||||
|
|
||||||
minfBox.setContent(stblBox,2);
|
minfBox.setContent(stblBox,2);
|
||||||
mdiaBox.setContent(minfBox, 2);
|
mdiaBox.setContent(minfBox, 2);
|
||||||
trakBox.setContent(mdiaBox, 1);
|
trakBox.setContent(mdiaBox, 1);
|
||||||
|
@ -288,10 +297,10 @@ namespace Converters {
|
||||||
boxOffset++;
|
boxOffset++;
|
||||||
}
|
}
|
||||||
//end arbitrary
|
//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;
|
unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8;
|
||||||
//update all STCO
|
//update all STCO
|
||||||
std::map <int, MP4::STCO&> STCOFix;
|
//std::map <int, MP4::STCO&> STCOFix;
|
||||||
//for tracks
|
//for tracks
|
||||||
for (unsigned int i = 1; i < moovBox.getContentCount(); i++){
|
for (unsigned int i = 1; i < moovBox.getContentCount(); i++){
|
||||||
//10 lines to get the STCO box.
|
//10 lines to get the STCO box.
|
||||||
|
@ -326,6 +335,9 @@ namespace Converters {
|
||||||
break;
|
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
|
//got the STCO box, fixing values with MP4 header offset
|
||||||
for (int j = 0; j < checkStcoBox.getEntryCount(); j++){
|
for (int j = 0; j < checkStcoBox.getEntryCount(); j++){
|
||||||
checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j);
|
checkStcoBox.setChunkOffset(checkStcoBox.getChunkOffset(j) + byteOffset, j);
|
||||||
|
|
Loading…
Add table
Reference in a new issue