/*
 * h224.cxx
 *
 * H.224 implementation for the OpenH323 Project.
 *
 * Copyright (c) 2006 Network for Educational Technology, ETH Zurich.
 * Written by Hannes Friederich.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * Contributor(s): ______________________________________.
 *
 * $Log: h224.cxx,v $
 * Revision 1.26  2016/08/12 16:55:05  roni
 * *** empty log message ***
 *
 * Revision 1.25  2016/06/06 16:37:23  joseph
 * Remove debug code from IMTC-2016 testing
 *
 * Revision 1.24  2016/05/20 16:00:27  roni
 * fixes from IMTC
 *
 * Revision 1.23  2016/05/04 22:01:50  joseph
 * Add support for SIP FECC (both directions)
 *
 * Revision 1.22  2016/03/07 23:38:44  joseph
 * VGW-1938 - implement FECC for H.323
 * - more changes for continuous camera control
 * - added logs for OnStartAction and OnStopAction
 *
 * Revision 1.21  2016/03/04 18:27:30  joseph
 * VGW-1938 - implement FECC for H.323
 * -added continuous camera control
 *
 * Revision 1.20  2016/02/25 00:07:23  joseph
 * VGW-1938 - implement FECC for H.323
 * -added H235 encryption for inbound FECC
 * -removed some warnings
 *
 * Revision 1.19  2016/02/18 18:27:35  joseph
 * VGW-1938 - implement FECC for H.323
 * - more changes related to the sessionID issue from the previous check-in.
 *
 * Revision 1.18  2016/02/13 01:28:36  joseph
 * VGW-1938 - implement FECC for H.323
 * - Fixed bugs in the H224 components of the H323 stack.  The problem was that the stack supported only a single bidirectional h224handler that assumed the same sessionID for both H224 logical channels (one opened by the GW and the other opened by the endpoint).  The issue arose when the two H224 logical channels are opened with different session id's.  These changes (hopefully) fixed that issue.  Now, two h224handlers will be created to handle each H224 logical channel independently of each other.
 *
 * Revision 1.17  2016/02/05 22:12:58  joseph
 * VGW-1938 - implement FECC for H.323
 * - Fixed a bug in the q922 HDLC decoder.  The decoder had a bug parsing an FECC messages when it contained two sets of 5 consecutive one's was separated by a 0 (i.e. this pattern 11111011111).
 * - Fixed the FECC ExtraCapabilitiesMessages such that now it advertises one "main camera" as being able to pan,tilt,zoom and focus.  This was preventing the Cisco EX60 endpoint from enabling it's FECC control panel.
 *
 * Revision 1.16  2016/01/27 12:06:41  joseph
 * Inbound FECC support for OTN
 *
 * Revision 1.15  2016/01/08 18:21:57  joseph
 * Initial Checking for inbound FECC. Expose fecc callbacks from the H323 stack to the GW.
 *
 * Revision 1.14  2015/10/13 20:22:34  vfurman
 * VGW-1434
 * use H323 stack (for FECC) before some params were initialized
 *
 * Revision 1.13  2014/11/09 08:57:35  roni
 * sdk 2.2 and 64 bit
 *
 * Revision 1.1.1.1  2014/07/11 15:30:12  roni
 * Initial import
 *
 * Revision 1.12  2014/05/12 22:10:09  roni
 * merge from 3.1
 *
 * Revision 1.9.2.1  2014/04/01 13:01:22  rich
 * VG-1475 - la crashed upon receiving an FECC command as the conference was ending
 *
 * Revision 1.9  2014/01/24 19:30:21  rich
 * Move HEAD to match TAG_GW_3_0_0_98 before branching for 3_1 release
 *
 * Revision 1.8.2.1  2013/12/03 19:05:04  joseph
 * stop processing if the transmitframe is invalid.
 *
 * Revision 1.8  2013/04/08 13:37:11  roni
 * merge from 2.2.1
 *
 * Revision 1.7.4.1  2013/02/22 16:46:20  joseph
 * Fix bug in H323 stack for FECC - ignore H224 frames with empty payloads.
 *
 * Revision 1.7  2012/01/10 18:26:37  roni
 * merge from branch
 *
 * Revision 1.5.4.2  2012-01-10 17:40:33  roni
 * clean up receiver thread when rtp channel is not up
 *
 * Revision 1.5.4.1  2012-01-06 17:47:33  rich
 * Delete H224 receiverThread to fix FECC memory leak
 *
 * Revision 1.5  2011-05-28 00:48:21  joseph
 * Made the following changes:
 * - Fixed issues with QOS (TOS valus for audio/video)
 * - Fixes H.239 issues with Sony PCS-1.
 *
 * Revision 1.4  2011-05-27 11:03:09  joseph
 * Made the following changes:
 * - Create VCAP client only for H323 calls
 * - For H.239 remote OLC, set sessionID to 5 in the OLC ACK
 * - For debug purposes, inserted assert statements in OpalH224Handler::TransmitFrame() method.
 * - Corrected the xmit bit rate to 64000 in the H.224 capability.
 *
 * Revision 1.3  2011-05-10 03:14:25  joseph
 * Added FECC (Far End Camera Control) feature to the GW.
 *
 * Revision 1.2  2009-12-11 20:02:01  roni
 * *** empty log message ***
 *
 * Revision 1.2  2009-01-14 17:17:06  roni
 * *** empty log message ***
 *
 * Revision 1.1.1.1  2009/01/02 16:23:16  roni
 * adding openH323
 *
 * Revision 1.2  2008/10/23 19:59:12  roni
 * *** empty log message ***
 *
 * Revision 1.2  2007/11/01 14:35:52  willamowius
 * add newline at end of file
 *
 * Revision 1.1  2007/08/06 20:51:05  shorne
 * First commit of h323plus
 *
 * Revision 1.1  2006/06/22 11:07:23  shorne
 * Backport of FECC (H.224) from Opal
 *
 * Revision 1.3  2006/05/01 10:29:50  csoutheren
 * Added pragams for gcc < 4
 *
 * Revision 1.2  2006/04/24 12:53:50  rjongbloed
 * Port of H.224 Far End Camera Control to DevStudio/Windows
 *
 * Revision 1.1  2006/04/20 16:48:17  hfriederich
 * Initial version of H.224/H.281 implementation.
 *
 */

