Working MP4 over Websockets

This commit is contained in:
Thulinma 2018-09-26 13:55:05 +02:00
parent fff62656ba
commit 5b79f296d6
2 changed files with 180 additions and 15 deletions

View file

@ -153,8 +153,9 @@ namespace Mist{
///\todo This function does not indicate errors anywhere... maybe fix this...
std::string OutProgressiveMP4::DTSCMeta2MP4Header(uint64_t & size, int fragmented){
if (myMeta.live){
if (myMeta.live || webSock){
needsLookAhead = 420;
if (webSock){needsLookAhead = 100;}
}
//Make sure we have a proper being value for the size...
size = 0;
@ -716,10 +717,19 @@ namespace Mist{
}
}
realBaseOffset += (moofBox.boxedSize() + mdatSize);
myConn.SendNow(moofBox.asBox(), moofBox.boxedSize());
char mdatHeader[8] ={0x00,0x00,0x00,0x00,'m','d','a','t'};
Bit::htobl(mdatHeader, mdatSize);
myConn.SendNow(mdatHeader, 8);
if (webSock){
if (webBuf.size()){
webSock->sendFrame(webBuf, webBuf.size(), 2);
webBuf.size() = 0;
}
webBuf.append(moofBox.asBox(), moofBox.boxedSize());
webBuf.append(mdatHeader, 8);
}else{
myConn.SendNow(moofBox.asBox(), moofBox.boxedSize());
myConn.SendNow(mdatHeader, 8);
}
}
void OutProgressiveMP4::onHTTP(){
@ -772,7 +782,7 @@ namespace Mist{
sending3GP = (H.url.find(".3gp") != std::string::npos);
fileSize = 0;
uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live);
uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live || webSock);
seekPoint = 0;
fragSeqNum = 0;
@ -781,7 +791,7 @@ namespace Mist{
char rangeType = ' ';
currPos = 0;
sortSet.clear();
if (!myMeta.live){
if (!myMeta.live && !webSock){
for (std::set<long unsigned int>::iterator subIt = selectedTracks.begin(); subIt != selectedTracks.end(); subIt++){
DTSC::Track & thisTrack = myMeta.tracks[*subIt];
keyPart temp;
@ -837,12 +847,6 @@ namespace Mist{
//HTTP_S.StartResponse(HTTP_R, conn);
}
leftOver = byteEnd - byteStart + 1;//add one byte, because range "0-0" = 1 byte of data
if (byteStart < headerSize){
std::string headerData = DTSCMeta2MP4Header(fileSize, myMeta.live);
myConn.SendNow(headerData.data() + byteStart, std::min(headerSize, byteEnd) - byteStart); //send MP4 header
leftOver -= std::min(headerSize, byteEnd) - byteStart;
}
currPos += headerSize;//we're now guaranteed to be past the header point, no matter what
}
///Builds up a datastructure that allows for access in the fragment send header function
@ -931,7 +935,7 @@ namespace Mist{
thisPacket.getString("data", dataPointer, len);
std::string subtitle;
if (myMeta.live){
if (myMeta.live || webSock){
//if header needed
if (!sortSet.size()){
if (fragSeqNum > 10 && !targetParams.count("start")){
@ -963,10 +967,14 @@ namespace Mist{
}
//The remainder of this function handles non-live situations
if (myMeta.live){
if (myMeta.live || webSock){
//generate content in mdat, meaning: send right parts
DONTEVEN_MSG("Sending %ld:%lld, size: %u", thisPacket.getTrackId(), thisPacket.getTime(), len);
myConn.SendNow(dataPointer, len);
if (webSock){
webBuf.append(dataPointer, len);
}else{
myConn.SendNow(dataPointer, len);
}
//delete from sortset
sortSet.erase(sortSet.begin());
return;
@ -1020,7 +1028,20 @@ namespace Mist{
}
void OutProgressiveMP4::sendHeader(){
if (myMeta.live){
//Send the header data
uint64_t headerSize = mp4HeaderSize(fileSize, myMeta.live || webSock);
if (byteStart < headerSize){
std::string headerData = DTSCMeta2MP4Header(fileSize, myMeta.live || webSock);
if (webSock){
webSock->sendFrame(headerData.data(), headerSize, 2); //send MP4 header
}else{
myConn.SendNow(headerData.data() + byteStart, std::min(headerSize, byteEnd) - byteStart); //send MP4 header
}
leftOver -= std::min(headerSize, byteEnd) - byteStart;
}
currPos += headerSize;//we're now guaranteed to be past the header point, no matter what
if (myMeta.live || webSock){
vidTrack = getMainSelectedTrack();
bool reSeek = false;
DTSC::Track & Trk = myMeta.tracks[vidTrack];
@ -1042,5 +1063,143 @@ namespace Mist{
sentHeader = true;
}
void OutProgressiveMP4::onWebsocketConnect(){
parseData = true;
wantRequest = true;
sentHeader = false;
fragSeqNum = 0;
sortSet.clear();
byteStart = 0;
idleInterval = 1000;
setBlocking(false);
onIdle();
}
void OutProgressiveMP4::onWebsocketFrame() {
if (webSock->frameType != 1) {
HIGH_MSG("Ignoring non-text websocket frame");
return;
}
JSON::Value command = JSON::fromString(webSock->data, webSock->data.size());
JSON::Value commandResult;
//Check if there's a command type
if (!command.isMember("type")) {
JSON::Value commandResult;
commandResult["type"] = "on_error";
commandResult["command"] = "error";
commandResult["message"] = "Received a command but no type property was given.";
webSock->sendFrame(commandResult.toString());
return;
}
if (command["type"] == "tracks"){
if (command.isMember("audio")){
if (!command["audio"].isNull()){
targetParams["audio"] = command["audio"].asString();
}else{
targetParams.erase("audio");
}
}
if (command.isMember("video")){
if (!command["video"].isNull()){
targetParams["video"] = command["video"].asString();
}else{
targetParams.erase("video");
}
}
selectDefaultTracks();
sortSet.clear();
sentHeader = false;
onIdle();
return;
}
if (command["type"] == "seek") {
if (!command.isMember("seek_time")) {
JSON::Value commandResult;
commandResult["type"] = "on_error";
commandResult["command"] = "on_seek";
commandResult["message"] = "Received a seek request but no `seek_time` property.";
webSock->sendFrame(commandResult.toString());
return;
}
uint64_t seek_time = command["seek_time"].asInt();
if (!parseData){
parseData = true;
selectDefaultTracks();
}
seek(seek_time, true);
sortSet.clear();
sentHeader = false;
JSON::Value commandResult;
commandResult["type"] = "on_seek";
commandResult["result"] = true;
webSock->sendFrame(commandResult.toString());
onIdle();
return;
}
if (command["type"] == "pause") {
parseData = !parseData;
JSON::Value commandResult;
commandResult["type"] = "on_time";
commandResult["paused"] = !parseData;
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
commandResult["tracks"].append(*it);
}
webSock->sendFrame(commandResult.toString());
return;
}
if (command["type"] == "stop") {
INFO_MSG("Received stop() command.");
myConn.close();
return;
}
//Unhandled
{
JSON::Value commandResult;
commandResult["type"] = "on_error";
commandResult["command"] = command["type"].asString();
commandResult["message"] = "Unhandled command type: "+command["type"].asString();
webSock->sendFrame(commandResult.toString());
}
}
void OutProgressiveMP4::onIdle(){
if (parseData && webSock){
JSON::Value commandResult;
commandResult["type"] = "on_time";
if (!sentHeader){commandResult["next_is_init"] = true;}
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
for (std::set<unsigned long>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
commandResult["tracks"].append(*it);
}
webSock->sendFrame(commandResult.toString());
}
}
bool OutProgressiveMP4::onFinish(){
if (parseData && webSock){
JSON::Value commandResult;
commandResult["type"] = "on_stop";
commandResult["current"] = currentTime();
commandResult["begin"] = startTime();
commandResult["end"] = endTime();
webSock->sendFrame(commandResult.toString());
parseData = false;
}
return true;
}
}

View file

@ -44,7 +44,13 @@ namespace Mist {
void onHTTP();
void sendNext();
void sendHeader();
bool doesWebsockets(){return true;}
void onIdle();
bool onFinish();
virtual void onWebsocketFrame();
virtual void onWebsocketConnect();
protected:
Util::ResizeablePointer webBuf;
uint64_t fileSize;
uint64_t byteStart;
uint64_t byteEnd;