Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究

几年来,我们在做无纸化同屏或在线教育相关场景的时候,总是被一件事情困扰:如何实现Android平台的系统内录,并推送到其他播放端,常用的场景比如做无纸化会议或教育的时候,主讲人或老师需要放一个视频,该怎么办呢?这里我们分析三种可行的技术方案:

方案1:解析视频文件推送

Android终端的话,先利用MediaExtractor,把mp4文件的音视频数据分离,然后调用我们publisher模块,实现编码后的数据对接到RTMP服务器,实例代码如下:

/** SmartPublisherActivity.java* Github: https://github.com/daniulive/SmarterStreaming*/  
private void InitMediaExtractor(){File mFile = new File("/storage/emulated/0/","2022.mp4");if (!mFile.exists()){Log.e(TAG, "mp4文件不存在");return;}MediaExtractor mediaExtractor = new MediaExtractor();try {mediaExtractor.setDataSource(mFile.getAbsolutePath());} catch (IOException e) {e.printStackTrace();}int count = mediaExtractor.getTrackCount();//获取轨道数量Log.e(TAG, "轨道数量 = "+count);for (int i = 0; i < count; i++){MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);String mineType = trackFormat.getString(MediaFormat.KEY_MIME);Log.e(TAG, i + "编号通道格式 = " + mineType);//视频信道if (mineType.startsWith("video/")) {video_track_index = i;is_has_video = true;try {video_media_extractor.setDataSource(mFile.getAbsolutePath());} catch (IOException e) {e.printStackTrace();}if(mineType.equals("video/avc")){video_codec_id = 1;}else if(mineType.equals("video/hevc")){video_codec_id = 2;}int width = trackFormat.getInteger(MediaFormat.KEY_WIDTH);int height = trackFormat.getInteger(MediaFormat.KEY_HEIGHT);long duration = trackFormat.getLong(MediaFormat.KEY_DURATION);//总时间int video_fps = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);//帧率max_sample_size = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);//获取视频缓存输出的最大大小Log.e(TAG, "video width " + width + ", height: " + height + ", duration: " + duration + ", max_sample_size: " + max_sample_size + ", fps: " + video_fps);}//音频信道if (mineType.startsWith("audio/")) {audio_track_index = i;is_has_audio = true;try {audio_media_extractor.setDataSource(mFile.getAbsolutePath());} catch (IOException e) {e.printStackTrace();}if(mineType.equals("audio/mp4a-latm")){audio_codec_id = 0x10002;}audio_sample_rate = trackFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);//获取采样率int audioTrackBitrate = trackFormat.getInteger(MediaFormat.KEY_BIT_RATE);      //获取比特率int channels = trackFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);       //获取声道数量Log.e(TAG, "mp4 audio_sample_rate " + audio_sample_rate + ", audioTrackBitrate: " + audioTrackBitrate + ", channels: " + channels);}}}

推送video数据:

if(IsVpsSpsPps(video_header_checker_buffer, video_codec_id))
{is_key_frame = true;
}if ( isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) {libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle, video_codec_id, byteBuffer, video_sample_size, is_key_frame?1:0, cur_sample_time, cur_sample_time);
}

推送audio数据:

int audio_sample_size = audio_media_extractor.readSampleData(byteBuffer, 0);if(audio_sample_size < 0)
{Log.i(TAG, "audio reach the end..");break;
}long cur_sample_time = audio_media_extractor.getSampleTime()/1000;if ( isPushing || isRTSPPublisherRunning || isGB28181StreamRunning) {libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle, audio_codec_id, byteBuffer, audio_sample_size, 0, cur_sample_time, parameter_info, parameter_info_size);
}

上述代码,实现原理很简单,无非就是想把audio video从容器中分离出来,然后打包发出去,我们有做流媒体后视镜相关场景的合作公司,就这么实现过。

方案2:REMOTE_SUBMIX

Android中可以通过使用MediaRecorder.AudioSource.REMOTE_SUBMIX来实现系统声音的录制,这个属性只有系统应用能够使用,而且这个属性会截掉耳机和扬声器的声音,让我们听不到手机中播放音乐或者视频时的声音,而录制结束后会发现播放录制好的文件是有这些声音的。一般来说,做无纸化会议或教育同屏的公司,如果硬件是厂商定制的,可以跟厂商提出来,修改ROM,得到内录audio权限和数据。为此,我们专门设计了个接口,便于有这个权限的厂商使用。

REMOTE_SUBMIX可以实现内录功能,有几点需要注意:需要有系统权限,而且会截走扬声器和耳机的声音,也就是说再录音时本地无法播放声音,对于系统权限,可在AndroidManifest.xml添加 android:sharedUserId="android.uid.system",然后使用系统签名来打包应用。 

