MoreRSS

site iconAllen Hua修改

Java工程师,常驻南京。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Allen Hua的 RSS 预览

绝壁与深谷:2026盛夏的南太行自驾游

2026-08-11 00:17:00

前言

从合肥休息好之后我回到了南京,决定接下来去哪里。因为时间不充足了,所以放弃了川藏线,尽管我还真的没有去过西藏,只走过川西小环线,那就以后再达成这个心愿吧。台风“红霞”刚走不久,“白海豚”又准备在浙江沿海登陆,所以也放弃了沿海自驾。最终确定南太行,这里也是一直魂牵梦绕我的地方。这一段旅程很紧凑,短短四天。

day 1

南京出发,前往河南省新乡市辉县市。整理好行李,去了一趟好想来买一些泡面香肠卤蛋什么的,开上我的斯柯达明锐,冲呀。上午快要11点才真正出发,但因为开的挺快挺顺畅,晚上7点就到了酒店。

下楼吃了一顿辉县特有的麻辣烫,但端上来时我询问是不是你们这边的特色“酸甜口味的麻辣烫”,老板说,“不是,很多人不习惯吃这种口味的,只有你们明确讲了需要酸甜口味才会给你们做那种” 那还有啥办法呢,好可惜,吃不到特色就算了吧,以后还有机会的。

当我做细致的路线攻略时发现天塌了,最近雨季,南太行这边有些地方山体滑坡、落石,导致武家湾和黑毛沟大峡谷封路了。然后我加了一个抖音自驾群获取了最新消息,群主分享了最保险且风景不差的路线,于是我就主要参考了这条线进行第二天的旅程。路线大致是:

宝上路-棋子山-浙水村-东柏坡天路-神龙湾挂壁观景台-井底村-高家台-鲁班天梯-石板岩小镇

美美睡觉,期待明天路上的美景。

day 2

早上7点多出发,加满油前往四十多公里之外的「崖边咖啡」,也就是「宝上路观景台」,或者「赶羊站观景台」。就是今天天气不给力,有很大的雾,暂时散不开,可惜在国家地理杂志封面图拍摄打卡点拍不到很极致的照片了。

HDK00207_compressed.jpg

到了崖边咖啡,要尽快找空位停车,今天是非工作日并且我来得早可能还好,大家以后想去一定要记得优先停车,多走几步路再看风景都是小事,因为越到后面停车越耽误时间才是大事。国内现在只要稍微人多的地方都是这样的,太多人出来游了,停车老大难。

空气不那么通透,因为雾气弥漫,所以先拉一下长焦拍个特写看看。

HDK00211_compressed.jpg

初次看到这个崖壁还是很震撼的,左侧崖壁之上就是公路,刚才就从那个方向驶来。很多崖壁像是刀切豆腐一样。

HDK00213_compressed.jpg

国家地理杂志封面图同款/相似机位,来这就是为了这张照片 2333。

HDK00219_compressed.jpg

下面应该是5A级景区宝泉旅游区的景区公路。只能景交车才能行驶🙃

HDK00220_compressed.jpg

我向前走了一段路,看到下面的宝泉旅游区,感慨这个观景位置也很不错啊,还是免费的,除了天气不通透,在这里吹一会山风也觉得来值了。

HDK00223_compressed.jpg

HDK00225_compressed.jpg

不知道为啥还是有很多车行驶到武家湾那边,也许还有别的小路或者他们就到途中再折返吧。反正我下山了,驶向了焦作云台山那边,然后走G207国道和乌海线到棋子山,就是网红猪八戒打卡点那里。这段路一直爬坡,还有个让我喜出望外的“叠彩洞”,我原本的攻略并没有这里,自驾群里的攻略也是建议走沿太行高速-郑辉高速-陵侯高速到棋子山,但是最终我选择了走国道,因为风景不在高速上(在的话也没办法停车欣赏和拍照)。这里让我哇哇叫,也太帅了:20条隧道,好几个隧道里都有U型弯,简直太刺激了。不过隧道里有一辆车轮子掉沟里了,大家注意安全驾驶。下图是进隧道之前拍摄的

HDK00236_compressed.jpg

上到山顶差不多来到了“一斗水村”,我找了荫凉处停车,拿出零食开炫,先垫吧垫吧。山顶处格外凉爽,这瓶AD钙奶都显得格外好喝。

山上一斗水景区一斗水村的AD钙奶格外好喝.jpg

后面继续赶路到棋子山,沿途遇到好看的风景我都尽量停车欣赏,当然停车不能影响别人,也不能给别人带来危险。下面几张这种高山小村落非常的宁静祥和,让人心情愉悦,晚上住在这里应该都不用空调还要盖被子吧。(PS:这里就是山西晋城市陵川县,一个可能不那么知名的避暑圣地)

HDK00244_compressed.jpg
HDK00252_compressed.jpg
HDK00254_compressed.jpg

到了棋子山这边,海拔1500多,山顶是真的凉爽,还有人铺上席子在打牌,好不惬意。

HDK00259_compressed.jpg
HDK00264_compressed.jpg

遇到一个大哥,找我搭话,后来和我讨论晚上住哪里,他说前面很多村子都有民宿,但我还坚持回县城住酒店。查了一下距离也才20多公里。
为什么回县城住酒店:

  1. 因为县城文明
  2. 服务和价格透明
  3. 还有更多的美食以及文化可以探索

我出去旅游不是很喜欢住民宿,民宿条件以及老板水平参差不平的。县城是我最爱的落脚点,地级市的市区可能千篇一律,并且价格稍高,但是县城很折中,很有性价比。

速度买好了县城中心的酒店,火速赶往,一路上非常快乐,下山的风景太好了。另外就是要提一下,陵川县城可能因为很多人前来避暑,尤其大批的河南人,加上陵川是晋城市唯一没有开发煤矿的县城,所以他们高度依赖旅游业,于是住宿相比周边其他县城以及物价水平都要稍微高一点。

今天时间还比较早,我在酒店收拾好洗了澡出来逛小县城,发现这个小县城的婚嫁门店还挺多。之前在陕西榆林,那边的街头很多洗脚店。哈哈,还真就反映了当地人民的生活吧。这也是旅行有意思的地方,能感受到不同地方不同人们的生活方式和文化差异。用随身的红米K70 拍了下面8张照片,都有比较大的压缩,方便快速加载。我觉得挺富有生活气息的,回味起来,总感慨同一片土地上,不同时空角落,就上演着截然不同的人间烟火。

陵川县城1.jpg
陵川县城2.jpg
陵川县城3.jpg
陵川县城4.jpg
陵川县城5.jpg
陵川县城6.jpg

这是陵川县城的崇安寺,坐落在陵川县城卧龙岗高地,就处在县城城区里,是全国重点文保单位,民间有 “先有崇安,后有陵川” 的说法。始建年代无确切记载,唐初叫丈八佛寺,北宋太平兴国元年(976)御赐名崇安寺,元明清屡经修缮。寺院建在高台之上,格局很特别:山门古陵楼地势最高,反而高于后方主殿,可以俯瞰整个陵川县城。

陵川县城7.jpg

“太行小书场”,非常有地域特色了。围绕着太行山,这些地方真可以靠山吃山靠水吃水,当然这不是贬义,这是很正常的市场经济。何况这里是作为文化输出,我非常认同。

陵川县城8.jpg

晚上9点出酒店来外面车上拿东西,发现只有20度,是真的好凉快啊,穿着短裤都有一丝丝凉意,也快立秋了,但是看了一下江浙沪皖,仍旧是29度,这里避暑真够nice。

day 3

今天正经打卡上文中提到的那几个点。早上并没有刻意早起,吃了当地特色“肉丸方便面”早餐,也就那样,我今天想吃正宗的山西刀削面。

