Пример #1
0
        /// <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);
            }
        }
Пример #2
0
        // 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);
        }
Пример #3
0
        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);
        }
Пример #4
0
        /** 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));
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }