Metadata upgrades as well as improvements to the buffer in terms of debugging and code maintainability.

This commit is contained in:
Thulinma 2013-11-27 15:30:34 +01:00
parent bb1971e961
commit cde42ce36e
25 changed files with 390 additions and 664 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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