#include <ptlib.h>
#include <h323.h>

#ifdef H323_H224

#ifdef __GNUC__
#pragma implementation "h224.h"
#pragma implementation "h224handler.h"
#endif

#ifdef _MSC_VER
#pragma warning(disable : 4244)
#endif

#include <h224.h>
#include <h224handler.h>
#include <h323con.h>
#include <h245.h>


static void prHexString (const unsigned char *data, int len) {
#if 0
	for (int i=0; i<len; i++)
		printf ("%02x",*(data+i));
	printf ("\n");
#endif
}

H224_Frame::H224_Frame(PINDEX size)
: Q922_Frame(H224_HEADER_SIZE + size)
{
  SetHighPriority(FALSE);
	
  SetControlFieldOctet(0x03);
	
  BYTE *data = GetInformationFieldPtr();
	
  // setting destination & source terminal address to BROADCAST
  data[0] = 0;
  data[1] = 0;
  data[2] = 0;
  data[3] = 0;
	
  // setting Client ID to CME
  data[4] = 0;
	
  // setting ES / BS / C1 / C0 / Segment number to zero
  data[5] = 0;
}

H224_Frame::H224_Frame(BYTE const *buffer, PINDEX length)
: Q922_Frame(buffer,length)
{
  SetInformationFieldSize(length - Q922_HEADER_SIZE);
}

H224_Frame::~H224_Frame()
{
}

void H224_Frame::SetHighPriority(BOOL flag)
{
  SetHighOrderAddressOctet(0x00);
	
  if(flag) {
    SetLowOrderAddressOctet(0x71);
  } else {
    SetLowOrderAddressOctet(0x61);
  }
}

WORD H224_Frame::GetDestinationTerminalAddress() const
{
  BYTE *data = GetInformationFieldPtr();
  return (WORD)((data[0] << 8) | data[1]);
}

void H224_Frame::SetDestinationTerminalAddress(WORD address)
{
  BYTE *data = GetInformationFieldPtr();
  data[0] = (BYTE)(address >> 8);
  data[1] = (BYTE) address;
}

WORD H224_Frame::GetSourceTerminalAddress() const
{
  BYTE *data = GetInformationFieldPtr();
  return (WORD)((data[2] << 8) | data[3]);
}

void H224_Frame::SetSourceTerminalAddress(WORD address)
{
  BYTE *data = GetInformationFieldPtr();
  data[2] = (BYTE)(address >> 8);
  data[3] = (BYTE) address;
}

