-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDVVideoStreamFramer.cpp
executable file
·220 lines (185 loc) · 9.44 KB
/
DVVideoStreamFramer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/**********
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
Free Software Foundation; either version 2.1 of the License, or (at your
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"
// Copyright (c) 1996-2013 Live Networks, Inc. All rights reserved.
// A filter that parses a DV input stream into DV frames to deliver to the downstream object
// Implementation
// (Thanks to Ben Hutchings for his help, including a prototype implementation.)
#include "DVVideoStreamFramer.hh"
#include "GroupsockHelper.hh"
////////// DVVideoStreamFramer implementation //////////
DVVideoStreamFramer::DVVideoStreamFramer(UsageEnvironment& env, FramedSource* inputSource,
Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified)
: FramedFilter(env, inputSource),
fLeavePresentationTimesUnmodified(leavePresentationTimesUnmodified),
fOurProfile(NULL), fInitialBlocksPresent(False), fSourceIsSeekable(sourceIsSeekable) {
fTo = NULL; // hack used when reading "fSavedInitialBlocks"
// Use the current wallclock time as the initial 'presentation time':
gettimeofday(&fNextFramePresentationTime, NULL);
}
DVVideoStreamFramer::~DVVideoStreamFramer() {
}
DVVideoStreamFramer*
DVVideoStreamFramer::createNew(UsageEnvironment& env, FramedSource* inputSource,
Boolean sourceIsSeekable, Boolean leavePresentationTimesUnmodified) {
return new DVVideoStreamFramer(env, inputSource, sourceIsSeekable, leavePresentationTimesUnmodified);
}
// Define the parameters for the profiles that we understand:
struct DVVideoProfile {
char const* name;
unsigned apt;
unsigned sType;
unsigned sequenceCount;
unsigned channelCount;
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)
};
static DVVideoProfile const profiles[] = {
{ "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 }
};
char const* DVVideoStreamFramer::profileName() {
if (fOurProfile == NULL) getProfile();
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() {
// 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);
}
Boolean DVVideoStreamFramer::isDVVideoStreamFramer() const {
return True;
}
void DVVideoStreamFramer::doGetNextFrame() {
fFrameSize = 0; // initially, until we deliver data
// 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) {
// 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;
fTo += DV_SAVED_INITIAL_BLOCKS_SIZE;
fInitialBlocksPresent = False; // for the future
}
// Arrange to read the (rest of the) requested data.
// (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);
}
void DVVideoStreamFramer::afterGettingFrame(void* clientData, unsigned frameSize,
unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {
DVVideoStreamFramer* source = (DVVideoStreamFramer*)clientData;
source->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime);
}
#define DVSectionId(n) ptr[(n)*DV_DIF_BLOCK_SIZE + 0]
#define DVData(n,i) ptr[(n)*DV_DIF_BLOCK_SIZE + 3+(i)]
#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
#ifndef MILLION
#define MILLION 1000000
#endif
void DVVideoStreamFramer::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {
if (fOurProfile == NULL && frameSize >= DV_SAVED_INITIAL_BLOCKS_SIZE) {
// (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;
}
}
break; // because we found a correct sequence header (even if we don't happen to define a profile for it)
}
}
}
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;
fPresentationTime = presentationTime; // by default; may get changed below
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:
if (!fLeavePresentationTimesUnmodified) fPresentationTime = fNextFramePresentationTime;
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;
}
afterGetting(this);
}
} else {
// We read data into our special buffer; signal that it has arrived:
fInitialBlocksPresent = True;
}
}