Help us shape the future of Go! Take the 2026 Go Developer Survey

orbbec

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 17 Imported by: 0

README

orbbec-go

CI Go Reference

Pure Go driver for the Orbbec Gemini 435Le Ethernet depth camera — no OrbbecSDK native libraries, no cgo. CGO_ENABLED=0 builds static binaries for Linux (amd64/arm64), Windows and macOS.

It speaks the camera's network protocols directly (GVCP control channel privilege, the vendor control protocol on TCP 8090, RTSP/RTP depth and color streams, on-device calibration), ported from the MIT-licensed OrbbecSDK v2 and checked against the official SDK on real hardware: intrinsics, extrinsics, baseline, frame metadata and the command sequence match, and depth pixels match the SDK's output with its noise filter off.


奥比中光 Gemini 435Le 网口深度相机的纯 Go 驱动:不依赖 OrbbecSDK 原生库,不用 cgo,一个静态二进制就能跑。

功能

  • 设备发现(GVCP 广播)、控制权(CCP)申请 / 保活 / 释放:别的程序占着相机时给出明确错误,不会像原生 SDK 那样把进程带崩
  • 深度流 Y16(1280×800 @ 5/10 fps),单位换算成 mm;彩色流 MJPEG(RFC 2435),保留原始 JPEG 字节方便直接转发
  • 从相机读标定:各分辨率的深度/彩色内参、深度→彩色外参、双目基线;相机报告的有效分辨率列表
  • 每帧元数据:曝光、增益、激光状态、传感器时间戳、帧号
  • 常驻收帧 + 最近 N 帧缓存(多帧取中值不必等相机出帧)、掉线检测、打开重试
  • obprobe 命令行:发现、只读查看、取帧存盘、长时间运行监控

安装

go get github.com/dropp-dev/orbbec-go
go install github.com/dropp-dev/orbbec-go/cmd/obprobe@latest

用法

import "github.com/dropp-dev/orbbec-go"

cam := orbbec.New(orbbec.Config{IP: "192.168.1.10"})
if err := cam.Open(); err != nil { // 拿控制权、读标定、起深度+彩色流
	log.Fatal(err)
}
defer cam.Close() // 关流并释放控制权,别的程序马上能打开

intr := cam.Intrinsics()                                    // 1280x800 深度内参
frames, err := cam.RecentDepths(5, 2*time.Second, time.Second) // 最近 5 帧,Data 单位 mm,0 = 无效
f, err := cam.GrabDepth(time.Second)                        // 下一帧新的深度
color, _ := cam.GrabColor(time.Second)                      // 最新彩色帧(JPEG 字节)
cp := cam.ColorParams()                                     // 彩色内参 + 深度->彩色外参
if !cam.IsAlive(0) {                                        // 断电、拔网线后变 false
	log.Print(cam.LastError())
}

取帧超时返回 ErrTimeout,Close() 之后返回 ErrClosed;IsAlive 只看深度流,彩色流断了不算离线。 帧回调(Device.StartDepth 的 onFrame)在收帧 goroutine 上执行,不要阻塞,也不要在里面调 Close。

更底层的 orbbec.OpenDevice 可以只读打开(不占控制权)、读写属性(如 PropLaserBool)、单独起深度或彩色流; Device.Close() 会先关掉它起的流。

obprobe

obprobe discover                          # 广播找相机
obprobe info  -ip 192.168.1.10            # 只读:设备信息、有效分辨率、深度精度、标定和内参(不占控制权)
obprobe grab  -ip 192.168.1.10 -out /tmp  # 取 5 秒流,报帧率,存 depth.png(16 位,mm)和 color.jpg
obprobe watch -ip 192.168.1.10            # 长时间运行,每 10 秒报一次帧率,掉线自动重连

-precision keep|default|0~6:深度精度档位。default 和官方 SDK 一样设成相机默认档(435Le 是 1 mm), keep 不动相机当前设置(obprobe 默认),库默认是 default。

支持的设备

设备 固件 状态
Gemini 435Le 1.3.19 已在真机上对照官方 SDK 核实

其他 Gemini 330 系列网口相机(335Le 等)走的是另一套 RTP 推流协议,暂不支持。

部署注意

  • 持有控制权期间,OrbbecViewer 等基于官方 SDK 的程序打不开相机(ACCESS_DENIED);Close() 后立即释放,进程被强杀时相机在心跳超时(约 6 s)后自动释放。
  • 不要同时用 VLC、ffmpeg 等直接拉 rtsp://相机IP:8554/...:相机会把帧轮流分给各客户端,后来的客户端断开时还会让先来的停帧约 5 s。
  • 防火墙放行到相机的 TCP 8090/8554/8888 和 UDP 3956。RTSP 走 TCP 交织,不需要调大 UDP 接收缓冲区。

协议概要

通道 端口 用途
GVCP UDP 3956 发现;CCP 寄存器 0x0A00 控制权;心跳超时 0x0938
控制协议 TCP 8090 8 字节头(魔数 0x4D47)+ 属性 ID;响应魔数 0x4252,补零到 1022 字节
RTSP TCP 8554 / 8888 rtsp://IP:8554/depth/1280_800_10_y16、rtsp://IP:8888/color/1280_800_10_mjpg,RTP 交织在同一 TCP 连接

与官方 SDK 的一致性

在 435Le(固件 1.3.19)上对照 OrbbecSDK v2.10.6 核实过:

  • 内参、外参、基线、设备信息、帧时间戳和元数据与 SDK 完全一致;
  • 打开、起流、关流的命令序列与 SDK 一致;
  • 深度像素与关闭去噪的 SDK 输出一致(静止场景中值图 99% 以上像素相差不超过 1 mm,无像素错位)。SDK 默认开着的去噪只额外去掉极少量孤立小斑点,本驱动不做;
  • 彩色 JPEG 的量化表、Huffman 表与 SDK 逐字节相同。

测试

go test -race ./...                       # 内置模拟相机(GVCP + 控制协议 + RTSP),标定数据来自真机
go test -run='^$' -fuzz=FuzzJPEG -fuzztime=30s .   # 协议解析器的 fuzz(FuzzParseRTP / FuzzJPEG / FuzzVendorFrame / FuzzGVCPAndCalib)

许可证

MIT。协议实现移植自 OrbbecSDK v2(MIT,Copyright (c) 2024-2026 Orbbec Inc),见 NOTICE。 本项目与奥比中光无关,"Orbbec" 是奥比中光的商标。

Documentation

Overview

Package orbbec 是奥比中光 Gemini 435Le 网口深度相机的纯 Go 驱动,不依赖 OrbbecSDK 原生库,不用 cgo。

协议移植自 OrbbecSDK v2(MIT,Copyright (c) Orbbec Inc.):

  • GVCP(UDP 3956):设备发现;CCP 寄存器 0x0A00 控制权的申请、保活和释放。
  • 控制协议(TCP 8090):属性、结构体、V1.1 结构体列表。
  • RTSP(TCP 8554 深度、8888 彩色),RTP 交织在同一连接上。
  • 标定:深度/彩色内参、深度到彩色外参、基线、有效分辨率。

用法:

cam := orbbec.New(orbbec.Config{IP: "192.168.1.10"})
if err := cam.Open(); err != nil {
	log.Fatal(err)
}
defer cam.Close()
frames, err := cam.RecentDepths(5, 2*time.Second, time.Second)

Index

Constants

View Source
const (
	GevStatusSuccess      uint16 = 0x0000
	GevStatusAccessDenied uint16 = 0x8006
	GevStatusError        uint16 = 0x8FFF
)

GVCP 状态码。

View Source
const (
	SensorIR         = 1
	SensorColor      = 2
	SensorDepth      = 3
	SensorIRLeft     = 6
	SensorIRRight    = 7
	SensorConfidence = 9

	FormatYUYV  = 0
	FormatMJPG  = 5
	FormatY16   = 8
	FormatY8    = 9
	FormatI420  = 15
	FormatRVL   = 27
	FormatY12C4 = 34
)

传感器类型和像素格式(OBSensorType / OBFormat)。

