You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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 SETUP and PLAY requests, 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 correct realm, nonce, and calculated response value for every request (Describe → Setup → Play all need valid auth headers).
  • Session ID Consistency: The Session header returned in the SETUP response must be included in your PLAY request. Without this, the server won't associate your play command with the established stream.
  • RTP-Info in PLAY: Did you include the RTP-Info header from the server's PLAY response in your request? Some servers require this metadata to start streaming correctly.

2. Check UDP Port & Transport Details

  • Port Matching: In your SETUP request, you specified client ports 1212-1213 (RTP on 1212, RTCP on 1213). But some RTSP servers ignore the client's requested ports and assign their own. Check the Transport header in the SETUP response—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 udp instead 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_packet function 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, and cv2.imdecode will 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, so imdecode won'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 pyav or rtspclient—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

火山引擎 最新活动