日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

live555 源码分析: PLAY 的处理

發布時間:2024/4/11 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 live555 源码分析: PLAY 的处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 SETUP 請求之后,客戶端會發起 PLAY 請求,以請求服務器開始傳輸音視頻數據。在 PLAY 請求執行時,一定是已經執行過 SETUP 請求,建立好了客戶端會話,因而會與其它要求客戶端會話已經建立的請求一起,通過 clientSession->handleCmd_withinSession() 執行:

} else if (strcmp(cmdName, "TEARDOWN") == 0|| strcmp(cmdName, "PLAY") == 0 || strcmp(cmdName, "PAUSE") == 0|| strcmp(cmdName, "GET_PARAMETER") == 0|| strcmp(cmdName, "SET_PARAMETER") == 0) {if (clientSession != NULL) {clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix,urlSuffix, (char const*) fRequestBuffer);} else {handleCmd_sessionNotFound();}}

RTSPServer::RTSPClientSession::handleCmd_withinSession() 的定義如下:

void RTSPServer::RTSPClientSession ::handleCmd_withinSession(RTSPServer::RTSPClientConnection* ourClientConnection,char const* cmdName,char const* urlPreSuffix, char const* urlSuffix,char const* fullRequestStr) {// This will either be:// - a non-aggregated operation, if "urlPreSuffix" is the session (stream)// name and "urlSuffix" is the subsession (track) name, or// - an aggregated operation, if "urlSuffix" is the session (stream) name,// or "urlPreSuffix" is the session (stream) name, and "urlSuffix" is empty,// or "urlPreSuffix" and "urlSuffix" are both nonempty, but when concatenated, (with "/") form the session (stream) name.// Begin by figuring out which of these it is:ServerMediaSubsession* subsession;if (fOurServerMediaSession == NULL) { // There wasn't a previous SETUP!ourClientConnection->handleCmd_notSupported();return;} else if (urlSuffix[0] != '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0) {// Non-aggregated operation.// Look up the media subsession whose track id is "urlSuffix":ServerMediaSubsessionIterator iter(*fOurServerMediaSession);while ((subsession = iter.next()) != NULL) {if (strcmp(subsession->trackId(), urlSuffix) == 0) break; // success}if (subsession == NULL) { // no such track!ourClientConnection->handleCmd_notFound();return;}} else if (strcmp(fOurServerMediaSession->streamName(), urlSuffix) == 0 ||(urlSuffix[0] == '\0' && strcmp(fOurServerMediaSession->streamName(), urlPreSuffix) == 0)) {// Aggregated operationsubsession = NULL;} else if (urlPreSuffix[0] != '\0' && urlSuffix[0] != '\0') {// Aggregated operation, if <urlPreSuffix>/<urlSuffix> is the session (stream) name:unsigned const urlPreSuffixLen = strlen(urlPreSuffix);if (strncmp(fOurServerMediaSession->streamName(), urlPreSuffix, urlPreSuffixLen) == 0&& fOurServerMediaSession->streamName()[urlPreSuffixLen] == '/'&& strcmp(&(fOurServerMediaSession->streamName())[urlPreSuffixLen + 1], urlSuffix) == 0) {subsession = NULL;} else {ourClientConnection->handleCmd_notFound();return;}} else { // the request doesn't match a known stream and/or track at all!ourClientConnection->handleCmd_notFound();return;}if (strcmp(cmdName, "TEARDOWN") == 0) {handleCmd_TEARDOWN(ourClientConnection, subsession);} else if (strcmp(cmdName, "PLAY") == 0) {handleCmd_PLAY(ourClientConnection, subsession, fullRequestStr);} else if (strcmp(cmdName, "PAUSE") == 0) {handleCmd_PAUSE(ourClientConnection, subsession);} else if (strcmp(cmdName, "GET_PARAMETER") == 0) {handleCmd_GET_PARAMETER(ourClientConnection, subsession, fullRequestStr);} else if (strcmp(cmdName, "SET_PARAMETER") == 0) {handleCmd_SET_PARAMETER(ourClientConnection, subsession, fullRequestStr);} }

在這個函數中,首先會找到請求的 URL 所相應的 ServerMediaSubsession,如果查找失敗,會直接返回 404 并結束處理;然后即是根據請求的具體類型,通過不同的方法進行處理。

找到的 ServerMediaSubsession 存儲于 subsession 中。subsession 值為空并不表示查找失敗。這里通過值為 NULL 表示操作應用于 fOurServerMediaSession 描述的整個流媒體會話。請求的資源的路徑可以分為兩部分,分別是資源路徑前綴 urlPreSuffix 和標識具體 track 的后綴 urlSuffix。live555 中通過 ServerMediaSession 和 ServerMediaSubsession 兩級對象管理流媒體會話。

根據請求的資源的路徑與流媒體會話狀態的對應關系的不同,判斷操作是否可以支持,以及操作應用的范圍。

查找 ServerMediaSubsession 之后,PLAY 請求通過RTSPServer::RTSPClientSession::handleCmd_PLAY() 函數處理:

void RTSPServer::RTSPClientSession ::handleCmd_PLAY(RTSPServer::RTSPClientConnection* ourClientConnection,ServerMediaSubsession* subsession, char const* fullRequestStr) {char* rtspURL = fOurRTSPServer.rtspURL(fOurServerMediaSession,ourClientConnection->fClientInputSocket);unsigned rtspURLSize = strlen(rtspURL);// Parse the client's "Scale:" header, if any:float scale;Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);// Try to set the stream's scale factor to this value:if (subsession == NULL /*aggregate op*/) {fOurServerMediaSession->testScaleFactor(scale);} else {subsession->testScaleFactor(scale);}char buf[100];char* scaleHeader;if (!sawScaleHeader) {buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back} else {sprintf(buf, "Scale: %f\r\n", scale);}scaleHeader = strDup(buf);// Parse the client's "Range:" header, if any:float duration = 0.0;double rangeStart = 0.0, rangeEnd = 0.0;char* absStart = NULL; char* absEnd = NULL;Boolean startTimeIsNow;Boolean sawRangeHeader= parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow);if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {// Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:duration = subsession == NULL /*aggregate op*/? fOurServerMediaSession->duration() : subsession->duration();if (duration < 0.0) {// We're an aggregate PLAY, but the subsessions have different durations.// Use the largest of these durations in our headerduration = -duration;}// Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header)// have sane values, before we send back our own "Range:" header in our response:if (rangeStart < 0.0) rangeStart = 0.0;else if (rangeStart > duration) rangeStart = duration;if (rangeEnd < 0.0) rangeEnd = 0.0;else if (rangeEnd > duration) rangeEnd = duration;if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0)|| (scale < 0.0 && rangeStart < rangeEnd)) {// "rangeStart" and "rangeEnd" were the wrong way around; swap them:double tmp = rangeStart;rangeStart = rangeEnd;rangeEnd = tmp;}}// Create a "RTP-Info:" line. It will get filled in from each subsession's state:char const* rtpInfoFmt ="%s" // "RTP-Info:", plus any preceding rtpInfo items"%s" // comma separator, if needed"url=%s/%s"";seq=%d"";rtptime=%u";unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);char* rtpInfo = strDup("RTP-Info: ");unsigned i, numRTPInfoItems = 0;// Do any required seeking/scaling on each subsession, before starting streaming.// (However, we don't do this if the "PLAY" request was for just a single subsession// of a multiple-subsession stream; for such streams, seeking/scaling can be done// only with an aggregate "PLAY".)for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {if (fStreamStates[i].subsession != NULL) {if (sawScaleHeader) {fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);}if (absStart != NULL) {// Special case handling for seeking by 'absolute' time:fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);} else {// Seeking by relative (NPT) time:u_int64_t numBytes;if (!sawRangeHeader || startTimeIsNow) {// We're resuming streaming without seeking, so we just do a 'null' seek// (to get our NPT, and to specify when to end streaming):fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken,rangeEnd, numBytes);} else {// We do a real 'seek':double streamDuration = 0.0; // by default; means: stream until the end of the mediaif (rangeEnd > 0.0 && (rangeEnd + 0.001) < duration) {// the 0.001 is because we limited the values to 3 decimal places// We want the stream to end early. Set the duration we want:streamDuration = rangeEnd - rangeStart;if (streamDuration < 0.0) streamDuration = -streamDuration;// should happen only if scale < 0.0}fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,rangeStart, streamDuration, numBytes);}}}}}// Create the "Range:" header that we'll send back in our response.// (Note that we do this after seeking, in case the seeking operation changed the range start time.)if (absStart != NULL) {// We're seeking by 'absolute' time:if (absEnd == NULL) {sprintf(buf, "Range: clock=%s-\r\n", absStart);} else {sprintf(buf, "Range: clock=%s-%s\r\n", absStart, absEnd);}delete[] absStart; delete[] absEnd;} else {// We're seeking by relative (NPT) time:if (!sawRangeHeader || startTimeIsNow) {// We didn't seek, so in our response, begin the range with the current NPT (normal play time):float curNPT = 0.0;for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */|| subsession == fStreamStates[i].subsession) {if (fStreamStates[i].subsession == NULL) continue;float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);if (npt > curNPT)curNPT = npt;// Note: If this is an aggregate "PLAY" on a multi-subsession stream,// then it's conceivable that the NPTs of each subsession may differ// (if there has been a previous seek on just one subsession).// In this (unusual) case, we just return the largest NPT; I hope that turns out OK...}}rangeStart = curNPT;}if (rangeEnd == 0.0 && scale >= 0.0) {sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);} else {sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);}}char* rangeHeader = strDup(buf);// Now, start streaming:for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */|| subsession == fStreamStates[i].subsession) {unsigned short rtpSeqNum = 0;unsigned rtpTimestamp = 0;if (fStreamStates[i].subsession == NULL) continue;fStreamStates[i].subsession->startStream(fOurSessionId,fStreamStates[i].streamToken,(TaskFunc*) noteClientLiveness, this,rtpSeqNum, rtpTimestamp,RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);const char *urlSuffix = fStreamStates[i].subsession->trackId();char* prevRTPInfo = rtpInfo;unsigned rtpInfoSize = rtpInfoFmtSize+ strlen(prevRTPInfo)+ 1+ rtspURLSize + strlen(urlSuffix)+ 5 /*max unsigned short len*/+ 10 /*max unsigned (32-bit) len*/+ 2 /*allows for trailing \r\n at final end of string*/;rtpInfo = new char[rtpInfoSize];sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo,numRTPInfoItems++ == 0 ? "" : ",", rtspURL, urlSuffix, rtpSeqNum,rtpTimestamp);delete[] prevRTPInfo;}}if (numRTPInfoItems == 0) {rtpInfo[0] = '\0';} else {unsigned rtpInfoLen = strlen(rtpInfo);rtpInfo[rtpInfoLen] = '\r';rtpInfo[rtpInfoLen + 1] = '\n';rtpInfo[rtpInfoLen + 2] = '\0';}// Fill in the response:snprintf((char*) ourClientConnection->fResponseBuffer,sizeof ourClientConnection->fResponseBuffer, "RTSP/1.0 200 OK\r\n""CSeq: %s\r\n""%s""%s""%s""Session: %08X\r\n""%s\r\n",ourClientConnection->fCurrentCSeq,dateHeader(),scaleHeader,rangeHeader,fOurSessionId,rtpInfo);delete[] rtpInfo;delete[] rangeHeader;delete[] scaleHeader;delete[] rtspURL; }

在具體分析這個函數之前,先來看一個 PLAY 請求/響應的示例。PLAY 請求示例:

PLAY rtsp://10.240.248.20:8554/video/raw_h264_stream.264/ RTSP/1.0 Range: npt=0.000- CSeq: 4 User-Agent: Lavf56.40.101 Session: D10C8C71

PLAY 響應的示例:

RTSP/1.0 200 OK CSeq: 4 Date: Sat, Sep 02 2017 08:54:03 GMT Range: npt=0.000- Session: D10C8C71 RTP-Info: url=rtsp://10.240.248.20:8554/video/raw_h264_stream.264/track1;seq=12647;rtptime=2457491257

然后來看 RTSPServer::RTSPClientSession::handleCmd_PLAY() 的具體處理過程。

第一步,解析 Scale: 頭部,為流媒體會話的各個子會話設置 Scale,并構造要返回的 Scale: 頭部。

// Parse the client's "Scale:" header, if any:float scale;Boolean sawScaleHeader = parseScaleHeader(fullRequestStr, scale);// Try to set the stream's scale factor to this value:if (subsession == NULL /*aggregate op*/) {fOurServerMediaSession->testScaleFactor(scale);} else {subsession->testScaleFactor(scale);}char buf[100];char* scaleHeader;if (!sawScaleHeader) {buf[0] = '\0'; // Because we didn't see a Scale: header, don't send one back} else {sprintf(buf, "Scale: %f\r\n", scale);}scaleHeader = strDup(buf);

用于解析 Scale: 頭部的 parseScaleHeader() 定義如下:

Boolean parseScaleHeader(char const* buf, float& scale) {// Initialize the result parameter to a default value:scale = 1.0;// First, find "Scale:"while (1) {if (*buf == '\0') return False; // not foundif (_strncasecmp(buf, "Scale:", 6) == 0) break;++buf;}char const* fields = buf + 6;while (*fields == ' ') ++fields;float sc;if (sscanf(fields, "%f", &sc) == 1) {scale = sc;} else {return False; // The header is malformed}return True; }

scale 默認情況下會被設置為1.0。

解釋一下 scale 值的作用。根據 RTSP 的規范 RFC 2326 12.34 Scale,scale 值是用于控制播放速度的。scale 值為 1,表示以正常的向前觀看速率正常播放或記錄。如果不是 1,值對應于相對于正常觀看速率的比率。比如,值為 2 的比率表示兩倍的正常觀看速率(“快進”),而值為 0.5 的比率表示一半的觀看速率。換句話說,值為 2 的比率播放的時間增長速率是墻上時鐘的兩倍。對于流逝的每一秒時間,將有 2 秒的內容被發送。負值表示相反的方向。

除非 Speed 參數另有要求,否則數據速率不得更改。Scale
變化的實現取決于服務器和媒體類型。對于視頻,服務器可以,比如,傳送一個關鍵幀或選擇的關鍵幀。對于音頻,它可以在保持音調的同時縮放音頻,或者更不希望地傳送音頻片段。服務器應嘗試提供近似的觀看速率,但可以限制其支持的比例值范圍。響應要包含服務器選擇的實際的 scale。如果請求包含了 Range 參數,新的 scale 值將在此時生效。

應用于整個流媒體會話的 PLAY 請求,通過 ServerMediaSession::testScaleFactor(float& scale) 設置 scale,應用于具體子會話的 PLAY 請求,則通過 ServerMediaSubsession 的 testScaleFactor(float& scale) 設置。

對于我們的 H264VideoFileServerMediaSubsession,testScaleFactor(float& scale) 的實現還是在 ServerMediaSubsession 中:

void ServerMediaSubsession::testScaleFactor(float& scale) {// default implementation: Support scale = 1 onlyscale = 1; }

ServerMediaSession::testScaleFactor(float& scale) 的定義如下:

void ServerMediaSession::testScaleFactor(float& scale) {// First, try setting all subsessions to the desired scale.// If the subsessions' actual scales differ from each other, choose the// value that's closest to 1, and then try re-setting all subsessions to that// value. If the subsessions' actual scales still differ, re-set them all to 1.float minSSScale = 1.0;float maxSSScale = 1.0;float bestSSScale = 1.0;float bestDistanceTo1 = 0.0;ServerMediaSubsession* subsession;for (subsession = fSubsessionsHead; subsession != NULL;subsession = subsession->fNext) {float ssscale = scale;subsession->testScaleFactor(ssscale);if (subsession == fSubsessionsHead) { // this is the first subsessionminSSScale = maxSSScale = bestSSScale = ssscale;bestDistanceTo1 = (float)fabs(ssscale - 1.0f);} else {if (ssscale < minSSScale) {minSSScale = ssscale;} else if (ssscale > maxSSScale) {maxSSScale = ssscale;}float distanceTo1 = (float) fabs(ssscale - 1.0f);if (distanceTo1 < bestDistanceTo1) {bestSSScale = ssscale;bestDistanceTo1 = distanceTo1;}}}if (minSSScale == maxSSScale) {// All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScalescale = minSSScale;return;}// The scales for each subsession differ. Try to set each one to the value// that's closest to 1:for (subsession = fSubsessionsHead; subsession != NULL;subsession = subsession->fNext) {float ssscale = bestSSScale;subsession->testScaleFactor(ssscale);if (ssscale != bestSSScale) break; // no luck}if (subsession == NULL) {// All subsessions are at the same scale: bestSSScalescale = bestSSScale;return;}// Still no luck. Set each subsession's scale to 1:for (subsession = fSubsessionsHead; subsession != NULL;subsession = subsession->fNext) {float ssscale = 1;subsession->testScaleFactor(ssscale);}scale = 1; }

在這個函數中,執行的步驟如下:
Step 1:嘗試將所有子會話的 scale 設置客戶端請求的值,并記錄下子會話選擇的 scale 的最大值,最小值,以及與 1 最接近的值。
Step 2:第 1 步執行過程中,所有子會話選擇的 scale 值相同,則將該 scale 返回給調用者。
Step 3:第 1 步執行過程中,所有子會話選擇的 scale 值不同,則嘗試將與 1 最接近的 scale 值設置給所有的。
Step 4:第 3 步可以成功將與 1 最接近的 scale 值設置給所有子會話,則將該 scale 返回給調用者。
Step 5:第 3 步無法將與 1 最接近的 scale 值設置給所有子會話,則將每個子會話的 scale 值設置為 1。

回到 RTSPServer::RTSPClientSession::handleCmd_PLAY()。

第二步,解析客戶端的 Range: 頭部,并根據 scale 值更新 range 值。

// Parse the client's "Range:" header, if any:float duration = 0.0;double rangeStart = 0.0, rangeEnd = 0.0;char* absStart = NULL; char* absEnd = NULL;Boolean startTimeIsNow;Boolean sawRangeHeader= parseRangeHeader(fullRequestStr, rangeStart, rangeEnd, absStart, absEnd, startTimeIsNow);if (sawRangeHeader && absStart == NULL/*not seeking by 'absolute' time*/) {// Use this information, plus the stream's duration (if known), to create our own "Range:" header, for the response:duration = subsession == NULL /*aggregate op*/? fOurServerMediaSession->duration() : subsession->duration();if (duration < 0.0) {// We're an aggregate PLAY, but the subsessions have different durations.// Use the largest of these durations in our headerduration = -duration;}// Make sure that "rangeStart" and "rangeEnd" (from the client's "Range:" header)// have sane values, before we send back our own "Range:" header in our response:if (rangeStart < 0.0) rangeStart = 0.0;else if (rangeStart > duration) rangeStart = duration;if (rangeEnd < 0.0) rangeEnd = 0.0;else if (rangeEnd > duration) rangeEnd = duration;if ((scale > 0.0 && rangeStart > rangeEnd && rangeEnd > 0.0)|| (scale < 0.0 && rangeStart < rangeEnd)) {// "rangeStart" and "rangeEnd" were the wrong way around; swap them:double tmp = rangeStart;rangeStart = rangeEnd;rangeEnd = tmp;}}

第四步,創建 RTP-Info: 行:

// Create a "RTP-Info:" line. It will get filled in from each subsession's state:char const* rtpInfoFmt ="%s" // "RTP-Info:", plus any preceding rtpInfo items"%s" // comma separator, if needed"url=%s/%s"";seq=%d"";rtptime=%u";unsigned rtpInfoFmtSize = strlen(rtpInfoFmt);char* rtpInfo = strDup("RTP-Info: ");unsigned i, numRTPInfoItems = 0;

第五步,在開始流式傳輸之前,對每個子會話進行所需的 seeking/scaling,即設置播放進度和播放速率。

for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */ || fNumStreamStates == 1) {if (fStreamStates[i].subsession != NULL) {if (sawScaleHeader) {fStreamStates[i].subsession->setStreamScale(fOurSessionId, fStreamStates[i].streamToken, scale);}if (absStart != NULL) {// Special case handling for seeking by 'absolute' time:fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken, absStart, absEnd);} else {// Seeking by relative (NPT) time:u_int64_t numBytes;if (!sawRangeHeader || startTimeIsNow) {// We're resuming streaming without seeking, so we just do a 'null' seek// (to get our NPT, and to specify when to end streaming):fStreamStates[i].subsession->nullSeekStream(fOurSessionId, fStreamStates[i].streamToken,rangeEnd, numBytes);} else {// We do a real 'seek':double streamDuration = 0.0; // by default; means: stream until the end of the mediaif (rangeEnd > 0.0 && (rangeEnd + 0.001) < duration) {// the 0.001 is because we limited the values to 3 decimal places// We want the stream to end early. Set the duration we want:streamDuration = rangeEnd - rangeStart;if (streamDuration < 0.0) streamDuration = -streamDuration;// should happen only if scale < 0.0}fStreamStates[i].subsession->seekStream(fOurSessionId, fStreamStates[i].streamToken,rangeStart, streamDuration, numBytes);}}}}}

第六步,創建將會在響應中發送的 Range: 頭部。

if (absStart != NULL) {// We're seeking by 'absolute' time:if (absEnd == NULL) {sprintf(buf, "Range: clock=%s-\r\n", absStart);} else {sprintf(buf, "Range: clock=%s-%s\r\n", absStart, absEnd);}delete[] absStart; delete[] absEnd;} else {// We're seeking by relative (NPT) time:if (!sawRangeHeader || startTimeIsNow) {// We didn't seek, so in our response, begin the range with the current NPT (normal play time):float curNPT = 0.0;for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */|| subsession == fStreamStates[i].subsession) {if (fStreamStates[i].subsession == NULL) continue;float npt = fStreamStates[i].subsession->getCurrentNPT(fStreamStates[i].streamToken);if (npt > curNPT)curNPT = npt;// Note: If this is an aggregate "PLAY" on a multi-subsession stream,// then it's conceivable that the NPTs of each subsession may differ// (if there has been a previous seek on just one subsession).// In this (unusual) case, we just return the largest NPT; I hope that turns out OK...}}rangeStart = curNPT;}if (rangeEnd == 0.0 && scale >= 0.0) {sprintf(buf, "Range: npt=%.3f-\r\n", rangeStart);} else {sprintf(buf, "Range: npt=%.3f-%.3f\r\n", rangeStart, rangeEnd);}}char* rangeHeader = strDup(buf);

第七步,啟動流媒體傳輸,并產生最終的 RTP-Info: 行。

// Now, start streaming:for (i = 0; i < fNumStreamStates; ++i) {if (subsession == NULL /* means: aggregated operation */|| subsession == fStreamStates[i].subsession) {unsigned short rtpSeqNum = 0;unsigned rtpTimestamp = 0;if (fStreamStates[i].subsession == NULL) continue;fStreamStates[i].subsession->startStream(fOurSessionId,fStreamStates[i].streamToken,(TaskFunc*) noteClientLiveness, this,rtpSeqNum, rtpTimestamp,RTSPServer::RTSPClientConnection::handleAlternativeRequestByte, ourClientConnection);const char *urlSuffix = fStreamStates[i].subsession->trackId();char* prevRTPInfo = rtpInfo;unsigned rtpInfoSize = rtpInfoFmtSize+ strlen(prevRTPInfo)+ 1+ rtspURLSize + strlen(urlSuffix)+ 5 /*max unsigned short len*/+ 10 /*max unsigned (32-bit) len*/+ 2 /*allows for trailing \r\n at final end of string*/;rtpInfo = new char[rtpInfoSize];sprintf(rtpInfo, rtpInfoFmt, prevRTPInfo,numRTPInfoItems++ == 0 ? "" : ",", rtspURL, urlSuffix, rtpSeqNum,rtpTimestamp);delete[] prevRTPInfo;}}if (numRTPInfoItems == 0) {rtpInfo[0] = '\0';} else {unsigned rtpInfoLen = strlen(rtpInfo);rtpInfo[rtpInfoLen] = '\r';rtpInfo[rtpInfoLen + 1] = '\n';rtpInfo[rtpInfoLen + 2] = '\0';}

第八步,產生最終的響應,并釋放臨時分配的內存。

// Fill in the response:snprintf((char*) ourClientConnection->fResponseBuffer,sizeof ourClientConnection->fResponseBuffer, "RTSP/1.0 200 OK\r\n""CSeq: %s\r\n""%s""%s""%s""Session: %08X\r\n""%s\r\n",ourClientConnection->fCurrentCSeq,dateHeader(),scaleHeader,rangeHeader,fOurSessionId,rtpInfo);delete[] rtpInfo;delete[] rangeHeader;delete[] scaleHeader;delete[] rtspURL;

Done。

live555 源碼分析系列文章

live555 源碼分析:簡介
live555 源碼分析:基礎設施
live555 源碼分析:MediaSever
Wireshark 抓包分析 RTSP/RTP/RTCP 基本工作過程
live555 源碼分析:RTSPServer
live555 源碼分析: DESCRIBE 的處理
live555 源碼分析: SETUP 的處理
live555 源碼分析: PLAY 的處理

總結

以上是生活随笔為你收集整理的live555 源码分析: PLAY 的处理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。