/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/audio_coding/neteq/dtmf_buffer.h" #include #include // max #include "webrtc/base/checks.h" #include "webrtc/base/logging.h" // Modify the code to obtain backwards bit-exactness. Once bit-exactness is no // longer required, this #define should be removed (and the code that it // enables). #define LEGACY_BITEXACT namespace webrtc { DtmfBuffer::DtmfBuffer(int fs_hz) { SetSampleRate(fs_hz); } DtmfBuffer::~DtmfBuffer() = default; void DtmfBuffer::Flush() { buffer_.clear(); } // The ParseEvent method parses 4 bytes from |payload| according to this format // from RFC 4733: // // 0 1 2 3 // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | event |E|R| volume | duration | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Legend (adapted from RFC 4733) // - event: The event field is a number between 0 and 255 identifying a // specific telephony event. The buffer will not accept any event // numbers larger than 15. // - E: If set to a value of one, the "end" bit indicates that this // packet contains the end of the event. For long-lasting events // that have to be split into segments, only the final packet for // the final segment will have the E bit set. // - R: Reserved. // - volume: For DTMF digits and other events representable as tones, this // field describes the power level of the tone, expressed in dBm0 // after dropping the sign. Power levels range from 0 to -63 dBm0. // Thus, larger values denote lower volume. The buffer discards // values larger than 36 (i.e., lower than -36 dBm0). // - duration: The duration field indicates the duration of the event or segment // being reported, in timestamp units, expressed as an unsigned // integer in network byte order. For a non-zero value, the event // or segment began at the instant identified by the RTP timestamp // and has so far lasted as long as indicated by this parameter. // The event may or may not have ended. If the event duration // exceeds the maximum representable by the duration field, the // event is split into several contiguous segments. The buffer will // discard zero-duration events. // int DtmfBuffer::ParseEvent(uint32_t rtp_timestamp, const uint8_t* payload, size_t payload_length_bytes, DtmfEvent* event) { RTC_CHECK(payload); RTC_CHECK(event); if (payload_length_bytes < 4) { LOG(LS_WARNING) << "ParseEvent payload too short"; return kPayloadTooShort; } event->event_no = payload[0]; event->end_bit = ((payload[1] & 0x80) != 0); event->volume = (payload[1] & 0x3F); event->duration = payload[2] << 8 | payload[3]; event->timestamp = rtp_timestamp; return kOK; } // Inserts a DTMF event into the buffer. The event should be parsed from the // bit stream using the ParseEvent method above before inserting it in the // buffer. // DTMF events can be quite long, and in most cases the duration of the event // is not known when the first packet describing it is sent. To deal with that, // the RFC 4733 specifies that multiple packets are sent for one and the same // event as it is being created (typically, as the user is pressing the key). // These packets will all share the same start timestamp and event number, // while the duration will be the cumulative duration from the start. When // inserting a new event, the InsertEvent method tries to find a matching event // already in the buffer. If so, the new event is simply merged with the // existing one. int DtmfBuffer::InsertEvent(const DtmfEvent& event) { if (event.event_no < 0 || event.event_no > 15 || event.volume < 0 || event.volume > 36 || event.duration <= 0 || event.duration > 65535) { LOG(LS_WARNING) << "InsertEvent invalid parameters"; return kInvalidEventParameters; } DtmfList::iterator it = buffer_.begin(); while (it != buffer_.end()) { if (MergeEvents(it, event)) { // A matching event was found and the new event was merged. return kOK; } ++it; } buffer_.push_back(event); // Sort the buffer using CompareEvents to rank the events. buffer_.sort(CompareEvents); return kOK; } bool DtmfBuffer::GetEvent(uint32_t current_timestamp, DtmfEvent* event) { DtmfList::iterator it = buffer_.begin(); while (it != buffer_.end()) { // |event_end| is an estimate of where the current event ends. If the end // bit is set, we know that the event ends at |timestamp| + |duration|. uint32_t event_end = it->timestamp + it->duration; #ifdef LEGACY_BITEXACT bool next_available = false; #endif if (!it->end_bit) { // If the end bit is not set, we allow extrapolation of the event for // some time. event_end += max_extrapolation_samples_; DtmfList::iterator next = it; ++next; if (next != buffer_.end()) { // If there is a next event in the buffer, we will not extrapolate over // the start of that new event. event_end = std::min(event_end, next->timestamp); #ifdef LEGACY_BITEXACT next_available = true; #endif } } if (current_timestamp >= it->timestamp && current_timestamp <= event_end) { // TODO(hlundin): Change to <. // Found a matching event. if (event) { event->event_no = it->event_no; event->end_bit = it->end_bit; event->volume = it->volume; event->duration = it->duration; event->timestamp = it->timestamp; } #ifdef LEGACY_BITEXACT if (it->end_bit && current_timestamp + frame_len_samples_ >= event_end) { // We are done playing this. Erase the event. buffer_.erase(it); } #endif return true; } else if (current_timestamp > event_end) { // TODO(hlundin): Change to >=. // Erase old event. Operation returns a valid pointer to the next element // in the list. #ifdef LEGACY_BITEXACT if (!next_available) { if (event) { event->event_no = it->event_no; event->end_bit = it->end_bit; event->volume = it->volume; event->duration = it->duration; event->timestamp = it->timestamp; } it = buffer_.erase(it); return true; } else { it = buffer_.erase(it); } #else it = buffer_.erase(it); #endif } else { ++it; } } return false; } size_t DtmfBuffer::Length() const { return buffer_.size(); } bool DtmfBuffer::Empty() const { return buffer_.empty(); } int DtmfBuffer::SetSampleRate(int fs_hz) { if (fs_hz != 8000 && fs_hz != 16000 && fs_hz != 32000 && fs_hz != 48000) { return kInvalidSampleRate; } max_extrapolation_samples_ = 7 * fs_hz / 100; frame_len_samples_ = fs_hz / 100; return kOK; } // The method returns true if the two events are considered to be the same. // The are defined as equal if they share the same timestamp and event number. // The special case with long-lasting events that have to be split into segments // is not handled in this method. These will be treated as separate events in // the buffer. bool DtmfBuffer::SameEvent(const DtmfEvent& a, const DtmfEvent& b) { return (a.event_no == b.event_no) && (a.timestamp == b.timestamp); } bool DtmfBuffer::MergeEvents(DtmfList::iterator it, const DtmfEvent& event) { if (SameEvent(*it, event)) { if (!it->end_bit) { // Do not extend the duration of an event for which the end bit was // already received. it->duration = std::max(event.duration, it->duration); } if (event.end_bit) { it->end_bit = true; } return true; } else { return false; } } // Returns true if |a| goes before |b| in the sorting order ("|a| < |b|"). // The events are ranked using their start timestamp (taking wrap-around into // account). In the unlikely situation that two events share the same start // timestamp, the event number is used to rank the two. Note that packets // that belong to the same events, and therefore sharing the same start // timestamp, have already been merged before the sort method is called. bool DtmfBuffer::CompareEvents(const DtmfEvent& a, const DtmfEvent& b) { if (a.timestamp == b.timestamp) { return a.event_no < b.event_no; } // Take wrap-around into account. return (static_cast(b.timestamp - a.timestamp) < 0xFFFFFFFF / 2); } } // namespace webrtc