[live555] testRTSPClient.cpp DESCRIBE (后续处理) 二

RTSP协议解析与MediaSession创建流程
本文详细解析了RTSP协议中的DESCRIBE、SETUP请求处理流程,包括如何根据SDP创建MediaSession,及RTPsocket的初始化过程。深入探讨了解析SDP获取编码方式,并创建相应解码器的机制。

摘要

DESCRIBE 请求收到返回SDP 就会根据SDP 创建rtp socket

轮询处理handleResponseBytes

在上一篇testRTSPClient中,已经走到

  handleResponseBytes(bytesRead);//处理reponse 数据

主要处理了
1. 解析返回的response 数据
2. 根据返回response, 后续进一步处理(即回调函数continueAfterDESCRIBE() 等)
3. 根据返回response,OK 就会进行下一步的请求发送 (DESCRIBE–>SETUP)

具体code:

void RTSPClient::handleResponseBytes(int newBytesRead) {

    //parse reponse code

    while ((request = fRequestsAwaitingResponse.dequeue()) != NULL) {
        //.... 判断是否是当前request
            if (request->cseq() == cseq) {
              foundRequest = request;
              break;
            }
    }

    //SETUP GET_PARAMETER TEAR_DOWN PLAY 请求reponse 处理 
    //......
    //返回成功回调
    if (responseSuccess) {
        if (responseCode == 200) {
          resultString = numBodyBytes > 0 ? strDup(bodyStart)
          :strDup(publicParamsStr);
        //...
        }
        //在DESCRIBE请求之后就是 continueAfterDESCRIBE回调
        (*foundRequest->handler())(this, resultCode, resultString);
    }

这里的cseq是根据RTSP 服务器返回的会话中提取
foundRequest通过fRequestsAwaitingResponse 队列获取,而在send 第一创建 request的时候,会放入队列中

unsigned RTSPClient::sendRequest(RequestRecord* request) {
//...
fRequestsAwaitingResponse.enqueue(request);
}

可以看出来RequestQueue 是一个单链表

void RTSPClient::RequestQueue::enqueue(RequestRecord* request) {
  if (fTail == NULL) {
    fHead = request;
  } else {
    fTail->next() = request;
  }
  fTail = request;
}

回调continueAfterDESCRIBE 函数

  1. 根据返回的DESCRIBE消息 ,本地创建MediaSession
  2. 发起SETUP请求
void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) {
  do {
    UsageEnvironment& env = rtspClient->envir(); // alias
    StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias
    char* const sdpDescription = resultString;

    // Create a media session object from this SDP description:
    scs.session = MediaSession::createNew(env, sdpDescription);

    // SETUP请求发起
    scs.iter = new MediaSubsessionIterator(*scs.session);
    setupNextSubsession(rtspClient);
    return;
  } while (0);

}

创建MediaSession

创建MediaSession,作用就是解析sdpDescription 消息,获取信息
用wireshark解析如下图
sdp
wireshark 解析log地址
上面标记了MIME Type H264 告诉客户端解码用H264

Boolean MediaSession::initializeWithSDP(char const* sdpDescription) {
      //....
          if (subsession->parseSDPAttribute_rtpmap(sdpLine)) continue;
    //....解析获取解码方式H264
}

setupNextSubsession 做了两件事情

  1. 创建socket 发送报文 即RTP
  2. 发起SETUP请求

void setupNextSubsession(RTSPClient* rtspClient) {
  UsageEnvironment& env = rtspClient->envir(); // alias
  StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias

  scs.subsession = scs.iter->next();
  if (scs.subsession != NULL) {
    if (!scs.subsession->initiate()) {//创建RTP socket
    //....
    } else {
      // Continue setting up this subsession, by sending a RTSP "SETUP" command:
      rtspClient->sendSetupCommand(*scs.subsession, continueAfterSETUP
      , False, REQUEST_STREAMING_OVER_TCP); 
    }
    return;
  }
}

创建 RTP socket
scs.subsession->initiate()

Boolean MediaSubsession::initiate(int useSpecialRTPoffset) {
    //创建RTP socket
    fRTPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, fClientPortNum);
    //创建 RTCP socket
    fRTCPSocket = new Groupsock(env(), tempAddr, fSourceFilterAddr, rtcpPortNum);

    // Try to use a big receive buffer for RTP - at least 0.1 second of
    // specified bandwidth and at least 50 KB
    unsigned rtpBufSize = fBandwidth * 25 / 2; // 1 kbps * 0.1 s = 12.5 bytes
    if (rtpBufSize < 50 * 1024){
      rtpBufSize = 50 * 1024;
      increaseReceiveBufferTo(env(), fRTPSocket->socketNum(), rtpBufSize);
    }


    // Create "fRTPSource" and "fReadSource":
    if (!createSourceObjects(useSpecialRTPoffset)) break;
}

需要根据解析的解码方式创建不同的解码

Boolean MediaSubsession::createSourceObjects(int useSpecialRTPoffset) {

    if (strcmp(fCodecName, "H264") == 0) {
        fReadSource = fRTPSource
          = H264VideoRTPSource::createNew(env(), fRTPSocket,//根据socket 作为数据入口
                          fRTPPayloadFormat,
                          fRTPTimestampFrequency);
    }

}

然后就是SETUP发起流程


<think>我们正在处理用户关于testRTSPClient.cpp文件中RTSP ANNOUNCE方法返回405 Method Not Allowed错误的问题。 根据提供的引用和用户的问题,我们需要分析RTSP协议中ANNOUNCE方法的使用以及返回405错误的原因。 引用[2]中提到了RTSP服务器支持的公共方法:OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER。注意,这里没有ANNOUNCE方法。 引用[3]展示了一个构造ANNOUNCE请求的代码片段,说明用户可能在尝试使用ANNOUNCE方法。 405错误表示方法不被允许(Method Not Allowed)。因此,可能的原因是服务器不支持ANNOUNCE方法。 解决方案思路: 1. 确认服务器是否支持ANNOUNCE方法:可以通过发送OPTIONS请求来获取服务器支持的方法列表(如引用[2]所示)。 2. 如果服务器不支持ANNOUNCE,则需要检查服务器配置或更换支持ANNOUNCE的服务器。 3. 如果服务器支持,则检查客户端代码中ANNOUNCE请求的构造是否正确(如Session头、CSeq头、Content-Type和Content-Length等是否正确设置)。 由于用户提到testRTSPClient.cpp,这可能是Live555库中的测试程序。在Live555中,testRTSPClient默认可能不支持ANNOUNCE方法,需要检查代码中是否明确启用了ANNOUNCE。 具体步骤: 步骤1:使用OPTIONS请求验证服务器支持的方法 在testRTSPClient.cpp中,可以修改代码发送OPTIONS请求,然后检查响应中的"Public"头,确认是否包含ANNOUNCE。 步骤2:检查服务器配置 如果服务器不支持ANNOUNCE,可能需要配置服务器以支持它。例如,对于Live555媒体服务器,默认可能不支持ANNOUNCE。 步骤3:检查ANNOUNCE请求的构造 参考引用[3]的代码,确保请求格式正确。特别注意: - 请求行:ANNOUNCE [RTSP URL] RTSP/1.0 - 必须有Session头(在SETUP之后获得) - CSeq必须递增 - Content-Type和Content-Length必须正确设置(如果请求中包含SDP内容) 步骤4:在testRTSPClient.cpp中启用对ANNOUNCE的支持 默认情况下,testRTSPClient可能没有实现ANNOUNCE方法。需要查看代码中是否包含发送ANNOUNCE请求的部分。如果没有,需要添加。 例如,在testRTSPClient.cpp中,可以添加如下代码(注意:这只是一个示例,具体需要根据程序结构): ```cpp // 在适当的地方,比如在建立会话后 if (allowAnnounce) { // 假设有一个标志允许发送ANNOUNCE sendAnnounceCommand(); } ``` 步骤5:定义sendAnnounceCommand函数 这个函数应该构造ANNOUNCE请求并发送。构造请求的格式参考引用[3]。 步骤6:处理服务器响应 如果服务器返回405,则说明方法不被允许;如果返回200,则成功。 另外,注意RTSP协议中,ANNOUNCE方法的作用:客户端使用ANNOUNCE向服务器提供媒体描述(SDP),通常用于推流。因此,服务器必须被配置为接受推流。 总结:首先确认服务器支持ANNOUNCE方法,然后确保客户端正确构造了请求。如果服务器不支持,则需要配置服务器支持;如果支持,检查请求构造的细节。 由于用户的问题具体到testRTSPClient.cpp,我们可能需要修改该文件以支持ANNOUNCE请求,并确保请求格式正确。 下面是一个修改testRTSPClient.cpp以发送ANNOUNCE请求的示例步骤: 1. 在代码中找到发送其他请求(如DESCRIBE, SETUP等)的地方,添加发送ANNOUNCE请求的代码。 2. 构造ANNOUNCE请求,注意包含必要的头字段和SDP内容。 3. 确保在发送ANNOUNCE之前已经建立了会话(即已经发送了SETUP并获得了Session ID)。 如果用户遇到405错误,那么首要的检查点应该是服务器是否支持ANNOUNCE方法。 </think>针对 `testRTSPClient.cpp` 文件中 RTSP `ANNOUNCE` 方法返回 `405 Method Not Allowed` 错误的问题,以下是系统性的解决方案: ### 原因分析 1. **服务器不支持 ANNOUNCE 方法** 根据引用[2]的服务器响应,`Public` 头未列出 `ANNOUNCE`: `Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE...` [^2] ➠ 表明服务器未启用此方法(RTSP 规范中 `ANNOUNCE` 属于可选方法)。 2. **请求构造错误** 引用[3]显示 `ANNOUNCE` 需严格包含以下字段[^3]: - 有效的 `Session ID`(需先通过 `SETUP` 获取) - 递增的 `CSeq` 序列号 - 正确的 `Content-Type: application/sdp` - 匹配 SDP 长度的 `Content-Length` 3. **客户端权限问题** `ANNOUNCE` 通常用于**媒体流推送**(如推流到服务器),客户端可能需要特殊认证。 --- ### 解决方案 #### 步骤 1:验证服务器支持性 ```cpp // 在 testRTSPClient.cpp 中添加 OPTIONS 请求检查 sendOptionsCommand(rtspURL, [](const char* response) { if (strstr(response, "ANNOUNCE") == nullptr) { cerr << "ERROR: Server does not support ANNOUNCE (405)"; } }); ``` - 若服务器响应不含 `ANNOUNCE`,需联系服务器管理员启用该方法(如修改 Live555 服务的 `ALLOW_ANNOUNCE_STREAMING` 配置)。 #### 步骤 2:检查请求构造(关键代码修正) 参考引用[3]的格式[^3],修正请求头: ```cpp // 正确示例(伪代码) string announceMsg = "ANNOUNCE " + rtspURL + " RTSP/1.0\r\n" + "Session: " + sessionID + "\r\n" + // 必须来自 SETUP 响应 "CSeq: " + (lastCSeq + 1) + "\r\n" + // 确保递增 "Content-Type: application/sdp\r\n" + "Content-Length: " + sdpData.length() + "\r\n\r\n" + sdpData; ``` #### 步骤 3:添加认证信息(如需) 若服务器要求认证: ```cpp announceMsg += "Authorization: Basic " + base64_encode(username + ":" + password) + "\r\n"; ``` #### 步骤 4:验证 SDP 内容有效性 - 检查 SDP 格式是否符合 RFC 4566(如 `v=0`, `m=` 字段必填) - 用工具(如 `ffprobe`)验证 SDP: ```bash ffprobe -protocol_whitelist file,rtp,udp -i input.sdp ``` --- ### 调试建议 1. **抓包分析** 使用 Wireshark 过滤 `rtsp` 协议,检查: - ANNOUNCE 请求头是否完整 - 服务器是否返回 `405` 或 `401 Unauthorized` 2. **日志增强** 在客户端添加调试日志: ```cpp fprintf(stderr, "[DEBUG] Sending ANNOUNCE:\n%s\n", announceMsg.c_str()); ``` > ⚠️ 若仍报错 405,需确认服务器配置(如 Darwin Streaming Server 需开启 `announce_requires_auth=0`)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值