普通视图

发现新文章,点击刷新页面。
昨天以前蒲小花的博客

大事件

作者 Jack Pu
2026年4月13日 20:14
大事件

对于这样的事情,大家似乎经历了一周多,才坦然接受。

对于在现场的我而言,确实从一开始不以为意,到突然的寂静,寂静的有一丝恐惧和茫然,留下了特别深的印象。

对于这样的事情,大家有所耳闻,但是没有发生在身边的时候,还是多少不会太忧虑。 很多年前,在双减发生的时候,我和小伙伴还没有那么明显的感触。而这次,这种所有铺垫,到官宣的一刻,还是会带来一定的冲击。

由于目前不具备任何官方的消息,这里也不能说发生了什么。暂且就叫大事件吧。

AI 焦虑

从去年大家密集接触 AI 工具,从数据分析,文档撰写,代码编写等等。无一例外感受到好处,好景不长。大家在今年开始听到了很多大的互联网公司因为 AI 的人才优化,AI 进步的太快了,以致于很多人都还沉浸 AI 提升的喜悦之中,而忽略了可替代的一项。

似乎我们处在变革的年代,能做的除了接受,也做不了什么了。个人相比时代,真的太渺小。多少年前的疫情,给大家都上了一课。乐观看待身边所发生的事情,淡然接受比什么都好。机会是终究是公平的。

工作还快乐么?

AI 编写代码真的太快了,质量高切速度快。回想到十多年前的某个下午,打开记事本,一行行敲到着 HTML 代码,还用着笨拙的 Table, 随后用 360 安全浏览器,打开,那种很原始,很复古的样子,给个人带来的冲击和愉悦,似乎现在很少就有了。

对于角色的变化,阵痛期和迷茫期都是有的,曾经有一段时间,我们还保留着 CSS 重构工程师的 Title, 就是切图的工种,他们享受将 PSD 还原成可交互网页的成就感。似乎,这个工种慢慢消失了,都记不清具体什么时候就被掩盖在历史的长河了,对于 PM ,无疑是兴奋地,他们终于可以摆脱喜欢推诿的工程师的接口了,就是干。

工程师失去了这种快乐,如何去寻找新的快乐?转型期,痛切深沉着!

Redesign your Life

面临着工作的转型,生活似乎也要有了变化。你不再依靠传统的搜索引擎了,AI 提供了很多高精准的答案。在过去长达十年多的高高速互联网发展,人民习惯了卷,对于9-10点的下班,认为这才是正常的生活方式。累的好多人抑郁了,还要坚持回复钉钉消息。反而现在在这个新的时代,效率已然卷不过 AI 了,我们可能反而需要静下心来,规划规划。慢节奏难道不是大自然进化的一个发展方向么?

好多人在消化后,重燃了曾经的计划,有人觉得,为什么不区别的城市呆一呆?去深刻体会下别的城市柴米油盐,昼出夜归。有的人觉得,也该好好练下身体,不就是慢跑么?马拉松?山区徒步么?先走走国内的徒步路线,去武功山,去虎跳峡,去三峡之巅,原来偌大的中国,自己都没有留下几个脚印?有人觉得,去重新读书,学一学新的知识,或许更好,我们不是老怀念学生时代么?

小时候,我喜欢看通灵王,特别喜欢麻仓叶经常说到:

船到桥头自然直

记一次警察访谈

作者 Jack Pu
2026年3月23日 20:35
记一次警察访谈

万万没想到我也经历了一次警察的 1 on 1。

当然不是什么和自己相关的事情,而是今年三月初回北京航班上遇见事情的后续。

事件起因是,我们在航班起飞前,空乘过来咨询我前面座位的人员,是否捡到一个平板或者手机。我们原本以为只是一起简单的问询,但是没过几分钟,发觉警察上来了。警察也是走到这个乘客面前,咨询是否捡到一个平板,平板和手机都描述成什么样子。但是这个乘客依旧否认。我们原本以为事情就这也过去了,但是飞机迟迟没有起飞,直到空乘通知,由于意外发生,飞机将延迟起飞。

消息没过多久,警察就又上飞机,直接走到乘客面前,再次确认是否捡到或者错那,随着乘客依旧否认,警察直接问询是否配合搜查行李,随后警察就开始一阵搜索,几个行李都翻了个便,这个时候又上来一名警察,叫检查座位。就在这个时候,我往下看,发现前面座位底下确实有个像平板的东西,我就摸了摸,然后立马拿了上来,反馈给了警官。

警察,然后叫继续看下,手机电筒照了下,发现还有手机和机票。交给警察后,警察问乘客知道这个是哪里的来的么?双方一致摇头,不知道这个是哪里来的。

我们这个时候还说,是不是上一位乘客忘记了,连机票都丢了。

原本我们以为事情告一段落,结果没过几分钟,警察又上来,还持枪上来,明确要求两位乘客下机配合调查。就这样,两位乘客联通警察一起下飞机了,然后我被登记说后面会有个笔录,留下了联系方式。

几周后,也就是今天,警察来京了。由于可能事情比较确凿,所以就简单了问询了我记录的事情经过,以及辨认下嫌疑人。随后就是材料的签字和按手印,差不多前前后后也就40多分钟左右。

随后,警察再次告知了对证人的保密,就离开了。

好吧,记录下,人生的另外一种第一次吧。

重庆

作者 Jack Pu
2026年3月1日 22:08

重庆

重庆

借着今年春节居家办公,赶上这个周末,我去重庆来了一次 24H 特种兵旅行。

现在火车非常方便了,自己的县城也有了火车站,不过我是送完小青橙到阆中后,再乘着火车前往重庆的。由于行程匆匆,自己都是快速订票,只有卧铺,不过已经很好了,因为我返程的站票。

原本计划的7点10分起床,但是还是拖延到7点四十才起来,然后给娃穿衣服喂饭,弄到了8点20才从家里面出发。

不过最近去往阆中的高速已经不堵了25分钟便开到了小青橙姥姥家,吧他顺利交给了姥姥手里。随后,立即驱车前往火车站,时间还算合适,9点21出发,差不多9点30多就到火车站了。

许久没做卧铺,瞬间勾起了我上大学的回忆。其实我大学绝大多数也是做的硬座,唯独毕业夏天,暑假人少,要不就奖励一下自己。还有一次, 便是快毕业的寒假,去北京实习,50多个小时的火车,选择了一次卧铺。两次卧铺,一次是中间位置,一次最上面的位置,躺着肯定还是比坐着舒服。由于这次只有三个小时车程,我也没趟多久变下来。这次是 K 字头的火车,普快,车厢里熟悉的声音就来了,聊天的,刷短视频的,吃饭的,和自己记忆中的保持一致。总感觉 K 字头的车,虽然慢,但是大家心却似乎轻松愉悦。G字的车,快是快,充满了一种急迫压抑感。还在早春,但是两旁的油菜花,梨花都开了,风风景宜人,慢摇着,边到了重庆北站。

我来重庆大概有四次(加上这次):

第一次,是我小时候,还在读五年级,去长江三峡玩的时候。新奇,但是记忆模糊。
第二次是和媳妇谈恋爱期间,回老家结婚,和她闺蜜吃饭的一次。
第三次,结婚万,从重庆赶飞机回北京,因为疫情(红码问题),大逃亡的一次(博客有记载)

这一次,相对来说是最为轻松的一次,目标很纯粹,就是自由在在 Citywalk 一次。

自己从重庆北站出发,做十号线转二号线,直达我的第一站,就是十八梯。由于早上吃饭很早,到了重庆已经是一点多了,出地铁边去路边找了一家小面馆,味道还不错,一股火锅的味道。这次比较意外的是,发现了茶颜悦色有很多了。而且它家还出了咖啡,顺便点了杯美式(忘记他们家叫什么名字),开启徒步之旅。从十八梯,下完坡,边右转去往山城步道。

重庆

重庆人不容易胖,是有原因的,确实上上下下对卡路里消耗真的很多,待我一口气爬上去之后,感觉后背都有些许汗水了。阆中没有吃到的小糍粑,这次吃到了。5元20个。随后便是沿着马路去往朝天门,由于春节还没过完,路上的旅客依旧很多,尤其学生偏多。跟着人流,差不多得走上快一个小时,到了来福士。重庆的新地标,和新加坡的帆船建筑类似。我来来福士,是因为抖音上说商城有花园的装修效果,我来来去去反复确认,不是这个地方,抖音推荐出错了。不过来了,边也是还是到顶层看了下,俯瞰了下重庆的城景。由于持续暴走,手机也没电了,变在上面小憩一会。

万万没想到,一会出现了太阳,虽然天一直阴沉沉的,但是此刻太阳出来,还是吸引了不少游客过来拍照记录。

重庆

随后便下到商场,时间已经是6点40了,感觉也有些许饥饿的。最后在抄手和串串之间选择了,串串,毕竟来了重庆,还是应该尝试下地道的火锅滋味呢。

没过多久,便是去往最后一站,砖石广场。这真的是欣赏重庆江景最好的地方,非常宽阔,易于出片。查了查导航,也就40来分钟步行,沿着朝天门,过桥,到了大剧院站,在沿着马路下走就行。钻石广场,顾名思义,就是有个钻石的地标。而下到这边之后,那种震撼感扑面而来,我觉得重庆的夜景,不亚于上海,而且那种楼区的参差不齐,给人一种思博朋克的感觉,尤其千厮门嘉陵江大桥红色结合着背景楼宇的光芒,印象非常深刻。

重庆

最后9点多,终于结束行程,一看手表1000+的卡路里消耗,也算是达成目标了。

重庆这次给我留下了非常多好的印象(也可能是错开了高峰)

  • 很多充电宝的网点
  • 爬楼有做下休息的凳子
  • 景点超级多的饮料小吃
  • 外国人也多了起来(甲亢哥的宣传?)

Nice~~~

一句抵一万句

作者 Jack Pu
2026年1月26日 19:55
一句抵一万句

最近终于读完刘震云的《一句抵一万句》 。这篇小说豆瓣评分9.0,也常年再很多书单推荐中。

我对这本书充满好奇是在很多年前,他女儿排的电影,因为电影里是自己比较关注的两个影星,李倩和毛孩,一个龙门镖局里的青橙,一个炊事班故事的小毛。然而电影里拍的就是普普通通的小老百姓的事情,也是交代了没有交流的婚姻,所带来的影响。我原本觉得小说也是这样,但是再读了几篇后,发现完全是不同的时代背景。

这部小说也被誉为中国的《百年孤独》,因为他的跨度非常大,不是一代人的事情,而是几代人的故事。小说其实远比电影精彩,毕竟电影只是拍了下半部,而且省略了大量的内容,很难表达出原著的内核。

自己有了孩子后,发现自己爱和孩子说话,尽管孩子只有2岁多,很多词汇还表达不全。但是自己就是喜欢和他说话。然而我与我的父亲,母亲,却几乎很少沟通,至于为什么,我也很难说出来。小说里,牛爱国小时候不被母亲待见,但是大了后,母亲却爱和牛爱国聊天,牛爱国母亲也爱和孙女聊天,几乎什么都说。而最多的还是她小时候的事情,也就是电影里没拍上半部的内容。

自己婆婆年纪大了,也老爱跟我说她遇到的事情,经历的事情。我和我妈不一样,我会安静的听着。就像现在小青橙和我,我说什么小青橙都会仔细的听,无论理解不理解。

大概人这一辈子,就是想找个能够聊得来的人吧。

The Plan of 2026

作者 Jack Pu
2026年1月12日 20:08
The Plan of 2026

These are some plans for me in 2026

[ ] High Personal Performance
[ ] Relocate to the US(2/2)
[ ] Using AI to build 1 or more mini games for WeChat
[ ] 10 VLOGs
[ ] 30 Books
[ ] Body Fit(<=62.5 KG)
[ ] 40+ Blogs
[ ] English Speaking
[ ] JAGX(10000+) + FUBO(5000+) + BYND(10000) + QUBT(1000+)
[ ] BILI 12000 + 4paradigm 10000+ Haidilao 10000+

2025

作者 Jack Pu
2026年1月4日 19:44
2025

每到年底,说是写点什么,开头是一件难事。

高中时候,语文老师觉得自己作文写的差,很是费解。他把作文结构成模板,强调总分总,开头和结尾,需要强调文章的主题,片中则是各述论点论据。虽然自己高中文笔确实不好,到了大学,因为对于博客的热爱,反而开始喜欢上写作,没有了八股文死板的结构,作文变成创作。

2025,于自己,两个字,

新与旧

新,是自己所经历所体验的新事物。旧,是自己所回忆所回访的旧地方。

人们常常按照地点或者时间回忆过去的点点滴滴。今年陆陆续续传了很多照片在自己的相册,记录生活的琐事变化,当然也方便年底写作提供点参考,今年边精选几张写写背后的故事,当然也是自己这一年的故事。

先聊聊新吧。新的,有自己第一次尝试的,挑战的,或者偶然撞到的。

春节小聚


春节每每都有我们这一代人的聚会。今年最大的变化是多了两个小孩子。孩子变化很快,春节可以依偎着在大人旁边玩了,也可以和同龄人争吃的抢玩具。每年春节是一家人团聚的时刻,也是记录岁月年华最明显的时候,转眼间,这里一起吃个饭,一张桌子坐不下了,我们这一代人也开始露出生活的压力,结婚的结婚,找工作的找工作,再也不会有几年前,还在校园生活寒假聚会那种轻松的氛围。然而又看看身边的小的,他们也才两岁不到,想吃什么就吃什么,想往哪里跑就往哪里跑,这个时候能理解孩童的快乐,是真的让人向往。

2025

离别的焦虑

春节后,由于工作原因自己和妻子边先行回到了北京。自己的想法也是孩子可以在老家多呆一阵子,避开回城高峰。然而离开后一天孩子身体就开始不舒服,然后高烧,食欲降低。才开始以为,普通感冒,过几天就好。然而事与愿违,后面视频 call 里面开始变得毫无精神,也不怎么说话,眼神里透露出一种迷茫。大概看了那个镜头,立马买了隔天正月十五的全价机票,将孩子和姥姥接回了北京,随后去医院,也只是开了止咳的药。

2025

人类的羁绊也是很神奇,回来后,第二天,精气神就完全好了,完全不像大病的样子,于是乎在798 拍了张夕阳的残影。我也是体会到父母对孩子,以及孩子对父母的依恋,真的是维系最为紧的吧。

追星的开心

今年自己主要关注英雄联盟的比赛和NBA的比赛。当然英雄联盟大家都比较关注的是 T1,尤其 Gumuyusi 选手。其实今年对于 Gumuyusi 选手真的坎坷的一年,年初被下放替补,随后面对 MSI 筛选前两轮被拉上来救火,随后夏季赛又再度下放,直到选手因为心里问题,被迫放弃主力位置,又从未主力位置。跌跌撞撞打完世界赛资格赛,被人疯狂诟病英雄池问题,芸阿娜,卡莎。当然今年有幸北京看到了现场 T1 的比赛,由于瑞士轮在北京举行,虽然了花些钱找了咸鱼黄牛,但是一次性看到诸多比赛队伍,TES, BLG,AL,GEN,尤其可以亲眼看到 Faker,也算是一饱眼福了。由于瑞士轮,北京场地相对较小,所以可以看到清晰的说实话,比赛开始,没人觉得今年 T1 能再度卫冕,Gumu 能拿下 FMVP,可是命运真的很神奇,这就是竞技体育吧。

2025

今年开船的比赛虽然看的不多,唯一就是季后赛掘金系列,见证了神奇的扣篮绝杀。其实现在 NBA 没有了小时候那种对抗,犯规和造犯规太多了,裁判的尺度也变得非常捉摸不定。恰好今年10月去旧金山,原本没有计划去看什么 NBA 比赛,但是运气很好撞见了金州勇士和快船的比赛,于是乎上午立马订票了,差不多50美刀左右,位置还算不错。场馆的灯光真的很足,如果早点过去,可以看到他们的热身,比如哈登小卡的投篮训练等,一起库里的三分特殊训练。

短暂的美国之行

10月下旬,公司有机会得去旧金山一趟。 这个国家无疑是整个中国最为关注的对手和学习对象。你可以看 CCTV4的内容,一大半都是报道美国的各种。美国签证非常麻烦,还得面试什么的,不过针对 business Travle,面试还好,当然如果你有家庭,有孩子似乎更好通过。签证虽然麻烦,但是十年有效期倒还算一个优点。

第一个问题便是长达十多个小时的飞行,第一次做这么长的飞机,真的算是特别煎熬。所以一定要多下载一些影视作品,那种无脑喜剧就行。

总的来说,加州真的是一个极为宜居的城市,落地还下着下雨,开车倒酒店的半个多小时,边转变成为了晴天,中午温度最高,但是非常适宜,穿短袖长裤即可,也不那么炙热。就这样的天气,你敢信,一年365 天都这样。

由于我们是公司搬家,你敢信,旧金山市长还莅临过来参加入驻仪式,市长在他的演讲提到了,这座城市的基因来源于创新,感谢公司的付出。好吧,我们自己都没觉得有什么特别的贡献。

吃的。真的是个大问题,一方面是贵,尽管可以报销,但是每每看到金额转换,真的不敢想。陆陆续续打车了几年,几乎都是10几刀刀20多刀,差不多都是100+。很多同事抱怨过来,最大的问题也是吃,尤其味道,这里芝士味真的很浓,导致我回来飞机上,闻到就有种呕吐的感觉。不过分量是真的大,早餐的三明治都可以分两顿吃了。

这里最震惊的还是安全问题,尽管旧金山已经改善几年了,但对于中国这样的环境,看街上的流浪汉,和吸食大骂后的人在街上闲逛,让人不寒而栗。到了晚上,街上是真的早早就没了人,你时不时就能听到警车的警鸣声音。当然安全问题一直是加州的问题,它是一个区域性最为明显的问题,自己还没有去什么比较好的白人小区,但是仅仅在 downtown,就足够让人大吃一惊了。

由于拜登法案的问题,很多同事今年 relocate 到了美国,公司变也会个人意向,一方面是美国带来的新的职业机会,一方面又是各种新的环境适应,自己也在想是不是,明年,或许我就在旧金山,洛杉矶或者圣地亚哥写总结,又或者还是在北京呢? 船到桥头自然直吧,也不必特意纠结。

2025

聊聊旧吧,过去一年,有回到某些地方,或者重新去体验的事情。

日本家庭之旅

终于在今年打算出去旅行,过去三年结婚,疫情,孩子,一直没能出去走走。如今孩子也算可以很好的表达,时间也有,就早早定下了计划。不过唯一的不好点,是时间不算去日本最好的点。因为日本6月底,进入梅雨季节,下雨是在所难免的。

16年去的时候,正好赶上红叶季,不过当时东京呆的时候相对较短,这次,东京计划了更多的时间。由于是旅游淡季,机票和酒店相对便宜,当然网红景点依旧人满为患。第一次去日本的时候,那种新鲜感是真的印象深刻,但是这次过去似乎发现大阪和东京变化真的很少,和第一次去没有太多的差异。去了海游馆,也赶上了大阪世博会,吃了日本烧肉,也喝了当地的啤酒。虽说带着娃,感觉一切都得围绕娃了,有几次都是在婴儿车上睡着的。东京几天天气很给力,小雨很小,而且去了镰仓,看到了灌篮高手的取景地,也在东京铁塔下散了散步,由于住在东京湾,晚上能够一眼望到彩虹大桥,和16年那个时候自己去港口独自散步对比,真的是不一样的感受,那一次是好奇,新鲜感,这一次确是难得的松弛和享受。

2025

海南旧旅