BYTE H224_Frame::GetClientID() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return data[4] & 0x7f;
}

void H224_Frame::SetClientID(BYTE clientID)
{
  // At the moment, only H.281 (client ID 0x01)
  // is supported
  PAssert(clientID <= 0x01, "Invalid client ID");
	
  BYTE *data = GetInformationFieldPtr();
	
  data[4] = clientID;
}

BOOL H224_Frame::GetBS() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return (data[5] & 0x80) != 0;
}

void H224_Frame::SetBS(BOOL flag)
{
  BYTE *data = GetInformationFieldPtr();
	
  if(flag) {
    data[5] |= 0x80;
  }	else {
    data[5] &= 0x7f;
  }
}

BOOL H224_Frame::GetES() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return (data[5] & 0x40) != 0;
}

void H224_Frame::SetES(BOOL flag)
{
  BYTE *data = GetInformationFieldPtr();
	
  if(flag) {
    data[5] |= 0x40;
  } else {
    data[5] &= 0xbf;
  }
}

BOOL H224_Frame::GetC1() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return (data[5] & 0x20) != 0;
}

void H224_Frame::SetC1(BOOL flag)
{
  BYTE *data = GetInformationFieldPtr();
	
  if(flag) {
    data[5] |= 0x20;
  } else {
    data[5] &= 0xdf;
  }
}

BOOL H224_Frame::GetC0() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return (data[5] & 0x10) != 0;
}

void H224_Frame::SetC0(BOOL flag)
{
  BYTE *data = GetInformationFieldPtr();
	
  if(flag) {
    data[5] |= 0x10;
  }	else {
    data[5] &= 0xef;
  }
}

BYTE H224_Frame::GetSegmentNumber() const
{
  BYTE *data = GetInformationFieldPtr();
	
  return (data[5] & 0x0f);
}

void H224_Frame::SetSegmentNumber(BYTE segmentNumber)
{
  BYTE *data = GetInformationFieldPtr();
	
  data[5] &= 0xf0;
  data[5] |= (segmentNumber & 0x0f);
}

BOOL H224_Frame::Decode(const BYTE *data, 
						PINDEX size)
{
  BOOL result = Q922_Frame::Decode(data, size);
	
  if(result == FALSE) {
	return FALSE;
  }
	
  // doing some validity check for H.224 frames
  BYTE highOrderAddressOctet = GetHighOrderAddressOctet();
  BYTE lowOrderAddressOctet = GetLowOrderAddressOctet();
  BYTE controlFieldOctet = GetControlFieldOctet();
	
  if((highOrderAddressOctet != 0x00) ||
     (!(lowOrderAddressOctet == 0x61 || lowOrderAddressOctet == 0x71)) ||
     (controlFieldOctet != 0x03) ||
     (GetClientID() > 0x02))
  {		
	  return FALSE;
  }
	
  return TRUE;
}

////////////////////////////////////

OpalH224Handler::OpalH224Handler()
: transmitMutex()
{
  session=NULL;
  canTransmit=TRUE;
  transmitFrame=NULL;
  transmitBitIndex=0;
  transmitStartTime=NULL;
  receiverThread=NULL;
  h281Handler=NULL;
  sessionKey=NULL;
  memset(buf,0,sizeof(buf));
}

OpalH224Handler::OpalH224Handler(H323Channel::Directions dir,
                                 H323Connection & connection,
								 unsigned sessionID)
: transmitMutex()
{
  // Really need to check this?

  H245_TransportAddress addr;
  connection.GetControlChannel().SetUpTransportPDU(addr, H323Transport::UseLocalTSAP);
  session=connection.UseSession(sessionID,addr,H323Channel::IsBidirectional);

  h281Handler = connection.CreateH281ProtocolHandler(dir,*this);
  receiverThread = NULL;
	
  sessionKey=0;
  transmitFrame = NULL;	
  canTransmit = FALSE;
}

OpalH224Handler::~OpalH224Handler()
{
  if (h281Handler)
    delete h281Handler;
}

void OpalH224Handler::setSessionKey(unsigned char *sessionKey_) {
  if (sessionKey_) {
    memcpy(buf,sessionKey_,16);
    sessionKey=buf;
  }
}

