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 breaks up an MPEG 1 or 2 video elementary stream into
19// frames for: Video_Sequence_Header, GOP_Header, Picture_Header
20// Implementation
21
22#include "MPEG1or2VideoStreamFramer.hh"
23#include "MPEGVideoStreamParser.hh"
24#include <string.h>
25
26////////// MPEG1or2VideoStreamParser definition //////////
27
28// An enum representing the current state of the parser:
29enum MPEGParseState {
30 PARSING_VIDEO_SEQUENCE_HEADER,
31 PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE,
32 PARSING_GOP_HEADER,
33 PARSING_GOP_HEADER_SEEN_CODE,
34 PARSING_PICTURE_HEADER,
35 PARSING_SLICE
36};
37
38#define VSH_MAX_SIZE 1000
39
40class MPEG1or2VideoStreamParser: public MPEGVideoStreamParser {
41public:
42 MPEG1or2VideoStreamParser(MPEG1or2VideoStreamFramer* usingSource,
43 FramedSource* inputSource,
44 Boolean iFramesOnly, double vshPeriod);
45 virtual ~MPEG1or2VideoStreamParser();
46
47private: // redefined virtual functions:
48 virtual void flushInput();
49 virtual unsigned parse();
50
51private:
52 void reset();
53
54 MPEG1or2VideoStreamFramer* usingSource() {
55 return (MPEG1or2VideoStreamFramer*)fUsingSource;
56 }
57 void setParseState(MPEGParseState parseState);
58
59 unsigned parseVideoSequenceHeader(Boolean haveSeenStartCode);
60 unsigned parseGOPHeader(Boolean haveSeenStartCode);
61 unsigned parsePictureHeader();
62 unsigned parseSlice();
63
64private:
65 MPEGParseState fCurrentParseState;
66 unsigned fPicturesSinceLastGOP;
67 // can be used to compute timestamp for a video_sequence_header
68 unsigned short fCurPicTemporalReference;
69 // used to compute slice timestamp
70 unsigned char fCurrentSliceNumber; // set when parsing a slice
71
72 // A saved copy of the most recently seen 'video_sequence_header',
73 // in case we need to insert it into the stream periodically:
74 unsigned char fSavedVSHBuffer[VSH_MAX_SIZE];
75 unsigned fSavedVSHSize;
76 double fSavedVSHTimestamp;
77 double fVSHPeriod;
78 Boolean fIFramesOnly, fSkippingCurrentPicture;
79
80 void saveCurrentVSH();
81 Boolean needToUseSavedVSH();
82 unsigned useSavedVSH(); // returns the size of the saved VSH
83};
84
85
86////////// MPEG1or2VideoStreamFramer implementation //////////
87
88MPEG1or2VideoStreamFramer::MPEG1or2VideoStreamFramer(UsageEnvironment& env,
89 FramedSource* inputSource,
90 Boolean iFramesOnly,
91 double vshPeriod,
92 Boolean createParser)
93 : MPEGVideoStreamFramer(env, inputSource) {
94 fParser = createParser
95 ? new MPEG1or2VideoStreamParser(this, inputSource,
96 iFramesOnly, vshPeriod)
97 : NULL;
98}
99
100MPEG1or2VideoStreamFramer::~MPEG1or2VideoStreamFramer() {
101}
102
103MPEG1or2VideoStreamFramer*
104MPEG1or2VideoStreamFramer::createNew(UsageEnvironment& env,
105 FramedSource* inputSource,
106 Boolean iFramesOnly,
107 double vshPeriod) {
108 // Need to add source type checking here??? #####
109 return new MPEG1or2VideoStreamFramer(env, inputSource, iFramesOnly, vshPeriod);
110}
111
112double MPEG1or2VideoStreamFramer::getCurrentPTS() const {
113 return fPresentationTime.tv_sec + fPresentationTime.tv_usec/1000000.0;
114}
115
116Boolean MPEG1or2VideoStreamFramer::isMPEG1or2VideoStreamFramer() const {
117 return True;
118}
119
120////////// MPEG1or2VideoStreamParser implementation //////////
121
122MPEG1or2VideoStreamParser
123::MPEG1or2VideoStreamParser(MPEG1or2VideoStreamFramer* usingSource,
124 FramedSource* inputSource,
125 Boolean iFramesOnly, double vshPeriod)
126 : MPEGVideoStreamParser(usingSource, inputSource),
127 fCurrentParseState(PARSING_VIDEO_SEQUENCE_HEADER),
128 fVSHPeriod(vshPeriod), fIFramesOnly(iFramesOnly) {
129 reset();
130}
131
132MPEG1or2VideoStreamParser::~MPEG1or2VideoStreamParser() {
133}
134
135void MPEG1or2VideoStreamParser::setParseState(MPEGParseState parseState) {
136 fCurrentParseState = parseState;
137 MPEGVideoStreamParser::setParseState();
138}
139
140void MPEG1or2VideoStreamParser::reset() {
141 fPicturesSinceLastGOP = 0;
142 fCurPicTemporalReference = 0;
143 fCurrentSliceNumber = 0;
144 fSavedVSHSize = 0;
145 fSkippingCurrentPicture = False;
146}
147
148void MPEG1or2VideoStreamParser::flushInput() {
149 reset();
150 StreamParser::flushInput();
151 if (fCurrentParseState != PARSING_VIDEO_SEQUENCE_HEADER) {
152 setParseState(PARSING_GOP_HEADER); // start from the next GOP
153 }
154}
155
156unsigned MPEG1or2VideoStreamParser::parse() {
157 try {
158 switch (fCurrentParseState) {
159 case PARSING_VIDEO_SEQUENCE_HEADER: {
160 return parseVideoSequenceHeader(False);
161 }
162 case PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE: {
163 return parseVideoSequenceHeader(True);
164 }
165 case PARSING_GOP_HEADER: {
166 return parseGOPHeader(False);
167 }
168 case PARSING_GOP_HEADER_SEEN_CODE: {
169 return parseGOPHeader(True);
170 }
171 case PARSING_PICTURE_HEADER: {
172 return parsePictureHeader();
173 }
174 case PARSING_SLICE: {
175 return parseSlice();
176 }
177 default: {
178 return 0; // shouldn't happen
179 }
180 }
181 } catch (int /*e*/) {
182#ifdef DEBUG
183 fprintf(stderr, "MPEG1or2VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n");
184#endif
185 return 0; // the parsing got interrupted
186 }
187}
188
189void MPEG1or2VideoStreamParser::saveCurrentVSH() {
190 unsigned frameSize = curFrameSize();
191 if (frameSize > sizeof fSavedVSHBuffer) return; // too big to save
192
193 memmove(fSavedVSHBuffer, fStartOfFrame, frameSize);
194 fSavedVSHSize = frameSize;
195 fSavedVSHTimestamp = usingSource()->getCurrentPTS();
196}
197
198Boolean MPEG1or2VideoStreamParser::needToUseSavedVSH() {
199 return usingSource()->getCurrentPTS() > fSavedVSHTimestamp+fVSHPeriod
200 && fSavedVSHSize > 0;
201}
202
203unsigned MPEG1or2VideoStreamParser::useSavedVSH() {
204 unsigned bytesToUse = fSavedVSHSize;
205 unsigned maxBytesToUse = fLimit - fStartOfFrame;
206 if (bytesToUse > maxBytesToUse) bytesToUse = maxBytesToUse;
207
208 memmove(fStartOfFrame, fSavedVSHBuffer, bytesToUse);
209
210 // Also reset the saved timestamp:
211 fSavedVSHTimestamp = usingSource()->getCurrentPTS();
212
213#ifdef DEBUG
214 fprintf(stderr, "used saved video_sequence_header (%d bytes)\n", bytesToUse);
215#endif
216 return bytesToUse;
217}
218
219#define VIDEO_SEQUENCE_HEADER_START_CODE 0x000001B3
220#define GROUP_START_CODE 0x000001B8
221#define PICTURE_START_CODE 0x00000100
222#define SEQUENCE_END_CODE 0x000001B7
223
224static double const frameRateFromCode[] = {
225 0.0, // forbidden
226 24000/1001.0, // approx 23.976
227 24.0,
228 25.0,
229 30000/1001.0, // approx 29.97
230 30.0,
231 50.0,
232 60000/1001.0, // approx 59.94
233 60.0,
234 0.0, // reserved
235 0.0, // reserved
236 0.0, // reserved
237 0.0, // reserved
238 0.0, // reserved
239 0.0, // reserved
240 0.0 // reserved
241};
242
243unsigned MPEG1or2VideoStreamParser
244::parseVideoSequenceHeader(Boolean haveSeenStartCode) {
245#ifdef DEBUG
246 fprintf(stderr, "parsing video sequence header\n");
247#endif
248 unsigned first4Bytes;
249 if (!haveSeenStartCode) {
250 while ((first4Bytes = test4Bytes()) != VIDEO_SEQUENCE_HEADER_START_CODE) {
251#ifdef DEBUG
252 fprintf(stderr, "ignoring non video sequence header: 0x%08x\n", first4Bytes);
253#endif
254 get1Byte(); setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
255 // ensures we progress over bad data
256 }
257 first4Bytes = get4Bytes();
258 } else {
259 // We've already seen the start code
260 first4Bytes = VIDEO_SEQUENCE_HEADER_START_CODE;
261 }
262 save4Bytes(first4Bytes);
263
264 // Next, extract the size and rate parameters from the next 8 bytes
265 unsigned paramWord1 = get4Bytes();
266 save4Bytes(paramWord1);
267 unsigned next4Bytes = get4Bytes();
268#ifdef DEBUG
269 unsigned short horizontal_size_value = (paramWord1&0xFFF00000)>>(32-12);
270 unsigned short vertical_size_value = (paramWord1&0x000FFF00)>>8;
271 unsigned char aspect_ratio_information = (paramWord1&0x000000F0)>>4;
272#endif
273 unsigned char frame_rate_code = (paramWord1&0x0000000F);
274 usingSource()->fFrameRate = frameRateFromCode[frame_rate_code];
275#ifdef DEBUG
276 unsigned bit_rate_value = (next4Bytes&0xFFFFC000)>>(32-18);
277 unsigned vbv_buffer_size_value = (next4Bytes&0x00001FF8)>>3;
278 fprintf(stderr, "horizontal_size_value: %d, vertical_size_value: %d, aspect_ratio_information: %d, frame_rate_code: %d (=>%f fps), bit_rate_value: %d (=>%d bps), vbv_buffer_size_value: %d\n", horizontal_size_value, vertical_size_value, aspect_ratio_information, frame_rate_code, usingSource()->fFrameRate, bit_rate_value, bit_rate_value*400, vbv_buffer_size_value);
279#endif
280
281 // Now, copy all bytes that we see, up until we reach a GROUP_START_CODE
282 // or a PICTURE_START_CODE:
283 do {
284 saveToNextCode(next4Bytes);
285 } while (next4Bytes != GROUP_START_CODE && next4Bytes != PICTURE_START_CODE);
286
287 setParseState((next4Bytes == GROUP_START_CODE)
288 ? PARSING_GOP_HEADER_SEEN_CODE : PARSING_PICTURE_HEADER);
289
290 // Compute this frame's timestamp by noting how many pictures we've seen
291 // since the last GOP header:
292 usingSource()->computePresentationTime(fPicturesSinceLastGOP);
293
294 // Save this video_sequence_header, in case we need to insert a copy
295 // into the stream later:
296 saveCurrentVSH();
297
298 return curFrameSize();
299}
300
301unsigned MPEG1or2VideoStreamParser::parseGOPHeader(Boolean haveSeenStartCode) {
302 // First check whether we should insert a previously-saved
303 // 'video_sequence_header' here:
304 if (needToUseSavedVSH()) return useSavedVSH();
305
306#ifdef DEBUG
307 fprintf(stderr, "parsing GOP header\n");
308#endif
309 unsigned first4Bytes;
310 if (!haveSeenStartCode) {
311 while ((first4Bytes = test4Bytes()) != GROUP_START_CODE) {
312#ifdef DEBUG
313 fprintf(stderr, "ignoring non GOP start code: 0x%08x\n", first4Bytes);
314#endif
315 get1Byte(); setParseState(PARSING_GOP_HEADER);
316 // ensures we progress over bad data
317 }
318 first4Bytes = get4Bytes();
319 } else {
320 // We've already seen the GROUP_START_CODE
321 first4Bytes = GROUP_START_CODE;
322 }
323 save4Bytes(first4Bytes);
324
325 // Next, extract the (25-bit) time code from the next 4 bytes:
326 unsigned next4Bytes = get4Bytes();
327 unsigned time_code = (next4Bytes&0xFFFFFF80)>>(32-25);
328#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
329 Boolean drop_frame_flag = (time_code&0x01000000) != 0;
330#endif
331 unsigned time_code_hours = (time_code&0x00F80000)>>19;
332 unsigned time_code_minutes = (time_code&0x0007E000)>>13;
333 unsigned time_code_seconds = (time_code&0x00000FC0)>>6;
334 unsigned time_code_pictures = (time_code&0x0000003F);
335#if defined(DEBUG) || defined(DEBUG_TIMESTAMPS)
336 fprintf(stderr, "time_code: 0x%07x, drop_frame %d, hours %d, minutes %d, seconds %d, pictures %d\n", time_code, drop_frame_flag, time_code_hours, time_code_minutes, time_code_seconds, time_code_pictures);
337#endif
338#ifdef DEBUG
339 Boolean closed_gop = (next4Bytes&0x00000040) != 0;
340 Boolean broken_link = (next4Bytes&0x00000020) != 0;
341 fprintf(stderr, "closed_gop: %d, broken_link: %d\n", closed_gop, broken_link);
342#endif
343
344 // Now, copy all bytes that we see, up until we reach a PICTURE_START_CODE:
345 do {
346 saveToNextCode(next4Bytes);
347 } while (next4Bytes != PICTURE_START_CODE);
348
349 // Record the time code:
350 usingSource()->setTimeCode(time_code_hours, time_code_minutes,
351 time_code_seconds, time_code_pictures,
352 fPicturesSinceLastGOP);
353
354 fPicturesSinceLastGOP = 0;
355
356 // Compute this frame's timestamp:
357 usingSource()->computePresentationTime(0);
358
359 setParseState(PARSING_PICTURE_HEADER);
360
361 return curFrameSize();
362}
363
364inline Boolean isSliceStartCode(unsigned fourBytes) {
365 if ((fourBytes&0xFFFFFF00) != 0x00000100) return False;
366
367 unsigned char lastByte = fourBytes&0xFF;
368 return lastByte <= 0xAF && lastByte >= 1;
369}
370
371unsigned MPEG1or2VideoStreamParser::parsePictureHeader() {
372#ifdef DEBUG
373 fprintf(stderr, "parsing picture header\n");
374#endif
375 // Note that we've already read the PICTURE_START_CODE
376 // Next, extract the temporal reference from the next 4 bytes:
377 unsigned next4Bytes = get4Bytes();
378 unsigned short temporal_reference = (next4Bytes&0xFFC00000)>>(32-10);
379 unsigned char picture_coding_type = (next4Bytes&0x00380000)>>19;
380#ifdef DEBUG
381 unsigned short vbv_delay = (next4Bytes&0x0007FFF8)>>3;
382 fprintf(stderr, "temporal_reference: %d, picture_coding_type: %d, vbv_delay: %d\n", temporal_reference, picture_coding_type, vbv_delay);
383#endif
384
385 fSkippingCurrentPicture = fIFramesOnly && picture_coding_type != 1;
386 if (fSkippingCurrentPicture) {
387 // Skip all bytes that we see, up until we reach a slice_start_code:
388 do {
389 skipToNextCode(next4Bytes);
390 } while (!isSliceStartCode(next4Bytes));
391 } else {
392 // Save the PICTURE_START_CODE that we've already read:
393 save4Bytes(PICTURE_START_CODE);
394
395 // Copy all bytes that we see, up until we reach a slice_start_code:
396 do {
397 saveToNextCode(next4Bytes);
398 } while (!isSliceStartCode(next4Bytes));
399 }
400
401 setParseState(PARSING_SLICE);
402
403 fCurrentSliceNumber = next4Bytes&0xFF;
404
405 // Record the temporal reference:
406 fCurPicTemporalReference = temporal_reference;
407
408 // Compute this frame's timestamp:
409 usingSource()->computePresentationTime(fCurPicTemporalReference);
410
411 if (fSkippingCurrentPicture) {
412 return parse(); // try again, until we get a non-skipped frame
413 } else {
414 return curFrameSize();
415 }
416}
417
418unsigned MPEG1or2VideoStreamParser::parseSlice() {
419 // Note that we've already read the slice_start_code:
420 unsigned next4Bytes = PICTURE_START_CODE|fCurrentSliceNumber;
421#ifdef DEBUG_SLICE
422 fprintf(stderr, "parsing slice: 0x%08x\n", next4Bytes);
423#endif
424
425 if (fSkippingCurrentPicture) {
426 // Skip all bytes that we see, up until we reach a code of some sort:
427 skipToNextCode(next4Bytes);
428 } else {
429 // Copy all bytes that we see, up until we reach a code of some sort:
430 saveToNextCode(next4Bytes);
431 }
432
433 // The next thing to parse depends on the code that we just saw:
434 if (isSliceStartCode(next4Bytes)) { // common case
435 setParseState(PARSING_SLICE);
436 fCurrentSliceNumber = next4Bytes&0xFF;
437 } else {
438 // Because we don't see any more slices, we are assumed to have ended
439 // the current picture:
440 ++fPicturesSinceLastGOP;
441 ++usingSource()->fPictureCount;
442 usingSource()->fPictureEndMarker = True; // HACK #####
443
444 switch (next4Bytes) {
445 case SEQUENCE_END_CODE: {
446 setParseState(PARSING_VIDEO_SEQUENCE_HEADER);
447 break;
448 }
449 case VIDEO_SEQUENCE_HEADER_START_CODE: {
450 setParseState(PARSING_VIDEO_SEQUENCE_HEADER_SEEN_CODE);
451 break;
452 }
453 case GROUP_START_CODE: {
454 setParseState(PARSING_GOP_HEADER_SEEN_CODE);
455 break;
456 }
457 case PICTURE_START_CODE: {
458 setParseState(PARSING_PICTURE_HEADER);
459 break;
460 }
461 default: {
462 usingSource()->envir() << "MPEG1or2VideoStreamParser::parseSlice(): Saw unexpected code "
463 << (void*)next4Bytes << "\n";
464 setParseState(PARSING_SLICE); // the safest way to recover...
465 break;
466 }
467 }
468 }
469
470 // Compute this frame's timestamp:
471 usingSource()->computePresentationTime(fCurPicTemporalReference);
472
473 if (fSkippingCurrentPicture) {
474 return parse(); // try again, until we get a non-skipped frame
475 } else {
476 return curFrameSize();
477 }
478}
479