Android平台如何实现屏幕数据采集并推送至RTMP服务器

随着无纸化、智慧教室等场景的普及,好多企业或者开发者开始寻求更高效稳定低延迟的RTMP同屏方案,本文以大牛直播SDK(Github)的同屏demo(对应工程:SmartServicePublisherV2)为例,介绍下如何采集编码推送RTMP数据到流媒体服务器。

系统要求:Android 5.0及以上系统。

废话不多说,上代码:

获取screen windows宽高,如需缩放,按照一定的比例缩放即可:

    private void createScreenEnvironment() {sreenWindowWidth = mWindowManager.getDefaultDisplay().getWidth();screenWindowHeight = mWindowManager.getDefaultDisplay().getHeight();Log.i(TAG, "screenWindowWidth: " + sreenWindowWidth + ",screenWindowHeight: "+ screenWindowHeight);if (sreenWindowWidth > 800){if (screenResolution == SCREEN_RESOLUTION_STANDARD){scale_rate = SCALE_RATE_HALF;sreenWindowWidth = align(sreenWindowWidth / 2, 16);screenWindowHeight = align(screenWindowHeight / 2, 16);}else if(screenResolution == SCREEN_RESOLUTION_LOW){scale_rate = SCALE_RATE_TWO_FIFTHS;sreenWindowWidth = align(sreenWindowWidth * 2 / 5, 16);}}Log.i(TAG, "After adjust mWindowWidth: " + sreenWindowWidth + ", mWindowHeight: " + screenWindowHeight);int pf = mWindowManager.getDefaultDisplay().getPixelFormat();Log.i(TAG, "display format:" + pf);DisplayMetrics displayMetrics = new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);mScreenDensity = displayMetrics.densityDpi;mImageReader = ImageReader.newInstance(sreenWindowWidth,screenWindowHeight, 0x1, 6);mMediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);}

