/**********
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 3 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-2022 Live Networks, Inc.  All rights reserved.
// Raw Video RTP Sources (RFC 4175)
// Implementation

#include "RawVideoRTPSource.hh"

////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory //////////

class RawVideoBufferedPacket: public BufferedPacket {
public:
  RawVideoBufferedPacket(RawVideoRTPSource* ourSource);
  virtual ~RawVideoBufferedPacket();
    
private: // redefined virtual functions
  virtual void getNextEnclosedFrameParameters(unsigned char*& framePtr,
                                              unsigned dataSize,
                                              unsigned& frameSize,
                                              unsigned& frameDurationInMicroseconds);
private:
  RawVideoRTPSource* fOurSource;
};
    
class RawVideoBufferedPacketFactory: public BufferedPacketFactory {
private: // redefined virtual functions
  virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
};
    

////////// LineHeader //////////

struct LineHeader {
  u_int16_t length;
  u_int16_t fieldIdAndLineNumber;
  u_int16_t offsetWithinLine;
};


///////// RawVideoRTPSource implementation (RFC 4175) ////////

RawVideoRTPSource*
RawVideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs,
			     unsigned char rtpPayloadFormat,
                             unsigned rtpTimestampFrequency) {
  return new RawVideoRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency);
}

RawVideoRTPSource
::RawVideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
		    unsigned char rtpPayloadFormat,
                    unsigned rtpTimestampFrequency)
  : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency,
			 new RawVideoBufferedPacketFactory),
    fNumLines(0), fNextLine(0), fLineHeaders(NULL) {
}

RawVideoRTPSource::~RawVideoRTPSource() {
  delete[] fLineHeaders;
}

u_int16_t RawVideoRTPSource::currentLineNumber() const {
  if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
  return fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x7FFF;
}

u_int8_t RawVideoRTPSource::currentLineFieldId() const {
  if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
  return (fLineHeaders[fNextLine-1].fieldIdAndLineNumber&0x8000)>>15;
}

u_int16_t RawVideoRTPSource::currentOffsetWithinLine() const {
  if (fNextLine == 0 || fLineHeaders == NULL) return 0; // we've called this function too soon!
  return fLineHeaders[fNextLine-1].offsetWithinLine;
}

Boolean RawVideoRTPSource
::processSpecialHeader(BufferedPacket* packet,
                       unsigned& resultSpecialHeaderSize) {

  unsigned char* headerStart = packet->data();
  unsigned packetSize = packet->dataSize();

  // The first 2 bytes of the header are the "Extended Sequence Number".
  // In the current implementation, we ignore this.
  if (packetSize < 2) return False;
  headerStart += 2;
  unsigned char* lineHeaderStart = headerStart;
  packetSize -= 2;

  // The rest of the header should consist of N*6 bytes (with N >= 1) for each line included.
  // Count how many of these there are:
  unsigned numLines = 0;
  while (1) {
    if (packetSize < 6) return False; // there's not enough room for another line header
    ++numLines;
    Boolean continuationBit = (headerStart[4]&0x80)>>7;
    headerStart += 6;
    packetSize -= 6;

    // Check the "C" (continuation) bit of this header to see whether any more line headers follow:
    if (continuationBit == 0) break; // no more line headers follow
  }

  // We now know how many lines are contained in this payload.  Allocate and fill in "fLineHeaders":
  fNumLines = numLines; // ASSERT: >= 1
  fNextLine = 0;
  delete[] fLineHeaders; fLineHeaders = new LineHeader[fNumLines];
  unsigned totalLength = 0;
  for (unsigned i = 0; i < fNumLines; ++i) {
    fLineHeaders[i].length = (lineHeaderStart[0]<<8) + lineHeaderStart[1];
    totalLength += fLineHeaders[i].length;
    fLineHeaders[i].fieldIdAndLineNumber = (lineHeaderStart[2]<<8) + lineHeaderStart[3];
    fLineHeaders[i].offsetWithinLine = ((lineHeaderStart[4]&0x7F)<<8) + lineHeaderStart[5];
    lineHeaderStart += 6;
  }

  // Make sure that we have enough bytes for all of the line lengths promised:
  if (totalLength > packetSize) {
    fNumLines = 0;
    delete[] fLineHeaders; fLineHeaders = NULL;
    return False;
  }

  // Everything looks good:
  fCurrentPacketBeginsFrame
    = (fLineHeaders[0].fieldIdAndLineNumber&0x7FFF) == 0 && fLineHeaders[0].offsetWithinLine == 0;
  // Don't set "fCurrentPacketCompletesFrame" until we've processed the last line in the packet
  resultSpecialHeaderSize = headerStart - packet->data();
  return True;
}

char const* RawVideoRTPSource::MIMEtype() const {
  return "video/RAW";
}


////////// RawVideoBufferedPacket and RawVideoBufferedPacketFactory implementation //////////

RawVideoBufferedPacket
::RawVideoBufferedPacket(RawVideoRTPSource* ourSource)
  : fOurSource(ourSource) {
}

RawVideoBufferedPacket::~RawVideoBufferedPacket() {
}
    
void RawVideoBufferedPacket::getNextEnclosedFrameParameters(unsigned char*& /*framePtr*/,
							    unsigned dataSize,
							    unsigned& frameSize,
							    unsigned& frameDurationInMicroseconds) {
  frameDurationInMicroseconds = 0; // because all lines within the same packet are from the same frame

  if (fOurSource->fNextLine >= fOurSource->fNumLines) {
       fOurSource->envir() << "RawVideoBufferedPacket::nextEnclosedFrameParameters("
			   << dataSize << "): data error ("
			   << fOurSource->fNextLine << " >= " << fOurSource->fNumLines << ")!\n";
    frameSize = dataSize;
    return;
  }

  // This line ('subframe') completes a frame if it's the last line in the packet,
  // and the packet's 'M' bit was set:
  fOurSource->fCurrentPacketCompletesFrame
    = fOurSource->fCurPacketMarkerBit && fOurSource->fNextLine == fOurSource->fNumLines-1;

  frameSize = fOurSource->fLineHeaders[fOurSource->fNextLine++].length;
}

BufferedPacket* RawVideoBufferedPacketFactory
::createNewPacket(MultiFramedRTPSource* ourSource) {
  return new RawVideoBufferedPacket((RawVideoRTPSource*)ourSource);
}