View Source
const (
	PropLaserBool                     uint32 = 3
	PropStopDepthStreamBool           uint32 = 38
	PropDepthAlignHardwareBool        uint32 = 42 // 硬件 D2C,开着时深度被对齐到彩色视角
	PropDepthPrecisionLevelInt        uint32 = 75
	PropStopColorStreamBool           uint32 = 77
	PropDisparityToDepthBool          uint32 = 85
	PropDeviceCommunicationTypeInt    uint32 = 97 // 1 = 网口
	PropDevicePIDInt                  uint32 = 111
	PropCompatVersionInt              uint32 = 237 // 1 = 彩色 JPEG 内嵌元数据
	PropColorAutoExposureBool         uint32 = 2000
	PropIRAutoExposureBool            uint32 = 2025
	StructVersion                     uint32 = 1000
	RawDataD2CAlignSupportProfileList uint32 = 4024
	RawDataDepthCalibParam            uint32 = 4026
	RawDataAlignCalibParam            uint32 = 4027
	RawDataEffectiveStreamProfileList uint32 = 4035
)

属性 ID(OrbbecSDK 的 Property.h / InternalProperty.hpp)。

Variables

View Source
var (
	// ErrTimeout 表示在给定时间内没有等到帧。
	ErrTimeout = errors.New("timed out waiting for a frame")
	// ErrAlreadyOpen 表示 Camera 已经打开。
	ErrAlreadyOpen = errors.New("camera is already open")
)
View Source
var ErrAccessDenied = errors.New("camera is controlled by another application (GVCP access denied)")

ErrAccessDenied 表示控制权在别的程序手里。持有者异常退出时,相机在心跳超时后自动释放。

View Source
var ErrClosed = errors.New("device is closed")

ErrClosed 表示设备或相机已经关闭。

Functions

This section is empty.

Types

type Calibration

type Calibration struct {
	Depth []DepthCalibParam
	// contains filtered or unexported fields
}

Calibration 是从相机读出的标定数据。

func (*Calibration) BaselineMM

func (c *Calibration) BaselineMM() float64

BaselineMM 返回双目基线(mm),未知时为 0。

func (*Calibration) ColorIntrinsics

func (c *Calibration) ColorIntrinsics(w, h int) (Intrinsics, error)

ColorIntrinsics 返回 w×h 彩色流的内参。

func (*Calibration) DepthIntrinsics

func (c *Calibration) DepthIntrinsics(w, h int) (Intrinsics, error)

DepthIntrinsics 返回 w×h 深度流的内参。

func (*Calibration) DepthToColor

func (c *Calibration) DepthToColor() (Extrinsic, bool)

DepthToColor 返回深度到彩色的外参。

type Camera

type Camera struct {
	// contains filtered or unexported fields
}

Camera 持续接收深度和彩色并缓存最近几帧。深度流停了 IsAlive 返回 false,Close 再 Open 即可重连。 返回的帧是共享的,只读;要修改先 Clone。必须用 New 创建;Open/Close 可以和取帧方法并发调用。

func New

func New(cfg Config) *Camera

New 创建相机对象,不连接。

func (*Camera) BaselineMM

func (c *Camera) BaselineMM() float64

BaselineMM 返回双目基线(mm),未知时为 0。

func (*Camera) Close

func (c *Camera) Close() error

Close 关流并释放控制权,可重复调用;会中断正在进行的 Open。

func (*Camera) ColorParams

func (c *Camera) ColorParams() *ColorParams

ColorParams 返回彩色内参和深度到彩色外参(副本),没有彩色流时为 nil。

func (*Camera) DeviceInfo

func (c *Camera) DeviceInfo() DeviceInfo

DeviceInfo 返回设备信息。

func (*Camera) GrabColor

func (c *Camera) GrabColor(timeout time.Duration) (*ColorFrame, error)

GrabColor 返回最新的彩色帧,没有时最多等 timeout,仍没有则返回 ErrTimeout。

func (*Camera) GrabDepth

func (c *Camera) GrabDepth(timeout time.Duration) (*DepthFrame, error)

GrabDepth 返回下一帧新的深度图;超时返回 ErrTimeout,关闭返回 ErrClosed。

func (*Camera) HasColor

func (c *Camera) HasColor() bool

func (*Camera) Intrinsics

func (c *Camera) Intrinsics() Intrinsics

Intrinsics 返回深度流内参。

func (*Camera) IsAlive

func (c *Camera) IsAlive(maxAge time.Duration) bool

IsAlive 表示 maxAge(0 用 AliveTimeout)内收到过深度帧。

func (*Camera) LastError

