Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some DTMF injection fixes #1809

Closed
wants to merge 7 commits into from

Conversation

tombriden
Copy link
Contributor

@tombriden tombriden commented Mar 13, 2024

A few scenarios are fixed for me with these changes.

packet_encoded_tx: add a ts delay when transmitting DTMF event packets

This fixes not passing a ts_delay into codec_output_rtp resulting in packets not being scheduled correctly (like already happens in codec_add_dtmf_packet) and an, eg, 100ms event has all packets transmitted in as few as 20ms

dtmf_event_payload: canonicalise DTMF end event ts if start packet send was delayed

in some scenarios DTMF injected with, eg, 100ms duration are transmitted much shorter due to the start time being adjusted but not the end time

dtmf_inject: fix generating one too many event packets

the starting value used to add num_samples to is that of the current packet, which is already increased from the previous packet. The result is a 100ms injection coming through as 6 packets with a total event-duration of 960
dtmf_inject: adjust start_pts if last_event + pause is later than it

codec_last_dtmf_event: return ts of dtmf_state if handler queue is empty
dtmf_inject: adjust start_pts if last_event + pause is later than it

DTMF requires an inter-digit pause, which is allowed for with the pause parameter. However, this is only accounted for if an injection request comes in during an insertion, as its the event queue used to return the last event end timestamp. If there's nothing on the queue, we can fall back to the dtmf_state var as that seems to always be the most recent.
The second commit can now use this value to calculate a minimum start ts for the injected event to ensure the pause is accounted for. Similar to the previous change, the actual time is increased by one packet's worth of samples to avoid an 100ms pause only having 80ms worth of packets

dtmf: only update recv list if not injected and send list if injected, delayed or not blocked

when the DTMF-security mode is one of the values between PCM_REPLACE_START/END, the event packets are transcoded to PCM DTMF. Injected DTMF gets added to the dtmf_recv list and can cause out of order timestamp in the list, resulting in is_in_dtmf incorrectly returning NULL, resulting in those PCM DTMF frames leaking through.
I'm not sure if its needed, but to me it seems logical that maintaining separate lists for recv and send shouldn't result in them being basically identical by always adding all events to it (other than an adjusted ts for dtmf-delay). So have included a change only add to the send list if its injected, unblocked or being delayed. This way each list contains only contains the relevant events.

@rfuchs
Copy link
Member

rfuchs commented Mar 14, 2024

Looks like this is breaking some of the test cases. I'll try to adapt.

daemon/dtmf.c Outdated
Comment on lines 856 to 880
// get the last event end time, and increase by the required pause
// conversely to the above, we need to add the last packet num samples to its TS before adding
// a pause so we dont generate one packet too few
// if that's later than start_pts, we need to adjust it
uint64_t last_end_pts = codec_last_dtmf_event(csh) + ((pause + ch->dest_pt.ptime) * ch->dest_pt.clock_rate / 1000);
if (last_end_pts > start_pts)
start_pts = last_end_pts;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure this is correct? This seems to add the pause even if this is the first DTMF event. Is this intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, good spot. have fixed that to only adjust last_end_pts if it isn't 0

@rfuchs
Copy link
Member

rfuchs commented Mar 18, 2024

I've pushed the final rebased series of commits (plus an extra one that was needed) including the updated tests to https://github.com/sipwise/rtpengine/tree/rfuchs/gh1809

Have a look if these look OK to you, then I will merge.

@tombriden
Copy link
Contributor Author

I've pushed the final rebased series of commits (plus an extra one that was needed) including the updated tests to https://github.com/sipwise/rtpengine/tree/rfuchs/gh1809

Have a look if these look OK to you, then I will merge.

I don't have a scenario that makes use of the extra commit you've added, but it looks sane to me. Tests are passing here now too. Thanks a lot!

