opus_repacketizer(3)

πŸ’– opus_repacketizer πŸ’–

okay bestie, welcome to the Opus manual, section 3 β€” this one's literally about the repacketizer and it's lowkey one of the most useful little guys in the whole library. it's giving "packet glow-up station." let's get into it 🫢


✨ NAME ✨

opus_repacketizer β€” Repacketizer 🌈

  • The repacketizer can be used to merge multiple Opus packets into a single packet, or alternatively to split Opus packets that have previously been merged. that's it. that's the whole vibe. both directions, iconic πŸ’…

✨ SYNOPSIS ✨

πŸŽ€ Typedefs

typedef struct OpusRepacketizer OpusRepacketizer

πŸŽ€ Functions

  • int opus_repacketizer_get_size (void) β€” Gets the size of an OpusRepacketizer structure. just the size, no allocation, no drama ✨
  • OpusRepacketizer * opus_repacketizer_init (OpusRepacketizer *rp) β€” (Re)initializes a previously allocated repacketizer state. yes bestie, re-initializes. it resets, it refreshes, it starts over πŸ’…
  • OpusRepacketizer * opus_repacketizer_create (void) β€” Allocates memory and initializes the new repacketizer with opus_repacketizer_init(). it's the "just do it for me" option 🫢
  • void opus_repacketizer_destroy (OpusRepacketizer *rp) β€” Frees an OpusRepacketizer allocated by opus_repacketizer_create(). clean up after yourself, queen 🧚
  • int opus_repacketizer_cat (OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) β€” Add a packet to the current repacketizer state. yes, "cat" like concatenate. not a cat. (though a cat would also be welcome 🐱)
  • opus_int32 opus_repacketizer_out_range (OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen) β€” Construct a new packet from data previously submitted to the repacketizer state via opus_repacketizer_cat(). range-based, so you pick exactly which frames make the cut 🎯
  • int opus_repacketizer_get_nb_frames (OpusRepacketizer *rp) β€” Return the total number of frames contained in packet data submitted to the repacketizer state so far via opus_repacketizer_cat() since the last call to opus_repacketizer_init() or opus_repacketizer_create(). it's literally just counting frames for you, no cap πŸ”’
  • opus_int32 opus_repacketizer_out (OpusRepacketizer *rp, unsigned char *data, opus_int32 maxlen) β€” Construct a new packet from data previously submitted to the repacketizer state via opus_repacketizer_cat(). the "give me everything" version of out_range πŸ’–
  • int opus_packet_pad (unsigned char *data, opus_int32 len, opus_int32 new_len) β€” Pads a given Opus packet to a larger size (possibly changing the TOC sequence). make it bigger, bestie 🎈
  • opus_int32 opus_packet_unpad (unsigned char *data, opus_int32 len) β€” Remove all padding from a given Opus packet and rewrite the TOC sequence to minimize space usage. the ✨snatch✨ to pad's… well, you know.
  • int opus_multistream_packet_pad (unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams) β€” Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence). same as above but make it multistream 🌈
  • opus_int32 opus_multistream_packet_unpad (unsigned char *data, opus_int32 len, int nb_streams) β€” Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to minimize space usage. multistream de-bloating service, at your service πŸ’…

🌸 DETAILED DESCRIPTION 🌸

The repacketizer can be used to merge multiple Opus packets into a single packet, or alternatively to split Opus packets that have previously been merged. πŸ¦‹

Splitting valid Opus packets is always guaranteed to succeed β€” like, always always. no notes. whereas merging valid packets only succeeds if all frames have the same mode, bandwidth, and frame size, and when the total duration of the merged packet is no more than 120 ms. that 120 ms limit comes from the specification and limits decoder memory requirements at a point where framing overhead becomes negligible. so it's not a vibe thing, it's a spec thing πŸŽ€

