1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
15**********/
16// "liveMedia"
17// Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved.
18// A filter that parses a DV input stream into DV frames to deliver to the downstream object
19// Implementation
20// (Thanks to Ben Hutchings for his help, including a prototype implementation.)
21
22#include "DVVideoStreamFramer.hh"
23#include "GroupsockHelper.hh"
24
25////////// DVVideoStreamFramer implementation //////////
26
27DVVideoStreamFramer::DVVideoStreamFramer(UsageEnvironment& env, FramedSource* inputSource,
28 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 // Use the current wallclock time as the initial 'presentation time':
34 gettimeofday(&fNextFramePresentationTime, NULL);
35}
36
37DVVideoStreamFramer::~DVVideoStreamFramer() {
38}
39
40DVVideoStreamFramer*
41DVVideoStreamFramer::createNew(UsageEnvironment& env, FramedSource* inputSource,
42 Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified) {
43 return new DVVideoStreamFramer(env, inputSource, sourceIsSeekable, leavePresentationTimesUnmodified);
44}
45
46// Define the parameters for the profiles that we understand:
47struct DVVideoProfile {
48 char const* name;
49 unsigned apt;
50 unsigned sType;
51 unsigned sequenceCount;
52 unsigned channelCount;
53 unsigned dvFrameSize; // in bytes (== sequenceCount*channelCount*(DV_NUM_BLOCKS_PER_SEQUENCE*DV_DIF_BLOCK_SIZE i.e. 12000))
54 double frameDuration; // duration of the above, in microseconds. (1000000/this == frame rate)
55};
56
57static DVVideoProfile const profiles[] = {
58 { "SD-VCR/525-60", 0, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
59 { "SD-VCR/625-50", 0, 0x00, 12, 1, 144000, 1000000/25.0 },
60 { "314M-25/525-60", 1, 0x00, 10, 1, 120000, (1000000*1001)/30000.0 },
61 { "314M-25/625-50", 1, 0x00, 12, 1, 144000, 1000000/25.0 },
62 { "314M-50/525-60", 1, 0x04, 10, 2, 240000, (1000000*1001)/30000.0 },
63 { "314M-50/625-50", 1, 0x04, 12, 2, 288000, 1000000/25.0 },
64 { "370M/1080-60i", 1, 0x14, 10, 4, 480000, (1000000*1001)/30000.0 },
65 { "370M/1080-50i", 1, 0x14, 12, 4, 576000, 1000000/25.0 },
66 { "370M/720-60p", 1, 0x18, 10, 2, 240000, (1000000*1001)/60000.0 },
67 { "370M/720-50p", 1, 0x18, 12, 2, 288000, 1000000/50.0 },
68 { NULL, 0, 0, 0, 0, 0, 0.0 }
69 };
70
71
72char const* DVVideoStreamFramer::profileName() {
73 if (fOurProfile == NULL) getProfile();
74
75 return fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->name : NULL;
76}
77
78Boolean DVVideoStreamFramer::getFrameParameters(unsigned& frameSize, double& frameDuration) {
79 if (fOurProfile == NULL) getProfile();
80 if (fOurProfile == NULL) return False;
81
82 frameSize = ((DVVideoProfile const*)fOurProfile)->dvFrameSize;
83 frameDuration = ((DVVideoProfile const*)fOurProfile)->frameDuration;
84 return True;
85}
86
87void DVVideoStreamFramer::getProfile() {
88 // To determine the stream's profile, we need to first read a chunk of data that we can parse:
89 fInputSource->getNextFrame(fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE,
90 afterGettingFrame, this, FramedSource::handleClosure, this);
91
92 // Handle events until the requested data arrives:
93 envir().taskScheduler().doEventLoop(&fInitialBlocksPresent);
94}
95
96Boolean DVVideoStreamFramer::isDVVideoStreamFramer() const {
97 return True;
98}
99
100void DVVideoStreamFramer::doGetNextFrame() {
101 fFrameSize = 0; // initially, until we deliver data
102
103 // If we have saved initial blocks (and won't be seeking back to re-read this data), so use this data first.
104 if (fInitialBlocksPresent && !fSourceIsSeekable) {
105 // For simplicity, we require the downstream object's buffer to be >= this data's size:
106 if (fMaxSize < DV_SAVED_INITIAL_BLOCKS_SIZE) {
107 fNumTruncatedBytes = fMaxSize;
108 afterGetting(this);
109 return;
110 }
111
112 memmove(fTo, fSavedInitialBlocks, DV_SAVED_INITIAL_BLOCKS_SIZE);
113 fFrameSize = DV_SAVED_INITIAL_BLOCKS_SIZE;
114 fTo += DV_SAVED_INITIAL_BLOCKS_SIZE;
115 fInitialBlocksPresent = False; // for the future
116 }
117
118 // Arrange to read the (rest of the) requested data.
119 // (But first, make sure that we read an integral multiple of the DV block size.)
120 fMaxSize -= fMaxSize%DV_DIF_BLOCK_SIZE;
121 getAndDeliverData();
122}
123
124#define DV_SMALLEST_POSSIBLE_FRAME_SIZE 120000
125
126void DVVideoStreamFramer::getAndDeliverData() {
127 unsigned const totFrameSize
128 = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
129 unsigned totBytesToDeliver = totFrameSize < fMaxSize ? totFrameSize : fMaxSize;
130 unsigned numBytesToRead = totBytesToDeliver - fFrameSize;
131
132 fInputSource->getNextFrame(fTo, numBytesToRead, afterGettingFrame, this, FramedSource::handleClosure, this);
133}
134
135void DVVideoStreamFramer::afterGettingFrame(void* clientData, unsigned frameSize,
136 unsigned numTruncatedBytes,
137 struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
138 DVVideoStreamFramer* source = (DVVideoStreamFramer*)clientData;
139 source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
140}
141
142#define DVSectionId(n) ptr[(n)*DV_DIF_BLOCK_SIZE + 0]
143#define DVData(n,i) ptr[(n)*DV_DIF_BLOCK_SIZE + 3+(i)]
144
145#define DV_SECTION_HEADER 0x1F
146#define DV_PACK_HEADER_10 0x3F
147#define DV_PACK_HEADER_12 0xBF
148#define DV_SECTION_VAUX_MIN 0x50
149#define DV_SECTION_VAUX_MAX 0x5F
150#define DV_PACK_VIDEO_SOURCE 60
151#ifndef MILLION
152#define MILLION 1000000
153#endif
154
155void DVVideoStreamFramer::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
156 if (fOurProfile == NULL && frameSize >= DV_SAVED_INITIAL_BLOCKS_SIZE) {
157 // (Try to) parse this data enough to figure out its profile.
158 // We assume that the data begins on a (80-byte) block boundary, but not necessarily on a (150-block) sequence boundary.
159 // We therefore scan each 80-byte block, until we find the 6-block header that begins a sequence:
160 u_int8_t const* data = (fTo == NULL) ? fSavedInitialBlocks : fTo;
161 for (u_int8_t const* ptr = data; ptr + 6*DV_DIF_BLOCK_SIZE <= &data[DV_SAVED_INITIAL_BLOCKS_SIZE]; ptr += DV_DIF_BLOCK_SIZE) {
162 // Check whether "ptr" points to an appropriate header:
163 u_int8_t const sectionHeader = DVSectionId(0);
164 u_int8_t const sectionVAUX = DVSectionId(5);
165 u_int8_t const packHeaderNum = DVData(0,0);
166
167 if (sectionHeader == DV_SECTION_HEADER
168 && (packHeaderNum == DV_PACK_HEADER_10 || packHeaderNum == DV_PACK_HEADER_12)
169 && (sectionVAUX >= DV_SECTION_VAUX_MIN && sectionVAUX <= DV_SECTION_VAUX_MAX)) {
170 // This data begins a sequence; look up the DV profile from this:
171 u_int8_t const apt = DVData(0,1)&0x07;
172 u_int8_t const sType = DVData(5,48)&0x1F;
173 u_int8_t const sequenceCount = (packHeaderNum == DV_PACK_HEADER_10) ? 10 : 12;
174
175 // Use these three parameters (apt, sType, sequenceCount) to look up the DV profile:
176 for (DVVideoProfile const* profile = profiles; profile->name != NULL; ++profile) {
177 if (profile->apt == apt && profile->sType == sType && profile->sequenceCount == sequenceCount) {
178 fOurProfile = profile;
179 break;
180 }
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 if (fTo != NULL) { // There is a downstream object; complete delivery to it (or read more data, if necessary)
188 unsigned const totFrameSize
189 = fOurProfile != NULL ? ((DVVideoProfile const*)fOurProfile)->dvFrameSize : DV_SMALLEST_POSSIBLE_FRAME_SIZE;
190 fFrameSize += frameSize;
191 fTo += frameSize;
192 fPresentationTime = presentationTime; // by default; may get changed below
193
194 if (fFrameSize < totFrameSize && fFrameSize < fMaxSize && numTruncatedBytes == 0) {
195 // We have more data to deliver; get it now:
196 getAndDeliverData();
197 } else {
198 // We're done delivering this DV frame (but check for truncation):
199 fNumTruncatedBytes = totFrameSize - fFrameSize;
200
201 if (fOurProfile != NULL) {
202 // Also set the presentation time, and increment it for next time,
203 // based on the length of this frame:
204 if (!fLeavePresentationTimesUnmodified) fPresentationTime = fNextFramePresentationTime;
205
206 DVVideoProfile const* ourProfile =(DVVideoProfile const*)fOurProfile;
207 double durationInMicroseconds = (fFrameSize*ourProfile->frameDuration)/ourProfile->dvFrameSize;
208 fDurationInMicroseconds = (unsigned)durationInMicroseconds;
209 fNextFramePresentationTime.tv_usec += fDurationInMicroseconds;
210 fNextFramePresentationTime.tv_sec += fNextFramePresentationTime.tv_usec/MILLION;
211 fNextFramePresentationTime.tv_usec %= MILLION;
212 }
213
214 afterGetting(this);
215 }
216 } else {
217 // We read data into our special buffer; signal that it has arrived:
218 fInitialBlocksPresent = True;
219 }
220}
221