summaryrefslogtreecommitdiff
path: root/linux-core
AgeCommit message (Expand)Author
2001-06-18Forgot to bump date stamp.Gareth Hughes
2001-06-18Enable shared IRQs in DMA template, use in i810 driver.Gareth Hughes
2001-06-14First pass of 4.1.0 merge.David Dawes
2001-05-23Only authenticated clients can mmap() (Jeff Hartmann).Gareth Hughes
2001-05-17Make the SiS module work again. At least glxinfo reports it's working, yetAlan Hourihane
2001-05-03Make SiS driver compile with the new templated format. Not tested. minorAlan Hourihane
2001-05-01Import of XFree86 4.0.99.3David Dawes
2001-04-30fix build of i810 kernel driver for 2.4.3 or greater kernelsAlan Hourihane
2001-04-30- PCIGART patches for Alpha from CompaqKevin E Martin
2001-04-09Import -f XFree86 4.0.99.2David Dawes
2001-04-06Handle drivers that don't have __HAVE_SG defined.Alan Hourihane
2001-04-05Merged ati-pcigart-1-0-0Kevin E Martin
2001-04-03include 2.4.2Alan Hourihane
2001-04-03make 2.4.2 -> 2.4.3 change conditional. works with older than 2.4.3 kernelsAlan Hourihane
2001-04-02Update radeon DRM to v1.1.0 (texture upload changes).Gareth Hughes
2001-03-30merge in 2.4.3 kernel change.Alan Hourihane
2001-03-21- Fix MGA header info.Gareth Hughes
2001-03-19Import of XFree86 4.0.99.1David Dawes
2001-03-19Update version, date stamp.Gareth Hughes
2001-03-19Remove PRIMPTR completely.Gareth Hughes
2001-03-18__REALLY_HAVE_MTRR, vmalloc_32 fixes from Jeff Wiedemeier.Gareth Hughes
2001-03-14Merged sarea-1-0-0Kevin E Martin
2001-03-08Fix ring space calculations, tests. Based on patch by Bruce Stockwell.Gareth Hughes
2001-03-07Change error message to debug message when client dies while holding theGareth Hughes
2001-03-07surround agp calls in drm_memory with __REALLY_HAVE_AGP instead ofAlan Hourihane
2001-03-06Merge tdfx-3-1-0 branch.Gareth Hughes
2001-03-05allow dristat to find out whether AGP is write-combined or not.Alan Hourihane
2001-03-05fix that last patch to initialize the MTRR when AGP available.Alan Hourihane
2001-03-04Don't try and setup the MTRR for AGP when AGP not available. CheckAlan Hourihane
2001-02-21Add Linux 2.2.x support for stubsRik Faith
2001-02-16- Clean up the way customization of the templates is done.Gareth Hughes
2001-02-15Added missing include "drm_lists.h".Keith Whitwell
2001-02-15- Fix up merge.Gareth Hughes
2001-02-15Merge mga-1-0-0-branch into trunk.Gareth Hughes
2001-01-29Corresponding sync with PCI GART updates.Gareth Hughes
2001-01-24- Misc cleanups.Gareth Hughes
2001-01-08Merged tdfx-3-0-0Nathan Hand
2001-01-05Merged ati-5-0-0Kevin E Martin
2001-01-04Sync with Linux 2.4.0-prereleaseRik Faith
2000-12-30add blit ioctl, fix plnwt handlingKeith Whitwell
2000-12-15Update date information.Gareth Hughes
2000-12-12- Fix nasty depth span bug. Drawable offset was not being added to pixelGareth Hughes
2000-12-02Merged ati-4-1-1-branch into trunk.Gareth Hughes
2000-11-15Sync with Linux 2.4.0-test11-pre5 Provide backward compatibility testedRik Faith
2000-11-08merge with 4.0.1dDavid Dawes
2000-09-29More changes for sync with Linux 2.4.0-test9-pre7Rik Faith
2000-09-29Audit calls to schedule() Remove tags from files shared with Linux kernelRik Faith
2000-09-28Fixed two things Rik pointed out in the last commitJeff Hartmann
2000-09-27Merged the mga-lock-debug-0-2-0-branch with the trunk. This includesJeff Hartmann
2000-09-24commit xfree86 4.0.1d-pre updateAlan Hourihane
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
#include <cstdio>
#include <poll.h>
#include <unistd.h>
#include <algorithm>
#include <fstream>

