android WebRtc 无法推流以及拉流有视频无声音问题

最近在开发使用WebRtc进行视频通话和语音通话,我使用的设备是MTK的手机,期间后台的技术人员几乎没法提供任何帮助,只有接口和测试的web端,有遇到不能推流。推流成功网页端有画面有声音,但是安卓端有画面,没有声音的情况

问题一:无法推流

一开始遇到问题是在进行推流前进行sdp的交换,一直返回返回code:400的情况,我们知道在 onCreateSuccess 方法回调中拿到offerSdp,用于向SRS服务进行网络请求,这时候一定注意其间的网络请求地址,token的拼接,peerConnection.addTransceiver添加的视频轨道和音频轨道一定要按照实际需求来,没有的就不要全部添加;全部中间步骤没错,这时候还是返回code:400的问题,后来在后台看到的错误日志是create session : create session : add publisher : publish negotiate : no found valid H.264 payload type,然后就是网上搜索下同样的问题,这里感谢

冬季穿短裤同学的知识帮助,就是接口请求里offer sdpm=videoH.264相关信息,即WebRTC在createOffer时,返回的sdp没有H.264相关信息;Android的使用WebRTC仅支持硬件上 H.264 解码和编码,并且仅支持部分芯片组。因此,如果设备不支持硬件 H.264 或具有不受支持的芯片组,您将只能使用 VP8、VP9。支持的芯片组仅有OMX.qcom.*OMX.Exynos.*,不支持的要自行添加。

解决方法:

VideoEncoderFactory创建

在创建PeerConnectionFactory,可以设VideoEncoderFactory

