henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 1 | /* |
| 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 Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 11 | #include "modules/audio_coding/neteq/dtmf_buffer.h" |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 12 | |
| 13 | #include <assert.h> |
| 14 | #include <algorithm> // max |
| 15 | |
Mirko Bonadei | 92ea95e | 2017-09-15 06:47:31 +0200 | [diff] [blame] | 16 | #include "rtc_base/checks.h" |
| 17 | #include "rtc_base/logging.h" |
Henrik Lundin | d67a219 | 2015-08-03 12:54:37 +0200 | [diff] [blame] | 18 | |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 19 | // 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 | |
| 24 | namespace webrtc { |
| 25 | |
Karl Wiberg | 7f6c4d4 | 2015-04-09 15:44:22 +0200 | [diff] [blame] | 26 | DtmfBuffer::DtmfBuffer(int fs_hz) { |
| 27 | SetSampleRate(fs_hz); |
| 28 | } |
| 29 | |
| 30 | DtmfBuffer::~DtmfBuffer() = default; |
| 31 | |
| 32 | void DtmfBuffer::Flush() { |
| 33 | buffer_.clear(); |
| 34 | } |
| 35 | |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 36 | // 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 | // |
| 69 | int DtmfBuffer::ParseEvent(uint32_t rtp_timestamp, |
| 70 | const uint8_t* payload, |
pkasting@chromium.org | 4591fbd | 2014-11-20 22:28:14 +0000 | [diff] [blame] | 71 | size_t payload_length_bytes, |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 72 | DtmfEvent* event) { |
henrikg | 91d6ede | 2015-09-17 00:24:34 -0700 | [diff] [blame] | 73 | RTC_CHECK(payload); |
| 74 | RTC_CHECK(event); |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 75 | if (payload_length_bytes < 4) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 76 | RTC_LOG(LS_WARNING) << "ParseEvent payload too short"; |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 77 | 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. |
| 100 | int DtmfBuffer::InsertEvent(const DtmfEvent& event) { |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame^] | 101 | if (event.event_no < 0 || event.event_no > 15 || event.volume < 0 || |
| 102 | event.volume > 63 || event.duration <= 0 || event.duration > 65535) { |
Mirko Bonadei | 675513b | 2017-11-09 11:09:25 +0100 | [diff] [blame] | 103 | RTC_LOG(LS_WARNING) << "InsertEvent invalid parameters"; |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 104 | 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 | |
| 120 | bool 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 Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame^] | 144 | if (current_timestamp >= it->timestamp && |
| 145 | current_timestamp <= event_end) { // TODO(hlundin): Change to <. |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 146 | // 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 Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame^] | 155 | if (it->end_bit && current_timestamp + frame_len_samples_ >= event_end) { |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 156 | // 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 Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame^] | 162 | // Erase old event. Operation returns a valid pointer to the next element |
| 163 | // in the list. |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 164 | #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 Wiberg | 7f6c4d4 | 2015-04-09 15:44:22 +0200 | [diff] [blame] | 188 | size_t DtmfBuffer::Length() const { |
| 189 | return buffer_.size(); |
| 190 | } |
| 191 | |
| 192 | bool DtmfBuffer::Empty() const { |
| 193 | return buffer_.empty(); |
| 194 | } |
| 195 | |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 196 | int DtmfBuffer::SetSampleRate(int fs_hz) { |
Yves Gerey | 665174f | 2018-06-19 15:03:05 +0200 | [diff] [blame^] | 197 | if (fs_hz != 8000 && fs_hz != 16000 && fs_hz != 32000 && fs_hz != 48000) { |
henrik.lundin@webrtc.org | d94659d | 2013-01-29 12:09:21 +0000 | [diff] [blame] | 198 | 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. |
| 210 | bool DtmfBuffer::SameEvent(const DtmfEvent& a, const DtmfEvent& b) { |
| 211 | return (a.event_no == b.event_no) && (a.timestamp == b.timestamp); |
| 212 | } |
| 213 | |
| 214 | bool 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. |
| 236 | bool 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 |