news 2026/9/9 22:25:42

Android投屏Demo实战:MediaProjection+H.264编码+TCP推流全链路解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android投屏Demo实战:MediaProjection+H.264编码+TCP推流全链路解析

简介:这套Android设备投屏Demo面向具备一定Android基础、希望深入理解屏幕采集与实时视频传输的开发者,以MediaProjection捕获屏幕、MediaCodec完成H264编解码、Socket进行网络传输为主线,覆盖了投屏功能的核心链路。压缩包内含1536个文件,以XML布局与配置、Java业务代码、JSON资源数据为主,另有Gradle脚本、PNG图标、签名文件与编译生成物等,整包仅4.87MB,结构紧凑便于快速查阅。当前已有3235人学习下载。通过该工程可具体看到录屏权限申请、MediaCodec输入输出缓冲区处理、NAL单元解析以及ServerSocket与Socket的数据交换等实现细节;同时,基于现有的H264裸流传输框架,也便于进一步扩展音视频同步、弱网抗丢包等优化能力,对研究Android多媒体开发和远程投屏场景有直接的参考价值。 前阵子要做一个展厅演示机,Android 平板上的数据面板要实时同步到大屏电视上,要求不拖线、延迟能接受,最好还能随时换台设备投一下。第一反应是用系统自带的无线投屏,但大屏不是智能电视,Miracast 这条路直接堵死;买硬件同屏器又要多等两天。后来干脆自己写了一个 Android 投屏功能 Demo,把屏幕采集、H.264 编码、局域网传输、PC 端解码播放整条链路全部跑通。这篇就把我的技术选型逻辑、核心代码思路和排障经验完整整理出来,给正在做投屏功能或者想研究 MediaProjection 采集体系的同学做个参考。

1. 先说清楚:投屏 Demo 到底在做什么

1.1 从接到需求的第一天说起

很多刚接触投屏的人会把"投屏"想得很简单:不就是把手机画面放大到电视上吗?实际一动手就会发现,这里面至少牵扯到四个环节:屏幕画面采集、视频编码、网络传输、接收端解码渲染。任何一个环节掉链子,结果都是黑屏、花屏或者延迟爆炸。

我当时的需求其实很明确:Android 平板是数据源,大屏电视负责展示,中间不能有数据线。这意味着我必须走无线传输,而且为了保证画面足够跟手,端到端延迟最好控制在半秒以内。考虑到接收端只是一台普通显示器加一个 mini PC,那么 PC 端用什么播放器、走什么协议,都得我自己掌控。

在动手写代码之前,我先把能想到的方案都列了一遍,逐个排除,最后才确定走 MediaProjection + MediaCodec + 自定义 TCP 推流这条路。这个过程很值得分享,因为很多人一上来就搜开源项目,搜到哪个用哪个,最后反而被别人的技术栈牵着走。

1.2 四条技术路线,为什么我选 MediaProjection 这套

投屏的技术路线大概可以分成四类:

方案基本原理延迟表现开发量兼容性
系统无线投屏(Miracast)Wi-Fi Direct + 系统底层采集/编码中等,通常 100~300ms无需开发依赖显示器或电视端支持
DLNA / Cast 推送把媒体资源 URL 推给接收端播放不适用于实时同屏电视普遍支持但非镜像
MediaProjection 自研采集推流系统 API 采集屏幕 + 软硬件编码 + 自定义传输可做到 200~400msAndroid 5.0+,生态统一
硬件同屏器HDMI dongle 直接接收无线信号需要额外采购硬件

系统 Miracast 看着最省事,但接收端不支持就白搭,而且它是个封闭协议,我根本无法定制画面参数。DLNA 本质是媒体推送,不是屏幕镜像,做不到"数据面板实时刷新"的效果。硬件同屏器其实是最稳的选择,但一台设备要几百块,而且多一个硬件就多一个出问题的点。

最终选 MediaProjection 这套方案,是因为它是 Android 官方公开 API,不依赖特定厂商,也不需要 root。像 scrcpy、QtScrcpy 这类知名投屏工具,底层用的就是同一套采集机制。跟着这条路走,既能满足当前需求,后续如果想加触控回传、多设备连接,也有足够的扩展空间。

1.3 Demo 的最终形态:Android 端采集推流 + PC 端解码播放

