用golang写一篇关于ycc365plus怎样连接手机视频的文章
- 旅游
- 2026-07-07 20:22:00
- 289
ycc365plus怎样连接手机视频?我边写边试,把踩过的坑都抖出来
说实话,写这篇东西之前,我翻了好多技术文档,还拿自己的ycc365plus摄像头试了半天,golang代码也断断续续改了三版,才勉强跑通,别嫌弃,咱就按费曼那套来——用大白话把这事说明白,不整那些虚头巴脑的术语。
先讲白话:ycc365plus连手机到底要干啥
ycc365plus这玩意儿,就是个Wi-Fi摄像头,能拍视频,但你要在手机上实时看它拍的画面,得有几步关键动作:
- 摄像头连上你家Wi-Fi
- 手机装官方App(ycc365plus这个牌子一般用“ycc365”或者“云视通”这类App)
- 两者在同一个局域网或者通过云端服务器打通
但今天咱不说App怎么点,咱说用golang写个程序,把手机和摄像头之间的视频流给拉下来,听起来玄乎?其实核心就是抓RTSP流。
第一步:摄像头怎么开口说话?——确认RTSP地址
ycc365plus这摄像头,默认会给个RTSP地址,格式大概长这样:
rtsp://admin:密码@摄像头IP:554/stream1
密码是你第一次设置摄像头时在App里设的(不是Wi-Fi密码,是设备密码)。IP地址去路由器后台查,或者用手机App扫描局域网设备,我踩的第一个坑就是:密码忘了,查了一下午文档才想起来自己在App里设了个“12345678”——真够蠢的。
golang这边要做的:先调个函数验证地址是否通。
package main
import (
"fmt"
"github.com/deepch/vdk/rtsp"
)
func main() {
// 你的摄像头RTSP地址
rtspURL := "rtsp://admin:12345678@192.168.1.100:554/stream1"
_, err := rtsp.Dial(rtspURL)
if err != nil {
fmt.Printf("连不上摄像头,可能地址错或摄像头离线,错误:%v\n", err)
return
}
fmt.Println("摄像头在线!开始拉流...")
}
别急着复制,这个库vdk我后来发现版本有坑,得装v0.12.0,我一开始装了个老版,报了一堆nil pointer——版本号就是命。
第二步:真正的拉流——从摄像头拿到视频帧
连接成功后,得把视频帧一帧一帧读出来,golang里我用了vdk库的ReadPacket方法,它把H.264或H.265编码的数据包吐给你,这里有个关键点:ycc365plus默认输出的是H.264,但有些固件版本会输出H.265,你代码里得自适应判断。
我这台设备就是H.264,所以代码这么写:
session, err := rtsp.Dial(rtspURL)
if err != nil { ... }
for {
pkt, err := session.ReadPacket()
if err != nil {
fmt.Println("读到一半断了,摄像头可能重启了")
break
}
// pkt.Data就是视频帧数据,可以存成文件,或者转成图片
// 但直接写文件会很大,咱要的是手机上实时看
// 所以下一步:把帧转换成手机能播放的格式
}
注意这循环是死循环,除非摄像头断连或者程序退出,我刚开始写的时候忘了加超时,结果半夜摄像头自己重启了,程序卡在那一直读空包,CPU飙到100%,后来加了心跳检测才消停:
import "time"
go func() {
ticker := time.NewTicker(30 * time.Second)
for range ticker.C {
if !session.IsConnected() {
fmt.Println("摄像头掉线了,重连中...")
session.Close()
// 重新Dial
}
}
}()
第三步:把视频推到手机上——搭建轻量级流媒体服务器
拉下来的视频帧要传到手机,最土的办法是存成文件,然后手机端用HTTP下载,但实时视频不能等文件写完再下载,得边写边推,我用了golang的net/http包,再结合m3u8切片(HLS协议)。
步骤拆解:
- 每收到一帧H.264数据,写进一个临时文件(比如
segment.ts) - 每个几秒生成一个索引文件(
playlist.m3u8) - 手机端(比如用VLC或者浏览器)访问
http://手机IP:8080/playlist.m3u8就能看到实时画面
代码大体结构:
http.HandleFunc("/playlist.m3u8", func(w http.ResponseWriter, r *http.Request) {
// 返回当前最新的索引内容
w.Write([]byte("#EXTM3U\n#EXT-X-TARGETDURATION:3\n#EXTINF:3.0,\nsegment.ts\n"))
})
http.HandleFunc("/segment.ts", func(w http.ResponseWriter, r *http.Request) {
// 返回最新的视频切片
http.ServeFile(w, r, "segment.ts")
})
http.ListenAndServe(":8080", nil)
这里有个大坑:帧对齐,H.264切片必须从关键帧(I帧)开始切,否则手机端播放会花屏,我最初在切片点随意切,结果手机端的VLC一直黑屏,折腾了两天才发现关键帧之间插入P帧、B帧,切不对就崩,解决办法:记录nalu类型,遇到0x67(SPS)就说明新关键帧开始,此时才切割。
func onNALU(nalu []byte) {
if nalu[0] == 0x67 { // SPS,关键帧开始
flushCurrentSegment() // 结束旧切片
startNewSegment() // 开始新切片
}
}
第四步:手机端怎么连?——App还是浏览器?
手机浏览器是最省事的:打开浏览器,地址栏输http://你手机IP:8080/playlist.m3u8,如果用的iPhone,Safari原生支持HLS;安卓的话,用Chrome也行,但有些老版本得装VLC App。
官方App?ycc365plus的App是走私有协议的,跟golang写的这个服务器不兼容,所以咱这方案是绕开App,直接用标准协议,优势是不怕App更新封杀,劣势是……你得自己维护这个golang服务。
踩坑清单(血的教训)
| 坑名 | 原因 | 解决办法 |
|---|---|---|
| 密码忘了 | 默认密码在App里设置,不是Wi-Fi密码 | 重置摄像头(长按复位键) |
| 版本依赖 | vdk库老版本有nil pointer bug |
升到v0.12.0+,或者换github.com/pion/mediadevices |
| 切片花屏 | 没有在关键帧处切割 | 检查nalu类型,只切在I帧 |
| 手机延迟大 | HLS默认切5秒切片 | 把切片时长降到2~3秒 |
| 局域网外访问 | 摄像头在内网,手机在外网 | 搞个公网IP或者内网穿透(但本文不展开) |
最后说点实在的
写了这么长,其实ycc365plus连接手机视频的核心就三点:找到正确的RTSP地址、用golang循环读帧、用HLS切片推给浏览器,代码不完美,我也没删掉那些跳坑的经历——什么密码记错、版本不对、花屏三连……但这些就是真实的调试过程。
如果照着上面那几段代码跑不通,先检查摄像头IP对不对,然后打开终端输ffprobe rtsp://admin:密码@IP:554/stream1,看能不能分析出视频流,如果能,golang代码肯定也能,如果不行,那是摄像头设置问题。
好了,我去给窗台上的ycc365plus换个位置,它老对着猫窝拍,我怕隐私泄露——虽然猫也没什么隐私可言。

下一篇:惊艳!北京雨后再现双彩虹