liveMedia/T140TextRTPSink.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2013 Live Networks, Inc.  All rights reserved.
00018 // RTP sink for T.140 text (RFC 2793)
00019 // Implementation
00020 
00021 #include "T140TextRTPSink.hh"
00022 #include <GroupsockHelper.hh> // for "gettimeofday()"
00023 
00025 
00026 T140TextRTPSink::T140TextRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat)
00027   : TextRTPSink(env, RTPgs, rtpPayloadFormat, 1000/*mandatory RTP timestamp frequency for this payload format*/, "T140"),
00028     fOurIdleFilter(NULL), fAreInIdlePeriod(True) {
00029 }
00030 
00031 T140TextRTPSink::~T140TextRTPSink() {
00032   fSource = fOurIdleFilter; // hack: in case "fSource" had gotten set to NULL before we were called
00033   stopPlaying(); // call this now, because we won't have our 'idle filter' when the base class destructor calls it later.
00034 
00035   // Close our 'idle filter' as well:
00036   Medium::close(fOurIdleFilter);
00037   fSource = NULL; // for the base class destructor, which gets called next
00038 }
00039 
00040 T140TextRTPSink*
00041 T140TextRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
00042                             unsigned char rtpPayloadFormat) {
00043   return new T140TextRTPSink(env, RTPgs, rtpPayloadFormat);
00044 }
00045 
00046 Boolean T140TextRTPSink::continuePlaying() {
00047   // First, check whether we have an 'idle filter' set up yet. If not, create it now, and insert it in front of our existing source:
00048   if (fOurIdleFilter == NULL) {
00049     fOurIdleFilter = new T140IdleFilter(envir(), fSource);
00050   } else {
00051     fOurIdleFilter->reassignInputSource(fSource);
00052   }
00053   fSource = fOurIdleFilter;
00054 
00055   // Then call the parent class's implementation:
00056   return MultiFramedRTPSink::continuePlaying();
00057 }
00058 
00059 void T140TextRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/,
00060                                              unsigned char* /*frameStart*/,
00061                                              unsigned numBytesInFrame,
00062                                              struct timeval framePresentationTime,
00063                                              unsigned /*numRemainingBytes*/) {
00064   // Set the RTP 'M' (marker) bit if we have just ended an idle period - i.e., if we were in an idle period, but just got data:
00065   if (fAreInIdlePeriod && numBytesInFrame > 0) setMarkerBit();
00066   fAreInIdlePeriod = numBytesInFrame == 0;
00067 
00068   setTimestamp(framePresentationTime);
00069 }
00070 
00071 Boolean T140TextRTPSink::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/, unsigned /*numBytesInFrame*/) const {
00072   return False; // We don't concatenate input data; instead, send it out immediately
00073 }
00074 
00075 
00077 
00078 T140IdleFilter::T140IdleFilter(UsageEnvironment& env, FramedSource* inputSource)
00079   : FramedFilter(env, inputSource),
00080     fIdleTimerTask(NULL),
00081     fBufferSize(OutPacketBuffer::maxSize), fNumBufferedBytes(0) {
00082   fBuffer = new char[fBufferSize];
00083 }
00084 
00085 T140IdleFilter::~T140IdleFilter() {
00086   envir().taskScheduler().unscheduleDelayedTask(fIdleTimerTask);
00087 
00088   delete[] fBuffer;
00089   detachInputSource(); // so that the subsequent ~FramedFilter() doesn't delete it
00090 }
00091 
00092 #define IDLE_TIMEOUT_MICROSECONDS 300000 /* 300 ms */
00093 
00094 void T140IdleFilter::doGetNextFrame() {
00095   // First, see if we have buffered data that we can deliver:
00096   if (fNumBufferedBytes > 0) {
00097     deliverFromBuffer();
00098     return;
00099   }
00100 
00101   // We don't have any buffered data, so ask our input source for data (unless we've already done so).
00102   // But also set a timer to expire if this doesn't arrive promptly:
00103   fIdleTimerTask = envir().taskScheduler().scheduleDelayedTask(IDLE_TIMEOUT_MICROSECONDS, handleIdleTimeout, this);
00104   if (fInputSource != NULL && !fInputSource->isCurrentlyAwaitingData()) {
00105     fInputSource->getNextFrame((unsigned char*)fBuffer, fBufferSize, afterGettingFrame, this, onSourceClosure, this);
00106   }
00107 }
00108 
00109 void T140IdleFilter::afterGettingFrame(void* clientData, unsigned frameSize,
00110                                        unsigned numTruncatedBytes,
00111                                        struct timeval presentationTime,
00112                                        unsigned durationInMicroseconds) {
00113   ((T140IdleFilter*)clientData)->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
00114 }
00115 
00116 void T140IdleFilter::afterGettingFrame(unsigned frameSize,
00117                                        unsigned numTruncatedBytes,
00118                                        struct timeval presentationTime,
00119                                        unsigned durationInMicroseconds) {
00120   // First, cancel any pending idle timer:
00121   envir().taskScheduler().unscheduleDelayedTask(fIdleTimerTask);
00122 
00123   // Then note the new data that we have in our buffer:
00124   fNumBufferedBytes = frameSize;
00125   fBufferedNumTruncatedBytes = numTruncatedBytes;
00126   fBufferedDataPresentationTime = presentationTime;
00127   fBufferedDataDurationInMicroseconds = durationInMicroseconds;
00128 
00129   // Then, attempt to deliver this data.  (If we can't deliver it now, we'll do so the next time the reader asks for data.)
00130   if (isCurrentlyAwaitingData()) (void)deliverFromBuffer();
00131 }
00132 
00133 void T140IdleFilter::doStopGettingFrames() {
00134   // Cancel any pending idle timer:
00135   envir().taskScheduler().unscheduleDelayedTask(fIdleTimerTask);
00136 
00137   // And call the parent's implementation of this virtual function:
00138   FramedFilter::doStopGettingFrames();
00139 }
00140 
00141 void T140IdleFilter::handleIdleTimeout(void* clientData) {
00142   ((T140IdleFilter*)clientData)->handleIdleTimeout();
00143 }
00144 
00145 void T140IdleFilter::handleIdleTimeout() {
00146   // No data has arrived from the upstream source within our specified 'idle period' (after data was requested from downstream).
00147   // Send an empty 'idle' frame to our downstream "T140TextRTPSink".  (This will cause an empty RTP packet to get sent.)
00148   deliverEmptyFrame();
00149 }
00150 
00151 void T140IdleFilter::deliverFromBuffer() {
00152   if (fNumBufferedBytes <= fMaxSize) { // common case
00153     fNumTruncatedBytes = fBufferedNumTruncatedBytes;
00154     fFrameSize = fNumBufferedBytes;
00155   } else {
00156     fNumTruncatedBytes = fBufferedNumTruncatedBytes + fNumBufferedBytes - fMaxSize;
00157     fFrameSize = fMaxSize;
00158   }  
00159 
00160   memmove(fTo, fBuffer, fFrameSize);
00161   fPresentationTime = fBufferedDataPresentationTime;
00162   fDurationInMicroseconds = fBufferedDataDurationInMicroseconds;
00163 
00164   fNumBufferedBytes = 0; // reset buffer
00165   
00166   FramedSource::afterGetting(this); // complete delivery
00167 }
00168 
00169 void T140IdleFilter::deliverEmptyFrame() {
00170   fFrameSize = fNumTruncatedBytes = 0;
00171   gettimeofday(&fPresentationTime, NULL);
00172   FramedSource::afterGetting(this); // complete delivery
00173 }
00174 
00175 void T140IdleFilter::onSourceClosure(void* clientData) {
00176   ((T140IdleFilter*)clientData)->onSourceClosure();
00177 }
00178 
00179 void T140IdleFilter::onSourceClosure() {
00180   envir().taskScheduler().unscheduleDelayedTask(fIdleTimerTask);
00181   fIdleTimerTask = NULL;
00182 
00183   FramedSource::handleClosure(this);
00184 }

Generated on Mon Apr 29 13:28:03 2013 for live by  doxygen 1.5.2