val encoderFactory = DefaultVideoEncoderFactory(eglBaseContext, true, true)
val peerConnectionFactory = PeerConnectionFactory.builder().setVideoEncoderFactory(encoderFactory).createPeerConnectionFactory()
public class DefaultVideoEncoderFactory implements VideoEncoderFactory {/*** 硬解件编码工厂*/private final VideoEncoderFactory hardwareVideoEncoderFactory;/*** 软件编码工厂*/private final VideoEncoderFactory softwareVideoEncoderFactory = new SoftwareVideoEncoderFactory();public DefaultVideoEncoderFactory(Context eglContext, boolean enableIntelVp8Encoder, boolean enableH264HighProfile) {//创建硬解编码工厂this.hardwareVideoEncoderFactory = new HardwareVideoEncoderFactory(eglContext, enableIntelVp8Encoder, enableH264HighProfile);}/*** 注意这个构造方法仅包可见*/DefaultVideoEncoderFactory(VideoEncoderFactory hardwareVideoEncoderFactory) {this.hardwareVideoEncoderFactory = hardwareVideoEncoderFactory;}...
}
/*** 用于创建视频编码器工厂*/
public interface VideoEncoderFactory {/** * 为给定的视频编解码器创建一个编码器。*/@Nullable@CalledByNativeVideoEncoder createEncoder(VideoCodecInfo var1);/*** 枚举支持的视频编解码器列表。这个方法只会被调用一次,结果将被缓存。*/@CalledByNativeVideoCodecInfo[] getSupportedCodecs();@CalledByNativedefault VideoCodecInfo[] getImplementations() {return this.getSupportedCodecs();}@CalledByNativedefault VideoEncoderFactory.VideoEncoderSelector getEncoderSelector() {return null;}
}

关键在于getSupportedCodecs() 在 HardwareVideoEncoderFactory中是如何实现的

@Override
public VideoCodecInfo[] getSupportedCodecs() {// Android19以下不支持硬解编码.if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {return new VideoCodecInfo[0];}List<VideoCodecInfo> supportedCodecInfos = new ArrayList<>();// 按优先顺序生成支持的编解码器列表:// VP8, VP9, H264 (high profile), and H264 (baseline profile).for (VideoCodecType type : new VideoCodecType[]{VideoCodecType.VP8, VideoCodecType.VP9, VideoCodecType.H264}) {//查找编解码器类型,这里是关键MediaCodecInfo codec = findCodecForType(type);if (codec != null) {String name = type.name();// supported by the decoder.if (type == VideoCodecType.H264 && isH264HighProfileSupported(codec)) {supportedCodecInfos.add(new VideoCodecInfo(name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true)));}supportedCodecInfos.add(new VideoCodecInfo(name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false)));}}return supportedCodecInfos.toArray(new VideoCodecInfo[0]);
}

findCodecForType(VideoCodecType),根据类型查找支持的编解码器

private MediaCodecInfo findCodecForType(VideoCodecType type) {for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {MediaCodecInfo info = null;try {info = MediaCodecList.getCodecInfoAt(i);} catch (IllegalArgumentException e) {//无法检索编码器编解码器信息Logging.e(TAG, "Cannot retrieve encoder codec info", e);}//编解器信息为null,或者不是编解码器不是编码器if (info == null || !info.isEncoder()) {continue;}//判断编解码器是否支持,这里就会去判断不同的芯片组是否支持if (isSupportedCodec(info, type)) {return info;}}return null; // 不支持的类型
}

isSupportedCodec(MediaCodecInfo, VideoCodecType):判断MediaCodecInfo和VideoCodecType结合设备芯片组信息是否支持

private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecType type) {if (!MediaCodecUtils.codecSupportsType(info, type)) {return false;}// Check for a supported color format.if (MediaCodecUtils.selectColorFormat(MediaCodecUtils.ENCODER_COLOR_FORMATS, /*这一步其实就可以判断编解码器是否支持了给定的类型了,如果不抛异常的话*/info.getCapabilitiesForType(type.mimeType()))== null) {return false;}return isHardwareSupportedInCurrentSdk(info, type) && isMediaCodecAllowed(info);
}/*** 结合当前的sdk,再次判断是否支持*/
private boolean isHardwareSupportedInCurrentSdk(MediaCodecInfo info, VideoCodecType type) {switch (type) {case VP8:return isHardwareSupportedInCurrentSdkVp8(info);case VP9:return isHardwareSupportedInCurrentSdkVp9(info);case H264:return isHardwareSupportedInCurrentSdkH264(info);}return false;
}private boolean isHardwareSupportedInCurrentSdkH264(MediaCodecInfo info) {//H264 硬件在此类型设备上可能表现不佳。"SAMSUNG-SGH-I337", "Nexus 7", "Nexus 4"if (H264_HW_EXCEPTION_MODELS.contains(Build.MODEL)) {return false;} else {String name = info.getName();//问题就在这,写死的仅支持的硬件编码器解码器组件名称的前缀。//所以要在后面自行追加我们自己设备支持H264名称信息。return name.startsWith("OMX.qcom.") && VERSION.SDK_INT >= 19 || name.startsWith("OMX.Exynos.") && VERSION.SDK_INT >= 21;}
}
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.csdn.net/csdn_shen0221/article/details/119982257

Github传送门

问题二:拉流成功出现没有声音

因为没有声音只有在mtk的设备上没有,在我其他的高通处理器手机是正常的,我们一开始想到的可能就是收到的流和编解码问题,还是比较相信webRtc的sdk没有问题的,事实证明打脸了

解决思路:

1、拦截拉流时的音频流,通过反射回调拿到PCM数据,对pcm进行播放

 audioDeviceModule = JavaAudioDeviceModule.builder(applicationContext).setSamplesReadyCallback {//音频输入数据,麦克风数据,原始pcm数据,可以直接录制成pcm文件,再转成mp3val audioFormat = it.audioFormatval channelCount = it.channelCountval sampleRate = it.sampleRate//pcm格式数据val data = it.data}.setAudioTrackStateCallback(object : JavaAudioDeviceModule.AudioTrackStateCallback {override fun onWebRtcAudioTrackStart() {audioDeviceModule.setAudioTrackSamplesReadyCallback {//音频输出数据,通话时对方数据,原始pcm数据,可以直接录制成pcm文件,再转成mp3val audioFormat = it.audioFormatval channelCount = it.channelCountval sampleRate = it.sampleRate//pcm格式数据val data = it.data} 
}override fun onWebRtcAudioTrackStop() {}}).createAudioDeviceModule()

回调数据拿到了pcm的信息audioFormat:2  channelCount:1  sampleRate:48000

a.直接使用AudioTrack播放 data,在高通手机可以,但是在mtk平台不行

b.生成.pcm文件,两设备都可以播放

