mistserver/src/output/output_ts.cpp
2014-08-29 22:54:00 +02:00

172 lines
6.1 KiB
C++

#include "output_ts.h"
#include <mist/http_parser.h>
#include <mist/defines.h>
namespace Mist {
OutTS::OutTS(Socket::Connection & conn) : Output(conn){
haveAvcc = false;
AudioCounter = 0;
VideoCounter = 0;
streamName = config->getString("streamname");
parseData = true;
wantRequest = false;
initialize();
std::string tracks = config->getString("tracks");
unsigned int currTrack = 0;
//loop over tracks, add any found track IDs to selectedTracks
if (tracks != ""){
selectedTracks.clear();
for (unsigned int i = 0; i < tracks.size(); ++i){
if (tracks[i] >= '0' && tracks[i] <= '9'){
currTrack = currTrack*10 + (tracks[i] - '0');
}else{
if (currTrack > 0){
selectedTracks.insert(currTrack);
}
currTrack = 0;
}
}
if (currTrack > 0){
selectedTracks.insert(currTrack);
}
}
}
OutTS::~OutTS() {}
void OutTS::init(Util::Config * cfg){
Output::init(cfg);
capa["name"] = "TS";
capa["desc"] = "Enables the raw MPEG Transport Stream protocol over TCP.";
capa["deps"] = "";
capa["required"]["streamname"]["name"] = "Stream";
capa["required"]["streamname"]["help"] = "What streamname to serve. For multiple streams, add this protocol multiple times using different ports.";
capa["required"]["streamname"]["type"] = "str";
capa["required"]["streamname"]["option"] = "--stream";
capa["optional"]["tracks"]["name"] = "Tracks";
capa["optional"]["tracks"]["help"] = "The track IDs of the stream that this connector will transmit separated by spaces";
capa["optional"]["tracks"]["type"] = "str";
capa["optional"]["tracks"]["option"] = "--tracks";
capa["codecs"][0u][0u].append("H264");
capa["codecs"][0u][1u].append("AAC");
capa["codecs"][0u][1u].append("MP3");
cfg->addOption("streamname",
JSON::fromString("{\"arg\":\"string\",\"short\":\"s\",\"long\":\"stream\",\"help\":\"The name of the stream that this connector will transmit.\"}"));
cfg->addOption("tracks",
JSON::fromString("{\"arg\":\"string\",\"value\":[\"\"],\"short\": \"t\",\"long\":\"tracks\",\"help\":\"The track IDs of the stream that this connector will transmit separated by spaces.\"}"));
cfg->addConnectorOptions(8888, capa);
config = cfg;
}
void OutTS::fillPacket(bool & first, const char * data, size_t dataLen, char & ContCounter){
if (!PackData.BytesFree()){
myConn.SendNow(PackData.ToString(), 188);
PackData.Clear();
}
if (!dataLen){return;}
if (PackData.BytesFree() == 184){
PackData.PID(0x100 - 1 + currentPacket.getTrackId());
PackData.ContinuityCounter(ContCounter++);
if (first){
PackData.UnitStart(1);
if (currentPacket.getInt("keyframe")){
PackData.RandomAccess(1);
PackData.PCR(currentPacket.getTime() * 27000);
}
first = false;
}
}
unsigned int tmp = PackData.FillFree(data, dataLen);
if (tmp != dataLen){
fillPacket(first, data+tmp, dataLen-tmp, ContCounter);
}
}
void OutTS::sendNext(){
char * dataPointer = 0;
unsigned int dataLen = 0;
currentPacket.getString("data", dataPointer, dataLen); //data
bool first = true;
std::string bs;
//prepare bufferstring
if (myMeta.tracks[currentPacket.getTrackId()].type == "video"){
bs = TS::Packet::getPESVideoLeadIn(0ul, currentPacket.getTime() * 90);
fillPacket(first, bs.data(), bs.size(),VideoCounter);
if (currentPacket.getInt("keyframe")){
if (!haveAvcc){
avccbox.setPayload(myMeta.tracks[currentPacket.getTrackId()].init);
haveAvcc = true;
}
bs = avccbox.asAnnexB();
fillPacket(first, bs.data(), bs.size(),VideoCounter);
}
unsigned int i = 0;
while (i + 4 < (unsigned int)dataLen){
unsigned int ThisNaluSize = (dataPointer[i] << 24) + (dataPointer[i+1] << 16) + (dataPointer[i+2] << 8) + dataPointer[i+3];
if (ThisNaluSize + i + 4 > (unsigned int)dataLen){
DEBUG_MSG(DLVL_WARN, "Too big NALU detected (%u > %d) - skipping!", ThisNaluSize + i + 4, dataLen);
break;
}
fillPacket(first, "\000\000\000\001",4,VideoCounter);
fillPacket(first, dataPointer+i+4,ThisNaluSize,VideoCounter);
i += ThisNaluSize+4;
}
}else if (myMeta.tracks[currentPacket.getTrackId()].type == "audio"){
bs = TS::Packet::getPESAudioLeadIn(7+dataLen, currentPacket.getTime() * 90);
fillPacket(first, bs.data(), bs.size(),AudioCounter);
bs = TS::GetAudioHeader(dataLen, myMeta.tracks[currentPacket.getTrackId()].init);
fillPacket(first, bs.data(), bs.size(),AudioCounter);
fillPacket(first, dataPointer,dataLen,AudioCounter);
}
if (PackData.BytesFree() < 184){
PackData.AddStuffing();
fillPacket(first, 0,0,VideoCounter);
}
}
///this function generates the PMT packet
std::string OutTS::createPMT(){
TS::ProgramMappingTable PMT;
PMT.PID(4096);
PMT.setTableId(2);
PMT.setSectionLength(0xB017);
PMT.setProgramNumber(1);
PMT.setVersionNumber(0);
PMT.setCurrentNextIndicator(0);
PMT.setSectionNumber(0);
PMT.setLastSectionNumber(0);
PMT.setPCRPID(0x100 + (*(selectedTracks.begin())) - 1);
PMT.setProgramInfoLength(0);
short id = 0;
//for all selected tracks
for (std::set<long unsigned int>::iterator it = selectedTracks.begin(); it != selectedTracks.end(); it++){
if (myMeta.tracks[*it].codec == "H264"){
PMT.setStreamType(0x1B,id);
}else if (myMeta.tracks[*it].codec == "AAC"){
PMT.setStreamType(0x0F,id);
}else if (myMeta.tracks[*it].codec == "MP3"){
PMT.setStreamType(0x03,id);
}
PMT.setElementaryPID(0x100 + (*it) - 1, id);
PMT.setESInfoLength(0,id);
id++;
}
PMT.calcCRC();
return PMT.getStrBuf();
}
void OutTS::sendHeader(){
myConn.SendNow(TS::PAT, 188);
myConn.SendNow(createPMT());
sentHeader = true;
}
}