年底,赶着圣诞节假期和多出来的育儿假,和妻子计划了二人旅行。上次旅行,带着孩子,明显感觉一切都是孩子为中心。自己在2010年高中毕业的时候来过一次,那个时候是暑假,天气非常炎热,由于是跟团旅行,景点是一个跟着一个,而且炎热天气,以至于大家想的就是回酒店吹空调或者回车上吹空调。那个时候经济上限制,买个椰子也都要纠结半天。和曾经一样,这里成为了东北人的长据地,跑车的,做生意的,好多都是东北人,也有长期定居的。不一样的是,这次有了很多外国人,很多俄罗斯人过来度假,还有蒙古的,还有不少韩国人也过来享受。12月份的天气真的很好,中午虽然热,但是完全可以接受,到了下午晚上,有着风吹着,都会有些许冷的感受。

2025  和高中毕业不一样,我们会和本地司机会聊,你发现疫情后的感受一样的,对于经济下行的无力感,自己家有孩子的,就是非常担心就业问题,生育下行这个问题,在哪都是一样。、

宏观层面的下行,年轻人在时代面前似乎真的微不足道,今年看《与日为鉴》,说总有一代人或者两代人会被牺牲掉,似乎我们选择了牺牲一部分35+,也牺牲一部分00后,这个时候很多建议,就是

锻炼身体,拮据生活,保持身心愉悦,这就是时代的一部分。

新 与 旧,在拉长时间线上,似乎就有不一样。 无论怎样,做你自己所热爱的事情,这才是生命里的体现。

2026, Just do it.

2025

2022 年往事

作者 Jack Pu
2025年12月19日 17:40
2022 年往事

最近发现,世界杯开始预热了,复仇者联盟5预告,拥有说,明年是感受经济上行的年份。

突然会议,上次世界杯,我们还处在疫情之中,自己也是世界杯后期比赛的时候第一次感染了。我依稀记得,我半夜发觉背很凉,然后就盖了个很厚的被子,但是第二天早上还是发现高烧了,测试果真感染了。

很多人不愿意回忆 2022年,那是个几乎让人倍感绝望和愤怒的日子。随着国外逐渐放开,我们迎来的确实疫情的反反复复,和极为严格的核酸测试。

那几年,自己经历了

  • 两天一次的核酸,有过7天一次,14天的,也有一天一次的
  • 行程码,各个地方查行程码,商场,地铁, 火车站,公司
  • 公司上班分 AB 班
  • 坐飞机,被标记高危地区,无法回京
  • 各种媒体出现应封尽封
  • 流调,查询你接触的人
  • 突然性的,放开管控,大规模的感染,自己和媳妇接连中招
  • 公司此起彼伏的咳嗽
  • 某个乌鲁木齐事件后,凌晨朋友圈的大规模刷屏
  • 贵州半夜转运的车祸
  • 孕妇因为红码事情无法正常入院
  • 媳妇因为行程码无法进入医院取药
  • 办公室带着口罩
  • 小区外卖被放在小区外面外卖架无法送上门
  • 隔离,上门的核酸检查
  • 突发性双减,教育公司遭遇众创,第一次大规模裁员
  • 镇子上过年,全部关门不做生意
  • 商城必须碰手洗消毒液

最近一些,从能听到新冠,又或者新冠疫苗后,的一些症状,以前从来没出险过的长期慢性病,自己也恍然大悟,说说自己的变化

  • 白头发变多了(也可能是年龄大了),但是白头发真的比以前多太多多了,主要集中在两侧
  • 溢脂性皮炎,头上长痘痘,这个确实疫情前没有的
  • 代谢变慢,容易胖,

小青橙第一次急疹

作者 Jack Pu
2025年12月8日 20:50
小青橙第一次急疹

其实自己一般很少记录孩子相关的,但是随着孩子不断长大,也确实会遇到一些棘手的事情。

孩子出身后, 作为父母而言,心里总会隐隐约约害怕染上什么大病。不过好在运气不错,最近几年一直还算健康成长,小有感冒和发烧,但是还没遇到过超出异常的问题。

这周,孩子突然便血。图片非常刺激,感觉内部有个伤口,鲜血直流。但是粑粑拉完后,也没了。原本以为是大便干燥,造成的硬伤。过了两天,再次出现了同样的情况,网上迅速咨询了,说是可能是肛门撕裂。于是乎,赶紧挂号了。

最先去的妇幼保健院,比较近,医生一看图片,立马就叫去挂儿研所或者儿童医院的。然后直接说过去挂急诊,这个第一时间处理。
然后火急火燎的,奔着首都儿研所了。

不亏是感冒高发期,整个大厅,全是儿童和父母,急疹里面也是输液的,人密密麻麻的。急疹是流程的,这里梳理下:

  • 1 一定去分诊服务台做初步咨询,需要挂什么样的科目
  • 2 去人工服务台,找急疹条
  • 3 去人工窗口挂急诊号(机器暂时不能挂急诊)
  • 4 拿着挂号后的单子去急诊分诊台,后续就是听从建议排队等待就诊了

我们需要同事挂外科和内科,优先外科看,需要医生看下肛门情况,然后是需要B超影响看下腹部。最后医生也确认是肛门撕裂,就是冬天不爱喝水吃蔬菜瓜果,大便干燥,拉上了内部,内部毛细血管密集,所以出血量大。

后续医生也是开了涂抹和软化大便的口服液。

其实作为父母而言,一遇到事情还是挺担心的,也觉得是不是太大意了。但是另一方面也需要大家放宽心,孩子的生命力或者抵抗力真的很强。

OTT 开发的挑战

作者 Jack Pu
2025年11月21日 10:49
OTT 开发的挑战

做 OTT 设备相关的开发有一段时间了,计划写一点关于这些设备上开发商学习到的经验和以及比较困难的部分。这里面有非常多反直觉的东西, 它和我们传统 WEB 开发有些区区别。

设备老化

这个是我觉得自己在从 WEB / Android 开发转型 OTT 开发面临的一个问题。电视和手机是完全不一样的消费场景。手机厂商每年会头发大量的资源做手机的更新,但是电视不一样。对于很多人,可能手机三年或者四年就计划换新,因为手机迭代的功能非常快,几年不换,就缺一些功能了。由于手机已经早已拜托了打电话单一功能的限制,变成了多功能的娱乐设备。很多人会用来看视频, 玩游戏,拍照等。

然而电视或者 OTT 盒子,它目前为止娱乐场景非常单一。你只能把它放到家里,而且时间比较单一,晚上或者周末。没有人出去旅游,拿一台自己的 FireTV stick 去酒店播放。所以他们的更新频率会慢道七八年,甚至10年多。没错,我们的数据反馈出有超过大部分用户依旧用着七年前的设备,超过十年的设备已经运行着。

平均更换周期: 根据市场调研公司 Circana(前身为 NPD Group)的最新数据,美国消费者平均每 6.6 年 会更换一次电视。
电视机“年龄”: 目前美国家庭中正在使用的电视,平均“年龄”大约为 5.2 年。值得注意的是,约有四分之一(25%)的电视使用时间已经超过了 7 年,这意味着这部分用户即将进入换机窗口期。

兼容性问题

随着设备更换迭代的问题,我们就必须面临兼容性的问题,10年 Android TV 或许都迭代了很多版本。如果使用 WEB 的话,你必须做好一些新 API 的兼容,比如我们关注在 Media 层,其中 MediaSource 在各个 Chrome version 的支持也不一样,有些 API 支持,有些 API 不支持,你最好能够有这个低在里面,确保能够捕获这些差异信息。

除了本身设备的老旧引起的兼容性问题,我们 OTT 面临的另外一个问题就是设备型号和设备厂商太多了。智能手机时代我们面临的最多就是 Android / iOS 阵营,而 OTT 时代,由于很多厂商的电视制造时间远比智能手机出现的长,比如

  • Sony
  • Samsung
  • LGTV
  • Hisense
  • TCL

比如机顶盒厂商又完全不一样:

  • Comcast
  • Android TV
  • FireTV

这些面临着传统 TV OS 和 智能 OS 的迭代更新,因此你会可能面临着 Android / Web 技术的夹杂,包括每个厂商的定制 SDK,想想肯定就非常头痛。

这些绝大多数厂商大多提供 WEB 技术栈的原因

  • 迭代更加快速
  • 兼容性问题适配更快
  • 相对技术栈更为成熟

用户或许对性能体验要求没那么高

如果我们做手机 APP 的开发,大家都非常关注性能体验,比如什么首屏时间,用户起播时长等等。由于 OTT 设备本身老化和娱乐场景的单一,用户其实反而没有对极致性能体验有那么强烈的要求。

Performance < Stability

你视频进入起播0.5秒和你起播2秒,其实带来的优化效果不会有那么特备明显的增长。反而让用户能够完成整个视频 Session的重要性高过起播的优化。

我们观看一部剧集,其实是一个单一的事情,我不会边看剧集,然后再电视做其他的事情,我对目标的期望值就是能够顺利完成,所以我们尝试很多实验,stability 比如对错误的优化处理,远超过所谓某些体验测指标,带来的反馈更好。

用户问题追踪

这个问题是由于机型庞大带来的,如同我们收到一个用户反馈,关于无法播放的问题,但是你的大盘不会因此播放,它或许就是某个机型的兼容性问题。但是我们实际在上线前测试,不会一一覆盖,因此不可避免这样的问题。因此做好线上问题的追踪非常重要。

这里就涉及你如何进行快速维度的判断,播放问题最终都会落实到用户中断了播放,而给予播放中断维度非常有用

  • 设备型号
  • Chrome 版本
  • 播放错误信息
  • 视频编码
  • 视频内容 ID

等等,这些能帮助你快速定位是否是规模性问题。

而你要构建一个完整的播放 Stage 列表,确保用户退出再什么 Stage,然后你要有一个基本 API Progress Pass score. 这样你能快速确认它中断在具体哪个 API 的执行下

总之 用户日志的设计是需要非常细致而又 Smart 的。

