title = “RTP Stream Matching” weight = 4 +++
Introduction
The fetch_and_match
method from TestRtpLuaAgent Lua API is used to validate the RTP packets which
have been received from the remote endpoint.
The method will:
- Request
TestRtpApp
to send us the stashed RTP packets. - Test that the stream matches expected (on-disk file) fragments.
- Test that the stream matches expected silence.
- Test that the stream matches expected AMR-WB mode changes.
Matching API
.fetch_and_match [Asynchronous]
The fetch_and_match
method requests the TestRtpApp
to send us all of the RTP packets which
where received by our local endpoint since the most recent previous call to fetch_and_match
or (more typically) since the initial registration of our local UDP port.
The agent will wait (asynchronous) for the stashed RTP packets to be supplied to us, and then will iterate and perform the testing logic according to the provided list of expected content.
The match will nearly always involve “fuzzy” timing boundaries, since there is inevitably a variable timing latency associated with the SIP signalling to set up the RTP stream. Hence the “expected” matching rules have a minimum/maximum timing window for each element, as described here.
The arguments to the method are:
Argument | Type | Description |
---|---|---|
expected
|
Object | [Required] A container with subfields describing the expected RTP packets. |
encoding
|
0 - 255
|
[Required] The event type number, indicating which key was pressed/released, typically as per RFC 4733. |
encoding
|
PCMU /
PCMA /
AMR /
AMR-WB
|
[Required] The encoding of the RTP audio stream. This must be one of the supported encoding constants, being PCMU (ITU-T REC G.711 µ-Law),
PCMA (ITU-T REC G.711 a-Law),
AMR (3GPP TS 26.073),
AMR-WB (ITU-T REC G.722.2)
|
fragments
|
Array of Object |
[Required] Array of "expected" fragments in sequence, each one being either a files fragment,
or a "silence" fragment with silence_min and silence_max attributes.
|
files
|
Array of String |
An array of filenames which are appended together to make the expected source for this fragment. Each filename may include a relative filepath within the audio_dir parameter of TestRtpLuaAgent .Each filename must include the file suffix. (Default = This fragment is not a "files" fragment). |
min
|
Float |
This value applies only when files is present for this fragment.The minimum audio duration (in seconds) of stream data expected to match from these appended files. (Default = The entire file content should be present in the stream). |
max
|
Float |
This value applies only when files is present for this fragment.The maximum audio duration (in seconds) of stream data expected to match from these appended files. (Default = Up to the the entire file content may be present in the stream). |
silence_min
|
Float |
This value applies only when files is not present for this fragment.The minimum audio duration (in seconds) of silence expected to receive for this fragment. (Default = The silence may be as short as zero seconds). |
silence_max
|
Float |
[Required] This value is mandatory when files is not present for this fragment.The maximum audio duration (in seconds) of silence expected to receive for this fragment. |
amr
|
Object |
Container for additional AMR mode changes that require validation. The expected AMR mode changes are tested against the entire stream, independent of the audio fragment validation. (Default = do not perform any validation of AMR mode changes). |
modes
|
Array of Object |
Array of object each of which describe an AMR mode change period to validate. (Default = do not perform any validation of AMR mode changes). |
mode
|
0 - 8
|
[Required] The AMR mode number which applies for this period. |
min
|
Float |
The minimum duration (in seconds) that we expect this AMR period to apply within the stream. (Default = the AMR mode has no mimimum duration). |
max
|
Float |
The maximum duration (in seconds) that we expect this AMR period to apply within the stream. (Default = the AMR mode has no maximum duration). |
Example negotiating AMR-WB with mode changes, and validating those changes:
-- Attempt to establish a RTP listener that we can direct RTP packets at and store for comparison.
local register_result = truo.register ()
-- Static for our call.
local endpoints = tsuo.default_endpoints ({ local_rtp_port = register_result.local_port })
local calling_party = '665566'
local called_party = '4000'
local encoding = 'AMR/8000'
-- Get a SIP outcall context from our helper library.
local context = tsuo.invite_context (endpoints, calling_party, called_party)
-- Construct the SDP.
local sdp_session_id = os.time () % 10000
local sdp_session_version = math.random (1000) - 1
local sdp_offer =
"v=0\
o=" .. calling_party .. " " .. sdp_session_id .. " " .. sdp_session_version .. " IN IP4 " .. endpoints.local_rtp_ip .. "\
s=-\
c=IN IP4 " .. endpoints.local_rtp_ip .. "\
t=0 0\
m=audio " .. endpoints.local_rtp_port .. " RTP/AVP 96 102\
a=rtpmap:96 AMR/8000\
a=fmtp:96 mode-set=2,4,7;mode-change-neighbor=1;mode-change-period=2;octet-align=1\
a=rtpmap:102 telephone-event/8000"
-- Construct and Send INVITE Request.
tsuo.invite_send_request (context, sdp_offer)
-- Expect Trying & Ringing.
tsuo.invite_expect_response (context, 100, "Trying", { ['User-Agent'] = "N-Squared LHO" })
tsuo.invite_expect_response (context, 180, "Ringing")
tv = match.elapsed ("Ring Notification (immediate)", tv, 0.0)
-- Expect INVITE Answer Response (200 OK) after 2 seconds.
local invite_response = tsuo.invite_expect_response (context, 200, "OK", nil, { sdp_media = tsuo.SDP_MEDIA_N2_AMRNB_OA })
tv = match.elapsed ("Call Answered (2.0s)", tv, 1.99)
-- Request our RTP tester to connect to the far-end IP/port. Now we can send RTP as well as receive it.
truo.connect (invite_response.sdp.connection.ip4.address, invite_response.sdp.media.audio.port)
-- Send an AMR CMR before we ACK to ensure streaming uses the desired mode.
truo.send_amr_cmr (96, 2)
-- Try to ENSURE the CMR goes on the wire before the ACK. Without this,
-- sometimes the ACK wins.
n2svcd.wait (0.02)
-- ACK the 200.
tsuo.invite_send_2xx_ack (context)
-- Voice call in progress.
-- The "Goodbye" announcement is 0.65 seconds and it is played twice before hangup.
-- Request to go to mode 7. The mode change restrictions will cause us to
-- send two mode 4 frames before our requested change will be applied.
n2svcd.wait (0.2)
truo.send_amr_cmr (96, 7)
-- ...and back to mode 2.
n2svcd.wait (0.2)
truo.send_amr_cmr (96, 2)
n2svcd.wait (0.1)
-- Send BYE from our side.
tsuo.bye_send_request (context)
tv = match.elapsed ("Send BYE (0.5s)", tv, 0.5)
-- Expect immediate acknowledgement.
tsuo.bye_expect_response (context, 200, "OK")
tv = match.elapsed ("BYE Confirmed (immediate)", tv, 0.0)
-- Trigger a collection of RTP packets and match against the expected configuration.
truo.fetch_and_match ({
encoding = encoding,
fragments = {
{ files = { 'English/announcements/Goodbye.amr' }, min = 0.5, max = 0.65 },
{ silence_min = 0, silence_max = 0.1 }
}, amr = {
modes = {
{ mode = 2, min = 0.15, max = 0.25 },
{ mode = 4, min = 0.04, max = 0.04 },
{ mode = 7, min = 0.15, max = 0.25 },
{ mode = 4, min = 0.04, max = 0.04 },
{ mode = 2 }
}
}
})