这个 Demo 我定义得很克制:Android 端负责采集屏幕、编码成 H.264 视频流,通过 TCP 推出去;PC 端用 ffplay 或者 VLC 拉流播放。不做音频、不做触控回传、不做多设备,先把链路打通再说。

为什么接收端选 ffplay 而不是自己写一个解码器?因为 Demo 阶段最重要的是验证采集端和传输链路是否正确,拿现成播放器能省掉大量排查成本。等链路确认没问题了,再考虑自研接收端或者改造开源播放器都来得及。

你会在后面的章节里看到,我每一步都尽量选择"可验证"的做法,而不是"看起来很高级"的做法。这是这次做 Demo 最大的心得。

2. 屏幕采集链路:MediaProjection、VirtualDisplay、MediaCodec 怎么配合

2.1 权限申请环节最容易翻车的两个细节

MediaProjection 的权限申请流程看起来很常规:通过MediaProjectionManager.createScreenCaptureIntent()弹窗让用户授权,然后在onActivityResult里拿到resultCodedata。但这里有两个细节非常容易踩坑。

第一个细节是 targetSdk 30+ 之后,尤其是 targetSdk 34 在 Android 14 上,投屏必须配合前台服务才能稳定运行。因为用户一旦切走或者息屏,Activity 被回收,MediaProjection 很容易跟着失效。正确做法是:在 Activity 的onActivityResult里拿到授权结果后,立刻启动一个前台服务,把resultCodedata通过 Intent 传给 Service,由 Service 持有 MediaProjection 并创建 VirtualDisplay。

// Activity 中发起授权 val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java) startActivityForResult( mediaProjectionManager.createScreenCaptureIntent(), CAPTURE_REQUEST_CODE ) // onActivityResult 中把结果交给前台服务 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == CAPTURE_REQUEST_CODE && resultCode == RESULT_OK && data != null) { val intent = Intent(this, ScreenCastService::class.java).apply { putExtra("resultCode", resultCode) putExtra("resultData", data) } startForegroundService(intent) } }

第二个细节是前台服务必须在 Manifest 里声明类型。Android 14 强制要求mediaProjection类型的前台服务权限:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" /> <service android:name=".ScreenCastService" android:foregroundServiceType="mediaProjection" android:exported="false" />

我一开始没加FOREGROUND_SERVICE_MEDIA_PROJECTION这个权限,在 Android 14 设备上直接崩溃,报错提示缺少 mediaProjection 类型权限。这个问题在官方文档里写得很清楚,但不实际跑一遍很容易漏掉。

还有一个经验:授权弹窗返回的data是一个Intent,它包含 MediaProjection 的 token 信息。这个 Intent 只能用一次,不能序列化保存复用。每次想重新投屏,都必须重新走一遍授权流程。我在做重连功能时尝试缓存这个 Intent,结果第二次怎么都创建不了 MediaProjection,后来查源码注释才发现问题。

2.2 VirtualDisplay 参数怎么定才不出黑边

拿到 MediaProjection 实例之后,核心动作是创建一个 VirtualDisplay,把屏幕画面"投影"到编码器的输入 Surface 上。这个环节最容易出问题的是参数设置。

直接看代码:

val metrics = resources.displayMetrics val width = metrics.widthPixels val height = metrics.heightPixels val dpi = metrics.densityDpi virtualDisplay = mediaProjection.createVirtualDisplay( "ScreenCast", width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, codec.createInputSurface(), null, null )

这里宽度、高度、DPI 我全部取自当前屏幕的真实参数,不做任何缩放。有些 Demo 为了方便会固定成 1280x720,这样做虽然也能出画面,但在高分辨率手机上会让画面模糊,而且可能因为宽高比不一致导致上下黑边。

DPI 是个非常容易被忽视的参数。同样的 1080x2400 分辨率,在 440 DPI 和 320 DPI 下渲染出来的画面大小完全不同。如果你设置的 DPI 和系统真实值不一致,采集出来的画面会明显发虚,或者字体异常大。最稳妥的做法就是直接读displayMetrics,不要去猜。

VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR这个标志位表示当前 VirtualDisplay 镜像主屏幕内容。还有一个VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY可以用,但那个适合做多屏互动,对普通投屏需求来说 AUTO_MIRROR 就够了。

