summaryrefslogtreecommitdiff
path: root/media/cast/rtp_receiver/rtp_parser/rtp_parser.cc
blob: 79cc55ebb032c381477a8bebf65814bba4a445f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/rtp_receiver/rtp_parser/rtp_parser.h"

#include "base/logging.h"
#include "media/cast/cast_defines.h"
#include "media/cast/rtp_receiver/rtp_receiver.h"
#include "net/base/big_endian.h"

namespace media {
namespace cast {

static const size_t kRtpCommonHeaderLength = 12;
static const size_t kRtpCastHeaderLength = 7;
static const uint8 kCastKeyFrameBitMask = 0x80;
static const uint8 kCastReferenceFrameIdBitMask = 0x40;

RtpParser::RtpParser(RtpData* incoming_payload_callback,
                     const RtpParserConfig parser_config)
    : data_callback_(incoming_payload_callback),
      parser_config_(parser_config) {
}

RtpParser::~RtpParser() {
}

bool RtpParser::ParsePacket(const uint8* packet, size_t length,
                            RtpCastHeader* rtp_header) {
  if (length == 0) return false;
  // Get RTP general header.
  if (!ParseCommon(packet, length, rtp_header)) return false;
  if (rtp_header->webrtc.header.payloadType == parser_config_.payload_type &&
    rtp_header->webrtc.header.ssrc == parser_config_.ssrc) {
    return ParseCast(packet + kRtpCommonHeaderLength,
        length - kRtpCommonHeaderLength, rtp_header);
  }
  // Not a valid payload type / ssrc combination.
  return false;
}

bool RtpParser::ParseCommon(const uint8* packet,
                            size_t length,
                            RtpCastHeader* rtp_header) {
  if (length < kRtpCommonHeaderLength) return false;
  uint8 version = packet[0] >> 6;
  if (version != 2) return false;
  uint8 cc = packet[0] & 0x0f;
  bool marker = ((packet[1] & 0x80) != 0);
  int payload_type = packet[1] & 0x7f;

  uint16 sequence_number;
  uint32 rtp_timestamp, ssrc;
  net::BigEndianReader big_endian_reader(packet + 2, 10);
  big_endian_reader.ReadU16(&sequence_number);
  big_endian_reader.ReadU32(&rtp_timestamp);
  big_endian_reader.ReadU32(&ssrc);

  if (ssrc != parser_config_.ssrc) return false;

  rtp_header->webrtc.header.markerBit      = marker;
  rtp_header->webrtc.header.payloadType    = payload_type;
  rtp_header->webrtc.header.sequenceNumber = sequence_number;
  rtp_header->webrtc.header.timestamp      = rtp_timestamp;
  rtp_header->webrtc.header.ssrc           = ssrc;
  rtp_header->webrtc.header.numCSRCs       = cc;

  uint8 csrc_octs = cc * 4;
  rtp_header->webrtc.type.Audio.numEnergy = rtp_header->webrtc.header.numCSRCs;
  rtp_header->webrtc.header.headerLength = kRtpCommonHeaderLength + csrc_octs;
  rtp_header->webrtc.type.Audio.isCNG = false;
  rtp_header->webrtc.type.Audio.channel = parser_config_.audio_channels;
  // TODO(pwestin): look at x bit and skip data.
  return true;
}

bool RtpParser::ParseCast(const uint8* packet,
                          size_t length,
                          RtpCastHeader* rtp_header) {
  if (length < kRtpCastHeaderLength) return false;

  // Extract header.
  const uint8* data_ptr = packet;
  size_t data_length = length;
  rtp_header->is_key_frame = (data_ptr[0] & kCastKeyFrameBitMask);
  rtp_header->is_reference = (data_ptr[0] & kCastReferenceFrameIdBitMask);
  rtp_header->frame_id = data_ptr[1];

  net::BigEndianReader big_endian_reader(data_ptr + 2, 4);
  big_endian_reader.ReadU16(&rtp_header->packet_id);
  big_endian_reader.ReadU16(&rtp_header->max_packet_id);

  if (rtp_header->is_reference) {
    rtp_header->reference_frame_id = data_ptr[6];
    data_ptr += kRtpCastHeaderLength;
    data_length -= kRtpCastHeaderLength;
  } else {
    data_ptr += kRtpCastHeaderLength - 1;
    data_length -= kRtpCastHeaderLength - 1;
  }

  if (rtp_header->max_packet_id < rtp_header->packet_id) return false;

  data_callback_->OnReceivedPayloadData(data_ptr, data_length, rtp_header);
  return true;
}

}  // namespace cast
}  // namespace media