2011-07-07 08:21:25 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by a BSD-style license
|
|
|
|
|
* that can be found in the LICENSE file in the root of the source
|
|
|
|
|
* tree. An additional intellectual property rights grant can be found
|
|
|
|
|
* in the file PATENTS. All contributing project authors may
|
|
|
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-08-19 22:33:08 +00:00
|
|
|
#include "system_wrappers/interface/cpu_features_wrapper.h"
|
2011-07-07 08:21:25 +00:00
|
|
|
#include "test_util.h"
|
2011-08-03 07:49:56 +00:00
|
|
|
#include "test_macros.h"
|
2011-07-07 08:21:25 +00:00
|
|
|
#include "rtp_dump.h"
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
|
|
using namespace webrtc;
|
|
|
|
|
|
|
|
|
|
/******************************
|
|
|
|
|
* VCMEncodeCompleteCallback
|
|
|
|
|
*****************************/
|
|
|
|
|
// Basic callback implementation
|
|
|
|
|
// passes the encoded frame directly to the encoder
|
|
|
|
|
// Packetization callback implmentation
|
|
|
|
|
VCMEncodeCompleteCallback::VCMEncodeCompleteCallback(FILE* encodedFile):
|
2011-08-03 07:49:56 +00:00
|
|
|
_encodedFile(encodedFile),
|
|
|
|
|
_encodedBytes(0),
|
|
|
|
|
_VCMReceiver(NULL),
|
|
|
|
|
_seqNo(0),
|
|
|
|
|
_encodeComplete(false),
|
|
|
|
|
_width(0),
|
|
|
|
|
_height(0),
|
|
|
|
|
_codecType(kRTPVideoNoVideo)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
}
|
|
|
|
|
VCMEncodeCompleteCallback::~VCMEncodeCompleteCallback()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2011-08-03 07:49:56 +00:00
|
|
|
VCMEncodeCompleteCallback::RegisterTransportCallback(
|
|
|
|
|
VCMPacketizationCallback* transport)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
2011-07-08 12:44:58 +00:00
|
|
|
VCMEncodeCompleteCallback::SendData(
|
|
|
|
|
const FrameType frameType,
|
|
|
|
|
const WebRtc_UWord8 payloadType,
|
|
|
|
|
const WebRtc_UWord32 timeStamp,
|
|
|
|
|
const WebRtc_UWord8* payloadData,
|
|
|
|
|
const WebRtc_UWord32 payloadSize,
|
|
|
|
|
const RTPFragmentationHeader& fragmentationHeader,
|
|
|
|
|
const webrtc::RTPVideoTypeHeader* videoTypeHdr)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
// will call the VCMReceiver input packet
|
|
|
|
|
_frameType = frameType;
|
|
|
|
|
// writing encodedData into file
|
|
|
|
|
fwrite(payloadData, 1, payloadSize, _encodedFile);
|
|
|
|
|
WebRtcRTPHeader rtpInfo;
|
|
|
|
|
rtpInfo.header.markerBit = true; // end of frame
|
|
|
|
|
rtpInfo.type.Video.isFirstPacket = true;
|
|
|
|
|
rtpInfo.type.Video.codec = _codecType;
|
|
|
|
|
switch (_codecType)
|
|
|
|
|
{
|
|
|
|
|
case webrtc::kRTPVideoH263:
|
|
|
|
|
rtpInfo.type.Video.codecHeader.H263.bits = false;
|
|
|
|
|
rtpInfo.type.Video.codecHeader.H263.independentlyDecodable = false;
|
|
|
|
|
rtpInfo.type.Video.height = (WebRtc_UWord16)_height;
|
|
|
|
|
rtpInfo.type.Video.width = (WebRtc_UWord16)_width;
|
|
|
|
|
break;
|
2011-08-03 07:49:56 +00:00
|
|
|
case webrtc::kRTPVideoVP8:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(false);
|
|
|
|
|
return -1;
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtpInfo.header.payloadType = payloadType;
|
|
|
|
|
rtpInfo.header.sequenceNumber = _seqNo++;
|
|
|
|
|
rtpInfo.header.ssrc = 0;
|
|
|
|
|
rtpInfo.header.timestamp = timeStamp;
|
|
|
|
|
rtpInfo.frameType = frameType;
|
|
|
|
|
// Size should also be received from that table, since the payload type
|
|
|
|
|
// defines the size.
|
|
|
|
|
|
|
|
|
|
_encodedBytes += payloadSize;
|
|
|
|
|
// directly to receiver
|
2011-07-08 13:05:23 +00:00
|
|
|
// TODO(hlundin): Remove assert once we've piped PictureID into VCM
|
|
|
|
|
// through the WebRtcRTPHeader.
|
|
|
|
|
assert(rtpInfo.type.Video.codec != kRTPVideoVP8);
|
2011-07-07 08:21:25 +00:00
|
|
|
_VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
|
|
|
|
|
_encodeComplete = true;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
VCMEncodeCompleteCallback::EncodedBytes()
|
|
|
|
|
{
|
|
|
|
|
return _encodedBytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
VCMEncodeCompleteCallback::EncodeComplete()
|
|
|
|
|
{
|
|
|
|
|
if (_encodeComplete)
|
|
|
|
|
{
|
|
|
|
|
_encodeComplete = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VCMEncodeCompleteCallback::Initialize()
|
|
|
|
|
{
|
|
|
|
|
_encodeComplete = false;
|
|
|
|
|
_encodedBytes = 0;
|
|
|
|
|
_seqNo = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
VCMEncodeCompleteCallback::ResetByteCount()
|
|
|
|
|
{
|
|
|
|
|
_encodedBytes = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 07:49:56 +00:00
|
|
|
/***********************************/
|
|
|
|
|
/* VCMRTPEncodeCompleteCallback */
|
|
|
|
|
/***********************************/
|
2011-07-07 08:21:25 +00:00
|
|
|
// Encode Complete callback implementation
|
|
|
|
|
// passes the encoded frame via the RTP module to the decoder
|
|
|
|
|
// Packetization callback implmentation
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
2011-07-08 12:44:58 +00:00
|
|
|
VCMRTPEncodeCompleteCallback::SendData(
|
|
|
|
|
const FrameType frameType,
|
|
|
|
|
const WebRtc_UWord8 payloadType,
|
|
|
|
|
const WebRtc_UWord32 timeStamp,
|
|
|
|
|
const WebRtc_UWord8* payloadData,
|
|
|
|
|
const WebRtc_UWord32 payloadSize,
|
|
|
|
|
const RTPFragmentationHeader& fragmentationHeader,
|
|
|
|
|
const webrtc::RTPVideoTypeHeader* videoTypeHdr)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
|
|
|
|
_frameType = frameType;
|
|
|
|
|
_encodedBytes+= payloadSize;
|
|
|
|
|
_encodeComplete = true;
|
|
|
|
|
//printf("encoded = %d Bytes\n", payloadSize);
|
2011-07-08 12:44:58 +00:00
|
|
|
return _RTPModule->SendOutgoingData(frameType,
|
|
|
|
|
payloadType,
|
|
|
|
|
timeStamp,
|
|
|
|
|
payloadData,
|
|
|
|
|
payloadSize,
|
|
|
|
|
&fragmentationHeader,
|
|
|
|
|
videoTypeHdr);
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
VCMRTPEncodeCompleteCallback::EncodedBytes()
|
|
|
|
|
{
|
|
|
|
|
// only good for one call - after which will reset value;
|
|
|
|
|
float tmp = _encodedBytes;
|
|
|
|
|
_encodedBytes = 0;
|
|
|
|
|
return tmp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
VCMRTPEncodeCompleteCallback::EncodeComplete()
|
|
|
|
|
{
|
|
|
|
|
if (_encodeComplete)
|
|
|
|
|
{
|
|
|
|
|
_encodeComplete = false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decoded Frame Callback Implmentation
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
|
|
|
|
VCMDecodeCompleteCallback::FrameToRender(VideoFrame& videoFrame)
|
|
|
|
|
{
|
|
|
|
|
fwrite(videoFrame.Buffer(), 1, videoFrame.Length(), _decodedFile);
|
|
|
|
|
_decodedBytes+= videoFrame.Length();
|
|
|
|
|
// keeping last decoded frame
|
|
|
|
|
_lastDecodedFrame.VerifyAndAllocate(videoFrame.Size());
|
|
|
|
|
_lastDecodedFrame.CopyFrame(videoFrame.Size(), videoFrame.Buffer());
|
|
|
|
|
_lastDecodedFrame.SetHeight(videoFrame.Height());
|
|
|
|
|
_lastDecodedFrame.SetWidth(videoFrame.Width());
|
|
|
|
|
_lastDecodedFrame.SetTimeStamp(videoFrame.TimeStamp());
|
|
|
|
|
|
|
|
|
|
return VCM_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
VCMDecodeCompleteCallback::PSNRLastFrame(const VideoFrame& sourceFrame, double *YPSNRptr)
|
|
|
|
|
{
|
|
|
|
|
double mse = 0.0;
|
|
|
|
|
double mseLogSum = 0.0;
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32 frameBytes = sourceFrame.Height() * sourceFrame.Width(); // only Y
|
|
|
|
|
WebRtc_UWord8 *ref = sourceFrame.Buffer();
|
|
|
|
|
if (_lastDecodedFrame.Height() == 0)
|
|
|
|
|
{
|
|
|
|
|
*YPSNRptr = 0;
|
|
|
|
|
return 0; // no new decoded frames
|
|
|
|
|
}
|
|
|
|
|
WebRtc_UWord8 *test = _lastDecodedFrame.Buffer();
|
|
|
|
|
for( int k = 0; k < frameBytes; k++ )
|
|
|
|
|
{
|
|
|
|
|
mse += (test[k] - ref[k]) * (test[k] - ref[k]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// divide by number of pixels
|
|
|
|
|
mse /= (double) (frameBytes);
|
|
|
|
|
|
|
|
|
|
// accumulate for total average
|
|
|
|
|
mseLogSum += std::log10( mse );
|
|
|
|
|
|
|
|
|
|
*YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum; // for only 1 frame
|
|
|
|
|
|
|
|
|
|
_lastDecodedFrame.Free();
|
|
|
|
|
_lastDecodedFrame.SetHeight(0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-03 07:49:56 +00:00
|
|
|
WebRtc_Word32
|
|
|
|
|
VCMDecodeCompleteCallback::DecodedBytes()
|
|
|
|
|
{
|
|
|
|
|
return _decodedBytes;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
|
2011-08-03 07:49:56 +00:00
|
|
|
RTPSendCompleteCallback::RTPSendCompleteCallback(RtpRtcp* rtp, const char* filename):
|
|
|
|
|
_sendCount(0),
|
|
|
|
|
_rtp(rtp),
|
|
|
|
|
_lossPct(0),
|
2011-08-11 22:51:58 +00:00
|
|
|
_burstLength(0),
|
|
|
|
|
_prevLossState(0),
|
2011-08-03 07:49:56 +00:00
|
|
|
_rtpDump(NULL)
|
|
|
|
|
{
|
|
|
|
|
if (filename != NULL)
|
|
|
|
|
{
|
|
|
|
|
_rtpDump = RtpDump::CreateRtpDump();
|
|
|
|
|
_rtpDump->Start(filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RTPSendCompleteCallback::~RTPSendCompleteCallback()
|
|
|
|
|
{
|
|
|
|
|
if (_rtpDump != NULL)
|
|
|
|
|
{
|
|
|
|
|
_rtpDump->Stop();
|
|
|
|
|
RtpDump::DestroyRtpDump(_rtpDump);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
int
|
|
|
|
|
RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len)
|
|
|
|
|
{
|
|
|
|
|
_sendCount++;
|
2011-08-11 22:51:58 +00:00
|
|
|
|
|
|
|
|
// Packet Loss
|
|
|
|
|
|
|
|
|
|
if (_burstLength <= 1.0)
|
2011-07-07 08:21:25 +00:00
|
|
|
{
|
2011-08-11 22:51:58 +00:00
|
|
|
// Random loss: if _burstLength parameter is not set, or <=1
|
|
|
|
|
if (PacketLoss(_lossPct))
|
|
|
|
|
{
|
|
|
|
|
// drop
|
|
|
|
|
//printf("\tDrop packet, sendCount = %d\n", _sendCount);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
2011-07-07 08:21:25 +00:00
|
|
|
}
|
2011-08-11 22:51:58 +00:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Simulate bursty channel (Gilbert model)
|
|
|
|
|
// (1st order) Markov chain model with memory of the previous/last
|
|
|
|
|
// packet state (loss or received)
|
|
|
|
|
|
|
|
|
|
// 0 = received state
|
|
|
|
|
// 1 = loss state
|
|
|
|
|
|
|
|
|
|
// probTrans10: if previous packet is lost, prob. to -> received state
|
|
|
|
|
// probTrans11: if previous packet is lost, prob. to -> loss state
|
|
|
|
|
|
|
|
|
|
// probTrans01: if previous packet is received, prob. to -> loss state
|
|
|
|
|
// probTrans00: if previous packet is received, prob. to -> received
|
|
|
|
|
|
|
|
|
|
// Map the two channel parameters (average loss rate and burst length)
|
|
|
|
|
// to the transition probabilities:
|
|
|
|
|
double probTrans10 = 100 * (1.0 / _burstLength);
|
|
|
|
|
double probTrans11 = (100.0 - probTrans10);
|
|
|
|
|
double probTrans01 = (probTrans10 * ( _lossPct / (100.0 - _lossPct) ) );
|
|
|
|
|
|
|
|
|
|
// Note: Random loss (Bernoulli) model is a special case where:
|
|
|
|
|
// burstLength = 100.0 / (100.0 - _lossPct) (i.e., p10 + p01 = 100)
|
|
|
|
|
|
|
|
|
|
if (_prevLossState == 0 )
|
|
|
|
|
{
|
|
|
|
|
// previous packet was received
|
|
|
|
|
if (PacketLoss(probTrans01) )
|
|
|
|
|
{
|
|
|
|
|
// drop, update previous state to loss
|
|
|
|
|
_prevLossState = 1;
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (_prevLossState == 1)
|
|
|
|
|
{
|
|
|
|
|
// previous packet was lost
|
|
|
|
|
if (PacketLoss(probTrans11) )
|
|
|
|
|
{
|
|
|
|
|
// drop, update previous state to loss
|
|
|
|
|
_prevLossState = 1;
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// no drop, update previous state to received
|
|
|
|
|
_prevLossState = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
if (_rtpDump != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (_rtpDump->DumpPacket((const WebRtc_UWord8*)data, len) != 0)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
RTPSendCompleteCallback::SendRTCPPacket(int channel, const void *data, int len)
|
|
|
|
|
{
|
|
|
|
|
if(_rtp->IncomingPacket((const WebRtc_UWord8*)data, len) == 0)
|
|
|
|
|
{
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
RTPSendCompleteCallback::SetLossPct(double lossPct)
|
|
|
|
|
{
|
|
|
|
|
_lossPct = lossPct;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-11 22:51:58 +00:00
|
|
|
void
|
|
|
|
|
RTPSendCompleteCallback::SetBurstLength(double burstLength)
|
|
|
|
|
{
|
|
|
|
|
_burstLength = burstLength;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
bool
|
|
|
|
|
RTPSendCompleteCallback::PacketLoss(double lossPct)
|
|
|
|
|
{
|
|
|
|
|
double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0);
|
|
|
|
|
return randVal < lossPct/100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
|
|
|
|
PacketRequester::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length)
|
|
|
|
|
{
|
|
|
|
|
return _rtp.SendNACK(sequenceNumbers, length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
|
|
|
|
PSNRfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *YPSNRptr)
|
|
|
|
|
{
|
|
|
|
|
FILE *refFp = fopen(refFileName, "rb");
|
|
|
|
|
if( refFp == NULL ) {
|
|
|
|
|
// cannot open reference file
|
|
|
|
|
fprintf(stderr, "Cannot open file %s\n", refFileName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE *testFp = fopen(testFileName, "rb");
|
|
|
|
|
if( testFp == NULL ) {
|
|
|
|
|
// cannot open test file
|
|
|
|
|
fprintf(stderr, "Cannot open file %s\n", testFileName);
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
double mse = 0.0;
|
|
|
|
|
double mseLogSum = 0.0;
|
|
|
|
|
WebRtc_Word32 frames = 0;
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32 frameBytes = 3*width*height/2; // bytes in one frame I420
|
|
|
|
|
WebRtc_UWord8 *ref = new WebRtc_UWord8[frameBytes]; // space for one frame I420
|
|
|
|
|
WebRtc_UWord8 *test = new WebRtc_UWord8[frameBytes]; // space for one frame I420
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32 refBytes = (WebRtc_Word32) fread(ref, 1, frameBytes, refFp);
|
|
|
|
|
WebRtc_Word32 testBytes = (WebRtc_Word32) fread(test, 1, frameBytes, testFp);
|
|
|
|
|
|
|
|
|
|
while( refBytes == frameBytes && testBytes == frameBytes )
|
|
|
|
|
{
|
|
|
|
|
mse = 0.0;
|
|
|
|
|
|
|
|
|
|
int sh = 8;//boundary offset
|
|
|
|
|
for( int k2 = sh; k2 < height-sh;k2++)
|
|
|
|
|
for( int k = sh; k < width-sh;k++)
|
|
|
|
|
{
|
|
|
|
|
int kk = k2*width + k;
|
|
|
|
|
mse += (test[kk] - ref[kk]) * (test[kk] - ref[kk]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// divide by number of pixels
|
|
|
|
|
mse /= (double) (width * height);
|
|
|
|
|
|
|
|
|
|
// accumulate for total average
|
|
|
|
|
mseLogSum += std::log10( mse );
|
|
|
|
|
frames++;
|
|
|
|
|
|
|
|
|
|
refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
|
|
|
|
testBytes = (int) fread(test, 1, frameBytes, testFp);
|
|
|
|
|
}
|
|
|
|
|
// for identical reproduction:
|
|
|
|
|
if (mse == 0)
|
|
|
|
|
{
|
|
|
|
|
*YPSNRptr = 48;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete [] ref;
|
|
|
|
|
delete [] test;
|
|
|
|
|
|
|
|
|
|
fclose(refFp);
|
|
|
|
|
fclose(testFp);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-19 18:16:59 +00:00
|
|
|
static double
|
|
|
|
|
similarity
|
|
|
|
|
(
|
|
|
|
|
unsigned long sum_s,
|
|
|
|
|
unsigned long sum_r,
|
|
|
|
|
unsigned long sum_sq_s,
|
|
|
|
|
unsigned long sum_sq_r,
|
|
|
|
|
unsigned long sum_sxr,
|
|
|
|
|
int count
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
int64_t ssim_n, ssim_d;
|
|
|
|
|
int64_t c1, c2;
|
|
|
|
|
const int64_t cc1 = 26634; // (64^2*(.01*255)^2
|
|
|
|
|
const int64_t cc2 = 239708; // (64^2*(.03*255)^2
|
|
|
|
|
|
|
|
|
|
//scale the constants by number of pixels
|
|
|
|
|
c1 = (cc1*count*count)>>12;
|
|
|
|
|
c2 = (cc2*count*count)>>12;
|
|
|
|
|
|
|
|
|
|
ssim_n = (2*sum_s*sum_r+ c1)*((int64_t) 2*count*sum_sxr-
|
|
|
|
|
(int64_t) 2*sum_s*sum_r+c2);
|
|
|
|
|
|
|
|
|
|
ssim_d = (sum_s*sum_s +sum_r*sum_r+c1)*
|
|
|
|
|
((int64_t)count*sum_sq_s-(int64_t)sum_s*sum_s +
|
|
|
|
|
(int64_t)count*sum_sq_r-(int64_t) sum_r*sum_r +c2) ;
|
|
|
|
|
|
|
|
|
|
return ssim_n * 1.0 / ssim_d;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double
|
2011-08-19 22:33:08 +00:00
|
|
|
ssim_8x8_c
|
|
|
|
|
(
|
|
|
|
|
unsigned char *s,
|
|
|
|
|
int sp,
|
|
|
|
|
unsigned char *r,
|
|
|
|
|
int rp
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
unsigned long sum_s = 0;
|
|
|
|
|
unsigned long sum_r = 0;
|
|
|
|
|
unsigned long sum_sq_s = 0;
|
|
|
|
|
unsigned long sum_sq_r = 0;
|
|
|
|
|
unsigned long sum_sxr = 0;
|
|
|
|
|
|
|
|
|
|
int i,j;
|
|
|
|
|
for(i=0;i<8;i++,s+=sp,r+=rp)
|
|
|
|
|
{
|
|
|
|
|
for(j=0;j<8;j++)
|
|
|
|
|
{
|
|
|
|
|
sum_s += s[j];
|
|
|
|
|
sum_r += r[j];
|
|
|
|
|
sum_sq_s += s[j] * s[j];
|
|
|
|
|
sum_sq_r += r[j] * r[j];
|
|
|
|
|
sum_sxr += s[j] * r[j];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(WEBRTC_USE_SSE2)
|
|
|
|
|
#include <xmmintrin.h>
|
|
|
|
|
static double
|
|
|
|
|
ssim_8x8_sse2
|
|
|
|
|
(
|
|
|
|
|
unsigned char *s,
|
|
|
|
|
int sp,
|
|
|
|
|
unsigned char *r,
|
|
|
|
|
int rp
|
|
|
|
|
)
|
2011-08-19 18:16:59 +00:00
|
|
|
{
|
2011-08-19 22:33:08 +00:00
|
|
|
int i;
|
|
|
|
|
const __m128i z = _mm_setzero_si128();
|
|
|
|
|
__m128i sum_s_16 = _mm_setzero_si128();
|
|
|
|
|
__m128i sum_r_16 = _mm_setzero_si128();
|
|
|
|
|
__m128i sum_sq_s_32 = _mm_setzero_si128();
|
|
|
|
|
__m128i sum_sq_r_32 = _mm_setzero_si128();
|
|
|
|
|
__m128i sum_sxr_32 = _mm_setzero_si128();
|
|
|
|
|
|
|
|
|
|
for(i=0;i<8;i++,s+=sp,r+=rp)
|
|
|
|
|
{
|
|
|
|
|
const __m128i s_8 = _mm_loadl_epi64((__m128i*)(s));
|
|
|
|
|
const __m128i r_8 = _mm_loadl_epi64((__m128i*)(r));
|
|
|
|
|
|
|
|
|
|
const __m128i s_16 = _mm_unpacklo_epi8(s_8,z);
|
|
|
|
|
const __m128i r_16 = _mm_unpacklo_epi8(r_8,z);
|
|
|
|
|
|
|
|
|
|
sum_s_16 = _mm_adds_epu16(sum_s_16, s_16);
|
|
|
|
|
sum_r_16 = _mm_adds_epu16(sum_r_16, r_16);
|
|
|
|
|
const __m128i sq_s_32 = _mm_madd_epi16(s_16, s_16);
|
|
|
|
|
sum_sq_s_32 = _mm_add_epi32(sum_sq_s_32, sq_s_32);
|
|
|
|
|
const __m128i sq_r_32 = _mm_madd_epi16(r_16, r_16);
|
|
|
|
|
sum_sq_r_32 = _mm_add_epi32(sum_sq_r_32, sq_r_32);
|
|
|
|
|
const __m128i sxr_32 = _mm_madd_epi16(s_16, r_16);
|
|
|
|
|
sum_sxr_32 = _mm_add_epi32(sum_sxr_32, sxr_32);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const __m128i sum_s_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_s_16, z),
|
|
|
|
|
_mm_unpacklo_epi16(sum_s_16, z));
|
|
|
|
|
const __m128i sum_r_32 = _mm_add_epi32(_mm_unpackhi_epi16(sum_r_16, z),
|
|
|
|
|
_mm_unpacklo_epi16(sum_r_16, z));
|
|
|
|
|
|
|
|
|
|
unsigned long sum_s_64[2];
|
|
|
|
|
unsigned long sum_r_64[2];
|
|
|
|
|
unsigned long sum_sq_s_64[2];
|
|
|
|
|
unsigned long sum_sq_r_64[2];
|
|
|
|
|
unsigned long sum_sxr_64[2];
|
|
|
|
|
|
|
|
|
|
_mm_store_si128 ((__m128i*)sum_s_64,
|
|
|
|
|
_mm_add_epi64(_mm_unpackhi_epi32(sum_s_32, z),
|
|
|
|
|
_mm_unpacklo_epi32(sum_s_32, z)));
|
|
|
|
|
_mm_store_si128 ((__m128i*)sum_r_64,
|
|
|
|
|
_mm_add_epi64(_mm_unpackhi_epi32(sum_r_32, z),
|
|
|
|
|
_mm_unpacklo_epi32(sum_r_32, z)));
|
|
|
|
|
_mm_store_si128 ((__m128i*)sum_sq_s_64,
|
|
|
|
|
_mm_add_epi64(_mm_unpackhi_epi32(sum_sq_s_32, z),
|
|
|
|
|
_mm_unpacklo_epi32(sum_sq_s_32, z)));
|
|
|
|
|
_mm_store_si128 ((__m128i*)sum_sq_r_64,
|
|
|
|
|
_mm_add_epi64(_mm_unpackhi_epi32(sum_sq_r_32, z),
|
|
|
|
|
_mm_unpacklo_epi32(sum_sq_r_32, z)));
|
|
|
|
|
_mm_store_si128 ((__m128i*)sum_sxr_64,
|
|
|
|
|
_mm_add_epi64(_mm_unpackhi_epi32(sum_sxr_32, z),
|
|
|
|
|
_mm_unpacklo_epi32(sum_sxr_32, z)));
|
|
|
|
|
|
|
|
|
|
const unsigned long sum_s = sum_s_64[0] + sum_s_64[1];
|
|
|
|
|
const unsigned long sum_r = sum_r_64[0] + sum_r_64[1];
|
|
|
|
|
const unsigned long sum_sq_s = sum_sq_s_64[0] + sum_sq_s_64[1];
|
|
|
|
|
const unsigned long sum_sq_r = sum_sq_r_64[0] + sum_sq_r_64[1];
|
|
|
|
|
const unsigned long sum_sxr = sum_sxr_64[0] + sum_sxr_64[1];
|
|
|
|
|
|
2011-08-19 18:16:59 +00:00
|
|
|
return similarity(sum_s, sum_r, sum_sq_s, sum_sq_r, sum_sxr, 64);
|
|
|
|
|
}
|
2011-08-19 22:33:08 +00:00
|
|
|
#endif
|
2011-08-19 18:16:59 +00:00
|
|
|
|
|
|
|
|
double
|
|
|
|
|
SSIM_frame
|
|
|
|
|
(
|
|
|
|
|
unsigned char *img1,
|
|
|
|
|
unsigned char *img2,
|
|
|
|
|
int stride_img1,
|
|
|
|
|
int stride_img2,
|
|
|
|
|
int width,
|
|
|
|
|
int height)
|
|
|
|
|
{
|
2011-08-19 22:33:08 +00:00
|
|
|
int i,j;
|
|
|
|
|
unsigned int samples = 0;
|
2011-08-19 18:16:59 +00:00
|
|
|
double ssim_total = 0;
|
2011-08-19 22:33:08 +00:00
|
|
|
double (*ssim_8x8)(unsigned char*, int, unsigned char*, int rp);
|
|
|
|
|
|
|
|
|
|
ssim_8x8 = ssim_8x8_c;
|
|
|
|
|
if(WebRtc_GetCPUInfo(kSSE2))
|
|
|
|
|
{
|
|
|
|
|
#if defined(WEBRTC_USE_SSE2)
|
|
|
|
|
ssim_8x8 = ssim_8x8_sse2;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2011-08-19 18:16:59 +00:00
|
|
|
|
|
|
|
|
// sample point start with each 4x4 location
|
|
|
|
|
for(i=0; i < height-8; i+=4, img1 += stride_img1*4, img2 += stride_img2*4)
|
|
|
|
|
{
|
|
|
|
|
for(j=0; j < width-8; j+=4 )
|
|
|
|
|
{
|
|
|
|
|
double v = ssim_8x8(img1+j, stride_img1, img2+j, stride_img2);
|
|
|
|
|
ssim_total += v;
|
|
|
|
|
samples++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ssim_total /= samples;
|
|
|
|
|
return ssim_total;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-07 08:21:25 +00:00
|
|
|
WebRtc_Word32
|
|
|
|
|
SSIMfromFiles(const WebRtc_Word8 *refFileName, const WebRtc_Word8 *testFileName, WebRtc_Word32 width, WebRtc_Word32 height, double *SSIMptr)
|
|
|
|
|
{
|
|
|
|
|
FILE *refFp = fopen(refFileName, "rb");
|
|
|
|
|
if( refFp == NULL ) {
|
|
|
|
|
// cannot open reference file
|
|
|
|
|
fprintf(stderr, "Cannot open file %s\n", refFileName);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FILE *testFp = fopen(testFileName, "rb");
|
|
|
|
|
if( testFp == NULL ) {
|
|
|
|
|
// cannot open test file
|
|
|
|
|
fprintf(stderr, "Cannot open file %s\n", testFileName);
|
|
|
|
|
return -2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int frames = 0;
|
|
|
|
|
|
2011-08-19 18:16:59 +00:00
|
|
|
const int frameBytes = 3*width*height/2; // bytes in one frame I420
|
|
|
|
|
unsigned char *ref = new unsigned char[frameBytes];
|
|
|
|
|
unsigned char *test = new unsigned char[frameBytes];
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
int refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
|
|
|
|
int testBytes = (int) fread(test, 1, frameBytes, testFp);
|
|
|
|
|
|
2011-08-19 18:16:59 +00:00
|
|
|
double ssimScene = 0.0; //avgerage SSIM for sequence
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
while( refBytes == frameBytes && testBytes == frameBytes )
|
|
|
|
|
{
|
2011-08-19 18:16:59 +00:00
|
|
|
ssimScene += SSIM_frame(ref, test, width, width, width, height);
|
2011-07-07 08:21:25 +00:00
|
|
|
|
|
|
|
|
frames++;
|
|
|
|
|
|
|
|
|
|
refBytes = (int) fread(ref, 1, frameBytes, refFp);
|
|
|
|
|
testBytes = (int) fread(test, 1, frameBytes, testFp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//SSIM: normalize/average for sequence
|
|
|
|
|
ssimScene = ssimScene / frames;
|
|
|
|
|
*SSIMptr = ssimScene;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
delete [] ref;
|
|
|
|
|
delete [] test;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fclose(refFp);
|
|
|
|
|
fclose(testFp);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RTPVideoCodecTypes
|
|
|
|
|
ConvertCodecType(const char* plname)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(plname,"VP8" , 3) == 0)
|
|
|
|
|
{
|
|
|
|
|
return kRTPVideoVP8;
|
|
|
|
|
}else if (strncmp(plname,"H263" , 5) == 0)
|
|
|
|
|
{
|
|
|
|
|
return kRTPVideoH263;
|
|
|
|
|
}else if (strncmp(plname, "H263-1998",10) == 0)
|
|
|
|
|
{
|
|
|
|
|
return kRTPVideoH263;
|
|
|
|
|
}else if (strncmp(plname,"I420" , 5) == 0)
|
|
|
|
|
{
|
|
|
|
|
return kRTPVideoI420;
|
|
|
|
|
}else
|
|
|
|
|
{
|
|
|
|
|
return kRTPVideoNoVideo; // defualt value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
|
|
|
|
SendStatsTest::SendStatistics(const WebRtc_UWord32 bitRate, const WebRtc_UWord32 frameRate)
|
|
|
|
|
{
|
|
|
|
|
TEST(frameRate <= _frameRate);
|
|
|
|
|
TEST(bitRate > 0 && bitRate < 100000);
|
|
|
|
|
printf("VCM 1 sec: Bit rate: %u\tFrame rate: %u\n", bitRate, frameRate);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WebRtc_Word32
|
|
|
|
|
KeyFrameReqTest::FrameTypeRequest(const FrameType frameType)
|
|
|
|
|
{
|
|
|
|
|
TEST(frameType == kVideoFrameKey);
|
|
|
|
|
if (frameType == kVideoFrameKey)
|
|
|
|
|
{
|
|
|
|
|
printf("Key frame requested\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
printf("Non-key frame requested: %d\n", frameType);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|