从酒店出来直接前往浙水村,最后有一段很长的下坡,伴着一路的山花和美景,还有好天气,开启了一天的好心情。

HDK00268_compressed.jpg

路边开满了鲜花~

HDK00270_compressed.jpg

小车车虽然是二手的,但是很可靠,还算好开,是EA111 1.4T + 7速干式双离合 DQ200,但是前车主刚换过机电单元。

HDK00273_compressed_mosaic.jpg

到达浙水村,这里很有东方瑞士的感觉,上午9点多的阳光洒向大地,而这里只有23.5°C,只要不在阳光下待很久,都会感到非常舒适。但是阳光下待久了还是很晒的。

HDK00275_compressed.jpg

沿着公路直接打卡,这一路主要行驶在「太行一号旅游公路」,这是山西省打造太行旅游板块的大型交旅融合路网工程。北起忻州繁峙平型关,南至运城垣曲西哄哄村,纵贯忻州、阳泉、晋中、长治、晋城等六市,整体规划总里程 5217 公里,主线约 1225 公里,包含大量支线、连接线,串联起太行山沿线 350 余处自然、人文、红色景点。

HDK00280_compressed.jpg
HDK00285_compressed.jpg

到达一处印象深刻的点,这里没有护栏,平台两端都是悬崖峭壁,对面的崖壁很陡峭也很壮观。我在这里还遇到一个老外,他还主动向我说你好,我用蹩脚的英文和它简单交流了几句,他离开的时候还向我竖大拇指,我很感谢他的 kindness。

HDK00292_compressed_mosaic.jpg
HDK00293_compressed.jpg

到达东柏坡天路,这里没有太行第一路(太行十六拐)惊险,这里的沥青路和路况都太好了。太行十六拐(香木河那边)以后有机会再去。这边的峡谷+公路景观不错,刚好也有荫凉处,适合吃点零食和小憩。

HDK00301_compressed.jpg
HDK00303_compressed.jpg
HDK00307_compressed.jpg

沿途美丽村落,这座桥就很山西。

HDK00317_compressed.jpg

到达神龙湾挂壁公路观景台,这里人实在是太多了,还堵车,当时也中午了太阳还大。对面的崖壁公路现在已不让通行,换成了神龙湾隧道通行,一会去「井底村」会经过。

HDK00319_compressed.jpg
HDK00320_compressed.jpg

山脚下这些是黑山羊,拉了个长焦看看👀,他们的活动空间真大!

HDK00327_compressed.jpg

这个挂壁公路观景台有不少商业,我点了一份「西红柿鸡蛋刀削面」,应该是真的正宗,面条很好吃,调味也很不错,15元在这种地方已经不算贵了。

吃完午饭前往井底村,在进入神龙湾隧道之前有一个漂亮的月亮桥,但是隧道口没有停车位置,遗憾无法拍下来。这是新的神龙湾隧道,2026‑06‑20正式通车,是「神龙湾挂壁公路‑井底改建项目」的核心控制性工程。

那条老的神龙湾挂壁公路,1985年至2000年,全村八百多村民,自带干粮工具,一锤一钎,15年时间在百米悬崖上硬凿出来全长1526米、拥有39个采光天窗的通道,这曾经是井底村唯一的汽车出山通道。不禁让人感慨,更让人肃然起敬。

驱车到达了井底村,然后很快到达了高家台村,这之间没啥好看的。这时候海拔也一直在下降,我有旅途谢幕的感受,竟然还有一点不舍。

准备去“鲁班天梯”,但是看了一下导航实在是堵的可怕,堵了几公里,刚好我也累了,于是直接导航去了终点站“石板岩小镇”,这里还有漂流的项目,可惜已经超过了下午3点,漂流的票买不到了。海拔下降到700左右,我能明显感受到骄阳似火,于是带着毛巾穿上洞洞鞋去了河里洗洗干净,洗干净了开车才舒服。

接下来规划行程,我打算回南京了。刚好从这里回南京有将近900公里,于是我打算今晚入住中间的某一个小县城,将明日里程三七分。然后买了河南省开封市兰考县的酒店,就直接赶路去兰考县。

开车到酒店的途中有很多感慨,这新乡交通也太差了(整个河南不好说),到处都是三轮车,有敞篷的三轮,有豪华装修的三轮(比如有沙发座椅的三轮车),有遮雨棚的三轮,各种电三轮乱窜,毫无交通规则可言…… 当晚开车我瞪着铜铃般大的眼睛,生怕遇到交通事故。最终安全抵达,享受了一个不错的睡眠。

day 4

今天从兰考县回南京,出发前瞥了一下兰考火车站,裕禄大道和裕禄公园。在兰考加满油出发,小的民营加油站居然还送早餐,我的早餐也省了。

说到民营加油站,昨天从林州(石板岩小镇就是河南安阳市林州市的)到兰考,路上看到巨多的我没见过名字的民营加油站。

  • 东方运输公司加油站
  • 储峰加油站
  • 云龙加油站
  • 兴业石化
  • 中亚石化
  • 卫辉市鑫鑫加油站
  • 核心石化
  • 太行石化

民营加油站是真发达啊……

回到了南京,热得出奇,和我昨天早上在浙水村那种感受形成了巨大的反差,谁知道今天还是“立秋”。

结语

旅程结束,非常的依依不舍,但是工作要继续,这种旅行本是生活的调味料。短暂山野的日子已落幕,那些绝壁峡谷间的山风都化作心底的片段,妥帖收藏。人终究要回归平凡的烟火日常,旅途不能长久停留,但那些所见所感,会留在记忆里,往后的日子还能慢慢回味。

海风与旧巷:2026盛夏的深港澳行记

2026-08-10 21:34:00

终于又到了属于我的假期,今年的假期有两个星期,我可要好好安排一下。准备分成两段,第一周去深圳香港澳门,第二周暂时没想好,但是决定了是自驾,皖南很多地方都转过了,所以有几个选项

  • 川藏线
  • 南太行
  • 浙江海岸线,比如仓南168黄金海岸线,中国东海岸1号公路

先写我的深圳香港澳门游记吧,具体的第二周安排我是在出发前一天才真正做好决定的。

day 1

今天的飞机飞深圳,因为台风“红霞”,延误了一个多小时,好在10点多正常降落深圳宝安机场,但是到达我哥那已经晚上12点了。这个台风稍微打乱了我的游览节奏。

day 2

第二天,深圳福田一直在下雨,直到下午天气才稍稍明朗些,但仍然会滴落一些雨滴。不过我在房间待不住,去了深圳湾人才公园打卡,并且拍了一些照片。

HDK09620_compressed.jpg
HDK09629_compressed.jpg

这两天深圳比较凉快,反而是南京顶着骄阳,还有35度高温,稍稍成功的一次避暑。找了辆共享单车,沿着深圳湾滨海大道骑了 4 5 公里。海湾区除了湿度太高,其他的条件还是蛮舒适的,因为风多,气温也不是很高。晚上和我哥吃了当地特色“隆江猪脚饭”,还是非常不错滴。

此时台风红霞的中心已经北上,但是深圳仍受其外围云系笼罩,饱含水汽的云团漫过楼宇,层云垂落,缭绕于城市高楼之间,看着颇有意境。

HDK09641_compressed.jpg

云层太厚,可惜今晚没有晚霞了。不然这个打卡点是个拍摄晚霞、夕阳🌇的好地方。

HDK09645_compressed.jpg

day 3

今天计划徒步海岸线,看过大海却没有进入过沙滩,刚好去大梅沙,小梅沙走走沙滩。从深铁盐田墟下车,沿着海岸步道(其实叫“盐梅路”)总体是向着东北徒步。今天有一些阳光,还是有些晒人的。