#include <kms++/kms++.h>
#include <kms++util/kms++util.h>
#include <kms++util/videodevice.h>

#define CAMERA_BUF_QUEUE_SIZE 5

using namespace std;
using namespace kms;

static vector<DumbFramebuffer*> s_fbs;
static vector<DumbFramebuffer*> s_free_fbs;
static vector<DumbFramebuffer*> s_wb_fbs;
static vector<DumbFramebuffer*> s_ready_fbs;

class WBStreamer
{
public:
	WBStreamer(VideoStreamer* streamer, Crtc* crtc, PixelFormat pixfmt)
		: m_capdev(*streamer)
	{
		Videomode m = crtc->mode();

		m_capdev.set_port(crtc->idx());
		m_capdev.set_format(pixfmt, m.hdisplay, m.vdisplay / (m.interlace() ? 2 : 1));
		m_capdev.set_queue_size(s_fbs.size());

		for (auto fb : s_free_fbs) {
			m_capdev.queue(fb);
			s_wb_fbs.push_back(fb);
		}

		s_free_fbs.clear();
	}

	~WBStreamer()
	{
	}

	WBStreamer(const WBStreamer& other) = delete;
	WBStreamer& operator=(const WBStreamer& other) = delete;

	int fd() const { return m_capdev.fd(); }

	void start_streaming()
	{
		m_capdev.stream_on();
	}

	void stop_streaming()
	{
		m_capdev.stream_off();
	}

	DumbFramebuffer* Dequeue()
	{
		auto fb = m_capdev.dequeue();

		auto iter = find(s_wb_fbs.begin(), s_wb_fbs.end(), fb);
		s_wb_fbs.erase(iter);

		s_ready_fbs.insert(s_ready_fbs.begin(), fb);

		return fb;
	}

	void Queue()
	{
		if (s_free_fbs.size() == 0)
			return;

		auto fb = s_free_fbs.back();
		s_free_fbs.pop_back();

		m_capdev.queue(fb);

		s_wb_fbs.insert(s_wb_fbs.begin(), fb);
	}

private:
	VideoStreamer& m_capdev;
};

class WBFlipState : private PageFlipHandlerBase
{
public:
	WBFlipState(Card& card, Crtc* crtc, Plane* plane)
		: m_card(card), m_crtc(crtc), m_plane(plane)
	{
		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);

		req.add(m_plane, "CRTC_ID", m_crtc->id());
		req.add(m_plane, "FB_ID", fb->id());

		req.add(m_plane, "CRTC_X", 0);
		req.add(m_plane, "CRTC_Y", 0);
		req.add(m_plane, "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()));
		req.add(m_plane, "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()));

		req.add(m_plane, "SRC_X", 0);
		req.add(m_plane, "SRC_Y", 0);
		req.add(m_plane, "SRC_W", fb->width() << 16);
		req.add(m_plane, "SRC_H", fb->height() << 16);

		int r = req.commit_sync();
		FAIL_IF(r, "initial plane setup failed");

		m_current_fb = fb;
	}

	void queue_next()
	{
		if (m_queued_fb)
			return;

		if (s_ready_fbs.size() == 0)
			return;

		auto fb = s_ready_fbs.back();
		s_ready_fbs.pop_back();

		AtomicReq req(m_card);
		req.add(m_plane, "FB_ID", fb->id());

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);

		m_queued_fb = fb;
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		if (m_queued_fb) {
			if (m_current_fb)
				s_free_fbs.insert(s_free_fbs.begin(), m_current_fb);

			m_current_fb = m_queued_fb;
			m_queued_fb = nullptr;
		}

		queue_next();
	}

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	DumbFramebuffer* m_current_fb = nullptr;
	DumbFramebuffer* m_queued_fb = nullptr;
};

