/*
* video.cxx
*
* Video conferencing functions for a simple MCU
*
* Copyright (c) 2000 Equivalence Pty. Ltd.
* Copyright (c) 2004 Post Increment
*
* 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.
*
* The Original Code is Portable Windows Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions of this code were written by Post Increment (http://www.postincrement.com)
* with the assistance of funding from Citron Networks (http://www.citron.com.tw)
*
* Portions are Copyright (C) 1993 Free Software Foundation, Inc.
* All Rights Reserved.
*
* Contributor(s): Derek J Smithies (derek@indranet.co.nz)
* ------------------------------
*
* $Log: video.cxx,v $
* Revision 2.2 2004/03/23 11:40:06 csoutheren
* Fixed problem where deleting map element in-place causes crash at end of call
* Fixed problem where referencing map by iterator rather than ID
* Fixed code formatting problems
*
* Revision 2.1 2004/03/11 20:49:44 csoutheren
* Removed warnings
*
* Revision 2.0 2004/03/08 02:06:24 csoutheren
* Totally rewritten to use new connection locking mecahnism
* Added ability to monitor conferences
* Added initial support for H.323 MCU messages
* Thanks to Citron Networks for supporting this work
*
*/
#include <ptlib.h>
#ifdef _WIN32
#pragma warning(disable:4786)
#endif
#include "main.h"
#ifndef NO_MCU_VIDEO
VideoBuffer::VideoBuffer()
: xSize(176), ySize(144)
{
buffer = NULL;
SetSize( xSize, ySize );
}
VideoBuffer::~VideoBuffer()
{
delete[] buffer;
}
void VideoBuffer::WriteAll(BYTE * data, PINDEX amount)
{
if (amount == 0)
return;
PWaitAndSignal mutex(videoBufferMutex);
BYTE *yFirst, *ySecond, *u, *v, *srcYFirst, *srcYSecond, *srcU, *srcV;
int srcFrameSize = (amount<<1)/3;
int srcXSize = (srcFrameSize == (176 * 144) ? 176 : 352 );
int srcYSize = (srcFrameSize == (176 * 144) ? 144 : 288 );
yFirst = buffer;
u= buffer + bufferFrameSize;
v= buffer + bufferFrameSize + (bufferFrameSize >> 2);
srcYFirst = data;
srcYSecond = srcYFirst + (xSize < srcXSize ?
srcXSize << 1 :
srcXSize);
srcU = data + srcFrameSize;
srcV = data + srcFrameSize + (srcFrameSize >> 2);
ySecond = yFirst + (xSize < srcXSize ?
xSize :
xSize << 1); // added down here so that any changes to
// yFirst are taken into account - pez
// Special case, can fit 'in' images perfectly inside 'out' images
if ( xSize == srcXSize ) {
// just copy the whole buffer
memcpy(buffer, data, amount);
} else if ( xSize == 176 && srcXSize == 352 ) {
// Copy 1 pixel out of 2
int src_step = 3 * srcXSize;
int step = xSize;
int srcuv_step = (srcXSize >> 1);
int uv_step = 0;
for(int i=0; i<srcYSize; i+=4) {
for(int j=0; j<srcXSize; j+=4) {
*(yFirst++) = *srcYFirst;
srcYFirst += 2;
*(yFirst++) = *srcYFirst;
srcYFirst += 2;
*(ySecond++) = *srcYSecond;
srcYSecond += 2;
*(ySecond++) = *srcYSecond;
srcYSecond += 2;
*(u++) = *srcU;
*(v++) = *srcV;
srcU+= 2;
srcV+= 2;
}
srcYFirst += src_step;
srcYSecond += src_step;
yFirst += step;
ySecond += step;
srcU += srcuv_step;
srcV += srcuv_step;
u += uv_step;
v += uv_step;
}
} else {
// Copy 2 pixels from 1
int src_step = srcXSize;
int step = 3 * xSize;
int srcuv_step = 0;
int uv_step = (xSize >> 1);
for(int i=0; i<srcYSize; i+=2) {
for(int j=0; j<srcXSize; j++) {
*yFirst = *srcYFirst;
*(yFirst + xSize) = *srcYFirst;
yFirst++;
*yFirst = *srcYFirst;
*(yFirst + xSize) = *(srcYFirst++);
yFirst++;
*ySecond = *srcYSecond;
*(ySecond + xSize) = *srcYSecond;
ySecond++;
*ySecond = *srcYSecond;
*(ySecond + xSize) = *(srcYSecond++);
ySecond++;
*u = *srcU;
*(u + (xSize >> 1)) = *srcU;
u++;
if (j%2) srcU++;
*v = *srcV;
*(v + (xSize >> 1)) = *srcV;
v++;
if (j%2) srcV++;
}
srcYFirst += src_step;
srcYSecond += src_step;
yFirst += step;
ySecond += step;
srcU += srcuv_step;
srcV += srcuv_step;
u += uv_step;
v += uv_step;
}
}
return;
}
//Writes data into the specified posn of the buffer.
//0 == top left, 1 == top right
//2 == bot left, 3 == bot right
void VideoBuffer::Write(BYTE * data, PINDEX amount, PINDEX posn)
{
// It appears that a full frame is always written.
// We can determine the size of the frame by how much
// is written.
if (amount == 0)
return;
PWaitAndSignal mutex(videoBufferMutex);
BYTE *yFirst, *ySecond, *u, *v, *srcYFirst, *srcYSecond, *srcU, *srcV;
int srcFrameSize = (amount<<1)/3;
int srcXSize = (srcFrameSize == (176 * 144) ? 176 : 352 );
int srcYSize = (srcFrameSize == (176 * 144) ? 144 : 288 );
yFirst = buffer;
u= buffer + bufferFrameSize;
v= buffer + bufferFrameSize + (bufferFrameSize >> 2);
srcYFirst = data;
srcYSecond = srcYFirst + (xSize == srcXSize ?
srcXSize << 1 :
srcXSize);
srcU = data + srcFrameSize;
srcV = data + srcFrameSize + (srcFrameSize >> 2);
switch (posn) {
case 0:
break;
case 1:
yFirst +=(xSize >> 1);
u +=(xSize >> 2);
v +=(xSize >> 2);
break;
case 2:
yFirst += (bufferFrameSize >> 1);
u += (bufferFrameSize >> 3);
v += (bufferFrameSize >> 3);
break;
case 3:
yFirst += (bufferFrameSize >> 1) + (xSize >> 1);
u += (bufferFrameSize >> 3) + (xSize >> 2);
v += (bufferFrameSize >> 3) + (xSize >> 2);
break;
default:
return;
}
ySecond = yFirst + xSize; // added down here so that any changes to
// yFirst are taken into account - pez
// Special case, can fit 'in' images perfectly inside 'out' images
if ( xSize == 352 && srcXSize == 176 ) {
for(int i=0; i < 144; i+=2) {
memcpy(yFirst, srcYFirst, 176);
memcpy(ySecond, srcYSecond, 176);
memcpy(u, srcU, 88 );
memcpy(v, srcV, 88 );
srcYFirst += 352;
srcYSecond += 352;
yFirst += 704;
ySecond += 704;
srcU += 88;
srcV += 88;
u += 176;
v += 176;
}
} else if ( xSize == 176 && srcXSize == 352 ) {
// Copy 1 pixel out of 4
int src_step = 7 * srcXSize;
int step = xSize + (xSize >> 1);
int srcuv_step = (srcXSize >> 1);
int uv_step = (xSize >> 2);
for(int i=0; i<srcYSize; i+=8) {
for(int j=0; j<srcXSize; j+=8) {
*(yFirst++) = *srcYFirst;
srcYFirst += 4;
*(yFirst++) = *srcYFirst;
srcYFirst += 4;
*(ySecond++)= *srcYSecond;
srcYSecond += 4;
*(ySecond++) = *srcYSecond;
srcYSecond += 4;
*(u++) = *srcU;
*(v++) = *srcV;
srcU += 4;
srcV += 4;
}
srcYFirst += src_step;
srcYSecond += src_step;
yFirst += step;
ySecond += step;
srcU += srcuv_step;
srcV += srcuv_step;
u += uv_step;
v += uv_step;
}
} else {
// This code handles the other 2 cases in a generic fashion
// Copy 1 pixel out of 2
int src_step = 3 * srcXSize;
int step = xSize + (xSize >> 1);
int srcuv_step = (srcXSize >> 1);
int uv_step = (xSize >> 2);
for(int i=0; i<srcYSize; i+=4) {
for(int j=0; j<srcXSize; j+=4) {
*(yFirst++) = *srcYFirst;
srcYFirst += 2;
*(yFirst++) = *srcYFirst;
srcYFirst += 2;
*(ySecond++) = *srcYSecond;
srcYSecond += 2;
*(ySecond++) = *srcYSecond;
srcYSecond += 2;
*(u++) = *srcU;
*(v++) = *srcV;
srcU+= 2;
srcV+= 2;
}
srcYFirst += src_step;
srcYSecond += src_step;
yFirst += step;
ySecond += step;
srcU += srcuv_step;
srcV += srcuv_step;
u += uv_step;
v += uv_step;
}
}
return;
}
void VideoBuffer::Clear(PINDEX posn)
{
PWaitAndSignal mutex(videoBufferMutex);
BYTE *yFirst, *ySecond, *u, *v;
yFirst = buffer;
u= buffer + bufferFrameSize;
v= buffer + bufferFrameSize + (bufferFrameSize/4);
switch (posn) {
case 0:
break;
case 1:
yFirst += (xSize >> 1);
u += (xSize >> 2);
v += (xSize >> 2);
break;
case 2:
yFirst += (bufferFrameSize >> 1);
u += (bufferFrameSize >> 3);
v += (bufferFrameSize >> 3);
break;
case 3:
yFirst += (bufferFrameSize >>1) + (xSize >> 1);
u += (bufferFrameSize >> 3) + (xSize >> 2);
v += (bufferFrameSize >> 3) + (xSize >> 2);
break;
default:
return;
}
ySecond = yFirst + xSize;
for(int y=0; y < (ySize>>1); y+=2) {
memset(yFirst, 0x80, xSize >> 1); // Mid Grey
memset(ySecond, 0x80, xSize >> 1); // Mid Grey
memset(u, 0x80, xSize >> 2);
memset(v, 0x80, xSize >> 2);
yFirst += xSize * 2;
ySecond += xSize * 2;
u += (xSize >> 1);
v += (xSize >> 1);
}
return;
}
void VideoBuffer::SetSize(int x, int y)
{
PWaitAndSignal mutex(videoBufferMutex);
if ( buffer != NULL )
delete[] buffer;
xSize = x;
ySize = y;
bufferFrameSize = xSize * ySize;
videoBufferSize = bufferFrameSize
+ (bufferFrameSize >> 2 )
+ (bufferFrameSize >> 2); // Y + U + V;
buffer = new BYTE[ videoBufferSize ];
memset( buffer, 0x80, videoBufferSize); // Set Y, U and V to 0x80 - Mid Grey.
}
void VideoBuffer::Read(BYTE * data, PINDEX amount)
{
if (amount == 0)
return;
PWaitAndSignal mutex(videoBufferMutex);
memcpy(data,buffer,amount);
}
////////////////////////////////////////////////////////////////////////////////////
BOOL VideoDelay::Delay(int frameTime)
{
if (firstTime)
{
firstTime = FALSE;
previousTime = PTime();
return TRUE;
}
error += frameTime;
PTime now;
PTimeInterval delay = now - previousTime;
error -= (int)delay.GetMilliSeconds();
previousTime = now;
if (error > 0) {
#ifdef P_LINUX
usleep(error * 1000);
#else
PThread::Current()->Sleep(error);
#endif
} else if (error <= -frameTime) {
PThread::Current()->Sleep(frameTime);
previousTime = PTime();
error = 0;
}
return error <= -frameTime;
}
BOOL Conference::DetectNoise(const void * buffer, PINDEX amount)
{
short *start = (short *)buffer;
short *end = start + (amount/2);
int sum;
sum=0;
while (start != end)
if(*start<0)
sum -= *start++;
else
sum += *start++;
return (sum/amount) > 50;
}
BOOL Conference::WriteVideo(const PString & thisToken,
const void * buffer,
PINDEX amount,
const PString & roomID)
{
PWaitAndSignal mutex(roomListMutex);
// Check that the room still exists
if (videoBufferDict.Contains(roomID) == FALSE) {
cout << "ROOM HAS BEEN REMOVED (WriteVideo)" << endl;
return FALSE;
}
VideoBuffer & videoBuffer = videoBufferDict[roomID];
if (singleStream) {
videoBuffer.WriteAll((BYTE *)buffer, amount);
}
else {
// The last four elements of spokenList indicate the last
// four connections from which audio was received.
PINDEX keyIndex = FindTokensVideoPosn(thisToken,roomID);
if (keyIndex != P_MAX_INDEX)
videoBuffer.Write((BYTE *)buffer, amount, keyIndex);
}
return TRUE;
}
BOOL MyH323EndPoint::ReadVideo(const PString & /*thisToken*/,
void * buffer,
PINDEX amount,
const PString & roomID)
{
PWaitAndSignal mutex(roomListMutex);
// Check that the room still exists
if (videoBufferDict.Contains(roomID) == FALSE) {
cout << "ROOM HAS BEEN REMOVED (ReadVideo)" << endl;
return FALSE;
}
VideoBuffer & videoBuffer = videoBufferDict[roomID];
videoBuffer.Read((BYTE *)buffer,amount);
return TRUE;
}
IncomingVideo::IncomingVideo(MyH323EndPoint & _ep, OpenMCUH323Connection & _conn)
: ep(_ep), conn(_conn), width(0), height(0), frameSize(0)
{
closed = FALSE;
}
IncomingVideo::~IncomingVideo()
{
IncomingVideo::Close();
PVideoChannel::Close();
}
BOOL IncomingVideo::Write(const void * buffer, PINDEX amount)
{
amount = (frameSize*3) >> 1; // frameSize==width*height
PWaitAndSignal mutex( videoChanMutex );
if (closed){
return FALSE;
}
conn.OnIncomingVideo(buffer, amount);
return TRUE;
}
void IncomingVideo::SetRenderFrameSize(int _width, int _height)
{
PTRACE(3,"IncomingVideo Set size");
width = _width;
height = _height;
frameSize = width * height;
}
BOOL IncomingVideo::Close()
{
PWaitAndSignal mutex(videoChanMutex);
closed = TRUE;
return TRUE;
}
///////////////////////////////////////////////////////////////////////////
OutgoingVideo::OutgoingVideo(H323EndPoint & _ep, OpenMCUH323Connection & _conn, int framesPerSec, BOOL _videoLarge)
: ep(_ep), conn(_conn), videoLarge(_videoLarge)
{
closed = FALSE;
if ( ( framesPerSec>0 ) && ( framesPerSec < 31 ) ) {
msBetweenFrames= 1000/framesPerSec;
} else {
cerr << "Invalid video transmit frame rate. Frame rate should be between 1 and 30 frames per second"<<endl;
msBetweenFrames = 100;
}
}
OutgoingVideo::~OutgoingVideo()
{
OutgoingVideo::Close();
}
BOOL OutgoingVideo::Read(void *buffer, PINDEX amount)
{
PWaitAndSignal mutex1(videoChanMutex);
amount = (( videoLarge ? 352*288*3 : 176*144*3 ) >> 1);
if (!delay.Delay(msBetweenFrames))
conn.OnOutgoingVideo(buffer, amount);
return TRUE;
}
BOOL OutgoingVideo::Close()
{
// PWaitAndSignal mutex(videoChanMutex);
closed = TRUE;
return TRUE;
}
#endif //NO_MCU_VIDEO
syntax highlighted by Code2HTML, v. 0.9.1