File: MPEG1or2VideoStreamDiscreteFramer.cpp

package info (click to toggle)
liblivemedia 2006.03.17-2
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 2,928 kB
  • ctags: 4,588
  • sloc: cpp: 35,064; ansic: 979; makefile: 78; sh: 73
file content (201 lines) | stat: -rw-r--r-- 8,234 bytes parent folder | download
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
/**********
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.,
59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**********/
// "liveMedia"
// Copyright (c) 1996-2005 Live Networks, Inc.  All rights reserved.
// A simplified version of "MPEG1or2VideoStreamFramer" that takes only
// complete, discrete frames (rather than an arbitrary byte stream) as input.
// This avoids the parsing and data copying overhead of the full
// "MPEG1or2VideoStreamFramer".
// Implementation

#include "MPEG1or2VideoStreamDiscreteFramer.hh"

MPEG1or2VideoStreamDiscreteFramer*
MPEG1or2VideoStreamDiscreteFramer::createNew(UsageEnvironment& env,
                                             FramedSource* inputSource,
                                             Boolean iFramesOnly,
                                             double vshPeriod) {
  // Need to add source type checking here???  #####
  return new MPEG1or2VideoStreamDiscreteFramer(env, inputSource,
                                               iFramesOnly, vshPeriod);
}
                                                                                
MPEG1or2VideoStreamDiscreteFramer
::MPEG1or2VideoStreamDiscreteFramer(UsageEnvironment& env,
                                    FramedSource* inputSource,
                                    Boolean iFramesOnly, double vshPeriod)
  : MPEG1or2VideoStreamFramer(env, inputSource, iFramesOnly, vshPeriod,
                              False/*don't create a parser*/),
    fLastNonBFrameTemporal_reference(0),
    fSavedVSHSize(0), fSavedVSHTimestamp(0.0),
    fIFramesOnly(iFramesOnly), fVSHPeriod(vshPeriod) {
  fLastNonBFramePresentationTime.tv_sec = 0;
  fLastNonBFramePresentationTime.tv_usec = 0;
}
                                                                                
MPEG1or2VideoStreamDiscreteFramer::~MPEG1or2VideoStreamDiscreteFramer() {
}
                                                                                
void MPEG1or2VideoStreamDiscreteFramer::doGetNextFrame() {
  // Arrange to read data (which should be a complete MPEG-1 or 2 video frame)
  // from our data source, directly into the client's input buffer.
  // After reading this, we'll do some parsing on the frame.
  fInputSource->getNextFrame(fTo, fMaxSize,
                             afterGettingFrame, this,
                             FramedSource::handleClosure, this);
}
                                                                                
void MPEG1or2VideoStreamDiscreteFramer
::afterGettingFrame(void* clientData, unsigned frameSize,
                    unsigned numTruncatedBytes,
                    struct timeval presentationTime,
                    unsigned durationInMicroseconds) {
  MPEG1or2VideoStreamDiscreteFramer* source
    = (MPEG1or2VideoStreamDiscreteFramer*)clientData;
  source->afterGettingFrame1(frameSize, numTruncatedBytes,
                             presentationTime, durationInMicroseconds);
}

static double const frameRateFromCode[] = {
  0.0,          // forbidden
  24000/1001.0, // approx 23.976
  24.0,
  25.0,
  30000/1001.0, // approx 29.97
  30.0,
  50.0,
  60000/1001.0, // approx 59.94
  60.0,
  0.0,          // reserved
  0.0,          // reserved
  0.0,          // reserved
  0.0,          // reserved
  0.0,          // reserved
  0.0,          // reserved
  0.0           // reserved
};

#define MILLION 1000000

void MPEG1or2VideoStreamDiscreteFramer
::afterGettingFrame1(unsigned frameSize, unsigned numTruncatedBytes,
                     struct timeval presentationTime,
                     unsigned durationInMicroseconds) {
  // Check that the first 4 bytes are a system code:
  if (frameSize >= 4 && fTo[0] == 0 && fTo[1] == 0 && fTo[2] == 1) {
    fPictureEndMarker = True; // Assume that we have a complete 'picture' here

    u_int8_t nextCode = fTo[3];
    if (nextCode == 0xB3) { // VIDEO_SEQUENCE_HEADER_START_CODE
      // Note the following 'frame rate' code:
      if (frameSize >= 8) {
	u_int8_t frame_rate_code = fTo[7]&0x0F;
	fFrameRate = frameRateFromCode[frame_rate_code];
      }

      // Also, save away this Video Sequence Header, in case we need it later:
      // First, figure out how big it is:
      unsigned vshSize;
      for (vshSize = 4; vshSize < frameSize-3; ++vshSize) {
	if (fTo[vshSize] == 0 && fTo[vshSize+1] == 0 && fTo[vshSize+2] == 1 &&
	    (fTo[vshSize+3] == 0xB8 || fTo[vshSize+3] == 0x00)) break;
      }
      if (vshSize == frameSize-3) vshSize = frameSize; // There was nothing else following it
      if (vshSize <= sizeof fSavedVSHBuffer) {
	memmove(fSavedVSHBuffer, fTo, vshSize);
	fSavedVSHSize = vshSize;
	fSavedVSHTimestamp
	  = presentationTime.tv_sec + presentationTime.tv_usec/(double)MILLION;
      }
    } else if (nextCode == 0xB8) { // GROUP_START_CODE
      // If necessary, insert a saved Video Sequence Header in front of this:
      double pts = presentationTime.tv_sec + presentationTime.tv_usec/(double)MILLION;
      if (pts > fSavedVSHTimestamp + fVSHPeriod &&
	  fSavedVSHSize + frameSize <= fMaxSize) {
	memmove(&fTo[fSavedVSHSize], &fTo[0], frameSize); // make room for the header
	memmove(&fTo[0], fSavedVSHBuffer, fSavedVSHSize); // insert it
	frameSize += fSavedVSHSize;
	fSavedVSHTimestamp = pts;
      }
    }

    unsigned i = 3;
    if (nextCode == 0xB3 /*VIDEO_SEQUENCE_HEADER_START_CODE*/ ||
	nextCode == 0xB8 /*GROUP_START_CODE*/) {
      // Skip to the following PICTURE_START_CODE (if any):
      for (i += 4; i < frameSize; ++i) {
	if (fTo[i] == 0x00 /*PICTURE_START_CODE*/
	    && fTo[i-1] == 1 && fTo[i-2] == 0 && fTo[i-3] == 0) {
	  nextCode = fTo[i];
	  break;
	}
      }
    }

    if (nextCode == 0x00 /*PICTURE_START_CODE*/ && i+2 < frameSize) { 
      // Get the 'temporal_reference' and 'picture_coding_type' from the
      // following 2 bytes:
      ++i;
      unsigned short temporal_reference = (fTo[i]<<2)|(fTo[i+1]>>6);
      unsigned char picture_coding_type = (fTo[i+1]&0x38)>>3;

      // If this is not an "I" frame, but we were asked for "I" frames only, then try again:
      if (fIFramesOnly && picture_coding_type != 1) {
	doGetNextFrame();
	return;
      }

      // If this is a "B" frame, then we have to tweak "presentationTime":
      if (picture_coding_type == 3/*B*/
	  && (fLastNonBFramePresentationTime.tv_usec > 0 ||
	      fLastNonBFramePresentationTime.tv_sec > 0)) {
	int trIncrement
            = fLastNonBFrameTemporal_reference - temporal_reference;
	if (trIncrement < 0) trIncrement += 1024; // field is 10 bits in size

	unsigned usIncrement = fFrameRate == 0.0 ? 0
	  : (unsigned)((trIncrement*MILLION)/fFrameRate);
	unsigned secondsToSubtract = usIncrement/MILLION;
	unsigned uSecondsToSubtract = usIncrement%MILLION;

	presentationTime = fLastNonBFramePresentationTime;
	if ((unsigned)presentationTime.tv_usec < uSecondsToSubtract) {
	  presentationTime.tv_usec += MILLION;
	  if (presentationTime.tv_sec > 0) --presentationTime.tv_sec;
	}
	presentationTime.tv_usec -= uSecondsToSubtract;
	if ((unsigned)presentationTime.tv_sec > secondsToSubtract) {
	  presentationTime.tv_sec -= secondsToSubtract;
	} else {
	  presentationTime.tv_sec = presentationTime.tv_usec = 0;
	}
      } else {
	fLastNonBFramePresentationTime = presentationTime;
	fLastNonBFrameTemporal_reference = temporal_reference;
      }
    }
  }

  // ##### Later:
  // - do "iFramesOnly" if requested
 
  // Complete delivery to the client:
  fFrameSize = frameSize;
  fNumTruncatedBytes = numTruncatedBytes;
  fPresentationTime = presentationTime;
  fDurationInMicroseconds = durationInMicroseconds;
  afterGetting(this);
}