onvif-go(mickeyzzc/onvif-go v2)完整参考手册:从建工程到写出自己的 NVR 接入

简介: `onvif-go/v2` 是 MiBee 生态核心 ONVIF 协议库,同时支持客户端(发现/认证/取流/PTZ/事件)与服务端(虚拟相机),零依赖、Go 原生实现。已真机兼容海康、大华、Axis 等主流品牌及 ESP32 最小系统,含诊断工具链与完整示例,开箱即用。(239字)

onvif-go 是 MiBee 相机接入链路里管「连相机」的库:你的程序要用 ONVIF 去发现、认证、取流、控制一台摄像机,或者反过来把一个进程装成摄像机让 NVR 来连,都用它。它同时提供客户端和虚拟相机 server 两套角色,MiBeeNvr 的 ONVIF 接入就跑在这上面,兼容性矩阵(海康、Axis、大华、Bosch、Amcrest、HiSilicon OEM、ESP32 最小实现)是真机抓包喂出来的。

这篇按「能直接抄去用」的标准写:每个功能一节,节里是完整可运行的程序加逐段讲解,读完就能在自己的项目里落地把对应部分。代码来自仓库 README 和 examples,接口签名与仓库当前发布版核对过。

安装与工程准备

库本体零第三方依赖,Module 路径带 /v2 后缀,要求 Go 1.26 以上:

mkdir my-nvr && cd my-nvr
go mod init example.com/my-nvr
go get github.com/mickeyzzc/onvif-go/v2

工程里通常只需要 import "github.com/mickeyzzc/onvif-go/v2/onvif" 一个包——客户端门面、发现、错误哨兵都从它出发;发现和虚拟相机 server 分别在 discovery/ 和 server/ 子包,用到再引。

仓库还带四个命令行工具,发版时提供 linux/macOS/Windows 预编译二进制,写代码前先用它们探一探相机很省事:

go install github.com/mickeyzzc/onvif-go/v2/cmd/onvif-diagnostics@v2.2.0
onvif-diagnostics -host 192.168.1.100 -user admin -pass camera-password
  • discover:全网段 ONVIF 发现;
  • onvif-quick:一台相机的主要动作一次跑完;
  • onvif-diagnostics:一次扫十一个主要操作出 JSON 报告,README 明说「提 issue 前先跑这个」;
  • onvif-server:起一台虚拟相机。

五分钟跑通第一台相机

前置条件:一台开着 ONVIF 的相机。到相机 Web 管理界面把 ONVIF 服务打开,设一个独立密码(别用管理员密码);海康系相机还要留意系统时间对不对,认证一节会讲为什么。

完整程序:

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/mickeyzzc/onvif-go/v2/onvif"
)

func main() {
   
    client, err := onvif.NewClient("192.168.1.100",
        onvif.WithCredentials("admin", "camera-password"),
        onvif.WithAutoClockSkew())
    if err != nil {
   
        log.Fatal(err)
    }
    if err := client.Initialize(context.Background()); err != nil {
   
        log.Fatal(err)
    }

    info, err := client.Device().GetDeviceInformation(context.Background())
    if err != nil {
   
        log.Fatal(err)
    }
    fmt.Printf("%s %s (fw %s)\n", info.Manufacturer, info.Model, info.FirmwareVersion)

    profiles, err := client.Media().GetProfiles(context.Background())
    if err != nil {
   
        log.Fatal(err)
    }
    mainToken := onvif.SelectMainProfile(profiles)
    uri, err := client.Media().GetStreamURI(context.Background(), mainToken)
    if err != nil {
   
        log.Fatal(err)
    }
    fmt.Println("主码流:", uri.URI)
}

跑起来(输出随相机型号不同):

Hikvision DS-2CD3T46 (fw 5.5.53)
主码流: rtsp://192.168.1.100:554/Streaming/Channels/101

逐段说:

  • NewClient 的第一个参数接受三种写法:完整 URL、IP:端口、裸 IP——后两种自动补成 /onvif/device_service。WithCredentials 给出凭据,WithAutoClockSkew 让客户端先无认证读一次设备时间、算出偏差再参与后续签名,海康系的 401 十有八九是它治的。
  • Initialize 做三件事:测时钟偏差、取 capabilities、解析各服务的 XAddr。第三件事很有用——不少相机在 capabilities 里报的是过期 IP,库会把错误地址修掉再存下来。
  • client.Device()、client.Media() 是 service facade:每个 ONVIF 服务一个长生命周期入口,还有 PTZ()、Imaging()、Events()、Media2()、Analytics()、DeviceIO()、Security()。
  • SelectMainProfile 不等于 profiles[0]:它按分辨率挑主码流,命名提示(main/主流/sub/辅流)做决胜,全都没分辨率信息时才回退第一个。这行的存在就是因为大量相机把子码流排在 profiles[0]。
  • uri.URI 是个 RTSP 地址。拿 VLC 或 ffplay 立刻验证:ffplay -rtsp_transport tcp "rtsp://192.168.1.100:554/Streaming/Channels/101"。

库在链路里的位置

mermaid diagram

左边是你写的程序,onvif-go 往下分两个角色:作为客户端向右连真相机;作为 server 向下当一台虚拟相机——测试 NVR 的发现、取流、PTZ 逻辑时不用插真机。两个角色共享同一套线格式代码,互为对方的第一批测试用例。

发现相机:主动、被动与定向

主动组播发现,同网段一发一收:

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/mickeyzzc/onvif-go/v2/discovery"
)

func main() {
   
    ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
    defer cancel()

    devices, err := discovery.Discover(ctx, 5*time.Second)
    if err != nil {
   
        log.Fatal(err)
    }
    for _, d := range discovery.FilterONVIFDevices(devices) {
   
        fmt.Printf("%-14s %-20s %s\n", d.GetName(), d.Hardware, d.GetDeviceEndpoint())
    }
}

FilterONVIFDevices 把 Synology、Windows、打印机这类「什么 Probe 都应答」的幽灵设备滤掉,实际网段里这一步基本必需。发现结果要补齐设备名、位置、序列号时,接一步并行补全:

enriched, err := discovery.EnrichDevices(ctx, filtered) // 默认并发 8

Device 的字段值得认识全:

字段 含义
EndpointRef WS-Discovery 的 urn:uuid 形式端点标识,传输层稳定 ID
Name / Hardware / Location 从 scopes 解析出的友好名、硬件型号、位置,没广告就为空
XAddrs 设备服务地址列表,连它
Types / Scopes / MetadataVersion 设备类型、原始 scopes、元数据版本

一个容易踩的坑写在字段注释里:EndpointRef 不是序列号。跨协议关联同一台相机(ONVIF 这边、GB28181 那边)要用 d.Info.SerialNumber,拿 EndpointRef 对序列号永远对不上。

被动监听是另一种思路——相机上电会发 Hello,别的 NVR 探测时也有应答可听,程序开着就能攒设备清单:

listener, err := discovery.NewListener("eth0", func(d *discovery.Device) {
   
    fmt.Println("上线:", d.GetName(), d.GetDeviceEndpoint())
})
if err != nil {
   
    log.Fatal(err)
}
if err := listener.Start(ctx); err != nil {
   
    log.Fatal(err)
}
<-listener.Done() // Stop() 后关闭

定向 HTTP 探测处理组播到不了的场景(路由器隔离、AP 隔离):已知 IP 直接问它的 ONVIF 端口,

if d := discovery.ProbeEndpoint(ctx, "192.168.1.64", 80, 2*time.Second); d != nil {
   
    fmt.Println("是一台 ONVIF 设备:", d.GetDeviceEndpoint())
}
serial, ok := discovery.ProbeSerial(ctx, "192.168.1.64", nil) // nil 用默认端口集

认证与时钟偏差

mermaid diagram

库支持四档认证:

档位 线上行为 适用
AuthDigest WS-Security UsernameToken,PasswordDigest(默认) 绝大多数相机
AuthPasswordText UsernameToken,密码明文进报文 老固件只认这个
AuthHTTPBasic HTTP Basic 头 个别 HTTP 层认证的设备
AuthNone 不带凭据 免认证设备、ESP32 最小实现

真实世界的相机什么怪癖都有,所以有认证梯队——主模式失败且错误属于认证类(HTTP 401/403、NotAuthorized fault、带 fault 的 200)时按序换档,第一个成功的档位会粘住记住:

client, err := onvif.NewClient(endpoint,
    onvif.WithCredentials("admin", "camera-password"),
    onvif.WithAuthFallback(onvif.AuthPasswordText, onvif.AuthHTTPBasic, onvif.AuthNone))

梯队耗尽返回 onvif.ErrUnauthorized,errors.Is 可靠判定。

时钟偏差为什么能打死 digest:PasswordDigest 的签名串里有 Created 时间戳,设备拿它算有效期,偏差超过窗口(各家几十秒到几分钟)直接判无效——表现为怎么改密码都 401。WithAutoClockSkew 在 Initialize 时先无认证调一次 GetSystemDateAndTime,算出偏差参与后续签名。

排错不要猜,用诊断接口:

diag, err := client.DiagnoseAuth(context.Background())
fmt.Println(diag.Status, diag.ClockSkew, diag.Detail)

Status 三态对应的处理:AuthStatusOK 什么都不用做;AuthStatusClockSkew 修相机 NTP(短期让 WithAutoClockSkew 顶着);AuthStatusBadCredentials 才是真改密码。凭据也可以运行时换:client.SetCredentials(user, pass),怀疑梯队粘住了旧档位就 client.ResetAuthLadder(),怀疑 capabilities 缓存过期(相机换过 IP)就 client.InvalidateCapabilitiesCache() 后重跑 Initialize。

取流与抓图

取流的正确姿势永远是先选 profile 再拿地址:

profiles, err := client.Media().GetProfiles(ctx)
if err != nil {
   
    log.Fatal(err)
}
for _, p := range profiles {
   
    fmt.Printf("profile %-10s %dx%d\n", p.Token, p.VideoEncoderConfiguration.Resolution.Width, p.VideoEncoderConfiguration.Resolution.Height)
}

mainToken := onvif.SelectMainProfile(profiles)
subToken := onvif.SelectSubProfile(profiles, mainToken)

uri, err := client.Media().GetStreamURI(ctx, mainToken)
snap, _ := client.Media().GetSnapshotURI(ctx, mainToken)
fmt.Println(uri.URI, "|", snap.URI)

两个选择函数各管一件事:SelectMainProfile 挑分辨率最高的;SelectSubProfile 在主流之下挑严格更小的最大者——同分辨率的第二个 profile 不算子流,Amcrest 有机型双 token 指向同一路流,这个函数就是为它写的。

GetStreamURI 默认要 RTP-Unicast/RTSP;要走 HTTP 隧道或组播用 GetStreamURIWithOptions 传 StreamSetup。拿到地址后 SetSynchronizationPoint 让相机立刻出 I 帧,多路轮显时很省等待;组播场景还有 StartMulticastStreaming/StopMulticastStreaming。

拿到的 uri.URI 直接喂给任何 RTSP 播放器验证:

ffplay -rtsp_transport tcp "rtsp://192.168.1.100:554/Streaming/Channels/101"

媒体面的收发(比如接 pion 做录制或转发)在自己的程序里做——这个库负责把地址、profile、编码参数这些「问相机」的事全部办妥。

PTZ 与预置位

ptz := client.PTZ()

// 向右匀速转 3 秒再停
err := ptz.ContinuousMove(ctx, mainToken, &onvif.PTZSpeed{
   
    PanTilt: &onvif.Vector2D{
   X: 0.5, Y: 0},
    Zoom:    &onvif.Vector1D{
   X: 0},
}, nil)
if err != nil {
   
    log.Fatal(err)
}
time.Sleep(3 * time.Second)
_ = ptz.Stop(ctx, mainToken, true, true) // 同时停云台和变焦

// 预置位:设、走、删
presetToken, err := ptz.SetPreset(ctx, mainToken, "大门口", "")
if err != nil {
   
    log.Fatal(err)
}
_ = ptz.GotoPreset(ctx, mainToken, presetToken)
presets, _ := ptz.GetPresets(ctx, mainToken)
for _, p := range presets {
   
    fmt.Println(p.Token, p.Name)
}

// 限位与状态
cfgs, _ := ptz.GetConfigurations(ctx, mainToken) // pan/tilt/zoom 空间限位
status, _ := ptz.GetStatus(ctx, mainToken)

PTZSpeed 的 X/Y 取值 -1 到 1,是归一化速度,相机自己换算成实际角速度;绝对定位走 AbsoluteMove(传 PTZVector 目标位置),相对微调走 RelativeMove。回家位(GotoHomePosition/SetHomePosition)覆盖「看完门口回原位」这类需求。聚焦在 client.Imaging():Move/StopFocus 配合自动对焦模式,GetImagingSettings/SetImagingSettings 读写亮度、对比度、曝光。

事件订阅

托管订阅把长轮询、续订、断线这些全包了:

package main

import (
    "context"
    "errors"
    "fmt"
    "log"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/mickeyzzc/onvif-go/v2/onvif"
)

func main() {
   
    client, err := onvif.NewClient("192.168.1.100",
        onvif.WithCredentials("admin", "camera-password"),
        onvif.WithAutoClockSkew())
    if err != nil {
   
        log.Fatal(err)
    }
    if err := client.Initialize(context.Background()); err != nil {
   
        log.Fatal(err)
    }

    ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
    defer stop()

    stream, err := client.Events().SubscribeEvents(ctx, func(msg onvif.NotificationMessage) {
   
        fmt.Printf("[%s] %s %s\n",
            msg.Message.UtcTime.Format("15:04:05"),
            msg.Topic, msg.Message.PropertyOperation)
        for _, item := range msg.Message.Data {
   
            fmt.Printf("    %s = %s\n", item.Name, item.Value)
        }
    }, &onvif.SubscribeEventsOptions{
   SubscriptionDuration: time.Hour})
    if errors.Is(err, onvif.ErrEventsNotSupported) {
   
        log.Fatal("这台相机没有事件服务")
    } else if err != nil {
   
        log.Fatal(err)
    }

    <-ctx.Done()
    _ = stream.Unsubscribe(context.Background())
}

跑起来后,触发一次移动侦测会看到:

[14:32:07] tns1:VideoSource/MotionAlarm Changed
    State = true
    Score = 87

SubscribeEventsOptions 的可调项:

字段 默认 含义
SubscriptionDuration 1h 订阅总时长,到期前自动续
RenewMargin 5m 提前多久续订
PullTimeout 30s 单次长轮询等待
MessageLimit 10 单次拉取条数上限
Filter 空 订阅主题过滤

收到的事件结构是 NotificationMessage{Topic, Message(EventMessage), ProducerAddress, SubscriptionID},EventMessage 里 PropertyOperation(Created/Changed/Deleted)、UtcTime 和三组 SimpleItem(Source/Key/Data,每项 Name+Value)。一条线格式细节值得知道:合规相机把 tt:Message 包在 wsnt:Message 里,穿透这层包装才能拿到 MotionAlarm 的 State 和 Score——托管订阅已处理;绕过它直接调 CreatePullPointSubscription/PullMessages/RenewSubscription/Unsubscribe 原语时得自己拆这两层,原语留给需要精细控制轮询节奏的场景。

把一个进程装成相机

server 角色的完整程序——一台有凭据、会报移动事件的虚拟相机:

package main

import (
    "context"
    "log"
    "time"

    "github.com/mickeyzzc/onvif-go/v2/server"
)

func main() {
   
    config := server.DefaultConfig()
    config.Port = 8081
    config.Username = "admin"
    config.Password = "nvr-test"
    config.SupportEvents = true

    srv, err := server.New(config)
    if err != nil {
   
        log.Fatal(err)
    }
    ctx := context.Background()
    if err := srv.Start(ctx); err != nil {
   
        log.Fatal(err)
    }
    log.Printf("虚拟相机: %s", srv.ListenAddr())

    // 每 30 秒推一条移动事件给在订的 NVR
    for range time.Tick(30 * time.Second) {
   
        srv.PublishEvent(server.Event{
   
            Topic: "tns1:VideoSource/MotionAlarm",
            Data: []server.SimpleItem{
   
                {
   Name: "State", Value: "true"},
                {
   Name: "Score", Value: "87"},
            },
        })
    }
}

NVR 那边手动添加 http://<你的IP>:8081 这台「相机」,就能发现它、读设备信息、拿到流地址、收到事件。PublishEvent 扇出到所有在订的 PullPoint 订阅,没有订阅者时是安全的空操作;PropertyOperation 留空默认 Changed,UtcTime 发布时自动盖。

开发 NVR 时它就是测试桩——不用插真机就能测发现、取流、PTZ、事件的全部逻辑。几个进阶开关:

config.Profiles = []server.ProfileConfig{
    /* 多镜头:Token/Name/VideoSource/VideoEncoder... */ }
config.AdvertiseHostProvider = func() string {
    return detectOutboundIP() } // DHCP 换 IP 后 XAddr 跟着走
config.AuthProtectedActions = []string{
   "SystemReboot"}                     // 额外保护的写动作
config.TLSCertFile, config.TLSKeyFile = "cert.pem", "key.pem"              // HTTPS(Profile T 传输基线)

流地址运行时会变(比如转码后换端口)用 srv.UpdateStreamURI(profileToken, "rtsp://127.0.0.1:8554/live");要按请求上下文自定义某个动作的应答,srv.RegisterContextHandler(action, func(rc *soap.RequestContext, body []byte)) 挂进去。server 的认证策略是「配了凭据只保护写型动作」(Set*/Remove*/Create*/Go* 加 SystemReboot),读操作开放。

最省事的验证方式是自测闭环:拿这个库的客户端连自己的 server,发现、认证、取流、事件全链路一遍过——examples/ 里的 conformance 回环测试干的就是这件事。

生产项目里的用法

生态里 ONVIF 客户端的大户是各类 NVR/VMS——海康、Axis 这些厂商的录像机互相都靠它对接,ONVIF Device Manager 则是大家常用的手动探查工具;onvif-go 的兼容矩阵(海康、Axis、大华、Bosch、Amcrest、HiSilicon OEM、ESP32 最小实现)就是对着这些真机抓包喂出来的。抓包的来源,是下面这些在生产里每天跑的项目:

功能 生产项目 用法
发现(主动 / 被动 / 定向) MiBeeNvr 相机接入的发现层:扫网段列设备、常驻监听攒清单、跨网段定向探测
认证 + 时钟偏差 MiBeeNvr 连商用相机的第一道关卡,WithAutoClockSkew 是对海康系 401 的默认防线
取流 / 抓图 / 主辅码流 MiBeeNvr ONVIF 信令拿到 RTSP 地址与编码参数,接它自己的录像、直播与回放管线
虚拟相机 server mibee-eye-go 把 Linux 板子的 UVC 相机变成一台 ONVIF Profile S 相机;RTSP gateway 模式把已有 RTSP 流包装成相机挂进商用 NVR

看完整实现有两个入口。NVR 侧(客户端怎么连真相机)看 MiBeeNvr——自托管录像机,单二进制跑在低功耗 ARM 上,ONVIF 接入就是这个库,兼容矩阵里每个机型背后都有它的联调记录。设备侧(server 怎么当相机)看 mibee-eye-go——纯 Go 零 CGO,USB/UVC 采集直出,onvif-go server 的每个接缝在真实产品里的样子,那里都有现成答案。

排错清单

