diff options
Diffstat (limited to 'utils')
| -rw-r--r-- | utils/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | utils/kmsview.cpp | 23 | ||||
| -rw-r--r-- | utils/wbcap.cpp | 362 | ||||
| -rw-r--r-- | utils/wbm2m.cpp | 168 | 
4 files changed, 541 insertions, 18 deletions
| diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 27e4bec..dd95f70 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -18,3 +18,9 @@ target_link_libraries(kmscapture kms++ kms++util ${LIBDRM_LIBRARIES})  add_executable (kmsblank kmsblank.cpp)  target_link_libraries(kmsblank kms++ kms++util ${LIBDRM_LIBRARIES}) + +add_executable (wbcap wbcap.cpp) +target_link_libraries(wbcap kms++ kms++util ${LIBDRM_LIBRARIES}) + +add_executable (wbm2m wbm2m.cpp) +target_link_libraries(wbm2m kms++ kms++util ${LIBDRM_LIBRARIES}) diff --git a/utils/kmsview.cpp b/utils/kmsview.cpp index b503f0a..6f236a1 100644 --- a/utils/kmsview.cpp +++ b/utils/kmsview.cpp @@ -79,27 +79,14 @@ int main(int argc, char** argv)  	Card card(dev_path); +	ResourceManager res(card); -	auto conn = card.get_first_connected_connector(); -	auto crtc = conn->get_current_crtc(); - -	auto fb = new DumbFramebuffer(card, w, h, pixfmt); - -	Plane* plane = 0; - -	for (Plane* p : crtc->get_possible_planes()) { -		if (p->plane_type() != PlaneType::Overlay) -			continue; - -		if (!p->supports_format(pixfmt)) -			continue; - -		plane = p; -		break; -	} - +	auto conn = res.reserve_connector(); +	auto crtc = res.reserve_crtc(conn); +	auto plane = res.reserve_overlay_plane(crtc, pixfmt);  	FAIL_IF(!plane, "available plane not found"); +	auto fb = new DumbFramebuffer(card, w, h, pixfmt);  	unsigned frame_size = 0;  	for (unsigned i = 0; i < fb->num_planes(); ++i) diff --git a/utils/wbcap.cpp b/utils/wbcap.cpp new file mode 100644 index 0000000..f4f2b71 --- /dev/null +++ b/utils/wbcap.cpp @@ -0,0 +1,362 @@ +#include <cstdio> +#include <poll.h> +#include <unistd.h> +#include <algorithm> + +#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, uint32_t width, uint32_t height, PixelFormat pixfmt) +		: m_capdev(*streamer) +	{ +		m_capdev.set_port(crtc->idx()); +		m_capdev.set_format(pixfmt, width, height); +		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(); +	} + +	void 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); +	} + +	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) +	{ +	} + +	void setup(uint32_t x, uint32_t y, uint32_t width, uint32_t height) +	{ +		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", x); +		req.add(m_plane, "CRTC_Y", y); +		req.add(m_plane, "CRTC_W", width); +		req.add(m_plane, "CRTC_H", 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) +		: m_card(card), m_crtc(crtc) +	{ +		m_plane = m_crtc->get_primary_plane(); + +		uint32_t w = m_crtc->mode().hdisplay; +		uint32_t h = m_crtc->mode().vdisplay; + +		for (unsigned i = 0; i < s_num_buffers; ++i) +			m_fbs[i] = new DumbFramebuffer(card, w, h, 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, { +				{ "FB_ID", fb->id() }, +			}); + +		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" +		"  -f, --format=4CC          Format" +		"  -h, --help                Print this help\n" +		; + +int main(int argc, char** argv) +{ +	string src_conn_name = "unknown"; +	string dst_conn_name = "hdmi"; +	PixelFormat pixfmt = PixelFormat::XRGB8888; + +	OptionSet optionset = { +		Option("s|src=", [&](string s) +		{ +			src_conn_name = s; +		}), +		Option("d|dst=", [&](string s) +		{ +			dst_conn_name = s; +		}), +		Option("f|format=", [&](string s) +		{ +			pixfmt = FourCCToPixelFormat(s); +		}), +		Option("h|help", [&]() +		{ +			puts(usage_str); +			exit(-1); +		}), +	}; + +	optionset.parse(argc, argv); + +	if (optionset.params().size() > 0) { +		puts(usage_str); +		exit(-1); +	} + +	VideoDevice vid("/dev/video11"); + +	Card card; +	ResourceManager resman(card); + +	auto src_conn = resman.reserve_connector(src_conn_name); +	auto src_crtc = resman.reserve_crtc(src_conn); + +	uint32_t src_width = src_crtc->mode().hdisplay; +	uint32_t src_height = src_crtc->mode().vdisplay; + +	printf("src %s, crtc %ux%u\n", src_conn->fullname().c_str(), src_width, src_height); + +	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"); + +	uint32_t dst_width = min((uint32_t)dst_crtc->mode().hdisplay, src_width); +	uint32_t dst_height = min((uint32_t)dst_crtc->mode().vdisplay, src_height); + +	printf("dst %s, crtc %ux%u, plane %ux%u\n", dst_conn->fullname().c_str(), +	       dst_crtc->mode().hdisplay, dst_crtc->mode().vdisplay, +	       dst_width, dst_height); + +	for (int i = 0; i < CAMERA_BUF_QUEUE_SIZE; ++i) { +		auto fb = new DumbFramebuffer(card, src_width, src_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); +	barflipper.start_flipping(); + +	// This shows the captures SRC frames on DST display +	WBFlipState wbflipper(card, dst_crtc, dst_plane); +	wbflipper.setup(0, 0, dst_width, dst_height); + +	WBStreamer wb(vid.get_capture_streamer(), src_crtc, src_width, src_height, 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; + +	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; + +			wb.Dequeue(); +			wbflipper.queue_next(); +		} + +		if (fds[2].revents) { +			fds[2].revents = 0; + +			card.call_page_flip_handlers(); +			wb.Queue(); +		} +	} + +	printf("exiting...\n"); +} diff --git a/utils/wbm2m.cpp b/utils/wbm2m.cpp new file mode 100644 index 0000000..1f8dffa --- /dev/null +++ b/utils/wbm2m.cpp @@ -0,0 +1,168 @@ +#include <cstdio> +#include <poll.h> +#include <unistd.h> +#include <algorithm> +#include <fstream> +#include <map> + +#include <kms++/kms++.h> +#include <kms++util/kms++util.h> +#include <kms++util/videodevice.h> + +const uint32_t NUM_SRC_BUFS=2; +const uint32_t NUM_DST_BUFS=2; + +using namespace std; +using namespace kms; + +static const char* usage_str = +		"Usage: wbm2m [OPTIONS]\n\n" +		"Options:\n" +		"  -f, --format=4CC          Output format" +		"  -h, --help                Print this help\n" +		; + +const int bar_speed = 4; +const int bar_width = 10; + +static unsigned get_bar_pos(DumbFramebuffer* fb, unsigned frame_num) +{ +	return (frame_num * bar_speed) % (fb->width() - bar_width + 1); +} + +static void read_frame(DumbFramebuffer* fb, unsigned frame_num) +{ +	static map<DumbFramebuffer*, int> s_bar_pos_map; + +	int old_pos = -1; +	if (s_bar_pos_map.find(fb) != s_bar_pos_map.end()) +		old_pos = s_bar_pos_map[fb]; + +	int pos = get_bar_pos(fb, frame_num); +	draw_color_bar(*fb, old_pos, pos, bar_width); +	draw_text(*fb, fb->width() / 2, 0, to_string(frame_num), RGB(255, 255, 255)); +	s_bar_pos_map[fb] = pos; +} + +int main(int argc, char** argv) +{ +	// XXX get from args +	const uint32_t src_width = 800; +	const uint32_t src_height = 480; +	const auto src_fmt = PixelFormat::XRGB8888; +	const uint32_t num_src_frames = 10; + +	const uint32_t dst_width = 800; +	const uint32_t dst_height = 480; +	auto dst_fmt = PixelFormat::XRGB8888; + +	const string filename = "wb-out.raw"; + +	OptionSet optionset = { +		Option("f|format=", [&](string s) +		{ +			dst_fmt = FourCCToPixelFormat(s); +		}), +		Option("h|help", [&]() +		{ +			puts(usage_str); +			exit(-1); +		}), +	}; + +	optionset.parse(argc, argv); + +	if (optionset.params().size() > 0) { +		puts(usage_str); +		exit(-1); +	} + +	VideoDevice vid("/dev/video10"); + +	Card card; + +	uint32_t src_frame_num = 0; +	uint32_t dst_frame_num = 0; + +	VideoStreamer* out = vid.get_output_streamer(); +	VideoStreamer* in = vid.get_capture_streamer(); + +	out->set_format(src_fmt, src_width, src_height); +	in->set_format(dst_fmt, dst_width, dst_height); + +	out->set_queue_size(NUM_SRC_BUFS); +	in->set_queue_size(NUM_DST_BUFS); + + +	for (unsigned i = 0; i < min(NUM_SRC_BUFS, num_src_frames); ++i) { +		auto fb = new DumbFramebuffer(card, src_width, src_height, src_fmt); + +		read_frame(fb, src_frame_num++); + +		out->queue(fb); +	} + +	for (unsigned i = 0; i < min(NUM_DST_BUFS, num_src_frames); ++i) { +		auto fb = new DumbFramebuffer(card, dst_width, dst_height, dst_fmt); +		in->queue(fb); +	} + +	vector<pollfd> fds(3); + +	fds[0].fd = 0; +	fds[0].events =  POLLIN; +	fds[1].fd = vid.fd(); +	fds[1].events =  POLLIN; +	fds[2].fd = card.fd(); +	fds[2].events =  POLLIN; + +	ofstream os(filename, ofstream::binary); + +	out->stream_on(); +	in->stream_on(); + +	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; + + +			try { +				DumbFramebuffer *dst_fb = in->dequeue(); +				printf("Writing frame %u\n", dst_frame_num); +				for (unsigned i = 0; i < dst_fb->num_planes(); ++i) +					os.write((char*)dst_fb->map(i), dst_fb->size(i)); +				in->queue(dst_fb); + +				dst_frame_num++; + +				if (dst_frame_num >= num_src_frames) +					break; + +			} catch (system_error& se) { +				if (se.code() != errc::resource_unavailable_try_again) +					FAIL("dequeue failed: %s", se.what()); + +				break; +			} + +			DumbFramebuffer *src_fb = out->dequeue(); + +			if (src_frame_num < num_src_frames) { +				read_frame(src_fb, src_frame_num++); +				out->queue(src_fb); +			} +		} + +		if (fds[2].revents) { +			fds[2].revents = 0; +		} +	} + +	printf("exiting...\n"); +} | 
