liveMedia/DVVideoStreamFramer.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-2014 Live Networks, Inc.  All rights reserved.
00018 // A filter that parses a DV input stream into DV frames to deliver to the downstream object
00019 // Implementation
00020 // (Thanks to Ben Hutchings for his help, including a prototype implementation.)
00021 
00022 #include "DVVideoStreamFramer.hh"
00023 #include "GroupsockHelper.hh"
00024 
00026 
00027 DVVideoStreamFramer::DVVideoStreamFramer(UsageEnvironment& env, FramedSource* inputSource,
00028                                          Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified)
00029   : FramedFilter(env, inputSource),
00030     fLeavePresentationTimesUnmodified(leavePresentationTimesUnmodified),
00031     fOurProfile(NULL), fInitialBlocksPresent(False), fSourceIsSeekable(sourceIsSeekable) {
00032   fTo = NULL; // hack used when reading "fSavedInitialBlocks"
00033   // Use the current wallclock time as the initial 'presentation time':
00034   gettimeofday(&fNextFramePresentationTime, NULL);
00035 }
00036 
00037 DVVideoStreamFramer::~DVVideoStreamFramer() {
00038 }
00039 
00040 DVVideoStreamFramer*
00041 DVVideoStreamFramer::createNew(UsageEnvironment& env, FramedSource* inputSource,
00042                                Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified) {
00043   return new DVVideoStreamFramer(env, inputSource, sourceIsSeekable, leavePresentationTimesUnmodified);
00044 }
00045 
00046 // Define the parameters for the profiles that we understand:
00047 struct DVVideoProfile {
00048   char const* name;
00049   unsigned apt;
00050   unsigned sType;
00051   unsigned sequenceCount;
00052   unsigned channelCount;
00053   unsigned dvFrameSize; // in bytes (== sequenceCount*channelCount*(DV_NUM_BLOCKS_PER_SEQUENCE*DV_DIF_BLOCK_SIZE i.e. 12000))
00054   double frameDuration; // duration of the above, in microseconds.  (1000000/this == frame rate)
00055 };
00056 
00057 static DVVideoProfile const profiles[] = {
00058    { "SD-VCR/525-60",  0, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
00059    { "SD-VCR/625-50",  0, 0x00, 12, 1, 144000, 1000000/25.0 },
00060    { "314M-25/525-60", 1, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
00061    { "314M-25/625-50", 1, 0x00, 12, 1, 144000, 1000000/25.0 },
00062    { "314M-50/525-60", 1, 0x04, 10, 2, 240000, (1000000*1001)/30000.0 },
00063    { "314M-50/625-50", 1, 0x04, 12, 2, 288000, 1000000/25.0 },
00064    { "370M/1080-60i",  1, 0x14, 10, 4, 480000, (1000000*1001)/30000.0 },
00065    { "370M/1080-50i",  1, 0x14, 12, 4, 576000, 1000000/25.0 },
00066    { "370M/720-60p",   1, 0x18, 10, 2, 240000, (1000000*1001)/60000.0 },
00067    { "370M/720-50p",   1, 0x18, 12, 2, 288000, 1000000/50.0 },
00068    { NULL, 0, 0, 0, 0, 0, 0.0 }
00069   };
00070 
00071 
00072 char const* DVVideoStreamFramer::profileName() {
00073   if (fOurProfile == NULL) getProfile();
00074 
00075   return fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->name : NULL;
00076 }
00077 
00078 Boolean DVVideoStreamFramer::getFrameParameters(unsigned& frameSize, double& frameDuration) {
00079   if (fOurProfile == NULL) getProfile();
00080   if (fOurProfile == NULL) return False;
00081 
00082   frameSize = ((DVVideoProfile const*)fOurProfile)->dvFrameSize;
00083   frameDuration = ((DVVideoProfile const*)fOurProfile)->frameDuration;
00084   return True;
00085 }
00086 
00087 void DVVideoStreamFramer::getProfile() {
00088   // To determine the stream's profile, we need to first read a chunk of data that we can parse:
00089   fInputSource->getNextFrame(fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE,
00090                              afterGettingFrame, this, FramedSource::handleClosure, this);
00091   
00092   // Handle events until the requested data arrives:
00093   envir().taskScheduler().doEventLoop(&fInitialBlocksPresent);
00094 }
00095 
00096 Boolean DVVideoStreamFramer::isDVVideoStreamFramer() const {
00097   return True;
00098 }
00099 
00100 void DVVideoStreamFramer::doGetNextFrame() {
00101   fFrameSize = 0; // initially, until we deliver data
00102 
00103   // If we have saved initial blocks (and won't be seeking back to re-read this data), so use this data first.
00104   if (fInitialBlocksPresent && !fSourceIsSeekable) {
00105     // For simplicity, we require the downstream object's buffer to be >= this data's size:
00106     if (fMaxSize < DV_SAVED_INITIAL_BLOCKS_SIZE) {
00107       fNumTruncatedBytes = fMaxSize;
00108       afterGetting(this);
00109       return;
00110     }
00111 
00112     memmove(fTo, fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE);
00113     fFrameSize = DV_SAVED_INITIAL_BLOCKS_SIZE;
00114     fTo += DV_SAVED_INITIAL_BLOCKS_SIZE;
00115     fInitialBlocksPresent = False; // for the future
00116   }
00117     
00118   // Arrange to read the (rest of the) requested data.
00119   // (But first, make sure that we read an integral multiple of the DV block size.)
00120   fMaxSize -= fMaxSize%DV_DIF_BLOCK_SIZE;
00121   getAndDeliverData();
00122 }
00123 
00124 #define DV_SMALLEST_POSSIBLE_FRAME_SIZE 120000
00125 
00126 void DVVideoStreamFramer::getAndDeliverData() {
00127   unsigned const totFrameSize
00128     = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
00129   unsigned totBytesToDeliver = totFrameSize < fMaxSize ? totFrameSize : fMaxSize;
00130   unsigned numBytesToRead = totBytesToDeliver - fFrameSize;
00131 
00132   fInputSource->getNextFrame(fTo, numBytesToRead, afterGettingFrame, this, FramedSource::handleClosure, this);
00133 }
00134 
00135 void DVVideoStreamFramer::afterGettingFrame(void* clientData, unsigned frameSize,
00136                                             unsigned numTruncatedBytes,
00137                                             struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
00138   DVVideoStreamFramer* source = (DVVideoStreamFramer*)clientData;
00139   source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
00140 }
00141 
00142 #define DVSectionId(n) ptr[(n)*DV_DIF_BLOCK_SIZE + 0]
00143 #define DVData(n,i) ptr[(n)*DV_DIF_BLOCK_SIZE + 3+(i)]
00144 
00145 #define DV_SECTION_HEADER 0x1F
00146 #define DV_PACK_HEADER_10 0x3F
00147 #define DV_PACK_HEADER_12 0xBF
00148 #define DV_SECTION_VAUX_MIN 0x50
00149 #define DV_SECTION_VAUX_MAX 0x5F
00150 #define DV_PACK_VIDEO_SOURCE 60
00151 #ifndef MILLION
00152 #define MILLION 1000000
00153 #endif
00154 
00155 void DVVideoStreamFramer::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
00156   if (fOurProfile == NULL && frameSize >= DV_SAVED_INITIAL_BLOCKS_SIZE) {
00157     // (Try to) parse this data enough to figure out its profile.
00158     // We assume that the data begins on a (80-byte) block boundary, but not necessarily on a (150-block) sequence boundary.
00159     // We therefore scan each 80-byte block, until we find the 6-block header that begins a sequence:
00160     u_int8_t const* data = (fTo == NULL) ? fSavedInitialBlocks : fTo;
00161     for (u_int8_t const* ptr = data; ptr + 6*DV_DIF_BLOCK_SIZE <= &data[DV_SAVED_INITIAL_BLOCKS_SIZE]; ptr += DV_DIF_BLOCK_SIZE) {
00162       // Check whether "ptr" points to an appropriate header:
00163       u_int8_t const sectionHeader = DVSectionId(0);
00164       u_int8_t const sectionVAUX = DVSectionId(5);
00165       u_int8_t const packHeaderNum = DVData(0,0);
00166 
00167       if (sectionHeader == DV_SECTION_HEADER
00168           && (packHeaderNum == DV_PACK_HEADER_10 || packHeaderNum == DV_PACK_HEADER_12)
00169           && (sectionVAUX >= DV_SECTION_VAUX_MIN && sectionVAUX <= DV_SECTION_VAUX_MAX)) {
00170         // This data begins a sequence; look up the DV profile from this:
00171         u_int8_t const apt = DVData(0,1)&0x07;
00172         u_int8_t const sType = DVData(5,48)&0x1F;
00173         u_int8_t const sequenceCount = (packHeaderNum == DV_PACK_HEADER_10) ? 10 : 12;
00174 
00175         // Use these three parameters (apt, sType, sequenceCount) to look up the DV profile:
00176         for (DVVideoProfile const* profile = profiles; profile->name != NULL; ++profile) {
00177           if (profile->apt == apt && profile->sType == sType && profile->sequenceCount == sequenceCount) {
00178             fOurProfile = profile;
00179             break;
00180           }
00181         }
00182         break; // because we found a correct sequence header (even if we don't happen to define a profile for it)
00183       }
00184     }
00185   }
00186 
00187   if (fTo != NULL) { // There is a downstream object; complete delivery to it (or read more data, if necessary)
00188     unsigned const totFrameSize
00189       = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
00190     fFrameSize += frameSize;
00191     fTo += frameSize;
00192     fPresentationTime = presentationTime; // by default; may get changed below
00193 
00194     if (fFrameSize < totFrameSize && fFrameSize < fMaxSize && numTruncatedBytes == 0) {
00195       // We have more data to deliver; get it now:
00196       getAndDeliverData();
00197     } else {
00198       // We're done delivering this DV frame (but check for truncation):
00199       fNumTruncatedBytes = totFrameSize - fFrameSize;
00200 
00201       if (fOurProfile != NULL) {
00202         // Also set the presentation time, and increment it for next time,
00203         // based on the length of this frame:
00204         if (!fLeavePresentationTimesUnmodified) fPresentationTime = fNextFramePresentationTime;
00205 
00206         DVVideoProfile const* ourProfile =(DVVideoProfile const*)fOurProfile;
00207         double durationInMicroseconds = (fFrameSize*ourProfile->frameDuration)/ourProfile->dvFrameSize;
00208         fDurationInMicroseconds = (unsigned)durationInMicroseconds;
00209         fNextFramePresentationTime.tv_usec += fDurationInMicroseconds;
00210         fNextFramePresentationTime.tv_sec += fNextFramePresentationTime.tv_usec/MILLION;
00211         fNextFramePresentationTime.tv_usec %= MILLION;
00212       }
00213 
00214       afterGetting(this);
00215     }
00216   } else {
00217     // We read data into our special buffer; signal that it has arrived:
00218     fInitialBlocksPresent = True;
00219   }
00220 }

Generated on Wed Apr 23 16:11:59 2014 for live by  doxygen 1.5.2