日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Android手機(jī)通過(guò)rtp發(fā)送aac數(shù)據(jù)給vlc播放的實(shí)現(xiàn)步驟

瀏覽:21日期:2022-09-19 14:24:52
截屏

Android手機(jī)通過(guò)rtp發(fā)送aac數(shù)據(jù)給vlc播放的實(shí)現(xiàn)步驟

AudioRecord音頻采集

private val sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) private val channelCount = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) private val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, if (channelCount == 1) CHANNEL_IN_MONO else CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);runInBackground { audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,if (channelCount == 1) CHANNEL_IN_MONO else CHANNEL_IN_STEREO,AudioFormat.ENCODING_PCM_16BIT,2 * minBufferSize ) audioRecord.startRecording()}

音頻采集時(shí)需要設(shè)置采集參數(shù),設(shè)置的這些參數(shù)需要與創(chuàng)建MediaCodec時(shí)的參數(shù)一致。

sampleRate是采樣率:44100 channelCount是通道數(shù):1 單個(gè)采樣數(shù)據(jù)大小格式:AudioFormat.ENCODING_PCM_16BIT 最小數(shù)據(jù)buffer:AudioRecord.getMinBufferSize()計(jì)算獲取

override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {try { codec.getInputBuffer(index)?.let { bb ->var startTime = System.currentTimeMillis();var readSize = audioRecord.read(bb, bb.capacity())log { 'read time ${System.currentTimeMillis() - startTime} read size $readSize' }if (readSize < 0) { readSize = 0}codec.queueInputBuffer(index, 0, readSize, System.nanoTime() / 1000, 0) }}catch (e:Exception){ e.printStackTrace()} }

這里采用的阻塞的方式采集數(shù)據(jù),所以AudioRecord依據(jù)設(shè)置的采樣頻率生成數(shù)據(jù)的,我們可以直接把當(dāng)前的時(shí)間設(shè)置為錄制的時(shí)間戳。

MediaCodec編碼音頻數(shù)據(jù)

val mediaFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,audioSampleRate,audioChannelCount)mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, audioBitRate)mediaFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,MediaCodecInfo.CodecProfileLevel.AACObjectLC)mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, audioMaxBufferSize)

為MediaCodec創(chuàng)建MediaFormat并設(shè)置參數(shù),這里設(shè)置的音頻參數(shù)必須與AudioRecord一致。

MIME_TYPE:'audio/mp4a-latm' 采樣頻率與AudioRecord一致:44100 通道數(shù)與AudioRecord一致:1 KEY_AAC_PROFILE配置為低帶寬要求類型:AACObjectLC KEY_BIT_RATE設(shè)置的大小影響編碼壓縮率:128 * 1024

override fun onInputBufferAvailable(codec: MediaCodec, index: Int) {try { codec.getInputBuffer(index)?.let { bb ->var startTime = System.currentTimeMillis();var readSize = audioRecord.read(bb, bb.capacity())log { 'read time ${System.currentTimeMillis() - startTime} read size $readSize' }if (readSize < 0) { readSize = 0}codec.queueInputBuffer(index, 0, readSize, System.nanoTime() / 1000, 0) }}catch (e:Exception){ e.printStackTrace()} }

給MediaCodec傳數(shù)據(jù)的時(shí)候設(shè)置的時(shí)間戳是當(dāng)前的系統(tǒng)時(shí)間,由于我們使用rtp發(fā)送實(shí)時(shí)數(shù)據(jù),所以flag不需要設(shè)置結(jié)束標(biāo)志。

audioCodec = object : AudioEncodeCodec(mediaFormat) { override fun onOutputBufferAvailable( codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo ) {try { val buffer = codec.getOutputBuffer(index) ?: return if (lastSendAudioTime == 0L) {lastSendAudioTime = info.presentationTimeUs; } val increase = (info.presentationTimeUs - lastSendAudioTime) * audioSampleRate / 1000 / 1000 if (hasAuHeader) {buffer.position(info.offset)buffer.get(bufferArray, 4, info.size)auHeaderLength.apply { bufferArray[0] = this[0] bufferArray[1] = this[1]}auHeader(info.size).apply { bufferArray[2] = this[0] bufferArray[3] = this[1]}audioRtpWrapper?.sendData(bufferArray, info.size + 4, 97, true, increase.toInt()) } else {buffer.position(info.offset)buffer.get(bufferArray, 0, info.size)audioRtpWrapper?.sendData(bufferArray, info.size, 97, true, increase.toInt()) } lastSendAudioTime = info.presentationTimeUs codec.releaseOutputBuffer(index, false)} catch (e: Exception) { e.printStackTrace()} } }