获取到image数据后,传递到processScreenImage()处理:

   private void setupMediaProjection() {mMediaProjection = mMediaProjectionManager.getMediaProjection(MainActivity.mResultCode, MainActivity.mResultData);}private void setupVirtualDisplay() {mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", sreenWindowWidth, screenWindowHeight,mScreenDensity,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(), null, null);mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {Image image = mImageReader.acquireLatestImage();if (image != null) {processScreenImage(image);//image.close();}}}, null);}

数据放到image list里面:

    private void  pushImage(Image image){if ( null ==image )return;final int image_list_max_count = 1;LinkedList<Image> close_images = null;synchronized (image_list_lock){if (image_list.size() > image_list_max_count ){close_images = new LinkedList();while ( image_list.size() > image_list_max_count){close_images.add(image_list.poll());}}image_list.add(image);}if ( close_images != null ){while( !close_images.isEmpty() ) {Image i = close_images.poll();if ( i != null ){i.close();//Log.i("PushImage", "drop image");}}}}

调用大牛直播SDK的RTMP初始化和参数设置接口:

        libPublisher = new SmartPublisherJniV2();    private void InitAndSetConfig() {//开始要不要采集音频或视频,请自行设置publisherHandle = libPublisher.SmartPublisherOpen(this.getApplicationContext(),audio_opt, video_opt, sreenWindowWidth,screenWindowHeight);if ( publisherHandle == 0 ){return;}Log.i(TAG, "publisherHandle=" + publisherHandle);libPublisher.SetSmartPublisherEventCallbackV2(publisherHandle, new EventHandeV2());if(videoEncodeType == 1){int h264HWKbps = setHardwareEncoderKbps(true, sreenWindowWidth,screenWindowHeight);Log.i(TAG, "h264HWKbps: " + h264HWKbps);int isSupportH264HWEncoder = libPublisher.SetSmartPublisherVideoHWEncoder(publisherHandle, h264HWKbps);if (isSupportH264HWEncoder == 0) {Log.i(TAG, "Great, it supports h.264 hardware encoder!");}}else if (videoEncodeType == 2){int hevcHWKbps = setHardwareEncoderKbps(false, sreenWindowWidth,screenWindowHeight);Log.i(TAG, "hevcHWKbps: " + hevcHWKbps);int isSupportHevcHWEncoder = libPublisher.SetSmartPublisherVideoHevcHWEncoder(publisherHandle, hevcHWKbps);if (isSupportHevcHWEncoder == 0) {Log.i(TAG, "Great, it supports hevc hardware encoder!");}}if(is_sw_vbr_mode){int is_enable_vbr = 1;int video_quality = CalVideoQuality(sreenWindowWidth,screenWindowHeight, true);int vbr_max_bitrate = CalVbrMaxKBitRate(sreenWindowWidth,screenWindowHeight);libPublisher.SmartPublisherSetSwVBRMode(publisherHandle, is_enable_vbr, video_quality, vbr_max_bitrate);}//音频相关可以参考SmartPublisher工程/*if (!is_speex){// set AAC encoderlibPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 1);}else{// set Speex encoderlibPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 2);libPublisher.SmartPublisherSetSpeexEncoderQuality(publisherHandle, 8);}libPublisher.SmartPublisherSetNoiseSuppression(publisherHandle, is_noise_suppression ? 1: 0);libPublisher.SmartPublisherSetAGC(publisherHandle, is_agc ? 1 : 0);*/// libPublisher.SmartPublisherSetClippingMode(publisherHandle, 0);//libPublisher.SmartPublisherSetSWVideoEncoderProfile(publisherHandle, sw_video_encoder_profile);//libPublisher.SmartPublisherSetSWVideoEncoderSpeed(publisherHandle, sw_video_encoder_speed);// libPublisher.SetRtmpPublishingType(publisherHandle, 0);libPublisher.SmartPublisherSetFPS(publisherHandle, 18);    //帧率可调libPublisher.SmartPublisherSetGopInterval(publisherHandle, 18*3);//libPublisher.SmartPublisherSetSWVideoBitRate(publisherHandle, 1200, 2400); //针对软编码有效,一般最大码率是平均码率的二倍libPublisher.SmartPublisherSetSWVideoEncoderSpeed(publisherHandle, 3);//libPublisher.SmartPublisherSaveImageFlag(publisherHandle, 1);}

初始化、参数设置后,设置RTMP推送的URL,并调用SartPublisher()接口,开始推送:

        //如果同时推送和录像,设置一次就可以InitAndSetConfig();if ( publisherHandle == 0 ){stopScreenCapture();return;}if(push_type == PUSH_TYPE_RTMP){String publishURL = intent.getStringExtra("PUBLISHURL");Log.i(TAG, "publishURL: " + publishURL);if (libPublisher.SmartPublisherSetURL(publisherHandle, publishURL) != 0) {stopScreenCapture();Log.e(TAG, "Failed to set publish stream URL..");if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}return;}}//启动传递数据线程post_data_thread = new Thread(new DataRunnable());Log.i(TAG, "new post_data_thread..");is_post_data_thread_alive = true;post_data_thread.start();
  int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);if (startRet != 0) {isPushingRtmp = false;Log.e(TAG, "Failed to start push rtmp stream..");return;}//如果同时推送和录像,Audio启动一次就可以了CheckInitAudioRecorder();

开始推送后,传递数据到底层SDK:

    public class DataRunnable implements Runnable{private final static String TAG = "DataRunnable==> ";@Overridepublic void run() {// TODO Auto-generated method stubLog.i(TAG, "post data thread is running..");ByteBuffer last_buffer = null;Image last_image = null;long last_post_time = System.currentTimeMillis();while (is_post_data_thread_alive){boolean is_skip = false;/*synchronized (data_list_lock){if ( data_list.isEmpty()){if((System.currentTimeMillis() - last_post_time) > frame_added_interval_setting){if(last_buffer != null){Log.i(TAG, "补帧中..");}else{is_skip = true;}}else{is_skip = true;}}else{last_buffer = data_list.get(0);data_list.remove(0);}}*/Image new_image = popImage();if ( new_image == null ){if((System.currentTimeMillis() - last_post_time) > frame_added_interval_setting){if(last_image != null){Log.i(TAG, "补帧中..");}else{is_skip = true;}}else{is_skip = true;}}else{if ( last_image != null ){last_image.close();}last_image = new_image;}if( is_skip ){// Log.i("OnScreenImage", "is_skip");try {Thread.sleep(5);   //休眠5ms} catch (InterruptedException e) {e.printStackTrace();}}else{//if( last_buffer != null && publisherHandle != 0 && (isPushing || isRecording || isRTSPPublisherRunning) )if( last_image != null && publisherHandle != 0 && (isPushingRtmp || isRecording || isRTSPPublisherRunning) ){long post_begin_time = System.currentTimeMillis();final Image.Plane[] planes = last_image.getPlanes();if ( planes != null && planes.length > 0 ){libPublisher.SmartPublisherOnCaptureVideoRGBAData(publisherHandle, planes[0].getBuffer(), planes[0].getRowStride(),last_image.getWidth(), last_image.getHeight());}last_post_time = System.currentTimeMillis();long post_cost_time = last_post_time - post_begin_time;if ( post_cost_time >=0 && post_cost_time < 10 ){try {Thread.sleep(10-post_cost_time);} catch (InterruptedException e) {e.printStackTrace();}}/*libPublisher.SmartPublisherOnCaptureVideoRGBAData(publisherHandle, last_buffer, row_stride_,width_, height_);*//*//实际裁剪比例,可酌情自行调整int left = 100;int cliped_left = 0;int top = 0;int cliped_top = 0;int cliped_width = width_;int cliped_height = height_;if(scale_rate == SCALE_RATE_HALF){cliped_left = left / 2;cliped_top = top / 2;//宽度裁剪后,展示3/4比例cliped_width = (width_ *3)/4;//高度不做裁剪cliped_height = height_;}else if(scale_rate == SCALE_RATE_TWO_FIFTHS){cliped_left = left * 2 / 5;cliped_top = top * 2 / 5;//宽度裁剪后,展示3/4比例cliped_width = (width_ *3)/4;//高度不做裁剪cliped_height = height_;}if(cliped_width % 2 != 0){cliped_width = cliped_width + 1;}if(cliped_height % 2 != 0){cliped_height = cliped_height + 1;}if ( (cliped_left + cliped_width) > width_){Log.e(TAG, " invalid cliped region settings, cliped_left: " + cliped_left + " cliped_width:" + cliped_width + " width:" + width_);return;}if ( (cliped_top + cliped_height) > height_){Log.e(TAG, "invalid cliped region settings, cliped_top: " + cliped_top + " cliped_height:" + cliped_height + " height:" + height_);return;}//Log.i(TAG, " clipLeft: " + cliped_left + " clipTop: " + cliped_top +  " clipWidth: " + cliped_width + " clipHeight: " + cliped_height);libPublisher.SmartPublisherOnCaptureVideoClipedRGBAData(publisherHandle, last_buffer, row_stride_,width_, height_, cliped_left, cliped_top, cliped_width, cliped_height );*/// Log.i(TAG, "post data: " + last_post_time + " cost:" + post_cost_time);}else{try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}if ( last_image != null){last_image.close();last_image = null;}}}

关闭采集推送:

    public void onDestroy() {// TODO Auto-generated method stubLog.i(TAG, "Service stopped..");stopScreenCapture();clearAllImages();if( is_post_data_thread_alive && post_data_thread != null ){Log.i(TAG, "onDestroy close post_data_thread++");is_post_data_thread_alive = false;try {post_data_thread.join();} catch (InterruptedException e) {e.printStackTrace();}post_data_thread = null;Log.i(TAG, "onDestroy post_data_thread closed--");}if (isPushingRtmp || isRecording || isRTSPPublisherRunning){if (audioRecord_ != null) {Log.i(TAG, "surfaceDestroyed, call StopRecording..");audioRecord_.Stop();if (audioRecordCallback_ != null) {audioRecord_.RemoveCallback(audioRecordCallback_);audioRecordCallback_ = null;}audioRecord_ = null;}stopPush();isPushingRtmp = false;stopRecorder();isRecording = false;stopRtspPublisher();isRTSPPublisherRunning = false;stopRtspService();isRTSPServiceRunning = false;if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}}libPublisher.UnInitRtspServer();super.onDestroy();}

以上就是Android平台数据采集、编码并推送的大概流程,感兴趣的开发者可参考下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网易邮箱大师如何注册邮箱 注册邮箱方法步骤介绍

网易邮箱大师是款高效强大的全平台邮箱客户端&#xff0c;支持所有邮箱登录&#xff0c;功能强大&#xff0c;一个PC端就能登录多个邮箱&#xff0c;很多小伙伴不知道如何注册登录邮箱&#xff0c;那么就跟着小编一起来看看如何操作吧。 操作步骤如下&#xff1a; 1、打开网易…

D3D还是GDI? Windows平台播放RTSP或RTMP渲染模式比较

先说结论&#xff0c;Windows平台播放渲染这块&#xff0c;支持D3D的前提下&#xff0c;优先D3D&#xff0c;如果检测到不支持D3D&#xff0c;数据回调上来&#xff0c;GDI模式绘制。 相比GDI模式&#xff0c;D3D绘制更细腻&#xff0c;绘制效率更高&#xff0c;CPU占用低&…

Android投屏(屏幕共享)设计需要考虑的关键因素

许多开发者&#xff0c;在做智慧教室同屏、会议同屏之类的方案时&#xff0c;基于Andriod平台的采集&#xff0c;往往遇到各种各样的问题&#xff0c;以下就几个点&#xff0c;抛砖引玉&#xff1a; 1. 内网环境下&#xff0c;组播还是RTMP&#xff1f; 回答&#xff1a;这个…

微信公众号怎么给微店设置运费

微信小店想要设置运费&#xff0c;该怎么设置呢?今天我们就来看看使用微信公众号设置微店运费的教程。 1、在电脑登录你的微信公众号服务号&#xff0c;确保已经事先开通了微信小店功能 微信公众号怎么给微店设置运费? 2、点击左侧导航栏的微信小店&#xff0c;进去后点击…

RTSP/RTMP播放端录像不可忽视的几个设计要点

很多开发者提到&#xff0c;拉取的摄像机&#xff08;一般RTSP流&#xff09;或RTMP流&#xff0c;如果需要录制&#xff0c;需要考虑哪些因素&#xff0c;本文以大牛直播SDK的Windows平台拉流端录像为例&#xff08;github&#xff09;&#xff0c;做个简单的介绍&#xff1a;…

海康摄像机rtsp地址格式(官方最新版)

★目前海康录像机、网络摄像机&#xff0c;网络球机的RTSP单播取流格式如下&#xff08;车载录像机不支持RTSP取流&#xff09;&#xff1a; rtsp://用户名:密码IP:554/Streaming/Channels/101 →录像机示例&#xff1a; 取第1个通道的主码流预览 rtsp://admin:hik1234510.…

QQ邮箱怎么发送文件夹 怎样在QQ邮箱里发送压缩文件夹

有很多用户想要知道怎么样才能通过QQ邮箱来发送自己的一些文件压缩包&#xff0c;应该怎么操作呢?不要慌&#xff0c;现在小编就给大家分享一下&#xff0c;下面一起来学习操作步骤吧! 其实&#xff0c;无论何种邮箱都无法发送文件夹&#xff0c;都只能对文件进行操作。如果文…

时光手帐怎么修改作品 时光手帐修改作品封面方法

对于自己在时光手账Pro中的手账本封面是可以随时更改的。那么&#xff0c;怎么在时光手账Pro中更改自己手账封面?下面我使用苹果手机(安卓端操作方法一致)来分享一下具体的操作流程。 首先打开手机&#xff0c;点击进入时光手账Pro。 时光手帐怎么修改作品 时光手帐修改作品…

几款优秀的点播、RTSP/RTMP直播播放器介绍

1.ijkplayer 项目地址&#xff1a; https://github.com/Bilibili/ijkplayer 介绍&#xff1a;Ijkplayer 是Bilibili发布的基于 FFplay 的轻量级 Android/iOS 视频播放器。实现了跨平台功能&#xff0c;API 易于集成&#xff1b;编译配置可裁剪&#xff0c;方便控制安装包大小&…

Android平台RTMP推流或轻量级RTSP服务(摄像头或同屏)编码前数据接入类型总结

很多开发者在做Android平台RTMP推流或轻量级RTSP服务&#xff08;摄像头或同屏&#xff09;时&#xff0c;总感觉接口不够用&#xff0c;以大牛直播SDK为例 (Github) 我们来总结下&#xff0c;我们常规需要支持的编码前音视频数据有哪些类型&#xff1a; 1. Android摄像头前后…

作业帮口算批改怎么开 作业帮口算批改如何用

作业帮口算批改功能怎么用?作业帮最近全新改版的消息传的很火&#xff0c;新增了口算批改的功能&#xff0c;很吸引人。但是还有很多用户不是很清楚作业帮口算批改功能怎么用&#xff0c;下面是小编整理的关于作业帮口算批改功能怎么用的相关资讯&#xff0c;快来看看吧! 作业…

RTSP播放器或RTMP播放器常用的事件回调设计

很多开发者在开发RTSP或RTMP播放器的时候&#xff0c;不晓得哪些event回调事件是有意义的&#xff0c;针对此&#xff0c;我们以大牛直播SDK&#xff08;github&#xff09;的Android平台RTSP/RTMP直播播放端为例&#xff0c;简单介绍下常用的event id&#xff0c;总的来说&…

Windows平台RTMP多实例推送探讨

之前&#xff0c;我们博客 https://blog.csdn.net/renhui1112/article/details/105624392 提到&#xff0c;Android平台RTMP多实例推送的几种情况探讨&#xff0c;简单来说有以下三种情况&#xff1a; 多路编码&#xff0c;多个实例分别推送到不同的RTMP URL&#xff08;如And…

Win7蓝屏代码0x000000001e怎么解决

蓝屏问题&#xff0c;是所有系统都会遇到的最普遍的电脑故障问题&#xff0c;电脑蓝屏是因为系统与硬件或软件不兼容冲突引起的&#xff0c;不同的提示代码表示了不同的蓝屏原因&#xff0c;那么Win7蓝屏代码0x000000001e怎么解决?为此问题困扰的用户&#xff0c;请来看看Win7…

开发个好的RTMP播放器到底难在哪里?RTMP播放器对标和考察指标

好多开发者提到&#xff0c;RTMP播放器&#xff0c;不知道有哪些对标和考察指标&#xff0c;以下大概聊聊我们的一点经验&#xff0c;感兴趣的&#xff0c;可以关注 github&#xff1a; 1. 低延迟&#xff1a;大多数RTMP的播放都面向直播场景&#xff0c;如果延迟过大&#xf…

Android平台RTMP/RTSP播放器开发系列之解码和绘制

本文主要抛砖引玉&#xff0c;粗略介绍下Android平台RTMP/RTSP播放器中解码和绘制相关的部分(Github)。 解码 提到解码&#xff0c;大家都知道软硬解&#xff0c;甚至一些公司觉得硬解码已经足够通用&#xff0c;慢慢抛弃软解了&#xff0c;如果考虑到设备匹配&#xff0c;软…

Win7图片查看器打印不了图片怎么办

当我们想浏览电脑中的图片文件时&#xff0c;可以选择系统自带的图片查看器或者第三方看图工具打开&#xff0c;但是有些win7用户发现自己想通过windows图片查看器打印图片却没有反应&#xff0c;Win7图片查看器打印不了图片怎么办?就此剖&#xff0c;下面小编就来教教大家解决…

Android平台屏幕/摄像头或外部数据采集及RTMP推送接口设计描述

好多开发者提到&#xff0c;为什么大牛直播SDK的Android平台RTMP推送接口怎么这么多&#xff1f;不像一些开源或者商业RTMP推送一样&#xff0c;就几个接口&#xff0c;简单明了。 不解释&#xff0c;以Android平台RTMP推送模块常用接口&#xff0c;看看这些接口有没有存在的意…

钉钉怎么设置考勤打卡规则

1、首先在电脑上登陆钉钉后台&#xff0c;然后点击如图所示的钉钉考勤打卡 钉钉后怎么设置考勤打卡规则? 2、比如一个公司生产型企业 互联网部门上班时间是 周一到周五上班时间 8:30-5:30 周六上班 8:30-12:00 以此作为案例说明 钉钉后怎么设置考勤打卡规则? 3、首先设…

变废为宝:使用废旧手机实现实时监控方案

随着手机淘汰的速度越来越快&#xff0c;大多数手机功能性能很强劲就不再使用了&#xff0c;以大牛直播SDK现有方案为例&#xff0c;本文探讨下&#xff0c;如何用废旧手机实现实时监控方案&#xff08;把手机当摄像头做监控之用&#xff09;&#xff1a; 本方案需要准备一个手…