forked from vdr-projects/vdr-plugin-dvd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer-dvd.c
3202 lines (2779 loc) · 99.1 KB
/
player-dvd.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* DVD Player plugin for VDR
*
* Copyright (C) 2001.2002 Andreas Schultz <[email protected]>
*
* This code is distributed under the terms and conditions of the
* GNU GENERAL PUBLIC LICENSE. See the file COPYING for details.
*
*/
// #define IFRAMEWRITE
#ifndef DEBUG
#ifndef NDEBUG
#define NDEBUG
#endif
#endif
#include <sys/time.h>
#include <vdr/i18n.h>
#include <vdr/thread.h>
#include <vdr/device.h>
#include <vdr/plugin.h>
// for VideoDirectory variable
#include <vdr/videodir.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "common-dvd.h"
#include "i18n.h"
#include "dvddev.h"
#include "setup-dvd.h"
#include "tools-dvd.h"
#include "player-dvd.h"
#include "control-dvd.h"
#include "dvd.h"
/**
* this was "weak"'s solution of a forced
* SPU only stream choice,
* which does _not_ work ..
*
* Problem:
* - forcedDisplay | menu ctrseq within the SPU,
* means kinda "just do it"
* - special SPU channels, which only shows up
* e.g. if a klingon speaks klingon,
* are handled by a special SPU channel id,
* which dvdnav will choose for you automatic,
* and if your appropriate language is set
* as the default language !
#define FORCED_SUBS_ONLY_SEMANTICS
*
* - we handle the forcedDisplay (outta a menu space)
* - we take care now of all SPU channels [0x00 .. 0x1F]
* - ...
*/
// #define NO_USLEEP
#ifdef NO_USLEEP
#define USLEEP(a)
#else
#define USLEEP(a) usleep((a))
#endif
#if defined( DEBUG )
#warning using verbose DEBUG mode
/**
#define DVDDEBUG
#undef DEBUGDVD
#define DEBUGDVD(format, args...) printf (format, ## args)
#define CTRLDEBUG
#undef DEBUG_CONTROL
#define DEBUG_CONTROL(format, args...) printf (format, ## args); fflush(NULL)
#define NAVDEBUG
#undef DEBUG_NAV
#define DEBUG_NAV(format, args...) printf (format, ## args); fflush(NULL)
**
**
#define NAV0DEBUG
#undef DEBUG_NAV0
#define DEBUG_NAV0(format, args...) printf (format, ## args); fflush(NULL)
**
**
**
#define SUBPDECDEBUG
#undef DEBUG_SUBP_DEC
#define DEBUG_SUBP_DEC(format, args...) printf (format, ## args); fflush(NULL)
**
#define SUBPDEBUG
#undef DEBUG_SUBP_ID
#define DEBUG_SUBP_ID(format, args...) printf (format, ## args); fflush(NULL)
#define IFRAMEDEBUG
#undef DEBUG_IFRAME
#define DEBUG_IFRAME(format, args...) printf (format, ## args); fflush(NULL)
#define IFRAMEDEBUG2
#undef DEBUG_IFRAME2
#define DEBUG_IFRAME2(format, args...) printf (format, ## args); fflush(NULL)
#define PTSDEBUG
#undef DEBUG_PTS
#define DEBUG_PTS(format, args...) printf (format, ## args); fflush(NULL)
#define AUDIOIDDEBUG
#undef DEBUG_AUDIO_ID
#define DEBUG_AUDIO_ID(format, args...) printf (format, ## args); fflush(NULL)
#define AUDIOPLAYDEBUG
#undef DEBUG_AUDIO_PLAY
#define DEBUG_AUDIO_PLAY(format, args...) printf (format, ## args); fflush(NULL)
#define AUDIOPLAYDEBUG2
#undef DEBUG_AUDIO_PLAY2
#define DEBUG_AUDIO_PLAY2(format, args...) printf (format, ## args); fflush(NULL)
*/
#endif
// --- static stuff ------------------------------------------------------
#if 1
#ifdef POLLTIMEOUTS_BEFORE_DEVICECLEAR
// polltimeout of 3 seems to be enough for a softdevice ..
#undef POLLTIMEOUTS_BEFORE_DEVICECLEAR
#endif
#endif
#ifndef POLLTIMEOUTS_BEFORE_DEVICECLEAR
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6
#else
#warning using patched POLLTIMEOUTS_BEFORE_DEVICECLEAR
#endif
#define OsdInUse() ((controller!=NULL)?controller->OsdVisible(this):false)
#define TakeOsd() ((controller!=NULL)?controller->TakeOsd((void *)this):false)
#ifdef CTRLDEBUG
static void printCellInfo( dvdnav_cell_change_event_t & lastCellEventInfo,
int64_t pgcTotalTicks, int64_t pgcTicksPerBlock )
{
int cell_length_s = (int)(lastCellEventInfo.cell_length / 90000L) ;
int pg_length_s = (int)(lastCellEventInfo.pg_length / 90000L) ;
int pgc_length_s = (int)(lastCellEventInfo.pgc_length / 90000L) ;
int cell_start_s = (int)(lastCellEventInfo.cell_start / 90000L) ;
int pg_start_s = (int)(lastCellEventInfo.pg_start / 90000L) ;
printf("cellN %ld, pgN=%d, cell_length=%ld %d, pg_length=%ld %d, pgc_length=%ld %d, cell_start %ld %d, pg_start %ld %d, ticksPerBlock=%ld, secPerBlock=%ld\n",
(long)lastCellEventInfo.cellN ,
lastCellEventInfo.pgN ,
(long)lastCellEventInfo.cell_length ,
cell_length_s,
(long)lastCellEventInfo.pg_length ,
pg_length_s,
(long)lastCellEventInfo.pgc_length ,
pgc_length_s,
(long)lastCellEventInfo.cell_start ,
cell_start_s,
(long)lastCellEventInfo.pg_start ,
pg_start_s,
(long)pgcTicksPerBlock, (long)(pgcTicksPerBlock/90000L)
);
}
#endif
const char *cDvdPlayer::PTSTicksToHMSm(int64_t ticks, bool WithMS)
{
static char buffer[200];
int ms = (int)(ticks / 90L) % 100;
int s = (int)(ticks / 90000L);
int m = s / 60 % 60;
int h = s / 3600;
s %= 60;
snprintf(buffer, sizeof(buffer), WithMS ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, ms);
return buffer;
}
// --- cIframeAssembler ------------------------------------------------------
class cIframeAssembler {
private:
int head;
int size;
unsigned char *buf;
cMutex mutex;
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
public:
cIframeAssembler(int Size);
virtual ~cIframeAssembler(void);
int Available(void) {return head;}
void Clear(void);
unsigned char *Get(int &Lenth);
void Put(unsigned char *Data, int Length);
};
cIframeAssembler::cIframeAssembler(int Size) {
size = Size;
head = 0;
buf = MALLOC(unsigned char, Size);
}
cIframeAssembler::~cIframeAssembler(void) {
free(buf);
}
void cIframeAssembler::Clear(void) {
Lock();
head = 0;
Unlock();
}
unsigned char *cIframeAssembler::Get(int &Length) {
Length = head;
return buf;
}
void cIframeAssembler::Put(unsigned char *Data, int Length) {
Lock();
if (head + Length < size) {
memcpy(buf + head, Data, Length);
head += Length;
} else
esyslog("ERROR: dvd-plugin iframeassembler full %d", head + Length);
Unlock();
}
// --- cResumeEntry ------------------------------------------------------------
// borrowed from the mplayer plugin code and adapted to the dvd resume requirements
class cResumeEntry : public cListObject {
public:
char *key;
int title;
int chapter;
int64_t second;
//
cResumeEntry(void);
~cResumeEntry();
};
cResumeEntry::cResumeEntry(void)
{
key=0;
}
cResumeEntry::~cResumeEntry()
{
free(key);
}
// --- cDVDPlayerResume ----------------------------------------------------------
// store resume database to this file ...
#define RESUME_FILE "dvdplayer.resume"
// ... in this directory (default: /video)
#ifndef RESUMEDIR
#if APIVERSNUM > 20101
#define RESUMEDIR cVideoDirectory::Name()
#else
#define RESUMEDIR VideoDirectory
#endif
#endif
// borrowed from the mplayer plugin code and adapted to the dvd resume requirements
class cDVDPlayerResume : public cList<cResumeEntry> {
private:
char* resfile; // the full pathname of resume file
bool modified; // flag to indicate that memory database was modified and needs to be saved
/**
* LoadResume():
* reads in the resume database file from resfile.
*/
void LoadResume();
/**
* SaveResume():
* saves the resume database to the file resfile.
* returns true on successful save.
*/
bool SaveResume(void);
/**
* search the (loaded) resume database for the given key.
* returns the cResumeEntry* if the key was found
* or NULL if no resume entry was found for the given key.
*/
cResumeEntry *FindResume(const char* key);
public:
cDVDPlayerResume(void);
~cDVDPlayerResume();
/**
* SetResume():
* set the given resume values for the given key into the resume database.
* the resume database is loaded from file if not yet loaded.
*/
void SetResume(const char* key, int title, int chapter, int64_t second);
/**
* GetResume():
* tries looking up the given key into the resume database.
* the resume database is loaded from file if not yet loaded.
* returns true if resume data could be found. In this case
* the givven arguments are filled with the resume data. Otherwise
* the arguments are not modified!
*/
bool GetResume(const char* key, int& title, int& chapter, int64_t& second);
};
cDVDPlayerResume::cDVDPlayerResume(void)
{
// initialize the resume filename string.
asprintf(&resfile, "%s/%s", RESUMEDIR, RESUME_FILE);
}
cDVDPlayerResume::~cDVDPlayerResume()
{
// save resume data to disc before self-destruction.
SaveResume();
// free the resume filename string, allocated in C'tor by asprintf
free(resfile);
}
void cDVDPlayerResume::SetResume(const char* key, int title, int chapter, int64_t second)
{
// (re)load resume data from file to be actual
LoadResume();
cResumeEntry* re = FindResume(key);
if (re) {
// found a resume entry, so we can update it.
DEBUGDVD("resume: setting resume %d:%d:%lld (update)", title, chapter, second);
} else {
// no resume entry found yet, creating a new one
re = new cResumeEntry;
re->key = strdup(key);
Add(re);
DEBUGDVD("resume: setting resume %d:%d:%lld (new)", title, chapter, second);
}
// set the new resume data for the found/created entry
re->title = title;
re->chapter = chapter;
re->second = second;
// and mark memory database as modified to be saved.
modified = true;
// save it now (sync!)
SaveResume();
}
bool cDVDPlayerResume::GetResume(const char* key, int& title, int& chapter, int64_t& second)
{
// (re)load the resume file to have actual values
LoadResume();
cResumeEntry* re = FindResume(key);
if (re) {
// found a resume entry, copy values
title = re->title;
chapter = re->chapter;
second = re->second;
// indicate successful search
return true;
}
// no resume entry found in database
return false;
}
void cDVDPlayerResume::LoadResume()
{
// we will load the file for sure and add all entries, clear all old entries.
Clear();
// no entries == no modifications
modified = false;
DEBUGDVD("resume: resume file is \"%s\"\n",resfile);
FILE *f = fopen(resfile,"r");
if (f) {
DEBUGDVD("resume: successfully opened resume file\n");
char line[768];
// read file line by line
while(fgets(line,sizeof(line),f)) {
char key[512];
int t, c;
int64_t s;
// parse line as "title:chapter:second:key"
if(sscanf(line,"%d:%d:%lld:%511[^\n]",&t,&c,&s,key) == 4) {
// successful parse, save in resume entry
cResumeEntry *re = new cResumeEntry;
re->key = strdup(key);
re->title = t;
re->chapter = c;
re->second = s;
// and add it to memory database
Add(re);
}
}
// don't forget to close what you have opened!
fclose(f);
}
// unsuccessful open leads to empty database as the file does not exists
}
bool cDVDPlayerResume::SaveResume(void)
{
if(modified) {
// modification indicated, save the database to the resume file
DEBUGDVD("resume: saving resume file\n");
cSafeFile f(resfile);
if(f.Open()) {
// forall resume entries in the memory database
for (cResumeEntry *re=First(); re; re=Next(re)) {
// save the as one line in the format "title:chapter:second:key"
fprintf(f, "%d:%d:%lld:%s\n", re->title, re->chapter, re->second, re->key);
}
// don't forget to close what you have opened!
f.Close();
// signal successful save
return true;
} else {
DEBUGDVD("resume: failed to save resume file\n");
// saving did not succeed!!!!
return false;
}
} else {
// no modifications -> successful "save" :-)
return true;
}
}
cResumeEntry *cDVDPlayerResume::FindResume(const char* key)
{
DEBUGDVD("resume: searching resume position for \"%s\"\n", key);
// iterate over all entries in the memory database
for(cResumeEntry *re=First(); re; re=Next(re)) {
if (!strcasecmp(re->key, key)) {
// return the entry iff the keys match
DEBUGDVD("resume: found resume position %d:%d:%lld\n",re->title, re->chapter, re->second);
return re;
}
}
DEBUGDVD("resume: no resume position found\n");
return NULL;
}
// --- cDvdPlayer ------------------------------------------------------------
//XXX+ also used in recorder.c - find a better place???
// The size of the array used to buffer video data:
// (must be larger than MINVIDEODATA - see remux.h)
#define VIDEOBUFSIZE MEGABYTE(1)
// The number of frames to back up when resuming an interrupted replay session:
#define RESUMEBACKUP (10 * FRAMESPERSEC)
#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
#define NORMAL_SPEED 10 // the index of the '1' entry in the following array
#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction,
// for the dvbplayer
#define MAX_MAX_SPEEDS MAX_SPEEDS*MAX_SPEEDS // the super speed maximum
#define SPEED_MULT 12 // the speed multiplier
int cDvdPlayer::Speeds[] = { 0, 0, 0, 0, 0, 0, 0, -2, -4, -8, 1, 2, 4, 12, 0, 0, 0, 0, 0, 0, 0 };
bool cDvdPlayer::BitStreamOutActive = false;
bool cDvdPlayer::HasBitStreamOut = false;
bool cDvdPlayer::HasSoftDeviceOut = false;
bool cDvdPlayer::SoftDeviceOutActive = false;
bool cDvdPlayer::HasReelbox = false;
const int cDvdPlayer::MaxAudioTracks = 0x20;
const int cDvdPlayer::AudioTrackMask = 0x1F;
const int cDvdPlayer::AC3AudioTrackMask = 0x07;
const int cDvdPlayer::MaxSubpStreams = 0x20;
const int cDvdPlayer::SubpStreamMask = 0x1F;
const char * cDvdPlayer::dummy_title = "DVD Title";
const char * cDvdPlayer::dummy_n_a = "n.a.";
cDvdPlayer::cDvdPlayer(void): cThread("dvd-plugin"), a52dec(*this) {
DEBUGDVD("cDvdPlayer::cDvdPlayer(void)\n");
ringBuffer=new cRingBufferFrame(VIDEOBUFSIZE);
rframe=NULL;
pframe=NULL;
controller = NULL;
active = true;
running = false;
playMode = pmPlay;
playDir = pdForward;
trickSpeed = NORMAL_SPEED;
stillTimer = 0;
currButtonN = -1;
nav = 0;
iframeAssembler = new cIframeAssembler(KILOBYTE(CIF_MAXSIZE));
IframeCnt = 0;
event_buf = new uint8_t[4096];
current_pci = 0;
prev_e_ptm = 0;
ptm_offs = 0;
DEBUG_SUBP_ID("SPU showSubs=%d\n", DVDSetup.ShowSubtitles);
skipPlayVideo=false;
fastWindFactor=1;
// resume
resume = new cDVDPlayerResume;
clearSeenSubpStream();
clearSeenAudioTrack();
currentNavSubpStream = -1;
currentNavAudioTrackType = -1;
lastNavAudioTrackType = -1;
pgcCurrentBlockNum=1;
pgcTotalBlockNum=1;
pgcTotalTicks=1;
pgcTicksPerBlock=1;
isInMenuDomain = false;
isInFeature = false;
dvd_aspect=0;
dvd_scaleperm=0;
SPUdecoder = NULL;
a52dec.setSyncMode(A52decoder::ptsGenerate);
titleinfo_str=0;
title_str=0;
aspect_str = NULL;
SetTitleInfoString();
SetTitleString();
SetAspectString();
memset(&lastCellEventInfo, 0, sizeof(dvdnav_cell_change_event_t));
}
cDvdPlayer::~cDvdPlayer()
{
DEBUGDVD("destructor cDvdPlayer::~cDvdPlayer()\n");
Detach();
Save();
delete []event_buf;
delete iframeAssembler;
delete ringBuffer;
if(titleinfo_str)
free(titleinfo_str);
if(title_str)
free(title_str);
if(aspect_str)
free(aspect_str);
delete resume;
}
void cDvdPlayer::setController (cDvdPlayerControl *ctrl )
{
controller = ctrl;
}
bool cDvdPlayer::IsDvdNavigationForced() const
{
return (controller?controller->IsDvdNavigationForced():false);
}
void cDvdPlayer::TrickSpeed(int Increment)
{
int nts = trickSpeed + Increment;
cntVidBlocksPlayed=0;
cntAudBlocksPlayed=0;
if (nts>0 && nts-NORMAL_SPEED <= MAX_SPEEDS && Speeds[nts] == 1) {
fastWindFactor = 1;
trickSpeed = nts;
if (playMode == pmFast)
Play();
else
Pause();
} else if (nts>0 && nts-NORMAL_SPEED <= MAX_SPEEDS && Speeds[nts]) {
fastWindFactor = 1;
if (playDir == pdBackward && DVDSetup.ReadAHead>0)
dvdnav_set_readahead_flag(nav, 0);
trickSpeed = nts;
int Mult = ( playMode == pmSlow ) ? 1 : SPEED_MULT;
int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
if (sp > MAX_VIDEO_SLOWMOTION)
sp = MAX_VIDEO_SLOWMOTION;
if (playDir == pdBackward) {
fastWindFactor = ( playMode == pmSlow ) ? trickSpeed - NORMAL_SPEED : trickSpeed - NORMAL_SPEED + 1 ;
if (playMode == pmSlow)
sp=2;
}
#if VDRVERSNUM < 20103
DeviceTrickSpeed(sp);
#else
DeviceTrickSpeed(sp,playDir == pdForward);
#endif
} else if ( nts>0 && nts-NORMAL_SPEED <= MAX_MAX_SPEEDS ) {
fastWindFactor = 1;
trickSpeed = nts;
if ( playDir == pdBackward )
if ( playMode == pmSlow )
fastWindFactor = trickSpeed - NORMAL_SPEED ;
else
fastWindFactor = ( trickSpeed - NORMAL_SPEED - MAX_SPEEDS + 1 ) * 2 ;
else if ( playDir == pdForward && playMode == pmFast )
fastWindFactor = ( trickSpeed - NORMAL_SPEED - MAX_SPEEDS + 1 ) * 2 ;
}
DEBUG_CONTROL("dvd TRICKSPEED: %d (%d), fastWindFactor=%d fwd=%d, slow=%d, fast=%d\n",
trickSpeed, Increment, fastWindFactor, playDir == pdForward, playMode == pmSlow, playMode == pmFast);
}
void cDvdPlayer::DeviceReset(void)
{
DEBUG_CONTROL("DeviceReset: Clear & Play\n");
cntVidBlocksPlayed=0;
cntAudBlocksPlayed=0;
cPlayer::DeviceClear();
cPlayer::DevicePlay();
}
void cDvdPlayer::DeviceClear(void)
{
DEBUG_CONTROL("DeviceClear\n");
if (IsAttached())
PlayPes(NULL, 0);
cPlayer::DeviceClear();
}
void cDvdPlayer::DevicePlay(void)
{
DEBUG_CONTROL("DevicePlay\n");
cPlayer::DevicePlay();
}
void cDvdPlayer::DrawSPU()
{
if (SPUdecoder && !SPUdecoder->IsVisible() && TakeOsd()) {
SPUdecoder->Draw();
}
}
void cDvdPlayer::HideSPU()
{
if (SPUdecoder) {
SPUdecoder->Hide();
}
}
void cDvdPlayer::EmptySPU()
{
if (SPUdecoder) {
ClearButtonHighlight();
SPUdecoder->Empty();
}
}
void cDvdPlayer::Empty(bool emptyDeviceToo)
{
DEBUG_CONTROL("dvd .. Empty ...\n");
LOCK_THREAD;
if( IframeCnt < 0 && cntVidBlocksPlayed > 0 ) {
DEBUG_IFRAME("I-Frame: Empty: Iframe used, VidBlocksPlayed -> emptyDevice\n");
emptyDeviceToo=true;
}
DEBUG_CONTROL("dvd .. Empty IframeCnt=%d, emptyDeviceToo=%d!\n", IframeCnt, emptyDeviceToo);
if (SPUdecoder) {
DEBUG_NAV("DVD NAV SPU clear & empty %s:%d\n", __FILE__, __LINE__);
EmptySPU();
}
currButtonN = -1;
a52dec.clear();
lastFrameType = 0xff;
VideoPts = 0xFFFFFFFF;
stcPTS = 0xFFFFFFFF;
stcPTSAudio = 0xFFFFFFFF;
stcPTSLastAudio = 0xFFFFFFFF;
pktpts = 0xFFFFFFFF;
pktptsAudio = 0xFFFFFFFF;
pktptsLastAudio = 0xFFFFFFFF;
prev_e_ptm = 0;
ptm_offs = 0;
DELETENULL(rframe);
pframe=NULL;
ringBuffer->Clear();
cntVidBlocksPlayed=0;
cntAudBlocksPlayed=0;
stillTimer = 0;
stillFrame = 0;
IframeCnt = 0;
iframeAssembler->Clear();
pictureNumber=0;
pictureFlip=false;
skipPlayVideo=false;
if(emptyDeviceToo) {
DeviceClear();
}
}
void cDvdPlayer::StillSkip()
{
DEBUGDVD("cDvdPlayer::StillSkip()\n");
if(stillTimer!=0) {
stillTimer = 0;
if(nav!=NULL) {
dvdnav_still_skip(nav);
}
}
}
#ifdef DVDDEBUG
static char *dvd_ev[] =
{ "BLOCK_OK",
"NOP",
"STILL_FRAME",
"SPU_STREAM_CHANGE",
"AUDIO_STREAM_CHANGE",
"VTS_CHANGE",
"CELL_CHANGE",
"NAV_PACKET",
"STOP",
"HIGHLIGHT",
"SPU_CLUT_CHANGE",
"SEEK_DONE", // remove in newer lib versions
"HOP_CHANNEL",
"WAIT", // since 0.1.7
"TRICK_MODE_CHANGE" };
#endif
#define CELL_STILL 0x02
#define NAV_STILL 0x04
/**
* timer funktions with ticks as its base
*
* 90000 ticks are 1 second, acording to MPEG !
*
* just to make things clear ;-)
*
* 1000 ms / s, 1000000 us / s
*
* 90000/1 t/s = 90/1 t/ms = 9/100 t/us
*
* 1/90000 s/t = 1/90 ms/t = 100/9 us/t
*/
uint64_t cDvdPlayer::time_ticks(void)
{
static time_t t0 = 0;
struct timeval t;
if (gettimeofday(&t, NULL) == 0) {
if (t0 == 0)
t0 = t.tv_sec; // this avoids an overflow (we only work with deltas)
return (uint64_t)(t.tv_sec - t0) * 90000U +
(uint64_t)( t.tv_usec * 9U ) / 100U ;
}
return 0;
}
uint64_t cDvdPlayer::delay_ticks(uint64_t ticks)
{
#ifndef NO_USLEEP
uint64_t t0 = time_ticks();
USLEEP(1000); // at least one guaranteed sleep of 1ms !!
uint64_t done = time_ticks() - t0 ;
while ( ticks > done )
{
// USLEEP resol. is about 19 ms
if(ticks-done>19U*90U)
USLEEP( (ticks-done)*100U/9U - 19000U );
done = time_ticks() - t0 ;
}
return time_ticks() - t0 ;
#else
return ticks;
#endif
}
char* cDvdPlayer::GetDVDResumeKey() const {
// first we fetch the total number of titles of the current dvd
int totalTitles;
if (dvdnav_get_number_of_titles(nav, &totalTitles)) {
// then we sum up the numbers of chapters for each title
int totalChapters = 0;
for (int t = 1; t <= totalTitles; t++) {
int curChapters;
dvdnav_get_number_of_parts(nav, t, &curChapters);
totalChapters += curChapters;
DEBUGDVD("resume: cDvdPlayer::Action() Title %d has %d chapters.\n", t, curChapters);
}
DEBUGDVD("resume: cDvdPlayer::Action() Titles: %d with %d chapters all together, Title: \"%s\"\n",
totalTitles, totalChapters, title_str);
// finally the key is build as "DVDName_TotalTitles_OverallChapters"
char* key;
asprintf(&key, "%s_%d_%d", title_str, totalTitles, totalChapters);
// note: this is not completly unique. Maybe some other informations are more suitable, like:
// - the "serial number" of the dvd as displayed in the libdvdnav debug output, but:
// it is not available through the current libdvdnav api
// - the total bytes of the dvd (quiet unique!!!), but:
// also not available through the libdvdnav api and no idea how to get it for a media not mounted.
// - any other ideas???
return key;
} else {
// if we cannot fetch the total number of titles of the current disc, there must be something wrong!
// Who needs a key for resuming then?
return NULL;
}
}
void cDvdPlayer::SaveResume() {
// make sure resume database is allocated (might be a possibility to completly disable resuming!)
if (resume) {
// fetch the current title and chapter number via libdvdnav api
int currentTitle, currentChapter;
if (dvdnav_current_title_info(nav, ¤tTitle, ¤tChapter) &&
(0 != currentTitle)) {
// fetch current time position through own class api
int64_t currentSec, totalSec;
GetPositionInSec(currentSec, totalSec);
// compute the resume key for the current dvd
char* key = GetDVDResumeKey();
if (key) {
// store computed/fetched resume data in database
DEBUGDVD("resume->SetResume(\"%s\", %d, %d, %lld)\n", key, currentTitle, currentChapter, currentSec);
resume->SetResume(key, currentTitle, currentChapter, currentSec);
// free the key string memory allocated by GetDVDResumeKey()
free(key);
} else {
DEBUGDVD("resume: ERROR computing resume key for this dvd!\n");
}
} else {
// in a menu title and chapter seams to be always 0 -> no way to resume there!
DEBUGDVD("resume: ERROR fetching current title and chapter (maybe in menus?).\n");
}
}
}
bool cDvdPlayer::LoadResume(int& title, int& chapter, int64_t& second) {
// helper variable for the return value
bool retval = false;
// make sure resume database is allocated (might be a possibility to completly disable resuming!)
if(resume) {
// compute the resume key for the current dvd
char* key = GetDVDResumeKey();
if (key) {
DEBUGDVD("resume->GetResume(\"%s\", ...): ", key);
// try loading the resume data for the computed key into the given arguments
if (resume->GetResume(key, title, chapter, second)) {
DEBUGDVD("%d:%d:%lld\n", title, chapter, second);
// continuing at the very same position might be inappropriate (vdr's recordings also rewind some seconds)
int ResumeRewind = 30; // rewind 30s if possible
// note: I used a variable here to show up, that this value might be made
// possible to configure (in the setup dialog). Doing so myself was
// not yet nesseccary and is so left to the plugin maintainers.
// make sure we do not rewind before the beginning
if (second > ResumeRewind) {
second -= ResumeRewind;
}
retval = true;
} else {
DEBUGDVD("<none>\n");
retval = false;
}
// free the key string memory allocated by GetDVDResumeKey()
free(key);
} else {
DEBUGDVD("resume: ERROR computing resume key for this dvd.\n");
}
}
return retval;
}
void cDvdPlayer::Action(void) {
memset(event_buf, 0, sizeof(uint8_t)*4096);
unsigned char *write_blk = NULL;
int blk_size = 0;
BitStreamOutActive = false;
HasBitStreamOut = (cPluginManager::GetPlugin("bitstreamout") != NULL);
HasReelbox = (cPluginManager::GetPlugin("reelbox") != NULL);
SoftDeviceOutActive = false;
HasSoftDeviceOut = (cPluginManager::GetPlugin("xine") != NULL);
cSetupLine *slBitStreamOutActive = NULL;
if(HasBitStreamOut) {
slBitStreamOutActive = cPluginDvd::GetSetupLine("active", "bitstreamout");
if(slBitStreamOutActive!=NULL)
BitStreamOutActive = atoi ( slBitStreamOutActive->Value() ) ? true: false ;
}
dsyslog("dvd-plugin: BitStreamOutActive=%d, HasBitStreamOut=%d (%d)", BitStreamOutActive, HasBitStreamOut, slBitStreamOutActive!=NULL);
if(HasSoftDeviceOut) {
SoftDeviceOutActive = true;
}
dsyslog("dvd-plugin: SoftDeviceOutActive=%d, HasSoftDeviceOut=%d", SoftDeviceOutActive, HasSoftDeviceOut);
if (dvdnav_open(&nav, const_cast<char *>(cDVD::getDVD()->DeviceName())) != DVDNAV_STATUS_OK) {
Skins.QueueMessage(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);
return;
}
dvdnav_set_readahead_flag(nav, DVDSetup.ReadAHead);
if (DVDSetup.PlayerRCE != 0)
dvdnav_set_region_mask(nav, 1 << (DVDSetup.PlayerRCE - 1));
else
dvdnav_set_region_mask(nav, 0xffff);
dvdnav_menu_language_select(nav, const_cast<char *>(ISO639code[DVDSetup.MenuLanguage]));
dvdnav_audio_language_select(nav, const_cast<char *>(ISO639code[DVDSetup.AudioLanguage]));
dvdnav_spu_language_select(nav, const_cast<char *>(ISO639code[DVDSetup.SpuLanguage]));
DEBUGDVD("Default-Langs: menu=%s, audio=%s, spu=%s\n",
const_cast<char *>(ISO639code[DVDSetup.MenuLanguage]),
const_cast<char *>(ISO639code[DVDSetup.AudioLanguage]),
const_cast<char *>(ISO639code[DVDSetup.SpuLanguage]));
if (IsAttached()) {
SPUdecoder = cDevice::PrimaryDevice()->GetSpuDecoder();
DEBUG_NAV("DVD NAV SPU clear & empty %s:%d\n", __FILE__, __LINE__);
EmptySPU();
}
int slomoloop=0;
uint64_t sleept = 0; // in ticks !
uint64_t sleept_done = 0; // in ticks !
bool trickMode = false;
bool noAudio = false;
int PollTimeouts = 0;
int playedPacket = pktNone;
uint32_t cntVidBlocksSkipped = 0;
Empty(false);
// IframeCnt = -1; // mark that we have to reset the device, before 1st PlayVideo ..
running = true;
eFrameType frameType = ftUnknown;
bool firstClear = true;
// we need to know the very first VTS change to hook inthe resume call
bool first_vts_change = true;
// we cannot directly resume to the exact time, so we hook on the next cell change when resuming
bool next_cell_change = false;
// and seek the the exact time stored here
int64_t resSecond = 0;
while(running && nav) {
if (!pframe) {
pframe = ringBuffer->Get();
if (pframe) {
write_blk=pframe->Data();
blk_size=pframe->Count();
frameType=pframe->Type();
}
}
/*
// clip PTS values ..
if ( pktptsAudio<pktptsLastAudio )
pktptsLastAudio=pktptsAudio;
if ( stcPTSAudio<stcPTSLastAudio )
stcPTSLastAudio=stcPTSAudio;
if ( playedPacket==pktAudio ) {
uint64_t sleept_trial = 0; // in ticks !
// do extra sleep, if stream's pts of audio diffs
// against dvb device's seen pts more/equal than 1 ms == 90 ticks !
if ( pktptsAudio-pktptsLastAudio >= stcPTSAudio-stcPTSLastAudio+90 ) {
sleept_trial = (pktptsAudio-pktptsLastAudio) -
(stcPTSAudio-stcPTSLastAudio) ;
if(sleept_trial>sleept) sleept=sleept_trial;
}
}
*/
/**
DEBUG_CONTROL("dvd: menu=%d, v:%u, a:%u, p:%d, stc:%8ums, blk_size=%3d, skipPlayV=%d, IframeCnt=%d, stillTimer=%8ums\n",
IsInMenuDomain(), cntVidBlocksPlayed, cntAudBlocksPlayed, playedPacket,
(unsigned int)(stcPTS/90U),