summaryrefslogtreecommitdiff
path: root/frontend.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontend.c')
-rw-r--r--frontend.c614
1 files changed, 614 insertions, 0 deletions
diff --git a/frontend.c b/frontend.c
new file mode 100644
index 00000000..0dc42273
--- /dev/null
+++ b/frontend.c
@@ -0,0 +1,614 @@
+/*
+ * frontend.c:
+ *
+ * See the main source file 'xineliboutput.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: frontend.c,v 1.1 2006-06-03 10:01:17 phintuka Exp $
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <vdr/config.h>
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+
+#include "logdefs.h"
+#include "config.h"
+#include "frontend.h"
+
+#include "tools/pes.h"
+#include "tools/general_remote.h"
+
+//#define LOG_CONTROL_MESSAGES
+//#define XINELIBOUTPUT_LOG_KEYS
+
+//----------------------------- cXinelibThread --------------------------------
+
+//
+// keyboard control handler
+//
+
+/*static*/
+void cXinelibThread::KeypressHandler(char *keymap, char *key,
+ bool repeat, bool release)
+{
+#ifdef XINELIBOUTPUT_LOG_KEYS
+ static FILE *flog = fopen("/video/keys.log","w");
+ fprintf(flog,"KEY %s %s %d %d\n",keymap,key,repeat,release);fflush(flog);
+#endif
+
+ TRACE("keypress_handler: " << (keymap?keymap:"") << " " << key);
+
+ if(!key)
+ return;
+
+ if(keymap) {
+ cRemote *item = Remotes.First();
+ while(item) {
+ if(!strcmp(item->Name(), keymap)) {
+ // dirty... but only way to support learning ...
+ ((cGeneralRemote*)item)->Put(key, repeat, release);
+ return;
+ }
+ item = Remotes.Next(item);
+ }
+ cGeneralRemote *r = new cGeneralRemote(keymap);
+ if(*key)
+ r->Put(key, repeat, release);
+ } else {
+ cRemote::Put(cKey::FromString(key));
+ }
+}
+
+cXinelibThread::cXinelibThread(const char *Description) : cThread(Description)
+{
+ TRACEF("cXinelibThread::cXinelibThread");
+
+ m_bStopThread = false;
+ m_bReady = false;
+ m_bIsFinished = false;
+ m_bNoVideo = true;
+ m_bLiveMode = false;
+ m_StreamPos = 0;
+ m_bEndOfStreamReached = false;
+ m_bPlayingFile = false;
+ m_FileName = NULL;
+}
+
+cXinelibThread::~cXinelibThread()
+{
+ TRACEF("cXinelibThread::~cXinelibThread");
+
+ if(Active())
+ Cancel();
+ if(m_FileName)
+ free(m_FileName);
+}
+
+//
+// Thread control
+//
+
+void cXinelibThread::Start(void)
+{
+ TRACEF("cXinelibThread::Start");
+
+ cThread::Start();
+}
+
+void cXinelibThread::Stop(void)
+{
+ TRACEF("cXinelibThread::Stop");
+
+ SetStopSignal();
+
+ //if(Active())
+ Cancel(5);
+}
+
+void cXinelibThread::SetStopSignal(void)
+{
+ TRACEF("cXinelibThread::SetStopSignal");
+
+ LOCK_THREAD;
+ m_bStopThread = true;
+}
+
+bool cXinelibThread::GetStopSignal(void)
+{
+ TRACEF("cXinelibThread::GetStopSignal");
+
+ LOCK_THREAD;
+ return m_bStopThread;
+}
+
+bool cXinelibThread::IsReady(void)
+{
+ LOCK_THREAD;
+ return m_bReady;
+}
+
+bool cXinelibThread::IsFinished(void)
+{
+ LOCK_THREAD;
+ return m_bIsFinished;
+}
+
+//
+// Playback control
+//
+
+void cXinelibThread::SetVolume(int NewVolume)
+{
+ Xine_Control("VOLUME", NewVolume * 100 / 255);
+}
+
+void cXinelibThread::TrickSpeed(int Speed)
+{
+ TRACEF("cXinelibThread::TrickSpeed");
+
+ Xine_Control("TRICKSPEED", Speed);
+}
+
+void cXinelibThread::SetLiveMode(bool LiveModeOn)
+{
+ TRACEF("cXinelibThread::SetLiveMode");
+
+ Lock();
+ if(m_bLiveMode == LiveModeOn) {
+ Unlock();
+ return;
+ }
+ m_bLiveMode = LiveModeOn;
+ Unlock();
+
+ Xine_Control("LIVE", m_bLiveMode ? 1 : 0);
+ Xine_Sync();
+}
+
+void cXinelibThread::SetStillMode(bool StillModeOn)
+{
+ TRACEF("cXinelibThread::SetStillMode");
+ Xine_Control("STILL", StillModeOn ? 1 : 0);
+ Xine_Sync();
+}
+
+void cXinelibThread::SetNoVideo(bool bVal)
+{
+ TRACEF("cXinelibThread::SetNoVideo");
+
+ Lock();
+ if(m_bNoVideo == bVal) {
+ Unlock();
+ return;
+ }
+ m_bNoVideo = bVal;
+ Unlock();
+
+ Xine_Control("NOVIDEO", m_bNoVideo ? 1 : 0);
+
+ if(m_bNoVideo && strcmp(xc.audio_visualization, "none")) {
+ ConfigurePostprocessing(xc.audio_visualization, true, NULL);
+ } else {
+ ConfigurePostprocessing("AudioVisualization", false, NULL);
+ }
+}
+
+void cXinelibThread::AudioStreamChanged(bool ac3, int StreamId)
+{
+ TRACEF("cXinelibThread::AudioStreamChanged");
+ if(ac3)
+ Xine_Control("NEWAUDIOSTREAM AC3");
+ else
+ Xine_Control("NEWAUDIOSTREAM", StreamId);
+}
+
+void cXinelibThread::SpuStreamChanged(int StreamId)
+{
+ TRACEF("cXinelibThread::SpuStreamChanged");
+ Xine_Control("NEWSPUSTREAM", StreamId);
+}
+
+void cXinelibThread::Clear(void)
+{
+ TRACEF("cXinelibThread::Clear");
+
+ Lock();
+ int64_t tmp = m_StreamPos;
+ Unlock();
+ Xine_Control("DISCARD", tmp);
+}
+
+bool cXinelibThread::Flush(int TimeoutMs)
+{
+ TRACEF("cXinelibThread::Flush");
+
+ return Xine_Control("FLUSH", TimeoutMs) <= 0;
+}
+
+bool cXinelibThread::Poll(cPoller& Poller, int TimeoutMs)
+{
+ TRACEF("cXinelibThread::Poll");
+
+ int n = Xine_Control("POLL", TimeoutMs);
+
+ if(n>0)
+ return true;
+
+ return false; // Poller.Poll(TimeoutMs);
+}
+
+//
+// Data transfer
+//
+
+int cXinelibThread::Play_PES(const uchar *data, int len)
+{
+ Lock();
+ m_StreamPos += len;
+ /*m_bEndOfStreamReached = false;*/
+ Unlock();
+ return len;
+}
+
+//
+// Stream conversions
+//
+
+// Convert MPEG1 PES headers to MPEG2 PES headers
+
+int cXinelibThread::Play_Mpeg1_PES(const uchar *data1, int len)
+{
+ if(!data1[0] && !data1[1] && data1[2] == 0x01 && len>7 && /* header sync bytes */
+ ( VIDEO_STREAM == (data1[3] & ~VIDEO_STREAM_MASK) || /* video stream */
+ AUDIO_STREAM == (data1[3] & ~AUDIO_STREAM_MASK) || /* audio stream */
+ PRIVATE_STREAM1 == data1[3]) && /* private stream 1 */
+ ((data1[6] & 0xC0) != 0x80) && /* really mpeg1 pes */
+ (len == ((data1[4]<<8) | data1[5]) + 6)) { /* whole PES packet and nothing else */
+ uchar *data2 = new uchar[len+64];
+ int i1=0, i2=0, r=0;
+
+ data2[i2++]=data1[i1++]; // 00 (sync)
+ data2[i2++]=data1[i1++]; // 00 (sync)
+ data2[i2++]=data1[i1++]; // 01 (sync)
+ data2[i2++]=data1[i1++]; // stream ID
+ data2[i2++]=data1[i1++]; // len hi
+ data2[i2++]=data1[i1++]; // len lo
+
+ // skip stuffing
+ while ((data1[i1] & 0x80) == 0x80)
+ i1++;
+
+ if ((data1[i1] & 0xc0) == 0x40) {
+ // skip STD_buffer_scale, STD_buffer_size
+ i1 += 2;
+ }
+
+ if(len<i1+5) return len;
+
+ data2[i2++] = 0x80;
+
+ if ((data1[i1] & 0xf0) == 0x20) {
+ /* PTS */
+ data2[i2++] = 0x80;
+ data2[i2++] = 5;
+ data2[i2++] = data1[i1++] & 0x0E;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+ }
+ else if ((data1[i1] & 0xf0) == 0x30) {
+ /* PTS & DTS */
+ data2[i2++] = 0x80|0x40;
+ data2[i2++] = 10;
+ data2[i2++] = data1[i1++] & 0x0E;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+
+ data2[i2++] = data1[i1++] & 0x0E;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+ data2[i2++] = data1[i1++] & 0xFF;
+ data2[i2++] = data1[i1++] & 0xFE;
+ } else {
+ i1++;
+ data2[i2++] = 0; /* no pts, no dts */
+ data2[i2++] = 0; /* header len */
+ }
+
+ int newlen = ((data1[4]<<8) | data1[5]) + (i2-i1), loops=0;
+ data2[4] = ((newlen)&0xff00)>>8;
+ data2[5] = ((newlen)&0xff);
+ if(len-i1 > 0) {
+ memcpy(data2+i2, data1+i1, len-i1);
+ cPoller p;
+ while(!Poll(p,100) && loops++ < 10) {
+ LOGDBG("Play_Mpeg1_PES: poll failed");
+ }
+ r = Play_PES(data2,newlen+6);
+ }
+
+ delete data2;
+ return r==newlen+6 ? ((data1[4]<<8)|data1[5])+6 : 0;
+ }
+ return len; // nothing useful found ...
+}
+
+// Pack elementary MPEG stream to PES
+
+bool cXinelibThread::Play_Mpeg2_ES(const uchar *data, int len, int streamID)
+{
+ static uchar hdr[] = {0x00,0x00,0x01,0xe0, 0x00,0x00,0x80,0,0}; /* mpeg2 */
+ int todo = len, done = 0, hdrlen = 9/*sizeof(hdr)*/;
+ uchar *frame = new uchar[PES_CHUNK_SIZE+32];
+
+ hdr[3] = (uchar)streamID;
+ while(todo) {
+ int blocklen = todo;
+ if(blocklen > ((PES_CHUNK_SIZE - hdrlen) & 0xfffc))
+ blocklen = (PES_CHUNK_SIZE - hdrlen) & 0xfffc;
+ hdr[4] = ((blocklen+3)&0xff00)>>8;
+ hdr[5] = ((blocklen+3)&0xff);
+
+ memcpy(frame, hdr, hdrlen);
+ memcpy(frame+hdrlen, data+done, blocklen);
+
+ done += blocklen;
+ todo -= blocklen;
+
+ cPoller p;
+#if 1
+ Poll(p, 100);
+#else
+ int loops=0;
+ while(!Poll(p,100) && loops++ < 10) {
+ LOGDBG("Play_ES: Poll Failed");
+ }
+#endif
+
+ if(blocklen+hdrlen != Play_PES(frame,blocklen+hdrlen)) {
+ delete frame;
+ return false;
+ }
+ }
+
+ // append sequence end code to video
+ if((streamID & 0xF0) == 0xE0) {
+ static uchar seqend[] = {0x00,0x00,0x01,0xe0, 0x00,0x07,0x80,0x00,
+ 0x00,
+ 0x00,0x00,0x01,0xB7}; /* mpeg2 */
+#if 0
+ frame[0] = 0x00;
+ frame[1] = 0x00;
+ frame[2] = 0x01;
+ frame[3] = (uchar)streamID;
+ frame[4] = 0x00;
+ frame[5] = 0x07;
+ frame[6] = 0x80;
+ frame[7] = 0x00;
+ frame[8] = 0x00;
+ frame[9] = 0x00;
+ frame[10] = 0x00;
+ frame[11] = 0x01;
+ frame[12] = 0xB7;
+ Play_PES(frame, 13);
+#else
+ seqend[3] = (uchar)streamID;
+ Play_PES(seqend, 13);
+#endif
+ }
+
+ delete frame;
+ return true;
+}
+
+//
+// Built-in still images
+//
+
+bool cXinelibThread::QueueBlankDisplay(void)
+{
+ TRACEF("cXinelibThread::BlankDisplay");
+
+ extern unsigned char v_mpg_black[]; // black_720x576.c
+ extern int v_mpg_black_length;
+
+ return Play_Mpeg2_ES(v_mpg_black, v_mpg_black_length, VIDEO_STREAM);
+}
+
+bool cXinelibThread::BlankDisplay(void)
+{
+ TRACEF("cXinelibThread::BlankDisplay");
+
+ bool r = QueueBlankDisplay();
+ for(int i=0; i<5 && !Flush(100); i++)
+ ;
+ return r;
+}
+
+bool cXinelibThread::LogoDisplay(void)
+{
+ TRACEF("cXinelibThread::LogoDisplay");
+
+ extern unsigned char v_mpg_vdrlogo[]; // vdrlogo_720x576.c
+ extern int v_mpg_vdrlogo_length;
+
+ bool r = Play_Mpeg2_ES(v_mpg_vdrlogo, v_mpg_vdrlogo_length, VIDEO_STREAM);
+ for(int i=0; i<5 && !Flush(100); i++)
+ ;
+ return r;
+}
+
+bool cXinelibThread::NoSignalDisplay(void)
+{
+ TRACEF("cXinelibThread::NoSignalDisplay");
+
+ extern unsigned char v_mpg_nosignal[]; // nosignal_720x576.c
+ extern int v_mpg_nosignal_length;
+
+ bool r = Play_Mpeg2_ES(v_mpg_nosignal, v_mpg_nosignal_length, VIDEO_STREAM);
+ for(int i=0; i<5 && !Flush(100); i++)
+ ;
+ return r;
+}
+
+//
+// Xine Control
+//
+
+int cXinelibThread::Xine_Control(const char *cmd, int p1)
+{
+ char buf[128];
+ sprintf(buf, "%s %d", cmd, p1);
+ return Xine_Control(buf);
+}
+
+int cXinelibThread::Xine_Control(const char *cmd, int64_t p1)
+{
+ char buf[128];
+ sprintf(buf, "%s %lld", cmd, p1);
+ return Xine_Control(buf);
+}
+
+int cXinelibThread::Xine_Control(const char *cmd, char *p1)
+{
+ char buf[128];
+ sprintf(buf, "%s %s", cmd, p1);
+ return Xine_Control(buf);
+}
+
+bool cXinelibThread::PlayFile(const char *FileName, int Position,
+ bool LoopPlay)
+{
+ TRACEF("cXinelibThread::PlayFile");
+ char buf[2048];
+ m_bEndOfStreamReached = false;
+ sprintf(buf, "PLAYFILE %s %d %s\r\n",
+ LoopPlay ? "Loop" : "", Position, FileName ? FileName : "");
+ int result = PlayFileCtrl(buf);
+
+ if(!FileName || result != 0) {
+ m_bPlayingFile = false;
+ if(m_FileName)
+ free(m_FileName);
+ } else {
+ m_FileName = strdup(FileName);
+ m_bPlayingFile = true;
+ }
+
+ return (!GetStopSignal()) && (result==0);
+}
+
+
+//
+// Configuration
+//
+
+int cXinelibThread::ConfigureOSD(bool prescale_osd, bool unscaled_osd)
+{
+ char s[256];
+ sprintf(s, "OSDSCALING %d", prescale_osd);
+ if(!xc.prescale_osd_downscale)
+ strcat(s, " NoDownscale");
+ if(unscaled_osd)
+ strcat(s, " UnscaledAlways");
+ if(xc.unscaled_osd_opaque)
+ strcat(s, " UnscaledOpaque");
+ if(xc.unscaled_osd_lowresvideo)
+ strcat(s, " UnscaledLowRes");
+
+ return Xine_Control(s);
+}
+
+int cXinelibThread::ConfigurePostprocessing(char *deinterlace_method, int audio_delay,
+ int audio_compression, int *audio_equalizer,
+ int audio_surround)
+{
+ char tmp[255];
+ int r = true;
+
+ if(strcmp(deinterlace_method, "tvtime"))
+ r = ConfigurePostprocessing("tvtime", false, NULL) && r;
+
+ r = Xine_Control("DEINTERLACE", deinterlace_method) && r;
+ r = Xine_Control("AUDIODELAY", audio_delay) && r;
+ r = Xine_Control("AUDIOCOMPRESSION", audio_compression) && r;
+ r = Xine_Control("AUDIOSURROUND", audio_compression) && r;
+ sprintf(tmp,"EQUALIZER %d %d %d %d %d %d %d %d %d %d",
+ audio_equalizer[0],audio_equalizer[1],
+ audio_equalizer[2],audio_equalizer[3],
+ audio_equalizer[4],audio_equalizer[5],
+ audio_equalizer[6],audio_equalizer[7],
+ audio_equalizer[8],audio_equalizer[9]);
+ r = Xine_Control(tmp) && r;
+
+ if(m_bNoVideo && strcmp(xc.audio_visualization, "none")) {
+ //fe->post_open(fe, xc.audio_visualization, NULL);
+ r = ConfigurePostprocessing(xc.audio_visualization, true, NULL) && r;
+ } else {
+ //fe->post_close(fe, NULL, 0);
+ r = ConfigurePostprocessing("AudioVisualization", false, NULL) && r;
+ }
+
+ if(!strcmp(deinterlace_method, "tvtime"))
+ r = ConfigurePostprocessing("tvtime", true, xc.deinterlace_opts) && r;
+
+ return r;
+}
+
+int cXinelibThread::ConfigurePostprocessing(char *name, bool on, char *args)
+{
+ char tmp[1024];
+ if(!name) name = "0";
+ if(!args) args = "";
+ if(on) {
+ sprintf(tmp, "POST %s On %s", name, args);
+ return Xine_Control(tmp);
+ } else {
+ // 0 - audio vis.
+ // 1 - audio post
+ // 2 - video post
+ //return fe->post_close(fe, name, -1);
+ sprintf(tmp, "POST %s Off", name);
+ return Xine_Control(tmp);
+ }
+ return -1;
+}
+
+int cXinelibThread::ConfigureVideo(int hue, int saturation,
+ int brightness, int contrast)
+{
+ char cmd[128];
+ sprintf(cmd, "VIDEO_PROPERTIES %d %d %d %d",
+ hue, saturation, brightness, contrast);
+ return Xine_Control(cmd);
+}
+
+//
+// Playback files
+//
+
+bool cXinelibThread::EndOfStreamReached(void)
+{
+ LOCK_THREAD;
+ bool r = m_bEndOfStreamReached;
+ return r;
+}
+
+