mplayer.c
author nathan
Sun, 12 Dec 2010 11:31:54 +0100
branchtrunk
changeset 38 79b272a68eb4
parent 29 640ce9201139
child 39 ba6464ebc3f9
permissions -rw-r--r--
fix compile without OGG library
nathan@0
     1
/*
nathan@0
     2
 * MP3/MPlayer plugin to VDR (C++)
nathan@0
     3
 *
nathan@22
     4
 * (C) 2001-2009 Stefan Huelswitt <s.huelswitt@gmx.de>
nathan@0
     5
 *
nathan@0
     6
 * This code is free software; you can redistribute it and/or
nathan@0
     7
 * modify it under the terms of the GNU General Public License
nathan@0
     8
 * as published by the Free Software Foundation; either version 2
nathan@0
     9
 * of the License, or (at your option) any later version.
nathan@0
    10
 *
nathan@0
    11
 * This code is distributed in the hope that it will be useful,
nathan@0
    12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
nathan@0
    13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
nathan@0
    14
 * GNU General Public License for more details.
nathan@0
    15
 *
nathan@0
    16
 * You should have received a copy of the GNU General Public License
nathan@0
    17
 * along with this program; if not, write to the Free Software
nathan@0
    18
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
nathan@0
    19
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
nathan@0
    20
 */
nathan@0
    21
nathan@0
    22
#include <getopt.h>
nathan@0
    23
#include <malloc.h>
nathan@0
    24
#include <stdlib.h>
nathan@0
    25
#include <ctype.h>
nathan@0
    26
nathan@0
    27
#include "common.h"
nathan@0
    28
nathan@0
    29
#include <vdr/plugin.h>
nathan@0
    30
#include <vdr/player.h>
nathan@0
    31
#include <vdr/status.h>
nathan@0
    32
#include <vdr/font.h>
nathan@0
    33
#include <vdr/osdbase.h>
nathan@0
    34
#include <vdr/menuitems.h>
nathan@0
    35
#include <vdr/skins.h>
nathan@0
    36
#include <vdr/remote.h>
nathan@0
    37
#include <vdr/menu.h>
nathan@0
    38
nathan@0
    39
#include "setup.h"
nathan@0
    40
#include "setup-mplayer.h"
nathan@0
    41
#include "menu.h"
nathan@0
    42
#include "player-mplayer.h"
nathan@0
    43
#include "data.h"
nathan@0
    44
#include "data-src.h"
nathan@0
    45
#include "i18n.h"
nathan@0
    46
#include "version.h"
nathan@0
    47
#include "service.h"
nathan@0
    48
nathan@0
    49
const char *sourcesSub=0;
nathan@0
    50
cFileSources MPlaySources;
nathan@0
    51
nathan@2
    52
static const char *plugin_name=0;
nathan@12
    53
const char *i18n_name=0;
nathan@2
    54
nathan@0
    55
// --- cMenuSetupMPlayer --------------------------------------------------------
nathan@0
    56
nathan@0
    57
class cMenuSetupMPlayer : public cMenuSetupPage {
nathan@0
    58
private:
nathan@0
    59
  cMPlayerSetup data;
nathan@0
    60
  const char *res[3];
nathan@0
    61
protected:
nathan@0
    62
  virtual void Store(void);
nathan@0
    63
public:
nathan@0
    64
  cMenuSetupMPlayer(void);
nathan@0
    65
  };
nathan@0
    66
nathan@0
    67
cMenuSetupMPlayer::cMenuSetupMPlayer(void)
nathan@0
    68
{
nathan@0
    69
  data=MPlayerSetup;
nathan@0
    70
  SetSection(tr("MPlayer"));
nathan@0
    71
  Add(new cMenuEditBoolItem(tr("Setup.MPlayer$Control mode"),  &data.SlaveMode, tr("Traditional"), tr("Slave")));
nathan@0
    72
  res[0]=tr("disabled");
nathan@0
    73
  res[1]=tr("global only");
nathan@0
    74
  res[2]=tr("local first");
nathan@0
    75
  Add(new cMenuEditStraItem(tr("Setup.MPlayer$Resume mode"),   &data.ResumeMode, 3, res));
nathan@0
    76
  Add(new cMenuEditBoolItem(tr("Hide mainmenu entry"),         &data.HideMainMenu));
nathan@0
    77
  for(int i=0; i<10; i++) {
nathan@0
    78
    char name[32];
nathan@0
    79
    snprintf(name,sizeof(name),"%s %d",tr("Setup.MPlayer$Slave command key"),i);
nathan@0
    80
    static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789!\"§$%&/()=?{}[]\\+*~#',;.:-_<>|@´`^°" };
nathan@0
    81
    Add(new cMenuEditStrItem(name, data.KeyCmd[i],MAX_KEYCMD,allowed));
nathan@0
    82
    }
nathan@0
    83
}
nathan@0
    84
nathan@0
    85