private void CheckInitAudioRecorder() {if (audioRecord_ == null) {//audioRecord_ = new NTAudioRecord(this, 1);audioRecord_ = new NTAudioRecordV2(this);}if (audioRecord_ != null) {Log.i(TAG, "CheckInitAudioRecorder call audioRecord_.start()+++...");audioRecordCallback_ = new NTAudioRecordV2CallbackImpl();//audioRecord_.IsMicSource(true);       //如音频采集声音过小,建议开启// audioRecord_.IsRemoteSubmixSource(true);audioRecord_.AddCallback(audioRecordCallback_);audioRecord_.Start();Log.i(TAG, "CheckInitAudioRecorder call audioRecord_.start()---...");}}

方案3:AudioPlaybackCapture API

也是本文提到的重点,实际上,Android 10 已引入 AudioPlaybackCapture API。应用可以借助此 API 复制其他应用正在播放的音频。此功能类似于屏幕采集,但采集对象是音频。主要用例是视频在线播放应用,这些应用希望捕获游戏正在播放的音频。对于其音频正在被捕获的应用,Capture API 不会影响该应用的延迟时间。

为确保安全性和隐私,“捕获播放的音频”功能会施加一些限制。为了能够捕获音频,应用必须满足以下要求:

  • 应用必须具有 ​​RECORD_AUDIO​​ 权限。
  • 应用必须调出 ​​MediaProjectionManager.createScreenCaptureIntent()​​显示的提示,用户必须批准该提示。

捕获和播放音频的应用必须使用同一份用户个人资料。

捕获音频

如要从其他应用中捕获音频,您的应用必须构建 ​​AudioRecord​​​ 对象,并向其添加 ​​AudioPlaybackCaptureConfiguration​​。请按以下步骤操作:

  1. 调用 ​​AudioPlaybackCaptureConfiguration.Builder.build()​​​ 以构建 ​​AudioPlaybackCaptureConfiguration​​。
  2. 通过调用 ​​setAudioPlaybackCaptureConfig​​​ 将配置传递到 ​​AudioRecord​​。

采集的话,10.0以上版本,按照上述设置即可获取到数据。

if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
{CheckInitAudioRecorderSpeaker();    //采集扬声器,需要android 10.0+版本
}private void CheckInitAudioRecorderSpeaker() {if (audioRecordSpeaker_ == null) {audioRecordSpeaker_ = new AudioRecordSpeaker(this);}if (audioRecordSpeaker_ != null) {Log.i(TAG, "CheckInitAudioRecorder call audioRecordSpeaker_.start()+++...");audioRecordSpeakerCallback_ = new AudioRecordSpeakerCallbackImpl();audioRecordSpeaker_.AddCallback(audioRecordSpeakerCallback_);audioRecordSpeaker_.Start(mMediaProjection, 44100, 1);Log.i(TAG, "CheckInitAudioRecorder call audioRecordSpeaker_.start()---...");}
}class AudioRecordSpeakerCallbackImpl implements AudioRecordSpeakerCallback {@Overridepublic void onAudioRecordSpeakerFrame(ByteBuffer data, int size, int sampleRate, int channel, int per_channel_sample_number) {//Log.i(TAG, "onAudioRecordSpeakerFrame size=" + size + " sampleRate=" + sampleRate + " channel=" + channel//        + " per_channel_sample_number=" + per_channel_sample_number);if ( publisherHandle != 0 && (isRecording || isPushingRtmp || isRTSPPublisherRunning) ){libPublisher.SmartPublisherOnMixPCMData(publisherHandle, 1, data, 0, size, sampleRate, channel, per_channel_sample_number);}}
}

大家注意到,我们数据投递,用的是SmartPublisherOnMixPCMData()这个接口,为什么这么做呢?我们考虑到,在做无纸化同屏或者教育投屏的时候,一般来说,主要还是采集麦克风音频为主,中间如果有视频播放或者类似需求的时候,我们把内录audio的打开即可(也可以做混音模式,或者推送过程中,实时静音麦克风或扬声器数据源,当然也可以实时调节二者的音量),具体在初始化的时候,可以做下设置:

//audio mix模式下, 如果需要切换麦克风和扬声器数据源,针对麦克风或扬声器实时静音即可
//混音模式下,也可以针对麦克风或扬声器,做实时音量调节
boolean is_audio_mix = true;   //是否混音
libPublisher.SmartPublisherSetAudioMix(publisherHandle, is_audio_mix?1:0);if(is_audio_mix)
{int index = 0;  //0: 麦克风音量调节 1: 扬声器音量调节libPublisher.SmartPublisherSetInputAudioVolume(publisherHandle, index, 0.0f);
}

无图无真相,Android平台RTMP推送端或者轻量级RTSP服务测,采集到屏幕画面和扬声器声音,打包传输,RTMP或RTSP播放端截到的同屏画面如下(本来想放一段视频的,没找到传视频的地方):


总结

低版本的Android系统,方案1应该是相对可行但局限很大的选择,方案2大多时候,非定制设备,很难满足权限要求,方案3对Android系统版本要求比较高。

通过测试,方案3除了对Android版本要求比较高外,体验式最好的,感兴趣的开发者,可以尝试看看,如果是特定场景下,本身选用的设备,Android的版本就比较高,又有内录audio需求的话,无疑是非常不错的选择。

 

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

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

相关文章

Android平台GB28181设备接入端PTZ对接详解

PTZCmd实现背景 上一篇blog“Android平台GB28181设备接入模块之球机/云台控制探究”谈到&#xff0c;Android平台做国标GB28181设备接入端的时候&#xff0c;PTZ控制要不要处理&#xff1f;如果处理&#xff0c;难度大不大&#xff1f; 首先说要不要处理&#xff1a;如果只是…

Android平台GB28181设备接入模块摄像头采集方向不对怎么办?

背景 我们在做Android平台GB28181设备接入模块的时候&#xff0c;有开发者提到这样的诉求&#xff1a;他们的智能头盔、执法记录仪等设备&#xff0c;采集到的图像&#xff0c;是旋转了90、180甚至270的&#xff0c;设备本身无法针对图像做翻转或者旋转操作&#xff0c;问我们…

Android平台GB28181设备接入模块分辨率发生变化怎么办?

技术背景 我们在做Android平台gb28181设备接入模块的时候&#xff0c;遇到这样的情况&#xff0c;比如横竖屏分辨率不锁定&#xff0c;采集摄像头的时候&#xff0c;可以实现&#xff0c;横屏状态采集横屏&#xff0c;竖屏状态采集竖屏&#xff0c;简单来说&#xff0c;横屏状…

RTSP、RTMP播放器拉到的视频图像角度不对怎么办?

我们在做RTSP、RTMP播放器的时候&#xff0c;遇到这样的诉求&#xff1a;特别是RTSP&#xff0c;有些摄像头安装可能倒置或者旋转了90亦或270&#xff0c;拉取到图像&#xff0c;势必需要对视频图像做一定的处理&#xff0c;确保显示正常。 为此&#xff0c;我们提供了以下接口…

Android平台GB28181设备接入端如何调节实时音量?

我们在对接Android平台GB28181设备接入端的时候&#xff0c;有开发者提出这样的疑惑&#xff0c;如何调整设备接入端的实时音量&#xff1f; 实际上&#xff0c;这块我们前几年在做RTMP直播推送模块的时候&#xff0c;已经发布了相关的接口&#xff0c;这里再回顾下&#xff1…

【技术分享】如何实现功能完备性能优异的RTMP、RTSP播放器?

技术背景 这几年&#xff0c;我们对接了太多有RTSP或RTMP直播播放器诉求的开发者&#xff0c;他们当中除了寻求完整的解决方案的&#xff0c;还有些是技术探讨&#xff0c;希望能借鉴我们播放端的开发思路或功能特性&#xff0c;完善自己的产品。 忙里偷闲&#xff0c;今天我…

我正在参加2022年度博客之星评选,大家帮我点个五星好评

大家好&#xff0c;我是音视频牛哥&#xff0c;致力于跨平台的实时RTMP推流、转发、RTMP/RTSP直播播放、GB28181设备接入。 有幸参加2022年度博客之星评选&#xff0c;欢迎大家帮点五星好评。如果我的博客能给开发者带来哪怕一丝启发&#xff0c;对博主来说&#xff0c;也甚感…

rtmp/rtsp/hls公网真正可用的测试地址

相信大家在调试播放器的时候&#xff0c;都有这样的困惑&#xff0c;很难找到合适的公有测试源&#xff0c;以下是大牛直播SDK&#xff08;GitHub地址&#xff09;整理的真正可用的直播地址源。 其中&#xff0c;rtmp和rtsp的url&#xff0c;用我们播放器验证通过。 1. rtmp:…

公网可用的RTMP、RTSP测试地址(更新于2021年3月)

好多博客提到的公网可测试的RTSP和RTMP URL大多都不用了&#xff0c;以下是大牛直播SDK(Github)于2021年3月亲测可用的几个URL&#xff0c;有其他可用的URL&#xff0c;也欢迎大家在评论区回复。 RTMP流地址 湖南卫视&#xff1a;rtmp://58.200.131.2:1935/livetv/hunantv (7…

Unity环境下RTMP推流+RTMP播放低延迟解决方案

在本文之前&#xff0c;我们发布了Unity环境下的RTMP推流&#xff08;Windows平台Android平台&#xff09;和RTMP|RTSP拉流&#xff08;Windows平台Android平台iOS平台&#xff09;低延迟的解决方案&#xff0c;今天做个整体汇总&#xff0c;权当抛砖引玉。 1. Unity环境下RTM…

麒麟操作系统|Linux下低延时RTMP|RTSP直播播放实现

背景 国产操作系统多为以Linux为基础二次开发的操作系统。2014年4月8日起&#xff0c;美国微软公司停止了对Windows XP SP3操作系统提供服务支持&#xff0c;这引起了社会和广大用户的广泛关注和对信息安全的担忧。而2020年对Windows7服务支持的终止再一次推动了国产系统的发展…

基于RTMP实现Linux|麒麟操作系统下屏幕|系统声音采集推送

背景 Windows操作系统自问世以来&#xff0c;以其简单易用的图形化界面操作受到大众追捧&#xff0c;为计算机的普及、科技的发展做出了不可磨灭的功绩&#xff0c;也慢慢的成为人们最依赖的操作系统。在中国&#xff0c;90&#xff05;以上的办公环境都是Windows&#xff0c;…

如何快速实现Android平台前端设备接入能力

技术背景 SIP(会话初始化协议)是在 IP网络上进行多媒体通信的应用层控制协议&#xff0c;以几种RFC的形式提供&#xff0c;其中最重要的是包含核心协议规范的RFC3261。该协议用于创建&#xff0c;修改和终止与一个或多个参与者的会话。通过会话&#xff0c;我们了解了一组进行…

长安渝北工厂机器人_长安工厂探秘!解密CS75 PLUS究竟是怎样造出来的

长安CS75 PLUS自打上市以来&#xff0c;销量真的是有目共睹&#xff0c;仅仅一个半月就有3万多的订单&#xff0c;这辆车真的很火很爆款。但我不仅要提出一些疑问了&#xff0c;CS75 PLUS卖这么好&#xff0c;制造工艺怎么样呢&#xff1f;它又是怎么造出来的呢&#xff1f;带着…

threadlocal存连接对象的目的_终于懂了ThreadLocal,不再害怕面试官问了

ThreadLocal解析synchronized和ThreadLocal的区别&#xff1a;synchronized:以时间换空间&#xff0c;只提供一份变量&#xff0c;让不同的线程排队访问&#xff0c;失去了并发性&#xff0c;降低了程序效率&#xff0c;着重对各线程之间访问资源的同步性ThreadLocal:以空间换时…

主板后置音频接口图解_颜值出众、用料靠谱——华擎(ASRock)Z490 Extreme4极限玩家主板 简析...

一、前言你们能想象嘛&#xff0c;那种主板已经到了&#xff0c;处理器却没抢到的感觉。刚开始看到Plus会员提前抢的时候&#xff0c;我心里面还有一些放心&#xff0c;但当时间刚过秒变无货的时候&#xff0c;一切又已回到当初&#xff0c;难受&#xff01;居然没有首发抢到10…

中查出所有姓张的学生为啥查不出来_只有笔试成绩没有面试成绩是什么原因 教师资格面试成绩怎么查...

[闽南网]对于很多同学来说&#xff0c;今晚是个不眠夜。就在几个小时前&#xff0c;教师资格证面试成绩查询入口开通了&#xff0c;相比之前发布的消息&#xff0c;成绩公布提前了很多。参加了这次教师资格证考试的同学&#xff0c;一得到消息就忙着查成绩&#xff0c;毕竟面试…

c++用模板实现稀疏多项式_用线性表实现一元多项式及相加运算

“ 本文主要讨论线性表在多项式计算中的应用&#xff0c;讨论内容涉及到一元n次多项式在计算机中的表示&#xff0c;及多项式相加运算。”01在数学上&#xff0c;一个一元n次多项式可以按照升幂写成Pn(x) p0 p1x p2x2 …… pnxn它由n1个系数唯一确定。因此&#xff0c;一个…

cdh mysql sqoop 驱动_大数据技术之Sqoop学习——原理、安装、使用案例、常用命令...

第1章 Sqoop 简介Sqoop 是一款开源的工具&#xff0c;主要用于在 Hadoop(Hive) 与传统的数据库 (mysql,postgresql,...) 间进行数据的高校传递&#xff0c;可以将一个关系型数据库(例如&#xff1a;MySQL,Oracle,Postgres等)中的数据导入到 Hadoop 的 HDFS 中&#xff0c;也可以…

邮票的孔怎么做出来的_金银花茶是怎么做出来的呢

花期爱亦长&#xff0c;变换自然妆。蝶恋金银露&#xff0c;风柔满院香。说起金银花&#xff0c;可能大家都不陌生&#xff0c;它无论是作为观赏性盆景还是作为金银花养生茶都是非常适宜的。金银花被称为夏天第一花&#xff0c;夏天喝不仅能清热解暑&#xff0c;还能去除许多小…