void AACPacketizer::pushBuffer(const uint8_t* const inBuffer, size_t inSize, IMetadata& metadata) { const auto now = std::chrono::steady_clock::now(); const uint64_t bufferDuration(metadata.timestampDelta * 1000000.); const double nowmicros (std::chrono::duration_cast<std::chrono::microseconds>(now - m_epoch).count()); const auto micros = std::floor(nowmicros / double(bufferDuration)) * bufferDuration; std::vector<uint8_t> & outBuffer = m_outbuffer; outBuffer.clear(); int flags = 0; const int flags_size = 2; //static int prev_ts = 0; int ts = micros / 1000; // m_audioTs * 1000.;// auto output = m_output.lock(); RTMPMetadata_t outMeta(metadata.timestampDelta); if(output) { flags = FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ | FLV_SAMPLESSIZE_16BIT | FLV_STEREO; outBuffer.reserve(inSize + flags_size); put_byte(outBuffer, flags); put_byte(outBuffer, m_sentAudioConfig); if(!m_sentAudioConfig) { m_sentAudioConfig = true; const char hdr[2] = { 0x12,0x10 }; put_buff(outBuffer, (uint8_t*)hdr, 2); } else { put_buff(outBuffer, inBuffer, inSize); m_audioTs += metadata.timestampDelta; } outMeta.setData(ts, static_cast<int>(outBuffer.size()), FLV_TAG_TYPE_AUDIO, kAudioChannelStreamId); output->pushBuffer(&outBuffer[0], outBuffer.size(), outMeta); } }
void AACPacketizer::pushBuffer(const uint8_t* const inBuffer, size_t inSize, IMetadata& metadata) { std::vector<uint8_t> & outBuffer = m_outbuffer; outBuffer.clear(); int flvStereoOrMono = (m_channelCount == 2 ? FLV_STEREO : FLV_MONO); int flvSampleRate = FLV_SAMPLERATE_44100HZ; // default if (m_sampleRate == 22050.0) { flvSampleRate = FLV_SAMPLERATE_22050HZ; } int flags = 0; const int flags_size = 2; int ts = metadata.timestampDelta + m_ctsOffset ; // DLog("AAC: %06d", ts); auto output = m_output.lock(); RTMPMetadata_t outMeta(ts); if(inSize == 2 && !m_asc[0] && !m_asc[1]) { m_asc[0] = inBuffer[0]; m_asc[1] = inBuffer[1]; } if(output) { flags = FLV_CODECID_AAC | flvSampleRate | FLV_SAMPLESSIZE_16BIT | flvStereoOrMono; outBuffer.reserve(inSize + flags_size); put_byte(outBuffer, flags); put_byte(outBuffer, m_sentAudioConfig); if(!m_sentAudioConfig) { m_sentAudioConfig = true; put_buff(outBuffer, (uint8_t*)m_asc, sizeof(m_asc)); } else { put_buff(outBuffer, inBuffer, inSize); } outMeta.setData(ts, static_cast<int>(outBuffer.size()), RTMP_PT_AUDIO, kAudioChannelStreamId, false); output->pushBuffer(&outBuffer[0], outBuffer.size(), outMeta); } }
void AACPacketizer::pushBuffer(const uint8_t* const inBuffer, size_t inSize, IMetadata& metadata) { std::vector<uint8_t> & outBuffer = m_outbuffer; outBuffer.clear(); int flags = 0; const int flags_size = 2; int ts = metadata.timestampDelta; auto output = m_output.lock(); RTMPMetadata_t outMeta(metadata.timestampDelta); if(inSize == 2 && !m_asc[0] && !m_asc[1]) { m_asc[0] = inBuffer[0]; m_asc[1] = inBuffer[1]; } if(output) { flags = FLV_CODECID_AAC | FLV_SAMPLERATE_44100HZ | FLV_SAMPLESSIZE_16BIT | FLV_STEREO; outBuffer.reserve(inSize + flags_size); put_byte(outBuffer, flags); put_byte(outBuffer, m_sentAudioConfig); if(!m_sentAudioConfig) { m_sentAudioConfig = true; put_buff(outBuffer, (uint8_t*)m_asc, sizeof(m_asc)); } else { put_buff(outBuffer, inBuffer, inSize); m_audioTs += metadata.timestampDelta; } outMeta.setData(ts, static_cast<int>(outBuffer.size()), FLV_TAG_TYPE_AUDIO, kAudioChannelStreamId); output->pushBuffer(&outBuffer[0], outBuffer.size(), outMeta); } }
void RTMPSession::sendSetChunkSize(int32_t chunkSize) { m_jobQueue.enqueue([&, chunkSize] { int streamId = 0; std::vector<uint8_t> buff; put_byte(buff, 2); // chunk stream ID 2 put_be24(buff, 0); // ts put_be24(buff, 4); // size (4 bytes) put_byte(buff, RTMP_PT_CHUNK_SIZE); // chunk type put_buff(buff, (uint8_t*)&streamId, sizeof(int32_t)); // msg stream id is little-endian put_be32(buff, chunkSize); write(&buff[0], buff.size()); m_outChunkSize = chunkSize; }); }
std::vector<uint8_t> H264Packetizer::configurationFromSpsAndPps() { std::vector<uint8_t> conf; put_byte(conf, 1); // version put_byte(conf, m_sps[1]); // profile put_byte(conf, m_sps[2]); // compat put_byte(conf, m_sps[3]); // level put_byte(conf, 0xff); // 6 bits reserved + 2 bits nal size length - 1 (11) put_byte(conf, 0xe1); // 3 bits reserved + 5 bits number of sps (00001) put_be16(conf, m_sps.size()); put_buff(conf, &m_sps[0], m_sps.size()); put_byte(conf, 1); put_be16(conf, m_pps.size()); put_buff(conf, &m_pps[0], m_pps.size()); return conf; }
void RTMPSession::sendHeaderPacket() { std::vector<uint8_t> outBuffer; std::vector<uint8_t> enc; RTMPChunk_0 metadata = {{0}}; put_string(enc, "@setDataFrame"); put_string(enc, "onMetaData"); put_byte(enc, kAMFEMCAArray); put_be32(enc, 5+5+2); // videoEnabled + audioEnabled + 2 put_named_double(enc, "duration", 0.0); put_named_double(enc, "width", m_frameWidth); put_named_double(enc, "height", m_frameHeight); put_named_double(enc, "videodatarate", static_cast<double>(m_bitrate) / 1024.); put_named_double(enc, "framerate", m_frameDuration); put_named_double(enc, "videocodecid", 7.); put_named_double(enc, "audiodatarate", 131152. / 1024.); put_named_double(enc, "audiosamplerate", m_audioSampleRate); put_named_double(enc, "audiosamplesize", 16); put_named_bool(enc, "stereo", m_audioStereo); put_named_double(enc, "audiocodecid", 10.); put_named_double(enc, "filesize", 0.); put_be16(enc, 0); put_byte(enc, kAMFObjectEnd); size_t len = enc.size(); put_buff(outBuffer, (uint8_t*)&enc[0], static_cast<size_t>(len)); metadata.msg_type_id = FLV_TAG_TYPE_META; metadata.msg_stream_id = kAudioChannelStreamId; metadata.msg_length.data = static_cast<int>( outBuffer.size() ); metadata.timestamp.data = 0; sendPacket(&outBuffer[0], outBuffer.size(), metadata); }
void RTMPSession::sendSetBufferTime(int milliseconds) { m_jobQueue.enqueue([=]{ int streamId = 0; std::vector<uint8_t> buff; put_byte(buff, 2); put_be24(buff, 0); put_be24(buff, 10); put_byte(buff, RTMP_PT_PING); put_buff(buff, (uint8_t*)&streamId, sizeof(int32_t)); put_be16(buff, 3); // SetBufferTime put_be32(buff, m_streamId); put_be32(buff, milliseconds); write(&buff[0], buff.size()); }); } bool
void RTMPSession::sendPong() { m_jobQueue.enqueue([&] { int streamId = 0; std::vector<uint8_t> buff; put_byte(buff, 2); // chunk stream ID 2 put_be24(buff, 0); // ts put_be24(buff, 6); // size (6 bytes) put_byte(buff, RTMP_PT_PING); // chunk type put_buff(buff, (uint8_t*)&streamId, sizeof(int32_t)); // msg stream id is little-endian put_be16(buff, 7); put_be16(buff, 0); put_be16(buff, 0); write(&buff[0], buff.size()); }); }
void H264Packetizer::pushBuffer(const uint8_t* const inBuffer, size_t inSize, IMetadata& inMetadata) { std::vector<uint8_t>& outBuffer = m_outbuffer; outBuffer.clear(); uint8_t nal_type = inBuffer[4] & 0x1F; int flags = 0; const int flags_size = 5; const int ts = inMetadata.timestampDelta; bool is_config = (nal_type == 7 || nal_type == 8); flags = FLV_CODECID_H264; auto output = m_output.lock(); RTMPMetadata_t outMeta(inMetadata.timestampDelta); switch(nal_type) { case 7: if(m_sps.size() == 0) { m_sps.resize(inSize-4); memcpy(&m_sps[0], inBuffer+4, inSize-4); } return; case 8: if(m_pps.size() == 0) { m_pps.resize(inSize-4); memcpy(&m_pps[0], inBuffer+4, inSize-4); } flags |= FLV_FRAME_KEY; break; case 5: flags |= FLV_FRAME_KEY; break; default: flags |= FLV_FRAME_INTER; break; } if(output) { std::vector<uint8_t> conf; if(is_config && m_sps.size() > 0 && m_pps.size() > 0 ) { conf = configurationFromSpsAndPps(); inSize = conf.size(); } outBuffer.reserve(inSize + flags_size); put_byte(outBuffer, flags); put_byte(outBuffer, !is_config); put_be24(outBuffer, 0); if(is_config) { // create modified SPS/PPS buffer if(m_sps.size() > 0 && m_pps.size() > 0 && !m_sentConfig) { put_buff(outBuffer, &conf[0], conf.size()); m_sentConfig = true; } else { return; } } else { put_buff(outBuffer, inBuffer, inSize); } static auto prev_time = std::chrono::steady_clock::now(); auto now = std::chrono::steady_clock::now(); auto m_micros = std::chrono::duration_cast<std::chrono::microseconds>(now - prev_time).count(); static uint64_t total = 0; static uint64_t count = 0; total+=m_micros; count++; prev_time = now; outMeta.setData(ts, static_cast<int>(outBuffer.size()), FLV_TAG_TYPE_VIDEO, kVideoChannelStreamId); output->pushBuffer(&outBuffer[0], outBuffer.size(), outMeta); } }
void H264Packetizer::pushBuffer(const uint8_t* const inBuffer, size_t inSize, IMetadata& inMetadata) { std::vector<uint8_t>& outBuffer = m_outbuffer; outBuffer.clear(); uint8_t nal_type = inBuffer[4] & 0x1F; int flags = 0; const int flags_size = 5; int dts = inMetadata.dts ; int pts = inMetadata.pts + m_ctsOffset; // correct for pts < dts which some players (ffmpeg) don't like dts = dts > 0 ? dts : pts - m_ctsOffset ; bool is_config = (nal_type == 7 || nal_type == 8); flags = FLV_CODECID_H264; auto output = m_output.lock(); switch(nal_type) { case 7: if(m_sps.size() == 0) { m_sps.resize(inSize-4); memcpy(&m_sps[0], inBuffer+4, inSize-4); } break; case 8: if(m_pps.size() == 0) { m_pps.resize(inSize-4); memcpy(&m_pps[0], inBuffer+4, inSize-4); } flags |= FLV_FRAME_KEY; break; case 5: flags |= FLV_FRAME_KEY; break; default: flags |= FLV_FRAME_INTER; break; } if(output) { RTMPMetadata_t outMeta(dts); std::vector<uint8_t> conf; if(is_config && m_sps.size() > 0 && m_pps.size() > 0 ) { conf = configurationFromSpsAndPps(); inSize = conf.size(); } outBuffer.reserve(inSize + flags_size); put_byte(outBuffer, flags); put_byte(outBuffer, !is_config); put_be24(outBuffer, pts - dts); // Decoder delay if(is_config ) { // create modified SPS/PPS buffer if(m_sps.size() > 0 && m_pps.size() > 0 && !m_sentConfig && (flags & FLV_FRAME_KEY)) { put_buff(outBuffer, &conf[0], conf.size()); m_sentConfig = true; } else { return; } } else { put_buff(outBuffer, inBuffer, inSize); } outMeta.setData(dts, static_cast<int>(outBuffer.size()), RTMP_PT_VIDEO, kVideoChannelStreamId, nal_type == 5); output->pushBuffer(&outBuffer[0], outBuffer.size(), outMeta); } }
void RTMPSession::sendHeaderPacket() { std::vector<uint8_t> outBuffer; std::vector<uint8_t> enc; RTMPChunk_0 metadata = {{0}}; put_string(enc, "@setDataFrame"); put_string(enc, "onMetaData"); put_byte(enc, kAMFObject); //put_be32(enc, 5+5+2); // videoEnabled + audioEnabled + 2 //put_named_double(enc, "duration", 0.0); put_named_double(enc, "width", m_frameWidth); put_named_double(enc, "height", m_frameHeight); put_named_double(enc, "displaywidth", m_frameWidth); put_named_double(enc, "displayheight", m_frameHeight); put_named_double(enc, "framewidth", m_frameWidth); put_named_double(enc, "frameheight", m_frameHeight); put_named_double(enc, "videodatarate", static_cast<double>(m_bitrate) / 1024.); put_named_double(enc, "videoframerate", 1. / m_frameDuration); put_named_string(enc, "videocodecid", "avc1"); { put_name(enc, "trackinfo"); put_byte(enc, kAMFStrictArray); put_be32(enc, 2); // // Audio stream metadata put_byte(enc, kAMFObject); put_named_string(enc, "type", "audio"); { std::stringstream ss; ss << "{AACFrame: codec:AAC, channels: " << m_audioStereo+1 << ", frequency:" << m_audioSampleRate << ", samplesPerFrame:1024, objectType:LC}"; put_named_string(enc, "description", ss.str()); } put_named_double(enc, "timescale", 1000.); put_name(enc, "sampledescription"); put_byte(enc, kAMFStrictArray); put_be32(enc, 1); put_byte(enc, kAMFObject); put_named_string(enc, "sampletype", "mpeg4-generic"); put_byte(enc, 0); put_byte(enc, 0); put_byte(enc, kAMFObjectEnd); put_named_string(enc, "language", "eng"); put_byte(enc, 0); put_byte(enc, 0); put_byte(enc, kAMFObjectEnd); // // Video stream metadata put_byte(enc, kAMFObject); put_named_string(enc, "type", "video"); put_named_double(enc, "timescale", 1000.); put_named_string(enc, "language", "eng"); put_name(enc, "sampledescription"); put_byte(enc, kAMFStrictArray); put_be32(enc, 1); put_byte(enc, kAMFObject); put_named_string(enc, "sampletype", "H264"); put_byte(enc, 0); put_byte(enc, 0); put_byte(enc, kAMFObjectEnd); put_byte(enc, 0); put_byte(enc, 0); put_byte(enc, kAMFObjectEnd); } put_be16(enc, 0); put_byte(enc, kAMFObjectEnd); put_named_double(enc, "audiodatarate", 131152. / 1024.); put_named_double(enc, "audiosamplerate", m_audioSampleRate); put_named_double(enc, "audiosamplesize", 16); put_named_double(enc, "audiochannels", m_audioStereo + 1); put_named_string(enc, "audiocodecid", "mp4a"); put_be16(enc, 0); put_byte(enc, kAMFObjectEnd); size_t len = enc.size(); put_buff(outBuffer, (uint8_t*)&enc[0], static_cast<size_t>(len)); metadata.msg_type_id = FLV_TAG_TYPE_META; metadata.msg_stream_id = kAudioChannelStreamId; metadata.msg_length.data = static_cast<int>( outBuffer.size() ); metadata.timestamp.data = 0; sendPacket(&outBuffer[0], outBuffer.size(), metadata); }
void RTMPSession::pushBuffer(const uint8_t* const data, size_t size, IMetadata& metadata) { if(m_ending) { return ; } std::shared_ptr<Buffer> buf = std::make_shared<Buffer>(size); buf->put(const_cast<uint8_t*>(data), size); const RTMPMetadata_t inMetadata = static_cast<const RTMPMetadata_t&>(metadata); m_jobQueue.enqueue([=]() { if(!this->m_ending) { static int c_count = 0; c_count ++; auto packetTime = std::chrono::steady_clock::now(); std::vector<uint8_t> chunk; std::shared_ptr<std::vector<uint8_t>> outb = std::make_shared<std::vector<uint8_t>>(); outb->reserve(size + 64); size_t len = buf->size(); size_t tosend = std::min(len, m_outChunkSize); uint8_t* p; buf->read(&p, buf->size()); uint64_t ts = inMetadata.getData<kRTMPMetadataTimestamp>() ; const int streamId = inMetadata.getData<kRTMPMetadataMsgStreamId>(); #ifndef RTMP_CHUNK_TYPE_0_ONLY auto it = m_previousChunkData.find(streamId); if(it == m_previousChunkData.end()) { #endif // Type 0. put_byte(chunk, ( streamId & 0x1F)); put_be24(chunk, static_cast<uint32_t>(ts)); put_be24(chunk, inMetadata.getData<kRTMPMetadataMsgLength>()); put_byte(chunk, inMetadata.getData<kRTMPMetadataMsgTypeId>()); put_buff(chunk, (uint8_t*)&m_streamId, sizeof(int32_t)); // msg stream id is little-endian #ifndef RTMP_CHUNK_TYPE_0_ONLY } else { // Type 1. put_byte(chunk, RTMP_CHUNK_TYPE_1 | (streamId & 0x1F)); put_be24(chunk, static_cast<uint32_t>(ts - it->second)); // timestamp delta put_be24(chunk, inMetadata.getData<kRTMPMetadataMsgLength>()); put_byte(chunk, inMetadata.getData<kRTMPMetadataMsgTypeId>()); } #endif m_previousChunkData[streamId] = ts; put_buff(chunk, p, tosend); outb->insert(outb->end(), chunk.begin(), chunk.end()); len -= tosend; p += tosend; while(len > 0) { tosend = std::min(len, m_outChunkSize); p[-1] = RTMP_CHUNK_TYPE_3 | (streamId & 0x1F); outb->insert(outb->end(), p-1, p+tosend); p+=tosend; len-=tosend; // this->write(&outb[0], outb.size(), packetTime); // outb.clear(); } this->write(&(*outb)[0], outb->size(), packetTime, inMetadata.getData<kRTMPMetadataIsKeyframe>() ); } }); }