Hi,

>> I am currently trying to implement uncompressed video streaming according to 
>> RFC4175.

> Because this is likely to be something of general use (e.g., someone else was 
> asking about this just yesterday), I suggest posting the code that you have 
> so far (i.e., for your subclass of "MultiFramedRTPSink" (for sending) and 
> "MultiFramedRTPSource" (for receiving)) to the mailing list, and I'll take a 
> look at it, and see if I can clean it up to include in the LIVE555 source 
> code.

Please find my current source code attached to this mail.

The .patch file contains everything that ought to go into the library anyhow 
according to the license. The "UncompressedVideoRTPSink" is currently 
subclassed from "VideoRTPSink". The use of "1448" as fixed value for the frame 
size is just the current state of experimentation.

In my "UncompressedVideoSink" class (that is too specific to post) I parse the 
SDP description string using
attrVal_int("width");
attrVal_int("height");
attrVal_int("depth");
attrVal_str("sampling");
if (strcmp(samplingStr, "mono") == 0) ...
and set up my player's receive buffer accordingly. 

Thank you for considering to add this feature. Please especially keep me 
updated on what I did wrong in my implementation attempt. And please, although 
it may not be especially mentioned in the RFC, please also consider the option 
for "mono" color sampling, as it is required in my case as well as for the 
earlier posted request on the Flir A320 camera.

Regards,
Marco Porsch

Attachment: live555-uncompressed-video-rtp-source.patch
Description: live555-uncompressed-video-rtp-source.patch

//---------------------------------------------------------------------------------------------------------------------
/// @file      UncompressedVideoRTPSink.cpp
///
/// @brief     implementation of UncompressedVideoRTPSink (inherits from 
VideoRTPSink)
///            sends uncompressed video according to RFC4175
///
/// @author    Marco Porsch (m.por...@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;
}
//---------------------------------------------------------------------------------------------------------------------
/// @file      UncompressedVideoRTPSink.h
///
/// @brief     definition of UncompressedVideoRTPSink (inherits from 
VideoRTPSink)
///            sends uncompressed video according to RFC4175
///
/// @author    Marco Porsch (m.por...@intenta.de)
/// @author    Intenta GmbH
///
/// @version   1.00
/// @date      2014-07-11
///
/// Copyright (c) Intenta GmbH 2014
//---------------------------------------------------------------------------------------------------------------------

#ifndef UNCOMPRESSED_VIDEO_RTP_SINK_H
#define UNCOMPRESSED_VIDEO_RTP_SINK_H


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

#include <liveMedia.hh>


//---------------------------------------------------------------------------------------------------------------------
// Classes
//---------------------------------------------------------------------------------------------------------------------

class UncompressedVideoRTPSink : public VideoRTPSink
{
public:
  static UncompressedVideoRTPSink* createNew(UsageEnvironment& env,
    Groupsock* rtpgs, unsigned char rtpPayloadFormat, const char* imgSampling,
    unsigned imgWidth, unsigned imgHeight, unsigned imgChannels,
    unsigned imgDepth);

protected:
  UncompressedVideoRTPSink(UsageEnvironment& env, Groupsock* rtpgs,
    unsigned char rtpPayloadFormat, const char* imgSampling, unsigned imgWidth,
    unsigned imgHeight, unsigned imgChannels, unsigned imgDepth);
  ~UncompressedVideoRTPSink();

private: // redefined virtual functions:
  virtual void doSpecialFrameHandling(unsigned fragmentationOffset,
    unsigned char* frameStart, unsigned numBytesInFrame,
    struct timeval framePresentationTime, unsigned numRemainingBytes);
  virtual unsigned specialHeaderSize() const;
  virtual unsigned frameSpecificHeaderSize() const;
  virtual char const* auxSDPLine();

private:
  unsigned m_nextHeaderSize;
  const unsigned m_lineLength;
  char m_auxSdpLine[150];
};

#endif // UNCOMPRESSED_VIDEO_RTP_SINK_H
_______________________________________________
live-devel mailing list
live-devel@lists.live555.com
http://lists.live555.com/mailman/listinfo/live-devel

Reply via email to