//---------------------------------------------------------------------------------------------------------------------
/// @file      UncompressedVideoRTPSink.cpp
///
/// @brief     implementation of UncompressedVideoRTPSink (inherits from VideoRTPSink)
///            sends uncompressed video according to RFC4175
///
/// @author    Marco Porsch (m.porsch@intenta.de)
/// @author    Intenta GmbH
///
/// @version   1.00
/// @date      2014-07-11
///
/// Copyright (c) Intenta GmbH 2014
//---------------------------------------------------------------------------------------------------------------------


//---------------------------------------------------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------------------------------------------------

#include "UncompressedVideoRTPSink.h"


//---------------------------------------------------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------------------------------------------------

UncompressedVideoRTPSink* UncompressedVideoRTPSink::createNew(
  UsageEnvironment& env, Groupsock* rtpgs, unsigned char rtpPayloadFormat,
  const char* imgSampling, unsigned imgWidth, unsigned imgHeight,
  unsigned imgChannels, unsigned imgDepth)
{
  return new UncompressedVideoRTPSink(env, rtpgs, rtpPayloadFormat,
    imgSampling, imgWidth, imgHeight, imgChannels, imgDepth);
}

UncompressedVideoRTPSink::UncompressedVideoRTPSink(UsageEnvironment& env,
  Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling,
  unsigned imgWidth, unsigned imgHeight, unsigned imgChannels,
  unsigned imgDepth)
: VideoRTPSink(env, rtpgs, rtpPayloadFormat, 90000, "raw"), // see http://tools.ietf.org/html/rfc4175
m_lineLength(imgWidth * imgChannels)
{
  // see http://tools.ietf.org/html/rfc4175#section-7
  char const* const fmt =
    "a=fmtp:%d sampling=%s; width=%d; height=%d; depth=%d\r\n";
  sprintf(m_auxSdpLine, fmt, fRTPPayloadType, imgSampling, imgWidth, imgHeight,
    imgChannels * 8);
}

UncompressedVideoRTPSink::~UncompressedVideoRTPSink()
{
}

unsigned getHeaderSize(unsigned fragmentationOffset, unsigned frameSize, unsigned lineLength)
{
  const unsigned numBytesLineFragmentStart = (lineLength - (fragmentationOffset % lineLength)) % lineLength;
  const unsigned numBytesWholeLines = (frameSize - numBytesLineFragmentStart) / lineLength;
  const unsigned numBytesLineFragmentEnd = (frameSize - numBytesLineFragmentStart) % lineLength;
  const unsigned headerSize = 2 + 6 * ((numBytesLineFragmentStart > 0 ? 1 : 0) +
    numBytesWholeLines +
    (numBytesLineFragmentEnd > 0 ? 1 : 0));

  return headerSize;
}

void UncompressedVideoRTPSink::doSpecialFrameHandling(
  unsigned fragmentationOffset, unsigned char* frameStart,
  unsigned numBytesInFrame, struct timeval framePresentationTime,
  unsigned numRemainingBytes)
{
  const unsigned headerSize = getHeaderSize(fragmentationOffset, numBytesInFrame, m_lineLength);
  unsigned char* payloadHeader = new unsigned char[headerSize];

  payloadHeader[0] = 0; // extended sequence number
  payloadHeader[1] = 0;

  if (numRemainingBytes == 0)
  {
    // This packet contains the last (or only) fragment of the frame.
    // Set the RTP 'M' ('marker') bit:
    setMarkerBit();
  }

  unsigned perLineOffset = fragmentationOffset;
  unsigned numBytesLeftInFrame = numBytesInFrame;
  unsigned char* payloadLineHeader = payloadHeader + 2;
  while (numBytesLeftInFrame > 0)
  {
    const unsigned lineNo = perLineOffset / m_lineLength;
    const unsigned offset = perLineOffset % m_lineLength;
    const unsigned length = (m_lineLength - offset > numBytesLeftInFrame ?
    numBytesLeftInFrame : m_lineLength - offset);

    perLineOffset += length;
    numBytesLeftInFrame -= length;

    // write length
    payloadLineHeader[0] = (length >> 8) & 0xff;
    payloadLineHeader[1] = length & 0xff;

    const unsigned char field = 0; // we only support progressive video for now
    // write line no and field bit
    payloadLineHeader[2] = ((lineNo >> 8) & 0x7f) | ((field << 7) & 0x80);
    payloadLineHeader[3] = lineNo & 0xff;

    const unsigned char continuation =
      (numBytesLeftInFrame == 0 ? 0x00 : 0x80);
    /* write offset and continuation marker */
    payloadLineHeader[4] = ((offset >> 8) & 0x7f) | continuation;
    payloadLineHeader[5] = offset & 0xff;

    payloadLineHeader += 6;
  }

  setSpecialHeaderBytes(payloadHeader, headerSize);
  //setFrameSpecificHeaderBytes(payloadHeader, m_lastHeaderSize);
  delete[] payloadHeader;

  // Important: Also call our base class's doSpecialFrameHandling(),
  // to set the packet's timestamp:
  MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset,
    frameStart, numBytesInFrame,
    framePresentationTime,
    numRemainingBytes);

  if (numRemainingBytes == 0) // next frame is the first frame
    m_nextHeaderSize = getHeaderSize(0, 1448, m_lineLength);
  else if (numRemainingBytes < 1448) // next frame is the frame last frame
    m_nextHeaderSize = getHeaderSize(fragmentationOffset + numBytesInFrame, numRemainingBytes, m_lineLength);
  else // regular case
    m_nextHeaderSize = getHeaderSize(fragmentationOffset + numBytesInFrame, 1448, m_lineLength);
}

unsigned UncompressedVideoRTPSink::specialHeaderSize() const
{
  return m_nextHeaderSize;
}

unsigned UncompressedVideoRTPSink::frameSpecificHeaderSize() const
{
    return 0;
}

char const* UncompressedVideoRTPSink::auxSDPLine()
{
  return m_auxSdpLine;
}