我了解的美国 VS 我体验的美国

作者 Jack Pu
2025年11月7日 18:10
我了解的美国 VS 我体验的美国

终于,上一周,自己踏上了这片科技热土,美国,加州,旧金山。

作为一个在中国生活和工作的人,美国对我来说,一直是一个由知识、影像和商业概念构建的符号。这次短暂的旅程,似乎让我发现了理解与现实的差异。

一 我理解的美国

在跨越太平洋之前,我对美国的认知来自三个主要维度:

书本与制度

书本描绘的美国,是一个拥有全球最发达经济体的国家,科技创新能力首屈一指。它的强大,似乎源于一套成熟、独特的政治制度。尽管建国历史不长,但其吸收了西方历史的教训,基于法治,三权分立,全民普选,这和我们社会主义制度,形成了鲜明对比。作为新时代年轻人,我们对于法治,普选都还感到非常陌生。我们经常开玩笑说,党大还是法大?我们觉得谈及这些,来源中国悠久历史下人民更加注重关系而非规则。

电视与文化作品

影视剧则赋予了美国血肉和情感。《阿甘正传》描绘了个人奋斗与时代变迁的交织;《生活大爆炸》(The Big Bang Theory)展现了充满活力的极客文化和开放的社交模式;**《中国合伙人》则从东方视角切入,展现了中国留学生面对美国文化壁垒时的奋斗、挫折与渴望成功的复杂心路历程,凸显了美国作为“淘金地”和“文化冲击地”的双重面貌。无论是电影还是美剧,都传递出一种“美国梦”**的叙事——只要努力,皆有可能。这里的文化是自信的、多元的,充满着无穷的可能性。

工作与科技

在日常工作中,我对美国的认知高度集中于硅谷。Meta、Google、Apple这些名字代表着世界最前沿的科技和最高效的互联网文化。它象征着灵活的办公环境、扁平化的管理、以及对工程师文化的无限尊重。我理解的美国,就是那个在深夜依然灯火通明、永不停歇的创新策源地。

二 我体验的美国

我了解的美国 VS 我体验的美国

当飞机降落在旧金山国际机场,我走上街头,发现眼前的城市与我脑海中的符号产生了剧烈的碰撞。旧金山,这座科技之都,以其特有的矛盾性,为我提供了一个多维度的体验。

消费文化与“中国胃”的挑战

接下来,是**“中国胃”对美国餐饮的抗议。我发现要在旧金山市区找到一碗清淡的面条或一碟新鲜的蔬菜是多么困难。美国食物的口味普遍偏甜、偏咸、量大**,酸甜口居多,且奶酪、芝士等乳制品的使用频率极高,几乎渗透到每一餐中:沙拉里撒满了浓郁的芝士碎、汉堡里夹着厚厚的奶酪片、意面更是浸泡在奶油酱中。这对于习惯了清爽口味和热食的中国胃来说,是一种持续的挑战。一顿简单的午餐,比如一个鸡肉沙拉或一份汉堡,价格通常在 $20 左右,且口味单调。

更令人印象深刻的是消费文化:咖啡店里一杯普通的美式咖啡加上小费,轻松超过 $5。这种高昂且必须的小费文化,让习惯了明码标价的我在每一次付款时都需要进行额外的计算,无形中增加了一种心理负担和不适感。这种小费机制的背后,是美国服务行业独特的薪酬结构:许多州的餐饮服务人员的基础时薪远低于法定最低工资(即“小费工资”),他们的收入在很大程度上依赖于顾客的小费。这使得小费从一种“服务奖励”变成了“工资支付”的组成部分,从而形成了这种无形的消费压力。

街道的冲突与无序感

这是最令人震惊的对比之一。在玻璃幕墙后的科技精英们讨论下一代人工智能的同一片天空下,旧金山的街道却显得如此脏乱且充满危机感。无家可归者(Homeless)的数量超出了我的想象。我看到简易的帐篷、纸板箱搭建的临时居所紧贴着高档写字楼,他们占据着人行道,形成了城市中一道触目惊心的伤疤。

这种无序感不仅仅体现在帐篷和垃圾上,还体现在光天化日之下,一些人在街角进行公开的吸食和注射行为。空气中常弥漫着一股浓烈的、刺鼻的大麻气味,这是在美国少数州合法化后的结果。更让人惊讶的是,路人似乎对此习以为常,政府和社会缺乏有效的干预或救助机制,使得这些区域仿佛处于城市管理之外。这种贫富差距的极端共存,以及公共秩序的某种程度上的失衡,给我带来了强烈的视觉和感官冲击,是书本和影视作品中绝不会展现的一面。

科技氛围与善于表达的文化

我了解的美国 VS 我体验的美国

尽管有街道上的混乱,科技的力量和效率依然强大。更重要的是,我感受到了这里的善于表达与即兴沟通的文化。在工作会议中,我注意到每个人都鼓励直言不讳,即使是初级员工也会自信地打断发言、提出尖锐的问题或直接否决方案。这种文化没有太多铺垫和寒暄,而是直奔主题、快速迭代。他们鼓励人们自信地、坦诚地表达自己的观点和需求,这种开放、高频的沟通模式,对于习惯了含蓄和层级分明交流的我来说,是一种强烈的冲击,但也是一种高效的驱动力。

体育的热情

另一个突出的感受是美国人对体育的热爱。无论是在酒吧里共同观看比赛,还是街上随处可见的运动队服,抑或是早晨在公园里跑步的人们,体育运动在这里不是一种消遣,而是一种生活方式和强大的社交纽带。这种全民投入的热情,体现出一种健康、团结的社会活力。

社会观察:仍在工作的老年人

在旧金山的超市或零售店中,我经常看到白发苍苍的老年人担任收银员或服务员。这种现象与国内普遍的退休文化形成了鲜明对比,也打破了我对发达国家老年人普遍安享晚年的固有认知。这背后,是美国社会老龄化和养老体系的复杂性:许多人依靠个人储蓄的401(k)退休计划,而不是过去由雇主负责的固定养老金。由于储蓄不足、投资风险,以及高昂的医疗费用和住房成本(尤其在加州),许多老年人发现仅靠社会保障金难以维持有尊严的生活。因此,他们选择或被迫在退休年龄后继续工作,这既是为了经济保障,也体现了一种保持社会连接和个人价值的文化。

时差的折磨与应对

太平洋两岸的时差,是我旅程开始时的第一重生理考验。旧金山与中国的时差长达十几个小时,这意味着抵达后的第一天,我的身体时钟彻底错乱。

长途飞行带来的时差和疲惫是第一重考验。这种昼夜颠倒带来的疲惫和思维迟钝,对我商务工作的效率造成了极大的挑战。凌晨三点,大脑清醒亢奋,而窗外依然漆黑一片;下午两点,会议正酣,却困倦到眼皮打架。

成功的时差调整是顺利开启旅程的关键。对于跨越十几个时区的旅程,有效的应对措施至关重要:

抵达后立即调整作息: 无论多困,抵达后坚决不睡午觉或只小憩半小时。必须坚持到目的地的夜晚,将身体时钟强行锁定在新时区。

白天保持活动: 尽量在白天进行户外活动,利用自然光和适度的运动来抑制褪黑素,帮助身体更快进入状态。

提前微调睡眠: 在飞行前几天就开始微调睡眠时间,能有效缓解抵达后的不适。

三 留下问题

我理解的美国,是教科书和电影中提炼出的精华;而我体验的美国,是旧金山这座城市光鲜亮丽背后的现实切片。

我并不能给出特别直接的结论说美国生活好与不好,毕竟我呆的时间很短,而且美国丰富性,各个城市也不一样。或许我们还需要更多了解才行。

我了解的美国 VS 我体验的美国

国庆小记

作者 Jack Pu
2025年10月9日 17:14

小镇

国庆小记

9月26号,得力于 WFH 的政策延续,自己今年早早回去了。不过这次自己是和娃一起的。娃已经2岁半了,自己可以 hold 住很多事情了。

回到这个镇子上,尤其是在节前,陆续还处在工作日当中。这个镇子给你的感觉,冷冷清清,如同迈入老年时代。除了2,5,8 赶场的时候,这条镇子的主街,几乎见不到太多人。做生意,各种店面,卖饭的,买衣服,五金的,倒也都按时开门,按时关门。小镇上的人,都按照这几十年来的习惯,过着每天的生活。

我们不得不承认,这么多年来,我们教育的普及,城市化的推进,必然会让小镇迈向衰落,如同中国老龄化进程,小镇则是最重要的见证者。这个镇子上,你很难看到小孩,尤其婴儿。似乎年轻人早已离开这个小镇,去大城市奔波了。镇子上没有年轻人爱喝的咖啡奶茶,没有各种口味的外卖,没有华丽的商业中心。同样好多年轻人,没有选择学他们的父母,外出打工,但是留下小孩在这里。

很难想象,当我还是一个小学生,我第一次读一年级的时候,我们班是七十几个人,而且一年级还有两个班。到了五六年纪,更是扩展到了五个班,接近350人。 转眼间,这里连个幼儿园都很难凑齐一个小班了。

下雨

自回到家,变开始了连续的下雨天。时而大雨,时而小雨。自己只能带着娃坐公交,去县城的商业中心。 虽说一直是下雨天,但是也没有太冷的感觉,除了最后几天,完全没觉得会给气温带来多大影响。除了把娃的短袖换成了长袖。

家里桂花开了,早几年,镇子上改造,把街道两旁的榕树,改成了桂花树。今年回来的及时,赶上了桂花全部开了,还计划着弄点桂花,晒干泡水。连续的雨打破了这个计划。

自己是5号走的,路上也还是时断时续的小雨。到了机场,远处的山,隐隐约约,倒有一幅山水画的感觉。回到北京,玩玩没想到,也开始了陆续的阴雨天气。看了看天气预报,似乎也得持续到下周。

