Merge branch 'development' into LTS_development
This commit is contained in:
commit
7f6b919e4f
11 changed files with 211 additions and 228 deletions
|
@ -295,6 +295,7 @@ namespace DTSC {
|
||||||
std::deque<Part> parts;
|
std::deque<Part> parts;
|
||||||
std::deque<Ivec> ivecs; /*LTS*/
|
std::deque<Ivec> ivecs; /*LTS*/
|
||||||
Key & getKey(unsigned int keyNum);
|
Key & getKey(unsigned int keyNum);
|
||||||
|
Fragment & getFrag(unsigned int fragNum);
|
||||||
unsigned int timeToKeynum(unsigned int timestamp);
|
unsigned int timeToKeynum(unsigned int timestamp);
|
||||||
unsigned int timeToFragnum(unsigned int timestamp);
|
unsigned int timeToFragnum(unsigned int timestamp);
|
||||||
void reset();
|
void reset();
|
||||||
|
@ -321,8 +322,11 @@ namespace DTSC {
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
int fpks;
|
int fpks;
|
||||||
|
void removeFirstKey();
|
||||||
|
uint32_t secsSinceFirstFragmentInsert();
|
||||||
private:
|
private:
|
||||||
std::string cachedIdent;
|
std::string cachedIdent;
|
||||||
|
std::deque<uint32_t> fragInsertTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
///\brief Class for storage of meta data
|
///\brief Class for storage of meta data
|
||||||
|
|
|
@ -1233,6 +1233,8 @@ namespace DTSC {
|
||||||
newFrag.setDuration(0);
|
newFrag.setDuration(0);
|
||||||
newFrag.setSize(0);
|
newFrag.setSize(0);
|
||||||
fragments.push_back(newFrag);
|
fragments.push_back(newFrag);
|
||||||
|
//We set the insert time lastms-firstms in the future, to prevent unstable playback
|
||||||
|
fragInsertTime.push_back(Util::bootSecs() + ((lastms - firstms)/1000));
|
||||||
} else {
|
} else {
|
||||||
Fragment & lastFrag = fragments[fragments.size() - 1];
|
Fragment & lastFrag = fragments[fragments.size() - 1];
|
||||||
lastFrag.setLength(lastFrag.getLength() + 1);
|
lastFrag.setLength(lastFrag.getLength() + 1);
|
||||||
|
@ -1242,6 +1244,41 @@ namespace DTSC {
|
||||||
(*keySizes.rbegin()) += packSendSize;
|
(*keySizes.rbegin()) += packSendSize;
|
||||||
fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
|
fragments.rbegin()->setSize(fragments.rbegin()->getSize() + packDataSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Removes the first buffered key, including any fragments it was part of
|
||||||
|
void Track::removeFirstKey(){
|
||||||
|
HIGH_MSG("Erasing key %d:%lu", trackID, keys[0].getNumber());
|
||||||
|
//remove all parts of this key
|
||||||
|
for (int i = 0; i < keys[0].getParts(); i++) {
|
||||||
|
parts.pop_front();
|
||||||
|
}
|
||||||
|
//remove the key itself
|
||||||
|
keys.pop_front();
|
||||||
|
keySizes.pop_front();
|
||||||
|
//update firstms
|
||||||
|
firstms = keys[0].getTime();
|
||||||
|
//delete any fragments no longer fully buffered
|
||||||
|
while (fragments[0].getNumber() < keys[0].getNumber()) {
|
||||||
|
fragments.pop_front();
|
||||||
|
fragInsertTime.pop_front();
|
||||||
|
//and update the missed fragment counter
|
||||||
|
++missedFrags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of whole seconds since the first fragment was inserted into the buffer.
|
||||||
|
/// This assumes playback from the start of the buffer at time of insert, meaning that
|
||||||
|
/// the time is offset by that difference. E.g.: if a buffer is 50s long, the newest fragment
|
||||||
|
/// will have a value of 0 until 50s have passed, after which it will increase at a rate of
|
||||||
|
/// 1 per second.
|
||||||
|
uint32_t Track::secsSinceFirstFragmentInsert(){
|
||||||
|
uint32_t bs = Util::bootSecs();
|
||||||
|
if (bs > fragInsertTime.front()){
|
||||||
|
return bs - fragInsertTime.front();
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Track::finalize(){
|
void Track::finalize(){
|
||||||
keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
|
keys.rbegin()->setLength(lastms - keys.rbegin()->getTime() + parts.rbegin()->getDuration());
|
||||||
|
@ -1270,6 +1307,7 @@ namespace DTSC {
|
||||||
return keys[keyNum - keys[0].getNumber()];
|
return keys[keyNum - keys[0].getNumber()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of the key containing timestamp, or last key if nowhere.
|
||||||
unsigned int Track::timeToKeynum(unsigned int timestamp){
|
unsigned int Track::timeToKeynum(unsigned int timestamp){
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
|
for (std::deque<Key>::iterator it = keys.begin(); it != keys.end(); it++){
|
||||||
|
@ -1281,13 +1319,12 @@ namespace DTSC {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets indice of the fragment containing timestamp, or last fragment if nowhere.
|
||||||
unsigned int Track::timeToFragnum(unsigned int timestamp){
|
unsigned int Track::timeToFragnum(unsigned int timestamp){
|
||||||
unsigned long long int totalTime = firstms;
|
|
||||||
for (unsigned int i = 0; i<fragments.size(); i++){
|
for (unsigned int i = 0; i<fragments.size(); i++){
|
||||||
if (timestamp <= totalTime){
|
if (timestamp <= getKey(fragments[i].getNumber()).getTime() + fragments[i].getDuration()){
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
totalTime += fragments[i].getDuration();
|
|
||||||
}
|
}
|
||||||
return fragments.size()-1;
|
return fragments.size()-1;
|
||||||
}
|
}
|
||||||
|
@ -1295,6 +1332,7 @@ namespace DTSC {
|
||||||
///\brief Resets a track, clears all meta values
|
///\brief Resets a track, clears all meta values
|
||||||
void Track::reset() {
|
void Track::reset() {
|
||||||
fragments.clear();
|
fragments.clear();
|
||||||
|
fragInsertTime.clear();
|
||||||
parts.clear();
|
parts.clear();
|
||||||
keySizes.clear();
|
keySizes.clear();
|
||||||
keys.clear();
|
keys.clear();
|
||||||
|
|
215
lib/flv_tag.cpp
215
lib/flv_tag.cpp
|
@ -518,7 +518,7 @@ void FLV::Tag::setLen() {
|
||||||
data[ --i] = (len4) & 0xFF;
|
data[ --i] = (len4) & 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FLV Video init data loader function from JSON.
|
/// FLV Video init data loader function from metadata.
|
||||||
bool FLV::Tag::DTSCVideoInit(DTSC::Track & video) {
|
bool FLV::Tag::DTSCVideoInit(DTSC::Track & video) {
|
||||||
//Unknown? Assume H264.
|
//Unknown? Assume H264.
|
||||||
len = 0;
|
len = 0;
|
||||||
|
@ -549,7 +549,7 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Track & video) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FLV Audio init data loader function from JSON.
|
/// FLV Audio init data loader function from metadata.
|
||||||
bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio) {
|
bool FLV::Tag::DTSCAudioInit(DTSC::Track & audio) {
|
||||||
len = 0;
|
len = 0;
|
||||||
//Unknown? Assume AAC.
|
//Unknown? Assume AAC.
|
||||||
|
@ -709,6 +709,7 @@ bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk & O) {
|
||||||
data[2] = (O.len >> 8) & 0xFF;
|
data[2] = (O.len >> 8) & 0xFF;
|
||||||
data[1] = (O.len >> 16) & 0xFF;
|
data[1] = (O.len >> 16) & 0xFF;
|
||||||
tagTime(O.timestamp);
|
tagTime(O.timestamp);
|
||||||
|
isKeyframe = ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,11 +795,7 @@ bool FLV::Tag::MemLoader(char * D, unsigned int S, unsigned int & P) {
|
||||||
//read tag body
|
//read tag body
|
||||||
if (MemReadUntil(data, len, sofar, D, S, P)) {
|
if (MemReadUntil(data, len, sofar, D, S, P)) {
|
||||||
//calculate keyframeness, next time read header again, return true
|
//calculate keyframeness, next time read header again, return true
|
||||||
if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)) {
|
isKeyframe = ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1));
|
||||||
isKeyframe = true;
|
|
||||||
} else {
|
|
||||||
isKeyframe = false;
|
|
||||||
}
|
|
||||||
done = true;
|
done = true;
|
||||||
sofar = 0;
|
sofar = 0;
|
||||||
return true;
|
return true;
|
||||||
|
@ -892,11 +889,7 @@ bool FLV::Tag::FileLoader(FILE * f) {
|
||||||
//read tag body
|
//read tag body
|
||||||
if (FileReadUntil(data, len, sofar, f)) {
|
if (FileReadUntil(data, len, sofar, f)) {
|
||||||
//calculate keyframeness, next time read header again, return true
|
//calculate keyframeness, next time read header again, return true
|
||||||
if ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1)) {
|
isKeyframe = ((data[0] == 0x09) && (((data[11] & 0xf0) >> 4) == 1));
|
||||||
isKeyframe = true;
|
|
||||||
} else {
|
|
||||||
isKeyframe = false;
|
|
||||||
}
|
|
||||||
done = true;
|
done = true;
|
||||||
sofar = 0;
|
sofar = 0;
|
||||||
fcntl(fileno(f), F_SETFL, preflags);
|
fcntl(fileno(f), F_SETFL, preflags);
|
||||||
|
@ -944,9 +937,7 @@ unsigned int FLV::Tag::getDataLen(){
|
||||||
return len - 16;
|
return len - 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack) {
|
void FLV::Tag::toMeta(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack){
|
||||||
JSON::Value pack_out; // Storage for outgoing metadata.
|
|
||||||
|
|
||||||
if (!reTrack){
|
if (!reTrack){
|
||||||
switch (data[0]){
|
switch (data[0]){
|
||||||
case 0x09: reTrack = 1; break;//video
|
case 0x09: reTrack = 1; break;//video
|
||||||
|
@ -954,7 +945,6 @@ JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, u
|
||||||
case 0x12: reTrack = 3; break;//meta
|
case 0x12: reTrack = 3; break;//meta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pack_out["trackid"] = reTrack;
|
|
||||||
|
|
||||||
if (data[0] == 0x12) {
|
if (data[0] == 0x12) {
|
||||||
AMF::Object meta_in = AMF::parse((unsigned char *)data + 11, len - 15);
|
AMF::Object meta_in = AMF::parse((unsigned char *)data + 11, len - 15);
|
||||||
|
@ -968,78 +958,56 @@ JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, u
|
||||||
}
|
}
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
amf_storage = *tmp;
|
amf_storage = *tmp;
|
||||||
bool empty = true;
|
|
||||||
for (int i = 0; i < tmp->hasContent(); ++i) {
|
|
||||||
if (tmp->getContentP(i)->Indice() == "videocodecid" || tmp->getContentP(i)->Indice() == "audiocodecid" || tmp->getContentP(i)->Indice() == "width" || tmp->getContentP(i)->Indice() == "height" || tmp->getContentP(i)->Indice() == "videodatarate" || tmp->getContentP(i)->Indice() == "videoframerate" || tmp->getContentP(i)->Indice() == "audiodatarate" || tmp->getContentP(i)->Indice() == "audiosamplerate" || tmp->getContentP(i)->Indice() == "audiosamplesize" || tmp->getContentP(i)->Indice() == "audiochannels") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (tmp->getContentP(i)->NumValue()) {
|
|
||||||
pack_out["data"][tmp->getContentP(i)->Indice()] = (long long)tmp->getContentP(i)->NumValue();
|
|
||||||
empty = false;
|
|
||||||
} else {
|
|
||||||
if (tmp->getContentP(i)->StrValue() != "") {
|
|
||||||
pack_out["data"][tmp->getContentP(i)->Indice()] = tmp->getContentP(i)->StrValue();
|
|
||||||
empty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!empty) {
|
|
||||||
pack_out["datatype"] = "meta";
|
|
||||||
pack_out["time"] = tagTime();
|
|
||||||
}else{
|
|
||||||
pack_out.null();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return pack_out; //empty
|
return;
|
||||||
}
|
}
|
||||||
if (data[0] == 0x08) {
|
if (data[0] == 0x08 && (metadata.tracks[reTrack].codec == "" || metadata.tracks[reTrack].codec != getAudioCodec() || (needsInitData() && isInitData()))) {
|
||||||
char audiodata = data[11];
|
char audiodata = data[11];
|
||||||
metadata.tracks[reTrack].trackID = reTrack;
|
metadata.tracks[reTrack].trackID = reTrack;
|
||||||
metadata.tracks[reTrack].type = "audio";
|
metadata.tracks[reTrack].type = "audio";
|
||||||
if (metadata.tracks[reTrack].codec == "" || metadata.tracks[reTrack].codec != getAudioCodec()) {
|
metadata.tracks[reTrack].codec = getAudioCodec();
|
||||||
metadata.tracks[reTrack].codec = getAudioCodec();
|
|
||||||
switch (audiodata & 0x0C) {
|
switch (audiodata & 0x0C) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
metadata.tracks[reTrack].rate = 5512;
|
metadata.tracks[reTrack].rate = 5512;
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
metadata.tracks[reTrack].rate = 11025;
|
metadata.tracks[reTrack].rate = 11025;
|
||||||
break;
|
break;
|
||||||
case 0x8:
|
case 0x8:
|
||||||
metadata.tracks[reTrack].rate = 22050;
|
metadata.tracks[reTrack].rate = 22050;
|
||||||
break;
|
break;
|
||||||
case 0xC:
|
case 0xC:
|
||||||
metadata.tracks[reTrack].rate = 44100;
|
metadata.tracks[reTrack].rate = 44100;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (amf_storage.getContentP("audiosamplerate")) {
|
if (amf_storage.getContentP("audiosamplerate")) {
|
||||||
metadata.tracks[reTrack].rate = (long long int)amf_storage.getContentP("audiosamplerate")->NumValue();
|
metadata.tracks[reTrack].rate = (long long int)amf_storage.getContentP("audiosamplerate")->NumValue();
|
||||||
}
|
}
|
||||||
switch (audiodata & 0x02) {
|
switch (audiodata & 0x02) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
metadata.tracks[reTrack].size = 8;
|
metadata.tracks[reTrack].size = 8;
|
||||||
break;
|
break;
|
||||||
case 0x2:
|
case 0x2:
|
||||||
metadata.tracks[reTrack].size = 16;
|
metadata.tracks[reTrack].size = 16;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (amf_storage.getContentP("audiosamplesize")) {
|
if (amf_storage.getContentP("audiosamplesize")) {
|
||||||
metadata.tracks[reTrack].size = (long long int)amf_storage.getContentP("audiosamplesize")->NumValue();
|
metadata.tracks[reTrack].size = (long long int)amf_storage.getContentP("audiosamplesize")->NumValue();
|
||||||
}
|
}
|
||||||
switch (audiodata & 0x01) {
|
switch (audiodata & 0x01) {
|
||||||
case 0x0:
|
case 0x0:
|
||||||
metadata.tracks[reTrack].channels = 1;
|
metadata.tracks[reTrack].channels = 1;
|
||||||
break;
|
break;
|
||||||
case 0x1:
|
case 0x1:
|
||||||
metadata.tracks[reTrack].channels = 2;
|
metadata.tracks[reTrack].channels = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (amf_storage.getContentP("stereo")) {
|
if (amf_storage.getContentP("stereo")) {
|
||||||
if (amf_storage.getContentP("stereo")->NumValue() == 1) {
|
if (amf_storage.getContentP("stereo")->NumValue() == 1) {
|
||||||
metadata.tracks[reTrack].channels = 2;
|
metadata.tracks[reTrack].channels = 2;
|
||||||
} else {
|
} else {
|
||||||
metadata.tracks[reTrack].channels = 1;
|
metadata.tracks[reTrack].channels = 1;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needsInitData() && isInitData()) {
|
if (needsInitData() && isInitData()) {
|
||||||
|
@ -1048,54 +1016,36 @@ JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, u
|
||||||
} else {
|
} else {
|
||||||
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
|
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
|
||||||
}
|
}
|
||||||
pack_out.null();
|
|
||||||
return pack_out; //skip rest of parsing, get next tag.
|
|
||||||
}
|
}
|
||||||
pack_out["time"] = tagTime();
|
|
||||||
if ((audiodata & 0xF0) == 0xA0) {
|
|
||||||
if (len < 18) {
|
|
||||||
return JSON::Value();
|
|
||||||
}
|
|
||||||
pack_out["data"] = std::string((char *)data + 13, (size_t)len - 17);
|
|
||||||
} else {
|
|
||||||
if (len < 17) {
|
|
||||||
return JSON::Value();
|
|
||||||
}
|
|
||||||
pack_out["data"] = std::string((char *)data + 12, (size_t)len - 16);
|
|
||||||
}
|
|
||||||
return pack_out;
|
|
||||||
}
|
}
|
||||||
if (data[0] == 0x09) {
|
|
||||||
|
if (data[0] == 0x09 && ((needsInitData() && isInitData()) || !metadata.tracks[reTrack].codec.size())){
|
||||||
char videodata = data[11];
|
char videodata = data[11];
|
||||||
if (metadata.tracks[reTrack].codec == "") {
|
metadata.tracks[reTrack].codec = getVideoCodec();
|
||||||
metadata.tracks[reTrack].codec = getVideoCodec();
|
|
||||||
}
|
|
||||||
metadata.tracks[reTrack].type = "video";
|
metadata.tracks[reTrack].type = "video";
|
||||||
metadata.tracks[reTrack].trackID = reTrack;
|
metadata.tracks[reTrack].trackID = reTrack;
|
||||||
if (!metadata.tracks[reTrack].width || !metadata.tracks[reTrack].height){
|
if (amf_storage.getContentP("width")) {
|
||||||
if (amf_storage.getContentP("width")) {
|
metadata.tracks[reTrack].width = (long long int)amf_storage.getContentP("width")->NumValue();
|
||||||
metadata.tracks[reTrack].width = (long long int)amf_storage.getContentP("width")->NumValue();
|
}
|
||||||
}
|
if (amf_storage.getContentP("height")) {
|
||||||
if (amf_storage.getContentP("height")) {
|
metadata.tracks[reTrack].height = (long long int)amf_storage.getContentP("height")->NumValue();
|
||||||
metadata.tracks[reTrack].height = (long long int)amf_storage.getContentP("height")->NumValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!metadata.tracks[reTrack].fpks && amf_storage.getContentP("videoframerate")) {
|
if (!metadata.tracks[reTrack].fpks && amf_storage.getContentP("videoframerate")) {
|
||||||
if (amf_storage.getContentP("videoframerate")->NumValue()){
|
if (amf_storage.getContentP("videoframerate")->NumValue()){
|
||||||
metadata.tracks[reTrack].fpks = (long long int)(amf_storage.getContentP("videoframerate")->NumValue() * 1000.0);
|
metadata.tracks[reTrack].fpks = (long long int)(amf_storage.getContentP("videoframerate")->NumValue() * 1000.0);
|
||||||
}else{
|
}else{
|
||||||
metadata.tracks[reTrack].fpks = JSON::Value(amf_storage.getContentP("videoframerate")->StrValue()).asInt() * 1000.0;
|
metadata.tracks[reTrack].fpks = atoi(amf_storage.getContentP("videoframerate")->StrValue().c_str()) * 1000.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needsInitData() && isInitData()) {
|
if (needsInitData() && isInitData()) {
|
||||||
if ((videodata & 0x0F) == 7) {
|
if ((videodata & 0x0F) == 7) {
|
||||||
if (len < 21) {
|
if (len < 21) {
|
||||||
return JSON::Value();
|
return;
|
||||||
}
|
}
|
||||||
metadata.tracks[reTrack].init = std::string((char *)data + 16, (size_t)len - 20);
|
metadata.tracks[reTrack].init = std::string((char *)data + 16, (size_t)len - 20);
|
||||||
} else {
|
} else {
|
||||||
if (len < 17) {
|
if (len < 17) {
|
||||||
return JSON::Value();
|
return;
|
||||||
}
|
}
|
||||||
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
|
metadata.tracks[reTrack].init = std::string((char *)data + 12, (size_t)len - 16);
|
||||||
}
|
}
|
||||||
|
@ -1108,48 +1058,9 @@ JSON::Value FLV::Tag::toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, u
|
||||||
metadata.tracks[reTrack].height = spsChar.height;
|
metadata.tracks[reTrack].height = spsChar.height;
|
||||||
metadata.tracks[reTrack].fpks = spsChar.fps * 1000;
|
metadata.tracks[reTrack].fpks = spsChar.fps * 1000;
|
||||||
}
|
}
|
||||||
pack_out.null();
|
|
||||||
return pack_out; //skip rest of parsing, get next tag.
|
|
||||||
}
|
}
|
||||||
switch (videodata & 0xF0) {
|
|
||||||
case 0x10:
|
|
||||||
case 0x40:
|
|
||||||
pack_out["keyframe"] = 1;
|
|
||||||
break;
|
|
||||||
case 0x50:
|
|
||||||
return JSON::Value();
|
|
||||||
break; //the video info byte we just throw away - useless to us...
|
|
||||||
}
|
|
||||||
pack_out["time"] = tagTime();
|
|
||||||
if (!getDataLen()){
|
|
||||||
//empty packet
|
|
||||||
pack_out["data"] = "";
|
|
||||||
return pack_out;
|
|
||||||
}
|
|
||||||
if ((videodata & 0x0F) == 7) {
|
|
||||||
switch (data[12]) {
|
|
||||||
case 1:
|
|
||||||
pack_out["nalu"] = 1;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
pack_out["nalu_end"] = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pack_out["offset"] = offset();
|
|
||||||
if (len < 21) {
|
|
||||||
return JSON::Value();
|
|
||||||
}
|
|
||||||
pack_out["data"] = std::string((char *)data + 16, (size_t)len - 20);
|
|
||||||
} else {
|
|
||||||
if (len < 17) {
|
|
||||||
return JSON::Value();
|
|
||||||
}
|
|
||||||
pack_out["data"] = std::string((char *)data + 12, (size_t)len - 16);
|
|
||||||
}
|
|
||||||
return pack_out;
|
|
||||||
}
|
}
|
||||||
return pack_out; //should never get here
|
}
|
||||||
} //FLV::Tag::toJSON
|
|
||||||
|
|
||||||
/// Checks if buf is large enough to contain len.
|
/// Checks if buf is large enough to contain len.
|
||||||
/// Attempts to resize data buffer if not/
|
/// Attempts to resize data buffer if not/
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "dtsc.h"
|
#include "dtsc.h"
|
||||||
#include "json.h"
|
|
||||||
#include "amf.h"
|
#include "amf.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ namespace FLV {
|
||||||
bool DTSCVideoInit(DTSC::Track & video);
|
bool DTSCVideoInit(DTSC::Track & video);
|
||||||
bool DTSCAudioInit(DTSC::Track & audio);
|
bool DTSCAudioInit(DTSC::Track & audio);
|
||||||
bool DTSCMetaInit(DTSC::Meta & M, std::set<long unsigned int> & selTracks);
|
bool DTSCMetaInit(DTSC::Meta & M, std::set<long unsigned int> & selTracks);
|
||||||
JSON::Value toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack = 0);
|
void toMeta(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack = 0);
|
||||||
bool MemLoader(char * D, unsigned int S, unsigned int & P);
|
bool MemLoader(char * D, unsigned int S, unsigned int & P);
|
||||||
bool FileLoader(FILE * f);
|
bool FileLoader(FILE * f);
|
||||||
unsigned int getTrackID();
|
unsigned int getTrackID();
|
||||||
|
@ -66,10 +65,6 @@ namespace FLV {
|
||||||
//loader helper functions
|
//loader helper functions
|
||||||
bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P);
|
bool MemReadUntil(char * buffer, unsigned int count, unsigned int & sofar, char * D, unsigned int S, unsigned int & P);
|
||||||
bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f);
|
bool FileReadUntil(char * buffer, unsigned int count, unsigned int & sofar, FILE * f);
|
||||||
//JSON writer helpers
|
|
||||||
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, std::string val);
|
|
||||||
void Meta_Put(JSON::Value & meta, std::string cat, std::string elem, uint64_t val);
|
|
||||||
bool Meta_Has(JSON::Value & meta, std::string cat, std::string elem);
|
|
||||||
};
|
};
|
||||||
//Tag
|
//Tag
|
||||||
|
|
||||||
|
|
18
lib/util.cpp
18
lib/util.cpp
|
@ -1,4 +1,8 @@
|
||||||
|
//This line will make ftello/fseeko work with 64 bits numbers
|
||||||
|
#define _FILE_OFFSET_BITS 64
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include <stdio.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace Util {
|
namespace Util {
|
||||||
|
@ -36,5 +40,19 @@ namespace Util {
|
||||||
}
|
}
|
||||||
return result.size() == positions.size();
|
return result.size() == positions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 64-bits version of ftell
|
||||||
|
uint64_t ftell(FILE * stream){
|
||||||
|
/// \TODO Windows implementation (e.g. _ftelli64 ?)
|
||||||
|
return ftello(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 64-bits version of fseek
|
||||||
|
uint64_t fseek(FILE * stream, uint64_t offset, int whence){
|
||||||
|
/// \TODO Windows implementation (e.g. _fseeki64 ?)
|
||||||
|
return fseeko(stream, offset, whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,6 @@
|
||||||
|
|
||||||
namespace Util {
|
namespace Util {
|
||||||
bool stringScan(const std::string & src, const std::string & pattern, std::deque<std::string> & result);
|
bool stringScan(const std::string & src, const std::string & pattern, std::deque<std::string> & result);
|
||||||
|
uint64_t ftell(FILE * stream);
|
||||||
|
uint64_t fseek(FILE * stream, uint64_t offset, int whence);
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,48 +329,50 @@ namespace Mist {
|
||||||
liveMeta.post();
|
liveMeta.post();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Checks if removing a key from this track is allowed/safe, and if so, removes it.
|
||||||
|
///Returns true if a key was actually removed, false otherwise
|
||||||
|
///Aborts if any of the following conditions are true (while active):
|
||||||
|
/// * no keys present
|
||||||
|
/// * not at least 4 whole fragments present
|
||||||
|
/// * first fragment hasn't been at least lastms-firstms ms in buffer
|
||||||
|
/// * less than 8 times the biggest fragment duration is buffered
|
||||||
|
/// If a key was deleted and the first buffered data page is no longer used, it is deleted also.
|
||||||
bool inputBuffer::removeKey(unsigned int tid) {
|
bool inputBuffer::removeKey(unsigned int tid) {
|
||||||
DTSC::Track & Trk = myMeta.tracks[tid];
|
DTSC::Track & Trk = myMeta.tracks[tid];
|
||||||
//Make sure we have at least 3 whole fragments at all times,
|
//If this track is empty, abort
|
||||||
//unless we're shutting down the whole buffer right now
|
if (!Trk.keys.size()) {
|
||||||
if (Trk.fragments.size() < 5 && config->is_active) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//If we're shutting down, and this track is empty, abort
|
//the following checks only run if we're not shutting down
|
||||||
if (!myMeta.tracks[tid].keys.size()) {
|
if (config->is_active){
|
||||||
return false;
|
//Make sure we have at least 4 whole fragments at all times,
|
||||||
}
|
if (Trk.fragments.size() < 5) {
|
||||||
if (config->is_active && Trk.fragments.size() > 2){
|
|
||||||
///Make sure we have at least 8X the target duration.
|
|
||||||
//The target duration is the biggest fragment, rounded up to whole seconds.
|
|
||||||
uint32_t targetDuration = (Trk.biggestFragment() / 1000 + 1) * 1000;
|
|
||||||
//The start is the third fragment's begin
|
|
||||||
uint32_t fragStart = Trk.getKey((++(++Trk.fragments.begin()))->getNumber()).getTime();
|
|
||||||
//The end is the last fragment's begin
|
|
||||||
uint32_t fragEnd = Trk.getKey(Trk.fragments.rbegin()->getNumber()).getTime();
|
|
||||||
if ((fragEnd - fragStart) < targetDuration * 8){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//ensure we have each fragment buffered for at least the whole bufferTime
|
||||||
|
if (!Trk.secsSinceFirstFragmentInsert() || (Trk.lastms - Trk.firstms) < bufferTime){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Trk.fragments.size() > 2){
|
||||||
|
///Make sure we have at least 8X the target duration.
|
||||||
|
//The target duration is the biggest fragment, rounded up to whole seconds.
|
||||||
|
uint32_t targetDuration = (Trk.biggestFragment() / 1000 + 1) * 1000;
|
||||||
|
//The start is the third fragment's begin
|
||||||
|
uint32_t fragStart = Trk.getKey((++(++Trk.fragments.begin()))->getNumber()).getTime();
|
||||||
|
//The end is the last fragment's begin
|
||||||
|
uint32_t fragEnd = Trk.getKey(Trk.fragments.rbegin()->getNumber()).getTime();
|
||||||
|
if ((fragEnd - fragStart) < targetDuration * 8){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
HIGH_MSG("Erasing key %d:%lu", tid, myMeta.tracks[tid].keys[0].getNumber());
|
//Alright, everything looks good, let's delete the key and possibly also fragment
|
||||||
//remove all parts of this key
|
Trk.removeFirstKey();
|
||||||
for (int i = 0; i < myMeta.tracks[tid].keys[0].getParts(); i++) {
|
|
||||||
myMeta.tracks[tid].parts.pop_front();
|
|
||||||
}
|
|
||||||
//remove the key itself
|
|
||||||
myMeta.tracks[tid].keys.pop_front();
|
|
||||||
myMeta.tracks[tid].keySizes.pop_front();
|
|
||||||
//re-calculate firstms
|
|
||||||
myMeta.tracks[tid].firstms = myMeta.tracks[tid].keys[0].getTime();
|
|
||||||
//delete the fragment if it's no longer fully buffered
|
|
||||||
if (myMeta.tracks[tid].fragments[0].getNumber() < myMeta.tracks[tid].keys[0].getNumber()) {
|
|
||||||
myMeta.tracks[tid].fragments.pop_front();
|
|
||||||
myMeta.tracks[tid].missedFrags ++;
|
|
||||||
}
|
|
||||||
//if there is more than one page buffered for this track...
|
//if there is more than one page buffered for this track...
|
||||||
if (bufferLocations[tid].size() > 1) {
|
if (bufferLocations[tid].size() > 1) {
|
||||||
//Check if the first key starts on the second page or higher
|
//Check if the first key starts on the second page or higher
|
||||||
if (myMeta.tracks[tid].keys[0].getNumber() >= (++(bufferLocations[tid].begin()))->first || !config->is_active){
|
if (Trk.keys[0].getNumber() >= (++(bufferLocations[tid].begin()))->first || !config->is_active){
|
||||||
|
//If so, we can delete the first page entirely
|
||||||
HIGH_MSG("Erasing track %d, keys %lu-%lu from buffer", tid, bufferLocations[tid].begin()->first, bufferLocations[tid].begin()->first + bufferLocations[tid].begin()->second.keyNum - 1);
|
HIGH_MSG("Erasing track %d, keys %lu-%lu from buffer", tid, bufferLocations[tid].begin()->first, bufferLocations[tid].begin()->first + bufferLocations[tid].begin()->second.keyNum - 1);
|
||||||
bufferRemove(tid, bufferLocations[tid].begin()->first);
|
bufferRemove(tid, bufferLocations[tid].begin()->first);
|
||||||
|
|
||||||
|
@ -537,6 +539,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Buffer size management
|
//Buffer size management
|
||||||
|
/// \TODO Make sure data has been in the buffer for at least bufferTime after it goes in
|
||||||
while (it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime) {
|
while (it->second.keys.size() > 1 && (it->second.lastms - it->second.keys[1].getTime()) > bufferTime) {
|
||||||
if (!removeKey(it->first)) {
|
if (!removeKey(it->first)) {
|
||||||
break;
|
break;
|
||||||
|
@ -817,11 +820,11 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
|
|
||||||
void inputBuffer::updateTrackMeta(unsigned long tNum) {
|
void inputBuffer::updateTrackMeta(unsigned long tNum) {
|
||||||
VERYHIGH_MSG("Updating meta for track %d", tNum);
|
|
||||||
//Store a reference for easier access
|
//Store a reference for easier access
|
||||||
std::map<unsigned long, DTSCPageData> & locations = bufferLocations[tNum];
|
std::map<unsigned long, DTSCPageData> & locations = bufferLocations[tNum];
|
||||||
char * mappedPointer = nProxy.metaPages[tNum].mapped;
|
char * mappedPointer = nProxy.metaPages[tNum].mapped;
|
||||||
if (!mappedPointer){return;}
|
if (!mappedPointer){return;}
|
||||||
|
VERYHIGH_MSG("Updating meta for track %lu, %lu pages", tNum, locations.size());
|
||||||
|
|
||||||
//First detect all entries on metaPage
|
//First detect all entries on metaPage
|
||||||
for (int i = 0; i < 8192; i += 8) {
|
for (int i = 0; i < 8192; i += 8) {
|
||||||
|
@ -830,11 +833,11 @@ namespace Mist {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned long keyNum = ntohl(tmpOffset[0]);
|
unsigned long keyNum = ntohl(tmpOffset[0]);
|
||||||
INSANE_MSG("Page %d detected, with %d keys", keyNum, ntohl(tmpOffset[1]));
|
|
||||||
|
|
||||||
//Add an entry into bufferLocations[tNum] for the pages we haven't handled yet.
|
//Add an entry into bufferLocations[tNum] for the pages we haven't handled yet.
|
||||||
if (!locations.count(keyNum)) {
|
if (!locations.count(keyNum)) {
|
||||||
locations[keyNum].curOffset = 0;
|
locations[keyNum].curOffset = 0;
|
||||||
|
VERYHIGH_MSG("Page %d detected, with %d keys", keyNum, ntohl(tmpOffset[1]));
|
||||||
}
|
}
|
||||||
locations[keyNum].pageNum = keyNum;
|
locations[keyNum].pageNum = keyNum;
|
||||||
locations[keyNum].keyNum = ntohl(tmpOffset[1]);
|
locations[keyNum].keyNum = ntohl(tmpOffset[1]);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <mist/util.h>
|
||||||
#include <mist/stream.h>
|
#include <mist/stream.h>
|
||||||
#include <mist/defines.h>
|
#include <mist/defines.h>
|
||||||
|
|
||||||
|
@ -53,33 +54,35 @@ namespace Mist {
|
||||||
//See whether a separate header file exists.
|
//See whether a separate header file exists.
|
||||||
if (readExistingHeader()){return true;}
|
if (readExistingHeader()){return true;}
|
||||||
//Create header file from FLV data
|
//Create header file from FLV data
|
||||||
fseek(inFile, 13, SEEK_SET);
|
Util::fseek(inFile, 13, SEEK_SET);
|
||||||
AMF::Object amf_storage;
|
AMF::Object amf_storage;
|
||||||
JSON::Value lastPack;
|
|
||||||
long long int lastBytePos = 13;
|
long long int lastBytePos = 13;
|
||||||
|
uint64_t bench = Util::getMicros();
|
||||||
while (!feof(inFile) && !FLV::Parse_Error){
|
while (!feof(inFile) && !FLV::Parse_Error){
|
||||||
if (tmpTag.FileLoader(inFile)){
|
if (tmpTag.FileLoader(inFile)){
|
||||||
lastPack.null();
|
tmpTag.toMeta(myMeta, amf_storage);
|
||||||
lastPack = tmpTag.toJSON(myMeta, amf_storage);
|
if (!tmpTag.getDataLen()){continue;}
|
||||||
lastPack["bpos"] = lastBytePos;
|
if (tmpTag.needsInitData() && tmpTag.isInitData()){continue;}
|
||||||
myMeta.update(lastPack);
|
myMeta.update(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe);
|
||||||
lastBytePos = ftell(inFile);
|
lastBytePos = Util::ftell(inFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bench = Util::getMicros(bench);
|
||||||
|
INFO_MSG("Header generated in %llu ms: @%lld, %s, %s", bench/1000, lastBytePos, myMeta.vod?"VoD":"NOVoD", myMeta.live?"Live":"NOLive");
|
||||||
if (FLV::Parse_Error){
|
if (FLV::Parse_Error){
|
||||||
std::cerr << FLV::Error_Str << std::endl;
|
FLV::Parse_Error = false;
|
||||||
return false;
|
ERROR_MSG("Stopping at FLV parse error @%lld: %s", lastBytePos, FLV::Error_Str.c_str());
|
||||||
}
|
}
|
||||||
myMeta.toFile(config->getString("input") + ".dtsh");
|
myMeta.toFile(config->getString("input") + ".dtsh");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void inputFLV::getNext(bool smart) {
|
void inputFLV::getNext(bool smart) {
|
||||||
long long int lastBytePos = ftell(inFile);
|
long long int lastBytePos = Util::ftell(inFile);
|
||||||
while (!feof(inFile) && !FLV::Parse_Error){
|
while (!feof(inFile) && !FLV::Parse_Error){
|
||||||
if (tmpTag.FileLoader(inFile)){
|
if (tmpTag.FileLoader(inFile)){
|
||||||
if ( !selectedTracks.count(tmpTag.getTrackID())){
|
if ( !selectedTracks.count(tmpTag.getTrackID())){
|
||||||
lastBytePos = ftell(inFile);
|
lastBytePos = Util::ftell(inFile);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -90,11 +93,12 @@ namespace Mist {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (FLV::Parse_Error){
|
if (FLV::Parse_Error){
|
||||||
FAIL_MSG("FLV error: %s", FLV::Error_Str.c_str());
|
FLV::Parse_Error = false;
|
||||||
|
FAIL_MSG("FLV error @ %lld: %s", lastBytePos, FLV::Error_Str.c_str());
|
||||||
thisPacket.null();
|
thisPacket.null();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!tmpTag.getDataLen()){
|
if (!tmpTag.getDataLen() || (tmpTag.needsInitData() && tmpTag.isInitData())){
|
||||||
return getNext();
|
return getNext();
|
||||||
}
|
}
|
||||||
thisPacket.genericFill(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getData(), tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe); //init packet from tmpTags data
|
thisPacket.genericFill(tmpTag.tagTime(), tmpTag.offset(), tmpTag.getTrackID(), tmpTag.getData(), tmpTag.getDataLen(), lastBytePos, tmpTag.isKeyframe); //init packet from tmpTags data
|
||||||
|
@ -104,14 +108,14 @@ namespace Mist {
|
||||||
//We will seek to the corresponding keyframe of the video track if selected, otherwise audio keyframe.
|
//We will seek to the corresponding keyframe of the video track if selected, otherwise audio keyframe.
|
||||||
//Flv files are never multi-track, so track 1 is video, track 2 is audio.
|
//Flv files are never multi-track, so track 1 is video, track 2 is audio.
|
||||||
int trackSeek = (selectedTracks.count(1) ? 1 : 2);
|
int trackSeek = (selectedTracks.count(1) ? 1 : 2);
|
||||||
size_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos();
|
uint64_t seekPos = myMeta.tracks[trackSeek].keys[0].getBpos();
|
||||||
for (unsigned int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){
|
for (unsigned int i = 0; i < myMeta.tracks[trackSeek].keys.size(); i++){
|
||||||
if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){
|
if (myMeta.tracks[trackSeek].keys[i].getTime() > seekTime){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
seekPos = myMeta.tracks[trackSeek].keys[i].getBpos();
|
seekPos = myMeta.tracks[trackSeek].keys[i].getBpos();
|
||||||
}
|
}
|
||||||
fseek(inFile, seekPos, SEEK_SET);
|
Util::fseek(inFile, seekPos, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inputFLV::trackSelect(std::string trackSpec) {
|
void inputFLV::trackSelect(std::string trackSpec) {
|
||||||
|
|
25
src/io.cpp
25
src/io.cpp
|
@ -482,16 +482,14 @@ namespace Mist {
|
||||||
unsigned long tid = packet.getTrackId();
|
unsigned long tid = packet.getTrackId();
|
||||||
//Do nothing if the trackid is invalid
|
//Do nothing if the trackid is invalid
|
||||||
if (!tid) {
|
if (!tid) {
|
||||||
INFO_MSG("Packet without trackid");
|
WARN_MSG("Packet without trackid!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//If the track is not negotiated yet, start the negotiation
|
//negotiate track ID if needed
|
||||||
if (!trackState.count(tid)) {
|
continueNegotiate(tid, myMeta);
|
||||||
continueNegotiate(tid, myMeta);
|
|
||||||
}
|
|
||||||
//If the track is declined, stop here
|
//If the track is declined, stop here
|
||||||
if (trackState[tid] == FILL_DEC) {
|
if (trackState[tid] == FILL_DEC) {
|
||||||
INFO_MSG("Track %lu Declined", tid);
|
INFO_MSG("Track %lu declined", tid);
|
||||||
preBuffer[tid].clear();
|
preBuffer[tid].clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -499,9 +497,12 @@ namespace Mist {
|
||||||
if (trackState[tid] != FILL_ACC) {
|
if (trackState[tid] != FILL_ACC) {
|
||||||
preBuffer[tid].push_back(packet);
|
preBuffer[tid].push_back(packet);
|
||||||
}else{
|
}else{
|
||||||
while (preBuffer[tid].size()){
|
if (preBuffer[tid].size()){
|
||||||
bufferSinglePacket(preBuffer[tid].front(), myMeta);
|
INFO_MSG("Track %lu accepted", tid);
|
||||||
preBuffer[tid].pop_front();
|
while (preBuffer[tid].size()){
|
||||||
|
bufferSinglePacket(preBuffer[tid].front(), myMeta);
|
||||||
|
preBuffer[tid].pop_front();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bufferSinglePacket(packet, myMeta);
|
bufferSinglePacket(packet, myMeta);
|
||||||
}
|
}
|
||||||
|
@ -513,9 +514,7 @@ namespace Mist {
|
||||||
//This update needs to happen whether the track is accepted or not.
|
//This update needs to happen whether the track is accepted or not.
|
||||||
bool isKeyframe = false;
|
bool isKeyframe = false;
|
||||||
if (myMeta.tracks[tid].type == "video") {
|
if (myMeta.tracks[tid].type == "video") {
|
||||||
if (packet.hasMember("keyframe") && packet.getFlag("keyframe")) {
|
isKeyframe = packet.getFlag("keyframe");
|
||||||
isKeyframe = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!pagesByTrack.count(tid) || pagesByTrack[tid].size() == 0) {
|
if (!pagesByTrack.count(tid) || pagesByTrack[tid].size() == 0) {
|
||||||
//Assume this is the first packet on the track
|
//Assume this is the first packet on the track
|
||||||
|
@ -532,6 +531,7 @@ namespace Mist {
|
||||||
//This also happens in bufferNext, with the same rules
|
//This also happens in bufferNext, with the same rules
|
||||||
if (myMeta.live){
|
if (myMeta.live){
|
||||||
if (packet.getTime() > 0xFFFF0000 && !myMeta.tracks[tid].lastms){
|
if (packet.getTime() > 0xFFFF0000 && !myMeta.tracks[tid].lastms){
|
||||||
|
INFO_MSG("Ignoring packet with unexpected timestamp");
|
||||||
return;//ignore bullshit timestamps
|
return;//ignore bullshit timestamps
|
||||||
}
|
}
|
||||||
if (packet.getTime() < myMeta.tracks[tid].lastms){
|
if (packet.getTime() < myMeta.tracks[tid].lastms){
|
||||||
|
@ -574,6 +574,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
//If we have no pages by track, we have not received a starting keyframe yet. Drop this packet.
|
//If we have no pages by track, we have not received a starting keyframe yet. Drop this packet.
|
||||||
if (!pagesByTrack.count(tid) || pagesByTrack[tid].size() == 0){
|
if (!pagesByTrack.count(tid) || pagesByTrack[tid].size() == 0){
|
||||||
|
INFO_MSG("Track %lu not starting with a keyframe!", tid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -556,6 +556,7 @@ namespace Mist {
|
||||||
nProxy.curPage[trackId].init(id, DEFAULT_DATA_PAGE_SIZE);
|
nProxy.curPage[trackId].init(id, DEFAULT_DATA_PAGE_SIZE);
|
||||||
if (!(nProxy.curPage[trackId].mapped)){
|
if (!(nProxy.curPage[trackId].mapped)){
|
||||||
FAIL_MSG("Initializing page %s failed", nProxy.curPage[trackId].name.c_str());
|
FAIL_MSG("Initializing page %s failed", nProxy.curPage[trackId].name.c_str());
|
||||||
|
currKeyOpen.erase(trackId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currKeyOpen[trackId] = pageNum;
|
currKeyOpen[trackId] = pageNum;
|
||||||
|
@ -978,7 +979,7 @@ namespace Mist {
|
||||||
}
|
}
|
||||||
//if this is a live stream, we might have just reached the live point.
|
//if this is a live stream, we might have just reached the live point.
|
||||||
//check where the next key is
|
//check where the next key is
|
||||||
nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, thisPacket.getTime());
|
nxtKeyNum[nxt.tid] = getKeyForTime(nxt.tid, nxt.time);
|
||||||
int nextPage = pageNumForKey(nxt.tid, nxtKeyNum[nxt.tid]+1);
|
int nextPage = pageNumForKey(nxt.tid, nxtKeyNum[nxt.tid]+1);
|
||||||
//if the next key hasn't shown up on another page, then we're waiting.
|
//if the next key hasn't shown up on another page, then we're waiting.
|
||||||
//VoD might be slow, so we check VoD case also, just in case
|
//VoD might be slow, so we check VoD case also, just in case
|
||||||
|
|
|
@ -426,7 +426,11 @@ namespace Mist {
|
||||||
data_len += dheader_len;
|
data_len += dheader_len;
|
||||||
|
|
||||||
unsigned int timestamp = thisPacket.getTime() - rtmpOffset;
|
unsigned int timestamp = thisPacket.getTime() - rtmpOffset;
|
||||||
if (rtmpOffset > thisPacket.getTime()){timestamp = 0;}//make sure we don't go negative
|
//make sure we don't go negative
|
||||||
|
if (rtmpOffset > thisPacket.getTime()){
|
||||||
|
timestamp = 0;
|
||||||
|
rtmpOffset = thisPacket.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
bool allow_short = RTMPStream::lastsend.count(4);
|
bool allow_short = RTMPStream::lastsend.count(4);
|
||||||
RTMPStream::Chunk & prev = RTMPStream::lastsend[4];
|
RTMPStream::Chunk & prev = RTMPStream::lastsend[4];
|
||||||
|
@ -1153,16 +1157,18 @@ namespace Mist {
|
||||||
}else{
|
}else{
|
||||||
amf_storage = &(pushMeta.begin()->second);
|
amf_storage = &(pushMeta.begin()->second);
|
||||||
}
|
}
|
||||||
JSON::Value pack_out = F.toJSON(myMeta, *amf_storage, next.cs_id*3 + (F.data[0] == 0x09 ? 0 : (F.data[0] == 0x08 ? 1 : 2) ));
|
|
||||||
if ( !pack_out.isNull()){
|
unsigned int reTrack = next.cs_id*3 + (F.data[0] == 0x09 ? 1 : (F.data[0] == 0x08 ? 2 : 3));
|
||||||
|
F.toMeta(myMeta, *amf_storage, reTrack);
|
||||||
|
if (F.getDataLen() && !(F.needsInitData() && F.isInitData())){
|
||||||
|
thisPacket.genericFill(F.tagTime(), F.offset(), reTrack, F.getData(), F.getDataLen(), 0, F.isKeyframe);
|
||||||
if (!nProxy.userClient.getData()){
|
if (!nProxy.userClient.getData()){
|
||||||
char userPageName[NAME_BUFFER_SIZE];
|
char userPageName[NAME_BUFFER_SIZE];
|
||||||
snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str());
|
snprintf(userPageName, NAME_BUFFER_SIZE, SHM_USERS, streamName.c_str());
|
||||||
nProxy.userClient = IPC::sharedClient(userPageName, PLAY_EX_SIZE, true);
|
nProxy.userClient = IPC::sharedClient(userPageName, PLAY_EX_SIZE, true);
|
||||||
}
|
}
|
||||||
continueNegotiate(pack_out["trackid"].asInt());
|
|
||||||
nProxy.streamName = streamName;
|
nProxy.streamName = streamName;
|
||||||
bufferLivePacket(pack_out);
|
bufferLivePacket(thisPacket);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue