vdr-dvd: drive speed limiter v2

Message ID 20071115230939.GA21872@section-eight
State New
Headers

Commit Message

Sebastian Nov. 15, 2007, 11:09 p.m. UTC
  Hi!

Here's a new version:

- set cmd_len correctly (libata checks for correct cmd_len before
  passing commands through)
- indent cleanup
- earlier entry point to slow down drive before playback starts

Todo:

- proper error handling
- try old SET CD/DVD SPEED command in case SET STREAMING isn't supported

Regards
Sebastian
  

Patch

diff -Nur dvd/i18n.c dvd.new/i18n.c
--- dvd/i18n.c	2007-11-13 15:59:36.000000000 +0100
+++ dvd.new/i18n.c	2007-11-13 15:51:06.000000000 +0100
@@ -280,6 +280,32 @@ 
 #endif
     },
     {
+     "Setup.DVD$DVD-ROM Speed",                             // English
+        "DVD-ROM-Geschwindigkeit",                          // Deutsch
+        "DVD-ROM Speed",                                    // Slovenski
+        "DVD-ROM Speed",                                    // Italiano
+        "DVD-ROM Speed",                                    // Nederlands
+        "DVD-ROM Speed",                                    // Português
+        "DVD-ROM Speed",                                    // Français
+        "DVD-ROM Speed",                                    // Norsk
+        "DVD-ROM Speed",                                    // suomi
+        "DVD-ROM Speed",                                    // Polski
+        "DVD-ROM Speed",                                    // Español
+        "DVD-ROM Speed",                                    // ÅëëçíéêÜ (Greek)
+        "DVD-ROM Speed",                                    // Svenska
+        "DVD-ROM Speed",                                    // Romaneste
+        "DVD-ROM Speed",                                    // Magyar
+        "DVD-ROM Speed",                                    // Català
+        "DVD-ROM Speed",                                    // ÀãááÚØÙ (Russian)
+        "DVD-ROM Speed",                                    // Hrvatski (Croatian)
+        "DVD-ROM Speed",                                    // Eesti
+        "DVD-ROM Speed",                                    // Dansk
+        "DVD-ROM Speed",                                    // Czech
+#if VDRVERSNUM >= 10502
+        "DVD-ROM Speed"                                     // Türkçe
+#endif
+    },
+    {
     "Setup.DVD$Gain (analog)",
         "Verstärkung (analog)",                             // Deutsch
         "Ojaèanje (analogno)",                              // Slovenski
diff -Nur dvd/player-dvd.c dvd.new/player-dvd.c
--- dvd/player-dvd.c	2007-09-17 21:04:43.000000000 +0200
+++ dvd.new/player-dvd.c	2007-11-13 15:58:50.000000000 +0100
@@ -35,6 +35,11 @@ 
 #include "control-dvd.h"
 #include "dvd.h"
 
+/* Needed for DvdSetSpeed() */
+#include <linux/cdrom.h>
+#include <scsi/sg.h>
+#include <sys/ioctl.h>
+
 /**
  * this was "weak"'s solution of a forced
  * SPU only stream choice,
@@ -252,6 +257,7 @@ 
 bool cDvdPlayer::HasBitStreamOut = false;
 bool cDvdPlayer::HasSoftDeviceOut = false;
 bool cDvdPlayer::SoftDeviceOutActive = false;
+bool cDvdPlayer::DvdSetSpeedActive = false;
 
 const int cDvdPlayer::MaxAudioTracks    = 0x20;
 const int cDvdPlayer::AudioTrackMask    = 0x1F;
@@ -565,6 +571,93 @@ 
 #endif
 }
 
+/* This function was ripped off of mplayer */
+void cDvdPlayer::DvdSetSpeed(const char *device, int speed)
+{
+#if defined(SG_IO) && defined(GPCMD_SET_STREAMING)
+    int fd;
+    unsigned char buffer[28];
+    unsigned char cmd[12];
+    unsigned char sense[16];
+    struct sg_io_hdr sghdr;
+    struct stat st;
+
+    memset(&sghdr, 0, sizeof(sghdr));
+    memset(buffer, 0, sizeof(buffer));
+    memset(sense, 0, sizeof(sense));
+    memset(cmd, 0, sizeof(cmd));
+    memset(&st, 0, sizeof(st));
+
+    if (stat(device, &st) == -1) {
+        esyslog("ERROR: dvd-plugin: DVD device %s doesn't exist", device);
+        return;
+    }
+
+    if (!S_ISBLK(st.st_mode)) {
+        esyslog("ERROR: dvd-plugin: DVD device %s is not a block device", device);
+        return;
+    }
+
+    if ((fd = open(device, O_RDWR | O_NONBLOCK)) == -1) {
+        esyslog("ERROR: dvd-plugin: Failed to open DVD device %s O_RDWR | O_NONBLOCK", device);
+        return;
+    }
+
+    if (speed < 100 && speed > 0) { /* speed times 1350KB/s (DVD single speed) */
+        speed *= 1350;
+    }
+
+    switch (speed) {
+        case 0: /* don't touch speed setting */
+            close(fd);
+            return;
+        case -1: /* restore default value */
+            speed = 0;
+            buffer[0] = 4; /* restore default */
+            isyslog("dvd-plugin: Restoring initial DVD drive speed");
+            break;
+        default: /* limit to <speed> KB/s */
+            isyslog("dvd-plugin: Limiting speed to %d KB/s", speed);
+            break;
+    }
+
+    sghdr.interface_id = 'S';
+    sghdr.timeout = 5000;
+    sghdr.dxfer_direction = SG_DXFER_TO_DEV;
+    sghdr.mx_sb_len = sizeof(sense);
+    sghdr.dxfer_len = sizeof(buffer);
+    sghdr.cmd_len = sizeof(cmd);
+    sghdr.sbp = sense;
+    sghdr.dxferp = buffer;
+    sghdr.cmdp = cmd;
+
+    cmd[0] = GPCMD_SET_STREAMING;
+    cmd[10] = sizeof(buffer);
+
+    buffer[8] = 0xff;  /* first sector 0, last sector 0xffffffff */
+    buffer[9] = 0xff;
+    buffer[10] = 0xff;
+    buffer[11] = 0xff;
+
+    buffer[12] = buffer[20] = (speed >> 24) & 0xff; /* <speed> kilobyte */
+    buffer[13] = buffer[21] = (speed >> 16) & 0xff;
+    buffer[14] = buffer[22] = (speed >> 8)  & 0xff;
+    buffer[15] = buffer[23] = speed & 0xff;
+
+    buffer[18] = buffer[26] = 0x03; /* 1 second */
+    buffer[19] = buffer[27] = 0xe8;
+
+    if (ioctl(fd, SG_IO, &sghdr) < 0) {
+        esyslog("ERROR: dvd-plugin: DVD speed limiting failed");
+        close(fd);
+        return;
+    }
+    isyslog("dvd-plugin: DVD speed limiting successful");
+    DvdSetSpeedActive = true;
+    close(fd);
+#endif
+}
+
 void cDvdPlayer::Action(void) {
     memset(event_buf, 0, sizeof(uint8_t)*4096);
 
@@ -590,12 +683,21 @@ 
     }
     dsyslog("dvd-plugin: SoftDeviceOutActive=%d, HasSoftDeviceOut=%d", SoftDeviceOutActive, HasSoftDeviceOut);
 
+    /* Try to reduce drive speed if the user wants us to */
+    if (DVDSetup.Speed)
+        DvdSetSpeed(const_cast<char *>(cDVD::getDVD()->DeviceName()), DVDSetup.Speed);
+
     if (dvdnav_open(&nav, const_cast<char *>(cDVD::getDVD()->DeviceName())) != DVDNAV_STATUS_OK) {
         Skins.Message(mtError, tr("Error.DVD$Error opening DVD!"));
         esyslog("ERROR: dvd-plugin cannot open dvdnav device %s -> input thread ended (pid=%d) !", const_cast<char *>(cDVD::getDVD()->DeviceName()), getpid());
         active = running = false;
         nav=NULL;
         fflush(NULL);
+        /* Try to restore drive speed if it was previously changed */
+        if (DvdSetSpeedActive) {
+            DvdSetSpeed(const_cast<char *>(cDVD::getDVD()->DeviceName()), -1);
+            DvdSetSpeedActive = false;
+        }
         return;
     }
     dvdnav_set_readahead_flag(nav, DVDSetup.ReadAHead);
@@ -1203,6 +1305,12 @@ 
   dvdnav_close(nav);
   nav=NULL;
 
+  /* Try to restore drive speed if it was previously changed */
+  if (DvdSetSpeedActive) {
+      DvdSetSpeed(const_cast<char *>(cDVD::getDVD()->DeviceName()), -1);
+      DvdSetSpeedActive = false;
+  }
+
   DEBUGDVD("%s:%d: input thread ended (pid=%d)\n", __FILE__, __LINE__, getpid());
   fflush(NULL);
 }
diff -Nur dvd/player-dvd.h dvd.new/player-dvd.h
--- dvd/player-dvd.h	2007-09-17 21:04:43.000000000 +0200
+++ dvd.new/player-dvd.h	2007-11-13 15:51:06.000000000 +0100
@@ -168,6 +168,7 @@ 
     static bool HasBitStreamOut;
     static bool SoftDeviceOutActive; // currently used to switch for xine
     static bool HasSoftDeviceOut;    // currently used to switch for xine
+    static bool DvdSetSpeedActive;
 
     //dvd stuff
     int currButtonN;
@@ -229,6 +230,7 @@ 
     void DrawSPU();
     void HideSPU();
     void EmptySPU();
+    void DvdSetSpeed(const char*, int);
 
     void Pause(void);
     void Play(void);
diff -Nur dvd/setup-dvd.c dvd.new/setup-dvd.c
--- dvd/setup-dvd.c	2007-11-13 15:59:36.000000000 +0100
+++ dvd.new/setup-dvd.c	2007-11-13 15:51:06.000000000 +0100
@@ -35,6 +35,7 @@ 
     Gain          = 4;
 
     AC3dynrng     = 0;
+    Speed         = 0;
 }
 
 bool cDVDSetup::SetupParse(const char *Name, const char *Value)