The repacketizer currently only operates on elementary Opus streams. It will not manipulate multistream packets successfully, except in the degenerate case where they consist of data from a single stream. so if you're trying to feed it a multistream packet, bestie… don't πŸ™ˆ (unless it's secretly just one stream wearing a multistream trenchcoat, iykyk)

The repacketizing process starts with creating a repacketizer state, either by calling opus_repacketizer_create() or by allocating the memory yourself, e.g.,

OpusRepacketizer *rp;
rp = (OpusRepacketizer*)malloc(opus_repacketizer_get_size());
if (rp != NULL)
    opus_repacketizer_init(rp);

Then the application should submit packets with opus_repacketizer_cat(), extract new packets with opus_repacketizer_out() or opus_repacketizer_out_range(), and then reset the state for the next set of input packets via opus_repacketizer_init(). submit β†’ extract β†’ reset. that's the whole loop, it's literally a three-step skincare routine for packets πŸ§–β€β™€οΈ

For example, to split a sequence of packets into individual frames:

unsigned char *data;
int len;
while (get_next_packet(&data, &len))
{
  unsigned char out[1276];
  opus_int32 out_len;
  int nb_frames;
  int err;
  int i;
  err = opus_repacketizer_cat(rp, data, len);
  if (err != OPUS_OK)
  {
    release_packet(data);
    return err;
  }
  nb_frames = opus_repacketizer_get_nb_frames(rp);
  for (i = 0; i < nb_frames; i++)
  {
    out_len = opus_repacketizer_out_range(rp, i, i+1, out, sizeof(out));
    if (out_len < 0)
    {
       release_packet(data);
       return (int)out_len;
    }
    output_next_packet(out, out_len);
  }
  opus_repacketizer_init(rp);
  release_packet(data);
}

Alternatively, to combine a sequence of frames into packets that each contain up to TARGET_DURATION_MS milliseconds of data:

// The maximum number of packets with duration TARGET_DURATION_MS occurs
// when the frame size is 2.5 ms, for a total of (TARGET_DURATION_MS*2/5)
// packets.
unsigned char *data[(TARGET_DURATION_MS*2/5)+1];
opus_int32 len[(TARGET_DURATION_MS*2/5)+1];
int nb_packets;
unsigned char out[1277*(TARGET_DURATION_MS*2/2)];
opus_int32 out_len;
int prev_toc;
nb_packets = 0;
while (get_next_packet(data+nb_packets, len+nb_packets))
{
  int nb_frames;
  int err;
  nb_frames = opus_packet_get_nb_frames(data[nb_packets], len[nb_packets]);
  if (nb_frames < 1)
  {
    release_packets(data, nb_packets+1);
    return nb_frames;
  }
  nb_frames += opus_repacketizer_get_nb_frames(rp);
  // If adding the next packet would exceed our target, or it has an
  // incompatible TOC sequence, output the packets we already have before
  // submitting it.
  // N.B., The nb_packets > 0 check ensures we've submitted at least one
  // packet since the last call to opus_repacketizer_init(). Otherwise a
  // single packet longer than TARGET_DURATION_MS would cause us to try to
  // output an (invalid) empty packet. It also ensures that prev_toc has
  // been set to a valid value. Additionally, len[nb_packets] > 0 is
  // guaranteed by the call to opus_packet_get_nb_frames() above, so the
  // reference to data[nb_packets][0] should be valid.
  if (nb_packets > 0 && (
      ((prev_toc & 0xFC) != (data[nb_packets][0] & 0xFC)) ||
      opus_packet_get_samples_per_frame(data[nb_packets], 48000)*nb_frames >
      TARGET_DURATION_MS*48))
  {
    out_len = opus_repacketizer_out(rp, out, sizeof(out));
    if (out_len < 0)
    {
       release_packets(data, nb_packets+1);
       return (int)out_len;
    }
    output_next_packet(out, out_len);
    opus_repacketizer_init(rp);
    release_packets(data, nb_packets);
    data[0] = data[nb_packets];
    len[0] = len[nb_packets];
    nb_packets = 0;
  }
  err = opus_repacketizer_cat(rp, data[nb_packets], len[nb_packets]);
  if (err != OPUS_OK)
  {
    release_packets(data, nb_packets+1);
    return err;
  }
  prev_toc = data[nb_packets][0];
  nb_packets++;
}
// Output the final, partial packet.
if (nb_packets > 0)
{
  out_len = opus_repacketizer_out(rp, out, sizeof(out));
  release_packets(data, nb_packets);
  if (out_len < 0)
    return (int)out_len;
  output_next_packet(out, out_len);
}
okay quick note bestie, that comment block inside the code is doing a LOT of heavy lifting β€” the nb_packets > 0 check is what stops a single packet longer than TARGET_DURATION_MS from making you try to output an (invalid) empty packet, and it also guarantees prev_toc has been set to a valid value. also len[nb_packets] > 0 is guaranteed by the call to opus_packet_get_nb_frames() above, so the reference to data[nb_packets][0] should be valid. read your comments, they're trying to help you 😭

An alternate way of merging packets is to simply call opus_repacketizer_cat() unconditionally until it fails. At that point, the merged packet can be obtained with opus_repacketizer_out() and the input packet for which opus_repacketizer_cat() needs to be re-added to a newly reinitialized repacketizer state. brute force but make it elegant πŸ’…


πŸ¦‹ TYPEDEF DOCUMENTATION πŸ¦‹

✨ typedef struct OpusRepacketizer OpusRepacketizer

yep. that's the opaque lil guy. you don't look inside, you just pass him around and he does his thing πŸ‘‘


and that's where part 1 wraps… except it doesn't wrap, because part 2 is literally right there β€” keep scrolling bestie, we're not done yet 🌸

πŸ’– FUNCTION DOCUMENTATION πŸ’–

✨ opus_multistream_packet_pad ✨

int opus_multistream_packet_pad (unsigned char * data, opus_int32 len,
    opus_int32 new_len, int nb_streams)

okay so this one pads a multi-stream Opus packet up to a bigger size πŸŽ€ (and yeah, it might change up the TOC sequence while it's doing that, no biggie).

🌸 Parameters 🌸

  • data const unsigned char* β€” the buffer holding the packet you wanna pad, bestie.
  • len opus_int32 β€” the size of the packet. this must be at least 1, fr fr.
  • new_len opus_int32 β€” the size you want the packet to be after padding. also has to be at least 1.
  • nb_streams opus_int32 β€” the number of streams (not channels!! streams πŸ’…) in the packet. this has to be at least as large as len.

🌸 Returns 🌸

an error code, that's the whole vibe.

🌸 Return values 🌸

  • OPUS_OK β€” slayed, it worked ✨
  • OPUS_BAD_ARG β€” len was less than 1.
  • OPUS_INVALID_PACKET β€” data did not contain a valid Opus packet. 😭

✨ opus_multistream_packet_unpad ✨

opus_int32 opus_multistream_packet_unpad (unsigned char * data, opus_int32
    len, int nb_streams)

the reverse glow-up πŸ’– it removes all the padding from a multi-stream Opus packet and rewrites the TOC sequence so it takes up the least space possible. minimalism era, periodt.

🌸 Parameters 🌸

  • data const unsigned char* β€” the buffer holding the packet you wanna strip.
  • len opus_int32 β€” the size of the packet. at least 1, no negotiating.
  • nb_streams opus_int32 β€” the number of streams (not channels) in the packet. at least 1.

🌸 Returns 🌸

the new size of the output packet on success 🫢 or an error code if it flopped.

🌸 Return values 🌸

  • OPUS_BAD_ARG β€” len was less than 1 or new_len was less than len.
  • OPUS_INVALID_PACKET β€” data did not contain a valid Opus packet. πŸ’€

✨ opus_packet_pad ✨

int opus_packet_pad (unsigned char * data, opus_int32 len, opus_int32
    new_len)

pads a single Opus packet up to a larger size 🌈 (possibly changing the TOC sequence, iykyk).

🌸 Parameters 🌸

  • data const unsigned char* β€” the buffer containing the packet to pad.
  • len opus_int32 β€” the size of the packet. has to be at least 1.
  • new_len opus_int32 β€” the desired size of the packet after padding. has to be at least as large as len.

🌸 Returns 🌸

an error code.

🌸 Return values 🌸

  • OPUS_OK β€” success, we ate πŸ‘‘
  • OPUS_BAD_ARG β€” len was less than 1 or new_len was less than len.
  • OPUS_INVALID_PACKET β€” data did not contain a valid Opus packet.

✨ opus_packet_unpad ✨

opus_int32 opus_packet_unpad (unsigned char * data, opus_int32 len)

strips all the padding off a given Opus packet and rewrites the TOC sequence to minimize space usage πŸ¦‹ tiny packet energy.

🌸 Parameters 🌸

  • data const unsigned char* β€” the buffer containing the packet to strip.
  • len opus_int32 β€” the size of the packet. at least 1, always.

🌸 Returns 🌸

the new size of the output packet on success, or an error code on failure.

🌸 Return values 🌸

  • OPUS_BAD_ARG β€” len was less than 1.
  • OPUS_INVALID_PACKET β€” data did not contain a valid Opus packet.

✨ opus_repacketizer_cat ✨

int opus_repacketizer_cat (OpusRepacketizer * rp, const unsigned char *
    data, opus_int32 len)

okay this is the big one, sit down bestie πŸ“ β€” you're adding a packet to the current repacketizer state. and here's the tea: this packet must match the configuration of every packet you already submitted for repacketization since the last call to opus_repacketizer_init(). meaning it has to have the same coding mode, audio bandwidth, frame size, and channel count. you can check that in advance by peeking at the top 6 bits of the first byte of the packet, and making sure they match the top 6 bits of the first byte of any previously submitted packet. also, the total duration of audio in the repacketizer state must not exceed 120 ms (that's the max duration of a single packet, bestie) after adding this packet.

the contents of the current repacketizer state can be extracted into new packets using opus_repacketizer_out() or opus_repacketizer_out_range() πŸ’–

and if you wanna add a packet with a different configuration, or add more audio past 120 ms, you have to clear the repacketizer state by calling opus_repacketizer_init(). also: if a packet is too large to add to the current state, no part of it is added β€” even if it contains multiple frames and some of those would totally fit. if you want to be able to add parts of packets like that, you should first use another repacketizer to split the packet into pieces and add them individually. teamwork, kind of πŸ’…

🌸 See also 🌸

  • opus_repacketizer_out_range
  • opus_repacketizer_out
  • opus_repacketizer_init

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” the repacketizer state you're adding the packet to.
  • data const unsigned char* β€” the packet data. the application must ensure this pointer remains valid until the next call to opus_repacketizer_init() or opus_repacketizer_destroy().
  • len opus_int32 β€” the number of bytes in the packet data.

🌸 Returns 🌸

an error code telling you whether or not the operation succeeded.

🌸 Return values 🌸

  • OPUS_OK β€” the packet's contents have been added to the repacketizer state πŸŽ‰
  • OPUS_INVALID_PACKET β€” the packet did not have a valid TOC sequence, the packet's TOC sequence was not compatible with previously submitted packets (because the coding mode, audio bandwidth, frame size, or channel count did not match), or adding this packet would push the total amount of audio stored in the repacketizer state to more than 120 ms. πŸ™ˆ

✨ opus_repacketizer_create ✨

OpusRepacketizer * opus_repacketizer_create (void )

allocates memory and initializes a brand-new repacketizer with opus_repacketizer_init() 🌱 it's giving fresh start.


✨ opus_repacketizer_destroy ✨

void opus_repacketizer_destroy (OpusRepacketizer * rp)

frees an OpusRepacketizer that was allocated by opus_repacketizer_create() 🧹 clean up after yourself, bestie.

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” state to be freed.

✨ opus_repacketizer_get_nb_frames ✨

int opus_repacketizer_get_nb_frames (OpusRepacketizer * rp)

returns the total number of frames contained in the packet data submitted to the repacketizer state so far via opus_repacketizer_cat(), since the last call to opus_repacketizer_init() or opus_repacketizer_create(). this defines the valid range of packets you can extract with opus_repacketizer_out_range() or opus_repacketizer_out() πŸ“

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” the repacketizer state containing the frames.

🌸 Returns 🌸

the total number of frames contained in the packet data submitted to the repacketizer state.


✨ opus_repacketizer_get_size ✨

int opus_repacketizer_get_size (void )

gets the size of an OpusRepacketizer structure πŸ“

🌸 Returns 🌸

the size in bytes.


✨ opus_repacketizer_init ✨

OpusRepacketizer * opus_repacketizer_init (OpusRepacketizer * rp)

(re)initializes a previously allocated repacketizer state 🌟 the state must be at least the size returned by opus_repacketizer_get_size(). this is for the apps that roll with their own allocator instead of malloc(), iykyk. you also must call it to reset the queue of packets waiting to be repacketized β€” which is necessary if the maximum packet duration of 120 ms is reached, or if you wanna submit packets with a different Opus configuration (coding mode, audio bandwidth, frame size, or channel count). skip this and a new packet will not be added by opus_repacketizer_cat(). don't say we didn't warn you πŸ’€

🌸 See also 🌸

  • opus_repacketizer_create
  • opus_repacketizer_get_size
  • opus_repacketizer_cat

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” the repacketizer state to (re)initialize.

🌸 Returns 🌸

a pointer to the same repacketizer state that was passed in 🫢


✨ opus_repacketizer_out ✨

opus_int32 opus_repacketizer_out (OpusRepacketizer * rp, unsigned char *
    data, opus_int32 maxlen)

constructs a new packet from data you previously submitted to the repacketizer state via opus_repacketizer_cat() 🎁 this is a convenience routine that returns all the data submitted so far in a single packet. it's equivalent to calling:

opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp),
                            data, maxlen)

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” the repacketizer state to construct the new packet from.
  • data const unsigned char* β€” the buffer to store the output packet in.
  • maxlen opus_int32 β€” the maximum number of bytes to store in the output buffer. to guarantee success, this should be at least 1277*opus_repacketizer_get_nb_frames(rp). however, 1*opus_repacketizer_get_nb_frames(rp) plus the size of all packet data submitted to the repacketizer since the last call to opus_repacketizer_init() or opus_repacketizer_create() is also sufficient, and possibly much smaller 🌸

🌸 Returns 🌸

the total size of the output packet on success, or an error code on failure.

🌸 Return values 🌸

  • OPUS_BUFFER_TOO_SMALL β€” maxlen was insufficient to contain the complete output packet. 😭

✨ opus_repacketizer_out_range ✨

opus_int32 opus_repacketizer_out_range (OpusRepacketizer * rp, int begin,
    int end, unsigned char * data, opus_int32 maxlen)

constructs a new packet from data you previously submitted to the repacketizer state via opus_repacketizer_cat() ✨ same energy, but now you get to pick a range.

🌸 Parameters 🌸

  • rp OpusRepacketizer* β€” the repacketizer state to construct the new packet from.
  • begin int β€” the index of the first frame in the current repacketizer state to include in the output.
  • end int β€” one past the index of the last frame in the current repacketizer state to include in the output.
  • data const unsigned char* β€” the buffer to store the output packet in.
  • maxlen opus_int32 β€” the maximum number of bytes to store in the output buffer. to guarantee success, this should be at least 1276 for a single frame, or for multiple frames, 1277*(end-begin). however, 1*(end-begin) plus the size of all packet data submitted to the repacketizer since the last call to opus_repacketizer_init() or opus_repacketizer_create() is also sufficient, and possibly much smaller πŸ¦‹

🌸 Returns 🌸

the total size of the output packet on success, or an error code on failure.

🌸 Return values 🌸

  • OPUS_BAD_ARG β€” [begin,end) was an invalid range of frames (begin < 0, begin >= end, or end > opus_repacketizer_get_nb_frames()). πŸ’€
  • OPUS_BUFFER_TOO_SMALL β€” maxlen was insufficient to contain the complete output packet.

πŸ‘‘ Author πŸ‘‘

generated automatically by Doxygen for Opus from the source code. no thoughts, just vibes πŸ€–πŸ’–


and that's a wrap, bestie!! πŸŽ€ this whole thing is Version 1.6.1 and it got generated on Thu Jan 29 2026 14:35:32 β€” so go forth, pad those packets, unpadding them, repacketize responsibly, and remember: if it says opus_repacketizer(3), you're in the right place πŸŒˆβœ¨πŸ’…

πŸ“¦ where this page comes from