home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.muug.mb.ca
/
2014.06.ftp.muug.mb.ca.tar
/
ftp.muug.mb.ca
/
pub
/
openh323.tar.gz
/
openh323.tar
/
openh323
/
src
/
h263codec.cxx
< prev
next >
Wrap
C/C++ Source or Header
|
2003-08-03
|
28KB
|
919 lines
/*
* h263codec.cxx
*
* H.323 protocol handler
*
* Open H323 Library
*
* Copyright (c) 1998-2000 Equivalence Pty. Ltd.
*
* 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 Open H323 Library.
*
* The Initial Developer of the Original Code is Equivalence Pty. Ltd.
*
* Portions of this code were written with the financial assistance of
* AliceStreet (http://www.alicestreet.com)
*
* Contributor(s): Guilhem Tardy (gtardy@marchnetworks.com)
*
* $Log: h263codec.cxx,v $
* Revision 1.23 2003/08/04 00:03:41 dereksmithies
* Reorganise test of H323_VICH263 switch
*
* Revision 1.22 2003/08/01 02:01:42 csoutheren
* Changed to disable when VIC 263 not installed
*
* Revision 1.21 2003/07/29 03:57:00 dereksmithies
* Remove weird boxes on startup.
*
* Revision 1.20 2003/07/24 08:09:18 dereksmithies
* Update to use vich263 codec, instead of ffmpeg
*
* Revision 1.19 2003/06/14 05:54:23 rjongbloed
* Fixed MSVC warning.
* Fixed what seemed an obvious mistake using boolean or operator (||) instead
* of the bitwise or operator (|).
*
* Revision 1.18 2003/06/12 00:24:32 dereksmithies
* Modify so QCIF-CIF behaviour similar to that for h261codec.cxx
*
* Revision 1.17 2003/06/10 01:37:25 dereksmithies
* Changes so it should not crash under windows/release mode
*
* Revision 1.16 2003/06/06 06:32:08 rjongbloed
* Fixed MSVC warning
*
* Revision 1.15 2003/06/06 05:18:54 dereksmithies
* Fix startup delay bug. Remove all large packets from the network. Improve reliability.
*
* Revision 1.14 2003/06/03 05:01:23 rjongbloed
* Fixed some trace logging (removed usage of cerr)
*
* Revision 1.13 2003/06/03 00:49:01 dereksmithies
* Fix divide by zero error, which happens on win95 & win98 machines.
*
* Revision 1.12 2003/06/02 07:56:56 rjongbloed
* Fixed media format passed to ancestor codec class
*
* Revision 1.11 2003/05/27 04:20:59 dereksmithies
* Fix so that codec resizes correctly after capability exchange
* No longer crashes if one endpoint is CIF, other is QCIF.
*
* Revision 1.10 2003/05/16 04:42:24 rjongbloed
* Removed extraneous code, and other cosmetic changes.
* Fixed compare function for capability.
* Extra trace logging.
*
* Revision 1.9 2003/05/15 01:35:27 dereksmithies
* Frame length fix
*
* Revision 1.8 2003/05/14 13:51:51 rjongbloed
* Removed hack of using special payload type for H.263 for a method which
* would be less prone to failure in the future.
* Removed static "initialisation" function as this should be done
* internally and not in the application.
* Changed media format to be straight H.263 and not OpalH.263
* Moved Win32 stderr output in ffmpeg AVCODEC interception from
* application to library.
* Fixed some warnings.
*
* Revision 1.7 2003/05/14 03:07:17 rjongbloed
* Made sure video buffer is large enough
*
* Revision 1.6 2003/05/05 11:59:25 robertj
* Changed to use autoconf style selection of options and subsystems.
*
* Revision 1.5 2003/05/02 04:21:30 craigs
* Added lots of extra H.263 support
*
* Revision 1.4 2003/04/21 21:50:22 dereks
* Implement suggestion from Guilhem Tardy. Many thanks.
*
* Revision 1.3 2003/04/16 04:26:57 dereks
* Initial release of h263 codec, which utilises the ffmpeg library.
* Thanks to Guilhem Tardy, and to AliceStreet.
*
* Revision 1.2 2002/08/05 10:03:47 robertj
* Cosmetic changes to normalise the usage of pragma interface/implementation.
*
* Revision 1.1 2002/05/19 22:31:12 dereks
* Initial release of stub file for h263 codec. Thanks Guilhem Tardy.
*/
/*
* Initial release notes from Guilhem Tardy::
*
* Added support for video capabilities & codec, still needs the actual codec itself!
* The code for varying bit rate is copied from h261codec.cxx,
* until it is moved to a separate file common to both video codecs.
*
*/
#include <ptlib.h>
#ifdef __GNUC__
#pragma implementation "h263codec.h"
#endif
#include "h263codec.h"
#if defined(H323_VICH263)
#include "h245.h"
#include "rtp.h"
#if defined(_MSC_VER)
#pragma comment(lib, H323_VICH263_LIBRARY)
#endif
#include <encoder-h263.h>
#include <decoder-h263.h>
/* #define INC_ENCODE 1 */
//////////////////////////////////////////////////////////////////////////////
static void h263_vic_printon(char * str);
static void h263_vic_printon(char * str)
{
PTRACE(6, "Vic H263\t" << str);
strlen(str); /*Stop any compiler warning about unused variable*/
}
class VicH263Link : public PObject
{
PCLASSINFO(VicH263Link, PObject)
public:
VicH263Link();
};
VicH263Link::VicH263Link()
{
vich263_set_print_fn(h263_vic_printon);
}
///////////////////////////////////////////////////////////////////////////////////////
#define new PNEW
static OpalMediaFormat const H263_MediaFormat("H.263",
OpalMediaFormat::DefaultVideoSessionID,
RTP_DataFrame::H263,
FALSE, // No jitter for video
180000, // bits/sec
2000, // Not sure of this value!
0, // No intrinsic time per frame
OpalMediaFormat::VideoTimeUnits);
//////////////////////////////////////////////////////////////////////////////
H323_H263Capability::H323_H263Capability(unsigned _sqcifMPI,
unsigned _qcifMPI,
unsigned _cifMPI,
unsigned _cif4MPI,
unsigned _cif16MPI,
unsigned _maxBitRate,
unsigned _videoFrameRate,
BOOL _unrestrictedVector,
BOOL _arithmeticCoding,
BOOL _advancedPrediction,
BOOL _pbFrames,
BOOL _temporalSpatialTradeOff,
unsigned _hrd_B,
unsigned _bppMaxKb,
unsigned _slowSqcifMPI,
unsigned _slowQcifMPI,
unsigned _slowCifMPI,
unsigned _slowCif4MPI,
unsigned _slowCif16MPI,
BOOL _errorCompensation)
{
sqcifMPI = (_sqcifMPI>0?_sqcifMPI:-(int)_slowSqcifMPI);
qcifMPI = (_qcifMPI>0?_qcifMPI:-(int)_slowQcifMPI);
cifMPI = (_cifMPI>0?_cifMPI:-(int)_slowCifMPI);
cif4MPI = (_cif4MPI>0?_cif4MPI:-(int)_slowCif4MPI);
cif16MPI = (_cif16MPI>0?_cif16MPI:-(int)_slowCif16MPI);
maxBitRate = _maxBitRate;
videoFrameRate = _videoFrameRate;
temporalSpatialTradeOff = _temporalSpatialTradeOff;
pbFrames = _pbFrames;
advancedPrediction = _advancedPrediction;
arithmeticCoding = _arithmeticCoding;
unrestrictedVector = _unrestrictedVector;
hrd_B = _hrd_B;
bppMaxKb = _bppMaxKb;
errorCompensation = _errorCompensation;
}
PObject * H323_H263Capability::Clone() const
{
return new H323_H263Capability(*this);
}
PObject::Comparison H323_H263Capability::Compare(const PObject & obj) const
{
Comparison result = H323Capability::Compare(obj);
if (result != EqualTo)
return result;
PAssert(obj.IsDescendant(H323_H263Capability::Class()), PInvalidCast);
const H323_H263Capability & other = (const H323_H263Capability &)obj;
/*
if ((sqcifMPI > other.sqcifMPI) ||
(qcifMPI > other.qcifMPI) ||
(cifMPI > other.cifMPI) ||
(cif4MPI > other.cif4MPI) ||
(cif16MPI > other.cif16MPI))
return GreaterThan;
if ((cif16MPI < other.cif16MPI) ||
(cif4MPI < other.cif4MPI) ||
(cifMPI < other.cifMPI) ||
(qcifMPI < other.qcifMPI))
return LessThan;
*/
if (
((sqcifMPI > 0) && (other.sqcifMPI > 0)) ||
((qcifMPI > 0) && (other.qcifMPI > 0)) ||
((cifMPI > 0) && (other.cifMPI > 0)) ||
((cif4MPI > 0) && (other.cif4MPI > 0)) ||
((cif16MPI > 0) && (other.cif16MPI > 0))
)
return EqualTo;
if (qcifMPI > 0)
return LessThan;
return GreaterThan;
return EqualTo;
}
PString H323_H263Capability::GetFormatName() const
{
PString ret = H263_MediaFormat;
if (sqcifMPI)
ret += "-SQCIF";
if (qcifMPI)
ret += "-QCIF";
if (cifMPI)
ret += "-CIF";
if (cif4MPI)
ret += "-CIF4";
if (cif16MPI)
ret += "-CIF16";
return ret;
}
unsigned H323_H263Capability::GetSubType() const
{
return H245_VideoCapability::e_h263VideoCapability;
}
BOOL H323_H263Capability::OnSendingPDU(H245_VideoCapability & cap) const
{
cap.SetTag(H245_VideoCapability::e_h263VideoCapability);
H245_H263VideoCapability & h263 = cap;
if (sqcifMPI > 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_sqcifMPI);
h263.m_sqcifMPI = sqcifMPI;
}
if (qcifMPI > 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_qcifMPI);
h263.m_qcifMPI = qcifMPI;
}
if (cifMPI > 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_cifMPI);
h263.m_cifMPI = cifMPI;
}
if (cif4MPI > 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_cif4MPI);
h263.m_cif4MPI = cif4MPI;
}
if (cif16MPI > 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_cif16MPI);
h263.m_cif16MPI = cif16MPI;
}
h263.m_temporalSpatialTradeOffCapability = temporalSpatialTradeOff;
h263.m_maxBitRate = maxBitRate;
if (sqcifMPI < 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_slowSqcifMPI);
h263.m_slowSqcifMPI = -sqcifMPI;
}
if (qcifMPI < 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_slowQcifMPI);
h263.m_slowQcifMPI = -qcifMPI;
}
if (cifMPI < 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_slowCifMPI);
h263.m_slowCifMPI = -cifMPI;
}
if (cif4MPI < 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_slowCif4MPI);
h263.m_slowCif4MPI = -cif4MPI;
}
if (cif16MPI < 0) {
h263.IncludeOptionalField(H245_H263VideoCapability::e_slowCif16MPI);
h263.m_slowCif16MPI = -cif16MPI;
}
return TRUE;
}
BOOL H323_H263Capability::OnSendingPDU(H245_VideoMode & pdu) const
{
pdu.SetTag(H245_VideoMode::e_h263VideoMode);
H245_H263VideoMode & mode = pdu;
mode.m_resolution.SetTag(cif16MPI ? H245_H263VideoMode_resolution::e_cif16
:(cif4MPI ? H245_H263VideoMode_resolution::e_cif4
:(cifMPI ? H245_H263VideoMode_resolution::e_cif
:(qcifMPI ? H245_H263VideoMode_resolution::e_qcif
: H245_H263VideoMode_resolution::e_sqcif))));
mode.m_bitRate = maxBitRate;
mode.m_unrestrictedVector = unrestrictedVector;
mode.m_arithmeticCoding = arithmeticCoding;
mode.m_advancedPrediction = advancedPrediction;
mode.m_pbFrames = pbFrames;
mode.m_errorCompensation = errorCompensation;
return TRUE;
}
BOOL H323_H263Capability::OnReceivedPDU(const H245_VideoCapability & cap)
{
if (cap.GetTag() != H245_VideoCapability::e_h263VideoCapability)
return FALSE;
const H245_H263VideoCapability & h263 = cap;
if (h263.HasOptionalField(H245_H263VideoCapability::e_sqcifMPI))
sqcifMPI = h263.m_sqcifMPI;
else if (h263.HasOptionalField(H245_H263VideoCapability::e_slowSqcifMPI))
sqcifMPI = -(int)h263.m_slowSqcifMPI;
else
sqcifMPI = 0;
if (h263.HasOptionalField(H245_H263VideoCapability::e_qcifMPI))
qcifMPI = h263.m_qcifMPI;
else if (h263.HasOptionalField(H245_H263VideoCapability::e_slowQcifMPI))
qcifMPI = -(int)h263.m_slowQcifMPI;
else
qcifMPI = 0;
if (h263.HasOptionalField(H245_H263VideoCapability::e_cifMPI))
cifMPI = h263.m_cifMPI;
else if (h263.HasOptionalField(H245_H263VideoCapability::e_slowCifMPI))
cifMPI = -(int)h263.m_slowCifMPI;
else
cifMPI = 0;
if (h263.HasOptionalField(H245_H263VideoCapability::e_cif4MPI))
cif4MPI = h263.m_cif4MPI;
else if (h263.HasOptionalField(H245_H263VideoCapability::e_slowCif4MPI))
cif4MPI = -(int)h263.m_slowCif4MPI;
else
cif4MPI = 0;
if (h263.HasOptionalField(H245_H263VideoCapability::e_cif16MPI))
cif16MPI = h263.m_cif16MPI;
else if (h263.HasOptionalField(H245_H263VideoCapability::e_slowCif16MPI))
cif16MPI = -(int)h263.m_slowCif16MPI;
else
cif16MPI = 0;
maxBitRate = h263.m_maxBitRate;
unrestrictedVector = h263.m_unrestrictedVector;
arithmeticCoding = h263.m_arithmeticCoding;
advancedPrediction = h263.m_advancedPrediction;
pbFrames = h263.m_pbFrames;
temporalSpatialTradeOff = h263.m_temporalSpatialTradeOffCapability;
hrd_B = h263.m_hrd_B;
bppMaxKb = h263.m_bppMaxKb;
errorCompensation = h263.m_errorCompensation;
return TRUE;
}
H323Codec * H323_H263Capability::CreateCodec(H323Codec::Direction direction) const
{
return new H323_H263Codec(direction, sqcifMPI, qcifMPI, cifMPI, cif4MPI, cif16MPI, maxBitRate, videoFrameRate);
}
//////////////////////////////////////////////////////////////////////////////
H323_H263Codec::H323_H263Codec(Direction dir,
unsigned _sqcifMPI,
unsigned _qcifMPI,
unsigned _cifMPI,
unsigned _cif4MPI,
unsigned _cif16MPI,
unsigned _maxBitRate,
unsigned _videoFrameRate)
: H323VideoCodec(H263_MediaFormat, dir)
{
PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De")
<< "coder created. Data rate=" << _maxBitRate
<< " Frame rate=" << _videoFrameRate);
bitRateHighLimit = _maxBitRate;
if (bitRateHighLimit == 0) {
PTRACE(3, "H263\tData Rate is set to 1000 Kb/sec, as supplied value (0) is invalid");
bitRateHighLimit = 1000 * 1024;
}
framesPerSec = _videoFrameRate;
if (framesPerSec == 0) {
PTRACE(3, "H263\tFrame Rate is set to 25 frames/sec, as supplied value (0) is invalid");
framesPerSec = 25;
}
int shifts = -1;
if (_sqcifMPI) {
shifts = 0; PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder for _sqcifMPI ");
}
if (_qcifMPI) {
shifts = 1; PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder for _qcifMPI");
}
if (_cifMPI) {
shifts = 2; PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder for _cifMPI");
}
if (_cif4MPI) {
shifts = 3; PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder for _cif4MPI");
}
if (_cif16MPI) {
shifts = 4; PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder for _cif16MPI");
}
if (shifts < 0) {
PTRACE(1, "H263\tERROR in definition of h263 size");
return;
}
PTRACE(3, "H263\t" << (dir == Encoder ? "En" : "De") << "coder created."
<< "for a size of " << (88 << shifts) << "x" << (72 << shifts));
Resize(88 << shifts , 72 << shifts); //Fill picture structure, open codec.
frameNum = 0;
InitialiseCodec();
}
H323_H263Codec::~H323_H263Codec()
{
PWaitAndSignal mutex1(videoHandlerActive);
CloseCodec();
if (videoDecoder) {
delete videoDecoder;
videoDecoder = NULL;
}
if (videoEncoder){
delete videoEncoder;
videoEncoder = NULL;
}
}
void H323_H263Codec::InitialiseCodec()
{
// no video decoder until we receive a packet
videoDecoder = NULL;
// no video encoder until we receive a packet
videoEncoder = NULL;
}
void H323_H263Codec::CloseCodec()
{
PTRACE(6, "H263\tClose h263 video " <<(direction == Encoder ? "En" : "De") << "coder");
}
/* Notes:
Quality was primarily for NetMeeting?
fillLevel simply set the greyscale of the background
*/
//This function grabs, displays, and compresses a video frame into
//into H263 packets.
//Get another frame if all packets of previous frame have been sent.
//Get next packet on list and send that one.
//Render the current frame if all of its packets have been sent.
BOOL H323_H263Codec::Read(BYTE * buffer,
unsigned & length,
RTP_DataFrame & frame)
{
PWaitAndSignal mutex1(videoHandlerActive);
PTRACE(6,"H263\tAcquire next packet from h263 encoder.\n");
if (videoEncoder == NULL) {
videoEncoder = new H263Encoder(videoQuality, fillLevel);
}
if (rawDataChannel == NULL) {
length = 0;
PTRACE(1,"H263\tNo channel to connect to video grabber, close down video transmission thread.");
return FALSE;
}
if (!rawDataChannel->IsOpen()) {
PTRACE(1,"H263\tVideo grabber is not initialised, close down video transmission thread.");
length = 0;
return FALSE;
}
frameWidth = ((PVideoChannel *)rawDataChannel)->GetGrabWidth();
frameHeight = ((PVideoChannel *)rawDataChannel)->GetGrabHeight();
PTRACE(6, "H263\tVideo grab size is " << frameWidth << "x" << frameHeight);
if (frameWidth == 0) {
PTRACE(1,"H263\tVideo grab width is 0 x 0, close down video transmission thread.");
length=0;
return FALSE;
}
videoEncoder->SetSize(frameWidth, frameHeight);
PINDEX bytesInFrame = 0;
BOOL ok = TRUE;
#define NUMAVG 8
#ifdef INC_ENCODE
if (!videoEncoder->MoreToIncEncode()) { // get a new frame
#else
if (!videoEncoder->PacketsOutStanding()) { // }get a new frame
#endif
if (0 == frameNum) { // frame 0 means no frame has been sent yet
frameStartTime = PTimer::Tick();
}
else {
int frameTimeMs, avgFrameTimeMs, adjFrameTimeMs, avgAdjFrameTimeMs, avgFrameBytes;
PTimeInterval currentTime;
currentTime = PTimer::Tick();
frameTimeMs = (int)(currentTime - frameStartTime).GetMilliSeconds();
adjFrameTimeMs = frameTimeMs - (int)grabInterval.GetMilliSeconds(); // subtract time possibly blocked in grabbing
frameStartTime = currentTime;
sumFrameTimeMs += frameTimeMs;
avgFrameTimeMs = sumFrameTimeMs / NUMAVG;
sumFrameTimeMs -= avgFrameTimeMs;
sumAdjFrameTimeMs += adjFrameTimeMs;
avgAdjFrameTimeMs = sumAdjFrameTimeMs / NUMAVG;
sumAdjFrameTimeMs -= avgAdjFrameTimeMs;
sumFrameBytes += frameBytes;
avgFrameBytes = sumFrameBytes / NUMAVG;
sumFrameBytes -= avgFrameBytes;
//PTRACE(3,"H263\tframeNum grabInterval: "
// << frameNum << " " << grabInterval.GetMilliSeconds());
//PTRACE(3,"H263\tframeNum frameBits frameTimeMs Bps: "
// << frameNum << " " << (frameBytes*8) << " " << frameTimeMs
// << " " << frameBytes*8*1000/frameTimeMs);
//PTRACE(3,"H263\tframeNum avgFrameBits avgFrameTimeMs avgBps: "
// << frameNum << " " << (avgFrameBytes*8) << " " << avgFrameTimeMs
// << " " << avgFrameBytes*8*1000/avgFrameTimeMs);
//PTRACE(3,"H263\tframeNum avgFrameBits avgAdjFrameTimeMs avgAdjBps: "
// << frameNum << " " << (avgFrameBytes*8) << " " << avgAdjFrameTimeMs
// << " " << avgFrameBytes*8*1000/avgAdjFrameTimeMs);
if (frameNum > NUMAVG) { // do quality adjustment after first NUMAVG frames
if (0 != targetFrameTimeMs && (videoBitRateControlModes & DynamicVideoQuality)) {
int error; // error signal
int aerror;
int act; // action signal
int newQuality;
int avgFrameBitRate;
int targetFrameBits;
// keep track of average frame size and
// adjust encoder quality to get targetFrameBits bits per frame
if (avgAdjFrameTimeMs)
avgFrameBitRate = avgFrameBytes*8*1000 / avgAdjFrameTimeMs; // bits per second
else
avgFrameBitRate = avgFrameBytes*8*1000;
targetFrameBits = avgFrameBitRate * targetFrameTimeMs / 1000;
error = (frameBytes*8) - targetFrameBits; // error signal
aerror = PABS(error);
act = 0;
if (aerror > (targetFrameBits/8)) {
if (aerror > (targetFrameBits/4)) {
if (aerror > (targetFrameBits/2)) {
act = error>0 ? 2 : -4;
}
else {
act = error>0 ? 1 : -2;
}
}
else {
act = error>0 ? 1 : -1;
}
}
newQuality = videoQuality + act;
newQuality = PMIN(PMAX(newQuality, videoQMin), videoQMax);
//PTRACE(3,"H263\tframeNum targetFrameBits frameBits videoQuality newQuality: "
// << frameNum << " " << targetFrameBits << " " << (frameBytes*8) << " "
// << videoQuality << " " << newQuality);
videoQuality = newQuality;
videoEncoder->SetQualityLevel(videoQuality);
//PTRACE(3,"H263\tframeNum avgFrameBitRate bitRateHighLimit: "
// << frameNum << " " << avgFrameBitRate << " " << bitRateHighLimit);
}
}
}
//NO data is waiting to be read. Go and get some with the read call.
PTRACE(3,"H263\tRead frame from the video source.");
PTimeInterval grabStartTime = PTimer::Tick();
if (rawDataChannel->Read(videoEncoder->GetFramePtr(), bytesInFrame)) {
PTRACE(3,"H263\tSuccess. Read frame from the video source in "
<< (PTimer::Tick() - grabStartTime).GetMilliSeconds() << " ms.");
if (frameNum == 0) {
memset(videoEncoder->GetFramePtr(), 64, (frameWidth * frameHeight * 3) >> 1);
}
packetNum = 0; // reset packet counter
// If there is a Renderer attached, display the grabbed video.
if (((PVideoChannel *)rawDataChannel)->IsRenderOpen()) {
ok = RenderFrame(); //use data from grab process.
}
#ifdef INC_ENCODE
videoEncoder->PreProcessOneFrame(); //Prepare to generate H263 packets
#else
videoEncoder->ProcessOneFrame(); //Generate H263 packets
#endif
frameNum++;
} else {
PTRACE(1,"H263\tFailed to read data from video grabber, close down video transmission thread.");
return FALSE; //Read failed, return false.
}
grabInterval = PTimer::Tick() - grabStartTime;
/////////////////////////////////////////////////////////////////
/// THIS VALUE MUST BE CALCULATED AND NOT JUST SET TO 29.97Hz!!!!
/////////////////////////////////////////////////////////////////
timestampDelta = 3003;
frameBytes = 0;
}
#ifdef INC_ENCODE
videoEncoder->IncEncodeAndGetPacket(buffer,length); //encode & get next packet
frame.SetMarker(!videoEncoder->MoreToIncEncode());
#else
videoEncoder->ReadOnePacket(buffer,length); //get next packet on list
frame.SetMarker(!videoEncoder->PacketsOutStanding());
#endif
packetNum++;
// Monitor and report bandwidth usage.
// If controlling bandwidth, limit the video bandwidth to
// bitRateHighLimit by introducing a variable delay between packets.
PTimeInterval currentTime;
if (0 != bitRateHighLimit &&
(videoBitRateControlModes & AdaptivePacketDelay)) {
PTimeInterval waitBeforeSending;
if (newTime != 0) { // calculate delay and wait
currentTime = PTimer::Tick();
waitBeforeSending = newTime - currentTime;
if (waitBeforeSending > 0) PThread::Current()->Sleep(waitBeforeSending);
// report bit rate & control error for previous packet
currentTime = PTimer::Tick(); //re-acquire current time after wait
//PTRACE(3, "H263\tBitRateControl Packet(" << oldPacketNum
// << ") Bits: " << oldLength*8
// << " Interval: " << (currentTime - oldTime).GetMilliSeconds()
// << " Rate: " << oldLength*8000/(currentTime - oldTime).GetMilliSeconds()
// << " Error: " << (currentTime - newTime).GetMilliSeconds()
// << " Slept: " << waitBeforeSending.GetMilliSeconds());
}
currentTime = PTimer::Tick(); // re-acquire current time due to possible PTRACE delay
// ms = (bytes * 8) / (bps / 1000)
if (bitRateHighLimit/1000)
newTime = currentTime + length*8/(bitRateHighLimit/1000);
else
newTime = currentTime + length*8;
}
else {
// monitor & report bit rate
if (oldTime != 0) { // report bit rate for previous packet
PTimeInterval currentTime = PTimer::Tick();
//PTRACE(3, "H263\tBitRateReport Packet(" << oldPacketNum
// << ") Bits: " << oldLength*8
// << " Interval: " << (currentTime - oldTime).GetMilliSeconds()
// << " Rate: " << oldLength*8000/(currentTime - oldTime).GetMilliSeconds());
}
currentTime = PTimer::Tick(); // re-acquire current time due to possible PTRACE delay
}
//oldPacketNum = packetNum; // used only for PTRACE
oldTime = currentTime;
oldLength = length;
frameBytes += length; // count current frame bytes
return ok;
}
BOOL H323_H263Codec::Write(const BYTE * buffer,
unsigned length,
const RTP_DataFrame & frame,
unsigned & written)
{
PWaitAndSignal mutex1(videoHandlerActive);
if (rawDataChannel == NULL) {
//Some other task has killed our videohandler. Exit.
return FALSE;
}
BOOL lostPreviousPacket = FALSE;
if ((++lastSequenceNumber) != frame.GetSequenceNumber()) {
lostPreviousPacket = TRUE;
PTRACE(3,"H263\tDetected loss of one video packet. "
<< lastSequenceNumber << " != "
<< frame.GetSequenceNumber() << " Will recover.");
lastSequenceNumber = frame.GetSequenceNumber();
// SendMiscCommand(H245_MiscellaneousCommand_type::e_lostPartialPicture);
}
// always indicate we have written the entire packet
written = length;
// H.263 header is usually at start of buffer
const unsigned char * header = buffer;
// adjust for any contributing source (see SSRC in RFC1889)
PINDEX cnt = frame.GetContribSrcCount();
if (cnt > 0) {
header += cnt * 4;
length -= cnt * 4;
}
// determine video codec type
if (videoDecoder == NULL) {
/*
if ((*header & 2) && !(*header & 1)) // check value of I field in header
AWM: Intra vs. Full?
*/
videoDecoder = new H263Decoder();
videoDecoder->marks(rvts);
}
videoDecoder->mark(now);
BOOL ok = videoDecoder->decode(header, length, (char)lostPreviousPacket,
(char)frame.GetMarker(), frame.GetSequenceNumber());
if (!ok) {
PTRACE (3, "H263\t Could not decode frame, continuing in hope.");
return TRUE;
}
// If the incoming video stream changes size, resize the rendering device.
ok = Resize(videoDecoder->width(), videoDecoder->height());
if (ok && frame.GetMarker()) {
videoDecoder->sync();
ndblk = videoDecoder->ndblk();
ok = RenderFrame();
videoDecoder->resetndblk();
}
return ok;
}
BOOL H323_H263Codec::Resize(int _width, int _height)
{
if ((frameWidth == _width) && (frameHeight == _height))
return TRUE;
PTRACE(6, "H263\t" << (direction == Encoder ? "En" : "De") << "coder resizing to "
<< _width << "x" << _height << ".");
frameWidth = _width;
frameHeight = _height;
return TRUE;
}
/* RenderFrame does two things:
a) Set size of the display frame. This call happens with every frame.
A very small overhead.
b) Display a frame.
*/
BOOL H323_H263Codec::RenderFrame(const void * buffer)
{
if (rawDataChannel == NULL)
return TRUE;
//Now display local image.
((PVideoChannel *)rawDataChannel)->SetRenderFrameSize(frameWidth, frameHeight);
if (buffer == NULL)
return TRUE;
return rawDataChannel->Write(buffer, 0 /*unused parameter*/);
}
/* AWM: Look-alike to H.261 implementation */
BOOL H323_H263Codec::RenderFrame()
{
void *srcData;
if (direction == Encoder)
srcData = videoEncoder->GetFramePtr();
else
srcData = videoDecoder->GetFramePtr();
return RenderFrame(srcData);
}
void H323_H263Codec::SetTxQualityLevel(int qLevel)
{
int qualityLevel = PMIN(14, PMAX(qLevel,3));
int lowLimit = PMIN(10, qualityLevel - 2);
int highLimit = qualityLevel + 12;
videoQuality = qLevel;
videoQMin = lowLimit;
videoQMax = highLimit;
}
void H323_H263Codec::SetBackgroundFill(int idle)
{
fillLevel = PMIN(99, PMAX(idle,1));
}
void H323_H263Codec::OnLostPartialPicture()
{
PTRACE(3, "H263\tLost partial picture message ignored, not implemented");
}
void H323_H263Codec::OnLostPicture()
{
PTRACE(3, "H263\tLost picture message ignored, not implemented");
}
//////////////////////////////////////////////////////////////////////
#endif // H323_VICH263