苹果爱拍微信发语音对方听不到听不到语音怎么回事

爱拍微信怎样查找以前的聊天记录_百度知道
爱拍微信怎样查找以前的聊天记录
我有更好的答案
高兴您解答登录微信账号点击设置选择退登录步骤主要微信系统文件root备份文件删除新乱码文件原备份文件复制文件夹没删除定要完全覆盖通USB线连接打\sdcard\Tencent\MicroMsg 文件夹删除新备份文件前备份旧聊记录文档复制进没删除定要完全覆盖完关闭文件夹重新登录微信聊记录恢复若满意请点击右侧【采纳答案】若问题请点击【追问】希望我答您所帮助望采纳
采纳率:91%
来自团队:
为您推荐:
其他类似问题
您可能关注的内容
聊天记录的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。你的偶像正在直播中,
可用的推荐票
苹果手机怎样找回删除了的微信聊天记录
礼物加载异常,请点击重试
礼物正在加载中,请稍后......
加载失败,请点击重试
(错误代码:001)
登录后查看你的排名
我未上榜╮(╯▽╰)╭
最佳战绩:
本视频还没有人送礼╮(╯▽╰)╭
有钱任性,赏TA一个礼物呗~
加载失败,请点击重试
(错误代码:001)
0人正在看,条弹幕
本作品还没有弹幕╮(╯▽╰)╭
等你来一发!
加载失败,请点击重试
(错误代码:001)
投推荐票最多的观众
送礼值最高的观众
加载异常,请点击重试
(错误代码:005)
同专辑作品
作者更多作品
我来说两句...
现在还没有评论哦~快来抢沙发!
加载异常,请刷新重试
王者荣耀星耀榜&&排名&第50名
加载异常,请点击重试
(错误代码:005)
视频作者排行
新的一周开始啦,速来打榜赢福利!
加载失败,请点击重试
(错误代码:001)
加载失败,请点击重试
(错误代码:001)
版权声明:
未经爱拍原创授权,任何第三方不得以转载、修改、复制、出版及其他方式使用本站视频。
确定取消关注TA (⊙o⊙)?
我再想想心意已决
下载爱拍APP
粤网文[8号
爱拍网违法和不良信息举报电话:
举报邮箱:
版权所有 广州爱拍网络科技有限公司
关注爱拍公众号
微信扫码关注或微信
搜索 “爱拍原创”
关注爱拍官方微博
微博扫码关注或微信下拉不能拍小视频是怎么回事 微信下拉没有小视频了吗_西西软件资讯
西西软件园多重安全检测下载网站、值得信赖的软件下载站!
→ 微信下拉不能拍小视频是怎么回事 微信下拉没有小视频了吗
v6.5.4 官网最新版
类型:社交聊天大小:38.7M语言:中文 评分:9.3
近日一些苹果用户反映最新版下拉不能拍小视频了,这是怎么回事呢,下拉不能拍小视频是怎么回事,下拉没有小视频了吗,下面小编就给大家带来详细的介绍。微信官方最新版:西西小编要告诉大家的是ios版微信已经取消了微信下拉小视频功能哦,那么微信为什么要取消微信下拉拍小视频功能呢小视频发送入口有两个:一个是顶上下拉发小视频,另一个是朋友圈右上角发小视频,请问你认为哪个入口使用人数更多呢?停顿两秒让自己思考下吧。朋友圈右上角占 95%,上方下拉只占了 5%,朋友圈右上角加号入口虽然更深,但用户已形成通过右上角加号发新内容的操作体验,而下拉体验用户没有形成习惯。Genie是腾讯唯一一个p4(专家级)女产品经理,也是我知道的唯一一个p4产品经理,是从无到有搭建出微信的产品负责人,如果张小龙是上帝,那genie就是为上帝造人的那个人,坊间称之为“天朝第一产品经理”,这是第一次genie和大家系统的讲述微信的产品原则吧,这要感谢“产品+”这个课程,不仅都是干货,而且都是高品质的精华。最难的是大概道理我们都懂,但没抽离出来形成自己的产品原则,更难的是,你都知道,但是没消化成自己的东西,在做产品策划的时候还在不断犯这些错误。对于熟人社交而言,三个价值导向:1、越亲密的关系越有价值2、越近的消息越有价值(所有最近的消息倒序在最顶上)3、越原创的内容越有价值根据这三条,在创建产品时很多产品逻辑和后台算法就能清晰的做出权重分配排序了。微信十条产品原则:1. 隐私vs便利:隐私重要性大于便利。案例:常有用户抱怨为什么换一台手机所有聊天记录都没了,为什么登陆网页版每次聊天记录都是从零开始,为什么聊天记录不能每次同步存储,因为一旦换设备/微信网页版每次登陆都显示之前的聊天记录,很可能别人在其他设备登陆你微信,所有记录都看到了,这种隐私泄漏给你带来的风险比聊天记录清零带来的伤害大得多,基于隐私性大于便利性原则,即使聊天记录清零可能带来不便利,依旧优先隐私性。所以微信在手机本地聊天记录不保存,宁可不够便利,也不牺牲隐私体验;2.发送方vs接收方:你觉得发送方和接收方哪个更重要?保护两端感受,但当两方有冲突时,更保护的是接收方,微信的产品理念认为接收方体验大于发送方。案例:a.因为这个产品原则,所以微信到现在没做已读体验。而阿里做社交的产品理念认为发送方比接收方重要,所以之前阿里的叮叮(不确定来往是不是)每发一条信息都会告知你已读未读状态,这让发送方很爽,我希望知道你是否看到了我的信息,但让接收方很有压力,因为我看到了不代表我现在想回你,但你知道我看到了,我不回你就显得没礼貌了,所以,用的时候压力很大,老板喜欢这样的功能,员工用的很受罪。所有产品体验都是基于产品原则作出的决策,对于从无到有创造一个产品,需要先建立起自己的产品原则。b.接收方体验优于发送方第二个细节:白底黑字比绿底黑字更清楚,所以微信里白底黑字展示给了好友发的信息,而自己发的话自己本来就清楚,所以自己发的文字底色是绿底黑字。c.接收方体验优于发送方第三个细节:很多人提为什么短视频不支持自拍功能,因为自拍需求大多是女生的需求,这让自拍的人挺爽,但试想一下当朋友圈被各种妹子自拍占领,就一个头,还会动,对于接收方而言,其实看的没那么爽,而且还挺恐怖,所以考虑到接收方体验没有做小视频自拍3.缺乏价值支撑的流量,事倍功半:这句话需要很深入的去理解,因为我们经常犯这样的错误,现在大多数app的思路还是引流,拉下载,框用户,流量是一千万有1%的用户转化,就有10万真实用户,所以不断找流量,而大多流量被浪费了,如果将思路放在提高被浪费的99%的转化,走精准路线,或许100万流量就能达到10万用户了;除此外,会员增值/游戏付费/谈n个风投也是一样的道理,基于拉过来的人多了,里面总有愿意掏钱的,走的都是海量流量低转化率的思路,这种就是缺乏价值支撑的流量,太浪费了,而小而精的模式就是公测1000人,500人活跃,200人愿意付费,这种就是现在越来越多人走的垂直化精细化的运营方式,不需要买那么多没用的流量,保证高转化就行。4.对用户而言固定路径是最近路径:案例:很多人在问为什么不能让最近发送的表情在最近的位置;原因是每次发表情都会改变表情顺序,每次打开表情顺序都被改动,反而会延迟找到想要表情的时间,觉得表情不好用。最快的路径永远是固定的路径。5.不一定按数据说话,按用户需求和价值说话:这条我是真的很佩服微信,是微信很牛的地方,也是绝大部分产品很难做到的地方。我们习惯了一切按数据说话,数据不好的产品就下线,数据不好的功能就下架,不赚钱的东西就撤掉,也是因为这么浮躁的心态,让极致的好产品出不来。案例:小视频发送入口有两个:一个是顶上下拉发小视频,另一个是朋友圈右上角发小视频,请问你认为哪个入口使用人数更多呢?停顿两秒让自己思考下吧。答:朋友圈右上角➕占95%,上方下拉只占了5%,朋友圈右上角加号入口虽然更深,但用户已形成通过右上角加号发新内容的操作体验,而下拉体验用户没有形成习惯。那么,为什么只有5%的用户通过下拉来拍摄小视频,还没把这个入口干掉,如果按数据说话应该干掉这个入口,那为什么没干掉?答:因为我们不是完全按数据说话的产品,更重视用户体验:主界面快捷方式对于要拍摄稍纵即逝的瞬间,需要最短路径马上拍摄,如果撤掉,当遇到非常好瞬间要拍摄的时候,拍摄路径太长会导致错过很多好的瞬间,并且这些稍纵即逝的瞬间的内容价值可能远高于慢慢从固定路径录制的内容价值。Os:清醒地知道产品每个功能的价值,数据只是反应现状的参考指标,而不是结果本身。6.效率价值对大化value/time(单位时间内的信息量):文字图片url视频哪个性价比最高,哪个信息量最大:url信息量最大,图片性价比最高,视频的信息量很大但性价比最低。微信里体现价值信息最大化原则案例:1.朋友圈点赞没有头像,2.小视频自动播放,3.url的弱化,4.文字太长时折叠,5.单图vs多图:单图缩略图很大,最高效率,不用点开大图也可以看清楚,而多图的时候会变成小的缩略图,这时候接收方的诉求是要第一时间知道这九张图核心要说的信息是什么,点开大图再看具体内容。7.不同很易,更好很难案例1:当时apple watch邀请微信做一个适合watch的产品,第一个版本微信做了雷达加好友,放在watch上看很创新又帅气,但apple没有采用雷达加好友这个方案,why?答:1.附近没几个人带手表,2.在手机上都没几个人用雷达加好友,何况手表上呢,这个属于然并卵的功能。比如新浪微博做的watch版app的功能是跑步记步功能,但微博属于信息类的,做个计步器和微博有啥关系,无法体现产品核心价值;所以最后还是选择把收发消息,看朋友圈,赞等基础核心功能发上去,做实用性的东西比做帅气不同的东西更有价值,不为创新而创新,不为不同而不同。案例2:为什么要做小视频:1.视频信息含量是最高的,是文字和图片无法比拟的;2.有些场景是很难用文字描述的,视频可以解决;而做小视频是因为大视频信息量很大,收看时间长,信息价值不高,所以用6秒小视频来做到既有信息量,又保证信息价值。反例是qq空间,空间一直有长视频,看到微信出了短视频,也把自己的长视频改成短视频,结果被用户投诉的要死,这就是产品经理没有想清楚自己的产品定位,一味模仿追随,空间最擅长的是沉淀,长视频是最好的沉淀形式之一,更别说空间还有pc阅读特性等差别。不同很易,更好很难。微信最大的价值是有关系链有朋友,不用通过和同类产品比内容价值来凸显自己:我不是为了拍一个有趣的视频给你,而是告诉你我在哪在干嘛。如果微信要做小视频,要做的是信息,而不是内容,不是视频美化。很清晰的看到自己的位置,不盲目的和同类产品攀比,从来不是与外部赛跑,而是与自己pk的过程。小视频定位:极快极易高价值核心定位是消息朋友圈8.简洁的原则:简洁不是简单,简洁不是简单的把功能裁掉。案例:为什么iPhone刚开始的icon是拟物设计,这种设计实际是很复杂的,在一个图标上每个角度纹理都要处理,但对于用户而言拟物很真实,学习成本更低,在智能手机刚面世的时候可以降低学习成本。为什么现在的iPhone要做扁平化,不再坚持拟物:因为智能机的普及,每个icon是做什么的,用户已经都理解了,不需要再用拟物的方式去普及,而扁平的icon并没有比拟物的简单,反而在设计上可能是更复杂了,需要用更简洁的方式让用理解icon。9.逻辑原则vs线性原则:避免用tabs,保持一个入口。如果你有两个tab的话,就会有一个主tab,按已有的数据显示2个tab会二八分配,80%的流量在主tab,只剩下20%的流量到第二个tab。如果你已经能决定哪个tab是核心,那为什么还需要第二个tab,如果你没办法决定哪个最重要,那分tab也没办法为你决定,还是会二八分流,专注主要功能,把所有流量聚焦在一个tab,不作无谓分流。不用多tab展示,一个地方不要两个按钮。(这点很多app都在犯这个错误)。在逻辑原理和线性原则相冲突的时候,优先线性原则:案例:微信的搜索原本是放在顶上加号旁边放一个放大镜的icon,但最后还是把搜索框直接加在聊天记录顶部的搜索框,而不是右上角放一个➕一个两个icon(抗拒两个icon,坚持线性原则)10.真实vs噱头:真实性大于噱头,真实的内容才有生命力,噱头往往是对信息的破坏。案例:美拍等视频拍摄工具都有配音,加特效等功能,美化后让整个视频变的很好看,但微信不会做这些,因为美化后的视频,去掉了声音等,无法还原给朋友传递信息的真实现场。真实性还体现在微信的所有数据,各种对外的方式都秉持真实性。打磨精品,注重产品细节注重细节:收到多条语音,会自动播放语音;语音可以上滑取消等极致的细节分享:细节1:小视频播放完后从最后一秒到第一秒循环时直接切换会导致视觉上闪过一下,脑子里会闪过一条白线,为了让这个闪不那么不舒服,在小视频最后一秒做了颜色渐弱,通过一秒弱化让用户视觉舒服地过渡。绝大部分用户是不会感知到微信做了这个体验优化的,好的产品让人用的爽,而不需要让人知道他们为什么爽。细节2:视频和照片的夜视效果在爱疯上不是特别好,所以微信的小视频和照片在监测到光线比较暗时,会提示加亮效果(为了这个小细节微信团队找过世面上所有产品比对各种相机产品,将加亮模式做到极致)常见的坑和注意事项:1.从目标倒推方案:比如我们要50万用户,那倒推要三件事每件事引流10万20万20万,按此方式可能会达到kpi目标,但很可能会背离你设计这个产品的初衷,用手段而不是产品功能达到目标,但这不是真的解决问题的方法案例:提升海外活跃度:发现当用户好友数超过15个时,活跃度会增加很多;所以当时的做法是引导加好友,然后又引导加陌生人,好友数还不够,就改版附近的人,这种就是按目标倒推数据方案的反例。数据可以帮助你了解原因,但不会告诉你原因。一个成功的决策,不知道原因,比一个失败的决策更危险。(这点非常重要,很多团队觉得达到kpi了就万事大吉,而不去分析是用的牺牲未来的短期营销达到的,还是真的是产品优质达到的;这个错误是每天每个产品,团队都在犯的,能意识到这点的存在本身已经很不容易。)2.关于AB test(分组测试):AB test可以用来对比效果,而不是选择方案,用的越多,表明产品经理判断力越弱使用AB test时要有很明确的选择,并且知道影响因素是不可控的。3.普通用户没法告诉你他们自己还不知道的需求,需求是产品经理去观察发现的。不要去问用户你觉得我怎么做会比较好,因为他不知道,或者只是他以为他知道。很多人用mac觉得很好用,但在mac出来前你会知道你需要的好用的电脑是酱紫的么?比如很多用户使用任务软件时,主动设置很多任务,并且都加上了提醒,当用户主动加提醒时觉得自己想要被提醒,但当每天都被提醒n次时就开始烦了,关掉提醒甚至直接删除这个app。4.能用标准方法/标准控件解决问题就不要用特殊处理以上是一整个下午听genie分享产品原则记录下来的笔记和心得,也是在听过的众多产品课程里最有收获和启发的一场,有的产品是被抬到神坛的,而有的产品是靠实力走上去的,我从不搞什么个人崇拜,但觉得genie以及微信在产品设计上做出的每个决策都有自己的产品原则作为方向标,是非常值得欣赏的,不是微信从没决策失误过,而是每个精良的产品都是在不断试错中越来越完美的,对于创建一个新产品亦是如此,不怕一遍遍试错,就怕不思考不知道错。很感谢genie和如此好的产品精品课程,受益良多。上述产品原则和体验就我自己而言感悟很深,很多原则需要再多次反复消化,对未来做产品很有启发,被提炼出的真理难的是在从无到有的提炼过程,而享受前人成果时,我们太容易因为理所当然,而忽略了每个真理对于现实运用的意义。
(22) 西西软件园微信抢红包神器专区为您提供微信抢红包神器手机版电脑版微信抢红包软件下载,都是经过小编亲测无毒安全,大家可以放心使用使用教程.........微信自从上线发红包的功能之后就吸引了很多的用户,小编也经常能收到老板们发的红包。但更多的时候由于没有及时登录微信或因为网速手机差的客观因素错失一个个红包。可能很多人都想过,有没有什么软件能够帮助大家自动抢红包呢西西为您来提供微信抢红包神器下载。抢红包技巧确保网...
09-13 / 2.5M
推荐理由:快乐抢红包神器,正品微信抢红包软件,在保证抢红包效果的同时,也不会造成个人信息泄漏,请放心使用!快乐
01-22 / 9.8M
推荐理由:抢红包神器2016是最新的一款安卓手机抢红包软件,这款软件不仅能够自动抢红包,而且内置很多小活动,软件游
10-12 / 2.6M
推荐理由:红包猎人高级版是2017最新的手机抢红包软件,此软件可以自动抢QQ和微信群红包,打开手机后台自动运行软件就
10-10 / 2.6M
推荐理由:乐乐抢红包app是为喜欢抢红包的人群量身打造的一款实用的手机应用软件,用户通过使用乐乐抢红包神器可以方便
01-17 / 512KB
推荐理由:小米红包助手,是小米专为MIUI6,7用户准备的春节抢红包神器,全面支持微信,支付宝等各大平台的抢红包提醒
10-10 / 2.6M
推荐理由:最近快过年了,又到了微信红包发得比较密集的时间段了,但是许多人却因为手不够快而错过了不少红包,那么有
(300) 西西软件园提供微信电脑版微信下载正式版官方免费下载,微信是腾讯公司开发的一款免费语音即时聊天工具,用户可以使用微信很方便的搜索和添加附近使用微信的用户,也可以使用摇一摇和漂流瓶来寻找陌生的人交往和聊天。微信下载手机版,用户可以使用微信免费的发送消息和语音聊天内容。微信不仅仅是聊天工具,更是一种生活方式。微信,已被越来越多的人使用,并将成为未来网络营销一大利器,为了帮助你提升个人行业品牌,让更多的人...
01-30 / 36.2M
推荐理由:微信是TX公司基于QQ开发的一种可以用语音说的软件,不过手机是智能机才可以用的,拥有其独特的摇一摇,漂流瓶功
06-03 / 32.9M
推荐理由:好消息,期待已久的微信电脑版今天终于发布,西西第一时间带来分享给亲们哦!下边带来微信电脑版详细资料及
01-21 / 62.3M
推荐理由:钉钉电脑版是阿里巴巴最新推出的新一代的团队通讯软件,钉钉电脑版是阿里巴巴专为中小企业和团队打造的沟通
07-27 / 36.0M
推荐理由:京东咚咚商家版是京东商城针对商家和用户推出的IM工具,京东IM工具“咚咚”为商家和用户架起沟通桥梁,支持
02-03 / 3.0M
推荐理由:海拓微信助手是一款专业的微信营销工具,软件采用全新的自动化技术帮你处理那些需要大量时间精力的机械操作
01-06 / 15.8M
推荐理由:国外一种老牌的聚合聊天软件最近异常火热,它叫MirandaIM.通过安装丰富的插件可以令MirandaIM(下简称为MIM)支
(34) 微信分身版是一款第三方的微信客户端,这款客户端可以支持用户同时登录多个微信账号,对于经常需要在多个微信号之间切换的微商们来说这款软件非常实用。微信目前不仅是一款社交软件还有很多人将其作为办公的通讯软件使用,因此很多人就会存在两个微信号,一个对公的,一个对私的。经常在两个微信号之间切换非常麻烦,这里西西给大家提供的微信分身版可以让你同时登录两个微信号,这样再也不用考虑登录这个账号收不到另一个账号信息...
01-23 / 38.7M
推荐理由:微信,可以群聊的手机对讲机。微信是腾讯公司推出的一个为智能手机提供即时通讯服务的免费应用程序。微信支
10-14 / 2.6M
推荐理由:逢年过节都是有很多的朋友喜欢发红包的,过年的时候,除了抢红包,我们还有很多事情要忙,和家人团聚,和朋
01-30 / 36.2M
推荐理由:微信是TX公司基于QQ开发的一种可以用语音说的软件,不过手机是智能机才可以用的,拥有其独特的摇一摇,漂流瓶功
11-21 / 38.8M
推荐理由:基于Android平台的腾讯微信服务,带给您全新的移动即时通信体验。您可以使用微信软件快速地发送消息,即时拍
01-30 / 19.8M
推荐理由:微信,是一个生活方式超过三亿人使用的手机应用支持发送语音短信、视频、图片和文字可以群聊,仅耗少量流量
01-24 / 187M
推荐理由:很多使用iPad的用户不知道如何微信,其实微信iPhone版兼容iPad,苹果iPad平板电脑请看下边的安装方法主要功
(21) 腹黑微信又名黑客破解微信,微信在当前国内的地位是不可动摇的,如果无法外部打倒,就要从内部瓦解,当然这些都是玩笑,一群极客们在无聊至极,或心怀远大理想的情况下对微信进行了破解,可以让大家在聊天时记录下所有信息,即使对方即发即撤在你面前也是无用哒,最逆天的就是智能抢红包功能,你可以在秒的时间内进行设置,比如设秒,红包发出最多秒后就能自动被你抢到了。相关版本一开始腹黑微信只有苹果版的,后来为了照顾非水果...
01-24 / 34.0M
推荐理由:微信腹黑版其实就是微信的破解版,因为这个破解版可以查看撤回的消息以及自动抢红包,这款软件是由黑客团队
01-25 / 12.2M
推荐理由:腹黑微信电脑版是一个黑客团队进行破解后的作品,相信此举已经惹火了微信官方,所以大家要早些下载哟,以免
01-25 / 34.0M
推荐理由:黑客破解微信是一款高科技的微信应用,这款微信不同于官方版本,下载安装后,与人聊天,对方再也不能随意给
10-18 / 2.3M
推荐理由:微信多开助手破解版是一款微信多开软件,由于微信的限制,所以正常的话一部手机是只能安装一个微信的,但是
03-11 / 5.8M
推荐理由:行政联盟安卓版是一款非常不错的手机企业办公必备软件,它为企业人员带来了便捷化的功能,服务大家办公变得
01-23 / 38.7M
推荐理由:微信,可以群聊的手机对讲机。微信是腾讯公司推出的一个为智能手机提供即时通讯服务的免费应用程序。微信支
(24) 微信公开课版一经推出便广受欢迎,除了官方的营销策略,更是基于微信的庞大用户群体,这款微信新功能可以帮助用户数据化管理自己的网络生活,虽然推出时间不长,但已经有不少用户刷爆朋友圈了,大家也赶快下载体验吧使用方法微信公开课版怎么用关注公众号微信公开课进去之后就能查看到。微信公开课版暂时不能在电脑上打开,只能在手机上打开,此方法仅用于手机。功能简介在微信中,只要打开微信公开课版活动链接,就可以查看您的微...
01-11 / 1KB
推荐理由:2016微信公开课pro版昨晚刷爆了微信朋友圈,那么2016微信公开课pro版能不能再电脑上测试呢?答案肯定是可以
01-11 / 106M
推荐理由:2016微信公开课pro版是腾讯官方为1月11日维修公开课pro版准备的官方体验活动,2016微信公开课pro版我和微信
01-11 / 106M
推荐理由:2016微信公开课pro版昨天刷爆了朋友圈,小伙伴们纷纷展现了微信的注册时间、微信的第一个好友、查看2015发布
01-11 / 106M
推荐理由:2016微信公开课pro版当前异常火爆,微信作为国内社交行业的巨头,其一举一动都被人所关注,新的2016,新的微
01-11 / 1KB
推荐理由:2016微信公开课pro已经火热推出,当前腾讯官方已经进入营销环节,众小编一片激动,微信将会为这个网络社会带
01-11 / 1KB
推荐理由:2016微信公开课pro版是微信在1月10日晚间推出的一款微信新功能,通过个应用你可以查询自己微信是哪一日注册
11-2711-2611-2611-2611-2611-2311-2011-2011-2011-20
阅读本文后您有什么感想? 已有23人给出评价!
名称大小下载前端(49)
http://www.iosre.com/t/topic/3199
这周做了一个新功能,就是全自动地把微信的语音文件给提取出来,然后转换成文字,一来是一目了然,二来是方便检索。未曾想到的是,在这个过程中,碰到了几个不大不小的坎,踩到了几个不深不浅的坑,在此逐一记录解决方案,供后来人参考。
想要体验这个功能,请参看
定位微信语音文件
通过中提到的思路,定位微信的语音文件并不是什么难事。它们一般在
/WeChat Path/Documents/Random Serial/Audio/Random Serial/random.aud
发出或收到一条语音消息,都会产生一个.aud文件。因为aud不是常见的后缀,我们不知道该用什么方式来播放这条语音,那么就先看看吧。
分析微信语音文件的格式
百度结果很多:
Screen Shot
at 5.49.02 PM.png
可以看到,大多数的帖子,都提到微信语音是amr格式的。其中说,aud就是少了amr文件头的amr文件,只需要在文件开头加上amr标识就可以了:
但是我发现,添加了amr头部的aud文件,还是不能用OSX自带的preview功能播放:
而正常情况下,preview是可以播放amr文件的。这说明,添加了amr标识的文件,并不能被preview正确识别;而这多半是aud文件的问题——它不是amr文件。
我把刚才的aud文件用MacVim重新打开,发现除去我们自行添加的amr头部之外,aud本身含有一个
字样的标识。虽然我对音频编码和文件格式不甚了解,但既然amr文件的头部标识是以#!开头,那么我推测#!SILK_V3应该也是一个文件头。Google之:
Screen Shot
at 6.12.59 PM.png
这样的话,我们基本可以确定微信的语音文件,就是 变种的 silk文件了,我们需要把它修改为纯正的silk文件。为什么说是变种呢?马上就知道了
生成纯正的silk文件
根据果壳网友的分析:
Screen Shot
at 6.29.07 PM.png
微信在aud文件的最开始添加了1个字节,如果把这个字节删掉,这个文件就是一个纯正的silk格式文件。他编译了一个decoder.exe,可以把silk文件给转换成pcm文件;但是我用的是OSX,无法执行exe文件。好么,继续寻找解决方案,把silk文件转换成pcm。
把silk文件转换成pcm文件
又是一顿Google,找到了,博主的目的跟我类似。按照他的操作,先用HomeBrew安装ffmpeg:brew install ffmpeg,然后从上下载SILKCodec工程(值得一提的是,这个工程的主人是一个中国开发者,音频处理达人,我也已经跟他取得了联系,请教一些音频技术上的问题),然后在SILK_SDK_SRC_ARM里先make
lib,再make decoder,即可生成OSX上的命令行工具decoder。对纯正的silk文件执行命令:/path/to/decoder
/path/to/silk path/to/pcm,即可生成pcm文件。
还可以用将silk转化成pcm, 亲测可用。
把pcm文件转换成wav文件
根据刚才那篇博文,用博主的就可以把pcm文件转换成wav文件,而他的解决方案正是ffmpeg:ffmpeg -f s16le -ar 24000 -i /path/to/pcm
-f wav /path/to/wav
播放此wav文件,听到的就是我在微信里发出的语音消息——
“定期整理用户昵称中含有的手机号、用户名、公司。” (267.3
用科大讯飞SDK解析wav文件
从下载讯飞的官方iOS
demo(注意勾选“语音听写”),在下载的工程中打开“MSCDemo -& MSCDemo -& business -& isr -& IATViewController.h”,其中的pcmFilePath即是音频文件的路径。
在IATViewController.m中的第42行,将pcmFilePath改为wav文件的地址,然后点击下图的“音频流识别”:
Simulator Screen Shot Mar 26, .24 PM.png
即可快速测试解析功能(注:讯飞iOS SDK仅支持pcm和wav格式的音频文件):
Simulator Screen Shot Mar 26, .29 PM.png
对比上面的wav文件,可以看到解析的偏差很大。因为讯飞的中文语音识别技术已经是全球领先的了,所以不大可能会出现这么大的识别错误。问了下讯飞的技术人员,得知问题可能在这些方面:
Screen Shot
at 7.51.11 PM.png
好了,继续研究,看看怎么处理。
进一步处理pcm
和爱拍的CTO指点:
得知讯飞哥们提到的“单通道,16位,”各指的是channels、bit rate/sample size和sample rate。它们在ffmpeg里对应的设置方式分别是-ac
channels、-b:a bitrate、-ar
rate。我们来试试看:ffmpeg -f s16le -ar 24000 -i /path/to/pcm
-f wav -ar 16000 -b:a 16 -ac 1 /path/to/wav
识别效果如图:
Pasted image
识别得几乎没有问题了。
因为在上面的识别中,“昵称”被听成了“旅程”,还是有些不爽,于是我又擅自改变ffmpeg的参数,想看看能不能做到100%识别准确。最后发现,按照如下的参数配置,识别率是最高的:ffmpeg -f s16le -ar 12k -ac 2 -i /path/to/pcm
-f wav -ar 16k -ac 1 /path/to/wav
但是,对于这个事实,我只知其然,不知其所以然,还请懂行的高手赐教
在iOS中全自动完成上述操作
总结上面的操作,大致的顺序是:
获取微信语音aud文件;去掉aud文件的第一个字节,将其转换为silk文件;用silk decoder将silk文件转换为pcm文件;用ffmpeg文件将pcm文件转换为wav文件;用讯飞iOS SDK将wav文件转换为文本。
这5步我已经在OSX上用半自动的方式完成了,那么要在iOS中全自动完成,需要做到的,也是5步,与上面一一对应:
hook微信中接收语音信息的函数(提示:这个函数所在的类,是以Mgr结尾的),从中获取aud文件;用NSMutableData处理aud,生成silk;有网友修改过Makefile文件的silk
decoder for iOS,把Makefile中的iOS SDK路径作适当调整,即可编译出iOS上的decoder;值得一提的是,这里的decoder版本是1.0.8而不是最新版的1.0.9;1.0.9的源代码,用1.0.8的Makefile编译会报错失败,具体原因仍待进一步研究。BigBoss源有现成的ffmpeg二进制文件下载;还值得一提的是,iOS 9上通过system()或者posix_spawn()执行命令行工具会碰到一些坑。要解决它们,用到的关键词主要有:沙盒、ldid、RocketBootstrap。讯飞SDK的集成比较简单,用到的主要是[IFlySpeechRecognizer
writeAudio:]及其回调函数。
至此,iOS全自动化解决方案的骨架已经出来了,剩下的肉,就留给各位看官自己来填吧
延伸阅读:
在里,我曾使用讯飞iOS
SDK完成了将微信语音转化为文字的功能。在, 说,微信本身就提供了长按语音转文字的功能,而我在前期研究微信时并没有发现这个功能。经过一番调研,发现只有当系统语言为中文时,微信才会开放这个功能,而我一直使用的是英文系统,所以没看到过这个功能。今天,我们就以这个功能为突破口,摒弃讯飞SDK,采用微信自带的方案,将语音全自动转换为文字。
开启英文版微信的语音转文字功能
这个过程我就不复述了,核心代码是:
%hook VoiceMessageNodeView
- (BOOL)canShowVoiceTransMenu
return YES;
大家编译一个tweak安装一下,就可以在英文版微信里开启这个功能了
IMG_0078.PNG
定位“Convert to Text”这个UIMenuItem的action
照例,我们先用Cycript,定位这个UIMenuItem。停留在上图的界面中,然后用choose命令做一次小小的hack:
cy# choose(UIMenuItem)
[#"&UIMenuItem: 0x&",#"&UIMenuItem: 0x&",#"&UIMenuItem: 0x&",#"&UIMenuItem: 0x&",#"&UIMenuItem: 0x157afe90&"]
cy# [#0x title]
@"Favorite"
cy# [#0x title]
@"Turn Off Speaker"
cy# [#0x title]
@"More..."
cy# [#0x title]
@"Convert to Text"
好了,找到了“Convert to Text”这个UIMenuItem,我们看看它的action是什么:
@selector(onVoiceTrans:)
好了,onVoiceTrans:就是我们的答案了。咱们grep一遍WeChat的头文件,看看哪个类实现了这个方法:
FunMaker-MBP:~ snakeninny$ grep -r onVoiceTrans: /Users/snakeninny/Code/RE/WeChat
/Users/snakeninny/Code/RE/WeChat/VoiceMessageNodeView.h:- (void)onVoiceTrans:(id)arg1;
Binary file /Users/snakeninny/Code/RE/WeChat/WeChat_arm64.decrypted matches
Binary file /Users/snakeninny/Code/RE/WeChat/WeChat_armv7.decrypted matches
看起来是VoiceMessageNodeView这个类。从类名上猜测,这个类应该是语音信息view的类,在后面我们会验证这个猜测。
查看[VoiceMessageNodeView onVoiceTrans:]的实现细节
把decrypt之后的WeChat可执行文件拖到Hopper里,待分析完毕之后,看看它的反编译C代码:
Pasted image
可以看到很明显的2条分支,1条简单调用了showVoiceTransView,另1条的操作相对复杂。我们先从简单的入手,看看showVoiceTransView都干了些什么。
查看[VoiceMessageNodeView showVoiceTransView]的实现细节
Pasted image
从不够完整的函数实现截图来看,showVoiceTransView做的基本都只是UI层面的操作;它们具体是什么呢?等会再揭晓,我们先看另1条相对复杂的分支。
查看else分支的实现细节
从截图里诸如getStringForCurLanguage:defaultTo:、initWithTitle:andImageName:andContent:andCancelText:、m_voiceTransIntro、show等关键词可以大概猜测,这段代码也是show了一个view出来,貌似也是UI层面的操作。如果if和else都是纯UI操作,那么语音转换的核心代码在哪里呢?是时候上LLDB,往更深一层次的代码去挖掘了。
用LLDB分析[VoiceMessageNodeView onVoiceTrans:]
用LLDB附加WeChat,然后在[VoiceMessageNodeView
onVoiceTrans:]上下断点并触发:
(lldb) br s -a 0xx
Breakpoint 1: where = WeChat`___lldb_unnamed_function88615$$WeChat, address = 0x018a2950
Process 3098 stopped
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2950 &+0&: push
{r4, r5, r6, r7, lr}
0x18a2952 &+2&: add
0x18a2954 &+4&: push.w {r8, r10, r11}
0x18a2958 &+8&: sub
我们用ni单步跟一下这个函数,看看每一个objc_msgSend都是在干嘛:
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2974 &+36&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2978 &+40&: mov
0x18a297a &+42&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a297e &+46&: mov
(lldb) p (char *)$r1
(char *) $0 = 0x0237235d "getMainSettingExt"
(lldb) po $r0
SettingUtil
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2998 &+72&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a299c &+76&: mov
0x18a299e &+78&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a29a2 &+82&: mov
(lldb) p (char *)$r1
(char *) $2 = 0x "theadSafeGetObject:"
(lldb) po $r0
&CSettingExt: 0x&
(lldb) po $r2
SETTINGEXT_VOICE_TRANS_TIP_TIMES
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a29b8 &+104&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a29bc &+108&: cmp
0x18a29be &+110&: ble
0x18a29d0 &+128&
0x18a29c0 &+112&: movw
(lldb) p (char *)$r1
(char *) $7 = 0x33c13528 "integerValue"
(lldb) po $r0
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2a04 &+180&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2a08 &+184&: str
0x18a2a0a &+186&: movw
0x18a2a0e &+190&: movt
(lldb) p (char *)$r1
(char *) $9 = 0x33c09603 "alloc"
(lldb) po $r0
MMTipsViewController
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2a26 &+214&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2a2a &+218&: mov
0x18a2a2c &+220&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2a30 &+224&: mov
(lldb) p (char *)$r1
(char *) $11 = 0x33c0c435 "defaultCenter"
(lldb) po $r0
MMServiceCenter
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2a50 &+256&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2a54 &+260&: mov
0x18a2a56 &+262&: movw
0x18a2a5a &+266&: movt
(lldb) p (char *)$r1
(char *) $13 = 0x33c0951e "class"
(lldb) po $r0
MMLanguageMgr
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2a68 &+280&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2a6c &+284&: mov
0x18a2a6e &+286&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2a72 &+290&: str
(lldb) p (char *)$r1
(char *) $15 = 0x0236945e "getService:"
(lldb) po $r0
&MMServiceCenter: 0x156dd840&
(lldb) po $r2
MMLanguageMgr
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2a8e &+318&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2a92 &+322&: mov
0x18a2a94 &+324&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2a98 &+328&: str
(lldb) p (char *)$r1
(char *) $18 = 0x0236a7db "getStringForCurLanguage:defaultTo:"
(lldb) po $r0
&MMLanguageMgr: 0x&
(lldb) po $r2
Voice_Trans_Tips_Title
(lldb) po $r3
Voice_Trans_Tips_Title
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2aa8 &+344&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2aac &+348&: mov
0x18a2aae &+350&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2ab2 &+354&: mov
(lldb) p (char *)$r1
(char *) $22 = 0x33c0c435 "defaultCenter"
(lldb) po $R0
MMServiceCenter
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2ac2 &+370&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2ac6 &+374&: mov
0x18a2ac8 &+376&: mov
0x18a2aca &+378&: mov
(lldb) p (char *)$r1
(char *) $24 = 0x33c0951e "class"
(lldb) po $R0
MMLanguageMgr
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2ace &+382&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2ad2 &+386&: mov
0x18a2ad4 &+388&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2ad8 &+392&: movw
(lldb) p (char *)$r1
(char *) $26 = 0x0236945e "getService:"
(lldb) po $r0
&MMServiceCenter: 0x156dd840&
(lldb) po $r2
MMLanguageMgr
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2ae8 &+408&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2aec &+412&: mov
0x18a2aee &+414&: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2af2 &+418&: mov
(lldb) po $r0
&MMLanguageMgr: 0x&
(lldb) p (char *)$r1
(char *) $30 = 0x0236a7db "getStringForCurLanguage:defaultTo:"
(lldb) po $r2
Voice_Trans_Tips_Content
(lldb) po $r3
Voice_Trans_Tips_Content
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2b10 &+448&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2b14 &+452&: ldr.w
r1, [r4, r8]
0x18a2b18 &+456&: str.w
r0, [r4, r8]
0x18a2b1c &+460&: mov
(lldb) p (char *)$r1
(char *) $33 = 0x023b452b "initWithTitle:andImageName:andContent:andCancelText:"
(lldb) po $r0
&MMTipsViewController: 0x&
(lldb) po $r2
Audio to Text
(lldb) po $r3
(lldb) x/10 $sp
0x27d99c88: 0x160ed0a90 0x
0x27d99c98: 0xx156dd840 0xx
0x27d99ca8: 0xx
(lldb) po 0x160e8500
This feature only supported for Mandarin Chinese, and the result is for reference only.
(lldb) po 0x
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2b5a &+522&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2b5e &+526&: movw
0x18a2b62 &+530&: movt
0x18a2b66 &+534&: add
(lldb) p (char *)$r1
(char *) $40 = 0x0236cc3c "setM_delegate:"
(lldb) po $r0
&MMTipsViewController: 0x&
(lldb) po $r2
&VoiceMessageNodeView: 0x; frame = (183 0; 128 59); layer = &CALayer: 0x&&
WeChat`___lldb_unnamed_function88615$$WeChat:
0x18a2b6e &+542&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2b72 &+546&: mov
0x18a2b74 &+548&: add
0x18a2b76 &+550&: pop.w
{r8, r10, r11}
(lldb) p (char *)$r1
(char *) $43 = 0x33c0f712 "show"
(lldb) po $r0
&MMTipsViewController: 0x&
到此告一段落。可以看到,这个分支其实是走了else的这一段,就是偏复杂的操作。值得注意的是0x18a2b10处的objc_msgSend,生成了一个关键句“This
feature only supported for Mandarin Chinese, and the result is for reference only.”,我们c一下即可看到:
Pasted image
好了,出现了一个WeChat自定义的弹框。我们用Cycript看看,点击“OK”后会触发什么样的操作。
找到点击弹框上“OK”按钮触发的操作
一般来说,弹框都不会出现在keyWindow上,需要到其他的window里找寻它的踪迹;因此,我们依次检查各个window:
cy# [[UIApp windows][1] recursiveDescription].toString()
` 0x; baseClass = UIW frame = (0 0; 320 45); hidden = YES; gestureRecognizers = &NSArray: 0x&; layer =
0x1601bc50; frame = (12.5 2.5; 40 40); opaque = NO; userInteractionEnabled = NO; layer = &CALayer: 0x&&
0x; frame = (295 12.5; 20 20); opaque = NO; layer = &CALayer: 0x&&
0x; frame = (0 0; 20 20); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = &CALayer: 0x&&
0x; baseClass = UIL frame = (65 5; 0 0); opaque = NO; layer = &CALayer: 0x&&`
cy# [[UIApp windows][2] recursiveDescription].toString()
" 0x156acc50; frame = (0 0; 320 480); hidden = YES; opaque = NO; gestureRecognizers = &NSArray: 0x16455e60&; layer =
cy# [[UIApp windows][3] recursiveDescription].toString()
" 0x162e66e0; frame = (0 0; 320 480); hidden = YES; gestureRecognizers = &NSArray: 0x&; layer =
0x1604ba00&&"
cy# [[UIApp windows][4] recursiveDescription].toString()
` 0x162cd5d0; baseClass = UIW frame = (0 0; 320 480); gestureRecognizers = &NSArray: 0x&; layer =
0x1645eb70&&
0x; frame = (0 0; 320 480); autoresize = W+H; layer = &CALayer: 0x&&
0x; frame = (0 0; 320 480); opaque = NO; autoresize = W+H; layer = &CALayer: 0x156dd060&&
0x; frame = (20 144; 280 192); layer = &CALayer: 0x156e3fc0&&
0x; frame = (0 0; 280 192); opaque = NO; userInteractionEnabled = NO; layer = &CALayer: 0x156e3f40&&
0x; baseClass = UIL frame = (15 22; 250 22); text = 'Audio to Text'; userInteractionEnabled = NO; layer = &CALayer: 0x156d7050&&
0x; baseClass = UIL frame = (16.5 59; 247 62); text = 'This feature only support...'; userInteractionEnabled = NO; layer = &CALayer: 0x156e3090&&
0x1604ae00; baseClass = UIB frame = (0 142; 280 50); opaque = NO; layer = &CALayer: 0x156e3480&&
0x; frame = (127 14; 26 22); text = 'OK'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = &CALayer: 0x156de430&&
0x16035ce0; frame = (0 0; 280 0.5); autoresize = W; layer = &CALayer: 0x156da050&&`
cy# [#0x1604ae00 allTargets]
[NSSet setWithArray:@[#" 0x&"]]]
cy# [#0x1604ae00 allControlEvents]
cy# [#0x1604ae00 actionsForTarget:#0x forControlEvent:64]
@["onClickBtn:"]
在第5个window里(注意,[UIApp windows][0]就是keyWindow,因此这里是第5个window),我们找到了OK按钮,且它的响应函数是[MMTipsViewController
onClickBtn:];去看看它的实现细节。
查看[MMTipsViewController
onClickBtn:]的实现细节
(lldb) br s -a 0x01cd64000
Breakpoint 2: where = WeChat`___lldb_unnamed_function108280$$WeChat, address = 0x01d3c120
Process 3098 stopped
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c120 &+0&: push
{r4, r5, r6, r7, lr}
0x1d3c122 &+2&: add
0x1d3c124 &+4&: push.w {r8, r10, r11}
0x1d3c128 &+8&: mov
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c140 &+32&: blx
0x20eb570 symbol stub for: objc_msgSend
0x1d3c144 &+36&: tst.w
0x1d3c148 &+40&: bne
0x1d3c15 &+60&
0x1d3c14a &+42&: movw
(lldb) p (char *)$r1
(char *) $45 = 0x023e6510 "bIsForbidCancelBtn"
(lldb) po $r0
&MMTipsViewController: 0x&
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c158 &+56&: blx
0x20eb570 symbol stub for: objc_msgSend
0x1d3c15c &+60&: movw
0x1d3c160 &+64&: movt
0x1d3c164 &+68&: add
(lldb) p (char *)$r1
(char *) $47 = 0x023e643d "hideTips"
(lldb) po $r0
&MMTipsViewController: 0x&
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c1a4 &+132&: blx
0x20eb570 symbol stub for: objc_msgSend
0x1d3c1a8 &+136&: mov
0x1d3c1aa &+138&: mov
0x1d3c1ac &+140&: blx
0x20eb5c0 symbol stub for: objc_release
(lldb) p (char *)$r1
(char *) $49 = 0x33c098a1 "respondsToSelector:"
(lldb) p (char *)$r2
(char *) $51 = 0x023b4abf "onClickTipsBtn:"
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c1c2 &+162&: blx
0x20eb570 symbol stub for: objc_msgSend
0x1d3c1c6 &+166&: mov
0x1d3c1c8 &+168&: blx
0x20eb5c0 symbol stub for: objc_release
0x1d3c1cc &+172&: mov
(lldb) p (char *)$r1
(char *) $52 = 0x023b4abf "onClickTipsBtn:"
(lldb) po $r0
&VoiceMessageNodeView: 0x; frame = (183 0; 128 59); layer = &CALayer: 0x&&
(lldb) po $r2
注意,这里调用了[VoiceMessageNodeView onClickTipsBtn:],我们把动态调试暂停在这里,去看看[VoiceMessageNodeView
onClickTipsBtn:]的实现细节。
查看[VoiceMessageNodeView
onClickTipsBtn:]的实现细节
Pasted image
这段代码很容易还原,它的核心操作是:
[[%c(SettingUtil) getMainSettingExt] theadSafeSetObject:@"1" forKey:@"SETTINGEXT_VOICE_TRANS_TIP_TIMES"];
AccountStorageMgr *manager = [[%c(MMServiceCenter) defaultCenter] getService:[%c(AccountStorageMgr) class]];
[manager SaveSettingExt];
从函数及参数名来看,这段代码的功能貌似就是作为语音转换的开关。我们继续ni,看看还有没有其他玄机。
继续查看[MMTipsViewController
onClickBtn:]的实现细节
WeChat`___lldb_unnamed_function108280$$WeChat:
0x1d3c1e8 &+200&: blx
0x20eb570 symbol stub for: objc_msgSend
0x1d3c1ec &+204&: mov
0x1d3c1ee &+206&: mov
0x1d3c1f0 &+208&: blx
0x20eb5c0 symbol stub for: objc_release
(lldb) p (char *)$r1
(char *) $55 = 0x33c098a1 "respondsToSelector:"
(lldb) p (char *)$r2
(char *) $56 = 0x023b4acf "onClickTipsBtn:Index:"
(lldb) po $r0
&VoiceMessageNodeView: 0x; frame = (183 0; 128 59); layer = &CALayer: 0x&&
因为[VoiceMessageNodeView respondsToSelector:@selector(onClickTipsBtn:Index:)]的返回值为NO,所以这一大段分析,得出的核心代码,就是上面提取出的这段,且它的功能,就是开启微信语音转换功能的开关。这段代码怎么使用呢?我们只需要在微信第一次启动时调用这段代码,就可以开启微信的语音转换功能了。
开启了语音转换之后,我们取得了阶段性胜利。但是下一个问题来了,哪段代码是负责实际转换操作的呢?这才是我们的重中之重。
找到语音转换操作的核心代码
刚才的[VoiceMessageNodeView onClickTipsBtn:]里,除了开启语音转换功能的代码外,还有一个低调的showVoiceTransView,它的调用者是一个VoiceMessageNodeView对象,我猜语音转换操作的核心代码就藏着这里。我们看看它的反编译代码:
Pasted image
还挺复杂。咱们动态跟跟看:
(lldb) br s -a 0xx64000
Breakpoint 3: where = WeChat`___lldb_unnamed_function88616$$WeChat, address = 0x018a2b84
Process 3098 stopped
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2b84 &+0&: push
{r4, r5, r6, r7, lr}
0x18a2b86 &+2&: add
0x18a2b88 &+4&: str
0x18a2b8c &+8&: vpush
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2bde &+90&:
0x20eb570 symbol stub for: objc_msgSend
0x18a2be2 &+94&:
0x18a2be4 &+96&:
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2be8 &+100&: mov
(lldb) p (char *)$r1
(char *) $59 = 0x0236deb3 "getAppViewControllerManager"
Process 3098 stopped
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2be2 &+94&:
0x18a2be4 &+96&:
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2be8 &+100&: mov
0x18a2bea &+102&: movw
(lldb) po $r0
&CAppViewControllerManager: 0x1601dcb0&
* thread #1: tid = 0x3a3d5, 0x018a2bf8 WeChat`___lldb_unnamed_function88616$$WeChat + 116, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x018a2bf8 WeChat`___lldb_unnamed_function88616$$WeChat + 116
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2bf8 &: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2bfc &: mov
0x18a2bfe &: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2c02 &: mov
(lldb) p (char *)$r1
(char *) $61 = 0x023743eb "getMainWindow"
(lldb) po $r0
0x1601dcb0&
Process 3098 stopped
* thread #1: tid = 0x3a3d5, 0x018a2bfc WeChat`___lldb_unnamed_function88616$$WeChat + 120, queue = 'com.apple.main-thread', stop reason = instruction step over
frame #0: 0x018a2bfc WeChat`___lldb_unnamed_function88616$$WeChat + 120
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2bfc &: mov
0x18a2bfe &: blx
0x20eb610 symbol stub for: objc_retainAutoreleasedReturnValue
0x18a2c02 &: mov
0x18a2c04 &: cbz
r5, 0x18a2c30 &
(lldb) po $r0
0x156b8ae0; baseClass = UIW frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = &NSArray: 0x156b9290&; layer =
0x156b8df0&&
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2d0e &+394&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2d12 &+398&: movw
0x18a2d16 &+402&: add.w
0x18a2d1a &+406&: movt
(lldb) po $r2
{m_uiMesLocalID=2, m_ui64MesSvrID=4038564, m_nsFromUsr=wxi*f12~19, m_nsToUsr=we*in~6, m_uiStatus=2, type=34, msgSource="(null)"}
(lldb) po $r0
&VoiceTransFloatPreview: 0x16543cd0; baseClass = UIW frame = (0 0; 320 480); hidden = YES; gestureRecognizers = &NSArray: 0x&; layer = &UIWindowLayer: 0x15624d10&&
(lldb) p (char *)$r1
(char *) $77 = 0x "setVoiceMsg:"
WeChat`___lldb_unnamed_function88616$$WeChat:
0x18a2d40 &+444&: blx
0x20eb570 symbol stub for: objc_msgSend
0x18a2d44 &+448&: add
0x18a2d46 &+450&: vpop
0x18a2d4a &+454&: ldr
(lldb) p (char *)$r1
(char *) $79 = 0x "showWithAnimate:"
我省略了一些明显是UI层的操作,留下了上面这些objc_msgSend。我们回顾一下微信语音转文字时的UI效果:当我们点击“Convert to Text”之后,新的界面上出现“converting...”的字样,等待几秒钟,转换好的文字就会出现在界面上。这说明,微信可能是先把显示文字的UI给画出来,以“converting...”提示用户等待,同时开一个另一个线程去转换语音,待语音转换完毕后再把文字给显示在UI上。结合我们的猜测,上面的一系列objc_msgSend中,最可疑的无疑是[VoiceTransFloatPreview
setVoiceMsg:(CMessageWrap *)]和[VoiceTransFloatPreview
showWithAnimate:]。我们分别看一下它们的实现细节。
查看[VoiceTransFloatPreview
setVoiceMsg:]的实现细节
Pasted image
看起来只是一个普通的setter,没有什么特别的地方,继续下一目标。
查看[VoiceTransFloatPreview
showWithAnimate:]的实现细节
Pasted image
在一系列的函数中,[r4 onStartGet]引起了我的注意,它是除IdleTimerUtil外,唯一没有出现UI字眼的函数;我们看看它的实现。
查看[VoiceTransFloatPreview
onStartGet]的实现细节
看到VoiceTransHelper的字眼,我知道我们的分析快要接近尾声了。打开VoiceTransHelper.h,诸如
- (id)initWithVoiceMsg:(id)arg1 VoiceID:(id)arg2;
- (void)startVoiceT
- (void)stopVoiceT
- (void)HandleGetVoiceTransResp:(id)arg1 Event:(unsigned long)arg2;
等可疑函数尽收眼底,等待检阅。对于这个类的调研,就留作练习,交给正在阅读此帖的你来完成吧~!
微信的语音识别技术想必不会比讯飞强大,仅论语音识别配置的精细度来说,讯飞就要专业很多。但是,如果我们的需求仅仅是简单的语音识别,没有太多定制化的需求,那么打包讯飞SDK之后的dylib会比采用微信原生语音转换的dylib大5M左右。跟我在提到的思路一脉相承——我们的dylib存活在别人的进程里,相当于是我们去其他人家做客,不给别人添麻烦是基本的教养和礼貌,能够节省5+M内存,是一种尊重他人劳动成果的体现,更是我们自我要求精益求精的缩影。工程师的素养体现在一点一滴中,见微知著,才能成就大业;祝愿大家都能持续进步,再攀高峰

我要回帖

更多关于 微信发语音对方听不到 的文章

 

随机推荐