2.3 为什么必须用 Surface 模式喂编码器

MediaCodec 编码器有两种输入模式:bytebuffer 模式和 Surface 模式。字节缓冲模式需要把采集到的图像帧拷贝到输入 buffer 里,中间多了一次内存拷贝,而且取屏幕帧的方式本身就麻烦。Surface 模式则是直接把编码器内部创建的输入 Surface 交给 VirtualDisplay,系统底层负责把画面写入 Surface,编码器直接读取并编码,全程零拷贝。

性能差异在低端设备上非常明显。我在一台骁龙 680 的平板上分别试过两种模式:bytebuffer 模式下 1080P 编码经常掉到十几帧,CPU 占用很高;换到 Surface 模式后稳定在 30 帧,机身发热也小了很多。

MediaCodec 的配置同样有讲究:

val format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height) format.setInteger(MediaFormat.KEY_COLOR_FORMAT, CodecCapabilities.COLOR_FormatSurface) format.setInteger(MediaFormat.KEY_BIT_RATE, 4_000_000) format.setInteger(MediaFormat.KEY_FRAME_RATE, 30) format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1) codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE) codec.start()

几个参数我解释一下:KEY_BIT_RATE是编码码率,Demo 阶段设 4Mbps 足够。KEY_FRAME_INTERVAL是关键帧间隔,单位是秒,设 1 表示每秒一个关键帧。关键帧的作用是让接收端在丢包或刚开始解码时能快速恢复画面,间隔太长会导致切换播放器后要等很久才能出图,太短又会让码率飙升,1 秒是比较平衡的选择。

编码器开始工作后,在编码线程循环里取输出 buffer:

