static void* dvbsub_thread(void* /*arg*/)
{
	struct timespec restartWait;
	struct timeval now;
	set_threadname("dvbsub:main");

	sub_debug.print(Debug::VERBOSE, "%s started\n", __FUNCTION__);
	if (!dvbSubtitleConverter)
		dvbSubtitleConverter = new cDvbSubtitleConverter;

	int timeout = 1000000;
#if HAVE_SPARK_HARDWARE || HAVE_DUCKBOX_HARDWARE
	CFrameBuffer *fb = CFrameBuffer::getInstance();
	int xres = fb->getScreenWidth(true);
	int yres = fb->getScreenHeight(true);
	int clr_x0 = xres, clr_y0 = yres, clr_x1 = 0, clr_y1 = 0;
	uint32_t colortable[256];
	memset(colortable, 0, sizeof(colortable));
	uint32_t last_color = 0;
#endif

	while(dvbsub_running) {
#if HAVE_SPARK_HARDWARE || HAVE_DUCKBOX_HARDWARE
		if (ass_track) {
			usleep(100000); // FIXME ... should poll instead

			OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(ass_mutex);

			if (!ass_track)
				continue;

			if (ass_size != sub_font_size) {
				ass_size = sub_font_size;
				ass_set_font_scale(ass_renderer, ((double) ass_size)/ASS_CUSTOM_FONT_SIZE);
			}

			int detect_change = 0;
			int64_t pts;
			getPlayerPts(&pts);
			ASS_Image *image = ass_render_frame(ass_renderer, ass_track, pts/90, &detect_change);
			if (detect_change) {
				if (clr_x1 && clr_y1) {
					fb->paintBox(clr_x0, clr_y0, clr_x1 + 1, clr_y1 + 1, 0);
					clr_x0 = xres;
					clr_y0 = yres;
					clr_x1 = clr_y1 = 0;
				}

				while (image) {
					if (last_color != image->color) {
						last_color = image->color;
						uint32_t c = last_color >> 8, a = 255 - (last_color & 0xff);
						for (int i = 0; i < 256; i++) {
							uint32_t k = (a * i) >> 8;
							colortable[i] = k ? (c | (k << 24)) : 0;
						}
					}
					if (image->w && image->h && image->dst_x > -1 && image->dst_x + image->w < xres && image->dst_y > -1 && image->dst_y + image->h < yres) {
						if (image->dst_x < clr_x0)
							clr_x0 = image->dst_x;
						if (image->dst_y < clr_y0)
							clr_y0 = image->dst_y;
						if (image->dst_x + image->w > clr_x1)
							clr_x1 = image->dst_x + image->w;
						if (image->dst_y + image->h > clr_y1)
							clr_y1 = image->dst_y + image->h;

						uint32_t *lfb = fb->getFrameBufferPointer() + image->dst_x + xres * image->dst_y;
						unsigned char *bm = image->bitmap;
						int bm_add = image->stride - image->w;
						int lfb_add = xres - image->w;
						for (int y = 0; y < image->h; y++) {
							for (int x = 0; x < image->w; x++) {
								if (*bm)
									*lfb = colortable[*bm];
								lfb++, bm++;
							}
							lfb += lfb_add;
							bm += bm_add;
						}
					}
					image = image->next;
				}
				fb->getInstance()->blit();
			}
			continue;
		} else {
			if (clr_x1 && clr_y1) {
static void *ass_reader_thread(void *)
{
	set_threadname("ass_reader_thread");
	while (!sem_wait(&ass_sem)) {
		if (!ass_reader_running)
			break;

		ass_data *a = (ass_data *) ass_queue.pop();
		if (!a) {
			if (!ass_reader_running)
				break;
			continue;
		}

		OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(ass_mutex);
		std::map<int,ASS_Track*>::iterator it = ass_map.find(a->pid);
		ASS_Track *track;
		if (it == ass_map.end()) {
			CFrameBuffer *fb = CFrameBuffer::getInstance();
			int xres = fb->getScreenWidth(true);
			int yres = fb->getScreenHeight(true);
			if (!ass_library) {
				ass_library = ass_library_init();
				ass_set_extract_fonts(ass_library, 1);
				ass_set_style_overrides(ass_library, NULL);
				ass_renderer = ass_renderer_init(ass_library);
				ass_set_frame_size(ass_renderer, xres, yres);
				ass_set_margins(ass_renderer, 3 * yres / 100, 3 * yres / 100, 3 * xres / 100, 3 * xres / 100);
				ass_set_use_margins(ass_renderer, 1);
				ass_set_hinting(ass_renderer, ASS_HINTING_LIGHT);
				ass_set_aspect_ratio(ass_renderer, 1.0, 1.0);
				ass_font = *sub_font_file;
				ass_set_fonts(ass_renderer, ass_font.c_str(), "Arial", 0, NULL, 1);
			}
			track = ass_new_track(ass_library);
			track->PlayResX = xres;
			track->PlayResY = yres;
			ass_size = sub_font_size;
			ass_set_font_scale(ass_renderer, ((double) ass_size)/ASS_CUSTOM_FONT_SIZE);
			if (a->c->subtitle_header) {
				std::string ass_hdr = ass_subtitle_header_default();
				if (ass_hdr.compare((char*) a->c->subtitle_header)) {
					ass_process_codec_private(track, (char *) a->c->subtitle_header, a->c->subtitle_header_size);
				} else {
					// This is the FFMPEG default ASS header. Use something more suitable instead:
					ass_hdr = ass_subtitle_header_custom();
					ass_process_codec_private(track, (char *) ass_hdr.c_str(), ass_hdr.length());
				}
			}
			ass_map[a->pid] = track;
			if (a->pid == dvbsub_pid)
				ass_track = track;
//fprintf(stderr, "### got subtitle track %d, subtitle header: \n---\n%s\n---\n", pid, c->subtitle_header);
		} else
			track = it->second;
		for (unsigned int i = 0; i < a->sub.num_rects; i++)
			if (a->sub.rects[i]->ass)
				ass_process_data(track, a->sub.rects[i]->ass, strlen(a->sub.rects[i]->ass));
		avsubtitle_free(&a->sub);
		delete a;
	}
	ass_reader_running = false;
	pthread_exit(NULL);
}