void OpalH224Handler::StartTransmit()
{
  PWaitAndSignal m(transmitMutex);
	
  if(canTransmit == TRUE) {
    return;
  }
	
  canTransmit = TRUE;
	
  transmitFrame = new RTP_DataFrame(300);
  
  // Use payload code 100 as this seems to be common to other implementations
  transmitFrame->SetPayloadType((RTP_DataFrame::PayloadTypes)100);
  transmitBitIndex = 7;
  transmitStartTime = new PTime();
	
  SendClientList();
  SendExtraCapabilities();
}

void OpalH224Handler::StopTransmit()
{
  PWaitAndSignal m(transmitMutex);
	
  delete transmitStartTime;
  transmitStartTime = NULL;
	
  canTransmit = FALSE;
}

void OpalH224Handler::StartReceive()
{
  if(receiverThread != NULL) {
    PTRACE(5, "H.224 handler is already receiving");
    return;
  }
	
  receiverThread = CreateH224ReceiverThread();
  receiverThread->Resume();
}

void OpalH224Handler::StopReceive()
{
  if(receiverThread != NULL) {
    receiverThread->Close();
    delete receiverThread;
    receiverThread=NULL;
  }
}

BOOL OpalH224Handler::SendClientList()
{
  PWaitAndSignal m(transmitMutex);
	
  PTRACE( 1, "can transmit " << canTransmit );
  if(canTransmit == FALSE) {
    return FALSE;
  }
	
  H224_Frame h224Frame = H224_Frame(4);
  h224Frame.SetHighPriority(TRUE);
  h224Frame.SetDestinationTerminalAddress(H224_BROADCAST);
  h224Frame.SetSourceTerminalAddress(H224_BROADCAST);
	
  // CME frame
  h224Frame.SetClientID(0x00);
	
  // Begin and end of sequence
  h224Frame.SetBS(TRUE);
  h224Frame.SetES(TRUE);
  h224Frame.SetC1(FALSE);
  h224Frame.SetC0(FALSE);
  h224Frame.SetSegmentNumber(0);
	
  BYTE *ptr = h224Frame.GetClientDataPtr();
	
  ptr[0] = 0x01; // Client list code
  ptr[1] = 0x00; // Message code
  ptr[2] = 0x01; // one client
  ptr[3] = (0x80 | H281_CLIENT_ID); // H.281 with extra capabilities
	
  PTRACE( 3, "sending client list:\n" );
  prHexString( ptr, h224Frame.GetSize() );

  TransmitFrame(h224Frame);
	
  return TRUE;
}

BOOL OpalH224Handler::SendExtraCapabilities()
{
  PWaitAndSignal m(transmitMutex);
	
  if(canTransmit == FALSE) {
    return FALSE;
  }
	
  h281Handler->SendExtraCapabilities();
	
  return TRUE;
}

BOOL OpalH224Handler::SendClientListCommand()
{
  PWaitAndSignal m(transmitMutex);
	
  PTRACE(1, "SendClientListCommand can transmit: " << canTransmit);
  
  if(canTransmit == FALSE) {
    return FALSE;
  }
	
  H224_Frame h224Frame = H224_Frame(2);
  h224Frame.SetHighPriority(TRUE);
  h224Frame.SetDestinationTerminalAddress(H224_BROADCAST);
  h224Frame.SetSourceTerminalAddress(H224_BROADCAST);
	
  // CME frame
  h224Frame.SetClientID(0x00);
	
  // Begin and end of sequence
  h224Frame.SetBS(TRUE);
  h224Frame.SetES(TRUE);
  h224Frame.SetC1(FALSE);
  h224Frame.SetC0(FALSE);
  h224Frame.SetSegmentNumber(0);
	
  BYTE *ptr = h224Frame.GetClientDataPtr();
	
  ptr[0] = 0x01; // Client list code
  ptr[1] = 0xff; // Command code
	
  TransmitFrame(h224Frame);
	
  return TRUE;
}

