基于jt808协议调用指令下发获取实时视频 qq:2055150936
一开始接到这个需求时,查看 1078 协议文档发现,只是在文档中简单介绍了实时音视频传输的指令,以及设备收到指令后上传的音视频 RTP 负载包格式。
什么?只有设备上传音视频相关的文档说明,既然是对讲,那么麦克风语音如何传给设备呢?
个人觉得,这些部标协议用到的技术都比较老旧了,比如检索设备视频并上传文件,竟然还用 FTP?现在一般都是把文件存到第三方的对象存储服务了,比如:阿里云的OSS。用 FTP 的话我们就有可能需要在 FTP 那边接收到文件然后又转存一次存到 OSS,岂不是多此一举?实时音视频传输也是一样的,为什么不用第三方的流媒体服务器?非要传RTP包然后再转发一次到流媒体服务器?
废话不多说了,之前说到麦克风语音如何传给设备,文档里没有说明,那我们就去猜吧,实际上传输和设备传给视频转发服务器一样,也是用的文档中的 RTP 负载包格式。
MDVR终端音频参数配置说明采样率:8K采样精度:16位采样点:320编码格式:AUDIO_CODEC_G711A,AUDIO_CODEC_G726_MEDIA_40K,AUDIO_CODEC_G726_MEDIA_32K,AUDIO_CODEC_G726_40K,AUDIO_CODEC_G726_32K,可以选以上几种编码格式。重要提示:1、由于采用海思芯片编码,每帧音频帧都会在标准音频帧前面增加4个字节的音频帧头信息,平台解码时需要去掉前面4个字节。2、G726编码格式含有多种压缩率格式,很多标准播放器的解码库只支持AUDIO_CODEC_G726_MEDIA_40K和AUDIO_CODEC_G726_MEDIA_32K两种编码格式,不支持AUDIO_CODEC_G726_40K和AUDIO_CODEC_G726_32K,虽然看上去压缩率一样,但是编码规则不一样,需要留意音频解码参数的配置。3、相对G726编码而言G711A编码则比较简单,调试时可以先选择这种编码格式。4、举例:采样率8K,采样精度16位,采样点:320原始音频数据码率:8K*16bit=128kpbs每秒音频帧数:8K/320=25帧比如选择AUDIO_CODEC_G726_40K编码,则每帧长度40Kbit/8bit/25=200字节长度,既每帧编码后的音频帧长度是200个字节,那么设备上传的就是204个字节,前面4个字节是海思音频编码私有帧头数据。
Speex转码成PCM也有需要注意的地方,因为根据上面我们厂商文档中说明,采样率是8K,采样点320,所以可以知道传输的每帧PCM字节数是320,对应的是Speex的窄带(160 sample,一个 sample 占两个字节,所以解码出的 PCM 刚好是 320 字节),那么我们在解码Speex为PCM的时候就应该选择窄带解码模式。
#region Using directivesusing System.Runtime.InteropServices;using System;// using System;// using System.IO;// using System.Collections.Generic;// using System.Text;// using System.Runtime.InteropServices;// using System.Diagnostics;#endregion Using directivesnamespace GpsNET.RTMP{public enum SpeexCtlCode {// Set enhancement on/off decoder only)SPEEX_SET_ENH=0,// Get enhancement state decoder only)SPEEX_GET_ENH=1,// Obtain frame size used by encoder/decoderSPEEX_GET_FRAME_SIZE=3,// Set quality valueSPEEX_SET_QUALITY=4,// Get current quality setting// SPEEX_GET_QUALITY=5 -- Doesn't make much sense, does it? */,// Set sub-mode to useSPEEX_SET_MODE=6,// Get current sub-mode in useSPEEX_GET_MODE=7,// Set low-band sub-mode to use wideband onlySPEEX_SET_LOW_MODE=8,// Get current low-band mode in use wideband onlySPEEX_GET_LOW_MODE=9,// Set high-band sub-mode to use wideband onlySPEEX_SET_HIGH_MODE=10,// Get current high-band mode in use wideband onlySPEEX_GET_HIGH_MODE=11,// Set VBR on 1) or off 0)SPEEX_SET_VBR=12,// Get VBR status 1 for on, 0 for off)SPEEX_GET_VBR=13,// Set quality value for VBR encoding 0-10)SPEEX_SET_VBR_QUALITY=14,// Get current quality value for VBR encoding 0-10)SPEEX_GET_VBR_QUALITY=15,// Set complexity of the encoder 0-10)SPEEX_SET_COMPLEXITY=16,// Get current complexity of the encoder 0-10)SPEEX_GET_COMPLEXITY=17,// Set bit-rate used by the encoder or lower)SPEEX_SET_BITRATE=18,// Get current bit-rate used by the encoder or decoderSPEEX_GET_BITRATE=19,// Define a handler function for in-band Speex requesSPEEX_SET_HANDLER=20,// Define a handler function for in-band user-defined requesSPEEX_SET_USER_HANDLER=22,// Set sampling rate used in bit-rate computationSPEEX_SET_SAMPLING_RATE=24,// Get sampling rate used in bit-rate computationSPEEX_GET_SAMPLING_RATE=25,// Reset the encoder/decoder memories to zerSPEEX_RESET_STATE=26,// Get VBR info mostly used internally)SPEEX_GET_RELATIVE_QUALITY=29,// Set VAD status 1 for on, 0 for off)SPEEX_SET_VAD=30,// Get VAD status 1 for on, 0 for off)SPEEX_GET_VAD=31,// Set Average Bit-Rate ABR) to n bits per secondsSPEEX_SET_ABR=32,// Get Average Bit-Rate ABR) setting in bps)SPEEX_GET_ABR=33,// Set DTX status 1 for on, 0 for off)SPEEX_SET_DTX=34,// Get DTX status 1 for on, 0 for off)SPEEX_GET_DTX=35,// Set submode encoding in each frame 1 for yes, 0 for no, setting to no breaks the standard)SPEEX_SET_SUBMODE_ENCODING=36,// Get submode encoding in each frameSPEEX_GET_SUBMODE_ENCODING=37,// SPEEX_SET_LOOKAHEAD=38,// Returns the lookahead used by SpeexSPEEX_GET_LOOKAHEAD=39,// Sets tuning for packet-loss concealment expected loss rate)SPEEX_SET_PLC_TUNING=40,// Gets tuning for PLCSPEEX_GET_PLC_TUNING=41,// Sets the max bit-rate allowed in VBR modeSPEEX_SET_VBR_MAX_BITRATE=42,// Gets the max bit-rate allowed in VBR modeSPEEX_GET_VBR_MAX_BITRATE=43,// Turn on/off input/output high-pass filteringSPEEX_SET_HIGHPASS=44,// Get status of input/output high-pass filteringSPEEX_GET_HIGHPASS=45,// Get "activity level" of the last decoded frame, i.e, // how much damage we cause if we remove the frameSPEEX_GET_ACTIVITY=47}// Preserving compatibility:public enum SpeexCompatCode {// Equivalent to SPEEX_SET_ENHSPEEX_SET_PF=0,// Equivalent to SPEEX_GET_ENHSPEEX_GET_PF=1}// Values allowed for mode queriespublic enum SpeexModeQuery {// Query the frame size of a modeSPEEX_MODE_FRAME_SIZE=0,// Query the size of an encoded frame for a particular sub-modeSPEEX_SUBMODE_BITS_PER_FRAME=1}public enum SpeexVersion {// Get major Speex versionSPEEX_LIB_GET_MAJOR_VERSION=1,// Get minor Speex versionSPEEX_LIB_GET_MINOR_VERSION=3,// Get micro Speex versionSPEEX_LIB_GET_MICRO_VERSION=5,// Get extra Speex versionSPEEX_LIB_GET_EXTRA_VERSION=7,// Get Speex version stringSPEEX_LIB_GET_VERSION_STRING=9,// SPEEX_LIB_SET_ALLOC_FUNC=10,// SPEEX_LIB_GET_ALLOC_FUNC=11,// SPEEX_LIB_SET_FREE_FUNC=12,// SPEEX_LIB_GET_FREE_FUNC=13,// SPEEX_LIB_SET_WARNING_FUNC=14,// SPEEX_LIB_GET_WARNING_FUNC=15,// SPEEX_LIB_SET_ERROR_FUNC=16,// SPEEX_LIB_GET_ERROR_FUNC=17,}// Modes supported by Speexpublic enum SpeexBand {// modeID for the defined narrowband modeSPEEX_MODEID_NB=0,// modeID for the defined wideband modeSPEEX_MODEID_WB=1,// modeID for the defined ultra-wideband modeSPEEX_MODEID_UWB=2,// Number of defined modes in SpeexSPEEX_NB_MODES=3}// Preprocessor control codespublic enum PreprocessCtlCode {// Set preprocessor denoiser stateSPEEX_PREPROCESS_SET_DENOISE=0,// Get preprocessor denoiser stateSPEEX_PREPROCESS_GET_DENOISE=1,// Set preprocessor Automatic Gain Control stateSPEEX_PREPROCESS_SET_AGC=2,// Get preprocessor Automatic Gain Control stateSPEEX_PREPROCESS_GET_AGC=3,// Set preprocessor Voice Activity Detection stateSPEEX_PREPROCESS_SET_VAD=4,// Get preprocessor Voice Activity Detection stateSPEEX_PREPROCESS_GET_VAD=5,// Set preprocessor Automatic Gain Control levelSPEEX_PREPROCESS_SET_AGC_LEVEL=6,// Get preprocessor Automatic Gain Control levelSPEEX_PREPROCESS_GET_AGC_LEVEL=7,// Set preprocessor dereverb stateSPEEX_PREPROCESS_SET_DEREVERB=8,// Get preprocessor dereverb stateSPEEX_PREPROCESS_GET_DEREVERB=9,// Set preprocessor dereverb levelSPEEX_PREPROCESS_SET_DEREVERB_LEVEL=10,// Get preprocessor dereverb levelSPEEX_PREPROCESS_GET_DEREVERB_LEVEL=11,// Set preprocessor dereverb decaySPEEX_PREPROCESS_SET_DEREVERB_DECAY=12,// Get preprocessor dereverb decaySPEEX_PREPROCESS_GET_DEREVERB_DECAY=13,// Set probability required for the VAD to go from silence to voiceSPEEX_PREPROCESS_SET_PROB_START=14,// Get probability required for the VAD to go from silence to voiceSPEEX_PREPROCESS_GET_PROB_START=15,// Set probability required for the VAD to stay in the voice state integer percent)SPEEX_PREPROCESS_SET_PROB_CONTINUE=16,// Get probability required for the VAD to stay in the voice state integer percent)SPEEX_PREPROCESS_GET_PROB_CONTINUE=17,// Set maximum attenuation of the noise in dB negative number)SPEEX_PREPROCESS_SET_NOISE_SUPPRESS=18,// Get maximum attenuation of the noise in dB negative number)SPEEX_PREPROCESS_GET_NOISE_SUPPRESS=19,// Set maximum attenuation of the residual echo in dB negative number)SPEEX_PREPROCESS_SET_ECHO_SUPPRESS=20,// Get maximum attenuation of the residual echo in dB negative number)SPEEX_PREPROCESS_GET_ECHO_SUPPRESS=21,// Set maximum attenuation of the residual echo in dB when near end is active negative number)SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE=22,// Get maximum attenuation of the residual echo in dB when near end is active negative number)SPEEX_PREPROCESS_GET_ECHO_SUPPRESS_ACTIVE=23,// Set the corresponding echo canceller state so that residual echo suppression can be performed NULL for no residual echo suppression)SPEEX_PREPROCESS_SET_ECHO_STATE=24,// Get the corresponding echo canceller stateSPEEX_PREPROCESS_GET_ECHO_STATE=25,// Set maximal gain increase in dB/second int32)SPEEX_PREPROCESS_SET_AGC_INCREMENT=26,// Get maximal gain increase in dB/second int32)SPEEX_PREPROCESS_GET_AGC_INCREMENT=27,// Set maximal gain decrease in dB/second int32)SPEEX_PREPROCESS_SET_AGC_DECREMENT=28,// Get maximal gain decrease in dB/second int32)SPEEX_PREPROCESS_GET_AGC_DECREMENT=29,// Set maximal gain in dB int32)SPEEX_PREPROCESS_SET_AGC_MAX_GAIN=30,// Get maximal gain in dB int32)SPEEX_PREPROCESS_GET_AGC_MAX_GAIN=31,// Can't set loudness// Get loudnessSPEEX_PREPROCESS_GET_AGC_LOUDNESS=33}public enum JitterBufferRetCode {// Packet has been retrievedJITTER_BUFFER_OK = 0,// Packet is lost or is lateJITTER_BUFFER_MISSING = 1,// A "fake" packet is meant to be inserted here to increase bufferingJITTER_BUFFER_INSERTION = 2,// There was an error in the jitter bufferJITTER_BUFFER_INTERNAL_ERROR = -1,// Invalid argumentJITTER_BUFFER_BAD_ARGUMENT = -2,}// Jitter Buffer Control Codespublic enum JitterBufferCtlCode {// Set minimum amount of extra buffering required margin)JITTER_BUFFER_SET_MARGIN = 0,// Get minimum amount of extra buffering required margin)JITTER_BUFFER_GET_MARGIN = 1,/* JITTER_BUFFER_SET_AVAILABLE_COUNT wouldn't make sense */// Get the amount of available packets currently bufferedJITTER_BUFFER_GET_AVAILABLE_COUNT = 3,// Included because of an early misspelling will remove in next release)JITTER_BUFFER_GET_AVALIABLE_COUNT = 3,// Assign a function to destroy unused packet. When setting// that, the jitter buffer no longer copies packet data.JITTER_BUFFER_SET_DESTROY_CALLBACK = 4,JITTER_BUFFER_GET_DESTROY_CALLBACK = 5,// Tell the jitter buffer to only adjust the delay in// multiples of the step parameter providedJITTER_BUFFER_SET_DELAY_STEP = 6,JITTER_BUFFER_GET_DELAY_STEP = 7,// Tell the jitter buffer to only do concealment in multiples of the size parameter providedJITTER_BUFFER_SET_CONCEALMENT_SIZE = 8,JITTER_BUFFER_GET_CONCEALMENT_SIZE = 9,// Absolute max amount of loss that can be tolerated// regardless of the delay. Typical loss should be half of// that or less.JITTER_BUFFER_SET_MAX_LATE_RATE = 10,JITTER_BUFFER_GET_MAX_LATE_RATE = 11,// Equivalent cost of one percent late packet in timestamp unitsJITTER_BUFFER_SET_LATE_COST = 12,JITTER_BUFFER_GET_LATE_COST = 13}public unsafe class SpeexCodec {public struct SpeexBits{char* chars; /* "raw" data */int nbBits; /* Total number of bits stored in the stream*/int charPtr; /* Position of the byte "cursor" */int bitPtr; /* Position of the bit "cursor" within the current char */int owner; /* Does the struct "own" the "raw" buffer member "chars") */int overflow; /* Set to one if we try to read past the valid data */int buf_size; /* Allocated size for buffer */int reserved1; /* Reserved for future use */void* reserved2; /* Reserved for future use */}public struct JitterBuffer {}public struct JitterBufferPacket {public byte *data; /* Data bytes contained in the packet */public uint len; /* Length of the packet in bytes */public uint timestamp; /* Timestamp for the packet */public uint span; /* Time covered by the packet timestamp units) */}public struct SpeexPreprocessState {}// EXPORTED ENCODER METHODS[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void *speex_encoder_init_newint modeID);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_encoder_ctlvoid *state, int request, void *ptr);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_encode_intvoid *state, short *input, SpeexBits *bits); // IntPtr[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_encoder_destroyvoid* state);// EXPORTED ENCODER BIT-OPERATION METHODS[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_bits_writeSpeexBits *bits, byte *bytes, int max_len); // char *[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_bits_write_whole_bytesSpeexBits *bits, byte *bytes, int max_len); // char *// EXPORTED DECODER METHODS[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void* speex_decoder_init_newint modeID);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_decoder_ctlvoid *state, int request, void *ptr);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_bits_read_fromSpeexBits *bits, byte *inputBuffer, int inputByteCount);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_decode_intvoid* state, SpeexBits *bits, short* output); // IntPtr[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_decoder_destroyvoid* state);// Preprocessor API[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern SpeexPreprocessState *speex_preprocess_state_initint frame_size, int sampling_rate);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void speex_preprocess_state_destroySpeexPreprocessState *st);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_preprocess_runSpeexPreprocessState *st, short *x);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void speex_preprocess_estimate_updateSpeexPreprocessState *st, short *x);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_preprocess_ctlSpeexPreprocessState *st, int request, void *ptr); // Jitter Buffer API[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern JitterBuffer *jitter_buffer_initint step_size);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void jitter_buffer_resetJitterBuffer *jitter);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void jitter_buffer_destroyJitterBuffer *jitter);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void jitter_buffer_putJitterBuffer *jitter, JitterBufferPacket *packet);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int jitter_buffer_getJitterBuffer *jitter, JitterBufferPacket *packet, int desired_span, int *start_offset);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int jitter_buffer_get_anotherJitterBuffer *jitter, JitterBufferPacket *packet);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int jitter_buffer_get_pointer_timestampJitterBuffer *jitter);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void jitter_buffer_tickJitterBuffer *jitter);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void jitter_buffer_remaining_spanJitterBuffer *jitter, uint rem);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int jitter_buffer_ctlJitterBuffer *jitter, int request, void *ptr);[DllImport"libspeexdsp.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int jitter_buffer_update_delayJitterBuffer *jitter, JitterBufferPacket *packet, int *start_offset);// Utility methods[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern void speex_bits_initSpeexBits* bits);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_bits_resetSpeexBits* bits);[DllImport"libspeex.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]public static extern int speex_bits_destroySpeexBits* bits);// SpeexCodec data membersprivate int frameSize; private int maxFrameSize;private void *encoderState;private SpeexBits encodedBits;private SpeexPreprocessState *preprocessState;private bool validJitterBits = false;private JitterBuffer *jitterBuffer = null;private byte[] encodedJitterFrame = new byte[2048];private int encodedJitterFrameLength;private int encodedJitterFrameErrorCode;// These are just used for loggingpublic byte[] EncodedJitterFrame {get {return encodedJitterFrame;}}public int EncodedJitterFrameLength {get {return encodedJitterFrameLength;}}public int EncodedJitterFrameErrorCode {get {return encodedJitterFrameErrorCode;}}// Provide something to lockprivate class JitterBufferLockable {}private JitterBufferLockable jitterBufferLockable = new JitterBufferLockable);void *decoderState;SpeexBits decodedBits;public int SetOneCodecSettingbool encoder, SpeexCtlCode setting, int value) {int retcode = 0;unsafe {int *intPtr = &value;if encoder)retcode = speex_encoder_ctlencoderState, int)setting, void *)intPtr);elseretcode = speex_decoder_ctldecoderState, int)setting, void *)intPtr);}return retcode;}public int GetOneCodecSettingbool encoder, SpeexCtlCode setting, ref int value) {int retcode = 0;unsafe {fixed int *intPtr = &value) {if encoder)retcode = speex_encoder_ctlencoderState, int)setting, void *)intPtr);elseretcode = speex_decoder_ctldecoderState, int)setting, void *)intPtr);}}return retcode;}public int SetOnePreprocessorSettingPreprocessCtlCode setting, int value) {int retcode = 0;unsafe {int *intPtr = &value;retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)intPtr);}return retcode;}public int SetOnePreprocessorSettingPreprocessCtlCode setting, float value) {int retcode = 0;unsafe {float *floatPtr = &value;retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)floatPtr);}return retcode;}public int SetOnePreprocessorSettingPreprocessCtlCode setting, bool bValue) {int value = bValue ? 1 : 0);int retcode = 0;unsafe {int *intPtr = &value;retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)intPtr);}return retcode;}public int GetOnePreprocessorSettingPreprocessCtlCode setting, ref int value) {int retcode = 0;unsafe {fixed int *intPtr = &value) {retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)intPtr);}}return retcode;}public int GetOnePreprocessorSettingPreprocessCtlCode setting, ref float value) {int retcode = 0;unsafe {fixed float *floatPtr = &value) {retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)floatPtr);}}return retcode;}public int GetOnePreprocessorSettingPreprocessCtlCode setting, ref bool value) {int retcode = 0;int intValue = 0;unsafe {retcode = speex_preprocess_ctlpreprocessState, int)setting, void *)&intValue);}value = intValue == 0 ? false : true);return retcode;}public int SetOneJitterBufferSettingJitterBufferCtlCode setting, int value) {int retcode = 0;unsafe {int *intPtr = &value;retcode = jitter_buffer_ctljitterBuffer, int)setting, void *)intPtr);}return retcode;}public int InitEncoderint maxFrameSize, int samplesPerFrame, int samplingRate) {this.maxFrameSize = maxFrameSize;encodedBits = new SpeexBits);encoderState = speex_encoder_init_new0);// Don't set VAD in the codec, because we're setting it in// the preprocessor insteadfixed int *fSize = &frameSize) {speex_encoder_ctlencoderState, int)SpeexCtlCode.SPEEX_GET_FRAME_SIZE, fSize);}fixed SpeexBits *bitsAdd = &encodedBits) {speex_bits_initbitsAdd);}preprocessState = speex_preprocess_state_initsamplesPerFrame, samplingRate);return frameSize;}public int PreprocessFrameshort[] sampleBuffer) {fixed short *fixedSamples = sampleBuffer) {return speex_preprocess_runpreprocessState, fixedSamples);}}public int EncodeFrameshort[] inputFrame, byte[] outputFrame) {int encodedDataSize = 0;fixed short *inputAdd = inputFrame) {fixed SpeexBits *bitsAdd = &encodedBits) {speex_encode_intencoderState, inputAdd, bitsAdd);fixed byte* outputBytes = outputFrame) {// encodedDataSize = speex_bits_write_whole_bytesbitsAdd, outputBytes, maxFrameSize);encodedDataSize = speex_bits_writebitsAdd, outputBytes, maxFrameSize);}}}fixed SpeexBits *bitsAdd = &encodedBits) {speex_bits_resetbitsAdd);}return encodedDataSize;}public void ResetEncoder) {fixed SpeexBits *bitsToAdd = &encodedBits) {speex_bits_destroybitsToAdd);}if encoderState != null) {speex_encoder_destroyencoderState);encoderState = null;}}public void InitDecoderbool useJitterBuffer, int stepSize, int frameSize) {this.frameSize = frameSize;decodedBits = new SpeexBits);decoderState = speex_decoder_init_new0);fixed SpeexBits *bitsDecode = &decodedBits){speex_bits_initbitsDecode);}if useJitterBuffer) {jitterBuffer = jitter_buffer_initstepSize);validJitterBits = false;}}const int FrameSize = 320;private static log4net.ILog logger = log4net.LogManager.GetLoggertypeofSpeexCodec));public int DecodeFramebyte[] inputToDecode, int encodedByteCount, short[] decodedFrame){DecoderReadFrominputToDecode, encodedByteCount);return DecoderDecodeBitsdecodedFrame);}public void DecoderReadFrombyte[] inputToDecode, int encodedByteCount) {fixed SpeexBits *bitsDecoder = &decodedBits) {fixed byte *inputFrame = inputToDecode) {speex_bits_read_frombitsDecoder, inputFrame, encodedByteCount);}}}public int DecoderDecodeBitsshort[] decodedFrame){fixed SpeexBits* bitsDecoder = &decodedBits){fixed short* outputFrame = decodedFrame){return speex_decode_intdecoderState, bitsDecoder, outputFrame);}}}public int DecoderDecodeNullBitsshort[] decodedFrame){fixed short* outputFrame = decodedFrame){return speex_decode_intdecoderState, null, outputFrame);}}public void ResetDecoder){fixed SpeexBits* bitsDecoder = &decodedBits){speex_bits_destroybitsDecoder);}if decoderState != null){speex_decoder_destroydecoderState);decoderState = null;}if jitterBuffer != null){jitter_buffer_destroyjitterBuffer);jitterBuffer = null;}}// Jitter buffer wrapper API// Locking must be done at the application level to ensure// that two threads can't be in jitter buffer methods. // timestamp is a counter incremented once per "tick"public void JitterBufferPutbyte[] frame, int startIndex, uint byteCount, uint timestamp){if jitterBuffer == null)throw new Exception"JitterBufferPut: jitterBuffer is null!");lock jitterBufferLockable){JitterBufferPacket p = new JitterBufferPacket);unsafe{fixed byte* frameBytes = &frame[startIndex]){p.data = frameBytes;p.len = byteCount;p.timestamp = timestamp;p.span = uint)frameSize;jitter_buffer_putjitterBuffer, &p);}}}}// Returns the length of the _encoded_ frame in bytespublic void JitterBufferGetshort[] decodedFrame, uint timestamp, ref int startOffset){int i;int ret;int activity = 0;if jitterBuffer == null)throw new Exception"JitterBufferPut: jitterBuffer is null!");lock jitterBufferLockable){if validJitterBits){// Try decoding last received packetret = DecoderDecodeBitsdecodedFrame);if ret == 0){jitter_buffer_tickjitterBuffer);return;}elsevalidJitterBits = false;}JitterBufferPacket packet = new JitterBufferPacket);packet.span = uint)frameSize;packet.timestamp = timestamp;// The encoded buffer must be fixed, because// jitter_buffer_get refers to it through packetunsafe{fixed byte* pData = &encodedJitterFrame[0]){fixed int* pStartOffset = &startOffset){packet.data = pData;packet.span = uint)frameSize;packet.len = 2048;ret = jitter_buffer_getjitterBuffer, &packet, frameSize, pStartOffset);}}}encodedJitterFrameErrorCode = ret;if ret != int)JitterBufferRetCode.JITTER_BUFFER_OK){// No packet found: Packet is late or lostDecoderDecodeNullBitsdecodedFrame);}else{encodedJitterFrameLength = int)packet.len;DecoderReadFromencodedJitterFrame, encodedJitterFrameLength);/* Decode packet */ret = DecoderDecodeBitsdecodedFrame);if ret == 0)validJitterBits = true;else{/* Error while decoding */for i = 0; i < frameSize; i++)decodedFrame[i] = 0;}}GetOneCodecSettingfalse, SpeexCtlCode.SPEEX_GET_ACTIVITY, ref activity);if activity < 30)jitter_buffer_update_delayjitterBuffer, &packet, null);jitter_buffer_tickjitterBuffer);}}}}
在jt1078里获取设备流,生成http链接在浏览器访问
但其中涉及到的音频编解码知识是以前都没接触过的,所以踩了很多的坑。
需要学习的知识:
-
G726 <-> PCM <-> AAC 之间的编解码
-
RTMP 中 Speex 音频流数据格式的解析
-
flash ActionScript 编程
-
librtmp 库的使用(拉取 RTMP 中的音频流),这又涉及到 C语言 编程(依赖的openssl、zlib库的编译配置比较繁琐)
一起沟通交流