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 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); }
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 }
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 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); } }
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 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); } } }
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; }
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(); } }
void ExternalOutput::writeVideoData(char* buf, int len){ RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); uint16_t currentVideoSeqNumber = head->getSeqNumber(); if (currentVideoSeqNumber != lastVideoSequenceNumber_ + 1) { // Something screwy. We should always see sequence numbers incrementing monotonically. ELOG_DEBUG("Unexpected video sequence number; current %d, previous %d", currentVideoSeqNumber, lastVideoSequenceNumber_); // Set our search state to look for the start of a frame, and discard what we currently have (if anything). it's now worthless. vp8SearchState_ = lookingForStart; unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; } lastVideoSequenceNumber_ = currentVideoSeqNumber; if (firstVideoTimestamp_ == -1) { firstVideoTimestamp_ = head->getTimestamp(); } // TODO we should be tearing off RTP padding here, if it exists. But WebRTC currently does not use padding. RtpVP8Parser parser; erizo::RTPPayloadVP8* payload = parser.parseVP8(reinterpret_cast<unsigned char*>(buf + head->getHeaderLength()), len - head->getHeaderLength()); bool endOfFrame = (head->getMarker() > 0); bool startOfFrame = payload->beginningOfPartition; bool deliver = false; switch(vp8SearchState_) { case lookingForStart: if(startOfFrame && endOfFrame) { // This packet is a standalone frame. Send it on. Look for start. unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; deliver = true; } else if (!startOfFrame && !endOfFrame) { // This is neither the start nor the end of a frame. Reset our buffers. Look for start. unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; } else if (startOfFrame && !endOfFrame) { // Found start frame. Copy to buffers. Look for our end. memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; vp8SearchState_ = lookingForEnd; } else { // (!startOfFrame && endOfFrame) // We got the end of a frame. Reset our buffers. unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; } break; case lookingForEnd: if(startOfFrame && endOfFrame) { // Unexpected. We were looking for the end of a frame, and got a whole new frame. // Reset our buffers, send this frame on, and go to the looking for start state. vp8SearchState_ = lookingForStart; unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; deliver = true; } else if (!startOfFrame && !endOfFrame) { // This is neither the start nor the end. Add it to our unpackage buffer. memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; } else if (startOfFrame && !endOfFrame) { // Unexpected. We got the start of a frame. Clear out our buffer, toss this payload in, and continue looking for the end. unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; } else { // (!startOfFrame && endOfFrame) // Got the end of a frame. Let's deliver and start looking for the start of a frame. vp8SearchState_ = lookingForStart; memcpy(unpackagedBufferpart_, payload->data, payload->dataLength); unpackagedSize_ += payload->dataLength; unpackagedBufferpart_ += payload->dataLength; deliver = true; } break; } delete payload; //ELOG_DEBUG("Parsed VP8 payload, endOfFrame: %d, startOfFrame: %d, partitionId: %d", endOfFrame, startOfFrame, partitionId); this->initContext(); if (video_stream_ == NULL) { // could not init our context yet. return; } if (deliver) { unpackagedBufferpart_ -= unpackagedSize_; long long currentTimestamp = head->getTimestamp(); if (currentTimestamp - firstVideoTimestamp_ < 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 - firstVideoTimestamp_) / (90000 / video_stream_->time_base.den); // All of our video offerings are using a 90khz clock. // Adjust for our start time offset timestampToWrite += videoOffsetMsec_ / (1000 / video_stream_->time_base.den); // in practice, our timebase den is 1000, so this operation is a no-op. /* ELOG_DEBUG("Writing video frame %d with timestamp %u, normalized timestamp %u, video offset msec %u, length %d, input timebase: %d/%d, target timebase: %d/%d", */ /* head->getSeqNumber(), head->getTimestamp(), timestampToWrite, videoOffsetMsec_, unpackagedSize_, */ /* video_stream_->codec->time_base.num, video_stream_->codec->time_base.den, // timebase we requested */ /* video_stream_->time_base.num, video_stream_->time_base.den); // actual timebase */ AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = unpackagedBufferpart_; avpkt.size = unpackagedSize_; avpkt.pts = timestampToWrite; avpkt.stream_index = 0; av_write_frame(context_, &avpkt); av_free_packet(&avpkt); unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; } }
void ExternalOutput::writeVideoData(char* buf, int len){ RtpHeader* head = reinterpret_cast<RtpHeader*>(buf); if (head->getPayloadType() == RED_90000_PT) { int totalLength = head->getHeaderLength(); int rtpHeaderLength = totalLength; RedHeader *redhead = reinterpret_cast<RedHeader*>(buf + totalLength); if (redhead->payloadtype == VP8_90000_PT) { while (redhead->follow) { totalLength += redhead->getLength() + 4; // RED header redhead = reinterpret_cast<RedHeader*>(buf + totalLength); } // Parse RED packet to VP8 packet. // Copy RTP header memcpy(deliverMediaBuffer_, buf, rtpHeaderLength); // Copy payload data memcpy(deliverMediaBuffer_ + totalLength, buf + totalLength + 1, len - totalLength - 1); // Copy payload type RtpHeader *mediahead = reinterpret_cast<RtpHeader*>(deliverMediaBuffer_); mediahead->setPayloadType(redhead->payloadtype); buf = reinterpret_cast<char*>(deliverMediaBuffer_); len = len - 1 - totalLength + rtpHeaderLength; } } if (firstVideoTimestamp_ == -1) { firstVideoTimestamp_ = head->getTimestamp(); } int gotUnpackagedFrame = false; int ret = inputProcessor_->unpackageVideo(reinterpret_cast<unsigned char*>(buf), len, unpackagedBufferpart_, &gotUnpackagedFrame); if (ret < 0){ ELOG_ERROR("Error Unpackaging Video"); return; } initContext(); if (video_stream_ == NULL) { // could not init our context yet. return; } unpackagedSize_ += ret; unpackagedBufferpart_ += ret; if (gotUnpackagedFrame) { unpackagedBufferpart_ -= unpackagedSize_; long long currentTimestamp = head->getTimestamp(); if (currentTimestamp - firstVideoTimestamp_ < 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 - firstVideoTimestamp_) / (90000 / video_stream_->time_base.den); // All of our video offerings are using a 90khz clock. // Adjust for our start time offset timestampToWrite += videoOffsetMsec_ / (1000 / video_stream_->time_base.den); // in practice, our timebase den is 1000, so this operation is a no-op. /* ELOG_DEBUG("Writing video frame %d with timestamp %u, normalized timestamp %u, video offset msec %u, length %d, input timebase: %d/%d, target timebase: %d/%d", */ /* head->getSeqNumber(), head->getTimestamp(), timestampToWrite, videoOffsetMsec_, unpackagedSize_, */ /* video_stream_->codec->time_base.num, video_stream_->codec->time_base.den, // timebase we requested */ /* video_stream_->time_base.num, video_stream_->time_base.den); // actual timebase */ AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = unpackagedBufferpart_; avpkt.size = unpackagedSize_; avpkt.pts = timestampToWrite; avpkt.stream_index = 0; av_write_frame(context_, &avpkt); av_free_packet(&avpkt); unpackagedSize_ = 0; unpackagedBufferpart_ = unpackagedBuffer_; } }