HDK09673_compressed.jpg

大梅沙,深圳东部大鹏湾的海湾。数百年前这里只是一处偏僻的客家小渔村,世代村民靠出海捕鱼、耕海为生,山海闭塞,与世隔绝深圳盐田。

HDK09678_compressed.jpg

谁曾想,乘着时代浪潮,昔日的渔家村落,蜕变为如今繁华发达的滨海小镇。月牙形的沙滩、林立的酒店楼宇,山海与城市在此相融。

HDK09679_compressed.jpg

第一次触到海滩的细沙,细碎的颗粒绵软顺滑,从指缝间悄悄滑落。它们本是山石的碎屑,经海浪千万次拍打打磨,才化为这般细腻模样。潮起潮落,造物的循环仿佛在此触手可及。

HDK09692_compressed.jpg

今天是工作日,但是海滩上仍然有很多人玩水,如果是炎热的工作日,这里就真的是下饺子。这边的海水挺干净的,确实很适合玩水,不在滨海城市生活的我有些羡慕。

HDK09695_compressed.jpg

day 4

HK是真的蛮出片的,但是限于篇幅,只挑选了最喜欢的一些分享给大家。今天一大早8点多就到了香港,但是没料到下了大雨,台风的影响仍未结束,这种天气也见怪不怪的。好在下午放晴~

我最喜欢太平山了,在香港市区生活,处处显得逼仄,街道、居室都局促狭小,高楼挤得很近,但因为香港有很多山,让我觉得又很相得益彰。虽然那些空间不属于你,但大部分也都是公共的,有山有海还有盘山公路,感觉累了就可以跑山,开车到山顶吹吹风,很喜欢这种感觉了。

另外也赞美一下太平山顶看中环以及对面维多利亚港那片的夜景,真的很赞。

HDK09740_compressed.jpg
HDK09761_compressed.jpg
HDK09772_compressed.jpg
HDK09779_compressed.jpg
HDK09781_compressed.jpg
HDK09788_compressed.jpg
HDK09805_compressed.jpg
HDK09827_compressed.jpg
HDK09852_compressed.jpg
HDK09855_compressed.jpg
HDK09870_compressed.jpg
HDK09919_compressed.jpg
HDK09969_compressed.jpg
HDK09982_compressed.jpg
HDK09985_compressed.jpg

晚上回深已经22点多了,都没赶上福田口岸,最终在罗湖口岸抵深。

day 5

又是下雨的一天,加上昨天3万多步走累了,就不想再去香港了,本来还打算续一下签注再去趟香港。

下午去打卡深圳华强北,这个对于我这种数码迷还是挺有吸引力的。打算买个华强北悦虎耳机,高仿 AirPods Pro的,但是搜了一下水太深:又是悦虎,又是洛达,还有杰里、中科蓝汛,还有FlyCC软件验证,各种名词(有主板方案,有芯片名称),这些倒不会出现在展台。但是展台上只有五代、六代、七代、八代,真七代这些模糊的词语,让人眼花缭乱的,不如京东自营买大厂成品 tws 耳机。看了好几家,也试用了一下,感觉还是不错的,就是需求不大,最后就没买。再记录几个有意思的点:

  • 我看有家卖共田的风扇,我说这个图案(logo)明明就是共田,他们居然说不是,倒是没说清楚具体是什么牌子
  • 看到有人手搓电池,是软包的,我发挥e人属性,问他我可以拍一下吗,他表情严肃的拒绝了 😂
  • 我居然买了一个触控笔,哈哈,老板说10元,被我还价到了7元。一头是塑胶的,另一头是导电布那种的,质感挺不错的,来华强北最大收获!(将在下文配图)

深圳华强北买的触控笔.jpg

day 6

我一个人的澳门一日游。一大早7点多乘上了前往珠海拱北口岸的大巴,现在涨价了要49.9,看小红书之前只要19.8,也是最近一年才涨价的,遇到节假日可能还更贵。

澳门老街眼见之处,也总是斑驳陈旧,墙皮剥落,街巷狭小。可一经镜头定格,褪色的墙面、复古的招牌,便沉淀出满满的肌理与故事,世俗的旧貌,化作浓郁的复古氛围感,也很出片。

澳门街头的日本元素相比香港也更多,丰田本田日产的车子,还有三菱,铃木等国内现在很少见的品牌。摩托车也有非常多的 Honda,便利店、居酒屋、唐吉诃德、药妆、动漫 IP,很浓厚的日本氛围。

HDK00003_compressed.jpg
HDK00016_compressed.jpg
HDK00033_compressed.jpg
HDK00035_compressed.jpg
HDK00038_compressed.jpg
HDK00058_compressed.jpg
HDK00077_compressed.jpg
HDK00084_compressed.jpg
HDK00108_compressed.jpg
HDK00128_compressed.jpg
HDK00130_compressed.jpg
HDK00149_compressed.jpg
HDK00156_compressed.jpg
HDK00165_compressed.jpg
HDK00166_compressed.jpg
HDK00168_compressed.jpg

在美狮美高梅看到了坤坤的演唱会海报,是真的很有风格嘞。

HDK00174_compressed.jpg
HDK00176_compressed.jpg
HDK00190_compressed.jpg
HDK00198_compressed.jpg

中午都没有吃饭,因为在大三巴一路试吃,还有澳门美高梅和美狮美高梅免费的小食饮品几乎都吃饱了。

下午坐着免费的发财车回口岸,今天也是接近3万步,又是特种兵的一天。


行笔至此,我的深圳香港澳门行就差不多结束了。第二天选择乘坐高铁返回了合肥,在合肥修整两天后我又返回了南京,准备下一场旅程。

机械革命泰钽 plus linux Debian Wayland 独显直连调优全记录

2026-06-24 10:21:00

机械革命泰钽 Plus 游戏本在 Linux Debian GNOME Wayland 下流畅使用诊断艰辛历程

一、软硬件配置

硬件

项目 规格
型号 MECHREVO Taitan Series GM7TG0M
CPU 11th Gen Intel Core i7-11800H × 16 (8C/16T, Tiger Lake-H)
独显 NVIDIA GeForce RTX 3060 Laptop GPU (6GB GDDR6)
核显 Intel UHD Graphics (Tiger Lake-H GT1, 集成于 i7-11800H)
内存 64 GiB DDR4
磁盘 1 CT240BX500SSD1 240GB SATA SSD
磁盘 2 Fanxiang S500PRO 1TB NVMe SSD
磁盘 3 IM2P33F8-512GD 512GB NVMe SSD
固件 N.1.07MRO06
BIOS 显卡模式 dGPU Only (独显直连) / MSHybrid (双显卡混合输出)

软件

项目 版本
操作系统 Debian GNU/Linux 13 (trixie)
架构 x86_64
桌面环境 GNOME 48.7
窗口系统 Wayland
内核 Linux 6.12.90+deb13.1-amd64
NVIDIA 驱动 595.80 (私有驱动)
NVIDIA VA-API nvidia-vaapi-driver 0.0.13 (NVDEC 后端)
PipeWire 1.4.2
腾讯会议 v3.26.10.401 (deb 包安装于 /opt/wemeet)

二、双显卡 vs 独显直连:两难抉择

2.1 混合输出模式 (MSHybrid) 的问题

