/* ------------------------------------------------------------------ * 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_decoder.cpp ** ** Description: ** This module implements the RTCP_Decoder class. This class is used to encode and ** decode RTCP_Decoder packets. Please refer to the RTCP_Decoder design document for ** details. */ /* ** Includes */ #define DEBUG_PRINT 0 #if DEBUG_PRINT #include <stdio.h> #endif #ifdef PV_OS_ZREX //you get a compile error on zrex for the arm target //unless these includes come first. #include <stdlib.h> #include <math.h> #endif #include "rtcp_decoder.h" #include "rtcp_constants.h" #include "oscl_mem.h" /* ** Constants */ /* ** Methods */ OSCL_EXPORT_REF RTCP_Decoder::RTCP_Decoder(const uint8 version) : RTCP_Base(version) { } OSCL_EXPORT_REF RTCP_Decoder::~RTCP_Decoder() { } OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::scan_compound_packet(OsclMemoryFragment& input_packet, int32 max_array_size, int32& filled_size, RTCPPacketType *array_of_packet_types, OsclMemoryFragment *array_of_packets) { uint8 *ptr = (uint8 *) input_packet.ptr; int32 remaining_len = input_packet.len; filled_size = 0; if (! ptr || remaining_len <= 0) { return FAIL; } OsclBinIStreamBigEndian inStream; inStream.Attach(ptr, remaining_len); uint8 tempChar; while (remaining_len > 0 && filled_size < max_array_size) { inStream >> tempChar; if (inStream.fail()) { return EOS_ON_READ; } // read the type uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return EOS_ON_READ; } // store the length and type switch (payloadType) { case SR_PACKET_TYPE: array_of_packet_types[filled_size] = SR_RTCP_PACKET; break; case RR_PACKET_TYPE: array_of_packet_types[filled_size] = RR_RTCP_PACKET; break; case SDES_PACKET_TYPE: array_of_packet_types[filled_size] = SDES_RTCP_PACKET; break; case BYE_PACKET_TYPE: array_of_packet_types[filled_size] = BYE_RTCP_PACKET; break; case APP_PACKET_TYPE: { // figure out whether this is a PVSS type // ptr is at the beginning of APP packet uint8* test_ptr = ptr + 8; if (oscl_memcmp(test_ptr, PVSS_APP_RTCP_NAME, 4)) { array_of_packet_types[filled_size] = APP_RTCP_PACKET; } else { array_of_packet_types[filled_size] = PVSS_APP_RTCP_PACKET; } break; } default: array_of_packet_types[filled_size] = UNKNOWN_RTCP_PACKET; break; } // record the ptr and length array_of_packets[filled_size].ptr = ptr; array_of_packets[filled_size++].len = (rtcpLength + 1) * 4; remaining_len -= (rtcpLength + 1) * 4; ptr += (rtcpLength + 1) * 4; if (rtcpLength) { inStream.seekFromCurrentPosition((rtcpLength*4)); if (inStream.fail()) { return EOS_ON_READ; } } } // end while loop return RTCP_SUCCESS; } // Start of new code /* ** Description: ** Decode a sender or receiver report block ** ** Returns: SUCCESS if successful, FAIL if not. ** Side effects: None. */ RTCP_Decoder::Error_t RTCP_Decoder::DecodeReportBlock( OsclBinIStreamBigEndian & inStream, /* Input stream reference */ RTCP_ReportBlock* report ) { if (! report) { return FAIL; } inStream >> report->sourceSSRC; const uint32 SIGN_BIT_MASK = 0x800000; const uint32 SIGN_EXTENSION = 0xFF000000; uint32 tempint32; inStream >> tempint32; report->fractionLost = (uint8)(tempint32 >> FRACTION_LOST_POSITION); report->cumulativeNumberOfPacketsLost = tempint32 & FRACTION_LOST_MASK; if (report->cumulativeNumberOfPacketsLost & SIGN_BIT_MASK) { report->cumulativeNumberOfPacketsLost |= SIGN_EXTENSION; } inStream >> report->highestSequenceNumberReceived; inStream >> report->interarrivalJitter; inStream >> report->lastSR; inStream >> report->delaySinceLastSR; if (inStream.fail()) { return FAIL; } else { return RTCP_SUCCESS; } } OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::DecodeRR(OsclMemoryFragment& input_packet, RTCP_RR& rr_packet) { #if DEBUG_PRINT printf("Within RTCP_Decoder::DecodeRR\n"); #endif // attach the bin stream OsclBinIStreamBigEndian inStream; if (input_packet.ptr == NULL || input_packet.len == 0) { return FAIL; } inStream.Attach(input_packet.ptr, input_packet.len); uint8 tempChar; // decode the version, report count, packet type, and length inStream >> tempChar; if (inStream.eof() || inStream.fail()) { return FAIL; } uint8 rcvdVersion = tempChar >> RTPRTCP_VERSION_BIT_POSITION; uint8 report_count = tempChar & RECORD_COUNT_MASK; if (rcvdVersion != rtcpVersion) { #if DEBUG_PRINT printf("Wrong RR RTP version\n"); #endif return UNSUPPORTED_RTCP_VERSION; } uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return FAIL; } if (payloadType != RR_PACKET_TYPE) { return RTCP_PACKET_TYPE_MISMATCH; } if (rtcpLength < (report_count*6 + 1)) { return RTCP_LENGTH_MISMATCH; } // set the number of report blocks rr_packet.set_max_report_blocks(report_count); // decode the sender SSRC inStream >> rr_packet.senderSSRC; Error_t status; // decode each of the report blocks for (uint ii = 0; ii < report_count; ++ii) { if ((status = DecodeReportBlock(inStream, rr_packet.get_report_block(ii))) != RTCP_SUCCESS) { return status; } } return RTCP_SUCCESS; } /* ** Description: ** Decode a SR (sender report) RTCP_Decoder object. ** ** Returns: SUCCESS if successful, FAIL if not. ** Side effects: None. */ OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::DecodeSR( const OsclMemoryFragment& input_packet, RTCP_SR& sr_packet) { #if DEBUG_PRINT printf("Within RTCP_Decoder::DecodeSR\n"); #endif // attach the bin stream OsclBinIStreamBigEndian inStream; if (input_packet.ptr == NULL || input_packet.len == 0) { return FAIL; } inStream.Attach(input_packet.ptr, input_packet.len); uint8 tempChar; // decode the version, report count, packet type, and length inStream >> tempChar; if (inStream.eof() || inStream.fail()) { return FAIL; } uint8 rcvdVersion = tempChar >> RTPRTCP_VERSION_BIT_POSITION; uint8 report_count = tempChar & RECORD_COUNT_MASK; if (rcvdVersion != rtcpVersion) { #if DEBUG_PRINT printf("Wrong SR RTCP version\n"); #endif return UNSUPPORTED_RTCP_VERSION; } // set the max report uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return FAIL; } if (payloadType != SR_PACKET_TYPE) { return RTCP_PACKET_TYPE_MISMATCH; } if (rtcpLength < (report_count*6 + 6)) { return RTCP_LENGTH_MISMATCH; } // read the sender information // decode the sender SSRC inStream >> sr_packet.senderSSRC; inStream >> sr_packet.NTP_timestamp_high; inStream >> sr_packet.NTP_timestamp_low; inStream >> sr_packet.RTP_timestamp; inStream >> sr_packet.packet_count; inStream >> sr_packet.octet_count; if (inStream.fail()) { return FAIL; } // set the number of report blocks sr_packet.set_max_report_blocks(report_count); Error_t status; // decode each of the report blocks for (uint ii = 0; ii < report_count; ++ii) { if ((status = DecodeReportBlock(inStream, sr_packet.get_report_block(ii))) != RTCP_SUCCESS) { return status; } } return RTCP_SUCCESS; } /* ** Description: ** Decode a SDES RTCP_Decoder object. The only field supported and encoded is CNAME. ** ** Returns: SUCCESS if successful, FAIL if not. ** Side effects: None. */ OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::DecodeSDES( const OsclMemoryFragment& input_packet, RTCP_SDES& sdes_packet) { // attach the bin stream OsclBinIStreamBigEndian inStream; if (input_packet.ptr == NULL || input_packet.len == 0) { return FAIL; } inStream.Attach(input_packet.ptr, input_packet.len); uint8 tempChar; // decode the version, report count, packet type, and length inStream >> tempChar; if (inStream.eof() || inStream.fail()) { return FAIL; } uint8 rcvdVersion = tempChar >> RTPRTCP_VERSION_BIT_POSITION; uint8 chunk_count = tempChar & RECORD_COUNT_MASK; if (rcvdVersion != rtcpVersion) { #if DEBUG_PRINT printf("Wrong RR RTP version\n"); #endif return UNSUPPORTED_RTCP_VERSION; } uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return FAIL; } if (payloadType != SDES_PACKET_TYPE) { return RTCP_PACKET_TYPE_MISMATCH; } sdes_packet.set_max_chunks(chunk_count); // now decode each chunk SDES_chunk* chunk_ptr; Error_t status; OsclMemoryFragment chunk_frag; for (uint ii = 0; ii < chunk_count; ++ii) { if (!(chunk_ptr = sdes_packet.get_chunk(ii))) { return FAIL; } chunk_frag.ptr = inStream.tellg() + ((int8*)input_packet.ptr); chunk_frag.len = input_packet.len - inStream.tellg(); if ((status = DecodeSDESChunk(inStream, chunk_ptr, chunk_frag)) != RTCP_SUCCESS) { return status; } } return RTCP_SUCCESS; } RTCP_Decoder::Error_t RTCP_Decoder::DecodeSDESChunk(OsclBinIStreamBigEndian & inStream, SDES_chunk* sdes_chunk, OsclMemoryFragment& chunk_data) { // create a temporary for up to TMP_SDES_STORAGE sdes items. const uint TMP_SDES_STORAGE = 10; if (! sdes_chunk) { return FAIL; } uint8* ptr = (uint8 *)chunk_data.ptr; int32 len = chunk_data.len; uint8* end_ptr = ptr + len; SDES_item tmp_sdes_items[TMP_SDES_STORAGE]; // get the ssrc inStream >> sdes_chunk->ssrc; len -= sizeof(sdes_chunk->ssrc); ptr += sizeof(sdes_chunk->ssrc); uint num_items = 0; // figure out the number of sdes items while ((ptr <= end_ptr - 2) && (*ptr != 0)) { if (num_items < TMP_SDES_STORAGE) { // store the information tmp_sdes_items[num_items].type = *ptr++; uint8 item_len = *ptr++; len -= 2; if (item_len > len) { return EOS_ON_READ; } tmp_sdes_items[num_items].content.ptr = ptr; tmp_sdes_items[num_items].content.len = item_len; ptr += item_len; len -= item_len; } else { // simply skip over this one and count it. ++ptr; uint8 item_len = *ptr++; len -= 2; if (item_len > len) { return EOS_ON_READ; } ptr += item_len; len -= item_len; } ++num_items; } if (*ptr != 0) { return EOS_ON_READ; } // set the max number of sdes items sdes_chunk->set_max_items(num_items); // record the tmp SDES items uint loop_limit = (num_items <= TMP_SDES_STORAGE) ? num_items : TMP_SDES_STORAGE; SDES_item* sdes_item_ptr; uint ii; for (ii = 0; ii < loop_limit; ++ii) { if (!(sdes_item_ptr = sdes_chunk->get_item(ii))) { return FAIL; } *sdes_item_ptr = tmp_sdes_items[ii]; } // record any remaining items beyond the temp storage size if ((loop_limit < num_items) && (ii < TMP_SDES_STORAGE)) { uint8* cp_ptr = (uint8*) tmp_sdes_items[ii].content.ptr; uint8 cp_len = (uint8)(tmp_sdes_items[ii].content.len & 0xFF); cp_ptr += cp_len; len = end_ptr - cp_ptr; ii = loop_limit; while ((ii < num_items) && (cp_ptr <= end_ptr - 2) && (*cp_ptr != 0)) { if (!(sdes_item_ptr = sdes_chunk->get_item(ii))) { return FAIL; } sdes_item_ptr->type = *cp_ptr++; uint8 item_len = *cp_ptr++; len -= 2; if (item_len > len) { return EOS_ON_READ; } sdes_item_ptr->content.ptr = cp_ptr; sdes_item_ptr->content.len = item_len; cp_ptr += item_len; len -= item_len; ++ii; } } // now skip over the padding // subtract off the SSRC length (even though it is len = ptr - sizeof(sdes_chunk->ssrc) - (uint8 *)chunk_data.ptr; int32 pad_bytes = 4 - (len & 0x3); // move the inStream pos ahead inStream.seekFromCurrentPosition(len + pad_bytes); if (inStream.fail()) { return FAIL; } return RTCP_SUCCESS; } /* ** Description: ** Decode a RTCP BYE report. ** ** Returns: SUCCESS if successful, FAIL if not. ** Side effects: None. */ OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::DecodeBYE(const OsclMemoryFragment& input_packet, RTCP_BYE& bye_packet) { #if DEBUG_PRINT printf("Within RTCP_Decoder::DecodeBYE\n"); #endif // attach the bin stream OsclBinIStreamBigEndian inStream; if (input_packet.ptr == NULL || input_packet.len == 0) { return FAIL; } inStream.Attach(input_packet.ptr, input_packet.len); uint8 tempChar; // decode the version, report count, packet type, and length inStream >> tempChar; if (inStream.eof() || inStream.fail()) { return FAIL; } uint8 rcvdVersion = tempChar >> RTPRTCP_VERSION_BIT_POSITION; uint8 sourceCount = tempChar & RECORD_COUNT_MASK; if (rcvdVersion != rtcpVersion) { #if DEBUG_PRINT printf("Wrong SR RTCP version\n"); #endif return UNSUPPORTED_RTCP_VERSION; } uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return FAIL; } if (payloadType != BYE_PACKET_TYPE) { return RTCP_PACKET_TYPE_MISMATCH; } bye_packet.src_count = sourceCount; for (uint ii = 0; ii < sourceCount; ++ii) { inStream >> bye_packet.ssrc_array[ii]; if (inStream.fail()) { return FAIL; } } // now check the reason string int32 len = inStream.PositionInBlock(); bye_packet.reason_string.ptr = 0; bye_packet.reason_string.len = 0; if (len < (rtcpLength + 1)*4) { uint8 tmplen; inStream >> tmplen; if (inStream.fail()) { return FAIL; } if (tmplen + len + 1 > (rtcpLength + 1)*4) { return EOS_ON_READ; } bye_packet.reason_string.ptr = ((uint8*) input_packet.ptr) + len + 1; bye_packet.reason_string.len = tmplen; } return RTCP_SUCCESS; } /* ** Description: ** Decode a RTCP BYE report. ** ** Returns: SUCCESS if successful, FAIL if not. ** Side effects: None. */ OSCL_EXPORT_REF RTCP_Decoder::Error_t RTCP_Decoder::DecodeAPP(const OsclMemoryFragment& input_packet, RTCP_APP& app_packet) { // attach the bin stream OsclBinIStreamBigEndian inStream; if (input_packet.ptr == NULL || input_packet.len == 0) { return FAIL; } inStream.Attach(input_packet.ptr, input_packet.len); uint8 tempChar; // decode the version, report count, packet type, and length inStream >> tempChar; if (inStream.eof() || inStream.fail()) { return FAIL; } uint8 rcvdVersion = tempChar >> RTPRTCP_VERSION_BIT_POSITION; uint8 padBit = ((tempChar & (1 << RTPRTCP_PAD_FLAG_BIT_POSITION)) != 0); uint8 subType = tempChar & RECORD_COUNT_MASK; if (rcvdVersion != rtcpVersion) { #if DEBUG_PRINT printf("Wrong SR RTCP version\n"); #endif return UNSUPPORTED_RTCP_VERSION; } uint8 payloadType; uint16 rtcpLength; inStream >> payloadType; inStream >> rtcpLength; if (inStream.fail()) { return FAIL; } if (payloadType != APP_PACKET_TYPE) { return RTCP_PACKET_TYPE_MISMATCH; } uint8* endPtr = (uint8*)input_packet.ptr + (rtcpLength + 1) * 4; if (padBit) { uint8 pad_size = *(endPtr - 1); endPtr -= pad_size; // must at least be 12 characters if (endPtr < (uint8*)input_packet.ptr + 12) { return FAIL; } } inStream >> app_packet.ssrc; inStream.get((int8 *) app_packet.type, 4); if (inStream.fail()) { return FAIL; } app_packet.subtype = subType; if (oscl_memcmp(app_packet.type, PVSS_APP_RTCP_NAME, 4)) { // some other app packet -- just record the memory ptr and length app_packet.app_data.ptr = ((uint8*)input_packet.ptr) + inStream.tellg(); app_packet.app_data.len = endPtr - ((uint8*)app_packet.app_data.ptr); return RTCP_SUCCESS; } // store the subtype in the PVSS APP data structure also. app_packet.pvss_app_data.subtype = subType; // Check SubType if (subType > RTCP_PVSS_APP_MAX_SUPPORTED_SUBTYPE) { #if DEBUG_PRINT printf("Unsupported APP SubType\n"); #endif inStream.seekFromCurrentPosition((rtcpLength - 2)*4); return UNSUPPORTED_RTCP_PVSS_APP; } int32 curpos = inStream.tellg(); inStream >> app_packet.pvss_app_data.common.sendTime; inStream >> app_packet.pvss_app_data.common.recvRate; inStream >> app_packet.pvss_app_data.common.recvRateInterval; inStream >> app_packet.pvss_app_data.common.playbackBufDepth; inStream >> app_packet.pvss_app_data.common.highestCtrlMediaSeqNum; inStream >> app_packet.pvss_app_data.common.cumulativeBytes; if (inStream.fail()) { return FAIL; } switch (subType) { case 0: // DRC inStream >> app_packet.pvss_app_data.extraDRC.rebufCount; inStream >> app_packet.pvss_app_data.extraDRC.missingPackets; inStream >> app_packet.pvss_app_data.extraDRC.cumulativePacketsReceived; inStream >> app_packet.pvss_app_data.extraDRC.totalProcessedFrames; inStream >> app_packet.pvss_app_data.extraDRC.totalSkippedFrames; inStream >> app_packet.pvss_app_data.extraDRC.cumulativePacketsLost; break; case 1: // BufLow inStream >> app_packet.pvss_app_data.extraBufLow.depletionRateInteger; inStream >> app_packet.pvss_app_data.extraBufLow.depletionRateFraction; break; case 2: // BufHigh inStream >> app_packet.pvss_app_data.extraBufHigh.fillRateInteger; inStream >> app_packet.pvss_app_data.extraBufHigh.fillRateFraction; break; default: break; } if (inStream.fail()) { return FAIL; } int32 diff = inStream.tellg() - curpos; diff = (rtcpLength - 2) * 4 - diff; if (diff < 0) { return FAIL; } else if (diff > 0) { inStream.seekFromCurrentPosition(diff); } return RTCP_SUCCESS; }