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:
parent
d20c27d020
commit
e7bcbc4f9f
4 changed files with 115 additions and 90 deletions
53
util/amf.cpp
53
util/amf.cpp
|
@ -2,6 +2,7 @@
|
|||
/// Holds all code for the AMF namespace.
|
||||
|
||||
#include "amf.h"
|
||||
#include <sstream>
|
||||
#include <cstdio> //needed for stderr only
|
||||
|
||||
/// 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.
|
||||
/// If this object contains other objects, it will call itself recursively
|
||||
/// and print all nested content in a nice human-readable format.
|
||||
void AMF::Object::Print(std::string indent){
|
||||
std::cerr << indent;
|
||||
std::string AMF::Object::Print(std::string indent){
|
||||
std::stringstream st;
|
||||
st << indent;
|
||||
// print my type
|
||||
switch (myType){
|
||||
case AMF::AMF0_NUMBER: std::cerr << "Number"; break;
|
||||
case AMF::AMF0_BOOL: std::cerr << "Bool"; break;
|
||||
case AMF::AMF0_NUMBER: st << "Number"; break;
|
||||
case AMF::AMF0_BOOL: st << "Bool"; break;
|
||||
case AMF::AMF0_STRING://short string
|
||||
case AMF::AMF0_LONGSTRING: std::cerr << "String"; break;
|
||||
case AMF::AMF0_OBJECT: std::cerr << "Object"; break;
|
||||
case AMF::AMF0_MOVIECLIP: std::cerr << "MovieClip"; break;
|
||||
case AMF::AMF0_NULL: std::cerr << "Null"; break;
|
||||
case AMF::AMF0_UNDEFINED: std::cerr << "Undefined"; break;
|
||||
case AMF::AMF0_REFERENCE: std::cerr << "Reference"; break;
|
||||
case AMF::AMF0_ECMA_ARRAY: std::cerr << "ECMA Array"; break;
|
||||
case AMF::AMF0_OBJ_END: std::cerr << "Object end"; break;
|
||||
case AMF::AMF0_STRICT_ARRAY: std::cerr << "Strict Array"; break;
|
||||
case AMF::AMF0_DATE: std::cerr << "Date"; break;
|
||||
case AMF::AMF0_UNSUPPORTED: std::cerr << "Unsupported"; break;
|
||||
case AMF::AMF0_RECORDSET: std::cerr << "Recordset"; break;
|
||||
case AMF::AMF0_XMLDOC: std::cerr << "XML Document"; break;
|
||||
case AMF::AMF0_TYPED_OBJ: std::cerr << "Typed Object"; break;
|
||||
case AMF::AMF0_UPGRADE: std::cerr << "Upgrade to AMF3"; break;
|
||||
case AMF::AMF0_DDV_CONTAINER: std::cerr << "DDVTech Container"; break;
|
||||
case AMF::AMF0_LONGSTRING: st << "String"; break;
|
||||
case AMF::AMF0_OBJECT: st << "Object"; break;
|
||||
case AMF::AMF0_MOVIECLIP: st << "MovieClip"; break;
|
||||
case AMF::AMF0_NULL: st << "Null"; break;
|
||||
case AMF::AMF0_UNDEFINED: st << "Undefined"; break;
|
||||
case AMF::AMF0_REFERENCE: st << "Reference"; break;
|
||||
case AMF::AMF0_ECMA_ARRAY: st << "ECMA Array"; break;
|
||||
case AMF::AMF0_OBJ_END: st << "Object end"; break;
|
||||
case AMF::AMF0_STRICT_ARRAY: st << "Strict Array"; break;
|
||||
case AMF::AMF0_DATE: st << "Date"; break;
|
||||
case AMF::AMF0_UNSUPPORTED: st << "Unsupported"; break;
|
||||
case AMF::AMF0_RECORDSET: st << "Recordset"; break;
|
||||
case AMF::AMF0_XMLDOC: st << "XML Document"; break;
|
||||
case AMF::AMF0_TYPED_OBJ: st << "Typed Object"; break;
|
||||
case AMF::AMF0_UPGRADE: st << "Upgrade to AMF3"; break;
|
||||
case AMF::AMF0_DDV_CONTAINER: st << "DDVTech Container"; break;
|
||||
}
|
||||
// print my string indice, if available
|
||||
std::cerr << " " << myIndice << " ";
|
||||
st << " " << myIndice << " ";
|
||||
// print my numeric or string contents
|
||||
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_STRING: case AMF::AMF0_LONGSTRING: case AMF::AMF0_XMLDOC: case AMF::AMF0_TYPED_OBJ: std::cerr << strval; 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: st << strval; break;
|
||||
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 (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
|
||||
|
||||
/// Packs the AMF object to a std::string for transfer over the network.
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace AMF{
|
|||
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, obj0type setType = AMF0_OBJECT);
|
||||
void Print(std::string indent = "");
|
||||
std::string Print(std::string indent = "");
|
||||
std::string Pack();
|
||||
protected:
|
||||
std::string myIndice; ///< Holds this objects indice, if any.
|
||||
|
|
|
@ -106,9 +106,9 @@ namespace DTSC{
|
|||
class Ring {
|
||||
public:
|
||||
Ring(unsigned int v);
|
||||
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.
|
||||
bool starved; ///< If true, this Ring can no longer receive valid data.
|
||||
volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly!
|
||||
volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill.
|
||||
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.
|
||||
|
|
144
util/flv_tag.cpp
144
util/flv_tag.cpp
|
@ -9,6 +9,7 @@
|
|||
#include <fcntl.h> //for Tag::FileLoader
|
||||
#include <stdlib.h> //malloc
|
||||
#include <string.h> //memcpy
|
||||
#include <sstream>
|
||||
|
||||
/// Holds the last FLV header parsed.
|
||||
/// 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
|
||||
/// of the encoding itself.
|
||||
std::string FLV::Tag::tagType(){
|
||||
std::string R = "";
|
||||
std::stringstream R;
|
||||
R << len << " bytes of ";
|
||||
switch (data[0]){
|
||||
case 0x09:
|
||||
switch (data[11] & 0x0F){
|
||||
case 1: R += "JPEG"; break;
|
||||
case 2: R += "H263"; break;
|
||||
case 3: R += "ScreenVideo1"; break;
|
||||
case 4: R += "VP6"; break;
|
||||
case 5: R += "VP6Alpha"; break;
|
||||
case 6: R += "ScreenVideo2"; break;
|
||||
case 7: R += "H264"; break;
|
||||
default: R += "unknown"; break;
|
||||
case 1: R << "JPEG"; break;
|
||||
case 2: R << "H263"; break;
|
||||
case 3: R << "ScreenVideo1"; break;
|
||||
case 4: R << "VP6"; break;
|
||||
case 5: R << "VP6Alpha"; break;
|
||||
case 6: R << "ScreenVideo2"; break;
|
||||
case 7: R << "H264"; break;
|
||||
default: R << "unknown"; break;
|
||||
}
|
||||
R += " video ";
|
||||
R << " video ";
|
||||
switch (data[11] & 0xF0){
|
||||
case 0x10: R += "keyframe"; break;
|
||||
case 0x20: R += "iframe"; break;
|
||||
case 0x30: R += "disposableiframe"; break;
|
||||
case 0x40: R += "generatedkeyframe"; break;
|
||||
case 0x50: R += "videoinfo"; break;
|
||||
case 0x10: R << "keyframe"; break;
|
||||
case 0x20: R << "iframe"; break;
|
||||
case 0x30: R << "disposableiframe"; break;
|
||||
case 0x40: R << "generatedkeyframe"; break;
|
||||
case 0x50: R << "videoinfo"; break;
|
||||
}
|
||||
if ((data[11] & 0x0F) == 7){
|
||||
switch (data[12]){
|
||||
case 0: R += " header"; break;
|
||||
case 1: R += " NALU"; break;
|
||||
case 2: R += " endofsequence"; break;
|
||||
case 0: R << " header"; break;
|
||||
case 1: R << " NALU"; break;
|
||||
case 2: R << " endofsequence"; break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x08:
|
||||
switch (data[11] & 0xF0){
|
||||
case 0x00: R += "linear PCM PE"; break;
|
||||
case 0x10: R += "ADPCM"; break;
|
||||
case 0x20: R += "MP3"; break;
|
||||
case 0x30: R += "linear PCM LE"; break;
|
||||
case 0x40: R += "Nelly16kHz"; break;
|
||||
case 0x50: R += "Nelly8kHz"; break;
|
||||
case 0x60: R += "Nelly"; break;
|
||||
case 0x70: R += "G711A-law"; break;
|
||||
case 0x80: R += "G711mu-law"; break;
|
||||
case 0x90: R += "reserved"; break;
|
||||
case 0xA0: R += "AAC"; break;
|
||||
case 0xB0: R += "Speex"; break;
|
||||
case 0xE0: R += "MP38kHz"; break;
|
||||
case 0xF0: R += "DeviceSpecific"; break;
|
||||
default: R += "unknown"; break;
|
||||
case 0x00: R << "linear PCM PE"; break;
|
||||
case 0x10: R << "ADPCM"; break;
|
||||
case 0x20: R << "MP3"; break;
|
||||
case 0x30: R << "linear PCM LE"; break;
|
||||
case 0x40: R << "Nelly16kHz"; break;
|
||||
case 0x50: R << "Nelly8kHz"; break;
|
||||
case 0x60: R << "Nelly"; break;
|
||||
case 0x70: R << "G711A-law"; break;
|
||||
case 0x80: R << "G711mu-law"; break;
|
||||
case 0x90: R << "reserved"; break;
|
||||
case 0xA0: R << "AAC"; break;
|
||||
case 0xB0: R << "Speex"; break;
|
||||
case 0xE0: R << "MP38kHz"; break;
|
||||
case 0xF0: R << "DeviceSpecific"; break;
|
||||
default: R << "unknown"; break;
|
||||
}
|
||||
switch (data[11] & 0x0C){
|
||||
case 0x0: R += " 5.5kHz"; break;
|
||||
case 0x4: R += " 11kHz"; break;
|
||||
case 0x8: R += " 22kHz"; break;
|
||||
case 0xC: R += " 44kHz"; break;
|
||||
case 0x0: R << " 5.5kHz"; break;
|
||||
case 0x4: R << " 11kHz"; break;
|
||||
case 0x8: R << " 22kHz"; break;
|
||||
case 0xC: R << " 44kHz"; break;
|
||||
}
|
||||
switch (data[11] & 0x02){
|
||||
case 0: R += " 8bit"; break;
|
||||
case 2: R += " 16bit"; break;
|
||||
case 0: R << " 8bit"; break;
|
||||
case 2: R << " 16bit"; break;
|
||||
}
|
||||
switch (data[11] & 0x01){
|
||||
case 0: R += " mono"; break;
|
||||
case 1: R += " stereo"; break;
|
||||
case 0: R << " mono"; break;
|
||||
case 1: R << " stereo"; break;
|
||||
}
|
||||
R += " audio";
|
||||
R << " audio";
|
||||
if ((data[12] == 0) && ((data[11] & 0xF0) == 0xA0)){
|
||||
R += " initdata";
|
||||
R << " initdata";
|
||||
}
|
||||
break;
|
||||
case 0x12:
|
||||
R += "(meta)data";
|
||||
case 0x12:{
|
||||
R << "(meta)data: ";
|
||||
AMF::Object metadata = AMF::parse((unsigned char*)data+11, len-15);
|
||||
R << metadata.Print();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
R += "unknown";
|
||||
R << "unknown";
|
||||
break;
|
||||
}
|
||||
return R;
|
||||
return R.str();
|
||||
}//FLV::Tag::tagtype
|
||||
|
||||
/// 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("disposableframe")){data[11] += 0x30;}
|
||||
break;
|
||||
case DTSC::AUDIO:
|
||||
case DTSC::AUDIO:{
|
||||
if ((unsigned int)len == S.lastData().length() + 16){
|
||||
memcpy(data+12, S.lastData().c_str(), S.lastData().length());
|
||||
}else{
|
||||
|
@ -307,12 +312,18 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
|
|||
data[11] = 0;
|
||||
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("rate")->NumValue() == 11025){data[11] += 0x04;}
|
||||
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22050){data[11] += 0x08;}
|
||||
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44100){data[11] += 0x0C;}
|
||||
unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue();
|
||||
if (datarate >= 44100){
|
||||
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("channels")->NumValue() > 1){data[11] += 0x01;}
|
||||
break;
|
||||
}
|
||||
case DTSC::META:
|
||||
memcpy(data+11, S.lastData().c_str(), S.lastData().length());
|
||||
break;
|
||||
|
@ -329,6 +340,9 @@ bool FLV::Tag::DTSCLoader(DTSC::Stream & S){
|
|||
data[1] = ((len-15) >> 16) & 0xFF;
|
||||
data[2] = ((len-15) >> 8) & 0xFF;
|
||||
data[3] = (len-15) & 0xFF;
|
||||
data[8] = 0;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
tagTime(S.getPacket().getContentP("time")->NumValue());
|
||||
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.
|
||||
void FLV::Tag::setLen(){
|
||||
int len4 = len - 4;
|
||||
int i = len-1;
|
||||
int i = len;
|
||||
data[--i] = (len4) & 0xFF;
|
||||
len4 >>= 8;
|
||||
data[--i] = (len4) & 0xFF;
|
||||
|
@ -375,6 +389,9 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Stream & S){
|
|||
data[1] = ((len-15) >> 16) & 0xFF;
|
||||
data[2] = ((len-15) >> 8) & 0xFF;
|
||||
data[3] = (len-15) & 0xFF;
|
||||
data[8] = 0;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
tagTime(0);
|
||||
return true;
|
||||
}
|
||||
|
@ -402,23 +419,25 @@ bool FLV::Tag::DTSCAudioInit(DTSC::Stream & S){
|
|||
data[11] = 0;
|
||||
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("rate")->NumValue() == 11000){data[11] += 0x04;}
|
||||
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 22000){data[11] += 0x08;}
|
||||
if (S.metadata.getContentP("audio")->getContentP("rate")->NumValue() == 44000){data[11] += 0x0C;}
|
||||
unsigned int datarate = S.metadata.getContentP("audio")->getContentP("rate")->NumValue();
|
||||
if (datarate >= 44100){
|
||||
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("channels")->NumValue() > 1){data[11] += 0x01;}
|
||||
}
|
||||
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[1] = ((len-15) >> 16) & 0xFF;
|
||||
data[2] = ((len-15) >> 8) & 0xFF;
|
||||
data[3] = (len-15) & 0xFF;
|
||||
data[8] = 0;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
tagTime(0);
|
||||
return true;
|
||||
}
|
||||
|
@ -501,6 +520,9 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S){
|
|||
data[1] = ((len-15) >> 16) & 0xFF;
|
||||
data[2] = ((len-15) >> 8) & 0xFF;
|
||||
data[3] = (len-15) & 0xFF;
|
||||
data[8] = 0;
|
||||
data[9] = 0;
|
||||
data[10] = 0;
|
||||
tagTime(0);
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue