internal static int validate_encoder_layout(ChannelLayout layout) { int s; for (s = 0; s < layout.nb_streams; s++) { if (s < layout.nb_coupled_streams) { if (OpusMultistream.get_left_channel(layout, s, -1) == -1) { return(0); } if (OpusMultistream.get_right_channel(layout, s, -1) == -1) { return(0); } } else { if (OpusMultistream.get_mono_channel(layout, s, -1) == -1) { return(0); } } } return(1); }
internal int opus_multistream_decode_native <T>( byte[] data, int data_ptr, int len, T[] pcm, int pcm_ptr, opus_copy_channel_out_func <T> copy_channel_out, int frame_size, int decode_fec, int soft_clip ) { int Fs; int s, c; int decoder_ptr; int do_plc = 0; short[] buf; /* Limit frame_size to avoid excessive stack allocations. */ Fs = this.SampleRate; frame_size = Inlines.IMIN(frame_size, Fs / 25 * 3); buf = new short[2 * frame_size]; decoder_ptr = 0; if (len == 0) { do_plc = 1; } if (len < 0) { return(OpusError.OPUS_BAD_ARG); } if (do_plc == 0 && len < 2 * this.layout.nb_streams - 1) { return(OpusError.OPUS_INVALID_PACKET); } if (do_plc == 0) { int ret = opus_multistream_packet_validate(data, data_ptr, len, this.layout.nb_streams, Fs); if (ret < 0) { return(ret); } else if (ret > frame_size) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } } for (s = 0; s < this.layout.nb_streams; s++) { OpusDecoder dec; int ret; dec = this.decoders[decoder_ptr++]; if (do_plc == 0 && len <= 0) { return(OpusError.OPUS_INTERNAL_ERROR); } int packet_offset; ret = dec.opus_decode_native( data, data_ptr, len, buf, 0, frame_size, decode_fec, (s != this.layout.nb_streams - 1) ? 1 : 0, out packet_offset, soft_clip); data_ptr += packet_offset; len -= packet_offset; if (ret <= 0) { return(ret); } frame_size = ret; if (s < this.layout.nb_coupled_streams) { int chan, prev; prev = -1; /* Copy "left" audio to the channel(s) where it belongs */ while ((chan = OpusMultistream.get_left_channel(this.layout, s, prev)) != -1) { copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 0, 2, frame_size); prev = chan; } prev = -1; /* Copy "right" audio to the channel(s) where it belongs */ while ((chan = OpusMultistream.get_right_channel(this.layout, s, prev)) != -1) { copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 1, 2, frame_size); prev = chan; } } else { int chan, prev; prev = -1; /* Copy audio to the channel(s) where it belongs */ while ((chan = OpusMultistream.get_mono_channel(this.layout, s, prev)) != -1) { copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 0, 1, frame_size); prev = chan; } } } /* Handle muted channels */ for (c = 0; c < this.layout.nb_channels; c++) { if (this.layout.mapping[c] == 255) { copy_channel_out(pcm, pcm_ptr, this.layout.nb_channels, c, null, 0, 0, frame_size); } } return(frame_size); }
internal int opus_multistream_encode_native <T> ( opus_copy_channel_in_func <T> copy_channel_in, T[] pcm, int pcm_ptr, int analysis_frame_size, byte[] data, int data_ptr, int max_data_bytes, int lsb_depth, Downmix.downmix_func <T> downmix, int float_api ) { int Fs; int s; int encoder_ptr; int tot_size; short[] buf; int[] bandSMR; byte[] tmp_data = new byte[MS_FRAME_TMP]; OpusRepacketizer rp = new OpusRepacketizer(); int vbr; CeltMode celt_mode; int[] bitrates = new int[256]; int[] bandLogE = new int[42]; int[] mem = null; int[] preemph_mem = null; int frame_size; int rate_sum; int smallest_packet; if (this.surround != 0) { preemph_mem = this.preemph_mem; mem = this.window_mem; } encoder_ptr = 0; Fs = this.encoders[encoder_ptr].SampleRate; vbr = this.encoders[encoder_ptr].UseVBR ? 1 : 0; celt_mode = this.encoders[encoder_ptr].GetCeltMode(); { int delay_compensation; int channels; channels = this.layout.nb_streams + this.layout.nb_coupled_streams; delay_compensation = this.encoders[encoder_ptr].Lookahead; delay_compensation -= Fs / 400; frame_size = CodecHelpers.compute_frame_size(pcm, pcm_ptr, analysis_frame_size, this.variable_duration, channels, Fs, this.bitrate_bps, delay_compensation, downmix, this.subframe_mem, this.encoders[encoder_ptr].analysis.enabled); } if (400 * frame_size < Fs) { return(OpusError.OPUS_BAD_ARG); } /* Validate frame_size before using it to allocate stack space. * This mirrors the checks in opus_encode[_float](). */ if (400 * frame_size != Fs && 200 * frame_size != Fs && 100 * frame_size != Fs && 50 * frame_size != Fs && 25 * frame_size != Fs && 50 * frame_size != 3 * Fs) { return(OpusError.OPUS_BAD_ARG); } /* Smallest packet the encoder can produce. */ smallest_packet = this.layout.nb_streams * 2 - 1; if (max_data_bytes < smallest_packet) { return(OpusError.OPUS_BUFFER_TOO_SMALL); } buf = new short[2 * frame_size]; bandSMR = new int[21 * this.layout.nb_channels]; if (this.surround != 0) { surround_analysis(celt_mode, pcm, pcm_ptr, bandSMR, mem, preemph_mem, frame_size, 120, this.layout.nb_channels, Fs, copy_channel_in); } /* Compute bitrate allocation between streams (this could be a lot better) */ rate_sum = surround_rate_allocation(bitrates, frame_size); if (vbr == 0) { if (this.bitrate_bps == OpusConstants.OPUS_AUTO) { max_data_bytes = Inlines.IMIN(max_data_bytes, 3 * rate_sum / (3 * 8 * Fs / frame_size)); } else if (this.bitrate_bps != OpusConstants.OPUS_BITRATE_MAX) { max_data_bytes = Inlines.IMIN(max_data_bytes, Inlines.IMAX(smallest_packet, 3 * this.bitrate_bps / (3 * 8 * Fs / frame_size))); } } for (s = 0; s < this.layout.nb_streams; s++) { OpusEncoder enc = this.encoders[encoder_ptr]; encoder_ptr += 1; enc.Bitrate = (bitrates[s]); if (this.surround != 0) { int equiv_rate; equiv_rate = this.bitrate_bps; if (frame_size * 50 < Fs) { equiv_rate -= 60 * (Fs / frame_size - 50) * this.layout.nb_channels; } if (equiv_rate > 10000 * this.layout.nb_channels) { enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_FULLBAND); } else if (equiv_rate > 7000 * this.layout.nb_channels) { enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND); } else if (equiv_rate > 5000 * this.layout.nb_channels) { enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); } else { enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND); } if (s < this.layout.nb_coupled_streams) { /* To preserve the spatial image, force stereo CELT on coupled streams */ enc.ForceMode = (OpusMode.MODE_CELT_ONLY); enc.ForceChannels = (2); } } } encoder_ptr = 0; /* Counting ToC */ tot_size = 0; for (s = 0; s < this.layout.nb_streams; s++) { OpusEncoder enc; int len; int curr_max; int c1, c2; rp.Reset(); enc = this.encoders[encoder_ptr]; if (s < this.layout.nb_coupled_streams) { int i; int left, right; left = OpusMultistream.get_left_channel(this.layout, s, -1); right = OpusMultistream.get_right_channel(this.layout, s, -1); copy_channel_in(buf, 0, 2, pcm, pcm_ptr, this.layout.nb_channels, left, frame_size); copy_channel_in(buf, 1, 2, pcm, pcm_ptr, this.layout.nb_channels, right, frame_size); encoder_ptr += 1; if (this.surround != 0) { for (i = 0; i < 21; i++) { bandLogE[i] = bandSMR[21 * left + i]; bandLogE[21 + i] = bandSMR[21 * right + i]; } } c1 = left; c2 = right; } else { int i; int chan = OpusMultistream.get_mono_channel(this.layout, s, -1); copy_channel_in(buf, 0, 1, pcm, pcm_ptr, this.layout.nb_channels, chan, frame_size); encoder_ptr += 1; if (this.surround != 0) { for (i = 0; i < 21; i++) { bandLogE[i] = bandSMR[21 * chan + i]; } } c1 = chan; c2 = -1; } if (this.surround != 0) { enc.SetEnergyMask(bandLogE); } /* number of bytes left (+Toc) */ curr_max = max_data_bytes - tot_size; /* Reserve one byte for the last stream and two for the others */ curr_max -= Inlines.IMAX(0, 2 * (this.layout.nb_streams - s - 1) - 1); curr_max = Inlines.IMIN(curr_max, MS_FRAME_TMP); /* Repacketizer will add one or two bytes for self-delimited frames */ if (s != this.layout.nb_streams - 1) { curr_max -= curr_max > 253 ? 2 : 1; } if (vbr == 0 && s == this.layout.nb_streams - 1) { enc.Bitrate = (curr_max * (8 * Fs / frame_size)); } len = enc.opus_encode_native(buf, 0, frame_size, tmp_data, 0, curr_max, lsb_depth, pcm, pcm_ptr, analysis_frame_size, c1, c2, this.layout.nb_channels, downmix, float_api); if (len < 0) { return(len); } /* We need to use the repacketizer to add the self-delimiting lengths * while taking into account the fact that the encoder can now return * more than one frame at a time (e.g. 60 ms CELT-only) */ rp.AddPacket(tmp_data, 0, len); len = rp.opus_repacketizer_out_range_impl(0, rp.GetNumFrames(), data, data_ptr, max_data_bytes - tot_size, (s != this.layout.nb_streams - 1) ? 1 : 0, (vbr == 0 && s == this.layout.nb_streams - 1) ? 1 : 0); data_ptr += len; tot_size += len; } return(tot_size); }