/* * 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. */ #include "test.h" #include "video_source.h" #include "vplib.h" #include "event_wrapper.h" #include "thread_wrapper.h" #include #include #include #include #include #include #include using namespace webrtc; long filesize(const char *filename); // local function defined at end of file struct SSIMcontext { SSIMcontext() : refFileName(NULL), testFileName(NULL), width(0), height(0), SSIMptr(NULL), startFrame(-1), endFrame(-1), evnt(NULL) {}; SSIMcontext(const char *ref, const char *test, int w, int h, double *Sptr, int start, int end, EventWrapper* ev) : refFileName(ref), testFileName(test), width(w), height(h), SSIMptr(Sptr), startFrame(start), endFrame(end), evnt(ev) {}; const char *refFileName; const char *testFileName; int width; int height; double *SSIMptr; int startFrame; int endFrame; EventWrapper* evnt; }; Test::Test(std::string name, std::string description) : _name(name), _description(description), _bitRate(0), _inname(""), _outname(""), _encodedName("") { memset(&_inst, 0, sizeof(_inst)); unsigned int seed = static_cast(0); std::srand(seed); } Test::Test(std::string name, std::string description, WebRtc_UWord32 bitRate) : _name(name), _description(description), _bitRate(bitRate), _inname(""), _outname(""), _encodedName("") { memset(&_inst, 0, sizeof(_inst)); unsigned int seed = static_cast(0); std::srand(seed); } void Test::Print() { std::cout << _name << " completed!" << std::endl; (*_log) << _name << std::endl; (*_log) << _description << std::endl; (*_log) << "Input file: " << _inname << std::endl; (*_log) << "Output file: " << _outname << std::endl; double psnr = -1.0, ssim = -1.0; PSNRfromFiles(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &psnr); ssim = SSIMfromFilesMT(4 /* number of threads*/); (*_log) << "PSNR: " << psnr << std::endl; std::cout << "PSNR: " << psnr << std::endl << std::endl; (*_log) << "SSIM: " << ssim << std::endl; std::cout << "SSIM: " << ssim << std::endl << std::endl; (*_log) << std::endl; } void Test::Setup() { int widhei = _inst.width*_inst.height; _lengthSourceFrame = 3*widhei/2; _sourceBuffer = new unsigned char[_lengthSourceFrame]; } void Test::CodecSettings(int width, int height, WebRtc_UWord32 frameRate /*=30*/, WebRtc_UWord32 bitRate /*=0*/) { if (bitRate > 0) { _bitRate = bitRate; } else if (_bitRate == 0) { _bitRate = 600; } _inst.maxFramerate = (unsigned char)frameRate; _inst.startBitrate = (int)_bitRate; _inst.maxBitrate = 8000; _inst.width = width; _inst.height = height; } void Test::Teardown() { delete [] _sourceBuffer; } void Test::SetEncoder(webrtc::VideoEncoder*encoder) { _encoder = encoder; } void Test::SetDecoder(VideoDecoder*decoder) { _decoder = decoder; } void Test::SetLog(std::fstream* log) { _log = log; } int Test::PSNRfromFiles(const char *refFileName, const char *testFileName, int width, int 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; int frames = 0; int frameBytes = 3*width*height/2; // bytes in one frame I420 unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420 unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420 int refBytes = (int) fread(ref, 1, frameBytes, refFp); int testBytes = (int) fread(test, 1, frameBytes, testFp); while( refBytes == frameBytes && testBytes == frameBytes ) { mse = 0.0; // calculate Y sum-square-difference for( int k = 0; k < width * height; k++ ) { mse += (test[k] - ref[k]) * (test[k] - ref[k]); } // 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); } // ypsnrAvg = sum( 10 log (255^2 / MSE) ) / frames // = 20 * log(255) - 10 * mseLogSum / frames *YPSNRptr = 20.0 * std::log10(255.0) - 10.0 * mseLogSum / frames; delete [] ref; delete [] test; fclose(refFp); fclose(testFp); return 0; } int Test::SSIMfromFiles(const char *refFileName, const char *testFileName, int width, int height, double *SSIMptr, int startFrame /*= -1*/, int endFrame /*= -1*/) { 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; int frameBytes = 3*width*height/2; // bytes in one frame I420 unsigned char *ref = new unsigned char[frameBytes]; // space for one frame I420 unsigned char *test = new unsigned char[frameBytes]; // space for one frame I420 if (startFrame >= 0) { if (fseek(refFp, frameBytes * startFrame, SEEK_SET) != 0){ fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, refFileName); return -1; } if (fseek(testFp, frameBytes * startFrame, SEEK_SET) != 0){ fprintf(stderr, "Cannot go to frame %i in %s\n", startFrame, testFileName); return -1; } } int refBytes = (int) fread(ref, 1, frameBytes, refFp); int testBytes = (int) fread(test, 1, frameBytes, testFp); // // SSIM: variable definition, window function, initialization int window = 10; int flag_window = 0; //0 for uniform window filter, 1 for gaussian symmetric window float variance_window = 2.0; //variance for window function float ssimFilter[121]; //2d window filter: typically 11x11 = (window+1)*(window+1) //statistics per column of window (#columns = window+1), 0 element for avg over all columns float avgTest[12]; float avgRef[12]; float contrastTest[12]; float contrastRef[12]; float crossCorr[12]; // //offsets for stability float offset1 = 0.1f; float offset2 = 0.1f; float offset3 = offset2/2; // //define window for SSIM: take uniform filter for now float sumfil = 0.0; int nn=-1; for(int j=-window/2;j<=window/2;j++) for(int i=-window/2;i<=window/2;i++) { nn+=1; if (flag_window == 0) ssimFilter[nn] = 1.0; else { float dist = (float)(i*i) + (float)(j*j); float tmp = 0.5f*dist/variance_window; ssimFilter[nn] = exp(-tmp); } sumfil +=ssimFilter[nn]; } //normalize window nn=-1; for(int j=-window/2;j<=window/2;j++) for(int i=-window/2;i<=window/2;i++) { nn+=1; ssimFilter[nn] = ssimFilter[nn]/((float)sumfil); } // float ssimScene = 0.0; //avgerage SSIM for sequence // //SSIM: done with variables and defintion // while( refBytes == frameBytes && testBytes == frameBytes && !(endFrame >= 0 && frames > endFrame - startFrame)) { float ssimFrame = 0.0; int sh = window/2+1; int numPixels = 0; for(int i=sh;irefFileName, ctx->testFileName, ctx->width, ctx->height, ctx->SSIMptr, ctx->startFrame, ctx->endFrame); ctx->evnt->Set(); return false; } double Test::SSIMfromFilesMT(const int numThreads) { int numFrames = filesize(_inname.c_str()) / _lengthSourceFrame; std::vector nFramesVec(numThreads); std::vector ssimVec(numThreads); int framesPerCore = (numFrames + numThreads - 1) / numThreads; // rounding up int i = 0; int nFrames; for (nFrames = numFrames; nFrames >= framesPerCore; nFrames -= framesPerCore) { nFramesVec[i++] = framesPerCore; } if (nFrames > 0) { assert(i == numThreads - 1); nFramesVec[i] = nFrames; // remainder } int frameIx = 0; std::vector eventVec(numThreads); std::vector threadVec(numThreads); std::vector ctxVec(numThreads); for (i = 0; i < numThreads; i++) { eventVec[i] = EventWrapper::Create(); ctxVec[i] = SSIMcontext(_inname.c_str(), _outname.c_str(), _inst.width, _inst.height, &ssimVec[i], frameIx, frameIx + nFramesVec[i] - 1, eventVec[i]); threadVec[i] = ThreadWrapper::CreateThread(SSIMthread, &(ctxVec[i]), kLowPriority); unsigned int id; threadVec[i]->Start(id); frameIx += nFramesVec[i]; } // wait for all events for (i = 0; i < numThreads; i++) { eventVec[i]->Wait(100000 /* ms*/); threadVec[i]->Stop(); delete threadVec[i]; delete eventVec[i]; } double avgSsim = 0; for (i = 0; i < numThreads; i++) { avgSsim += (ssimVec[i] * nFramesVec[i]); } avgSsim /= numFrames; return avgSsim; } double Test::ActualBitRate(int nFrames) { return 8.0 * _sumEncBytes / (nFrames / _inst.maxFramerate); } bool Test::PacketLoss(double lossRate) { return RandUniform() < lossRate; } void Test::VideoBufferToRawImage(TestVideoBuffer& videoBuffer, RawImage &image) { image._buffer = videoBuffer.GetBuffer(); image._size = videoBuffer.GetSize(); image._length = videoBuffer.GetLength(); image._width = videoBuffer.GetWidth(); image._height = videoBuffer.GetHeight(); image._timeStamp = videoBuffer.GetTimeStamp(); } void Test::VideoEncodedBufferToEncodedImage(TestVideoEncodedBuffer& videoBuffer, EncodedImage &image) { image._buffer = videoBuffer.GetBuffer(); image._length = videoBuffer.GetLength(); image._size = videoBuffer.GetSize(); image._frameType = static_cast(videoBuffer.GetFrameType()); image._timeStamp = videoBuffer.GetTimeStamp(); image._encodedWidth = videoBuffer.GetCaptureWidth(); image._encodedHeight = videoBuffer.GetCaptureHeight(); image._completeFrame = true; } long filesize(const char *filename) { FILE *f = fopen(filename,"rb"); /* open the file in read only */ long size = 0; if (fseek(f,0,SEEK_END)==0) /* seek was successful */ size = ftell(f); fclose(f); return size; }