<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<base href="https://wiki.asterisk.org/wiki" />
<title>Message Title</title>
<style type="text/css">@media only screen and (max-device-width: 480px) {.mobile-only {
width: auto !important;
height: auto !important;
overflow: visible !important;
line-height: normal !important;
font-size: inherit !important;
mso-hide: all;
}
.desktop-only {
display: none !important;
}
/* iPhone 3GS fix for unwanted 20px right margin */
body { min-width: 100% !important; padding: 0; margin: 0; }
#center-content-table { max-width: none; !important; }
#header-pattern-container { padding: 10px 10px 10px 10px !important; line-height: 20px !important; }
#header-avatar-image-container { padding-right: 8px !important; }
#email-content-container { padding: 0 !important; }
.mobile-expand { border-radius: 0 !important; border-left: 0 !important; border-right: 0 !important; padding-left: 26px !important;}
.mobile-resize-text { font-size: 16px !important; line-height: 22px !important; }
#page-title-pattern-header { font-size: 20px !important; line-height: 28px !important; }
#page-title-pattern-icon-image-container-cell { padding-top: 7px !important; }
#inline-user-pattern { display: block !important; }
#inline-user-pattern-avatar { padding-top: 3px !important; }
.contextual-area-pattern { border-bottom: 1px solid #ccc !important; padding: 15px 10px 0 10px !important;}
.users-involved-pattern-column-table { width: 100% !important; }
.users-involved-pattern-avatar-table-cell { padding: 3px 5px 5px 0 !important; }
.users-involved-pattern-column-container { padding-right: 0 !important; }
.contextual-excerpt-pattern, #users-involved-pattern { border: 0 !important; }
/** Aui Typography upsized for mobile **/
#content-excerpt-pattern-container, #contextual-excerpt-pattern-text-container { font-size: 16px !important; line-height: 22px !important; }
#content-excerpt-pattern-container h1, #contextual-excerpt-pattern-text-container h1 { font-size: 24px !important; line-height: 28px !important; }
#content-excerpt-pattern-container h2, #contextual-excerpt-pattern-text-container h2 { font-size: 20px !important; line-height: 28px !important; }
#content-excerpt-pattern-container h3, #contextual-excerpt-pattern-text-container h3 { font-size: 18px !important; line-height: 24px !important; }
#content-excerpt-pattern-container h4, #contextual-excerpt-pattern-text-container h4 { font-size: 16px !important; line-height: 22px !important; }
#content-excerpt-pattern-container h5, #contextual-excerpt-pattern-text-container h5 { font-size: 14px !important; line-height: 20px !important; }
#content-excerpt-pattern-container h6, #contextual-excerpt-pattern-text-container h6 { font-size: 14px !important; line-height: 20px !important; }
.user-mention { line-height: 18px !important; }
/** Aui Typography end **/
/* Show appropriate footer logo on mobile, display links vertically */
#footer-pattern { padding: 15px 10px !important; }
#footer-pattern-logo-desktop-container { padding: 0 !important; }
#footer-pattern-logo-desktop { width: 0 !important; height: 0 !important; }
#footer-pattern-logo-mobile {
padding-top: 10px !important;
width: 30px !important;
height: 27px !important;
display: inline !important;
}
#footer-pattern-text {
display: block !important;
}
#footer-pattern-links-container { line-height: 0 !important;}
.footer-pattern-links.mobile-resize-text,
.footer-pattern-links.mobile-resize-text,
#footer-pattern-text.mobile-resize-text,
#footer-pattern-links-container.no-footer-links {
font-size: 14px !important;
line-height: 20px !important;
}
.footer-link { display: block !important; }
#footer-pattern-links-container table { display: inline-block !important; float: none !important; }
#footer-pattern-links-container, #footer-pattern-text { text-align: center !important; }
#footer-pattern-links { padding-bottom: 5px !important; }
/** Team Calendar overrides, these should be removed when notifications are updated in Team Calendars. For now CSS
overrides are being used because the structure of the content can't change without rereleasing the plugin */
.mail-calendar-container .day-header + table tr td:first-child {
vertical-align: top !important;
padding-top: 5px !important;
}}
@media (min-width: 900px) {#center-content-table { width: 900px; }}
@media all {#outlook a {padding:0;} /* Force Outlook to provide a "view in browser" menu link. */
/* Prevent Webkit and Windows Mobile platforms from changing default font sizes.*/
body{-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%;}
.ExternalClass {width:100%;} /* Force Hotmail to display emails at full width */
#background-table {margin:0; padding:0; width:100% !important; }
/* Needed to override highlighting on date and time links in iOS */
.grey a {color: #707070; text-decoration: none; }/* These styles are appended to the head element of a notification in order to prevent Apple Mail and similar
clients from underlining the due dates with a blue hyperlink */
/* a lozenge outside an inline task should always be #333, lozenges inside an inline task should be
colored according to their upcoming due dates, a completed task date lozenge or deleted task date
lozenge should always be #707070 */
.date-time-lozenge a {color: #333333; text-decoration: none; }
.inline-task-text-container .date-time-lozenge.date-upcoming a {color: #DF6F00; text-decoration: none; }
.inline-task-text-container .date-time-lozenge.date-past a {color: #D04437; text-decoration: none; }
.inline-task-text-container.content-deleted-color .date-time-lozenge a,
.inline-task-text-container.checked .date-time-lozenge a {
color: #707070; text-decoration: none;
}}
</style>
</head>
<body>
<table id="background-table" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; background-color: #f5f5f5">
<tbody>
<tr>
<td id="header-pattern-container" style="padding: 0px; border-collapse: collapse; padding: 10px 20px">
<table id="header-pattern" cellspacing="0" cellpadding="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td id="header-avatar-image-container" valign="top" style="padding: 0px; border-collapse: collapse; vertical-align: top; width: 32px; padding-right: 9px"><a href="https://wiki.asterisk.org/wiki/display/~mmichelson?src=email" style="color: #3b73af; text-decoration: none"><img id="header-avatar-image" class="image_fix" src="cid:avatar_99ed0aa29d6f204db4785296f8170422" height="32" width="32" border="0" style="border-radius: 3px; vertical-align: top" /></a></td>
<td id="header-text-container" valign="middle" style="padding: 0px; border-collapse: collapse; vertical-align: middle; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 1px">Mark Michelson <strong>edited</strong> a page</td>
</tr>
</tbody>
</table> </td>
</tr>
<!-- End Header pattern -->
<tr>
<td id="email-content-container" style="padding: 0px; border-collapse: collapse; padding: 0 20px">
<table id="email-content-table" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; border-spacing: 0; border-collapse: separate">
<tbody>
<tr>
<td class="email-content-rounded-top mobile-expand" style="padding: 0px; border-collapse: collapse; color: #fff; padding: 0 15px 0 16px; height: 15px; background-color: #fff; border-left: 1px solid #ccc; border-top: 1px solid #ccc; border-right: 1px solid #ccc; border-bottom: 0; border-top-right-radius: 5px; border-top-left-radius: 5px"> </td>
</tr>
<tr>
<td class="email-content-main mobile-expand" style="padding: 0px; border-collapse: collapse; border-left: 1px solid #ccc; border-right: 1px solid #ccc; border-top: 0; border-bottom: 0; padding: 0 15px 15px 16px; background-color: #fff">
<table id="page-title-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td id="page-title-pattern-icon-image-container" valign="top" style="padding: 0px; border-collapse: collapse; width: 16px; vertical-align: top">
<table cellspacing="0" cellpadding="0" border="0" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td id="page-title-pattern-icon-image-container-cell" style="padding: 0px; border-collapse: collapse; width: 16px; padding: 9px 8px 0px 0px; mso-text-raise: 5px; mso-line-height-rule: exactly"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?src=email" title="page icon" style="vertical-align: top;; color: #3b73af; text-decoration: none"><img style="vertical-align: top; display: block;" src="cid:page-icon" alt="page icon" title="page icon" height="16" width="16" border="0" /></a></td>
</tr>
</tbody>
</table> </td>
<td style="vertical-align: top;; padding: 0px; border-collapse: collapse; padding-right: 5px; font-size: 20px; line-height: 30px; mso-line-height-rule: exactly" id="page-title-pattern-header-container"><span id="page-title-pattern-header" style="font-family: Arial, sans-serif; padding: 0; font-size: 20px; line-height: 30px; mso-text-raise: 2px; mso-line-height-rule: exactly; vertical-align: middle"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?src=email" title="RTP task list" style="color: #3b73af; text-decoration: none">RTP task list</a></span></td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td class="email-content-main mobile-expand" style="padding: 0px; border-collapse: collapse; border-left: 1px solid #ccc; border-right: 1px solid #ccc; border-top: 0; border-bottom: 0; padding: 0 15px 15px 16px; background-color: #fff">
<table class="content-excerpt-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 1px">
<tbody>
<tr>
<td class="content-excerpt-pattern-container mobile-resize-text " style="padding: 0px; border-collapse: collapse; padding: 0 0 0 24px"> <p class="diff-context-placeholder" style="margin: 10px 0 0 0; margin-top: 0">...</p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>Create the function that runs in the thread. The thread should run a loop that polls a collection of file descriptors to determine if any is ready for reading. If any file descriptor is ready for reading, read data from that file descriptor. Pass this data to the associated stream's demultiplexer.</li>
<li>Write a function that will allow for a file descriptor to be monitored by the monitor thread. The function should take a file descriptor and an RTP stream as arguments so that a file descriptor can be associated with its associated stream.</li>
<li>Modify the <code style="font-family: monospace">init()</code> method for the transport to call the above function after creating its socket.</li>
</ul> <h4 id="RTPtasklist-CreateanRTPpackethandler" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-changed" id="changed-diff-0" style="background-color: #d6f0ff;">Create an RTP </span><span class="diff-html-added" id="added-diff-0" style="font-size: 100%; background-color: #ddfade;">packet handler</span> </h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0"> <span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">The RTP packet handler will be responsible for taking decoded RTP frames, packaging the payload into an </span><code style="font-family: monospace"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">ast_frame</span></code><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> and queuing that frame onto the channel associated with the stream. For the time being, the packet handler can assume that all frames are </span><code style="font-family: monospace"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">AST_FRAME_VOICE</span></code><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> frames and that all frames should be queued onto the channel.</span> </p> <h4 id="RTPtasklist-CreateanRTPDecoder" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span style="color: rgb(133,120,102);"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">Create an RTP </span>Decoder</span> </h4> <p class="diff-block-context" style="margin: 10px 0 0 0"> <span style="color: rgb(133,120,102);">Create a structure to represent an RTP decoder, and add this structure to the RTP stream structure.</span> </p>
<ul class="diff-block-target" style="margin: 10px 0 0 0">
<li> <span style="color: rgb(133,120,102);">Create a structure that represents an RTP header. A suggested structure can be found in <a href="http://tools.ietf.org/html/rfc3550#appendix-A" class="external-link" rel="nofollow" style="color: #3b73af; text-decoration: none">Appendix A</a> of RFC 3550.</span> </li>
<li> <span style="color: rgb(133,120,102);">Write a <code style="font-family: monospace">decode()</code> method that can take raw bytes and separate them into a decoded RTP header and a payload. When decoding RTP, the routine does not need to try to understand or decode RTP header extensions for the time being, but it does need to be prepared for header extensions to be present. This routine should not attempt to perform any processing on the payload. The decoder should pass the decoded RTP <span class="diff-html-removed" id="removed-diff-0" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">header and payload </span><span class="diff-html-added" id="added-diff-1" style="font-size: 100%; background-color: #ddfade;">packet </span>to the <span class="diff-html-removed" id="removed-diff-1" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">bufferer</span><span class="diff-html-added" id="added-diff-2" style="font-size: 100%; background-color: #ddfade;">RTP packet handler created in the previous step</span>.</span> </li>
</ul> <h4 id="RTPtasklist-CreateanRTPdemultiplexer" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span style="color: rgb(133,120,102);">Create an RTP demultiplexer</span> </h4> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>Write a <code style="font-family: monospace">demux()</code> method that will pass all data it receives to the RTP decoder's <code style="font-family: monospace">decode()</code> method. For now, this method is pretty useless, but having it in place now will make future demultiplexing efforts much easier.</li>
</ul> <h4 id="RTPtasklist-CreateanRTPbufferer" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-removed" id="removed-diff-2" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Create an RTP bufferer</span> </h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0"> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Create a structure for buffering RTP packets. Add this structure to the RTP session structure.</span> </p>
<ul class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">
<li> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Write a </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">buffer()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method that takes RTP headers and payloads. The function should take the RTP packets and place them into a buffer. The bufferer should inspect RTP sequence numbers and order the packets based on that information. The bufferer should be prepared to possibly receive packets out of order and re-order them as necessary.</span> </li>
<li> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Write a </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method that will return an </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">ast_frame</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> of the appropriate type given the RTP samples in the buffer. The method should take a packetization parameter (not sure if samples or time is better for this) so that the bufferer can know how many RTP packets to look through in order to get the appropriate number of samples. For now, the bufferer can ignore the packetization parameter and always return 20 ms (160 samples if using an 8000 Hz format) of audio.</span> </li>
</ul> <h4 id="RTPtasklist-CreateanRTPSynchronizer" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Create an RTP Synchronizer</span> </h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0"> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">The synchronizer will be implemented using the scheduler argument that is passed into RTP instance creation. The synchronizer should be added to the RTP session structure (*not* the RTP stream). The synchronizer will use the scheduler context that is passed into the Steel Zebra </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">new()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method for scheduling tasks.</span> </p>
<ul class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">
<li> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Write a scheduler callback whose job is to call each bufferer's </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method for each stream on the session. For now, the synchronizer should always request 20 ms (160 samples) of data, and it should assume that the frame type it receives will always be an </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">AST_FRAME_VOICE</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">. Once the frame is retrieved, the synchronizer will queue the frame onto the RTP stream's associated channel.</span> </li>
<li> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Schedule this callback when the RTP session is created. Since our initial setup is assuming ulaw audio with 20 ms packetization, the callback can be scheduled to be called every 20 ms.</span> </li>
</ul> <h2 id="RTPtasklist-Destruction" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 20px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0">Destruction</h2> <p class="diff-block-context" style="margin: 10px 0 0 0">Once a call is hung up, we need to be able to clean up the RTP</p> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p>
<ul class="diff-block-target" style="margin: 10px 0 0 0">
<li>Create a "null" packet router. This packet router's <code style="font-family: monospace">route()</code> method should simply drop all packets passed to it.</li>
<li>Write a <code style="font-family: monospace">stop()</code> RTP engine method. It should do the following:
<ul style="margin: 10px 0 0 0">
<li>Install the null packet router for the RTP stream.</li>
<li> <span class="diff-html-removed" id="removed-diff-3" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Since we are only working with a single RTP stream per RTP session, stop the RTP session's synchronization task by deleting the task from the scheduler.</span> </li>
<li>Remove the file descriptor for the UDP socket from the RTP monitor thread.</li>
</ul> </li>
</ul> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>Create an RTP playout structure. For the time being, the structure should only have an <code style="font-family: monospace">ast_smoother</code> on it.</li>
<li>Add a <code style="font-family: monospace">write()</code> method to the playout structure that takes an <code style="font-family: monospace">AST_FRAME_VOICE</code> as a parameter. The first time that a frame of a particular audio type is received, the smoother should have <code style="font-family: monospace">ast_smoother_reset()</code> called on it, with the size of the smoother being calculated from the format on the frame. The audio data should be fed into the smoother. Then we should read from the smoother in order to get the correct number of samples to send. After these samples have been retrieved, these are passed down to the encoder.</li>
<li>Modify the <code style="font-family: monospace">write()</code> method on Steel Zebra not to call the encoder's <code style="font-family: monospace">encode()</code> method. Instead, this should be replaced by calling the playout's <code style="font-family: monospace">write()</code> method.</li>
</ul> <p class="diff-block-target" style="margin: 10px 0 0 0"> <span class="diff-html-removed" id="removed-diff-4" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">The synchronizer should be altered to tick at intervals equal to the packetization that we request for incoming audio. This interval can be set using </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">ast_rtp_codecs_get_framing()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">. When the synchronizer calls into the bufferer's </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method, the synchronizer needs to specify the appropriate packetization. The bufferer needs to be prepared to provide the requested amount of audio, which may span multiple RTP packets. It may also mean that an RTP packet has a portion of its samples returned on one </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> call and the rest of the samples need to be returned in a subsequent </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> call. If the synchronizer requests more samples than are currently buffered, then the bufferer should return an </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">AST_FRAME_NULL</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> and the synchronizer should not queue anything onto the channel.</span><span class="diff-html-added" id="added-diff-3" style="font-size: 100%; background-color: #ddfade;">No change is required for the inflow since code at the channel or bridge layer can provide buffering and smoothing of media. The RTP inflow should remain packaging received media in </span><code style="font-family: monospace"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">ast_frames</span></code><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> based on the amount of media received.</span> </p> <h2 id="RTPtasklist-Addtelephone-eventsupport" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 20px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0">Add telephone-event support</h2> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <h4 id="RTPtasklist-Incomingtelephoneevents" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0">Incoming telephone events</h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0"> <span class="diff-html-removed" id="removed-diff-5" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Bufferer </span><span class="diff-html-added" id="added-diff-4" style="font-size: 100%; background-color: #ddfade;">RTP packet handler </span>changes:</p>
<ul class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">
<li> <span class="diff-html-removed" id="removed-diff-6" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Bufferer's </span><span class="diff-html-added" id="added-diff-5" style="font-size: 100%; background-color: #ddfade;">The RTP packet handler </span>will need to be made pluggable, so that when <span class="diff-html-removed" id="removed-diff-7" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">the </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method is called, the bufferer can call into plugged-in elements to interpret certain RTP packets into appropriate frame types</span><span class="diff-html-added" id="added-diff-6" style="font-size: 100%; background-color: #ddfade;">RFC 4733 packets are received, they can be packaged into an </span><code style="font-family: monospace"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">AST_FRAME_DTMF_BEGIN</span></code><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> or </span><code style="font-family: monospace"><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;">AST_FRAME_DTMF_END</span></code><span class="diff-html-added" style="font-size: 100%; background-color: #ddfade;"> as appropriate</span>.</li>
<li>An RFC4733 <span class="diff-html-removed" id="removed-diff-8" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">bufferer </span>plugin will need to be created and installed <span class="diff-html-removed" id="removed-diff-9" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">on the bufferer </span>when the <code style="font-family: monospace">AST_RTP_PROPERTY_DTMF</code> property is set on a Steel Zebra instance. The plugin will implement a state machine to determine the current DTMF state of incoming audio. Here are the states:<br />
<ul style="margin: 10px 0 0 0">
<li>No DTMF: There currently is no DTMF in progress</li>
<li>DTMF in progress: An RFC 4733 RTP packet with the marker bit set has been received.</li>
<li>DTMF ended: An RFC 4733 RTP packet with the marker bit has been received.</li>
</ul>The transitions between the states <span class="diff-html-added" id="added-diff-7" style="font-size: 100%; background-color: #ddfade;">can </span>occur when <span class="diff-html-removed" id="removed-diff-10" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">the bufferer's </span><code style="font-family: monospace"><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">get_frame()</span></code><span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> method is called</span><span class="diff-html-added" id="added-diff-8" style="font-size: 100%; background-color: #ddfade;">incoming RTP packets are received</span>.</li>
<li>The <span class="diff-html-removed" id="removed-diff-11" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">bufferer </span><span class="diff-html-added" id="added-diff-9" style="font-size: 100%; background-color: #ddfade;">packet handler </span>will need to call into the plugin on <strong>all</strong> RTP frames. This may seem odd since RFC 4733 should be concerned with specific payloads of RTP; however, the plugin does need to know when non-DTMF RTP has been received since that can affect its operation.</li>
<li> <span class="diff-html-removed" id="removed-diff-12" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">The bufferer will need to be prepared to return multiple frames when called into. For instance, if 40 ms worth of data is requested, it may be that the first 20 ms is ulaw audio and the next 20 ms are DTMF. Since frames are already designed to be returned this way, this should not be difficult.</span> </li>
</ul> <p class="diff-block-context" style="margin: 10px 0 0 0">Decoder changes:</p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>The decoder will need to be able to decode specific types of encoded payloads, such as RFC 4733. So whereas the payload of an RTP packet may have been stored as an array of bytes before, the payload now needs to be able to be stored in alternate formats.</li>
<li>The decoder will need to be made pluggable so that when decoding, the RTP packet may be passed to the plugin in order to be decoded properly. Plugins only need to be called into when an RTP packet of an appropriate payload is received.</li>
</ul> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <p class="diff-block-context" style="margin: 10px 0 0 0">The channel driver maintains information about whether RFC 4733 DTMF should be used on an RTP stream and will call into an RTP stream in order to begin and end DTMF.</p> <p class="diff-block-target" style="margin: 10px 0 0 0"> <span class="diff-html-removed" id="removed-diff-13" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> </span> </p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>Write a <code style="font-family: monospace">dtmf_begin()</code> method for Steel Zebra.
<ul style="margin: 10px 0 0 0">
<li>The method should begin by creating a DTMF payload based on the specified digit.</li>
<li>The method should pass this payload to the RTP encoder, indicating that the marker bit should be set.</li>
<li>The method should schedule DTMF continuations to be sent at regular intervals based on the packetization of the outgoing stream.</li>
</ul> </li>
<li>Write a <code style="font-family: monospace">dtmf_end()</code> method for Steel Zebra.
<ul style="margin: 10px 0 0 0">
<li>The method should begin by canceling the DTMF continuations that have been sent.</li>
<li>The method should generate an RFC 4733 DTMF end payload.</li>
<li>The method should send send this payload to the RTP encoder three times.</li>
</ul> </li>
<li>Write a DTMF continuation scheduler callback function.
<ul style="margin: 10px 0 0 0; margin-top: 0">
<li>This method will take as its user data the DTMF digit being sent, and the timestamp of the DTMF begin.</li>
<li>The method will create an RFC 4733 payload with updated duration based on the timestamp.</li>
<li>The method will then pass this RFC 4733 payload to the RTP encoder to be transmitted.</li>
</ul> </li>
</ul> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p>
<table class="diff-macro diff-block-target" style="background-color: #f0f0f0;border: 1px solid #dddddd;margin: 10px 1px;padding: 0 2px 2px;width: 100%;; border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<thead>
<tr>
<th class="diff-macro-title" style="background-color: transparent; text-align: left; font-weight: normal;padding: 5px;"><span class="icon macro-placeholder-icon" style="background-color: ;line-height: 20px;"><img src="https://wiki.asterisk.org/wiki/s/en_GB/5639/a252d7f5e75d7a8bf7047b4b2c92f71a56a8f048.48/_/images/icons/macrobrowser/dropdown/warning.png" style="padding-right: 5px; vertical-align: text-bottom;" /> </span>Warning</th>
</tr>
</thead>
<tbody>
<tr>
<td class="diff-macro-body" style="background-color: #fff;border: 1px solid #dddddd;padding: 10px;; padding: 0px; border-collapse: collapse"> <p style="margin: 10px 0 0 0; margin-top: 0">Honestly, I'm not really sure what should be done here. Should <span class="diff-html-removed" id="removed-diff-14" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">we have a buffer for incoming audio from each SSRC? Should </span>this mostly be a statistical thing? Should we actually even expect to be communicating with an endpoint that sends us audio from multiple sources on a single stream?</p> </td>
</tr>
</tbody>
</table> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p>
<ul class="diff-block-context" style="margin: 10px 0 0 0">
<li>A transport plugin that will record the address of received RTP/RTCP.</li>
<li>A packet router that will be used to route the packets to the recorded address. </li>
</ul> <h2 id="RTPtasklist-Addsupportformultiplemediastreamsonasession" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 20px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0"> <span class="diff-html-changed" id="changed-diff-1" style="background-color: #d6f0ff;">Add support for multiple media streams on a session</span> </h2> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <h2 id="RTPtasklist-Addsupportformultiplemediastreamsonasession" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 20px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0"></h2> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">Most changes required for supporting multiple streams will happen at the RTP session level. For this task, the easiest way to implement this is to think of the common situation of having an audio and video stream on a session. Most of the work is going to be verifying that previous tasks provided for multiple streams correctly and that there are no bugs.<span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> </span><span class="diff-html-removed" id="removed-diff-16" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">However, there is also a task of updating the synchronizer on an RTP session to be able to synchronize multiple streams so that it when it queues frames onto the channel, the frames "line up" properly.</span> </p> <h4 id="RTPtasklist-Verifymultiplestreamcreationworksasexpected" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0">Verify multiple stream creation works as expected</h4> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <p class="diff-block-context" style="margin: 10px 0 0 0">When a single RTP stream is destroyed, it should be removed from the RTP session. Ensure that destroying the first of multiple RTP streams does not result in the session being destroyed. Verify that once all streams are destroyed that the session is destroyed.</p> <h4 id="RTPtasklist-AddsynchronizationofmultipleRTPstreams" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-removed" id="removed-diff-17" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">Add synchronization of multiple RTP streams</span> </h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0"> <span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">The synchronizer needs to be updated to be able to query multiple streams and queue multiple frames onto a channel. The synchronizer will be responsible for inspecting timestamps and queuing them as deemed appropriate. My RTP book has a chapter on receiver behavior that will explain what all is needed in order to properly synchronize media from multiple incoming streams. For the time being, I will just say "do what the book says". If this means completely changing how the synchronizer currently works, I apologize in advance.</span> </p> <h2 id="RTPtasklist-NativeRTPbridging" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 20px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0">Native RTP bridging</h2> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">Native RTP bridging refers to a shortcut where received RTP packets can have their payloads directly placed into a new RTP header and sent out without incurring the overhead of the Asterisk core. The way this currently works is that the native RTP bridge technology will set pointers on the RTP instances in the native bridge so that they can know if they should short-circuit. <span class="diff-html-removed" id="removed-diff-18" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">We want to take advantage of the buffering and packet reordering that we have built into Steel Zebra, so the process of receiving incoming RTP will not actually change until the synchronizer runs. The synchronizer will be altered so </span><span class="diff-html-added" id="added-diff-10" style="font-size: 100%; background-color: #ddfade;">Since we have such a well defined inflow and outflow of media, the best way to handle native bridging is to run all of the current steps for media inflow, except </span>that instead of queuing the <span class="diff-html-removed" id="removed-diff-19" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">created voice, video, or DTMF </span>frame onto the channel, <span class="diff-html-removed" id="removed-diff-20" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">it will </span><span class="diff-html-added" id="added-diff-11" style="font-size: 100%; background-color: #ddfade;">we </span>instead <span class="diff-html-removed" id="removed-diff-21" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">pass </span><span class="diff-html-added" id="added-diff-12" style="font-size: 100%; background-color: #ddfade;">write </span>the <span class="diff-html-removed" id="removed-diff-22" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">created </span>frame to the bridged RTP instance. <span class="diff-html-removed" id="removed-diff-23" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">The bridged RTP instance will treat the received frame </span><span class="diff-html-added" id="added-diff-13" style="font-size: 100%; background-color: #ddfade;">This way, the outflow works </span>exactly the same as <span class="diff-html-removed" id="removed-diff-24" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">it would </span>any other frame<span class="diff-html-removed" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;"> </span><span class="diff-html-removed" id="removed-diff-25" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">from Asterisk</span><span class="diff-html-added" id="added-diff-14" style="font-size: 100%; background-color: #ddfade;">, and the inflow is only slightly altered</span>.</p> <h4 id="RTPtasklist-AltertheRTPPacketHandler" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-changed" id="changed-diff-3" style="background-color: #d6f0ff;">Alter the </span> </h4> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <h4 id="RTPtasklist-AltertheRTPPacketHandler" class="diff-block-target diff-block-context" style="margin: 10px 0 0 0; font-size: 14px; line-height: 20px; margin: 20px 0 0 0"> <span class="diff-html-added" id="added-diff-15" style="font-size: 100%; background-color: #ddfade;">RTP Packet Handler</span> </h4> <p class="diff-block-target diff-block-context" style="margin: 10px 0 0 0">The <span class="diff-html-removed" id="removed-diff-27" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">synchronizer </span><span class="diff-html-added" id="added-diff-16" style="font-size: 100%; background-color: #ddfade;">RTP packet handler </span>currently <span class="diff-html-removed" id="removed-diff-28" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">pulls </span><span class="diff-html-added" id="added-diff-17" style="font-size: 100%; background-color: #ddfade;">creates </span>frames from <span class="diff-html-removed" id="removed-diff-29" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">individual streams' buffers </span><span class="diff-html-added" id="added-diff-18" style="font-size: 100%; background-color: #ddfade;">incoming decoded RTP packets </span>and queues them onto the channel. The <span class="diff-html-removed" id="removed-diff-30" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">synchronizer </span><span class="diff-html-added" id="added-diff-19" style="font-size: 100%; background-color: #ddfade;">RTP packet handler </span>needs to be altered so that when <span class="diff-html-removed" id="removed-diff-31" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">reading frames from a stream's buffer, if that stream </span><span class="diff-html-added" id="added-diff-20" style="font-size: 100%; background-color: #ddfade;">the stream </span>has a bridged RTP instance, the <span class="diff-html-removed" id="removed-diff-32" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">synchronizer </span><span class="diff-html-added" id="added-diff-21" style="font-size: 100%; background-color: #ddfade;">RTP packet handler </span>will write the frames to the bridged RTP instance instead of queuing the frames on the channel.</p> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> <p class="diff-block-target" style="margin: 10px 0 0 0">The majority of RTCP feedback defined in RFC 4585 is payload-specific and is mostly based around problems during video reception. Payload-specific feedback would have to be requested by a higher layer than the RTP stack since the RTP stack does not possess the capability to understand payload-specific information. <span class="diff-html-removed" id="removed-diff-33" style="font-size: 100%; background-color: #ffe7e7; text-decoration: line-through;">However, there is also transport layer feedback defined in the RFC, which is used to indicate that an RTP packet with a specific sequence number was not received. This kind of feedback could maybe be initiated by the bufferer if it notices a missing RTP sequence number while reordering packets</span><span class="diff-html-added" id="added-diff-22" style="font-size: 100%; background-color: #ddfade;">Even transport-specific RTP feedback would need to be initiated by a higher-level construct in Asterisk since it would be responsible for knowing if any packets were missed</span>.</p> <h1 id="RTPtasklist-Trickle-ICE(?)" class="diff-block-context" style="margin: 10px 0 0 0; font-size: 24px; font-weight: normal; line-height: 30px; margin: 40px 0 0 0">Trickle-ICE (?)</h1> <p class="diff-block-context" style="margin: 10px 0 0 0">Trickle-ICE is a draft that modifies ICE to be able to initiate connectivity checks before all local ICE candidates have been gathered. Since it is a draft, it is unlikely that we would aim to implement this in the new RTP stack until the draft is finalized into an RFC. Using the current draft as a guide, we can get a good idea of the tasks involved in adding support for Trickle ICE</p> <p class="diff-context-placeholder" style="margin: 10px 0 0 0">...</p> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td class="email-content-main mobile-expand action-padding last-row-padding" style="padding: 0px; border-collapse: collapse; border-left: 1px solid #ccc; border-right: 1px solid #ccc; border-top: 0; border-bottom: 0; padding: 0 15px 15px 16px; background-color: #fff; padding-bottom: 10px; padding-bottom: 10px">
<table id="actions-pattern" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 1px">
<tbody>
<tr>
<td id="actions-pattern-container" valign="middle" style="padding: 0px; border-collapse: collapse; padding: 15px 0 0 24px; vertical-align: middle">
<table align="left" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td class="actions-pattern-action-icon-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 0px; vertical-align: middle"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?src=email" title="View page Icon" style="color: #3b73af; text-decoration: none"><img class="actions-pattern-action-icon-image" height="16" width="16" border="0" title="View page Icon" src="cid:com.atlassian.confluence.plugins.confluence-email-resources%3Aview-page-email-adg-footer-item%3Aicon" alt="View page Icon" style="vertical-align: middle" /></a></td>
<td class="actions-pattern-action-text-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; padding-left: 5px; white-space: nowrap"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?src=email" title="View page" style="color: #3b73af; text-decoration: none">View page</a></td>
<td class="actions-pattern-action-bull" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; color: #999; padding: 0 5px">•</td>
</tr>
</tbody>
</table>
<table align="left" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td class="actions-pattern-action-icon-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 0px; vertical-align: middle"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?showComments=true&showCommentArea=true&src=email#addcomment" title="Add comment Icon" style="color: #3b73af; text-decoration: none"><img class="actions-pattern-action-icon-image" height="16" width="16" border="0" title="Add comment Icon" src="cid:com.atlassian.confluence.plugins.confluence-email-resources%3Aadd-comment-to-content-email-adg-footer-item%3Aicon" alt="Add comment Icon" style="vertical-align: middle" /></a></td>
<td class="actions-pattern-action-text-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; padding-left: 5px; white-space: nowrap"><a href="https://wiki.asterisk.org/wiki/display/AST/RTP+task+list?showComments=true&showCommentArea=true&src=email#addcomment" title="Add comment" style="color: #3b73af; text-decoration: none">Add comment</a></td>
<td class="actions-pattern-action-bull" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; color: #999; padding: 0 5px">•</td>
</tr>
</tbody>
</table>
<table style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td class="actions-pattern-action-icon-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 0px; vertical-align: middle"><a href="https://wiki.asterisk.org/wiki/plugins/likes/like.action?contentId=31752472&src=email" title="Like Icon" style="color: #3b73af; text-decoration: none"><img class="actions-pattern-action-icon-image" height="16" width="16" border="0" title="Like Icon" src="cid:com.atlassian.confluence.plugins.confluence-like%3Aview-email-adg-content-item%3Aicon" alt="Like Icon" style="vertical-align: middle" /></a></td>
<td class="actions-pattern-action-text-container" style="padding: 0px; border-collapse: collapse; font-family: Arial, sans-serif; font-size: 14px; line-height: 20px; mso-line-height-rule: exactly; mso-text-raise: 4px; padding-left: 5px; white-space: nowrap"><a href="https://wiki.asterisk.org/wiki/plugins/likes/like.action?contentId=31752472&src=email" title="Like" style="color: #3b73af; text-decoration: none">Like</a></td>
</tr>
</tbody>
</table> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td class="email-content-rounded-bottom mobile-expand" style="padding: 0px; border-collapse: collapse; color: #fff; height: 5px; line-height: 5px; padding: 0 15px 0 16px; background-color: #fff; border-bottom-right-radius: 5px; border-bottom-left-radius: 5px; border-top: 0; border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; border-right: 1px solid #ccc; mso-line-height-rule: exactly"> </td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td id="footer-pattern" style="padding: 0px; border-collapse: collapse; padding: 12px 20px">
<table id="footer-pattern-container" cellspacing="0" cellpadding="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333">
<tbody>
<tr>
<td id="footer-pattern-links-container" width="100%" style="padding: 0px; border-collapse: collapse; color: #999; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px">
<table align="left" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px">
<tbody>
<tr>
<td class="footer-pattern-links mobile-resize-text" style="padding: 0px; border-collapse: collapse"><a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=AST&src=email" title="" style="color: #3b73af; text-decoration: none">Stop watching space</a></td>
<td class="footer-pattern-links-bull" style="padding: 0px; border-collapse: collapse; padding: 0 5px; color: #999">•</td>
</tr>
</tbody>
</table>
<table style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px">
<tbody>
<tr>
<td class="footer-pattern-links mobile-resize-text" style="padding: 0px; border-collapse: collapse"><a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action?src=email" title="" style="color: #3b73af; text-decoration: none">Manage notifications</a></td>
</tr>
</tbody>
</table> </td>
</tr>
<tr>
<td id="footer-pattern-text" class="mobile-resize-text" width="100%" style="padding: 0px; border-collapse: collapse; color: #999; font-size: 12px; line-height: 18px; font-family: Arial, sans-serif; mso-line-height-rule: exactly; mso-text-raise: 2px; display: none">This message was sent by Atlassian Confluence 5.6.6</td>
</tr>
</tbody>
</table> </td>
</tr>
</tbody>
</table>
<table id="sealed-section" border="0" cellpadding="0" cellspacing="0" width="0" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt; color: #333; display: none">
<tbody>
<tr>
<td style="padding: 0px; border-collapse: collapse; border: 0; font-size: 0px; line-height: 0; mso-line-height-rule: exactly"></td>
</tr>
</tbody>
</table>
</body>
</html>