diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0627c37b..fd220b16 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -19,6 +19,7 @@ Heino Goldenstein Guido Fiala for implementing slow forward/back for implementing the SVDRP command 'HITK' + for implementing image grabbing Robert Schneider for implementing EIT support for displaying the current/next info diff --git a/HISTORY b/HISTORY index eb7f7009..6fade416 100644 --- a/HISTORY +++ b/HISTORY @@ -182,7 +182,8 @@ Video Disk Recorder Revision History response time on user actions. As a consequence the EIT data may sometimes not be displayed, but this will change later when cEIT runs as a separate thread. -- The new SVDRP command 'HITK' can be used to 'hit' a remote control key. - Establish an SVDRP connection and enter HITK without a parameter for a list - of all valid key names. - +- The new SVDRP command 'HITK' (thanks to Guido Fiala!) can be used to 'hit' + a remote control key. Establish an SVDRP connection and enter HITK without + a parameter for a list of all valid key names. +- The new SVDRP command 'GRAB' (thanks to Guido Fiala!) can be used to grab + the current frame and save it to a file. diff --git a/Makefile b/Makefile index 2f00459b..583a3f84 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.9 2000/09/10 08:55:45 kls Exp $ +# $Id: Makefile 1.10 2000/09/17 10:19:44 kls Exp $ DVBDIR = ../DVB @@ -40,7 +40,7 @@ tools.o : tools.c tools.h videodir.o : videodir.c tools.h videodir.h vdr: $(OBJS) - g++ -g -O2 $(OBJS) -lncurses -o vdr + g++ -g -O2 $(OBJS) -lncurses -ljpeg -o vdr clean: -rm $(OBJS) vdr diff --git a/dvbapi.c b/dvbapi.c index 6c08e065..2c8a6ba1 100644 --- a/dvbapi.c +++ b/dvbapi.c @@ -4,14 +4,18 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbapi.c 1.25 2000/09/15 13:23:00 kls Exp $ + * $Id: dvbapi.c 1.26 2000/09/17 11:53:35 kls Exp $ */ #include "dvbapi.h" #include #include +extern "C" { +#include +} #include #include +#include #include #include #include @@ -315,8 +319,8 @@ char *cIndexFile::Str(int Index, bool WithFrame) static char buffer[16]; int f = (Index % FRAMESPERSEC) + 1; int s = (Index / FRAMESPERSEC); - int m = s / 60 % 60; - int h = s / 3600; + int m = s / 60 % 60; + int h = s / 3600; s %= 60; snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f); return buffer; @@ -511,7 +515,7 @@ protected: char *fileName, *pFileNumber; bool stop; int GetAvPesLength(void) - { + { if (Byte(0) == 'A' && Byte(1) == 'V' && Byte(4) == 'U') return (Byte(6) << 8) + Byte(7) + AV_PES_HEADER_LEN; return 0; @@ -751,7 +755,7 @@ int cRecordBuffer::Write(int Max) if (n) { if (stop && pictureType == I_FRAME) { ok = false; - return -1; // finish the recording before the next 'I' frame + return -1; // finish the recording before the next 'I' frame } if (NextFile()) { if (index && pictureType != NO_PICTURE) @@ -801,7 +805,7 @@ private: void Close(void); public: cReplayBuffer(int *OutFile, const char *FileName); - virtual ~cReplayBuffer(); + virtual ~cReplayBuffer(); virtual int Read(int Max = -1); virtual int Write(int Max = -1); void SetMode(eReplayMode Mode); @@ -1067,7 +1071,7 @@ cDvbApi::cDvbApi(const char *FileName) cols = rows = 0; #if defined(DEBUG_OSD) || defined(REMOTE_KBD) initscr(); - keypad(stdscr, TRUE); + keypad(stdscr, true); nonl(); cbreak(); noecho(); @@ -1076,7 +1080,7 @@ cDvbApi::cDvbApi(const char *FileName) #if defined(DEBUG_OSD) memset(&colorPairs, 0, sizeof(colorPairs)); start_color(); - leaveok(stdscr, TRUE); + leaveok(stdscr, true); window = NULL; #endif lastProgress = lastTotal = -1; @@ -1187,6 +1191,93 @@ void cDvbApi::Cleanup(void) PrimaryDvbApi = NULL; } +bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY) +{ + 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; + } + } + fclose(f); + } + else { + LOG_ERROR_STR(FileName); + result |= 1; + } + munmap(mem, msize); + return result == 0; +} + #ifdef DEBUG_OSD void cDvbApi::SetColor(eDvbColor colorFg, eDvbColor colorBg) { @@ -1233,7 +1324,7 @@ void cDvbApi::Open(int w, int h) rows = h; #ifdef DEBUG_OSD window = subwin(stdscr, h, w, d, 0); - syncok(window, TRUE); + syncok(window, true); #define B2C(b) (((b) * 1000) / 255) #define SETCOLOR(n, r, g, b, o) init_color(n, B2C(r), B2C(g), B2C(b)) #else diff --git a/dvbapi.h b/dvbapi.h index a43e7075..5ae3a513 100644 --- a/dvbapi.h +++ b/dvbapi.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbapi.h 1.14 2000/09/10 10:03:29 kls Exp $ + * $Id: dvbapi.h 1.15 2000/09/17 11:43:10 kls Exp $ */ #ifndef __DVBAPI_H @@ -70,6 +70,10 @@ public: // Closes down all DVB devices. // Must be called at the end of the program. + // Image Grab facilities + + bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + // On Screen Display facilities private: diff --git a/svdrp.c b/svdrp.c index fe25c8bf..09cffe3d 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 1.8 2000/09/17 09:24:52 kls Exp $ + * $Id: svdrp.c 1.9 2000/09/17 11:29:33 kls Exp $ */ #define _GNU_SOURCE @@ -120,6 +120,10 @@ const char *HelpPages[] = { " Delete channel.", "DELT \n" " Delete timer.", + "GRAB [ jpeg | pnm [ [ ] ] ]\n" + " Grab the current frame and save it to the given file. Images can\n" + " be stored as JPEG (default) or PNM, at the given quality (default\n" + " is 'maximum', only applies to JPEG) and size (default is full screen).", "HELP [ ]\n" " The HELP command gives help info.", "HITK [ ]\n" @@ -363,6 +367,67 @@ void cSVDRP::CmdDELT(const char *Option) Reply(501, "Missing timer number"); } +void cSVDRP::CmdGRAB(const char *Option) +{ + char *FileName = NULL; + bool Jpeg = true; + int Quality = -1, SizeX = -1, SizeY = -1; + if (*Option) { + char buf[strlen(Option) + 1]; + char *p = strcpy(buf, Option); + const char *delim = " \t"; + FileName = strtok(p, delim); + if ((p = strtok(NULL, delim)) != NULL) { + if (strcasecmp(p, "JPEG") == 0) + Jpeg = true; + else if (strcasecmp(p, "PNM") == 0) + Jpeg = false; + else { + Reply(501, "Unknown image type \"%s\"", p); + return; + } + } + if ((p = strtok(NULL, delim)) != NULL) { + if (isnumber(p)) + Quality = atoi(p); + else { + Reply(501, "Illegal quality \"%s\"", p); + return; + } + } + if ((p = strtok(NULL, delim)) != NULL) { + if (isnumber(p)) + SizeX = atoi(p); + else { + Reply(501, "Illegal sizex \"%s\"", p); + return; + } + if ((p = strtok(NULL, delim)) != NULL) { + if (isnumber(p)) + SizeY = atoi(p); + else { + Reply(501, "Illegal sizey \"%s\"", p); + return; + } + } + else { + Reply(501, "Missing sizey"); + return; + } + } + if ((p = strtok(NULL, delim)) != NULL) { + Reply(501, "Unexpected parameter \"%s\"", p); + return; + } + if (cDvbApi::PrimaryDvbApi->GrabImage(FileName, Jpeg, Quality, SizeX, SizeY)) + Reply(250, "Grabbed image %s", Option); + else + Reply(451, "Grab image failed"); + } + else + Reply(501, "Missing filename"); +} + void cSVDRP::CmdHELP(const char *Option) { if (*Option) { @@ -636,6 +701,7 @@ void cSVDRP::Execute(char *Cmd) if (CMD("CHAN")) CmdCHAN(s); else if (CMD("DELC")) CmdDELC(s); else if (CMD("DELT")) CmdDELT(s); + else if (CMD("GRAB")) CmdGRAB(s); else if (CMD("HELP")) CmdHELP(s); else if (CMD("HITK")) CmdHITK(s); else if (CMD("LSTC")) CmdLSTC(s); diff --git a/svdrp.h b/svdrp.h index 6c1fbb29..134332a0 100644 --- a/svdrp.h +++ b/svdrp.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: svdrp.h 1.4 2000/09/17 08:52:51 kls Exp $ + * $Id: svdrp.h 1.5 2000/09/17 10:22:49 kls Exp $ */ #ifndef __SVDRP_H @@ -35,6 +35,7 @@ private: void CmdCHAN(const char *Option); void CmdDELC(const char *Option); void CmdDELT(const char *Option); + void CmdGRAB(const char *Option); void CmdHELP(const char *Option); void CmdHITK(const char *Option); void CmdLSTC(const char *Option);