diff options
Diffstat (limited to 'dvbapi.c')
-rw-r--r-- | dvbapi.c | 941 |
1 files changed, 412 insertions, 529 deletions
@@ -7,7 +7,7 @@ * DVD support initially written by Andreas Schultz <aschultz@warp10.net> * based on dvdplayer-0.5 by Matjaz Thaler <matjaz.thaler@guest.arnes.si> * - * $Id: dvbapi.c 1.132 2001/10/21 13:36:27 kls Exp $ + * $Id: dvbapi.c 1.137 2001/11/04 12:05:36 kls Exp $ */ //#define DVDDEBUG 1 @@ -200,6 +200,7 @@ cIndexFile::~cIndexFile() if (f >= 0) close(f); delete fileName; + delete index; } bool cIndexFile::CatchUp(int Index) @@ -713,7 +714,10 @@ protected: void TrickSpeed(int Increment); virtual void Empty(bool Block = false); virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {} + virtual void PlayExternalDolby(const uchar *b, int MaxLength); virtual void Output(void); + void putFrame(cFrame *Frame); + void putFrame(unsigned char *Data, int Length, eFrameType Type = ftUnknown); public: cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev); virtual ~cPlayBuffer(); @@ -759,6 +763,28 @@ cPlayBuffer::~cPlayBuffer() { } +void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength) +{ + if (dolbyDev) { + if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) { + if (b[3] == 0xBD) { // dolby + int l = b[4] * 256 + b[5] + 6; + int written = b[8] + 9; // skips the PES header + int n = min(l - written, MaxLength); + while (n > 0) { + int w = fwrite(&b[written], 1, n, dolbyDev); + if (w < 0) { + LOG_ERROR; + break; + } + n -= w; + written += w; + } + } + } + } +} + void cPlayBuffer::Output(void) { dsyslog(LOG_INFO, "output thread started (pid=%d)", getpid()); @@ -771,24 +797,28 @@ void cPlayBuffer::Output(void) } const cFrame *frame = Get(); if (frame) { - StripAudioPackets((uchar *)frame->Data(), frame->Count(), (playMode == pmFast || playMode == pmSlow) ? 0x00 : audioTrack);//XXX - const uchar *p = frame->Data(); - int r = frame->Count(); - while (r > 0 && Busy() && !blockOutput) { - cFile::FileReadyForWriting(videoDev, 100); - int w = write(videoDev, p, r); - if (w > 0) { - p += w; - r -= w; - } - else if (w < 0 && FATALERRNO) { - LOG_ERROR; - Stop(); - return; + if (frame->Type() == ftDolby) + PlayExternalDolby(frame->Data(), frame->Count()); + else { + StripAudioPackets((uchar *)frame->Data(), frame->Count(), (playMode == pmFast || playMode == pmSlow) ? 0x00 : audioTrack);//XXX + const uchar *p = frame->Data(); + int r = frame->Count(); + while (r > 0 && Busy() && !blockOutput) { + cFile::FileReadyForWriting(videoDev, 100); + int w = write(videoDev, p, r); + if (w > 0) { + p += w; + r -= w; + } + else if (w < 0 && FATALERRNO) { + LOG_ERROR; + Stop(); + return; + } } - } - writeIndex = frame->Index(); - backTrace.Add(frame->Index(), frame->Count()); + writeIndex = frame->Index(); + backTrace.Add(frame->Index(), frame->Count()); + } Drop(frame); } } @@ -796,6 +826,20 @@ void cPlayBuffer::Output(void) dsyslog(LOG_INFO, "output thread ended (pid=%d)", getpid()); } +void cPlayBuffer::putFrame(cFrame *Frame) +{ + while (Busy() && !blockInput) { + if (Put(Frame)) + return; + } + delete Frame; // caller relies on frame being put, otherwise this would be a memory leak! +} + +void cPlayBuffer::putFrame(unsigned char *Data, int Length, eFrameType Type) +{ + putFrame(new cFrame(Data, Length, Type)); +} + void cPlayBuffer::TrickSpeed(int Increment) { int nts = trickSpeed + Increment; @@ -1089,11 +1133,8 @@ void cReplayBuffer::Input(void) } else // allows replay even if the index file is missing r = read(replayFile, b, sizeof(b)); - if (r > 0) { - cFrame *frame = new cFrame(b, r, readIndex); - while (Busy() && !blockInput && !Put(frame)) - ; - } + if (r > 0) + putFrame(new cFrame(b, r, ftUnknown, readIndex)); else if (r == 0) eof = true; else if (r < 0 && FATALERRNO) { @@ -1117,19 +1158,8 @@ void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except) int l = b[i + 4] * 256 + b[i + 5] + 6; switch (c) { case 0xBD: // dolby - if (Except && dolbyDev) { - int written = b[i + 8] + 9; // skips the PES header - int n = l - written; - while (n > 0) { - int w = fwrite(&b[i + written], 1, n, dolbyDev); - if (w < 0) { - LOG_ERROR; - break; - } - n -= w; - written += w; - } - } + if (Except && dolbyDev) + PlayExternalDolby(&b[i], Length - i); // continue with deleting the data - otherwise it disturbs DVB replay case 0xC0 ... 0xC1: // audio if (c == 0xC1) @@ -1290,10 +1320,166 @@ bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset) } #ifdef DVDSUPPORT + +#define SYSTEM_HEADER 0xBB +#define PROG_STREAM_MAP 0xBC +#ifndef PRIVATE_STREAM1 +#define PRIVATE_STREAM1 0xBD +#endif +#define PADDING_STREAM 0xBE +#ifndef PRIVATE_STREAM2 +#define PRIVATE_STREAM2 0xBF +#endif +#define AUDIO_STREAM_S 0xC0 +#define AUDIO_STREAM_E 0xDF +#define VIDEO_STREAM_S 0xE0 +#define VIDEO_STREAM_E 0xEF +#define ECM_STREAM 0xF0 +#define EMM_STREAM 0xF1 +#define DSM_CC_STREAM 0xF2 +#define ISO13522_STREAM 0xF3 +#define PROG_STREAM_DIR 0xFF + +#define cOPENDVD 0 +#define cOPENTITLE 1 +#define cOPENCHAPTER 2 +#define cOUTCELL 3 +#define cREADFRAME 4 +#define cOUTPACK 5 +#define cOUTFRAMES 6 + +#define aAC3 0x80 +#define aLPCM 0xA0 + +// --- cAC3toPCM ------------------------------------------------------------- + +class cAC3toPCM { +private: + enum { AC3_STOP, AC3_START, AC3_PLAY } ac3stat; + uchar *ac3data; + int ac3inp; + int ac3outp; +public: + cAC3toPCM(void); + ~cAC3toPCM(); + void Clear(void); + void Put(unsigned char *sector, int length); + cFrame *Get(int size, uchar PTSflags = 0, uchar *PTSdata = 0); + }; + +cAC3toPCM::cAC3toPCM(void) +{ + ac3dec_init(); + ac3data = new uchar[AC3_BUFFER_SIZE]; + Clear(); +} + +cAC3toPCM::~cAC3toPCM() +{ + delete ac3data; +} + +void cAC3toPCM::Clear(void) +{ + ac3stat = AC3_START; + ac3outp = ac3inp = 0; +} + +void cAC3toPCM::Put(unsigned char *sector, int length) +{ + ac3dec_decode_data(sector, sector + length, ac3stat == AC3_START, &ac3inp, &ac3outp, (char *)ac3data); + ac3stat = AC3_PLAY; +} + +// data=PCM samples, 16 bit, LSB first, 48kHz, stereo +cFrame *cAC3toPCM::Get(int size, uchar PTSflags, uchar *PTSdata) +{ + if (ac3inp == ac3outp) + return NULL; + +#define MAXSIZE 2022 + + uchar buffer[2048]; + uchar *data; + + if (size > 0) { + int p_size = (size > MAXSIZE) ? MAXSIZE : size; + int length = 10; // default header bytes + int header = 0; + int stuffb = 0; + + switch (PTSflags) { + case 2: header = 5; // additional header bytes + stuffb = 1; + break; + case 3: header = 10; + break; + default: header = 0; + } + + // header = 0; //XXX ??? + stuffb = 0; //XXX ??? + + length += header; + length += stuffb; + + buffer[0] = 0x00; + buffer[1] = 0x00; + buffer[2] = 0x01; + buffer[3] = PRIVATE_STREAM1; + + buffer[6] = 0x80; + buffer[7] = PTSflags << 6; + buffer[8] = header + stuffb; + + if (header) + memcpy(&buffer[9], (void *)PTSdata, header); + + // add stuffing + data = buffer + 9 + header; + for (int cnt = 0; cnt < stuffb; cnt++) + data[cnt] = 0xff; + length += stuffb; + + // add data + data = buffer + 9 + header + stuffb + 7; + int cnt = 0; + while (p_size) { + if (ac3outp != ac3inp) { // data in the buffer + data[cnt ^ 1] = ac3data[ac3outp]; // swab because ac3dec delivers wrong byteorder (the "xor" (^) is a swab!) + p_size--; + cnt++; + length++; + ac3outp = (ac3outp + 1) % AC3_BUFFER_SIZE; + } + else + break; + } + + data = buffer + 9 + header + stuffb; + data[0] = aLPCM; // substream ID + data[1] = 0x00; // other stuff (see DVB specs), ignored by driver + data[2] = 0x00; + data[3] = 0x00; + data[4] = 0x00; + data[5] = 0x00; + data[6] = 0x00; + + buffer[4] = (length >> 8) & 0xff; + buffer[5] = length & 0xff; + + length += 6; + + return new cFrame(buffer, length); + } + return NULL; +} + // --- cDVDplayBuffer -------------------------------------------------------- class cDVDplayBuffer : public cPlayBuffer { private: + cAC3toPCM AC3toPCM; uchar audioTrack; cDVD *dvd;//XXX necessary??? @@ -1308,7 +1494,6 @@ private: int doplay; int cyclestate; int prevcycle; - int brakeCounter; int skipCnt; tt_srpt_t *tt_srpt; @@ -1335,11 +1520,6 @@ private: int logAudioTrack; int maxAudioTrack; - enum { AC3_STOP, AC3_START, AC3_PLAY } ac3stat; - uchar *ac3data; - int ac3inp; - int ac3outp; - int lpcm_count; int is_nav_pack(unsigned char *buffer); void Close(void); virtual void Empty(bool Block = false); @@ -1350,10 +1530,7 @@ private: int GetStuffingLen(const uchar *Data); int GetPacketLength(const uchar *Data); int GetPESHeaderLength(const uchar *Data); - int SendPCM(int size); - void playDecodedAC3(void); - void handleAC3(unsigned char *sector, int length); - void putFrame(unsigned char *sector, int length); + void handleAC3(unsigned char *sector, int length, uchar PTSflags, uchar *PTSdata); unsigned int getAudioStream(unsigned int StreamId); void setChapid(void); void NextState(int State) { prevcycle = cyclestate; cyclestate = State; } @@ -1369,17 +1546,6 @@ public: virtual void ToggleAudioTrack(void); }; -#define cOPENDVD 0 -#define cOPENTITLE 1 -#define cOPENCHAPTER 2 -#define cOUTCELL 3 -#define cREADFRAME 4 -#define cOUTPACK 5 -#define cOUTFRAMES 6 - -#define aAC3 0x80 -#define aLPCM 0xA0 - cDVDplayBuffer::cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD *DvD, int title) :cPlayBuffer(DvbApi, VideoDev, AudioDev) { @@ -1389,15 +1555,10 @@ cDVDplayBuffer::cDVDplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, cDVD angle = 0; cyclestate = cOPENDVD; prevcycle = 0; - brakeCounter = 0; skipCnt = 0; logAudioTrack = 0; canToggleAudioTrack = true;//XXX determine from cDVD! - ac3dec_init(); data = new uchar[1024 * DVD_VIDEO_LB_LEN]; - ac3data = new uchar[AC3_BUFFER_SIZE]; - ac3inp = ac3outp = 0; - ac3stat = AC3_START; canDoTrickMode = true; dvbApi->SetModeReplay(); Start(); @@ -1408,7 +1569,6 @@ cDVDplayBuffer::~cDVDplayBuffer() Stop(); Close(); dvbApi->SetModeNormal(false); - delete ac3data; delete data; } @@ -1449,8 +1609,7 @@ void cDVDplayBuffer::ToggleAudioTrack(void) #ifdef DVDDEBUG dsyslog(LOG_INFO, "DVB: Audio Stream ID changed to: %x", audioTrack); #endif - ac3stat = AC3_START; - ac3outp = ac3inp; + AC3toPCM.Clear(); } } @@ -1869,141 +2028,21 @@ int cDVDplayBuffer::ScanVideoPacket(const uchar *Data, int Count, uchar *Picture return -1; } -#define SYSTEM_HEADER 0xBB -#define PROG_STREAM_MAP 0xBC -#ifndef PRIVATE_STREAM1 -#define PRIVATE_STREAM1 0xBD -#endif -#define PADDING_STREAM 0xBE -#ifndef PRIVATE_STREAM2 -#define PRIVATE_STREAM2 0xBF -#endif -#define AUDIO_STREAM_S 0xC0 -#define AUDIO_STREAM_E 0xDF -#define VIDEO_STREAM_S 0xE0 -#define VIDEO_STREAM_E 0xEF -#define ECM_STREAM 0xF0 -#define EMM_STREAM 0xF1 -#define DSM_CC_STREAM 0xF2 -#define ISO13522_STREAM 0xF3 -#define PROG_STREAM_DIR 0xFF - -// data=PCM samples, 16 bit, LSB first, 48kHz, stereo -int cDVDplayBuffer::SendPCM(int size) -{ - -#define MAXSIZE 2032 - - uchar buffer[MAXSIZE + 16]; - int length = 0; - int p_size; - - if (ac3inp == ac3outp) - return 1; - - while (size > 0) { - if (size >= MAXSIZE) - p_size = MAXSIZE; - else - p_size = size; - length = 10; - - while (p_size) { - if (ac3outp != ac3inp) { // data in the buffer - buffer[(length + 6) ^ 1] = ac3data[ac3outp]; // swab because ac3dec delivers wrong byteorder - // XXX there is no 'swab' here??? (kls) - p_size--; - length++; - ac3outp = (ac3outp + 1) % AC3_BUFFER_SIZE; - } - else - break; - } - - buffer[0] = 0x00; - buffer[1] = 0x00; - buffer[2] = 0x01; - buffer[3] = PRIVATE_STREAM1; - - buffer[4] = (length >> 8) & 0xff; - buffer[5] = length & 0xff; - - buffer[6] = 0x80; - buffer[7] = 0x00; - buffer[8] = 0x00; - - buffer[9] = aLPCM; // substream ID - buffer[10] = 0x00; // other stuff (see DVD specs), ignored by driver - buffer[11] = 0x00; - buffer[12] = 0x00; - buffer[13] = 0x00; - buffer[14] = 0x00; - buffer[15] = 0x00; - - length += 6; - - putFrame(buffer, length); - size -= MAXSIZE; - } - return 0; -} - -void cDVDplayBuffer::playDecodedAC3(void) +void cDVDplayBuffer::handleAC3(unsigned char *sector, int length, uchar PTSflags, uchar *PTSdata) { - int ac3_datasize = (AC3_BUFFER_SIZE + ac3inp - ac3outp) % AC3_BUFFER_SIZE; - - if (ac3_datasize) { - if (ac3_datasize > 1024 * 48) - SendPCM(3096); - else if (ac3_datasize > 1024 * 32) - SendPCM(1536); - else if (ac3_datasize > 1024 * 16 && !(lpcm_count % 2)) - SendPCM(1536); - else if (ac3_datasize && !(lpcm_count % 4)) - SendPCM(1536); - lpcm_count++; - } - else - lpcm_count=0; -} - -void cDVDplayBuffer::handleAC3(unsigned char *sector, int length) -{ - if (dolbyDev) { - while (length > 0) { - int w = fwrite(sector, 1, length , dolbyDev); - if (w < 0) { - LOG_ERROR; - break; - } - length -= w; - sector += w; - } - } - else { - if (ac3stat == AC3_PLAY) - ac3dec_decode_data(sector, sector + length, 0, &ac3inp, &ac3outp, (char *)ac3data); - else if (ac3stat == AC3_START) { - ac3dec_decode_data(sector, sector + length, 1, &ac3inp, &ac3outp, (char *)ac3data); - ac3stat = AC3_PLAY; - } - } - //playDecodedAC3(); -} - -void cDVDplayBuffer::putFrame(unsigned char *sector, int length) -{ - cFrame *frame = new cFrame(sector, length); - while (Busy() && !blockInput && !Put(frame)) - ; +#define PCM_FRAME_SIZE 1536 + AC3toPCM.Put(sector, length); + cFrame *frame; + if ((frame = AC3toPCM.Get(PCM_FRAME_SIZE, PTSflags, PTSdata)) != NULL) + putFrame(frame); + while ((frame = AC3toPCM.Get(PCM_FRAME_SIZE)) != NULL) + putFrame(frame); } int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) { + //XXX kls 2001-11-03: do we really need all these different return values? uchar pt = 1; -#if 0 - uchar *osect = sector; -#endif //make sure we got a PS packet header if (!PacketStart(§or, DVD_VIDEO_LB_LEN) && GetPacketType(sector) != 0xBA) { @@ -2017,6 +2056,8 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) int datalen = r; sector[6] &= 0x8f; + uchar PTSflags = sector[7] >> 6; + uchar *PTSdata = sector + 9; uchar *data = sector; switch (GetPacketType(sector)) { @@ -2025,6 +2066,7 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) ScanVideoPacket(sector, r, &pt); if (trickMode && pt != 1) return pt; + putFrame(sector, r, ftVideo); break; } case AUDIO_STREAM_S ... AUDIO_STREAM_E: { @@ -2033,6 +2075,7 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) return 1; if (audioTrack != GetPacketType(sector)) return 5; + putFrame(sector, r, ftAudio); break; } case PRIVATE_STREAM1: @@ -2060,14 +2103,15 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) if (audioTrack == *data) { switch (audioTrack & 0xF8) { case aAC3: + if (dolbyDev) + putFrame(sector, r, ftDolby); data += 4; - // correct a3 data lenght - FIXME: why 13 ??? - datalen -= 13; - handleAC3(data, datalen); + datalen -= 13; // 3 (mandatory header) + 6 (PS header) + 4 (AC3 header) = 13 + handleAC3(data, datalen, PTSflags, PTSdata); break; case aLPCM: // write(audio, sector+14 , sector[19]+(sector[18]<<8)+6); - putFrame(sector, GetPacketLength(sector)); + putFrame(sector, GetPacketLength(sector), ftAudio); break; default: break; @@ -2096,9 +2140,6 @@ int cDVDplayBuffer::decode_packet(unsigned char *sector, bool trickMode) return pt; } } - putFrame(sector, r); - if ((audioTrack & 0xF8) == aAC3) - playDecodedAC3(); return pt; } @@ -2106,8 +2147,7 @@ void cDVDplayBuffer::Empty(bool Block) { if (!(blockInput || blockOutput)) { cPlayBuffer::Empty(true); - ac3stat = AC3_START; - ac3outp = ac3inp; + AC3toPCM.Clear(); } if (!Block) cPlayBuffer::Empty(false); @@ -2149,9 +2189,7 @@ void cDVDplayBuffer::SkipSeconds(int Seconds) Empty(true); chapid = newchapid; NextState(cOPENCHAPTER); - if (ac3stat != AC3_STOP) - ac3stat = AC3_START; - ac3outp = ac3inp; + AC3toPCM.Clear(); Empty(false); Play(); } @@ -2488,6 +2526,7 @@ char *cDvbApi::audioCommand = NULL; cDvbApi::cDvbApi(int n) { + frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0; siProcessor = NULL; recordBuffer = NULL; @@ -2518,10 +2557,6 @@ cDvbApi::cDvbApi(int n) fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK); fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK); - // Devices that may not be available, and are not necessary for normal operation: - - videoDev = OstOpen(DEV_VIDEO, n, O_RDWR); - // Devices that will be dynamically opened and closed when necessary: fd_dvr = -1; @@ -2536,15 +2571,14 @@ cDvbApi::cDvbApi(int n) siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n)); if (!dvbApi[0]) // only the first one shall set the system time siProcessor->SetUseTSTime(Setup.SetSystemTime); + FrontendInfo feinfo; + CHECK(ioctl(fd_frontend, FE_GET_INFO, &feinfo)); + frontendType = feinfo.type; } else esyslog(LOG_ERR, "ERROR: can't open video device %d", n); cols = rows = 0; - ovlGeoSet = ovlStat = ovlFbSet = false; - ovlBrightness = ovlColour = ovlHue = ovlContrast = 32768; - ovlClipCount = 0; - #if defined(DEBUG_OSD) || defined(REMOTE_KBD) initscr(); keypad(stdscr, true); @@ -2573,7 +2607,6 @@ cDvbApi::~cDvbApi() StopReplay(); StopRecord(); StopTransfer(); - OvlO(false); //Overlay off! // We're not explicitly closing any device files here, since this sometimes // caused segfaults. Besides, the program is about to terminate anyway... #if defined(DEBUG_OSD) || defined(REMOTE_KBD) @@ -2688,255 +2721,96 @@ const cSchedules *cDvbApi::Schedules(cThreadLock *ThreadLock) const bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) { - if (videoDev < 0) - return false; int result = 0; - // just do this once? - struct video_mbuf mbuf; - result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf); - int msize = mbuf.size; - // gf: this needs to be a protected member of cDvbApi! //XXX kls: WHY??? - unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); - if (!mem || mem == (unsigned char *)-1) - return false; - // set up the size and RGB - struct video_capability vc; - result |= ioctl(videoDev, VIDIOCGCAP, &vc); - struct video_mmap vm; - vm.frame = 0; - if ((SizeX > 0) && (SizeX <= vc.maxwidth) && - (SizeY > 0) && (SizeY <= vc.maxheight)) { - vm.width = SizeX; - vm.height = SizeY; - } - else { - vm.width = vc.maxwidth; - vm.height = vc.maxheight; - } - vm.format = VIDEO_PALETTE_RGB24; - // this needs to be done every time: - result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm); - result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame); - // make RGB out of BGR: - int memsize = vm.width * vm.height; - unsigned char *mem1 = mem; - for (int i = 0; i < memsize; i++) { - unsigned char tmp = mem1[2]; - mem1[2] = mem1[0]; - mem1[0] = tmp; - mem1 += 3; - } - - if (Quality < 0) - Quality = 255; //XXX is this 'best'??? - - isyslog(LOG_INFO, "grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); - FILE *f = fopen(FileName, "wb"); - if (f) { - if (Jpeg) { - // write JPEG file: - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpeg_stdio_dest(&cinfo, f); - cinfo.image_width = vm.width; - cinfo.image_height = vm.height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, Quality, true); - jpeg_start_compress(&cinfo, true); - - int rs = vm.width * 3; - JSAMPROW rp[vm.height]; - for (int k = 0; k < vm.height; k++) - rp[k] = &mem[rs * k]; - jpeg_write_scanlines(&cinfo, rp, vm.height); - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - } - else { - // write PNM file: - if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 || - fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) { - LOG_ERROR_STR(FileName); - result |= 1; + int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR); + if (videoDev >= 0) { + struct video_mbuf mbuf; + result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf); + if (result == 0) { + int msize = mbuf.size; + unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0); + if (mem && mem != (unsigned char *)-1) { + // set up the size and RGB + struct video_capability vc; + result |= ioctl(videoDev, VIDIOCGCAP, &vc); + struct video_mmap vm; + vm.frame = 0; + if ((SizeX > 0) && (SizeX <= vc.maxwidth) && + (SizeY > 0) && (SizeY <= vc.maxheight)) { + vm.width = SizeX; + vm.height = SizeY; + } + else { + vm.width = vc.maxwidth; + vm.height = vc.maxheight; + } + vm.format = VIDEO_PALETTE_RGB24; + result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm); + result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame); + // make RGB out of BGR: + int memsize = vm.width * vm.height; + unsigned char *mem1 = mem; + for (int i = 0; i < memsize; i++) { + unsigned char tmp = mem1[2]; + mem1[2] = mem1[0]; + mem1[0] = tmp; + mem1 += 3; + } + + if (Quality < 0) + Quality = 255; //XXX is this 'best'??? + + isyslog(LOG_INFO, "grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height); + FILE *f = fopen(FileName, "wb"); + if (f) { + if (Jpeg) { + // write JPEG file: + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpeg_stdio_dest(&cinfo, f); + cinfo.image_width = vm.width; + cinfo.image_height = vm.height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, Quality, true); + jpeg_start_compress(&cinfo, true); + + int rs = vm.width * 3; + JSAMPROW rp[vm.height]; + for (int k = 0; k < vm.height; k++) + rp[k] = &mem[rs * k]; + jpeg_write_scanlines(&cinfo, rp, vm.height); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + } + else { + // write PNM file: + if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 || + fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) { + LOG_ERROR_STR(FileName); + result |= 1; + } + } + fclose(f); + } + else { + LOG_ERROR_STR(FileName); + result |= 1; + } + munmap(mem, msize); } + else + result |= 1; } - fclose(f); - } - else { - LOG_ERROR_STR(FileName); - result |= 1; - } - - if (ovlStat && ovlGeoSet) { - // switch the Overlay on again (gf: why have i to do anything again?) - OvlG(ovlSizeX, ovlSizeY, ovlPosX, ovlPosY); - } - if (ovlFbSet) - OvlP(ovlBrightness, ovlColour, ovlHue, ovlContrast); - - munmap(mem, msize); - return result == 0; -} - -bool cDvbApi::OvlF(int SizeX, int SizeY, int FbAddr, int Bpp, int Palette) -{ - if (videoDev < 0) - return false; - int result = 0; - // get the actual X-Server settings??? - // plausibility-check problem: can't be verified w/o X-server!!! - if (SizeX <= 0 || SizeY <= 0 || FbAddr == 0 || Bpp / 8 > 4 || - Bpp / 8 <= 0 || Palette <= 0 || Palette > 13 || ovlClipCount < 0 || - SizeX > 4096 || SizeY > 4096) { - ovlFbSet = ovlGeoSet = false; - OvlO(false); - return false; - } - else { - dsyslog(LOG_INFO, "OvlF: %d %d %x %d %d", SizeX, SizeY, FbAddr, Bpp, Palette); - // this is the problematic part! - struct video_buffer vb; - result |= ioctl(videoDev, VIDIOCGFBUF, &vb); - vb.base = (void*)FbAddr; - vb.depth = Bpp; - vb.height = SizeY; - vb.width = SizeX; - vb.bytesperline = ((vb.depth + 1) / 8) * vb.width; - //now the real thing: setting the framebuffer - result |= ioctl(videoDev, VIDIOCSFBUF, &vb); - if (result) { - ovlFbSet = ovlGeoSet = false; - ovlClipCount = 0; - OvlO(false); - return false; - } - else { - ovlFbSizeX = SizeX; - ovlFbSizeY = SizeY; - ovlBpp = Bpp; - ovlPalette = Palette; - ovlFbSet = true; - return true; - } - } -} - -bool cDvbApi::OvlG(int SizeX, int SizeY, int PosX, int PosY) -{ - if (videoDev < 0) - return false; - int result = 0; - // get the actual X-Server settings??? - struct video_capability vc; - result |= ioctl(videoDev, VIDIOCGCAP, &vc); - if (!ovlFbSet) - return false; - if (SizeX < vc.minwidth || SizeY < vc.minheight || - SizeX > vc.maxwidth || SizeY>vc.maxheight -// || PosX > FbSizeX || PosY > FbSizeY -// PosX < -SizeX || PosY < -SizeY || - ) { - ovlGeoSet = false; - OvlO(false); - return false; - } - else { - struct video_window vw; - result |= ioctl(videoDev, VIDIOCGWIN, &vw); - vw.x = PosX; - vw.y = PosY; - vw.width = SizeX; - vw.height = SizeY; - vw.chromakey = ovlPalette; -#ifndef VID_TYPE_CHROMAKEY // name changed somewhere down the road in kernel 2.4.x -#define VID_TYPE_CHROMAKEY VIDEO_WINDOW_CHROMAKEY -#endif - vw.flags = VID_TYPE_CHROMAKEY; // VIDEO_WINDOW_INTERLACE; //VIDEO_CLIP_BITMAP; - vw.clips = ovlClipRects; - vw.clipcount = ovlClipCount; - result |= ioctl(videoDev, VIDIOCSWIN, &vw); - if (result) { - ovlGeoSet = false; - ovlClipCount = 0; - return false; - } - else { - ovlSizeX = SizeX; - ovlSizeY = SizeY; - ovlPosX = PosX; - ovlPosY = PosY; - ovlGeoSet = true; - ovlStat = true; - return true; - } + close(videoDev); } -} - -bool cDvbApi::OvlC(int ClipCount, CRect *cr) -{ - if (videoDev < 0) - return false; - if (ovlGeoSet && ovlFbSet) { - for (int i = 0; i < ClipCount; i++) { - ovlClipRects[i].x = cr[i].x; - ovlClipRects[i].y = cr[i].y; - ovlClipRects[i].width = cr[i].width; - ovlClipRects[i].height = cr[i].height; - ovlClipRects[i].next = &(ovlClipRects[i + 1]); - } - ovlClipCount = ClipCount; - //use it: - return OvlG(ovlSizeX, ovlSizeY, ovlPosX, ovlPosY); - } - return false; -} - -bool cDvbApi::OvlP(__u16 Brightness, __u16 Colour, __u16 Hue, __u16 Contrast) -{ - if (videoDev < 0) - return false; - int result = 0; - ovlBrightness = Brightness; - ovlColour = Colour; - ovlHue = Hue; - ovlContrast = Contrast; - struct video_picture vp; - if (!ovlFbSet) - return false; - result |= ioctl(videoDev, VIDIOCGPICT, &vp); - vp.brightness = Brightness; - vp.colour = Colour; - vp.hue = Hue; - vp.contrast = Contrast; - vp.depth = ovlBpp; - vp.palette = ovlPalette; // gf: is this always ok? VIDEO_PALETTE_RGB565; - result |= ioctl(videoDev, VIDIOCSPICT, &vp); return result == 0; } -bool cDvbApi::OvlO(bool Value) -{ - if (videoDev < 0) - return false; - int result = 0; - if (!ovlGeoSet && Value) - return false; - int one = 1; - int zero = 0; - result |= ioctl(videoDev, VIDIOCCAPTURE, Value ? &one : &zero); - ovlStat = Value; - if (result) { - ovlStat = false; - return false; - } - return true; -} - #ifdef DEBUG_OSD void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg) { @@ -3257,97 +3131,106 @@ eSetChannelResult cDvbApi::SetChannel(int ChannelNumber, int FrequencyMHz, char bool ChannelSynced = false; - if (fd_sec >= 0) { // DVB-S + switch (frontendType) { + case FE_QPSK: { // DVB-S - // Frequency offsets: + // Frequency offsets: - unsigned int freq = FrequencyMHz; - int tone = SEC_TONE_OFF; + unsigned int freq = FrequencyMHz; + int tone = SEC_TONE_OFF; - if (freq < (unsigned int)Setup.LnbSLOF) { - freq -= Setup.LnbFrequLo; - tone = SEC_TONE_OFF; - } - else { - freq -= Setup.LnbFrequHi; - tone = SEC_TONE_ON; - } + if (freq < (unsigned int)Setup.LnbSLOF) { + freq -= Setup.LnbFrequLo; + tone = SEC_TONE_OFF; + } + else { + freq -= Setup.LnbFrequHi; + tone = SEC_TONE_ON; + } - FrontendParameters Frontend; - Frontend.Frequency = freq * 1000UL; - Frontend.Inversion = INVERSION_AUTO; - Frontend.u.qpsk.SymbolRate = Srate * 1000UL; - Frontend.u.qpsk.FEC_inner = FEC_AUTO; + FrontendParameters Frontend; + Frontend.Frequency = freq * 1000UL; + Frontend.Inversion = INVERSION_AUTO; + Frontend.u.qpsk.SymbolRate = Srate * 1000UL; + Frontend.u.qpsk.FEC_inner = FEC_AUTO; - int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; + int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18; - // DiseqC: + // DiseqC: - secCommand scmd; - scmd.type = 0; - scmd.u.diseqc.addr = 0x10; - scmd.u.diseqc.cmd = 0x38; - scmd.u.diseqc.numParams = 1; - scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); + secCommand scmd; + scmd.type = 0; + scmd.u.diseqc.addr = 0x10; + scmd.u.diseqc.cmd = 0x38; + scmd.u.diseqc.numParams = 1; + scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0); - secCmdSequence scmds; - scmds.voltage = volt; - scmds.miniCommand = SEC_MINI_NONE; - scmds.continuousTone = tone; - scmds.numCommands = Setup.DiSEqC ? 1 : 0; - scmds.commands = &scmd; + secCmdSequence scmds; + scmds.voltage = volt; + scmds.miniCommand = SEC_MINI_NONE; + scmds.continuousTone = tone; + scmds.numCommands = Setup.DiSEqC ? 1 : 0; + scmds.commands = &scmd; - CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); + CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds)); - // Tuning: + // Tuning: - CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); + CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); - // Wait for channel sync: + // Wait for channel sync: - if (cFile::FileReady(fd_frontend, 5000)) { - FrontendEvent event; - int res = ioctl(fd_frontend, FE_GET_EVENT, &event); - if (res >= 0) - ChannelSynced = event.type == FE_COMPLETION_EV; - else - esyslog(LOG_ERR, "ERROR %d in frontend get event", res); - } - else - esyslog(LOG_ERR, "ERROR: timeout while tuning"); - } - else if (fd_frontend >= 0) { // DVB-C + if (cFile::FileReady(fd_frontend, 5000)) { + FrontendEvent event; + int res = ioctl(fd_frontend, FE_GET_EVENT, &event); + if (res >= 0) + ChannelSynced = event.type == FE_COMPLETION_EV; + else + esyslog(LOG_ERR, "ERROR %d in frontend get event", res); + } + else + esyslog(LOG_ERR, "ERROR: timeout while tuning"); + } + break; + case FE_QAM: { // DVB-C - // Frequency and symbol rate: + // Frequency and symbol rate: - FrontendParameters Frontend; - Frontend.Frequency = FrequencyMHz * 1000000UL; - Frontend.Inversion = INVERSION_AUTO; - Frontend.u.qam.SymbolRate = Srate * 1000UL; - Frontend.u.qam.FEC_inner = FEC_AUTO; - Frontend.u.qam.QAM = QAM_64; + FrontendParameters Frontend; + Frontend.Frequency = FrequencyMHz * 1000000UL; + Frontend.Inversion = INVERSION_AUTO; + Frontend.u.qam.SymbolRate = Srate * 1000UL; + Frontend.u.qam.FEC_inner = FEC_AUTO; + Frontend.u.qam.QAM = QAM_64; - // Tuning: + // Tuning: - CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); + CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend)); - // Wait for channel sync: + // Wait for channel sync: - if (cFile::FileReady(fd_frontend, 5000)) { - FrontendEvent event; - int res = ioctl(fd_frontend, FE_GET_EVENT, &event); - if (res >= 0) - ChannelSynced = event.type == FE_COMPLETION_EV; - else - esyslog(LOG_ERR, "ERROR %d in frontend get event", res); - } - else - esyslog(LOG_ERR, "ERROR: timeout while tuning"); - } - else { - esyslog(LOG_ERR, "ERROR: attempt to set channel without DVB-S or DVB-C device"); - return scrFailed; - } + if (cFile::FileReady(fd_frontend, 5000)) { + FrontendEvent event; + int res = ioctl(fd_frontend, FE_GET_EVENT, &event); + if (res >= 0) + ChannelSynced = event.type == FE_COMPLETION_EV; + else + esyslog(LOG_ERR, "ERROR %d in frontend get event", res); + } + else + esyslog(LOG_ERR, "ERROR: timeout while tuning"); + } + break; + case FE_OFDM: { // DVB-T + //XXX TODO: implement DVB-T tuning (anybody with a DVB-T card out there?) + esyslog(LOG_ERR, "ERROR: DVB-T tuning support not yet implemented"); + return scrFailed; + } + break; + default: + esyslog(LOG_ERR, "ERROR: attempt to set channel with unknown DVB frontend type"); + return scrFailed; + } if (!ChannelSynced) { esyslog(LOG_ERR, "ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1); |