void cMenuSetupMPlayer::Store(void)
nathan@0
    86
{
nathan@0
    87
  MPlayerSetup=data;
nathan@0
    88
  SetupStore("ControlMode", MPlayerSetup.SlaveMode);
nathan@0
    89
  SetupStore("HideMainMenu",MPlayerSetup.HideMainMenu);
nathan@0
    90
  SetupStore("ResumeMode",  MPlayerSetup.ResumeMode);
nathan@0
    91
  for(int i=0; i<10; i++) {
nathan@0
    92
    char name[16];
nathan@0
    93
    snprintf(name,sizeof(name),"KeyCmd%d",i);
nathan@0
    94
    SetupStore(name,MPlayerSetup.KeyCmd[i]);
nathan@0
    95
    }
nathan@0
    96
}
nathan@0
    97
nathan@0
    98
// --- cMPlayerControl ---------------------------------------------------------
nathan@0
    99
nathan@0
   100
class cMPlayerControl : public cControl {
nathan@0
   101
private:
nathan@0
   102
  static cFileObj *file;
nathan@0
   103
  static bool rewind;
nathan@0
   104
  cMPlayerPlayer *player;
nathan@0
   105
  cSkinDisplayReplay *display;
nathan@24
   106
  bool visible, modeOnly;
nathan@0
   107
  time_t timeoutShow;
nathan@0
   108
  int lastCurrent, lastTotal;
nathan@0
   109
  char *lastReplayMsg;
nathan@0
   110
  //
nathan@0
   111
  bool jumpactive, jumphide, jumpmode;
nathan@0
   112
  int jumpval;
nathan@0
   113
  //
nathan@0
   114
  void Stop(void);
nathan@0
   115
  void ShowTimed(int Seconds=0);
nathan@0
   116
  void ShowProgress(void);
nathan@0
   117
  void ShowMode(void);
nathan@0
   118
  void ShowTitle(void);
nathan@0
   119
  void Jump(void);
nathan@0
   120
  void JumpProcess(eKeys Key);
nathan@0
   121
  void JumpDisplay(void);
nathan@0
   122
public:
nathan@0
   123
  cMPlayerControl(void);
nathan@0
   124
  virtual ~cMPlayerControl();
nathan@0
   125
  virtual eOSState ProcessKey(eKeys Key);
nathan@0
   126
  virtual void Show(void) { ShowTimed(); }
nathan@0
   127
  virtual void Hide(void);
nathan@0
   128
  static void SetFile(const cFileObj *File, bool Rewind);
nathan@0
   129
  };
nathan@0
   130
nathan@0
   131
cFileObj *cMPlayerControl::file=0;
nathan@0
   132
bool cMPlayerControl::rewind=false;
nathan@0
   133
nathan@0
   134
cMPlayerControl::cMPlayerControl(void)
nathan@0
   135
:cControl(player=new cMPlayerPlayer(file,rewind))
nathan@0
   136
{
nathan@24
   137
  visible=modeOnly=jumpactive=false;
nathan@0
   138
  lastReplayMsg=0;
nathan@0
   139
  display=0;
nathan@0
   140
  ShowTitle();
nathan@0
   141
}
nathan@0
   142
nathan@0
   143
cMPlayerControl::~cMPlayerControl()
nathan@0
   144
{
nathan@0
   145
  Stop();
nathan@0
   146
  cStatus::MsgReplaying(this,0,0,false);
nathan@0
   147
  free(lastReplayMsg);
nathan@0
   148
}
nathan@0
   149
nathan@0
   150
void cMPlayerControl::SetFile(const cFileObj *File, bool Rewind)
nathan@0
   151
{
nathan@0
   152
  delete file;
nathan@0
   153
  file=File ? new cFileObj(File) : 0;
nathan@0
   154
  rewind=Rewind;
nathan@0
   155
}
nathan@0
   156
nathan@0
   157
void cMPlayerControl::Stop(void)
nathan@0
   158
{
nathan@0
   159
  delete player; player=0;
nathan@0
   160
}
nathan@0
   161
nathan@0
   162
void cMPlayerControl::ShowTimed(int Seconds)
nathan@0
   163
{
nathan@0
   164
  if(modeOnly) Hide();
nathan@0
   165
  if(!visible) {
nathan@0
   166
    ShowProgress();
nathan@0
   167
    timeoutShow = Seconds>0 ? time(0)+Seconds : 0;
nathan@0
   168
    }
nathan@0
   169
}
nathan@0
   170
nathan@0
   171
void cMPlayerControl::Hide(void)
nathan@0
   172
{
nathan@0
   173
  if(visible) {
nathan@0
   174
    delete display; display=0;
nathan@0
   175
    visible=modeOnly=false;
nathan@0
   176
#if APIVERSNUM >= 10500
nathan@0
   177
    SetNeedsFastResponse(false);
nathan@0
   178
#else
nathan@0
   179
    needsFastResponse=false;
nathan@0
   180
#endif
nathan@0
   181
    }
nathan@0
   182
}
nathan@0
   183
nathan@0
   184
void cMPlayerControl::ShowTitle(void)
nathan@0
   185
{
nathan@0
   186
  const char *path=0;
nathan@0
   187
  bool release=true;
nathan@0
   188
  if(player) path=player->GetCurrentName();
nathan@0
   189
  if(!path) {
nathan@0
   190
    path=file->FullPath();
nathan@0
   191
    release=false;
nathan@0
   192
    }
nathan@0
   193
  if(path) {
nathan@0
   194
    const char *name=rindex(path,'/');
nathan@0
   195
    if(name) name++; else name=path;
nathan@0
   196
    if(!lastReplayMsg || strcmp(lastReplayMsg,path)) {
nathan@0
   197
      cStatus::MsgReplaying(this,name,path,true);
nathan@0
   198
      free(lastReplayMsg);
nathan@0
   199
      lastReplayMsg=strdup(path);
nathan@0
   200
      }
nathan@0
   201
    if(visible) {
nathan@0
   202
      if(display) display->SetTitle(name);
nathan@0
   203
      }
nathan@0
   204
    }
nathan@0
   205
  if(release) free((void *)path);
nathan@0
   206
}
nathan@0
   207
