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 sdp中m=video无H.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
相关文章:
android WebRtc 无法推流以及拉流有视频无声音问题
最近在开发使用WebRtc进行视频通话和语音通话,我使用的设备是MTK的手机,期间后台的技术人员几乎没法提供任何帮助,只有接口和测试的web端,有遇到不能推流。推流成功网页端有画面有声音,但是安卓端有画面,没…...
【5G】Spectrum 频谱
频谱是移动运营商的关键资产,可用的频谱是定义移动网络容量和覆盖范围的重要因素。本章讨论了5G的不同频谱选项、它们的特性以及5G早期部署阶段的预期频谱。5G是首个旨在利用大约400 MHz到90 GHz之间所有频段的移动无线系统。5G还设计用于在许可、共享和非许可频谱带…...
Flink学习连载文章11--双流Join
双流 Join 和两个流合并是不一样的 两个流合并:两个流变为 1 个流 union connect 双流 join: 两个流 join,其实这两个流还是原来的,只是满足条件的数据会变为一个新的流。 可以结合 sql 语句中的 union 和 join 的区别。 在离线 Hive 中&…...
R语言 | 峰峦图 / 山脊图
目的:为展示不同数据分布的差异。 1. ggplot2 实现 # 准备数据 datmtcars[, c("mpg", "cyl")] colnames(dat)c("value", "type") head(dat) # value type #Mazda RX4 21.0 6 #Mazda RX4 Wag …...
16-03、JVM系列之:内存与垃圾回收篇(三)
JVM系列之:内存与垃圾回收篇(三) ##本篇内容概述: 1、执行引擎 2、StringTable 3、垃圾回收一、执行引擎 ##一、执行引擎概述 如果想让一个java程序运行起来,执行引擎的任务就是将字节码指令解释/编译为对应平台上的本地机器指令才可以。 简…...
解决Windows与Ubuntu云服务器无法通过Socket(udp)通信问题
今天在写Socket通信代码的时候,使用云服务器自己与自己通信没有问题,但是当我们把客户端换为Windows系统的时候却无法发送信息到Linux当中,耗时一上午终于搞定了😒。 问题: 如上图,当我在windows的客户端…...
Mysql 中的锁机制
在 MySQL 中,锁是一种机制,用于管理并发访问以确保数据的一致性和完整性。MySQL 支持多种类型的锁,主要分为以下几类: 全局锁:锁定整个数据库,适用于备份等操作,期间禁止所有其他操作。表级锁&…...
12月第1周AI资讯
阅读时间:3-4min 更新时间:2024.12.2-2024.12.6 目录 OpenAI CEO Sam Altman 预告“12天OpenAI”系列活动 腾讯HunyuanVideo:130亿参数的开源视频生成模型 李飞飞的World Labs发布空间智能技术预览版 中科院联手腾讯打造“AI带货王”AnchorCrafter OpenAI CEO Sam Alt…...
【音频识别】数据集合集!
本文将为您介绍经典、热门的数据集,希望对您在选择适合的数据集时有所帮助。 1 Chenyme-AAVT 更新时间:2024-08-23 访问地址: GitHub 描述: 这是一个全自动(音频)视频翻译项目。利用Whisper识别声音,AI…...
Nginx核心配置详解
一、配置文件说明 nginx官方帮助文档:nginx documentation nginx的配置文件的组成部分: 主配置文件:nginx.conf子配置文件: include conf.d/*.conffastcgi, uwsgi,scgi 等协议相关的配置文件mime.types:…...
智能工厂的设计软件 用“力force”的性质构造智能体原型
本文要点 在“智能工厂的设计软件”中 我将对力的研究分为三个领域:经典力学,相对论力学和量子力学,每个研究领域都涉及到force自身性质所具有两个侧面: 明示高度内聚的不可观测的内部表征-“互相性”(哲学性质/哲学…...
Apache AGE:基于PostgreSQL的图数据库
Apache AGE(A Graph Extension)是一个基于 PostgreSQL 的图数据库。它以扩展插件的形式提供,可以在利用 PostgreSQL 先进的 SQL 查询功能和事务支持的同时,享受图数据库的灵活性和可扩展性。 Apache AGE 最初由 Bitnine Global In…...
RabbitMQ延迟消息的实现
RabbitMQ延迟队列的实现 延迟消息是什么延迟消息的实现死信交换机代码实现 延迟消息插件 延迟消息是什么 延迟消息是将消息发送到MQ中,消费者不会立即收到消息,而是过一段时间之后才会收到消息,进行处理。在一些业务中,可以用到延…...
SAP在中国:助力企业跨越成长的新篇章
在当今这个数字化转型风起云涌的时代,每一个企业都在寻求更高效、更智能的管理方式,以期在激烈的市场竞争中脱颖而出。在这场变革中,SAP作为全球领先的企业管理软件解决方案提供商,正以其卓越的产品与服务,在中国这片充…...
数据结构代码归纳
线性表 线性表的顺序表示 定义与初始化 typedef struct SqList{ElemType data[MaxSize];//ElemType *data 开动态数组 int length; }Sqlist; void InitList(SqList &L){L.length0;//若静态数组//若动态数组 //L.data(ElemType*)malloc(sizeof(ElemType)*MaxSize); } …...
数仓技术hive与oracle对比(一)
准备 包括软硬件环境、数据、测试数据三方面的准备内容。 环境 虚拟机软件virtualbox7,同样的虚拟机配置:内存2G、cpu一核,物理主机同一台macbookpro(13-2020款),所以硬盘IO读写速度一致。 综上&#x…...
筑起厂区安全--叉车安全防护装置全解析
在繁忙的工业生产领域中,叉车作为搬运工,穿梭于仓储与生产线之间。然而,叉车的高效运作背后,也隐藏着诸多安全风险,尤其是在那些空间狭小、物流繁忙的环境中。为了降低这些潜在的危险,叉车安全防护装置便成…...
深入浅出云计算 ---笔记
这是博主工作闲时的一些日常学习记录,有些之前很熟悉的,但工作中不常用,慢慢就遗忘了,在这里记录,也是为了激励自己坚持复习,如果有能帮到你,那我将感到非常的荣幸~ 快速到达↓↓↓ IaaS篇>&…...
ARINC 标准全解析:航空电子领域多系列标准的核心内容、应用与重要意义
ARINC标准概述 ARINC标准是航空电子领域一系列重要的标准规范,由航空电子工程委员会(AEEC)编制,众多航空公司等参与支持。这些标准涵盖了从飞机设备安装、数据传输到航空电子设备功能等众多方面,确保航空电子系统的兼…...
SNMP 协议介绍
SNMP 协议详细介绍 SNMP(Simple Network Management Protocol,简单网络管理协议)是一个用于管理和监控计算机网络设备(如路由器、交换机、服务器等)的协议。它允许网络管理员通过网络查看和控制这些设备的状态、配置和性能。 SNMP 协议定义了网络设备如何与管理系统进行通…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