然而明显差异的是,气温降的很快。短袖已然对付不了。三件已然是必备的,即使坐在室内也把羽绒马甲给套上。北京的秋,虽然美,但是最怕太短。一不小心,就进入冬季了。

出发

回来的一件事情,便是美国签证。国庆还刷到了,当初看的《中国合伙人》。恢复大学的八十年代,各种西方文化进入中国,而那个时候大学生对于西方的向往也成为了最热的话题。陈冬青,虽然被拒无数次,但依然保留对英语的执着和热爱,以至于后面从家教老师,到教培机构的创建。自己其实也从来没想过啥时候回去美国,大学的时候,看过社交网络,到接触互联网,开始对这个国家,尤其硅谷有种特别的好奇,什么趋势着这段创新热土

回到面签上,除了草草几个问题,似乎没什么可以记录的。当然确实相比日本签证,还需要现场排队面试。而且似乎这完全取决于面试官的心情,过或者不过,不可预知。 不过面签的人真的很多,7点钟排起了特别长的队伍,这里的人形形色色,比如我旁边便是一组十几个人的Air China 的员工在进行更新。也有好多带着孩子的,小到看着只有2岁的都有,也有年长外出看望孩子的。

中国经历改革开发,这几十年,我们对西方或者说美国,保持着分分合合的关系。美国见证者中国经济的崛起,中国也一直思考者美国为什么长久不衰。这是完全两个问题,我们小时候所了解的美国,现在了解的美国,都不如去真实的体验下美国。

【DRAFT】ES2025 的语法糖科普

作者 Jack Pu
2025年9月30日 19:23
【DRAFT】ES2025 的语法糖科普

不知不觉 ES2025 都快推出了,这里分享一些比较有趣的用法。

Time API

更新的时间API

// Simplified date creation
const now = Temporal.now();
const birthday = @2024-01-15; // New date literal syntax
const meeting = @2024-12-25T10:30:00[Asia/Shanghai];

// Syntactic sugar for date/time arithmetic
const nextWeek = now + 7.days;
const lastMonth = now - 1.month;
const deadline = meeting + 2.hours + 30.minutes;

// Time range syntax
const workingHours = @09:00..17:00;
const workingDays = @Monday..Friday;

console.log(workingHours.contains(@14:30)); // true
console.log(workingDays.contains(Temporal.now().dayOfWeek)); // Check if today is a working day

Record & Tuple

对不可修改数据的支持

// Record: immutable object
const userRecord = #{
  id: 1,
  name: "Zhang San",
  email: "zhangsan@example.com"
};

// Tuple: immutable array
const coordinates = #[10, 20, 30];
// Strict equality check
const user1 = #{ id: 1, name: "Zhang San" };
const user2 = #{ id: 1, name: "Zhang San" };
console.log(user1 === user2); // true!

// Nested structures
const complexData = #{
  users: #[
    #{ id: 1, name: "Zhang San" },
    #{ id: 2, name: "Li Si" }
  ],
  config: #{
    theme: "dark",
    language: "zh-CN"
  }
};

Decimal Data Type

解决十进制精度问题来了

// Old approach: precision loss
console.log(0.1 + 0.2); // 0.30000000000000004

// New approach: precise calculation
console.log(0.1m + 0.2m); // 0.3m
// A blessing for financial calculations
const price = 19.99m;
const tax = 0.08m;
const total = price * (1m + tax);
console.log(total); // 21.5892m, perfectly accurate!
// Conversion with Number
const decimalValue = 123.456m;
const numberValue = Number(decimalValue); // 123.456
const backToDecimal = Decimal(numberValue); // 123.456m

Upgraded Import Assertions

增强了导入断言,使模块导入更安全、更灵活

// Importing JSON modules
import config from './config.json' with { type: 'json' };

// Importing CSS modules
import styles from './styles.css' with { type: 'css' };

// Importing WebAssembly modules
import wasmModule from './math.wasm' with { type: 'webassembly' };

// Dynamic import with assertions 
const loadConfig = async (env) => {
  const config = await import(`./config-${env}.json`, { with: { type: 'json' } });
  return config.default;
};

// Conditional import
if (process.env.NODE_ENV === 'development') { import devConfig from './config-dev.json' with { type: 'json' }; }

Template String Enhancements

更强大的字符串模板

// Automatic multi-line indentation handling
const html = html`
<div class="container">
  <h1>${title}</h1>
  <p>${content}</p>
</div>
`; // Indentation handled automatically

// Custom interpolation handling
const sql = sql`
SELECT * FROM users
WHERE age > ${minAge}
AND city = ${city}
`; // Automatically protects against SQL injection


// Internationalized template
const message = i18n`Hello ${name}, you have ${count} new messages`; // Automatically handles locale and pluralization rules

// Styled string
const styledText = css`
color: ${primaryColor};
font-size: ${fontSize}px;
margin: ${margin};
`;

参考

https://medium.com/codeelevation/you-can-write-javascript-like-this-new-es2025-syntactic-sugar-be4081cf5187

开学第一课

作者 Jack Pu
2025年9月2日 10:10
开学第一课

不知不觉暑假,就过去了。今年自己开始主动的去追求一些工作上的东西,和前几年类似于非强制性的追求有些区别,而在这其中其实学到了很多。

Clear Context

最近聊 AI,我们都强调 Context。这个关坚持不光是 AI 里很重要的东西,也是很多公司写进公司文化的一部分。自己最近就犯过一次 context 不够清晰的错误。

比如最近在和同事沟通实验优先级的时候,就容易引入自己主观的一些想法,导致对方没能理解清楚需要做什么。其实在日常沟通中,我们要避免这样的问题,可以在沟通的时候划分三个部分

  • Background
  • Progress
  • Action

其中 Action 部分尤为重要,这个才是你沟通的目的,所以你每次沟通的时候最后 Review的时候看看是不是三部分都齐全了。我们经常聊了很多,发现 Action 没有,这就是典型的 Clear Context 缺失。

工作不夹杂感情

工作就不要带任何感情!!!

这个是自己刚入职场就很多前辈强调了,这里面有很多因素,工作中不带入任何感情,会降低一些内耗。其实自己在过去一年里,也有一些新的体会。

我们可以反过来理解,工作时候应该像个机器,因为机器是没有感情的,就是单纯的执行。

为什么引入感情后,会影响自己在 action 的时候变得犹豫。比如,经常很多人觉得中国人不善于提问,交流。这里面国人思维就经常内心活动,想很远,觉得如果提了问题很糟糕,别人怎么看?这个问题是不是太低级了,别人觉得自己 SB。

这里面就引入了自己主观感情的判断,你这个时候你把自己想想成一个机器人,你 action 就类似于一个个 TODO list.

当然和同事相处,是另外的内容,仅仅从工作内容上出发,我觉得这个有道理的。

Keep Fit

最近一个暑假,很好的是工作日下午锻炼,坚持了很久。对面操场随着暑假,便也空出来让市民锻炼。其中也不乏课外班在这里训练长跑。自己打底的是五公里,但是天气炎热,一般一半就会休息,慢走一会再继续跑。体重增加,明显感觉是跑步的配速变高了,当然也不知道是不是下午炎热的气温影响,逼近30度,感觉跑太快,会不会随时”挂掉“。随着天气降温,办的次卡也开始生效,今年公司推出的福利报销,感觉还是挺好的,每周可以去体育馆2次,一次无氧,一次游泳。

悄悄然,感觉肚子是小了些的,不过没有称体重,可以再坚持一阵子,看看是不是有明显的变化。锻炼这种事情,就是需要坚持,不找任何理由,计划了就去做,这是一个最简单锻炼毅力的事情。

为什么 Android Webview 不支持 L1 级别的 Widevine DRM 安全级别

作者 Jack Pu
2025年7月31日 15:13
为什么 Android Webview 不支持 L1 级别的 Widevine DRM  安全级别

接着前一篇文章 DRM Widevine L1 在 Android Webview 的支持情况, 这一篇文章是基于 Cursor 对 Chroimum 源码分析得出的一些结论。本文主要回答两个问题:

  • 为什么 Android Webview 不支持 L1 级别的 Widevine DRM 安全级别
  • 为什么 Android Chrome 应用支持 L1 级别的 Widevine DRM 安全级别

为什么 Android Webview 不支持 L1 级别的 Widevine DRM 安全级别?

主要原因:

1. 硬件安全解码要求
根据 Chromium 代码中的逻辑,在 Android 平台上:

// 在 Android 上,SW_SECURE_DECODE 及以上级别需要硬件安全编解码器
if (robustness >= Robustness::SW_SECURE_DECODE || hw_secure_codecs_required) {
    return EmeConfig{.hw_secure_codecs = EmeConfigRuleState::kRequired};
}

这意味着:

任何高于或等于 SW_SECURE_DECODE 的安全级别都需要硬件安全编解码器但是 WebView 没有提供硬件安全编解码器的基础设施

2. WebView 的架构限制

单进程架构:WebView 运行在单进程中,不像 Chrome 那样有进程隔离缺少安全表面:硬件安全解码需要特殊的安全表面处理,WebView 没有实现权限限制:硬件安全解码通常需要额外的系统权限和特殊处理

3. 安全考虑

硬件安全解码需要更严格的进程隔离和安全机制 在 WebView 中实现硬件安全解码可能会带来安全风险
需要大量的架构改动才能安全地支持

技术细节:

DRM 安全级别从低到高:

  • SW_SECURE_CRYPTO:软件安全加密
  • SW_SECURE_DECODE:软件安全解码(WebView 支持的最高级别)
  • HW_SECURE_CRYPTO:硬件安全加密
  • HW_SECURE_ALL:硬件安全全部

** WebView 的限制:**

  • 只支持到 SW_SECURE_DECODE 级别
  • 不支持 HW_SECURE_CRYPTO 和 HW_SECURE_ALL
  • 当网页请求更高安全级别时,WebView 会拒绝

实际影响:

  • 内容兼容性:某些高安全级别的内容可能无法在 WebView 中播放
  • 性能影响:软件解码比硬件解码消耗更多 CPU 资源
  • 电池消耗:软件解码会增加电池消耗