nathan@0
   208
void cMPlayerControl::ShowProgress(void)
nathan@0
   209
{
nathan@0
   210
  int Current, Total;
nathan@0
   211
nathan@0
   212
  if(GetIndex(Current,Total) && Total>0) {
nathan@0
   213
    bool flush=false;
nathan@0
   214
    if(!visible) {
nathan@0
   215
      display=Skins.Current()->DisplayReplay(false);
nathan@0
   216
      visible=true; modeOnly=false;
nathan@0
   217
#if APIVERSNUM >= 10500
nathan@0
   218
      SetNeedsFastResponse(true);
nathan@0
   219
#else
nathan@0
   220
      needsFastResponse=true;
nathan@0
   221
#endif
nathan@0
   222
      lastCurrent=lastTotal=-1;
nathan@0
   223
      flush=true;
nathan@0
   224
      }
nathan@0
   225
nathan@0
   226
    if(abs(Current-lastCurrent)>12) {
nathan@0
   227
      if(Total>0) display->SetProgress(Current, Total);
nathan@0
   228
      display->SetCurrent(IndexToHMSF(Current));
nathan@0
   229
      display->SetTotal(IndexToHMSF(Total));
nathan@0
   230
      bool Play, Forward;
nathan@0
   231
      int Speed;
nathan@0
   232
      if(GetReplayMode(Play,Forward,Speed)) 
nathan@0
   233
        display->SetMode(Play, Forward, Speed);
nathan@0
   234
      ShowTitle();
nathan@0
   235
      flush=true;
nathan@0
   236
      lastCurrent=Current; lastTotal=Total;
nathan@0
   237
      }
nathan@0
   238
    if(flush) 
nathan@0
   239
      Skins.Flush();
nathan@0
   240
    ShowMode();
nathan@0
   241
    }
nathan@0
   242
}
nathan@0
   243
nathan@0
   244
void cMPlayerControl::ShowMode(void)
nathan@0
   245
{
nathan@0
   246
  if(Setup.ShowReplayMode && !jumpactive) {
nathan@0
   247
    bool Play, Forward;
nathan@0
   248
    int Speed;
nathan@0
   249
    if(GetReplayMode(Play, Forward, Speed)) {
nathan@0
   250
       bool NormalPlay = (Play && Speed == -1);
nathan@0
   251
nathan@0
   252
       if(!visible) {
nathan@0
   253
         if(NormalPlay) return;
nathan@0
   254
         display = Skins.Current()->DisplayReplay(true);
nathan@0
   255
         visible=modeOnly=true;
nathan@0
   256
         }
nathan@0
   257
nathan@0
   258
       if(modeOnly && !timeoutShow && NormalPlay) timeoutShow=time(0)+SELECTHIDE_TIMEOUT;
nathan@0
   259
nathan@0
   260
       display->SetMode(Play, Forward, Speed);
nathan@0
   261
       }
nathan@0
   262
    }
nathan@0
   263
}
nathan@0
   264
nathan@0
   265
void cMPlayerControl::JumpDisplay(void)
nathan@0
   266
{
nathan@0
   267
  char buf[64];
nathan@2
   268
  const char *j=trVDR("Jump: "), u=jumpmode?'%':'m';
nathan@0
   269
  if(!jumpval) sprintf(buf,"%s- %c",  j,u);
nathan@0
   270
  else         sprintf(buf,"%s%d- %c",j,jumpval,u);
nathan@0
   271
  display->SetJump(buf);
nathan@0
   272
}
nathan@0
   273
nathan@0
   274
void cMPlayerControl::JumpProcess(eKeys Key)
nathan@0
   275
{
nathan@0
   276
  const int n=Key-k0;
nathan@0
   277
  switch (Key) {
nathan@0
   278
    case k0 ... k9:
nathan@0
   279
      {
nathan@0
   280
      const int max=jumpmode?100:lastTotal;
nathan@0
   281
      if(jumpval*10+n <= max) jumpval=jumpval*10+n;
nathan@0
   282
      JumpDisplay();
nathan@0
   283
      }
nathan@0
   284
      break;
nathan@0
   285
    case kBlue:
nathan@0
   286
      jumpmode=!jumpmode; jumpval=0;
nathan@22
   287
      JumpDisplay();
nathan@0
   288
      break;
nathan@0
   289
    case kPlay:
nathan@0
   290
    case kUp:
nathan@0
   291
      player->Goto(jumpval*(jumpmode?1:60),jumpmode,false);
nathan@0
   292
      jumpactive=false;
nathan@0
   293
      break;
nathan@0
   294
    case kFastRew:
nathan@0
   295
    case kFastFwd:
nathan@0
   296
    case kLeft:
nathan@0
   297
    case kRight:
nathan@0
   298
      if(!jumpmode) {
nathan@0
   299
        player->SkipSeconds(jumpval*60 * ((Key==kLeft || Key==kFastRew) ? -1:1));
nathan@0
   300
        jumpactive=false;
nathan@0
   301
        }
nathan@0
   302
      break;
nathan@0
   303
    default:
nathan@0
   304
      jumpactive=false;
nathan@0
   305
      break;
nathan@0
   306
    }
nathan@0
   307
nathan@0
   308
  if(!jumpactive) {
nathan@0
   309
    if(jumphide) Hide();
nathan@0
   310
    else 
nathan@0
   311
      display->SetJump(0);
nathan@0
   312
    }
nathan@0
   313
}
nathan@0
   314
