blob: 656cff9606ba9db0480329de4728bbe6bad9ac62 [file] [log] [blame]
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +00001/*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "modules/audio_coding/neteq/dtmf_buffer.h"
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000012
13#include <assert.h>
14#include <algorithm> // max
15
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/checks.h"
17#include "rtc_base/logging.h"
Henrik Lundind67a2192015-08-03 12:54:37 +020018
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000019// Modify the code to obtain backwards bit-exactness. Once bit-exactness is no
20// longer required, this #define should be removed (and the code that it
21// enables).
22#define LEGACY_BITEXACT
23
24namespace webrtc {
25
Karl Wiberg7f6c4d42015-04-09 15:44:22 +020026DtmfBuffer::DtmfBuffer(int fs_hz) {
27 SetSampleRate(fs_hz);
28}
29
30DtmfBuffer::~DtmfBuffer() = default;
31
32void DtmfBuffer::Flush() {
33 buffer_.clear();
34}
35
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000036// The ParseEvent method parses 4 bytes from |payload| according to this format
37// from RFC 4733:
38//
39// 0 1 2 3
40// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
41// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42// | event |E|R| volume | duration |
43// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44//
45// Legend (adapted from RFC 4733)
46// - event: The event field is a number between 0 and 255 identifying a
47// specific telephony event. The buffer will not accept any event
48// numbers larger than 15.
49// - E: If set to a value of one, the "end" bit indicates that this
50// packet contains the end of the event. For long-lasting events
51// that have to be split into segments, only the final packet for
52// the final segment will have the E bit set.
53// - R: Reserved.
54// - volume: For DTMF digits and other events representable as tones, this
55// field describes the power level of the tone, expressed in dBm0
56// after dropping the sign. Power levels range from 0 to -63 dBm0.
57// Thus, larger values denote lower volume. The buffer discards
58// values larger than 36 (i.e., lower than -36 dBm0).
59// - duration: The duration field indicates the duration of the event or segment
60// being reported, in timestamp units, expressed as an unsigned
61// integer in network byte order. For a non-zero value, the event
62// or segment began at the instant identified by the RTP timestamp
63// and has so far lasted as long as indicated by this parameter.
64// The event may or may not have ended. If the event duration
65// exceeds the maximum representable by the duration field, the
66// event is split into several contiguous segments. The buffer will
67// discard zero-duration events.
68//
69int DtmfBuffer::ParseEvent(uint32_t rtp_timestamp,
70 const uint8_t* payload,
pkasting@chromium.org4591fbd2014-11-20 22:28:14 +000071 size_t payload_length_bytes,
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000072 DtmfEvent* event) {
henrikg91d6ede2015-09-17 00:24:34 -070073 RTC_CHECK(payload);
74 RTC_CHECK(event);
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000075 if (payload_length_bytes < 4) {
Mirko Bonadei675513b2017-11-09 11:09:25 +010076 RTC_LOG(LS_WARNING) << "ParseEvent payload too short";
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +000077 return kPayloadTooShort;
78 }
79
80 event->event_no = payload[0];
81 event->end_bit = ((payload[1] & 0x80) != 0);
82 event->volume = (payload[1] & 0x3F);
83 event->duration = payload[2] << 8 | payload[3];
84 event->timestamp = rtp_timestamp;
85 return kOK;
86}
87
88// Inserts a DTMF event into the buffer. The event should be parsed from the
89// bit stream using the ParseEvent method above before inserting it in the
90// buffer.
91// DTMF events can be quite long, and in most cases the duration of the event
92// is not known when the first packet describing it is sent. To deal with that,
93// the RFC 4733 specifies that multiple packets are sent for one and the same
94// event as it is being created (typically, as the user is pressing the key).
95// These packets will all share the same start timestamp and event number,
96// while the duration will be the cumulative duration from the start. When
97// inserting a new event, the InsertEvent method tries to find a matching event
98// already in the buffer. If so, the new event is simply merged with the
99// existing one.
100int DtmfBuffer::InsertEvent(const DtmfEvent& event) {
Yves Gerey665174f2018-06-19 15:03:05 +0200101 if (event.event_no < 0 || event.event_no > 15 || event.volume < 0 ||
102 event.volume > 63 || event.duration <= 0 || event.duration > 65535) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100103 RTC_LOG(LS_WARNING) << "InsertEvent invalid parameters";
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000104 return kInvalidEventParameters;
105 }
106 DtmfList::iterator it = buffer_.begin();
107 while (it != buffer_.end()) {
108 if (MergeEvents(it, event)) {
109 // A matching event was found and the new event was merged.
110 return kOK;
111 }
112 ++it;
113 }
114 buffer_.push_back(event);
115 // Sort the buffer using CompareEvents to rank the events.
116 buffer_.sort(CompareEvents);
117 return kOK;
118}
119
120bool DtmfBuffer::GetEvent(uint32_t current_timestamp, DtmfEvent* event) {
121 DtmfList::iterator it = buffer_.begin();
122 while (it != buffer_.end()) {
123 // |event_end| is an estimate of where the current event ends. If the end
124 // bit is set, we know that the event ends at |timestamp| + |duration|.
125 uint32_t event_end = it->timestamp + it->duration;
126#ifdef LEGACY_BITEXACT
127 bool next_available = false;
128#endif
129 if (!it->end_bit) {
130 // If the end bit is not set, we allow extrapolation of the event for
131 // some time.
132 event_end += max_extrapolation_samples_;
133 DtmfList::iterator next = it;
134 ++next;
135 if (next != buffer_.end()) {
136 // If there is a next event in the buffer, we will not extrapolate over
137 // the start of that new event.
138 event_end = std::min(event_end, next->timestamp);
139#ifdef LEGACY_BITEXACT
140 next_available = true;
141#endif
142 }
143 }
Yves Gerey665174f2018-06-19 15:03:05 +0200144 if (current_timestamp >= it->timestamp &&
145 current_timestamp <= event_end) { // TODO(hlundin): Change to <.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000146 // Found a matching event.
147 if (event) {
148 event->event_no = it->event_no;
149 event->end_bit = it->end_bit;
150 event->volume = it->volume;
151 event->duration = it->duration;
152 event->timestamp = it->timestamp;
153 }
154#ifdef LEGACY_BITEXACT
Yves Gerey665174f2018-06-19 15:03:05 +0200155 if (it->end_bit && current_timestamp + frame_len_samples_ >= event_end) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000156 // We are done playing this. Erase the event.
157 buffer_.erase(it);
158 }
159#endif
160 return true;
161 } else if (current_timestamp > event_end) { // TODO(hlundin): Change to >=.
Yves Gerey665174f2018-06-19 15:03:05 +0200162// Erase old event. Operation returns a valid pointer to the next element
163// in the list.
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000164#ifdef LEGACY_BITEXACT
165 if (!next_available) {
166 if (event) {
167 event->event_no = it->event_no;
168 event->end_bit = it->end_bit;
169 event->volume = it->volume;
170 event->duration = it->duration;
171 event->timestamp = it->timestamp;
172 }
173 it = buffer_.erase(it);
174 return true;
175 } else {
176 it = buffer_.erase(it);
177 }
178#else
179 it = buffer_.erase(it);
180#endif
181 } else {
182 ++it;
183 }
184 }
185 return false;
186}
187
Karl Wiberg7f6c4d42015-04-09 15:44:22 +0200188size_t DtmfBuffer::Length() const {
189 return buffer_.size();
190}
191
192bool DtmfBuffer::Empty() const {
193 return buffer_.empty();
194}
195
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000196int DtmfBuffer::SetSampleRate(int fs_hz) {
Yves Gerey665174f2018-06-19 15:03:05 +0200197 if (fs_hz != 8000 && fs_hz != 16000 && fs_hz != 32000 && fs_hz != 48000) {
henrik.lundin@webrtc.orgd94659d2013-01-29 12:09:21 +0000198 return kInvalidSampleRate;
199 }
200 max_extrapolation_samples_ = 7 * fs_hz / 100;
201 frame_len_samples_ = fs_hz / 100;
202 return kOK;
203}
204
205// The method returns true if the two events are considered to be the same.
206// The are defined as equal if they share the same timestamp and event number.
207// The special case with long-lasting events that have to be split into segments
208// is not handled in this method. These will be treated as separate events in
209// the buffer.
210bool DtmfBuffer::SameEvent(const DtmfEvent& a, const DtmfEvent& b) {
211 return (a.event_no == b.event_no) && (a.timestamp == b.timestamp);
212}
213
214bool DtmfBuffer::MergeEvents(DtmfList::iterator it, const DtmfEvent& event) {
215 if (SameEvent(*it, event)) {
216 if (!it->end_bit) {
217 // Do not extend the duration of an event for which the end bit was
218 // already received.
219 it->duration = std::max(event.duration, it->duration);
220 }
221 if (event.end_bit) {
222 it->end_bit = true;
223 }
224 return true;
225 } else {
226 return false;
227 }
228}
229
230// Returns true if |a| goes before |b| in the sorting order ("|a| < |b|").
231// The events are ranked using their start timestamp (taking wrap-around into
232// account). In the unlikely situation that two events share the same start
233// timestamp, the event number is used to rank the two. Note that packets
234// that belong to the same events, and therefore sharing the same start
235// timestamp, have already been merged before the sort method is called.
236bool DtmfBuffer::CompareEvents(const DtmfEvent& a, const DtmfEvent& b) {
237 if (a.timestamp == b.timestamp) {
238 return a.event_no < b.event_no;
239 }
240 // Take wrap-around into account.
241 return (static_cast<uint32_t>(b.timestamp - a.timestamp) < 0xFFFFFFFF / 2);
242}
243} // namespace webrtc