日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

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

编程问答

live555 源码分析:播放启动

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

本文分析 live555 中,流媒體播放啟動,數據開始通過 RTP/RTCP 傳輸的過程。

如我們在 live555 源碼分析:子會話 SETUP 中看到的,一個流媒體子會話的播放啟動,由 StreamState::startPlaying 完成:

void OnDemandServerMediaSubsession::startStream(unsigned clientSessionId,void* streamToken,TaskFunc* rtcpRRHandler,void* rtcpRRHandlerClientData,unsigned short& rtpSeqNum,unsigned& rtpTimestamp,ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,void* serverRequestAlternativeByteHandlerClientData) {StreamState* streamState = (StreamState*)streamToken;Destinations* destinations= (Destinations*)(fDestinationsHashTable->Lookup((char const*)clientSessionId));if (streamState != NULL) {streamState->startPlaying(destinations, clientSessionId,rtcpRRHandler, rtcpRRHandlerClientData,serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);RTPSink* rtpSink = streamState->rtpSink(); // aliasif (rtpSink != NULL) {rtpSeqNum = rtpSink->currentSeqNo();rtpTimestamp = rtpSink->presetNextTimestamp();}} }

在這個函數中,首先找到子會話的目標地址,也就是客戶端的 IP 地址,和用于接收 RTP/RTCP 的端口號,然后通過 StreamState::startPlaying() 啟動播放,最后將 RTP 包的初始序列號和初始時間戳返回給調用者,也就是 RTSPServer,并由后者返回給客戶端,以用于客戶端的播放同步。

StreamState::startPlaying() 的實現是這樣的:

void StreamState ::startPlaying(Destinations* dests, unsigned clientSessionId,TaskFunc* rtcpRRHandler, void* rtcpRRHandlerClientData,ServerRequestAlternativeByteHandler* serverRequestAlternativeByteHandler,void* serverRequestAlternativeByteHandlerClientData) {if (dests == NULL) return;if (fRTCPInstance == NULL && fRTPSink != NULL) {// Create (and start) a 'RTCP instance' for this RTP sink:fRTCPInstance = fMaster.createRTCP(fRTCPgs, fTotalBW, (unsigned char*)fMaster.fCNAME, fRTPSink);// Note: This starts RTCP running automaticallyfRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, fMaster.fAppHandlerClientData);}if (dests->isTCP) {// Change RTP and RTCP to use the TCP socket instead of UDP:if (fRTPSink != NULL) {fRTPSink->addStreamSocket(dests->tcpSocketNum, dests->rtpChannelId);RTPInterface::setServerRequestAlternativeByteHandler(fRTPSink->envir(), dests->tcpSocketNum,serverRequestAlternativeByteHandler, serverRequestAlternativeByteHandlerClientData);// So that we continue to handle RTSP commands from the client}if (fRTCPInstance != NULL) {fRTCPInstance->addStreamSocket(dests->tcpSocketNum, dests->rtcpChannelId);fRTCPInstance->setSpecificRRHandler(dests->tcpSocketNum, dests->rtcpChannelId,rtcpRRHandler, rtcpRRHandlerClientData);}} else {// Tell the RTP and RTCP 'groupsocks' about this destination// (in case they don't already have it):if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort, clientSessionId);if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && dests->rtcpPort.num() == dests->rtpPort.num())) {fRTCPgs->addDestination(dests->addr, dests->rtcpPort, clientSessionId);}if (fRTCPInstance != NULL) {fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,rtcpRRHandler, rtcpRRHandlerClientData);}}if (fRTCPInstance != NULL) {// Hack: Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will (likely) be able to// get RTCP-synchronized presentation times immediately:fRTCPInstance->sendReport();}if (!fAreCurrentlyPlaying && fMediaSource != NULL) {if (fRTPSink != NULL) {fRTPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);fAreCurrentlyPlaying = True;} else if (fUDPSink != NULL) {fUDPSink->startPlaying(*fMediaSource, afterPlayingStreamState, this);fAreCurrentlyPlaying = True;}} }

在這個函數中,首先在 RTCPInstance 還沒有創建時去創建它:

RTCPInstance* OnDemandServerMediaSubsession ::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */unsigned char const* cname, RTPSink* sink) {// Default implementation; may be redefined by subclasses:return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, sink, NULL/*we're a server*/); }

忽略 RTP/RTCP 包走 TCP 的情況。隨后 StreamState::startPlaying() 對 RTP 和 RTCP 的 groupsock 做一些設置,即為它們添加目標地址,并為 RTCPInstance 做了一些設置:

} else {// Tell the RTP and RTCP 'groupsocks' about this destination// (in case they don't already have it):if (fRTPgs != NULL) fRTPgs->addDestination(dests->addr, dests->rtpPort, clientSessionId);if (fRTCPgs != NULL && !(fRTCPgs == fRTPgs && dests->rtcpPort.num() == dests->rtpPort.num())) {fRTCPgs->addDestination(dests->addr, dests->rtcpPort, clientSessionId);}if (fRTCPInstance != NULL) {fRTCPInstance->setSpecificRRHandler(dests->addr.s_addr, dests->rtcpPort,rtcpRRHandler, rtcpRRHandlerClientData);}}

之后 StreamState::startPlaying() 發出一個 RTCP 包。

if (fRTCPInstance != NULL) {// Hack: Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will (likely) be able to// get RTCP-synchronized presentation times immediately:fRTCPInstance->sendReport();}

fUDPSink 用于流模式為 RAW UDP 的情況,忽略這種流模式的情況。最后執行 MediaSink::startPlaying(),并設置標記 fAreCurrentlyPlaying,表示流播放已經啟動。

RTP 包的發送

下面具體來看 RTP 包是怎么被發送出去的。MediaSink::startPlaying() 函數的定義如下:

Boolean MediaSink::startPlaying(MediaSource& source,afterPlayingFunc* afterFunc,void* afterClientData) {// Make sure we're not already being played:if (fSource != NULL) {envir().setResultMsg("This sink is already being played");return False;}// Make sure our source is compatible:if (!sourceIsCompatibleWithUs(source)) {envir().setResultMsg("MediaSink::startPlaying(): source is not compatible!");return False;}fSource = (FramedSource*)&source;fAfterFunc = afterFunc;fAfterClientData = afterClientData;return continuePlaying(); }

在這個函數中,保存了傳入的回調及回調的參數,然后執行 continuePlaying(),continuePlaying() 是一個純虛函數,其實現由 MediaSink 的子類 H264or5VideoRTPSink 實現:

Boolean H264or5VideoRTPSink::continuePlaying() {// First, check whether we have a 'fragmenter' class set up yet.// If not, create it now:if (fOurFragmenter == NULL) {fOurFragmenter = new H264or5Fragmenter(fHNumber, envir(), fSource, OutPacketBuffer::maxSize,ourMaxPacketSize() - 12/*RTP hdr size*/);} else {fOurFragmenter->reassignInputSource(fSource);}fSource = fOurFragmenter;// Then call the parent class's implementation:return MultiFramedRTPSink::continuePlaying(); }

在這個類中,主要是為 H264or5Fragmenter 設置了流媒體數據源,并將 fSource 設置為 H264or5Fragmenter。在這里,MultiFramedRTPSink 持有的流媒體數據源 FramedSource 由最初在 H264VideoFileServerMediaSubsession 中創建的 H264VideoStreamFramer 變為了 H264or5Fragmenter,而 H264or5Fragmenter 則封裝了 H264VideoStreamFramer。

隨后 H264or5VideoRTPSink::continuePlaying() 執行 MultiFramedRTPSink::continuePlaying() 做進一步的處理。

Boolean MultiFramedRTPSink::continuePlaying() {// Send the first packet.// (This will also schedule any future sends.)buildAndSendPacket(True);return True; } . . . . . . void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {nextTask() = NULL;fIsFirstPacket = isFirstPacket;// Set up the RTP header:unsigned rtpHdr = 0x80000000; // RTP version 2; marker ('M') bit not set (by default; it can be set later)rtpHdr |= (fRTPPayloadType<<16);rtpHdr |= fSeqNo; // sequence numberfOutBuf->enqueueWord(rtpHdr);// Note where the RTP timestamp will go.// (We can't fill this in until we start packing payload frames.)fTimestampPosition = fOutBuf->curPacketSize();fOutBuf->skipBytes(4); // leave a hole for the timestampfOutBuf->enqueueWord(SSRC());// Allow for a special, payload-format-specific header following the// RTP header:fSpecialHeaderPosition = fOutBuf->curPacketSize();fSpecialHeaderSize = specialHeaderSize();fOutBuf->skipBytes(fSpecialHeaderSize);// Begin packing as many (complete) frames into the packet as we can:fTotalFrameSpecificHeaderSizes = 0;fNoFramesLeft = False;fNumFramesUsedSoFar = 0;packFrame(); }

MultiFramedRTPSink::continuePlaying() 執行 MultiFramedRTPSink::buildAndSendPacket()。而 MultiFramedRTPSink::buildAndSendPacket() 則是在輸出緩沖區構造了 RTP 頭部,對于其中暫時無法準確獲得的頭部字段,還預留了空間。隨后調用了 MultiFramedRTPSink::packFrame()。

void MultiFramedRTPSink::packFrame() {// Get the next frame.// First, skip over the space we'll use for any frame-specific header:fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;// See if we have an overflow frame that was too big for the last pktif (fOutBuf->haveOverflowData()) {// Use this frame before reading a new one from the sourceunsigned frameSize = fOutBuf->overflowDataSize();struct timeval presentationTime = fOutBuf->overflowPresentationTime();unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();fOutBuf->useOverflowData();afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);} else {// Normal case: we need to read a new frame from the sourceif (fSource == NULL) return;fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),afterGettingFrame, this, ourHandleClosure, this);} }

MultiFramedRTPSink::packFrame() 由 FramedSource 的 getNextFrame() 獲得幀數據,并在獲得幀數據之后得到通知。

void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,afterGettingFunc* afterGettingFunc,void* afterGettingClientData,onCloseFunc* onCloseFunc,void* onCloseClientData) {// Make sure we're not already being read:if (fIsCurrentlyAwaitingData) {envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!\n";envir().internalError();}fTo = to;fMaxSize = maxSize;fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()fAfterGettingFunc = afterGettingFunc;fAfterGettingClientData = afterGettingClientData;fOnCloseFunc = onCloseFunc;fOnCloseClientData = onCloseClientData;fIsCurrentlyAwaitingData = True;doGetNextFrame(); }

這個函數主要用于為 FramedSource 設置媒體流數據要讀到哪里,可以讀多少自己,以及回調函數的地址。并最終執行 doGetNextFrame() 讀取數據。

最終數據將由 ByteStreamFileSource 的 doGetNextFrame() 執行讀取任務的調度,并從文件中讀取。

#0 ByteStreamFileSource::doGetNextFrame (this=0x6d8f10) at ByteStreamFileSource.cpp:96 #1 0x000000000043004c in FramedSource::getNextFrame (this=0x6d8f10, to=0x6da9c0 "(\243\203\367\377\177", maxSize=150000, afterGettingFunc=0x46f6c8 <StreamParser::afterGettingBytes(void*, unsigned int, unsigned int, timeval, unsigned int)>, afterGettingClientData=0x6d91b0, onCloseFunc=0x46f852 <StreamParser::onInputClosure(void*)>, onCloseClientData=0x6d91b0) at FramedSource.cpp:78-------------------------------------------------------------------------------------------------------------------------------------#2 0x000000000046f69c in StreamParser::ensureValidBytes1 (this=0x6d91b0, numBytesNeeded=4) at StreamParser.cpp:159 #3 0x00000000004343e5 in StreamParser::ensureValidBytes (this=0x6d91b0, numBytesNeeded=4) at StreamParser.hh:118 #4 0x0000000000434179 in StreamParser::test4Bytes (this=0x6d91b0) at StreamParser.hh:54 #5 0x0000000000471b85 in H264or5VideoStreamParser::parse (this=0x6d91b0) at H264or5VideoStreamFramer.cpp:951 #6 0x000000000043510f in MPEGVideoStreamFramer::continueReadProcessing (this=0x6d9000) at MPEGVideoStreamFramer.cpp:159 #7 0x0000000000435077 in MPEGVideoStreamFramer::doGetNextFrame (this=0x6d9000) at MPEGVideoStreamFramer.cpp:142 #8 0x000000000043004c in FramedSource::getNextFrame (this=0x6d9000, to=0x748d61 "", maxSize=100000, afterGettingFunc=0x474cd2 <H264or5Fragmenter::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>, afterGettingClientData=0x700300, onCloseFunc=0x4300c6 <FramedSource::handleClosure(void*)>, onCloseClientData=0x700300) at FramedSource.cpp:78-------------------------------------------------------------------------------------------------------------------------------------#9 0x000000000047480a in H264or5Fragmenter::doGetNextFrame (this=0x700300) at H264or5VideoRTPSink.cpp:181 #10 0x000000000043004c in FramedSource::getNextFrame (this=0x700300, to=0x7304ec "", maxSize=100452, afterGettingFunc=0x45af82 <MultiFramedRTPSink::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>, afterGettingClientData=0x6d92e0, onCloseFunc=0x45b96c <MultiFramedRTPSink::ourHandleClosure(void*)>, onCloseClientData=0x6d92e0) at FramedSource.cpp:78-------------------------------------------------------------------------------------------------------------------------------------#11 0x000000000045af61 in MultiFramedRTPSink::packFrame (this=0x6d92e0) at MultiFramedRTPSink.cpp:224 #12 0x000000000045adae in MultiFramedRTPSink::buildAndSendPacket (this=0x6d92e0, isFirstPacket=1 '\001') at MultiFramedRTPSink.cpp:199 #13 0x000000000045abed in MultiFramedRTPSink::continuePlaying (this=0x6d92e0) at MultiFramedRTPSink.cpp:159-------------------------------------------------------------------------------------------------------------------------------------#14 0x000000000047452a in H264or5VideoRTPSink::continuePlaying (this=0x6d92e0) at H264or5VideoRTPSink.cpp:127 #15 0x0000000000405d2a in MediaSink::startPlaying (this=0x6d92e0, source=..., afterFunc=0x4621f4 <afterPlayingStreamState(void*)>, afterClientData=0x6d95b0) at MediaSink.cpp:78 #16 0x00000000004626ea in StreamState::startPlaying (this=0x6d95b0, dests=0x6d9620, clientSessionId=1584618840, rtcpRRHandler=0x407280 <GenericMediaServer::ClientSession::noteClientLiveness(GenericMediaServer::ClientSession*)>, rtcpRRHandlerClientData=0x70ba40, serverRequestAlternativeByteHandler=0x4093a6 <RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void*, unsigned char)>, serverRequestAlternativeByteHandlerClientData=0x6ce910) at OnDemandServerMediaSubsession.cpp:576 #17 0x000000000046138d in OnDemandServerMediaSubsession::startStream (this=0x6d8710, clientSessionId=1584618840, streamToken=0x6d95b0, rtcpRRHandler=0x407280 <GenericMediaServer::ClientSession::noteClientLiveness(GenericMediaServer::ClientSession*)>, rtcpRRHandlerClientData=0x70ba40, rtpSeqNum=@0x7fffffffcd76: 0, rtpTimestamp=@0x7fffffffcdc0: 0, serverRequestAlternativeByteHandler=0x4093a6 <RTSPServer::RTSPClientConnection::handleAlternativeRequestByte(void*, unsigned char)>, serverRequestAlternativeByteHandlerClientData=0x6ce910) at OnDemandServerMediaSubsession.cpp:223

這個調用棧比較深。看起來可能會讓人感覺比較費解。實際上 live555 中采用裝飾器模式來設計 FramedSource,一個 FramedSource 可以包裝另一個 FramedSource,并額外提供一些功能,或為了性能優化,或為了數據解析等。

live555 中眾多的 FramedSource 類之間的關系大概如下圖所示:

上面的調用棧,也主要根據 FramedSource 的包裝關系,由虛線分割為幾個不同的階段。

在 ByteStreamFileSource 的 doGetNextFrame() 中,調度讀取任務:

void ByteStreamFileSource::doGetNextFrame() {if (feof(fFid) || ferror(fFid) || (fLimitNumBytesToStream && fNumBytesToStream == 0)) {handleClosure();return;}#ifdef READ_FROM_FILES_SYNCHRONOUSLYdoReadFromFile(); #elseif (!fHaveStartedReading) {// Await readable data from the file:envir().taskScheduler().turnOnBackgroundReadHandling(fileno(fFid),(TaskScheduler::BackgroundHandlerProc*)&fileReadableHandler, this);fHaveStartedReading = True;} #endif }

ByteStreamFileSource::fileReadableHandler() 讀取流媒體內容,并通知調用者:

void FramedSource::afterGetting(FramedSource* source) {source->nextTask() = NULL;source->fIsCurrentlyAwaitingData = False;// indicates that we can be read again// Note that this needs to be done here, in case the "fAfterFunc"// called below tries to read another frame (which it usually will)if (source->fAfterGettingFunc != NULL) {(*(source->fAfterGettingFunc))(source->fAfterGettingClientData,source->fFrameSize, source->fNumTruncatedBytes,source->fPresentationTime,source->fDurationInMicroseconds);} } . . . . . . void ByteStreamFileSource::fileReadableHandler(ByteStreamFileSource* source, int /*mask*/) {if (!source->isCurrentlyAwaitingData()) {source->doStopGettingFrames(); // we're not ready for the data yetreturn;}source->doReadFromFile(); }void ByteStreamFileSource::doReadFromFile() {// Try to read as many bytes as will fit in the buffer provided (or "fPreferredFrameSize" if less)if (fLimitNumBytesToStream && fNumBytesToStream < (u_int64_t)fMaxSize) {fMaxSize = (unsigned)fNumBytesToStream;}if (fPreferredFrameSize > 0 && fPreferredFrameSize < fMaxSize) {fMaxSize = fPreferredFrameSize;} #ifdef READ_FROM_FILES_SYNCHRONOUSLYfFrameSize = fread(fTo, 1, fMaxSize, fFid); #elseif (fFidIsSeekable) {fFrameSize = fread(fTo, 1, fMaxSize, fFid);} else {// For non-seekable files (e.g., pipes), call "read()" rather than "fread()", to ensure that the read doesn't block:fFrameSize = read(fileno(fFid), fTo, fMaxSize);} #endifif (fFrameSize == 0) {handleClosure();return;}fNumBytesToStream -= fFrameSize;// Set the 'presentation time':if (fPlayTimePerFrame > 0 && fPreferredFrameSize > 0) {if (fPresentationTime.tv_sec == 0 && fPresentationTime.tv_usec == 0) {// This is the first frame, so use the current time:gettimeofday(&fPresentationTime, NULL);} else {// Increment by the play time of the previous data:unsigned uSeconds = fPresentationTime.tv_usec + fLastPlayTime;fPresentationTime.tv_sec += uSeconds/1000000;fPresentationTime.tv_usec = uSeconds%1000000;}// Remember the play time of this data:fLastPlayTime = (fPlayTimePerFrame*fFrameSize)/fPreferredFrameSize;fDurationInMicroseconds = fLastPlayTime;} else {// We don't know a specific play time duration for this data,// so just record the current time as being the 'presentation time':gettimeofday(&fPresentationTime, NULL);}// Inform the reader that he has data: #ifdef READ_FROM_FILES_SYNCHRONOUSLY// To avoid possible infinite recursion, we need to return to the event loop to do this:nextTask() = envir().taskScheduler().scheduleDelayedTask(0,(TaskFunc*)FramedSource::afterGetting, this); #else// Because the file read was done from the event loop, we can call the// 'after getting' function directly, without risk of infinite recursion:FramedSource::afterGetting(this); #endif }

數據讀取完成之后,MultiFramedRTPSink 將得到通知:

#0 MultiFramedRTPSink::afterGettingFrame (clientData=0x6d92e0, numBytesRead=18, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=0) at MultiFramedRTPSink.cpp:233---------------------------------------------------------------------------------------------------------------------------#1 0x00000000004300c2 in FramedSource::afterGetting (source=0x7002c0) at FramedSource.cpp:92 #2 0x0000000000474ca6 in H264or5Fragmenter::doGetNextFrame (this=0x7002c0) at H264or5VideoRTPSink.cpp:263 #3 0x0000000000474dac in H264or5Fragmenter::afterGettingFrame1 (this=0x7002c0, frameSize=18, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=0) at H264or5VideoRTPSink.cpp:292 #4 0x0000000000474d25 in H264or5Fragmenter::afterGettingFrame (clientData=0x7002c0, frameSize=18, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=0) at H264or5VideoRTPSink.cpp:279---------------------------------------------------------------------------------------------------------------------------#5 0x00000000004300c2 in FramedSource::afterGetting (source=0x6d9000) at FramedSource.cpp:92 #6 0x00000000004351ea in MPEGVideoStreamFramer::continueReadProcessing (this=0x6d9000) at MPEGVideoStreamFramer.cpp:179 #7 0x00000000004350da in MPEGVideoStreamFramer::continueReadProcessing (clientData=0x6d9000) at MPEGVideoStreamFramer.cpp:155 #8 0x000000000046f84f in StreamParser::afterGettingBytes1 (this=0x6d91b0, numBytesRead=150000, presentationTime=...) at StreamParser.cpp:191 #9 0x000000000046f718 in StreamParser::afterGettingBytes (clientData=0x6d91b0, numBytesRead=150000, presentationTime=...)at StreamParser.cpp:170---------------------------------------------------------------------------------------------------------------------------#10 0x00000000004300c2 in FramedSource::afterGetting (source=0x6d8f10) at FramedSource.cpp:92 #11 0x0000000000430c2c in ByteStreamFileSource::doReadFromFile (this=0x6d8f10) at ByteStreamFileSource.cpp:182 #12 0x00000000004309cb in ByteStreamFileSource::fileReadableHandler (source=0x6d8f10) at ByteStreamFileSource.cpp:126

我們同樣將回調的調用棧,根據 FramedSource 的包裝關系,分為幾個階段,不同階段以虛線分割。

MultiFramedRTPSink::afterGettingFrame() 函數定義如下:

void MultiFramedRTPSink ::afterGettingFrame(void* clientData, unsigned numBytesRead,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds) {MultiFramedRTPSink* sink = (MultiFramedRTPSink*)clientData;sink->afterGettingFrame1(numBytesRead, numTruncatedBytes,presentationTime, durationInMicroseconds); }

在這個函數中調用 afterGettingFrame1(), afterGettingFrame1() 則會根據需要調用 sendPacketIfNecessary()。MultiFramedRTPSink::sendPacketIfNecessary() 定義如下:

void MultiFramedRTPSink::sendPacketIfNecessary() {if (fNumFramesUsedSoFar > 0) {// Send the packet: #ifdef TEST_LOSSif ((our_random()%10) != 0) // simulate 10% packet loss ##### #endifif (!fRTPInterface.sendPacket(fOutBuf->packet(), fOutBuf->curPacketSize())) {// if failure handler has been specified, call itif (fOnSendErrorFunc != NULL) (*fOnSendErrorFunc)(fOnSendErrorData);}++fPacketCount;fTotalOctetCount += fOutBuf->curPacketSize();fOctetCount += fOutBuf->curPacketSize()- rtpHeaderSize - fSpecialHeaderSize - fTotalFrameSpecificHeaderSizes;++fSeqNo; // for next time}if (fOutBuf->haveOverflowData()&& fOutBuf->totalBytesAvailable() > fOutBuf->totalBufferSize()/2) {// Efficiency hack: Reset the packet start pointer to just in front of// the overflow data (allowing for the RTP header and special headers),// so that we probably don't have to "memmove()" the overflow data// into place when building the next packet:unsigned newPacketStart = fOutBuf->curPacketSize()- (rtpHeaderSize + fSpecialHeaderSize + frameSpecificHeaderSize());fOutBuf->adjustPacketStart(newPacketStart);} else {// Normal case: Reset the packet start pointer back to the start:fOutBuf->resetPacketStart();}fOutBuf->resetOffset();fNumFramesUsedSoFar = 0;if (fNoFramesLeft) {// We're done:onSourceClosure();} else {// We have more frames left to send. Figure out when the next frame// is due to start playing, then make sure that we wait this long before// sending the next packet.struct timeval timeNow;gettimeofday(&timeNow, NULL);int secsDiff = fNextSendTime.tv_sec - timeNow.tv_sec;int64_t uSecondsToGo = secsDiff*1000000 + (fNextSendTime.tv_usec - timeNow.tv_usec);if (uSecondsToGo < 0 || secsDiff < 0) { // sanity check: Make sure that the time-to-delay is non-negative:uSecondsToGo = 0;}// Delay this amount of time:nextTask() = envir().taskScheduler().scheduleDelayedTask(uSecondsToGo, (TaskFunc*)sendNext, this);} }

在 MultiFramedRTPSink::sendPacketIfNecessary() 中,會發送幀數據。且如果流媒體數據發送沒有結束的話,在一幀數據發送完成之后,會調度一個定時器任務 MultiFramedRTPSink::sendNext() 再次發送幀數據。

MultiFramedRTPSink::sendNext() 執行與 MultiFramedRTPSink::continuePlaying() 類似的流程,獲取下一幀數據并發送。

void MultiFramedRTPSink::sendNext(void* firstArg) {MultiFramedRTPSink* sink = (MultiFramedRTPSink*)firstArg;sink->buildAndSendPacket(False); }

當然也并不是每一次發送幀數據的時候,都需要直接從流媒體源中去獲得數據。在 StreamParser 中會做判斷,當需要幀數據的時候,它會發起對流媒體文件的讀取。若無需從文件中讀取流媒體數據,則會直接回調:

#0 MultiFramedRTPSink::sendPacketIfNecessary (this=0x702140) at MultiFramedRTPSink.cpp:365 #1 0x000000000045b5a4 in MultiFramedRTPSink::afterGettingFrame1 (this=0x702140, frameSize=1444, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=40000) at MultiFramedRTPSink.cpp:347 #2 0x000000000045afd5 in MultiFramedRTPSink::afterGettingFrame (clientData=0x702140, numBytesRead=1444, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=40000) at MultiFramedRTPSink.cpp:235 #3 0x00000000004300c2 in FramedSource::afterGetting (source=0x7036d0) at FramedSource.cpp:92------------------------------------------------------------------------------------------------------------------------------------#4 0x0000000000474ca6 in H264or5Fragmenter::doGetNextFrame (this=0x7036d0) at H264or5VideoRTPSink.cpp:263 #5 0x0000000000474dac in H264or5Fragmenter::afterGettingFrame1 (this=0x7036d0, frameSize=53527, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=40000) at H264or5VideoRTPSink.cpp:292 #6 0x0000000000474d25 in H264or5Fragmenter::afterGettingFrame (clientData=0x7036d0, frameSize=53527, numTruncatedBytes=0, presentationTime=..., durationInMicroseconds=40000) at H264or5VideoRTPSink.cpp:279 #7 0x00000000004300c2 in FramedSource::afterGetting (source=0x701e20) at FramedSource.cpp:92------------------------------------------------------------------------------------------------------------------------------------#8 0x00000000004351ea in MPEGVideoStreamFramer::continueReadProcessing (this=0x701e20) at MPEGVideoStreamFramer.cpp:179 #9 0x0000000000435077 in MPEGVideoStreamFramer::doGetNextFrame (this=0x701e20) at MPEGVideoStreamFramer.cpp:142------------------------------------------------------------------------------------------------------------------------------------#10 0x000000000043004c in FramedSource::getNextFrame (this=0x701e20, to=0x7c3091 "\205\270@\367\017\204?\017", <incomplete sequence \340>, maxSize=100000, afterGettingFunc=0x474cd2 <H264or5Fragmenter::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>, afterGettingClientData=0x7036d0, onCloseFunc=0x4300c6 <FramedSource::handleClosure(void*)>, onCloseClientData=0x7036d0)at FramedSource.cpp:78 #11 0x000000000047480a in H264or5Fragmenter::doGetNextFrame (this=0x7036d0) at H264or5VideoRTPSink.cpp:181------------------------------------------------------------------------------------------------------------------------------------#12 0x000000000043004c in FramedSource::getNextFrame (this=0x7036d0, to=0x7aa81c "|\205\270@\367\017\204?\017", <incomplete sequence \340>, maxSize=100452, afterGettingFunc=0x45af82 <MultiFramedRTPSink::afterGettingFrame(void*, unsigned int, unsigned int, timeval, unsigned int)>, afterGettingClientData=0x702140, onCloseFunc=0x45b96c <MultiFramedRTPSink::ourHandleClosure(void*)>, onCloseClientData=0x702140)at FramedSource.cpp:78 #13 0x000000000045af61 in MultiFramedRTPSink::packFrame (this=0x702140) at MultiFramedRTPSink.cpp:224 #14 0x000000000045adae in MultiFramedRTPSink::buildAndSendPacket (this=0x702140, isFirstPacket=0 '\000') at MultiFramedRTPSink.cpp:199 #15 0x000000000045b969 in MultiFramedRTPSink::sendNext (firstArg=0x702140) at MultiFramedRTPSink.cpp:422 #16 0x000000000047f165 in AlarmHandler::handleTimeout (this=0x7038a0) at BasicTaskScheduler0.cpp:34 #17 0x000000000047d268 in DelayQueue::handleAlarm (this=0x6cdc28) at DelayQueue.cpp:187 #18 0x000000000047c196 in BasicTaskScheduler::SingleStep (this=0x6cdc20, maxDelayTime=0) at BasicTaskScheduler.cpp:212

總結一下 RTP 數據包的發送過程:

  • OnDemandServerMediaSubsession 中執行 startStream() 時,將發起一個對流媒體文件進行讀取的任務,讀取文件的工作由 ByteStreamFileSource 的 doReadFromFile() 執行。
  • 在文件讀取了一些數據之后,MultiFramedRTPSink 得到回調 afterGetting(),在這個回調中,發送幀數據。
  • MultiFramedRTPSink 的回調中,如果流媒體數據還沒有讀完的話,則調度一個定時器任務,一段時間之后再次發起獲取幀數據的動作。
  • 重復 2 和 3 兩步,直到所有的數據都發送完。
  • RTCP 包的接收

    StreamState::startPlaying() 通過 OnDemandServerMediaSubsession::createRTCP() 創建 RTCPInstance:

    RTCPInstance* OnDemandServerMediaSubsession ::createRTCP(Groupsock* RTCPgs, unsigned totSessionBW, /* in kbps */unsigned char const* cname, RTPSink* sink) {fprintf(stderr, "OnDemandServerMediaSubsession::createRTCP().\n");// Default implementation; may be redefined by subclasses:return RTCPInstance::createNew(envir(), RTCPgs, totSessionBW, cname, sink, NULL/*we're a server*/); }

    OnDemandServerMediaSubsession::createRTCP() 則通過 RTCPInstance::createNew() 創建:

    RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs,unsigned totSessionBW,unsigned char const* cname,RTPSink* sink, RTPSource* source,Boolean isSSMSource): Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW),fSink(sink), fSource(source), fIsSSMSource(isSSMSource),fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1),fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0),fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0),fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE),fHaveJustSentPacket(False), fLastPacketSentSize(0),fByeHandlerTask(NULL), fByeHandlerClientData(NULL),fSRHandlerTask(NULL), fSRHandlerClientData(NULL),fRRHandlerTask(NULL), fRRHandlerClientData(NULL),fSpecificRRHandlerTable(NULL),fAppHandlerTask(NULL), fAppHandlerClientData(NULL) { #ifdef DEBUGfprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n", this); #endifif (fTotSessionBW == 0) { // not allowed!env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n";fTotSessionBW = 1;}if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicastdouble timeNow = dTimeNow();fPrevReportTime = fNextReportTime = timeNow;fKnownMembers = new RTCPMemberDatabase(*this);fInBuf = new unsigned char[maxRTCPPacketSize];if (fKnownMembers == NULL || fInBuf == NULL) return;fNumBytesAlreadyRead = 0;fOutBuf = new OutPacketBuffer(preferredRTCPPacketSize, maxRTCPPacketSize, maxRTCPPacketSize);if (fOutBuf == NULL) return;if (fSource != NULL && fSource->RTPgs() == RTCPgs) {// We're receiving RTCP reports that are multiplexed with RTP, so ask the RTP source// to give them to us:fSource->registerForMultiplexedRTCPPackets(this);} else {// Arrange to handle incoming reports from the network:TaskScheduler::BackgroundHandlerProc* handler= (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler;fRTCPInterface.startNetworkReading(handler);}// Send our first report.fTypeOfEvent = EVENT_REPORT;onExpire(this); } . . . . . . RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs,unsigned totSessionBW,unsigned char const* cname,RTPSink* sink, RTPSource* source,Boolean isSSMSource) {return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source,isSSMSource); }

    可以看到,在 RTCPInstance 的構造函數中,調用 RTPInterface::startNetworkReading() 注冊了一個回調:

    void RTPInterface ::startNetworkReading(TaskScheduler::BackgroundHandlerProc* handlerProc) {// Normal case: Arrange to read UDP packets:envir().taskScheduler().turnOnBackgroundReadHandling(fGS->socketNum(), handlerProc, fOwner);// Also, receive RTP over TCP, on each of our TCP connections:fReadHandlerProc = handlerProc;for (tcpStreamRecord* streams = fTCPStreams; streams != NULL;streams = streams->fNext) {// Get a socket descriptor for "streams->fStreamSocketNum":SocketDescriptor* socketDescriptor = lookupSocketDescriptor(envir(), streams->fStreamSocketNum);// Tell it about our subChannel:socketDescriptor->registerRTPInterface(streams->fStreamChannelId, this);} }

    在 RTPInterface::startNetworkReading() 中則會向 TaskScheduler 注冊 RTCP 的 socket 及該 socket 上的事件的處理程序。live555 中正是通過這種方式,在有 RTCP 包到來時得到通知,并通過 RTCPInstance::incomingReportHandler() 來處理 RTCP 包的。

    RTCP 包的發送

    RTCP 包根據需要,由 RTCPInstance::sendReport() 等函數發送:

    void RTCPInstance::sendReport() { #ifdef DEBUGfprintf(stderr, "sending REPORT\n"); #endif// Begin by including a SR and/or RR report:if (!addReport()) return;// Then, include a SDES:addSDES();// Send the report:sendBuiltPacket();// Periodically clean out old members from our SSRC membership database:const unsigned membershipReapPeriod = 5;if ((++fOutgoingReportCount) % membershipReapPeriod == 0) {unsigned threshold = fOutgoingReportCount - membershipReapPeriod;fKnownMembers->reapOldMembers(threshold);} }void RTCPInstance::sendBYE() { #ifdef DEBUGfprintf(stderr, "sending BYE\n"); #endif// The packet must begin with a SR and/or RR report:(void)addReport(True);addBYE();sendBuiltPacket(); }void RTCPInstance::sendBuiltPacket() { #ifdef DEBUGfprintf(stderr, "sending RTCP packet\n");unsigned char* p = fOutBuf->packet();for (unsigned i = 0; i < fOutBuf->curPacketSize(); ++i) {if (i%4 == 0) fprintf(stderr," ");fprintf(stderr, "%02x", p[i]);}fprintf(stderr, "\n"); #endifunsigned reportSize = fOutBuf->curPacketSize();fRTCPInterface.sendPacket(fOutBuf->packet(), reportSize);fOutBuf->resetOffset();fLastSentSize = IP_UDP_HDR_SIZE + reportSize;fHaveJustSentPacket = True;fLastPacketSentSize = reportSize; }

    就像在 StreamState::startPlaying() 中看到的那樣。

    Done.

    live555 源碼分析系列文章

    live555 源碼分析:簡介
    live555 源碼分析:基礎設施
    live555 源碼分析:MediaSever
    Wireshark 抓包分析 RTSP/RTP/RTCP 基本工作過程
    live555 源碼分析:RTSPServer
    live555 源碼分析:DESCRIBE 的處理
    live555 源碼分析:SETUP 的處理
    live555 源碼分析:PLAY 的處理
    live555 源碼分析:RTSPServer 組件結構
    live555 源碼分析:ServerMediaSession
    live555 源碼分析:子會話 SDP 行生成
    live555 源碼分析:子會話 SETUP
    live555 源碼分析:播放啟動

    總結

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

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

    中文一区在线 | 国内精品视频免费 | 国产啊v在线观看 | 在线观看免费视频你懂的 | 中文资源在线官网 | 日日日日干 | 欧美日韩国产精品一区二区三区 | 久久精品成人 | 黄色三级网站 | bbbbb女女女女女bbbbb国产 | 视频一区二区国产 | 国产视频久久久久 | 亚洲黄色片 | 久草国产精品 | 久久a国产 | 最近中文字幕大全 | 天天色天天爱天天射综合 | 成人国产电影在线观看 | 久久精品国产亚洲精品2020 | 欧美视频在线二区 | 国产精品 日韩精品 | 91日韩国产| a级片网站 | 91麻豆视频 | 色婷婷97| 丁香花五月 | 久草久| 一二三久久久 | 美女国产精品 | 成人av在线影视 | 国产手机在线观看视频 | 国产伦精品一区二区三区无广告 | 久久理论影院 | 国产高清免费在线观看 | 国产精品久久99综合免费观看尤物 | 国产黄影院色大全免费 | 亚洲 欧洲 国产 日本 综合 | 久久久久久蜜桃一区二区 | 免费精品视频在线观看 | 久久免费观看少妇a级毛片 久久久久成人免费 | 91精品国产高清 | 日韩中文字幕亚洲一区二区va在线 | 久久午夜网 | 四虎影视精品成人 | 欧美激情亚洲综合 | 91av在线国产 | 91精品国产自产老师啪 | 国产精品久久久久久高潮 | 午夜国产福利在线 | 国产精品视频免费看 | 亚洲精品美女视频 | 久草在线视频看看 | 欧美日韩视频一区二区 | 激情五月***国产精品 | 国内一级片在线观看 | 日韩在线精品 | 成人欧美一区二区三区在线观看 | 日韩美女高潮 | 久久人人爽人人 | av黄色免费在线观看 | 国产中文字幕国产 | 国产精品久久久久久久久岛 | 久久久久久久久久久影视 | 99爱视频在线观看 | 综合视频在线 | 黄色av免费电影 | 毛片随便看 | 亚洲激情在线观看 | 99精品欧美一区二区蜜桃免费 | 91av在线视频免费观看 | 99亚洲精品| 超碰日韩 | 精品国产一区二区在线 | 96av麻豆蜜桃一区二区 | 国产视频在线观看一区二区 | 中文资源在线观看 | 久久夜色精品国产亚洲aⅴ 91chinesexxx | 狠狠网亚洲精品 | 日韩国产欧美视频 | 亚洲最大的av网站 | 在线播放日韩av | 久久久久久久久久久黄色 | 免费a网 | 91丨九色丨勾搭 | 永久免费毛片 | 精品美女国产在线 | 日韩激情在线视频 | 成人午夜影院在线观看 | 草久草久 | 国产精品va在线 | 久久精品一区八戒影视 | 91看片黄色 | 五月天婷婷免费视频 | 色婷婷六月天 | 国产午夜精品免费一区二区三区视频 | 欧美一二三视频 | 亚洲成人软件 | aaa亚洲精品一二三区 | 91污污| 人人狠狠综合久久亚洲 | 91中文字幕网 | www.在线观看av | 黄色大片入口 | av资源在线看 | 日本在线观看一区二区 | 国产成人精品久久亚洲高清不卡 | 免费av观看 | 91九色蝌蚪国产 | 久久精品中文字幕免费mv | 国产福利91精品张津瑜 | 亚洲成人黄色av | 国产精品av一区二区 | 欧美九九九 | 99精品国产一区二区三区麻豆 | 2022国产精品视频 | 亚洲精品乱码 | 色综合色综合久久综合频道88 | 不卡电影免费在线播放一区 | 成人国产一区二区 | 欧美一二区在线 | 最新av在线播放 | 午夜久久福利 | 日日干美女| 久久亚洲精品国产亚洲老地址 | 免费精品国产va自在自线 | 99久久一区| 97成人精品视频在线观看 | 在线观看视频在线观看 | 99精品欧美一区二区 | 婷婷日日| 人人澡人人澡人人 | 中文字幕九九 | 久久久国产精品一区二区中文 | 亚洲国产中文字幕在线视频综合 | 91精品综合在线观看 | 欧美色图亚洲图片 | 日本中文字幕在线播放 | 精品久久免费 | 成人小视频在线播放 | 婷婷av资源| 久久色视频 | av在线电影免费观看 | 中文字幕成人一区 | 欧美在线一二 | 国产亚洲免费的视频看 | 日韩精品视频免费 | 在线观看视频国产 | 日韩区在线观看 | 日韩女同一区二区三区在线观看 | 久久久伊人网 | 国产91国语对白在线 | 在线观看视频国产一区 | 亚洲va欧洲va国产va不卡 | 亚av在线| 人人草在线观看 | 国产成人在线一区 | 欧美夫妻性生活电影 | 国产婷婷视频在线 | 永久免费毛片 | 国产精品嫩草影视久久久 | 国产va在线观看免费 | 国产亚洲精品久久19p | 国产成人久久av | 久99久久| 国产日韩视频在线播放 | 久久国产精品久久精品国产演员表 | 亚洲国产成人久久综合 | 国产在线观看高清视频 | 免费精品视频在线观看 | 国产精品成人aaaaa网站 | 91尤物国产尤物福利在线播放 | 99色视频 | 国产成人性色生活片 | 日韩在线观看你懂的 | 午夜精品视频免费在线观看 | 人人舔人人爽 | 黄色三级久久 | 久久久在线免费观看 | 五月婷婷天堂 | 欧美性久久久久久 | 超碰97免费 | 黄色福利网 | 国产精品丝袜久久久久久久不卡 | 成年人在线视频观看 | 久久精品视频2 | 国产精品久久久久久久久久三级 | 中文字幕 国产视频 | 精品999久久久 | 日韩高清三区 | 免费黄色在线播放 | 日韩欧美综合精品 | 国产精品 日韩 | 丁香激情综合国产 | 日韩爱爱片 | 毛片网站在线 | 国产又黄又爽无遮挡 | 美女av免费看 | 精品无人国产偷自产在线 | 99精品福利 | 在线观看视频免费播放 | 久久中文网 | 欧美成年性 | 天天综合网在线观看 | 成人一级黄色片 | 一区二区影视 | 精品久久久久久久 | 精品v亚洲v欧美v高清v | 免费在线黄色av | 日韩高清一二区 | 国产亚洲人 | 99欧美视频| 精品96久久久久久中文字幕无 | 日韩在线观看视频免费 | 日本在线观看一区二区 | 91九色自拍 | 亚洲三级在线免费观看 | 天天操天天怕 | 三级性生活视频 | 国产精品免费在线播放 | 在线免费av播放 | 一区二区三区影院 | 中文字幕久久精品 | 伊人久久国产精品 | 成人免费视频观看 | 久久精品99精品国产香蕉 | 亚洲一区二区精品3399 | 日日麻批40分钟视频免费观看 | 中文字幕日韩一区二区三区不卡 | 91豆麻精品91久久久久久 | 久99视频 | 狠狠狠狠狠狠狠干 | 国产精品成人av电影 | 337p日本欧洲亚洲大胆裸体艺术 | 免费三级大片 | 97超碰在线资源 | 99精品视频免费观看 | 6080yy午夜一二三区久久 | 黄网站大全 | 免费看的黄色网 | 欧美性脚交 | 99资源网 | 中文字幕大全 | 国产成人精品一区二区在线观看 | 成人资源在线 | 99亚洲国产精品 | 国产69精品久久app免费版 | 日韩在线观看视频免费 | 国产伦精品一区二区三区免费 | 97天天综合网| 国产色视频123区 | 亚洲人成在线观看 | 在线观看91网站 | 黄色网址中文字幕 | 色视频 在线 | 久视频在线| 久久久久99精品成人片三人毛片 | 亚洲dvd| 激情五月播播久久久精品 | 精品播放 | 超碰在线免费福利 | 欧美怡红院视频 | 久久综合狠狠综合久久狠狠色综合 | 九色琪琪久久综合网天天 | 在线国产能看的 | 国产一级片毛片 | 久久人人精| 黄色a一级片 | 精品美女久久久久 | 国产亚洲一区 | 91九色国产 | 日韩一级电影在线观看 | 欧美日韩久久一区 | 久久99精品久久久久久清纯直播 | 久久伊人91| 欧美一区二区三区四区夜夜大片 | 亚洲一级片免费观看 | 日韩av资源站 | 成人作爱视频 | 狠狠躁夜夜a产精品视频 | 最近的中文字幕大全免费版 | 久久一精品 | 国产精品久久久久久久久久妇女 | 免费三级黄 | 国产第一页在线播放 | 久草精品国产 | 亚洲精品中文字幕在线观看 | 色综合久久88色综合天天人守婷 | 国产成人在线综合 | 一级黄色电影网站 | 天天操夜夜叫 | 久久一区二区三区四区 | 国产精品免费久久久久久久久久中文 | 91九色porn在线资源 | www色婷婷com| 亚洲第一av在线 | 亚洲视频久久久久 | x99av成人免费 | 伊人久久婷婷 | 久久av免费电影 | 国产一卡二卡在线 | 日韩精品视频第一页 | 深爱婷婷网 | 2024av| 黄色网中文字幕 | 久久99国产视频 | 日本精品在线视频 | 奇米影视777影音先锋 | 亚洲一级理论片 | 亚洲国产日韩欧美在线 | av电影一区 | 久久久精品 一区二区三区 国产99视频在线观看 | 国产97碰免费视频 | 中文字幕亚洲高清 | 五月婷婷综| 亚州精品国产 | 在线网站黄 | av导航福利| 国产精品免费在线播放 | 久久免费视屏 | 99久久久成人国产精品 | а中文在线天堂 | 国产一区二区在线精品 | 911精品视频 | 欧美少妇的秘密 | 久久久久欧美精品999 | 国产精品久久99综合免费观看尤物 | 好看av在线| 久久桃花网| 91大神在线观看视频 | 欧美精品久久久 | 69av视频在线观看 | 永久免费精品视频网站 | 亚洲成a人片77777kkkk1在线观看 | 成人在线免费观看网站 | 国产精品麻豆三级一区视频 | 亚洲粉嫩av| 国产69久久| 国产免费又粗又猛又爽 | 一级a性色生活片久久毛片波多野 | 国产不卡一二三区 | 国产精品日韩高清 | 香蕉视频在线网站 | 就要干b | 激情五月在线视频 | 波多野结衣在线观看一区 | 国产一区二区三区免费在线 | 久草在线视频网 | 国产又粗又猛又爽又黄的视频先 | 国产一级免费电影 | www色综合| 天天操综 | 国产网站在线免费观看 | 精品国产一区二区三区久久久蜜月 | a级一a一级在线观看 | 国语自产偷拍精品视频偷 | 一区二区三区四区免费视频 | 玖玖在线资源 | 国产中文字幕三区 | 婷婷综合影院 | 亚洲精品久久久久久国 | 国产中文字幕网 | 婷婷色狠狠 | 三三级黄色片之日韩 | 久久久久久免费网 | 久久久蜜桃一区二区 | 色鬼综合网 | 久久久久久福利 | 久久综合久久综合久久 | 国产成人精品久久久久 | 日韩成人中文字幕 | 777奇米四色 | 96超碰在线 | 国产剧在线观看片 | 亚洲精品成人 | a爱爱视频| 九九99视频 | 国产精品久久久久久久久久久久久久 | 日本高清久久久 | 亚洲欧洲国产日韩精品 | 精品国产伦一区二区三区观看体验 | 91在线色| 欧美日韩一级在线 | 欧美午夜激情网 | 日韩精品久久久久久 | 中日韩三级视频 | 江苏妇搡bbbb搡bbbb | 99一区二区三区 | 91精品成人久久 | 国产一区二区高清不卡 | 婷婷网站天天婷婷网站 | 91精品国产综合久久久久久久 | 免费观看特级毛片 | 欧美另类重口 | 欧美激情综合五月色丁香 | 午夜av影院 | 色橹橹欧美在线观看视频高清 | 国产在线观看h | 在线观看亚洲专区 | 国产一区二区三区视频在线 | 亚洲三级精品 | 天天综合狠狠精品 | 91九色网址 | 婷婷综合亚洲 | 黄色www | 欧美日本中文字幕 | 丁香激情网 | 在线一二三区 | 99在线观看 | 岛国av在线不卡 | 日本精品久久久久 | 一本一本久久a久久精品牛牛影视 | 最新av在线播放 | 国产在线观看你懂得 | 91精品影视 | 在线亚洲播放 | 欧洲一区二区三区精品 | 天天躁日日躁狠狠躁 | 在线免费av网 | 99精品国产在热久久 | 天堂资源在线观看视频 | 久久久免费视频播放 | 免费av高清| 久久视频在线观看 | 国产精品久久久久久久电影 | 亚洲乱亚洲乱妇 | 91久久国产自产拍夜夜嗨 | 亚洲成人精品 | 国产一级91 | 中文字幕在线视频网站 | 国产一二区在线观看 | 99国产一区二区三精品乱码 | 免费精品在线观看 | 91九色视频在线播放 | 一级免费黄视频 | 丝袜美腿亚洲综合 | 精品999在线观看 | 伊人网综合在线观看 | 欧美成人亚洲成人 | 美女福利视频在线 | 8090yy亚洲精品久久 | 精品超碰| 91成人短视频在线观看 | 亚洲三级黄色 | 久久只有精品 | 亚洲影视九九影院在线观看 | 久久久蜜桃一区二区 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 激情综合中文娱乐网 | 国产九九在线 | 国产精品国产亚洲精品看不卡 | 日韩欧美一区二区三区在线 | av免费网站在线观看 | 中文资源在线观看 | 中文乱幕日产无线码1区 | 久久黄色小说 | 日日干av| 91九色国产视频 | 精品国产观看 | 日韩va欧美va亚洲va久久 | 久久国产精品99国产 | 午夜久久成人 | 国产99免费| 欧美电影黄色 | 在线观看的av | 欧美激情片在线观看 | 国产黄色在线网站 | www.夜夜干.com | 久久一级电影 | 国产成人精品综合久久久 | 亚洲国产丝袜在线观看 | 99在线国产 | 欧美在线视频一区二区三区 | 国产日产欧美在线观看 | 国产精品久久久久久久免费大片 | 综合久久一本 | 福利av在线 | 国产高清免费av | 亚洲在线视频免费 | 天天射综合 | 国产精久久 | 精品免费| 国产免费av一区二区三区 | 美女视频黄色免费 | 成人动图 | 久久成人精品电影 | 亚洲午夜精品久久久久久久久 | 色橹橹欧美在线观看视频高清 | 又黄又爽的视频在线观看网站 | 日韩最新av在线 | 亚洲成aⅴ人在线观看 | 99精品视频在线看 | 国产亚洲激情视频在线 | 超碰在线个人 | 在线最新av | 亚洲黄色区 | 国产精品久久久久久999 | 国产中文字幕在线免费观看 | 黄色片视频免费 | 欧美日韩精品电影 | 欧美精品在线视频观看 | 中文字幕在线看片 | 色七七亚洲影院 | 中文字幕在线网 | 麻豆国产精品va在线观看不卡 | 久久福利剧场 | 久久亚洲免费 | av片在线观看免费 | 久 久久影院 | 99久久久国产精品 | 日韩久久久久久久久久久久 | 国产视频一二三 | 久草精品视频在线观看 | 成人a免费视频 | 国产成本人视频在线观看 | 亚洲成人av片在线观看 | 在线免费色视频 | 在线国产日韩 | av免费观看网址 | 久久婷婷精品视频 | www.91国产| 免费视频久久久 | 免费在线观看视频a | 色天天中文 | 国产美女精品视频 | 国产黄在线观看 | 国产精品九色 | av中文在线影视 | 久久久久久国产精品免费 | 亚洲电影影音先锋 | 精品一区中文字幕 | 人人爽人人做 | 六月丁香婷婷在线 | 九九涩涩av台湾日本热热 | 在线观看免费高清视频大全追剧 | 色黄久久久久久 | 精品 一区 在线 | 亚洲免费av电影 | 亚洲成熟女人毛片在线 | 天天操狠狠干 | 天天干天天摸 | 五月天伊人 | 亚洲 成人 一区 | 国产精品自产拍在线观看蜜 | 久久久久久草 | 久久久久久精 | 91人人爽久久涩噜噜噜 | 日韩av视屏在线观看 | 狠狠插天天干 | 在线观看视频99 | 国产色啪 | 国产免费亚洲 | 久久精品久久精品 | 免费性网站 | а天堂中文最新一区二区三区 | 日韩av在线小说 | 2021国产在线 | 日韩免费观看av | 亚洲国产欧美在线人成大黄瓜 | 国产玖玖视频 | 中文字幕av有码 | 久久久久久久久久影视 | 国内丰满少妇猛烈精品播放 | 国产粉嫩在线 | 免费精品国产va自在自线 | 69视频在线 | 国产最新网站 | 亚洲伦理中文字幕 | 最近中文字幕mv | 久久乱码卡一卡2卡三卡四 五月婷婷久 | ww视频在线观看 | 亚洲清纯国产 | 国产精品免费不卡 | 日韩高清在线一区二区三区 | 97人人看 | 久久午夜鲁丝片 | 激情偷乱人伦小说视频在线观看 | 国产精品手机在线 | 国产高清在线 | 亚洲成av人影院 | 欧美成亚洲 | 欧美日韩伦理在线 | 国产精品一区二区免费视频 | 久热精品国产 | 丁香综合激情 | 超碰在线人 | 97精品超碰一区二区三区 | 亚洲国产网站 | 顶级bbw搡bbbb搡bbbb | 黄色亚洲精品 | 一本一本久久a久久精品综合妖精 | 日日操日日插 | 亚洲人xxx| 91天堂素人约啪 | 日韩伦理片hd | av免费在线观看网站 | 精品一区二区久久久久久久网站 | 91av免费观看| 国产91丝袜在线播放动漫 | 爱色婷婷 | 亚洲国产精久久久久久久 | 色综合久久久久综合 | 欧美国产一区在线 | 美女视频黄是免费的 | av中文在线 | 嫩模bbw搡bbbb搡bbbb | 国产精品福利在线播放 | 国产精品久久久久久麻豆一区 | 国产一级a毛片视频爆浆 | 欧美日韩裸体免费视频 | 日韩r级在线 | 在线国产日本 | 美女国内精品自产拍在线播放 | 99色在线观看视频 | 日日干激情五月 | 成人av一区二区兰花在线播放 | 国产群p视频 | 97在线视| 天天综合网在线观看 | 日韩一区二区三区视频在线 | 又爽又黄又刺激的视频 | 日韩精品久久久久久中文字幕8 | 深爱激情婷婷网 | 精品视频中文字幕 | 奇米影视777四色米奇影院 | 国产喷水在线 | 久久久久国产精品免费网站 | 欧美黄色成人 | 国产精品去看片 | 欧美日韩网站 | 日本丶国产丶欧美色综合 | 久久成人午夜视频 | 欧美视频在线观看免费网址 | 色网免费观看 | 国产精品综合久久久久 | 日韩欧美区 | 中文字幕av网站 | 少妇视频一区 | 日韩免费在线观看视频 | 一区二区三区中文字幕在线 | 亚洲理论视频 | 国产一区二区三区免费观看视频 | 操操操日日日 | 日本天天操 | 久久精品视频在线看 | 在线成人高清电影 | 日本中文字幕网站 | 亚洲 欧洲 国产 日本 综合 | 日韩欧美在线综合网 | 欧美精品中文在线免费观看 | av免费看在线 | 久久亚洲综合国产精品99麻豆的功能介绍 | 麻花豆传媒mv在线观看 | 日本一区二区不卡高清 | 国产高清成人av | 黄网站色视频免费观看 | 人人插人人爱 | 在线观看中文 | 成人羞羞视频在线观看免费 | 男女啪啪免费网站 | 丁香狠狠 | 亚洲一级在线观看 | 国产特级毛片aaaaaaa高清 | 国产精品黄色 | 日韩欧美精品一区 | 日韩一区二区在线免费观看 | 国产视频一区在线 | 精品少妇一区二区三区在线 | 91视频麻豆视频 | 久草视频在线资源 | 中文字幕美女免费在线 | 综合在线观看色 | 国产精品久久久久久久久久久久午夜片 | 91九色免费视频 | 亚洲精品视频第一页 | 九色精品免费永久在线 | 婷婷国产在线观看 | 国产免费一区二区三区网站免费 | 精品久久久精品 | 国产乱对白刺激视频在线观看女王 | 超碰人人乐| 亚洲视频专区在线 | 久久99中文字幕 | av在线电影网站 | 成人全视频免费观看在线看 | 97视频一区 | 免费美女久久99 | 国产黄色美女 | 蜜桃传媒一区二区 | 国产精品6 | av天天澡天天爽天天av | 国产一区视频免费在线观看 | 精品在线视频一区二区三区 | 99视频在线精品国自产拍免费观看 | 激情欧美一区二区三区 | 天天插天天色 | 国产精品剧情 | 91成人精品一区在线播放69 | 射九九 | 日韩一区二区免费视频 | 最近中文字幕国语免费av | 亚洲婷婷综合色高清在线 | 韩国av一区二区三区 | 久久久首页 | 少妇性xxx| 国产精品中文在线 | 久久综合狠狠综合 | 久久免费公开视频 | zzijzzij亚洲日本少妇熟睡 | 五月婷婷亚洲 | 91精品视频免费在线观看 | 天天操天天能 | 精品国产资源 | 好看的国产精品视频 | 伊人色综合久久天天网 | 久久精品久久久久电影 | 91九色视频观看 | 久久成年人 | av免费网站观看 | 日韩欧美黄色网址 | 天天色天天爱天天射综合 | 蜜臀av性久久久久av蜜臀三区 | 亚洲小视频在线观看 | 五月开心婷婷网 | 亚洲视频在线观看网站 | 狠狠伊人 | www.久久91 | av高清免费在线 | 国产高清免费在线观看 | 亚洲综合成人专区片 | 九九99 | 97精品超碰一区二区三区 | 久久久久人人 | 日韩美在线观看 | 亚洲精品欧美视频 | 国产久草在线 | 日韩色在线观看 | 一区二区三区免费播放 | 91精品国自产拍天天拍 | 日韩午夜高清 | 天天天综合网 | 亚洲精品www. | 欧美日韩国产页 | 欧亚日韩精品一区二区在线 | 99久久精品国产系列 | 在线观看视频一区二区 | 日韩黄色中文字幕 | 99视| 久久www免费人成看片高清 | 成年人免费看的视频 | 97电影院在线观看 | 免费男女羞羞的视频网站中文字幕 | 国产高清绿奴videos | 热久久免费国产视频 | 国产精品视频永久免费播放 | 久久国产精品精品国产色婷婷 | 欧美精品乱码99久久影院 | 久久免费视频精品 | 久久爽久久爽久久av东京爽 | 亚洲爱视频 | 最新国产一区二区三区 | 亚洲综合最新在线 | 日韩精品中文字幕久久臀 | 亚洲国产剧情av | 东方av在| 91精品国产91 | 92精品国产成人观看免费 | 国产精品久久久久久久妇 | 久久66热这里只有精品 | avlulu久久精品| 久久欧美综合 | 狠狠色伊人亚洲综合成人 | 国产精品久久久久久高潮 | 久久伊人免费视频 | 国产精品美女久久久免费 | 国产精品免费久久久久久 | 国产1区2区 | 18国产精品白浆在线观看免费 | 成人在线观看影院 | 毛片美女网站 | 婷五月激情 | 看国产黄色片 | av免费观看网址 | 亚洲a色 | 啪一啪在线 | 婷婷在线网| 中文字幕 国产专区 | 在线天堂中文www视软件 | 蜜臀av性久久久久蜜臀aⅴ四虎 | 久久精品视频4 | 欧美日韩亚洲第一 | 日韩欧美综合 | 欧美成人性网 | 久久艹在线观看 | 亚洲免费在线视频 | 亚洲日本va在线观看 | 国产精品久久久久久久av电影 | 超碰在线亚洲 | 国产精品video爽爽爽爽 | 免费看黄网站在线 | 亚洲天天在线 | 亚洲精品在线播放视频 | 天天操天天射天天爽 | www日韩视频| 久久国产精品精品国产色婷婷 | 久久视频在线视频 | 亚洲 综合 精品 | 亚洲日本va午夜在线电影 | 色六月婷婷 | 国产美女在线精品免费观看 | 久久久国产精品电影 | 手机av电影在线 | av成人在线网站 | 欧美一级性视频 | 久久爱影视i | 欧美伦理一区二区三区 | 97福利在线 | 久久在线精品 | 亚洲电影影音先锋 | 国产视频在线观看一区 | 亚洲精品美女免费 | 欧美成人h版电影 | 天天操天天干天天爱 | 麻豆手机在线 | 91超级碰| 成人黄色在线观看视频 | 日韩免费观看视频 | wwwww.国产 | 91精品在线免费视频 | 美女啪啪图片 | 国产精品1区 | av品善网 | 在线免费av电影 | 99视频精品 | 久久精品欧美一区二区三区麻豆 | 亚洲国产天堂av | 日韩a免费 | 久久精品在线 | 国产精品露脸在线 | 国产一区精品在线 | 日批在线观看 | 色综合久久久久综合99 | 欧美另类交在线观看 | 欧美韩国日本在线观看 | 亚洲精品一区二区在线观看 | 国产精品久久久久一区二区国产 | 久久久久久久久久久电影 | 国产99久久久久 | 国产免费不卡 | 五月的婷婷 | 国产精品久久久久影视 | 免费特级黄色片 | 四虎成人精品在永久免费 | 中文国产成人精品久久一 | 一区二区三区免费播放 | 色综合久久88色综合天天人守婷 | 成人黄色电影在线播放 | 日韩中文字幕91 | 国产综合片 | 婷婷丁香六月 | 成人理论在线观看 | 久久电影国产免费久久电影 | 日韩一区二区在线免费观看 | 9999激情| 亚洲国产精品第一区二区 | 亚洲专区一二三 | 2019中文最近的2019中文在线 | 精品一区三区 | 亚洲国产精品日韩 | 欧美另类重口 | 国产这里只有精品 | 日韩精品在线观看av | 国产日韩欧美视频 | 综合天堂av久久久久久久 | 在线播放国产一区二区三区 | 婷婷色综合色 | 日韩在线视频网 | 日韩激情三级 | 色婷婷一区 | 91中文字幕 | 91成人精品在线 | 狠狠狠干狠狠 | 国产乱码精品一区二区三区介绍 | 国产亚洲欧美日韩高清 | 亚洲精品影院在线观看 | a天堂一码二码专区 | 天天色综合久久 | 91少妇精拍在线播放 | 成年人免费看av | 奇米先锋 | 五月天天色| 免费精品在线观看 | 波多野结衣电影一区 | 色综合久久久网 | 婷婷视频在线播放 | 国产精品成人免费精品自在线观看 | 久久久久久久久影院 | 亚洲国产激情 | 蜜桃视频日韩 | 国产精品欧美日韩在线观看 | 六月婷婷网 | 久久久久蜜桃 | www.狠狠色 | 日韩高清免费在线观看 | 国产又黄又硬又爽 | 伊人久操 | 日本久久精 | 美女久久久久久久 | 麻豆视频免费入口 | 国产视频欧美视频 | 久久久久久久久毛片 | 日韩av有码在线 | 中文在线天堂资源 | 国产大片黄色 | 婷婷午夜 | 欧美福利在线播放 | 在线小视频 | 久草久草久草久草 | 美女视频黄是免费的 | 久久视频精品在线 | 国产精品永久久久久久久久久 | 国语精品久久 | 日本久久片 | 91成人在线观看喷潮 | 免费看片黄色 | 婷婷色中文 | 欧美一级电影在线观看 | 久久国产精品免费一区 | 亚洲理论影院 | 四虎国产精品成人免费影视 | 欧美国产高清 | 深爱激情婷婷网 | 人人爽人人澡人人添人人人人 | 人人插人人费 | 91免费高清观看 | 久久久久国产成人免费精品免费 | 中文字幕日韩一区二区三区不卡 | 久草剧场| 天天操夜夜想 | 在线播放一区二区三区 | 国产又粗又长又硬免费视频 | 狠狠久久伊人 | 国产一级免费视频 | 久久精品xxx| 91中文字幕一区 | 久久99国产精品免费 | 国产精品一区二区久久久 | 又爽又黄又无遮挡网站动态图 | 午夜精品福利一区二区三区蜜桃 | 97精品国自产拍在线观看 | 国产成人一区二区三区电影 | 欧美乱大交 | 欧美a视频 | 天天操天天干天天爽 | 色噜噜在线观看视频 | 国产精品密入口果冻 | 国产女教师精品久久av | 亚洲国产中文在线 | 中文字幕乱码电影 | 久久久视频在线 | 国产一区二区在线视频观看 | 精品视频免费久久久看 | 亚洲精品久久久蜜桃直播 | 香蕉视频网站在线观看 | 97色在线 | 欧美精品在线观看 | 激情五月综合 | 国产视频手机在线 | 免费亚洲视频在线观看 | 97在线观视频免费观看 | 91精品国产91热久久久做人人 | 国产一级免费av | 欧美一级视频免费看 | 国产精品福利在线 | 亚洲精品国产麻豆 | 91av在线免费观看 | 国产免费二区 | 在线观看精品视频 | 在线成人高清电影 | 在线高清av | 精品福利在线视频 | 天天爱综合 | av在线官网| 日韩精品一区二区三区免费观看视频 | 亚洲国产中文在线观看 | 中文字幕二区 | 亚洲成av人电影 | 日本一区二区三区免费看 | 黄色av成人在线观看 | 中文字幕日韩精品有码视频 | 国产精品亚洲人在线观看 | 日本中文字幕系列 | 手机av资源 | 色综合www| 懂色av一区二区在线播放 | 就要干b| 五月婷婷六月丁香在线观看 | 国产成人精品一区二三区 | 久久精品99国产精品亚洲最刺激 |