网络摄像头走的是网络协议,不是设备接口——直接写 URL 就行。 它的坑不在「名字」,在「时间」:断流、卡住、超时。
9.2 采的是插在机器上的设备,这一节换成挂在网上的摄像头。 第八章讲过的那套 RTSP/HTTP,在这里原样复用。
一、两类摄像头,两套接法
这件事最容易被搞混:以为网络摄像头也能用 -f dshow。
类型 怎么接 用什么
USB 摄像头 插在机器上 dshow/vfwcap/v4l2 网络摄像头 网线或 Wi-Fi RTSP/HTTP/RTMP 手机当摄像头 局域网 RTSP 或 HTTP-FLV
后两类根本不用设备接口,直接把地址写在 -i 后面。
因为对 FFmpeg 来说,它就是「一条流」——
跟第八章从 RTSP 拉一路监控画面,是同一件事。
所以真正多出来的只有一件事:网络会断。 本地设备不会「拔了就重连」,网络源却必须考虑这个。
二、实测:拉一路流再转存
起一个 HTTP 服务,把 s7.mp4 当成网络源,拉下来转存成 mp4。
项 值
录到帧数 50 成片字节 30336 耗时 123 ms
50 帧一口气取完只花 123 毫秒——因为源是文件,不是活的摄像头。 真网络摄像头会按帧率一点点给你,一秒只给 25 帧,快也快不了。
这个差别很重要:调试时用文件源会显得「特别顺」, 一换真摄像头就发现处处卡——不是代码退化了,是节奏变了。
三、网络源必须设超时
这一类直播源最典型的故障不是报错,是「卡住不动」。 TCP 还连着、只是没数据,客户端就一直等—— 没有超时参数,进程能挂到天荒地老。
参数 作用 不写会怎样
-rtsp_transport tcp RTSP 改走 TCP UDP 丢包直接花屏 -timeout 5000000 读超时(微秒) 对面不发就永远卡住 -reconnect 1 断了自动重连 一断就退出 -rw_timeout 读写都设超时 同上
-timeout 的单位是微秒,写 5000000 才是 5 秒。
这一条最容易写错——写成 5 就是 5 微秒,等于没设。
-rtsp_transport tcp 是为了避开 UDP 丢包。
第八章量过:UDP 丢几个包就能让整条流解不出来,
而 RTSP 默认是 UDP——所以拉监控时这条几乎必加。
四、本地设备与网络源差在哪
把两者的差别列清楚,排查时就不会乱试。
对比项 本地设备 网络源
打开方式 -f dshow -i video=x 直接写 URL
帧从哪来 设备按帧率吐 网络按带宽来
典型故障 设备被占用 断流、卡住
必须加的 设备名要写全 超时与重连
一句话总结:本地设备的坑在「名字」,网络源的坑在「时间」。 名字写错立刻报错,时间出问题却悄无声息——所以后者更难查。
调试顺序也顺着这个来:本地设备先确认设备名, 网络源先确认「端口通不通、有没有数据」。
五、动手 Demo:起个服务当网络摄像头,拉下来转存
下面这段用 HTTP 把 s7.mp4 挂出去,当成一路网络源。 然后拉下来、一帧帧重编码,写成一个新的 mp4。
import http.server, os, tempfile, threading, time
from fractions import Fraction
import av
d = tempfile.mkdtemp()
class H(http.server.SimpleHTTPRequestHandler):
def __init__(self, *a, **kw):
http.server.SimpleHTTPRequestHandler.__init__(
self, *a, directory=os.getcwd(), **kw)
def log_message(self, *a):
pass
srv = http.server.ThreadingHTTPServer(("127.0.0.1", 0), H)
port = srv.server_address[1]
threading.Thread(target=srv.serve_forever, daemon=True).start()
url = "http://127.0.0.1:%d/s7.mp4" % port
out = os.path.join(d, "net.mp4")
t0 = time.time()
c = av.open(url, options={"timeout": "5000000"})
o = av.open(out, "w")
v = o.add_stream("libx264", rate=Fraction(25, 1))
v.width, v.height, v.pix_fmt = 160, 90, "yuv420p"
v.bit_rate = 400000
n = 0
for f in c.decode(c.streams.video[0]):
g = f.reformat(width=160, height=90, format="yuv420p")
g.pts = n
g.time_base = Fraction(1, 25)
for p in v.encode(g):
o.mux(p)
n += 1
for p in v.encode(None):
o.mux(p)
o.close()
c.close()
srv.shutdown()
srv.server_close()
print(u"录到 %d 帧 %d 字节 耗时 %.0f ms"
% (n, os.path.getsize(out), (time.time() - t0) * 1000))
实跑输出:
--------------------------------------------------------------------
9.3 FFmpeg 采集网络摄像头获取的数据并录制
--------------------------------------------------------------------
**① 网络摄像头走的是网络协议,不是设备接口**
类型 怎么接 用什么
---------------------------------------------------------
USB 摄像头 插在机器上 dshow/vfwcap/v4l2
网络摄像头 网线或 Wi-Fi RTSP/HTTP/RTMP
手机当摄像头 局域网 RTSP 或 HTTP-FLV
**所以这一类根本不用 `-f dshow`,直接写 URL 就行。**
**第八章讲的 RTSP/HTTP 那一套,在这里原样复用。**
**唯一多出来的是「断流怎么办」这件事。**
**② 实测:从 HTTP 拉一路流,转存成 mp4**
项 值
----------------------------------------
源 http://127.0.0.1/s7.mp4
录到帧数 50
成片字节 30336
耗时 530 ms
**注意这次是「拉完再录」,50 帧一口气取完只花 530 毫秒——**
**因为源是文件,不是活的摄像头。真网络摄像头会按帧率一点点给你。**
**③ 网络源必须设超时,否则会一直挂着**
参数 作用 不写会怎样
-----------------------------------------------------------------
-rtsp_transport tcp RTSP 改走 TCP UDP 丢包直接花屏
-timeout 5000000 读超时(微秒) 对面不发就永远卡住
-reconnect 1 断了自动重连 一断就退出
-rw_timeout 读写都设超时 同上
**这一类直播源最典型的故障是「卡住不动」,不是报错。**
**因为 TCP 还连着、只是没数据,客户端就一直等——**
**没有超时参数,进程能挂到天荒地老。**
**④ 本地设备与网络源,差在哪**
对比项 本地设备 网络源
-------------------------------------------------------
打开方式 `-f dshow -i video=x` 直接写 URL
帧从哪来 设备按帧率吐 网络按带宽来
典型故障 设备被占用 断流、卡住
必须加的 设备名要写全 超时与重连
**一句话总结:本地设备的坑在「名字」,网络源的坑在「时间」。**
50 帧 30336 字节、123 毫秒跑完——因为源是文件,不是活摄像头。
options={"timeout": "5000000"} 那 5 秒是给「对面不发数据」准备的,
文件源永远用不上它;真摄像头断流时,就是它把进程救出来。
30 秒实验
- 把
timeout改成5:体会一下单位写错的后果(几乎立刻超时)。 - 把 URL 改成不存在的文件:看报错是什么,跟「卡住」有什么区别。
- 在循环里加
time.sleep(0.04):模拟真摄像头每秒 25 帧的节奏。
常见误区
- 拿网络摄像头去配
-f dshow。它走网络协议,直接写 URL。 - 不设超时就拉流。对面不发数据会一直挂着,不报错。
- 把
-timeout当成秒。单位是微秒,写 5 等于没设。
自测三问(点开对答案)
1. 网络摄像头该用什么接口? —— 不用设备接口,直接把 RTSP/HTTP/RTMP 地址写在 -i 后面。 2. 网络源为什么不设超时会卡死? —— TCP 还连着只是没数据,客户端会一直等,不会自己报错退出。 3. `-rtsp_transport tcp` 是为了解决什么? —— 避开 UDP 丢包,第八章量过丢几个包就能让整条流解不出来。- ☐ 9.3 完,下一节 9.4 FFmpeg 采集摄像头与话筒获取的数据并直播