--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+ <storageModule moduleId="org.eclipse.cdt.core.settings">
+ <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1038513072">
+ <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1038513072" moduleId="org.eclipse.cdt.core.settings" name="Default">
+ <externalSettings/>
+ <extensions>
+ <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+ <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+ </extensions>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <configuration buildProperties="" id="cdt.managedbuild.toolchain.gnu.base.1038513072" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+ <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1038513072.71015752" name="/" resourcePath="">
+ <toolChain id="cdt.managedbuild.toolchain.gnu.base.1873147613" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
+ <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.592077177" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+ <builder id="cdt.managedbuild.target.gnu.builder.base.1210866582" managedBuildOn="false" name="Gnu Make Builder.Default" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.archiver.base.1523043784" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1661515438" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1380832673" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.c.linker.base.2027547661" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1137861790" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
+ <tool id="cdt.managedbuild.tool.gnu.assembler.base.1410144416" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"/>
+ </toolChain>
+ </folderInfo>
+ </configuration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ </storageModule>
+ <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+ <project id="QPSNR.null.1329330942" name="QPSNR"/>
+ </storageModule>
+ <storageModule moduleId="scannerConfiguration">
+ <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+</cproject>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>QPSNR</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+ <triggers>full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.core.ccnature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+ <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+#Makefile generated by amake
+#On Wed Feb 17 10:48:47 2010
+#To print amake help use 'amake --help'.
+CC=gcc
+CPPC=g++
+LINK=g++
+SRCDIR=src
+OBJDIR=obj
+FLAGS=-O2 -g -pthread -D__STDC_CONSTANT_MACROS
+LIBS=-lavcodec -lavformat -lswscale
+OBJS=$(OBJDIR)/qav.o $(OBJDIR)/stats.o $(OBJDIR)/main.o $(OBJDIR)/settings.o
+EXEC=qpsnr
+
+$(EXEC) : $(OBJS)
+ $(LINK) $(OBJS) -o $(EXEC) $(FLAGS) $(LIBS)
+
+$(OBJDIR)/qav.o: src/qav.cpp src/qav.h src/settings.h $(OBJDIR)/__setup_obj_dir
+ $(CPPC) $(FLAGS) src/qav.cpp -c -o $@
+
+$(OBJDIR)/stats.o: src/stats.cpp src/stats.h src/mt.h src/shared_ptr.h \
+ src/settings.h $(OBJDIR)/__setup_obj_dir
+ $(CPPC) $(FLAGS) src/stats.cpp -c -o $@
+
+$(OBJDIR)/main.o: src/main.cpp src/mt.h src/shared_ptr.h src/qav.h src/settings.h \
+ src/stats.h $(OBJDIR)/__setup_obj_dir
+ $(CPPC) $(FLAGS) src/main.cpp -c -o $@
+
+$(OBJDIR)/settings.o: src/settings.cpp src/settings.h $(OBJDIR)/__setup_obj_dir
+ $(CPPC) $(FLAGS) src/settings.cpp -c -o $@
+
+$(OBJDIR)/__setup_obj_dir :
+ mkdir -p $(OBJDIR)
+ touch $(OBJDIR)/__setup_obj_dir
+
+.PHONY: clean bzip
+
+clean :
+ rm -rf $(OBJDIR)/*.o
+ rm -rf $(EXEC)
+
+bzip :
+ tar -cvf $(EXEC).tar $(SRCDIR)/* Makefile
+ bzip2 $(EXEC).tar
+
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iostream>
+#include <stdexcept>
+#include <vector>
+#include <memory>
+#include <sstream>
+#include <unistd.h>
+#include <getopt.h>
+#include <map>
+#include "mt.h"
+#include "shared_ptr.h"
+#include "qav.h"
+#include "settings.h"
+#include "stats.h"
+
+template<typename T>
+std::string XtoS(const T& in) {
+ std::ostringstream oss;
+ oss << in;
+ return in.str();
+}
+
+std::string get_filename(const std::string& in) {
+ const char *pslash = strrchr(in.c_str(), '/');
+ if (pslash) return std::string(pslash+1);
+ return in;
+}
+
+class video_producer : public mt::Thread {
+ int &_frame;
+ mt::Semaphore &_s_prod,
+ &_s_cons;
+ std::vector<unsigned char> &_buf;
+ qav::qvideo &_video;
+ bool &_exit;
+public:
+ video_producer(int& frame, mt::Semaphore& s_prod, mt::Semaphore& s_cons, std::vector<unsigned char>& buf, qav::qvideo& video, bool& __exit) :
+ _frame(frame), _s_prod(s_prod), _s_cons(s_cons), _buf(buf), _video(video), _exit(__exit) {
+ }
+
+ virtual void run(void) {
+ while(!_exit) {
+ if (!_video.get_frame(_buf, &_frame)) _frame = -1;
+ // signal cons we're done
+ _s_cons.pop();
+ // wait for cons to tell us to go
+ _s_prod.push();
+ }
+ }
+};
+
+typedef std::vector<unsigned char> VUCHAR;
+typedef shared_ptr<qav::qvideo> SP_QVIDEO;
+struct vp_data {
+ mt::Semaphore prod;
+ VUCHAR buf;
+ int frame;
+ SP_QVIDEO video;
+ std::string name;
+};
+typedef std::vector<shared_ptr<vp_data> > V_VPDATA;
+typedef std::vector<shared_ptr<video_producer> > V_VPTH;
+
+const std::string __qpsnr__ = "qpsnr",
+ __version__ = "0.2.1";
+
+void print_help(void) {
+ std::cerr << __qpsnr__ << " v" << __version__ << " - (C) 2010 E. Oriani\n"
+ "Usage: " << __qpsnr__ << " [options] -r ref.video compare.video1 compare.video2 ...\n\n"
+ "-r,--reference:\n\tset reference video (mandatory)\n"
+ "\n-v,--video-size:\n\tset analysis video size WIDTHxHEIGHT (ie. 1280x720), default is reference video size\n"
+ "\n-s,--skip-frames:\n\tskip n initial frames\n"
+ "\n-m,--max-frames:\n\tset max frames to process before quit\n"
+ "\n-I,--save-frames:\n\tsave frames (ppm format)\n"
+ "\n-G,--ignore-fps:\n\tanalyze videos even if the expected fps are different\n"
+ "\n-a,--analyzer:\n"
+ "\tpsnr : execute the psnr for each frame\n"
+ "\tavg_psnr : take the average of the psnr every n frames (use option \"fpa\" to set it)\n"
+ "\tssim : execute the ssim (Y colorspace) on the frames divided in blocks (use option \"blocksize\" to set the size)\n"
+ "\tavg_ssim : take the average of the ssim (Y colorspace) every n frames (use option \"fpa\" to set it)\n"
+ "\n-o,--aopts: (specify option1=value1:option2=value2:...)\n"
+ "\tfpa : set the frames per average, default 25\n"
+ "\tcolorspace : set the colorspace (\"rgb\", \"hsi\", \"ycbcr\" or \"y\"), default \"rgb\"\n"
+ "\tblocksize : set blocksize for ssim analysis, default 8\n"
+ "\n-l,--log-level:\n"
+ "\t0 : No log\n"
+ "\t1 : Errors only\n"
+ "\t2 : Warnings and errors\n"
+ "\t3 : Info, warnings and errors (default)\n"
+ "\t4 : Debug, info, warnings and errors\n"
+ "\n-h,--help:\n\tprint this help and exit\n"
+ << std::flush;
+}
+
+int parse_options(int argc, char *argv[], std::map<std::string, std::string>& aopt) {
+ aopt.clear();
+ opterr = 0;
+ int c = 0,
+ option_index = 0;
+
+ static struct option long_options[] =
+ {
+ {"analyzer", required_argument, 0, 'a'},
+ {"max-frames", required_argument, 0, 'm'},
+ {"skip-frames", required_argument, 0, 's'},
+ {"reference", required_argument, 0, 'r'},
+ {"log-level", required_argument, 0, 'l'},
+ {"save-frames", no_argument, 0, 'I'},
+ {"video-size", required_argument, 0, 'v'},
+ {"ignore-fps", no_argument, 0, 'G'},
+ {"help", no_argument, 0, 'h'},
+ {"aopts", required_argument, 0, 'o'},
+ {0, 0, 0, 0}
+ };
+
+ while ((c = getopt_long (argc, argv, "a:l:m:o:r:s:v:hIG", long_options, &option_index)) != -1) {
+ switch (c) {
+ case 'a':
+ settings::ANALYZER = optarg;
+ break;
+ case 'v':
+ {
+ const char *p_x = strchr(optarg, 'x');
+ if (!p_x) p_x = strchr(optarg, 'X');
+ if (!p_x) throw std::runtime_error("Invalid video size specified (use WIDTHxHEIGHT format, ie. 1280x720)");
+ const std::string s_x(optarg, p_x-optarg),
+ s_y(p_x+1);
+ if (s_x.empty() || s_y.empty())
+ throw std::runtime_error("Invalid video size specified (use WIDTHxHEIGHT format, ie. 1280x720)");
+ const int i_x = atoi(s_x.c_str()),
+ i_y = atoi(s_y.c_str());
+ if (i_x <=0 || i_y <=0)
+ throw std::runtime_error("Invalid video size specified, negative or 0 width/height");
+ settings::VIDEO_SIZE_W = i_x;
+ settings::VIDEO_SIZE_H = i_y;
+ }
+ break;
+ case 's':
+ {
+ const int skip_frames = atoi(optarg);
+ if (skip_frames > 0 ) settings::SKIP_FRAMES = skip_frames;
+ }
+ break;
+ case 'r':
+ settings::REF_VIDEO = optarg;
+ break;
+ case 'm':
+ {
+ const int max_frames = atoi(optarg);
+ if (max_frames > 0 ) settings::MAX_FRAMES = max_frames;
+ }
+ break;
+ case 'l':
+ if (isdigit(optarg[0])) {
+ char log_level[2];
+ log_level[0] = optarg[0];
+ log_level[1] = '\0';
+ const int log_ilev = atoi(optarg);
+ switch (log_ilev) {
+ case 0:
+ settings::LOG = 0x00;
+ break;
+ case 1:
+ settings::LOG = 0x01;
+ break;
+ case 2:
+ settings::LOG = 0x03;
+ break;
+ case 3:
+ settings::LOG = 0x07;
+ break;
+ case 4:
+ settings::LOG = 0x0F;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case 'h':
+ print_help();
+ exit(0);
+ break;
+ case 'I':
+ settings::SAVE_IMAGES = true;
+ break;
+ case 'G':
+ settings::IGNORE_FPS = true;
+ break;
+ case 'o':
+ {
+ const char *p_opts = optarg,
+ *p_colon = 0;
+ while(p_colon = strchr(p_opts, ':')) {
+ const std::string c_opt(p_opts, (p_colon-p_opts));
+ const size_t p_equal = c_opt.find('=');
+ if (std::string::npos != p_equal)
+ aopt[c_opt.substr(0, p_equal)] = c_opt.substr(p_equal+1);
+ p_opts = p_colon+1;
+ }
+ const std::string c_opt(p_opts);
+ const size_t p_equal = c_opt.find('=');
+ if (std::string::npos != p_equal)
+ aopt[c_opt.substr(0, p_equal)] = c_opt.substr(p_equal+1);
+ }
+ break;
+ case '?':
+ if (strchr("almorsv", optopt)) {
+ std::cerr << "Option -" << (char)optopt << " requires an argument" << std::endl;
+ print_help();
+ exit(1);
+ } else if (isprint (optopt)) {
+ std::cerr << "Option -" << (char)optopt << " is unknown" << std::endl;
+ print_help();
+ exit(1);
+ }
+ break;
+ default:
+ std::cerr << "Invalid option: " << c << std::endl;
+ print_help();
+ exit(1);
+ break;
+ }
+ }
+ // fix here the frame limit
+ if (settings::SKIP_FRAMES > 0 && settings::MAX_FRAMES>0) settings::MAX_FRAMES += settings::SKIP_FRAMES;
+ return optind;
+}
+
+namespace producers_utils {
+ void start(video_producer& ref_vpth, V_VPTH& v_th) {
+ ref_vpth.start();
+ for(V_VPTH::iterator it = v_th.begin(); it != v_th.end(); ++it)
+ (*it)->start();
+ }
+
+ void stop(video_producer& ref_vpth, V_VPTH& v_th) {
+ ref_vpth.join();
+ for(V_VPTH::iterator it = v_th.begin(); it != v_th.end(); ++it)
+ (*it)->join();
+ }
+
+ void sync(mt::Semaphore& sem_cons, const int& n_v_th) {
+ for(int i = 0; i < n_v_th; ++i)
+ sem_cons.push();
+ }
+
+ void lock(mt::Semaphore& sem_cons, mt::Semaphore& ref_prod, V_VPDATA& v_data) {
+ // init all the semaphores
+ sem_cons.push();
+ ref_prod.push();
+ for(V_VPDATA::iterator it = v_data.begin(); it != v_data.end(); ++it)
+ (*it)->prod.push();
+ }
+
+ void unlock(mt::Semaphore& ref_prod, V_VPDATA& v_data) {
+ ref_prod.pop();
+ for(V_VPDATA::iterator it = v_data.begin(); it != v_data.end(); ++it)
+ (*it)->prod.pop();
+ }
+}
+
+int main(int argc, char *argv[]) {
+ try {
+ std::map<std::string, std::string> aopt;
+ const int param = parse_options(argc, argv, aopt);
+ // Register all formats and codecs
+ av_register_all();
+ if (settings::REF_VIDEO == "")
+ throw std::runtime_error("Reference video not specified");
+ bool glb_exit = false;
+ mt::Semaphore sem_cons;
+ // create data for reference video
+ mt::Semaphore ref_prod;
+ VUCHAR ref_buf;
+ int ref_frame;
+ qav::qvideo ref_video(settings::REF_VIDEO.c_str(), settings::VIDEO_SIZE_W, settings::VIDEO_SIZE_H);
+ // get const values
+ const qav::scr_size ref_sz = ref_video.get_size();
+ const int ref_fps_k = ref_video.get_fps_k();
+ //
+ //ref_video.get_frame(ref_buf);
+ //return 0;
+ //
+ V_VPDATA v_data;
+ for(int i = param; i < argc; ++i) {
+ try {
+ shared_ptr<vp_data> vpd(new vp_data);
+ vpd->name = get_filename(argv[i]);
+ vpd->video = new qav::qvideo(argv[i], ref_sz.x, ref_sz.y);
+ if (vpd->video->get_fps_k() != ref_fps_k) {
+ if (settings::IGNORE_FPS) {
+ LOG_WARNING << '[' << argv[i] << "] has different FPS (" << vpd->video->get_fps_k()/1000 << ')' << std::endl;
+ v_data.push_back(vpd);
+ } else LOG_ERROR << '[' << argv[i] << "] skipped different FPS" << std::endl;
+ } else v_data.push_back(vpd);
+ } catch(std::exception& e) {
+ LOG_ERROR << '[' << argv[i] << "] skipped " << e.what() << std::endl;
+ }
+ }
+ if (v_data.empty()) return 0;
+ // print some infos
+ LOG_INFO << "Skip frames: " << ((settings::SKIP_FRAMES > 0) ? settings::SKIP_FRAMES : 0) << std::endl;
+ LOG_INFO << "Max frames: " << ((settings::MAX_FRAMES > 0) ? settings::MAX_FRAMES : 0) << std::endl;
+ // create the stats analyzer (like the psnr)
+ LOG_INFO << "Analyzer set: " << settings::ANALYZER << std::endl;
+ std::auto_ptr<stats::s_base> s_analyzer(stats::get_analyzer(settings::ANALYZER.c_str(), v_data.size(), ref_sz.x, ref_sz.y, std::cout));
+ // set the default values, in case will get overwritten
+ s_analyzer->set_parameter("fpa", "25");
+ s_analyzer->set_parameter("blocksize", "8");
+ // load the passed parameters
+ for(std::map<std::string, std::string>::const_iterator it = aopt.begin(); it != aopt.end(); ++it) {
+ LOG_INFO << "Analyzer parameter: " << it->first << " = " << it->second << std::endl;
+ s_analyzer->set_parameter(it->first.c_str(), it->second.c_str());
+ }
+ // create all the threads
+ video_producer ref_vpth(ref_frame, ref_prod, sem_cons, ref_buf, ref_video, glb_exit);
+ V_VPTH v_th;
+ for(V_VPDATA::iterator it = v_data.begin(); it != v_data.end(); ++it)
+ v_th.push_back(new video_producer((*it)->frame, (*it)->prod, sem_cons, (*it)->buf, *((*it)->video), glb_exit));
+ // we'll need some tmp buffers
+ VUCHAR t_ref_buf;
+ std::vector<VUCHAR> t_bufs(v_data.size());
+ // and now the core algorithm
+ // init all the semaphores
+ producers_utils::lock(sem_cons, ref_prod, v_data);
+ // start the threads
+ producers_utils::start(ref_vpth, v_th);
+ // print header
+ std::cout << "Sample,";
+ for(V_VPDATA::const_iterator it = v_data.begin(); it != v_data.end(); ++it)
+ std::cout << (*it)->name << ',';
+ std::cout << std::endl;
+ while(!glb_exit) {
+ // wait for the consumer to be signalled 1 + n times
+ const static int CONS_SIG_NUM = 1 + v_th.size();
+ producers_utils::sync(sem_cons, CONS_SIG_NUM);
+ // now check everything is ok
+ const int cur_ref_frame = ref_frame;
+ if (-1 == cur_ref_frame) {
+ glb_exit = true;
+ // allow the producers to run
+ producers_utils::unlock(ref_prod, v_data);
+ continue;
+ }
+ // in case we have to skip frames...
+ if (settings::SKIP_FRAMES > 0 && settings::SKIP_FRAMES >= cur_ref_frame) {
+ // allow the producers to run
+ producers_utils::unlock(ref_prod, v_data);
+ continue;
+ }
+ // vector of bool telling if everything is ok
+ std::vector<bool> v_ok;
+ for(V_VPDATA::const_iterator it = v_data.begin(); it != v_data.end(); ++it)
+ if ((*it)->frame == cur_ref_frame) v_ok.push_back(true);
+ else v_ok.push_back(false);
+ // then swap the vectors
+ t_ref_buf.swap(ref_buf);
+ for(int i = 0; i < v_data.size(); ++i)
+ t_bufs[i].swap(v_data[i]->buf);
+ // allow the producers to run
+ producers_utils::unlock(ref_prod, v_data);
+ // finally process data
+ s_analyzer->process(cur_ref_frame, t_ref_buf, v_ok, t_bufs);
+ // check if we have to exit
+ if (settings::MAX_FRAMES > 0 && cur_ref_frame >= settings::MAX_FRAMES) {
+ glb_exit = true;
+ // allow the producers to run
+ producers_utils::unlock(ref_prod, v_data);
+ break;
+ }
+ }
+ // wait for all threads
+ producers_utils::stop(ref_vpth, v_th);
+ } catch(std::exception& e) {
+ LOG_ERROR << e.what() << std::endl;
+ } catch(...) {
+ LOG_ERROR << "Unknown exception" << std::endl;
+ }
+}
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+* myNZB (C) 2009 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of myNZB.
+*
+* myNZB is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* myNZB is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with myNZB. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _MT_H_
+#define _MT_H_
+
+#include <pthread.h>
+#include <semaphore.h>
+#include <list>
+#include <vector>
+#include <errno.h>
+#include <exception>
+#include <string>
+
+namespace mt {
+
+ class mt_exception : public std::exception {
+ const std::string _what;
+ public:
+ mt_exception(const std::string& what) : _what(what) {
+ }
+
+ virtual const char* what() const throw() {
+ return _what.c_str();
+ }
+
+ ~mt_exception() throw () {
+ }
+ };
+
+ class Semaphore {
+ sem_t _sem;
+
+ Semaphore(const Semaphore&);
+ Semaphore& operator=(const Semaphore&);
+ public:
+ Semaphore() {
+ if (0 != sem_init(&_sem, 0, 1))
+ throw mt_exception("Semaphore: Semaphore()");
+ }
+
+ void push(void) {
+ if (0 != sem_wait(&_sem))
+ throw mt_exception("Sempahore: push");
+ }
+
+ bool trypush(void) {
+ if (0 == sem_trywait(&_sem)) {
+ return true;
+ } else if (errno == EAGAIN) {
+ return false;
+ }
+ throw mt_exception("Semaphore: trywait");
+ }
+
+ void pop(void) {
+ if (0 != sem_post(&_sem))
+ throw mt_exception("Sempahore: pop");
+ }
+
+ ~Semaphore() {
+ sem_destroy(&_sem);
+ }
+ };
+
+ class Mutex {
+ pthread_mutex_t _mtx;
+
+ Mutex(const Mutex&);
+ Mutex& operator=(const Mutex&);
+ public:
+ Mutex() {
+ if (0 != pthread_mutex_init(&_mtx, NULL))
+ throw mt_exception("Mutex: Mutex()");
+ }
+
+ void lock(void) {
+ if (0 != pthread_mutex_lock(&_mtx))
+ throw mt_exception("Mutex: lock");
+ }
+
+ void unlock(void) {
+ if (0 != pthread_mutex_unlock(&_mtx))
+ throw mt_exception("Mutex: unlock");
+ }
+
+ ~Mutex() {
+ pthread_mutex_destroy(&_mtx);
+ }
+ };
+
+ class ScopedLock {
+ Mutex& _mtx;
+
+ ScopedLock(const ScopedLock&);
+ ScopedLock& operator=(const ScopedLock&);
+ public:
+ ScopedLock(Mutex& mtx) : _mtx(mtx) {
+ _mtx.lock();
+ }
+
+ ~ScopedLock() {
+ _mtx.unlock();
+ }
+ };
+
+ class Thread {
+ pthread_t _th;
+
+ static void* exec(void* par) throw() {
+ try {
+ Thread *p = (Thread*)par;
+ p->run();
+ } catch (...) {
+ }
+ return 0;
+ }
+
+ Thread(const Thread&);
+ Thread& operator=(const Thread&);
+ public:
+ Thread() : _th(0) {
+ }
+
+ virtual void run(void) = 0;
+
+ void start(void) {
+ if (0 != pthread_create(&_th, NULL, &exec, this))
+ throw mt_exception("Thread: start");
+ }
+
+ void join(void) {
+ if (0!=_th) pthread_join(_th, NULL);
+ }
+
+ virtual ~Thread() {
+ }
+ };
+
+ class ThreadPool {
+ public:
+ class Job {
+ friend class ThreadPool;
+ Semaphore _sem;
+ volatile bool _on_hold;
+ const bool _to_be_deleted;
+ public:
+ Job(const bool& to_be_deleted = false) : _on_hold(true), _to_be_deleted(to_be_deleted) {
+ _sem.push();
+ }
+
+ virtual void run(void) = 0;
+
+ /* Just rememeber that
+ - void wait(void)
+ - bool is_running(void)
+ can only be used when the ownership of the
+ ThreadPool::Job instance is external (eg.
+ to_be_deleted = false).
+ Otherwise this will lead to a crash!
+ */
+
+ // We pop the semaphore again in case someone else
+ // will ask to wait again...
+ void wait(void) {
+ _sem.push();
+ _sem.pop();
+ };
+
+ bool is_running(void) {
+ if (_on_hold) return false;
+ if (false == _sem.trypush())
+ return true;
+ _sem.pop();
+ return false;
+ }
+
+ virtual ~Job() {
+ }
+ };
+ private:
+ Mutex _list_mtx;
+ Semaphore _list_sem;
+ std::list<Job*> _list_jobs;
+
+ // the following variable is not mutex
+ // protected because is sort of write-only
+ // by main ThreadPool thread, see destructor
+ volatile bool _tp_quit;
+ const unsigned int _n_execs;
+ std::vector<pthread_t> _th_ids;
+
+ bool get_job(Job** _job) {
+ ScopedLock _sl(_list_mtx);
+ if (!_list_jobs.empty()) {
+ *_job = _list_jobs.front();
+ _list_jobs.pop_front();
+ return true;
+ }
+ return false;
+ }
+
+ // Technically we should declare it as extern "C" but we
+ // don't care as seen as the function pointer will correspond
+ // to a proper C-like function even if the name will be C++ like
+ static void* job_exec(void* par) throw() {
+ try {
+ ThreadPool *p = (ThreadPool*)par;
+ while(true) {
+ // first push on the semaphore
+ p->_list_sem.push();
+ // check if we have to quit
+ if (p->_tp_quit) return 0;
+ // get an element to process
+ Job *curJob = 0;
+ if (p->get_job(&curJob)) {
+ // we need to save the delete_job varibale because
+ // after the semaphore has been popped the object
+ // could not exist anymore
+ const bool delete_job = curJob->_to_be_deleted;
+ try {
+ curJob->_on_hold = false;
+ curJob->run();
+ } catch(...) {
+ }
+ // if the following instruction throws is better
+ // to let the user know because this means that
+ // the semaphore is not valid anymore...this should
+ // never happen...
+ curJob->_sem.pop();
+ // if it has to be deleted do it!
+ if (delete_job) delete curJob;
+ }
+ // check if we have to quit
+ if (p->_tp_quit) return 0;
+ }
+ } catch(...) {
+ }
+ return 0;
+ }
+
+ ThreadPool(const ThreadPool&);
+ ThreadPool& operator=(const ThreadPool&);
+ public:
+ // Just take into account that semaphores are not syscall immune
+ // so when you run in debug mode you can have exceptions thrown on
+ // push because of system interrruption! Don't get scared!
+ ThreadPool(const unsigned int& n_execs) : _n_execs(n_execs), _th_ids(n_execs), _tp_quit(false) {
+ if (_n_execs == 0 || _n_execs > 256)
+ throw mt_exception("ThreadPool: invalid number of n_execs");
+ // create the job_exec threads
+ for (unsigned int i = 0; i < _n_execs; ++i)
+ if (0 != pthread_create(&_th_ids[i], NULL, &job_exec, this)) {
+ for (unsigned int j=0; j < i; ++j)
+ pthread_cancel(_th_ids[j]);
+ throw mt_exception("ThreadPool: could not start all specified n_execs");
+ }
+ }
+
+ void add(Job* job) {
+ ScopedLock _sl(_list_mtx);
+ _list_jobs.push_back(job);
+ _list_sem.pop();
+ }
+
+ ~ThreadPool() {
+ _tp_quit = true;
+ for (unsigned int i = 0; i < _n_execs; ++i)
+ _list_sem.pop();
+ for (unsigned int i = 0; i < _n_execs; ++i)
+ pthread_join(_th_ids[i], NULL);
+ // potentialy unsafe...this could throw...but should never...
+ ScopedLock _sl(_list_mtx);
+ for (std::list<Job*>::iterator it = _list_jobs.begin(); it != _list_jobs.end(); ++it)
+ if ((*it)->_to_be_deleted) delete *it;
+ }
+ };
+}
+
+#endif //_MT_H_
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "qav.h"
+#include "settings.h"
+#include <stdexcept>
+#include <sstream>
+
+qav::qvideo::qvideo(const char* file, int _out_width, int _out_height) : frnum(0), videoStream(-1), out_width(_out_width), out_height(_out_height) {
+ const char* pslash = strrchr(file, '/');
+ if (pslash) fname = pslash+1;
+ else fname = file;
+ pFormatCtx=NULL;
+ if (avformat_open_input(&pFormatCtx, file, NULL, NULL)!=0)
+ throw std::runtime_error("Can't open file");
+ if (av_find_stream_info(pFormatCtx)<0) {
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Multimedia type not supported");
+ }
+ LOG_INFO << "File info for (" << file << ")..." << std::endl;
+ av_dump_format(pFormatCtx, 0, file, false);
+ // find video stream (first)
+ for (int i=0; i<pFormatCtx->nb_streams; i++)
+ if (AVMEDIA_TYPE_VIDEO == pFormatCtx->streams[i]->codec->codec_type) {
+ videoStream=i;
+ break;
+ }
+ if (-1==videoStream) {
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Can't find video stream");
+ }
+ // Get a pointer to the codec context for the video stream
+ pCodecCtx=pFormatCtx->streams[videoStream]->codec;
+ pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
+ if(!pCodec) {
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Can't find codec for video stream");
+ }
+ if(avcodec_open(pCodecCtx, pCodec)<0) {
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Can't open codec for video stream");
+ }
+ // alloacate data to extract frames
+ pFrame = avcodec_alloc_frame();
+ if (!pFrame) {
+ avcodec_close(pCodecCtx);
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Can't allocated frame for video stream");
+ }
+ // populate the out_width/out_height members
+ if (out_width > 0 && out_height > 0) {
+ LOG_INFO << "Output frame size for (" << file << ") is: " << out_width << 'x' << out_height << std::endl;
+ } else if (-1 == out_width && -1 == out_height) {
+ out_width = pCodecCtx->width;
+ out_height = pCodecCtx->height;
+ LOG_INFO << "Output frame size for (" << file << ") (default) is: " << out_width << 'x' << out_height << std::endl;
+ } else {
+ avcodec_close(pCodecCtx);
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Invalid output frame size for video stream");
+ }
+ // just report if we're using a different video size
+ if (out_width!=pCodecCtx->width || out_height!=pCodecCtx->height)
+ LOG_WARNING << "Video (" << file <<") will get scaled: " << pCodecCtx->width << 'x' << pCodecCtx->height << " (in), " << out_width << 'x' << out_height << " (out)" << std::endl;
+ // sw context
+ img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
+ out_width, out_height, PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
+ if (!img_convert_ctx) {
+ av_free(pFrame);
+ avcodec_close(pCodecCtx);
+ av_close_input_file(pFormatCtx);
+ throw std::runtime_error("Can't allocated sw_scale context");
+ }
+}
+
+qav::scr_size qav::qvideo::get_size(void) const {
+ return scr_size(out_width, out_height);
+}
+
+int qav::qvideo::get_fps_k(void) const {
+ if (pFormatCtx->streams[videoStream]->r_frame_rate.den)
+ return 1000*pFormatCtx->streams[videoStream]->r_frame_rate.num/pFormatCtx->streams[videoStream]->r_frame_rate.den;
+ return 0;
+}
+
+bool qav::qvideo::get_frame(std::vector<unsigned char>& out, int *_frnum) {
+ out.resize(avpicture_get_size(PIX_FMT_RGB24, out_width, out_height));
+ AVPacket packet;
+ bool is_read = false;
+ av_init_packet(&packet);
+ while (av_read_frame(pFormatCtx, &packet)>=0) {
+ if (packet.stream_index==videoStream) {
+ int frameFinished = 0;
+ // Decode video frame
+ //avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);
+ avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet); // packet.data, packet.size);
+ if(frameFinished) {
+ AVPicture picRGB;
+ // Assign appropriate parts of buffer to image planes in pFrameRGB
+ avpicture_fill((AVPicture*)&picRGB, (unsigned char*)&out[0], PIX_FMT_RGB24, out_width, out_height);
+ // Convert the image from its native format to RGB
+ //img_convert((AVPicture*)&picRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
+ sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picRGB.data, picRGB.linesize);
+ ++frnum;
+ if (_frnum) *_frnum = frnum;
+ if (settings::SAVE_IMAGES) {
+ if ((settings::SKIP_FRAMES < frnum) && (-1 == settings::MAX_FRAMES || frnum <= settings::MAX_FRAMES))
+ save_frame(&out[0]);
+ }
+ is_read=true;
+ }
+ }
+ av_free_packet(&packet);
+ if (is_read) return true;
+ }
+ return false;
+}
+
+/*bool SaveTGA(char *name, const unsigned char *data, int sizeX, int sizeY) {
+ BYTE TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};
+ BYTE header[6];
+ header[1] = (BYTE)(sizeX/256);
+ header[0] = (BYTE) sizeX%256;
+ header[3] = (BYTE)(sizeY/256);
+ header[2] = (BYTE) sizeY%256;
+ header[4] = (BYTE) 24;
+ header[5] = (BYTE) 0x00;
+ int fh = open(name, _O_CREAT|_O_TRUNC|_O_WRONLY);
+ if (-1 == fh) return false;
+ if (12*sizeof(BYTE) != write(fh, TGAheader, 12*sizeof(BYTE))) return false;
+ if (6*sizeof(BYTE) != write(fh, header, 6*sizeof(BYTE))) return false;
+ for (int i = 0; i < sizeY; i++) {
+ if (3*sizeX != write(fh, data + sizeX*i*3, sizeX*3)) {
+ close(fh);
+ return false;
+ }
+ }
+ close(fh);
+ return true;
+}*/
+
+void qav::qvideo::save_frame(const unsigned char *buf, const char* __fname) {
+ FILE *pFile;
+ std::string s_fname;
+ char num_buf[32];
+
+ //
+ sprintf(num_buf, ".%08d.ppm", frnum);
+ num_buf[31] = '\0';
+ std::ostringstream oss;
+ oss << ((__fname) ? __fname : fname.c_str()) << num_buf;
+ // Open file
+ pFile=fopen(oss.str().c_str(), "wb");
+ if(pFile==NULL)
+ return;
+
+ // Write header
+ fprintf(pFile, "P6\n%d %d\n255\n", out_width, out_height);
+
+ // Write pixel data
+ for(int y=0; y<out_height; y++)
+ fwrite(buf+y*out_width*3, 1, out_width*3, pFile);
+
+ // Close file
+ fclose(pFile);
+}
+
+qav::qvideo::~qvideo() {
+ sws_freeContext(img_convert_ctx);
+ av_free(pFrame);
+ avcodec_close(pCodecCtx);
+ av_close_input_file(pFormatCtx);
+}
+
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _QAV_H_
+#define _QAV_H_
+
+// libavcodec is a C library without C++ guards...
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+}
+
+#include <string>
+#include <vector>
+
+namespace qav {
+ struct scr_size {
+ int x,
+ y;
+ scr_size(const int& _x = 0, const int& _y = 0) : x(_x), y(_y) {
+ }
+
+ inline friend bool operator==(const scr_size& lhs, const scr_size& rhs) {
+ return lhs.x==rhs.x && lhs.y==rhs.y;
+ }
+ };
+
+ class qvideo {
+ int frnum,
+ videoStream,
+ out_width,
+ out_height;
+ AVFormatContext *pFormatCtx;
+ AVCodecContext *pCodecCtx;
+ AVCodec *pCodec;
+ AVFrame *pFrame;
+ struct SwsContext *img_convert_ctx;
+ std::string fname;
+ public:
+ qvideo(const char* file, int _out_width = -1, int _out_height = -1);
+ scr_size get_size(void) const;
+ int get_fps_k(void) const;
+ bool get_frame(std::vector<unsigned char>& out, int *_frnum = 0);
+ void save_frame(const unsigned char *buf, const char* __fname = 0);
+ ~qvideo();
+ };
+}
+
+
+#endif /*_QAV_H_*/
+
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "settings.h"
+
+namespace settings {
+ char LOG = 0x07;
+ std::string REF_VIDEO = "";
+ int MAX_FRAMES = -1;
+ int SKIP_FRAMES = -1;
+ bool SAVE_IMAGES = false;
+ std::string ANALYZER = "psnr";
+ bool IGNORE_FPS = false;
+ int VIDEO_SIZE_W = -1;
+ int VIDEO_SIZE_H = -1;
+}
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SETTINGS_H_
+#define _SETTINGS_H_
+
+#include <string>
+#include <iostream>
+
+#define LEVEL_LOG_ERROR (0x01)
+#define LEVEL_LOG_WARNING (0x02)
+#define LEVEL_LOG_INFO (0x04)
+#define LEVEL_LOG_DEBUG (0x08)
+
+#define __LOG(x) if ( x & settings::LOG) std::cerr
+
+#define LOG_ERROR __LOG(LEVEL_LOG_ERROR) << "[ERROR] "
+#define LOG_WARNING __LOG(LEVEL_LOG_WARNING) << "[WARNING] "
+#define LOG_INFO __LOG(LEVEL_LOG_INFO) << "[INFO] "
+#define LOG_DEBUG __LOG(LEVEL_LOG_DEBUG) << "[DEBUG] "
+
+namespace settings {
+ extern char LOG;
+ extern std::string REF_VIDEO;
+ extern int MAX_FRAMES;
+ extern int SKIP_FRAMES;
+ extern bool SAVE_IMAGES;
+ extern std::string ANALYZER;
+ extern bool IGNORE_FPS;
+ extern int VIDEO_SIZE_W;
+ extern int VIDEO_SIZE_H;
+}
+
+
+#endif /*_SETTINGS_H_*/
+
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+* myNZB (C) 2009 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of myNZB.
+*
+* myNZB is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* myNZB is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with myNZB. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SHARED_PTR_H_
+#define _SHARED_PTR_H_
+
+template<typename T>
+class shared_ptr
+{
+public:
+ typedef T type;
+ typedef T* ptr_type;
+ typedef unsigned int count_type;
+
+ shared_ptr() :
+ _ptr(0),
+ _count(0)
+ {
+ }
+
+ shared_ptr(const ptr_type& in) :
+ _ptr(in)
+ {
+ _count = new (std::nothrow) count_type;
+ if (!_count)
+ {
+ delete in;
+ throw std::bad_alloc();
+ }
+ *_count = 1;
+ }
+
+ shared_ptr(const shared_ptr& in)
+ {
+ _ptr = in._ptr;
+ _count = in._count;
+ if (_count) *_count += 1;
+ }
+
+ shared_ptr& operator=(const shared_ptr& in)
+ {
+ if (_ptr != in._ptr)
+ {
+ if (_ptr)
+ {
+ if (!--*_count)
+ {
+ delete _ptr;
+ delete _count;
+ }
+ }
+ _ptr = in._ptr;
+ _count = in._count;
+ if (_count) *_count += 1;
+ }
+ return *this;
+ }
+
+ ~shared_ptr()
+ {
+ if (_count)
+ {
+ if (!--*_count)
+ {
+ delete _ptr;
+ delete _count;
+ }
+ }
+ }
+
+ ptr_type get(void) const
+ {
+ return _ptr;
+ }
+
+ count_type get_count(void) const
+ {
+ if (_count) return *_count;
+ return 0;
+ }
+
+ ptr_type operator->() const
+ {
+ return _ptr;
+ }
+
+ type& operator*() const
+ {
+ return *_ptr;
+ }
+
+ template<typename U>
+ bool operator==(shared_ptr<U>& in)
+ {
+ return _ptr == in._ptr;
+ }
+
+ template<typename U>
+ bool operator!=(shared_ptr<U>& in)
+ {
+ return _ptr != in._ptr;
+ }
+
+ template<typename U>
+ bool operator<(shared_ptr<U>& in)
+ {
+ return _ptr < in._ptr;
+ }
+private:
+ ptr_type _ptr;
+ count_type *_count;
+};
+
+#endif //_SHARED_PTR_H_
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stats.h"
+#include "mt.h"
+#include "shared_ptr.h"
+#include "settings.h"
+#include <cmath>
+#include <string>
+#include <stdexcept>
+#include <set>
+#include <fstream>
+#include <algorithm>
+#include <cstdlib>
+
+// define these classes just locally
+namespace stats {
+ static double compute_psnr(const unsigned char *ref, const unsigned char *cmp, const unsigned int& sz) {
+ double mse = 0.0;
+ for(unsigned int i = 0; i < sz; ++i) {
+ const int diff = ref[i]-cmp[i];
+ mse += (diff*diff);
+ }
+ mse /= (double)sz;
+ if (0.0 == mse) mse = 1e-10;
+ return 10.0*log10(65025.0/mse);
+ }
+
+ static double compute_ssim(const unsigned char *ref, const unsigned char *cmp, const unsigned int& x, const unsigned int& y, const unsigned int& b_sz) {
+ // we return the average of all the blocks
+ const unsigned int x_bl_num = x/b_sz,
+ y_bl_num = y/b_sz;
+ if (!x_bl_num || !y_bl_num) return 0.0;
+ std::vector<double> ssim_accum;
+ // for each block do it
+ for(int yB = 0; yB < y_bl_num; ++yB)
+ for(int xB = 0; xB < x_bl_num; ++xB) {
+ const unsigned int base_offset = xB*b_sz + yB*b_sz*x;
+ double ref_acc = 0.0,
+ ref_acc_2 = 0.0,
+ cmp_acc = 0.0,
+ cmp_acc_2 = 0.0,
+ ref_cmp_acc = 0.0;
+ for(int j = 0; j < b_sz; ++j)
+ for(int i = 0; i < b_sz; ++i) {
+ // we have to multiply by 3, colorplanes are Y Cb Cr, we need
+ // only Y component
+ const unsigned char c_ref = ref[3*(base_offset + j*x + i)],
+ c_cmp = cmp[3*(base_offset + j*x + i)];
+ ref_acc += c_ref;
+ ref_acc_2 += (c_ref*c_ref);
+ cmp_acc += c_cmp;
+ cmp_acc_2 += (c_cmp*c_cmp);
+ ref_cmp_acc += (c_ref*c_cmp);
+ }
+ // now finally get the ssim for this block
+ // http://en.wikipedia.org/wiki/SSIM
+ // http://en.wikipedia.org/wiki/Variance
+ // http://en.wikipedia.org/wiki/Covariance
+ const double n_samples = (b_sz*b_sz),
+ ref_avg = ref_acc/n_samples,
+ ref_var = ref_acc_2/n_samples - (ref_avg*ref_avg),
+ cmp_avg = cmp_acc/n_samples,
+ cmp_var = cmp_acc_2/n_samples - (cmp_avg*cmp_avg),
+ ref_cmp_cov = ref_cmp_acc/n_samples - (ref_avg*cmp_avg),
+ c1 = 6.5025, // (0.01*255.0)^2
+ c2 = 58.5225, // (0.03*255)^2
+ ssim_num = (2.0*ref_avg*cmp_avg + c1)*(2.0*ref_cmp_cov + c2),
+ ssim_den = (ref_avg*ref_avg + cmp_avg*cmp_avg + c1)*(ref_var + cmp_var + c2),
+ ssim = ssim_num/ssim_den;
+ ssim_accum.push_back(ssim);
+ }
+
+ double avg = 0.0;
+ for(std::vector<double>::const_iterator it = ssim_accum.begin(); it != ssim_accum.end(); ++it)
+ avg += *it;
+ return avg/ssim_accum.size();
+ }
+
+ static inline double r_0_1(const double& d) {
+ return std::max(0.0, std::min(1.0, d));
+ }
+
+ static void rgb_2_hsi(unsigned char *p, const int& sz) {
+ /*
+ I = (1/3) *(R+G+B)
+ S = 1 - ( (3/(R+G+B)) * min(R,G,B))
+ H = cos^-1( ((1/2) * ((R-G) + (R - B))) / ((R-G)^2 + (R-B)*(G-B)) ^(1/2) )
+ H = cos^-1 ( (((R-G)+(R-B))/2)/ (sqrt((R-G)^2 + (R-B)*(G-B) )))
+ */
+ const static double PI = 3.14159265;
+ for(int j =0; j < sz; j += 3) {
+ const double r = p[j+0]/255.0,
+ g = p[j+1]/255.0,
+ b = p[j+2]/255.0,
+ i = r_0_1((r+g+b)/3),
+ s = r_0_1(1.0 - (3.0/(r+g+b) * std::min(r, std::min(g, b)))),
+ h = r_0_1(acos(0.5*(r-g + r-b) / sqrt((r-g)*(r-g) + (r-b)*(g-b)))/PI);
+ p[j+0] = 255.0*h + 0.5;
+ p[j+1] = 255.0*s + 0.5;
+ p[j+2] = 255.0*i + 0.5;
+ }
+ }
+
+ static void rgb_2_YCbCr(unsigned char *p, const int& sz) {
+ // http://en.wikipedia.org/wiki/YCbCr
+ for(int j =0; j < sz; j += 3) {
+ const double r = p[j+0]/255.0,
+ g = p[j+1]/255.0,
+ b = p[j+2]/255.0;
+ p[j+0] = 16 + (65.481*r + 128.553*g + 24.966*b);
+ p[j+1] = 128 + (-37.797*r - 74.203*g + 112.0*b);
+ p[j+2] = 128 + (112.0*r - 93.786*g - 12.214*b);
+ }
+ }
+
+ static void rgb_2_Y(unsigned char *p, const int& sz) {
+ // http://en.wikipedia.org/wiki/YCbCr
+ for(int j =0; j < sz; j += 3) {
+ const double r = p[j+0]/255.0,
+ g = p[j+1]/255.0,
+ b = p[j+2]/255.0;
+ p[j+0] = 16 + (65.481*r + 128.553*g + 24.966*b);
+ p[j+1] = 0.0;
+ p[j+2] = 0.0;
+ }
+ }
+
+ static int getCPUcount(void) {
+ std::ifstream cpuinfo("/proc/cpuinfo");
+ std::string line;
+ std::set<std::string> IDs;
+ while (cpuinfo){
+ std::getline(cpuinfo,line);
+ if (line.empty())
+ continue;
+ if (line.find("processor") != 0)
+ continue;
+ IDs.insert(line);
+ }
+ if (IDs.empty()) return 1;
+ return IDs.size();
+ }
+
+ static mt::ThreadPool __stats_tp(getCPUcount());
+
+ class psnr_job : public mt::ThreadPool::Job {
+ const unsigned char *_ref,
+ *_cmp;
+ const unsigned int _sz;
+ double &_res;
+ public:
+ psnr_job(const unsigned char *ref, const unsigned char *cmp, const unsigned int& sz, double& res) :
+ _ref(ref), _cmp(cmp), _sz(sz), _res(res) {
+ }
+
+ virtual void run(void) {
+ _res = compute_psnr(_ref, _cmp, _sz);
+ }
+ };
+
+ static void get_psnr_tp(const VUCHAR& ref, const std::vector<bool>& v_ok, const std::vector<VUCHAR>& streams, std::vector<double>& res) {
+ const unsigned int sz = v_ok.size();
+ std::vector<shared_ptr<psnr_job> > v_jobs;
+ for(unsigned int i =0; i < sz; ++i) {
+ if (v_ok[i]) {
+ v_jobs.push_back(new psnr_job(&ref[0], &(streams[i][0]), ref.size(), res[i]));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ } else res[i] = 0.0;
+ }
+ //wait for all
+ for(std::vector<shared_ptr<psnr_job> >::iterator it = v_jobs.begin(); it != v_jobs.end(); ++it) {
+ (*it)->wait();
+ (*it) = 0;
+ }
+ }
+
+ class ssim_job : public mt::ThreadPool::Job {
+ const unsigned char *_ref,
+ *_cmp;
+ const unsigned int _x,
+ _y,
+ _b_sz;
+ double &_res;
+ public:
+ ssim_job(const unsigned char *ref, const unsigned char *cmp, const unsigned int& x, const unsigned int& y, const unsigned int b_sz, double& res) :
+ _ref(ref), _cmp(cmp), _x(x), _y(y), _b_sz(b_sz), _res(res) {
+ }
+
+ virtual void run(void) {
+ _res = compute_ssim(_ref, _cmp, _x, _y, _b_sz);
+ }
+ };
+
+ static void get_ssim_tp(const VUCHAR& ref, const std::vector<bool>& v_ok, const std::vector<VUCHAR>& streams, std::vector<double>& res, const unsigned int& x, const unsigned int& y, const unsigned int& b_sz) {
+ const unsigned int sz = v_ok.size();
+ std::vector<shared_ptr<ssim_job> > v_jobs;
+ for(unsigned int i =0; i < sz; ++i) {
+ if (v_ok[i]) {
+ v_jobs.push_back(new ssim_job(&ref[0], &(streams[i][0]), x, y, b_sz, res[i]));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ } else res[i] = 0.0;
+ }
+ //wait for all
+ for(std::vector<shared_ptr<ssim_job> >::iterator it = v_jobs.begin(); it != v_jobs.end(); ++it) {
+ (*it)->wait();
+ (*it) = 0;
+ }
+ }
+
+ class hsi_job : public mt::ThreadPool::Job {
+ unsigned char *_buf;
+ const unsigned int _sz;
+ public:
+ hsi_job(unsigned char *buf, const unsigned int& sz) :
+ _buf(buf), _sz(sz) {
+ }
+
+ virtual void run(void) {
+ rgb_2_hsi(_buf, _sz);
+ }
+ };
+
+ class YCbCr_job : public mt::ThreadPool::Job {
+ unsigned char *_buf;
+ const unsigned int _sz;
+ public:
+ YCbCr_job(unsigned char *buf, const unsigned int& sz) :
+ _buf(buf), _sz(sz) {
+ }
+
+ virtual void run(void) {
+ rgb_2_YCbCr(_buf, _sz);
+ }
+ };
+
+ class Y_job : public mt::ThreadPool::Job {
+ unsigned char *_buf;
+ const unsigned int _sz;
+ public:
+ Y_job(unsigned char *buf, const unsigned int& sz) :
+ _buf(buf), _sz(sz) {
+ }
+
+ virtual void run(void) {
+ rgb_2_Y(_buf, _sz);
+ }
+ };
+
+ static void rgb_2_hsi_tp(VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ const unsigned int sz = v_ok.size();
+ std::vector<shared_ptr<hsi_job> > v_jobs;
+ v_jobs.push_back(new hsi_job(&ref[0], ref.size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ for(unsigned int i =0; i < sz; ++i) {
+ if (v_ok[i]) {
+ v_jobs.push_back(new hsi_job(&(streams[i][0]), streams[i].size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ }
+ }
+ //wait for all
+ for(std::vector<shared_ptr<hsi_job> >::iterator it = v_jobs.begin(); it != v_jobs.end(); ++it) {
+ (*it)->wait();
+ (*it) = 0;
+ }
+ }
+
+ static void rgb_2_YCbCr_tp(VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ const unsigned int sz = v_ok.size();
+ std::vector<shared_ptr<YCbCr_job> > v_jobs;
+ v_jobs.push_back(new YCbCr_job(&ref[0], ref.size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ for(unsigned int i =0; i < sz; ++i) {
+ if (v_ok[i]) {
+ v_jobs.push_back(new YCbCr_job(&(streams[i][0]), streams[i].size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ }
+ }
+ //wait for all
+ for(std::vector<shared_ptr<YCbCr_job> >::iterator it = v_jobs.begin(); it != v_jobs.end(); ++it) {
+ (*it)->wait();
+ (*it) = 0;
+ }
+ }
+
+ static void rgb_2_Y_tp(VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ const unsigned int sz = v_ok.size();
+ std::vector<shared_ptr<Y_job> > v_jobs;
+ v_jobs.push_back(new Y_job(&ref[0], ref.size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ for(unsigned int i =0; i < sz; ++i) {
+ if (v_ok[i]) {
+ v_jobs.push_back(new Y_job(&(streams[i][0]), streams[i].size()));
+ __stats_tp.add(v_jobs.rbegin()->get());
+ }
+ }
+ //wait for all
+ for(std::vector<shared_ptr<Y_job> >::iterator it = v_jobs.begin(); it != v_jobs.end(); ++it) {
+ (*it)->wait();
+ (*it) = 0;
+ }
+ }
+
+ class psnr : public s_base {
+ std::string _colorspace;
+ protected:
+ void print(const int& ref_frame, const std::vector<double>& v_res) {
+ _ostr << ref_frame << ',';
+ for(int i = 0; i < _n_streams; ++i)
+ _ostr << v_res[i] << ',';
+ _ostr << std::endl;
+ }
+
+ void process_colorspace(VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ if (_colorspace == "hsi") {
+ rgb_2_hsi_tp(ref, v_ok, streams);
+ } else if (_colorspace == "ycbcr") {
+ rgb_2_YCbCr_tp(ref, v_ok, streams);
+ } else if (_colorspace == "y") {
+ rgb_2_Y_tp(ref, v_ok, streams);
+ }
+ }
+ public:
+ psnr(const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) :
+ s_base(n_streams, i_width, i_height, ostr), _colorspace("rgb") {
+ }
+
+ virtual void set_parameter(const char* p_name, const char *p_value) {
+ const std::string s_name(p_name);
+ if (s_name == "colorspace") {
+ _colorspace = p_value;
+ if (_colorspace != "rgb" && _colorspace != "hsi"
+ && _colorspace != "ycbcr" && _colorspace != "y")
+ throw std::runtime_error("Invalid colorspace passed to analyzer");
+ }
+ }
+
+ virtual void process(const int& ref_frame, VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ if (v_ok.size() != streams.size() || v_ok.size() != _n_streams) throw std::runtime_error("Invalid data size passed to analyzer");
+ // process colorspace
+ process_colorspace(ref, v_ok, streams);
+ //
+ std::vector<double> v_res(_n_streams);
+ get_psnr_tp(ref, v_ok, streams, v_res);
+ //
+ print(ref_frame, v_res);
+ }
+ };
+
+ class avg_psnr : public psnr {
+ int _fpa,
+ _accum_f,
+ _last_frame;
+ std::vector<double> _accum_v;
+ protected:
+ void print(const int& ref_frame) {
+ // check if we have to print infor or not
+ if (_accum_f < _fpa) return;
+ // else print data
+ for(int i=0; i < _n_streams; ++i)
+ _accum_v[i] /= _accum_f;
+ psnr::print(ref_frame, _accum_v);
+ // clear the vector and set _accum_f to 0
+ _accum_v.clear();
+ _accum_v.resize(_n_streams);
+ _accum_f = 0;
+ }
+ public:
+ avg_psnr(const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) :
+ psnr(n_streams, i_width, i_height, ostr), _fpa(1), _accum_f(0), _last_frame(-1), _accum_v(n_streams) {
+ }
+
+ virtual void set_parameter(const char* p_name, const char *p_value) {
+ psnr::set_parameter(p_name, p_value);
+ //
+ const std::string s_name(p_name);
+ if (s_name == "fpa") {
+ const int fpa = atoi(p_value);
+ if (fpa > 0) _fpa = fpa;
+ }
+ }
+
+ virtual void process(const int& ref_frame, VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ if (v_ok.size() != streams.size() || v_ok.size() != _n_streams) throw std::runtime_error("Invalid data size passed to analyzer");
+ // set last frame
+ _last_frame = ref_frame;
+ // process colorspace
+ process_colorspace(ref, v_ok, streams);
+ // compute the psnr
+ std::vector<double> v_res(_n_streams);
+ get_psnr_tp(ref, v_ok, streams, v_res);
+ // accumulate for each
+ for(int i = 0; i < _n_streams; ++i) {
+ if (v_ok[i]) {
+ _accum_v[i] += v_res[i];
+ } else _accum_v[i] += 0.0;
+ }
+ ++_accum_f;
+ // try to print data
+ print(ref_frame);
+ }
+
+ virtual ~avg_psnr() {
+ // on exit check if we have some data
+ if (_accum_f > 0) {
+ _ostr << _last_frame << ',';
+ for(int i = 0; i < _n_streams; ++i) {
+ _ostr << _accum_v[i]/_accum_f << ',';
+ }
+ _ostr << std::endl;
+ }
+ }
+ };
+
+ class ssim : public s_base {
+ protected:
+ int _blocksize;
+
+ void print(const int& ref_frame, const std::vector<double>& v_res) {
+ _ostr << ref_frame << ',';
+ for(int i = 0; i < _n_streams; ++i)
+ _ostr << v_res[i] << ',';
+ _ostr << std::endl;
+ }
+ public:
+ ssim(const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) :
+ s_base(n_streams, i_width, i_height, ostr), _blocksize(8) {
+ }
+
+ virtual void set_parameter(const char* p_name, const char *p_value) {
+ const std::string s_name(p_name);
+ if (s_name == "blocksize") {
+ const int blocksize = atoi(p_value);
+ if (blocksize > 0) _blocksize = blocksize;
+ }
+ }
+
+ virtual void process(const int& ref_frame, VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ if (v_ok.size() != streams.size() || v_ok.size() != _n_streams) throw std::runtime_error("Invalid data size passed to analyzer");
+ // convert to Y colorspace
+ rgb_2_Y_tp(ref, v_ok, streams);
+ //
+ std::vector<double> v_res(_n_streams);
+ get_ssim_tp(ref, v_ok, streams, v_res, _i_width, _i_height, _blocksize);
+ //
+ print(ref_frame, v_res);
+ }
+ };
+
+ class avg_ssim : public ssim {
+ int _fpa,
+ _accum_f,
+ _last_frame;
+ std::vector<double> _accum_v;
+ protected:
+ void print(const int& ref_frame) {
+ // check if we have to print infor or not
+ if (_accum_f < _fpa) return;
+ // else print data
+ for(int i=0; i < _n_streams; ++i)
+ _accum_v[i] /= _accum_f;
+ ssim::print(ref_frame, _accum_v);
+ // clear the vector and set _accum_f to 0
+ _accum_v.clear();
+ _accum_v.resize(_n_streams);
+ _accum_f = 0;
+ }
+ public:
+ avg_ssim(const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) :
+ ssim(n_streams, i_width, i_height, ostr), _fpa(1), _accum_f(0), _last_frame(-1), _accum_v(n_streams) {
+ }
+
+ virtual void set_parameter(const char* p_name, const char *p_value) {
+ ssim::set_parameter(p_name, p_value);
+ //
+ const std::string s_name(p_name);
+ if (s_name == "fpa") {
+ const int fpa = atoi(p_value);
+ if (fpa > 0) _fpa = fpa;
+ }
+ }
+
+ virtual void process(const int& ref_frame, VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) {
+ if (v_ok.size() != streams.size() || v_ok.size() != _n_streams) throw std::runtime_error("Invalid data size passed to analyzer");
+ // set last frame
+ _last_frame = ref_frame;
+ // convert to Y colorspace
+ rgb_2_Y_tp(ref, v_ok, streams);
+ //
+ std::vector<double> v_res(_n_streams);
+ get_ssim_tp(ref, v_ok, streams, v_res, _i_width, _i_height, _blocksize);
+ // accumulate for each
+ for(int i = 0; i < _n_streams; ++i) {
+ if (v_ok[i]) {
+ _accum_v[i] += v_res[i];
+ } else _accum_v[i] += 0.0;
+ }
+ ++_accum_f;
+ // try to print data
+ print(ref_frame);
+ }
+
+ virtual ~avg_ssim() {
+ // on exit check if we have some data
+ if (_accum_f > 0) {
+ _ostr << _last_frame << ',';
+ for(int i = 0; i < _n_streams; ++i) {
+ _ostr << _accum_v[i]/_accum_f << ',';
+ }
+ _ostr << std::endl;
+ }
+ }
+ };
+}
+
+stats::s_base* stats::get_analyzer(const char* id, const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) {
+ const std::string s_id(id);
+ if (s_id == "psnr") return new psnr(n_streams, i_width, i_height, ostr);
+ else if (s_id == "avg_psnr") return new avg_psnr(n_streams, i_width, i_height, ostr);
+ else if (s_id == "ssim") return new ssim(n_streams, i_width, i_height, ostr);
+ else if (s_id == "avg_ssim") return new avg_ssim(n_streams, i_width, i_height, ostr);
+ throw std::runtime_error("Invalid analyzer id");
+}
--- /dev/null
+/*
+* qpsnr (C) 2010 E. Oriani, ema <AT> fastwebnet <DOT> it
+*
+* This file is part of qpsnr.
+*
+* qpsnr is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation, either version 3 of the License, or
+* (at your option) any later version.
+*
+* qpsnr is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with qpsnr. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _STATS_H_
+#define _STATS_H_
+
+#include <vector>
+#include <ostream>
+
+namespace stats {
+ typedef std::vector<unsigned char> VUCHAR;
+
+ class s_base {
+ protected:
+ const int _n_streams,
+ _i_width,
+ _i_height;
+ std::ostream &_ostr;
+ public:
+ s_base(const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr) :
+ _n_streams(n_streams), _i_width(i_width), _i_height(i_height), _ostr(ostr) {
+ }
+
+ virtual void set_parameter(const char* p_name, const char *p_value) {
+ }
+
+ virtual void process(const int& ref_frame, VUCHAR& ref, const std::vector<bool>& v_ok, std::vector<VUCHAR>& streams) = 0;
+
+ virtual ~s_base() {
+ }
+ };
+
+ extern s_base* get_analyzer(const char* id, const int& n_streams, const int& i_width, const int& i_height, std::ostream& ostr);
+}
+
+#endif /*_STATS_H_*/
+