RFC 5219
A More Loss-Tolerant RTP Payload Format for MP3 Audio
RFC 5219: A More Loss-Tolerant RTP Payload Format for MP3 Audio π
Okay bestie, let's start with the header stuff π This one is RFC 5219, written by R. Finlayson from Live Networks, Inc. It was published in February 2008 as a Standard Track doc, and it obsoletes RFC 3119 β¨ It doesn't list any updates.
Status of This Memo
This is an Internet standards track protocol for the Internet community, and it's asking everyone for discussion and suggestions for improvements π¦ For the current standardization state and status, check the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited πΈ
Abstract
So this doc describes an RTP (Real-Time Protocol) payload format for carrying MPEG (Moving Picture Experts Group) 1 or 2, layer III audio, which everyone knows as "MP3" π§ It's an alternative to the one in RFC 2250, and it performs better when packets get lost π It also obsoletes RFC 3119, fixing typos in the "SDP usage" section and the pseudo-code appendices π
Table of Contents
1. Introduction ....................................................2
2. Terminology .....................................................3
3. The Structure of MP3 Frames .....................................3
4. A New Payload Format ............................................4
4.1. ADU Frames .................................................4
4.2. ADU Descriptors ............................................4
4.3. Packing Rules ..............................................5
4.4. RTP Header Fields ..........................................6
4.5. Handling Received Data .....................................6
5. Handling Multiple MPEG Audio Layers .............................6
6. Frame Packetizing and Depacketizing .............................7
7. ADU Frame Interleaving ..........................................8
8. IANA Considerations ............................................10
9. SDP Usage ......................................................11
10. Security Considerations .......................................11
11. Acknowledgements ..............................................11
12. Normative References ..........................................12
Appendix A. Translating between "MP3 Frames" and "ADU Frames" .....13
A.1. Converting a Sequence of "MP3 Frames"
to a Sequence of "ADU Frames" .............................14
A.2. Converting a Sequence of "ADU Frames"
to a Sequence of "MP3 Frames" .............................15
Appendix B. Interleaving and Deinterleaving .......................18
B.1. Interleaving a Sequence of "ADU Frames" ...................18
B.2. Deinterleaving a Sequence of (Interleaved) "ADU Frames" ...19
Appendix C. Changes from RFC 3119 .................................20
1. Introduction β¨
Okay so RFC 2250 [1] is generally fine for all forms of MPEG audio or video, BUT it's kinda sub-optimal for MPEG-1 or 2, layer III audio (aka MP3) π The reason is that an MP3 frame isn't a true "Application Data Unit" -- it has a back-pointer to data in earlier frames, so it can't be decoded on its own without those earlier frames. RFC 2250 says packet boundaries line up with frame boundaries, so when it carries MP3 data it handles loss badly. Losing one MP3 frame makes some data in previous (or future) frames useless, even if those frames arrived totally fine. Rude π
So in this doc, we define an alternative RTP payload format for MP3 audio π¦ It does a data-preserving rearrangement of the original MPEG frames, so packet boundaries now match up with true MP3 "Application Data Units." These can also (optionally) get rearranged in an interleaving pattern. This new format is way more data efficient than RFC 2250 when packets go missing, which is so iconic π
2. Terminology π
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 [2] π
3. The Structure of MP3 Frames π§
In this section we give a quick overview of what an MP3 frame looks like. (For the full details, see the MPEG-1 audio [3] and MPEG-2 audio [4] specs.)
Each MPEG audio frame starts with a 4-byte header π That header tells you stuff like:
- Whether the audio is MPEG-1 or MPEG-2.
- Whether the audio is layer I, II, or III. (The rest of this doc assumes layer III, i.e., "MP3" frames.)
- Whether the audio is mono or stereo.
- Whether or not there's a 2-byte CRC field after the header.
- (indirectly) The size of the frame.
After the header, you'll find these structures:
- (optionally) A 2-byte Cyclic Redundancy Check (CRC) field
- A "side info" structure. Its length is:
- 32 bytes for MPEG-1 stereo
- 17 bytes for MPEG-1 mono, or for MPEG-2 stereo
- 9 bytes for MPEG-2 mono
- Encoded audio data, plus optional ancillary data (filling out the rest of the frame)
For this doc, the "side info" structure is the most important one, because it defines the location and size of the "Application Data Unit" (ADU) that an MP3 decoder processes π₯ Specifically, "side info" defines:
main_data_begin: This is a back-pointer (in bytes) to the start of the ADU. It's counted from the beginning of the frame, and it only counts encoded audio data and any ancillary data (so it ignores the header, CRC, and "side info" fields).
An MP3 decoder processes each ADU independently π ADUs will generally vary in length, but their average length will be the length of the MP3 frames (minus the header, CRC, and "side info" fields). (In MPEG literature this ADU is sometimes called a "bit reservoir." Iykyk π)
4. A New Payload Format π
As noted in [5], a payload format should be designed so that packet boundaries line up with "codec frame boundaries" -- so, with ADUs. In the RFC 2250 payload format for MPEG audio [1], each RTP packet payload carries MP3 frames. In this new payload format for MP3 audio, though, each RTP packet payload carries "ADU frames," and each one is preceded by an "ADU descriptor" β¨
4.1. ADU Frames πΈ
An "ADU frame" is made up of:
- The 4-byte MPEG header (same as the original MP3 frame, except that the first 11 bits are (optionally) replaced by an "Interleaving Sequence Number," as described in Section 7 below)
- The optional 2-byte CRC field (same as the original MP3 frame)
- The "side info" structure (same as the original MP3 frame)
- The complete sequence of encoded audio data (and any ancillary data) for the ADU (i.e., running from the start of this MP3 frame's
main_data_beginback-pointer, up to the start of the next MP3 frame's back-pointer)
Note that there's a one-to-one mapping between MP3 frames and ADU frames π¦ Because MP3 frames are self-describing, with the bitrate (and sampling frequency) encoded in the 4-byte MPEG header, the same is true for ADU frames. So, just like with MP3 streams, the bitrate can change within a stream and may be used for congestion control. Slay π
4.2. ADU Descriptors π
Inside each RTP packet payload, each "ADU frame" is preceded by a 1- or 2-byte "ADU descriptor." It tells you the size of the ADU and whether this packet's data continues the previous packet's data. (That only happens when a single "ADU descriptor" + "ADU frame" is too big to fit in one RTP packet.) An ADU descriptor has these fields:
C: Continuation flag (1 bit): 1, if the data after the ADU descriptor is a continuation of an ADU frame that was too large to fit within a single RTP packet; 0 otherwise.T: Descriptor Type flag (1 bit): 0 if this is a 1-byte ADU descriptor; 1 if this is a 2-byte ADU descriptor.ADU size(6 or 14 bits): The size (in bytes) of the ADU frame that follows this ADU descriptor (i.e., not including the size of the descriptor itself). A 2-byte ADU descriptor (with a 14-bitADU sizefield) is used for ADU frame sizes of 64 bytes or more. For smaller ADU frame sizes, senders MAY alternatively use a 1-byte ADU descriptor (with a 6-bitADU sizefield). Receivers MUST be able to accept an ADU descriptor of either size.
So a 1-byte ADU descriptor looks like this:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|C|0| ADU size |
+-+-+-+-+-+-+-+-+
and a 2-byte ADU descriptor looks like this:
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|C|1| ADU size (14 bits) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4.3. Packing Rules π§
Each RTP packet payload starts with an "ADU descriptor," followed by "ADU frame" data. Normally this "ADU descriptor" + "ADU frame" fits completely inside the RTP packet. In that case, more than one successive "ADU descriptor" + "ADU frame" MAY be packed into a single RTP packet, as long as they all fit completely π
But if a single "ADU descriptor" + "ADU frame" is too big for one RTP packet, the "ADU frame" gets split across two or more successive RTP packets. Each of those packets starts with an ADU descriptor. The first packet's descriptor has a "C" (continuation) flag of 0, and the following packets' descriptors each have a "C" flag of 1. In this case, every descriptor has the same "ADU size" value: the size of the entire "ADU frame" (not just the part that fits in one RTP packet). Each such packet (even the last one) contains only one "ADU descriptor" π
4.4. RTP Header Fields π
- Payload Type: The (static) payload type 14 that was defined for MPEG audio [6] MUST NOT be used. Instead, a different, dynamic payload type MUST be used -- i.e., one within the range
[96..127]. β οΈ - M bit: This payload format doesn't define any use for this bit. Senders SHOULD set this bit to zero in each outgoing packet.
- Timestamp: This is a 32-bit, 90 kHz timestamp, representing the presentation time of the first ADU packed within the packet. β°
4.5. Handling Received Data π«Ά
No information is lost when you convert a sequence of MP3 frames into a corresponding sequence of "ADU frames," so a receiving RTP implementation can either feed the ADU frames straight into an appropriately modified MP3 decoder, or convert them back into a sequence of MP3 frames, as described in Appendix A.2 below π
Part 1 is done, and there's a lot more coming π§β¨
5. Handling Multiple MPEG Audio Layers β¨
Okay bestie, this payload format is only meant for MPEG-1 or 2, layer III audio ("MP3") π§. Layer I and layer II frames are self-contained, with no back-pointer to earlier frames πΈ. Mixing is possible (although unusual π), so a sequence can contain layer III frames plus layer I or II frames. Only the layer III frames get converted to ADUs. Layer I and II frames go out 'as is', except for the prepended "ADU descriptor" π .
The receiver leaves layer I and II frames alone (after stripping the ADU descriptor) and converts the layer III ADU frames back into regular MP3 frames. Reminder that each frame's layer is identified from its 4-byte MPEG header π₯.
If you're sending a stream made only of layer I or layer II frames (no MP3 data at all), this format gives you no benefit, unless you're using the interleaving mechanism from Section 7 below π¦.
6. Frame Packetizing and Depacketizing π
Here's how a sequence of MP3 frames gets sent, step by step:
MP3 frames
-1-> ADU frames
-2-> interleaved ADU frames
-3-> RTP packets
Step 1 is turning the MP3 frames into a matching sequence of ADU frames. It happens the way Sections 3 and 4.1 above describe π. (Also peek at the pseudo-code in Appendix A.1 π§.)
Step 2 is the optional part where the ADU frames get shuffled into an interleaving pattern before packetization, as explained in Section 7 below π¦. Interleaving helps soften the blow of packet loss by spreading consecutive ADU frames across non-consecutive packets. (Heads up: because of the back-pointer in MP3 frames, interleaving generally only works on ADU frames. That's why interleaving wasn't possible for RFC 2250 π. Also see the pseudo-code in Appendix B.1.)
Step 3 is packing the (interleaved) ADU frames into RTP packets, as described in Section 4.3 above π¦. Each packet's RTP timestamp is the presentation time of the first ADU packed inside it β°. If you interleaved in Step 2, the RTP timestamps on outgoing packets won't necessarily be monotonically nondecreasing, so don't panic if they look a little jumpy π.
Receiving works the other way around, like a reverse glow-up:
RTP packets
-4-> RTP packets ordered by RTP sequence number
-5-> interleaved ADU frames
-6-> ADU frames
-7-> MP3 frames
Step 4 is the usual sorting of incoming RTP packets by RTP sequence number ποΈ.
Step 5 is pulling ADU frames back out of the RTP packets, which is the reverse of Step 3 β¨. The receiver uses the "C" (continuation) flag in the ADU descriptor to notice when an ADU frame has been split across more than one packet. If one of those packets gets lost, the receiver throws out the whole ADU frame π.
Step 6 is putting the ADU frames back in their original order (ADU frames lost to packet loss are the only exception) π§©. This is covered in Section 7 below. Also check the pseudo-code in Appendix B.2 π.
Step 7 is turning the sequence of ADU frames back into MP3 frames, the reverse of Step 1 π. Check the pseudo-code in Appendix A.2 too. If your MP3 decoder has been appropriately modified, you can skip this step and feed the ADU frames straight into that decoder instead π₯.
7. ADU Frame Interleaving π¦
In MPEG audio frames (MPEG-1 or 2, all layers), the high-order 11 bits of the 4-byte MPEG header (the syncword) are always all-ones, which is 0xFFE π. When you reorder ADU frames for transmission, we reuse those 11 bits as an "Interleaving Sequence Number" (ISN) π. (When they arrive, they get set back to 0xFFE, so nothing's lost in translation π«Ά.)
The ISN is structured as (a,b), where:
a == bits 0-7:8-bit Interleave Index (within Cycle)b == bits 8-10:3-bit Interleave Cycle Count
So the 4-byte MPEG header gets reused like this:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Interleave Idx |CycCt| The rest of the original MPEG header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Let's do an example π. Picture this interleave cycle (of size 8):
1,3,5,7,0,2,4,6
This particular pattern is so good π€. Any loss of up to four consecutive ADUs in the interleaved stream still leaves a deinterleaved stream with no gaps greater than one. It produces this sequence of ISNs:
(1,0) (3,0) (5,0) (7,0) (0,0) (2,0) (4,0) (6,0) (1,1) (3,1) (5,1)
etc.
So in this example, a sequence of ADU frames like this:
f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 (etc.)
would get reordered in Step 2 into:
(1,0)f1 (3,0)f3 (5,0)f5 (7,0)f7 (0,0)f0 (2,0)f2 (4,0)f4 (6,0)f6
(1,1)f9 (3,1)f11 (5,1)f13 (etc.)
The reverse reordering (plus putting the 0xFFE back) happens on reception π.
Why split the ISN into an "Interleave Cycle Count" and an "Interleave Index", instead of just treating it as one 11-bit counter? Because it gives receivers a way to know when an ADU frame is ready to be released to the ADU-to-MP3 conversion (Step 7 above) without waiting around for more interleaved ADU frames π°οΈ. In the example above, when the receiver sees a frame with ISN (<something>,1), it knows it can release all the previously seen frames with ISN (<something>,0), even if some other (<something>,0) frames are still missing because of packet loss π. An 8-bit Interleave Index lets interleave cycles go up to 256 in size π.
You can pick your interleaving order completely separately from RTP packetization π§ . A simple implementation could choose an interleaving order first, reorder the ADU frames to match (Step 2), and then just pack them into RTP packets in order (Step 3). The catch is that ADU frames vary in size, so the number that fit into each RTP packet also varies. A more optimal implementation would combine Steps 2 and 3, choosing an interleaving order that better reflects how many ADU frames end up in each packet π .
Each receiving implementation of this payload format MUST recognize the ISN and be able to deinterleave incoming ADU frames (Step 6) π. However, a sending implementation of this payload format MAY choose not to interleave at all, which means skipping Step 2 π¦. In that case, the high-order 11 bits of each 4-byte MPEG header stay at 0xFFE. Receiving implementations would then see a run of identical ISNs (all 0xFFE). They'd handle that the same way they'd handle an Interleave Cycle Count that changed with every ADU frame: they'd just release the incoming ADU frames one by one to the ADU-to-MP3 conversion (Step 7), with no reordering needed π§. (Also see the pseudo-code in Appendix B.2.)
8. IANA Considerations π
Here's the registration template for the media type, copied exactly as it's written so nothing gets lost π:
Media type name: audio
Media subtype: mpa-robust
Required parameters: none
Optional parameters: none
Encoding considerations:
This type is defined only for transfer via RTP, as specified in
RFC 5219.
Security considerations:
See the "Security Considerations" section of RFC 5219.
Interoperability considerations:
This encoding is incompatible with both the "audio/mpa" and
"audio/mpeg" media types.
Published specification:
The ISO/IEC MPEG-1 [3] and MPEG-2 [4] audio specifications, and
RFC 5219.
Applications that use this media type:
Audio streaming tools (transmitting and receiving)
Additional information: none
Person & email address to contact for further information:
Ross Finlayson
finlayson@live555.com
Intended usage: COMMON
Author/Change controller:
Author: Ross Finlayson
Change controller: IETF AVT Working Group
9. SDP Usage π‘
When you're conveying info by SDP [7], the encoding name SHALL be mpa-robust (same as the media subtype) π. Here's an example of the media representation in SDP:
m=audio 49000 RTP/AVP 121
a=rtpmap:121 mpa-robust/90000
Note that the RTP timestamp frequency MUST be 90000 β±οΈ. No exceptions, bestie π .
10. Security Considerations π
Okay, this part is serious π. If a session using this payload format is encrypted AND interleaving is in use, then the sender SHOULD ensure that any change of encryption key lines up with the start of a new interleave cycle π. Apart from that, the security considerations for this payload format are identical to the ones noted for RFC 2250 [1] π‘οΈ.
11. Acknowledgements π«Ά
Huge shoutout to the people who made this happen πΈ. The idea of adding an interleaving option (using the first bits of the MPEG syncword, which would otherwise be all-ones, as an interleaving index) came from Dave Singer and Stefan Gewinner π. Dave Singer also gave valuable feedback that helped clarify and improve how this payload format is described π. And Chris Sloan's feedback led to the "ADU descriptor" that goes in front of each ADU frame in the RTP packet π¦.
12. Normative References π
- [1] Hoffman, D., Fernando, G., Goyal, V., and M. Civanlar, "RTP Payload Format for MPEG1/MPEG2 Video", RFC 2250, January 1998.
- [2] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
- [3] ISO/IEC International Standard 11172-3; "Coding of moving pictures and associated audio for digital storage media up to about 1,5 Mbits/s -- Part 3: Audio", 1993.
- [4] ISO/IEC International Standard 13818-3; "Generic coding of moving pictures and associated audio information -- Part 3: Audio", 1998.
- [5] Handley, M. and C. Perkins, "Guidelines for Writers of RTP Payload Format Specifications", BCP 36, RFC 2736, December 1999.
- [6] Schulzrinne, H. and S. Casner, "RTP Profile for Audio and Video Conferences with Minimal Control", STD 65, RFC 3551, July 2003.
- [7] Handley, M., Jacobson, V., and C. Perkins, "SDP: Session Description Protocol", RFC 4566, July 2006.
Appendix A. Translating between "MP3 Frames" and "ADU Frames" β¨
okay bestie, this appendix is basically a recipe π It's written as pseudo code, and it shows how a sender using this payload format can turn a line of regular "MP3 Frames" into "ADU Frames", and how a receiver can go the other way, from "ADU Frames" back to "MP3 Frames" π¦
First, we set up some abstract data structures π
- "Segment": a record that stands for either an "MP3 Frame" or an "ADU Frame". It has these fields:
header: the 4-byte MPEG headerheaderSize: a constant (== 4)sideInfo: the 'side info' structure, including the optional 2-byte CRC field, if presentsideInfoSize: the size (in bytes) of the structure aboveframeData: the remaining data in this frameframeDataSize: the size (in bytes) of the data abovebackpointer: the value (in bytes) of the backpointer for this frameaduDataSize: the size (in bytes) of the ADU associated with this frame. (If the frame is already an "ADU Frame", thenaduDataSize == frameDataSize)mp3FrameSize: the total size (in bytes) this frame would be if it were a regular "MP3 Frame". (If it already is an "MP3 Frame", thenmp3FrameSize == headerSize + sideInfoSize + frameDataSize.) Note that this size can be worked out entirely fromheaderπ§
- "SegmentQueue": a FIFO queue of "Segments", with these operations:
void enqueue(Segment)Segment dequeue()Boolean isEmpty()Segment head()Segment tail()Segment previous(Segment): returns the segment prior to a given oneSegment next(Segment): returns the segment after a given oneunsigned totalDataSize(): returns the sum of theframeDataSizefields of each entry in the queue
A.1. Converting a Sequence of "MP3 Frames" to a Sequence of "ADU Frames" π
This is the sender's side, and it's giving very organised vibes π
SegmentQueue pendingMP3Frames; // initially empty
while (1) {
// Enqueue new MP3 Frames, until we have enough data to
// generate the ADU for a frame:
do {
int totalDataSizeBefore
= pendingMP3Frames.totalDataSize();
Segment newFrame = 'the next MP3 Frame';
pendingMP3Frames.enqueue(newFrame);
int totalDataSizeAfter
= pendingMP3Frames.totalDataSize();
} while (totalDataSizeBefore < newFrame.backpointer ||
totalDataSizeAfter < newFrame.aduDataSize);
// We now have enough data to generate the ADU for the most
// recently enqueued frame (i.e., the tail of the queue).
// (The earlier frames in the queue -- if any -- must be
// discarded, as we don't have enough data to generate
// their ADUs.)
Segment tailFrame = pendingMP3Frames.tail();
// Output the header and side info:
output(tailFrame.header);
output(tailFrame.sideInfo);
// Go back to the frame that contains the start of our
// ADU data:
int offset = 0;
Segment curFrame = tailFrame;
int prevBytes = tailFrame.backpointer;
while (prevBytes > 0) {
curFrame = pendingMP3Frames.previous(curFrame);
int dataHere = curFrame.frameDataSize;
if (dataHere < prevBytes) {
prevBytes -= dataHere;
} else {
offset = dataHere - prevBytes;
break;
}
}
// Dequeue any frames that we no longer need:
while (pendingMP3Frames.head() != curFrame) {
pendingMP3Frames.dequeue();
}
// Output, from the remaining frames, the ADU data that
// we want:
int bytesToUse = tailFrame.aduDataSize;
while (bytesToUse > 0) {
int dataHere = curFrame.frameDataSize - offset;
int bytesUsedHere
= dataHere < bytesToUse ? dataHere :
bytesToUse;
output("bytesUsedHere" bytes from
curFrame.frameData, starting from "offset");
bytesToUse -= bytesUsedHere;
offset = 0;
curFrame = pendingMP3Frames.next(curFrame);
}
}
A.2. Converting a Sequence of "ADU Frames" to a Sequence of "MP3 Frames" πΈ
Now the receiver's side, where we rebuild the "MP3 Frames" from the "ADU Frames" π§ββοΈ
SegmentQueue pendingADUFrames; // initially empty
while (1) {
while (needToGetAnADU()) {
Segment newADU = 'the next ADU Frame';
pendingADUFrames.enqueue(newADU);
insertDummyADUsIfNecessary();
}
generateFrameFromHeadADU();
}
Boolean needToGetAnADU() {
// Checks whether we need to enqueue one or more new ADUs
// before we have enough data to generate a frame for the
// head ADU.
Boolean needToEnqueue = True;
if (!pendingADUFrames.isEmpty()) {
Segment curADU = pendingADUFrames.head();
int endOfHeadFrame = curADU.mp3FrameSize
- curADU.headerSize - curADU.sideInfoSize;
int frameOffset = 0;
while (1) {
int endOfData = frameOffset
- curADU.backpointer +
curADU.aduDataSize;
if (endOfData >= endOfHeadFrame) {
// We have enough data to generate a
// frame.
needToEnqueue = False;
break;
}
frameOffset += curADU.mp3FrameSize
- curADU.headerSize
- curADU.sideInfoSize;
if (curADU == pendingADUFrames.tail()) break;
curADU = pendingADUFrames.next(curADU);
}
}
return needToEnqueue;
}
void generateFrameFromHeadADU() {
Segment curADU = pendingADUFrames.head();
// Output the header and side info:
output(curADU.header);
output(curADU.sideInfo);
// Begin by zeroing out the rest of the frame, in case the
// ADU data doesn't fill it in completely:
int endOfHeadFrame = curADU.mp3FrameSize
- curADU.headerSize - curADU.sideInfoSize;
output("endOfHeadFrame" zero bytes);
// Fill in the frame with appropriate ADU data from this and
// subsequent ADUs:
int frameOffset = 0;
int toOffset = 0;
while (toOffset < endOfHeadFrame) {
int startOfData = frameOffset - curADU.backpointer;
if (startOfData > endOfHeadFrame) {
break; // no more ADUs are needed
}
int endOfData = startOfData + curADU.aduDataSize;
if (endOfData > endOfHeadFrame) {
endOfData = endOfHeadFrame;
}
int fromOffset;
if (startOfData <= toOffset) {
fromOffset = toOffset - startOfData;
startOfData = toOffset;
if (endOfData < startOfData) {
endOfData = startOfData;
}
} else {
fromOffset = 0;
// leave some zero bytes beforehand:
toOffset = startOfData;
}
int bytesUsedHere = endOfData - startOfData;
output(starting at offset "toOffset", "bytesUsedHere"
bytes from "&curADU.frameData[fromOffset]");
toOffset += bytesUsedHere;
frameOffset += curADU.mp3FrameSize
- curADU.headerSize - curADU.sideInfoSize;
curADU = pendingADUFrames.next(curADU);
}
pendingADUFrames.dequeue();
}
void insertDummyADUsIfNecessary() {
// The tail segment (ADU) is assumed to have been recently
// enqueued. If its backpointer would overlap the data
// of the previous ADU, then we need to insert one or more
// empty, 'dummy' ADUs ahead of it. (This situation
// should occur only if an intermediate ADU was missing
// -- e.g., due to packet loss.)
while (1) {
Segment tailADU = pendingADUFrames.tail();
int prevADUend; // relative to the start
of the tail ADU
if (pendingADUFrames.head() != tailADU) {
// there is a previous ADU
Segment prevADU
= pendingADUFrames.previous(tailADU);
prevADUend
= prevADU.mp3FrameSize +
prevADU.backpointer
- prevADU.headerSize
- prevADU.sideInfoSize;
if (prevADU.aduDataSize > prevADUend) {
// this shouldn't happen if the
// previous ADU was well-formed
prevADUend = 0;
} else {
prevADUend -= prevADU.aduDataSize;
}
} else {
prevADUend = 0;
}
if (tailADU.backpointer > prevADUend) {
// Insert a 'dummy' ADU in front of the tail.
// This ADU can have the same "header" (and thus,
// "mp3FrameSize") as the tail ADU, but should
// have a "backpointer" of "prevADUend", and
// an "aduDataSize" of zero. The simplest
// way to do this is to copy the "sideInfo" from
// the tail ADU, replace the value of
// "main_data_begin" with "prevADUend", and set
// all of the "part2_3_length" fields to zero.
} else {
break; // no more dummy ADUs need to be
// inserted
}
}
}
Appendix B. Interleaving and Deinterleaving π¦
Here's the setup for the next bit π The following 'pseudo code' describes how a sender can reorder a sequence of "ADU Frames" according to an interleaving pattern (step 2), and how a receiver can perform the reverse reordering (step 6).
B.1. Interleaving a Sequence of "ADU Frames" π
okay so first we set up our little toolkit of data structures. these are the things the interleaver keeps track of while it's working π
interleaveCycleSize: an integer in the range [1..256]interleaveCycle: an array, of sizeinterleaveCycleSize, containing some permutation of the integers from the set [0 .. interleaveCycleSize-1]. e.g., ifinterleaveCycleSize== 8,interleaveCyclemight contain:1,3,5,7,0,2,4,6π¦inverseInterleaveCycle: an array containing the inverse of the permutation ininterleaveCycle. it's the reverse mapping, so thatinterleaveCycle[inverseInterleaveCycle[i]] == iβ¨ii: the current Interleave Index (initially 0)icc: the current Interleave Cycle Count (initially 0)aduFrameBuffer: an array, of sizeinterleaveCycleSize, of ADU Frames that are awaiting packetization π§
here's the loop itself. it's giving very "shuffle the deck, then deal it out" π
while (1) {
int positionOfNextFrame = inverseInterleaveCycle[ii];
aduFrameBuffer[positionOfNextFrame] = the next ADU frame;
replace the high-order 11 bits of this frame's MPEG header
with (ii,icc);
// Note: Be sure to leave the remaining 21 bits
// as is
if (++ii == interleaveCycleSize) {
// We've finished this cycle, so pass all
// pending frames to the packetizing step
for (int i = 0; i < interleaveCycleSize; ++i) {
pass aduFrameBuffer[i] to the packetizing step;
}
ii = 0;
icc = (icc+1)%8;
}
}
so every time a cycle wraps up, the frames get passed along in order, icc ticks forward, and it all starts again. the high-order 11 bits get stamped with (ii,icc) and the other 21 bits stay exactly as they were πΈ
B.2. Deinterleaving a Sequence of (Interleaved) "ADU Frames" π«Ά
now the reverse, the deinterleaver's side of the story π here are its data structures:
ii: the Interleave Index from the current incoming ADU frameicc: the Interleave Cycle Count from the current incoming ADU frameiiLastSeen: the most recently seen Interleave Index (initially, some integer not in the range [0..255])iccLastSeen: the most recently seen Interleave Cycle Count (initially, some integer not in the range [0..7])aduFrameBuffer: an array, of size 256, of (pointers to) ADU Frames that have just been depacketized (initially, all entries are NULL) π
and the loop, which is lowkey the cleverest bit of the whole thing π
while (1) {
aduFrame = the next ADU frame from the depacketizing step;
(ii,icc) = "the high-order 11 bits of aduFrame's MPEG
header"; "the high-order 11 bits of aduFrame's MPEG
header" = 0xFFE;
// Note: Be sure to leave the remaining 21 bits
// as is
if (icc != iccLastSeen || ii == iiLastSeen) {
// We've started a new interleave cycle
// (or interleaving was not used). Release all
// pending ADU frames to the ADU->MP3 conversion
// step:
for (int i = 0; i < 256; ++i) {
if (aduFrameBuffer[i] != NULL) {
release aduFrameBuffer[i];
aduFrameBuffer[i] = NULL;
}
}
}
iiLastSeen = ii;
iccLastSeen = icc;
aduFrameBuffer[ii] = aduFrame;
}
so when a new cycle starts (or interleaving wasn't used at all), everything still pending gets released to the ADU->MP3 conversion step. otherwise each frame just parks itself in the slot its Interleave Index points to π
Appendix C. Changes from RFC 3119 β¨
the big change from RFC 3119 is fixing the encoding name in the "SDP usage" section. the correct one is "mpa-robust" π also, "media type" takes over from "mime type", and a few minor bug fixes and clarifications got sprinkled into the (non-normative) pseudo code in Appendix A and Appendix B. π
Author's Address ποΈ
Ross Finlayson, Live Networks, Inc. 650 Castro St., suite 120-196 Mountain View, CA 94041 USA
EMail: finlayson@live555.com URI: http://www.live555.com/
Full Copyright Statement π
Copyright (C) The IETF Trust (2008).
this doc is subject to the rights, licenses and restrictions in BCP 78, and except as set forth there, the authors keep all their rights π
This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY, THE IETF TRUST AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. π
Intellectual Property π₯
the IETF doesn't take a position on whether any Intellectual Property Rights (or other rights) that might be claimed for the tech in this doc are valid or in scope, or whether any license under those rights might be available. it also doesn't claim to have independently tried to find any such rights. info about how rights work for RFC docs is in BCP 78 and BCP 79 π
copies of IPR disclosures sent to the IETF Secretariat, any assurances of licenses that will be made available, and the results of any attempt to get a general license or permission for implementers or users to use those proprietary rights can be found in the IETF on-line IPR repository at http://www.ietf.org/ipr π
and the IETF invites anyone to bring to its attention any copyrights, patents or patent applications, or other proprietary rights that might cover technology needed to implement this standard. send that info to ietf-ipr@ietf.org π«Άπ
π¦ where this page comes from
- ietf: rfc5219 2008-02
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms