|
Real Time Messaging Protocol(RTMP)即實(shí)時消息傳輸協(xié)議,是 Adobe 公司開發(fā)的一個基于 TCP 的應(yīng)用層協(xié)議,目前國內(nèi)的視頻云服務(wù)都是以 RTMP 為主要推流協(xié)議。
關(guān)于RTMP推流組件
EasyRTMP是一套調(diào)用簡單、功能完善、運(yùn)行高效穩(wěn)定的RTMP推流功能組件,經(jīng)過多年客戶實(shí)戰(zhàn)和線上運(yùn)行打造,支持RTMP推送斷線重連、環(huán)形緩沖、智能丟幀、網(wǎng)絡(luò)事件回調(diào),支持Windows、Linux、ARM、Android、iOS平臺,支持市面上絕大部分的RTMP流媒體服務(wù)器,能夠完美應(yīng)用于各種行業(yè)的直播需求,手機(jī)直播、桌面直播、攝像機(jī)直播、課堂直播等方面。結(jié)合EasyDSS流媒體服務(wù)器,為開發(fā)者提供專業(yè)、穩(wěn)定的直播推流、轉(zhuǎn)碼、分發(fā)服務(wù),全面滿足低超低延遲、超高畫質(zhì)、超大并發(fā)訪問量的要求。

EasyRTMP-Android介紹屏幕推流及其注意點(diǎn)
解決問題
1、錄屏
VirtualDisplay類代表一個虛擬顯示器,需要調(diào)用DisplayManager 類的 createVirtualDisplay()方法,將虛擬顯示器的內(nèi)容渲染在一個Surface控件上,當(dāng)進(jìn)程終止時虛擬顯示器會被自動的釋放,并且所有的Window都會被強(qiáng)制移除。當(dāng)不再使用時,需要調(diào)用release() 方法來釋放資源。
1)獲取MediaProjectionManager實(shí)例
mMpmngr = (MediaProjectionManager) getApplicationContext()
.getSystemService(MEDIA_PROJECTION_SERVICE);
2)在Android 6.0后,Android需要動態(tài)獲取權(quán)限,若沒有權(quán)限,則不啟用該service:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
return;
}
}
3)MediaProjection是Android5.0后才開放的屏幕采集接口,通過系統(tǒng)級服務(wù)MediaProjectionManager進(jìn)行管理。
if (mMpj == null) {
mMpj = mMpmngr.getMediaProjection(StreamActivity.mResultCode,
StreamActivity.mResultIntent);
StreamActivity.mResultCode = 0;
StreamActivity.mResultIntent = null;
}
4)通過MediaProjection對象的createVirtualDisplay方法,拿到VirtureDisplay對象,拿這個對象的時候,需要把Surface對象傳進(jìn)去,Surface是由MediaCodec獲得。
mVirtualDisplay = mMpj.createVirtualDisplay(
"record_screen",
windowWidth,
windowHeight,
screenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION,
mSurface,
null,
null);
2、編碼及推流
使用Android硬編碼MediaCodec,將VirtualDisplay獲取到的視頻數(shù)據(jù)編碼后通過EasyRTMP推送出去。
1)初始化MediaCodec,同時獲取Surface對象,并啟動編碼器
mMediaCodec = MediaCodec.createByCodecName(ci.mName);
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc",
windowWidth, windowHeight);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
mediaFormat.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER,
20000000);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mediaFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
mMediaCodec.configure(mediaFormat, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
// 獲取Surface對象
mSurface = mMediaCodec.createInputSurface();
mMediaCodec.start();
2)音頻編碼的工作直接使用AudioStream,
final AudioStream audioStream =
AudioStream.getInstance(EasyApplication.getEasyApplication(),
SPUtil.getEnableAudio(EasyApplication.getEasyApplication()));
3)啟動編碼線程,不停的將獲取的音頻幀和視頻幀,編碼并推流。詳情見代碼注釋:
// 啟動線程,
mPushThread.start();
// 初始化推流器
mEasyPusher = new EasyRTMP(mHevc ? EasyRTMP.VIDEO_CODEC_H265 :
EasyRTMP.VIDEO_CODEC_H264, RTMP_KEY);
// 視頻的硬編碼
ByteBuffer outputBuffer;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
} else {
outputBuffer = outputBuffers[outputBufferIndex];
}
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset bufferInfo.size);
try {
boolean sync = false;
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
sync = (bufferInfo.flags&MediaCodec.BUFFER_FLAG_SYNC_FRAME)!=0;
if (!sync) {
byte[] temp = new byte[bufferInfo.size];
outputBuffer.get(temp);
mPpsSps = temp;
continue;
} else {
mPpsSps = new byte[0];
}
}
sync |= (bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
int len = mPpsSps.length bufferInfo.size;
if (len > h264.length) {
h264 = new byte[len];
}
// 將編碼后的視頻數(shù)據(jù)發(fā)送出去
if (sync) {
System.arraycopy(mPpsSps, 0, h264, 0, mPpsSps.length);
outputBuffer.get(h264, mPpsSps.length, bufferInfo.size);
mEasyPusher.push(h264, 0, mPpsSps.length bufferInfo.size, bufferInfo.presentationTimeUs / 1000, 2);
} else {
outputBuffer.get(h264, 0, bufferInfo.size);
mEasyPusher.push(h264, 0, bufferInfo.size, bufferInfo.presentationTimeUs / 1000, 1);
}
來源:https://www./content-4-448351.html
|