daemon/codec.c Outdated
Comment on lines 4001 to 4005
struct telephone_event_payload *ev_pt = (void *) inout->s;
ts_delay = ntohs(ev_pt->duration) - (ch->handler->dest_pt.ptime * ch->handler->dest_pt.clock_rate / 1000);
if (is_dtmf == 3)
repeats = 2; // DTMF end event
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would make sense to always do this TS adjustment, and not just for non-start event packets? I assume in your case the delay probably comes out as zero for the initial packet, but it's possible the first packet (triggering the start event) uses a different duration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. the first event packet has had its ts increased from the previous packet already so it gets scheduled correctly; in the pcaps I made during testing, the first packet always looked right (~20ms from the previous non-event packet). Then, because the ts_delay is applied on top of that first event packet TS, this adjustment has to be one ptime less than the current event duration so that they don't end up being scheduled starting 40ms after the first event.

Copy link
Member

@rfuchs rfuchs Mar 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. the first event packet has had its ts increased from the previous packet already so it gets scheduled correctly; in the pcaps I made during testing, the first packet always looked right (~20ms from the previous non-event packet). Then, because the ts_delay is applied on top of that first event packet TS, this adjustment has to be one ptime less than the current event duration so that they don't end up being scheduled starting 40ms after the first event.

AFAICT the first event packet usually has a duration of 20 ms or 160 samples, so that would come out as a delay of 160 - 20 * 8 = 0. But what if that packet got lost, and the packet triggering the start event (i.e. the state change) is actually the second one, using a duration of 40 ms or 320 samples?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see what you mean. In which case that second-now-first event packet wouldn't be scheduled correctly as it should have had a ts_delay of 160 but ended up with 0. Yep, makes sense. you want me to amend that from your branch and repush here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i gone ahead and done that anyway :)

tombriden and others added 7 commits March 20, 2024 15:14
…nt packets

when DTMF is being transmitted using codec_output_rtp, the scheduled
time to send the packet is calculated using the timestamp difference
from the last transmitted applied on top of the realtime time the
last packet was sent. However, the timestamp is only updated on the
first event packet so a delay needs to be passed in to codec_output_rtp
to ensure the subsequent packets are scheduled based on the event
duration of the event packets

Change-Id: I5a2f6cf67b5f570f6099d201592d9a6fc01d60a5
…packet send was delayed

in some scenarios the start event ts can be before the *pts value, which
will result in a shortened DTMF event being transmitted than expected during
injection as the end event ts is calculated based on that initial dtmf start
value.
This change updates the end event ts by the amount the start ts was
behind, so that the resulting event has the right duration

Change-Id: Ia637d1e1c5d92de8b35317ec552c22eae23c0645
Change-Id: Ia16ffefc0791d01575248ac5d8025eb30ccaec67
the num_samples was added to the start_pts, which is the first event
packet timestamp, which has already increased its ts by its event
duration. so, the total duration of events ends up being one packet
more than intended.

Change-Id: I423bb222a81c5bd78e570ff2026c72dd4dd1b100
…eue is empty

this function is used to determine if a pause is needed on a new
injected DTMF's start ts to ensure a gap between the events. However,
if an inject request comes in after the end of the previous event
but before it would have been offset due to pause, no pause is added

This change returns the ts value from dtmf_state if the queue is
empty as that will always be the ts of the last DTMF transmitted

Change-Id: I4f3cf5115d1a8e26c0ca1bc9570c46e29391e0d0
… than it

now that the returned last_event_ts is always that of the previous
DTMF, we can ensure that the next one isn't transmitted until that
time plus the required pause.
Like the num_samples calculation, the actual time needs to be
increased by 1 packets worth of samples so tha pause lasts the
full duration required

Change-Id: I6da1dd7cbcf49f7f0431a5123df2cdc382fe3dba
… injected, delayed or not blocked

having injected events on the recv list can cause out of order TS
values which results in is_in_dtmf incorrectly returning NULL and
letting the transcoded PCM frames through.
It also doesn't make sense to add DTMF to the send list unless they're
actually being sent, so injected delayed or unblocked

Change-Id: I07e2a35e27142715a5257f199326b7a3d133e2a8
@rfuchs
Copy link
Member

rfuchs commented Mar 20, 2024

Everything merged and backported, thanks!

@rfuchs rfuchs closed this Mar 20, 2024
@tombriden tombriden deleted the dtmf-injection-fixes branch March 21, 2024 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants