/* ------------------------------------------------------------------ * Copyright (C) 1998-2009 PacketVideo * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. * See the License for the specific language governing permissions * and limitations under the License. * ------------------------------------------------------------------- */ /* */ /*********************************************************************************/ /* ** File: rtcp_encoder.cpp ** ** Description: ** This module implements the RTCP class. This class is used to encode and ** decode RTCP packets. Please refer to the RTCP design document for ** details. */ /* ** Includes */ #include "oscl_time.h" #include "oscl_mem.h" #include "rtcp_encoder.h" #include "rtcp_constants.h" /* ** Constants */ /* ** Methods */ OSCL_EXPORT_REF RTCP_Encoder::RTCP_Encoder(const uint8 *cname, const int32 cname_len, uint32 ssrc, const uint8 version) : RTCP_Base(version), SSRC(ssrc) { setCName(cname, cname_len); } void RTCP_Encoder::setCName(const uint8 * cname, int32 cname_len) { if (cname && cname_len) { cName_length = (cname_len < RTCP_ENCODER_MAX_CNAME_SIZE) ? cname_len : RTCP_ENCODER_MAX_CNAME_SIZE; oscl_memcpy(cName, cname, cName_length); } else { oscl_memcpy(cName, "PVSS", 4); cName_length = 4; } } RTCP_Encoder::Error_t RTCP_Encoder::EncodeReportBlock(OsclBinOStreamBigEndian & outStream,/* Input * stream * reference */ const RTCP_ReportBlock* report ) { if (! report) { return FAIL; } outStream << report->sourceSSRC; const int32 MAX_CUMULATIVE_LOST = 0x7FFFFF; uint32 tempint32 = report->cumulativeNumberOfPacketsLost; if (report->cumulativeNumberOfPacketsLost > MAX_CUMULATIVE_LOST) { tempint32 = MAX_CUMULATIVE_LOST; } tempint32 &= FRACTION_LOST_MASK; tempint32 |= (report->fractionLost) << FRACTION_LOST_POSITION; outStream << tempint32; outStream << report->highestSequenceNumberReceived; outStream << report->interarrivalJitter; outStream << report->lastSR; outStream << report->delaySinceLastSR; if (outStream.fail()) { return FAIL; } else { return RTCP_SUCCESS; } } RTCP_Encoder::Error_t RTCP_Encoder::output_rtcp_header(uint8 packet_type, uint8 count_field, uint16 size, OsclBinOStreamBigEndian& outStream, bool pad_bit) { uint8 tempChar = rtcpVersion << RTPRTCP_VERSION_BIT_POSITION; if (pad_bit) { tempChar |= RTCP_PAD_BIT_MASK; } // transform size into number of 32 bit words - 1 size = (size / 4) - 1; tempChar |= count_field & RECORD_COUNT_MASK; outStream << tempChar; outStream << packet_type; outStream << size; if (outStream.fail()) { return FAIL; } return RTCP_SUCCESS; } int32 RTCP_Encoder::GetEncodedSize(const RTCP_RR& rr_packet) { uint num_report_blocks = rr_packet.get_num_report_blocks(); int32 size = (num_report_blocks * RTCP_REPORT_BLOCK_SIZE) + RTCP_HEADER_SIZE + RTCP_RR_SENDER_INFO_SIZE; return size; } int32 RTCP_Encoder::GetEncodedSize(const RTCP_SR& sr_packet) { uint num_report_blocks = sr_packet.get_num_report_blocks(); int32 size = (num_report_blocks * RTCP_REPORT_BLOCK_SIZE) + RTCP_HEADER_SIZE + RTCP_SR_SENDER_INFO_SIZE; return size; } int32 RTCP_Encoder::GetEncodedSize(const RTCP_SDES& sdes_packet) { uint num_chunks = sdes_packet.get_num_chunks(); // now go through the chunks and get the sizes const SDES_chunk* chunk_ptr; int32 size = 0; for (uint ii = 0; ii < num_chunks; ++ii) { if ((chunk_ptr = sdes_packet.read_chunk(ii)) != NULL) { int32 chunk_size = chunk_ptr->get_chunk_size(); // add enough for the SSRC, the NULL item, and any padding chunk_size += 8 - (chunk_size & 0x3); size += chunk_size; } } size += RTCP_HEADER_SIZE; return size; } int32 RTCP_Encoder::GetEncodedSize(const RTCP_APP& app_packet) { int32 size; if (oscl_memcmp(app_packet.type, PVSS_APP_RTCP_NAME, 4)) { // this is a non PVSS APP packet int32 app_data_size = sizeof(RTCP_PSS0_APP); if (app_data_size % 4) { app_data_size += 4 - (app_data_size & 0x3); } size = RTCP_HEADER_SIZE + RTCP_APP_HEADER_INFO_SIZE + app_data_size; } else { size = RTCP_HEADER_SIZE + RTCP_APP_HEADER_INFO_SIZE + RTCP_PVSS_APP_COMMON_SIZE; switch (app_packet.pvss_app_data.subtype) { case DRC_REPORT: { size += RTCP_PVSS_APP_DRC_SIZE; break; } case LOW_BUF_WARNING: { size += RTCP_PVSS_APP_BUFLOW_SIZE; break; } case HIGH_BUF_WARNING: { size += RTCP_PVSS_APP_BUFHIGH_SIZE; break; } } } return size; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeRR(const RTCP_RR& rr_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { OsclBinOStreamBigEndian outStream; outStream.Attach(1, &output_buffer); // figure out how many bytes will be needed uint num_report_blocks = rr_packet.get_num_report_blocks(); uint size = (num_report_blocks * RTCP_REPORT_BLOCK_SIZE) + RTCP_HEADER_SIZE + RTCP_RR_SENDER_INFO_SIZE + pad_length; if ((size & 0x3) != 0) { // improper pad length -- the packet length must be multiple of 4 return INVALID_PAD_LENGTH; } if (output_buffer.len < size) { output_buffer.ptr = 0; output_buffer.len = size; return OUTPUT_TRUNCATED; } Error_t status; if ((status = output_rtcp_header(RR_PACKET_TYPE, num_report_blocks, size, outStream, (pad_length != 0))) != RTCP_SUCCESS) { return status; } // output the ssrc outStream << rr_packet.senderSSRC; if (outStream.fail()) { return FAIL; } // output the report blocks for (uint ii = 0; ii < num_report_blocks; ++ii) { if ((status = EncodeReportBlock(outStream, rr_packet.read_report_block(ii))) != RTCP_SUCCESS) { return status; } } // output any pad bits if (pad_length) { uint8 pad_value = 0; for (int32 ii = 0; ii < pad_length - 1; ++ii) { outStream << pad_value; } outStream << pad_length; if (outStream.fail()) { return FAIL; } } output_buffer.len = outStream.tellg(); return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeSR(const RTCP_SR& sr_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { OsclBinOStreamBigEndian outStream; outStream.Attach(1, &output_buffer); // figure out how many bytes will be needed uint num_report_blocks = sr_packet.get_num_report_blocks(); uint size = (num_report_blocks * RTCP_REPORT_BLOCK_SIZE) + RTCP_HEADER_SIZE + RTCP_SR_SENDER_INFO_SIZE + pad_length; if ((size & 0x3) != 0) { // improper pad length -- the packet length must be multiple of 4 return INVALID_PAD_LENGTH; } if (output_buffer.len < size) { output_buffer.ptr = 0; output_buffer.len = size; return OUTPUT_TRUNCATED; } Error_t status; if ((status = output_rtcp_header(SR_PACKET_TYPE, num_report_blocks, size, outStream, (pad_length != 0))) != RTCP_SUCCESS) { return status; } // output the sender information outStream << sr_packet.senderSSRC; outStream << sr_packet.NTP_timestamp_high; outStream << sr_packet.NTP_timestamp_low; outStream << sr_packet.RTP_timestamp; outStream << sr_packet.packet_count; outStream << sr_packet.octet_count; if (outStream.fail()) { return FAIL; } // output the report blocks for (uint ii = 0; ii < num_report_blocks; ++ii) { if ((status = EncodeReportBlock(outStream, sr_packet.read_report_block(ii))) != RTCP_SUCCESS) { return status; } } // output any pad bits if (pad_length) { uint8 pad_value = 0; for (int32 ii = 0; ii < pad_length - 1; ++ii) { outStream << pad_value; } outStream << pad_length; if (outStream.fail()) { return FAIL; } } output_buffer.len = outStream.tellg(); return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeBYE(const RTCP_BYE& bye_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { OsclBinOStreamBigEndian outStream; outStream.Attach(1, &output_buffer); // figure out how many bytes will be needed int32 ssrc_count = bye_packet.src_count & RECORD_COUNT_MASK; uint size = (ssrc_count * sizeof(bye_packet.ssrc_array[0])) + RTCP_HEADER_SIZE + pad_length; uint8 reason_len = (uint8)(bye_packet.reason_string.len & 0xFF); uint8 reason_len_pad = 0; if (bye_packet.reason_string.ptr && reason_len) { // figure out how many bytes in the reason string including any padding reason_len_pad = reason_len + 1; if (reason_len_pad & 0x3) { reason_len_pad += (4 - (reason_len_pad & 0x3)); size += reason_len + reason_len_pad; } } if ((size & 0x3) != 0) { // improper pad length -- the packet length must be multiple of 4 return INVALID_PAD_LENGTH; } if (output_buffer.len < size) { output_buffer.ptr = 0; output_buffer.len = size; return OUTPUT_TRUNCATED; } Error_t status; if ((status = output_rtcp_header(RR_PACKET_TYPE, ssrc_count, size, outStream, (pad_length != 0))) != RTCP_SUCCESS) { return status; } for (int32 ii = 0; ii < ssrc_count; ++ii) { outStream << bye_packet.ssrc_array[ii]; } if (outStream.fail()) { return FAIL; } // output the reason string if any if (reason_len) { outStream << reason_len; outStream.write((int8*)bye_packet.reason_string.ptr, reason_len); uint8 tmpchar = 0; for (int32 jj = 0; jj < reason_len_pad; ++jj) { outStream << tmpchar; } if (outStream.fail()) { return FAIL; } } // output any pad bits if (pad_length) { uint8 pad_value = 0; for (int32 ii = 0; ii < pad_length - 1; ++ii) { outStream << pad_value; } outStream << pad_length; if (outStream.fail()) { return FAIL; } } output_buffer.len = outStream.tellg(); return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeSDESItem(OsclBinOStreamBigEndian& outStream, const SDES_item* item_ptr) { if (! item_ptr) { return FAIL; } outStream << item_ptr->type; uint8 len = (uint8)(item_ptr->content.len & 0xFF); // limit to 255 characters outStream << len; outStream.write((int8*)item_ptr->content.ptr, len); if (outStream.fail()) { return FAIL; } return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeSDESChunk(OsclBinOStreamBigEndian& outStream, const SDES_chunk* chunk_ptr) { if (! chunk_ptr) { return FAIL; } outStream << chunk_ptr->ssrc; if (outStream.fail()) { return FAIL; } // get the number of items uint num_items = chunk_ptr->get_num_items(); // record the position uint32 pos = outStream.tellg(); Error_t status; for (uint ii = 0; ii < num_items; ++ii) { if ((status = EncodeSDESItem(outStream, chunk_ptr->read_item(ii))) != RTCP_SUCCESS) { return status; } } uint32 len = outStream.tellg() - pos; // Add the null item and any padding to make it to the next 32-bit boundary len = 4 - (len & 0x3); uint8 pad[4] = {0, 0, 0, 0}; outStream.write((int8*)pad, len); if (outStream.fail()) { return FAIL; } return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeSDES(const RTCP_SDES& sdes_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { OsclBinOStreamBigEndian outStream; outStream.Attach(1, &output_buffer); // figure out how many bytes will be needed uint num_chunks = sdes_packet.get_num_chunks(); int32 size = GetEncodedSize(sdes_packet) + pad_length; if ((size & 0x3) != 0) { // improper pad length -- the packet length must be multiple of 4 return INVALID_PAD_LENGTH; } Error_t status; if ((status = output_rtcp_header(SDES_PACKET_TYPE, num_chunks, size, outStream, (pad_length != 0))) != RTCP_SUCCESS) { return status; } for (uint ii = 0; ii < num_chunks; ++ii) { if ((status = EncodeSDESChunk(outStream, sdes_packet.read_chunk(ii))) != RTCP_SUCCESS) { return status; } } // output any pad bits if (pad_length) { uint8 pad_value = 0; for (int32 ii = 0; ii < pad_length - 1; ++ii) { outStream << pad_value; } outStream << pad_length; if (outStream.fail()) { return FAIL; } } output_buffer.len = outStream.tellg(); return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeAPP(const RTCP_APP& app_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { OsclBinOStreamBigEndian outStream; outStream.Attach(1, &output_buffer); // figure out how many bytes will be needed uint size = GetEncodedSize(app_packet) + pad_length; if ((size & 0x3) != 0) { // improper pad length -- the packet length must be multiple of 4 return INVALID_PAD_LENGTH; } if (output_buffer.len < size) { output_buffer.ptr = 0; output_buffer.len = size; return OUTPUT_TRUNCATED; } Error_t status; if ((status = output_rtcp_header(APP_PACKET_TYPE, app_packet.subtype, size, outStream, (pad_length != 0))) != RTCP_SUCCESS) { return status; } // output the ssrc and type outStream << app_packet.ssrc; outStream.write((int8*)app_packet.type, 4); if (oscl_memcmp(app_packet.type, PSS0_APP_RTCP_NAME, 4) == 0) { outStream << app_packet.pss0_app_data.sourcessrc; outStream << app_packet.pss0_app_data.playoutdelayinms; outStream << app_packet.pss0_app_data.nsn; outStream << app_packet.pss0_app_data.nun; outStream << app_packet.pss0_app_data.freebufferspace; //in 64 byte blocks } else if (oscl_memcmp(app_packet.type, PVSS_APP_RTCP_NAME, 4) == 0) { // output the PVSS common data outStream << app_packet.pvss_app_data.common.sendTime; outStream << app_packet.pvss_app_data.common.recvRate; // bits/sec outStream << app_packet.pvss_app_data.common.recvRateInterval; outStream << app_packet.pvss_app_data.common.playbackBufDepth; outStream << app_packet.pvss_app_data.common.highestCtrlMediaSeqNum; outStream << app_packet.pvss_app_data.common.cumulativeBytes; switch (app_packet.pvss_app_data.subtype) { case DRC_REPORT: { outStream << app_packet.pvss_app_data.extraDRC.rebufCount; outStream << app_packet.pvss_app_data.extraDRC.missingPackets; outStream << app_packet.pvss_app_data.extraDRC.cumulativePacketsReceived; outStream << app_packet.pvss_app_data.extraDRC.totalProcessedFrames; outStream << app_packet.pvss_app_data.extraDRC.totalSkippedFrames; outStream << app_packet.pvss_app_data.extraDRC.cumulativePacketsLost; break; } case LOW_BUF_WARNING: { outStream << app_packet.pvss_app_data.extraBufLow.depletionRateInteger; outStream << app_packet.pvss_app_data.extraBufLow.depletionRateFraction; break; } case HIGH_BUF_WARNING: { outStream << app_packet.pvss_app_data.extraBufHigh.fillRateInteger; outStream << app_packet.pvss_app_data.extraBufHigh.fillRateFraction; break; } } } else { // this is not a PVSS APP packet or PSS0 APP packet outStream.write((int8*)app_packet.app_data.ptr, app_packet.app_data.len); if (app_packet.app_data.len & 0x3) { // pad the data to the next 32-bit boundary int8 pad[4] = {0, 0, 0, 0}; outStream.write(pad, 4 - (app_packet.app_data.len & 0x3)); } } if (outStream.fail()) { return FAIL; } // output any pad bits if (pad_length) { uint8 pad_value = 0; for (int32 ii = 0; ii < pad_length - 1; ++ii) { outStream << pad_value; } outStream << pad_length; if (outStream.fail()) { return FAIL; } } output_buffer.len = outStream.tellg(); return RTCP_SUCCESS; } OSCL_EXPORT_REF RTCP_Encoder::Error_t RTCP_Encoder::EncodeCompoundRR(const RTCP_RR& rr_packet, OsclMemoryFragment& output_buffer, const RTCP_APP* app_packet, uint8 pad_length) { Error_t status; // encode the RR packet OsclMemoryFragment working_buffer = output_buffer; uint remaining_len = working_buffer.len; if ((status = EncodeRR(rr_packet, working_buffer)) != RTCP_SUCCESS) { return status; } working_buffer.ptr = (uint8*) working_buffer.ptr + working_buffer.len; remaining_len -= working_buffer.len; working_buffer.len = remaining_len; // encode the SDES packet SDES_item cname_item; cname_item.type = CNAME_RTCP_SDES; cname_item.content.ptr = cName; cname_item.content.len = cName_length; SDES_chunk chunk(SSRC); chunk.add_item(cname_item); RTCP_SDES sdes_packet; sdes_packet.add_chunk(chunk); uint8 sdes_pad = (app_packet) ? 0 : pad_length; if ((status = EncodeSDES(sdes_packet, working_buffer, sdes_pad)) != RTCP_SUCCESS) { return status; } working_buffer.ptr = (uint8*) working_buffer.ptr + working_buffer.len; remaining_len -= working_buffer.len; working_buffer.len = remaining_len; if (app_packet) { // encode the app if ((status = EncodeAPP(*app_packet, working_buffer, sdes_pad)) != RTCP_SUCCESS) { return status; } remaining_len -= working_buffer.len; } // update the length field output_buffer.len -= remaining_len; return RTCP_SUCCESS; } RTCP_Encoder::Error_t RTCP_Encoder::EncodeCompoundSR(const RTCP_SR& sr_packet, OsclMemoryFragment& output_buffer, uint8 pad_length) { Error_t status; // encode the SR packet OsclMemoryFragment working_buffer = output_buffer; uint remaining_len = working_buffer.len; if ((status = EncodeSR(sr_packet, working_buffer)) != RTCP_SUCCESS) { return status; } working_buffer.ptr = (uint8*) working_buffer.ptr + working_buffer.len; remaining_len -= working_buffer.len; working_buffer.len = remaining_len; // encode the SDES packet SDES_item cname_item; cname_item.type = CNAME_RTCP_SDES; cname_item.content.ptr = cName; cname_item.content.len = cName_length; SDES_chunk chunk(SSRC); chunk.add_item(cname_item); RTCP_SDES sdes_packet; sdes_packet.add_chunk(chunk); if ((status = EncodeSDES(sdes_packet, working_buffer, pad_length)) != RTCP_SUCCESS) { return status; } remaining_len -= working_buffer.len; // update the length field output_buffer.len -= remaining_len; return RTCP_SUCCESS; }