2、替换播放器,使用编译openSL ES播放文件流,可以播放,但是集成到项目里就不行了,这时候可以想到,应该时webrtc内部做了处理,在拉流播放的时候会关闭其他所有的声道播放

3、使用蓝牙进行播放, mtk设备使用蓝牙进行语音通话有声音,这时候立马想到数据流声道的问题

解决方法:查资料发现webrtc播放音频流是在WebRtcAudioTrack中实现的,点击进去看到也是使用AudioTrack进行播放的,使用了AudioManager.STREAM_VOICE_CALL声道,我们类搜索将所有的Call声道换成AudioManager.STREAM_MUSIC声道,以及AudioAttributes.USAGE_VOICE_COMMUNICATION改成AudioAttributes.USAGE_MEDIA

打开通话测试,冒得问题了

一定不要遗漏了AudioAttributes.USAGE_VOICE_COMMUNICATION

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

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

相关文章

锻造船用发动机动力系统,铸强船舶“心脏”

船舶是海洋、湖泊及河流中重要的水上交通工具&#xff0c;不仅能够促进海上经济的发展&#xff0c;还能够保卫国家的制海权。船舶动力装置&#xff0c;也就是船舶的核心动力源——船用发动机动力系统对船舶的重要作用不言自明&#xff0c;关系到船舶的性能质量&#xff0c;能够…

uniapp 自定义导航栏增加首页按钮,仿微信小程序操作胶囊

实现效果如图 抽成组件navbar.vue&#xff0c;放入分包 <template><view class"header-nav-box":style"{height:Props.imgShow?:statusBarHeightpx,background:Props.imgShow?:Props.bgColor||#ffffff;}"><!-- 是否使用图片背景 false…

WPF中的VisualState(视觉状态)

以前在设置控件样式或自定义控件时&#xff0c;都是使用触发器来进行样式更改。触发器可以在属性值发生更改时启动操作。 像这样&#xff1a; <Style TargetType"ListBoxItem"><Setter Property"Opacity" Value"0.5" /><Setter …

Java 实现手机号码归属地查询

1.pom坐标 <dependency><groupId>com.googlecode.libphonenumber</groupId><artifactId>geocoder</artifactId><version>2.205</version></dependency> 2.代码 package test;import com.alibaba.excel.util.StringUtils; im…

C 进阶 — 数据在内存中的存储

C 进阶 — 数据在内存中的存储 主要内容 1、数据类型详细介绍 2、整形在内存中的存储&#xff1a;原码、反码、补码 3、大小端字节序介绍及判断 4、浮点型在内存中的存储解析 一 数据类型介绍 基本内置类型 char //字符数据类型 1 short //短整型 …

工作:SolidWorks从3D文件导出2D的DWG或DXF类型文件方法

工作&#xff1a;SolidWorks从3D文件导出2D的DWG或DXF类型文件方法 SolidWorks从3D文件导出2D的DWG或2D DXF类型文件方法&#xff08;一&#xff09;打开3D文件&#xff08;二&#xff09;从装配体到工程图&#xff08;三&#xff09;拖出想要的角度的图型&#xff08;四&#…

Linux-PWM驱动实验

在裸机篇我们已经学习过了如何使用 I.MX6ULL 的 PWM 外设来实现 LCD 的背光调节&#xff0c;其实在 Linux 的 LCD 驱动实验我们也提到过 I.MX6ULL 的 PWM 背光调节&#xff0c;但是并没有专门的去讲解 PWM 部分&#xff0c;本章我们就来学习一下 Linux 下的 PWM 驱动开发。 PWM…

Mysql学习-Mysql查询(1)

1.基本查询&#xff08;SELECT&#xff09; SELECT语句基本格式&#xff1a; SELECT {*|<字段列表>} [ FROM<表1>&#xff0c;<表2>.. [WHERE <表达式> [GROUP BY<group by definition>] [HAVING <expression>[{<operator><exp…

深入解析ETL与ELT架构:数据集成技术的演进与发展

摘要&#xff1a;随着大数据时代的到来&#xff0c;数据集成成为企业信息化建设的重要环节。本文将深入探讨ETL与ELT两种架构&#xff0c;分析它们在数据处理、性能、可扩展性等方面的差异&#xff0c;为企业数据集成提供技术指导。 一、引言 在大数据时代&#xff0c;企业需要…

