Started on mp4 progressive

This commit is contained in:
Oswald Auguste de Bruin 2013-08-22 14:09:53 +02:00 committed by Erik Zandvliet
parent d6f91d17d2
commit a304d017d7
3 changed files with 250 additions and 4 deletions

View file

@ -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

View 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

View file

@ -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);