while (isCasting) { val outIndex = codec.dequeueOutputBuffer(bufferInfo, 10_000) if (outIndex >= 0) { val outputBuffer = codec.getOutputBuffer(outIndex) // 在这里处理编码后的 H.264 数据 codec.releaseOutputBuffer(outIndex, false) } }

需要特别注意的是,Surface 模式下部分设备的 MediaCodec 会先输出一个包含 SPS/PPS 的BUFFER_FLAG_CODEC_CONFIG数据,这个数据不是可播放的画面帧,但接收端必须有它才能正确解码。后面的传输环节,我会专门讲怎么处理这部分数据。

3. 把视频流送到对端:传输协议与接收端选型

3.1 自己封一个简单帧,还是一个字节流打天下

采集端编码好了,接下来要想办法把 H.264 数据送到接收端。这一步有两个选择:自己定义一套带帧头的协议,或者直接把 H.264 字节流往网络里灌。

我推荐的做法是:直接给每一帧数据前面加上起始码0x00000001,拼成 Annex-B 格式的字节流,然后通过 TCP 发送。这种方式不需要额外的帧封装协议栈,而且接收端对 Annex-B 的支持最好,ffplay、VLC 都能直接解析。

具体实现时,先处理关键帧的场景。MediaCodec 输出关键帧时,前面通常会有 SPS/PPS(可能独立成帧,也可能附在关键帧前面)。为了保证接收端随时能开始解码,我的策略是:

  • 遇到BUFFER_FLAG_CODEC_CONFIG时,把数据保存下来,不直接发送。
  • 遇到带BUFFER_FLAG_KEY_FRAME的关键帧时,先把之前保存的 SPS/PPS 拼上起始码发出去,再发送关键帧数据。
  • 普通帧只需要加起始码发送即可。

这样做以后,接收端随时接入都能在下一个关键帧处恢复画面,不会出现长时间黑屏。代码逻辑并不复杂:

if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { configData = ByteArray(bufferInfo.size).apply { outputBuffer?.get(this) } return } val bos = ByteArrayOutputStream() if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) { configData?.let { bos.write(byteArrayOf(0, 0, 0, 1)) bos.write(it) } } bos.write(byteArrayOf(0, 0, 0, 1)) outputBuffer?.position(bufferInfo.offset) outputBuffer?.limit(bufferInfo.offset + bufferInfo.size) // 读取真实帧数据 val frameData = ByteArray(bufferInfo.size) outputBuffer?.get(frameData) bos.write(frameData) // bos.toByteArray() 通过 TCP Socket 发送

传输层我建议直接走 TCP 而不是 UDP。UDP 虽然天生是流媒体传输的首选,但 Demo 阶段如果丢包,接收端会出现花屏和画面撕裂,排查起来非常痛苦。TCP 虽然有重传导致延迟抖动的风险,但在局域网环境下表现很稳,代码也简单。真要优化,后续再引入 RTP 或 QUIC 都不迟。

3.2 接收端三种玩法,最推荐哪种

接收端有三种选择:

第一,PC 上用 ffplay 直接拉 TCP 流。这是最简单、最符合 Demo 定位的方案。

第二,Android 端集成一个 HTTP-FLV 服务,PC 端用 VLC 打开 URL 播放。好处是 VLC 的界面更友好,且可以通过浏览器播放;缺点是要多做一层 HTTP-FLV 封装,工作量上了一个台阶。

第三,完全自研接收端,使用 MediaCodec 或 FFmpeg 解码,自己渲染。适合做产品级工具,但 Demo 阶段完全没必要。

我最推荐第一种。ffplay 是 FFmpeg 自带的播放器,参数简单,对 H.264 裸流的兼容性极好。在开发过程中,它还能输出详细的解码日志,配合排障非常方便。

3.3 ffplay 直接拉流的一条龙配置

接收端具体操作分两种场景。

如果手机和电脑在同一局域网,直接把 TCP 数据发到电脑的某个端口,然后执行:

ffplay -f h264 tcp://192.168.1.100:1234?listen

注意?listen是让 ffplay 作为服务端监听,Android 端主动连接过来。这样做的好处是无需在 PC 端先启动抓包工具,Android 端一开启投屏,数据就连过来播了。

如果手机连不上电脑所在网络,而你是通过 USB 调试线连接的,可以用 adb forward 做端口转发:

adb forward tcp:1234 tcp:1234 ffplay -f h264 tcp://127.0.0.1:1234?listen

这招在开发调试阶段非常实用,尤其是现场网络环境不可控的时候。USB 线的传输带宽远大于投屏所需码率,画面稳定性和帧率反而比 WiFi 更好。

还需要提醒一点:如果测试时 Android 端启动后提示连接成功但 ffplay 黑屏,不要急着怀疑接收端,先看是不是 SPS/PPS 没有正确拼上去,或者编码器根本没有输出数据。后面我会详细讲排查思路。

4. 黑屏、卡顿、延迟大:完整排查链路与调优参数

4.1 我踩过最典型的黑屏:SPS/PPS 缺失

我在第一版实现里吃过一个很大的亏:编码器输出的裸流直接通过网络发了,ffplay 一直黑屏,但是日志显示编码器明明在出数据。后来把同样的字节流保存到本地文件,用ffplay local.h264播放,居然也是黑屏。

这时候我意识到问题不在网络,而在数据本身。检查后确认,我漏掉了BUFFER_FLAG_CODEC_CONFIG里的 SPS/PPS。H.264 解码器必须拿到 SPS(序列参数集)和 PPS(图像参数集)才能开始解码,这两段数据通常在编码器启动后最先输出,而且只输出一次。如果我把它们丢掉了,接收端既不知道视频的分辨率,也不知道编码的 profile,自然解不出画面。

正确的做法我已经在前面代码里写了:必须把 config 数据缓存下来,每当发送关键帧时,把 SPS/PPS 放在关键帧之前一起发出去。这样接收端哪怕是从中间开始接收,也能在下一个关键帧到来时正确恢复解码。

这个坑是投屏开发里非常典型的问题,scrcpy 的 issue 列表里也能看到大量黑屏反馈,大部分都跟 SPS/PPS 处理有关。所以我把排查路径完整列出来,大家可以照着走一遍。

4.2 用帧率和码率判断是采集端还是网络问题

遇到卡顿或者延迟增大,我建议先用数据说话,别靠肉眼猜。具体做法是:在 Android 端编码循环里,每隔 5 秒打印一次输出帧数和编码帧的平均字节数。

我实际遇到过一个现象:画面流畅一两秒,然后突然卡死几秒,再恢复。打印日志后发现,关键帧间隔之内编码输出帧率正常,但到关键帧附近,单帧数据量暴增到几十 KB,TCP 发送出现背压,导致队列积压。局域网的带宽其实完全够用,问题出在发送线程只往 Socket 里写,没有做背压处理。

解决办法是给发送线程加一个阻塞队列:队列长度超过一定阈值时,主动丢弃非关键帧,只保留最新画面。这样接收端永远显示的是最新状态,而不是堆积了很久的旧帧。改进后卡顿基本消失,延迟也稳定下来了。

帧率判断方法:如果编码输出帧率只有个位数,说明编码器能力不足。此时优先降低分辨率而不是盲目调大码率。比如从 1080P 降到 720P,健康状况往往会立刻好转。

4.3 不同分辨率/码率下的实测表现

我在中端平板上测了几个梯度,结果供参考:

采集分辨率目标码率帧率端到端延迟主观画质
1080P4Mbps30300~400ms清晰,文字边缘锐利
1080P2Mbps30250~350ms有轻微模糊,动态画面有块状噪声
720P2Mbps30200~300ms清晰度尚可,流畅
720P1Mbps30200~300ms文字略微发虚

日常演示场景,我最后选了 720P + 2Mbps + 30fps 的组合,延迟和画质比较平衡。如果你的场景以静态图表为主,1080P + 2Mbps 会更好看一些。

延迟优化方面还有一个小技巧:ffplay 默认会有几百毫秒的缓冲,这是为了平滑播放设计的,但对实时性要求高的场景反而坏事。可以通过参数关掉部分缓冲:

ffplay -fflags nobuffer -f h264 tcp://127.0.0.1:1234?listen

加上-fflags nobuffer之后,延迟能再降几十到一百毫秒,画面实时感明显提升。代价是网络瞬时抖动可能带来短暂卡顿,但在稳定的局域网环境里,利大于弊。

5. 从 Demo 到能用,还差这几步工程化

5.1 Android 版本差异造成的兼容坑

同一个 Demo 在不同系统版本上的表现差距很大。我总结下来有几个必须提前处理的点。

Android 10 及以上,前台服务必须附带通知,否则直接崩溃。这个大部分人知道,但投屏服务在后台运行时,如果通知被用户划掉,服务会被系统杀死。我建议在服务里监听通知移除事件,一旦发现投屏还在跑但通知没了,就主动弹一个更明显的前台通知,或者在应用内加一个"投屏运行中"的常驻状态。

Android 14(API 34)是个大分水岭。前面已经提到,MediaProjection 必须和前台服务绑定,并且每次创建投屏会话都要重新申请授权,不能长时间持久化复用。我在 Android 14 上测试时发现,旧代码里缓存的 MediaProjection 实例在使用一段时间后会自动失效,系统文档里明确写了这是预期行为,目的是防止应用在后台偷偷录屏。

国产 ROM 的杀后台策略也是个大坑。小米、华为、OPPO 的省电策略会把长时间在前台运行的服务判为"滥用",然后悄悄杀掉。我调试时经常遇到投屏运行几分钟后无端中断。解决办法是在应用内引导用户把投屏服务的电池策略设为"无限制",并且开启自启动权限。这部分逻辑必须做成引导弹窗,不然用户根本不会注意到服务已经死了。

5.2 音频、触控回传的扩展思路

做完画面投屏,很多人第一反应就是加音频。Android 10 之后有AudioPlaybackCaptureConfiguration可以用来采集其他应用播放的声音,但使用限制不少:目标应用必须允许音频捕获,系统设置里也要开启"录制音频"权限。实际体验下来,很多主流播放器默认禁止被采集,投出来只有自己应用的声音,或者干脆没声音。如果只是投"当前正在播放的视频",更可靠的方案还是用 AudioRecord 采麦克风再编码成 AAC,但音画同步又是一个新课题,Demo 阶段最好不要一次性引入。

触控回传是另一个常见需求。PC 端收到鼠标事件后,通过 Socket 回传给 Android,Android 端注入 MotionEvent。但普通应用没有权限向系统注入触摸事件,需要 root 或者通过辅助功能(AccessibilityService)来实现。scrcpy 之所以能做到,是因为它在 PC 端维护了一套虚拟输入映射。如果你想自研,建议先评估一下工作量和必要性。

我的建议是:第一版只做画面投屏,把延迟和稳定性打磨好;音频排到第二阶段;触控回传放到第三阶段。一次引入太多功能,出了问题根本不知道怪谁。

5.3 给想继续做投屏的同学三条建议

一是先做"本地录制验证"。在把数据往网络上塞之前,先把编码器的输出保存成.h264文件,用播放器验证这段文件能正常播放。这一步过了,才说明采集和编码没问题,后续只需要排查网络。

二是做"人工可控的推流开关"。稳定运行的投屏服务一定要有停止机制,比如通知栏常驻停止按钮。不要只在代码里写死运行时间,真到了现场演示才发现停不下来,会很被动。

三是充分利用现成开源项目做对照。scrcpy 是基于 MediaProjection 的成熟实现,阅读它的源码能帮你理解很多边界情况,比如分辨率变化时编码器怎么处理、屏幕休眠时如何保持采集。我在调通链路之后专门花时间跟了一遍 scrcpy 的编码模块,收获比我自己看文档大得多。

做这个 Demo 给我最大的体会是:投屏链路不长,但每个环节都是"看起来简单,做起来全是细节"。尤其是 SPS/PPS 处理和背压控制这类问题,不实际跑一遍根本想不到。建议刚开始做的朋友,严格按本文第 4 章的排查思路走一遍,把你自己的黑屏和卡顿问题先解决掉,再考虑更复杂的特性。这套流程跑通了,后续加再多功能都有底气。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 22:25:08

Flutter for OpenHarmony 动效优化实战:从掉帧到流畅的跨平台调优指南

第20天的训练营&#xff0c;主题终于落到了我最关心的方向&#xff1a;Flutter for OpenHarmony 的动效优化。作为一个平时主要在 Android 和 iOS 上写 Flutter 的开发者&#xff0c;我对 OpenHarmony 的态度一直是“观望多、上手少”&#xff0c;这次借着训练营的完整项目周期…

作者头像 李华
网站建设 2026/9/9 22:23:12

基于SVM的风电功率预测完整方案:Matlab实现与调参实战

简介&#xff1a;面向风电功率预测与时间序列建模&#xff0c;这份基于Matlab的SVM实现提供完整可运行源码&#xff0c;并附带可直接替换的Excel数据&#xff0c;适合电力系统、机器学习方向的初学者与研究人员快速搭建自己的预测流程。资源共72个文件&#xff0c;主要包括Matl…

作者头像 李华
网站建设 2026/9/9 22:21:25

从热门霸屏到引流获客:系统化内容增长模型的完整拆解

1. 项目概述&#xff1a;别再“碰运气”做热门了 做内容的人大概都经历过这种尴尬&#xff1a;精心准备了一条内容&#xff0c;发出去之后阅读量惨淡&#xff1b;随手发的一条&#xff0c;反而莫名其妙爆了。这种“玄学”感让很多人既兴奋又焦虑——因为不可复制&#xff0c;就…

作者头像 李华
网站建设 2026/9/9 22:19:11

网安专业学模式识别:从贝叶斯到SVM,安全实战的算法基石

简介&#xff1a;东南大学网安学院模式识别课程复习资料&#xff0c;专注解决“作业难找、考试题型不明”的痛点。课程难度不大&#xff0c;但课后作业几乎是考试的晴雨表&#xff0c;约九成考题从作业中变式&#xff0c;因此作业答案与真题回忆的价值远高于普通笔记。压缩包共…

作者头像 李华
网站建设 2026/9/9 22:17:31

Ventoy 如何按 DMPATCH 文档编译 dm_patch.ko 内核模块?

Ventoy 如何按 DMPATCH 文档编译 dm_patch.ko 内核模块&#xff1f; 【免费下载链接】Ventoy A new bootable USB solution. 项目地址: https://gitcode.com/GitHub_Trending/ve/Ventoy 本文解决的任务是&#xff1a;按照 Ventoy 仓库 DMPATCH 目录中的 readme.txt 文档…

作者头像 李华
网站建设 2026/9/9 22:17:12

AI学习路径四阶段:从大模型原理到Agent实战

这两年我周围问AI学习路径的人特别多&#xff0c;而且问法高度相似&#xff1a;我已经会用ChatGPT写周报了&#xff0c;也知道Midjourney能画图&#xff0c;但再往深处走就完全没方向了——大模型原理要不要学&#xff1f;Agent到底是什么&#xff1f;那些动辄几十万的AI应用是…

作者头像 李华