using System; using System.Runtime.InteropServices; namespace AX.AudioSystem { /// /// Opus C API Bindings。具体请参考 http://www.opus-codec.org/docs/。 /// internal static class OpusNative { #region Opus Encoder [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_get_size(int channels); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_encoder_create(int Fs, int channels, int application, out int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_init(IntPtr st, int Fs, int channels, int application); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_encode(IntPtr st, short* pcm, int frame_size, byte* data, int max_data_bytes); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_encode_float(IntPtr st, float* pcm, int frame_size, byte* data, int max_data_bytes); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern void opus_encoder_destroy(IntPtr st); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_ctl(IntPtr st, int request); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_get_ctl(IntPtr st, int request, out int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_set_ctl(IntPtr st, int request, int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_encoder_reset(IntPtr st); public static IntPtr OpusEncoderCreate(SamplingRate Fs, Channel channels, OpusApplication application) { var size = opus_encoder_get_size((int)channels); var intptr = Marshal.AllocHGlobal(size); var error = opus_encoder_init(intptr, (int)Fs, (int)channels, (int)application); if (error != (int)OpusErrorCode.OK && intptr != IntPtr.Zero) { OpusEncoderDestroy(intptr); ThrowOpusException(error); } return intptr; } public static void OpusEncoderDestroy(IntPtr st) { Marshal.FreeHGlobal(st); } public static int OpusEncode(IntPtr st, short[] pcm, int frame_size, byte[] data) { return OpusEncode(st, pcm, 0, frame_size, data); } public static unsafe int OpusEncode(IntPtr st, short[] pcm, int pcmOffset, int frame_size, byte[] data) { fixed (short* pcmPtr = &pcm[pcmOffset]) fixed (byte* dataPtr = &data[0]) { var result = opus_encode(st, pcmPtr, frame_size, dataPtr, data.Length); if (result < 0) ThrowOpusException(result); return result; } } public static int OpusEncode(IntPtr st, float[] pcm, int frame_size, byte[] data) { return OpusEncode(st, pcm, 0, frame_size, data); } public static unsafe int OpusEncode(IntPtr st, float[] pcm, int pcmOffset, int frame_size, byte[] data) { fixed (float* pcmPtr = &pcm[pcmOffset]) fixed (byte* dataPtr = &data[0]) { var result = opus_encode_float(st, pcmPtr, frame_size, dataPtr, data.Length); if (result < 0) ThrowOpusException(result); return result; } } public static int OpusEncoderGetCtl(IntPtr st, OpusEncoderGetRequest request) { int result = 0; var error = opus_encoder_get_ctl(st, (int)request, out result); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); return result; } public static void OpusEncoderSetCtl(IntPtr st, OpusEncoderSetRequest request, int value) { var error = opus_encoder_set_ctl(st, (int)request, value); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); } public static void OpusEncoderReset(IntPtr st) { var error = opus_encoder_reset(st); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); } #endregion #region Opus Decoder [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_get_size(int channels); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_decoder_create(int Fs, int channels, out int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_init(IntPtr st, int Fs, int channels); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_decode(IntPtr st, byte* data, int len, short* pcm, int frame_size, int decode_fec); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_decode_float(IntPtr st, byte* data, int len, float* pcm, int frame_size, int decode_fec); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_ctl(IntPtr st, int request); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_get_ctl(IntPtr st, int request, out int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_set_ctl(IntPtr st, int request, int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_reset(IntPtr st); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern void opus_decoder_destroy(IntPtr st); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_parse(byte* data, int len, byte* out_toc, [MarshalAs(UnmanagedType.LPArray, SizeConst = 48)]byte[] frames, [MarshalAs(UnmanagedType.LPArray, SizeConst = 48 * 2)]short[] size, int* payload_offset); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_get_bandwidth(byte[] data); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_get_samples_per_frame(byte[] data, int Fs); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_get_nb_channels(byte[] data); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_packet_get_nb_frames(byte[] packet, int len); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_packet_get_nb_samples(byte[] packet, int len, int Fs); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_decoder_get_nb_samples(IntPtr dec, byte[] packet, int len); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe void opus_pcm_soft_clip(float* pcm, int frame_size, int channels, float* softclip_mem); public static IntPtr OpusDecoderCreate(SamplingRate Fs, Channel channels) { var size = opus_decoder_get_size((int)channels); var intptr = Marshal.AllocHGlobal(size); var error = opus_decoder_init(intptr, (int)Fs, (int)channels); if (error != (int)OpusErrorCode.OK && intptr != IntPtr.Zero) { OpusDecoderDestroy(intptr); ThrowOpusException(error); } return intptr; } public static void OpusDecoderDestroy(IntPtr st) { Marshal.FreeHGlobal(st); } public static unsafe int OpusDecode(IntPtr st, byte[] data, short[] pcm, int channels, int decode_fec) { if (st == IntPtr.Zero) throw new ObjectDisposedException("OpusDecoder"); int samplesDecoded = 0; fixed (short* pcmPtr = &pcm[0]) { if (data != null) { fixed (byte* dataPtr = &data[0]) { samplesDecoded = opus_decode(st, dataPtr, data.Length, pcmPtr, pcm.Length / channels, decode_fec); } } else samplesDecoded = opus_decode(st, null, 0, pcmPtr, pcm.Length / channels, decode_fec); } if (samplesDecoded == (int)OpusErrorCode.InvalidPacket) return 0; if (samplesDecoded <= (int)OpusErrorCode.OK) ThrowOpusException(samplesDecoded); return samplesDecoded; } public static unsafe int OpusDecode(IntPtr st, byte[] data, float[] pcm, int channels, int decode_fec) { if (st == IntPtr.Zero) throw new ObjectDisposedException("OpusDecoder"); int samplesDecoded = 0; fixed (float* pcmPtr = &pcm[0]) { if (data != null) { fixed (byte* dataPtr = &data[0]) { samplesDecoded = opus_decode_float(st, dataPtr, data.Length, pcmPtr, pcm.Length / channels, decode_fec); } } else samplesDecoded = opus_decode_float(st, null, 0, pcmPtr, pcm.Length / channels, decode_fec); } if (samplesDecoded == (int)OpusErrorCode.InvalidPacket) return 0; if (samplesDecoded <= (int)OpusErrorCode.OK) ThrowOpusException(samplesDecoded); return samplesDecoded; } public static int OpusDecoderGetCtl(IntPtr st, OpusDecoderGetRequest request) { int result = 0; var error = opus_decoder_get_ctl(st, (int)request, out result); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); return result; } public static void OpusDecoderSetCtl(IntPtr st, OpusDecoderSetRequest request, int value) { var error = opus_decoder_set_ctl(st, (int)request, value); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); } public static void OpusDecoderReset(IntPtr st) { var error = opus_decoder_reset(st); if (error != (int)OpusErrorCode.OK) ThrowOpusException(error); } #endregion #region Repacketizer [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_repacketizer_get_size(); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_repacketizer_init(IntPtr rp); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_repacketizer_create(); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern void opus_repacketizer_destroy(IntPtr rp); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_repacketizer_cat(IntPtr rp, byte* data, int len); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_repacketizer_out_range(IntPtr rp, int begin, int end, byte* data, int maxlen); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_repacketizer_get_nb_frames(IntPtr rp); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_repacketizer_out(IntPtr rp, byte* data, int maxlen); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_pad(byte* data, int len, int new_len); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_packet_unpad(byte* data, int len); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_packet_pad(byte* data, int len, int new_len, int nb_streams); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_packet_unpad(byte* data, int len, int nb_streams); #endregion #region Opus Multistream API #region Multistream encoder functions [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_encoder_get_size(int streams, int coupled_streams); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_surround_encoder_get_size(int channels, int mapping_family); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe IntPtr opus_multistream_encoder_create(int Fs, int channels, int streams, int coupled_streams, byte* mapping, int application, out int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe IntPtr opus_multistream_surround_encoder_create(int Fs, int channels, int mapping_family, int* streams, int* coupled_streams, byte* mapping, int application, out int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_encoder_init(IntPtr st, int Fs, int channels, int streams, int coupled_streams, byte* mapping, int application); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_surround_encoder_init(IntPtr st, int Fs, int channels, int mapping_family, int* streams, int* coupled_streams, byte* mapping, int application); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_encode(IntPtr st, short* pcm, int frame_size, byte* data, int max_data_bytes); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_encode_float(IntPtr st, float* pcm, int frame_size, byte* data, int max_data_bytes); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern void opus_multistream_encoder_destroy(IntPtr st); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_encoder_ctl(IntPtr st, int request); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_encoder_get_ctl(IntPtr st, int request, out int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_encoder_set_ctl(IntPtr st, int request, int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_encoder_reset(IntPtr st); #endregion #region Multistream decoder functions [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_decoder_get_size(int streams, int coupled_streams); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe IntPtr opus_multistream_decoder_create(int Fs, int channels, int streams, int coupled_streams, byte* mapping, out int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_decoder_init(IntPtr st, int Fs, int channels, int streams, int coupled_streams, byte* mapping); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_decode(IntPtr st, byte* data, int len, short* pcm, int frame_size, int decode_fec); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern unsafe int opus_multistream_decode_float(IntPtr st, byte* data, int len, float* pcm, int frame_size, int decode_fec); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_decoder_ctl(IntPtr st, int request); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_decoder_get_ctl(IntPtr st, int request, out int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_decoder_set_ctl(IntPtr st, int request, int value); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern int opus_multistream_decoder_reset(IntPtr st); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern void opus_multistream_decoder_destroy(IntPtr st); #endregion #endregion #region Opus library information functions [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_strerror(int error); [DllImport("opus", CallingConvention = CallingConvention.Cdecl)] internal static extern IntPtr opus_get_version_string(); public static string OpusStringError(OpusErrorCode errorCode) { return Marshal.PtrToStringAnsi(opus_strerror((int)errorCode)); } public static string OpusGetVersionString() { return Marshal.PtrToStringAnsi(opus_get_version_string()); } #endregion #region Opus Custom #endregion #region Others public static void ThrowOpusException(int error) { throw new OpusException((OpusErrorCode)error, OpusStringError((OpusErrorCode)error)); } #endregion } }