为什么这样设计?

  • 简化架构:WebView 作为嵌入式组件,保持简单和安全
  • 兼容性:确保在各种 Android 设备上都能正常工作
  • 安全性:避免在嵌入式环境中引入复杂的安全机制

这就是为什么 Android WebView 只支持 SW_SECURE_DECODE 的技术原因。如果需要更高级别的 DRM 支持,通常需要使用原生 Android 应用或 Chrome 浏览器。

1. Android 硬件安全编解码器支持检查代码

// 硬件安全编解码器的渲染只有在 AndroidOverlay 启用时才支持
const bool is_secure = robustness == CdmInfo::Robustness::kHardwareSecure;
if (is_secure) {
    bool are_overlay_supported =
        content::AndroidOverlayProvider::GetInstance()->AreOverlaysSupported();
    bool overlay_fullscreen_video =
        base::FeatureList::IsEnabled(media::kOverlayFullscreenVideo);
    if (!are_overlay_supported || !overlay_fullscreen_video) {
        DVLOG(1) << "Hardware secure codecs not supported for key system"
                 << key_system << ".";
        std::move(cdm_capability_cb)
            .Run(base::unexpected(media::CdmCapabilityQueryStatus::
                                      kHardwareSecureCodecNotSupported));
        return;
    }
}

关键点:

  • 硬件安全编解码器需要 AndroidOverlay 支持
  • 需要 kOverlayFullscreenVideo 功能启用
  • 如果这些条件不满足,就会返回 kHardwareSecureCodecNotSupported 错误

2. WebView 明确禁用了相关功能
在 src/android_webview/browser/aw_field_trials.cc 中:

// WebView 还不支持视频覆盖的全屏
aw_feature_overrides.DisableFeature(media::kOverlayFullscreenVideo);

// WebView 不支持多进程,所以不要尝试在单独进程中调用一些 MediaDrm API
aw_feature_overrides.DisableFeature(media::kMediaDrmQueryInSeparateProcess);

关键点:

  • WebView 明确禁用了 kOverlayFullscreenVideo 功能
  • 禁用了 kMediaDrmQueryInSeparateProcess 功能

这些正是硬件安全编解码器所需的基础设施

3. Widevine 密钥系统信息中的 Android 特定逻辑
在 src/components/cdm/renderer/widevine_key_system_info.cc 中:

当 Android 应用嵌入 WebView 时,WebView 的浏览器代码运行在应用的进程中
(我们称之为"浏览器进程")。这意味着 WebView 代码共享相同的地址空间,
我们通常认为应用就像任何其他浏览器进程代码一样是可信的。

关键点:

  • Android 平台要求 SW_SECURE_DECODE 及以上级别必须使用硬件安全编解码器
  • 但是 WebView 没有提供硬件安全编解码器的基础设施

4. WebView 的架构限制

在 src/android_webview/docs/architecture.md 中:

当 Android 应用嵌入 WebView 时,WebView 的浏览器代码运行在应用的进程中
(我们称之为"浏览器进程")。这意味着 WebView 代码共享相同的地址空间,
我们通常认为应用就像任何其他浏览器进程代码一样是可信的。

关键点:

  • WebView 运行在单进程中,没有进程隔离
  • 硬件安全编解码器通常需要特殊的进程隔离和安全机制
  • WebView 的架构不支持这些安全要求

5. WebView 中缺少硬件安全编解码器相关代码

通过搜索发现,在 src/android_webview/ 目录中:

总结

这些代码证据清楚地表明:

  • 硬件安全编解码器需要 AndroidOverlay 支持,但 WebView 禁用了这个功能
  • WebView 禁用了多进程 MediaDrm 查询,这是硬件安全编解码器所需的基础设施
  • WebView 运行在单进程中,缺乏硬件安全编解码器所需的进程隔离
  • WebView 代码库中完全没有硬件安全编解码器的实现

因此,WebView 确实没有提供硬件安全编解码器的基础设施,这就是为什么它只能支持 SW_SECURE_DECODE 的原因。

为什么 Android Chrome 应用支持 L1 级别的 Widevine DRM 安全级别?

核心差异:Chrome vs WebView

1. 技术基础设施支持

Chrome 具备完整的硬件安全基础设施:

AndroidOverlay 支持

// Chrome 支持 AndroidOverlay(在 AndroidOverlayProviderImpl.java 中)
@CalledByNative
private static boolean areOverlaysSupported() {
    return true;  // Chrome 返回 true,支持 overlay
}	

OverlayFullscreenVideo 功能启用

// 在 media_switches.cc 中,Chrome 默认启用此功能
BASE_FEATURE(kOverlayFullscreenVideo,
             "overlay-fullscreen-video", 
             base::FEATURE_ENABLED_BY_DEFAULT);  // 默认启用

WebView 明确禁用这些功能:

// 在 aw_field_trials.cc 中,WebView 明确禁用
// WebView does not support overlay fullscreen yet for video overlays.
aw_feature_overrides.DisableFeature(media::kOverlayFullscreenVideo);

2. 多进程架构支持

Chrome 的复杂多进程架构:

<!-- Chrome 有 40 个沙盒进程用于安全隔离 -->
{% for i in range(40) %}
<service android:name="org.chromium.content.app.SandboxedProcessService{{ i }}"
    android:process=":sandboxed_process{{ i }}"
    android:isolatedProcess="true" />
{% endfor %}

<!-- Chrome 还有 10 个特权进程 -->
{% for i in range(10) %}
<service android:name="org.chromium.content.app.PrivilegedProcessService{{ i }}"
    android:process=":privileged_process{{ i }}" />
{% endfor %}

进程隔离的安全优势:

  • 渲染器进程隔离:DRM 内容在独立的沙盒进程中处理
  • 特权进程分离:硬件安全操作在特权进程中执行
  • 安全边界:进程间有严格的安全边界

3. 硬件安全检查机制

Chrome 的硬件安全检查:

// 在 key_system_support_android.cc 中
const bool is_secure = robustness == CdmInfo::Robustness::kHardwareSecure;
if (is_secure) {
    bool are_overlay_supported =
        content::AndroidOverlayProvider::GetInstance()->AreOverlaysSupported();
    bool overlay_fullscreen_video =
        base::FeatureList::IsEnabled(media::kOverlayFullscreenVideo);
    
    if (!are_overlay_supported || !overlay_fullscreen_video) {
        DVLOG(1) << "Hardware secure codecs not supported";
        return; // 拒绝硬件安全编解码器
    }
    // Chrome 通过检查,支持硬件安全
}

4. DRM 安全级别支持对比

Chrome 支持的 DRM 级别:

  • ✅ SW_SECURE_CRYPTO - 软件安全加密
  • ✅ SW_SECURE_DECODE - 软件安全解码
  • ✅ HW_SECURE_CRYPTO - 硬件安全加密
  • ✅ HW_SECURE_DECODE - 硬件安全解码
  • ✅ HW_SECURE_ALL - 硬件全安全(L1 级别)

WebView 支持的 DRM 级别:

  • ✅ SW_SECURE_CRYPTO - 软件安全加密
  • ✅ SW_SECURE_DECODE - 软件安全解码(最高级别)
  • ❌ HW_SECURE_CRYPTO - 不支持
  • ❌ HW_SECURE_DECODE - 不支持
  • ❌ HW_SECURE_ALL - 不支持

5. 具体技术实现

Chrome 的 L1 支持实现:

  • 硬件安全表面(Secure Surface):
  • Chrome 使用 DialogOverlayImpl 创建安全的视频表面
  • 支持 AndroidOverlay 进行硬件加速渲染
  • 具备 TEE(Trusted Execution Environment)集成

安全视频路径:

// Chrome 支持 HW_SECURE_ALL 级别
max_video_robustness = Robustness::HW_SECURE_ALL;

OEMCrypto 集成:

// Chrome 支持基于硬件的 L1 Widevine DRM
// Enable use of HW based L1 Widevine DRM via the cdm-oemcrypto daemon	

6. 架构总结

特性 Chrome WebView
多进程架构 ✅ 40个沙盒进程 + 10个特权进程 ❌ 简单的双进程模型
AndroidOverlay ✅ 完全支持 ❌ 不支持
OverlayFullscreenVideo ✅ 默认启用 ❌ 明确禁用
硬件安全表面 ✅ 支持 ❌ 不支持
进程隔离 ✅ 严格隔离 ❌ 基本隔离
TEE 集成 ✅ 支持 ❌ 不支持
最高 DRM 级别 HW_SECURE_ALL (L1) SW_SECURE_DECODE (L3)

结论

Android Chrome 支持 L1 DRM Security Level 是因为它具备了完整的硬件安全基础设施:

  • 完整的多进程安全架构
  • AndroidOverlay 硬件表面支持
  • 进程隔离和沙盒机制
  • TEE(可信执行环境)集成
  • 专门的安全视频渲染路径

而 WebView 为了保持轻量级和兼容性,选择了更简单但安全性较低的架构,因此只能支持到 SW_SECURE_DECODE 级别。

夏末

作者 Jack Pu
2025年7月28日 20:57
夏末

北京周末变得特别湿热。一场暴风雨似乎即将到来。

汗水

来北京,觉得这一周真的特别的闷热,出汗率大增。尤其每每下午锻炼完,感觉汗水足足装满一瓶 300 ml 的矿泉水瓶。看看了天气预报,热也就这一周了,下一周就要开始陆陆续续降温了。 自己最近锻炼,就在附近的操场,暑假学生放假,操场边开发给了普通大众。绕着操场跑,比沿着街道要好多了,没有太多跑跑停停,按着自己的节奏和计划,当然在操场,没有太多意外截停,比如红绿灯这些,自然跑的公里数也增加了。最后一圈,自己也会尝试冲刺,35分钟左右的锻炼时间,自己却发现需要长达一个多小时的慢慢停歇,汗水也才慢慢止住。

溺亡

