Metadata upgrades as well as improvements to the buffer in terms of debugging and code maintainability.
This commit is contained in:
parent
bb1971e961
commit
cde42ce36e
25 changed files with 390 additions and 664 deletions
|
@ -27,16 +27,16 @@
|
|||
/// Holds everything unique to HTTP Connectors.
|
||||
namespace Connector_HTTP {
|
||||
|
||||
std::set<std::string> videoTracks;///<< Holds valid video tracks for playback
|
||||
std::set<int> videoTracks;///<< Holds valid video tracks for playback
|
||||
long long int audioTrack = 0;///<< Holds audio track ID for playback
|
||||
void getTracks(JSON::Value & metadata){
|
||||
void getTracks(DTSC::Meta & metadata){
|
||||
videoTracks.clear();
|
||||
for (JSON::ObjIter it = metadata["tracks"].ObjBegin(); it != metadata["tracks"].ObjEnd(); it++){
|
||||
if (it->second["type"] == "video"){
|
||||
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||
if (it->second.type == "video"){
|
||||
videoTracks.insert(it->first);
|
||||
}
|
||||
if (it->second["type"] == "audio"){
|
||||
audioTrack = it->second["trackid"].asInt();
|
||||
if (it->second.type == "audio"){
|
||||
audioTrack = it->first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ namespace Connector_HTTP {
|
|||
///\param metadata The current metadata, used to generate the index.
|
||||
///\param fragnum The index of the current fragment
|
||||
///\return The generated bootstrap.
|
||||
std::string dynamicBootstrap(std::string & streamName, JSON::Value & metadata, bool isLive = false, int fragnum = 0){
|
||||
std::string dynamicBootstrap(std::string & streamName, DTSC::Track & trackMeta, bool isLive = false, int fragnum = 0){
|
||||
std::string empty;
|
||||
|
||||
MP4::ASRT asrt;
|
||||
|
@ -57,7 +57,7 @@ namespace Connector_HTTP {
|
|||
if (isLive){
|
||||
asrt.setSegmentRun(1, 4294967295ul, 0);
|
||||
}else{
|
||||
asrt.setSegmentRun(1, metadata["keys"].size(), 0);
|
||||
asrt.setSegmentRun(1, trackMeta.keys.size(), 0);
|
||||
}
|
||||
|
||||
MP4::AFRT afrt;
|
||||
|
@ -66,14 +66,14 @@ namespace Connector_HTTP {
|
|||
afrt.setTimeScale(1000);
|
||||
//afrt.setQualityEntry(empty, 0);
|
||||
MP4::afrt_runtable afrtrun;
|
||||
long long int currTime = 0;
|
||||
for (int i = 0; i < metadata["keys"].size(); i++){
|
||||
if (metadata["keys"][i]["len"].asInt() > 0){
|
||||
afrtrun.firstFragment = metadata["keys"][i]["num"].asInt();
|
||||
afrtrun.firstTimestamp = metadata["keys"][i]["time"].asInt();
|
||||
afrtrun.duration = metadata["keys"][i]["len"].asInt();
|
||||
currTime = afrtrun.firstTimestamp + afrtrun.duration;
|
||||
int i = 0;
|
||||
for (std::deque<DTSC::Key>::iterator it = trackMeta.keys.begin(); it != trackMeta.keys.end(); it++){
|
||||
if (it->getLength()){
|
||||
afrtrun.firstFragment = it->getNumber();
|
||||
afrtrun.firstTimestamp = it->getTime();
|
||||
afrtrun.duration = it->getLength();
|
||||
afrt.setFragmentRun(afrtrun, i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ namespace Connector_HTTP {
|
|||
abst.setUpdate(false);
|
||||
abst.setTimeScale(1000);
|
||||
abst.setLive(isLive);
|
||||
abst.setCurrentMediaTime(metadata["lastms"].asInt());
|
||||
abst.setCurrentMediaTime(trackMeta.lastms);
|
||||
abst.setSmpteTimeCodeOffset(0);
|
||||
abst.setMovieIdentifier(streamName);
|
||||
abst.setSegmentRunTable(asrt, 0);
|
||||
|
@ -100,7 +100,7 @@ namespace Connector_HTTP {
|
|||
///\param streamName The name of the stream.
|
||||
///\param metadata The current metadata, used to generate the index.
|
||||
///\return The index file for HTTP Dynamic Streaming.
|
||||
std::string dynamicIndex(std::string & streamName, JSON::Value & metadata){
|
||||
std::string dynamicIndex(std::string & streamName, DTSC::Meta & metadata){
|
||||
if ( !audioTrack){getTracks(metadata);}
|
||||
std::stringstream Result;
|
||||
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl;
|
||||
|
@ -108,27 +108,27 @@ namespace Connector_HTTP {
|
|||
Result << " <id>" << streamName << "</id>" << std::endl;
|
||||
Result << " <mimeType>video/mp4</mimeType>" << std::endl;
|
||||
Result << " <deliveryType>streaming</deliveryType>" << std::endl;
|
||||
if (metadata.isMember("vod")){
|
||||
Result << " <duration>" << metadata["length"].asInt() << ".000</duration>" << std::endl;
|
||||
if (metadata.vod){
|
||||
Result << " <duration>" << metadata.tracks[*videoTracks.begin()].lastms / 1000 << ".000</duration>" << std::endl;
|
||||
Result << " <streamType>recorded</streamType>" << std::endl;
|
||||
}else{
|
||||
Result << " <duration>0.00</duration>" << std::endl;
|
||||
Result << " <streamType>live</streamType>" << std::endl;
|
||||
}
|
||||
for (std::set<std::string>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||
for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||
Result << " <bootstrapInfo "
|
||||
"profile=\"named\" "
|
||||
"id=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
|
||||
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << ".abst\">"
|
||||
"id=\"boot" << (*it) << "\" "
|
||||
"url=\"" << (*it) << ".abst\">"
|
||||
"</bootstrapInfo>" << std::endl;
|
||||
}
|
||||
for (std::set<std::string>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||
for (std::set<int>::iterator it = videoTracks.begin(); it != videoTracks.end(); it++){
|
||||
Result << " <media "
|
||||
"url=\"" << metadata["tracks"][(*it)]["trackid"].asInt() << "-\" "
|
||||
"bitrate=\"" << metadata["tracks"][(*it)]["bps"].asInt() * 8 << "\" "
|
||||
"bootstrapInfoId=\"boot" << metadata["tracks"][(*it)]["trackid"].asInt() << "\" "
|
||||
"width=\"" << metadata["tracks"][(*it)]["width"].asInt() << "\" "
|
||||
"height=\"" << metadata["tracks"][(*it)]["height"].asInt() << "\">" << std::endl;
|
||||
"url=\"" << (*it) << "-\" "
|
||||
"bitrate=\"" << metadata.tracks[(*it)].bps * 8 << "\" "
|
||||
"bootstrapInfoId=\"boot" << (*it) << "\" "
|
||||
"width=\"" << metadata.tracks[(*it)].width << "\" "
|
||||
"height=\"" << metadata.tracks[(*it)].height << "\">" << std::endl;
|
||||
Result << " <metadata>AgAKb25NZXRhRGF0YQMAAAk=</metadata>" << std::endl;
|
||||
Result << " </media>" << std::endl;
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ namespace Connector_HTTP {
|
|||
std::string streamID = HTTP_R.url.substr(streamname.size() + 10);
|
||||
streamID = streamID.substr(0, streamID.find(".abst"));
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.getTrackById(atoll(streamID.c_str())),Strm.metadata.isMember("live")));
|
||||
HTTP_S.SetBody(dynamicBootstrap(streamname, Strm.metadata.tracks[atoll(streamID.c_str())], Strm.metadata.live));
|
||||
HTTP_S.SetHeader("Content-Type", "binary/octet");
|
||||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||
HTTP_S.SendResponse("200", "OK", conn);
|
||||
|
@ -202,32 +202,30 @@ namespace Connector_HTTP {
|
|||
printf("Video track %d, segment %d, fragment %d\n", Quality, Segment, ReqFragment);
|
||||
#endif
|
||||
if (!audioTrack){getTracks(Strm.metadata);}
|
||||
JSON::Value & vidTrack = Strm.getTrackById(Quality);
|
||||
DTSC::Track & vidTrack = Strm.metadata.tracks[Quality];
|
||||
mstime = 0;
|
||||
mslen = 0;
|
||||
if (vidTrack.isMember("keys")){
|
||||
for (JSON::ArrIter it = vidTrack["keys"].ArrBegin(); it != vidTrack["keys"].ArrEnd(); it++){
|
||||
if ((*it)["num"].asInt() >= ReqFragment){
|
||||
mstime = (*it)["time"].asInt();
|
||||
mslen = (*it)["len"].asInt();
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (it == vidTrack["keys"].ArrEnd() - 2){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||
HTTP_S.SendResponse("208", "Ask again later", conn);
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
|
||||
if (ss.spool()){
|
||||
while (Strm.parsePacket(ss.Received())){}
|
||||
}
|
||||
for (std::deque<DTSC::Key>::iterator it = vidTrack.keys.begin(); it != vidTrack.keys.end(); it++){
|
||||
if (it->getNumber() >= ReqFragment){
|
||||
mstime = it->getTime();
|
||||
mslen = it->getLength();
|
||||
if (Strm.metadata.live){
|
||||
if (it == vidTrack.keys.end() - 2){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||
HTTP_S.SendResponse("208", "Ask again later", conn);
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment after fragment " << ReqFragment << " not available yet" << std::endl;
|
||||
if (ss.spool()){
|
||||
while (Strm.parsePacket(ss.Received())){}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (Strm.metadata.live){
|
||||
if (mstime == 0 && ReqFragment > 1){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||
|
@ -245,18 +243,18 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||
HTTP_S.StartResponse(HTTP_R, conn);
|
||||
//send the bootstrap
|
||||
std::string bootstrap = dynamicBootstrap(streamname, Strm.getTrackById(Quality), Strm.metadata.isMember("live"), ReqFragment);
|
||||
std::string bootstrap = dynamicBootstrap(streamname, Strm.metadata.tracks[Quality], Strm.metadata.live, ReqFragment);
|
||||
HTTP_S.Chunkify(bootstrap, conn);
|
||||
//send a zero-size mdat, meaning it stretches until end of file.
|
||||
HTTP_S.Chunkify("\000\000\000\000mdat", 8, conn);
|
||||
//send init data, if needed.
|
||||
if (audioTrack > 0 && Strm.getTrackById(audioTrack).isMember("init")){
|
||||
tmp.DTSCAudioInit(Strm.getTrackById(audioTrack));
|
||||
if (audioTrack > 0){
|
||||
tmp.DTSCAudioInit(Strm.metadata.tracks[audioTrack]);
|
||||
tmp.tagTime(mstime);
|
||||
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
||||
}
|
||||
if (Quality > 0 && Strm.getTrackById(Quality).isMember("init")){
|
||||
tmp.DTSCVideoInit(Strm.getTrackById(Quality));
|
||||
if (Quality > 0){
|
||||
tmp.DTSCVideoInit(Strm.metadata.tracks[Quality]);
|
||||
tmp.tagTime(mstime);
|
||||
HTTP_S.Chunkify(tmp.data, tmp.len, conn);
|
||||
}
|
||||
|
|
|
@ -121,12 +121,14 @@ namespace Connector_HTTP {
|
|||
std::stringstream cmd;
|
||||
cmd << "t";
|
||||
|
||||
if (Strm.metadata["tracks"].size()){
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if ( objIt->second["type"].asStringRef() == "meta" ){
|
||||
cmd << " " << objIt->second["trackid"].asInt();
|
||||
int tid = -1;
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (it->second.type == "meta" ){
|
||||
if (tid == -1){
|
||||
tid = it->second.trackID;
|
||||
}
|
||||
}
|
||||
cmd << " " << it->second.trackID;
|
||||
}
|
||||
}
|
||||
|
||||
if( cmd.str() == "t" ){
|
||||
|
@ -134,7 +136,7 @@ namespace Connector_HTTP {
|
|||
cmd.clear();
|
||||
}
|
||||
|
||||
int maxTime = Strm.metadata["lastms"].asInt();
|
||||
int maxTime = Strm.metadata.tracks[tid].lastms;
|
||||
|
||||
cmd << "\ns " << seek_sec << "\np " << maxTime << "\n";
|
||||
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
||||
|
|
|
@ -28,62 +28,65 @@ namespace Connector_HTTP {
|
|||
///\brief Builds an index file for HTTP Live streaming.
|
||||
///\param metadata The current metadata, used to generate the index.
|
||||
///\return The index file for HTTP Live Streaming.
|
||||
std::string liveIndex(JSON::Value & metadata, bool isLive){
|
||||
std::string liveIndex(DTSC::Meta & metadata, bool isLive){
|
||||
std::stringstream result;
|
||||
if (metadata.isMember("tracks")){
|
||||
result << "#EXTM3U\r\n";
|
||||
int audioId = -1;
|
||||
std::string audioName;
|
||||
bool defAudio = false;//set default audio track;
|
||||
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
|
||||
if (trackIt->second["type"].asStringRef() == "audio"){
|
||||
audioId = trackIt->second["trackid"].asInt();
|
||||
audioName = trackIt->first;
|
||||
break;
|
||||
}
|
||||
result << "#EXTM3U\r\n";
|
||||
int audioId = -1;
|
||||
std::string audioName;
|
||||
bool defAudio = false;//set default audio track;
|
||||
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||
if (it->second.type == "audio"){
|
||||
audioId = it->first;
|
||||
audioName = it->second.getIdentifier();
|
||||
break;
|
||||
}
|
||||
for (JSON::ObjIter trackIt = metadata["tracks"].ObjBegin(); trackIt != metadata["tracks"].ObjEnd(); trackIt++){
|
||||
if (trackIt->second["type"].asStringRef() == "video"){
|
||||
int bWidth = trackIt->second["maxbps"].asInt();
|
||||
if (audioId != -1){
|
||||
bWidth += (metadata["tracks"][audioName]["maxbps"].asInt() * 2);
|
||||
}
|
||||
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
|
||||
result << trackIt->second["trackid"].asInt();
|
||||
if (audioId != -1){
|
||||
result << "_" << audioId;
|
||||
}
|
||||
result << "/index.m3u8\r\n";
|
||||
}
|
||||
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||
if (it->second.type == "video"){
|
||||
int bWidth = it->second.bps * 2;
|
||||
if (audioId != -1){
|
||||
bWidth += metadata.tracks[audioId].bps * 2;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//parse single track
|
||||
int longestFragment = 0;
|
||||
for (JSON::ArrIter ai = metadata["frags"].ArrBegin(); ai != metadata["frags"].ArrEnd(); ai++){
|
||||
if ((*ai)["dur"].asInt() > longestFragment){
|
||||
longestFragment = (*ai)["dur"].asInt();
|
||||
result << "#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" << bWidth * 10 << "\r\n";
|
||||
result << it->first;
|
||||
if (audioId != -1){
|
||||
result << "_" << audioId;
|
||||
}
|
||||
result << "/index.m3u8\r\n";
|
||||
}
|
||||
result << "#EXTM3U\r\n"
|
||||
"#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:" << metadata["missed_frags"].asInt() << "\r\n";
|
||||
for (JSON::ArrIter ai = metadata["frags"].ArrBegin(); ai != metadata["frags"].ArrEnd(); ai++){
|
||||
long long int starttime = 0;
|
||||
JSON::ArrIter fi = metadata["keys"].ArrBegin();
|
||||
while (fi != metadata["keys"].ArrEnd() && (*fi)["num"].asInt() < (*ai)["num"].asInt()){
|
||||
fi++;
|
||||
}
|
||||
if (fi != metadata["keys"].ArrEnd()){
|
||||
starttime = (*fi)["time"].asInt();
|
||||
}
|
||||
|
||||
result << "#EXTINF:" << (((*ai)["dur"].asInt() + 500) / 1000) << ", no desc\r\n"
|
||||
<< starttime << "_" << (*ai)["dur"].asInt() + starttime << ".ts\r\n";
|
||||
}
|
||||
if ( !isLive){
|
||||
result << "#EXT-X-ENDLIST\r\n";
|
||||
}
|
||||
#if DEBUG >= 8
|
||||
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
||||
#endif
|
||||
return result.str();
|
||||
}
|
||||
|
||||
std::string liveIndex(DTSC::Track & metadata, bool isLive){
|
||||
std::stringstream result;
|
||||
//parse single track
|
||||
int longestFragment = 0;
|
||||
for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); (it + 1) != metadata.fragments.end(); it++){
|
||||
if (it->getDuration() > longestFragment){
|
||||
longestFragment = it->getDuration();
|
||||
}
|
||||
}
|
||||
result << "#EXTM3U\r\n"
|
||||
"#EXT-X-TARGETDURATION:" << (longestFragment / 1000) + 1 << "\r\n"
|
||||
"#EXT-X-MEDIA-SEQUENCE:" << metadata.missedFrags << "\r\n";
|
||||
for (std::deque<DTSC::Fragment>::iterator it = metadata.fragments.begin(); it != metadata.fragments.end(); it++){
|
||||
long long int starttime = metadata.getKey(it->getNumber()).getTime();
|
||||
|
||||
if (it != (metadata.fragments.end() - 1)){
|
||||
result << "#EXTINF:" << ((it->getDuration() + 500) / 1000) << ", no desc\r\n"
|
||||
<< starttime << "_" << it->getDuration() + starttime << ".ts\r\n";
|
||||
}else{
|
||||
result << "#EXTINF:" << ((metadata.lastms - starttime + 500) / 1000) << ", no desc\r\n"
|
||||
<< starttime << "_" << metadata.lastms << ".ts\r\n";
|
||||
}
|
||||
}
|
||||
if ( !isLive){
|
||||
result << "#EXT-X-ENDLIST\r\n";
|
||||
}
|
||||
#if DEBUG >= 8
|
||||
std::cerr << "Sending this index:" << std::endl << Result.str() << std::endl;
|
||||
#endif
|
||||
|
@ -161,7 +164,7 @@ namespace Connector_HTTP {
|
|||
lastVid = Segment * 90;
|
||||
temp = HTTP_R.url.find("_", temp) + 1;
|
||||
int frameCount = atoi(HTTP_R.url.substr(temp, HTTP_R.url.find(".ts", temp) - temp).c_str());
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (Strm.metadata.live){
|
||||
int seekable = Strm.canSeekms(Segment);
|
||||
if (seekable < 0){
|
||||
HTTP_S.Clean();
|
||||
|
@ -207,10 +210,10 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetHeader("Cache-Control", "no-cache");
|
||||
std::string manifest;
|
||||
if (request.find("/") == std::string::npos){
|
||||
manifest = liveIndex(Strm.metadata, Strm.metadata.isMember("live"));
|
||||
manifest = liveIndex(Strm.metadata, Strm.metadata.live);
|
||||
}else{
|
||||
int selectId = atoi(request.substr(0,request.find("/")).c_str());
|
||||
manifest = liveIndex(Strm.getTrackById(selectId), Strm.metadata.isMember("live"));
|
||||
manifest = liveIndex(Strm.metadata.tracks[selectId], Strm.metadata.live);
|
||||
}
|
||||
HTTP_S.SetBody(manifest);
|
||||
conn.SendNow(HTTP_S.BuildResponse("200", "OK"));
|
||||
|
@ -235,7 +238,7 @@ namespace Connector_HTTP {
|
|||
handlingRequest = false;
|
||||
}
|
||||
if ( !haveAvcc){
|
||||
avccbox.setPayload(Strm.getTrackById(trackID)["init"].asString());
|
||||
avccbox.setPayload(Strm.metadata.tracks[trackID].init);
|
||||
haveAvcc = true;
|
||||
}
|
||||
if (Strm.lastType() == DTSC::VIDEO || Strm.lastType() == DTSC::AUDIO){
|
||||
|
@ -269,10 +272,10 @@ namespace Connector_HTTP {
|
|||
}
|
||||
}
|
||||
ToPack.prepend(TS::Packet::getPESVideoLeadIn(0ul, Strm.getPacket()["time"].asInt() * 90));
|
||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||
ContCounter = &VideoCounter;
|
||||
}else if (Strm.lastType() == DTSC::AUDIO){
|
||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.getTrackById(audioTrackID)["init"].asString()));
|
||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[audioTrackID].init));
|
||||
ToPack.append(Strm.lastData());
|
||||
if (AppleCompat){
|
||||
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), lastVid));
|
||||
|
|
|
@ -101,19 +101,19 @@ namespace Connector_HTTP {
|
|||
}
|
||||
Strm.waitForMeta(ss);
|
||||
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();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (videoID == -1 && it->second.type == "video"){
|
||||
videoID = it->second.trackID;
|
||||
}
|
||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
if (audioID == -1 && it->second.type == "audio"){
|
||||
audioID = it->second.trackID;
|
||||
}
|
||||
}
|
||||
if (videoID != -1){
|
||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[videoID].bps;
|
||||
}
|
||||
if (audioID != -1){
|
||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[audioID].bps;
|
||||
}
|
||||
if ( !byterate){byterate = 1;}
|
||||
if (seek_byte){
|
||||
|
@ -146,16 +146,16 @@ namespace Connector_HTTP {
|
|||
conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
|
||||
conn.SendNow(FLV::Header, 13); //write FLV header
|
||||
//write metadata
|
||||
tag.DTSCMetaInit(Strm, Strm.getTrackById(videoID), Strm.getTrackById(audioID));
|
||||
tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]);
|
||||
conn.SendNow(tag.data, tag.len);
|
||||
//write video init data, if needed
|
||||
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
|
||||
tag.DTSCVideoInit(Strm.getTrackById(videoID));
|
||||
if (videoID != -1){
|
||||
tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
|
||||
conn.SendNow(tag.data, tag.len);
|
||||
}
|
||||
//write audio init data, if needed
|
||||
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
|
||||
tag.DTSCAudioInit(Strm.getTrackById(audioID));
|
||||
if (audioID != -1){
|
||||
tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
|
||||
conn.SendNow(tag.data, tag.len);
|
||||
}
|
||||
progressive_has_sent_header = true;
|
||||
|
@ -170,7 +170,7 @@ namespace Connector_HTTP {
|
|||
conn.close();
|
||||
}
|
||||
if (Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){
|
||||
std::string codec = Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["codec"].asString();
|
||||
std::string codec = Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].codec;
|
||||
if (codec == "AAC" || codec == "MP3" || codec == "H264" || codec == "H263" || codec == "VP6"){
|
||||
tag.DTSCLoader(Strm);
|
||||
conn.SendNow(tag.data, tag.len); //write the tag contents
|
||||
|
|
|
@ -96,13 +96,13 @@ namespace Connector_HTTP {
|
|||
}
|
||||
Strm.waitForMeta(ss);
|
||||
int byterate = 0;
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (audioID == -1 && objIt->second["codec"].asString() == "MP3"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (audioID == -1 && it->second.codec == "MP3"){
|
||||
audioID = it->second.trackID;
|
||||
}
|
||||
}
|
||||
if (audioID != -1){
|
||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[audioID].bps;
|
||||
}
|
||||
if ( !byterate){byterate = 1;}
|
||||
if (seek_byte){
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetHeader("Content-Type", "video/MP4"); //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
|
||||
conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata));//SENDING MP4HEADER
|
||||
conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata.toJSON()));//SENDING MP4HEADER
|
||||
keyPartIt = Conv.keyParts.begin();
|
||||
{//using scope to have cmd not declared after action
|
||||
std::stringstream cmd;
|
||||
|
@ -127,19 +127,19 @@ namespace Connector_HTTP {
|
|||
}
|
||||
}
|
||||
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();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (videoID == -1 && it->second.type == "video"){
|
||||
videoID = it->second.trackID;
|
||||
}
|
||||
if (audioID == -1 && objIt->second["type"].asString() == "audio"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
if (audioID == -1 && it->second.type == "audio"){
|
||||
audioID = it->second.trackID;
|
||||
}
|
||||
}
|
||||
if (videoID != -1){
|
||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[videoID].bps;
|
||||
}
|
||||
if (audioID != -1){
|
||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[audioID].bps;
|
||||
}
|
||||
if ( !byterate){byterate = 1;}
|
||||
seek_sec = (seek_byte / byterate) * 1000;
|
||||
|
|
|
@ -112,19 +112,19 @@ namespace Connector_HTTP {
|
|||
}
|
||||
Strm.waitForMeta(ss);
|
||||
int byterate = 0;
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (videoID == -1 && objIt->second["codec"].asString() == "theora"){
|
||||
videoID = objIt->second["trackid"].asInt();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (videoID == -1 && it->second.codec == "theora"){
|
||||
videoID = it->second.trackID;
|
||||
}
|
||||
if (audioID == -1 && objIt->second["codec"].asString() == "vorbis"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
if (audioID == -1 && it->second.codec == "vorbis"){
|
||||
audioID = it->second.trackID;
|
||||
}
|
||||
}
|
||||
if (videoID != -1){
|
||||
byterate += Strm.getTrackById(videoID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[videoID].bps;
|
||||
}
|
||||
if (audioID != -1){
|
||||
byterate += Strm.getTrackById(audioID)["bps"].asInt();
|
||||
byterate += Strm.metadata.tracks[audioID].bps;
|
||||
}
|
||||
if ( !byterate){byterate = 1;}
|
||||
if (seek_byte){
|
||||
|
@ -163,7 +163,7 @@ namespace Connector_HTTP {
|
|||
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 ogg header here
|
||||
oggMeta.readDTSCHeader(Strm.metadata);
|
||||
oggMeta.readDTSCHeader(Strm.metadata.toJSON());
|
||||
conn.SendNow((char*)oggMeta.parsedPages.c_str(), oggMeta.parsedPages.size());
|
||||
progressive_has_sent_header = true;
|
||||
//setting sendReady to not ready
|
||||
|
|
|
@ -30,42 +30,42 @@ namespace Connector_HTTP {
|
|||
///\brief Builds an index file for HTTP Smooth streaming.
|
||||
///\param metadata The current metadata, used to generate the index.
|
||||
///\return The index file for HTTP Smooth Streaming.
|
||||
std::string smoothIndex(JSON::Value & metadata){
|
||||
std::string smoothIndex(DTSC::Meta & metadata){
|
||||
std::stringstream Result;
|
||||
Result << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||
Result << "<SmoothStreamingMedia "
|
||||
"MajorVersion=\"2\" "
|
||||
"MinorVersion=\"0\" "
|
||||
"TimeScale=\"10000000\" ";
|
||||
if (metadata.isMember("vod")){
|
||||
Result << "Duration=\"" << metadata["lastms"].asInt() << "0000\"";
|
||||
}else{
|
||||
Result << "Duration=\"0\" "
|
||||
"IsLive=\"TRUE\" "
|
||||
"LookAheadFragmentCount=\"2\" "
|
||||
"DVRWindowLength=\"" + metadata["buffer_window"].asString() + "0000\" "
|
||||
"CanSeek=\"TRUE\" "
|
||||
"CanPause=\"TRUE\" ";
|
||||
}
|
||||
Result << ">\n";
|
||||
JSON::Value allAudio;
|
||||
JSON::Value allVideo;
|
||||
std::map<int,DTSC::Track> allAudio;
|
||||
std::map<int,DTSC::Track> allVideo;
|
||||
long long int maxWidth = 0;
|
||||
long long int maxHeight = 0;
|
||||
long long int minWidth = 99999999;
|
||||
long long int minHeight = 99999999;
|
||||
for (JSON::ObjIter oIt = metadata["tracks"].ObjBegin(); oIt != metadata["tracks"].ObjEnd(); oIt++){
|
||||
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
|
||||
allAudio[oIt->first] = oIt->second;
|
||||
for (std::map<int,DTSC::Track>::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++){
|
||||
if (it->second.type == "audio" && it->second.codec == "AAC"){
|
||||
allAudio.insert(*it);
|
||||
}
|
||||
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
|
||||
allVideo[oIt->first] = oIt->second;
|
||||
if (oIt->second["width"].asInt() > maxWidth){maxWidth = oIt->second["width"].asInt();}
|
||||
if (oIt->second["width"].asInt() < minWidth){minWidth = oIt->second["width"].asInt();}
|
||||
if (oIt->second["height"].asInt() > maxHeight){maxHeight = oIt->second["height"].asInt();}
|
||||
if (oIt->second["height"].asInt() < minHeight){minHeight = oIt->second["height"].asInt();}
|
||||
if (it->second.type == "video" && it->second.codec == "H264"){
|
||||
allVideo.insert(*it);
|
||||
if (it->second.width > maxWidth){maxWidth = it->second.width;}
|
||||
if (it->second.width < minWidth){minWidth = it->second.width;}
|
||||
if (it->second.height > maxHeight){maxHeight = it->second.height;}
|
||||
if (it->second.height < minHeight){minHeight = it->second.height;}
|
||||
}
|
||||
}
|
||||
if (metadata.vod){
|
||||
Result << "Duration=\"" << metadata.tracks[allVideo.begin()->first].lastms << "0000\"";
|
||||
}else{
|
||||
Result << "Duration=\"0\" "
|
||||
"IsLive=\"TRUE\" "
|
||||
"LookAheadFragmentCount=\"2\" "
|
||||
"DVRWindowLength=\"" << metadata.bufferWindow << "0000\" "
|
||||
"CanSeek=\"TRUE\" "
|
||||
"CanPause=\"TRUE\" ";
|
||||
}
|
||||
Result << ">\n";
|
||||
|
||||
//Add audio entries
|
||||
if (allAudio.size()){
|
||||
|
@ -73,36 +73,36 @@ namespace Connector_HTTP {
|
|||
"Type=\"audio\" "
|
||||
"QualityLevels=\"" << allAudio.size() << "\" "
|
||||
"Name=\"audio\" "
|
||||
"Chunks=\"" << allAudio.ObjBegin()->second["keys"].size() << "\" "
|
||||
"Chunks=\"" << allAudio.begin()->second.keys.size() << "\" "
|
||||
"Url=\"Q({bitrate},{CustomAttributes})/A({start time})\">\n";
|
||||
int index = 0;
|
||||
for (JSON::ObjIter oIt = allAudio.ObjBegin(); oIt != allAudio.ObjEnd(); oIt++){
|
||||
for (std::map<int,DTSC::Track>::iterator it = allAudio.begin(); it != allAudio.end(); it++){
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
|
||||
"Bitrate=\"" << it->second.bps * 8 << "\" "
|
||||
"CodecPrivateData=\"" << std::hex;
|
||||
for (int i = 0; i < oIt->second["init"].asString().size(); i++){
|
||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)oIt->second["init"].asString()[i];
|
||||
for (int i = 0; i < it->second.init.size(); i++){
|
||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)it->second.init[i];
|
||||
}
|
||||
Result << std::dec << "\" "
|
||||
"SamplingRate=\"" << oIt->second["rate"].asInt() << "\" "
|
||||
"SamplingRate=\"" << it->second.rate << "\" "
|
||||
"Channels=\"2\" "
|
||||
"BitsPerSample=\"16\" "
|
||||
"PacketSize=\"4\" "
|
||||
"AudioTag=\"255\" "
|
||||
"FourCC=\"AACL\" >\n";
|
||||
Result << "<CustomAttributes>\n"
|
||||
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
|
||||
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
|
||||
"</CustomAttributes>";
|
||||
Result << "</QualityLevel>\n";
|
||||
index++;
|
||||
}
|
||||
for (JSON::ArrIter keyIt = allAudio.ObjBegin()->second["keys"].ArrBegin(); keyIt != ((allAudio.ObjBegin()->second["keys"].ArrEnd()) - 1); keyIt++){
|
||||
for (std::deque<DTSC::Key>::iterator it = allAudio.begin()->second.keys.begin(); it != ((allAudio.begin()->second.keys.end()) - 1); it++){
|
||||
Result << "<c ";
|
||||
if (keyIt == allAudio.ObjBegin()->second["keys"].ArrBegin()){
|
||||
Result << "t=\"" << allAudio.ObjBegin()->second["firstms"].asInt() * 10000 << "\" ";
|
||||
if (it == allAudio.begin()->second.keys.begin()){
|
||||
Result << "t=\"" << it->getTime() * 10000 << "\" ";
|
||||
}
|
||||
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
|
||||
Result << "d=\"" << it->getLength() * 10000 << "\" />\n";
|
||||
}
|
||||
Result << "</StreamIndex>\n";
|
||||
}
|
||||
|
@ -112,41 +112,41 @@ namespace Connector_HTTP {
|
|||
"Type=\"video\" "
|
||||
"QualityLevels=\"" << allVideo.size() << "\" "
|
||||
"Name=\"video\" "
|
||||
"Chunks=\"" << allVideo.ObjBegin()->second["keys"].size() << "\" "
|
||||
"Chunks=\"" << allVideo.begin()->second.keys.size() << "\" "
|
||||
"Url=\"Q({bitrate},{CustomAttributes})/V({start time})\" "
|
||||
"MaxWidth=\"" << maxWidth << "\" "
|
||||
"MaxHeight=\"" << maxHeight << "\" "
|
||||
"DisplayWidth=\"" << maxWidth << "\" "
|
||||
"DisplayHeight=\"" << maxHeight << "\">\n";
|
||||
int index = 0;
|
||||
for (JSON::ObjIter oIt = allVideo.ObjBegin(); oIt != allVideo.ObjEnd(); oIt++){
|
||||
//Add video qualities
|
||||
for (std::map<int,DTSC::Track>::iterator it = allVideo.begin(); it != allVideo.end(); it++){
|
||||
//Add video qualities
|
||||
Result << "<QualityLevel "
|
||||
"Index=\"" << index << "\" "
|
||||
"Bitrate=\"" << oIt->second["bps"].asInt() * 8 << "\" "
|
||||
"Bitrate=\"" << it->second.bps * 8 << "\" "
|
||||
"CodecPrivateData=\"" << std::hex;
|
||||
MP4::AVCC avccbox;
|
||||
avccbox.setPayload(oIt->second["init"].asString());
|
||||
avccbox.setPayload(it->second.init);
|
||||
std::string tmpString = avccbox.asAnnexB();
|
||||
for (int i = 0; i < tmpString.size(); i++){
|
||||
Result << std::setfill('0') << std::setw(2) << std::right << (int)tmpString[i];
|
||||
}
|
||||
Result << std::dec << "\" "
|
||||
"MaxWidth=\"" << oIt->second["width"].asInt() << "\" "
|
||||
"MaxHeight=\"" << oIt->second["height"].asInt() << "\" "
|
||||
"MaxWidth=\"" << it->second.width << "\" "
|
||||
"MaxHeight=\"" << it->second.height << "\" "
|
||||
"FourCC=\"AVC1\" >\n";
|
||||
Result << "<CustomAttributes>\n"
|
||||
"<Attribute Name = \"TrackID\" Value = \"" << oIt->second["trackid"].asString() << "\" />"
|
||||
"<Attribute Name = \"TrackID\" Value = \"" << it->first << "\" />"
|
||||
"</CustomAttributes>";
|
||||
Result << "</QualityLevel>\n";
|
||||
index++;
|
||||
}
|
||||
for (JSON::ArrIter keyIt = allVideo.ObjBegin()->second["keys"].ArrBegin(); keyIt != ((allVideo.ObjBegin()->second["keys"].ArrEnd()) - 1); keyIt++){
|
||||
for (std::deque<DTSC::Key>::iterator it = allVideo.begin()->second.keys.begin(); it != ((allVideo.begin()->second.keys.end()) - 1); it++){
|
||||
Result << "<c ";
|
||||
if (keyIt == allVideo.ObjBegin()->second["keys"].ArrBegin()){
|
||||
Result << "t=\"" << (*keyIt)["time"].asInt() * 10000 << "\" ";
|
||||
if (it == allVideo.begin()->second.keys.begin()){
|
||||
Result << "t=\"" << it->getTime() * 10000 << "\" ";
|
||||
}
|
||||
Result << "d=\"" << (*keyIt)["len"].asInt() * 10000 << "\" />\n";
|
||||
Result << "d=\"" << it->getLength() * 10000 << "\" />\n";
|
||||
}
|
||||
Result << "</StreamIndex>\n";
|
||||
}
|
||||
|
@ -183,8 +183,8 @@ namespace Connector_HTTP {
|
|||
unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
|
||||
conn.setBlocking(false);//Set the client socket to non-blocking
|
||||
|
||||
JSON::Value allAudio;
|
||||
JSON::Value allVideo;
|
||||
std::map<int,DTSC::Track> allAudio;
|
||||
std::map<int,DTSC::Track> allVideo;
|
||||
|
||||
while (conn.connected()){
|
||||
if ( !handlingRequest){
|
||||
|
@ -211,12 +211,12 @@ namespace Connector_HTTP {
|
|||
}
|
||||
ss.setBlocking(false);
|
||||
Strm.waitForMeta(ss);
|
||||
for (JSON::ObjIter oIt = Strm.metadata["tracks"].ObjBegin(); oIt != Strm.metadata["tracks"].ObjEnd(); oIt++){
|
||||
if (oIt->second["type"].asString() == "audio" && oIt->second["codec"].asString() == "AAC"){
|
||||
allAudio[oIt->first] = oIt->second;
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (it->second.type == "audio" && it->second.codec == "AAC"){
|
||||
allAudio[it->first] = it->second;
|
||||
}
|
||||
if (oIt->second["type"].asString() == "video" && oIt->second["codec"].asString() == "H264"){
|
||||
allVideo[oIt->first] = oIt->second;
|
||||
if (it->second.type == "video" && it->second.codec == "H264"){
|
||||
allVideo[it->first] = it->second;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -250,14 +250,14 @@ namespace Connector_HTTP {
|
|||
parseString = parseString.substr(parseString.find("(") + 1);
|
||||
requestedTime = atoll(parseString.substr(0, parseString.find(")")).c_str());
|
||||
long long int selectedQuality = atoll(Quality.c_str());
|
||||
JSON::Value & myRef = Strm.getTrackById(selectedQuality);
|
||||
if (Strm.metadata.isMember("live")){
|
||||
DTSC::Track & myRef = Strm.metadata.tracks[selectedQuality];
|
||||
if (Strm.metadata.live){
|
||||
int seekable = Strm.canSeekms(requestedTime / 10000);
|
||||
if (seekable == 0){
|
||||
// iff the fragment in question is available, check if the next is available too
|
||||
for (JSON::ArrIter aIt = myRef["keys"].ArrBegin(); aIt != myRef["keys"].ArrEnd(); aIt++){
|
||||
if ((*aIt)["time"].asInt() >= (requestedTime / 10000)){
|
||||
if ((aIt + 1) == myRef["keys"].ArrEnd()){
|
||||
for (std::deque<DTSC::Key>::iterator it = myRef.keys.begin(); it != myRef.keys.end(); it++){
|
||||
if (it->getTime() >= (requestedTime / 10000)){
|
||||
if ((it + 1) == myRef.keys.end()){
|
||||
seekable = 1;
|
||||
}
|
||||
break;
|
||||
|
@ -269,7 +269,7 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||
conn.SendNow(HTTP_S.BuildResponse("412", "Fragment out of range"));
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << myRef["keys"][0u]["time"].asInt() << " - " << myRef["keys"][myRef["keys"].size() - 1]["time"].asInt() << " ms)" << std::endl;
|
||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms too old (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (seekable > 0){
|
||||
|
@ -277,7 +277,7 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << myRef["keys"][0u]["time"].asInt() << " - " << myRef["keys"][myRef["keys"].size() - 1]["time"].asInt() << " ms)" << std::endl;
|
||||
std::cout << "Fragment @ " << requestedTime / 10000 << "ms not available yet (" << myRef.keys.begin()->getTime() << " - " << myRef.keys.rbegin()->getTime() << " ms)" << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -286,26 +286,24 @@ namespace Connector_HTTP {
|
|||
|
||||
long long mstime = 0;
|
||||
long long mslen = 0;
|
||||
if (myRef.isMember("keys")){
|
||||
for (JSON::ArrIter it = myRef["keys"].ArrBegin(); it != myRef["keys"].ArrEnd(); it++){
|
||||
if ((*it)["time"].asInt() >= (requestedTime / 10000)){
|
||||
mstime = (*it)["time"].asInt();
|
||||
mslen = (*it)["len"].asInt();
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (it == myRef["keys"].ArrEnd() - 2){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
|
||||
}
|
||||
for (std::deque<DTSC::Key>::iterator it = myRef.keys.end(); it != myRef.keys.end(); it++){
|
||||
if (it->getTime() >= (requestedTime / 10000)){
|
||||
mstime = it->getTime();
|
||||
mslen = it->getLength();
|
||||
if (Strm.metadata.live){
|
||||
if (it == myRef.keys.end() - 2){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("Proxy, re-request this in a second or two.\n");
|
||||
conn.SendNow(HTTP_S.BuildResponse("208", "Ask again later"));
|
||||
HTTP_R.Clean(); //clean for any possible next requests
|
||||
std::cout << "Fragment after fragment @ " << (requestedTime / 10000) << " not available yet" << std::endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (HTTP_R.url == "/"){continue;}//Don't continue, but continue instead.
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (Strm.metadata.live){
|
||||
if (mstime == 0 && (requestedTime / 10000) > 1){
|
||||
HTTP_S.Clean();
|
||||
HTTP_S.SetBody("The requested fragment is no longer kept in memory on the server and cannot be served.\n");
|
||||
|
@ -317,73 +315,83 @@ namespace Connector_HTTP {
|
|||
}
|
||||
|
||||
//Obtain the corresponding track;
|
||||
JSON::Value trackRef;
|
||||
DTSC::Track trackRef;
|
||||
if (wantsVideo){
|
||||
trackRef = allVideo.ObjBegin()->second;
|
||||
trackRef = allVideo.begin()->second;
|
||||
}
|
||||
if (wantsAudio){
|
||||
trackRef = allAudio.ObjBegin()->second;
|
||||
trackRef = allAudio.begin()->second;
|
||||
}
|
||||
//Also obtain the associated keyframe;
|
||||
JSON::Value keyObj;
|
||||
for (JSON::ArrIter keyIt = trackRef["keys"].ArrBegin(); keyIt != trackRef["keys"].ArrEnd(); keyIt++){
|
||||
if ((*keyIt)["time"].asInt() >= (requestedTime / 10000)){
|
||||
DTSC::Key keyObj;
|
||||
int partOffset = 0;
|
||||
int keyDur = 0;
|
||||
for (std::deque<DTSC::Key>::iterator keyIt = trackRef.keys.begin(); keyIt != trackRef.keys.end(); keyIt++){
|
||||
if (keyIt->getTime() >= (requestedTime / 10000)){
|
||||
keyObj = (*keyIt);
|
||||
std::deque<DTSC::Key>::iterator nextIt = keyIt;
|
||||
nextIt++;
|
||||
if (nextIt != trackRef.keys.end()){
|
||||
keyDur = nextIt->getTime() - keyIt->getTime();
|
||||
}else{
|
||||
keyDur = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
partOffset += keyIt->getParts();
|
||||
}
|
||||
|
||||
sstream << "t " << myRef["trackid"].asInt() << "\n";
|
||||
sstream << "s " << keyObj["time"].asInt() << "\n";
|
||||
sstream << "p " << keyObj["time"].asInt() + keyObj["len"].asInt() << "\n";
|
||||
// sstream << "o\n";
|
||||
sstream << "t " << myRef.trackID << "\n";
|
||||
sstream << "s " << keyObj.getTime() << "\n";
|
||||
if (keyDur != -1){
|
||||
sstream << "p " << keyObj.getTime() + keyDur << "\n";
|
||||
}else{
|
||||
sstream << "p\n";
|
||||
}
|
||||
|
||||
ss.SendNow(sstream.str().c_str());
|
||||
std::cerr << "[[]]Requested " << sstream.str() << std::endl;
|
||||
|
||||
unsigned int myDuration;
|
||||
|
||||
//Wrap everything in mp4 boxes
|
||||
MP4::MFHD mfhd_box;
|
||||
mfhd_box.setSequenceNumber(keyObj["num"].asInt());
|
||||
myDuration = keyObj["len"].asInt() * 10000;
|
||||
mfhd_box.setSequenceNumber(keyObj.getNumber());
|
||||
myDuration = keyObj.getLength() * 10000;
|
||||
|
||||
MP4::TFHD tfhd_box;
|
||||
tfhd_box.setFlags(MP4::tfhdSampleFlag);
|
||||
tfhd_box.setTrackID(1);
|
||||
//if (trackRef["type"].asString() == "video"){
|
||||
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
||||
//}else{
|
||||
// tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noKeySample);
|
||||
//}
|
||||
tfhd_box.setDefaultSampleFlags(0x000000C0 | MP4::noIPicture | MP4::noDisposable | MP4::noKeySample);
|
||||
|
||||
MP4::TRUN trun_box;
|
||||
trun_box.setDataOffset(42);
|
||||
if (trackRef["type"].asString() == "video"){
|
||||
int keySize = 0;
|
||||
if (trackRef.type == "video"){
|
||||
trun_box.setFlags(MP4::trundataOffset | MP4::trunfirstSampleFlags | MP4::trunsampleDuration | MP4::trunsampleSize);
|
||||
}else{
|
||||
trun_box.setFlags(MP4::trundataOffset | MP4::trunsampleDuration | MP4::trunsampleSize);
|
||||
// trun_box.setFirstSampleFlags(0x00000040 | MP4::noKeySample);
|
||||
}
|
||||
trun_box.setFirstSampleFlags(0x00000040 | MP4::isIPicture | MP4::noDisposable | MP4::isKeySample);
|
||||
std::deque<long long int> tmpParts;
|
||||
JSON::decodeVector(keyObj["parts"].asString(), tmpParts);
|
||||
for (int i = 0; i < tmpParts.size(); i++){
|
||||
for (int i = 0; i < keyObj.getParts(); i++){
|
||||
MP4::trunSampleInformation trunSample;
|
||||
trunSample.sampleSize = tmpParts[i];
|
||||
trunSample.sampleSize = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize();
|
||||
keySize += Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getSize();
|
||||
//Guesstimate sample duration.
|
||||
trunSample.sampleDuration = ((double)(keyObj["len"].asInt() * 10000) / tmpParts.size());
|
||||
trunSample.sampleDuration = Strm.metadata.tracks[myRef.trackID].parts[i + partOffset].getDuration() * 10000;
|
||||
trun_box.setSampleInformation(trunSample, i);
|
||||
}
|
||||
|
||||
MP4::SDTP sdtp_box;
|
||||
sdtp_box.setVersion(0);
|
||||
if (trackRef["type"].asString() == "video"){
|
||||
if (trackRef.type == "video"){
|
||||
sdtp_box.setValue(36, 4);
|
||||
for (int i = 1; i < keyObj["partsize"].asInt(); i++){
|
||||
for (int i = 1; i < keyObj.getParts(); i++){
|
||||
sdtp_box.setValue(20, 4 + i);
|
||||
}
|
||||
}else{
|
||||
sdtp_box.setValue(40, 4);
|
||||
for (int i = 1; i < keyObj["partsize"].asInt(); i++){
|
||||
for (int i = 1; i < keyObj.getParts(); i++){
|
||||
sdtp_box.setValue(40, 4 + i);
|
||||
}
|
||||
}
|
||||
|
@ -394,22 +402,24 @@ namespace Connector_HTTP {
|
|||
traf_box.setContent(sdtp_box, 2);
|
||||
|
||||
//If the stream is live, we want to have a fragref box if possible
|
||||
if (Strm.metadata.isMember("live")){
|
||||
if (Strm.metadata.live){
|
||||
///\todo Fix this for live
|
||||
MP4::UUID_TrackFragmentReference fragref_box;
|
||||
fragref_box.setVersion(1);
|
||||
fragref_box.setFragmentCount(0);
|
||||
int fragCount = 0;
|
||||
for (int i = 0; i < 2 && i < trackRef["keys"].size() - 1; i++){//< trackRef["keys"].size() - 1; i++){
|
||||
if (trackRef["keys"][i]["time"].asInt() > (requestedTime / 10000)){
|
||||
fragref_box.setTime(fragCount, trackRef["keys"][i]["time"].asInt() * 10000);
|
||||
fragref_box.setDuration(fragCount, trackRef["keys"][i]["len"].asInt() * 10000);
|
||||
for (int i = 0; i < 2 && i < trackRef.keys.size() - 1; i++){//< trackRef["keys"].size() - 1; i++){
|
||||
if (trackRef.keys[i].getTime() > (requestedTime / 10000)){
|
||||
fragref_box.setTime(fragCount, trackRef.keys[i].getTime() * 10000);
|
||||
fragref_box.setDuration(fragCount, trackRef.keys[i].getLength() * 10000);
|
||||
fragref_box.setFragmentCount(++fragCount);
|
||||
}
|
||||
}
|
||||
traf_box.setContent(fragref_box, 3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
MP4::MOOF moof_box;
|
||||
moof_box.setContent(mfhd_box, 0);
|
||||
moof_box.setContent(traf_box, 1);
|
||||
|
@ -423,7 +433,7 @@ namespace Connector_HTTP {
|
|||
HTTP_S.SetHeader("Content-Type", "video/mp4");
|
||||
HTTP_S.StartResponse(HTTP_R, conn);
|
||||
HTTP_S.Chunkify(moof_box.asBox(), moof_box.boxedSize(), conn);
|
||||
int size = htonl(keyObj["size"].asInt() + 8);
|
||||
int size = htonl(keySize + 8);
|
||||
HTTP_S.Chunkify((char*)&size, 4, conn);
|
||||
HTTP_S.Chunkify("mdat", 4, conn);
|
||||
handlingRequest = true;
|
||||
|
@ -465,6 +475,8 @@ namespace Connector_HTTP {
|
|||
handlingRequest = false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
Util::sleep(10);
|
||||
}
|
||||
if ( !ss.connected()){
|
||||
break;
|
||||
|
|
|
@ -107,23 +107,16 @@ namespace Connector_HTTP {
|
|||
|
||||
if(trackID == -1){
|
||||
// no track was given. Fetch the first track that has SRT data
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (objIt->second["codec"].asStringRef() == "srt"){
|
||||
trackID = objIt->second["trackid"].asInt();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (it->second.codec == "srt"){
|
||||
trackID = it->second.trackID;
|
||||
subtitleTrack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// track *was* given, but we have to check whether it's an actual srt track
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (objIt->second["trackid"].asInt() == trackID){
|
||||
subtitleTrack = (objIt->second["codec"].asStringRef() == "srt");
|
||||
break;
|
||||
}else{
|
||||
subtitleTrack = false;
|
||||
}
|
||||
}
|
||||
subtitleTrack = Strm.metadata.tracks[trackID].codec == "srt";
|
||||
}
|
||||
|
||||
if(!subtitleTrack){
|
||||
|
@ -139,7 +132,7 @@ namespace Connector_HTTP {
|
|||
|
||||
cmd << "t " << trackID;
|
||||
|
||||
int maxTime = Strm.metadata["lastms"].asInt();
|
||||
int maxTime = Strm.metadata.tracks[trackID].lastms;
|
||||
|
||||
cmd << "\ns " << seek_time << "\np " << maxTime << "\n";
|
||||
ss.SendNow(cmd.str().c_str(), cmd.str().size());
|
||||
|
|
|
@ -33,14 +33,14 @@ int main(int argc, char ** argv){
|
|||
}
|
||||
long long int lastStats = 0;
|
||||
long long int started = Util::epoch();
|
||||
while (std::cout.good()){
|
||||
while (std::cout.good() && S.connected()){
|
||||
if (S.spool()){
|
||||
while (S.Received().size()){
|
||||
std::cout.write(S.Received().get().c_str(), S.Received().get().size());
|
||||
S.Received().get().clear();
|
||||
}
|
||||
}else{
|
||||
Util::sleep(10); //sleep 10ms if no data
|
||||
Util::sleep(500); //sleep 500ms if no data
|
||||
}
|
||||
unsigned int now = Util::epoch();
|
||||
if (now != lastStats){
|
||||
|
|
|
@ -568,12 +568,12 @@ namespace Connector_RTMP {
|
|||
ss.setBlocking(false);
|
||||
Strm.waitForMeta(ss);
|
||||
//find first audio and video tracks
|
||||
for (JSON::ObjIter objIt = Strm.metadata["tracks"].ObjBegin(); objIt != Strm.metadata["tracks"].ObjEnd(); objIt++){
|
||||
if (videoID == -1 && objIt->second["type"].asStringRef() == "video"){
|
||||
videoID = objIt->second["trackid"].asInt();
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){
|
||||
if (videoID == -1 && it->second.type == "video"){
|
||||
videoID = it->second.trackID;
|
||||
}
|
||||
if (audioID == -1 && objIt->second["type"].asStringRef() == "audio"){
|
||||
audioID = objIt->second["trackid"].asInt();
|
||||
if (audioID == -1 && it->second.type == "audio"){
|
||||
audioID = it->second.trackID;
|
||||
}
|
||||
}
|
||||
//select the tracks and play
|
||||
|
@ -612,7 +612,7 @@ namespace Connector_RTMP {
|
|||
amfreply.getContentP(3)->addContent(AMF::Object("clientid", (double)1337));
|
||||
sendCommand(amfreply, playMessageType, playStreamId);
|
||||
//send streamisrecorded if stream, well, is recorded.
|
||||
if (Strm.metadata.isMember("length") && Strm.metadata["length"].asInt() > 0){
|
||||
if (Strm.metadata.vod){//isMember("length") && Strm.metadata["length"].asInt() > 0){
|
||||
Socket.Send(RTMPStream::SendUSR(4, 1)); //send UCM StreamIsRecorded (4), stream 1
|
||||
}
|
||||
//send streambegin
|
||||
|
@ -638,14 +638,14 @@ namespace Connector_RTMP {
|
|||
|
||||
//sent init data if needed
|
||||
if ( !streamInited){
|
||||
init_tag.DTSCMetaInit(Strm, Strm.getTrackById(videoID), Strm.getTrackById(audioID));
|
||||
init_tag.DTSCMetaInit(Strm, Strm.metadata.tracks[videoID], Strm.metadata.tracks[audioID]);
|
||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||
if (audioID != -1 && Strm.getTrackById(audioID).isMember("init")){
|
||||
init_tag.DTSCAudioInit(Strm.getTrackById(audioID));
|
||||
if (audioID != -1){
|
||||
init_tag.DTSCAudioInit(Strm.metadata.tracks[audioID]);
|
||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||
}
|
||||
if (videoID != -1 && Strm.getTrackById(videoID).isMember("init")){
|
||||
init_tag.DTSCVideoInit(Strm.getTrackById(videoID));
|
||||
if (videoID != -1){
|
||||
init_tag.DTSCVideoInit(Strm.metadata.tracks[videoID]);
|
||||
Socket.SendNow(RTMPStream::SendMedia(init_tag));
|
||||
}
|
||||
streamInited = true;
|
||||
|
|
|
@ -61,36 +61,25 @@ namespace Connector_TS {
|
|||
}
|
||||
|
||||
if(trackIDs == ""){
|
||||
std::stringstream tmpTracks;
|
||||
// no track ids given? Find the first video and first audio track (if available) and use those!
|
||||
int videoID = -1;
|
||||
int audioID = -1;
|
||||
|
||||
Strm.waitForMeta(ss);
|
||||
|
||||
if (Strm.metadata.isMember("tracks")){
|
||||
for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it){
|
||||
if (audioID == -1 && it->second.type == "audio"){
|
||||
audioID = it->first;
|
||||
tmpTracks << " " << it->first;
|
||||
}
|
||||
|
||||
for (JSON::ObjIter trackIt = Strm.metadata["tracks"].ObjBegin(); trackIt != Strm.metadata["tracks"].ObjEnd(); trackIt++){
|
||||
if (videoID == -1 && it->second.type == "video"){
|
||||
videoID = it->first;
|
||||
tmpTracks << " " << it->first;
|
||||
}
|
||||
|
||||
if (audioID == -1 && trackIt->second["type"].asString() == "audio"){
|
||||
audioID = trackIt->second["trackid"].asInt();
|
||||
if( trackIDs != ""){
|
||||
trackIDs += " " + trackIt->second["trackid"].asString();
|
||||
}else{
|
||||
trackIDs = trackIt->second["trackid"].asString();
|
||||
}
|
||||
}
|
||||
|
||||
if (videoID == -1 && trackIt->second["type"].asString() == "video"){
|
||||
videoID = trackIt->second["trackid"].asInt();
|
||||
if( trackIDs != ""){
|
||||
trackIDs += " " + trackIt->second["trackid"].asString();
|
||||
}else{
|
||||
trackIDs = trackIt->second["trackid"].asString();
|
||||
}
|
||||
}
|
||||
|
||||
} // for iterator
|
||||
} // if isMember("tracks")
|
||||
} // for iterator
|
||||
trackIDs += tmpTracks.str();
|
||||
} // if trackIDs == ""
|
||||
|
||||
std::string cmd = "t " + trackIDs + "\ns 0\np\n";
|
||||
|
@ -115,7 +104,7 @@ namespace Connector_TS {
|
|||
char * ContCounter = 0;
|
||||
if (Strm.lastType() == DTSC::VIDEO){
|
||||
if ( !haveAvcc){
|
||||
avccbox.setPayload(Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString());
|
||||
avccbox.setPayload(Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init);
|
||||
haveAvcc = true;
|
||||
}
|
||||
|
||||
|
@ -139,7 +128,7 @@ namespace Connector_TS {
|
|||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||
ContCounter = &VideoCounter;
|
||||
}else if (Strm.lastType() == DTSC::AUDIO){
|
||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.getTrackById(Strm.getPacket()["trackid"].asInt())["init"].asString()));
|
||||
ToPack.append(TS::GetAudioHeader(Strm.lastData().size(), Strm.metadata.tracks[Strm.getPacket()["trackid"].asInt()].init));
|
||||
ToPack.append(Strm.lastData());
|
||||
ToPack.prepend(TS::Packet::getPESAudioLeadIn(ToPack.bytes(1073741824ul), Strm.getPacket()["time"].asInt() * 90));
|
||||
PIDno = 0x100 - 1 + Strm.getPacket()["trackid"].asInt();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue