Thanks for the LiveMedia Software! While receiving rtp over rtsp from my liveMedia server implementation, I receive bad timestamps every time when a new client starts to receive the same stream. I fixed this behaviour with the patch below.
This patch also gives me full control over the timestamps in the RTCP patckets, meaning the outgoing timestamps match exactly the values of fPresentationTime provided in doGetNextFrame(), which is good when retransmitting a received stream. I hope this helps soneone with the same problem. Yours, Johannes Gajdosik
diff -rp live.2016.08.07/liveMedia/OnDemandServerMediaSubsession.cpp live.2016.08.07.patched/liveMedia/OnDemandServerMediaSubsession.cpp *** live.2016.08.07/liveMedia/OnDemandServerMediaSubsession.cpp Mon Aug 8 00:45:22 2016 --- live.2016.08.07.patched/liveMedia/OnDemandServerMediaSubsession.cpp Wed Aug 10 15:00:43 2016 *************** void OnDemandServerMediaSubsession::star *** 221,227 **** RTPSink* rtpSink = streamState->rtpSink(); // alias if (rtpSink != NULL) { rtpSeqNum = rtpSink->currentSeqNo(); ! rtpTimestamp = rtpSink->presetNextTimestamp(); } } } --- 221,227 ---- RTPSink* rtpSink = streamState->rtpSink(); // alias if (rtpSink != NULL) { rtpSeqNum = rtpSink->currentSeqNo(); ! rtpTimestamp = rtpSink->getLastRtpTime(); } } } *************** void StreamState *** 512,517 **** --- 512,518 ---- 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); + fRTPSink->setRTCPInstance(fRTCPInstance); // Note: This starts RTCP running automatically fRTCPInstance->setAppHandler(fMaster.fAppHandlerTask, fMaster.fAppHandlerClientData); } *************** void StreamState *** 544,552 **** } 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) { --- 545,553 ---- } if (fRTCPInstance != NULL) { ! // Send an initial RTCP "SR" packet, before the initial RTP packet, so that receivers will be able to // get RTCP-synchronized presentation times immediately: ! if (fRTPSink && fRTPSink->framesReceived()) fRTCPInstance->sendReport(); } if (!fAreCurrentlyPlaying && fMediaSource != NULL) { diff -rp live.2016.08.07/liveMedia/PassiveServerMediaSubsession.cpp live.2016.08.07.patched/liveMedia/PassiveServerMediaSubsession.cpp *** live.2016.08.07/liveMedia/PassiveServerMediaSubsession.cpp Mon Aug 8 00:45:22 2016 --- live.2016.08.07.patched/liveMedia/PassiveServerMediaSubsession.cpp Wed Aug 10 15:00:43 2016 *************** PassiveServerMediaSubsession *** 34,39 **** --- 34,40 ---- ::PassiveServerMediaSubsession(RTPSink& rtpSink, RTCPInstance* rtcpInstance) : ServerMediaSubsession(rtpSink.envir()), fSDPLines(NULL), fRTPSink(rtpSink), fRTCPInstance(rtcpInstance) { + fRTPSink.setRTCPInstance(fRTCPInstance); fClientRTCPSourceRecords = HashTable::create(ONE_WORD_HASH_KEYS); } *************** void PassiveServerMediaSubsession::start *** 174,180 **** ServerRequestAlternativeByteHandler* /*serverRequestAlternativeByteHandler*/, void* /*serverRequestAlternativeByteHandlerClientData*/) { rtpSeqNum = fRTPSink.currentSeqNo(); ! rtpTimestamp = fRTPSink.presetNextTimestamp(); // Try to use a big send buffer for RTP - at least 0.1 second of // specified bandwidth and at least 50 KB --- 175,181 ---- ServerRequestAlternativeByteHandler* /*serverRequestAlternativeByteHandler*/, void* /*serverRequestAlternativeByteHandlerClientData*/) { rtpSeqNum = fRTPSink.currentSeqNo(); ! rtpTimestamp = fRTPSink.getLastRtpTime(); // Try to use a big send buffer for RTP - at least 0.1 second of // specified bandwidth and at least 50 KB diff -rp live.2016.08.07/liveMedia/RTCP.cpp live.2016.08.07.patched/liveMedia/RTCP.cpp *** live.2016.08.07/liveMedia/RTCP.cpp Mon Aug 8 00:45:22 2016 --- live.2016.08.07.patched/liveMedia/RTCP.cpp Wed Aug 10 15:00:43 2016 *************** Boolean RTCPInstance::addReport(Boolean *** 935,945 **** if (fSink != NULL) { if (!alwaysAdd) { if (!fSink->enableRTCPReports()) return False; - - // Hack: Don't send a SR during those (brief) times when the timestamp of the - // next outgoing RTP packet has been preset, to ensure that that timestamp gets - // used for that outgoing packet. (David Bertrand, 2006.07.18) - if (fSink->nextTimestampHasBeenPreset()) return False; } addSR(); --- 935,940 ---- *************** void RTCPInstance::addSR() { *** 965,977 **** // Insert the NTP and RTP timestamps for the 'wallclock time': struct timeval timeNow; ! gettimeofday(&timeNow, NULL); fOutBuf->enqueueWord(timeNow.tv_sec + 0x83AA7E80); // NTP timestamp most-significant word (1970 epoch -> 1900 epoch) double fractionalPart = (timeNow.tv_usec/15625.0)*0x04000000; // 2^32/10^6 fOutBuf->enqueueWord((unsigned)(fractionalPart+0.5)); // NTP timestamp least-significant word ! unsigned rtpTimestamp = fSink->convertToRTPTimestamp(timeNow); fOutBuf->enqueueWord(rtpTimestamp); // RTP ts // Insert the packet and byte counts: --- 960,981 ---- // Insert the NTP and RTP timestamps for the 'wallclock time': struct timeval timeNow; ! ! // gettimeofday(&timeNow, NULL); ! // const long long int time_diff = timeNow.tv_sec*1000000LL + timeNow.tv_usec - fSink->getLastFrameTime(); ! // const u_int32_t timestampIncrement = (fSink->rtpTimestampFrequency()*(1e-6)*time_diff + 0.5); // note: rounding ! ! timeNow.tv_sec = fSink->getLastFrameTime() / 1000000LL; ! timeNow.tv_usec = fSink->getLastFrameTime() - (timeNow.tv_sec*1000000LL); ! fOutBuf->enqueueWord(timeNow.tv_sec + 0x83AA7E80); // NTP timestamp most-significant word (1970 epoch -> 1900 epoch) double fractionalPart = (timeNow.tv_usec/15625.0)*0x04000000; // 2^32/10^6 fOutBuf->enqueueWord((unsigned)(fractionalPart+0.5)); // NTP timestamp least-significant word ! unsigned rtpTimestamp = fSink->getLastRtpTime() ! // + timestampIncrement ! ; fOutBuf->enqueueWord(rtpTimestamp); // RTP ts // Insert the packet and byte counts: Only in live.2016.08.07.patched/liveMedia: RTCP.cpp.orig diff -rp live.2016.08.07/liveMedia/RTPSink.cpp live.2016.08.07.patched/liveMedia/RTPSink.cpp *** live.2016.08.07/liveMedia/RTPSink.cpp Mon Aug 8 00:45:22 2016 --- live.2016.08.07.patched/liveMedia/RTPSink.cpp Wed Aug 10 15:00:43 2016 *************** along with this library; if not, write t *** 20,25 **** --- 20,26 ---- #include "RTPSink.hh" #include "GroupsockHelper.hh" + #include "RTCP.hh" ////////// RTPSink ////////// *************** RTPSink::RTPSink(UsageEnvironment& env, *** 51,57 **** : MediaSink(env), fRTPInterface(this, rtpGS), fRTPPayloadType(rtpPayloadType), fPacketCount(0), fOctetCount(0), fTotalOctetCount(0), ! fTimestampFrequency(rtpTimestampFrequency), fNextTimestampHasBeenPreset(False), fEnableRTCPReports(True), fNumChannels(numChannels), fEstimatedBitrate(0) { fRTPPayloadFormatName = strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName); --- 52,59 ---- : MediaSink(env), fRTPInterface(this, rtpGS), fRTPPayloadType(rtpPayloadType), fPacketCount(0), fOctetCount(0), fTotalOctetCount(0), ! rtcp_instance(0), ! fTimestampFrequency(rtpTimestampFrequency), frames_received(False), fEnableRTCPReports(True), fNumChannels(numChannels), fEstimatedBitrate(0) { fRTPPayloadFormatName = strDup(rtpPayloadFormatName == NULL ? "???" : rtpPayloadFormatName); *************** RTPSink::~RTPSink() { *** 75,114 **** } u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) { ! // Begin by converting from "struct timeval" units to RTP timestamp units: ! u_int32_t timestampIncrement = (fTimestampFrequency*tv.tv_sec); ! timestampIncrement += (u_int32_t)(fTimestampFrequency*(tv.tv_usec/1000000.0) + 0.5); // note: rounding ! ! // Then add this to our 'timestamp base': ! if (fNextTimestampHasBeenPreset) { ! // Make the returned timestamp the same as the current "fTimestampBase", ! // so that timestamps begin with the value that was previously preset: ! fTimestampBase -= timestampIncrement; ! fNextTimestampHasBeenPreset = False; } ! ! u_int32_t const rtpTimestamp = fTimestampBase + timestampIncrement; ! #ifdef DEBUG_TIMESTAMPS ! fprintf(stderr, "fTimestampBase: 0x%08x, tv: %lu.%06ld\n\t=> RTP timestamp: 0x%08x\n", ! fTimestampBase, tv.tv_sec, tv.tv_usec, rtpTimestamp); ! fflush(stderr); ! #endif ! ! return rtpTimestamp; ! } ! ! u_int32_t RTPSink::presetNextTimestamp() { ! struct timeval timeNow; ! gettimeofday(&timeNow, NULL); ! ! u_int32_t tsNow = convertToRTPTimestamp(timeNow); ! if (!groupsockBeingUsed().hasMultipleDestinations()) { ! // Don't adjust the timestamp stream if we already have another destination ongoing ! fTimestampBase = tsNow; ! fNextTimestampHasBeenPreset = True; ! } ! ! return tsNow; } void RTPSink::getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime) { --- 77,97 ---- } u_int32_t RTPSink::convertToRTPTimestamp(struct timeval tv) { ! /// gaj: this is only called by MultiFramedRTPSink.cpp with the frame time ! /// and not anymore by RTPSink with the wallclock time ! if (!frames_received) { // first time ! frames_received = True; ! last_frame_time = tv.tv_sec*1000000LL + tv.tv_usec; ! // send rtcp SR before any rtp packets ! if (rtcp_instance && fEnableRTCPReports) rtcp_instance->sendReport(); ! } else { ! const long long int t = tv.tv_sec*1000000LL + tv.tv_usec; ! const long long int time_diff = t - last_frame_time; ! const u_int32_t timestampIncrement = (fTimestampFrequency*(1e-6)*time_diff + 0.5); // note: rounding ! fTimestampBase += timestampIncrement; ! last_frame_time = t; } ! return fTimestampBase; } void RTPSink::getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime) { diff -rp live.2016.08.07/liveMedia/include/RTPSink.hh live.2016.08.07.patched/liveMedia/include/RTPSink.hh *** live.2016.08.07/liveMedia/include/RTPSink.hh Mon Aug 8 00:45:22 2016 --- live.2016.08.07.patched/liveMedia/include/RTPSink.hh Wed Aug 10 15:00:43 2016 *************** along with this library; if not, write t *** 29,34 **** --- 29,35 ---- #endif class RTPTransmissionStatsDB; // forward + class RTCPInstance; class RTPSink: public MediaSink { public: *************** public: *** 39,44 **** --- 40,47 ---- Groupsock const& groupsockBeingUsed() const { return *(fRTPInterface.gs()); } Groupsock& groupsockBeingUsed() { return *(fRTPInterface.gs()); } + void setRTCPInstance(RTCPInstance *i) {rtcp_instance = i;} + unsigned char rtpPayloadType() const { return fRTPPayloadType; } unsigned rtpTimestampFrequency() const { return fTimestampFrequency; } void setRTPTimestampFrequency(unsigned freq) { *************** public: *** 54,68 **** // optional SDP line (e.g. a=fmtp:...) u_int16_t currentSeqNo() const { return fSeqNo; } ! u_int32_t presetNextTimestamp(); ! // ensures that the next timestamp to be used will correspond to ! // the current 'wall clock' time. RTPTransmissionStatsDB& transmissionStatsDB() const { return *fTransmissionStatsDB; } ! Boolean nextTimestampHasBeenPreset() const { return fNextTimestampHasBeenPreset; } Boolean& enableRTCPReports() { return fEnableRTCPReports; } void getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime); --- 57,69 ---- // optional SDP line (e.g. a=fmtp:...) u_int16_t currentSeqNo() const { return fSeqNo; } ! u_int32_t getLastRtpTime(void) const {return fTimestampBase;} RTPTransmissionStatsDB& transmissionStatsDB() const { return *fTransmissionStatsDB; } ! Boolean framesReceived() const { return frames_received; } Boolean& enableRTCPReports() { return fEnableRTCPReports; } void getTotalBitrate(unsigned& outNumBytes, double& outElapsedTime); *************** protected: *** 103,108 **** --- 104,110 ---- friend class RTCPInstance; friend class RTPTransmissionStats; u_int32_t convertToRTPTimestamp(struct timeval tv); + long long int getLastFrameTime(void) const {return last_frame_time;} unsigned packetCount() const {return fPacketCount;} unsigned octetCount() const {return fOctetCount;} *************** private: *** 120,127 **** private: u_int32_t fSSRC, fTimestampBase; unsigned fTimestampFrequency; ! Boolean fNextTimestampHasBeenPreset; Boolean fEnableRTCPReports; // whether RTCP "SR" reports should be sent for this sink (default: True) char const* fRTPPayloadFormatName; unsigned fNumChannels; --- 122,131 ---- private: u_int32_t fSSRC, fTimestampBase; + long long int last_frame_time; + RTCPInstance *rtcp_instance; unsigned fTimestampFrequency; ! Boolean frames_received; Boolean fEnableRTCPReports; // whether RTCP "SR" reports should be sent for this sink (default: True) char const* fRTPPayloadFormatName; unsigned fNumChannels;
_______________________________________________ live-devel mailing list live-devel@lists.live555.com http://lists.live555.com/mailman/listinfo/live-devel