月底,看到新闻报道东北大学学生溺亡的消息。第一时间,习以为常,每年总会有溺亡的事情发生,无论小孩还是大人。但是六个人, 确实还是很夸张。知道点击进去后,才发现,不是溺亡,是一起见习安全事故。自己学安全的,很明白作为安全员的基本守则,设备存在安全隐患,造成损失的,是会有连带责任的。可是我始终觉得,这个事情最痛心的是,企业居然对外宣称是溺亡。六个大学生连死因都不能正常告知大众。近几年,尤其疫情,我感受到社会的变化。尤其政府的变化,没有什么时候能比现在跟追求稳定。是什么变化,我不敢说。但是这则新闻必定存在于历史的长河中,溺亡的翻译权似乎也只能交给后人。

全局BP

今年 MSI 引入全局 BP。我越发意识到 LOL 这款游戏,变得更加有趣了,它对操作的要求是很高,但是岁教练的战术储备,选手的英雄熟悉度都有了进一步的提升,换句话说,他能够延长很多选手的职业生涯。近日看到 faker 续约到2029年,什么概念,33 岁的年龄还在进行电子竞技。所以很多人担心35岁就裁员的程序员,不妨想想,时代一直在发展,抓住时代趋势才是最重要的。

DRM Widevine L1 在 Android Webview 的支持情况

作者 Jack Pu
2025年7月20日 11:27
DRM Widevine L1 在 Android  Webview 的支持情况

最近在开发中遇到了 Android TV 无法通过 L1 的检测。代码检测如下:

async function checkWidevineL1Support() {
    const keySystem = 'com.widevine.alpha';

    // Robustness levels to test, from most secure (L1 equivalent) to least secure (L3 equivalent)
    const robustnessLevels = [
        'HW_SECURE_DECODE', // Likely L1: decryption and decoding in TEE
        'HW_SECURE_CRYPTO', // Likely L2: decryption in TEE, decoding in software
        'SW_SECURE_DECODE', // Likely L3: software-based, with decode in software
        'SW_SECURE_CRYPTO'  // Likely L3: software-based, with crypto in software
    ];

    for (const level of robustnessLevels) {
        const config = [{
            initDataTypes: ['cenc'], // Common Encryption
            videoCapabilities: [{
                contentType: 'video/mp4; codecs="avc1.42E01E"', // Example codec
                // You might need to add other properties like robustness
                robustness: level
            }],
            audioCapabilities: [{
                contentType: 'audio/mp4; codecs="mp4a.40.2"', // Example codec
                robustness: level // Audio often doesn't need L1
            }]
            // persistentState: 'required', // Only if you need offline playback
            // distinctiveIdentifier: 'optional' // Optional, not usually required for L1 check
        }];

        try {
            console.log(`Attempting to request Widevine with robustness: ${level}`);
            const keySystemAccess = await navigator.requestMediaKeySystemAccess(keySystem, config);

            // If we reach here, this robustness level is supported.
            // Check the exact configuration that was negotiated.
            const negotiatedConfig = keySystemAccess.getConfiguration();
            console.log(`Widevine supported with negotiated robustness:`, negotiatedConfig);

            // If the highest level (HW_SECURE_DECODE) was successfully negotiated,
            // or a sufficiently high hardware-backed level, you can infer L1 support.
            if (negotiatedConfig.videoCapabilities && negotiatedConfig.videoCapabilities[0].robustness === 'HW_SECURE_DECODE') {
                console.log('🎉 Widevine L1 (or equivalent hardware-backed decoding) is likely supported!');
                return 'L1 Supported';
            } else if (negotiatedConfig.videoCapabilities && negotiatedConfig.videoCapabilities[0].robustness === 'HW_SECURE_CRYPTO') {
                 console.log('💡 Widevine L2 (hardware-backed crypto) is likely supported.');
                 return 'L2 Supported';
            } else {
                console.log(`⚠️ Widevine L3 (software-based) is likely supported with robustness: ${negotiatedConfig.videoCapabilities ? negotiatedConfig.videoCapabilities[0].robustness : 'unknown'}.`);
                return 'L3 Supported';
            }

        } catch (error) {
            console.warn(`Widevine with robustness ${level} NOT supported:`, error.message);
        }
    }

    console.log('❌ Widevine is not supported with any tested robustness level.');
    return 'Not Supported';
}

// Call the function to check
checkWidevineL1Support().then(result => {
    console.log("Overall Widevine L1 check result:", result);
});

自己第一时间怀疑的是,Android TV 作为盒子,难道天生不就应该有更好的视频播放支持么?

目前可以确认的是桌面端,Chrome 确实不支持 L1, 只支持到 L3 级别: https://www.vdocipher.com/blog/widevine-drm-hollywood-video/#:~:text=system in use.-,Widevine DRM Compatibility,higher security features than L3.

然而原生确实是支持 L1,Android TV 很早就开始支持了 Widevine L1 的播放。 这里有个疑问点其实就是 WebView 的播放和原生播放是不是存在一些限制?

WebView VS Chrome in Android

Android 开发中的 WebView 组件 和 Chrome 浏览器 都是基于 Chromium 开源项目,因此它们共享相同的渲染引擎(Blink)。这意味着在大多数情况下,同一个网页在 WebView 和 Chrome 中会有相似的渲染效果。然而,它们在功能、用途和独立性方面存在显著区别。

特性 Android WebView 组件 Chrome 浏览器
类型 Android 应用内的 UI 组件 独立的 完整浏览器应用
UI 内置的浏览器UI(需开发者实现) 完整的浏览器UI和功能
用途 在应用内嵌入显示网页内容 独立的网页浏览体验
控制 通过 API 由应用开发者编程控制 用户直接交互和控制
数据 与Chrome共享数据 可以 与Google账号同步数据
更新 通常随系统或“Android System WebView”应用更新 独立通过应用商店更新,更频繁
功能 功能相对 受限 功能 齐全,提供高级浏览体验

在 DRM 上差异:

Chrome 浏览器 在 DRM 支持方面通常更全面、稳定且更新及时,因为它是一个旨在提供完整浏览体验的独立应用。它默认启用了对 Widevine DRM 的支持,并且会随着浏览器更新而不断提升兼容性。

Android WebView 组件虽然基于相同的 Chromium 内核,但作为嵌入式组件,它需要应用开发者进行额外的配置和权限处理才能启用 DRM 支持。在某些高级 DRM 功能(如持久性许可)方面,或者对于对播放环境有严格要求的服务,WebView 可能存在限制或兼容性问题。

所以这里会有两个问题:

  • 我们是否做好相应的 WebView 配置
  • WebView 支持 L1 这块限制是不是本身就一直存在

我们尝试做过一些配置的更改,但是都没能达到效果。目前我们的数据采集给出的结论是,目前都不支持 L1。支持到 L3 级别。所以我更倾向于这块被限制了。

也欢迎评论分享有没有别的解法?

一年工作回顾

作者 Jack Pu
2025年7月3日 19:04
一年工作回顾

不知不觉有一年,今年似乎自己有很多要写的,主要是公司发展遇到了很多未知不可预测的事情。

今年绩效Review的时候,自我感觉就是很不对劲,当然这是主观的感觉,绩效Review依旧是管理层的事情,其实自己一贯以来,更加关注自己的成长和回报。

意外

公司绩效优化 今年开始了一批绩效优化,今年员工的流动性明显加强了,自己入职后认识的一些小伙伴已经走了一部分,这个给极其稳定的团队还是带来了非常大的影响。似乎佛系的态度在这里开始变得有一定被动了。自己也纠结焦虑过一段时间,然而我在今年写过一篇《裁员后计划》,更新完之后,焦虑确实消失了。焦虑其实来源于未来未发生的事情,而如果针对未来发生的事情,有个很好的计划或者目标,反而变成了一种值得期待的事情。今年由于别的原因,也想要好一点的绩效,结合更加严苛的比例,似乎变成了一件更加困难的事情。

数据法案影响 由于今年来对于数据法案的影响,我们已经看到了部分撤退的外企,尤其近期 Comcast 中国团队的关闭,让大家也有些许担忧。这件事情其实也是一个属于我们这一层无法预知结果的事情,当然我们面临的有一种结果就是部分员工关闭回家,部分员工transfer海外。所以这个影响会联系到今年的绩效。

结果

当然自己对于结果是能够接受和预期的。自己是一个心态可以快速调整,胜负心又极强的人。我在很早的时候写过《下等马理论》 分享如何缓解工作的焦虑。但是今年自己希望拿出自信来挑战更高的目标。

改善

过去一年,自己在工作中主要有些许方面做得不好,

  • 事情优先级并没有把握很好,没能处理好绩效高优先级的事情,我们还是应该相信数据的说服力远比任何文字
  • 没能追求极致,自己在参与的事情,并未调整做出超出预期的事情,这个其实也是一个关键点,如果我们可以深入一点,或许结果就不一样
  • 些许松懈,尽管公司强调 WLB,但是对于成绩有要求,还是应该多付出保持快速响应
  • 拥抱 AI,如果你不够熟练,但是如果你比任何人善于利用 AI,那也能成为一个极为高效的选手。人类进化最重要的点,便是利用工具,所以应该多可能尝试这些工具

我回忆的2008

作者 Jack Pu
2025年5月19日 16:20
我回忆的2008

前些天是 512,汶川大地震距今已经17 年了,自己从当初的高中生,已然迈入需要思考中年生活的日子。近些年很多事件,尤其最近的协和4+4按键,以及杨钿甜230万天价耳环中涉及到的赈灾款挪用的问题,让大家又想起了曾经,全国人上下一心为汶川祈福的时候。

我的2008年经历了很多,我依稀记得入春的时候,某个周末洗澡发生在身上的疙瘩事件,这件事困扰我很多年。医学术语上,叫荨麻疹,而且自己并非过敏,后来医生定义的是物理性荨麻疹,自己有尖锐的物体,或者其他棱角的物体,物理划过或者挤压,皮肤都会在相应区域出现荨麻疹。才开始,自己非常苦恼,稍不留意就留下荨麻疹,很痒,但是又不能乱碰,结合着天气日渐炎热,心情自然是焦躁不少。

