You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			227 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
		
		
			
		
	
	
			227 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
| 
											10 years ago
										 | /*
 | ||
|  |  *  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 <assert.h>
 | ||
|  | #include <algorithm>  // max
 | ||
|  | 
 | ||
|  | // 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 { | ||
|  | 
 | ||
|  | // 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, | ||
|  |                            int payload_length_bytes, | ||
|  |                            DtmfEvent* event) { | ||
|  |   if (!payload || !event) { | ||
|  |     return kInvalidPointer; | ||
|  |   } | ||
|  |   if (payload_length_bytes < 4) { | ||
|  |     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) { | ||
|  |     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; | ||
|  | } | ||
|  | 
 | ||
|  | 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<uint32_t>(b.timestamp - a.timestamp) < 0xFFFFFFFF / 2); | ||
|  | } | ||
|  | }  // namespace webrtc
 |