void WebRtcConnection::writeSsrc(char* buf, int len, unsigned int ssrc) { ELOG_DEBUG("LEN %d", len); RtpHeader *head = reinterpret_cast<RtpHeader*> (buf); RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (buf); //if it is RTCP we check it it is a compound packet if (chead->isRtcp()) { char* movingBuf = buf; int rtcpLength = 0; int totalLength = 0; do{ movingBuf+=rtcpLength; RtcpHeader *chead= reinterpret_cast<RtcpHeader*>(movingBuf); rtcpLength= (ntohs(chead->length)+1)*4; totalLength+= rtcpLength; ELOG_DEBUG("Is RTCP, prev SSRC %u, new %u, len %d ", chead->getSSRC(), ssrc, rtcpLength); chead->ssrc=htonl(ssrc); if (chead->packettype == RTCP_PS_Feedback_PT){ FirHeader *thefir = reinterpret_cast<FirHeader*>(movingBuf); if (thefir->fmt == 4){ // It is a FIR Packet, we generate it this->sendPLI(); } } } while(totalLength<len); } else { head->setSSRC(ssrc); } }
int InputProcessor::unpackageVideo(unsigned char* inBuff, int inBuffLen, unsigned char* outBuff, int* gotFrame) { if (videoUnpackager == 0) { ELOG_DEBUG("Unpackager not correctly initialized"); return -1; } int inBuffOffset = 0; *gotFrame = 0; RtpHeader* head = reinterpret_cast<RtpHeader*>(inBuff); if (head->getPayloadType() != 100) { return -1; } int l = inBuffLen - head->getHeaderLength(); inBuffOffset+=head->getHeaderLength(); erizo::RTPPayloadVP8* parsed = pars.parseVP8((unsigned char*) &inBuff[inBuffOffset], l); memcpy(outBuff, parsed->data, parsed->dataLength); if (head->getMarker()) { *gotFrame = 1; } int ret = parsed->dataLength; delete parsed; return ret; }
void MediaStream::onTransportData(std::shared_ptr<DataPacket> incoming_packet, Transport *transport) { if ((audio_sink_ == nullptr && video_sink_ == nullptr && fb_sink_ == nullptr)) { return; } std::shared_ptr<DataPacket> packet = std::make_shared<DataPacket>(*incoming_packet); if (transport->mediaType == AUDIO_TYPE) { packet->type = AUDIO_PACKET; } else if (transport->mediaType == VIDEO_TYPE) { packet->type = VIDEO_PACKET; } auto stream_ptr = shared_from_this(); worker_->task([stream_ptr, packet]{ if (!stream_ptr->pipeline_initialized_) { ELOG_DEBUG("%s message: Pipeline not initialized yet.", stream_ptr->toLog()); return; } char* buf = packet->data; RtpHeader *head = reinterpret_cast<RtpHeader*> (buf); RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (buf); if (!chead->isRtcp()) { uint32_t recvSSRC = head->getSSRC(); if (stream_ptr->isVideoSourceSSRC(recvSSRC)) { packet->type = VIDEO_PACKET; } else if (stream_ptr->isAudioSourceSSRC(recvSSRC)) { packet->type = AUDIO_PACKET; } } stream_ptr->pipeline_->read(std::move(packet)); }); }
int OutputProcessor::packageAudio(unsigned char* inBuff, int inBuffLen, unsigned char* outBuff, long int pts) { if (audioPackager == 0) { ELOG_DEBUG("No se ha inicializado el codec de output audio RTP"); return -1; } timeval time; gettimeofday(&time, NULL); long millis = (time.tv_sec * 1000) + (time.tv_usec / 1000); RtpHeader head; head.setSeqNumber(audioSeqnum_++); // head.setTimestamp(millis*8); head.setMarker(1); if (pts==0){ // head.setTimestamp(audioSeqnum_*160); head.setTimestamp(av_rescale(audioSeqnum_, (mediaInfo.audioCodec.sampleRate/1000), 1)); }else{ // head.setTimestamp(pts*8); head.setTimestamp(av_rescale(pts, mediaInfo.audioCodec.sampleRate,1000)); } head.setSSRC(44444); head.setPayloadType(mediaInfo.rtpAudioInfo.PT); // memcpy (rtpAudioBuffer_, &head, head.getHeaderLength()); // memcpy(&rtpAudioBuffer_[head.getHeaderLength()], inBuff, inBuffLen); memcpy (outBuff, &head, head.getHeaderLength()); memcpy(&outBuff[head.getHeaderLength()], inBuff, inBuffLen); // sink_->sendData(rtpBuffer_, l); // rtpReceiver_->receiveRtpData(rtpBuffer_, (inBuffLen + RTP_HEADER_LEN)); return (inBuffLen+head.getHeaderLength()); }
int main(int argc, const char * argv[]) { // data setup uint32_t first = 0xFFFF | 0x1FFFFFFF; uint32_t timestamp = 0x0128; uint32_t ssrc = 0x01 | 0x02 | 0x04 | 0x08 | 0x256; int somedata[4]; // The htonl() function converts the unsigned integer hostlong from host byte order to network byte order. // on the other side: // The ntohl() function converts the unsigned integer netlong from network byte order to host byte order. somedata[0] = htonl(first); somedata[1] = htonl(timestamp); somedata[2] = htonl(ssrc); somedata[2] = htonl(ssrc); somedata[3] = htonl(ssrc); RtpHeader* head = reinterpret_cast<RtpHeader*>(somedata); printf("version: %" PRIu8 "\n", head->getVersion()); printf("padding: %" PRIu8 "\n", head->hasPadding()); printf("extension: %" PRIu8 "\n", head->getExtension()); printf("marker: %" PRIu8 "\n", head->getMarker()); printf("payload type: %" PRIu8 "\n", head->getPayloadType()); printf("sequence number: %" PRIu16 "\n", head->getSeqNumber()); printf("timestamp %" PRIu32 "\n", head->getTimestamp()); printf("ssrc %" PRIu32 "\n", head->getSSRC()); printf("header length: %u\n", head->getHeaderLength()); return 0; }
void ExternalOutput::writeVideoData(char* buf, int len) { RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); uint16_t current_video_sequence_number = head->getSeqNumber(); if (current_video_sequence_number != last_video_sequence_number_ + 1) { // Something screwy. We should always see sequence numbers incrementing monotonically. ELOG_DEBUG("Unexpected video sequence number; current %d, previous %d", current_video_sequence_number, last_video_sequence_number_); // Restart the depacketizer so it looks for the start of a frame if (depacketizer_!= nullptr) { depacketizer_->reset(); } } last_video_sequence_number_ = current_video_sequence_number; if (first_video_timestamp_ == -1) { first_video_timestamp_ = head->getTimestamp(); } auto map_iterator = video_maps_.find(head->getPayloadType()); if (map_iterator != video_maps_.end()) { updateVideoCodec(map_iterator->second); if (map_iterator->second.encoding_name == "VP8" || map_iterator->second.encoding_name == "H264") { maybeWriteVideoPacket(buf, len); } } }
void MediaStream::onTransportData(std::shared_ptr<DataPacket> packet, Transport *transport) { if ((audio_sink_ == nullptr && video_sink_ == nullptr && fb_sink_ == nullptr)) { return; } if (transport->mediaType == AUDIO_TYPE) { packet->type = AUDIO_PACKET; } else if (transport->mediaType == VIDEO_TYPE) { packet->type = VIDEO_PACKET; } char* buf = packet->data; RtpHeader *head = reinterpret_cast<RtpHeader*> (buf); RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (buf); if (!chead->isRtcp()) { uint32_t recvSSRC = head->getSSRC(); if (isVideoSourceSSRC(recvSSRC)) { packet->type = VIDEO_PACKET; } else if (isAudioSourceSSRC(recvSSRC)) { packet->type = AUDIO_PACKET; } } if (!pipeline_initialized_) { ELOG_DEBUG("%s message: Pipeline not initialized yet.", toLog()); return; } pipeline_->read(std::move(packet)); }
int ExternalOutput::deliverVideoData_(char* buf, int len) { if (videoSourceSsrc_ == 0) { RtpHeader* h = reinterpret_cast<RtpHeader*>(buf); videoSourceSsrc_ = h->getSSRC(); } this->queueData(buf, len, VIDEO_PACKET); return 0; }
int ExternalOutput::deliverVideoData_(std::shared_ptr<dataPacket> video_packet) { std::shared_ptr<dataPacket> copied_packet = std::make_shared<dataPacket>(*video_packet); if (videoSourceSsrc_ == 0) { RtpHeader* h = reinterpret_cast<RtpHeader*>(copied_packet->data); videoSourceSsrc_ = h->getSSRC(); } this->queueData(copied_packet->data, copied_packet->length, VIDEO_PACKET); return 0; }
void ExternalOutput::writeAudioData(char* buf, int len){ RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); if (firstAudioTimestamp_ == -1) { firstAudioTimestamp_ = head->getTimestamp(); } timeval time; gettimeofday(&time, NULL); // Figure out our audio codec. if(context_->oformat->audio_codec == AV_CODEC_ID_NONE) { //We dont need any other payload at this time if(head->getPayloadType() == PCMU_8000_PT){ context_->oformat->audio_codec = AV_CODEC_ID_PCM_MULAW; } else if (head->getPayloadType() == OPUS_48000_PT) { context_->oformat->audio_codec = AV_CODEC_ID_OPUS; } } initContext(); if (audio_stream_ == NULL) { // not yet. return; } int ret = inputProcessor_->unpackageAudio(reinterpret_cast<unsigned char*>(buf), len, unpackagedAudioBuffer_); if (ret <= 0) return; long long currentTimestamp = head->getTimestamp(); if (currentTimestamp - firstAudioTimestamp_ < 0) { // we wrapped. add 2^32 to correct this. We only handle a single wrap around since that's 13 hours of recording, minimum. currentTimestamp += 0xFFFFFFFF; } long long timestampToWrite = (currentTimestamp - firstAudioTimestamp_) / (audio_stream_->codec->time_base.den / audio_stream_->time_base.den); // Adjust for our start time offset timestampToWrite += audioOffsetMsec_ / (1000 / audio_stream_->time_base.den); // in practice, our timebase den is 1000, so this operation is a no-op. /* ELOG_DEBUG("Writing audio frame %d with timestamp %u, normalized timestamp %u, audio offset msec %u, length %d, input timebase: %d/%d, target timebase: %d/%d", */ /* head->getSeqNumber(), head->getTimestamp(), timestampToWrite, audioOffsetMsec_, ret, */ /* audio_stream_->codec->time_base.num, audio_stream_->codec->time_base.den, // timebase we requested */ /* audio_stream_->time_base.num, audio_stream_->time_base.den); // actual timebase */ AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = unpackagedAudioBuffer_; avpkt.size = ret; avpkt.pts = timestampToWrite; avpkt.stream_index = 1; av_write_frame(context_, &avpkt); av_free_packet(&avpkt); }
int ExternalOutput::deliverVideoData_(std::shared_ptr<DataPacket> video_packet) { if (video_source_ssrc_ == 0) { RtpHeader* h = reinterpret_cast<RtpHeader*>(video_packet->data); video_source_ssrc_ = h->getSSRC(); } std::shared_ptr<DataPacket> copied_packet = std::make_shared<DataPacket>(*video_packet); copied_packet->type = VIDEO_PACKET; queueDataAsync(copied_packet); return 0; }
void MediaStream::read(std::shared_ptr<DataPacket> packet) { char* buf = packet->data; int len = packet->length; // PROCESS RTCP RtpHeader *head = reinterpret_cast<RtpHeader*> (buf); RtcpHeader *chead = reinterpret_cast<RtcpHeader*> (buf); uint32_t recvSSRC = 0; if (!chead->isRtcp()) { recvSSRC = head->getSSRC(); } else if (chead->packettype == RTCP_Sender_PT) { // Sender Report recvSSRC = chead->getSSRC(); } // DELIVER FEEDBACK (RR, FEEDBACK PACKETS) if (chead->isFeedback()) { if (fb_sink_ != nullptr && should_send_feedback_) { fb_sink_->deliverFeedback(std::move(packet)); } } else { // RTP or RTCP Sender Report if (bundle_) { // Check incoming SSRC // Deliver data if (isVideoSourceSSRC(recvSSRC)) { parseIncomingPayloadType(buf, len, VIDEO_PACKET); video_sink_->deliverVideoData(std::move(packet)); } else if (isAudioSourceSSRC(recvSSRC)) { parseIncomingPayloadType(buf, len, AUDIO_PACKET); audio_sink_->deliverAudioData(std::move(packet)); } else { ELOG_DEBUG("%s read video unknownSSRC: %u, localVideoSSRC: %u, localAudioSSRC: %u", toLog(), recvSSRC, this->getVideoSourceSSRC(), this->getAudioSourceSSRC()); } } else { if (packet->type == AUDIO_PACKET && audio_sink_ != nullptr) { parseIncomingPayloadType(buf, len, AUDIO_PACKET); // Firefox does not send SSRC in SDP if (getAudioSourceSSRC() == 0) { ELOG_DEBUG("%s discoveredAudioSourceSSRC:%u", toLog(), recvSSRC); this->setAudioSourceSSRC(recvSSRC); } audio_sink_->deliverAudioData(std::move(packet)); } else if (packet->type == VIDEO_PACKET && video_sink_ != nullptr) { parseIncomingPayloadType(buf, len, VIDEO_PACKET); // Firefox does not send SSRC in SDP if (getVideoSourceSSRC() == 0) { ELOG_DEBUG("%s discoveredVideoSourceSSRC:%u", toLog(), recvSSRC); this->setVideoSourceSSRC(recvSSRC); } // change ssrc for RTP packets, don't touch here if RTCP video_sink_->deliverVideoData(std::move(packet)); } } // if not bundle } // if not Feedback }
void SRPacketHandler::handleRtpPacket(std::shared_ptr<dataPacket> packet) { RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); uint32_t ssrc = head->getSSRC(); auto sr_selected_info_iter = sr_info_map_.find(ssrc); std::shared_ptr<SRInfo> selected_info; if (sr_selected_info_iter == sr_info_map_.end()) { ELOG_DEBUG("message: Inserting new SSRC in sr_info_map, ssrc: %u", ssrc); sr_info_map_[ssrc] = std::make_shared<SRInfo>(); } selected_info = sr_info_map_[ssrc]; selected_info->sent_packets++; selected_info->sent_octets += (packet->length - head->getHeaderLength()); }
bool RtcpRrGenerator::isRetransmitOfOldPacket(std::shared_ptr<dataPacket> packet) { RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); if (!RtpUtils::sequenceNumberLessThan(head->getSeqNumber(), rr_info_.max_seq) || rr_info_.jitter.jitter == 0) { return false; } int64_t time_diff_ms = static_cast<uint32_t>(packet->received_time_ms) - rr_info_.last_recv_ts; int64_t timestamp_diff = static_cast<int32_t>(head->getTimestamp() - rr_info_.last_rtp_ts); uint16_t clock_rate = type_ == VIDEO_PACKET ? getVideoClockRate(head->getPayloadType()) : getAudioClockRate(head->getPayloadType()); int64_t rtp_time_stamp_diff_ms = timestamp_diff / clock_rate; int64_t max_delay_ms = ((2 * rr_info_.jitter.jitter) / clock_rate); return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms; }
bool RtcpRrGenerator::handleRtpPacket(std::shared_ptr<dataPacket> packet) { RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); if (ssrc_ != head->getSSRC()) { ELOG_DEBUG("message: handleRtpPacket ssrc not found, ssrc: %u", head->getSSRC()); return false; } uint16_t seq_num = head->getSeqNumber(); rr_info_.packets_received++; if (rr_info_.base_seq == -1) { rr_info_.base_seq = head->getSeqNumber(); } if (rr_info_.max_seq == -1) { rr_info_.max_seq = seq_num; } else if (!RtpUtils::sequenceNumberLessThan(seq_num, rr_info_.max_seq)) { if (seq_num < rr_info_.max_seq) { rr_info_.cycle++; } rr_info_.max_seq = seq_num; } rr_info_.extended_seq = (rr_info_.cycle << 16) | rr_info_.max_seq; uint16_t clock_rate = type_ == VIDEO_PACKET ? getVideoClockRate(head->getPayloadType()) : getAudioClockRate(head->getPayloadType()); if (head->getTimestamp() != rr_info_.last_rtp_ts && !isRetransmitOfOldPacket(packet)) { int transit_time = static_cast<int>((packet->received_time_ms * clock_rate) - head->getTimestamp()); int delta = abs(transit_time - rr_info_.jitter.transit_time); if (rr_info_.jitter.transit_time != 0 && delta < MAX_DELAY) { rr_info_.jitter.jitter += (1. / 16.) * (static_cast<double>(delta) - rr_info_.jitter.jitter); } rr_info_.jitter.transit_time = transit_time; } rr_info_.last_rtp_ts = head->getTimestamp(); rr_info_.last_recv_ts = static_cast<uint32_t>(packet->received_time_ms); uint64_t now = ClockUtils::timePointToMs(clock_->now()); if (rr_info_.next_packet_ms == 0) { // Schedule the first packet uint16_t selected_interval = selectInterval(); rr_info_.next_packet_ms = now + selected_interval; return false; } if (now >= rr_info_.next_packet_ms) { ELOG_DEBUG("message: should send packet, ssrc: %u", ssrc_); return true; } return false; }
int OneToManyTranscoder::deliverVideoData_(char* buf, int len) { memcpy(sendVideoBuffer_, buf, len); RtpHeader* theHead = reinterpret_cast<RtpHeader*>(buf); // ELOG_DEBUG("extension %d pt %u", theHead->getExtension(), // theHead->getPayloadType()); if (theHead->getPayloadType() == 100) { ip_->deliverVideoData(sendVideoBuffer_, len); } else { this->receiveRtpData((unsigned char*) buf, len); } sentPackets_++; return 0; }
void MediaStream::changeDeliverPayloadType(DataPacket *dp, packetType type) { RtpHeader* h = reinterpret_cast<RtpHeader*>(dp->data); RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(dp->data); if (!chead->isRtcp()) { int internalPT = h->getPayloadType(); int externalPT = internalPT; if (type == AUDIO_PACKET) { externalPT = remote_sdp_->getAudioExternalPT(internalPT); } else if (type == VIDEO_PACKET) { externalPT = remote_sdp_->getVideoExternalPT(externalPT); } if (internalPT != externalPT) { h->setPayloadType(externalPT); } } }
void ExternalOutput::writeAudioData(char* buf, int len){ RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); uint16_t currentAudioSequenceNumber = head->getSeqNumber(); if (currentAudioSequenceNumber != lastAudioSequenceNumber_ + 1) { // Something screwy. We should always see sequence numbers incrementing monotonically. ELOG_DEBUG("Unexpected audio sequence number; current %d, previous %d", currentAudioSequenceNumber, lastAudioSequenceNumber_); } lastAudioSequenceNumber_ = currentAudioSequenceNumber; if (firstAudioTimestamp_ == -1) { firstAudioTimestamp_ = head->getTimestamp(); } timeval time; gettimeofday(&time, NULL); // Figure out our audio codec. if(context_->oformat->audio_codec == AV_CODEC_ID_NONE) { //We dont need any other payload at this time if(head->getPayloadType() == PCMU_8000_PT){ context_->oformat->audio_codec = AV_CODEC_ID_PCM_MULAW; } else if (head->getPayloadType() == OPUS_48000_PT) { context_->oformat->audio_codec = AV_CODEC_ID_OPUS; } } initContext(); if (audio_stream_ == NULL) { // not yet. return; } long long currentTimestamp = head->getTimestamp(); if (currentTimestamp - firstAudioTimestamp_ < 0) { // we wrapped. add 2^32 to correct this. We only handle a single wrap around since that's 13 hours of recording, minimum. currentTimestamp += 0xFFFFFFFF; } long long timestampToWrite = (currentTimestamp - firstAudioTimestamp_) / (audio_stream_->codec->time_base.den / audio_stream_->time_base.den); // Adjust for our start time offset timestampToWrite += audioOffsetMsec_ / (1000 / audio_stream_->time_base.den); // in practice, our timebase den is 1000, so this operation is a no-op. /* ELOG_DEBUG("Writing audio frame %d with timestamp %u, normalized timestamp %u, audio offset msec %u, length %d, input timebase: %d/%d, target timebase: %d/%d", */ /* head->getSeqNumber(), head->getTimestamp(), timestampToWrite, audioOffsetMsec_, ret, */ /* audio_stream_->codec->time_base.num, audio_stream_->codec->time_base.den, // timebase we requested */ /* audio_stream_->time_base.num, audio_stream_->time_base.den); // actual timebase */ AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = (uint8_t*) buf + head->getHeaderLength(); avpkt.size = len - head->getHeaderLength(); avpkt.pts = timestampToWrite; avpkt.stream_index = 1; av_write_frame(context_, &avpkt); av_free_packet(&avpkt); }
void RtcpFeedbackGenerationHandler::read(Context *ctx, std::shared_ptr<DataPacket> packet) { // Pass packets to RR and NACK Generator RtcpHeader *chead = reinterpret_cast<RtcpHeader*>(packet->data); if (!initialized_) { ctx->fireRead(std::move(packet)); return; } if (chead->getPacketType() == RTCP_Sender_PT) { uint32_t ssrc = chead->getSSRC(); auto generator_it = generators_map_.find(ssrc); if (generator_it != generators_map_.end()) { generator_it->second->rr_generator->handleSr(packet); } else { ELOG_DEBUG("message: no RrGenerator found, ssrc: %u", ssrc); } ctx->fireRead(std::move(packet)); return; } bool should_send_rr = false; bool should_send_nack = false; if (!chead->isRtcp()) { RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); uint32_t ssrc = head->getSSRC(); auto generator_it = generators_map_.find(ssrc); if (generator_it != generators_map_.end()) { should_send_rr = generator_it->second->rr_generator->handleRtpPacket(packet); if (nacks_enabled_) { should_send_nack = generator_it->second->nack_generator->handleRtpPacket(packet); } } else { ELOG_DEBUG("message: no Generator found, ssrc: %u", ssrc); } if (should_send_rr || should_send_nack) { ELOG_DEBUG("message: Should send Rtcp, ssrc %u", ssrc); std::shared_ptr<DataPacket> rtcp_packet = generator_it->second->rr_generator->generateReceiverReport(); if (nacks_enabled_ && generator_it->second->nack_generator != nullptr) { generator_it->second->nack_generator->addNackPacketToRr(rtcp_packet); } ctx->fireWrite(std::move(rtcp_packet)); } } ctx->fireRead(std::move(packet)); }
// parses incoming payload type, replaces occurence in buf void MediaStream::parseIncomingPayloadType(char *buf, int len, packetType type) { RtcpHeader* chead = reinterpret_cast<RtcpHeader*>(buf); RtpHeader* h = reinterpret_cast<RtpHeader*>(buf); if (!chead->isRtcp()) { int externalPT = h->getPayloadType(); int internalPT = externalPT; if (type == AUDIO_PACKET) { internalPT = remote_sdp_->getAudioInternalPT(externalPT); } else if (type == VIDEO_PACKET) { internalPT = remote_sdp_->getVideoInternalPT(externalPT); } if (externalPT != internalPT) { h->setPayloadType(internalPT); } else { // ELOG_WARN("onTransportData did not find mapping for %i", externalPT); } } }
void RRGenerationHandler::handleRtpPacket(std::shared_ptr<dataPacket> packet) { RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); auto rr_packet_pair = rr_info_map_.find(head->getSSRC()); if (rr_packet_pair == rr_info_map_.end()) { ELOG_DEBUG("%s message: handleRtpPacket ssrc not found, ssrc: %u", connection_->toLog(), head->getSSRC()); return; } std::shared_ptr<RRPackets> selected_packet_info = rr_packet_pair->second; uint16_t seq_num = head->getSeqNumber(); selected_packet_info->packets_received++; if (selected_packet_info->base_seq == -1) { selected_packet_info->ssrc = head->getSSRC(); selected_packet_info->base_seq = head->getSeqNumber(); } if (selected_packet_info->max_seq == -1) { selected_packet_info->max_seq = seq_num; } else if (!rtpSequenceLessThan(seq_num, selected_packet_info->max_seq)) { if (seq_num < selected_packet_info->max_seq) { selected_packet_info->cycle++; } selected_packet_info->max_seq = seq_num; } selected_packet_info->extended_seq = (selected_packet_info->cycle << 16) | selected_packet_info->max_seq; uint16_t clock_rate = selected_packet_info->type == VIDEO_PACKET ? getVideoClockRate(head->getPayloadType()) : getAudioClockRate(head->getPayloadType()); if (head->getTimestamp() != selected_packet_info->last_rtp_ts && !isRetransmitOfOldPacket(packet, selected_packet_info)) { int transit_time = static_cast<int>((packet->received_time_ms * clock_rate) - head->getTimestamp()); int delta = abs(transit_time - selected_packet_info->jitter.transit_time); if (selected_packet_info->jitter.transit_time != 0 && delta < MAX_DELAY) { selected_packet_info->jitter.jitter += (1. / 16.) * (static_cast<double>(delta) - selected_packet_info->jitter.jitter); } selected_packet_info->jitter.transit_time = transit_time; } selected_packet_info->last_rtp_ts = head->getTimestamp(); selected_packet_info->last_recv_ts = static_cast<uint32_t>(packet->received_time_ms); uint64_t now = ClockUtils::timePointToMs(clock::now()); if (selected_packet_info->next_packet_ms == 0) { // Schedule the first packet uint16_t selected_interval = selectInterval(selected_packet_info); selected_packet_info->next_packet_ms = now + selected_interval; return; } if (now >= selected_packet_info->next_packet_ms) { sendRR(selected_packet_info); } }
void ExternalOutput::writeAudioData(char* buf, int len) { RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); uint16_t currentAudioSequenceNumber = head->getSeqNumber(); if (first_audio_timestamp_ != -1 && currentAudioSequenceNumber != lastAudioSequenceNumber_ + 1) { // Something screwy. We should always see sequence numbers incrementing monotonically. ELOG_DEBUG("Unexpected audio sequence number; current %d, previous %d", currentAudioSequenceNumber, lastAudioSequenceNumber_); } lastAudioSequenceNumber_ = currentAudioSequenceNumber; if (first_audio_timestamp_ == -1) { first_audio_timestamp_ = head->getTimestamp(); } // Figure out our audio codec. if (context_->oformat->audio_codec == AV_CODEC_ID_NONE) { // We dont need any other payload at this time if (head->getPayloadType() == PCMU_8000_PT) { context_->oformat->audio_codec = AV_CODEC_ID_PCM_MULAW; } else if (head->getPayloadType() == OPUS_48000_PT) { context_->oformat->audio_codec = AV_CODEC_ID_OPUS; } } initContext(); if (audio_stream_ == NULL) { // not yet. return; } long long currentTimestamp = head->getTimestamp(); // NOLINT if (currentTimestamp - first_audio_timestamp_ < 0) { // we wrapped. add 2^32 to correct this. We only handle a single wrap around // since that's 13 hours of recording, minimum. currentTimestamp += 0xFFFFFFFF; } long long timestampToWrite = (currentTimestamp - first_audio_timestamp_) / // NOLINT (audio_stream_->codec->sample_rate / audio_stream_->time_base.den); // generally 48000 / 1000 for the denominator portion, at least for opus // Adjust for our start time offset // in practice, our timebase den is 1000, so this operation is a no-op. timestampToWrite += audio_offset_ms_ / (1000 / audio_stream_->time_base.den); AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = reinterpret_cast<uint8_t*>(buf) + head->getHeaderLength(); avpkt.size = len - head->getHeaderLength(); avpkt.pts = timestampToWrite; avpkt.stream_index = 1; av_interleaved_write_frame(context_, &avpkt); // takes ownership of the packet }
// parses incoming payload type, replaces occurence in buf void WebRtcConnection::parseIncomingPayloadType(char *buf, int len, packetType type) { RtcpHeader* chead = reinterpret_cast<RtcpHeader*>(buf); RtpHeader* h = reinterpret_cast<RtpHeader*>(buf); if (!chead->isRtcp()) { int externalPT = h->getPayloadType(); int internalPT = externalPT; if (type == AUDIO_PACKET) { internalPT = remoteSdp_.getAudioInternalPT(externalPT); } else if (type == VIDEO_PACKET) { internalPT = remoteSdp_.getVideoInternalPT(externalPT); } if (externalPT != internalPT) { h->setPayloadType(internalPT); // ELOG_ERROR("onTransportData mapping %i to %i", externalPT, internalPT); } else { // ELOG_ERROR("onTransportData did not find mapping for %i", externalPT); } } }
int OutputProcessor::packageVideo(unsigned char* inBuff, int buffSize, unsigned char* outBuff, long int pts) { if (videoPackager == 0) { ELOG_DEBUG("No se ha inicailizado el codec de output vĂdeo RTP"); return -1; } // ELOG_DEBUG("To packetize %u", buffSize); if (buffSize <= 0) return -1; RtpVP8Fragmenter frag(inBuff, buffSize, 1100); bool lastFrame = false; unsigned int outlen = 0; timeval time; gettimeofday(&time, NULL); long millis = (time.tv_sec * 1000) + (time.tv_usec / 1000); // timestamp_ += 90000 / mediaInfo.videoCodec.frameRate; //int64_t pts = av_rescale(lastPts_, 1000000, (long int)video_time_base_); do { outlen = 0; frag.getPacket(outBuff, &outlen, &lastFrame); RtpHeader rtpHeader; rtpHeader.setMarker(lastFrame?1:0); rtpHeader.setSeqNumber(seqnum_++); if (pts==0){ rtpHeader.setTimestamp(av_rescale(millis, 90000, 1000)); }else{ rtpHeader.setTimestamp(av_rescale(pts, 90000, 1000)); } rtpHeader.setSSRC(55543); rtpHeader.setPayloadType(100); memcpy(rtpBuffer_, &rtpHeader, rtpHeader.getHeaderLength()); memcpy(&rtpBuffer_[rtpHeader.getHeaderLength()],outBuff, outlen); int l = outlen + rtpHeader.getHeaderLength(); // sink_->sendData(rtpBuffer_, l); rtpReceiver_->receiveRtpData(rtpBuffer_, l); } while (!lastFrame); return 0; }
int WebRtcConnection::deliverVideoData_(char* buf, int len) { writeSsrc(buf, len, this->getVideoSinkSSRC()); if (videoTransport_ != NULL) { if (videoEnabled_ == true) { RtpHeader* h = reinterpret_cast<RtpHeader*>(buf); if (h->getPayloadType() == RED_90000_PT && !remoteSdp_.supportPayloadType(RED_90000_PT)) { // This is a RED/FEC payload, but our remote endpoint doesn't support that (most likely because it's firefox :/ ) // Let's go ahead and run this through our fec receiver to convert it to raw VP8 webrtc::RTPHeader hackyHeader; hackyHeader.headerLength = h->getHeaderLength(); hackyHeader.sequenceNumber = h->getSeqNumber(); // FEC copies memory, manages its own memory, including memory passed in callbacks (in the callback, be sure to memcpy out of webrtc's buffers if (fec_receiver_.AddReceivedRedPacket(hackyHeader, (const uint8_t*) buf, len, ULP_90000_PT) == 0) { fec_receiver_.ProcessReceivedFec(); } } else { this->queueData(0, buf, len, videoTransport_); } } } return len; }
void ExternalOutput::maybeWriteVideoPacket(char* buf, int len) { RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); depacketizer_->fetchPacket((unsigned char*)buf, len); bool deliver = depacketizer_->processPacket(); initContext(); if (video_stream_ == nullptr) { // could not init our context yet. return; } if (deliver) { long long current_timestamp = head->getTimestamp(); // NOLINT if (current_timestamp - first_video_timestamp_ < 0) { // we wrapped. add 2^32 to correct this. // We only handle a single wrap around since that's ~13 hours of recording, minimum. current_timestamp += 0xFFFFFFFF; } // All of our video offerings are using a 90khz clock. long long timestamp_to_write = (current_timestamp - first_video_timestamp_) / // NOLINT (video_map_.clock_rate / video_stream_->time_base.den); // Adjust for our start time offset // in practice, our timebase den is 1000, so this operation is a no-op. timestamp_to_write += video_offset_ms_ / (1000 / video_stream_->time_base.den); AVPacket av_packet; av_init_packet(&av_packet); av_packet.data = depacketizer_->frame(); av_packet.size = depacketizer_->frameSize(); av_packet.pts = timestamp_to_write; av_packet.stream_index = 0; av_interleaved_write_frame(context_, &av_packet); // takes ownership of the packet depacketizer_->reset(); } }
bool RtcpNackGenerator::handleRtpPacket(std::shared_ptr<dataPacket> packet) { if (packet->type != VIDEO_PACKET) { return false; } RtpHeader *head = reinterpret_cast<RtpHeader*>(packet->data); uint16_t seq_num = head->getSeqNumber(); if (head->getSSRC() != ssrc_) { ELOG_DEBUG("message: handleRtpPacket Unknown SSRC, ssrc: %u", head->getSSRC()); return false; } if (!initialized_) { highest_seq_num_ = seq_num; initialized_ = true; return 0; } if (seq_num == highest_seq_num_) { return false; } // TODO(pedro) Consider clearing the nack list if this is a keyframe if (RtpUtils::sequenceNumberLessThan(seq_num, highest_seq_num_)) { ELOG_DEBUG("message: packet out of order, ssrc: %u, seq_num: %u, highest_seq_num: %u", seq_num, highest_seq_num_, ssrc_); // Look for it in nack list, remove it if its there auto nack_info = std::find_if(nack_info_list_.begin(), nack_info_list_.end(), [seq_num](NackInfo& current_nack) { return (current_nack.seq_num == seq_num); }); if (nack_info != nack_info_list_.end()) { ELOG_DEBUG("message: Recovered Packet %u", seq_num); nack_info_list_.erase(nack_info); } return false; } bool available_nacks = addNacks(seq_num); highest_seq_num_ = seq_num; return available_nacks; }
void ExternalOutput::writeAudioData(char* buf, int len) { RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); uint16_t current_audio_sequence_number = head->getSeqNumber(); if (first_audio_timestamp_ != -1 && current_audio_sequence_number != last_audio_sequence_number_ + 1) { // Something screwy. We should always see sequence numbers incrementing monotonically. ELOG_DEBUG("Unexpected audio sequence number; current %d, previous %d", current_audio_sequence_number, last_audio_sequence_number_); } last_audio_sequence_number_ = current_audio_sequence_number; if (first_audio_timestamp_ == -1) { first_audio_timestamp_ = head->getTimestamp(); } auto map_iterator = audio_maps_.find(head->getPayloadType()); if (map_iterator != audio_maps_.end()) { updateAudioCodec(map_iterator->second); } initContext(); if (audio_stream_ == nullptr) { // not yet. return; } long long current_timestamp = head->getTimestamp(); // NOLINT if (current_timestamp - first_audio_timestamp_ < 0) { // we wrapped. add 2^32 to correct this. We only handle a single wrap around // since that's 13 hours of recording, minimum. current_timestamp += 0xFFFFFFFF; } long long timestamp_to_write = (current_timestamp - first_audio_timestamp_) / // NOLINT (audio_stream_->codec->sample_rate / audio_stream_->time_base.den); // generally 48000 / 1000 for the denominator portion, at least for opus // Adjust for our start time offset // in practice, our timebase den is 1000, so this operation is a no-op. timestamp_to_write += audio_offset_ms_ / (1000 / audio_stream_->time_base.den); AVPacket av_packet; av_init_packet(&av_packet); av_packet.data = reinterpret_cast<uint8_t*>(buf) + head->getHeaderLength(); av_packet.size = len - head->getHeaderLength(); av_packet.pts = timestamp_to_write; av_packet.stream_index = 1; av_interleaved_write_frame(context_, &av_packet); // takes ownership of the packet }
void ExternalOutput::queueData(char* buffer, int length, packetType type) { if (!recording_) { return; } RtcpHeader *head = reinterpret_cast<RtcpHeader*>(buffer); if (head->isRtcp()) { return; } if (first_data_received_ == time_point()) { first_data_received_ = clock::now(); if (getAudioSinkSSRC() == 0) { ELOG_DEBUG("No audio detected"); audio_map_ = RtpMap{0, "PCMU", 8000, AUDIO_TYPE, 1}; audio_codec_ = AV_CODEC_ID_PCM_MULAW; } } if (need_to_send_fir_ && video_source_ssrc_) { sendFirPacket(); need_to_send_fir_ = false; } if (type == VIDEO_PACKET) { RtpHeader* h = reinterpret_cast<RtpHeader*>(buffer); uint8_t payloadtype = h->getPayloadType(); if (video_offset_ms_ == -1) { video_offset_ms_ = ClockUtils::durationToMs(clock::now() - first_data_received_); ELOG_DEBUG("File %s, video offset msec: %llu", context_->filename, video_offset_ms_); video_queue_.setTimebase(video_maps_[payloadtype].clock_rate); } // If this is a red header, let's push it to our fec_receiver_, which will spit out frames in one // of our other callbacks. // Otherwise, just stick it straight into the video queue. if (payloadtype == RED_90000_PT) { // The only things AddReceivedRedPacket uses are headerLength and sequenceNumber. // Unfortunately the amount of crap // we would have to pull in from the WebRtc project to fully construct // a webrtc::RTPHeader object is obscene. So // let's just do this hacky fix. webrtc::RTPHeader hacky_header; hacky_header.headerLength = h->getHeaderLength(); hacky_header.sequenceNumber = h->getSeqNumber(); // AddReceivedRedPacket returns 0 if there's data to process if (0 == fec_receiver_->AddReceivedRedPacket(hacky_header, (const uint8_t*)buffer, length, ULP_90000_PT)) { fec_receiver_->ProcessReceivedFec(); } } else { video_queue_.pushPacket(buffer, length); } } else { if (audio_offset_ms_ == -1) { audio_offset_ms_ = ClockUtils::durationToMs(clock::now() - first_data_received_); ELOG_DEBUG("File %s, audio offset msec: %llu", context_->filename, audio_offset_ms_); // Let's also take a moment to set our audio queue timebase. RtpHeader* h = reinterpret_cast<RtpHeader*>(buffer); if (h->getPayloadType() == PCMU_8000_PT) { audio_queue_.setTimebase(8000); } else if (h->getPayloadType() == OPUS_48000_PT) { audio_queue_.setTimebase(48000); } } audio_queue_.pushPacket(buffer, length); } if (audio_queue_.hasData() || video_queue_.hasData()) { // One or both of our queues has enough data to write stuff out. Notify our writer. cond_.notify_one(); } }
void ExternalOutput::queueData(char* buffer, int length, packetType type){ if (!recording_) { return; } RtcpHeader *head = reinterpret_cast<RtcpHeader*>(buffer); if (head->isRtcp()){ return; } if (firstDataReceived_ == -1) { timeval time; gettimeofday(&time, NULL); firstDataReceived_ = (time.tv_sec * 1000) + (time.tv_usec / 1000); if (this->getAudioSinkSSRC() == 0){ ELOG_DEBUG("No audio detected"); context_->oformat->audio_codec = AV_CODEC_ID_PCM_MULAW; } } timeval time; gettimeofday(&time, NULL); unsigned long long millis = (time.tv_sec * 1000) + (time.tv_usec / 1000); if (millis -lastFullIntraFrameRequest_ >FIR_INTERVAL_MS){ this->sendFirPacket(); lastFullIntraFrameRequest_ = millis; } if (type == VIDEO_PACKET){ if(this->videoOffsetMsec_ == -1) { videoOffsetMsec_ = ((time.tv_sec * 1000) + (time.tv_usec / 1000)) - firstDataReceived_; ELOG_DEBUG("File %s, video offset msec: %llu", context_->filename, videoOffsetMsec_); } // If this is a red header, let's push it to our fec_receiver_, which will spit out frames in one of our other callbacks. // Otherwise, just stick it straight into the video queue. RtpHeader* h = reinterpret_cast<RtpHeader*>(buffer); if (h->getPayloadType() == RED_90000_PT) { // The only things AddReceivedRedPacket uses are headerLength and sequenceNumber. Unfortunately the amount of crap // we would have to pull in from the WebRtc project to fully construct a webrtc::RTPHeader object is obscene. So // let's just do this hacky fix. webrtc::RTPHeader hackyHeader; hackyHeader.headerLength = h->getHeaderLength(); hackyHeader.sequenceNumber = h->getSeqNumber(); fec_receiver_.AddReceivedRedPacket(hackyHeader, (const uint8_t*)buffer, length, ULP_90000_PT); fec_receiver_.ProcessReceivedFec(); } else { videoQueue_.pushPacket(buffer, length); } }else{ if(this->audioOffsetMsec_ == -1) { audioOffsetMsec_ = ((time.tv_sec * 1000) + (time.tv_usec / 1000)) - firstDataReceived_; ELOG_DEBUG("File %s, audio offset msec: %llu", context_->filename, audioOffsetMsec_); } audioQueue_.pushPacket(buffer, length); } if( audioQueue_.hasData() || videoQueue_.hasData()) { // One or both of our queues has enough data to write stuff out. Notify our writer. cond_.notify_one(); } }