DVVideoStreamFramer.cpp 9.43 KB
Newer Older
1 2 3
/**********
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
4
Free Software Foundation; either version 3 of the License, or (at your
5 6 7 8 9 10 11 12 13 14 15 16
option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)

This library is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
more details.

You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
**********/
// "liveMedia"
17
// Copyright (c) 1996-2019 Live Networks, Inc.  All rights reserved.
18 19
// A filter that parses a DV input stream into DV frames to deliver to the downstream object
// Implementation
20
// (Thanks to Ben Hutchings for his help, including a prototype implementation.)
21 22

#include "DVVideoStreamFramer.hh"
23
#include "GroupsockHelper.hh"
24 25 26

////////// DVVideoStreamFramer implementation //////////

27 28
DVVideoStreamFramer::DVVideoStreamFramer(UsageEnvironment& env, FramedSource* inputSource,
					 Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified)
29
  : FramedFilter(env, inputSource),
30
    fLeavePresentationTimesUnmodified(leavePresentationTimesUnmodified),
31
    fOurProfile(NULL), fInitialBlocksPresent(False), fSourceIsSeekable(sourceIsSeekable) {
32
  fTo = NULL; // hack used when reading "fSavedInitialBlocks"
33 34
  // Use the current wallclock time as the initial 'presentation time':
  gettimeofday(&fNextFramePresentationTime, NULL);
35 36 37 38 39 40
}

DVVideoStreamFramer::~DVVideoStreamFramer() {
}

DVVideoStreamFramer*
41 42 43
DVVideoStreamFramer::createNew(UsageEnvironment& env, FramedSource* inputSource,
			       Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified) {
  return new DVVideoStreamFramer(env, inputSource, sourceIsSeekable, leavePresentationTimesUnmodified);
44 45
}

46 47 48 49
// Define the parameters for the profiles that we understand:
struct DVVideoProfile {
  char const* name;
  unsigned apt;
50
  unsigned sType;
51 52
  unsigned sequenceCount;
  unsigned channelCount;
53 54
  unsigned dvFrameSize; // in bytes (== sequenceCount*channelCount*(DV_NUM_BLOCKS_PER_SEQUENCE*DV_DIF_BLOCK_SIZE i.e. 12000))
  double frameDuration; // duration of the above, in microseconds.  (1000000/this == frame rate)
55
};
56

57
static DVVideoProfile const profiles[] = {
58 59 60 61 62 63 64 65 66 67 68
   { "SD-VCR/525-60",  0, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
   { "SD-VCR/625-50",  0, 0x00, 12, 1, 144000, 1000000/25.0 },
   { "314M-25/525-60", 1, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
   { "314M-25/625-50", 1, 0x00, 12, 1, 144000, 1000000/25.0 },
   { "314M-50/525-60", 1, 0x04, 10, 2, 240000, (1000000*1001)/30000.0 },
   { "314M-50/625-50", 1, 0x04, 12, 2, 288000, 1000000/25.0 },
   { "370M/1080-60i",  1, 0x14, 10, 4, 480000, (1000000*1001)/30000.0 },
   { "370M/1080-50i",  1, 0x14, 12, 4, 576000, 1000000/25.0 },
   { "370M/720-60p",   1, 0x18, 10, 2, 240000, (1000000*1001)/60000.0 },
   { "370M/720-50p",   1, 0x18, 12, 2, 288000, 1000000/50.0 },
   { NULL, 0, 0, 0, 0, 0, 0.0 }
69
  };
70

71

72
char const* DVVideoStreamFramer::profileName() {
73
  if (fOurProfile == NULL) getProfile();
74

75 76 77 78 79 80 81 82 83 84 85 86 87
  return fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->name : NULL;
}

Boolean DVVideoStreamFramer::getFrameParameters(unsigned& frameSize, double& frameDuration) {
  if (fOurProfile == NULL) getProfile();
  if (fOurProfile == NULL) return False;

  frameSize = ((DVVideoProfile const*)fOurProfile)->dvFrameSize;
  frameDuration = ((DVVideoProfile const*)fOurProfile)->frameDuration;
  return True;
}

void DVVideoStreamFramer::getProfile() {
88 89 90 91 92 93
  // To determine the stream's profile, we need to first read a chunk of data that we can parse:
  fInputSource->getNextFrame(fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE,
			     afterGettingFrame, this, FramedSource::handleClosure, this);
  
  // Handle events until the requested data arrives:
  envir().taskScheduler().doEventLoop(&fInitialBlocksPresent);
94 95 96 97 98 99 100
}

Boolean DVVideoStreamFramer::isDVVideoStreamFramer() const {
  return True;
}

void DVVideoStreamFramer::doGetNextFrame() {
101 102
  fFrameSize = 0; // initially, until we deliver data

103 104
  // If we have saved initial blocks (and won't be seeking back to re-read this data), so use this data first.
  if (fInitialBlocksPresent && !fSourceIsSeekable) {
105 106 107 108 109 110 111 112 113
    // For simplicity, we require the downstream object's buffer to be >= this data's size:
    if (fMaxSize < DV_SAVED_INITIAL_BLOCKS_SIZE) {
      fNumTruncatedBytes = fMaxSize;
      afterGetting(this);
      return;
    }

    memmove(fTo, fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE);
    fFrameSize = DV_SAVED_INITIAL_BLOCKS_SIZE;
114
    fTo += DV_SAVED_INITIAL_BLOCKS_SIZE;
115 116 117 118
    fInitialBlocksPresent = False; // for the future
  }
    
  // Arrange to read the (rest of the) requested data.
119 120 121 122 123 124 125 126 127 128 129 130 131 132
  // (But first, make sure that we read an integral multiple of the DV block size.)
  fMaxSize -= fMaxSize%DV_DIF_BLOCK_SIZE;
  getAndDeliverData();
}

#define DV_SMALLEST_POSSIBLE_FRAME_SIZE 120000

void DVVideoStreamFramer::getAndDeliverData() {
  unsigned const totFrameSize
    = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
  unsigned totBytesToDeliver = totFrameSize < fMaxSize ? totFrameSize : fMaxSize;
  unsigned numBytesToRead = totBytesToDeliver - fFrameSize;

  fInputSource->getNextFrame(fTo, numBytesToRead, afterGettingFrame, this, FramedSource::handleClosure, this);
133 134 135 136
}

void DVVideoStreamFramer::afterGettingFrame(void* clientData, unsigned frameSize,
					    unsigned numTruncatedBytes,
137
					    struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
138
  DVVideoStreamFramer* source = (DVVideoStreamFramer*)clientData;
139
  source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
140 141
}

142 143
#define DVSectionId(n) ptr[(n)*DV_DIF_BLOCK_SIZE + 0]
#define DVData(n,i) ptr[(n)*DV_DIF_BLOCK_SIZE + 3+(i)]
144 145 146 147 148 149 150

#define DV_SECTION_HEADER 0x1F
#define DV_PACK_HEADER_10 0x3F
#define DV_PACK_HEADER_12 0xBF
#define DV_SECTION_VAUX_MIN 0x50
#define DV_SECTION_VAUX_MAX 0x5F
#define DV_PACK_VIDEO_SOURCE 60
151
#ifndef MILLION
152
#define MILLION 1000000
153
#endif
154

155
void DVVideoStreamFramer::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
156
  if (fOurProfile == NULL && frameSize >= DV_SAVED_INITIAL_BLOCKS_SIZE) {
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    // (Try to) parse this data enough to figure out its profile.
    // We assume that the data begins on a (80-byte) block boundary, but not necessarily on a (150-block) sequence boundary.
    // We therefore scan each 80-byte block, until we find the 6-block header that begins a sequence:
    u_int8_t const* data = (fTo == NULL) ? fSavedInitialBlocks : fTo;
    for (u_int8_t const* ptr = data; ptr + 6*DV_DIF_BLOCK_SIZE <= &data[DV_SAVED_INITIAL_BLOCKS_SIZE]; ptr += DV_DIF_BLOCK_SIZE) {
      // Check whether "ptr" points to an appropriate header:
      u_int8_t const sectionHeader = DVSectionId(0);
      u_int8_t const sectionVAUX = DVSectionId(5);
      u_int8_t const packHeaderNum = DVData(0,0);

      if (sectionHeader == DV_SECTION_HEADER
	  && (packHeaderNum == DV_PACK_HEADER_10 || packHeaderNum == DV_PACK_HEADER_12)
	  && (sectionVAUX >= DV_SECTION_VAUX_MIN && sectionVAUX <= DV_SECTION_VAUX_MAX)) {
	// This data begins a sequence; look up the DV profile from this:
	u_int8_t const apt = DVData(0,1)&0x07;
	u_int8_t const sType = DVData(5,48)&0x1F;
	u_int8_t const sequenceCount = (packHeaderNum == DV_PACK_HEADER_10) ? 10 : 12;

	// Use these three parameters (apt, sType, sequenceCount) to look up the DV profile:
	for (DVVideoProfile const* profile = profiles; profile->name != NULL; ++profile) {
	  if (profile->apt == apt && profile->sType == sType && profile->sequenceCount == sequenceCount) {
	    fOurProfile = profile;
	    break;
	  }
181
	}
182
	break; // because we found a correct sequence header (even if we don't happen to define a profile for it)
183 184 185 186
      }
    }
  }

187 188 189 190 191
  if (fTo != NULL) { // There is a downstream object; complete delivery to it (or read more data, if necessary)
    unsigned const totFrameSize
      = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
    fFrameSize += frameSize;
    fTo += frameSize;
192
    fPresentationTime = presentationTime; // by default; may get changed below
193 194 195 196 197 198 199 200 201 202 203

    if (fFrameSize < totFrameSize && fFrameSize < fMaxSize && numTruncatedBytes == 0) {
      // We have more data to deliver; get it now:
      getAndDeliverData();
    } else {
      // We're done delivering this DV frame (but check for truncation):
      fNumTruncatedBytes = totFrameSize - fFrameSize;

      if (fOurProfile != NULL) {
	// Also set the presentation time, and increment it for next time,
	// based on the length of this frame:
204
	if (!fLeavePresentationTimesUnmodified) fPresentationTime = fNextFramePresentationTime;
205 206 207 208 209 210 211 212

	DVVideoProfile const* ourProfile =(DVVideoProfile const*)fOurProfile;
	double durationInMicroseconds = (fFrameSize*ourProfile->frameDuration)/ourProfile->dvFrameSize;
	fDurationInMicroseconds = (unsigned)durationInMicroseconds;
	fNextFramePresentationTime.tv_usec += fDurationInMicroseconds;
	fNextFramePresentationTime.tv_sec += fNextFramePresentationTime.tv_usec/MILLION;
	fNextFramePresentationTime.tv_usec %= MILLION;
      }
213

214 215 216 217 218 219
      afterGetting(this);
    }
  } else {
    // We read data into our special buffer; signal that it has arrived:
    fInitialBlocksPresent = True;
  }
220
}