/* * 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 #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> 1); for(int i=0; i> 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> 1); int srcuv_step = (srcXSize >> 1); int uv_step = (xSize >> 2); for(int i=0; i> 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"<> 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