func (c *Camera) LastError() string

LastError 返回离线原因,在线时为空。

func (*Camera) LatestColor

func (c *Camera) LatestColor(newerThan uint64, timeout time.Duration) (*ColorFrame, uint64)

LatestColor 等到序号大于 newerThan 的彩色帧后返回,超时则返回当前最新帧;没有彩色帧时返回 nil。

func (*Camera) Open

func (c *Camera) Open() error

Open 连接相机并开始取流,失败时按 RetryWait 重试。已打开时返回 ErrAlreadyOpen。

func (*Camera) OpenContext

func (c *Camera) OpenContext(ctx context.Context) error

OpenContext 同 Open,可取消;Close 也会中断正在进行的打开。

func (*Camera) RecentDepths

func (c *Camera) RecentDepths(n int, timeout, maxAge time.Duration) ([]*DepthFrame, error)

RecentDepths 返回缓存里最近 n 帧(按时间先后)。最新一帧早于 maxAge(0 用 AliveTimeout)时等新帧, 避免离线后用旧帧。

func (*Camera) Stats added in v0.1.1

func (c *Camera) Stats() Stats

Stats 返回当前流统计;没打开时全零。

type ColorFrame

type ColorFrame struct {
	Width, Height int
	JPEG          []byte
	Seq           uint64
	Time          time.Time
	Metadata      []byte // JPEG COM 段里的帧元数据
}

ColorFrame 是一帧彩色图,保留相机发来的 JPEG 字节。

func (*ColorFrame) Clone

func (f *ColorFrame) Clone() *ColorFrame

Clone 深拷贝一帧(JPEG 和 Metadata 都复制)。

type ColorParams

type ColorParams struct {
	Intrinsics Intrinsics `json:"intr"`
	DepthToRGB Extrinsic  `json:"extrinsic"`
}

ColorParams 是彩色相机内参和深度到彩色的外参。

type Config

type Config struct {
	IP     string
	Logger *slog.Logger
	// 端口,0 用默认值。
	GVCPPort, ControlPort, DepthRTSPPort, ColorRTSPPort int

	Depth StreamConfig // 默认 1280x800@10
	// Color 为 nil 时用 1280x800@10;NoColor 为 true 时不开彩色流(优先于 Color)。
	Color   *StreamConfig
	NoColor bool

	DepthPrecision *DepthPrecision // 见 DeviceOptions.DepthPrecision

	RingSize         int           // 缓存的深度帧数,默认 8
	AliveTimeout     time.Duration // 多久没有新深度帧算离线,默认 3 s
	OpenRetries      int           // 默认 5
	RetryWait        time.Duration // 默认 3 s
	FrameIdleTimeout time.Duration // 见 DeviceOptions.FrameIdleTimeout
}

Config 是 Camera 的参数,零值字段用默认值。

type DepthCalibParam

type DepthCalibParam struct {
	DepthMode   uint32
	Baseline    float32
	Z0          float32
	FocalPix    float32
	Unit        float32
	DispOffset  float32
	InvalidDisp int32
}

DepthCalibParam 是深度标定参数(OBDepthCalibrationParam,28 字节)。

type DepthFrame

type DepthFrame struct {
	Width, Height     int
	Data              []uint16
	Seq               uint64 // 本地序号,从 1 开始
	DeviceSeq         uint64 // 相机帧号
	DeviceTimestampUs uint64
	Time              time.Time // 主机收到的时间
	Metadata          []byte    // 原始帧元数据,用 ParseFrameMetadata 解析
}

DepthFrame 是一帧深度图,Data 按行存储,单位 mm,0 表示无效。

func (*DepthFrame) At

func (f *DepthFrame) At(u, v int) uint16

At 返回 (u, v) 处的深度。

func (*DepthFrame) Clone

func (f *DepthFrame) Clone() *DepthFrame

Clone 深拷贝一帧(Data 和 Metadata 都复制)。

type DepthPrecision

type DepthPrecision int32

DepthPrecision 是深度精度档位,决定一个深度值代表多少 mm。