@@ -47,6 +48,7 @@ 
     else if (!strcasecmp(Name, "ShowSubtitles")) ShowSubtitles = atoi(Value);
     else if (!strcasecmp(Name, "HideMainMenu"))  HideMainMenu  = atoi(Value);
     else if (!strcasecmp(Name, "ReadAHead"))     ReadAHead     = atoi(Value);
+    else if (!strcasecmp(Name, "Speed"))         Speed         = atoi(Value);
     else if (!strcasecmp(Name, "Gain"))          Gain          = atoi(Value);
     else if (!strcasecmp(Name, "AC3dynrng"))     AC3dynrng     = atoi(Value);
     else
@@ -73,6 +75,7 @@ 
     Add(new cMenuEditBoolItem(tr("Setup.DVD$Display subtitles"),           &data.ShowSubtitles));
     Add(new cMenuEditBoolItem(tr("Setup.DVD$Hide Mainmenu Entry"),         &data.HideMainMenu));
     Add(new cMenuEditBoolItem(tr("Setup.DVD$ReadAHead"),                   &data.ReadAHead));
+    Add(new cMenuEditIntItem( tr("Setup.DVD$DVD-ROM Speed"),               &data.Speed, 0, 4));
     Add(new cMenuEditIntItem( tr("Setup.DVD$Gain (analog)"),               &data.Gain, 0, 10));
     Add(new cMenuEditBoolItem(tr("Setup.DVD$A52 DRC"),                     &data.AC3dynrng));
 }
@@ -87,6 +90,7 @@ 
     SetupStore("ShowSubtitles", DVDSetup.ShowSubtitles );
     SetupStore("HideMainMenu",  DVDSetup.HideMainMenu  );
     SetupStore("ReadAHead",     DVDSetup.ReadAHead  );
+    SetupStore("Speed",         DVDSetup.Speed  );
     SetupStore("Gain",          DVDSetup.Gain  );
     SetupStore("AC3dynrng",     DVDSetup.AC3dynrng  );
 }
diff -Nur dvd/setup-dvd.h dvd.new/setup-dvd.h
--- dvd/setup-dvd.h	2005-01-05 17:32:21.000000000 +0100
+++ dvd.new/setup-dvd.h	2007-11-13 15:51:06.000000000 +0100
@@ -23,6 +23,7 @@ 
     int HideMainMenu;
     int ReadAHead;
     int Gain;
+    int Speed;
 
     // AC3 stuff
     int AC3dynrng;