BIOS 中设为双显卡混合输出时,系统主 GPU 是 Intel 核显,桌面合成器(Mutter)由核显驱动。但笔者的两台外接显示器(一台 4K 27 寸、一台 1080P 24 寸)通过 HDMI 和 USB-C 直连到了 RTX 3060 独显,这意味着:外接屏幕的最终画面输出必须经过 NVIDIA 独显。每一帧渲染流程为 Intel 核显合成→经 PCIe 总线拷贝到 NVIDIA 显存→NVIDIA 输出到屏幕,这个跨 GPU 的 PRIME 缓冲区同步过程引入了显著的延迟和掉帧;同时 Mutter 同时管理两个 GPU 的 framebuffer,帧时序难以对齐,进一步加剧了卡顿感。

为什么 Windows 没这个问题? Windows 下 NVIDIA Optimus 的混合输出走的是一个更高效的路径:NVIDIA 驱动内置硬件拷贝引擎(cross-adapter copy engine),独显渲染的画面通过 PCIe 直接写进核显的 framebuffer 然后输出到屏幕,不需要等待双向同步和帧对齐。而 Linux 的 PRIME 方案目前依赖通用 DMA-BUF 机制进行跨 GPU 缓冲区共享,多了一层软件调度开销,在 4K 高分辨率下带宽压力更大,卡顿感就非常明显。本质上不是硬件不行,是 Linux 图形栈在双 GPU 调度上还没做到 Windows 的成熟度。NVIDIA 在 Windows 下的图形驱动已经打磨了超过 25 年(自 1999 年 GeForce 256 发布算起),Optimus 双显卡切换技术也已在 Windows 上迭代了 15 年以上;而 Linux 下的 PRIME 多 GPU 同步方案直到 2016 年前后才逐渐可用,Wayland 下的双 GPU 混合渲染就更加年轻。十几年的工程积累差距,不是短期内能抹平的。

  • Waydroid 正常 — Android 容器通过 Mesa 调用 Intel iGPU 做 GPU 加速
  • 腾讯会议各项功能正常 — 屏幕共享打 patch 后也能正常创建分享,别人能看到
  • 桌面流畅度不佳 — GNOME 动画卡顿,窗口拖拽/切换时有可感知的延迟

补充:混合模式下若改用 X11 显示协议,流畅度问题同样可以得到缓解——X11 的 GPU 调度路径更成熟,PRIME 同步开销较低。但笔者不选择此方案,因为 Wayland 是 Linux 桌面显示的明确未来方向,且大部分日常软件(包括通过 XWayland 兼容层运行的)在 Wayland 下已足够稳定。放弃 Wayland 换 X11 是走回头路。

2.2 为什么不能只用 Intel 核显 (iGPU Only)

最理想的情况似乎是 BIOS 设为 iGPU Only,只用 Intel 核显驱动内置屏幕,简单省电。但这是不可能的,原因在于本游戏本的硬件设计:

HDMI 和 USB-C(DP Alt Mode)视频输出接口在物理上直连到了 RTX 3060 独显,而非 Intel 核显。

这意味着:

  • 若 BIOS 禁用 NVIDIA 独显(iGPU Only),HDMI 和 USB-C 接口完全无信号输出
  • 笔者日常外接两台显示器(一台 HDMI + 一台 USB-C),这两个接口离开独显就无法使用
  • 所以 iGPU Only 模式从一开始就被硬件设计排除了

因此实际可选的模式只有两种:MSHybrid(双显卡混合输出)dGPU Only(独显直连)

2.3 独显直连模式 (dGPU Only) 的问题

BIOS 切换为独显直连后,所有渲染由 RTX 3060 承担,桌面流畅度大幅提升。但带来三个问题:

  1. 腾讯会议 webcam 黑屏 — 摄像头画面无法显示
  2. 腾讯会议屏幕共享失败 — 无法创建分享,别人也看不到(混合模式下打 patch 后已修复的部分,在独显下仍不行)
  3. Waydroid 不可用 — 无解(详见下文 2.3)

2.4 Waydroid 在 NVIDIA 独显下不可用的根因

参见 waydroid/waydroid#1619

Waydroid 维护者 electrikjesus 于 2025 年 12 月明确表态:

Android uses only Mesa drivers. No way to get around it.

Android 图形栈的 GPU 加速只能通过 Mesa 体系内的驱动实现。NVIDIA 私有驱动(包括 nvidia-open 内核模块)不属于 Mesa 生态,waydroid 无法调用它为 Android 容器渲染界面。

NVIDIA 支持的唯一例外是更换为 nouveau 开源驱动,但 nouveau 在 RTX 3060 上的性能极差,没有实用价值。


三、最终方案:放弃 Waydroid,全面转向 dGPU Only

既然 Waydroid 使用频率不高,而桌面流畅度与日常体验息息相关,最终决定:

BIOS 设为 dGPU Only(独显直连),放弃 Waydroid。

3.1 受影响的软件

软件 状态 说明
Waydroid 不可用 硬伤,无解
腾讯会议 webcam 已修复 见第四章
腾讯会议屏幕共享 已修复 见第四章
kooha 已卸载 VA-API 编码依赖,已用 GNOME Screenshot + ffmpeg NVENC 替代
音频输入输出 正常 之前测试时为空是因为 systemctl --user stop pipewire.socket 关掉了 PipeWire,与独显直连无关
GNOME 桌面 流畅 RTX 3060 渲染 + nvidia-clock-unlock 锁频,体验显著改善
其他软件 正常 Chrome、VS Code、飞书、LibreOffice、GIMP、Krita、VLC、Docker、Flatpak 等均不依赖 Intel GPU

3.2 NVIDIA GPU 锁频服务(提升桌面流畅度)

独显直连能改善流畅度,但 NVIDIA 私有驱动在默认情况下会根据负载动态调整 GPU 频率。桌面合成器(Mutter)的渲染负载波动较大,GPU 在低频↔高频之间频繁切换,导致 GNOME 动画仍有偶尔的掉帧。

为此创建了 nvidia-clock-unlock.service 系统服务,在启动时:

  1. 启用 NVIDIA 持久化模式(nvidia-smi -pm 1),防止 GPU 在空闲时进入深度休眠
  2. 锁定 GPU 最低频率为 1200 MHz(nvidia-smi -lgc 1200,2100),确保合成器始终获得足够的渲染算力
# /etc/systemd/system/nvidia-clock-unlock.service
[Unit]
Description=Unlock NVIDIA GPU clock floor for smooth Wayland compositing
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/bin/nvidia-smi -pm 1
ExecStart=/usr/bin/nvidia-smi -lgc 1200,2100
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

启用方式:

sudo systemctl daemon-reload
sudo systemctl enable --now nvidia-clock-unlock.service

代价:GPU 无法完全进入最低功耗状态,闲置功耗略高(对本游戏本影响不大,日常插电使用)。

3.3 死重量但保留的包(为日后切回混合模式留余地)

i965-va-driver           # Intel 老 GPU VA-API 驱动
intel-media-va-driver    # Intel 新 GPU VA-API 驱动
firmware-intel-graphics  # Intel GPU 固件
libdrm-intel1            # Intel DRM 用户态库
libigdgmm12              # Intel 显存管理库

以上包在不加载 i915 模块时不生效,占空间极小,保留以免日后切回混合模式时需重装。

3.4 内核参数清理

# /etc/default/grub 中以下 i915 参数可删除(独显直连下 i915 模块不加载):
i915.enable_psr=0 i915.enable_fbc=1 i915.enable_guc=3

四、腾讯会议在 dGPU Only 下的完整修复方案

4.1 Webcam 黑屏修复

现象:启动腾讯会议后,webcam 预览画面黑屏,无法显示视频。

根因:xcast 日志中 eglCreateWindowSurface 返回 error:3005EGL_BAD_CONFIG)。wemeet 以 X11(XCB) 后端运行在 XWayland 下,NVIDIA EGL 无法将 XWayland 的 X11 窗口句柄转换为 EGL Window Surface。

