电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

瀏覽:2日期: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)文章:
主站蜘蛛池模板: 齿辊分级破碎机,高低压压球机,立式双动力磨粉机-郑州长城冶金设备有限公司 | 校服厂家,英伦校服定做工厂,园服生产定制厂商-东莞市艾咪天使校服 | 灌木树苗-绿化苗木-常绿乔木-价格/批发/基地 - 四川成都途美园林 | 机器视觉检测系统-视觉检测系统-机器视觉系统-ccd检测系统-视觉控制器-视控一体机 -海克易邦 | 继电器模组-IO端子台-plc连接线-省配线模组厂家-世麦德 | 真空乳化机-灌装封尾机-首页-温州精灌 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 伊卡洛斯软装首页-电动窗帘,别墅窗帘,定制窗帘,江浙沪1000+别墅窗帘案例 | 路斯特伺服驱动器维修,伦茨伺服驱动器维修|万骏自动化百科 | 山东臭氧发生器,臭氧发生器厂家-山东瑞华环保设备 | 润滑脂-高温润滑脂-轴承润滑脂-食品级润滑油-索科润滑油脂厂家 | 线材成型机,线材折弯机,线材成型机厂家,贝朗自动化设备有限公司1 | 充气膜专家-气膜馆-PTFE膜结构-ETFE膜结构-商业街膜结构-奥克金鼎 | 粉碎机_塑料粉碎机_塑料破碎机厂家-星标机械 | 云南成人高考网| 工业制氮机_psa制氮机厂家-宏骁智能装备科技江苏有限公司 | GAST/BRIWATEC/CINCINNATI/KARL-KLEIN/ZIEHL-ABEGG风机|亚喜科技 | 济南ISO9000认证咨询代理公司,ISO9001认证,CMA实验室认证,ISO/TS16949认证,服务体系认证,资产管理体系认证,SC食品生产许可证- 济南创远企业管理咨询有限公司 郑州电线电缆厂家-防火|低压|低烟无卤电缆-河南明星电缆 | 水压力传感器_数字压力传感器|佛山一众传感仪器有限公司|首页 | 超声波清洗机-超声波清洗设备定制生产厂家 - 深圳市冠博科技实业有限公司 | 泰兴市热钻机械有限公司-热熔钻孔机-数控热熔钻-热熔钻孔攻牙一体机 | 机房监控|动环监控|动力环境监控系统方案产品定制厂家 - 迈世OMARA | 手机存放柜,超市储物柜,电子储物柜,自动寄存柜,行李寄存柜,自动存包柜,条码存包柜-上海天琪实业有限公司 | 北京亦庄厂房出租_经开区产业园招商信息平台 | 数控专用机床,专用机床,自动线,组合机床,动力头,自动化加工生产线,江苏海鑫机床有限公司 | DDoS安全防护官网-领先的DDoS安全防护服务商 | 深圳富泰鑫五金_五金冲压件加工_五金配件加工_精密零件加工厂 | 丹佛斯压力传感器,WISE温度传感器,WISE压力开关,丹佛斯温度开关-上海力笙工业设备有限公司 | 振动筛-交叉筛-螺旋筛-滚轴筛-正弦筛-方形摇摆筛「新乡振动筛厂家」 | 南京欧陆电气股份有限公司-风力发电机官网 | 环境模拟实验室_液体-气体控温机_气体控温箱_无锡双润冷却科技有限公司 | 电缆隧道在线监测-智慧配电站房-升压站在线监测-江苏久创电气科技有限公司 | 万家财经_财经新闻_在线财经资讯网 | 专业广州网站建设,微信小程序开发,一物一码和NFC应用开发、物联网、外贸商城、定制系统和APP开发【致茂网络】 | 谷歌关键词优化-外贸网站优化-Google SEO小语种推广-思亿欧外贸快车 | 卫生型双针压力表-高温防腐差压表-安徽康泰电气有限公司 | 深圳诚暄fpc首页-柔性线路板,fpc柔性线路板打样生产厂家 | 精密机械零件加工_CNC加工_精密加工_数控车床加工_精密机械加工_机械零部件加工厂 | 北京租车公司_汽车/客车/班车/大巴车租赁_商务会议/展会用车/旅游大巴出租_北京桐顺创业租车公司 | 智能化的检漏仪_气密性测试仪_流量测试仪_流阻阻力测试仪_呼吸管快速检漏仪_连接器防水测试仪_车载镜头测试仪_奥图自动化科技 | 海尔生物医疗四川代理商,海尔低温冰箱四川销售-成都壹科医疗器械有限公司 |