RTMP推流组件EasyRTMP实现内网摄像头RTSP拉流转码RTMP推流到RTMP服务器之Android版屏幕推流方法及其注意点

Real Time Messaging Protocol(RTMP)即实时消息传输协议,是 Adobe 公司开发的一个基于 TCP 的应用层协议,目前国内的视频云服务都是以 RTMP 为主要推流协议。

关于RTMP推流组件

EasyRTMP是一套调用简单、功能完善、运行高效稳定的RTMP推流功能组件,经过多年客户实战和线上运行打造,支持RTMP推送断线重连、环形缓冲、智能丢帧、网络事件回调,支持Windows、Linux、ARM、Android、iOS平台,支持市面上绝大部分的RTMP流媒体服务器,能够完美应用于各种行业的直播需求,手机直播、桌面直播、摄像机直播、课堂直播等方面。结合EasyDSS流媒体服务器,为开发者提供专业、稳定的直播推流、转码、分发服务,全面满足低超低延迟、超高画质、超大并发访问量的要求。

EasyRTMP

EasyRTMP-Android介绍屏幕推流及其注意点

解决问题

1、录屏

VirtualDisplay类代表一个虚拟显示器,需要调用DisplayManager 类的 createVirtualDisplay()方法,将虚拟显示器的内容渲染在一个Surface控件上,当进程终止时虚拟显示器会被自动的释放,并且所有的Window都会被强制移除。当不再使用时,需要调用release() 方法来释放资源。

1)获取MediaProjectionManager实例

	mMpmngr = (MediaProjectionManager) getApplicationContext()
					.getSystemService(MEDIA_PROJECTION_SERVICE);

2)在Android 6.0后,Android需要动态获取权限,若没有权限,则不启用该service:

	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
		if (!Settings.canDrawOverlays(this)) {
			return;
		}
	}

3)MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。

if (mMpj == null) {
		mMpj = mMpmngr.getMediaProjection(StreamActivity.mResultCode, 
												StreamActivity.mResultIntent);
		StreamActivity.mResultCode = 0;
		StreamActivity.mResultIntent = null;
	}

4)通过MediaProjection对象的createVirtualDisplay方法,拿到VirtureDisplay对象,拿这个对象的时候,需要把Surface对象传进去,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获取到的视频数据编码后通过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];
		}
		
	// 将编码后的视频数据发送出去
	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);
	}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值