mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-29 16:31:11 +02:00
our current experimental Neutrino branch
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@27 e54a6e83-5905-42d5-8d5c-058d10e6a962
This commit is contained in:
41
lib/libdvbsub/Debug.cpp
Normal file
41
lib/libdvbsub/Debug.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include "Debug.hpp"
|
||||
|
||||
Debug::Debug() :
|
||||
file_(""), level_(0), fp_(stdout)
|
||||
{
|
||||
}
|
||||
|
||||
Debug::~Debug()
|
||||
{
|
||||
if (fp_ && fp_ != stdout) {
|
||||
fclose(fp_);
|
||||
}
|
||||
}
|
||||
|
||||
void Debug::set_level(int level)
|
||||
{
|
||||
level_ = level;
|
||||
}
|
||||
|
||||
FILE* Debug::set_file(char* file)
|
||||
{
|
||||
FILE* fp = fopen(file, "a");
|
||||
if (!fp) {
|
||||
return NULL;
|
||||
}
|
||||
fp_ = fp;
|
||||
return fp_;
|
||||
}
|
||||
|
||||
void Debug::print(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
// if (level < level_) {
|
||||
vfprintf(fp_, fmt, argp);
|
||||
// }
|
||||
va_end(argp);
|
||||
}
|
||||
|
28
lib/libdvbsub/Debug.hpp
Normal file
28
lib/libdvbsub/Debug.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef DEBUG_HPP_
|
||||
#define DEBUG_HPP_
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
class Debug {
|
||||
public:
|
||||
Debug();
|
||||
~Debug();
|
||||
|
||||
void set_level(int level);
|
||||
FILE* set_file(char* file);
|
||||
void print(int level, const char *fmt, ...);
|
||||
|
||||
static const int ERROR = 0;
|
||||
static const int INFO = 1;
|
||||
static const int VERBOSE = 2;
|
||||
|
||||
private:
|
||||
char* file_;
|
||||
int level_;
|
||||
FILE* fp_;
|
||||
|
||||
};
|
||||
|
||||
extern Debug sub_debug;
|
||||
|
||||
#endif
|
11
lib/libdvbsub/Makefile.am
Normal file
11
lib/libdvbsub/Makefile.am
Normal file
@@ -0,0 +1,11 @@
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/src/zapit/include \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/lib/libcoolstream
|
||||
|
||||
AM_CPPFLAGS = -fno-rtti -fno-exceptions
|
||||
|
||||
noinst_LIBRARIES = libdvbsub.a
|
||||
|
||||
libdvbsub_a_SOURCES = dvbsub.cpp dvbsubtitle.cpp \
|
||||
tools.cpp osd.cpp PacketQueue.cpp helpers.cpp Debug.cpp
|
47
lib/libdvbsub/PacketQueue.cpp
Normal file
47
lib/libdvbsub/PacketQueue.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include <cstdlib>
|
||||
#include <pthread.h>
|
||||
#include <list>
|
||||
#include "Debug.hpp"
|
||||
#include "PacketQueue.hpp"
|
||||
|
||||
PacketQueue::PacketQueue()
|
||||
{
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
}
|
||||
|
||||
PacketQueue::~PacketQueue()
|
||||
{
|
||||
while (queue.begin() != queue.end()) {
|
||||
delete[] queue.front();
|
||||
queue.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void PacketQueue::push(uint8_t* data)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
queue.push_back(data);
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
uint8_t* PacketQueue::pop()
|
||||
{
|
||||
uint8_t* retval;
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
retval = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
size_t PacketQueue::size()
|
||||
{
|
||||
return queue.size();
|
||||
}
|
||||
|
20
lib/libdvbsub/PacketQueue.hpp
Normal file
20
lib/libdvbsub/PacketQueue.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef PACKET_QUEUE_H_
|
||||
#define PACKET_QUEUE_H_
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <list>
|
||||
|
||||
class PacketQueue {
|
||||
public:
|
||||
PacketQueue();
|
||||
~PacketQueue();
|
||||
void push(uint8_t* data);
|
||||
uint8_t* pop();
|
||||
size_t size();
|
||||
|
||||
private:
|
||||
std::list<uint8_t*> queue;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
|
||||
#endif
|
369
lib/libdvbsub/dvbsub.cpp
Normal file
369
lib/libdvbsub/dvbsub.cpp
Normal file
@@ -0,0 +1,369 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include <dmx_cs.h>
|
||||
|
||||
#include "Debug.hpp"
|
||||
#include "PacketQueue.hpp"
|
||||
#include "semaphore.h"
|
||||
#include "reader_thread.hpp"
|
||||
#include "dvbsub_thread.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "dvbsubtitle.h"
|
||||
|
||||
#define Log2File printf
|
||||
#define RECVBUFFER_STEPSIZE 1024
|
||||
|
||||
enum {NOERROR, NETWORK, DENIED, NOSERVICE, BOXTYPE, THREAD, ABOUT};
|
||||
enum {GET_VOLUME, SET_VOLUME, SET_MUTE, SET_CHANNEL};
|
||||
|
||||
Debug sub_debug;
|
||||
static PacketQueue packet_queue;
|
||||
//sem_t event_semaphore;
|
||||
|
||||
static pthread_t threadReader;
|
||||
static pthread_t threadDvbsub;
|
||||
|
||||
static pthread_cond_t readerCond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t readerMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t packetCond = PTHREAD_COND_INITIALIZER;
|
||||
static pthread_mutex_t packetMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static int reader_running;
|
||||
static int dvbsub_running;
|
||||
static int dvbsub_paused = true;
|
||||
static int dvbsub_pid;
|
||||
static int dvbsub_stopped;
|
||||
|
||||
cDvbSubtitleConverter *dvbSubtitleConverter;
|
||||
|
||||
int dvbsub_init() {
|
||||
int trc;
|
||||
|
||||
sub_debug.set_level(42);
|
||||
|
||||
reader_running = true;
|
||||
// reader-Thread starten
|
||||
trc = pthread_create(&threadReader, 0, reader_thread, (void *) NULL);
|
||||
if (trc) {
|
||||
fprintf(stderr, "[dvb-sub] failed to create reader-thread (rc=%d)\n", trc);
|
||||
reader_running = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dvbsub_running = true;
|
||||
// subtitle decoder-Thread starten
|
||||
trc = pthread_create(&threadDvbsub, 0, dvbsub_thread, NULL);
|
||||
if (trc) {
|
||||
fprintf(stderr, "[dvb-sub] failed to create dvbsub-thread (rc=%d)\n", trc);
|
||||
dvbsub_running = false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int dvbsub_pause()
|
||||
{
|
||||
if(reader_running) {
|
||||
if(dvbSubtitleConverter) {
|
||||
dvbSubtitleConverter->Pause(true);
|
||||
}
|
||||
dvbsub_paused = true;
|
||||
printf("[dvb-sub] paused\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dvbsub_start(int pid)
|
||||
{
|
||||
if(!dvbsub_paused && (pid == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if(pid) {
|
||||
if(pid != dvbsub_pid)
|
||||
dvbsub_pause();
|
||||
dvbsub_pid = pid;
|
||||
}
|
||||
|
||||
while(!dvbsub_stopped)
|
||||
usleep(10);
|
||||
|
||||
if(dvbsub_pid > 0) {
|
||||
dvbsub_paused = false;
|
||||
dvbSubtitleConverter->Pause(false);
|
||||
pthread_mutex_lock(&readerMutex);
|
||||
pthread_cond_broadcast(&readerCond);
|
||||
pthread_mutex_unlock(&readerMutex);
|
||||
printf("[dvb-sub] started with pid 0x%x\n", pid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dvbsub_stop()
|
||||
{
|
||||
dvbsub_pid = 0;
|
||||
if(reader_running) {
|
||||
dvbsub_pause();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dvbsub_getpid()
|
||||
{
|
||||
return dvbsub_pid;
|
||||
}
|
||||
|
||||
int dvbsub_close()
|
||||
{
|
||||
if(threadReader) {
|
||||
dvbsub_pause();
|
||||
reader_running = false;
|
||||
pthread_mutex_lock(&readerMutex);
|
||||
pthread_cond_broadcast(&readerCond);
|
||||
pthread_mutex_unlock(&readerMutex);
|
||||
pthread_join(threadReader, NULL);
|
||||
threadReader = NULL;
|
||||
}
|
||||
if(threadDvbsub) {
|
||||
dvbsub_running = false;
|
||||
pthread_join(threadDvbsub, NULL);
|
||||
threadDvbsub = NULL;
|
||||
}
|
||||
printf("[dvb-sub] stopped\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static cDemux * dmx;
|
||||
|
||||
void* reader_thread(void *arg)
|
||||
{
|
||||
uint8_t tmp[16]; /* actually 6 should be enough */
|
||||
int count;
|
||||
int len;
|
||||
uint16_t packlen;
|
||||
uint8_t* buf;
|
||||
|
||||
dmx = new cDemux(0);
|
||||
dmx->Open(DMX_PES_CHANNEL, NULL, 64*1024);
|
||||
|
||||
while (reader_running) {
|
||||
if(dvbsub_paused) {
|
||||
sub_debug.print(Debug::VERBOSE, "%s stopped\n", __FUNCTION__);
|
||||
dmx->Stop();
|
||||
dvbsub_stopped = 1;
|
||||
pthread_mutex_lock(&readerMutex );
|
||||
int ret = pthread_cond_wait(&readerCond, &readerMutex);
|
||||
pthread_mutex_unlock(&readerMutex);
|
||||
if (ret) {
|
||||
sub_debug.print(Debug::VERBOSE, "pthread_cond_timedwait fails with %d\n", ret);
|
||||
}
|
||||
if(reader_running) {
|
||||
dmx->pesFilter(dvbsub_pid);
|
||||
dmx->Start();
|
||||
sub_debug.print(Debug::VERBOSE, "%s started: pid 0x%x\n", __FUNCTION__, dvbsub_pid);
|
||||
dvbsub_stopped = 0;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Wait for pes sync */
|
||||
len = 0;
|
||||
count = 0;
|
||||
int tosync = 0;
|
||||
|
||||
while (!dvbsub_paused) {
|
||||
//len = read(fd, &tmp[count], 3-count);
|
||||
len = dmx->Read(&tmp[count], 3-count, 1000);
|
||||
//printf("reader: fd = %d, len = %d\n", fd, len);
|
||||
if(len <= 0)
|
||||
continue;
|
||||
else if (len == (3-count)) {
|
||||
if ( (tmp[0] == 0x00) &&
|
||||
(tmp[1] == 0x00) &&
|
||||
(tmp[2] == 0x01)) {
|
||||
count = 3;
|
||||
break;
|
||||
}
|
||||
tosync += len;
|
||||
} else {
|
||||
count += len;
|
||||
tosync += len;
|
||||
continue;
|
||||
}
|
||||
tmp[0] = tmp[1];
|
||||
tmp[1] = tmp[2];
|
||||
count = 2;
|
||||
} // while (!dvbsub_paused)
|
||||
if(tosync)
|
||||
printf("[subtitles] sync after %d bytes\n", tosync);
|
||||
/* read stream id & length */
|
||||
while ((count < 6) && !dvbsub_paused) {
|
||||
//printf("try to read 6-count = %d\n", 6-count);
|
||||
//len = read(fd, &tmp[count], 6-count);
|
||||
len = dmx->Read(&tmp[count], 6-count, 1000);
|
||||
if (len < 0) {
|
||||
continue;
|
||||
} else {
|
||||
count += len;
|
||||
}
|
||||
}
|
||||
|
||||
packlen = getbits(tmp, 4*8, 16) + 6;
|
||||
|
||||
buf = new uint8_t[packlen];
|
||||
|
||||
/* TODO: Throws an exception on out of memory */
|
||||
|
||||
/* copy tmp[0..5] => buf[0..5] */
|
||||
*(uint32_t*)buf = *(uint32_t*)tmp;
|
||||
((uint16_t*)buf)[2] = ((uint16_t*)tmp)[2];
|
||||
|
||||
/* read rest of the packet */
|
||||
while((count < packlen) && !dvbsub_paused) {
|
||||
//len = read(fd, buf+count, packlen-count);
|
||||
len = dmx->Read(buf+count, packlen-count, 1000);
|
||||
if (len < 0) {
|
||||
continue;
|
||||
} else {
|
||||
count += len;
|
||||
}
|
||||
}
|
||||
if(!dvbsub_paused) {
|
||||
printf("[subtitles] adding packet, len %d\n", count);
|
||||
/* Packet now in memory */
|
||||
packet_queue.push(buf);
|
||||
/* TODO: allocation exception */
|
||||
// wake up dvb thread
|
||||
pthread_mutex_lock(&packetMutex);
|
||||
pthread_cond_broadcast(&packetCond);
|
||||
pthread_mutex_unlock(&packetMutex);
|
||||
} else {
|
||||
delete[] buf;
|
||||
buf=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dmx->Stop();
|
||||
delete dmx;
|
||||
dmx = NULL;
|
||||
|
||||
sub_debug.print(Debug::VERBOSE, "%s shutdown\n", __FUNCTION__);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void* dvbsub_thread(void* arg)
|
||||
{
|
||||
struct timespec restartWait;
|
||||
struct timeval now;
|
||||
|
||||
sub_debug.print(Debug::VERBOSE, "%s started\n", __FUNCTION__);
|
||||
if (!dvbSubtitleConverter)
|
||||
dvbSubtitleConverter = new cDvbSubtitleConverter;
|
||||
|
||||
while(dvbsub_running) {
|
||||
uint8_t* packet;
|
||||
int64_t pts;
|
||||
int pts_dts_flag;
|
||||
int dataoffset;
|
||||
int packlen;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
TIMEVAL_TO_TIMESPEC(&now, &restartWait);
|
||||
restartWait.tv_sec += 1;
|
||||
|
||||
int ret = 0;
|
||||
if(packet_queue.size() == 0) {
|
||||
pthread_mutex_lock( &packetMutex );
|
||||
ret = pthread_cond_timedwait( &packetCond, &packetMutex, &restartWait );
|
||||
pthread_mutex_unlock( &packetMutex );
|
||||
}
|
||||
if (ret == ETIMEDOUT)
|
||||
{
|
||||
dvbSubtitleConverter->Action();
|
||||
continue;
|
||||
}
|
||||
else if (ret == EINTR)
|
||||
{
|
||||
sub_debug.print(Debug::VERBOSE, "pthread_cond_timedwait fails with %s\n", strerror(errno));
|
||||
}
|
||||
sub_debug.print(Debug::VERBOSE, "\nPES: Wakeup, queue size %d\n", packet_queue.size());
|
||||
if(dvbsub_paused) {
|
||||
do {
|
||||
packet = packet_queue.pop();
|
||||
if(packet)
|
||||
delete[] packet;
|
||||
} while(packet);
|
||||
continue;
|
||||
}
|
||||
packet = packet_queue.pop();
|
||||
if (!packet) {
|
||||
sub_debug.print(Debug::VERBOSE, "Error no packet found\n");
|
||||
continue;
|
||||
}
|
||||
packlen = (packet[4] << 8 | packet[5]) + 6;
|
||||
|
||||
/* Get PTS */
|
||||
pts_dts_flag = getbits(packet, 7*8, 2);
|
||||
if ((pts_dts_flag == 2) || (pts_dts_flag == 3)) {
|
||||
pts = (uint64_t)getbits(packet, 9*8+4, 3) << 30; /* PTS[32..30] */
|
||||
pts |= getbits(packet, 10*8, 15) << 15; /* PTS[29..15] */
|
||||
pts |= getbits(packet, 12*8, 15); /* PTS[14..0] */
|
||||
} else {
|
||||
pts = 0;
|
||||
}
|
||||
|
||||
dataoffset = packet[8] + 8 + 1;
|
||||
if (packet[dataoffset] != 0x20) {
|
||||
sub_debug.print(Debug::VERBOSE, "Not a dvb subtitle packet, discard it (len %d)\n", packlen);
|
||||
for(int i = 0; i < packlen; i++)
|
||||
printf("%02X ", packet[i]);
|
||||
printf("\n");
|
||||
goto next_round;
|
||||
}
|
||||
|
||||
sub_debug.print(Debug::VERBOSE, "PES packet: len %d PTS=%Ld (%02d:%02d:%02d.%d)\n",
|
||||
packlen, pts, (int)(pts/324000000), (int)((pts/5400000)%60),
|
||||
(int)((pts/90000)%60), (int)(pts%90000));
|
||||
|
||||
if (packlen <= dataoffset + 3) {
|
||||
sub_debug.print(Debug::INFO, "Packet too short, discard\n");
|
||||
goto next_round;
|
||||
}
|
||||
|
||||
if (packet[dataoffset + 2] == 0x0f) {
|
||||
dvbSubtitleConverter->Convert(&packet[dataoffset + 2],
|
||||
packlen - (dataoffset + 2), pts);
|
||||
} else {
|
||||
sub_debug.print(Debug::INFO, "End_of_PES is missing\n");
|
||||
}
|
||||
dvbSubtitleConverter->Action();
|
||||
|
||||
next_round:
|
||||
delete[] packet;
|
||||
}
|
||||
|
||||
delete dvbSubtitleConverter;
|
||||
|
||||
sub_debug.print(Debug::VERBOSE, "%s shutdown\n", __FUNCTION__);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
void dvbsub_get_stc(int64_t * STC)
|
||||
{
|
||||
if(dmx)
|
||||
dmx->getSTC(STC);
|
||||
}
|
8
lib/libdvbsub/dvbsub_thread.hpp
Normal file
8
lib/libdvbsub/dvbsub_thread.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef DVBSUB_THREAD_HPP_
|
||||
#define DVBSUB_THREAD_HPP_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
void* dvbsub_thread(void* arg);
|
||||
|
||||
#endif
|
1200
lib/libdvbsub/dvbsubtitle.cpp
Normal file
1200
lib/libdvbsub/dvbsubtitle.cpp
Normal file
File diff suppressed because it is too large
Load Diff
51
lib/libdvbsub/dvbsubtitle.h
Normal file
51
lib/libdvbsub/dvbsubtitle.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* dvbsubtitle.h: DVB subtitles
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* Original author: Marco Schl<68><6C>ler <marco@lordzodiac.de>
|
||||
*
|
||||
* $Id: dvbsubtitle.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBSUBTITLE_H
|
||||
#define __DVBSUBTITLE_H
|
||||
|
||||
#include "osd.h"
|
||||
#include "tools.h"
|
||||
|
||||
class cDvbSubtitlePage;
|
||||
class cDvbSubtitleAssembler; // for legacy PES recordings
|
||||
class cDvbSubtitleBitmaps;
|
||||
|
||||
class cDvbSubtitleConverter /*: public cThread */{
|
||||
private:
|
||||
static int setupLevel;
|
||||
cDvbSubtitleAssembler *dvbSubtitleAssembler;
|
||||
// cOsd *osd;
|
||||
cList<cDvbSubtitlePage> *pages;
|
||||
cList<cDvbSubtitleBitmaps> *bitmaps;
|
||||
tColor yuv2rgb(int Y, int Cb, int Cr);
|
||||
bool AssertOsd(void);
|
||||
int ExtractSegment(const uchar *Data, int Length, int64_t Pts);
|
||||
void FinishPage(cDvbSubtitlePage *Page);
|
||||
bool running;
|
||||
pthread_mutex_t mutex;
|
||||
public:
|
||||
cDvbSubtitleConverter(void);
|
||||
virtual ~cDvbSubtitleConverter();
|
||||
void Action(void);
|
||||
void Reset(void);
|
||||
void Clear(void);
|
||||
void Pause(bool pause);
|
||||
void Lock();
|
||||
void Unlock();
|
||||
int ConvertFragments(const uchar *Data, int Length, int64_t pts); // for legacy PES recordings
|
||||
int Convert(const uchar *Data, int Length, int64_t pts);
|
||||
static void SetupChanged(void);
|
||||
bool Running() { return running; };
|
||||
};
|
||||
|
||||
|
||||
#endif //__DVBSUBTITLE_H
|
50
lib/libdvbsub/helpers.cpp
Normal file
50
lib/libdvbsub/helpers.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
/* Max 24 bit */
|
||||
uint32_t getbits(const uint8_t* buf, uint32_t offset, uint8_t len)
|
||||
{
|
||||
const uint8_t* a = buf + (offset / 8);
|
||||
uint32_t retval = 0;
|
||||
uint32_t mask = 1;
|
||||
|
||||
retval = ((*(a)<<8) | *(a+1));
|
||||
mask <<= len;
|
||||
|
||||
if (len > 8) {
|
||||
retval <<= 8;
|
||||
retval |= *(a+2);
|
||||
len -= 8;
|
||||
}
|
||||
if (len > 8) {
|
||||
retval <<= 8;
|
||||
retval |= *(a+3);
|
||||
len -= 8;
|
||||
}
|
||||
if (len > 8) {
|
||||
unsigned long long tmp = retval << 8;
|
||||
tmp |= *(a+4);
|
||||
tmp >>= ((8-(offset%8)) + (8-(len)));
|
||||
return tmp & (mask -1);
|
||||
}
|
||||
|
||||
retval >>= ((8-(offset%8)) + (8-len));
|
||||
return retval & (mask -1);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void hexdump(uint8_t* buf)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < 16 ; i++) {
|
||||
debug.print(Debug::DEBUG, "%02x ", buf[i]);
|
||||
}
|
||||
debug.print(Debug::DEBUG, "\n");
|
||||
for (i = 16 ; i < 32 ; i++) {
|
||||
debug.print(Debug::DEBUG, "%02x ", buf[i]);
|
||||
}
|
||||
debug.print(Debug::DEBUG, "\n");
|
||||
}
|
||||
|
||||
#endif
|
14
lib/libdvbsub/helpers.hpp
Normal file
14
lib/libdvbsub/helpers.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef HELPERS_H_
|
||||
#define HELPERS_H_
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
uint32_t getbits(const uint8_t* buf, uint32_t offset, uint8_t len);
|
||||
void hexdump(uint8_t* buf);
|
||||
|
||||
#define likely(x) __builtin_expect((x),1)
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
|
||||
#endif
|
287
lib/libdvbsub/osd.cpp
Normal file
287
lib/libdvbsub/osd.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* osd.c: Abstract On Screen Display layer
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osd.cpp,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $
|
||||
*/
|
||||
|
||||
#include "dvbsubtitle.h"
|
||||
//#include "device.h"
|
||||
|
||||
#define PAGE_COMPOSITION_SEGMENT 0x10
|
||||
#define REGION_COMPOSITION_SEGMENT 0x11
|
||||
#define CLUT_DEFINITION_SEGMENT 0x12
|
||||
#define OBJECT_DATA_SEGMENT 0x13
|
||||
#define END_OF_DISPLAY_SET_SEGMENT 0x80
|
||||
|
||||
// --- cPalette --------------------------------------------------------------
|
||||
|
||||
cPalette::cPalette(int Bpp)
|
||||
{
|
||||
SetBpp(Bpp);
|
||||
SetAntiAliasGranularity(10, 10);
|
||||
}
|
||||
|
||||
void cPalette::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
|
||||
{
|
||||
if (FixedColors >= MAXNUMCOLORS || BlendColors == 0)
|
||||
antiAliasGranularity = MAXNUMCOLORS - 1;
|
||||
else {
|
||||
int ColorsForBlending = MAXNUMCOLORS - FixedColors;
|
||||
int ColorsPerBlend = ColorsForBlending / BlendColors + 2; // +2 = the full foreground and background colors, which are amoung the fixed colors
|
||||
antiAliasGranularity = double(MAXNUMCOLORS - 1) / (ColorsPerBlend - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void cPalette::Reset(void)
|
||||
{
|
||||
numColors = 0;
|
||||
modified = false;
|
||||
}
|
||||
|
||||
int cPalette::Index(tColor Color)
|
||||
{
|
||||
// Check if color is already defined:
|
||||
for (int i = 0; i < numColors; i++) {
|
||||
if (color[i] == Color)
|
||||
return i;
|
||||
}
|
||||
// No exact color, try a close one:
|
||||
int i = ClosestColor(Color, 4);
|
||||
if (i >= 0)
|
||||
return i;
|
||||
// No close one, try to define a new one:
|
||||
if (numColors < maxColors) {
|
||||
color[numColors++] = Color;
|
||||
modified = true;
|
||||
return numColors - 1;
|
||||
}
|
||||
// Out of colors, so any close color must do:
|
||||
return ClosestColor(Color);
|
||||
}
|
||||
|
||||
void cPalette::SetBpp(int Bpp)
|
||||
{
|
||||
bpp = Bpp;
|
||||
maxColors = 1 << bpp;
|
||||
Reset();
|
||||
}
|
||||
|
||||
void cPalette::SetColor(int Index, tColor Color)
|
||||
{
|
||||
if (Index < maxColors) {
|
||||
if (numColors <= Index) {
|
||||
numColors = Index + 1;
|
||||
modified = true;
|
||||
}
|
||||
else
|
||||
modified |= color[Index] != Color;
|
||||
color[Index] = Color;
|
||||
}
|
||||
}
|
||||
|
||||
const tColor *cPalette::Colors(int &NumColors) const
|
||||
{
|
||||
NumColors = numColors;
|
||||
return numColors ? color : NULL;
|
||||
}
|
||||
|
||||
void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg)
|
||||
{
|
||||
for (int i = 0; i < Palette.numColors; i++) {
|
||||
tColor Color = Palette.color[i];
|
||||
if (ColorFg || ColorBg) {
|
||||
switch (i) {
|
||||
case 0: Color = ColorBg; break;
|
||||
case 1: Color = ColorFg; break;
|
||||
}
|
||||
}
|
||||
int n = Index(Color);
|
||||
if (Indexes)
|
||||
(*Indexes)[i] = n;
|
||||
}
|
||||
}
|
||||
|
||||
void cPalette::Replace(const cPalette &Palette)
|
||||
{
|
||||
for (int i = 0; i < Palette.numColors; i++)
|
||||
SetColor(i, Palette.color[i]);
|
||||
numColors = Palette.numColors;
|
||||
antiAliasGranularity = Palette.antiAliasGranularity;
|
||||
}
|
||||
|
||||
tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const
|
||||
{
|
||||
if (antiAliasGranularity > 0)
|
||||
Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity);
|
||||
int Af = (ColorFg & 0xFF000000) >> 24;
|
||||
int Rf = (ColorFg & 0x00FF0000) >> 16;
|
||||
int Gf = (ColorFg & 0x0000FF00) >> 8;
|
||||
int Bf = (ColorFg & 0x000000FF);
|
||||
int Ab = (ColorBg & 0xFF000000) >> 24;
|
||||
int Rb = (ColorBg & 0x00FF0000) >> 16;
|
||||
int Gb = (ColorBg & 0x0000FF00) >> 8;
|
||||
int Bb = (ColorBg & 0x000000FF);
|
||||
int A = (Ab + (Af - Ab) * Level / 0xFF) & 0xFF;
|
||||
int R = (Rb + (Rf - Rb) * Level / 0xFF) & 0xFF;
|
||||
int G = (Gb + (Gf - Gb) * Level / 0xFF) & 0xFF;
|
||||
int B = (Bb + (Bf - Bb) * Level / 0xFF) & 0xFF;
|
||||
return (A << 24) | (R << 16) | (G << 8) | B;
|
||||
}
|
||||
|
||||
int cPalette::ClosestColor(tColor Color, int MaxDiff) const
|
||||
{
|
||||
int n = 0;
|
||||
int d = INT_MAX;
|
||||
int A1 = (Color & 0xFF000000) >> 24;
|
||||
int R1 = (Color & 0x00FF0000) >> 16;
|
||||
int G1 = (Color & 0x0000FF00) >> 8;
|
||||
int B1 = (Color & 0x000000FF);
|
||||
for (int i = 0; i < numColors; i++) {
|
||||
int A2 = (color[i] & 0xFF000000) >> 24;
|
||||
int R2 = (color[i] & 0x00FF0000) >> 16;
|
||||
int G2 = (color[i] & 0x0000FF00) >> 8;
|
||||
int B2 = (color[i] & 0x000000FF);
|
||||
int diff = (abs(A1 - A2) << 1) + (abs(R1 - R2) << 1) + (abs(G1 - G2) << 1) + (abs(B1 - B2) << 1);
|
||||
if (diff < d) {
|
||||
d = diff;
|
||||
n = i;
|
||||
}
|
||||
}
|
||||
return d <= MaxDiff ? n : -1;
|
||||
}
|
||||
|
||||
cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0)
|
||||
:cPalette(Bpp)
|
||||
{
|
||||
bitmap = NULL;
|
||||
x0 = X0;
|
||||
y0 = Y0;
|
||||
SetSize(Width, Height);
|
||||
}
|
||||
|
||||
cBitmap::~cBitmap()
|
||||
{
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
void cBitmap::SetIndex(int x, int y, tIndex Index)
|
||||
{
|
||||
if (bitmap) {
|
||||
if (0 <= x && x < width && 0 <= y && y < height) {
|
||||
if (bitmap[width * y + x] != Index) {
|
||||
bitmap[width * y + x] = Index;
|
||||
if (dirtyX1 > x) dirtyX1 = x;
|
||||
if (dirtyY1 > y) dirtyY1 = y;
|
||||
if (dirtyX2 < x) dirtyX2 = x;
|
||||
if (dirtyY2 < y) dirtyY2 = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cBitmap::SetSize(int Width, int Height)
|
||||
{
|
||||
if (bitmap && Width == width && Height == height)
|
||||
return;
|
||||
width = Width;
|
||||
height = Height;
|
||||
free(bitmap);
|
||||
bitmap = NULL;
|
||||
dirtyX1 = 0;
|
||||
dirtyY1 = 0;
|
||||
dirtyX2 = width - 1;
|
||||
dirtyY2 = height - 1;
|
||||
if (width > 0 && height > 0) {
|
||||
bitmap = MALLOC(tIndex, width * height);
|
||||
if (bitmap)
|
||||
memset(bitmap, 0x00, width * height);
|
||||
else
|
||||
esyslog("ERROR: can't allocate bitmap!");
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height);
|
||||
}
|
||||
|
||||
void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
|
||||
{
|
||||
if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
|
||||
if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
|
||||
Reset();
|
||||
x -= x0;
|
||||
y -= y0;
|
||||
if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
|
||||
Replace(Bitmap);
|
||||
for (int ix = 0; ix < Bitmap.width; ix++) {
|
||||
for (int iy = 0; iy < Bitmap.height; iy++) {
|
||||
if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
|
||||
SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
tIndexes Indexes;
|
||||
Take(Bitmap, &Indexes, ColorFg, ColorBg);
|
||||
for (int ix = 0; ix < Bitmap.width; ix++) {
|
||||
for (int iy = 0; iy < Bitmap.height; iy++) {
|
||||
if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
|
||||
SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool cBitmap::Contains(int x, int y) const
|
||||
{
|
||||
x -= x0;
|
||||
y -= y0;
|
||||
return 0 <= x && x < width && 0 <= y && y < height;
|
||||
}
|
||||
|
||||
bool cBitmap::Covers(int x1, int y1, int x2, int y2) const
|
||||
{
|
||||
x1 -= x0;
|
||||
y1 -= y0;
|
||||
x2 -= x0;
|
||||
y2 -= y0;
|
||||
return x1 <= 0 && y1 <= 0 && x2 >= width - 1 && y2 >= height - 1;
|
||||
}
|
||||
|
||||
bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const
|
||||
{
|
||||
x1 -= x0;
|
||||
y1 -= y0;
|
||||
x2 -= x0;
|
||||
y2 -= y0;
|
||||
return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height);
|
||||
}
|
||||
|
||||
bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2)
|
||||
{
|
||||
if (dirtyX2 >= 0) {
|
||||
x1 = dirtyX1;
|
||||
y1 = dirtyY1;
|
||||
x2 = dirtyX2;
|
||||
y2 = dirtyY2;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cBitmap::Clean(void)
|
||||
{
|
||||
dirtyX1 = width;
|
||||
dirtyY1 = height;
|
||||
dirtyX2 = -1;
|
||||
dirtyY2 = -1;
|
||||
}
|
||||
|
||||
const tIndex *cBitmap::Data(int x, int y)
|
||||
{
|
||||
return &bitmap[y * width + x];
|
||||
}
|
||||
|
147
lib/libdvbsub/osd.h
Normal file
147
lib/libdvbsub/osd.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* osd.h: Abstract On Screen Display layer
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* Original author: Marco Schl<68><6C>ler <marco@lordzodiac.de>
|
||||
*
|
||||
* $Id: osd.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $
|
||||
*/
|
||||
|
||||
#ifndef __OSD_H
|
||||
#define __OSD_H
|
||||
|
||||
#include "tools.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define MAXNUMCOLORS 256
|
||||
|
||||
typedef uint32_t tColor;
|
||||
typedef unsigned char tIndex;
|
||||
|
||||
class cPalette {
|
||||
private:
|
||||
tColor color[MAXNUMCOLORS];
|
||||
int bpp;
|
||||
int maxColors, numColors;
|
||||
bool modified;
|
||||
double antiAliasGranularity;
|
||||
protected:
|
||||
typedef tIndex tIndexes[MAXNUMCOLORS];
|
||||
public:
|
||||
cPalette(int Bpp = 8);
|
||||
///< Initializes the palette with the given color depth.
|
||||
void SetAntiAliasGranularity(uint FixedColors, uint BlendColors);
|
||||
///< Allows the system to optimize utilization of the limited color
|
||||
///< palette entries when generating blended colors for anti-aliasing.
|
||||
///< FixedColors is the maximum number of colors used, and BlendColors
|
||||
///< is the maximum number of foreground/background color combinations
|
||||
///< used with anti-aliasing. If this function is not called with
|
||||
///< useful values, the palette may be filled up with many shades of
|
||||
///< a single color combination, and may not be able to serve all
|
||||
///< requested colors. By default the palette assumes there will be
|
||||
///< 10 fixed colors and 10 color combinations.
|
||||
int Bpp(void) const { return bpp; }
|
||||
void Reset(void);
|
||||
///< Resets the palette, making it contain no colors.
|
||||
int Index(tColor Color);
|
||||
///< Returns the index of the given Color (the first color has index 0).
|
||||
///< If Color is not yet contained in this palette, it will be added if
|
||||
///< there is a free slot. If the color can't be added to this palette,
|
||||
///< the closest existing color will be returned.
|
||||
tColor Color(int Index) const { return Index < maxColors ? color[Index] : 0; }
|
||||
///< Returns the color at the given Index. If Index is outside the valid
|
||||
///< range, 0 will be returned.
|
||||
void SetBpp(int Bpp);
|
||||
///< Sets the color depth of this palette to the given value.
|
||||
///< The palette contents will be reset, so that it contains no colors.
|
||||
void SetColor(int Index, tColor Color);
|
||||
///< Sets the palette entry at Index to Color. If Index is larger than
|
||||
///< the number of currently used entries in this palette, the entries
|
||||
///< in between will have undefined values.
|
||||
const tColor *Colors(int &NumColors) const;
|
||||
///< Returns a pointer to the complete color table and stores the
|
||||
///< number of valid entries in NumColors. If no colors have been
|
||||
///< stored yet, NumColors will be set to 0 and the function will
|
||||
///< return NULL.
|
||||
void Take(const cPalette &Palette, tIndexes *Indexes = NULL, tColor ColorFg = 0, tColor ColorBg = 0);
|
||||
///< Takes the colors from the given Palette and adds them to this palette,
|
||||
///< using existing entries if possible. If Indexes is given, it will be
|
||||
///< filled with the index values that each color of Palette has in this
|
||||
///< palette. If either of ColorFg or ColorBg is not zero, the first color
|
||||
///< in Palette will be taken as ColorBg, and the second color will become
|
||||
///< ColorFg.
|
||||
void Replace(const cPalette &Palette);
|
||||
///< Replaces the colors of this palette with the colors from the given
|
||||
///< palette.
|
||||
tColor Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const;
|
||||
///< Determines a color that consists of a linear blend between ColorFg
|
||||
///< and ColorBg. If Level is 0, the result is ColorBg, if it is 255,
|
||||
///< the result is ColorFg. If SetAntiAliasGranularity() has been called previously,
|
||||
///< Level will be mapped to a limited range of levels that allow to make best
|
||||
///< use of the palette entries.
|
||||
int ClosestColor(tColor Color, int MaxDiff = INT_MAX) const;
|
||||
///< Returns the index of a color in this palette that is closest to the given
|
||||
///< Color. MaxDiff can be used to control the maximum allowed color difference.
|
||||
///< If no color with a maximum difference of MaxDiff can be found, -1 will
|
||||
///< be returned. With the default value of INT_MAX, there will always be
|
||||
///< a valid color index returned, but the color may be completely different.
|
||||
};
|
||||
|
||||
class cBitmap : public cPalette {
|
||||
private:
|
||||
tIndex *bitmap;
|
||||
int x0, y0;
|
||||
int width, height;
|
||||
int dirtyX1, dirtyY1, dirtyX2, dirtyY2;
|
||||
public:
|
||||
cBitmap(int Width, int Height, int Bpp, int X0 = 0, int Y0 = 0);
|
||||
///< Creates a bitmap with the given Width, Height and color depth (Bpp).
|
||||
///< X0 and Y0 define the offset at which this bitmap will be located on the OSD.
|
||||
///< All coordinates given in the other functions will be relative to
|
||||
///< this offset (unless specified otherwise).
|
||||
cBitmap(const char *FileName);
|
||||
///< Creates a bitmap and loads an XPM image from the given file.
|
||||
virtual ~cBitmap();
|
||||
int X0(void) const { return x0; }
|
||||
int Y0(void) const { return y0; }
|
||||
int Width(void) const { return width; }
|
||||
int Height(void) const { return height; }
|
||||
void SetSize(int Width, int Height);
|
||||
///< Sets the size of this bitmap to the given values. Any previous
|
||||
///< contents of the bitmap will be lost. If Width and Height are the same
|
||||
///< as the current values, nothing will happen and the bitmap remains
|
||||
///< unchanged.
|
||||
void SetIndex(int x, int y, tIndex Index);
|
||||
///< Sets the index at the given coordinates to Index.
|
||||
///< Coordinates are relative to the bitmap's origin.
|
||||
void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false, bool Overlay = false);
|
||||
///< Sets the pixels in this bitmap with the data from the given
|
||||
///< Bitmap, putting the upper left corner of the Bitmap at (x, y).
|
||||
///< If ColorFg or ColorBg is given, the first palette entry of the Bitmap
|
||||
///< will be mapped to ColorBg and the second palette entry will be mapped to
|
||||
///< ColorFg (palette indexes are defined so that 0 is the background and
|
||||
///< 1 is the foreground color). ReplacePalette controls whether the target
|
||||
///< area shall have its palette replaced with the one from Bitmap.
|
||||
///< If Overlay is true, any pixel in Bitmap that has color index 0 will
|
||||
///< not overwrite the corresponding pixel in the target area.
|
||||
const tIndex *Data(int x, int y);
|
||||
///< Returns the address of the index byte at the given coordinates.
|
||||
|
||||
bool Contains(int x, int y) const;
|
||||
bool Covers(int x1, int y1, int x2, int y2) const;
|
||||
bool Intersects(int x1, int y1, int x2, int y2) const;
|
||||
bool Dirty(int &x1, int &y1, int &x2, int &y2);
|
||||
void Clean(void);
|
||||
};
|
||||
|
||||
struct tArea {
|
||||
int x1, y1, x2, y2;
|
||||
int bpp;
|
||||
int Width(void) const { return x2 - x1 + 1; }
|
||||
int Height(void) const { return y2 - y1 + 1; }
|
||||
bool Intersects(const tArea &Area) const { return !(x2 < Area.x1 || x1 > Area.x2 || y2 < Area.y1 || y1 > Area.y2); }
|
||||
};
|
||||
|
||||
#endif //__OSD_H
|
6
lib/libdvbsub/reader_thread.hpp
Normal file
6
lib/libdvbsub/reader_thread.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef READER_THREAD_HPP_
|
||||
#define READER_THREAD_HPP_
|
||||
|
||||
void* reader_thread(void *arg);
|
||||
|
||||
#endif
|
267
lib/libdvbsub/tools.cpp
Normal file
267
lib/libdvbsub/tools.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* tools.c: Various tools
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* vdr: Id: tools.c 2.0 2008/03/05 17:23:47 kls Exp
|
||||
*/
|
||||
|
||||
#include "tools.h"
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <utime.h>
|
||||
|
||||
int SysLogLevel = 3;
|
||||
|
||||
#define MAXSYSLOGBUF 256
|
||||
|
||||
// --- cTimeMs ---------------------------------------------------------------
|
||||
|
||||
cTimeMs::cTimeMs(int Ms)
|
||||
{
|
||||
Set(Ms);
|
||||
}
|
||||
|
||||
uint64_t cTimeMs::Now(void)
|
||||
{
|
||||
#if 0 && _POSIX_TIMERS > 0 && defined(_POSIX_MONOTONIC_CLOCK)
|
||||
#define MIN_RESOLUTION 5 // ms
|
||||
static bool initialized = false;
|
||||
static bool monotonic = false;
|
||||
struct timespec tp;
|
||||
if (!initialized) {
|
||||
// check if monotonic timer is available and provides enough accurate resolution:
|
||||
if (clock_getres(CLOCK_MONOTONIC, &tp) == 0) {
|
||||
long Resolution = tp.tv_nsec;
|
||||
// require a minimum resolution:
|
||||
if (tp.tv_sec == 0 && tp.tv_nsec <= MIN_RESOLUTION * 1000000) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
|
||||
dsyslog("cTimeMs: using monotonic clock (resolution is %ld ns)", Resolution);
|
||||
monotonic = true;
|
||||
}
|
||||
else
|
||||
esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
|
||||
}
|
||||
else
|
||||
dsyslog("cTimeMs: not using monotonic clock - resolution is too bad (%ld s %ld ns)", tp.tv_sec, tp.tv_nsec);
|
||||
}
|
||||
else
|
||||
esyslog("cTimeMs: clock_getres(CLOCK_MONOTONIC) failed");
|
||||
initialized = true;
|
||||
}
|
||||
if (monotonic) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
|
||||
return (uint64_t(tp.tv_sec)) * 1000 + tp.tv_nsec / 1000000;
|
||||
esyslog("cTimeMs: clock_gettime(CLOCK_MONOTONIC) failed");
|
||||
monotonic = false;
|
||||
// fall back to gettimeofday()
|
||||
}
|
||||
#else
|
||||
# warning Posix monotonic clock not available
|
||||
#endif
|
||||
struct timeval t;
|
||||
if (gettimeofday(&t, NULL) == 0)
|
||||
return (uint64_t(t.tv_sec)) * 1000 + t.tv_usec / 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cTimeMs::Set(int Ms)
|
||||
{
|
||||
begin = Now() + Ms;
|
||||
}
|
||||
|
||||
bool cTimeMs::TimedOut(void)
|
||||
{
|
||||
return Now() >= begin;
|
||||
}
|
||||
|
||||
uint64_t cTimeMs::Elapsed(void)
|
||||
{
|
||||
return Now() - begin;
|
||||
}
|
||||
|
||||
// --- cListObject -----------------------------------------------------------
|
||||
|
||||
cListObject::cListObject(void)
|
||||
{
|
||||
prev = next = NULL;
|
||||
}
|
||||
|
||||
cListObject::~cListObject()
|
||||
{
|
||||
}
|
||||
|
||||
void cListObject::Append(cListObject *Object)
|
||||
{
|
||||
next = Object;
|
||||
Object->prev = this;
|
||||
}
|
||||
|
||||
void cListObject::Insert(cListObject *Object)
|
||||
{
|
||||
prev = Object;
|
||||
Object->next = this;
|
||||
}
|
||||
|
||||
void cListObject::Unlink(void)
|
||||
{
|
||||
if (next)
|
||||
next->prev = prev;
|
||||
if (prev)
|
||||
prev->next = next;
|
||||
next = prev = NULL;
|
||||
}
|
||||
|
||||
int cListObject::Index(void) const
|
||||
{
|
||||
cListObject *p = prev;
|
||||
int i = 0;
|
||||
|
||||
while (p) {
|
||||
i++;
|
||||
p = p->prev;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// --- cListBase -------------------------------------------------------------
|
||||
|
||||
cListBase::cListBase(void)
|
||||
{
|
||||
objects = lastObject = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
cListBase::~cListBase()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void cListBase::Add(cListObject *Object, cListObject *After)
|
||||
{
|
||||
if (After && After != lastObject) {
|
||||
After->Next()->Insert(Object);
|
||||
After->Append(Object);
|
||||
}
|
||||
else {
|
||||
if (lastObject)
|
||||
lastObject->Append(Object);
|
||||
else
|
||||
objects = Object;
|
||||
lastObject = Object;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
void cListBase::Ins(cListObject *Object, cListObject *Before)
|
||||
{
|
||||
if (Before && Before != objects) {
|
||||
Before->Prev()->Append(Object);
|
||||
Before->Insert(Object);
|
||||
}
|
||||
else {
|
||||
if (objects)
|
||||
objects->Insert(Object);
|
||||
else
|
||||
lastObject = Object;
|
||||
objects = Object;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
void cListBase::Del(cListObject *Object, bool DeleteObject)
|
||||
{
|
||||
if (Object == objects)
|
||||
objects = Object->Next();
|
||||
if (Object == lastObject)
|
||||
lastObject = Object->Prev();
|
||||
Object->Unlink();
|
||||
if (DeleteObject) {
|
||||
delete Object;
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
||||
void cListBase::Move(int From, int To)
|
||||
{
|
||||
Move(Get(From), Get(To));
|
||||
}
|
||||
|
||||
void cListBase::Move(cListObject *From, cListObject *To)
|
||||
{
|
||||
if (From && To) {
|
||||
if (From->Index() < To->Index())
|
||||
To = To->Next();
|
||||
if (From == objects)
|
||||
objects = From->Next();
|
||||
if (From == lastObject)
|
||||
lastObject = From->Prev();
|
||||
From->Unlink();
|
||||
if (To) {
|
||||
if (To->Prev())
|
||||
To->Prev()->Append(From);
|
||||
From->Append(To);
|
||||
}
|
||||
else {
|
||||
lastObject->Append(From);
|
||||
lastObject = From;
|
||||
}
|
||||
if (!From->Prev())
|
||||
objects = From;
|
||||
}
|
||||
}
|
||||
|
||||
void cListBase::Clear(void)
|
||||
{
|
||||
while (objects) {
|
||||
cListObject *object = objects->Next();
|
||||
delete objects;
|
||||
objects = object;
|
||||
}
|
||||
objects = lastObject = NULL;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
cListObject *cListBase::Get(int Index) const
|
||||
{
|
||||
if (Index < 0)
|
||||
return NULL;
|
||||
cListObject *object = objects;
|
||||
while (object && Index-- > 0)
|
||||
object = object->Next();
|
||||
return object;
|
||||
}
|
||||
|
||||
static int CompareListObjects(const void *a, const void *b)
|
||||
{
|
||||
const cListObject *la = *(const cListObject **)a;
|
||||
const cListObject *lb = *(const cListObject **)b;
|
||||
return la->Compare(*lb);
|
||||
}
|
||||
|
||||
void cListBase::Sort(void)
|
||||
{
|
||||
int n = Count();
|
||||
cListObject *a[n];
|
||||
cListObject *object = objects;
|
||||
int i = 0;
|
||||
while (object && i < n) {
|
||||
a[i++] = object;
|
||||
object = object->Next();
|
||||
}
|
||||
qsort(a, n, sizeof(cListObject *), CompareListObjects);
|
||||
objects = lastObject = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
a[i]->Unlink();
|
||||
count--;
|
||||
Add(a[i]);
|
||||
}
|
||||
}
|
193
lib/libdvbsub/tools.h
Normal file
193
lib/libdvbsub/tools.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* tools.h: Various tools
|
||||
*
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.h,v 1.1 2009/02/23 19:46:44 rhabarber1848 Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TOOLS_H
|
||||
#define __TOOLS_H
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <iconv.h>
|
||||
#include <poll.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
extern int SysLogLevel;
|
||||
|
||||
#if 0
|
||||
#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR, a) : void() )
|
||||
#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_ERR, a) : void() )
|
||||
#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_ERR, a) : void() )
|
||||
#else
|
||||
#define esyslog printf
|
||||
#define isyslog printf
|
||||
#define dsyslog printf
|
||||
#endif
|
||||
|
||||
#define LOG_ERROR esyslog("ERROR (%s,%d): %m", __FILE__, __LINE__)
|
||||
#define LOG_ERROR_STR(s) esyslog("ERROR: %s: %m", s)
|
||||
|
||||
#define SECSINDAY 86400
|
||||
|
||||
#define KILOBYTE(n) ((n) * 1024)
|
||||
#define MEGABYTE(n) ((n) * 1024 * 1024)
|
||||
|
||||
#define MALLOC(type, size) (type *)malloc(sizeof(type) * (size))
|
||||
|
||||
#define DELETENULL(p) (delete (p), p = NULL)
|
||||
|
||||
#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
|
||||
#define FATALERRNO (errno && errno != EAGAIN && errno != EINTR)
|
||||
|
||||
#ifndef __STL_CONFIG_H // in case some plugin needs to use the STL
|
||||
template<class T> inline T min(T a, T b) { return a <= b ? a : b; }
|
||||
template<class T> inline T max(T a, T b) { return a >= b ? a : b; }
|
||||
template<class T> inline int sgn(T a) { return a < 0 ? -1 : a > 0 ? 1 : 0; }
|
||||
template<class T> inline void swap(T &a, T &b) { T t = a; a = b; b = t; }
|
||||
#endif
|
||||
|
||||
class cTimeMs {
|
||||
private:
|
||||
uint64_t begin;
|
||||
public:
|
||||
cTimeMs(int Ms = 0);
|
||||
///< Creates a timer with ms resolution and an initial timeout of Ms.
|
||||
static uint64_t Now(void);
|
||||
void Set(int Ms = 0);
|
||||
bool TimedOut(void);
|
||||
uint64_t Elapsed(void);
|
||||
};
|
||||
|
||||
class cListObject {
|
||||
private:
|
||||
cListObject *prev, *next;
|
||||
public:
|
||||
cListObject(void);
|
||||
virtual ~cListObject();
|
||||
virtual int Compare(const cListObject &ListObject) const { return 0; }
|
||||
///< Must return 0 if this object is equal to ListObject, a positive value
|
||||
///< if it is "greater", and a negative value if it is "smaller".
|
||||
void Append(cListObject *Object);
|
||||
void Insert(cListObject *Object);
|
||||
void Unlink(void);
|
||||
int Index(void) const;
|
||||
cListObject *Prev(void) const { return prev; }
|
||||
cListObject *Next(void) const { return next; }
|
||||
};
|
||||
|
||||
class cListBase {
|
||||
protected:
|
||||
cListObject *objects, *lastObject;
|
||||
cListBase(void);
|
||||
int count;
|
||||
public:
|
||||
virtual ~cListBase();
|
||||
void Add(cListObject *Object, cListObject *After = NULL);
|
||||
void Ins(cListObject *Object, cListObject *Before = NULL);
|
||||
void Del(cListObject *Object, bool DeleteObject = true);
|
||||
virtual void Move(int From, int To);
|
||||
void Move(cListObject *From, cListObject *To);
|
||||
virtual void Clear(void);
|
||||
cListObject *Get(int Index) const;
|
||||
int Count(void) const { return count; }
|
||||
void Sort(void);
|
||||
};
|
||||
|
||||
template<class T> class cList : public cListBase {
|
||||
public:
|
||||
T *Get(int Index) const { return (T *)cListBase::Get(Index); }
|
||||
T *First(void) const { return (T *)objects; }
|
||||
T *Last(void) const { return (T *)lastObject; }
|
||||
T *Prev(const T *object) const { return (T *)object->cListObject::Prev(); } // need to call cListObject's members to
|
||||
T *Next(const T *object) const { return (T *)object->cListObject::Next(); } // avoid ambiguities in case of a "list of lists"
|
||||
};
|
||||
|
||||
template<class T> class cVector {
|
||||
private:
|
||||
mutable int allocated;
|
||||
mutable int size;
|
||||
mutable T *data;
|
||||
cVector(const cVector &Vector) {} // don't copy...
|
||||
cVector &operator=(const cVector &Vector) { return *this; } // ...or assign this!
|
||||
void Realloc(int Index) const
|
||||
{
|
||||
if (++Index > allocated) {
|
||||
data = (T *)realloc(data, Index * sizeof(T));
|
||||
for (int i = allocated; i < Index; i++)
|
||||
data[i] = T(0);
|
||||
allocated = Index;
|
||||
}
|
||||
}
|
||||
public:
|
||||
cVector(int Allocated = 10)
|
||||
{
|
||||
allocated = 0;
|
||||
size = 0;
|
||||
data = NULL;
|
||||
Realloc(Allocated);
|
||||
}
|
||||
virtual ~cVector() { free(data); }
|
||||
T& At(int Index) const
|
||||
{
|
||||
Realloc(Index);
|
||||
if (Index >= size)
|
||||
size = Index + 1;
|
||||
return data[Index];
|
||||
}
|
||||
const T& operator[](int Index) const
|
||||
{
|
||||
return At(Index);
|
||||
}
|
||||
T& operator[](int Index)
|
||||
{
|
||||
return At(Index);
|
||||
}
|
||||
int Size(void) const { return size; }
|
||||
virtual void Insert(T Data, int Before = 0)
|
||||
{
|
||||
if (Before < size) {
|
||||
Realloc(size);
|
||||
memmove(&data[Before + 1], &data[Before], (size - Before) * sizeof(T));
|
||||
size++;
|
||||
data[Before] = Data;
|
||||
}
|
||||
else
|
||||
Append(Data);
|
||||
}
|
||||
virtual void Append(T Data)
|
||||
{
|
||||
if (size >= allocated)
|
||||
Realloc(allocated * 4 / 2); // increase size by 50%
|
||||
data[size++] = Data;
|
||||
}
|
||||
virtual void Remove(int Index)
|
||||
{
|
||||
if (Index < size - 1)
|
||||
memmove(&data[Index], &data[Index + 1], (size - Index) * sizeof(T));
|
||||
size--;
|
||||
}
|
||||
virtual void Clear(void)
|
||||
{
|
||||
size = 0;
|
||||
}
|
||||
void Sort(__compar_fn_t Compare)
|
||||
{
|
||||
qsort(data, size, sizeof(T), Compare);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__TOOLS_H
|
Reference in New Issue
Block a user