Note: We no longer publish the latest version of our code here. We primarily use a kumc-bmi github organization. The heron ETL repository, in particular, is not public. Peers in the informatics community should see MultiSiteDev for details on requesting access.

source: webrtc/webrtc/modules/audio_coding/neteq4/dtmf_buffer.cc @ 0:4bda6873e34c

pub_scrub_3792 tip
Last change on this file since 0:4bda6873e34c was 0:4bda6873e34c, checked in by Michael Prittie <mprittie@…>, 6 years ago

Scrubbed password for publication.

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