Added H264 Annex B stream input
This commit is contained in:
parent
efcb038a26
commit
a0d54e1a95
3 changed files with 161 additions and 0 deletions
|
@ -282,6 +282,7 @@ makeInput(MP3 mp3)
|
|||
makeInput(FLV flv)
|
||||
makeInput(OGG ogg)
|
||||
makeInput(Buffer buffer)
|
||||
makeInput(H264 h264)
|
||||
|
||||
########################################
|
||||
# MistServer - Outputs #
|
||||
|
|
127
src/input/input_h264.cpp
Normal file
127
src/input/input_h264.cpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#include "input_h264.h"
|
||||
#include <mist/h264.h>
|
||||
#include <mist/mp4_generic.h>
|
||||
|
||||
namespace Mist{
|
||||
InputH264::InputH264(Util::Config *cfg) : Input(cfg){
|
||||
capa["name"] = "H264";
|
||||
capa["desc"] = "H264 Annex B input";
|
||||
capa["source_match"] = "h264-exec:*";
|
||||
//May be set to always-on mode
|
||||
capa["always_match"].append("h264-exec:*");
|
||||
capa["priority"] = 0ll;
|
||||
capa["codecs"][0u][0u].append("H264");
|
||||
frameCount = 0;
|
||||
startTime = Util::bootMS();
|
||||
inputProcess = 0;
|
||||
}
|
||||
|
||||
int InputH264::run(){
|
||||
if (config->getString("input") != "-"){
|
||||
std::string input = config->getString("input");
|
||||
const char *argv[2];
|
||||
input = input.substr(10);
|
||||
|
||||
char *args[128];
|
||||
uint8_t argCnt = 0;
|
||||
char *startCh = 0;
|
||||
for (char *i = (char*)input.c_str(); i <= input.data() + input.size(); ++i){
|
||||
if (!*i){
|
||||
if (startCh){args[argCnt++] = startCh;}
|
||||
break;
|
||||
}
|
||||
if (*i == ' '){
|
||||
if (startCh){
|
||||
args[argCnt++] = startCh;
|
||||
startCh = 0;
|
||||
*i = 0;
|
||||
}
|
||||
}else{
|
||||
if (!startCh){startCh = i;}
|
||||
}
|
||||
}
|
||||
args[argCnt] = 0;
|
||||
|
||||
int fin = -1, fout = -1, ferr = -1;
|
||||
inputProcess = Util::Procs::StartPiped(args, &fin, &fout, &ferr);
|
||||
myConn = Socket::Connection(-1, fout);
|
||||
}else{
|
||||
myConn = Socket::Connection(fileno(stdout), fileno(stdin));
|
||||
}
|
||||
myConn.Received().splitter.assign("\000\000\001", 3);
|
||||
myMeta.vod = false;
|
||||
myMeta.live = true;
|
||||
myMeta.tracks[1].type = "video";
|
||||
myMeta.tracks[1].codec = "H264";
|
||||
myMeta.tracks[1].trackID = 1;
|
||||
waitsSinceData = 0;
|
||||
return Input::run();
|
||||
}
|
||||
|
||||
bool InputH264::setup(){
|
||||
std::string input = config->getString("input");
|
||||
if (input != "-" && input.substr(0, 10) != "h264-exec:"){
|
||||
FAIL_MSG("Unsupported input type: %s", input.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputH264::getNext(bool smart){
|
||||
do{
|
||||
if (!myConn.spool()){
|
||||
Util::sleep(25);
|
||||
++waitsSinceData;
|
||||
if (waitsSinceData > 5000 / 25 && (waitsSinceData % 40) == 0){
|
||||
WARN_MSG("No H264 data received for > 5s, killing source process");
|
||||
Util::Procs::Stop(inputProcess);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
waitsSinceData = 0;
|
||||
uint32_t bytesToRead = myConn.Received().bytesToSplit();
|
||||
if (!bytesToRead){continue;}
|
||||
std::string NAL = myConn.Received().remove(bytesToRead);
|
||||
uint32_t nalSize = NAL.size() - 3;
|
||||
while (nalSize && NAL.data()[nalSize - 1] == 0){--nalSize;}
|
||||
if (!nalSize){continue;}
|
||||
uint8_t nalType = NAL.data()[0] & 0x1F;
|
||||
INSANE_MSG("NAL unit, type %u, size %lu", nalType, nalSize);
|
||||
if (nalType == 7 || nalType == 8){
|
||||
if (nalType == 7){spsInfo = NAL.substr(0, nalSize);}
|
||||
if (nalType == 8){ppsInfo = NAL.substr(0, nalSize);}
|
||||
if (!myMeta.tracks[1].init.size() && spsInfo.size() && ppsInfo.size()){
|
||||
h264::sequenceParameterSet sps(spsInfo.data(), spsInfo.size());
|
||||
h264::SPSMeta spsChar = sps.getCharacteristics();
|
||||
myMeta.tracks[1].width = spsChar.width;
|
||||
myMeta.tracks[1].height = spsChar.height;
|
||||
myMeta.tracks[1].fpks = spsChar.fps * 1000;
|
||||
if (myMeta.tracks[1].fpks < 100 || myMeta.tracks[1].fpks > 1000000){
|
||||
myMeta.tracks[1].fpks = 0;
|
||||
}
|
||||
MP4::AVCC avccBox;
|
||||
avccBox.setVersion(1);
|
||||
avccBox.setProfile(spsInfo[1]);
|
||||
avccBox.setCompatibleProfiles(spsInfo[2]);
|
||||
avccBox.setLevel(spsInfo[3]);
|
||||
avccBox.setSPSNumber(1);
|
||||
avccBox.setSPS(spsInfo);
|
||||
avccBox.setPPSNumber(1);
|
||||
avccBox.setPPS(ppsInfo);
|
||||
myMeta.tracks[1].init = std::string(avccBox.payload(), avccBox.payloadSize());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (myMeta.tracks[1].init.size()){
|
||||
uint64_t ts = Util::bootMS() - startTime;
|
||||
if (myMeta.tracks[1].fpks){ts = frameCount * (1000000 / myMeta.tracks[1].fpks);}
|
||||
thisPacket.genericFill(ts, 0, 1, 0, 0, 0, h264::isKeyframe(NAL.data(), nalSize));
|
||||
thisPacket.appendNal(NAL.data(), nalSize, nalSize);
|
||||
++frameCount;
|
||||
return;
|
||||
}
|
||||
}while (myConn && (inputProcess == 0 || Util::Procs::childRunning(inputProcess)));
|
||||
if (inputProcess){myConn.close();}
|
||||
}
|
||||
}
|
||||
|
33
src/input/input_h264.h
Normal file
33
src/input/input_h264.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#include "input.h"
|
||||
#include <mist/dtsc.h>
|
||||
#include <mist/procs.h>
|
||||
|
||||
namespace Mist{
|
||||
class InputH264 : public Input{
|
||||
public:
|
||||
InputH264(Util::Config *cfg);
|
||||
int run();
|
||||
|
||||
protected:
|
||||
bool setup();
|
||||
void getNext(bool smart = true);
|
||||
Socket::Connection myConn;
|
||||
std::string ppsInfo;
|
||||
std::string spsInfo;
|
||||
uint64_t frameCount;
|
||||
// Empty defaults
|
||||
bool readHeader(){return true;}
|
||||
bool openStreamSource(){return true;}
|
||||
void closeStreamSource(){}
|
||||
void parseStreamHeader(){}
|
||||
void seek(int seekTime){}
|
||||
void trackSelect(std::string trackSpec){}
|
||||
bool needsLock(){return false;}
|
||||
uint64_t startTime;
|
||||
pid_t inputProcess;
|
||||
uint32_t waitsSinceData;
|
||||
};
|
||||
}
|
||||
|
||||
typedef Mist::InputH264 mistIn;
|
||||
|
Loading…
Add table
Reference in a new issue