從MediaCodec讀出的是aac原始的數(shù)據(jù),我們可以根據(jù)具體的需求來(lái)決定是否添加au header發(fā)送。這里實(shí)現(xiàn)了有au header和沒(méi)有 au header兩種方案。沒(méi)有au header的情況我們直接把MediaCode讀出的數(shù)據(jù)通過(guò)rtp發(fā)送出去。有au header的情況我們需要在原始的aac數(shù)據(jù)前面追加4個(gè)字節(jié)的au header。是否有au header與vlc播放的sdp內(nèi)容有關(guān)。后面會(huì)詳解介紹sdp內(nèi)容的設(shè)置。

private val auHeaderLength = ByteArray(2).apply {this[0] = 0this[1] = 0x10 } private fun auHeader(len: Int): ByteArray {return ByteArray(2).apply { this[0] = (len and 0x1fe0 shr 5).toByte() this[1] = (len and 0x1f shl 3).toByte()} } au header length占用兩個(gè)字節(jié),它會(huì)描述au header的大小,這里設(shè)置為2. au header 占用兩個(gè)字節(jié),它描述了aac原始數(shù)據(jù)的大小,這里需要根據(jù)MediaCodec返回的aac原始數(shù)據(jù)大小進(jìn)行設(shè)置。 Rtp發(fā)送數(shù)據(jù)

我們使用jrtplib庫(kù)來(lái)發(fā)送數(shù)據(jù),這里對(duì)庫(kù)進(jìn)行簡(jiǎn)單的封裝并提供了java封裝類RtpWrapper。

public class RtpWrapper { private long nativeObject = 0; private IDataCallback callback; public RtpWrapper() {init(); } @Override protected void finalize() throws Throwable {release();super.finalize(); } public void setCallback(IDataCallback callback) {this.callback = callback; } void receivedData(byte[] buffer, int len) {if(this.callback != null)this.callback.onReceivedData(buffer, len); } public interface IDataCallback {void onReceivedData(byte[] buffer, int len); } static {try { System.loadLibrary('rtp-lib'); initLib();} catch (Throwable e) { e.printStackTrace();} } private native static void initLib(); private native boolean init(); private native boolean release(); public native boolean open(int port, int payloadType, int sampleRate); public native boolean close(); /** * @param ip '192.168.1.1' * @return */ public native boolean addDestinationIp(String ip); public native int sendData(byte[] buffer, int len, int payloadType, boolean mark, int increase);}

open方法要指定發(fā)送數(shù)據(jù)使用的端口,payloadType設(shè)置載體類型,sampleRate是采樣率。addDestinationIp用于添加接收端ip地址,地址格式: '192.168.1.1'。sendData方法用于發(fā)送數(shù)據(jù),increase是時(shí)間間隔,時(shí)間單位是 sampleRate/秒

override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) {audioRtpWrapper = RtpWrapper()audioRtpWrapper?.open(audioRtpPort, audioPayloadType, audioSampleRate)audioRtpWrapper?.addDestinationIp(ip) }

MediaCodec返回format的時(shí)候創(chuàng)建rtp連接并指定目的地址。

try { val buffer = codec.getOutputBuffer(index) ?: return if (lastSendAudioTime == 0L) {lastSendAudioTime = info.presentationTimeUs; } val increase = (info.presentationTimeUs - lastSendAudioTime) * audioSampleRate / 1000 / 1000 if (hasAuHeader) {buffer.position(info.offset)buffer.get(bufferArray, 4, info.size)auHeaderLength.apply { bufferArray[0] = this[0] bufferArray[1] = this[1]}auHeader(info.size).apply { bufferArray[2] = this[0] bufferArray[3] = this[1]}audioRtpWrapper?.sendData(bufferArray, info.size + 4, 97, true, increase.toInt()) } else {buffer.position(info.offset)buffer.get(bufferArray, 0, info.size)audioRtpWrapper?.sendData(bufferArray, info.size, 97, true, increase.toInt()) } lastSendAudioTime = info.presentationTimeUs codec.releaseOutputBuffer(index, false)} catch (e: Exception) { e.printStackTrace()}

發(fā)送數(shù)據(jù)的時(shí)候需要指定payloadType,距離上次發(fā)送數(shù)據(jù)的時(shí)間間隔等信息。(info.presentationTimeUs - lastSendAudioTime)計(jì)算的是以微妙為單位的時(shí)間間隔。(info.presentationTimeUs - lastSendAudioTime) * audioSampleRate / 1000 / 1000轉(zhuǎn)換成sampleRate/秒為單位的時(shí)間間隔。rtp發(fā)送aac數(shù)據(jù)使用的payloadType為97。