nathan@0
   315
void cMPlayerControl::Jump(void)
nathan@0
   316
{
nathan@0
   317
  jumpval=0; jumphide=jumpmode=false;
nathan@0
   318
  if(!visible) {
nathan@0
   319
    ShowTimed(); if(!visible) return;
nathan@0
   320
    jumphide=true;
nathan@0
   321
    }
nathan@0
   322
  JumpDisplay();
nathan@0
   323
  jumpactive=true;
nathan@0
   324
}
nathan@0
   325
nathan@0
   326
eOSState cMPlayerControl::ProcessKey(eKeys Key)
nathan@0
   327
{
nathan@0
   328
  if(!player->Active()) { Hide(); Stop(); return osEnd; }
nathan@0
   329
nathan@0
   330
  if(!player->SlaveMode()) {
nathan@0
   331
    if(Key==kBlue) { Hide(); Stop(); return osEnd; }
nathan@0
   332
    }
nathan@0
   333
  else {
nathan@0
   334
    if(visible) {
nathan@0
   335
      if(timeoutShow && time(0)>timeoutShow) {
nathan@0
   336
        Hide(); ShowMode();
nathan@0
   337
        timeoutShow = 0;
nathan@0
   338
        }
nathan@0
   339
      else {
nathan@0
   340
        if(modeOnly) ShowMode();
nathan@0
   341
        else ShowProgress();
nathan@0
   342
        }
nathan@0
   343
      }
nathan@0
   344
    else ShowTitle();
nathan@0
   345
nathan@0
   346
    if(jumpactive && Key != kNone) {
nathan@0
   347
      JumpProcess(Key);
nathan@0
   348
      return osContinue;
nathan@0
   349
      }
nathan@0
   350
nathan@0
   351
    bool DoShowMode = true;
nathan@0
   352
    switch (Key) {
nathan@0
   353
      case kPlay:
nathan@0
   354
      case kUp:      player->Play(); break;
nathan@0
   355
nathan@0
   356
      case kPause:
nathan@0
   357
      case kDown:    player->Pause(); break;
nathan@0
   358
nathan@0
   359
      case kFastRew|k_Repeat:
nathan@0
   360
      case kFastRew:
nathan@0
   361
      case kLeft|k_Repeat:
nathan@0
   362
      case kLeft:    player->SkipSeconds(-10); break;
nathan@0
   363
nathan@0
   364
      case kFastFwd|k_Repeat:
nathan@0
   365
      case kFastFwd:
nathan@0
   366
      case kRight|k_Repeat:
nathan@0
   367
      case kRight:   player->SkipSeconds(10); break;
nathan@0
   368
nathan@0
   369
      case kRed:     Jump(); break;
nathan@0
   370
nathan@0
   371
      case kGreen|k_Repeat:                      // temporary use
nathan@0
   372
      case kGreen:   player->SkipSeconds(-60); break;
nathan@0
   373
      case kYellow|k_Repeat:
nathan@0
   374
      case kYellow:  player->SkipSeconds(60); break;
nathan@0
   375
  //    case kGreen|k_Repeat:                      // reserved for future use
nathan@0
   376
  //    case kGreen:   player->SkipPrev(); break;
nathan@0
   377
  //    case kYellow|k_Repeat:
nathan@0
   378
  //    case kYellow:  player->SkipNext(); break;
nathan@0
   379
nathan@0
   380
      case kBack:
nathan@0
   381
                     Hide();
nathan@2
   382
                     cRemote::CallPlugin(plugin_name);
nathan@0
   383
                     return osBack;
nathan@0
   384
      case kStop:
nathan@0
   385
      case kBlue:    Hide(); Stop(); return osEnd;
nathan@0
   386
nathan@0
   387
      default:
nathan@0
   388
        DoShowMode = false;
nathan@0
   389
        switch(Key) {
nathan@0
   390
          case kOk: if(visible && !modeOnly) { Hide(); DoShowMode=true; }
nathan@0
   391
                    else ShowTimed();
nathan@0
   392
                    break;
nathan@20
   393
          case kAudio:
nathan@20
   394
                    player->KeyCmd("switch_audio");
nathan@20
   395
                    break;
nathan@27
   396
          case kNext:
nathan@27
   397
                    player->KeyCmd("seek_chapter +1");
nathan@27
   398
                    break;
nathan@27
   399
          case kPrev:
nathan@27
   400
                    player->KeyCmd("seek_chapter -1");
nathan@27
   401
                    break;
nathan@0
   402
          case k0:
nathan@0
   403
          case k1:
nathan@0
   404
          case k2:
nathan@0
   405
          case k3:
nathan@0
   406
          case k4:
nathan@0
   407
          case k5:
nathan@0
   408
          case k6:
nathan@0
   409
          case k7:
nathan@0
   410
          case k8:
nathan@0
   411
          case k9:  {
nathan@0
   412
                    const char *cmd=MPlayerSetup.KeyCmd[Key-k0];
nathan@0
   413
                    if(cmd[0]) player->KeyCmd(cmd);
nathan@0
   414
                    }
nathan@0
   415
                    break;
nathan@0
   416
          default:  break;
nathan@0
   417
          }
nathan@0
   418
        break;
nathan@0
   419
      }
nathan@0
   420
nathan@0
   421
    if(DoShowMode) ShowMode();
nathan@0
   422
    }
nathan@0
   423
  return osContinue;
nathan@0
   424
}
nathan@0
   425
nathan@0
   426
// --- cMenuMPlayAid -----------------------------------------------------------
nathan@0
   427
nathan@0
   428
class cMenuMPlayAid : public cOsdMenu {
nathan@0
   429
public:
nathan@0
   430
  cMenuMPlayAid(void);
nathan@0
   431
  virtual eOSState ProcessKey(eKeys Key);
nathan@0
   432
  };
nathan@0
   433
nathan@0
   434
cMenuMPlayAid::cMenuMPlayAid(void)
nathan@0
   435
:cOsdMenu(tr("MPlayer Audio ID"),20)
nathan@0
   436
{
nathan@0
   437
  Add(new cMenuEditIntItem(tr("Audiostream ID"),&MPlayerAid,-1,255));
nathan@0
   438
  Display();
nathan@0
   439
}
nathan@0
   440
nathan@0
   441
eOSState cMenuMPlayAid::ProcessKey(eKeys Key)
nathan@0
   442
{
nathan@0
   443
  eOSState state=cOsdMenu::ProcessKey(Key);
nathan@0
   444
  if(state==osUnknown) {
nathan@0
   445
    switch(Key) {
nathan@0
   446
      case kOk: state=osBack; break;
nathan@0
   447
      default:  break;
nathan@0
   448
      }
nathan@0
   449
    }
nathan@0
   450
  return state;
nathan@0
   451
}
nathan@0
   452
nathan@0
   453
// --- cMenuMPlayBrowse ---------------------------------------------------------
nathan@0
   454
nathan@0
   455
class cMenuMPlayBrowse : public cMenuBrowse {
nathan@0
   456
private:
nathan@0
   457
  bool sourcing, aidedit;
nathan@0
   458
  eOSState Source(bool second);
nathan@0
   459
  eOSState Summary(void);
nathan@0
   460
protected:
nathan@0
   461
  virtual void SetButtons(void);
nathan@0
   462
public:
nathan@0
   463
  cMenuMPlayBrowse(void);
nathan@0
   464
  virtual eOSState ProcessKey(eKeys Key);
nathan@0
   465
  };
nathan@0
   466
nathan@0
   467
static const char *excl_sum[] = { ".*","*.summary","*.txt","*.nfo",0 };
nathan@0
   468
nathan@0
   469
cMenuMPlayBrowse::cMenuMPlayBrowse(void)
nathan@0
   470
:cMenuBrowse(MPlaySources.GetSource(),false,false,tr("MPlayer browser"),excl_sum)
nathan@0
   471
{
nathan@0
   472
  sourcing=aidedit=false;
nathan@0
   473
  SetButtons();
nathan@0
   474
}
nathan@0
   475
nathan@0
   476
void cMenuMPlayBrowse::SetButtons(void)
nathan@0
   477
{
nathan@0
   478
  static char blue[12];
nathan@0
   479
  snprintf(blue,sizeof(blue),MPlayerAid>=0 ? "AID:%d" : "AID:def",MPlayerAid);
nathan@23
   480
  SetHelp(trVDR("Button$Play"), MPlayerSetup.ResumeMode ? trVDR("Button$Rewind"):0, tr("Source"), blue);
nathan@0
   481
  Display();
nathan@0
   482
}
nathan@0
   483
nathan@0
   484
eOSState cMenuMPlayBrowse::Source(bool second)
nathan@0
   485
{
nathan@0
   486
  if(HasSubMenu()) return osContinue;
nathan@0
   487
nathan@0
   488
  if(!second) {
nathan@0
   489
    sourcing=true;
nathan@0
   490
    return AddSubMenu(new cMenuSource(&MPlaySources,tr("MPlayer source")));
nathan@0
   491
    }
nathan@0
   492
  sourcing=false;
nathan@0
   493
  cFileSource *src=cMenuSource::GetSelected();
nathan@0
   494
  if(src) {
nathan@0
   495
    MPlaySources.SetSource(src);
nathan@0
   496
    SetSource(src);
nathan@0
   497
    NewDir(0);
nathan@0
   498
    }
nathan@0
   499
  return osContinue;
nathan@0
   500
}
nathan@0
   501
nathan@0
   502
eOSState cMenuMPlayBrowse::Summary(void)
nathan@0
   503
{
nathan@0
   504
  cFileObj *item=CurrentItem();
nathan@0
   505
  if(item && item->Type()==otFile) {
nathan@0
   506
    static const char *exts[] = { ".summary",".txt",".nfo",0 };
nathan@0
   507
    for(int i=0; exts[i]; i++) {
nathan@0
   508
      char buff[4096];
nathan@0
   509
      strn0cpy(buff,item->FullPath(),sizeof(buff)-20);
nathan@0
   510
      char *e=&buff[strlen(buff)];
nathan@0
   511
      strn0cpy(e,exts[i],20);
nathan@0
   512
      int fd=open(buff,O_RDONLY);
nathan@0
   513
      *e=0;
nathan@0
   514
      if(fd<0 && (e=rindex(buff,'.'))) {
nathan@0
   515
        strn0cpy(e,exts[i],20);
nathan@0
   516
        fd=open(buff,O_RDONLY);
nathan@0
   517
        }
nathan@0
   518
      if(fd>=0) {
nathan@0
   519
        int r=read(fd,buff,sizeof(buff)-1);
nathan@0
   520
        close(fd);
nathan@0
   521
        if(r>0) {
nathan@0
   522
          buff[r]=0;
nathan@0
   523
          return AddSubMenu(new cMenuText(tr("Summary"),buff));
nathan@0
   524
          }
nathan@0
   525
        }
nathan@0
   526
      }
nathan@0
   527
    }
nathan@0
   528
  return osContinue;
nathan@0
   529
}
nathan@0
   530
nathan@0
   531
eOSState cMenuMPlayBrowse::ProcessKey(eKeys Key)
nathan@0
   532
{
nathan@0
   533
  eOSState state=cOsdMenu::ProcessKey(Key);
nathan@0
   534
  if(state==osContinue && !HasSubMenu()) {
nathan@0
   535
    if(sourcing) return Source(true);
nathan@0
   536
    if(aidedit) { aidedit=false; SetButtons(); }
nathan@0
   537
    }
nathan@0
   538
  bool rew=false;
nathan@0
   539
  if(state==osUnknown) {
nathan@0
   540
    switch(Key) {
nathan@0
   541
      case kGreen:
nathan@0
   542
        {
nathan@0
   543
        cFileObj *item=CurrentItem();
nathan@0
   544
        if(item && item->Type()==otFile) {
nathan@0
   545
          lastselect=new cFileObj(item);
nathan@0
   546
          state=osBack;
nathan@0
   547
          rew=true;
nathan@0
   548
          } 
nathan@0
   549
        else state=osContinue;
nathan@0
   550
        break;
nathan@0
   551
        }
nathan@0
   552
      case kYellow:
nathan@0
   553
        state=Source(false);
nathan@0
   554
        break;
nathan@0
   555
      case kBlue:
nathan@0
   556
        aidedit=true;
nathan@0
   557
        state=AddSubMenu(new cMenuMPlayAid);
nathan@0
   558
        break;
nathan@0
   559
      case k0:
nathan@0
   560
        state=Summary();
nathan@0
   561
        break;
nathan@0
   562
      default:
nathan@0
   563
        break;
nathan@0
   564
      }
nathan@0
   565
    }
nathan@0
   566
  if(state==osUnknown) state=cMenuBrowse::ProcessStdKey(Key,state);
nathan@0
   567
  if(state==osBack && lastselect) {
nathan@0
   568
    cMPlayerControl::SetFile(lastselect,rew);
nathan@0
   569
    cControl::Launch(new cMPlayerControl);
nathan@0
   570
    return osEnd;
nathan@0
   571
    }
nathan@0
   572
  return state;
nathan@0
   573
}
nathan@0
   574
nathan@0
   575
// --- cPluginMPlayer ----------------------------------------------------------
nathan@0
   576
nathan@2
   577
static const char *DESCRIPTION    = trNOOP("Media replay via MPlayer");
nathan@0
   578
static const char *MAINMENUENTRY  = "MPlayer";
nathan@0
   579
nathan@0
   580
class cPluginMPlayer : public cPlugin {
nathan@0
   581
private:
nathan@0
   582
  bool ExternalPlay(const char *path, bool test);
nathan@0
   583
public:
nathan@0
   584
  cPluginMPlayer(void);
nathan@0
   585
  virtual ~cPluginMPlayer();
nathan@9
   586
  virtual const char *Version(void) { return PluginVersion; }
nathan@0
   587
  virtual const char *Description(void) { return tr(DESCRIPTION); }
nathan@0
   588
  virtual const char *CommandLineHelp(void);
nathan@0
   589
  virtual bool ProcessArgs(int argc, char *argv[]);
nathan@0
   590
  virtual bool Initialize(void);
nathan@0
   591
  virtual const char *MainMenuEntry(void);
nathan@0
   592
  virtual cOsdMenu *MainMenuAction(void);
nathan@0
   593
  virtual cMenuSetupPage *SetupMenu(void);
nathan@0
   594
  virtual bool SetupParse(const char *Name, const char *Value);
nathan@0
   595
  virtual bool Service(const char *Id, void *Data);
nathan@0
   596
  virtual const char **SVDRPHelpPages(void);
nathan@0
   597
  virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
nathan@0
   598
  };
nathan@0
   599
nathan@0
   600
cPluginMPlayer::cPluginMPlayer(void)
nathan@0
   601
{
nathan@0
   602
  // Initialize any member variables here.
nathan@0
   603
  // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
nathan@0
   604
  // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
nathan@0
   605
  status=0;
nathan@0
   606
}
nathan@0
   607
nathan@0
   608
cPluginMPlayer::~cPluginMPlayer()
nathan@0
   609
{
nathan@0
   610
  delete status;
nathan@0
   611
}
nathan@0
   612
nathan@0
   613
const char *cPluginMPlayer::CommandLineHelp(void)
nathan@0
   614
{
nathan@0
   615
  static char *help_str=0;
nathan@0
   616
  
nathan@0
   617
  free(help_str);    //                                     for easier orientation, this is column 80|
nathan@29
   618
  help_str=aprintf(  "  -m CMD,   --mount=CMD    use CMD to mount/unmount/eject mp3 sources\n"
nathan@0
   619
                     "                           (default: %s)\n"
nathan@0
   620
                     "  -M CMD,   --mplayer=CMD  use CMD when calling MPlayer\n"
nathan@0
   621
                     "                           (default: %s)\n"
nathan@0
   622
                     "  -S SUB,   --sources=SUB  search sources config in SUB subdirectory\n"
nathan@0
   623
                     "                           (default: %s)\n"
nathan@0
   624
                     "  -R DIR,   --resume=DIR   store global resume file in DIR\n"
nathan@0
   625
                     "                           (default: %s)\n",
nathan@0
   626
                     mountscript,
nathan@0
   627
                     MPlayerCmd,
nathan@0
   628
                     sourcesSub ? sourcesSub:"none",
nathan@0
   629
                     globalResumeDir ? globalResumeDir:"video dir"
nathan@0
   630
                     );
nathan@0
   631
  return help_str;
nathan@0
   632
}
nathan@0
   633
nathan@0
   634
bool cPluginMPlayer::ProcessArgs(int argc, char *argv[])
nathan@0
   635
{
nathan@0
   636
  static struct option long_options[] = {
nathan@0
   637
      { "mount",    required_argument, NULL, 'm' },
nathan@0
   638
      { "mplayer",  required_argument, NULL, 'M' },
nathan@0
   639
      { "sources",  required_argument, NULL, 'S' },
nathan@0
   640
      { "resume",   required_argument, NULL, 'R' },
nathan@0
   641
      { NULL }
nathan@0
   642
    };
nathan@0
   643
nathan@0
   644
  int c, option_index = 0;
nathan@0
   645
  while((c=getopt_long(argc,argv,"m:M:S:R:",long_options,&option_index))!=-1) {
nathan@0
   646
    switch (c) {
nathan@0
   647
      case 'm': mountscript=optarg; break;
nathan@0
   648
      case 'M': MPlayerCmd=optarg; break;
nathan@0
   649
      case 'S': sourcesSub=optarg; break;
nathan@0
   650
      case 'R': globalResumeDir=optarg; break;
nathan@0
   651
      default:  return false;
nathan@0
   652
      }
nathan@0
   653
    }
nathan@0
   654
  return true;
nathan@0
   655
}
nathan@0
   656
nathan@0
   657
bool cPluginMPlayer::Initialize(void)
nathan@22
   658
{
nathan@22
   659
  if(!CheckVDRVersion(1,4,5,"mplayer")) return false;
nathan@2
   660
  plugin_name="mplayer";
nathan@2
   661
#if APIVERSNUM < 10507
nathan@2
   662
  i18n_name="mplayer";
nathan@2
   663
#else
nathan@2
   664
  i18n_name="vdr-mplayer";
nathan@2
   665
#endif
nathan@0
   666
  MPlaySources.Load(AddDirectory(ConfigDirectory(sourcesSub),"mplayersources.conf"));
nathan@0
   667
  if(MPlaySources.Count()<1) {
nathan@0
   668
    esyslog("ERROR: you must have defined at least one source in mplayersources.conf");
nathan@0
   669
    fprintf(stderr,"No source(s) defined in mplayersources.conf\n");
nathan@0
   670
    return false;
nathan@0
   671
    }
nathan@2
   672
#if APIVERSNUM < 10507
nathan@0
   673
  RegisterI18n(Phrases);
nathan@2
   674
#endif
nathan@0
   675
  if(!(status=new cMPlayerStatus)) return false;
nathan@0
   676
  return true;
nathan@0
   677
}
nathan@0
   678
nathan@0
   679
const char *cPluginMPlayer::MainMenuEntry(void)
nathan@0
   680
{
nathan@0
   681
  return MPlayerSetup.HideMainMenu ? 0 : tr(MAINMENUENTRY);
nathan@0
   682
}
nathan@0
   683
nathan@0
   684
cOsdMenu *cPluginMPlayer::MainMenuAction(void)
nathan@0
   685
{
nathan@0
   686
  return new cMenuMPlayBrowse;
nathan@0
   687
}
nathan@0
   688
nathan@0
   689
cMenuSetupPage *cPluginMPlayer::SetupMenu(void)
nathan@0
   690
{
nathan@0
   691
  return new cMenuSetupMPlayer;
nathan@0
   692
}
nathan@0
   693
nathan@0
   694
