blob: a7d45e403e066d7c00f992d427e8b8c5eef216d4 [file] [log] [blame]
/* ------------------------------------------------------------------
* 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)
{
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;
}