关键线索:AUR wemeet-bin 评论区 pinned 评论(by anlorsp, 2025-03-08):

对于 N 卡视频黑屏的问题,可以尝试为腾讯会议单独设置 __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json 环境变量(不要全局设置该变量!)。在双显卡环境下可以修复黑屏问题,在单 NVIDIA 显卡环境下未测试。

本机验证:dGPU Only 模式(即单 NVIDIA)下,设置此变量后 webcam 加载成功。原理:强制 wemeet 走 Mesa 的 libEGL_mesa.so.0,走 llvmpipe 软件渲染路径,虽然性能不如硬件加速,但可以正常创建 EGL Window Surface 并显示摄像头画面。

修复步骤:编辑 /opt/wemeet/wemeetapp.sh,在第 53 行添加:

export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json

注意:此处必须有 export,仅写变量赋值不导出的话子进程 wemeetapp 继承不到,变量无效。

当前完整脚本内容见文末 附录 B

4.2 屏幕共享修复

现象:点击"共享屏幕"无反应,或能发起共享但别人看到白屏。

根因(来自 wemeet-screenshare-patch 技术细节):

  • Hunk 0libscreen_share_module.so 中 DBus 异步调用顺序错误 — Start 调用放在了 SelectSources 之后而未等待其完成,依赖竞态条件。xdg-desktop-portal-gnome 后端将 SelectSources 立即返回、Start 弹窗等待用户选择显示器,掩盖了此 bug;但在 xdg-desktop-portal-wlr 后端则会必然失败。
  • Hunk 1a/1b:PipeWire 颜色格式协商硬编码了 BGRx 格式,部分 portal 后端不支持,导致对方看到白屏。补丁中的 libhook.so 劫持缓冲区路径做了 BGRx↔RGBx 转换。

本机修复过程

  1. 按 patch README 执行 Step 1 (运行 python3 patch.py) 和 Step 2 (添加 LD_PRELOAD),能发起共享但别人看到白屏(颜色格式问题)。
  2. 参考 Issue #2,创建并执行 reverse_colors.py 撤销 Hunk 1a/1b 的颜色格式劫持,同时删除 LD_PRELOAD 行。
  3. 最终效果:只保留 Hunk 0(DBus 异步调用修复),去掉颜色格式转换,屏幕共享恢复正常,别人能看到且颜色正确。

reverse_colors.py 代码(保存为 .py 文件后用 sudo python3 执行):

#!/usr/bin/env python3
"""Reverse Hunk 1a/1b (color format patches) while keeping Hunk 0 (DBus fix)."""
import mmap
import os
import sys

REVERSE_PATCHES = {
    "/opt/wemeet/bin/modules/screen_share/libscreen_share_module.so": [
        (0x450fb6, bytes.fromhex("08")),
        (0x4566e4, bytes.fromhex("8b45c08b4808")),
        (0x6b1951, bytes.fromhex("4889e54883ec1048897df8488b7df8e88b01")),
        (0x6b1964, bytes.fromhex("004889c7e8b30100004883c4105dc3662e0f1f84")),
    ],
    "/opt/wemeet/lib/libxcast.so": [
        (0xfedbfa, bytes.fromhex("660f1f44")),
        (0xfedc3c, bytes.fromhex("23")),
        (0xfedc4b, bytes.fromhex("4883")),
        (0xfedc4f, bytes.fromhex("5b5d415c415d415e415fc3660f1f440000")),
    ],
}

def apply(path, patches):
    print(f"Patching: {path}")
    with open(path, "r+b") as fh:
        with mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_WRITE) as mm:
            for offset, new_bytes in patches:
                current = mm[offset:offset+len(new_bytes)]
                print(f"  0x{offset:08x}: {current.hex()} => {new_bytes.hex()}")
                mm.seek(offset)
                mm.write(new_bytes)
            mm.flush()
    print("  OK\n")

for f, patches in REVERSE_PATCHES.items():
    apply(f, patches)

print("Done. Hunks 1a/1b reversed. Hunk 0 (DBus fix) retained.")

执行后删除启动脚本中的 LD_PRELOAD 行:

sudo sed -i '/^export LD_PRELOAD.*libhook\.so/d' /opt/wemeet/wemeetapp.sh

4.3 修复后腾讯会议功能状态

功能 状态 备注
Webcam 预览 正常 50_mesa.json → llvmpipe 软件渲染
观看别人共享屏幕 正常 软解 + 软渲染,稍慢但可用
创建屏幕共享给别人看 正常 Hunk 0 DBus 修复
共享画面颜色 正常 已撤销 Hunk 1a/1b
音频输入/输出 正常 PipeWire 正常启动即可
视频硬件编解码 关闭 (hwdec:0, hwenc:0) 走 Mesa llvmpipe 软件路径

五、总结

维度 混合输出 (MSHybrid) 独显直连 (dGPU Only)
桌面流畅度
Waydroid 可用 不可用
腾讯会议 webcam 正常 已修复(Mesa llvmpipe 兜底)
腾讯会议屏幕共享 打 patch 后正常 打 patch 后正常
NVIDIA VA-API 硬解 NVDEC + Intel 都可用 仅 NVDEC
NVIDIA 硬编 NVENC 正常 NVENC 正常

核心妥协:放弃 Waydroid,换取桌面流畅度。腾讯会议通过 __EGL_VENDOR_LIBRARY_FILENAMES + wemeet-screenshare-patch(仅 Hunk 0)组合方案完全可用。


附录 A:相关链接

附录 B:/opt/wemeet/wemeetapp.sh 当前内容

#!/bin/bash

os_release="/etc/os-release"
if [[ -e ${os_release} ]];then
  source /etc/os-release
  main=`echo ${VERSION_ID} | awk -F . '{print $1}'`
  case $ID in
  ubuntu)
    if [[ ${main} -le "16" ]];then
      zenity --info --title="腾讯会议" --text="腾讯会议检测到您操作系统版本过低,请升级系统到ubuntu18.04或以上版本!" --width=350 --height=100
      exit 1
    fi

    if [[ ${main} -le "18" ]];then
      if [ $XMODIFIERS ];then
        im_module=$XMODIFIERS
        echo 'use XMODIFIERS'
        export QT_IM_MODULE=${im_module#*=}
      elif [ $GTK_IM_MODULE ];then
        echo 'use GTK_IM_MODULE'
        export QT_IM_MODULE=${GTK_IM_MODULE}
      fi
      echo ${QT_IM_MODULE}
    fi
    ;;
  *)
    ;;
  esac
fi

if [ "$XDG_SESSION_TYPE" = "wayland" ];then
  if [ -f "/opt/x11-wayland/x11-ext.sh" ];then
    source /opt/x11-wayland/x11-ext.sh
  else
    export QT_QPA_PLATFORM=xcb
    export XDG_SESSION_TYPE=x11
    unset WAYLAND_DISPLAY
    export WEMEET_XWAYLAND=1
  fi
fi

