所以,在经过大量的谷歌搜索和wireshark分析后,我找到了正确的解决方案。我在这里发布了结果演示代码...我认为它可能对社区有用。如果您想使用Python读取IP摄像头并将H264流转储到可读文件中,那么这就是您要寻找的东西。享受吧!
"""
A demo python code that ..
1) Connects to an IP cam with RTSP
2) Draws RTP/NAL/H264 packets from the camera
3) Writes them to a file that can be read with any stock video player (say, mplayer, vlc & other ffmpeg based video-players)
Done for educative/demonstrative purposes, not for efficiency..!
written 2015 by Sampsa Riikonen.
"""
import socket
import re
import bitstring
ip="192.168.1.74"
adr="rtsp://admin:12345@192.168.1.74"
clientports=[60784,60785]
fname="stream.h264"
rn=5000
dest="DESCRIBE "+adr+" RTSP/1.0\r\nCSeq: 2\r\nUser-Agent: python\r\nAccept: application/sdp\r\n\r\n"
setu="SETUP "+adr+"/trackID=1 RTSP/1.0\r\nCSeq: 3\r\nUser-Agent: python\r\nTransport: RTP/AVP;unicast;client_port="+str(clientports[0])+"-"+str(clientports[1])+"\r\n\r\n"
play="PLAY "+adr+" RTSP/1.0\r\nCSeq: 5\r\nUser-Agent: python\r\nSession: SESID\r\nRange: npt=0.000-\r\n\r\n"
def getPorts(searchst,st):
""" Searching port numbers from rtsp strings using regular expressions
"""
pat=re.compile(searchst+"=\d*-\d*")
pat2=re.compile('\d+')
mstring=pat.findall(st)[0]
nums=pat2.findall(mstring)
numas=[]
for num in nums:
numas.append(int(num))
return numas
def getLength(st):
""" Searching "content-length" from rtsp strings using regular expressions
"""
pat=re.compile("Content-Length: \d*")
pat2=re.compile('\d+')
mstring=pat.findall(st)[0]
num=int(pat2.findall(mstring)[0])
return num
def printrec(recst):
""" Pretty-printing rtsp strings
"""
recs=recst.split('\r\n')
for rec in recs:
print rec
def sessionid(recst):
""" Search session id from rtsp strings
"""
recs=recst.split('\r\n')
for rec in recs:
ss=rec.split()
if (ss[0].strip()=="Session:"):
return int(ss[1].split(";")[0].strip())
def setsesid(recst,idn):
""" Sets session id in an rtsp string
"""
return recst.replace("SESID",str(idn))
def digestpacket(st):
""" This routine takes a UDP packet, i.e. a string of bytes and ..
(a) strips off the RTP header
(b) adds NAL "stamps" to the packets, so that they are recognized as NAL's
(c) Concantenates frames
(d) Returns a packet that can be written to disk as such and that is recognized by stock media players as h264 stream
"""
startbytes="\x00\x00\x00\x01"
bt=bitstring.BitArray(bytes=st)
lc=12
bc=12*8
version=bt[0:2].uint
p=bt[3]
x=bt[4]
cc=bt[4:8].uint
m=bt[9]
pt=bt[9:16].uint
sn=bt[16:32].uint
timestamp=bt[32:64].uint
ssrc=bt[64:96].uint
lc=12
bc=12*8
print "version, p, x, cc, m, pt",version,p,x,cc,m,pt
print "sequence number, timestamp",sn,timestamp
print "sync. source identifier",ssrc
cids=[]
for i in range(cc):
cids.append(bt[bc:bc+32].uint)
bc+=32; lc+=4;
print "csrc identifiers:",cids
if (x):
hid=bt[bc:bc+16].uint
bc+=16; lc+=2;
hlen=bt[bc:bc+16].uint
bc+=16; lc+=2;
print "ext. header id, header len",hid,hlen
hst=bt[bc:bc+32*hlen]
bc+=32*hlen; lc+=4*hlen;
"""
5.3. NAL Unit Header Usage
The structure and semantics of the NAL unit header were introduced in
Section 1.3. For convenience, the format of the NAL unit header is
reprinted below:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
This section specifies the semantics of F and NRI according to this
specification.
"""
"""
Table 3. Summary of allowed NAL unit types for each packetization
mode (yes = allowed, no = disallowed, ig = ignore)
Payload Packet Single NAL Non-Interleaved Interleaved
Type Type Unit Mode Mode Mode
-------------------------------------------------------------
0 reserved ig ig ig
1-23 NAL unit yes yes no
24 STAP-A no yes no
25 STAP-B no no yes
26 MTAP16 no no yes
27 MTAP24 no no yes
28 FU-A no yes yes
29 FU-B no no yes
30-31 reserved ig ig ig
"""
"""
First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS]
Second byte: [ START BIT | RESERVED BIT | END BIT | 5 NAL UNIT BITS]
Other bytes: [... VIDEO FRAGMENT DATA...]
"""
fb=bt[bc]
nri=bt[bc+1:bc+3].uint
nlu0=bt[bc:bc+3]
typ=bt[bc+3:bc+8].uint
print "F, NRI, Type :", fb, nri, typ
print "first three bits together :",bt[bc:bc+3]
if (typ==7 or typ==8):
if (typ==7):
print ">>>>> SPS packet"
else:
print ">>>>> PPS packet"
return startbytes+st[lc:]
bc+=8; lc+=1;
start=bt[bc]
end=bt[bc+2]
nlu1=bt[bc+3:bc+8]
if (start):
print ">>> first fragment found"
nlu=nlu0+nlu1
head=startbytes+nlu.bytes
lc+=1
if (start==False and end==False):
head=""
lc+=1
elif (end==True):
head=""
print "<<<< last fragment found"
lc+=1
if (typ==28):
return head+st[lc:]
else:
raise(Exception,"unknown frame type for this piece of s***")
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip,554))
print
print "*** SENDING DESCRIBE ***"
print
s.send(dest)
recst=s.recv(4096)
print
print "*** GOT ****"
print
printrec(recst)
print
print "*** SENDING SETUP ***"
print
s.send(setu)
recst=s.recv(4096)
print
print "*** GOT ****"
print
printrec(recst)
idn=sessionid(recst)
serverports=getPorts("server_port",recst)
clientports=getPorts("client_port",recst)
print "****"
print "ip,serverports",ip,serverports
print "****"
s1=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s1.bind(("", clientports[0]))
s1.settimeout(5)
print
print "*** SENDING PLAY ***"
print
play=setsesid(play,idn)
s.send(play)
recst=s.recv(4096)
print
print "*** GOT ****"
print
printrec(recst)
print
print
print "** STRIPPING RTP INFO AND DUMPING INTO FILE **"
f=open(fname,'w')
for i in range(rn):
print
print
recst=s1.recv(4096)
print "read",len(recst),"bytes"
st=digestpacket(recst)
print "dumping",len(st),"bytes"
f.write(st)
f.close()
s.close()
s1.close()