Live555 source code analysis-processing of the play command

Source: Internet
Author: User
Document directory
  • Play command Overview
  • 1. About ULR processing in the setup command request package
  • 2. handleapps_play (1.1), the play command processing function)
  • 3. About the playback speed parameter Scale (2.1)
  • 4. Set the playback time range (2.2)
  • 5. Start Playing (2.3)
Play command Overview

The play command must be executed after the setup command. Data is sent during the command processing and the rtcpinstance instance is created during the process of the play command.

The client can use the scale header field of the play command to specify the playback speed. However, this function depends on the server's specific implementation of the specific media. When sacale = 1, it can play normally, and when sacale> 1, It will be fast forward, sacale <0: return quickly.

The client can use the range header field of the play command to specify the playing time range. This function also depends on the specific implementation of specific media on the server.

The URL in the play command request has the following situations (same as pause, teardown, get_parameter, and set_parameter ):
1) Non-aggregation, such as rtsp: // 192.168.1.1/urlpresuffix/urlsuffix, urlpresuffix as stream name, And urlsuffix as the trackID of subsession
2) subsession can be found based on trackID only when the subsession is not aggregated.
3) aggregation, such
RTSP: // 192.168.1.1/urlpresuffix/urlsuffix, which uses urlsuffix as the stream name, while urlpresuffix ignores
RTSP: // 192.168.1.1/urlpresuffix, which only exists in urlpresuffix and uses it as the stream name. This should be the most common case.
4) aggregate, such as rtsp: // 192.168.1.1/urlpresuffix/urlsuffix, and use urlpresuffix/urlsuffix as the stream name
We can independently control the subsession in the session (this requires the trackID of the subsession), or control the entire session (this should be the most common case ).

Paste a setup message instance:

PLAY rtsp://192.168.9.80/123.mpg/ RTSP/1.0CSeq: 5Session: 263BD44BRange: npt=0.000-User-Agent: LibVLC/1.1.0 (LIVE555 Streaming Media v2010.03.16)response: RTSP/1.0 200 OKCSeq: 5Date: Wed, Nov 30 2011 06:55:07 GMTRange: npt=0.000-Session: 263BD44BRTP-Info: url=rtsp://192.168.9.80/123.mpg/track1;seq=38851;rtptime=1434098600,url=rtsp://192.168.9.80/123.mpg/track2;seq=27752;rtptime=3595585826

The code analysis process is cumbersome, so we should put summative things at the beginning.

1. About ULR processing in the setup command request package
Void rtspserver: rtspclientsession: Resolve (char const * handle name, char const * urlpresuffix, char const * urlsuffix, char const * CSeq, char const * fullrequeststr) {// non-aggregation, for example, RTSP: // 192.168.1.1/urlpresuffix/urlsuffix, urlpresuffix as stream name, And urlsuffix as trackID of subsession // subsession can be found based on trackID for aggregation, for example, // 1) rtsp: // 192.168.1.1/urlpresuffix/urlsuffix, which uses urlsuffix as the stream name, while urlp Resuffix ignore // 2) rtsp: // 192.168.1.1/urlpresuffix only exists in urlpresuffix and uses it as stream name. This should be the most common case of // aggregation, such as rtsp: // 192.168.1.1/urlpresuffix/urlsuffix, use urlpresuffix/urlsuffix as the stream name // This will either be: //-an operation on the entire server, if "urlpresuffix" is "", and "urlsuffix" is "*" (I. E ., the special "*" url), or //-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: servermediasubsessio N * subsession; If (urlpresuffix [0] = '\ 0' & urlsuffix [0] =' * '& urlsuffix [1] =' \ 0 ') {// an operation on the entire server. this works only for get_parameter and set_parameter: If (strcmp (parameter name, "get_parameter") = 0) {handleapps_get_parameter (null, CSeq, fullrequeststr);} else if (strcmp (parameter name, "set_parameter") = 0) {handleapps_set_parameter (null, CSeq, fullrequeststr);} else {hand Lecmd_notsupported (CSeq);} return;} else if (fourservermediasession = NULL) {// there wasn't a previous setup! Handlecmd_notsupported (CSeq); return;} else if (urlsuffix [0]! = '\ 0' & strcmp (fourservermediasession-> streamname (), urlpresuffix) = 0) {// non-aggregation, such as rtsp: // 192.168.1.1/urlpresuffix/urlsuffix, urlpresuffix is used as the stream name and urlsuffix is used as the trackID of the subsession. // subsession/non-aggregated operation can be found based on the trackID when it is not aggregated. // 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! Handleapps_notfound (CSeq); Return ;}} else if (strcmp (fourservermediasession-> streamname (), urlsuffix) = 0 | (urlsuffix [0] = '\ 0' & strcmp (fourservermediasession-> streamname (), urlpresuffix) = 0) {// aggregation, for example, // 1) rtsp: // 192.168.1.1/urlpresuffix/urlsuffix. Use urlsuffix as the stream name while ignore // 2) rtsp: // 192.168.1.1/urlpresuffix. Only urlpresuffix exists, and use it as stream name // aggregated operation subsessi On = NULL;} else if (urlpresuffix [0]! = '\ 0' & urlsuffix [0]! = '\ 0') {// aggregation, such as rtsp: // 192.168.1.1/urlpresuffix/urlsuffix. Use urlpresuffix/urlsuffix as the stream name // aggregated operation, // aggregated operation, if <urlpresuffix>/<urlsuffix> is the session (Stream) Name: Unsigned const sequence = strlen (sequence); If (strncmp (fourservermediasession-> streamname (), urlpresuffix, sequence) = 0 & fourservermediasession-> streamname () [urlpresuffixle N] = '/' & strcmp (& (fourservermediasession-> streamname () [urlpresuffixlen + 1], urlsuffix) = 0) {subsession = NULL ;} else {handlespon_notfound (CSeq); Return ;}} else {// The request doesn't match a known stream and/or track at all! Handle+_notfound (CSeq); return;} If (strcmp (partition name, "teardown") = 0) {handle+_teardown (subsession, CSeq);} else if (strcmp (partition name, "Play") = 0) {handlespon_play (subsession, CSeq, fullrequeststr);} else if (strcmp (Signature Name, "pause") = 0) {handlespon_pause (subsession, CSeq);} else if (strcmp (partition name, "get_parameter") = 0) {handleapps_get_parameter (subsession, CSeq, fullrequeststr);} else if (strcmp (partition name, "set_parameter") = 0) {handleapps_set_parameter (subsession, CSeq, fullrequeststr );}}
2. handleapps_play (1.1), the play command processing function)
Void rtspserver: rtspclientsession: handlepai_play (servermediasubsession * subsession, char const * CSeq, char const * fullrequeststr) {char * rtspurl = fourserver. rtspurl (fourservermediasession, fclientinputsocket); unsigned rtspurlsize = strlen (rtspurl); // analyzes the "scale:" header // scale header, indicating the playback speed, scale = 1: normal playback, greater than 1 fast forward, less than 0 indicates fast return // parse the client's "scale:" header, if any: Float scale; Boolean sawscaleheader = Parsescaleheader (fullrequeststr, scale); // test whether the scale value is satisfied, during this period, the scale value may be changed. // try to set the stream's scale factor to this value: If (subsession = NULL/* Aggregate op */) {// In the case of aggregation, subsession is not sure about fourservermediasession-> testscalefactor (scale); // test the scale value (see 2.1 )} 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); // analyze "range:" header // "range:" header, the time range of the video to be played. For example, if the range is not set to 0.000-, it is valid to see the end of the play request that does not include the range header field from the 0-moment. It starts playing from the beginning of the media stream until the media stream is paused // parse the client's "range:" header, if any: Double rangestart = 0.0, rangeend = 0.0; boolean sawrangeheader = parserangeheader (fullrequeststr, rangestart, rangeend); // other operations on the "range:" header... // The following creates the "RTP-Info:" Row // 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; // as required, perform seeking/scaling on each subsession // do any required seeking/scaling on each subsession, before starting streaming: for (I = 0; I <fnumstreamstates; ++ I) {If (subsession = NULL/* means: aggregated Opera Tion */| subsession = fstreamstates [I]. subsession) {If (sawscaleheader) {fstreamstates [I]. subsession-> setstreamscale (foursessionid, // set the scale value of subsession (see 2.1) fstreamstates [I]. streamtoken, scale);} If (sawrangeheader) {// streamduration 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; // shocould happen only if scale <0.0 in this case, perform the quick return operation} u_int64_t numbytes; fstreamstates [I]. subsession-> seekstream (foursessionid, // set the playing time range of each subsession (see 2.2) fstreamstates [I]. streamtoken, ran Gestart, 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 .) char * rangeheader; If (! Sawrangeheader) {Buf [0] = '\ 0'; // because we didn't see a range: Header, don't send one back} else if (rangeend = 0.0 & scale> = 0.0) {sprintf (BUF, "range: Treaty = %. 3f-\ r \ n ", rangestart);} else {sprintf (BUF," range: Treaty = %. 3f-%. 3f \ r \ n ", rangestart, rangeend);} rangeheader = strdup (BUF); // now the media data has finally been transmitted // now, start streaming: for (I = 0; I <fnumstreamstates; ++ I) {If (subsession = NULL /* Means: aggregated operation */| subsession = fstreamstates [I]. subsession) {unsigned short rt1_qnum = 0; unsigned rtptimestamp = 0; // start data transmission on each subsession, that is, start playing (see 2.3) fstreamstates [I]. subsession-> startstream (foursessionid, fstreamstates [I]. streamtoken, (taskfunc *) noteclientliveness, this, rt1_qnum, rtptimestamp, handlealternativerequestbyte, this); const char * urlsuffix = fstreamstates [I]. su Bsession-> 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]; // Add the information in the subsession to "RTP-Info:" sprintf (rtpinfo, rtpinfofmt, prevrtpinfo, numrtpinfoitems ++ = 0? "": ",", Rtspurl, urlsuffix, rt1_qnum, rtptimestamp); Delete [] prevrtpinfo ;}}// the following operations are performed to assemble the response package ...}
3. About the playback speed parameter Scale (2.1)

The scale parameter specifies the playback speed. Scale = 1 indicates normal playback. If it is greater than 1, fast forward. If it is smaller than 0, the system Returns quickly. Whether the scale requirement can be met depends on whether the server supports it. View the test functions in servermediasession.

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 subsession      minSSScale = 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 == maxSSScale    scale = 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: bestSSScale    scale = 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;}

The above function processing process has some complex locks, mainly to deal with the situation that each subsession supports different scales, in which case the value of scale is the closest to 1 (1 is the normal rate) will be selected ). The above function actually calls the testscalefactor function on subsession. It has the default implementation in the servermediasubsession class, as follows:

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

By default, only normal playback is supported. Check the setstreamscale function on subsession.

void ServerMediaSubsession::setStreamScale(unsigned /*clientSessionId*/,  void* /*streamToken*/, float /*scale*/) {  // default implementation: do nothing}

Do nothing! However, this is a virtual function that has been re-implemented in ndemandservermediasubsession.

Void ondemandservermediasubsession: setstreamscale (unsigned/* clientsessionid */, void * streamtoken, float scale) {// when multiple clients receive data from the same source, the sacale value cannot be changed. // changing the scale factor isn't allowed if multiple clients are processing data // from the same source: If (freusefirstsource) return; streamstate * streamstate = (streamstate *) streamtoken; If (streamstate! = NULL & streamstate-> mediasource ()! = NULL) {setstreamsourcescale (streamstate-> mediasource (), scale );}}

Continue to check the setstreamsourcescale Function

void OnDemandServerMediaSubsession::setStreamSourceScale(FramedSource* /*inputSource*/, float /*scale*/) {  // Default implementation: Do nothing}

In this way, if you want to implement fast forward/return operations, you only need to re-implement this function in your subsession.

4. Set the playback time range (2.2)

Seekstream is a virtual function of the servermediasubsession class. By default, do nothing is implemented. You can directly view the implementation in the ondemandservermediasubsession class.

Void ondemandservermediasubsession: seekstream (unsigned/* clientsessionid */, void * streamtoken, double & seektreaty, double streamduration, u_int64_t & numbytes) {numbytes = 0; // by default: unknown // when the same client corresponds to the same source, this operation is not allowed. // seeking isn't allowed if multiple clients are processing data from // The same source: if (freusefirstsource) return; streamstate * streamstate = (streamstate *) streamtoken; I F (streamstate! = NULL & streamstate-> mediasource ()! = NULL) {seekstreamsource (streamstate-> mediasource (), seektreaty, streamduration, numbytes );}}

Seekstreamsource is also a virtual function defined on ondemandservermediasubsession. The default implementation is also do nothing and needs to be implemented again in its own subclass. After reading the hsf-videofileservermediasubsession class, the seekstreamsource is not implemented. Therefore, the *. 264 file can only be viewed from the beginning each time it is opened.

5. Start Playing (2.3)

After so much work, I have finally begun playing the video...
The startstream function is a pure virtual function defined in the servermediasubsession class. First, let's look at the implementation of its subclass ondemandservermediasubsession.

Void authorization: startstream (unsigned clientsessionid, void * streamtoken, taskfunc * rtcprrhandler, void * handle, unsigned short & rt1_qnum, unsigned & rtptimestamp, comment * handle, void * handle) {streamstate * streamstate = (streamstate *) streamtoken; destinations * destination S = (destinations *) (fdestinationshashtable-> Lookup (char const *) clientsessionid); If (streamstate! = NULL) {streamstate-> startplaying (destinations, rtcprrhandler, rtcprrhandlerclientdata, serverrequestalternativebytehandler, listener); If (streamstate-> rtpsink ()! = NULL) {rt1_qnum = streamstate-> rtpsink ()-> currentseqno (); // What is the use of this rt1_qnum? Rtptimestamp = streamstate-> rtpsink ()-> presetnexttimestamp ();}}}

Here we call the streamstate function: startplaying.

Void streamstate: startplaying (destinations * dests, taskfunc * rtcprrhandler, void * rtcprrhandlerclientdata, serverrequestalternativebytehandler * handle, void * handle) {If (dests = NULL) return; // create an rtcpinstance instance if (frtcpinstance = NULL & frtpsink! = NULL) {// create (and start) A 'rtcp instance' for this RTP sink: frtcpinstance = rtcpinstance: createnew (frtpsink-> envir (), frtcpgs, ftotalbw, (unsigned char *) fmaster. fcname, frtpsink, null/* We're a server */); // Note: This starts RTCP running automatically} If (dests-> istcp) {// use TCP to transmit RTP and RTCP // change RTP and RTCP to use the TCP socket instead of UDP: If (frtpsink! = NULL) {frtpsink-> addstreamsocket (dests-> tcpsocketnum, dests-> rtpchannelid); frtpsink-> deny (dests-> tcpsocketnum, deny, deny);} If (frtcpinstance! = NULL) {frtcpinstance-> addstreamsocket (dests-> tcpsocketnum, dests-> rtcpchannelid); frtcpinstance-> deny (dests-> tcpsocketnum, dests-> rtcpchannelid, rtcprrhandler, rtcprrhandlerclientdata );}} else {// use UDP to transmit RTP, RTCP // tell the RTP and RTCP 'groupdocker' about this destination // (in case they don't already have it ): if (frtpgs! = NULL) frtpgs-> adddestination (dests-> ADDR, dests-> rtpport); If (frtcpgs! = NULL) frtcpgs-> adddestination (dests-> ADDR, dests-> rtcpport); If (frtcpinstance! = NULL) {frtcpinstance-> setspecificrrhandler (dests-> ADDR. s_addr, dests-> rtcpport, rtcprrhandler, rtcprrhandlerclientdata) ;}// the following calls the sdtartplaying function on the sink to start data transmission if (! Farecurrentlyplaying & fmediasource! = NULL) {If (frtpsink! = NULL) {// transmit frtpsink over RTP-> startplaying (* fmediasource, afterplayingstreamstate, this); farecurrentlyplaying = true;} else if (fudpsink! = NULL) {// raw UDP packet, without using the RTP protocol fudpsink-> startplaying (* fmediasource, afterplayingstreamstate, this); farecurrentlyplaying = true ;}}}

The preceding function finally calls the mediasink: startplaying function to start data transmission. The function calls in this process are complex and will be analyzed separately in the next article. The general process is to take a frame and send it out, then this function returns, and then process the other operations such as sending the response package.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.