Python Socket接收RTP数据包实现RTSP视频流(Digest认证)无数据问题排查及代码咨询
Python Socket接收RTP数据包实现RTSP视频流(Digest认证)无数据问题排查及代码咨询
Hey there, let's work through this problem step by step. Handling RTSP manually with Digest auth instead of relying on OpenCV's built-in methods can be tricky, so let's break down what might be going wrong and how to fix your code.
First, Let's Troubleshoot the "No RTP Packets" Issue
Here are the key checks you should run first:
1. Verify Your RTSP Request Flow & Digest Auth
- Digest Auth Validation: Even if you got a response to your
SETUPandPLAYrequests, double-check that the Digest auth header was correctly generated for each request. Servers often silently reject invalid auth by not sending RTP packets, even if they return a 200 OK. Make sure you're using the correctrealm,nonce, and calculatedresponsevalue for every request (Describe → Setup → Play all need valid auth headers). - Session ID Consistency: The
Sessionheader returned in theSETUPresponse must be included in yourPLAYrequest. Without this, the server won't associate your play command with the established stream. - RTP-Info in PLAY: Did you include the
RTP-Infoheader from the server'sPLAYresponse in your request? Some servers require this metadata to start streaming correctly.
2. Check UDP Port & Transport Details
- Port Matching: In your
SETUPrequest, you specified client ports1212-1213(RTP on 1212, RTCP on 1213). But some RTSP servers ignore the client's requested ports and assign their own. Check theTransportheader in theSETUPresponse—if it lists different ports, you need to bind your UDP socket to those ports instead of 1212! - Firewall/Network Block: Ensure your local firewall isn't blocking incoming UDP traffic on port 1212. Try temporarily disabling it, or switch to a more common port range like
5000-5001. - Wireshark Filter: Use a broader filter like
udpinstead of just targeting port 1212. Sometimes servers send RTP to a different IP or port than you expected. Also, check if you're receiving any RTCP packets (on 1213)—if you get RTCP but no RTP, the server might be having issues with your stream request.
3. RTP Packet Parsing & Payload Handling
- RTP Header Parsing: Your
parse_rtp_packetfunction must correctly strip the 12-byte RTP header (version, payload type, sequence number, timestamp, SSRC). If you're not stripping this correctly, your payload will be garbage, andcv2.imdecodewill fail (making you think no packets are coming in). - Payload Type & Framing: Most IP cameras send H.264 over RTP, which uses fragmented NAL units. You can't just pass a single RTP payload to
cv2.imdecode—you need to collect all fragments of a single frame, reassemble the full NAL unit, then decode it. JPEG over RTP is rare, soimdecodewon't work for H.264 out of the box.
Code Adjustments to Fix the Issue
Here's an updated version of your code with critical fixes and additions:
1. First, Add Proper Digest Auth for RTSP Requests
You'll need a function to generate the Digest auth header. Here's a quick implementation:
import hashlib import re def generate_digest_auth_header(username, password, realm, nonce, method, uri): # Calculate HA1 and HA2 for Digest auth ha1 = hashlib.md5(f"{username}:{realm}:{password}".encode()).hexdigest() ha2 = hashlib.md5(f"{method}:{uri}".encode()).hexdigest() response = hashlib.md5(f"{ha1}:{nonce}:{ha2}".encode()).hexdigest() return f'Digest username="{username}", realm="{realm}", nonce="{nonce}", uri="{uri}", response="{response}"'
2. Verify Setup Response & Bind Correct UDP Port
When you send the SETUP request, parse the response to get the actual RTP port the server is using:
# After sending SETUP request and getting response setup_response = ... # Your code to receive the response transport_match = re.search(r'transport:.+client_port=(\d+)-(\d+);server_port=(\d+)-(\d+)', setup_response, re.IGNORECASE) if transport_match: # Check if server uses your requested client port or its own server port # Adjust based on your actual response rtp_port = int(transport_match.group(1)) else: print("Failed to parse Transport header from SETUP response") exit(1)
3. Updated RTP Receiving & Frame Reassembly (for H.264)
This example adds basic H.264 fragment reassembly:
import socket import cv2 import numpy as np # Initialize variables for H.264 reassembly current_frame_buffer = b'' expected_seq = None def parse_rtp_packet(packet): # RTP header is 12 bytes: version(2), padding(1), extension(1), CSRC count(4) # Marker(1), Payload Type(7), Sequence Number(16), Timestamp(32), SSRC(32) version = (packet[0] >> 6) & 0x03 if version != 2: return None, None, None marker = (packet[1] >> 7) & 0x01 seq_num = int.from_bytes(packet[2:4], byteorder='big') payload = packet[12:] return seq_num, marker, payload sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.bind(("0.0.0.0", 1212)) # Adjust this port if server uses a different one print("RTP stream receive...") while True: packet, addr = sock.recvfrom(65536) seq_num, marker, payload = parse_rtp_packet(packet) if payload is None: continue # Handle H.264 fragment reassembly if expected_seq is None: expected_seq = seq_num if seq_num == expected_seq: current_frame_buffer += payload expected_seq += 1 # Marker bit set means end of frame if marker: # Note: cv2.imdecode doesn't handle raw H.264 directly # For proper decoding, use libraries like pyav or ffmpeg # For testing, write to a file and play with ffplay with open("stream.h264", "ab") as f: f.write(current_frame_buffer) current_frame_buffer = b'' # For display, you'll need a H.264 decoder (example using pyav): # import av # container = av.open("stream.h264", format="h264") # for frame in container.decode(video=0): # cv2.imshow('RTSP Frame', frame.to_ndarray(format='bgr24')) if cv2.waitKey(1) & 0xFF == ord('q'): break sock.close() cv2.destroyAllWindows()
Final Tips
- Use a RTSP Library: If you don't need to implement RTSP from scratch, consider using libraries like
pyavorrtspclient—they handle Digest auth and RTP streaming out of the box, saving you a lot of headache. - RTCP Feedback: Some servers require RTCP "Receiver Report" packets to keep the stream alive. Bind a UDP socket to the RTCP port (1213) and send empty RTCP packets periodically, or at least listen for them.
备注:内容来源于stack exchange,提问作者Come Oh