const (
	Precision1mm   DepthPrecision = 0
	Precision0mm8  DepthPrecision = 1
	Precision0mm4  DepthPrecision = 2
	Precision0mm1  DepthPrecision = 3
	Precision0mm2  DepthPrecision = 4
	Precision0mm5  DepthPrecision = 5
	Precision0mm05 DepthPrecision = 6

	// PrecisionKeep 表示不改相机当前档位。
	PrecisionKeep DepthPrecision = -1
)

func (DepthPrecision) Unit

func (p DepthPrecision) Unit() (float32, error)

Unit 返回该档位的深度单位(mm)。

type Device

type Device struct {
	// contains filtered or unexported fields
}

Device 是一台已连接的相机。方法可以并发调用;Close 后其它方法返回 ErrClosed。

func OpenDevice

func OpenDevice(ctx context.Context, opts DeviceOptions) (*Device, error)

OpenDevice 连接相机并初始化,顺序和官方 SDK 一致:申请控制权 → 设通信方式和兼容版本 → 读版本、标定和有效分辨率 → 开相机端视差转深度、设深度精度。ctx 只管打开过程。

func (*Device) Calibration

func (d *Device) Calibration() *Calibration

Calibration 返回从相机读出的标定(只读)。

func (*Device) Close

func (d *Device) Close() error

Close 关掉这台设备起的所有流、释放控制权并断开,可重复调用。

func (*Device) ControlLost

func (d *Device) ControlLost() bool

ControlLost 表示控制权被别的程序拿走了。

func (*Device) DepthUnitMM

func (d *Device) DepthUnitMM() float32

DepthUnitMM 返回当前深度单位(mm)。

func (*Device) GetProperty

func (d *Device) GetProperty(id uint32) (int32, error)

GetProperty 读一个整型/布尔属性的当前值。

func (*Device) GetPropertyRange

func (d *Device) GetPropertyRange(id uint32) (PropertyRange, error)

GetPropertyRange 读一个属性的当前值和范围。

func (*Device) Info

func (d *Device) Info() DeviceInfo

func (*Device) Profiles

func (d *Device) Profiles() []EffectiveProfile

Profiles 返回相机报告的有效流配置(副本)。

func (*Device) SetProperty

func (d *Device) SetProperty(id uint32, v int32) error

SetProperty 写一个整型/布尔属性。改深度精度会更新 DepthUnitMM,但已在收的深度流仍按旧单位换算, 要先关流再改。

func (*Device) StartColor

func (d *Device) StartColor(ctx context.Context, cfg StreamConfig, onFrame func(*ColorFrame)) (*Stream, error)

StartColor 开彩色流(MJPEG),onFrame 收到完整 JPEG。约束同 StartDepth。

func (*Device) StartDepth

func (d *Device) StartDepth(ctx context.Context, cfg StreamConfig, onFrame func(*DepthFrame)) (*Stream, error)

StartDepth 开深度流(Y16),onFrame 在读循环里调用:不能阻塞,也不能调用 Close。ctx 只管起流过程。

func (*Device) Version

func (d *Device) Version() VersionInfo

type DeviceError

type DeviceError struct {
	PropertyID uint32
	Opcode     uint16
	Code       uint16
	Msg        string
}

DeviceError 是相机对命令返回的错误码。

func (*DeviceError) Error

func (e *DeviceError) Error() string

type DeviceInfo

type DeviceInfo struct {
	Name       string `json:"name"`
	Serial     string `json:"serial"`
	Firmware   string `json:"firmware"`
	Hardware   string `json:"hardware,omitempty"`
	Connection string `json:"connection"`
	PID        uint16 `json:"pid,omitempty"`
	IP         string `json:"ip,omitempty"`
	MAC        string `json:"mac,omitempty"`
}

DeviceInfo 是设备信息。

type DeviceOptions

type DeviceOptions struct {
	IP     string
	Logger *slog.Logger
	// ReadOnly 不申请控制权、不写属性,可以和别的程序同时连着,但不能取流。
	ReadOnly bool
	// DepthPrecision 为 nil 时设成相机默认档(和官方 SDK 一致),PrecisionKeep 保持当前档位。
	DepthPrecision *DepthPrecision

	// 端口,0 用默认值。
	GVCPPort, ControlPort, DepthRTSPPort, ColorRTSPPort int

	// FrameIdleTimeout 内收不到流数据就认为流断了,默认 10 s。
	// 另一个客户端断开同一路流时相机会停发约 5 s,所以别设得太短。
	FrameIdleTimeout time.Duration
}

DeviceOptions 是打开设备的参数。

type DiscoveredDevice

type DiscoveredDevice struct {
	IP           string
	Mask         string
	Gateway      string
	MAC          string
	Manufacturer string
	Model        string
	PID          uint16
	Firmware     string
	Serial       string
	UserName     string
	LocalIP      string // 收到应答的本机地址
}

DiscoveredDevice 是 GVCP 发现应答里的设备信息。

func Discover

func Discover(ctx context.Context, wait time.Duration) ([]DiscoveredDevice, error)

Discover 在每块 IPv4 网卡的子网上广播发现命令,返回 wait 内收到应答的设备。

type EffectiveProfile

type EffectiveProfile struct {
	Sensor, Format        uint32
	Width, Height, MaxFPS int
}

EffectiveProfile 是相机报告的一个可用流配置。

func (EffectiveProfile) String

func (p EffectiveProfile) String() string

type Extrinsic

type Extrinsic struct {
	R [9]float64 `json:"R"`
	T [3]float64 `json:"t"`
}

Extrinsic 是刚体变换 p_dst = R·p_src + T,R 按行存储,T 单位 mm。

type FrameMetadata

type FrameMetadata struct {
	FrameCounter      uint32
	SensorTimestampUs int64 // 曝光起始时间
	Exposure          uint32
	DigitalGain       uint16
	AnalogGain        uint16
	AutoExposure      bool

	// 仅深度帧
	Width, Height   uint16
	GainLevel       uint16
	LaserPower      uint16
	LaserPowerLevel uint8
	LaserStatus     uint8
	ActualFPS       uint8
}

FrameMetadata 是每帧 108 字节元数据里的常用字段。

func ParseFrameMetadata

func ParseFrameMetadata(b []byte, depth bool) (FrameMetadata, error)

ParseFrameMetadata 解析帧元数据,depth 为 true 时按深度帧布局解析。

type Intrinsics

type Intrinsics struct {
	Width  int     `json:"width"`
	Height int     `json:"height"`
	Fx     float64 `json:"fx"`
	Fy     float64 `json:"fy"`
	Cx     float64 `json:"cx"`
	Cy     float64 `json:"cy"`
}

Intrinsics 是针孔相机内参。

type PropertyRange

type PropertyRange struct {
	Cur, Max, Min, Def, Step int32
}

PropertyRange 是属性的当前值和范围,布尔和浮点属性也按 int32 传输。

type Stats added in v0.1.1

type Stats struct {
	DepthFrames, DepthDropped int64
	ColorFrames, ColorDropped int64
	DepthDeviceSeq            uint64
}

HasColor 表示 AliveTimeout 内收到过彩色帧。 Stats 是流的累计统计:收到的帧数、因 RTP 丢包而丢弃的帧数,以及最近一帧深度的相机帧号 (相机帧号的增量减去收到的帧数 = 相机发了但没收全的帧)。

type Stream

type Stream struct {
	// contains filtered or unexported fields
}

Stream 是一路正在接收的流。

func (*Stream) Close

func (s *Stream) Close()

Close 结束会话并等读循环退出,可重复调用。不能在 onFrame 回调里调用(回调在读循环上执行,会死锁)。

func (*Stream) Done

func (s *Stream) Done() <-chan struct{}

Done 在读循环退出后关闭;此时连接已关,Err 给出原因(正常 Close 时为 nil)。

func (*Stream) Err

func (s *Stream) Err() error

Err 返回流异常结束的原因。

func (*Stream) Stats

func (s *Stream) Stats() (frames, dropped int64)

Stats 返回收到的帧数和丢弃的帧数。

type StreamConfig

type StreamConfig struct {
	Width, Height, FPS int
}

StreamConfig 是一路流的分辨率和帧率。

type VersionInfo

type VersionInfo struct {
	Firmware, Hardware, MinSDK, DepthChip, SystemChip, Serial string
	DeviceType                                                int32
	DeviceName, SubSystem                                     string
}

VersionInfo 是设备版本信息(OBVersionInfo)。

Directories

Path Synopsis
cmd
obprobe command
Command obprobe inspects a Gemini 435Le camera.
Command obprobe inspects a Gemini 435Le camera.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL