176 lines
6.6 KiB
C++
Executable File
176 lines
6.6 KiB
C++
Executable File
/**********
|
|
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-2016 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 framePresentationTime,
|
|
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(framePresentationTime);
|
|
|
|
// 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 video header:
|
|
return 4;
|
|
}
|