BOOL OpalH224Handler::SendExtraCapabilitiesCommand(BYTE clientID)
{
  PWaitAndSignal m(transmitMutex);
	
  if(canTransmit == FALSE) {
    return FALSE;
  }
	
  if(clientID != H281_CLIENT_ID) {
    return FALSE;
  }
	
  H224_Frame h224Frame = H224_Frame(4);
  h224Frame.SetHighPriority(TRUE);
  h224Frame.SetDestinationTerminalAddress(H224_BROADCAST);
  h224Frame.SetSourceTerminalAddress(H224_BROADCAST);
	
  // CME frame
  h224Frame.SetClientID(0x00);
	
  // Begin and end of sequence
  h224Frame.SetBS(TRUE);
  h224Frame.SetES(TRUE);
  h224Frame.SetC1(FALSE);
  h224Frame.SetC0(FALSE);
  h224Frame.SetSegmentNumber(0);
	
  BYTE *ptr = h224Frame.GetClientDataPtr();
	
  ptr[0] = 0x01; // Client list code
  ptr[1] = 0xFF; // Response code
  ptr[2] = (0x80 | clientID); // clientID with extra capabilities
	
  TransmitFrame(h224Frame);
	
  return TRUE;
}

BOOL OpalH224Handler::SendExtraCapabilitiesMessage(BYTE clientID, 
												   BYTE *data, PINDEX length)
{	
  PWaitAndSignal m(transmitMutex);
	
  // only H.281 supported at the moment
  if(clientID != H281_CLIENT_ID) {
	
    return FALSE;
  }
	
  if(canTransmit == FALSE) {
    return FALSE;
  }
	
  H224_Frame h224Frame = H224_Frame(length+3);
  h224Frame.SetHighPriority(FALSE);
  h224Frame.SetDestinationTerminalAddress(H224_BROADCAST);
  h224Frame.SetSourceTerminalAddress(H224_BROADCAST);
	
  // use clientID zero to indicate a CME frame
  h224Frame.SetClientID(0x00);
	
  // Begin and end of sequence, rest is zero
  h224Frame.SetBS(TRUE);
  h224Frame.SetES(TRUE);
  h224Frame.SetC1(FALSE);
  h224Frame.SetC0(FALSE);
  h224Frame.SetSegmentNumber(0);
	
  BYTE *ptr = h224Frame.GetClientDataPtr();
	
  ptr[0] = 0x02; // Extra Capabilities code
  ptr[1] = 0x00; // Response Code
  ptr[2] = (0x80 | clientID); // EX CAPS and ClientID
	
  memcpy(ptr+3, data, length);
	
  TransmitFrame(h224Frame);
	
  return TRUE;	
}

