res_rtp_asterisk: Add support for sending NACK requests.

Support has been added for receiving a NACK request and handling it.
Now, Asterisk can detect when a NACK request should be sent and knows
how to construct one based on the packets we've received from the remote
end. A buffer has been added that will store out of order packets until
we receive the packet we are expecting. Then, these packets are handled
like normal and frames are queued to the core like normal. Asterisk
knows which packets to request in the NACK request using a vector
which stores the sequence numbers of the packets we are currently missing.

If a missing packet is received, cycle through the buffer until we reach
another packet we have not received yet. If the buffer reaches a certain
size, send a NACK request. If the buffer reaches its max size, queue all
frames to the core and wipe the buffer and vector.

According to RFC3711, the NACK request must be sent out in a compound
packet. All compound packets must start with a sender or receiver
report, so some work was done to refactor the current sender / receiver
code to allow it to be used without having to also include sdes
information and automatically send the report.

Also added additional functionality to ast_data_buffer, along with some
testing.

For more information, refer to the wiki page:
https://wiki.asterisk.org/wiki/display/AST/WebRTC+User+Experience+Improvements

ASTERISK-27810 #close

Change-Id: Idab644b08a1593659c92cda64132ccc203fe991d
16.0
Ben Ford 7 years ago committed by Benjamin Keith Ford
parent 5f01f73f51
commit 5bacde37a2

@ -110,6 +110,35 @@ int ast_data_buffer_put(struct ast_data_buffer *buffer, size_t pos, void *payloa
*/
void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos);
/*!
* \brief Remove a data payload from the data buffer
*
* \param buffer The data buffer
* \param pos The position of the data payload
*
* \retval non-NULL success
* \retval NULL failure
*
* \note This DOES remove the data payload from the data buffer. It does not free it, though.
*
* \since 15.5.0
*/
void *ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos);
/*!
* \brief Remove the first payload from the data buffer
*
* \param buffer The data buffer
*
* \retval non-NULL success
* \retval NULL failure
*
* \note This DOES remove the data payload from the data buffer.
*
* \since 15.5.0
*/
void *ast_data_buffer_remove_head(struct ast_data_buffer *buffer);
/*!
* \brief Free a data buffer (and all held data payloads)
*

@ -281,6 +281,60 @@ void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos)
return NULL;
}
static void data_buffer_free_buffer_payload(struct ast_data_buffer *buffer,
struct data_buffer_payload_entry *buffer_payload)
{
buffer_payload->payload = NULL;
buffer->count--;
if (buffer->cache_count < CACHED_PAYLOAD_MAX
&& buffer->cache_count < (buffer->max - buffer->count)) {
AST_LIST_INSERT_TAIL(&buffer->cached_payloads, buffer_payload, list);
buffer->cache_count++;
} else {
ast_free(buffer_payload);
}
}
void *ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos)
{
struct data_buffer_payload_entry *buffer_payload;
ast_assert(buffer != NULL);
AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, buffer_payload, list) {
if (buffer_payload->pos == pos) {
void *payload = buffer_payload->payload;
AST_LIST_REMOVE_CURRENT(list);
data_buffer_free_buffer_payload(buffer, buffer_payload);
return payload;
}
}
AST_LIST_TRAVERSE_SAFE_END;
return NULL;
}
void *ast_data_buffer_remove_head(struct ast_data_buffer *buffer)
{
ast_assert(buffer != NULL);
if (buffer->count > 0) {
struct data_buffer_payload_entry *buffer_payload;
void *payload;
buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);
payload = buffer_payload->payload;
data_buffer_free_buffer_payload(buffer, buffer_payload);
return payload;
}
return NULL;
}
void ast_data_buffer_free(struct ast_data_buffer *buffer)
{
struct data_buffer_payload_entry *buffer_payload;

File diff suppressed because it is too large Load Diff

@ -217,6 +217,7 @@ AST_TEST_DEFINE(buffer_resize)
AST_TEST_DEFINE(buffer_nominal)
{
RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);
RAII_VAR(struct mock_payload *, removed_payload, NULL, ast_free_ptr);
struct mock_payload *payload;
struct mock_payload *fetched_payload;
int ret;
@ -247,6 +248,9 @@ AST_TEST_DEFINE(buffer_nominal)
"Failed to allocate memory for payload %d", i);
ret = ast_data_buffer_put(buffer, i, payload);
if (ret) {
ast_free(payload);
}
ast_test_validate(test, ret == 0,
"Failed to add payload %d to buffer", i);
@ -268,7 +272,11 @@ AST_TEST_DEFINE(buffer_nominal)
ast_test_validate(test, payload != NULL,
"Failed to allocate memory for payload %d", i + BUFFER_MAX_NOMINAL);
payload->id = i;
ret = ast_data_buffer_put(buffer, i + BUFFER_MAX_NOMINAL, payload);
if (ret) {
ast_free(payload);
}
ast_test_validate(test, ret == 0,
"Failed to add payload %d to buffer", i + BUFFER_MAX_NOMINAL);
@ -289,6 +297,30 @@ AST_TEST_DEFINE(buffer_nominal)
"Failed to get payload at position %d during second loop", i + BUFFER_MAX_NOMINAL);
}
removed_payload = (struct mock_payload *)ast_data_buffer_remove_head(buffer);
ast_test_validate(test, removed_payload != NULL,
"Failed to get the payload at the HEAD of the buffer");
ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL - 1,
"Removing payload from HEAD of buffer did not decrease buffer size");
ast_test_validate(test, removed_payload->id == 1,
"Removing payload from HEAD of buffer did not return expected payload");
ast_free(removed_payload);
removed_payload = (struct mock_payload *)ast_data_buffer_remove(buffer, BUFFER_MAX_NOMINAL * 2);
ast_test_validate(test, removed_payload != NULL,
"Failed to get payload at position %d from buffer", BUFFER_MAX_NOMINAL * 2);
ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL - 2,
"Removing payload from buffer did not decrease buffer size");
ast_test_validate(test, removed_payload->id == BUFFER_MAX_NOMINAL,
"Removing payload from buffer did not return expected payload");
return AST_TEST_PASS;
}

Loading…
Cancel
Save