Ability to send silence to RTMP outputs

This commit is contained in:
Thulinma 2020-07-24 15:29:32 +02:00
parent dc2239e70f
commit 99907782ae
6 changed files with 131 additions and 29 deletions

View file

@ -464,28 +464,22 @@ bool FLV::Tag::DTSCVideoInit(DTSC::Meta &meta, uint32_t vTrack){
}
/// FLV Audio init data loader function from metadata.
bool FLV::Tag::DTSCAudioInit(DTSC::Meta &meta, uint32_t aTrack){
bool FLV::Tag::DTSCAudioInit(const std::string & codec, unsigned int sampleRate, unsigned int sampleSize, unsigned int channels, const std::string & initData){
len = 0;
// Unknown? Assume AAC.
if (meta.getCodec(aTrack) == "?"){meta.setCodec(aTrack, "AAC");}
std::string initData = meta.getInit(aTrack);
if (meta.getCodec(aTrack) == "AAC"){len = initData.size() + 17;}
if (codec == "AAC"){len = initData.size() + 17;}
if (len <= 0 || !checkBufferSize()){return false;}
memcpy(data + 13, initData.c_str(), len - 17);
data[12] = 0; // AAC sequence header
data[11] = 0;
if (meta.getCodec(aTrack) == "AAC"){data[11] += 0xA0;}
if (meta.getCodec(aTrack) == "MP3"){data[11] += 0x20;}
unsigned int datarate = meta.getRate(aTrack);
if (datarate >= 44100){
data[11] += 0x0C;
}else if (datarate >= 22050){
data[11] += 0x08;
}else if (datarate >= 11025){
data[11] += 0x04;
data[11] = 0xA0;
if (sampleRate >= 44100){
data[11] |= 0x0C;
}else if (sampleRate >= 22050){
data[11] |= 0x08;
}else if (sampleRate >= 11025){
data[11] |= 0x04;
}
if (meta.getSize(aTrack) != 8){data[11] += 0x02;}
if (meta.getChannels(aTrack) > 1){data[11] += 0x01;}
if (sampleSize != 8){data[11] += 0x02;}
if (channels > 1){data[11] += 0x01;}
setLen();
data[0] = 0x08;
data[1] = ((len - 15) >> 16) & 0xFF;

View file

@ -51,7 +51,7 @@ namespace FLV{
bool ChunkLoader(const RTMPStream::Chunk &O);
bool DTSCLoader(DTSC::Packet &packData, const DTSC::Meta &M, size_t idx);
bool DTSCVideoInit(DTSC::Meta &meta, uint32_t vTrack);
bool DTSCAudioInit(DTSC::Meta &meta, uint32_t aTrack);
bool DTSCAudioInit(const std::string & codec, unsigned int sampleRate, unsigned int sampleSize, unsigned int channels, const std::string & initData);
bool DTSCMetaInit(const DTSC::Meta &M, std::set<long unsigned int> &selTracks);
void toMeta(DTSC::Meta &meta, AMF::Object &amf_storage);
void toMeta(DTSC::Meta &meta, AMF::Object &amf_storage, size_t &reTrack, const std::map<std::string, std::string> &targetParams);

View file

@ -64,7 +64,7 @@ namespace Mist{
if (M.getType(it->first) == "video" && tag.DTSCVideoInit(meta, it->first)){
myConn.SendNow(tag.data, tag.len);
}
if (M.getType(it->first) == "audio" && tag.DTSCAudioInit(meta, it->first)){
if (M.getType(it->first) == "audio" && tag.DTSCAudioInit(meta.getCodec(it->first), meta.getRate(it->first), meta.getSize(it->first), meta.getChannels(it->first), meta.getInit(it->first))){
myConn.SendNow(tag.data, tag.len);
}
}
@ -117,7 +117,7 @@ namespace Mist{
if (M.getType(*it) == "video" && tag.DTSCVideoInit(meta, *it)){
myConn.SendNow(tag.data, tag.len);
}
if (M.getType(*it) == "audio" && tag.DTSCAudioInit(meta, *it)){
if (M.getType(*it) == "audio" && tag.DTSCAudioInit(meta.getCodec(*it), meta.getRate(*it), meta.getSize(*it), meta.getChannels(*it), meta.getInit(*it))){
myConn.SendNow(tag.data, tag.len);
}
}

View file

@ -281,7 +281,7 @@ namespace Mist{
H.Chunkify("\000\000\000\000mdat", 8, myConn);
// send init data, if needed.
if (audioTrack != INVALID_TRACK_ID && M.getInit(audioTrack) != ""){
if (tag.DTSCAudioInit(meta, audioTrack)){
if (tag.DTSCAudioInit(meta.getCodec(audioTrack), meta.getRate(audioTrack), meta.getSize(audioTrack), meta.getChannels(audioTrack), meta.getInit(audioTrack))){
tag.tagTime(mstime);
H.Chunkify(tag.data, tag.len, myConn);
}

View file

@ -13,6 +13,8 @@
namespace Mist{
OutRTMP::OutRTMP(Socket::Connection &conn) : Output(conn){
lastSilence = 0;
hasSilence = false;
lastAck = Util::bootSecs();
lastOutTime = 0;
setRtmpOffset = false;
@ -256,6 +258,87 @@ namespace Mist{
"push out, when pushing out.\"}"));
}
void OutRTMP::sendSilence(uint64_t timestamp){
const char * tmpData = "\257\001!\020\004`\214\034";
size_t data_len = 8;
char rtmpheader[] ={0, // byte 0 = cs_id | ch_type
0, 0, 0, // bytes 1-3 = timestamp
0, 0, 0, // bytes 4-6 = length
0x08, // byte 7 = msg_type_id
1, 0, 0, 0, // bytes 8-11 = msg_stream_id = 1
0, 0, 0, 0}; // bytes 12-15 = extended timestamp
bool allow_short = RTMPStream::lastsend.count(4);
RTMPStream::Chunk &prev = RTMPStream::lastsend[4];
uint8_t chtype = 0x00;
size_t header_len = 12;
bool time_is_diff = false;
if (allow_short && (prev.cs_id == 4)){
if (prev.msg_stream_id == 1){
chtype = 0x40;
header_len = 8; // do not send msg_stream_id
if (data_len == prev.len && rtmpheader[7] == prev.msg_type_id){
chtype = 0x80;
header_len = 4; // do not send len and msg_type_id
if (timestamp == prev.timestamp){
chtype = 0xC0;
header_len = 1; // do not send timestamp
}
}
// override - we always sent type 0x00 if the timestamp has decreased since last chunk in this channel
if (timestamp < prev.timestamp){
chtype = 0x00;
header_len = 12;
}else{
// store the timestamp diff instead of the whole timestamp
timestamp -= prev.timestamp;
time_is_diff = true;
}
}
}
// update previous chunk variables
prev.cs_id = 4;
prev.msg_stream_id = 1;
prev.len = data_len;
prev.msg_type_id = 0x08;
if (time_is_diff){
prev.timestamp += timestamp;
}else{
prev.timestamp = timestamp;
}
// cs_id and ch_type
rtmpheader[0] = chtype | 4;
// data length, 3 bytes
rtmpheader[4] = (data_len >> 16) & 0xff;
rtmpheader[5] = (data_len >> 8) & 0xff;
rtmpheader[6] = data_len & 0xff;
// timestamp, 3 bytes
if (timestamp >= 0x00ffffff){
// send extended timestamp
rtmpheader[1] = 0xff;
rtmpheader[2] = 0xff;
rtmpheader[3] = 0xff;
rtmpheader[header_len++] = (timestamp >> 24) & 0xff;
rtmpheader[header_len++] = (timestamp >> 16) & 0xff;
rtmpheader[header_len++] = (timestamp >> 8) & 0xff;
rtmpheader[header_len++] = timestamp & 0xff;
}else{
// regular timestamp
rtmpheader[1] = (timestamp >> 16) & 0xff;
rtmpheader[2] = (timestamp >> 8) & 0xff;
rtmpheader[3] = timestamp & 0xff;
}
// send the packet
myConn.setBlocking(true);
myConn.SendNow(rtmpheader, header_len);
myConn.SendNow(tmpData, data_len);
RTMPStream::snd_cnt += header_len+data_len; // update the sent data counter
myConn.setBlocking(false);
}
void OutRTMP::sendNext(){
// If there are now more selectable tracks, select the new track and do a seek to the current
// timestamp Set sentHeader to false to force it to send init data
@ -283,6 +366,29 @@ namespace Mist{
}
lastOutTime = thisPacket.getTime() - rtmpOffset;
}
uint64_t timestamp = thisPacket.getTime() - rtmpOffset;
// make sure we don't go negative
if (rtmpOffset > (int64_t)thisPacket.getTime()){
timestamp = 0;
rtmpOffset = (int64_t)thisPacket.getTime();
}
//Send silence packets if needed
if (hasSilence){
//If there's more than 15s of skip, skip audio as well
if (timestamp > 15000 && lastSilence < timestamp - 15000){
lastSilence = timestamp - 30;
}
//convert time to packet counter
uint64_t currSilence = ((lastSilence*44100+512000)/1024000)+1;
uint64_t silentTime = currSilence*1024000/44100;
//keep sending silent packets until we've caught up to the current timestamp
while (silentTime < timestamp){
sendSilence(silentTime);
lastSilence = silentTime;
silentTime = (++currSilence)*1024000/44100;
}
}
char rtmpheader[] ={0, // byte 0 = cs_id | ch_type
0, 0, 0, // bytes 1-3 = timestamp
@ -361,13 +467,6 @@ namespace Mist{
}
data_len += dheader_len;
uint64_t timestamp = thisPacket.getTime() - rtmpOffset;
// make sure we don't go negative
if (rtmpOffset > (int64_t)thisPacket.getTime()){
timestamp = 0;
rtmpOffset = (int64_t)thisPacket.getTime();
}
bool allow_short = RTMPStream::lastsend.count(4);
RTMPStream::Chunk &prev = RTMPStream::lastsend[4];
uint8_t chtype = 0x00;
@ -485,9 +584,15 @@ namespace Mist{
if (tag.DTSCVideoInit(meta, *it)){myConn.SendNow(RTMPStream::SendMedia(tag));}
}
if (type == "audio"){
if (tag.DTSCAudioInit(meta, *it)){myConn.SendNow(RTMPStream::SendMedia(tag));}
if (tag.DTSCAudioInit(meta.getCodec(*it), meta.getRate(*it), meta.getSize(*it), meta.getChannels(*it), meta.getInit(*it))){myConn.SendNow(RTMPStream::SendMedia(tag));}
}
}
//Insert silent init data if audio set to silent
hasSilence = (targetParams.count("audio") && targetParams["audio"] == "silent");
if (hasSilence && tag.DTSCAudioInit("AAC", 44100, 32, 2, std::string("\022\020V\345\000", 5))){
INFO_MSG("Inserting silence track init data");
myConn.SendNow(RTMPStream::SendMedia(tag));
}
sentHeader = true;
}

View file

@ -32,6 +32,9 @@ namespace Mist{
uint64_t lastAck;
HTTP::URL pushApp, pushUrl;
uint8_t authAttempts;
void sendSilence(uint64_t currTime);
bool hasSilence;
uint64_t lastSilence;
};
}// namespace Mist