医院也没有给出任何治疗的方案,而是以患者心情调整为主,没啥大毛病,如果出现了,开一些 炉甘石洗剂 涂抹的药剂。随着时间推移,自己也渐渐适应了这样的病情。心态上忽视,专注与别的事情。渐渐地,这种苦恼便也没有太多了,虽然偶有发作,但是依然不是一个特别大事情。自己后续几年发现,除了天气炎热的时候,入冬了,温度降了,反而出现这种事情的次数少了。尤其后来,去了大学,来到湖南,除了大一深夜有一次出现过,几乎就再也没有遇到了。后面毕业来了北京,几乎就测地消除了。

所以人体免疫系统真的很复杂,但是遇到这类怪病,心态是真的很重要。

夏天到了,县城的温度也还可以接受,中午虽然大太阳天,但是却热的可以接受。我依稀记得2008年5月12日那天,我从午休中醒来,然后正准备去学校。下楼途中,发现外边滴滴答答的巨响,明显比暴雨时候的大,然后,我开始犹豫,又上楼了,看见窗外不停的掉东西,楼下种花的,我大概知道是什么掉下来,随后开始感觉楼梯左摇右摆,有种松动的感觉,不对劲。立马冲下楼去了。那个时候,好多人都已经下了楼,听到别人一句: 地震了。

心想,这个就是地震。可是自己还是坚持去了学校。教室里已经坐了很多学生,随后广播通知了,今天不上课,大家去操场。我依稀记得那个夜晚,班主任拿着他的收音机,电台里重复的是地震信息,以及温总理地震2小时后已经乘坐专机前往救灾现场。那个夜晚自己没有睡觉,很多同学席地而坐,看着满天星空,脑海里还是一片空白。

后面我们放假了,让学生回到家中,而家里电视上是 24 小时不停的直播,7天后全国各地降半旗,悼念汶川地震逝去的同胞。但是自己并没有感触,而是前几年看2008年的视频时候,519那天,天安门广场集结着大量自发的群众,不停的呐喊:加油汶川,加油中国。

那一年,温总理在黑板上留下了 多难兴邦。 胡主席说出了,任何困难都压不到英雄的中国人民。

2个多月后,我们迎来了奥运会。万人空巷,这个词汇,是经常考的。而用这个词汇形容拿一天,再恰当不过。几乎镇子上的人都在晚上,守候在电视前看这场开幕式。震撼的场景有很多,在极为震撼的倒计时后,升国旗的那一刻,环绕鸟巢的观众一起唱国歌,在我当时的印象中,绝对是难以忘记的,那种自豪之情其实是油然而生的,虽然那时刻我们并没有联系到那一年的大雪,那一年的地震。

如果说2008年给大家留下了什么, 谈不上是民族自豪感,2008 留下了一种对未来极为期待的希望。因为我们刚刚经历地震的灾害,看到全国各族人民空前的团结,看到经济彭博发展,民生逐步改变的希望。

其实那一年,很多所谓的公知记者,孜孜不倦的批判这个国家,这个政府,教育的不公,贪污的问题,林芝总总,我们能听到异样声音非常多。

而时间来到现在,记者或者公知已经再也不谈这些事情,网上的这些热搜问题,除了自媒体带节奏,我们真的看不到有人再愿意去认真调查。《老男孩》才出来的时候,我们听不懂,但是好听,现在我们终于知道人生的无奈是什么。

所以我们怀念过去的希望,而如今社会,公信力下降严重,就业环境异常糟糕,年轻人没工作,年龄大的延迟退休,房价翻了几倍,收入增长滞后,家庭成本过高,失望和绝望已是常态。我最近看过12年记者会,总理谈到了当前社会问题,十几年过去了,其实一点都没有进步。

记者问: 关于缓解收入分配差距的问题,我想着重从四个方面入手:
回答: 第一,提高城乡居民的收入,提高最低工资水平,使城乡居民的收入能与经济增长和劳动生产率的提高相适应。第二,调节收入分配。要限制高收入者的收入,特别是国有企业和国有金融企业高管人员的收入,要增加中等收入的比重。第三,建立健全社会保障制度。第四,保护合法收入和取缔非法收入。
我以为在收入分配当中,特别应该把握好三点:
首先,就是要为所有的人创造一个学习、就业和创业的均等条件,让他们在同一起跑线上起跑。其次,要关心弱势群体的生活。一个国家如果弱势群体生活状况得以改善,那么整个国家群众生活的状况也会得以改善。第三,要重视财政和收入分配制度的改革,使共同富裕建立在制度的基础之上。谢谢。

使用 JS 判断 PSSH Box

作者 Jack Pu
2025年3月17日 12:58
使用 JS 判断 PSSH Box

现在 AI 已经非常牛逼了,如果你有这样的需求,非常明确的,建议你直接使用 AI。这是谷歌 Gemini 给出的结果:

使用 JavaScript 检测 MP4 文件中的 PSSH(保护系统特定标头)是一项复杂的任务,主要是因为:

二进制数据:MP4 文件是二进制的,而不是基于文本的。浏览器环境中的 JavaScript 在低级别上限制了对二进制数据的直接访问。
文件结构:PSSH 嵌入在 MP4 的原子结构中,需要解析才能找到它。
浏览器安全性:浏览器限制直接文件系统访问,限制您直接读取任意 MP4 文件的能力。
但是,您可以通过多种技术组合来实现这一点,主要涉及用于检索文件的 fetch API、用于二进制数据处理的 ArrayBuffer 和自定义解析逻辑。以下是一般方法的细分:

  1. 获取 MP4 文件:
async function getPSSH(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const arrayBuffer = await response.arrayBuffer();
    return findPSSH(arrayBuffer);
  } catch (error) {
    console.error("Error fetching or parsing MP4:", error;
    return null;
  }
}
  1. 解析 MP4 原子结构:

这是最具挑战性的部分。您需要实现一个能够理解 MP4 原子结构的解析器。这是一个简化的示例(您可能需要一个更强大的解析器来应对实际场景):

function findPSSH(arrayBuffer) {
  const dataView = new DataView(arrayBuffer);
  let offset = 0;

  while (offset < dataView.byteLength) {
    const size = dataView.getUint32(offset);
    const type = String.fromCharCode(
      dataView.getUint8(offset + 4),
      dataView.getUint8(offset + 5),
      dataView.getUint8(offset + 6),
      dataView.getUint8(offset + 7)
    );

    if (type === "pssh") {
       // 找到 PSSH 原子
      const psshData = new Uint8Array(
      arrayBuffer.slice(offset + 8, offset + size)
    );
    //这里是原始 pssh 数据。
    return psshData;
  }

    offset += size;
  }

  return null; // 未找到 PSSH
}

  1. 提取 PSSH 数据:
    找到“pssh”原子后,即可提取其中的数据。PSSH 结构本身有自己的格式,您可能需要根据具体用例进一步解析。
getPSSH("your_mp4_file.mp4").then((pssh) => {
  if (pssh) {
    console.log("PSSH found:", pssh);
    // 处理 pssh 数据。
    // 您可以将 Uint8Array 转换为十六进制字符串以便于查看。
    const hexPssh = Array.from(pssh).map(byte => byte.toString(16).padStart(2, '0')).join('');
    console.log("PSSH hex:", hexPssh);
  } else {
    console.log("PSSH not found.");
  }
 });

重要注意事项:

复杂性:MP4 解析很复杂。可靠的实现通常依赖于专门的库。

性能:在 JavaScript 中解析大型 MP4 文件可能非常耗费资源。

跨域请求:如果您的 MP4 文件位于不同的域中,则需要通过在服务器上设置适当的标头来处理 CORS(跨域资源共享)。

浏览器限制:浏览器具有安全限制,可能会限制您执行某些操作的能力。

替代方法:

对于服务器端处理,带有 mp4box.js 或 FFMPEG 等库的 Node.js 会更高效。

对于处理 DRM 的客户端应用程序,媒体源扩展 (MSE) API 和加密媒体扩展 (EME) API 是标准机制,它们以更受控制的方式提供对 PSSH 数据的访问。

EME 和 MSE:如果您正在使用受 DRM 保护的内容,则 EME 和 MSE API 是处理 pssh 的正确方法。EME 将为您提供 pssh 信息。
此信息提供了一般方向。对于生产级应用程序,强烈建议使用专用库或服务器端处理。

不到三分钟,这个问题就解决了,但是,这里有一个瑕疵,自己在测试中发现的,

findPSSH 中使用 while 循环进行 type 类型的对比没有任何问题,但是 mp4 box 的结构一般如下:

使用 JS 判断 PSSH Box

我发现运行到 moov 后就停止了。

const size = dataView.getUint32(offset); 

我们获取到了 moov 的长度后,理论应该继续进入 moov box 进行嗅探,然而我们并没有,因此我们可以尝试一下递归

export function findPSSH(arrayBuffer, offset) {
  const dataView = new DataView(arrayBuffer);

  while (offset < dataView.byteLength) {
    const size = dataView.getUint32(offset);
    const type = String.fromCharCode(
      dataView.getUint8(offset + 4),
      dataView.getUint8(offset + 5),
      dataView.getUint8(offset + 6),
      dataView.getUint8(offset + 7)
    );
    if (type === "moov") {
      return findPSSH(arrayBuffer, offset + 8);
    }
    if (type === "pssh") {
      // Found the PSSH atom
      const psshData = new Uint8Array(
        arrayBuffer.slice(offset + 8, offset + size)
      );
        //Here you have the raw pssh data.
      return psshData;
    }

    offset += size;
  }

  return null; // PSSH not found
}

最后 debug 效果还不错。 所以现在 AI 质量真的非常高,我们只需要稍许 debug 就可以了,无需要浪费太多时间。

❌
❌