Skip to content
Snippets Groups Projects
  • moenck's avatar
    e335be08
    Restructuring · e335be08
    moenck authored
    - New config structure
    - CI/CD in yaml (see gitlab)
    - Config in userforlders (%APPDATA%)
    - MSI Installer and appimage as CI/CD products
    - Exploded Biotracker in multiple repos (interfaces, utility and trackers)
    e335be08
    History
    Restructuring
    moenck authored
    - New config structure
    - CI/CD in yaml (see gitlab)
    - Config in userforlders (%APPDATA%)
    - MSI Installer and appimage as CI/CD products
    - Exploded Biotracker in multiple repos (interfaces, utility and trackers)
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
VideoCoder.cpp 6.14 KiB
#include "VideoCoder.h"
#include <chrono>
#include <thread>

#ifdef _WIN32
#include <conio.h>
#include <windows.h>
#endif

using namespace BioTrackerUtilsMisc; //getTimeAndDate

#define mySleep(x) std::this_thread::sleep_for(std::chrono::milliseconds(x));

void MutexLinkedList::push(std::shared_ptr<ImageBuffer> imbuffer, bool dropFrames) {

	if (dropFrames) {
		while (images.size() > MAXIMUMQUEUE)
			pop();
	}
	else {
		while (images.size() > MAXIMUMQUEUE)
			mySleep(10);
	}

	_Access.lock();

	images.push_back(imbuffer);
	unsigned long s = images.size();
	unsigned long w = imbuffer->getWidth();
	unsigned long h = imbuffer->getHeight();
	//if (s*w*h / 1024 / 1024 > BUFFER_HARDLIMIT) {
	//	std::cout << "ERROR: Buffer exceeds hardlimit (" << BUFFER_HARDLIMIT << " MB). Exiting. " << std::endl;
	//	std::exit(1);
	//}
	_Access.unlock();
}

std::shared_ptr<ImageBuffer> MutexLinkedList::pop() {
	_Access.lock();
	if (images.size()>0) {
		std::shared_ptr<ImageBuffer> img = images.front();
		images.pop_front();
		_Access.unlock();
		return img;
	}
	_Access.unlock();

	return std::make_shared<ImageBuffer>();
}

void MutexLinkedList::MutexLinkedList::clear() {
	_Access.lock();
	while (images.size()>0) {
		images.pop_front();
	}
	_Access.unlock();
}


//Simple function to get the current size of the buffer in elements.
//Locks the data structure.
int MutexLinkedList::size() {
	int tsize = 0;
	_Access.lock();
	tsize = images.size();
	_Access.unlock();
	return tsize;
}

///////////////////////////////////////////////////////////////////////////////////////////////

void YuvConverter::convert()
{
	int w = inImg.size().width;
	int h = inImg.size().height;
	unsigned char *prtM = inImg.data;
	unsigned int stride = 1;
	//Y
	int x, y;
	for (y = 0; y < h; y++) {
		for (x = 0; x < w; x++) {
			//This works but is horribly slow!
			//out0[y * w + x] = inImg.at<cv::Vec3b>(y, x)[0];
			//out1[y * w + x] = inImg.at<cv::Vec3b>(y, x)[1];
			//out2[y * w + x] = inImg.at<cv::Vec3b>(y, x)[2];
			//This works and is quite a bunch faster
			out0[y * w + x] = (uint8_t)(*prtM);
			prtM++;
			out1[y * w + x] = (uint8_t)(*prtM);
			prtM++;
			out2[y * w + x] = (uint8_t)(*prtM);
			prtM++;
		}
	}
}

void YuvConverter::convert420()
{
	int w = inImg.size().width;
	int h = inImg.size().height;
	unsigned char *prtM = inImg.data;
	unsigned int stride = 1;
	//Y
	int x, y;
	for (y = 0; y < (h / 3 * 2); y++) {
		for (x = 0; x < w; x++) {
			unsigned char ch = (uint8_t)(*prtM);
			out0[y * w + x] = ch;
			prtM++;
		}
	}
	
	for (y = 0; y < (h / 3 * 1); y++) {
		for (x = 0; x < w/2; x++) {
			out2[y * w + x] = (uint8_t)(*prtM);
			prtM++;
			out1[y * w + x] = (uint8_t)(*prtM);
			prtM++;
		}
	}
}

///////////////////////////////////////////////////////////////////////////////////////////////