SELF=$(readlink -f "$0")
HERE=${SELF%/*}

export LC_ALL=zh_CN.UTF-8
export PATH="${HERE}/bin${PATH:+:$PATH}"
export LD_LIBRARY_PATH="${HERE}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export QT_PLUGIN_PATH="${HERE}/plugins"
export TZ=Asia/Shanghai

export __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json

exec wemeetapp $*

MECHREVO WUJIE14 PRO on Linux 声卡完整修复历程

2026-06-18 16:46:00

为了解决我的机械革命无界 14 pro 笔记本在 linux debian testing(forky) 下的自带麦克风、自带扬声器、插入 3.5mm 耳机这三个场景下,对应硬件都能正常工作的问题,遂有了此文。

前景提要:机械革命无界14Pro笔记本debian forky成功驱动内置扬声器和麦克风

系统信息

项目
机型 MECHREVO WUJIE14 PRO
CPU 12th Gen Intel Core i7-12650H
声卡 Intel Alder Lake PCH-P HD Audio (8086:51c8)
Codec Realtek ALC256 (10ec:0256)
子系统 ID 1d05:170b
固件 SOF (Sound Open Firmware) 签名固件 2025.05.1
OS Debian forky/sid, GNOME 50, Wayland
内核 7.0.10+deb14-amd64
PipeWire 1.6.6 + WirePlumber 1.6.6
ALSA UCM2 alsa-ucm-conf 1.2.15.3-1

目标

确保以下三个场景全部正常工作:

场景 状态
内置数字麦克风(DMIC) 始终正常
内置扬声器 始终正常
3.5mm 耳机插入后 修复前无声,修复后正常

问题

插入 3.5mm 耳机后声音仍然从笔记本内置扬声器输出,耳机无声音。系统设置输入设备显示正常(内置数字麦克风可用)。

根因分析

三重故障叠加

┌─────────────────────────────────────────────────────────┐
│  问题 1:硬件插孔检测不工作                                │
│  ALSA 控制 "Headphone Jack" 始终 = off                    │
│  即使耳机物理插入,Codec (Node 0x21) 的 pin sense 未触发   │
│  可能原因:SOF 驱动与 ALC256 pin sense 事件处理有兼容问题    │
├─────────────────────────────────────────────────────────┤
│  问题 2:UCM2 启动时禁用 Auto-Mute                         │
│  /usr/share/alsa/ucm2/HDA/init.conf                      │
│  → cset "name='Auto-Mute Mode' off"                      │
│  设计意图:让 PipeWire 用户态管理切换,但 UCM2 配置有缺陷    │
├─────────────────────────────────────────────────────────┤
│  问题 3:Speaker 和 Headphones 在不同 HiFi Profile 中      │
│  两者共用 PlaybackPCM "hw:0",ACP 拆分成了两个互斥 Profile  │
│  Headphones 依赖 JackControl,端口永远 "not available"     │
│  导致 PipeWire 无法激活耳机 DSP 通路                       │
└─────────────────────────────────────────────────────────┘

为什么 model= 参数无效

  • 本机使用 SOF/avs 驱动栈snd_soc_skl_hda_dsp),声卡名 sof-hda-dsp
  • snd-hda-intel 模块虽然被加载,但依赖计数为 0,不参与实际驱动
  • /etc/modprobe.d/alsa-alc256.conf 中配置的 options snd-hda-intel model=xxx 在 SOF 下不生效
  • 修复后该文件应当为空(或直接删除),避免误导
  • 强制使用传统 HDA(snd-intel-dspcfg.dsp_driver=1)会丢失内置 DMIC

音频拓扑(ALC256 Codec 节点路由)

                    ┌──────────┐
    hw:0 PCM ──────┤ DAC 0x02 │──┬── Pin 0x14 (Speaker) ── 扬声器
                    │ (Speaker)│  │
                    └──────────┘  │
                                  ├── Pin 0x1b (unused)
                                  └── Pin 0x21 (HP β) ── 耳机(辅)
                    ┌──────────┐
    hw:0 PCM ──────┤ DAC 0x03 │───── Pin 0x21* (HP α) ── 耳机(主)
                    │(Headphone)│
                    └──────────┘

                    * 表示活动连接

关键:两个 DAC 接收同一路 PCM 音频流,SOF DSP 固件根据激活的 Profile 控制哪个 DAC 通路工作。

修复历程(失败记录)

尝试 方法 结果 原因
1 model=laptop-amicheadset-mic 无效 SOF 驱动忽略 model= 参数
2 方案A:加 dsp_driver=1 切传统 HDA + headset-mic 耳机无效、麦消失 传统 HDA 不支持 DMIC;headset-mic 合并插孔类型不匹配
3 SOF 下手动切 Headphones Profile 端口 not available 无法路由 JackControl 依赖失败检测
4 Speaker Profile 下手动 amixer 开耳机 无声音 DSP 未激活耳机通路,DAC 0x03 无 stream
5 amixer toggle 脚本(操作 Playback Switch) 调音量时扬声器被激活 WirePlumber EnableSequence 每回路由激活都重开 Speaker

最终修复方案

核心思路

绕过硬件插孔检测,通过切换 Profile 来切换耳机/扬声器,让 WirePlumber 独立管理每个 Profile 的音量。

步骤

1. 修改 UCM2 配置,移除耳机 JackControl

# 备份
sudo cp /usr/share/alsa/ucm2/HDA/HiFi-analog.conf \
        /usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak

# 注释 JackControl 行(保留缩进)
sudo sed -i 's/^\([[:space:]]*\)JackControl "${var:hpjack}"/\1#JackControl "${var:hpjack}"/' \
    /usr/share/alsa/ucm2/HDA/HiFi-analog.conf

修改位置:/usr/share/alsa/ucm2/HDA/HiFi-analog.conf 第 156 行

         PlaybackSwitch "${var:hpvol} Playback Switch"
-        JackControl "${var:hpjack}"
+        #JackControl "${var:hpjack}"

效果:Headphones 端口不再依赖 Headphone Jack ALSA 控制,始终显示为可用(如同 Speaker 的 availability unknown)。

2. 创建快捷键切换脚本

~/.local/bin/toggle-audio.sh

#!/bin/bash
# 在扬声器和耳机之间切换

CARD="alsa_card.pci-0000_00_1f.3-platform-skl_hda_dsp_generic"
SPK_PROFILE="HiFi (HDMI1, HDMI2, HDMI3, Mic1, Mic2, Speaker)"
HP_PROFILE="HiFi (HDMI1, HDMI2, HDMI3, Headphones, Mic1, Mic2)"
SPK_SINK="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Speaker__sink"
HP_SINK="alsa_output.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__Headphones__sink"

current=$(pactl list cards 2>/dev/null | grep "Active Profile" | grep -o "Speaker")

if [ -n "$current" ]; then
    pactl set-card-profile "$CARD" "$HP_PROFILE"
    sleep 0.3
    pactl set-default-sink "$HP_SINK"
    for i in $(pactl list sink-inputs short 2>/dev/null | awk '{print $1}'); do
        pactl move-sink-input "$i" "$HP_SINK" 2>/dev/null
    done
    notify-send -a "音频切换" "已切换到耳机" -i audio-headphones
else
    pactl set-card-profile "$CARD" "$SPK_PROFILE"
    sleep 0.3
    pactl set-default-sink "$SPK_SINK"
    for i in $(pactl list sink-inputs short 2>/dev/null | awk '{print $1}'); do
        pactl move-sink-input "$i" "$SPK_SINK" 2>/dev/null
    done
    notify-send -a "音频切换" "已切换到扬声器" -i audio-speakers
fi
chmod +x ~/.local/bin/toggle-audio.sh

3. 绑定快捷键

GNOME 设置 → 键盘 → 查看及自定义快捷键 → 自定义快捷键 → 添加:

  • 命令:/home/dk/.local/bin/toggle-audio.sh
  • 快捷键:Super+Alt+S

4. 重启音频服务

systemctl --user restart wireplumber pipewire pipewire-pulse

注意事项

  • alsa-ucm-conf 包更新后会覆盖修改的配置文件,需重新执行 sed 命令
  • 备份文件位置:/usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak
  • 恢复:sudo cp /usr/share/alsa/ucm2/HDA/HiFi-analog.conf.bak /usr/share/alsa/ucm2/HDA/HiFi-analog.conf

涉及知识点

ALSA 驱动栈

层级 组件 说明
用户态 PipeWire / WirePlumber 音频路由、流管理、音量控制
用户态 ACP (ALSA Card Profile) 解析 UCM2 配置,生成 Profile/Port
用户态 ALSA UCM2 声卡用例配置(Volume、Switch、Jack、PCM)
内核态 SOF 固件 DSP 音频处理管线
内核态 snd_soc_skl_hda_dsp ASoC machine driver
内核态 snd_hda_codec_alc269 ALC256 Codec 驱动
硬件 ALC256 Codec DAC/ADC/Pin Complex 寄存器

UCM2 关键文件

文件 作用
/usr/share/alsa/ucm2/HDA/init.conf 初始化序列(含 Auto-Mute off)
/usr/share/alsa/ucm2/HDA/HiFi-analog.conf 模拟设备定义(Speaker/Headphones/Mic)
/usr/share/alsa/ucm2/Intel/sof-hda-dsp/HiFi.conf SOF HiFi 顶层用例
/usr/share/alsa/ucm2/Intel/sof-hda-dsp/HiFi-sof.conf SOF 特定覆盖(DRC/EQ/固件)

诊断用命令

# 查看声卡
cat /proc/asound/cards
aplay -l

# 查看 Codec 寄存器(Pin Sense、DAC 音量、静音状态)
cat /proc/asound/card0/codec#0

# 查看 ALSA 控制和插孔状态
amixer -c0 contents | grep -E "Jack|Auto-Mute|name="

# 查看 PipeWire 状态
pactl info | grep "Default Sink"
pactl list cards | grep -A2 "Active Profile"
pactl list sinks short

# 查看 snd_hda_intel 是否被使用
ls /sys/module/snd_hda_intel/holders/
cat /sys/module/snd_hda_intel/refcnt

# 查看内核参数
cat /proc/cmdline

# 手动控制 Codec
amixer -c0 cset name='Headphone Playback Switch' on,on
amixer -c0 cset name='Auto-Mute Mode' Enabled

记录一下我删除相机 SD 卡当中废片的工作流

2026-06-14 10:05:00

采用 python 脚本实现(python3)。支持图片(JPG/ARW/HEIF 等)和视频(MP4 + 配套 XML 元数据)的筛选删除。

相关说明:

  1. Linux/mac 电脑,如果你是 windows 电脑那么脚本需要稍加改造
  2. 相机当中开启了拍摄的照片按照日期文件夹存储,这种功能,否则可能一个文件夹下的照片数量特别多,你需要维护一个特别长的 keep.txt
  3. keep.txt 当中的文件名可以是完整文件名,也可以不包含扩展名。
    a. 如果是完整文件名,那脚本就根据完整文件名匹配去保留/删除文件
    b. 如果只包含文件名 stem,没有扩展名,将匹配所有包含 stem 的文件名,进而执行保留/删除逻辑。比如 keep.txt 当中有这样一行 HDK09432 ,那这些文件 HDK09432.JPG, HDK09432.ARW, HDK09432.HEIF 都会被保留,这尤其适用于你开启了 JPEG/RAW 拍摄,那么一张图片会有两个文件
  4. 支持模拟运行,只需要在最后添加 --dry-run
  5. 支持的扩展名:.jpg .jpeg .png .arw .heic .heif .hif .dng .mp4 .xml
  6. 索尼视频的 XML 元数据文件会自动匹配对应的 MP4 主文件(如 C0054M01.XML 跟随 C0054.MP4 同进退),无需在 keep.txt 中单独列出

照片工作流

索尼 JPEG 照片扩展名是 JPG,RAW 拍摄文件扩展名是 ARW,HEIF 拍摄扩展名是 HIF (Apple 的 HEIF 照片扩展名是 HEIC)

cd /run/media/dk/708D-3AF3/DCIM/15660613/

ls
HDK09429.JPG  HDK09432.JPG  HDK09435.JPG  HDK09438.JPG  HDK09441.JPG  HDK09444.JPG  HDK09447.JPG
HDK09430.JPG  HDK09433.JPG  HDK09436.JPG  HDK09439.JPG  HDK09442.JPG  HDK09445.JPG  HDK09448.JPG
HDK09431.JPG  HDK09434.JPG  HDK09437.JPG  HDK09440.JPG  HDK09443.JPG  HDK09446.JPG  HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt --dry-run

========== 执行模式 ==========
DRY RUN
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG

========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG

========== 统计 ==========
模式: DRY-RUN
计划删除文件数: 15
保留文件数: 6

========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG

========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt

========== 执行模式 ==========
REAL DELETE
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
已删除: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG

========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
保留: /run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG

========== 统计 ==========
模式: DELETE
计划删除文件数: 15
保留文件数: 6
实际删除成功数: 15

========== 删除列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09429.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09430.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09431.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09433.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09434.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09435.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09436.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09437.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09439.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09443.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09444.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09445.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09446.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09447.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09448.JPG

========== 保留列表 ==========
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09432.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09438.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09440.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09441.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09442.JPG
/run/media/dk/708D-3AF3/DCIM/15660613/HDK09449.JPG
ls
HDK09432.JPG  HDK09438.JPG  HDK09440.JPG  HDK09441.JPG  HDK09442.JPG  HDK09449.JPG  keep.txt

视频工作流

索尼 A7C2 录制视频后,存储卡上的目录结构为 PRIVATE/M4ROOT/CLIP/。与照片不同,视频不按日期分子目录,所有日期的视频文件都平铺在同一个 CLIP/ 目录下,因此 keep.txt 会较长。不过首次维护好之后,每次拍摄后只需要在末尾追加新增的 clip 号即可。

每个视频片段会生成:

  • C0054.MP4 — 视频主文件
  • C0054M01.XML — 配套元数据文件(自动跟随 MP4 同进退)

cd /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/

ls
C0054.MP4  C0054M01.XML  C0055.MP4  C0055M01.XML  C0056.MP4  C0056M01.XML  C0057.MP4  C0057M01.XML
C0061.MP4  C0061M01.XML  C0062.MP4  C0062M01.XML  C0063.MP4  C0063M01.XML  C0068.MP4  C0068M01.XML
C0070.MP4  C0070M01.XML  C0071.MP4  C0071M01.XML  C0074.MP4  C0074M01.XML  C0076.MP4  C0076M01.XML
C0077.MP4  C0077M01.XML  C0079.MP4  C0079M01.XML  C0080.MP4  C0080M01.XML  C0083.MP4  C0083M01.XML
C0084.MP4  C0084M01.XML  C0085.MP4  C0085M01.XML  C0086.MP4  C0086M01.XML  C0087.MP4  C0087M01.XML
C0088.MP4  C0088M01.XML  C0089.MP4  C0089M01.XML  C0090.MP4  C0090M01.XML  C0091.MP4  C0091M01.XML
C0092.MP4  C0092M01.XML  C0093.MP4  C0093M01.XML  C0094.MP4  C0094M01.XML  C0095.MP4  C0095M01.XML
C0096.MP4  C0096M01.XML

keep.txt 内容(只写 stem,不包含扩展名):

$cat keep.txt 
# start to record keep.txt list
C0054
C0055
C0056
C0057
C0061
C0062
C0063
C0068
C0070
C0071
C0074
C0076
C0077
C0080
C0083
C0086
C0092
C0094
C0095
C0096
# 2026-07-04

先模拟运行确认预期:

$python3 ~/Downloads/myapps/delete_unwanted_media.py ./keep.txt --dry-run

========== 执行模式 ==========
DRY RUN
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091.MP4
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093M01.XML
[DRY-RUN] 将删除: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093.MP4

========== 保留文件 ==========
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095.MP4
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096M01.XML
保留: /run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096.MP4

========== 统计 ==========
模式: DRY-RUN
计划删除文件数: 18
保留文件数: 40

========== 删除列表 ==========
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0079.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0084.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0085.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0087.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0088.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0089.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0090.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0091.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0093.MP4

========== 保留列表 ==========
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0054.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0055.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0056.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0057.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0061.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0062.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0063.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0068.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0071.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0070.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0074.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0077.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0076.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0080.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0083.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0086.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0092.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0094.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0095.MP4
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096M01.XML
/run/media/dk/708D-3AF3/PRIVATE/M4ROOT/CLIP/C0096.MP4

确认无误后去掉 --dry-run 执行实际删除。


脚本内容

$cat ~/Downloads/myapps/delete_unwanted_media.py
#!/usr/bin/env python3
import os
import sys

TARGET_EXTS = {".jpg", ".jpeg", ".png", ".arw", ".heic", ".heif", ".hif", ".dng", ".mp4", ".xml"}

def load_keep_list(path):
    exact_keep = set()
    stem_keep = set()

    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            raw = line.strip()
            if not raw or raw.startswith("#"):
                continue

            stem, ext = os.path.splitext(raw)

            if ext:  # 有扩展名 → 精确匹配
                exact_keep.add(raw)
            else:    # 无扩展名 → stem 匹配
                stem_keep.add(stem)

    return exact_keep, stem_keep

def get_effective_stem(filename, mp4_stems):
    stem, ext = os.path.splitext(filename)
    if ext.lower() == ".xml" and mp4_stems:
        match = ""
        for mp4_stem in mp4_stems:
            if len(mp4_stem) > len(match) and stem.startswith(mp4_stem):
                match = mp4_stem
        if match:
            return match
    return stem

def is_target_file(filename):
    ext = os.path.splitext(filename)[1].lower()
    return ext in TARGET_EXTS

def main():
    if len(sys.argv) < 2:
        print("Usage: python3 delete_unwanted_media.py ./keep.txt [--dry-run]")
        sys.exit(1)

    keep_path = sys.argv[1]
    dry_run = "--dry-run" in sys.argv[2:]

    root_dir = os.getcwd()

    if not os.path.exists(keep_path):
        print(f"[ERROR] keep.txt not found: {keep_path}")
        sys.exit(1)

    exact_keep, stem_keep = load_keep_list(keep_path)

    to_delete = []
    to_keep = []

    # 扫描文件
    for root, dirs, files in os.walk(root_dir):
        mp4_stems = [os.path.splitext(f)[0] for f in files if f.lower().endswith(".mp4")]
        for file in files:
            if not is_target_file(file):
                continue

            full_path = os.path.join(root, file)
            stem = get_effective_stem(file, mp4_stems)

            if (file in exact_keep) or (stem in stem_keep):
                to_keep.append(full_path)
            else:
                to_delete.append(full_path)

    deleted_count = 0

    print("\n========== 执行模式 ==========")
    print("DRY RUN" if dry_run else "REAL DELETE")

    # 删除逻辑
    for path in to_delete:
        if dry_run:
            print(f"[DRY-RUN] 将删除: {path}")
        else:
            try:
                os.remove(path)
                print(f"已删除: {path}")
                deleted_count += 1
            except Exception as e:
                print(f"[ERROR] 删除失败 {path}: {e}")

    # 保留输出
    print("\n========== 保留文件 ==========")
    for path in to_keep:
        print(f"保留: {path}")

    # 统计
    print("\n========== 统计 ==========")
    print(f"模式: {'DRY-RUN' if dry_run else 'DELETE'}")
    print(f"计划删除文件数: {len(to_delete)}")
    print(f"保留文件数: {len(to_keep)}")

    if not dry_run:
        print(f"实际删除成功数: {deleted_count}")

    # 列表输出
    print("\n========== 删除列表 ==========")
    for path in to_delete:
        print(path)

    print("\n========== 保留列表 ==========")
    for path in to_keep:
        print(path)

if __name__ == "__main__":
    main()

keep.txt 内容示例(照片):

$cat keep.txt
HDK09432
HDK09438
HDK09440
HDK09441
HDK09442
HDK09449

keep.txt 内容示例(视频,支持 # 注释行):

$cat keep.txt
# start to record keep.txt list
C0054
C0055
C0056
C0057
C0061
C0062
C0063
C0068
C0070
C0071
C0074
C0076
C0077
C0080
C0083
C0086
C0092
C0094
C0095
C0096
# 2026-07-04

Linux Desktop 使用 NVIDIA 独显驱动 Google Earth Pro 和 WeChat

2026-06-12 16:09:00

更新说明

2026年6月17日 09:00

gnome 桌面自带这样的功能,右键就能“使用独立显卡启动”,看下图

gnome直接右键就可以使用独显打开.jpg


原理

NVIDIA 官方提供了 PRIME Render Offload 机制,通过环境变量让指定程序走独显渲染:

  • __NV_PRIME_RENDER_OFFLOAD=1 — 告诉 NVIDIA 驱动将 GL/Vulkan 请求路由到独显
  • __GLX_VENDOR_LIBRARY_NAME=nvidia — 强制 GLX 使用 NVIDIA 的 vendor 库

适用条件

  • 双显卡(Intel 核显 + NVIDIA 独显)笔记本
  • 已安装 NVIDIA 闭源驱动(nvidia-driver)
  • 桌面环境不限(GNOME / KDE / XFCE 等均可)

操作步骤

1. 修改 .desktop 文件

将系统目录下的 .desktop 文件拷贝到用户目录(避免被系统更新覆盖):

cp /usr/share/applications/google-earth-pro.desktop ~/.local/share/applications/
cp /usr/share/applications/wechat.desktop ~/.local/share/applications/

修改 Exec 行,在程序路径前加上环境变量:

sed -i 's|^Exec=/opt/google/earth/pro/google-earth-pro %f|Exec=env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia /opt/google/earth/pro/google-earth-pro %f|' \
  ~/.local/share/applications/google-earth-pro.desktop

sed -i 's|^Exec=/usr/bin/wechat %U|Exec=env __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia /usr/bin/wechat %U|' \
  ~/.local/share/applications/wechat.desktop

2. 注销并重新登录

让 GNOME Shell 重新加载 .desktop 文件缓存。

3. 验证

nvidia-smi

启动 Google Earth Pro 和 WeChat 后,应看到类似输出:

+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A           2327      G   /usr/bin/gnome-shell                    130MiB |
|    0   N/A  N/A          25233      G   ...gle/earth/pro/googleearth-bin        112MiB |
|    0   N/A  N/A          25399      G   ...ns-seed-version --log-level=2         72MiB |
+-----------------------------------------------------------------------------------------+

googleearth-bin 为 Google Earth Pro,...ns-seed-version 为 WeChat 的 GPU 子进程。

说明

  • Chrome 未纳入:日常浏览网页、看视频等场景 Intel 核显已足够,独显无感知收益且徒增功耗发热。如有 3D/WebGL 重负载需求,可自行添加 --use-gl=egl 参数。
  • 环境变量直接写在 Exec 行:避免了对 shell alias 或外部脚本的依赖,桌面菜单点击即可生效。
  • 恢复默认:删除 ~/.local/share/applications/ 下对应的 .desktop 文件即可回到系统默认配置。
  • 为什么不使用独显直连?虽然改成独显直连,桌面都会变流畅,大部分程序都会默认走英伟达独显,比如 telegram-desktop,但是对我来说有一个致命性问题,腾讯会议的视频功能无法使用了,摄像头也无法调用,无法分享桌面,别人分享桌面我这边查看也是黑屏,这个问题暂时无法解决,只能依赖腾讯会议那边针对性开发:在英伟达独显上正常工作