#!/bin/sh /usr/share/dpatch/dpatch-run ## image patch from eloy (vdrportal.de) ## ## original file: vdr-1.3.11_mp3-0.9.4_image-0.1.diff ## see: http://www.vdrportal.de/board/thread.php?postid=194766#post194766 ## ## adapted to VDR-1.3.18 by Thomas Günther ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: Displays images while playing mp3 @DPATCH@ diff -Nru mp3-0.9.4-orig/data-mp3.c mp3-0.9.4/data-mp3.c --- mp3-0.9.4-orig/data-mp3.c 2004-09-01 12:05:41.000000000 +0200 +++ mp3-0.9.4/data-mp3.c 2004-09-05 14:35:43.000000000 +0200 @@ -332,7 +332,13 @@ void cInstantPlayList::DoItem(cFileSource *src, const char *subdir, const char *name) { - Add(new cSong(src,subdir,name)); + if (strlen (name) > 4) + { + const char *suffix = &name[strlen (name) - 3]; + if (strcasecmp (suffix, "gif") && strcasecmp (suffix, "jpg") && + strcasecmp (suffix, "png")) + Add(new cSong(src,subdir,name)); + } } // -- cPlayLists -------------------------------------------------------------- diff -Nru mp3-0.9.4-orig/data-mp3.h mp3-0.9.4/data-mp3.h --- mp3-0.9.4-orig/data-mp3.h 2004-09-01 12:30:19.000000000 +0200 +++ mp3-0.9.4/data-mp3.h 2004-09-05 14:35:43.000000000 +0200 @@ -57,6 +57,7 @@ cSongInfo *Info(bool get=true); cDecoder *Decoder(void); inline const char *Name(void) { return obj->Name(); } + inline const char *Fullname(void) { return obj->FullPath (); } }; // ---------------------------------------------------------------- diff -Nru mp3-0.9.4-orig/examples/image_convert.sh.example mp3-0.9.4/examples/image_convert.sh.example --- mp3-0.9.4-orig/examples/image_convert.sh.example 1970-01-01 01:00:00.000000000 +0100 +++ mp3-0.9.4/examples/image_convert.sh.example 2004-09-05 14:35:43.000000000 +0200 @@ -0,0 +1,90 @@ +#!/bin/sh +# +# requires: ...topnm, pnmscale, pnmcomp, ppmntsc, ppmtoy4m, mpeg2enc +# + +PATH=${PATH}:/usr/local/bin +SELF=`basename $0 .sh` +VDR_DIR=/usr/local/vdr +BACKGROUND=${VDR_DIR}/etc/mp3_background.pnm + +IMG=$1 +MPG=$2 + +DW=704 # destination frame width +DH=576 # destination frame height + +TW=632 # target image width (taking into account visible screen area) +TH=512 # target image height + +IW=0 # image width +IH=0 # image height + +if [ -s "${MPG}" ] +then + logger "${SELF}: $MPG already exists" +else + logger "${SELF}: $IMG -> $MPG" + DIR=`dirname "${MPG}"` + if [ ! -d "${DIR}" ] + then + mkdir -p "${DIR}" + fi + # + # get the file type and set the according converter to PNM + # + FILE_DETECT=$(file -i -L -b "${IMG}" 2>/dev/null) + FILE_TYPE=$(echo "$FILE_DETECT"|cut -f1 -d";"|cut -f1 -d"/") + FILE_SUB_TYPE=$(echo "$FILE_DETECT"|cut -f1 -d";"|cut -f2 -d"/") + + case "$FILE_SUB_TYPE" in + jpg | jpeg) + TO_PNM=jpegtopnm + ;; + tiff) + TO_PNM=tifftopnm + ;; + bmp | x-bmp) + TO_PNM=bmptoppm + ;; + png | x-png) + TO_PNM=pngtopnm + ;; + Netpbm | pnm | x-portable-pixmap) + TO_PNM=cat + ;; + gif) + TO_PNM=giftopnm + ;; + *) + echo "file '$FILE_TYPE/$FILE_SUB_TYPE' is not of supported type , EXIT 1" 1>&2 + exit 1 + ;; + esac + # + # extract the image size + # + $TO_PNM "${IMG}" > /tmp/image.pnm 2> /dev/null + IMG_SIZE=`pnmfile /tmp/image.pnm | awk '{ printf "%dx%d", $4, $6 }'` + IW=`echo $IMG_SIZE | cut -d'x' -f1` + IH=`echo $IMG_SIZE | cut -d'x' -f2` + # + # compute the scaling factor and the target image size + # + S=`echo $IW ${TW} $IH ${TH} | awk '{ sw = $2 / $1; sh = $4 / $3; s = (sw < sh) ? sw : sh; } END { if (s < 1) print s; else print 1.0 }'` + IW=`echo $IW $S | awk '{ printf "%.0f", $1 * $2 }'` + IH=`echo $IH $S | awk '{ printf "%.0f", $1 * $2 }'` + #echo "${SELF}: scaling by $S to ${IW}x${IH}" 1>&2 + # + # now run the conversion + # + pnmscale -xsize=${IW} -ysize=${IH} /tmp/image.pnm | \ + pnmcomp -align=center -valign=middle - $BACKGROUND | \ + ppmntsc --pal | \ + ppmtoy4m -v 0 -n 1 -r -F 25:1 | \ + mpeg2enc -f 7 -T 90 -F 3 -np -a 2 -v 0 -o "${MPG}" + # + # cleanup + # + rm /tmp/image.pnm +fi diff -Nru mp3-0.9.4-orig/mp3.c mp3-0.9.4/mp3.c --- mp3-0.9.4-orig/mp3.c 2004-09-03 17:54:33.000000000 +0200 +++ mp3-0.9.4/mp3.c 2004-09-05 14:36:31.000000000 +0200 @@ -60,6 +60,7 @@ cMenuSetupMP3::cMenuSetupMP3(void) { static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" }; + static const char *bg_modes[] = { tr ("Black"), tr ("Image"), tr ("Live") }; int numModes=0; aout[numModes]=tr("DVB"); amodes[numModes]=AUDIOOUTMODE_DVB; numModes++; #ifdef WITH_OSS @@ -80,7 +81,7 @@ Add(new cMenuEditStraItem(tr("Setup.MP3$Replay display"), &data.ReplayDisplay, 2, disp)); #endif Add(new cMenuEditIntItem( tr("Setup.MP3$Display mode"), &data.DisplayMode, 1, 3)); - Add(new cMenuEditBoolItem(tr("Setup.MP3$Background mode"), &data.BackgrMode, tr("Black"), tr("Live"))); + Add(new cMenuEditStraItem(tr("Setup.MP3$Background mode"), &data.BackgrMode, sizeof (bg_modes) / sizeof (bg_modes[0]), bg_modes)); Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial loop mode"), &data.InitLoopMode)); Add(new cMenuEditBoolItem(tr("Setup.MP3$Initial shuffle mode"), &data.InitShuffleMode)); Add(new cMenuEditBoolItem(tr("Setup.MP3$Abort player at end of list"),&data.AbortAtEOL)); @@ -102,6 +103,8 @@ Add(new cMenuEditStraItem(tr("Setup.MP3$CDDB for CD-Audio"), &data.UseCddb,3,cddb)); Add(new cMenuEditStrItem( tr("Setup.MP3$CDDB server"), data.CddbHost,MAX_HOSTNAME,allowed)); Add(new cMenuEditIntItem( tr("Setup.MP3$CDDB port"), &data.CddbPort,1,65535)); + Add(new cMenuEditStrItem( tr("Setup.MP3$Image Cache Dir"), data.ImageCacheDir,255,allowed)); + Add(new cMenuEditBoolItem(tr("Setup.MP3$UseDeviceStillPicture"), &data.UseDeviceStillPicture)); } void cMenuSetupMP3::Store(void) @@ -127,6 +130,8 @@ SetupStore("UseCddb", MP3Setup.UseCddb ); SetupStore("CddbHost", MP3Setup.CddbHost ); SetupStore("CddbPort", MP3Setup.CddbPort ); + SetupStore("ImageCacheDir", MP3Setup.ImageCacheDir ); + SetupStore("UseDeviceStillPicture", MP3Setup.UseDeviceStillPicture ); SetupStore("AbortAtEOL", MP3Setup.AbortAtEOL ); #if VDRVERSNUM >= 10307 SetupStore("ReplayDisplay", MP3Setup.ReplayDisplay ); @@ -1558,6 +1563,8 @@ } #endif } + else if (!strcasecmp(Name, "ImageCacheDir")) strn0cpy(MP3Setup.ImageCacheDir,Value,sizeof(MP3Setup.ImageCacheDir)); + else if (!strcasecmp(Name, "UseDeviceStillPicture")) MP3Setup.UseDeviceStillPicture = atoi(Value); #if VDRVERSNUM >= 10307 else if (!strcasecmp(Name, "ReplayDisplay")) MP3Setup.ReplayDisplay = atoi(Value); #endif diff -Nru mp3-0.9.4-orig/player-mp3.c mp3-0.9.4/player-mp3.c --- mp3-0.9.4-orig/player-mp3.c 2004-09-03 17:53:39.000000000 +0200 +++ mp3-0.9.4/player-mp3.c 2004-09-05 14:46:41.000000000 +0200 @@ -27,6 +27,8 @@ #include #endif +#include + #include #include @@ -1335,7 +1337,7 @@ { active=true; started=false; isStream=false; ringBuffer=new cRingBufferFrame(MP3BUFSIZE); - rframe=0; pframe=0; decoder=0; + rframe=0; pframe=0; iFrame=0; decoder=0; playMode=pmStartup; state=msStop; index=0; total=-1; playing=0; } @@ -1389,6 +1391,231 @@ } } +char *cMP3Player::CheckImage (char *fileName, size_t j) +{ + static char tmpFile[1024]; + char *tmp[2048]; + char *result = NULL; + FILE *fp; + + sprintf (tmpFile, "%s/%s", MP3Setup.ImageCacheDir, &fileName[j]); + strcpy (strrchr (tmpFile, '.'), ".mpg"); + d(printf("mp3[%d]: cache %s\n", getpid (), tmpFile)) + if ((fp = fopen (tmpFile, "rb"))) + { + fclose (fp); + result = tmpFile; + } + else + { + if ((fp = fopen (fileName, "rb"))) + { + fclose (fp); + d(printf("mp3[%d]: image %s found\n", getpid (), fileName)) + sprintf ((char *) tmp, "image_convert.sh \"%s\" \"%s\"", fileName, tmpFile); + system ((const char*) tmp); + result = tmpFile; + } + } + fp = fopen ("/tmp/vdr_mp3_current_image.txt", "w"); + fprintf (fp, "%s\n", fileName); + fclose (fp); + return result; +} + +char *cMP3Player::LoadImage(const char *fullname) +{ + size_t i, j = strlen (MP3Sources.GetSource()->BaseDir()) + 1; + char imageFile[1024]; + static char mpgFile[1024]; + char *p, *q = NULL; + char *imageSuffixes[] = { "png", "gif", "jpg" }; + + d(printf("mp3[%d]: checking %s for images\n", getpid (), fullname)) + strcpy (imageFile, fullname); + strcpy (mpgFile, ""); + // + // track specific image, e.g. .jpg + // + p = strrchr (imageFile, '.'); + if (p) + { + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + // + // album specific image, e.g. cover.jpg in song directory + // + if (!strlen (mpgFile)) + { + p = strrchr (imageFile, '/'); + if (p) + { + strcpy (p + 1, "cover."); + p += 6; + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + } + // + // artist specific image, e.g. artist.jpg in artist directory + // + if (!strlen (mpgFile)) + { + p = strrchr (imageFile, '/'); + if (p) + { + *p = '\0'; + p = strrchr (imageFile, '/'); + } + if (p) + { + strcpy (p + 1, "artist."); + p += 7; + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + strcpy (p + 1, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, j); + if (q) + { + strcpy (mpgFile, q); + } + } + } + } + // + // default background image + // + if (!strlen (mpgFile)) + { + for (i = 0; i < sizeof (imageSuffixes) / sizeof (imageSuffixes[0]); i++) + { + sprintf (imageFile, "%s/background.%s", MP3Setup.ImageCacheDir, imageSuffixes[i]); + d(printf("mp3[%d]: %s\n", getpid (), imageFile)) + q = CheckImage (imageFile, strlen(MP3Setup.ImageCacheDir) + 1); + if (q) + { + strcpy (mpgFile, q); + } + } + } + if (!strlen (mpgFile)) + { + sprintf (mpgFile, "%s/background.mpg", MP3Setup.ImageCacheDir); + } + return mpgFile; +} + +void cMP3Player::ShowImage (char *file) +{ + uchar *buffer; + int fd; + struct stat st; + struct video_still_picture sp; + + if ((fd = open (file, O_RDONLY)) >= 0) + { + d(printf("mp3[%d]: cover still picture %s\n", getpid (), file)) + fstat (fd, &st); + sp.iFrame = (char *) malloc (st.st_size); + if (sp.iFrame) + { + sp.size = st.st_size; + if (read (fd, sp.iFrame, sp.size) > 0) + { + buffer = (uchar *) sp.iFrame; + d(printf("mp3[%d]: new image frame (size %d)\n", getpid(), sp.size)) + if(MP3Setup.UseDeviceStillPicture) + DeviceStillPicture (buffer, sp.size); + else + { + for (int i = 1; i <= 25; i++) + { + send_pes_packet (buffer, sp.size, i); + } + } + } + free (sp.iFrame); + } + else + { + esyslog ("mp3[%d]: cannot allocate memory (%d bytes) for still image", + getpid(), (int) st.st_size); + } + close (fd); + } + else + { + esyslog ("mp3[%d]: cannot open image file '%s'", + getpid(), file); + } +} + +void cMP3Player::send_pes_packet(unsigned char *data, int len, int timestamp) +{ +#define PES_MAX_SIZE 2048 + int ptslen = timestamp ? 5 : 1; + static unsigned char pes_header[PES_MAX_SIZE]; + + pes_header[0] = pes_header[1] = 0; + pes_header[2] = 1; + pes_header[3] = 0xe0; + + while(len > 0) + { + int payload_size = len; + if(6 + ptslen + payload_size > PES_MAX_SIZE) + payload_size = PES_MAX_SIZE - (6 + ptslen); + + pes_header[4] = (ptslen + payload_size) >> 8; + pes_header[5] = (ptslen + payload_size) & 255; + + if(ptslen == 5) + { + int x; + x = (0x02 << 4) | (((timestamp >> 30) & 0x07) << 1) | 1; + pes_header[8] = x; + x = ((((timestamp >> 15) & 0x7fff) << 1) | 1); + pes_header[7] = x >> 8; + pes_header[8] = x & 255; + x = ((((timestamp) & 0x7fff) < 1) | 1); + pes_header[9] = x >> 8; + pes_header[10] = x & 255; + } else + { + pes_header[6] = 0x0f; + } + + memcpy(&pes_header[6 + ptslen], data, payload_size); +#if VDRVERSNUM < 10318 + PlayVideo(pes_header, 6 + ptslen + payload_size); +#else + PlayPes(pes_header, 6 + ptslen + payload_size); +#endif + + len -= payload_size; + data += payload_size; + ptslen = 1; + } +} + void cMP3Player::Action(void) { struct Decode *ds=0; @@ -1404,6 +1627,7 @@ #ifdef DEBUG int beat=0; #endif + char *currentImageFile = 0; dsyslog("mp3: player thread started (pid=%d)", getpid()); state=msStop; @@ -1444,6 +1668,18 @@ index=0; total=-1; playing=mgr->Current(); if(playing) { + if (MP3Setup.BackgrMode == 1) + { + char *tmp = LoadImage (playing->Fullname()); + if ((currentImageFile == NULL) || strcmp (tmp, currentImageFile)) + { + if (currentImageFile) + free (currentImageFile); + currentImageFile = strdup (tmp); + strcpy (currentImageFile, tmp); + ShowImage (currentImageFile); + } + } if((decoder=playing->Decoder()) && decoder->Start()) { isStream=decoder->IsStream(); levelgood=!isStream; haslevel=false; cSongInfo *si=playing->Info(true); diff -Nru mp3-0.9.4-orig/player-mp3.h mp3-0.9.4/player-mp3.h --- mp3-0.9.4-orig/player-mp3.h 2004-09-02 16:06:01.000000000 +0200 +++ mp3-0.9.4/player-mp3.h 2004-09-05 14:39:22.000000000 +0200 @@ -103,7 +103,7 @@ cSong *playing; int index, total; cDecoder *decoder; - cFrame *rframe, *pframe; + cFrame *rframe, *pframe, *iFrame; enum ePlayMode { pmPlay, pmStopped, pmPaused, pmStartup }; ePlayMode playMode; enum eState { msStart, msStop, msDecode, msNormalize, msResample, msOutput, msError, msEof, msWait, msRestart }; @@ -129,6 +129,10 @@ virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); bool Active(void) { return active; } bool IsStream(void) { return isStream; } + char *CheckImage(char *fileName, size_t j); + char *LoadImage(const char *fullname); + void ShowImage(char *file); + void send_pes_packet(unsigned char *data, int len, int timestamp); }; #endif //___DVB_MP3_H diff -Nru mp3-0.9.4-orig/setup-mp3.c mp3-0.9.4/setup-mp3.c --- mp3-0.9.4-orig/setup-mp3.c 2004-09-03 17:52:27.000000000 +0200 +++ mp3-0.9.4/setup-mp3.c 2004-09-05 14:43:47.000000000 +0200 @@ -48,6 +48,8 @@ strcpy(CddbHost,"freedb.freedb.org"); CddbPort = 888; AudioOutMode = 0; + strcpy(ImageCacheDir,"/var/cache/images/mp3"); + UseDeviceStillPicture = 1; AbortAtEOL = 1; ReplayDisplay = 0; HideMainMenu = 0; diff -Nru mp3-0.9.4-orig/setup-mp3.h mp3-0.9.4/setup-mp3.h --- mp3-0.9.4-orig/setup-mp3.h 2004-09-03 17:52:10.000000000 +0200 +++ mp3-0.9.4/setup-mp3.h 2004-09-05 14:43:36.000000000 +0200 @@ -61,6 +61,8 @@ char CddbHost[MAX_HOSTNAME]; int CddbPort; int AudioOutMode; + char ImageCacheDir[256]; + int UseDeviceStillPicture; int AbortAtEOL; int ReplayDisplay; int HideMainMenu;