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