/// <summary> /// Decodes an Opus packet, putting the output data into a floating-point buffer. /// </summary> /// <param name="in_data">The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled)</param> /// <param name="in_data_offset">The offset to use when reading the input payload. Usually 0</param> /// <param name="len">The number of bytes in the payload</param> /// <param name="out_pcm">A buffer to put the output PCM. The output size is (# of samples) * (# of channels). /// You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need /// exact sizing. Otherwise, the minimum safe buffer size is 5760 samples</param> /// <param name="out_pcm_offset">The offset to use when writing to the output buffer</param> /// <param name="frame_size">The number of samples (per channel) of available space in the output PCM buf. /// If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will /// not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), /// then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will /// not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* /// be a multiple of 10 ms.</param> /// <param name="decode_fec">Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet /// recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not /// available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case, /// the length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state.</param> /// <returns>The number of decoded samples (per channel)</returns> public int Decode(byte[] in_data, int in_data_offset, int len, float[] out_pcm, int out_pcm_offset, int frame_size, bool decode_fec = false) { short[] output; int ret, i; int nb_samples; if (frame_size <= 0) { throw new ArgumentException("Frame size must be > 0"); } if (in_data != null && len > 0 && !decode_fec) { nb_samples = OpusPacketInfo.GetNumSamples(this, in_data, in_data_offset, len); if (nb_samples > 0) { frame_size = Inlines.IMIN(frame_size, nb_samples); } else { throw new OpusException("An invalid packet was provided (unable to parse # of samples)"); } } output = new short[frame_size * this.channels]; try { int dummy; ret = opus_decode_native(in_data, in_data_offset, len, output, 0, frame_size, decode_fec ? 1 : 0, 0, out dummy, 0); if (ret < 0) { // An error happened; report it if (ret == OpusError.OPUS_BAD_ARG) { throw new ArgumentException("OPUS_BAD_ARG when decoding"); } throw new OpusException("An error occurred during decoding", ret); } if (ret > 0) { for (i = 0; i < ret * this.channels; i++) { out_pcm[out_pcm_offset + i] = (1.0f / 32768.0f) * (output[i]); } } return(ret); } catch (ArgumentException e) { throw new OpusException("Internal error during decoding: " + e.Message); } }
// FIXME THIS METHOD FAILS IN TEST_OPUS_ENCODE /** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to * minimize space usage. * @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the * packet to strip. * @param len <tt>opus_int32</tt>: The size of the packet. * This must be at least 1. * @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet. * This must be at least 1. * @returns The new size of the output packet on success, or an error code * on failure. * @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ public static int UnpadMultistreamPacket(byte[] data, int data_offset, int len, int nb_streams) { int s; byte dummy_toc; short[] size = new short[48]; int packet_offset; int dummy_offset; OpusRepacketizer rp = new OpusRepacketizer(); int dst; int dst_len; if (len < 1) { return(OpusError.OPUS_BAD_ARG); } dst = data_offset; dst_len = 0; /* Unpad all frames */ for (s = 0; s < nb_streams; s++) { int ret; int self_delimited = ((s != nb_streams) ? 1 : 0) - 1; if (len <= 0) { return(OpusError.OPUS_INVALID_PACKET); } rp.Reset(); ret = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, self_delimited, out dummy_toc, null, null, 0, size, 0, out dummy_offset, out packet_offset); if (ret < 0) { return(ret); } ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset, self_delimited); if (ret < 0) { return(ret); } ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, dst, len, self_delimited, 0); if (ret < 0) { return(ret); } else { dst_len += ret; } dst += ret; data_offset += packet_offset; len -= packet_offset; } return(dst_len); }
internal int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited) { byte dummy_toc; int dummy_offset; int curr_nb_frames, ret; /* Set of check ToC */ if (len < 1) { return(OpusError.OPUS_INVALID_PACKET); } if (this.nb_frames == 0) { this.toc = data[data_ptr]; this.framesize = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, 8000); } else if ((this.toc & 0xFC) != (data[data_ptr] & 0xFC)) { /*fprintf(stderr, "toc mismatch: 0x%x vs 0x%x\n", rp.toc, data[0]);*/ return(OpusError.OPUS_INVALID_PACKET); } curr_nb_frames = OpusPacketInfo.GetNumFrames(data, data_ptr, len); if (curr_nb_frames < 1) { return(OpusError.OPUS_INVALID_PACKET); } /* Check the 120 ms maximum packet size */ if ((curr_nb_frames + this.nb_frames) * this.framesize > 960) { return(OpusError.OPUS_INVALID_PACKET); } ret = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out dummy_toc, this.frames, this.frames_ptrs, this.nb_frames, this.len, this.nb_frames, out dummy_offset, out dummy_offset); if (ret < 1) { return(ret); } this.nb_frames += curr_nb_frames; return(OpusError.OPUS_OK); }
/** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence). * @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the * packet to pad. * @param len <tt>opus_int32</tt>: The size of the packet. * This must be at least 1. * @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding. * This must be at least 1. * @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet. * This must be at least as large as len. * @returns an error code * @retval #OPUS_OK \a on success. * @retval #OPUS_BAD_ARG \a len was less than 1. * @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. */ public static int PadMultistreamPacket(byte[] data, int data_offset, int len, int new_len, int nb_streams) { int s; int count; byte dummy_toc; short[] size = new short[48]; int packet_offset; int dummy_offset; int amount; if (len < 1) { return(OpusError.OPUS_BAD_ARG); } if (len == new_len) { return(OpusError.OPUS_OK); } else if (len > new_len) { return(OpusError.OPUS_BAD_ARG); } amount = new_len - len; /* Seek to last stream */ for (s = 0; s < nb_streams - 1; s++) { if (len <= 0) { return(OpusError.OPUS_INVALID_PACKET); } count = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, 1, out dummy_toc, null, null, 0, size, 0, out dummy_offset, out packet_offset); if (count < 0) { return(count); } data_offset += packet_offset; len -= packet_offset; } return(PadPacket(data, data_offset, len, len + amount)); }
internal static int opus_multistream_packet_validate(byte[] data, int data_ptr, int len, int nb_streams, int Fs) { int s; int count; byte toc; short[] size = new short[48]; int samples = 0; int packet_offset; int dummy; for (s = 0; s < nb_streams; s++) { int tmp_samples; if (len <= 0) { return(OpusError.OPUS_INVALID_PACKET); } count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, (s != nb_streams - 1) ? 1 : 0, out toc, null, null, 0, size, 0, out dummy, out packet_offset); if (count < 0) { return(count); } tmp_samples = OpusPacketInfo.GetNumSamples(data, data_ptr, packet_offset, Fs); if (s != 0 && samples != tmp_samples) { return(OpusError.OPUS_INVALID_PACKET); } samples = tmp_samples; data_ptr += packet_offset; len -= packet_offset; } return(samples); }
internal int opus_repacketizer_out_range_impl(int begin, int end, byte[] data, int data_ptr, int maxlen, int self_delimited, int pad) { int i, count; int tot_size; int ptr; if (begin < 0 || begin >= end || end > this.nb_frames) { /*fprintf(stderr, "%d %d %d\n", begin, end, rp.nb_frames);*/ return(OpusError.OPUS_BAD_ARG); } count = end - begin; if (self_delimited != 0) { tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); } else { tot_size = 0; } ptr = data_ptr; if (count == 1) { /* Code 0 */ tot_size += this.len[0] + 1; if (tot_size > maxlen) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } data[ptr++] = (byte)(this.toc & 0xFC); } else if (count == 2) { if (this.len[1] == this.len[0]) { /* Code 1 */ tot_size += 2 * this.len[0] + 1; if (tot_size > maxlen) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } data[ptr++] = (byte)((this.toc & 0xFC) | 0x1); } else { /* Code 2 */ tot_size += this.len[0] + this.len[1] + 2 + (this.len[0] >= 252 ? 1 : 0); if (tot_size > maxlen) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } data[ptr++] = (byte)((this.toc & 0xFC) | 0x2); ptr += OpusPacketInfo.encode_size(this.len[0], data, ptr); } } if (count > 2 || (pad != 0 && tot_size < maxlen)) { /* Code 3 */ int vbr; int pad_amount = 0; /* Restart the process for the padding case */ ptr = data_ptr; if (self_delimited != 0) { tot_size = 1 + (this.len[count - 1] >= 252 ? 1 : 0); } else { tot_size = 0; } vbr = 0; for (i = 1; i < count; i++) { if (this.len[i] != this.len[0]) { vbr = 1; break; } } if (vbr != 0) { tot_size += 2; for (i = 0; i < count - 1; i++) { tot_size += 1 + (this.len[i] >= 252 ? 1 : 0) + this.len[i]; } tot_size += this.len[count - 1]; if (tot_size > maxlen) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } data[ptr++] = (byte)((this.toc & 0xFC) | 0x3); data[ptr++] = (byte)(count | 0x80); } else { tot_size += count * this.len[0] + 2; if (tot_size > maxlen) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } data[ptr++] = (byte)((this.toc & 0xFC) | 0x3); data[ptr++] = (byte)(count); } pad_amount = pad != 0 ? (maxlen - tot_size) : 0; if (pad_amount != 0) { int nb_255s; data[data_ptr + 1] |= 0x40; nb_255s = (pad_amount - 1) / 255; for (i = 0; i < nb_255s; i++) { data[ptr++] = 255; } data[ptr++] = (byte)(pad_amount - 255 * nb_255s - 1); tot_size += pad_amount; } if (vbr != 0) { for (i = 0; i < count - 1; i++) { ptr += (OpusPacketInfo.encode_size(this.len[i], data, ptr)); } } } if (self_delimited != 0) { int sdlen = OpusPacketInfo.encode_size(this.len[count - 1], data, ptr); ptr += (sdlen); } /* Copy the actual data */ for (i = begin; i < count + begin; i++) { if (this.frames[i] == data) { /* Using OPUS_MOVE() instead of OPUS_COPY() in case we're doing in-place * padding from opus_packet_pad or opus_packet_unpad(). */ Arrays.MemMove <byte>(data, frames_ptrs[i], ptr, this.len[i]); } else { Array.Copy(this.frames[i], frames_ptrs[i], data, ptr, this.len[i]); } ptr += this.len[i]; } if (pad != 0) { /* Fill padding with zeros. */ //Arrays.MemSetWithOffset<byte>(ptr.Data, 0, ptr.Offset, data.Offset + maxlen - ptr.Offset); // FIXME why did they not just use a MemSet(0) here? while (ptr < data_ptr + maxlen) { data[ptr++] = 0; } } return(tot_size); }
internal int opus_decode_native(byte[] data, int data_ptr, int len, short[] pcm_out, int pcm_out_ptr, int frame_size, int decode_fec, int self_delimited, out int packet_offset, int soft_clip) { int i, nb_samples; int count, offset; byte toc; int packet_frame_size, packet_stream_channels; packet_offset = 0; OpusBandwidth packet_bandwidth; OpusMode packet_mode; /* 48 x 2.5 ms = 120 ms */ // fixme: make sure these values can fit in an int16 short[] size = new short[48]; if (decode_fec < 0 || decode_fec > 1) { return(OpusError.OPUS_BAD_ARG); } /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */ if ((decode_fec != 0 || len == 0 || data == null) && frame_size % (this.Fs / 400) != 0) { return(OpusError.OPUS_BAD_ARG); } if (len == 0 || data == null) { int pcm_count = 0; do { int ret; ret = opus_decode_frame(null, 0, 0, pcm_out, pcm_out_ptr + (pcm_count * this.channels), frame_size - pcm_count, 0); if (ret < 0) { return(ret); } pcm_count += ret; } while (pcm_count < frame_size); Inlines.OpusAssert(pcm_count == frame_size); this.last_packet_duration = pcm_count; return(pcm_count); } else if (len < 0) { return(OpusError.OPUS_BAD_ARG); } packet_mode = OpusPacketInfo.GetEncoderMode(data, data_ptr); packet_bandwidth = OpusPacketInfo.GetBandwidth(data, data_ptr); packet_frame_size = OpusPacketInfo.GetNumSamplesPerFrame(data, data_ptr, this.Fs); packet_stream_channels = OpusPacketInfo.GetNumEncodedChannels(data, data_ptr); count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, out toc, null, null, 0, size, 0, out offset, out packet_offset); if (count < 0) { return(count); } data_ptr += offset; if (decode_fec != 0) { int dummy; int duration_copy; int ret; /* If no FEC can be present, run the PLC (recursive call) */ if (frame_size < packet_frame_size || packet_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY) { return(opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size, 0, 0, out dummy, soft_clip)); } /* Otherwise, run the PLC on everything except the size for which we might have FEC */ duration_copy = this.last_packet_duration; if (frame_size - packet_frame_size != 0) { ret = opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size - packet_frame_size, 0, 0, out dummy, soft_clip); if (ret < 0) { this.last_packet_duration = duration_copy; return(ret); } Inlines.OpusAssert(ret == frame_size - packet_frame_size); } /* Complete with FEC */ this.mode = packet_mode; this.bandwidth = packet_bandwidth; this.frame_size = packet_frame_size; this.stream_channels = packet_stream_channels; ret = opus_decode_frame(data, data_ptr, size[0], pcm_out, pcm_out_ptr + (this.channels * (frame_size - packet_frame_size)), packet_frame_size, 1); if (ret < 0) { return(ret); } else { this.last_packet_duration = frame_size; return(frame_size); } } if (count * packet_frame_size > frame_size) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } /* Update the state as the last step to avoid updating it on an invalid packet */ this.mode = packet_mode; this.bandwidth = packet_bandwidth; this.frame_size = packet_frame_size; this.stream_channels = packet_stream_channels; nb_samples = 0; for (i = 0; i < count; i++) { int ret; ret = opus_decode_frame(data, data_ptr, size[i], pcm_out, pcm_out_ptr + (nb_samples * this.channels), frame_size - nb_samples, 0); if (ret < 0) { return(ret); } Inlines.OpusAssert(ret == packet_frame_size); data_ptr += size[i]; nb_samples += ret; } this.last_packet_duration = nb_samples; return(nb_samples); }