SDP文件配置

vlc播放器播放rtp音頻數(shù)據(jù)時(shí)需要指定sdp文件,它通過(guò)讀取sdp文件中的信息可以了解rpt接收端口、payloadType類型、音頻的格式等信息用于接收數(shù)據(jù)流并解碼播放。這里有兩種配置方式用于支持有au header和沒(méi)有au header的情況。

有au header

m=audio 40020 RTP/AVP 97a=rtpmap:97 mpeg4-generic/44100/1a=fmtp: 97 streamtype=5;config=1208;sizeLength=13; indexLength=3 沒(méi)有au header

m=audio 40020 RTP/AVP 97a=rtpmap:97 mpeg4-generic/44100/1a=fmtp: 97 streamtype=5;config=1208

sdp文件配置了端口號(hào)為40020, Rtp payload type為97,音頻的采樣率為44100、通道數(shù)為1。

音頻config配置計(jì)算方式:

Android手機(jī)通過(guò)rtp發(fā)送aac數(shù)據(jù)給vlc播放的實(shí)現(xiàn)步驟

比較有au header和沒(méi)有au header的兩個(gè)版本,發(fā)現(xiàn)它們的區(qū)別在于是否配置了sizeLength和indexLength。

我這里的au header是兩個(gè)字節(jié)的,sizeLength為13代表占用了13bit,indexLength為3代表占用3bit。配合發(fā)送數(shù)據(jù)時(shí)添加au header的代碼就容易理解了。

private fun auHeader(len: Int): ByteArray {return ByteArray(2).apply { this[0] = (len and 0x1fe0 shr 5).toByte() this[1] = (len and 0x1f shl 3).toByte()} }vlc測(cè)試播放 vlc打開(kāi)工程目錄下的play_audio.sdp/play_audio_auheader.sdp 。 啟動(dòng)Android應(yīng)用指定運(yùn)行vlc的電腦的ip地址。 開(kāi)始錄制,如何vlc打開(kāi)的是play_audio_auheader.sdp,那么在開(kāi)始錄制前需要選中auHeader check box 總結(jié) AudioRecord的設(shè)置信息與MediaCodec的配置信息必須一致。 AudioRecord采用block的方式讀取數(shù)據(jù),這樣我們可以直接使用系統(tǒng)時(shí)間來(lái)配置encode時(shí)間戳。 是否需要添加au header與sdp配置有關(guān),vlc播放器會(huì)按照sdp配置解析au header。 sdp中的config需要按照實(shí)際的音頻配置信息計(jì)算得出,否則不能正常播放。 工程git地址

https://github.com/mjlong123123/AudioRecorder