void Worker::run() {

#ifdef WITH_CUDA
	if (m_nvEncoder) {
		unsigned char *o0 = m_nvEncoder->getYuvChannel(0);
		unsigned char *o1 = m_nvEncoder->getYuvChannel(1);
		unsigned char *o2 = m_nvEncoder->getYuvChannel(2);
		std::shared_ptr<ImageBuffer> mat;
		while (1) {
			while ((mat = ll.pop())->getWidth() == -1) {
				mySleep(10);
				if (m_abort) return;
			}
			if (m_abort) return;

			cv::Mat writeMat;
			cv::cvtColor(*(mat->_img), writeMat, CV_BGR2YUV_I420);//CV_BGR2YUV_I420 //CV_BGR2YUV
			int chans = writeMat.channels();
			YuvConverter yc(writeMat, o0, o1, o2);
			yc.convert420();
			m_nvEncoder->encodeNext();
		}
	}
	else
#endif
	{
		std::shared_ptr<ImageBuffer> mat;
		while (1) {
			while ((mat = ll.pop())->getWidth() == -1) {
				mySleep(10);
				if (m_abort) return;
			}
			if (m_abort) return;

			m_vWriter->write(*mat->_img);
		}
		
	}
}

int VideoCoder::toggle(int w, int h, double fps) {

	//Grab the codec from config file
	std::string codecStr = codecList[_cfg->VideoCodecUsed].second;
	std::string videoDir = _cfg->DirVideos.toStdString();
	m_dropFrames = _cfg->DropFrames;
	m_qp = _cfg->GpuQp;
    if (fps == -1) {
        fps = m_fps;
    }


	if (!m_recording)
	{
		worker = std::make_shared<Worker>();

		//Check which one to use
		if (codecStr == "X264") {
			int codec = CV_FOURCC('X', 'V', 'I', 'D');
			vWriter = std::make_shared<cv::VideoWriter>(getTimeAndDate(videoDir+"CameraCapture", ".avi"), codec, fps, CvSize(w, h), 1);
			m_recording = vWriter->isOpened();
            vWriter->set(cv::VIDEOWRITER_PROP_QUALITY, 100);
			std::cout << "Video is open:" << m_recording << std::endl;
			m_recType = 2;
			int ok = start();
			m_recording = (ok == 1 ? false : true);
		}
		else if (codecStr == "X264GPU") {
#ifdef WITH_CUDA
			auto cfg = m_nvEncoder->getEncodeConfig();
			cfg->fps = fps;
			cfg->width = w;
			cfg->height = h;
			cfg->codec = NV_ENC_H264;
			cfg->inputFormat = NV_ENC_BUFFER_FORMAT_NV12;//NV_ENC_BUFFER_FORMAT_NV12;//NV_ENC_BUFFER_FORMAT_YUV444
			const std::string f = getTimeAndDate(std::string(CFG_DIR_VIDEOS) + "CameraCapture", ".avi");
			char* chr = strdup(f.c_str());
			m_nvEncoder->setOutfile(chr);
			cfg->qp = m_qp;
			m_recType = 1;
			int ok = start();
			free(chr);
			m_recording = (ok == 1 ? false : true);
			std::cout << "Video is open:" << m_recording << std::endl;
#endif
		}
	}
	else {
		m_recording = false;
		if (m_recType == 1)
			stop();
		if (m_recType == 2)
			stop();//vWriter->release();
		vWriter = 0;
	}
	if (!m_recording)
		m_recType = 0;
	return m_recording;
}

void VideoCoder::add(std::shared_ptr<cv::Mat> m, int needsConversion) {
	worker->ll.push(std::make_shared<ImageBuffer>(m, needsConversion), m_dropFrames);
}

int VideoCoder::start() {
#ifdef WITH_CUDA
	if (m_recType == 1) {
		int ok = m_nvEncoder->init();
		if (ok > 0)
			return ok;
		worker->m_nvEncoder = m_nvEncoder;
		worker->start();
	}
	else
#endif
		if (m_recType == 2) {
			worker->m_vWriter = vWriter;
			worker->start();
		}

	return 0;
}

void VideoCoder::stop() {
	if (m_recType > 0) {
		worker->m_abort = true;
		worker->wait();
#ifdef WITH_CUDA
		if (m_nvEncoder)
			m_nvEncoder->close();
#endif
		if (vWriter)
			vWriter->release();
		worker->ll.clear();
	}
}