bool cPluginMPlayer::SetupParse(const char *Name, const char *Value)
nathan@0
   695
{
nathan@0
   696
  if(      !strcasecmp(Name, "ControlMode"))  MPlayerSetup.SlaveMode    = atoi(Value);
nathan@0
   697
  else if (!strcasecmp(Name, "HideMainMenu")) MPlayerSetup.HideMainMenu = atoi(Value);
nathan@0
   698
  else if (!strcasecmp(Name, "ResumeMode"))   MPlayerSetup.ResumeMode   = atoi(Value);
nathan@0
   699
  else if (!strncasecmp(Name,"KeyCmd", 6) && strlen(Name)==7 && isdigit(Name[6]))
nathan@0
   700
    strn0cpy(MPlayerSetup.KeyCmd[Name[6]-'0'],Value,sizeof(MPlayerSetup.KeyCmd[0]));
nathan@0
   701
  else return false;
nathan@0
   702
  return true;
nathan@0
   703
}
nathan@0
   704
nathan@0
   705
bool cPluginMPlayer::ExternalPlay(const char *path, bool test)
nathan@0
   706
{
nathan@0
   707
  char real[PATH_MAX+1];
nathan@0
   708
  if(realpath(path,real)) {
nathan@0
   709
    cFileSource *src=MPlaySources.FindSource(real);
nathan@0
   710
    if(src) {
nathan@0
   711
      cFileObj *item=new cFileObj(src,0,0,otFile);
nathan@0
   712
      if(item) {
nathan@0
   713
        item->SplitAndSet(real);
nathan@0
   714
        if(item->GuessType()) {
nathan@0
   715
          if(item->Exists()) {
nathan@0
   716
            if(!test) {
nathan@0
   717
              cMPlayerControl::SetFile(item,true);
nathan@0
   718
              cControl::Launch(new cMPlayerControl);
nathan@0
   719
              cControl::Attach();
nathan@0
   720
              }
nathan@0
   721
            delete item;
nathan@0
   722
            return true;
nathan@0
   723
            }
nathan@0
   724
          else dsyslog("MPlayer service: cannot play '%s'",path);
nathan@0
   725
          }
nathan@0
   726
        else dsyslog("MPlayer service: GuessType() failed for '%s'",path);
nathan@0
   727
        delete item;
nathan@0
   728
        }
nathan@0
   729
      }
nathan@0
   730
    else dsyslog("MPlayer service: cannot find source for '%s', real '%s'",path,real);
nathan@0
   731
    }
nathan@0
   732
  else if(errno!=ENOENT && errno!=ENOTDIR)
nathan@0
   733
    esyslog("ERROR: realpath: %s: %s",path,strerror(errno));
nathan@0
   734
  return false;
nathan@0
   735
}
nathan@0
   736
nathan@0
   737
bool cPluginMPlayer::Service(const char *Id, void *Data)
nathan@0
   738
{
nathan@0
   739
  if(!strcasecmp(Id,"MPlayer-Play-v1")) {
nathan@0
   740
    if(Data) {
nathan@0
   741
      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
nathan@0
   742
      msd->result=ExternalPlay(msd->data.filename,false);
nathan@0
   743
      }
nathan@0
   744
    return true;
nathan@0
   745
    }
nathan@0
   746
  else if(!strcasecmp(Id,"MPlayer-Test-v1")) {
nathan@0
   747
    if(Data) {
nathan@0
   748
      struct MPlayerServiceData *msd=(struct MPlayerServiceData *)Data;
nathan@0
   749
      msd->result=ExternalPlay(msd->data.filename,true);
nathan@0
   750
      }
nathan@0
   751
    return true;
nathan@0
   752
    }
nathan@0
   753
  return false;
nathan@0
   754
}
nathan@0
   755
nathan@0
   756
const char **cPluginMPlayer::SVDRPHelpPages(void)
nathan@0
   757
{
nathan@0
   758
  static const char *HelpPages[] = {
nathan@0
   759
    "PLAY <filename>\n"
nathan@0
   760
    "    Triggers playback of file 'filename'.",
nathan@0
   761
    "TEST <filename>\n"
nathan@0
   762
    "    Tests is playback of file 'filename' is possible.",
nathan@0
   763
    NULL
nathan@0
   764
    };
nathan@0
   765
  return HelpPages;
nathan@0
   766
}
nathan@0
   767
nathan@0
   768
cString cPluginMPlayer::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
nathan@0
   769
{
nathan@0
   770
  if(!strcasecmp(Command,"PLAY")) {
nathan@0
   771
    if(*Option) {
nathan@0
   772
      if(ExternalPlay(Option,false)) return "Playback triggered";
nathan@0
   773
      else { ReplyCode=550; return "Playback failed"; }
nathan@0
   774
      }
nathan@0
   775
    else { ReplyCode=501; return "Missing filename"; }
nathan@0
   776
    }
nathan@0
   777
  else if(!strcasecmp(Command,"TEST")) {
nathan@0
   778
    if(*Option) {
nathan@0
   779
      if(ExternalPlay(Option,true)) return "Playback possible";
nathan@0
   780
      else { ReplyCode=550; return "Playback not possible"; }
nathan@0
   781
      }
nathan@0
   782
    else { ReplyCode=501; return "Missing filename"; }
nathan@0
   783
    }
nathan@0
   784
  return NULL;
nathan@0
   785
}
nathan@0
   786
nathan@0
   787
VDRPLUGINCREATOR(cPluginMPlayer); // Don't touch this!