diff -Paur --no-dereference -- video-player.upstream/Makefile video-player/Makefile
--- video-player.upstream/Makefile
+++ video-player/Makefile
@@ -0,0 +1,36 @@
+include ../../../build-aux/compiler.mak
+include ../../../build-aux/version.mak
+include ../../../build-aux/dirs.mak
+
+PKG_CONFIG?=pkg-config
+
+OPTLEVEL?=-g -O2
+CXXFLAGS?=$(OPTLEVEL)
+
+CPPFLAGS:=$(CPPFLAGS)
+CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
+
+BINARY:=video-player
+LIBS!=$(PKG_CONFIG) --libs libavcodec libavformat libswscale
+
+DISPLAY:=$(shell $(LD) -ldisplay -o /dev/null 2>/dev/null && echo 1)
+ifeq ($(DISPLAY),1)
+LIBS:=$(LIBS) -ldisplay
+CPPFLAGS:=$(CPPFLAGS) -DDISPLAY
+else
+LIBS:=$(LIBS) -ldispd
+endif
+
+all: $(BINARY)
+
+.PHONY: all install clean
+
+%: %.cpp
+	$(CXX) -std=gnu++11 $(CXXFLAGS) $(CPPFLAGS) $< -o $@ $(LIBS)
+
+install: all
+	mkdir -p $(DESTDIR)$(BINDIR)
+	install $(BINARY) $(DESTDIR)$(BINDIR)
+
+clean:
+	rm -f $(BINARY)
diff -Paur --no-dereference -- video-player.upstream/video-player.cpp video-player/video-player.cpp
--- video-player.upstream/video-player.cpp
+++ video-player/video-player.cpp
@@ -0,0 +1,302 @@
+#define __STDC_CONSTANT_MACROS
+#define __STDC_LIMIT_MACROS
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <timespec.h>
+#include <unistd.h>
+
+#ifdef DISPLAY
+#include <display.h>
+#else
+#include <dispd.h>
+#endif
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libswscale/swscale.h>
+} // extern "C"
+
+uint32_t WINDOW_ID = 0;
+uint32_t WINDOW_WIDTH = 0;
+uint32_t WINDOW_HEIGHT = 0;
+
+bool need_show = true;
+bool need_exit = false;
+
+uint32_t* framebuffer = NULL;
+size_t framebuffer_size = 0;
+
+void on_disconnect(void*)
+{
+	need_exit = true;
+}
+
+void on_quit(void*, uint32_t window_id)
+{
+	if ( window_id != WINDOW_ID )
+		return;
+	need_exit = true;
+}
+
+void on_resize(void*, uint32_t window_id, uint32_t width, uint32_t height)
+{
+	if ( window_id != WINDOW_ID )
+		return;
+	WINDOW_WIDTH = width;
+	WINDOW_HEIGHT = height;
+}
+
+void on_keyboard(void*, uint32_t window_id, uint32_t)
+{
+	if ( window_id != WINDOW_ID )
+		return;
+}
+
+#ifdef DISPLAY
+static void DisplayVideoFrame(AVFrame* frame, struct display_connection* connection)
+#else
+static void DisplayVideoFrame(AVFrame* frame, struct dispd_window* window)
+#endif
+{
+#ifdef DISPLAY
+	size_t framebuffer_needed = sizeof(uint32_t) * WINDOW_WIDTH * WINDOW_HEIGHT;
+	if ( framebuffer_size != framebuffer_needed )
+	{
+		framebuffer = (uint32_t*) realloc(framebuffer, framebuffer_size = framebuffer_needed);
+		memset(framebuffer, 255, framebuffer_needed);
+	}
+	uint32_t width = WINDOW_WIDTH;
+	uint32_t height = WINDOW_HEIGHT;
+#else
+	struct dispd_framebuffer* window_fb = dispd_begin_render(window);
+	uint32_t width = dispd_get_framebuffer_width(window_fb);
+	uint32_t height = dispd_get_framebuffer_height(window_fb);
+	uint32_t* framebuffer = (uint32_t*) dispd_get_framebuffer_data(window_fb);
+#endif
+
+	SwsContext* sws_ctx = sws_getContext(frame->width, frame->height,
+	                                     (AVPixelFormat) frame->format, width,
+	                                     height, AV_PIX_FMT_RGB32, SWS_BILINEAR,
+	                                     NULL, NULL, NULL);
+	assert(sws_ctx);
+
+	uint8_t* data_arr[1] = { (uint8_t*) framebuffer };
+	int stride_arr[1] = { (int) (sizeof(framebuffer[0]) * width) };
+	sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height, data_arr,
+	          stride_arr);
+
+	sws_freeContext(sws_ctx);
+
+#ifdef DISPLAY
+	display_render_window(connection, WINDOW_ID, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, framebuffer);
+
+	struct display_event_handlers handlers;
+	memset(&handlers, 0, sizeof(handlers));
+	handlers.disconnect_handler = on_disconnect;
+	handlers.quit_handler = on_quit;
+	handlers.resize_handler = on_resize;
+	handlers.keyboard_handler = on_keyboard;
+	while ( display_poll_event(connection, &handlers) == 0 );
+#else
+	dispd_finish_render(window_fb);
+#endif
+}
+
+#ifdef DISPLAY
+bool PlayVideo(const char* path, struct display_connection* connection)
+#else
+bool PlayVideo(const char* path, struct dispd_window* connection)
+#endif
+{
+	bool ret = false;
+	int av_error;
+	AVFormatContext* format_ctx = NULL;
+	int video_stream_id;
+	int audio_stream_id;
+	AVStream* video_stream = NULL;
+	AVStream* audio_stream = NULL;
+	const AVCodec* video_codec = NULL;
+	const AVCodec* audio_codec = NULL;
+	AVCodecContext* video_codec_ctx = NULL;
+	AVCodecContext* audio_codec_ctx = NULL;
+	AVFrame* video_frame = NULL;
+	AVFrame* audio_frame = NULL;
+	AVPacket packet;
+
+	if ( (av_error = avformat_open_input(&format_ctx, path, NULL, NULL)) < 0 )
+	{
+		error(0, 0, "%s: cannot open: %i\n", path, av_error);
+		goto cleanup_done;
+	}
+
+	if ( (av_error = avformat_find_stream_info(format_ctx, NULL)) < 0 )
+	{
+		error(0, 0, "%s: avformat_find_stream_info: %i\n", path, av_error);
+		goto cleanup_input;
+	}
+
+	video_stream_id = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1,
+	                                      -1, &video_codec, 0);
+	audio_stream_id = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1,
+	                                      -1, &audio_codec, 0);
+
+	if ( 0 <= video_stream_id )
+		video_stream = format_ctx->streams[video_stream_id];
+	if ( 0 <= audio_stream_id )
+		audio_stream = format_ctx->streams[audio_stream_id];
+
+	if ( !video_stream )
+	{
+		error(0, 0, "%s: no video stream found\n", path);
+		goto cleanup_input;
+	}
+
+	if ( video_codec && !(video_codec_ctx = avcodec_alloc_context3(video_codec)))
+		goto cleanup_input;
+	if ( audio_codec && !(audio_codec_ctx = avcodec_alloc_context3(audio_codec)))
+		goto cleanup_video_codec_ctx;
+
+
+	if ( video_codec_ctx )
+	{
+		if ( (av_error = avcodec_open2(video_codec_ctx, NULL, NULL)) < 0 )
+			goto cleanup_audio_codec_ctx;
+	}
+	if ( audio_codec_ctx )
+	{
+		if ( (av_error = avcodec_open2(audio_codec_ctx, NULL, NULL)) < 0 )
+			goto cleanup_audio_codec_ctx;
+	}
+
+	if ( !(video_frame = av_frame_alloc()) )
+		goto cleanup_audio_codec_ctx;
+	if ( !(audio_frame = av_frame_alloc()) )
+		goto cleanup_video_frame;
+
+	struct timespec next_frame_at;
+	clock_gettime(CLOCK_MONOTONIC, &next_frame_at);
+
+	while ( !need_exit && 0 <= (av_error = av_read_frame(format_ctx, &packet)) )
+	{
+		int stream_index = packet.stream_index;
+		if ( stream_index == video_stream->index )
+		{
+			if ( (av_error = avcodec_send_packet(video_codec_ctx,
+			                                     &packet)) < 0 )
+				goto break_decode_loop;
+			while ( !(av_error = avcodec_receive_frame(video_codec_ctx,
+			                                           video_frame)) )
+			{
+				struct timespec now;
+				clock_gettime(CLOCK_MONOTONIC, &now);
+				while ( timespec_le(now, next_frame_at) )
+				{
+					struct timespec left = timespec_sub(next_frame_at, now);
+					clock_nanosleep(CLOCK_MONOTONIC, 0, &left, NULL);
+					clock_gettime(CLOCK_MONOTONIC, &now);
+				}
+
+				DisplayVideoFrame(video_frame, connection);
+
+				uintmax_t usecs = video_codec_ctx->ticks_per_frame * 1000000 *
+					              video_codec_ctx->time_base.num /
+					              video_codec_ctx->time_base.den;
+				next_frame_at = timespec_add(next_frame_at, timespec_make(0, usecs * 1000));
+			}
+			if ( av_error != AVERROR(EAGAIN) && av_error != AVERROR_EOF )
+				goto break_decode_loop;
+		}
+		if ( stream_index == audio_stream->index )
+		{
+			// TODO: Add sound support when an backend is available.
+		}
+	}
+break_decode_loop:
+
+	// TODO: Determine whether the are here because of EOF or whether an error
+	//       occured and we need to print an error.
+	// TODO: Do we need to clean up the last packet or does the av_read_frame
+	//       function do that for us upon error?
+	ret = true;
+
+goto cleanup_audio_frame;
+cleanup_audio_frame:
+	if ( audio_frame )
+		av_frame_free(&audio_frame);
+cleanup_video_frame:
+	if ( video_frame )
+		av_frame_free(&video_frame);
+cleanup_audio_codec_ctx:
+	if ( audio_codec_ctx )
+	{
+		avcodec_close(audio_codec_ctx);
+		av_free(audio_codec_ctx);
+	}
+cleanup_video_codec_ctx:
+	if ( video_codec_ctx )
+	{
+		avcodec_close(video_codec_ctx);
+		av_free(video_codec_ctx);
+	}
+cleanup_input:
+	avformat_close_input(&format_ctx);
+cleanup_done:
+	return ret;
+}
+
+int main(int argc, char* argv[])
+{
+#ifdef DISPLAY
+	struct display_connection* connection = display_connect_default();
+	if ( !connection )
+		error(1, errno, "Could not connect to display server");
+#else
+	if ( !isatty(0) )
+		error(1, errno, "standard input");
+	if ( !dispd_initialize(&argc, &argv) )
+		error(1, 0, "couldn't initialize dispd library");
+	struct dispd_session* session = dispd_attach_default_session();
+	if ( !session )
+		error(1, 0, "couldn't attach to dispd default session");
+	if ( !dispd_session_setup_game_rgba(session) )
+		error(1, 0, "couldn't setup dispd rgba session");
+	struct dispd_window* window = dispd_create_window_game_rgba(session);
+	if ( !window )
+		error(1, 0, "couldn't create dispd rgba window");
+	struct dispd_window* connection = window;
+#endif
+
+#ifdef DISPLAY
+	WINDOW_WIDTH = 800;
+	WINDOW_HEIGHT = 450;
+
+	display_create_window(connection, WINDOW_ID);
+	display_resize_window(connection, WINDOW_ID, WINDOW_WIDTH, WINDOW_HEIGHT);
+	display_show_window(connection, WINDOW_ID);
+#endif
+
+	for ( int i = 1; i < argc; i++ )
+	{
+#ifdef DISPLAY
+		display_title_window(connection, WINDOW_ID, argv[i]);
+#endif
+		if ( !PlayVideo(argv[i], connection) )
+			return 1;
+	}
+
+#ifdef DISPLAY
+	display_disconnect(connection);
+#else
+	dispd_destroy_window(window);
+	dispd_detach_session(session);
+#endif
+
+	return 0;
+}