以上就是Android手機(jī)通過(guò)rtp發(fā)送aac數(shù)據(jù)給vlc播放的實(shí)現(xiàn)步驟的詳細(xì)內(nèi)容,更多關(guān)于Android rtp發(fā)送aac數(shù)據(jù)給vlc播放的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Android
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
在线日韩av| 亚洲精品看片| 欧美aa国产视频| 国产亚洲一区在线| 国产视频亚洲| 亚洲精品韩国| 你懂的国产精品永久在线| 国产精品一在线观看| 日日摸夜夜添夜夜添国产精品| 日韩国产精品久久久| 国产精品一在线观看| 日韩成人亚洲| 尤物在线精品| 欧美私人啪啪vps| 国产欧洲在线| 久久国产精品毛片| 久久精品国产99| 在线精品视频在线观看高清| 蜜臀va亚洲va欧美va天堂 | 麻豆理论在线观看| 久久国产电影| 欧美综合精品| 午夜国产精品视频免费体验区| 欧美在线黄色| 99久久精品费精品国产| 亚洲精品护士| 国产精品国产三级国产在线观看| 性一交一乱一区二区洋洋av| 国产日韩1区| 伊人影院久久| 国产粉嫩在线观看| 欧美日韩精品一区二区三区视频 | 国产精品v一区二区三区| 欧美一区三区| 国产精品久久久久久av公交车| 好看的av在线不卡观看| 日本一区二区免费高清| 日韩国产欧美三级| 欧美专区一区二区三区| 日韩免费av| 国产精品高清一区二区| 热久久国产精品| 极品日韩av| 国产精品亲子伦av一区二区三区| 亚洲综合不卡| 久久久精品久久久久久96| 久久91视频| 麻豆中文一区二区| 久久国产麻豆精品| 亚洲美女91| 午夜在线精品偷拍| 亚洲精品一区二区妖精| 999国产精品| 国产 日韩 欧美一区| 98精品视频| 亚洲三级欧美| 国产精品videossex久久发布| 欧美久久精品| 亚洲天堂日韩在线| 久久亚洲色图| 亚洲综合中文| 日本91福利区| 欧美精品91| 精品国产不卡一区二区| 欧美好骚综合网| 日韩三区免费| 久久午夜影视| 婷婷五月色综合香五月| 国产精品亚洲二区| 精品国产午夜肉伦伦影院| 欧美三区四区| 丝袜a∨在线一区二区三区不卡| 日本在线观看不卡视频| 天堂久久一区| 在线亚洲精品| 久久gogo国模啪啪裸体| 日韩精品永久网址| 亚洲aⅴ网站| 91精品韩国| 99日韩精品| 国产精品色婷婷在线观看| 六月婷婷综合| 美国三级日本三级久久99 | 国产在线日韩| 午夜亚洲福利| 日韩1区2区| 日本亚洲最大的色成网站www| 国产一区不卡| 亚洲精品大全| 国产精区一区二区| 国产视频一区免费看| 国产日韩视频| 亚洲一区二区三区四区电影| 成午夜精品一区二区三区软件| 日韩一区精品字幕| 美女久久精品| 在线观看视频免费一区二区三区| 日本中文字幕视频一区| 丝袜美腿一区| 人人精品久久| 欧产日产国产精品视频| 国产一级成人av| 尤物在线精品| 四虎4545www国产精品 | 亚洲色图网站| 日韩精品欧美| 欧美国产不卡| 亚洲一区二区三区在线免费| 国产一区二区三区精品在线观看| 亚洲在线免费| 青青久久av| 免费在线观看一区| 三级在线观看一区二区| 欧美国产美女| 国产精品久久久久久久久免费高清 | 欧美另类专区| 欧洲亚洲一区二区三区| 精品黄色一级片| 69精品国产久热在线观看| 亚洲一区二区三区高清不卡| 在线一区av| 美女视频网站久久| 欧美一区在线观看视频| 日韩在线观看中文字幕| 亚洲欧洲另类| 激情婷婷综合| 国产在线观看www| 美女国产一区二区三区| 国产欧美亚洲一区| 欧美一区=区三区| 国产日韩在线观看视频| 国产精品三级| 麻豆国产精品一区二区三区| 久久精品99国产精品| 欧美午夜三级| 国产精品探花在线观看| 国产精品a级| 国产一区二区三区视频在线| 精品久久网站| 日韩高清成人| 午夜精品网站| 免费一级片91| 亚洲影视一区二区三区| 国产日韩亚洲| 亚洲国产成人二区| 成人免费电影网址| 久久最新视频| 国产精品一二| 四虎影视精品| 亚洲天堂免费| 精品一区二区三区的国产在线观看| 麻豆精品视频在线观看免费| 伊人久久在线| 美女国产精品| 欧美日韩亚洲三区| 最近高清中文在线字幕在线观看1| 亚洲h色精品| 日本亚洲欧美天堂免费| 精品在线网站观看| 激情久久五月| 欧美一级网址| 夜夜嗨av一区二区三区网站四季av| 青草久久视频| 久久久精品五月天| 日韩欧美中文字幕一区二区三区| 欧美激情视频一区二区三区免费| 99精品美女| 国产精品入口久久| 中文在线不卡| 天堂√中文最新版在线| 91亚洲精品视频在线观看| 亚洲v在线看| 日本色综合中文字幕| 免费av一区| 成人国产精品一区二区免费麻豆| 婷婷成人av| 在线看片一区| 在线视频免费在线观看一区二区| 精品一区电影| 日韩av中文字幕一区二区| 免费av一区二区三区四区| 国产成人在线中文字幕| 国产欧美自拍| 亚洲三级网站| 在线 亚洲欧美在线综合一区| 成人小电影网站| 色婷婷亚洲mv天堂mv在影片| 91精品国产自产观看在线| 亚洲一区二区小说| 西西人体一区二区| 日韩亚洲在线| 欧美日韩视频一区二区三区| 日韩欧美中文| 国产精品蜜芽在线观看| 久久精品九色| 麻豆精品国产91久久久久久| 久久国产日韩欧美精品| 91精品日本| 久久丁香四色| 久久99国产精品视频|