现象 原因 处理
海康相机恒 401,报 "sender not authorized" 设备时钟偏差超出签名窗口 WithAutoClockSkew 顶着,DiagnoseAuth 确认,根治是修相机 NTP
请求返回 200 但设备什么都不做 旧版请求载荷命名空间错位,严格设备解析出空载荷 升级到 v2.1.0 以上(这版起线格式以官方 WSDL 为准)
GetStreamURI 返回错误 相机响应里没有 Uri 元素 错误为 ErrEmptyMediaURI,消息里带 512 字节响应摘要;换 profile 再试
发现列表里设备名是空的 相机没在 scopes 里广告名字 用 EnrichDevices 补全,或读 GetDeviceInformation
跨协议关联不上同一台设备 拿 EndpointRef 对了序列号 用 d.Info.SerialNumber 关联,EndpointRef 只是发现层标识
换了密码还是旧认证在跑 认证梯队粘住了成功档 client.ResetAuthLadder()
相机换 IP 后连不上 capabilities 缓存里的旧地址 重跑 Initialize 或 InvalidateCapabilitiesCache()
use-go/onvif 的老代码编译不过 上游停更,新 Go 版本语法冲突 迁移到 v2:import 路径换 github.com/mickeyzzc/onvif-go/v2/onvif,多数代码原样可编

API 速查表

需求 入口 说明
建客户端 onvif.NewClient(endpoint, opts...) URL/IP:port/裸 IP 均可
初始化 client.Initialize(ctx) 测偏差、取 capabilities、修 XAddr
认证 WithCredentials + WithAuthFallback 四档梯队,成功档粘住
时钟偏差 WithAutoClockSkew / DiagnoseAuth 前者自动容偏差,后者出诊断报告
运行时调整 SetCredentials / ResetAuthLadder / InvalidateCapabilitiesCache 换凭据、重置梯队、清缓存
发现 discovery.Discover / NewListener / ProbeEndpoint 主动 / 被动 / 跨网段定向
过滤补全 FilterONVIFDevices / EnrichDevices 滤幽灵设备、并发补全信息
选码流 onvif.SelectMainProfile / SelectSubProfile 分辨率优先,防双 token 同流陷阱
取流/抓图 Media().GetStreamURI / GetSnapshotURI 返回 URI,媒体面自己做
强制 I 帧 Media().SetSynchronizationPoint 多路轮显省等待
云台 PTZ().ContinuousMove / GotoPreset 等 速度 -1..1;限位在 GetConfigurations
事件 Events().SubscribeEvents 托管订阅,自动续订
H.265/Profile M Media2() / Analytics() / metadata.Parse 编码配置面 + 元数据流解析
当相机 server.New(server.DefaultConfig()) PublishEvent 注入事件,TLS 成对配置
动态流地址 server.UpdateStreamURI 转码、端口变化场景
自定义动作 server.RegisterContextHandler 按请求上下文应答

相关链接

相关文章
|
8天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
7385 12
|
6天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1545 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
7天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
1006 8
|
3天前
|
人工智能 JavaScript 芯片
DeepSeek 官方偷偷上传 Harness 桌面端安装包,我已经用上了。。附最新下载地址
DeepSeek Harness 官方的桌面端安装包被网友扒出来了,2 分钟讲明白如何使用,体验如何,适合作为 AI 编程工具么?附最新 Windows 和 Mac 双端的下载地址
1199 1
|
20天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3581 10
|
15天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
1611 1
|
4天前
|
编解码 缓存 PyTorch
16G 显卡能跑 Qwen-Image 2.1 吗?
9月20日,阿里Qwen开源Qwen-Image-2.1:7B DiT图像模型+8B文本编码器+VAE,单模型支持文生图与图像编辑,原生输出2K PNG(含Alpha通道),支持10张参考图。在自建Qwen-Image-Bench达60.28分(开源模型第一),GenAI Showdown文生图排名7/15。16G显存可跑1024×1024(需INT8量化+ComfyUI优化),但2K需24G以上。注意其Qwen Research License限非商业用途。
506 1
|
5天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)