class BarFlipState : private PageFlipHandlerBase
{
public:
	BarFlipState(Card& card, Crtc* crtc, Plane* plane, uint32_t width, uint32_t height)
		: m_card(card), m_crtc(crtc), m_plane(plane)
	{
		for (unsigned i = 0; i < s_num_buffers; ++i)
			m_fbs[i] = new DumbFramebuffer(card, width, height, PixelFormat::XRGB8888);
	}

	~BarFlipState()
	{
		for (unsigned i = 0; i < s_num_buffers; ++i)
			delete m_fbs[i];
	}

	void start_flipping()
	{
		m_frame_num = 0;
		queue_next();
	}

private:
	void handle_page_flip(uint32_t frame, double time)
	{
		m_frame_num++;
		queue_next();
	}

	static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num)
	{
		return (frame_num * bar_speed) % (fb->width() - bar_width + 1);
	}

	void draw_bar(DumbFramebuffer* fb, unsigned frame_num)
	{
		int old_xpos = frame_num < s_num_buffers ? -1 : get_bar_pos(fb, frame_num - s_num_buffers);
		int new_xpos = get_bar_pos(fb, frame_num);

		draw_color_bar(*fb, old_xpos, new_xpos, bar_width);
		draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255));
	}

	void queue_next()
	{
		AtomicReq req(m_card);

		unsigned cur = m_frame_num % s_num_buffers;

		auto fb = m_fbs[cur];

		draw_bar(fb, m_frame_num);

		req.add(m_plane, {
				{ "CRTC_ID", m_crtc->id() },
				{ "FB_ID", fb->id() },

				{ "CRTC_X", 0 },
				{ "CRTC_Y", 0 },
				{ "CRTC_W", min((uint32_t)m_crtc->mode().hdisplay, fb->width()) },
				{ "CRTC_H", min((uint32_t)m_crtc->mode().vdisplay, fb->height()) },

				{ "SRC_X", 0 },
				{ "SRC_Y", 0 },
				{ "SRC_W", fb->width() << 16 },
				{ "SRC_H", fb->height() << 16 },
			});

		int r = req.commit(this);
		if (r)
			EXIT("Flip commit failed: %d\n", r);
	}

	static const unsigned s_num_buffers = 3;

	DumbFramebuffer* m_fbs[s_num_buffers];

	Card& m_card;
	Crtc* m_crtc;
	Plane* m_plane;

	unsigned m_frame_num;

	static const unsigned bar_width = 20;
	static const unsigned bar_speed = 8;
};

static const char* usage_str =
		"Usage: wbcap [OPTIONS]\n\n"
		"Options:\n"
		"  -s, --src=CONN            Source connector\n"
		"  -d, --dst=CONN            Destination connector\n"
		"  -m, --smode=MODE          Source connector videomode\n"
		"  -M, --dmode=MODE          Destination connector videomode\n"
		"  -f, --format=4CC          Format\n"
		"  -w, --write               Write captured frames to wbcap.raw file\n"
		"  -h, --help                Print this help\n"
		;

int main(int argc, char** argv)
{
	string src_conn_name;
	string src_mode_name;
	string dst_conn_name;
	string dst_mode_name;
	PixelFormat pixfmt = PixelFormat::XRGB8888;
	bool write_file = false;

	OptionSet optionset = {
		Option("s|src=", [&](string s)
		{
			src_conn_name = s;
		}),
		Option("m|smode=", [&](string s)
		{
			src_mode_name = s;
		}),
		Option("d|dst=", [&](string s)
		{
			dst_conn_name = s;
		}),
		Option("M|dmode=", [&](string s)
		{
			dst_mode_name = s;
		}),
		Option("f|format=", [&](string s)
		{
			pixfmt = FourCCToPixelFormat(s);
		}),
		Option("w|write", [&]()
		{
			write_file = true;
		}),
		Option("h|help", [&]()
		{
			puts(usage_str);
			exit(-1);
		}),
	};

	optionset.parse(argc, argv);

	if (optionset.params().size() > 0) {
		puts(usage_str);
		exit(-1);
	}

	if (src_conn_name.empty())
		EXIT("No source connector defined");

	if (dst_conn_name.empty())
		EXIT("No destination connector defined");

	VideoDevice vid("/dev/video11");

	Card card;
	ResourceManager resman(card);

	card.disable_all();

	auto src_conn = resman.reserve_connector(src_conn_name);
	auto src_crtc = resman.reserve_crtc(src_conn);
	auto src_plane = resman.reserve_generic_plane(src_crtc, pixfmt);
	FAIL_IF(!src_plane, "Plane not found");
	Videomode src_mode = src_mode_name.empty() ? src_conn->get_default_mode() : src_conn->get_mode(src_mode_name);
	src_crtc->set_mode(src_conn, src_mode);


	auto dst_conn = resman.reserve_connector(dst_conn_name);
	auto dst_crtc = resman.reserve_crtc(dst_conn);
	auto dst_plane = resman.reserve_overlay_plane(dst_crtc, pixfmt);
	FAIL_IF(!dst_plane, "Plane not found");
	Videomode dst_mode = dst_mode_name.empty() ? dst_conn->get_default_mode() : dst_conn->get_mode(dst_mode_name);
	dst_crtc->set_mode(dst_conn, dst_mode);

	uint32_t src_width = src_mode.hdisplay;
	uint32_t src_height = src_mode.vdisplay;

	uint32_t dst_width = src_mode.hdisplay;
	uint32_t dst_height = src_mode.vdisplay;
	if (src_mode.interlace())
		dst_height /= 2;

	printf("src %s, crtc %s\n", src_conn->fullname().c_str(), src_mode.to_string().c_str());

	printf("dst %s, crtc %s\n", dst_conn->fullname().c_str(), dst_mode.to_string().c_str());

	printf("src_fb %ux%u, dst_fb %ux%u\n", src_width, src_height, dst_width, dst_height);

	for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) {
		auto fb = new DumbFramebuffer(card, dst_width, dst_height, pixfmt);
		s_fbs.push_back(fb);
		s_free_fbs.push_back(fb);
	}

	// get one fb for initial setup
	s_ready_fbs.push_back(s_free_fbs.back());
	s_free_fbs.pop_back();

	// This draws a moving bar to SRC display
	BarFlipState barflipper(card, src_crtc, src_plane, src_width, src_height);
	barflipper.start_flipping();

	// This shows the captured SRC frames on DST display
	WBFlipState wbflipper(card, dst_crtc, dst_plane);

	WBStreamer wb(vid.get_capture_streamer(), src_crtc, pixfmt);
	wb.start_streaming();

	vector<pollfd> fds(3);

	fds[0].fd = 0;
	fds[0].events =  POLLIN;
	fds[1].fd = wb.fd();
	fds[1].events =  POLLIN;
	fds[2].fd = card.fd();
	fds[2].events =  POLLIN;

	uint32_t dst_frame_num = 0;

	const string filename = "wbcap.raw";
	unique_ptr<ofstream> os;
	if (write_file)
		os = unique_ptr<ofstream>(new ofstream(filename, ofstream::binary));

	while (true) {
		int r = poll(fds.data(), fds.size(), -1);
		ASSERT(r > 0);

		if (fds[0].revents != 0)
			break;

		if (fds[1].revents) {
			fds[1].revents = 0;

			DumbFramebuffer* fb = wb.Dequeue();

			if (write_file) {
				printf("Writing frame %u to %s\n", dst_frame_num, filename.c_str());

				for (unsigned i = 0; i < fb->num_planes(); ++i)
					os->write((char*)fb->map(i), fb->size(i));

				dst_frame_num++;
			}

			wbflipper.queue_next();
		}

		if (fds[2].revents) {
			fds[2].revents = 0;

			card.call_page_flip_handlers();
			wb.Queue();
		}
	}

	printf("exiting...\n");
}