File: MPEG1or2VideoRTPSink.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 (175 lines) | stat: -rw-r--r-- 6,759 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
/**********
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.
// RTP sink for MPEG video (RFC 2250)
// Implementation

#include "MPEG1or2VideoRTPSink.hh"
#include "MPEG1or2VideoStreamFramer.hh"

MPEG1or2VideoRTPSink::MPEG1or2VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs)
  : VideoRTPSink(env, RTPgs, 32, 90000, "MPV") {
  fPictureState.temporal_reference = 0;
  fPictureState.picture_coding_type = fPictureState.vector_code_bits = 0;
}

MPEG1or2VideoRTPSink::~MPEG1or2VideoRTPSink() {
}

MPEG1or2VideoRTPSink*
MPEG1or2VideoRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs) {
  return new MPEG1or2VideoRTPSink(env, RTPgs);
}

Boolean MPEG1or2VideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
  // Our source must be an appropriate framer:
  return source.isMPEG1or2VideoStreamFramer();
}

Boolean MPEG1or2VideoRTPSink::allowFragmentationAfterStart() const {
  return True;
}

Boolean MPEG1or2VideoRTPSink
::frameCanAppearAfterPacketStart(unsigned char const* frameStart,
				 unsigned numBytesInFrame) const {
  // A 'frame' (which in this context can mean a header or a slice as well as a
  // complete picture) can appear at other than the first position in a packet
  // in all situations, EXCEPT when it follows the end of (i.e., the last slice
  // of) a picture.  I.e., the headers at the beginning of a picture must
  // appear at the start of a RTP packet. 
  if (!fPreviousFrameWasSlice) return True;

  // A slice is already packed into this packet.  We allow this new 'frame'
  // to be packed after it, provided that it is also a slice:
  return numBytesInFrame >= 4
    && frameStart[0] == 0 && frameStart[1] == 0 && frameStart[2] == 1
    && frameStart[3] >= 1 && frameStart[3] <= 0xAF;
}

#define VIDEO_SEQUENCE_HEADER_START_CODE 0x000001B3
#define PICTURE_START_CODE               0x00000100

void MPEG1or2VideoRTPSink
::doSpecialFrameHandling(unsigned fragmentationOffset,
			 unsigned char* frameStart,
			 unsigned numBytesInFrame,
			 struct timeval frameTimestamp,
			 unsigned numRemainingBytes) {
  Boolean thisFrameIsASlice = False; // until we learn otherwise
  if (isFirstFrameInPacket()) {
    fSequenceHeaderPresent = fPacketBeginsSlice = fPacketEndsSlice = False; 
  }

  if (fragmentationOffset == 0) {
    // Begin by inspecting the 4-byte code at the start of the frame:
    if (numBytesInFrame < 4) return; // shouldn't happen
    unsigned startCode = (frameStart[0]<<24) | (frameStart[1]<<16)
                       | (frameStart[2]<<8) | frameStart[3];

    if (startCode == VIDEO_SEQUENCE_HEADER_START_CODE) {
      // This is a video sequence header
      fSequenceHeaderPresent = True;
    } else if (startCode == PICTURE_START_CODE) {
      // This is a picture header

      // Record the parameters of this picture:
      if (numBytesInFrame < 8) return; // shouldn't happen
      unsigned next4Bytes = (frameStart[4]<<24) | (frameStart[5]<<16)
	                  | (frameStart[6]<<8) | frameStart[7];
      unsigned char byte8 = numBytesInFrame == 8 ? 0 : frameStart[8];

      fPictureState.temporal_reference = (next4Bytes&0xFFC00000)>>(32-10);
      fPictureState.picture_coding_type = (next4Bytes&0x00380000)>>(32-(10+3)); 

      unsigned char FBV, BFC, FFV, FFC;
      FBV = BFC = FFV = FFC = 0;
      switch (fPictureState.picture_coding_type) {
      case 3:
	FBV = (byte8&0x40)>>6;
	BFC = (byte8&0x38)>>3;
	// fall through to:
      case 2:
	FFV = (next4Bytes&0x00000004)>>2;
	FFC = ((next4Bytes&0x00000003)<<1) | ((byte8&0x80)>>7);
      }

      fPictureState.vector_code_bits = (FBV<<7) | (BFC<<4) | (FFV<<3) | FFC;
    } else if ((startCode&0xFFFFFF00) == 0x00000100) {
      unsigned char lastCodeByte = startCode&0xFF;

      if (lastCodeByte <= 0xAF) {
	// This is (the start of) a slice
	thisFrameIsASlice = True;
      } else {
	// This is probably a GOP header; we don't do anything with this
      }
    } else {
      // The first 4 bytes aren't a code that we recognize.
      envir() << "Warning: MPEG1or2VideoRTPSink::doSpecialFrameHandling saw strange first 4 bytes "
	      << (void*)startCode << ", but we're not a fragment\n";
    }
  } else {
    // We're a fragment (other than the first) of a slice.
    thisFrameIsASlice = True;
  }

  if (thisFrameIsASlice) {
    // This packet begins a slice iff there's no fragmentation offset:
    fPacketBeginsSlice = (fragmentationOffset == 0);

    // This packet also ends a slice iff there are no fragments remaining:
    fPacketEndsSlice = (numRemainingBytes == 0);
  }

  // Set the video-specific header based on the parameters that we've seen.
  // Note that this may get done more than once, if several frames appear
  // in the packet.  That's OK, because this situation happens infrequently,
  // and we want the video-specific header to reflect the most up-to-date
  // information (in particular, from a Picture Header) anyway.
  unsigned videoSpecificHeader = 
    // T == 0
    (fPictureState.temporal_reference<<16) |
    // AN == N == 0
    (fSequenceHeaderPresent<<13) |
    (fPacketBeginsSlice<<12) |
    (fPacketEndsSlice<<11) |
    (fPictureState.picture_coding_type<<8) |
    fPictureState.vector_code_bits;
  setSpecialHeaderWord(videoSpecificHeader);

  // Also set the RTP timestamp.  (As above, we do this for each frame
  // in the packet.)
  setTimestamp(frameTimestamp);

  // Set the RTP 'M' (marker) bit iff this frame ends (i.e., is the last
  // slice of) a picture (and there are no fragments remaining).
  // This relies on the source being a "MPEG1or2VideoStreamFramer".
  MPEG1or2VideoStreamFramer* framerSource = (MPEG1or2VideoStreamFramer*)fSource;
  if (framerSource != NULL && framerSource->pictureEndMarker()
      && numRemainingBytes == 0) {
    setMarkerBit();
    framerSource->pictureEndMarker() = False;
  }

  fPreviousFrameWasSlice = thisFrameIsASlice;
}

unsigned MPEG1or2VideoRTPSink::specialHeaderSize() const {
  // There's a 4 byte special audio header:
  return 4;
}