BOOL OpalH224Handler::TransmitClientFrame(BYTE clientID, H224_Frame & frame)
{
  PWaitAndSignal m(transmitMutex);
	
  // only H.281 is supported at the moment
  if(clientID != H281_CLIENT_ID) {
    return FALSE;
  }
	
  frame.SetClientID(clientID);
	
  TransmitFrame(frame);
	
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedFrame(H224_Frame & frame)
{
  if(frame.GetDestinationTerminalAddress() != H224_BROADCAST) {
    // only broadcast frames are handled at the moment
	PTRACE(3, "Received H.224 frame with non-broadcast address");
    return TRUE;
  }
  BYTE clientID = frame.GetClientID();
	
  if(clientID == 0x00) {
    return OnReceivedCMEMessage(frame);
  }
	
  if(clientID == H281_CLIENT_ID)	{
    h281Handler->OnReceivedMessage((const H281_Frame &)frame);
  }
	
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedCMEMessage(H224_Frame & frame)
{
  BYTE *data = frame.GetClientDataPtr();
	
  if(data[0] == 0x01) { // Client list code
	
    if(data[1] == 0x00) { // Message
      return OnReceivedClientList(frame);
		
    } else if(data[1] == 0xff) { // Command
      return OnReceivedClientListCommand();
    }
	  
  } else if(data[0] == 0x02) { // Extra Capabilities code
	  
    if(data[1] == 0x00) { // Message
      return OnReceivedExtraCapabilities(frame);
		
    } else if(data[1] == 0xff) {// Command
      return OnReceivedExtraCapabilitiesCommand();
    }
  }
	
  // incorrect frames are simply ignored
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedClientList(H224_Frame & frame)
{
  BYTE *data = frame.GetClientDataPtr();
	
  BYTE numberOfClients = data[2];
	
  PINDEX i = 3;
	
  BOOL remoteHasH281 = FALSE;
	
  PTRACE( 3, "received client list:\n" );
  prHexString( data, frame.GetSize() );

  while(numberOfClients > 0) {
	  
	BYTE clientID = (data[i] & 0x7f);
		
	if(clientID == H281_CLIENT_ID) {
	  remoteHasH281 = TRUE;
	  i++;
	} else if(clientID == 0x7e) { // extended client ID
      i += 2;
    } else if(clientID == 0x7f) { // non-standard client ID
      i += 6;
    } else { // other standard client ID such as T.140
      i++;
    }
    numberOfClients--;
  }
	
  h281Handler->SetRemoteHasH281(remoteHasH281);
	
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedClientListCommand()
{
  PTRACE( 1, "OnReceivedClientListCommand" );
  SendClientList();
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedExtraCapabilities(H224_Frame & frame)
{
  BYTE *data = frame.GetClientDataPtr();
	
  BYTE clientID = (data[2] & 0x7f);
	
  if(clientID == H281_CLIENT_ID) {
    PINDEX size = frame.GetClientDataSize() - 3;
    h281Handler->OnReceivedExtraCapabilities((data + 3), size);
  }
	
  return TRUE;
}

BOOL OpalH224Handler::OnReceivedExtraCapabilitiesCommand()
{
  SendExtraCapabilities();
  return TRUE;
}

OpalH224ReceiverThread * OpalH224Handler::CreateH224ReceiverThread()
{
  return new OpalH224ReceiverThread(this, *session);
}

void OpalH224Handler::TransmitFrame(H224_Frame & frame)
{	
  PINDEX size = frame.GetEncodedSize();
	
  if (!transmitFrame) {
    PTRACE(0, "H.224 Invalid transmitFrame size=" << size);
    return;
  }

  if (!transmitFrame->GetPayloadPtr()) {
    PTRACE(0, "H.224 Invalid GetPayloadPtr size=" << size);
    return;
  }
	
  if(!frame.Encode(transmitFrame->GetPayloadPtr(), size, transmitBitIndex)) {
    PTRACE(3, "Failed to encode H.224 frame");
    return;
  }
	
  // determining correct timestamp
  PTime currentTime = PTime();
  PTimeInterval timePassed = currentTime - *transmitStartTime;
  transmitFrame->SetTimestamp((DWORD)timePassed.GetMilliSeconds() * 8);
  
  transmitFrame->SetPayloadSize(size);
  transmitFrame->SetMarker(TRUE);
	
  if(!session->WriteData(*transmitFrame,sessionKey)) {
    PTRACE(3, "Failed to write encoded H.224 frame");
  }
  prHexString(transmitFrame->GetPayloadPtr(),transmitFrame->GetPayloadSize());
}

////////////////////////////////////

OpalH224ReceiverThread::OpalH224ReceiverThread(OpalH224Handler *theH224Handler, RTP_Session & session)
: PThread(10000, NoAutoDeleteThread, HighestPriority, "H.224 Receiver Thread"),
  rtpSession(session)
{
  h224Handler = theH224Handler;
  timestamp = 0;
  terminate = FALSE;
}

OpalH224ReceiverThread::~OpalH224ReceiverThread()
{
}

void OpalH224ReceiverThread::Main()
{	
  RTP_DataFrame packet = RTP_DataFrame(300);
  H224_Frame h224Frame = H224_Frame();
	
  unsigned char *sessionKey = h224Handler->getSessionKey();

  for (;;) {
	  
    inUse.Wait();
		
    if(!rtpSession.ReadBufferedData(timestamp, packet)) {
      inUse.Signal();
      return;
    }
	
    if (sessionKey)
      H323Channel::AesDecrypt(sessionKey,packet);
    //printf ("sessionKey = %p\n", sessionKey);
    timestamp = packet.GetTimestamp();
		
    if(h224Frame.Decode(packet.GetPayloadPtr(), packet.GetPayloadSize())) {
      BOOL result = h224Handler->OnReceivedFrame(h224Frame);
	  PTRACE(3, "Decoding of H.224 ignored - return TRUE");

      if(result == FALSE) {
        // FALSE indicates a serious problem, therefore the thread is closed
        return;
      }
    } else {
	  PTRACE(3, "Decoding of H.224 frame failed");
    }
		
    inUse.Signal();
		
    if(terminate == TRUE) {
      return;
    }
  }
}

void OpalH224ReceiverThread::Close()
{
  PTRACE(3, "H.224 Receiver Close");
  if ( terminate )
    return;

  rtpSession.Close(TRUE);
	
  inUse.Wait();
	
  terminate = TRUE;
	
  inUse.Signal();
	
  PAssert(WaitForTermination(10000), "H224 receiver thread not terminated");
}

#endif // H323_H224