探索自然语言处理奥秘(NLP)

摘要 自然语言处理&#xff08;NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;它致力于使计算机能够理解、解释和生成人类语言。这项技术让机器能够阅读文本、听懂语音&#xff0c;并与人类进行基本的对话交流。 通俗理解 自然语言处理&#xff08;NLP&#xff09…

product/admin/list?page=0size=10field=jancodevalue=4562249292272

文章目录 1、ProductController2、AdminCommonService3、ProductApiService4、ProductCommonService5、ProductSqlService https://api.crossbiog.com/product/admin/list?page0&size10&fieldjancode&value45622492922721、ProductController GetMapping("ad…

Appium:安装uiautomator2失败

目录 1、通过nmp安装uiautomator2&#xff1a;失败 2、通过 Appium 的平台直接安装驱动程序 3、通过pip 来安装 uiautomator2 1、通过nmp安装uiautomator2&#xff1a;失败 我先是通过npm安装的uiautomator2&#xff0c;也显示已经安装成功了&#xff1a; npm install -g …

【Golang】Go语言编程思想(二):函数式编程

函数式编程 函数与闭包 支持函数式编程的语言当中&#xff0c;函数是一等公民&#xff0c;参数、变量、返回值都可以是函数。 以 adder 为例&#xff0c;下例实现了一个函数式编程&#xff1a; package mainimport "fmt"func adder() func(int) int {sum : 0retu…

摄影后期学什么_好学吗?

当你按下相机快门&#xff0c;捕捉到那珍贵的瞬间&#xff0c;摄影可还没画上句号哦&#xff01;摄影后期就像是一场神奇的魔法秀&#xff0c;能让你的照片从平凡瞬间变身惊艳大片。那在这场魔法之旅中&#xff0c;咱们得学习哪些厉害的法术呢&#xff1f; 先来说说光影调整这…

windows11 安装nginx 教程(并开机自启动)

windows11 安装nginx 教程&#xff08;并开机自启动&#xff09; 1、官网下载安装 官网地址&#xff1a;http://nginx.org/en/download.html 2. 找个文件位置解压压缩包 3 启动NGINX 4 相关命令 查詢端口被占用的进程 netstat -ano | findstr "80"1、查看nginx的…

如何在谷歌浏览器中启用双重身份验证

在当今数字化时代&#xff0c;保护个人信息的安全变得尤为重要。双重身份验证&#xff08;2FA&#xff09;是一种增强账户安全性的有效方法&#xff0c;通过要求用户在输入密码之外&#xff0c;还需提供第二种身份验证信息&#xff0c;从而大大降低了账户被非法访问的风险。本文…

高级 CEF 内核集成与 VC++——CEF系统架构与开发环境搭建

目录 1. 系统架构总体设计&#xff1a;CEF 内核与 VC 主程序分层架构设计 1.1 多进程架构设计 1.2 主程序与 CEF 内核的分离 1.3 UI 设计与布局 1.4 性能与资源管理 2. 分进程设计与通信机制&#xff1a;渲染进程与主进程分离&#xff0c;保证安全性与稳定性 2.1 分进程…

RTCMultiConnection 跨域问题解决

js套件地址 https://github.com/muaz-khan/RTCMultiConnection server套件地址 https://github.com/muaz-khan/RTCMultiConnection-Server 要解决的就是server代码的跨域问题 原装写法&#xff1a; 解决写法&#xff1a; // 喜欢组合语法的自己组 const io new ioServer.S…

【Flink】Flink Checkpoint 流程解析

Flink Checkpoint 流程解析 Checkpoint 流程解析 Flink Checkpoint 流程解析Checkpint 流程概括Checkpoint 触发流程解析 (Flink 1.20)任务启动后 JobManager 开始定期对任务执行 CheckpointJobManager 使用 CheckpointCoordinator 触发 CheckpointCheckpointCoordinator 初始化…

Redis探秘Sentinel(哨兵模式)

概述 Redis的高可用机制有持久化、复制、哨兵和集群。其主要的作用和解决的问题分别是&#xff1a; 持久化&#xff1a;持久化是最简单的高可用方法(有时甚至不被归为高可用的手段)&#xff0c;主要作用是数据备份&#xff0c;即将数据存储在硬盘&#xff0c;保证数据不会因进程…