Several major bugfixes in FLV handling, multithreaded buffer now actually works (still crashes when multiple users connect, though... needs further "tweaking"), updated toolset.

This commit is contained in:
Thulinma 2012-04-10 16:26:30 +02:00
parent d20c27d020
commit e7bcbc4f9f
4 changed files with 115 additions and 90 deletions

View file

@ -2,6 +2,7 @@
/// Holds all code for the AMF namespace. /// Holds all code for the AMF namespace.
#include "amf.h" #include "amf.h"
#include <sstream>
#include <cstdio> //needed for stderr only #include <cstdio> //needed for stderr only
/// Returns the std::string Indice for the current object, if available. /// Returns the std::string Indice for the current object, if available.
@ -105,43 +106,45 @@ AMF::Object::Object(std::string indice, AMF::obj0type setType){//object type ini
/// Prints the contents of this object to std::cerr. /// Prints the contents of this object to std::cerr.
/// If this object contains other objects, it will call itself recursively /// If this object contains other objects, it will call itself recursively
/// and print all nested content in a nice human-readable format. /// and print all nested content in a nice human-readable format.
void AMF::Object::Print(std::string indent){ std::string AMF::Object::Print(std::string indent){
std::cerr << indent; std::stringstream st;
st << indent;
// print my type // print my type
switch (myType){ switch (myType){
case AMF::AMF0_NUMBER: std::cerr << "Number"; break; case AMF::AMF0_NUMBER: st << "Number"; break;
case AMF::AMF0_BOOL: std::cerr << "Bool"; break; case AMF::AMF0_BOOL: st << "Bool"; break;
case AMF::AMF0_STRING://short string case AMF::AMF0_STRING://short string
case AMF::AMF0_LONGSTRING: std::cerr << "String"; break; case AMF::AMF0_LONGSTRING: st << "String"; break;
case AMF::AMF0_OBJECT: std::cerr << "Object"; break; case AMF::AMF0_OBJECT: st << "Object"; break;
case AMF::AMF0_MOVIECLIP: std::cerr << "MovieClip"; break; case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break;
case AMF::AMF0_NULL: std::cerr << "Null"; break; case AMF::AMF0_NULL: st << "Null"; break;
case AMF::AMF0_UNDEFINED: std::cerr << "Undefined"; break; case AMF::AMF0_UNDEFINED: st << "Undefined"; break;
case AMF::AMF0_REFERENCE: std::cerr << "Reference"; break; case AMF::AMF0_REFERENCE: st << "Reference"; break;
case AMF::AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break; case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break;
case AMF::AMF0_OBJ_END: std::cerr << "Object end"; break; case AMF::AMF0_OBJ_END: st << "Object end"; break;
case AMF::AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break; case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break;
case AMF::AMF0_DATE: std::cerr << "Date"; break; case AMF::AMF0_DATE: st << "Date"; break;
case AMF::AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break; case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break;
case AMF::AMF0_RECORDSET: std::cerr << "Recordset"; break; case AMF::AMF0_RECORDSET: st << "Recordset"; break;
case AMF::AMF0_XMLDOC: std::cerr << "XML Document"; break; case AMF::AMF0_XMLDOC: st << "XML Document"; break;
case AMF::AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break; case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break;
case AMF::AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break; case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break;
case AMF::AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break; case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break;
} }
// print my string indice, if available // print my string indice, if available
std::cerr << " " << myIndice << " "; st << " " << myIndice << " ";
// print my numeric or string contents // print my numeric or string contents
switch (myType){ switch (myType){
case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: std::cerr << numval; break; case AMF::AMF0_NUMBER: case AMF::AMF0_BOOL: case AMF::AMF0_REFERENCE: case AMF::AMF0_DATE: st << numval; break;
case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: std::cerr << strval; break; case AMF::AMF0_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: st << strval; break;
default: break;//we don't care about the rest, and don't want a compiler warning... default: break;//we don't care about the rest, and don't want a compiler warning...
} }
std::cerr << std::endl; st << std::endl;
// if I hold other objects, print those too, recursively. // if I hold other objects, print those too, recursively.
if (contents.size() > 0){ if (contents.size() > 0){
for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){it->Print(indent+" ");} for (std::vector<AMF::Object>::iterator it = contents.begin(); it != contents.end(); it++){st << it->Print(indent+" ");}
} }
return st.str();
};//print };//print
/// Packs the AMF object to a std::string for transfer over the network. /// Packs the AMF object to a std::string for transfer over the network.

View file

@ -70,7 +70,7 @@ namespace AMF{
Object(std::string indice, double val, obj0type setType = AMF0_NUMBER); Object(std::string indice, double val, obj0type setType = AMF0_NUMBER);
Object(std::string indice, std::string val, obj0type setType = AMF0_STRING); Object(std::string indice, std::string val, obj0type setType = AMF0_STRING);
Object(std::string indice, obj0type setType = AMF0_OBJECT); Object(std::string indice, obj0type setType = AMF0_OBJECT);
void Print(std::string indent = ""); std::string Print(std::string indent = "");
std::string Pack(); std::string Pack();
protected: protected:
std::string myIndice; ///< Holds this objects indice, if any. std::string myIndice; ///< Holds this objects indice, if any.

View file

@ -106,9 +106,9 @@ namespace DTSC{
class Ring { class Ring {
public: public:
Ring(unsigned int v); Ring(unsigned int v);
unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly!
bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill.
bool starved; ///< If true, this Ring can no longer receive valid data. volatile bool starved; ///< If true, this Ring can no longer receive valid data.
}; };
/// Holds temporary data for a DTSC stream and provides functions to utilize it. /// Holds temporary data for a DTSC stream and provides functions to utilize it.

View file

@ -9,6 +9,7 @@
#include <fcntl.h> //for Tag::FileLoader #include <fcntl.h> //for Tag::FileLoader
#include <stdlib.h> //malloc #include <stdlib.h> //malloc
#include <string.h> //memcpy #include <string.h> //memcpy
#include <sstream>
/// Holds the last FLV header parsed. /// Holds the last FLV header parsed.
/// Defaults to a audio+video header on FLV version 0x01 if no header received yet. /// Defaults to a audio+video header on FLV version 0x01 if no header received yet.
@ -100,80 +101,84 @@ bool FLV::Tag::isInitData(){
/// audio, video or metadata, what encoding is used, and the details /// audio, video or metadata, what encoding is used, and the details
/// of the encoding itself. /// of the encoding itself.
std::string FLV::Tag::tagType(){ std::string FLV::Tag::tagType(){
std::string R = ""; std::stringstream R;
R << len << " bytes of ";
switch (data[0]){ switch (data[0]){
case 0x09: case 0x09:
switch (data[11] & 0x0F){ switch (data[11] & 0x0F){
case 1: R += "JPEG"; break; case 1: R << "JPEG"; break;
case 2: R += "H263"; break; case 2: R << "H263"; break;
case 3: R += "ScreenVideo1"; break; case 3: R << "ScreenVideo1"; break;
case 4: R += "VP6"; break; case 4: R << "VP6"; break;
case 5: R += "VP6Alpha"; break; case 5: R << "VP6Alpha"; break;
case 6: R += "ScreenVideo2"; break; case 6: R << "ScreenVideo2"; break;
case 7: R += "H264"; break; case 7: R << "H264"; break;
default: R += "unknown"; break; default: R << "unknown"; break;
} }
R += " video "; R << " video ";
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x10: R += "keyframe"; break; case 0x10: R << "keyframe"; break;
case 0x20: R += "iframe"; break; case 0x20: R << "iframe"; break;
case 0x30: R += "disposableiframe"; break; case 0x30: R << "disposableiframe"; break;
case 0x40: R += "generatedkeyframe"; break; case 0x40: R << "generatedkeyframe"; break;
case 0x50: R += "videoinfo"; break; case 0x50: R << "videoinfo"; break;
} }
if ((data[11] & 0x0F) == 7){ if ((data[11] & 0x0F) == 7){
switch (data[12]){ switch (data[12]){
case 0: R += " header"; break; case 0: R << " header"; break;
case 1: R += " NALU"; break; case 1: R << " NALU"; break;
case 2: R += " endofsequence"; break; case 2: R << " endofsequence"; break;
} }
} }
break; break;
case 0x08: case 0x08:
switch (data[11] & 0xF0){ switch (data[11] & 0xF0){
case 0x00: R += "linear PCM PE"; break; case 0x00: R << "linear PCM PE"; break;
case 0x10: R += "ADPCM"; break; case 0x10: R << "ADPCM"; break;
case 0x20: R += "MP3"; break; case 0x20: R << "MP3"; break;
case 0x30: R += "linear PCM LE"; break; case 0x30: R << "linear PCM LE"; break;
case 0x40: R += "Nelly16kHz"; break; case 0x40: R << "Nelly16kHz"; break;
case 0x50: R += "Nelly8kHz"; break; case 0x50: R << "Nelly8kHz"; break;
case 0x60: R += "Nelly"; break; case 0x60: R << "Nelly"; break;
case 0x70: R += "G711A-law"; break; case 0x70: R << "G711A-law"; break;
case 0x80: R += "G711mu-law"; break; case 0x80: R << "G711mu-law"; break;
case 0x90: R += "reserved"; break; case 0x90: R << "reserved"; break;
case 0xA0: R += "AAC"; break; case 0xA0: R << "AAC"; break;
case 0xB0: R += "Speex"; break; case 0xB0: R << "Speex"; break;
case 0xE0: R += "MP38kHz"; break; case 0xE0: R << "MP38kHz"; break;
case 0xF0: R += "DeviceSpecific"; break; case 0xF0: R << "DeviceSpecific"; break;
default: R += "unknown"; break; default: R << "unknown"; break;
} }
switch (data[11] & 0x0C){ switch (data[11] & 0x0C){
case 0x0: R += " 5.5kHz"; break; case 0x0: R << " 5.5kHz"; break;
case 0x4: R += " 11kHz"; break; case 0x4: R << " 11kHz"; break;
case 0x8: R += " 22kHz"; break; case 0x8: R << " 22kHz"; break;
case 0xC: R += " 44kHz"; break; case 0xC: R << " 44kHz"; break;
} }
switch (data[11] & 0x02){ switch (data[11] & 0x02){
case 0: R += " 8bit"; break; case 0: R << " 8bit"; break;
case 2: R += " 16bit"; break; case 2: R << " 16bit"; break;
} }
switch (data[11] & 0x01){ switch (data[11] & 0x01){
case 0: R += " mono"; break; case 0: R << " mono"; break;
case 1: R += " stereo"; break; case 1: R << " stereo"; break;
} }
R += " audio"; R << " audio";
if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){ if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){
R += " initdata"; R << " initdata";
} }
break; break;
case 0x12: case 0x12:{
R += "(meta)data"; R << "(meta)data: ";
AMF::Object metadata = AMF::parse((unsigned char*)data+11, len-15);
R << metadata.Print();
break; break;
}
default: default:
R += "unknown"; R << "unknown";
break; break;
} }
return R; return R.str();
}//FLV::Tag::tagtype }//FLV::Tag::tagtype
/// Returns the 32-bit timestamp of this tag. /// Returns the 32-bit timestamp of this tag.
@ -297,7 +302,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
if (S.getPacket().getContentP("interframe")){data[11] += 0x20;} if (S.getPacket().getContentP("interframe")){data[11] += 0x20;}
if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;} if (S.getPacket().getContentP("disposableframe")){data[11] += 0x30;}
break; break;
case DTSC::AUDIO: case DTSC::AUDIO:{
if ((unsigned int)len == S.lastData().length() + 16){ if ((unsigned int)len == S.lastData().length() + 16){
memcpy(data+12, S.lastData().c_str(), S.lastData().length()); memcpy(data+12, S.lastData().c_str(), S.lastData().length());
}else{ }else{
@ -307,12 +312,18 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
data[11] = 0; data[11] = 0;
if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;}
if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;}
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11025){data[11] += 0x04;} unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue();
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22050){data[11] += 0x08;} if (datarate >= 44100){
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44100){data[11] += 0x0C;} data[11] += 0x0C;
}else if(datarate >= 22050){
data[11] += 0x08;
}else if(datarate >= 11025){
data[11] += 0x04;
}
if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;}
if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;}
break; break;
}
case DTSC::META: case DTSC::META:
memcpy(data+11, S.lastData().c_str(), S.lastData().length()); memcpy(data+11, S.lastData().c_str(), S.lastData().length());
break; break;
@ -329,6 +340,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
data[1] = ((len-15) >> 16) & 0xFF; data[1] = ((len-15) >> 16) & 0xFF;
data[2] = ((len-15) >> 8) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF;
data[3] = (len-15) & 0xFF; data[3] = (len-15) & 0xFF;
data[8] = 0;
data[9] = 0;
data[10] = 0;
tagTime(S.getPacket().getContentP("time")->NumValue()); tagTime(S.getPacket().getContentP("time")->NumValue());
return true; return true;
} }
@ -336,7 +350,7 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
/// Helper function that properly sets the tag length from the internal len variable. /// Helper function that properly sets the tag length from the internal len variable.
void FLV::Tag::setLen(){ void FLV::Tag::setLen(){
int len4 = len - 4; int len4 = len - 4;
int i = len-1; int i = len;
data[--i] = (len4) & 0xFF; data[--i] = (len4) & 0xFF;
len4 >>= 8; len4 >>= 8;
data[--i] = (len4) & 0xFF; data[--i] = (len4) & 0xFF;
@ -375,6 +389,9 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){
data[1] = ((len-15) >> 16) & 0xFF; data[1] = ((len-15) >> 16) & 0xFF;
data[2] = ((len-15) >> 8) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF;
data[3] = (len-15) & 0xFF; data[3] = (len-15) & 0xFF;
data[8] = 0;
data[9] = 0;
data[10] = 0;
tagTime(0); tagTime(0);
return true; return true;
} }
@ -402,23 +419,25 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){
data[11] = 0; data[11] = 0;
if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "AAC"){data[11] += 0xA0;}
if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;} if (S.metadata.getContentP("audio")->getContentP("codec")->StrValue() == "MP3"){data[11] += 0x20;}
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 11000){data[11] += 0x04;} unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue();
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22000){data[11] += 0x08;} if (datarate >= 44100){
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44000){data[11] += 0x0C;} data[11] += 0x0C;
}else if(datarate >= 22050){
data[11] += 0x08;
}else if(datarate >= 11025){
data[11] += 0x04;
}
if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;} if (S.metadata.getContentP("audio")->getContentP("size")->NumValue() == 16){data[11] += 0x02;}
if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;} if (S.metadata.getContentP("audio")->getContentP("channels")->NumValue() > 1){data[11] += 0x01;}
} }
setLen(); setLen();
switch (S.lastType()){
case DTSC::VIDEO: data[0] = 0x09; break;
case DTSC::AUDIO: data[0] = 0x08; break;
case DTSC::META: data[0] = 0x12; break;
default: break;
}
data[0] = 0x08; data[0] = 0x08;
data[1] = ((len-15) >> 16) & 0xFF; data[1] = ((len-15) >> 16) & 0xFF;
data[2] = ((len-15) >> 8) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF;
data[3] = (len-15) & 0xFF; data[3] = (len-15) & 0xFF;
data[8] = 0;
data[9] = 0;
data[10] = 0;
tagTime(0); tagTime(0);
return true; return true;
} }
@ -501,6 +520,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){
data[1] = ((len-15) >> 16) & 0xFF; data[1] = ((len-15) >> 16) & 0xFF;
data[2] = ((len-15) >> 8) & 0xFF; data[2] = ((len-15) >> 8) & 0xFF;
data[3] = (len-15) & 0xFF; data[3] = (len-15) & 0xFF;
data[8] = 0;
data[9] = 0;
data[10] = 0;
tagTime(0); tagTime(0);
return true; return true;
} }