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

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

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

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

Android手機(jī)通過rtp發(fā)送aac數(shù)據(jù)給vlc播放的實現(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ù)需要與創(chuàng)建MediaCodec時的參數(shù)一致。

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

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è)置為錄制的時間戳。

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è)置的時間戳是當(dāng)前的系統(tǒng)時間,由于我們使用rtp發(fā)送實時數(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ù)具體的需求來決定是否添加au header發(fā)送。這里實現(xiàn)了有au header和沒有 au header兩種方案。沒有au header的情況我們直接把MediaCode讀出的數(shù)據(jù)通過rtp發(fā)送出去。有au header的情況我們需要在原始的aac數(shù)據(jù)前面追加4個字節(jié)的au header。是否有au header與vlc播放的sdp內(nèi)容有關(guān)。后面會詳解介紹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占用兩個字節(jié),它會描述au header的大小,這里設(shè)置為2. au header 占用兩個字節(jié),它描述了aac原始數(shù)據(jù)的大小,這里需要根據(jù)MediaCodec返回的aac原始數(shù)據(jù)大小進(jìn)行設(shè)置。 Rtp發(fā)送數(shù)據(jù)

我們使用jrtplib庫來發(fā)送數(shù)據(jù),這里對庫進(jì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是時間間隔,時間單位是 sampleRate/秒

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

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

SDP文件配置

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

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

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

音頻config配置計算方式:

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

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

我這里的au header是兩個字節(jié)的,sizeLength為13代表占用了13bit,indexLength為3代表占用3bit。配合發(fā)送數(shù)據(jù)時添加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測試播放 vlc打開工程目錄下的play_audio.sdp/play_audio_auheader.sdp 。 啟動Android應(yīng)用指定運行vlc的電腦的ip地址。 開始錄制,如何vlc打開的是play_audio_auheader.sdp,那么在開始錄制前需要選中auHeader check box 總結(jié) AudioRecord的設(shè)置信息與MediaCodec的配置信息必須一致。 AudioRecord采用block的方式讀取數(shù)據(jù),這樣我們可以直接使用系統(tǒng)時間來配置encode時間戳。 是否需要添加au header與sdp配置有關(guān),vlc播放器會按照sdp配置解析au header。 sdp中的config需要按照實際的音頻配置信息計算得出,否則不能正常播放。 工程git地址

https://github.com/mjlong123123/AudioRecorder

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

標(biāo)簽: Android
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
国产精品男女| 国产精品二区不卡| 日韩中文字幕无砖| 久久亚洲专区| 久久尤物视频| 国产亚洲高清在线观看| 麻豆精品91| 亚洲国产一区二区在线观看 | 日本在线不卡视频| 伊人久久大香线蕉av超碰演员| av在线资源| 久久精品国产久精国产爱| 午夜天堂精品久久久久| 老牛影视一区二区三区| 精品久久不卡| 精品中文在线| 国产精品夜夜夜| 国产日韩欧美一区在线| 石原莉奈一区二区三区在线观看| 美女少妇全过程你懂的久久| 欧美亚洲国产激情| 视频在线不卡免费观看| 国产999精品在线观看| 精品一区二区三区免费看 | 中文一区一区三区高中清不卡免费| 国产精品v亚洲精品v日韩精品| 免费在线观看一区二区三区| 99xxxx成人网| 免费看欧美美女黄的网站| 日韩精品一级中文字幕精品视频免费观看 | 99精品视频精品精品视频| 麻豆视频在线观看免费网站黄| 都市激情国产精品| 日韩久久一区二区三区| 99成人在线| 国产欧美一区二区三区精品观看| 国产精品麻豆成人av电影艾秋 | 99久久久久国产精品| 影音国产精品| 国产色99精品9i| 日韩国产欧美| 日韩影院免费视频| 国产精品99久久免费观看| 天堂√8在线中文| 亚洲一区欧美二区| 日韩av网站在线观看| 成人午夜在线| 国产一区导航| 美女性感视频久久| 黄色在线一区| 鲁大师精品99久久久| 蜜臀久久99精品久久一区二区| 欧美日韩亚洲一区在线观看| 色婷婷精品视频| 日韩av网站在线免费观看| 日韩影院二区| 日本成人手机在线| 日韩一区二区三区免费| 日韩精品亚洲专区| 国产高潮在线| 天堂va在线高清一区| 麻豆传媒一区二区三区| 亚洲一区网站| 91亚洲人成网污www| 日本伊人久久| 久久国产中文字幕| 国产日韩视频| 国产精品美女久久久| 精品香蕉视频| 日本不卡一二三区黄网| 国产综合精品一区| 久久久久久亚洲精品美女| 免费欧美在线视频| 99久久九九| 精品久久不卡| 欧美视频一区| 尤物精品在线| 中文字幕在线高清| 欧美日韩xxxx| 天堂成人免费av电影一区| 九九九精品视频| 91精品国产自产观看在线| 免费观看不卡av| 国产中文欧美日韩在线| 日本在线观看不卡视频| 91精品精品| 久久精品五月| 7777精品| 日韩一区精品视频| 亚洲一级二级| 97国产精品| 欧美一级二区| 免费精品视频在线| 久久国产影院| 国产精品久久久久久久免费观看| 欧美色综合网| 视频一区在线播放| 欧美aa国产视频| 蜜桃视频在线网站| 欧美交a欧美精品喷水| 日本不卡视频在线观看| 香蕉精品视频在线观看| 韩国久久久久久| 久久av影视| 国产日产精品_国产精品毛片| 在线观看一区| 欧美特黄a级高清免费大片a级| 中文字幕在线看片| 国产精品一线天粉嫩av| 日韩精品导航| 亚洲精品系列| 亚洲一区二区小说| 日韩av中文字幕一区二区| 日韩综合小视频| 日韩午夜精品| 亚洲2区在线| 午夜在线视频观看日韩17c| 亚洲天堂一区二区| 国产资源在线观看入口av| 国产精品亚洲产品| 国产免费av国片精品草莓男男| 在线亚洲欧美| 日韩一区二区免费看| 激情综合自拍| 极品av在线| 国产成人黄色| 国产成人精品999在线观看| 精品国产乱码久久久| 国产伦精品一区二区三区在线播放| 涩涩涩久久久成人精品| 一区二区三区四区精品视频| 亚洲综合福利| 婷婷久久免费视频| 欧美一区二区三区久久| 奇米亚洲欧美| 免费亚洲一区| 久久亚洲道色| 国产精品高颜值在线观看| 激情黄产视频在线免费观看| 成人午夜亚洲| 日韩欧美中文| 激情丁香综合| 亚洲在线国产日韩欧美| 综合在线一区| 欧美日韩一区二区三区不卡视频 | 亚洲精品中文字幕乱码| 蜜桃视频欧美| 免费精品视频在线| 日本欧美韩国一区三区| 国产精品毛片久久久| 久久激情五月激情| 精品国产乱码久久久| 日韩电影免费网址| 黄色成人在线网址| 中文字幕av一区二区三区四区| 日本免费一区二区视频| 国产精品久久久久久模特 | 日韩精品网站| 久久国产精品久久w女人spa| 99riav国产精品| 日本伊人午夜精品| 欧美激情麻豆| 999精品色在线播放| 性色一区二区| 国产精品一区二区99| 高清久久一区| 欧美日韩视频| 日韩精品免费视频一区二区三区| 国产精品对白| 韩国精品主播一区二区在线观看 | 日韩高清一区二区| 国产精品久久久久久久久久10秀| 日韩国产在线| 一区二区三区四区在线观看国产日韩| 国产精品自拍区| 99久精品视频在线观看视频| 香蕉视频成人在线观看| 国产精选久久| 特黄特色欧美大片| 日本高清久久| 中文字幕一区久| 亚洲精品国模| 国产aⅴ精品一区二区三区久久 | 日韩精品高清不卡| 国产一区二区三区网| 免费视频国产一区| 国产精品日韩精品中文字幕| 99精品在线免费在线观看| 日韩高清二区| 欧美综合另类| 国产精品s色| 亚洲欧洲一区| 高清精品久久| 亚洲精品日韩久久| 欧美aa在线观看| 日韩1区2区3区| 亚洲午夜91| 欧美交a欧美精品喷水| 鲁大师成人一区二区三区| 国产精品宾馆|