Compare commits

..

71 Commits

Author SHA1 Message Date
Klaus Schmidinger
dd71a004e2 Official release of version 2.7.4 2025-02-26 10:35:03 +01:00
Klaus Schmidinger
45091fbd72 Revised locking in cMenuSchedule and cMenuWhatsOn 2025-02-25 15:53:43 +01:00
Klaus Schmidinger
988d5aebfa Added missing locks to SetMenuItem() functions 2025-02-20 10:23:15 +01:00
Klaus Schmidinger
8c3671fae6 Fixed cPtsIndex::FindFrameNumber() to handle the case where Pts points to an I-frame 2025-02-19 15:39:16 +01:00
Klaus Schmidinger
2a12af481a Fixed spurious fast frames when switching from "slow back" to "slow forward" 2025-02-18 17:06:15 +01:00
Klaus Schmidinger
7817e64695 Fixed progress display when switching from "pause" to "slow back" 2025-02-18 15:37:24 +01:00
Klaus Schmidinger
ebbaa39098 Added '~' to the list of delimiters in cTextWrapper 2025-02-17 11:13:13 +01:00
Klaus Schmidinger
d3dcbbd4f2 Fixed unnecessary redisplays of menus 2025-02-17 10:49:10 +01:00
Klaus Schmidinger
3045995bbc Fixed displaying the current item when pressing a hotkey 2025-02-13 13:58:07 +01:00
Klaus Schmidinger
1b4233d6ad The function cPlugin::MainThreadHook() has been deprecated 2025-02-12 22:22:20 +01:00
Klaus Schmidinger
34aa8fe8b4 Adjusted PLUGINS.html to the new API version numbering introduced in version 2.7.2 2025-02-12 22:02:25 +01:00
Klaus Schmidinger
baa97e9ff1 Added missing calls to cStatus::MsgOsdStatusMessage() and added the new virtual function cStatus::OsdStatusMessage2(), which can be used to get the type of the message 2025-02-12 21:18:53 +01:00
Klaus Schmidinger
03afc4a353 Fixed unnecessary calls to cStatus::OsdCurrentItem2() when scrolling 2025-02-05 22:12:32 +01:00
Klaus Schmidinger
ef4ebeb7ee The new virtual function cStatus::OsdCurrentItem2() can be used to get the index of the current menu item 2025-01-29 11:15:26 +01:00
Klaus Schmidinger
80d8851e62 Added cStatus::OsdCurrentItem2() 2025-01-29 11:15:26 +01:00
Klaus Schmidinger
ead135f716 Fixed unnecessary calls to DisplayCurrent() for editable menu items 2025-01-29 10:35:25 +01:00
Klaus Schmidinger
49dc61a92c Fixed an unnecessary redisplay of the menu when pressing a hotkey 2025-01-29 10:25:55 +01:00
Klaus Schmidinger
af0309cc40 Fixed an improper call of cStatus::OsdCurrentItem() before cStatus::OsdItem2() 2025-01-29 10:20:17 +01:00
Klaus Schmidinger
4ed7421b1c Activated logging of OsdItem2() 2025-01-28 10:41:03 +01:00
Klaus Schmidinger
3058354dba Fixed setting the file name of the info file after renaming a recording 2025-01-18 20:57:06 +01:00
Klaus Schmidinger
20a8c5d240 Added cStatus::OsdItem2() 2025-01-16 10:23:12 +01:00
Klaus Schmidinger
0749a34342 The new virtual function cStatus::OsdItem2() can be used to get the information whether a menu item is selectable 2025-01-16 09:42:11 +01:00
Klaus Schmidinger
e595eed57d The info files of recordings are now only re-read if they have been modified 2025-01-15 10:50:29 +01:00
Klaus Schmidinger
a7576f0b6c Added parameter checks to strn0cpy() and Utf8Strn0Cpy() 2025-01-15 08:57:45 +01:00
Klaus Schmidinger
657e5dda5d Added a header to the backtrace 2025-01-15 08:43:12 +01:00
Klaus Schmidinger
8fb6a2b24b Fixed handling margins for timers that are not VPS controlled and not spawned 2025-01-13 14:44:18 +01:00
Klaus Schmidinger
53cac302d8 Added 1 to Utf8BufSize() for worst case 2025-01-13 13:18:42 +01:00
Klaus Schmidinger
2c6c014dd8 Checking for VPS control is now limited to local timers 2025-01-13 12:34:18 +01:00
Klaus Schmidinger
a7071f580e Added some missing locking 2025-01-10 16:11:02 +01:00
Klaus Schmidinger
de5327a048 Fixed a possible deadlock when canceling an editing process 2025-01-10 13:12:04 +01:00
Klaus Schmidinger
7ab94c7bcb Fixed accessing a timer's event schedule in case the event has been removed from the schedule 2025-01-07 10:46:22 +01:00
Klaus Schmidinger
0f80fc5e86 Fixed handling the fps value if it can't be determined from the video data 2024-12-05 10:37:15 +01:00
Klaus Schmidinger
d169f30e5c TS packets with errors are now skipped when parsing for frames 2024-12-05 10:33:31 +01:00
Klaus Schmidinger
7a1842cba6 Fixed a typo 2024-12-04 14:33:22 +01:00
Klaus Schmidinger
e4e9d7a55f Added some missing locking 2024-12-02 12:40:56 +01:00
Klaus Schmidinger
ccbef6ce6c Fixed some typos in the translation files 2024-11-30 14:30:46 +01:00
Klaus Schmidinger
7461a1ba3a Updated the Finnish OSD texts 2024-10-24 09:58:43 +02:00
Klaus Schmidinger
bb55e3036e The VDR homepage is now accessible via HTTPS 2024-10-21 19:04:53 +02:00
Klaus Schmidinger
bbf2cca198 Updated the Italian OSD texts 2024-10-20 10:05:19 +02:00
Klaus Schmidinger
8ce034d124 Fixed wrong credits 2024-10-17 20:17:57 +02:00
Klaus Schmidinger
4030698007 Fixed error checking in case the fps value can't be determined by the frame parser 2024-10-13 13:34:32 +02:00
Klaus Schmidinger
66fea5c9f1 Removed all DEPRECATED_* code 2024-10-13 09:47:18 +02:00
Klaus Schmidinger
285574eeaa Official release of version 2.7.3 2024-10-12 13:32:46 +02:00
Klaus Schmidinger
55cfb057e0 Added vdrrootdir and incdir to vdr.pc 2024-10-11 14:21:04 +02:00
Klaus Schmidinger
b4c538cff7 The Channel+/- keys can now be used to jump between errors while replaying a recording 2024-10-11 14:10:50 +02:00
Klaus Schmidinger
5a626fef9f Fixed expiring of one-time VPS timers in case there is more than one event with the same VPS time 2024-10-11 13:58:11 +02:00
Klaus Schmidinger
2bcd8ba8f3 Fixed handling negative values in cSource::Position() on systems where 'int' is 64 bit 2024-10-09 10:36:16 +02:00
Klaus Schmidinger
2dacc776bd Fixed error checking in case of large PTS discontinuities 2024-10-08 08:46:38 +02:00
Klaus Schmidinger
a91d687a1a Removed defining DEPRECATED_* macros with value 0, because this is the preprocessor's default 2024-10-08 08:09:48 +02:00
Klaus Schmidinger
0d3882d43e Official release of version 2.7.2 2024-09-27 09:15:33 +02:00
Klaus Schmidinger
72ad601328 Added a note about re-generating the index of old recordings with errors 2024-09-27 09:14:23 +02:00
Klaus Schmidinger
2c6fd804f6 Fixed a problem with duplicate events if they are moved to a lower table ID and at the same time get a new event ID 2024-09-26 19:25:41 +02:00
Klaus Schmidinger
c590444b7d Fixed error checking when regenerating the index 2024-09-21 19:18:18 +02:00
Klaus Schmidinger
4805af7915 Increased the bpp of cProgressBar to 4 to handle all different colors 2024-09-21 16:21:08 +02:00
Klaus Schmidinger
171b20a80d Fixed singular when displaying number of errors in the recording info 2024-09-21 10:53:07 +02:00
Klaus Schmidinger
d00ae923ab Edited recordings now show error count of original if there are no error indicators in the index 2024-09-20 21:34:18 +02:00
Klaus Schmidinger
d8ab5dc5c6 Fixed syncing the frame checker to I-frames 2024-09-20 14:21:39 +02:00
Klaus Schmidinger
292af5d4f4 The info file of an edited recording now contains the number of errors in the edited version 2024-09-19 20:21:58 +02:00
Klaus Schmidinger
3d6b31b115 When the index file of a recording is regenerated, errors in the recording are now stored in the index file 2024-09-19 12:06:55 +02:00
Klaus Schmidinger
9e523073aa Errors are now shown as diamond shaped markers in the replay progress display of the default skins 2024-09-19 09:49:02 +02:00
Klaus Schmidinger
32d8e473fb Fixed description of cSkinDisplayReplay::SetRecording() 2024-09-18 11:10:56 +02:00
Klaus Schmidinger
5cd25df60c Recording errors are now marked in the index file 2024-09-18 09:23:07 +02:00
Klaus Schmidinger
52c4816c9c Now distinguishing between frames with errors and completely missing frames 2024-09-17 11:30:28 +02:00
Klaus Schmidinger
6f6b05ffcb The number of errors in a recording now represents the number of broken frames 2024-09-17 09:39:50 +02:00
Klaus Schmidinger
6dd5854b7a Moved error checking from recorder.c to remux.c 2024-09-17 09:31:15 +02:00
Klaus Schmidinger
83425df0b6 Updated the Italian OSD texts 2024-09-16 09:14:41 +02:00
Klaus Schmidinger
82b09eaa8e Silenced a compiler warning with gcc 14.1.0 2024-09-16 09:07:12 +02:00
Klaus Schmidinger
ec5b1aadab Fixed a problem in cSchedule::Sort(), in case hasRunning was true, but there was no event with RunningStatus() >= SI::RunningStatusPausing 2024-09-14 14:17:12 +02:00
Klaus Schmidinger
f786510ba2 Made APIVERSION independent from VDRVERSION to avoid irritation in case only VDRVERSION is incremented 2024-09-12 12:48:40 +02:00
Klaus Schmidinger
f006884e57 Deprecated code is now marked with [[deprecated]] to issue a compile time warning when used 2024-09-09 22:15:59 +02:00
Klaus Schmidinger
c0a005b3cd Fix for compilers that don't like non-constant format strings 2024-09-09 13:39:05 +02:00
87 changed files with 1219 additions and 653 deletions

View File

@ -2581,6 +2581,21 @@ Markus Ehrnsperger <markus.ehrnsperger@googlemail.com>
for suggesting to add the lines from 'Fixed a timeout in cDvbDevice while tuning after for suggesting to add the lines from 'Fixed a timeout in cDvbDevice while tuning after
the frontend has been reopened' to cDvbTuner::ProvidesFrontend() the frontend has been reopened' to cDvbTuner::ProvidesFrontend()
for improving the error message when closing a frontend for improving the error message when closing a frontend
for reporting a problem in cSchedule::Sort(), in case hasRunning was true, but there
was no event with RunningStatus() >= SI::RunningStatusPausing
for reporting problem with duplicate events if they are moved to a lower table ID and
at the same time get a new event ID
for reporting a bug in handling negative values in cSource::Position() on
systems where 'int' is 64 bit
for suggesting a fix for expiring of one-time VPS timers in case there is more than
one event with the same VPS time
for fixing handling margins for timers that are not VPS controlled and not spawned
for implementing cStatus::OsdItem2() with the information whether the item is selectable
for reporting an improper call of cStatus::OsdCurrentItem() before cStatus::OsdItem2()
for fixing unnecessary calls to DisplayCurrent() for editable menu items
for implementing cStatus::OsdCurrentItem2() with the index of the current item
for adding missing calls to cStatus::MsgOsdStatusMessage() and implementing
cStatus::OsdStatusMessage2() with the type of the message
Werner Färber <w.faerber@gmx.de> Werner Färber <w.faerber@gmx.de>
for reporting a bug in handling the cPluginManager::Active() result when pressing for reporting a bug in handling the cPluginManager::Active() result when pressing
@ -2839,6 +2854,12 @@ Winfried K
for adding missing rounding when dividing frequencies in processing the NIT for adding missing rounding when dividing frequencies in processing the NIT
for suggesting to add note about not messing with event ids in EPG handlers for suggesting to add note about not messing with event ids in EPG handlers
for adding a missing initialization of cChannel::nameSourceMode for adding a missing initialization of cChannel::nameSourceMode
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
for reporting a compiler warning with gcc 14.1.0
for suggesting to remove defining DEPRECATED_* macros with value 0, because this
is the preprocessor's default
for suggesting a fix for handling negative values in cSource::Position() on
systems where 'int' is 64 bit
Hans-Werner Hilse <hilse@web.de> Hans-Werner Hilse <hilse@web.de>
for adding the command line option --userdump to enable core dumps in case VDR for adding the command line option --userdump to enable core dumps in case VDR
@ -3009,6 +3030,7 @@ Manuel Reimer <Manuel.Reimer@gmx.de>
number is given number is given
for reporting that LSTE doesn't work after PUTE in case a channel didn't already for reporting that LSTE doesn't work after PUTE in case a channel didn't already
have EPG data have EPG data
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
Rene van den Braken <rene@vandenbraken.name> Rene van den Braken <rene@vandenbraken.name>
for reporting a bug in writing the PCR pid into the PMT in for reporting a bug in writing the PCR pid into the PMT in
@ -3062,6 +3084,7 @@ Lars Hanisch <dvb@flensrocker.de>
for fixing a typo in the description of cTimers::GetTimersRead() for fixing a typo in the description of cTimers::GetTimersRead()
for suggesting to use dynamic buffering in handling CA descriptors to avoid a for suggesting to use dynamic buffering in handling CA descriptors to avoid a
possible buffer overflow possible buffer overflow
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
Alex Lasnier <alex@fepg.org> Alex Lasnier <alex@fepg.org>
for adding tuning support for ATSC devices for adding tuning support for ATSC devices
@ -3351,6 +3374,16 @@ Mike Hay <mike.hay@linenshorts.com>
Stefan Hofmann <stefan.hofmann@t-online.de> Stefan Hofmann <stefan.hofmann@t-online.de>
for suggesting to implement support for remote controls that only have a combined for suggesting to implement support for remote controls that only have a combined
"Play/Pause" key instead of separate keys for "Play" and "Pause" "Play/Pause" key instead of separate keys for "Play" and "Pause"
for a fix for compilers that don't like non-constant format strings
for suggesting to implement jumping between errors while replaying a recording
for adding vdrrootdir and incdir to vdr.pc
for fixing some typos in the translation files
for adding 1 to Utf8BufSize() for worst case
for adding a header to the backtrace
for adding parameter checks to strn0cpy()
for making the info files of recordings only be re-read if they have been modified
for reporting missing setting the file name of the info file after renaming a recording
for adding '~' to the list of delimiters in cTextWrapper
Stefan Blochberger <Stefan.Blochberger@gmx.de> Stefan Blochberger <Stefan.Blochberger@gmx.de>
for suggesting to automatically display the progress display whenever replay of a for suggesting to automatically display the progress display whenever replay of a
@ -3783,3 +3816,5 @@ Jose Angel <joseangelpp@gmail.com>
Andreas Baierl <post@andreasbaierl.de> Andreas Baierl <post@andreasbaierl.de>
for implementing scaling images for implementing scaling images
for reporting a problem in the progress display when switching from "pause" to
"slow back"

106
HISTORY
View File

@ -9983,3 +9983,109 @@ Video Disk Recorder Revision History
- There will be no more distinction between "developer" and "stable" versions regarding - There will be no more distinction between "developer" and "stable" versions regarding
version numbering. Version numbers are simply counted upwards, with each of the three version numbering. Version numbers are simply counted upwards, with each of the three
parts ("version", "major", "minor") always being a single digit, and '0' being skipped. parts ("version", "major", "minor") always being a single digit, and '0' being skipped.
2024-09-27: Version 2.7.2
- Fix for compilers that don't like non-constant format strings (thanks to Stefan Hofmann).
- Deprecated code is now marked with [[deprecated]] to issue a compile time warning when
used.
- Made APIVERSION independent from VDRVERSION to avoid irritation in case only
VDRVERSION is incremented (originally suggested by Winfried Köhler, with contributions
from Lars Hanisch and Manuel Reimer).
APIVERSNUM is now 30003.
- Fixed a problem in cSchedule::Sort(), in case hasRunning was true, but there was no event
with RunningStatus() >= SI::RunningStatusPausing (reported by Markus Ehrnsperger).
- Silenced a compiler warning with gcc 14.1.0 (reported by Winfried Köhler).
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- Moved error checking from recorder.c to remux.c.
- The number of errors in a recording now represents the number of broken frames.
- Now distinguishing between frames with errors and completely missing frames.
- Recording errors are now marked in the index file.
- Fixed description of cSkinDisplayReplay::SetRecording().
- Errors are now shown as diamond shaped markers in the replay progress display of the
default skins. Plugin authors can switch to the new constructor of cProgressBar to
benefit from this feature. Of course this only works with recordings that have error
information stored in their index file. If you have existing recordings with errors
and want them to be displayed in the progress bar, you can re-generate their index
file, either by deleting the old index, or by running vdr with the --genindex option.
APIVERSNUM is now 30004.
- When the index file of a recording is regenerated, errors in the recording are now
stored in the index file.
- The info file of an edited recording now contains the number of errors in the edited
version. Note that this applies only to recordings that have errors stored in their
index file. If errors are not stored in the index file, the edited version will have
its number of errors set to that of the original recording.
APIVERSNUM is now 30005.
- Fixed singular when displaying number of errors in the recording info.
- Increased the bpp of cProgressBar to 4 to handle all different colors.
- Fixed a problem with duplicate events if they are moved to a lower table ID and at the
same time get a new event ID (reported by Markus Ehrnsperger).
2024-10-12: Version 2.7.3
- Removed defining DEPRECATED_* macros with value 0, because this is the preprocessor's
default (suggested by Winfried Köhler).
- Fixed error checking in case of large PTS discontinuities.
- Fixed handling negative values in cSource::Position() on systems where 'int' is 64 bit
(reported by Markus Ehrnsperger, fix suggested by Winfried Köhler).
- Fixed expiring of one-time VPS timers in case there is more than one event with the
same VPS time (suggested by Markus Ehrnsperger).
- The Channel+/- keys can now be used to jump between errors while replaying a recording
(suggested by Stefan Hofmann).
- Added vdrrootdir and incdir to vdr.pc (thanks to Stefan Hofmann).
2025-02-26: Version 2.7.4
- Removed all DEPRECATED_* code.
- Fixed error checking in case the fps value can't be determined by the frame parser.
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- The VDR homepage is now accessible via HTTPS.
- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
- Fixed some typos in the translation files (thanks to Stefan Hofmann).
- Added some missing locking.
- TS packets with errors are now skipped when parsing for frames.
- Fixed handling the fps value if it can't be determined from the video data.
- Fixed accessing a timer's event schedule in case the event has been removed from the
schedule.
- Fixed a possible deadlock when canceling an editing process.
- Checking for VPS control is now limited to local timers.
- Added 1 to Utf8BufSize() for worst case (thanks to Stefan Hofmann).
- Fixed handling margins for timers that are not VPS controlled and not spawned (thanks
to Markus Ehrnsperger).
- Added a header to the backtrace (thanks to Stefan Hofmann).
- Added parameter checks to strn0cpy() (thanks to Stefan Hofmann). Same for Utf8Strn0Cpy().
- The info files of recordings are now only re-read if they have been modified (thanks
to Stefan Hofmann).
- The new virtual function cStatus::OsdItem2() can be used to get the information whether
a menu item is selectable (thanks to Markus Ehrnsperger). Plugins that implemented
cStatus::OsdItem() will still work as before, because the default implementation of
cStatus::OsdItem2() calls cStatus::OsdItem().
APIVERSNUM is now 30006.
- Fixed setting the file name of the info file after renaming a recording (reported by
Stefan Hofmann).
- Fixed an improper call of cStatus::OsdCurrentItem() before cStatus::OsdItem2() (reported
by Markus Ehrnsperger).
- Fixed an unnecessary redisplay of the menu when pressing a hotkey.
- Fixed unnecessary calls to DisplayCurrent() for editable menu items (thanks to Markus
Ehrnsperger).
- The new virtual function cStatus::OsdCurrentItem2() can be used to get the index of the
current menu item (thanks to Markus Ehrnsperger). Plugins that implemented
cStatus::OsdCurrentItem() will still work as before, because the default implementation
of cStatus::OsdCurrentItem2() calls cStatus::OsdCurrentItem().
- Fixed unnecessary calls to cStatus::OsdCurrentItem2() when scrolling.
- Added missing calls to cStatus::MsgOsdStatusMessage() and added the new virtual function
cStatus::OsdStatusMessage2(), which can be used to get the type of the message (thanks
to Markus Ehrnsperger). Plugins that implemented cStatus::OsdStatusMessage() will still
work as before, because the default implementation of cStatus::OsdStatusMessage2() calls
cStatus::OsdStatusMessage().
- Adjusted PLUGINS.html to the new API version numbering introduced in version 2.7.2.
- The function cPlugin::MainThreadHook() has been deprecated and may be removed in future
versions. Use proper locking instead.
- Fixed unnecessary redisplays of menus.
- Added '~' to the list of delimiters in cTextWrapper (thanks to Stefan Hofmann).
- Fixed progress display when switching from "pause" to "slow back" (reported by Andreas
Baierl).
- Fixed spurious fast frames when switching from "slow back" to "slow forward".
- Fixed cPtsIndex::FindFrameNumber() to handle the case where Pts points to an I-frame.
- Added missing locks to SetMenuItem() functions.
- Revised locking in cMenuSchedule and cMenuWhatsOn.

4
MANUAL
View File

@ -50,8 +50,8 @@ Version 2.7
Next Next/previous channel group (in live tv mode) Next Next/previous channel group (in live tv mode)
Prev or next/previous editing mark (in replay mode) Prev or next/previous editing mark (in replay mode)
Channel+ channel up Channel+ channel up (live view), next error (replay)
Channel- channel down Channel- channel down (live view), previous error (replay)
PrevChannel previous channel PrevChannel previous channel
Power shutdown Power shutdown

View File

@ -6,7 +6,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: Make.config.template 5.1 2022/11/26 13:37:06 kls Exp $ # $Id: Make.config.template 5.2 2024/10/11 14:21:04 kls Exp $
### The C compiler and options: ### The C compiler and options:
@ -33,14 +33,15 @@ endif
# Default directories (adjust as necessary or desired): # Default directories (adjust as necessary or desired):
#PREFIX = /usr/local #PREFIX = /usr/local
#BINDIR = $(PREFIX)/bin #VDRROOT = $(PREFIX)
#INCDIR = $(PREFIX)/include #BINDIR = $(VDRROOT)/bin
#LIBDIR = $(PREFIX)/lib/vdr #INCDIR = $(VDRROOT)/include
#LOCDIR = $(PREFIX)/share/locale #LIBDIR = $(VDRROOT)/lib
#MANDIR = $(PREFIX)/share/man #LOCDIR = $(VDRROOT)/locale
#PCDIR = $(PREFIX)/lib/pkgconfig #MANDIR = $(VDRROOT)/man
#RESDIR = $(PREFIX)/share/vdr #PCDIR = $(VDRROOT)/pkgconfig
#DVBDIR = /usr/src/v4l-dvb/linux/include/uapi #RESDIR = $(VDRROOT)/share
#DVBDIR = /usr/include
#VIDEODIR = /srv/vdr/video #VIDEODIR = /srv/vdr/video
#CONFDIR = /var/lib/vdr #CONFDIR = /var/lib/vdr

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: Makefile 5.2 2024/01/05 14:16:16 kls Exp $ # $Id: Makefile 5.4 2024/10/21 19:01:16 kls Exp $
.DELETE_ON_ERROR: .DELETE_ON_ERROR:
@ -46,13 +46,14 @@ ARGSDIR ?= /etc/vdr/conf.d
CACHEDIR ?= /var/cache/vdr CACHEDIR ?= /var/cache/vdr
PREFIX ?= /usr/local PREFIX ?= /usr/local
BINDIR ?= $(PREFIX)/bin VDRROOT ?= $(PREFIX)
INCDIR ?= $(PREFIX)/include BINDIR ?= $(VDRROOT)/bin
LIBDIR ?= $(PREFIX)/lib/vdr INCDIR ?= $(VDRROOT)/include
LOCDIR ?= $(PREFIX)/share/locale LIBDIR ?= $(VDRROOT)/lib/vdr
MANDIR ?= $(PREFIX)/share/man LOCDIR ?= $(VDRROOT)/share/locale
PCDIR ?= $(PREFIX)/lib/pkgconfig MANDIR ?= $(VDRROOT)/share/man
RESDIR ?= $(PREFIX)/share/vdr PCDIR ?= $(VDRROOT)/lib/pkgconfig
RESDIR ?= $(VDRROOT)/share/vdr
# Source documentation # Source documentation
@ -169,7 +170,9 @@ make-libsi: # empty rule makes sure the sub-make for libsi is always called
.PHONY: vdr.pc .PHONY: vdr.pc
vdr.pc: vdr.pc:
@echo "bindir=$(BINDIR)" > $@ @echo "vdrrootdir=$(VDRROOT)" > $@
@echo "bindir=$(BINDIR)" >> $@
@echo "incdir=$(INCDIR)" >> $@
@echo "mandir=$(MANDIR)" >> $@ @echo "mandir=$(MANDIR)" >> $@
@echo "videodir=$(VIDEODIR)" >> $@ @echo "videodir=$(VIDEODIR)" >> $@
@echo "configdir=$(CONFDIR)" >> $@ @echo "configdir=$(CONFDIR)" >> $@
@ -185,7 +188,7 @@ vdr.pc:
@echo "" >> $@ @echo "" >> $@
@echo "Name: VDR" >> $@ @echo "Name: VDR" >> $@
@echo "Description: Video Disk Recorder" >> $@ @echo "Description: Video Disk Recorder" >> $@
@echo "URL: http://www.tvdr.de/" >> $@ @echo "URL: https://www.tvdr.de/" >> $@
@echo "Version: $(VDRVERSION)" >> $@ @echo "Version: $(VDRVERSION)" >> $@
@echo "Cflags: \$${cflags}" >> $@ @echo "Cflags: \$${cflags}" >> $@
@ -320,6 +323,7 @@ install-doc:
install-plugins: plugins install-plugins: plugins
@-for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\ @-for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do\
echo; echo "*** Plugin $$i:";\
$(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" VDRDIR=$(CWD) DESTDIR=$(DESTDIR) install;\ $(MAKE) --no-print-directory -C "$(PLUGINDIR)/src/$$i" VDRDIR=$(CWD) DESTDIR=$(DESTDIR) install;\
done done
@if [ -d $(PLUGINDIR)/lib ] ; then\ @if [ -d $(PLUGINDIR)/lib ] ; then\
@ -356,7 +360,7 @@ srcdoc:
clean: clean:
@$(MAKE) --no-print-directory -C $(LSIDIR) clean @$(MAKE) --no-print-directory -C $(LSIDIR) clean
@-rm -f $(OBJS) $(DEPFILE) vdr vdr.pc core* *~ @-rm -f $(OBJS) $(DEPFILE) vdr vdr.pc core* *~
@-rm -rf $(LOCALEDIR) $(PODIR)/*.mo $(PODIR)/*.pot @-rm -rf $(LOCALEDIR) $(PODIR)/*~ $(PODIR)/*.mo $(PODIR)/*.pot
@-rm -rf include @-rm -rf include
@-rm -rf srcdoc @-rm -rf srcdoc
CLEAN: clean CLEAN: clean

View File

@ -35,7 +35,7 @@ modified {
<p> <p>
Copyright &copy; 2021 Klaus Schmidinger<br> Copyright &copy; 2021 Klaus Schmidinger<br>
<a href="mailto:vdr@tvdr.de">vdr@tvdr.de</a><br> <a href="mailto:vdr@tvdr.de">vdr@tvdr.de</a><br>
<a href="http://www.tvdr.de">www.tvdr.de</a> <a href="https://www.tvdr.de">www.tvdr.de</a>
</div> </div>
<p> <p>
VDR provides an easy to use plugin interface that allows additional functionality VDR provides an easy to use plugin interface that allows additional functionality
@ -74,7 +74,6 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Main menu entry">Main menu entry</a> <li><a href="#Main menu entry">Main menu entry</a>
<li><a href="#User interaction">User interaction</a> <li><a href="#User interaction">User interaction</a>
<li><a href="#Housekeeping">Housekeeping</a> <li><a href="#Housekeeping">Housekeeping</a>
<li><a href="#Main thread hook">Main thread hook</a>
<li><a href="#Activity">Activity</a> <li><a href="#Activity">Activity</a>
<li><a href="#Wakeup">Wakeup</a> <li><a href="#Wakeup">Wakeup</a>
<li><a href="#Setup parameters">Setup parameters</a> <li><a href="#Setup parameters">Setup parameters</a>
@ -166,7 +165,7 @@ is used:
VDR/PLUGINS/src VDR/PLUGINS/src
VDR/PLUGINS/src/hello VDR/PLUGINS/src/hello
VDR/PLUGINS/lib VDR/PLUGINS/lib
VDR/PLUGINS/lib/libvdr-hello.so.1.1.0 VDR/PLUGINS/lib/libvdr-hello.so.1
</pre></td></tr></table><p> </pre></td></tr></table><p>
The <tt>src</tt> directory contains one subdirectory for each plugin, which carries The <tt>src</tt> directory contains one subdirectory for each plugin, which carries
@ -185,7 +184,7 @@ The <tt>lib</tt> directory contains the dynamically loadable libraries of all
available plugins. Note that the names of these files are created by concatenating available plugins. Note that the names of these files are created by concatenating
<p> <p>
<table border=2> <table border=2>
<tr><td align=center><b><tt>libvdr-</tt></b></td><td align=center><b><tt>hello</tt></b></td><td align=center><b><tt>.so.</tt></b></td><td align=center><b><tt>1.1.0</tt></b></td></tr> <tr><td align=center><b><tt>libvdr-</tt></b></td><td align=center><b><tt>hello</tt></b></td><td align=center><b><tt>.so.</tt></b></td><td align=center><b><tt>1</tt></b></td></tr>
<tr><td align=center><small>VDR plugin<br>library prefix</small></td><td align=center><small>name of<br>the plugin</small></td><td align=center><small>shared object<br>indicator</small></td><td align=center><small>API version number<br>this plugin was<br>compiled for</small></td></tr> <tr><td align=center><small>VDR plugin<br>library prefix</small></td><td align=center><small>name of<br>the plugin</small></td><td align=center><small>shared object<br>indicator</small></td><td align=center><small>API version number<br>this plugin was<br>compiled for</small></td></tr>
</table> </table>
<p> <p>
@ -196,6 +195,11 @@ the current VDR version. That way minor fixes to VDR, that don't require changes
to the VDR header files, can be made without requiring all plugins to be to the VDR header files, can be made without requiring all plugins to be
recompiled. recompiled.
<p> <p>
While in earlier versions of VDR the API version number was closely related to the
VDR version number, starting with VDR version 2.7.2 the API version number was changed
from a dot separated, three part number to a single integer, completely unrelated to
the VDR version. This was done to avoid confusion.
<p>
The plugin library files can be stored in any directory. If the default organization The plugin library files can be stored in any directory. If the default organization
is not used, the path to the plugin directory has be be given to VDR through the is not used, the path to the plugin directory has be be given to VDR through the
<b><tt>-L</tt></b> option. <b><tt>-L</tt></b> option.
@ -391,13 +395,7 @@ just like shown in the above example. This is a convention that allows the <tt>M
to extract the version number when generating the file name for the distribution archive. to extract the version number when generating the file name for the distribution archive.
<p> <p>
A new plugin project should start with version number <tt>0.0.1</tt> and should reach A new plugin project should start with version number <tt>0.0.1</tt> and should reach
version <tt>1.0.0</tt> once it is completely operative and well tested. Following the version <tt>1.0.0</tt> once it is completely operative and well tested.
Linux kernel version numbering scheme, versions with <i>even</i> release numbers
(like <tt>1.0.x</tt>, <tt>1.2.x</tt>, <tt>1.4.x</tt>...) should be stable releases,
while those with <i>odd</i> release numbers (like <tt>1.1.x</tt>, <tt>1.3.x</tt>,
<tt>1.5.x</tt>...) are usually considered "under development". The three parts of
a version number are not limited to single digits, so a version number of <tt>1.2.15</tt>
would be acceptable.
<hr><h2><a name="Description">Description</a></h2> <hr><h2><a name="Description">Description</a></h2>
@ -693,27 +691,6 @@ interaction is possible. If a specific action takes longer than a few seconds,
the plugin should launch a separate thread to do this. the plugin should launch a separate thread to do this.
</b> </b>
<hr><h2><a name="Main thread hook">Main thread hook</a></h2>
<div class="blurb">Pushing in...</div><p>
Normally a plugin only reacts on user input if directly called through its
<a href="#Main menu entry">main menu entry</a>, or performs some background
activity in a separate thread. However, sometimes a plugin may need to do
something in the context of the main program thread, without being explicitly
called up by the user. In such a case it can implement the function
<p><table><tr><td class="code"><pre>
virtual void MainThreadHook(void);
</pre></td></tr></table><p>
in which it can do this. This function is called for every plugin once during
every cycle of VDR's main program loop, which typically happens once every
second.
<b>Be very careful when using this function, and make sure you return from it
as soon as possible! If you spend too much time in this function, the user
interface performance will become sluggish!</b>
<hr><h2><a name="Activity">Activity</a></h2> <hr><h2><a name="Activity">Activity</a></h2>
<div class="blurb">Now is not a good time!</div><p> <div class="blurb">Now is not a good time!</div><p>

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Udo Richter <udo_richter@gmx.de> Written by: Udo Richter <udo_richter@gmx.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -76,3 +76,10 @@ VDR Plugin 'status' Revision History
2021-12-27: Version 2.6.0 2021-12-27: Version 2.6.0
- Official release. - Official release.
2025-02-10: Version 2.6.1
- Added cStatus::OsdItem2().
- Activated logging of OsdItem2().
- Added cStatus::OsdCurrentItem2().
- Added cStatus::OsdStatusMessage2().

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <vdr@tvdr.de> Written by: Klaus Schmidinger <vdr@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@ -3,13 +3,13 @@
* *
* See the README file for copyright information and how to reach the author. * See the README file for copyright information and how to reach the author.
* *
* $Id: status.c 4.1 2018/04/10 13:01:03 kls Exp $ * $Id: status.c 5.4 2025/02/12 21:18:53 kls Exp $
*/ */
#include <vdr/plugin.h> #include <vdr/plugin.h>
#include <vdr/status.h> #include <vdr/status.h>
static const char *VERSION = "2.4.0"; static const char *VERSION = "2.6.1";
static const char *DESCRIPTION = "Status monitor test"; static const char *DESCRIPTION = "Status monitor test";
static const char *MAINMENUENTRY = NULL; static const char *MAINMENUENTRY = NULL;
@ -27,10 +27,10 @@ protected:
virtual void SetSubtitleTrack(int Index, const char * const *Tracks); virtual void SetSubtitleTrack(int Index, const char * const *Tracks);
virtual void OsdClear(void); virtual void OsdClear(void);
virtual void OsdTitle(const char *Title); virtual void OsdTitle(const char *Title);
virtual void OsdStatusMessage(const char *Message); virtual void OsdStatusMessage2(eMessageType Type, const char *Message);
virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue); virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
virtual void OsdItem(const char *Text, int Index); virtual void OsdItem2(const char *Text, int Index, bool Selectable);
virtual void OsdCurrentItem(const char *Text); virtual void OsdCurrentItem2(const char *Text, int Index);
virtual void OsdTextItem(const char *Text, bool Scroll); virtual void OsdTextItem(const char *Text, bool Scroll);
virtual void OsdChannel(const char *Text); virtual void OsdChannel(const char *Text);
virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle); virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
@ -86,9 +86,9 @@ void cStatusTest::OsdTitle(const char *Title)
dsyslog("status: cStatusTest::OsdTitle '%s'", Title); dsyslog("status: cStatusTest::OsdTitle '%s'", Title);
} }
void cStatusTest::OsdStatusMessage(const char *Message) void cStatusTest::OsdStatusMessage2(eMessageType Type, const char *Message)
{ {
dsyslog("status: cStatusTest::OsdStatusMessage '%s'", Message); dsyslog("status: cStatusTest::OsdStatusMessage2 %d '%s'", Type, Message);
} }
void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
@ -96,14 +96,14 @@ void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Ye
dsyslog("status: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green, Yellow, Blue); dsyslog("status: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green, Yellow, Blue);
} }
void cStatusTest::OsdItem(const char *Text, int Index) void cStatusTest::OsdItem2(const char *Text, int Index, bool Selected)
{ {
//dsyslog("status: cStatusTest::OsdItem %s %d", Text, Index); dsyslog("status: cStatusTest::OsdItem2 %s %d %d", Text, Index, Selected);
} }
void cStatusTest::OsdCurrentItem(const char *Text) void cStatusTest::OsdCurrentItem2(const char *Text, int Index)
{ {
dsyslog("status: cStatusTest::OsdCurrentItem %s", Text); dsyslog("status: cStatusTest::OsdCurrentItem %s %d", Text, Index);
} }
void cStatusTest::OsdTextItem(const char *Text, bool Scroll) void cStatusTest::OsdTextItem(const char *Text, bool Scroll)

View File

@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <Klaus.Schmidinger@tvdr.de> Written by: Klaus Schmidinger <Klaus.Schmidinger@tvdr.de>
Project's homepage: http://www.tvdr.de Project's homepage: https://www.tvdr.de
Latest version available at: http://www.tvdr.de Latest version available at: https://www.tvdr.de
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

4
README
View File

@ -4,7 +4,7 @@ Video Disk Recorder ('VDR')
These files contain the source code of the "Video Disk Recorder", These files contain the source code of the "Video Disk Recorder",
which is based on the DVB driver of the LinuxTV project (http://linuxtv.org). which is based on the DVB driver of the LinuxTV project (http://linuxtv.org).
For details about the "Video Disk Recorder" project please For details about the "Video Disk Recorder" project please
refer to http://www.tvdr.de. refer to https://www.tvdr.de.
Please see the INSTALL file for details on how to install Please see the INSTALL file for details on how to install
this program on your computer. this program on your computer.
@ -33,7 +33,7 @@ the ones in this system, but here we have the full source code
and can modify the menus in whatever way desired. and can modify the menus in whatever way desired.
If you actually use VDR, please add yourself to the "VDR User Counter" If you actually use VDR, please add yourself to the "VDR User Counter"
at http://www.tvdr.de/counter.htm. You can also like VDR on facebook at https://www.tvdr.de/counter.htm. You can also like VDR on facebook
at https://www.facebook.com/VideoDiskRecorder. at https://www.facebook.com/VideoDiskRecorder.

View File

@ -10,7 +10,7 @@ Plugins:
- Implemented a universal plugin interface. See the file PLUGINS.html - Implemented a universal plugin interface. See the file PLUGINS.html
for a detailed description. The man page vdr(1) describes the new options '-L' for a detailed description. The man page vdr(1) describes the new options '-L'
and '-P' used to load plugins. and '-P' used to load plugins.
See http://www.tvdr.de/plugins.htm for a list of available plugins. See https://www.tvdr.de/plugins.htm for a list of available plugins.
- Rearranged the remote control key handling to allow plugins to implement - Rearranged the remote control key handling to allow plugins to implement
additional types of remote controls (see PLUGINS.html, section "Remote Control"). additional types of remote controls (see PLUGINS.html, section "Remote Control").
The previously used files 'keys.conf' and 'keys-pc.conf' have been replaced The previously used files 'keys.conf' and 'keys-pc.conf' have been replaced

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.h 5.19 2024/09/09 10:58:55 kls Exp $ * $Id: config.h 5.26 2025/02/26 10:35:03 kls Exp $
*/ */
#ifndef __CONFIG_H #ifndef __CONFIG_H
@ -22,19 +22,21 @@
// VDR's own version number: // VDR's own version number:
#define VDRVERSION "2.7.1" #define VDRVERSION "2.7.4"
#define VDRVERSNUM 20701 // Version * 10000 + Major * 100 + Minor #define VDRVERSNUM 20704 // Version * 10000 + Major * 100 + Minor
// The plugin API's version number: // The plugin API's version number:
#define APIVERSION "2.6.9" #define APIVERSION "6"
#define APIVERSNUM 20609 // Version * 10000 + Major * 100 + Minor #define APIVERSNUM 30006
// When loading plugins, VDR searches them by their APIVERSION, which // When loading plugins, VDR searches files by their APIVERSION, which
// may be smaller than VDRVERSION in case there have been no changes to // is different from VDRVERSION. APIVERSION is a plain number, incremented
// VDR header files since the last APIVERSION. This allows compiled // only when there are changes to the plugin API. This allows compiled
// plugins to work with newer versions of the core VDR as long as no // plugins to work with newer versions of the core VDR as long as no
// VDR header files have changed. // interfaces have changed. APIVERSNUM begins with "300.." for backwards
// compatibility and can be used in #if preprocessor statements to handle
// version dependent code.
#define MAXPRIORITY 99 #define MAXPRIORITY 99
#define MINPRIORITY (-MAXPRIORITY) #define MINPRIORITY (-MAXPRIORITY)

View File

@ -4,12 +4,11 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: cutter.c 5.1 2022/11/06 11:25:13 kls Exp $ * $Id: cutter.c 5.4 2025/01/10 13:12:04 kls Exp $
*/ */
#include "cutter.h" #include "cutter.h"
#include "menu.h" #include "menu.h"
#include "recording.h"
#include "remux.h" #include "remux.h"
#include "videodir.h" #include "videodir.h"
@ -232,6 +231,10 @@ private:
int numSequences; int numSequences;
off_t maxVideoFileSize; off_t maxVideoFileSize;
off_t fileSize; off_t fileSize;
int frameErrors;
time_t lastErrorHandling;
cString editedRecordingName;
cRecordingInfo *recordingInfo;
bool suspensionLogged; bool suspensionLogged;
int sequence; // cutting sequence int sequence; // cutting sequence
int delta; // time between two frames (PTS ticks) int delta; // time between two frames (PTS ticks)
@ -246,7 +249,7 @@ private:
cPatPmtParser patPmtParser; cPatPmtParser patPmtParser;
bool Throttled(void); bool Throttled(void);
bool SwitchFile(bool Force = false); bool SwitchFile(bool Force = false);
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length); bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors = NULL, bool *Missing = NULL);
bool FramesAreEqual(int Index1, int Index2); bool FramesAreEqual(int Index1, int Index2);
void GetPendingPackets(uchar *Buffer, int &Length, int Index); void GetPendingPackets(uchar *Buffer, int &Length, int Index);
// Gather all non-video TS packets from Index upward that either belong to // Gather all non-video TS packets from Index upward that either belong to
@ -254,15 +257,16 @@ private:
// and add them to the end of the given Data. // and add them to the end of the given Data.
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut); bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex); bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
void HandleErrors(bool Force = false);
protected: protected:
virtual void Action(void); virtual void Action(void);
public: public:
cCuttingThread(const char *FromFileName, const char *ToFileName); cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo);
virtual ~cCuttingThread(); virtual ~cCuttingThread();
const char *Error(void) { return error; } const char *Error(void) { return error; }
}; };
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName) cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo)
:cThread("video cutting", true) :cThread("video cutting", true)
{ {
error = NULL; error = NULL;
@ -274,6 +278,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
framesPerSecond = Recording.FramesPerSecond(); framesPerSecond = Recording.FramesPerSecond();
suspensionLogged = false; suspensionLogged = false;
fileSize = 0; fileSize = 0;
frameErrors = 0;
lastErrorHandling = 0;
editedRecordingName = ToFileName;
recordingInfo = RecordingInfo;
sequence = 0; sequence = 0;
delta = int(round(PTSTICKS / framesPerSecond)); delta = int(round(PTSTICKS / framesPerSecond));
lastVidPts = -1; lastVidPts = -1;
@ -294,6 +302,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
if (fromIndex->GetErrors()->Size() > 0) {
recordingInfo->SetErrors(0); // the fromIndex has error indicators, so we reset the error count
recordingInfo->Write();
}
Start(); Start();
} }
else else
@ -328,11 +340,11 @@ bool cCuttingThread::Throttled(void)
return false; return false;
} }
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length) bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length, bool *Errors, bool *Missing)
{ {
uint16_t FileNumber; uint16_t FileNumber;
off_t FileOffset; off_t FileOffset;
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) { if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length, Errors, Missing)) {
fromFile = fromFileName->SetOffset(FileNumber, FileOffset); fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
if (fromFile) { if (fromFile) {
fromFile->SetReadAhead(MEGABYTE(20)); fromFile->SetReadAhead(MEGABYTE(20));
@ -555,7 +567,9 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) { for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
bool Independent; bool Independent;
int Length; int Length;
if (LoadFrame(Index, Buffer, Independent, Length)) { bool Errors;
bool Missing;
if (LoadFrame(Index, Buffer, Independent, Length, &Errors, &Missing)) {
// Make sure there is enough disk space: // Make sure there is enough disk space:
AssertFreeDiskSpace(-1); AssertFreeDiskSpace(-1);
bool CutIn = !SeamlessBegin && Index == BeginIndex; bool CutIn = !SeamlessBegin && Index == BeginIndex;
@ -572,10 +586,12 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
return false; return false;
} }
// Write index: // Write index:
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) { if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize, Errors, Missing)) {
error = "toIndex"; error = "toIndex";
return false; return false;
} }
frameErrors += Errors + Missing;
HandleErrors();
// Write data: // Write data:
if (toFile->Write(Buffer, Length) < 0) { if (toFile->Write(Buffer, Length) < 0) {
error = "safe_write"; error = "safe_write";
@ -596,6 +612,27 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
return true; return true;
} }
#define ERROR_HANDLING_DELTA 1 // seconds between handling errors
void cCuttingThread::HandleErrors(bool Force)
{
if (Force || time(NULL) - lastErrorHandling >= ERROR_HANDLING_DELTA) {
if (frameErrors > recordingInfo->Errors()) {
recordingInfo->SetErrors(frameErrors);
recordingInfo->Write();
Force = true;
}
if (Force) {
cStateKey StateKey;
if (cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey, 1)) {
Recordings->UpdateByName(editedRecordingName);
StateKey.Remove();
}
}
lastErrorHandling = time(NULL);
}
}
void cCuttingThread::Action(void) void cCuttingThread::Action(void)
{ {
if (cMark *BeginMark = fromMarks.GetNextBegin()) { if (cMark *BeginMark = fromMarks.GetNextBegin()) {
@ -604,6 +641,7 @@ void cCuttingThread::Action(void)
if (!fromFile || !toFile) if (!fromFile || !toFile)
return; return;
int LastEndIndex = -1; int LastEndIndex = -1;
HandleErrors(true); // to make sure an initially reset error count is displayed correctly
while (BeginMark && Running()) { while (BeginMark && Running()) {
// Suspend cutting if we have severe throughput problems: // Suspend cutting if we have severe throughput problems:
if (Throttled()) { if (Throttled()) {
@ -634,6 +672,7 @@ void cCuttingThread::Action(void)
} }
} }
} }
HandleErrors(true);
} }
else else
esyslog("no editing marks found!"); esyslog("no editing marks found!");
@ -642,6 +681,7 @@ void cCuttingThread::Action(void)
// --- cCutter --------------------------------------------------------------- // --- cCutter ---------------------------------------------------------------
cCutter::cCutter(const char *FileName) cCutter::cCutter(const char *FileName)
:recordingInfo(FileName)
{ {
cuttingThread = NULL; cuttingThread = NULL;
error = false; error = false;
@ -676,9 +716,11 @@ bool cCutter::Start(void)
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different! if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName); cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) { if (cVideoDirectory::RemoveVideoFile(editedVersionName) && MakeDirs(editedVersionName, true)) {
Recording.WriteInfo(editedVersionName); recordingInfo.Read();
recordingInfo.SetFileName(editedVersionName);
recordingInfo.Write();
SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName)); SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName); cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
return true; return true;
} }
} }

View File

@ -4,12 +4,13 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: cutter.h 3.1 2013/10/05 11:34:55 kls Exp $ * $Id: cutter.h 5.1 2024/09/19 20:21:58 kls Exp $
*/ */
#ifndef __CUTTER_H #ifndef __CUTTER_H
#define __CUTTER_H #define __CUTTER_H
#include "recording.h"
#include "thread.h" #include "thread.h"
#include "tools.h" #include "tools.h"
@ -19,6 +20,7 @@ class cCutter {
private: private:
cString originalVersionName; cString originalVersionName;
cString editedVersionName; cString editedVersionName;
cRecordingInfo recordingInfo;
cCuttingThread *cuttingThread; cCuttingThread *cuttingThread;
bool error; bool error;
public: public:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbplayer.c 5.3 2022/12/27 15:57:20 kls Exp $ * $Id: dvbplayer.c 5.7 2025/02/19 15:39:16 kls Exp $
*/ */
#include "dvbplayer.h" #include "dvbplayer.h"
@ -36,7 +36,7 @@ public:
bool IsEmpty(void); bool IsEmpty(void);
void Put(uint32_t Pts, int Index, bool Independent); void Put(uint32_t Pts, int Index, bool Independent);
int FindIndex(uint32_t Pts); int FindIndex(uint32_t Pts);
int FindFrameNumber(uint32_t Pts); int FindFrameNumber(uint32_t Pts, bool Forward);
}; };
cPtsIndex::cPtsIndex(void) cPtsIndex::cPtsIndex(void)
@ -90,29 +90,29 @@ int cPtsIndex::FindIndex(uint32_t Pts)
return Index; return Index;
} }
int cPtsIndex::FindFrameNumber(uint32_t Pts) int cPtsIndex::FindFrameNumber(uint32_t Pts, bool Forward)
{ {
if (!Forward)
return FindIndex(Pts); // there are only I frames in backward
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
if (w == r) if (w == r)
return lastFound; // replay always starts at an I frame return lastFound; // replay always starts at an I frame
bool Valid = false; bool Valid = false;
int d;
int FrameNumber = 0; int FrameNumber = 0;
int UnplayedIFrame = 2; // GOPs may intersect, so we're looping until we found two unplayed I frames int UnplayedIFrame = 2; // GOPs may intersect, so we loop until we processed a complete unplayed GOP
for (int i = r; i != w && UnplayedIFrame; ) { for (int i = r; i != w && UnplayedIFrame; ) {
d = Pts - pi[i].pts; int32_t d = int32_t(Pts - pi[i].pts); // typecast handles rollover
if (d > 0x7FFFFFFF) if (d >= 0) {
d = 0xFFFFFFFF - d; // handle rollover
if (d > 0) {
if (pi[i].independent) { if (pi[i].independent) {
FrameNumber = pi[i].index; // an I frame's index represents its frame number FrameNumber = pi[i].index; // an I frame's index represents its frame number
Valid = true; Valid = true;
if (d == 0)
UnplayedIFrame = 1; // if Pts is at an I frame we only need to check up to the next I frame
} }
else else
FrameNumber++; // for every played non-I frame, increase frame number FrameNumber++; // for every played non-I frame, increase frame number
} }
else else if (pi[i].independent)
if (pi[i].independent)
--UnplayedIFrame; --UnplayedIFrame;
if (++i >= PTSINDEX_ENTRIES) if (++i >= PTSINDEX_ENTRIES)
i = 0; i = 0;
@ -283,6 +283,7 @@ public:
void Goto(int Position, bool Still = false); void Goto(int Position, bool Still = false);
virtual double FramesPerSecond(void) { return framesPerSecond; } virtual double FramesPerSecond(void) { return framesPerSecond; }
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId); virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId);
virtual const cErrors *GetErrors(void);
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
virtual bool GetFrameNumber(int &Current, int &Total); virtual bool GetFrameNumber(int &Current, int &Total);
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed); virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
@ -792,15 +793,17 @@ void cDvbPlayer::Forward(void)
Pause(); Pause();
break; break;
} }
Empty();
// run into pmPause // run into pmPause
case pmStill: case pmStill:
case pmPause: case pmPause: {
LOCK_THREAD;
Empty();
DeviceMute(); DeviceMute();
playMode = pmSlow; playMode = pmSlow;
playDir = pdForward; playDir = pdForward;
trickSpeed = NORMAL_SPEED; trickSpeed = NORMAL_SPEED;
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS); TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
}
break; break;
default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__); default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
} }
@ -841,7 +844,6 @@ void cDvbPlayer::Backward(void)
Pause(); Pause();
break; break;
} }
Empty();
// run into pmPause // run into pmPause
case pmStill: case pmStill:
case pmPause: { case pmPause: {
@ -940,6 +942,13 @@ void cDvbPlayer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
resyncAfterPause = true; resyncAfterPause = true;
} }
const cErrors *cDvbPlayer::GetErrors(void)
{
if (index)
return index->GetErrors();
return NULL;
}
bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame) bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{ {
if (index) { if (index) {
@ -959,7 +968,7 @@ bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
bool cDvbPlayer::GetFrameNumber(int &Current, int &Total) bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
{ {
if (index) { if (index) {
Current = ptsIndex.FindFrameNumber(DeviceGetSTC()); Current = ptsIndex.FindFrameNumber(DeviceGetSTC(), playDir == pdForward);
Total = index->Last(); Total = index->Last();
return true; return true;
} }
@ -1047,6 +1056,13 @@ int cDvbPlayerControl::SkipFrames(int Frames)
return -1; return -1;
} }
const cErrors *cDvbPlayerControl::GetErrors(void)
{
if (player)
return player->GetErrors();
return NULL;
}
bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame) bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
{ {
if (player) { if (player) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbplayer.h 4.2 2016/12/22 10:36:50 kls Exp $ * $Id: dvbplayer.h 5.1 2024/09/19 09:49:02 kls Exp $
*/ */
#ifndef __DVBPLAYER_H #ifndef __DVBPLAYER_H
@ -47,6 +47,8 @@ public:
// The sign of 'Seconds' determines the direction in which to skip. // The sign of 'Seconds' determines the direction in which to skip.
// Use a very large negative value to go all the way back to the // Use a very large negative value to go all the way back to the
// beginning of the recording. // beginning of the recording.
const cErrors *GetErrors(void);
// Returns the frame indexes of errors in the recording (if any).
bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false); bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
// Returns the current and total frame index, optionally snapped to the // Returns the current and total frame index, optionally snapped to the
// nearest I-frame. // nearest I-frame.

52
epg.c
View File

@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by * Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* *
* $Id: epg.c 5.9 2024/06/21 06:27:20 kls Exp $ * $Id: epg.c 5.13 2024/11/30 14:30:46 kls Exp $
*/ */
#include "epg.h" #include "epg.h"
@ -145,7 +145,10 @@ cEvent::~cEvent()
int cEvent::Compare(const cListObject &ListObject) const int cEvent::Compare(const cListObject &ListObject) const
{ {
cEvent *e = (cEvent *)&ListObject; cEvent *e = (cEvent *)&ListObject;
return startTime - e->startTime; int d = startTime - e->startTime;
if (d == 0)
d = int(tableID) - int(e->tableID);
return d;
} }
tChannelID cEvent::ChannelID(void) const tChannelID cEvent::ChannelID(void) const
@ -300,7 +303,7 @@ const char *cEvent::ContentToString(uchar Content)
case 0x01: return tr("Content$News/Weather Report"); case 0x01: return tr("Content$News/Weather Report");
case 0x02: return tr("Content$News Magazine"); case 0x02: return tr("Content$News Magazine");
case 0x03: return tr("Content$Documentary"); case 0x03: return tr("Content$Documentary");
case 0x04: return tr("Content$Discussion/Inverview/Debate"); case 0x04: return tr("Content$Discussion/Interview/Debate");
} }
break; break;
case ecgShow: case ecgShow:
@ -346,7 +349,7 @@ const char *cEvent::ContentToString(uchar Content)
case 0x00: return tr("Content$Music/Ballet/Dance"); case 0x00: return tr("Content$Music/Ballet/Dance");
case 0x01: return tr("Content$Rock/Pop"); case 0x01: return tr("Content$Rock/Pop");
case 0x02: return tr("Content$Serious/Classical Music"); case 0x02: return tr("Content$Serious/Classical Music");
case 0x03: return tr("Content$Folk/Tradional Music"); case 0x03: return tr("Content$Folk/Traditional Music");
case 0x04: return tr("Content$Jazz"); case 0x04: return tr("Content$Jazz");
case 0x05: return tr("Content$Musical/Opera"); case 0x05: return tr("Content$Musical/Opera");
case 0x06: return tr("Content$Ballet"); case 0x06: return tr("Content$Ballet");
@ -928,7 +931,6 @@ cSchedule::cSchedule(tChannelID ChannelID)
channelID = ChannelID; channelID = ChannelID;
events.SetUseGarbageCollector(); events.SetUseGarbageCollector();
numTimers = 0; numTimers = 0;
hasRunning = false;
modified = 0; modified = 0;
onActualTp = false; onActualTp = false;
presentSeen = 0; presentSeen = 0;
@ -1028,18 +1030,6 @@ const cEvent *cSchedule::GetFollowingEvent(void) const
return p; return p;
} }
#if DEPRECATED_SCHEDULE_GET_EVENT
const cEvent *cSchedule::GetEvent(tEventID EventID, time_t StartTime) const
{
// Returns the event info with the given StartTime or, if no actual StartTime
// is given, the one with the given EventID.
if (StartTime > 0) // 'StartTime < 0' is apparently used with NVOD channels
return eventsHashStartTime.Get(StartTime);
else
return eventsHashID.Get(EventID);
}
#endif
const cEvent *cSchedule::GetEventById(tEventID EventID) const const cEvent *cSchedule::GetEventById(tEventID EventID) const
{ {
return eventsHashID.Get(EventID); return eventsHashID.Get(EventID);
@ -1068,7 +1058,6 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel) void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel)
{ {
hasRunning = false;
for (cEvent *p = events.First(); p; p = events.Next(p)) { for (cEvent *p = events.First(); p; p = events.Next(p)) {
if (p == Event) { if (p == Event) {
if (p->RunningStatus() > SI::RunningStatusNotRunning || RunningStatus > SI::RunningStatusNotRunning) { if (p->RunningStatus() > SI::RunningStatusNotRunning || RunningStatus > SI::RunningStatusNotRunning) {
@ -1078,25 +1067,20 @@ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChanne
} }
else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime()) else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime())
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel); p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
if (p->RunningStatus() >= SI::RunningStatusPausing)
hasRunning = true;
} }
SetPresentSeen(); SetPresentSeen();
} }
void cSchedule::ClrRunningStatus(cChannel *Channel) void cSchedule::ClrRunningStatus(cChannel *Channel)
{ {
if (hasRunning) {
for (cEvent *p = events.First(); p; p = events.Next(p)) { for (cEvent *p = events.First(); p; p = events.Next(p)) {
if (p->RunningStatus() >= SI::RunningStatusPausing) { if (p->RunningStatus() >= SI::RunningStatusPausing) {
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel); p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
hasRunning = false;
SetModified(); SetModified();
break; break;
} }
} }
} }
}
void cSchedule::ResetVersions(void) void cSchedule::ResetVersions(void)
{ {
@ -1107,14 +1091,6 @@ void cSchedule::ResetVersions(void)
void cSchedule::Sort(void) void cSchedule::Sort(void)
{ {
events.Sort(); events.Sort();
// Make sure there are no RunningStatusUndefined before the currently running event:
if (hasRunning) {
for (cEvent *p = events.First(); p; p = events.Next(p)) {
if (p->RunningStatus() >= SI::RunningStatusPausing)
break;
p->SetRunningStatus(SI::RunningStatusNotRunning);
}
}
SetModified(); SetModified();
} }
@ -1141,6 +1117,20 @@ void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar Table
p = n; p = n;
} }
} }
// Make sure there are no two events with the same start time:
for (cEvent *p = events.First(); p; p = events.Next(p)) {
cEvent *n = events.Next(p);
if (n && n->StartTime() == p->StartTime())
DelEvent(n);
}
// Make sure there are no RunningStatusUndefined before the currently running event:
for (cEvent *p = events.First(); p; p = events.Next(p)) {
if (p->RunningStatus() >= SI::RunningStatusPausing) {
for (p = events.Prev(p); p; p = events.Prev(p))
p->SetRunningStatus(SI::RunningStatusNotRunning);
break;
}
}
} }
void cSchedule::Cleanup(void) void cSchedule::Cleanup(void)

9
epg.h
View File

@ -7,7 +7,7 @@
* Original version (as used in VDR before 1.3.0) written by * Original version (as used in VDR before 1.3.0) written by
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>. * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
* *
* $Id: epg.h 5.4 2024/07/15 14:42:22 kls Exp $ * $Id: epg.h 5.8 2024/10/13 09:47:18 kls Exp $
*/ */
#ifndef __EPG_H #ifndef __EPG_H
@ -157,7 +157,6 @@ private:
cHash<cEvent> eventsHashID; cHash<cEvent> eventsHashID;
cHash<cEvent> eventsHashStartTime; cHash<cEvent> eventsHashStartTime;
mutable u_int16_t numTimers;// The number of timers that use this schedule mutable u_int16_t numTimers;// The number of timers that use this schedule
bool hasRunning;
bool onActualTp; bool onActualTp;
int modified; int modified;
time_t presentSeen; time_t presentSeen;
@ -187,12 +186,6 @@ public:
const cList<cEvent> *Events(void) const { return &events; } const cList<cEvent> *Events(void) const { return &events; }
const cEvent *GetPresentEvent(void) const; const cEvent *GetPresentEvent(void) const;
const cEvent *GetFollowingEvent(void) const; const cEvent *GetFollowingEvent(void) const;
#ifndef DEPRECATED_SCHEDULE_GET_EVENT
#define DEPRECATED_SCHEDULE_GET_EVENT 0
#endif
#if DEPRECATED_SCHEDULE_GET_EVENT
const cEvent *GetEvent(tEventID EventID, time_t StartTime = 0) const;
#endif
const cEvent *GetEventById(tEventID EventID) const; const cEvent *GetEventById(tEventID EventID) const;
const cEvent *GetEventByTime(time_t StartTime) const; const cEvent *GetEventByTime(time_t StartTime) const;
const cEvent *GetEventAround(time_t Time) const; const cEvent *GetEventAround(time_t Time) const;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: filter.c 5.1 2021/03/16 15:10:54 kls Exp $ * $Id: filter.c 5.2 2024/10/13 09:47:18 kls Exp $
*/ */
#include "filter.h" #include "filter.h"
@ -73,35 +73,6 @@ bool cSectionSyncer::Processed(int SectionNumber, int LastSectionNumber, int Seg
return complete; return complete;
} }
#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT
void cSectionSyncer::Repeat(void)
{
SetSectionFlag(currentSection, false);
synced = false;
complete = false;
}
bool cSectionSyncer::Sync(uchar Version, int Number, int LastNumber)
{
if (Version != currentVersion) {
Reset();
currentVersion = Version;
}
if (!synced) {
if (Number != 0)
return false;
else
synced = true;
}
currentSection = Number;
bool Result = !GetSectionFlag(Number);
SetSectionFlag(Number, true);
if (Number == LastNumber)
complete = true;
return Result;
}
#endif
// --- cFilterData ----------------------------------------------------------- // --- cFilterData -----------------------------------------------------------
cFilterData::cFilterData(void) cFilterData::cFilterData(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: filter.h 5.2 2024/07/15 14:42:22 kls Exp $ * $Id: filter.h 5.5 2024/10/13 09:47:18 kls Exp $
*/ */
#ifndef __FILTER_H #ifndef __FILTER_H
@ -13,10 +13,6 @@
#include <sys/types.h> #include <sys/types.h>
#include "tools.h" #include "tools.h"
#ifndef DEPRECATED_SECTIONSYNCER_SYNC_REPEAT
#define DEPRECATED_SECTIONSYNCER_SYNC_REPEAT 0
#endif
class cSectionSyncer { class cSectionSyncer {
private: private:
int currentVersion; int currentVersion;
@ -51,10 +47,6 @@ public:
///< Returns true if all sections have been processed. ///< Returns true if all sections have been processed.
bool Complete(void) { return complete; } bool Complete(void) { return complete; }
///< Returns true if all sections have been processed. ///< Returns true if all sections have been processed.
#if DEPRECATED_SECTIONSYNCER_SYNC_REPEAT
void Repeat(void);
bool Sync(uchar Version, int Number, int LastNumber);
#endif
}; };
class cSectionSyncerRandom : public cSectionSyncer { class cSectionSyncerRandom : public cSectionSyncer {

4
font.c
View File

@ -6,7 +6,7 @@
* *
* BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya. * BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya.
* *
* $Id: font.c 5.2 2022/12/06 12:30:13 kls Exp $ * $Id: font.c 5.3 2025/02/17 11:13:13 kls Exp $
*/ */
#include "font.h" #include "font.h"
@ -618,7 +618,7 @@ void cTextWrapper::Set(const char *Text, const cFont *Font, int Width)
} }
} }
w += cw; w += cw;
if (strchr("-.,:;!?_", *p)) { if (strchr("-.,:;!?_~", *p)) {
Delim = p; Delim = p;
Blank = NULL; Blank = NULL;
} }

108
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 5.16 2024/08/30 20:43:26 kls Exp $ * $Id: menu.c 5.23 2025/02/25 15:53:43 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -342,6 +342,7 @@ void cMenuChannelItem::Set(void)
void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
{ {
LOCK_CHANNELS_READ;
if (!DisplayMenu->SetItemChannel(channel, Index, Current, Selectable, sortMode == csmProvider)) if (!DisplayMenu->SetItemChannel(channel, Index, Current, Selectable, sortMode == csmProvider))
DisplayMenu->SetItem(Text(), Index, Current, Selectable); DisplayMenu->SetItem(Text(), Index, Current, Selectable);
} }
@ -443,8 +444,9 @@ eOSState cMenuChannels::Number(eKeys Key)
number = number * 10 + Key - k0; number = number * 10 + Key - k0;
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) { for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) {
if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) { if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) {
DisplayCurrent(false);
SetCurrent(ci); SetCurrent(ci);
Display(); DisplayCurrent(true);
break; break;
} }
} }
@ -1263,6 +1265,7 @@ void cMenuTimerItem::Set(void)
} }
const char *File = timer->Pattern(); const char *File = timer->Pattern();
if (!*File) { if (!*File) {
LOCK_SCHEDULES_READ;
if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title()) if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title())
File = timer->Event()->Title(); File = timer->Event()->Title();
else { else {
@ -1291,6 +1294,7 @@ void cMenuTimerItem::Set(void)
void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
{ {
LOCK_TIMERS_READ;
if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable)) if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable))
DisplayMenu->SetItem(Text(), Index, Current, Selectable); DisplayMenu->SetItem(Text(), Index, Current, Selectable);
} }
@ -1334,12 +1338,15 @@ void cMenuTimers::Set(void)
const cTimer *CurrentTimer = GetTimer(); const cTimer *CurrentTimer = GetTimer();
cMenuTimerItem *CurrentItem = NULL; cMenuTimerItem *CurrentItem = NULL;
Clear(); Clear();
{
LOCK_SCHEDULES_READ;
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) { for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
cMenuTimerItem *Item = new cMenuTimerItem(Timer); cMenuTimerItem *Item = new cMenuTimerItem(Timer);
Add(Item); Add(Item);
if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0)) if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
CurrentItem = Item; CurrentItem = Item;
} }
}
Sort(); Sort();
SetCurrent(CurrentItem ? CurrentItem : First()); SetCurrent(CurrentItem ? CurrentItem : First());
SetHelpKeys(); SetHelpKeys();
@ -1454,6 +1461,7 @@ eOSState cMenuTimers::Info(void)
return osContinue; return osContinue;
LOCK_TIMERS_READ; LOCK_TIMERS_READ;
LOCK_CHANNELS_READ; LOCK_CHANNELS_READ;
LOCK_SCHEDULES_READ;
cTimer *Timer = GetTimer(); cTimer *Timer = GetTimer();
if (Timer && Timer->Event()) if (Timer && Timer->Event())
return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event())); return AddSubMenu(new cMenuEvent(Timers, Channels, Timer->Event()));
@ -1513,6 +1521,7 @@ cMenuEvent::cMenuEvent(const cTimers *Timers, const cChannels *Channels, const c
void cMenuEvent::Display(void) void cMenuEvent::Display(void)
{ {
LOCK_SCHEDULES_READ;
cOsdMenu::Display(); cOsdMenu::Display();
DisplayMenu()->SetEvent(event); DisplayMenu()->SetEvent(event);
if (event->Description()) if (event->Description())
@ -1599,6 +1608,8 @@ static const char *TimerMatchChars = " tT iI";
bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force) bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
{ {
LOCK_CHANNELS_READ;
LOCK_SCHEDULES_READ;
eTimerMatch OldTimerMatch = timerMatch; eTimerMatch OldTimerMatch = timerMatch;
bool OldTimerActive = timerActive; bool OldTimerActive = timerActive;
const cTimer *Timer = Timers->GetMatch(event, &timerMatch); const cTimer *Timer = Timers->GetMatch(event, &timerMatch);
@ -1626,6 +1637,8 @@ bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) void cMenuScheduleItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
{ {
LOCK_CHANNELS_READ;
LOCK_SCHEDULES_READ;
if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, withDate, timerMatch, timerActive)) if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, withDate, timerMatch, timerActive))
DisplayMenu->SetItem(Text(), Index, Current, Selectable); DisplayMenu->SetItem(Text(), Index, Current, Selectable);
} }
@ -1671,7 +1684,6 @@ cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, con
} }
} }
currentChannel = CurrentChannelNr; currentChannel = CurrentChannelNr;
Display();
SetHelpKeys(Channels); SetHelpKeys(Channels);
} }
@ -1772,10 +1784,8 @@ eOSState cMenuWhatsOn::Record(void)
if (HasSubMenu()) if (HasSubMenu())
CloseSubMenu(); CloseSubMenu();
} }
if (Update()) { if (Update())
LOCK_SCHEDULES_READ;
Display(); Display();
}
LOCK_CHANNELS_READ; LOCK_CHANNELS_READ;
SetHelpKeys(Channels); SetHelpKeys(Channels);
return osContinue; return osContinue;
@ -1795,6 +1805,7 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
case kGreen: { case kGreen: {
cMenuScheduleItem *mi = (cMenuScheduleItem *)Get(Current()); cMenuScheduleItem *mi = (cMenuScheduleItem *)Get(Current());
if (mi) { if (mi) {
LOCK_CHANNELS_READ;
scheduleEvent = mi->event; scheduleEvent = mi->event;
currentChannel = mi->channel->Number(); currentChannel = mi->channel->Number();
} }
@ -1807,14 +1818,12 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
case kChanUp: case kChanUp:
case kChanDn|k_Repeat: case kChanDn|k_Repeat:
case kChanDn: if (!HasSubMenu()) { case kChanDn: if (!HasSubMenu()) {
LOCK_CHANNELS_READ;
for (cOsdItem *item = First(); item; item = Next(item)) { for (cOsdItem *item = First(); item; item = Next(item)) {
if (((cMenuScheduleItem *)item)->channel->Number() == cDevice::CurrentChannel()) { if (((cMenuScheduleItem *)item)->channel->Number() == cDevice::CurrentChannel()) {
DisplayCurrent(false);
SetCurrent(item); SetCurrent(item);
{ DisplayCurrent(true);
LOCK_SCHEDULES_READ;
Display();
}
LOCK_CHANNELS_READ;
SetHelpKeys(Channels); SetHelpKeys(Channels);
break; break;
} }
@ -1832,10 +1841,8 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
} }
} }
else if (!HasSubMenu()) { else if (!HasSubMenu()) {
if (HadSubMenu && Update()) { if (HadSubMenu && Update())
LOCK_SCHEDULES_READ;
Display(); Display();
}
if (Key != kNone) { if (Key != kNone) {
LOCK_CHANNELS_READ; LOCK_CHANNELS_READ;
SetHelpKeys(Channels); SetHelpKeys(Channels);
@ -2083,10 +2090,8 @@ eOSState cMenuSchedule::Record(void)
if (HasSubMenu()) if (HasSubMenu())
CloseSubMenu(); CloseSubMenu();
} }
if (Update()) { if (Update())
LOCK_SCHEDULES_READ;
Display(); Display();
}
SetHelpKeys(); SetHelpKeys();
return osContinue; return osContinue;
} }
@ -2180,10 +2185,8 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
Set(Timers, Channels, Channel, true); Set(Timers, Channels, Channel, true);
} }
} }
else if (HadSubMenu && Update()) { else if (HadSubMenu && Update())
LOCK_SCHEDULES_READ;
Display(); Display();
}
if (Key != kNone) if (Key != kNone)
SetHelpKeys(); SetHelpKeys();
} }
@ -3042,6 +3045,7 @@ void cMenuRecordingItem::IncrementCounter(bool New)
void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable) void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
{ {
LOCK_RECORDINGS_READ;
if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries)) if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries))
DisplayMenu->SetItem(Text(), Index, Current, Selectable); DisplayMenu->SetItem(Text(), Index, Current, Selectable);
} }
@ -4666,15 +4670,16 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
default: break; default: break;
} }
} }
if (!HasSubMenu() && Update(HadSubMenu)) bool DoDisplay = Update();
Display();
if (Key != kNone) { if (Key != kNone) {
if (I18nCurrentLanguage() != osdLanguage) { if (I18nCurrentLanguage() != osdLanguage) {
Set(); Set();
if (!HasSubMenu()) if (!HasSubMenu())
DoDisplay = true;
}
}
if (DoDisplay)
Display(); Display();
}
}
return state; return state;
} }
@ -5709,6 +5714,7 @@ cReplayControl::cReplayControl(bool PauseLive)
displayReplay = NULL; displayReplay = NULL;
marksModified = false; marksModified = false;
visible = modeOnly = shown = displayFrames = false; visible = modeOnly = shown = displayFrames = false;
lastErrors = 0;
lastCurrent = lastTotal = -1; lastCurrent = lastTotal = -1;
lastPlay = lastForward = false; lastPlay = lastForward = false;
lastSpeed = -2; // an invalid value lastSpeed = -2; // an invalid value
@ -5882,6 +5888,7 @@ bool cReplayControl::ShowProgress(bool Initial)
if (!visible) { if (!visible) {
displayReplay = Skins.Current()->DisplayReplay(modeOnly); displayReplay = Skins.Current()->DisplayReplay(modeOnly);
displayReplay->SetMarks(&marks); displayReplay->SetMarks(&marks);
displayReplay->SetErrors(GetErrors());
SetNeedsFastResponse(true); SetNeedsFastResponse(true);
visible = true; visible = true;
} }
@ -5893,7 +5900,9 @@ bool cReplayControl::ShowProgress(bool Initial)
} }
lastCurrent = lastTotal = -1; lastCurrent = lastTotal = -1;
} }
if (Current != lastCurrent || Total != lastTotal) { const cErrors *Errors = GetErrors();
int NumErrors = Errors ? Errors->Size() : 0;
if (Current != lastCurrent || Total != lastTotal || NumErrors != lastErrors) {
if (Setup.ShowRemainingTime || Total != lastTotal) { if (Setup.ShowRemainingTime || Total != lastTotal) {
int Index = Total; int Index = Total;
if (Setup.ShowRemainingTime) if (Setup.ShowRemainingTime)
@ -5902,10 +5911,12 @@ bool cReplayControl::ShowProgress(bool Initial)
} }
displayReplay->SetProgress(Current, Total); displayReplay->SetProgress(Current, Total);
displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond())); displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond()));
displayReplay->SetErrors(Errors);
displayReplay->Flush(); displayReplay->Flush();
lastCurrent = Current; lastCurrent = Current;
}
lastTotal = Total; lastTotal = Total;
lastErrors = NumErrors;
}
ShowMode(); ShowMode();
updateTimer.Set(PROGRESSTIMEOUT); updateTimer.Set(PROGRESSTIMEOUT);
return true; return true;
@ -6091,6 +6102,47 @@ void cReplayControl::MarkMove(int Frames, bool MarkRequired)
} }
} }
void cReplayControl::ErrorJump(bool Forward)
{
const cErrors *Errors = GetErrors();
int NumErrors = Errors ? Errors->Size() : 0;
if (NumErrors > 0) {
int Current, Total;
if (GetIndex(Current, Total)) {
if (Forward) {
int Offset = 0;
for (int i = 0; i < NumErrors; i++) {
int Position = Errors->At(i);
if (Position > Current + Offset) {
int NextIFrame = SkipFrames(Position - Current) + Offset; // this takes us to the I-frame at or right after Position
if (NextIFrame > Position) {
if (SkipFrames(Offset + 1) == NextIFrame) { // means Current is the I-frame right before Position
Offset = NextIFrame - Current;
continue;
}
}
Goto(Position, true); // this takes us to the I-frame at or right before Position
return;
}
}
if (Current < Total)
Goto(Total, true);
}
else {
for (int i = NumErrors - 1; i >= 0; i--) {
if (Errors->At(i) < Current) {
int Position = Errors->At(i);
Goto(Position, true); // this takes us to the I-frame at or right before Position
return;
}
}
if (Current > 0)
Goto(0, true);
}
}
}
}
void cReplayControl::EditCut(void) void cReplayControl::EditCut(void)
{ {
if (*fileName) { if (*fileName) {
@ -6235,6 +6287,10 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
case kMarkSkipBack: MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break; case kMarkSkipBack: MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
case kMarkSkipForward|k_Repeat: case kMarkSkipForward|k_Repeat:
case kMarkSkipForward: MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false); break; case kMarkSkipForward: MarkMove(+adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
case kChanUp|k_Repeat:
case kChanUp: ErrorJump(true); break;
case kChanDn|k_Repeat:
case kChanDn: ErrorJump(false); break;
case kEditCut: EditCut(); break; case kEditCut: EditCut(); break;
case kEditTest: EditTest(); break; case kEditTest: EditTest(); break;
default: { default: {

4
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.h 5.3 2024/08/30 09:55:15 kls Exp $ * $Id: menu.h 5.5 2024/10/11 14:10:50 kls Exp $
*/ */
#ifndef __MENU_H #ifndef __MENU_H
@ -297,6 +297,7 @@ private:
cMarks marks; cMarks marks;
bool marksModified; bool marksModified;
bool visible, modeOnly, shown, displayFrames; bool visible, modeOnly, shown, displayFrames;
int lastErrors;
int lastCurrent, lastTotal; int lastCurrent, lastTotal;
bool lastPlay, lastForward; bool lastPlay, lastForward;
int lastSpeed; int lastSpeed;
@ -315,6 +316,7 @@ private:
void MarkToggle(void); void MarkToggle(void);
void MarkJump(bool Forward); void MarkJump(bool Forward);
void MarkMove(int Frames, bool MarkRequired); void MarkMove(int Frames, bool MarkRequired);
void ErrorJump(bool Forward);
void EditCut(void); void EditCut(void);
void EditTest(void); void EditTest(void);
public: public:

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menuitems.c 5.2 2024/07/13 09:12:18 kls Exp $ * $Id: menuitems.c 5.3 2025/01/29 10:20:17 kls Exp $
*/ */
#include "menuitems.h" #include "menuitems.h"
@ -38,7 +38,6 @@ void cMenuEditItem::SetValue(const char *Value)
{ {
cString buffer = cString::sprintf("%s:\t%s", name, Value); cString buffer = cString::sprintf("%s:\t%s", name, Value);
SetText(buffer); SetText(buffer);
cStatus::MsgOsdCurrentItem(buffer);
} }
void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)

View File

@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: newplugin 5.1 2021/01/02 14:32:20 kls Exp $ # $Id: newplugin 5.2 2025/02/12 22:22:20 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n"; $PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
@ -216,7 +216,6 @@ public:
virtual bool Start(void); virtual bool Start(void);
virtual void Stop(void); virtual void Stop(void);
virtual void Housekeeping(void); virtual void Housekeeping(void);
virtual void MainThreadHook(void);
virtual cString Active(void); virtual cString Active(void);
virtual time_t WakeupTime(void); virtual time_t WakeupTime(void);
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
@ -274,12 +273,6 @@ void cPlugin${PLUGIN_CLASS}::Housekeeping(void)
// Perform any cleanup or other regular tasks. // Perform any cleanup or other regular tasks.
} }
void cPlugin${PLUGIN_CLASS}::MainThreadHook(void)
{
// Perform actions in the context of the main program thread.
// WARNING: Use with great care - see PLUGINS.html!
}
cString cPlugin${PLUGIN_CLASS}::Active(void) cString cPlugin${PLUGIN_CLASS}::Active(void)
{ {
// Return a message string if shutdown should be postponed // Return a message string if shutdown should be postponed

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: osdbase.c 5.1 2024/01/19 12:10:47 kls Exp $ * $Id: osdbase.c 5.9 2025/02/17 10:49:10 kls Exp $
*/ */
#include "osdbase.h" #include "osdbase.h"
@ -78,12 +78,16 @@ void cOsdObject::Show(void)
cSkinDisplayMenu *cOsdMenu::displayMenu = NULL; cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
int cOsdMenu::displayMenuCount = 0; int cOsdMenu::displayMenuCount = 0;
int cOsdMenu::osdState = 0; int cOsdMenu::osdState = 0;
cOsdMenu *cOsdMenu::topMenu = NULL;
cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4) cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
{ {
isMenu = true; isMenu = true;
digit = 0; digit = 0;
hasHotkeys = false; hasHotkeys = false;
if (!topMenu)
topMenu = this;
active = this == topMenu;
displayMenuItems = 0; displayMenuItems = 0;
title = NULL; title = NULL;
menuCategory = mcUnknown; menuCategory = mcUnknown;
@ -93,6 +97,7 @@ cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
SetCols(c0, c1, c2, c3, c4); SetCols(c0, c1, c2, c3, c4);
first = 0; first = 0;
lastOffset = -1; lastOffset = -1;
conveyStatus = true;
current = marked = -1; current = marked = -1;
subMenu = NULL; subMenu = NULL;
helpRed = helpGreen = helpYellow = helpBlue = NULL; helpRed = helpGreen = helpYellow = helpBlue = NULL;
@ -113,6 +118,8 @@ cOsdMenu::~cOsdMenu()
cStatus::MsgOsdClear(); cStatus::MsgOsdClear();
if (!--displayMenuCount) if (!--displayMenuCount)
DELETENULL(displayMenu); DELETENULL(displayMenu);
if (this == topMenu)
topMenu = NULL;
} }
void cOsdMenu::SetMenuCategory(eMenuCategory MenuCategory) void cOsdMenu::SetMenuCategory(eMenuCategory MenuCategory)
@ -125,6 +132,11 @@ void cOsdMenu::SetMenuSortMode(eMenuSortMode MenuSortMode)
menuSortMode = MenuSortMode; menuSortMode = MenuSortMode;
} }
void cOsdMenu::SetActive(bool Active)
{
active = Active;
}
void cOsdMenu::SetDisplayMenu(void) void cOsdMenu::SetDisplayMenu(void)
{ {
if (displayMenu) { if (displayMenu) {
@ -169,6 +181,7 @@ void cOsdMenu::SetStatus(const char *s)
free(status); free(status);
status = s ? strdup(s) : NULL; status = s ? strdup(s) : NULL;
displayMenu->SetMessage(mtStatus, s); displayMenu->SetMessage(mtStatus, s);
cStatus::MsgOsdStatusMessage(mtStatus, s);
} }
void cOsdMenu::SetTitle(const char *Title) void cOsdMenu::SetTitle(const char *Title)
@ -181,6 +194,7 @@ void cOsdMenu::DisplayHelp(bool Force)
{ {
if (!helpDisplayed || Force) { if (!helpDisplayed || Force) {
displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue); displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
if (conveyStatus)
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue); cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
helpDisplayed = true; helpDisplayed = true;
} }
@ -224,16 +238,27 @@ void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
current = Item->Index(); current = Item->Index();
} }
void cOsdMenu::DisplayNoStatus(void)
{
conveyStatus = false;
Display();
conveyStatus = true;
}
void cOsdMenu::Display(void) void cOsdMenu::Display(void)
{ {
if (subMenu) { if (subMenu) {
subMenu->Display(); subMenu->Display();
return; return;
} }
if (!active)
return;
if (cOsdProvider::OsdSizeChanged(osdState)) if (cOsdProvider::OsdSizeChanged(osdState))
SetDisplayMenu(); SetDisplayMenu();
displayMenu->SetMessage(mtStatus, NULL); displayMenu->SetMessage(mtStatus, NULL);
cStatus::MsgOsdStatusMessage(mtStatus, NULL);
displayMenu->Clear(); displayMenu->Clear();
if (conveyStatus)
cStatus::MsgOsdClear(); cStatus::MsgOsdClear();
if (menuCategory != displayMenu->MenuCategory()) if (menuCategory != displayMenu->MenuCategory())
displayMenu->SetMenuCategory(menuCategory); displayMenu->SetMenuCategory(menuCategory);
@ -242,13 +267,15 @@ void cOsdMenu::Display(void)
displayMenuItems = displayMenu->MaxItems(); displayMenuItems = displayMenu->MaxItems();
displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
displayMenu->SetTitle(title); displayMenu->SetTitle(title);
if (conveyStatus)
cStatus::MsgOsdTitle(title); cStatus::MsgOsdTitle(title);
DisplayHelp(true); DisplayHelp(true);
int count = Count(); int count = Count();
if (count > 0) { if (count > 0) {
int ni = 0; int ni = 0;
for (cOsdItem *item = First(); item; item = Next(item)) { for (cOsdItem *item = First(); item; item = Next(item)) {
cStatus::MsgOsdItem(item->Text(), ni++); if (conveyStatus)
cStatus::MsgOsdItem(item->Text(), ni++, item->Selectable());
if (current < 0 && item->Selectable()) if (current < 0 && item->Selectable())
current = item->Index(); current = item->Index();
} }
@ -267,16 +294,18 @@ void cOsdMenu::Display(void)
for (cOsdItem *item = Get(first); item; item = Next(item)) { for (cOsdItem *item = Get(first); item; item = Next(item)) {
bool CurrentSelectable = (i == current) && item->Selectable(); bool CurrentSelectable = (i == current) && item->Selectable();
item->SetMenuItem(displayMenu, i - first, CurrentSelectable, item->Selectable()); item->SetMenuItem(displayMenu, i - first, CurrentSelectable, item->Selectable());
if (CurrentSelectable) if (CurrentSelectable) // not checking conveyStatus here!
cStatus::MsgOsdCurrentItem(item->Text()); cStatus::MsgOsdCurrentItem(item->Text(), i);
if (++n == displayMenuItems) if (++n == displayMenuItems)
break; break;
i++; i++;
} }
} }
displayMenu->SetScrollbar(count, first); displayMenu->SetScrollbar(count, first);
if (!isempty(status)) if (!isempty(status)) {
displayMenu->SetMessage(mtStatus, status); displayMenu->SetMessage(mtStatus, status);
cStatus::MsgOsdStatusMessage(mtStatus, status);
}
} }
void cOsdMenu::SetCurrent(cOsdItem *Item) void cOsdMenu::SetCurrent(cOsdItem *Item)
@ -299,9 +328,13 @@ void cOsdMenu::DisplayCurrent(bool Current)
cOsdItem *item = Get(current); cOsdItem *item = Get(current);
if (item) { if (item) {
item->SetMenuItem(displayMenu, current - first, Current && item->Selectable(), item->Selectable()); item->SetMenuItem(displayMenu, current - first, Current && item->Selectable(), item->Selectable());
if (Current && item->Selectable()) if (Current) {
cStatus::MsgOsdCurrentItem(item->Text()); if (current - first >= displayMenuItems || current < first)
if (!Current) DisplayNoStatus();
else if (item->Selectable())
cStatus::MsgOsdCurrentItem(item->Text(), current);
}
else
item->SetFresh(true); // leaving the current item resets 'fresh' item->SetFresh(true); // leaving the current item resets 'fresh'
if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) { if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
if (!MenuEditItem->DisplayHelp(Current)) if (!MenuEditItem->DisplayHelp(Current))
@ -321,7 +354,7 @@ void cOsdMenu::DisplayItem(cOsdItem *Item)
bool Current = Index == current; bool Current = Index == current;
Item->SetMenuItem(displayMenu, Offset, Current && Item->Selectable(), Item->Selectable()); Item->SetMenuItem(displayMenu, Offset, Current && Item->Selectable(), Item->Selectable());
if (Current && Item->Selectable()) if (Current && Item->Selectable())
cStatus::MsgOsdCurrentItem(Item->Text()); cStatus::MsgOsdCurrentItem(Item->Text(), Index);
} }
} }
} }
@ -355,7 +388,7 @@ void cOsdMenu::CursorUp(void)
if (first > 0) { if (first > 0) {
// make non-selectable items at the beginning visible: // make non-selectable items at the beginning visible:
first = 0; first = 0;
Display(); DisplayNoStatus();
return; return;
} }
if (Setup.MenuScrollWrap) if (Setup.MenuScrollWrap)
@ -371,11 +404,11 @@ void cOsdMenu::CursorUp(void)
current = tmpCurrent; current = tmpCurrent;
if (current < first) { if (current < first) {
first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current; first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
Display(); DisplayNoStatus();
} }
else if (current > lastOnScreen) { else if (current > lastOnScreen) {
first = max(0, current - displayMenuItems + 1); first = max(0, current - displayMenuItems + 1);
Display(); DisplayNoStatus();
} }
else else
DisplayCurrent(true); DisplayCurrent(true);
@ -393,7 +426,7 @@ void cOsdMenu::CursorDown(void)
if (first < last - displayMenuItems) { if (first < last - displayMenuItems) {
// make non-selectable items at the end visible: // make non-selectable items at the end visible:
first = last - displayMenuItems + 1; first = last - displayMenuItems + 1;
Display(); DisplayNoStatus();
return; return;
} }
if (Setup.MenuScrollWrap) if (Setup.MenuScrollWrap)
@ -411,11 +444,11 @@ void cOsdMenu::CursorDown(void)
first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1); first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
if (first + displayMenuItems > last) if (first + displayMenuItems > last)
first = max(0, last - displayMenuItems + 1); first = max(0, last - displayMenuItems + 1);
Display(); DisplayNoStatus();
} }
else if (current < first) { else if (current < first) {
first = current; first = current;
Display(); DisplayNoStatus();
} }
else else
DisplayCurrent(true); DisplayCurrent(true);
@ -448,7 +481,7 @@ void cOsdMenu::PageUp(void)
first = current - displayMenuItems + 1; first = current - displayMenuItems + 1;
} }
if (current != oldCurrent || first != oldFirst) if (current != oldCurrent || first != oldFirst)
Display(); DisplayNoStatus();
else if (Setup.MenuScrollWrap) else if (Setup.MenuScrollWrap)
CursorUp(); CursorUp();
} }
@ -480,7 +513,7 @@ void cOsdMenu::PageDown(void)
first = current - displayMenuItems + 1; first = current - displayMenuItems + 1;
} }
if (current != oldCurrent || first != oldFirst) if (current != oldCurrent || first != oldFirst)
Display(); DisplayNoStatus();
else if (Setup.MenuScrollWrap) else if (Setup.MenuScrollWrap)
CursorDown(); CursorDown();
} }
@ -499,9 +532,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
const char *s = item->Text(); const char *s = item->Text();
if (s && (s = skipspace(s)) != NULL) { if (s && (s = skipspace(s)) != NULL) {
if (*s == Key - k1 + '1') { if (*s == Key - k1 + '1') {
DisplayCurrent(false);
current = item->Index(); current = item->Index();
RefreshCurrent(); RefreshCurrent();
Display(); DisplayCurrent(true);
cRemote::Put(kOk, true); cRemote::Put(kOk, true);
break; break;
} }
@ -512,8 +546,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu) eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
{ {
SetActive(false);
delete subMenu; delete subMenu;
subMenu = SubMenu; subMenu = SubMenu;
subMenu->SetActive(true);
subMenu->Display(); subMenu->Display();
return osContinue; // convenience return value return osContinue; // convenience return value
} }
@ -522,6 +558,7 @@ eOSState cOsdMenu::CloseSubMenu(bool ReDisplay)
{ {
delete subMenu; delete subMenu;
subMenu = NULL; subMenu = NULL;
SetActive(true);
if (ReDisplay) { if (ReDisplay) {
RefreshCurrent(); RefreshCurrent();
Display(); Display();
@ -542,6 +579,7 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
if (marked < 0 && item) { if (marked < 0 && item) {
eOSState state = item->ProcessKey(Key); eOSState state = item->ProcessKey(Key);
if (state != osUnknown) { if (state != osUnknown) {
if (Key != kNone)
DisplayCurrent(true); DisplayCurrent(true);
return state; return state;
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: osdbase.h 4.5 2018/01/25 15:09:23 kls Exp $ * $Id: osdbase.h 5.2 2025/02/17 10:49:10 kls Exp $
*/ */
#ifndef __OSDBASE_H #ifndef __OSDBASE_H
@ -87,11 +87,13 @@ private:
static cSkinDisplayMenu *displayMenu; static cSkinDisplayMenu *displayMenu;
static int displayMenuCount; static int displayMenuCount;
static int osdState; static int osdState;
static cOsdMenu *topMenu;
int displayMenuItems; int displayMenuItems;
char *title; char *title;
int cols[cSkinDisplayMenu::MaxTabs]; int cols[cSkinDisplayMenu::MaxTabs];
int first, current, marked; int first, current, marked;
int lastOffset; int lastOffset;
bool conveyStatus;
eMenuCategory menuCategory; eMenuCategory menuCategory;
eMenuSortMode menuSortMode; eMenuSortMode menuSortMode;
eMenuOrientation menuOrientation; eMenuOrientation menuOrientation;
@ -101,7 +103,10 @@ private:
char *status; char *status;
int digit; int digit;
bool hasHotkeys; bool hasHotkeys;
bool active;
void SetActive(bool Active);
void DisplayHelp(bool Force = false); void DisplayHelp(bool Force = false);
void DisplayNoStatus(void);
protected: protected:
void SetDisplayMenu(void); void SetDisplayMenu(void);
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; } cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: player.c 5.1 2024/07/16 12:33:27 kls Exp $ * $Id: player.c 5.2 2024/10/13 09:47:18 kls Exp $
*/ */
#include "player.h" #include "player.h"
@ -70,14 +70,6 @@ cString cControl::GetHeader(void)
return ""; return "";
} }
#if DEPRECATED_CCONTROL
cControl *cControl::Control(bool Hidden)
{
cMutexLock MutexLock(&mutex);
return (control && (!control->hidden || Hidden)) ? control : NULL;
}
#endif
cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden) cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden)
{ {
MutexLock.Lock(&mutex); MutexLock.Lock(&mutex);
@ -87,9 +79,8 @@ cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden)
void cControl::Launch(cControl *Control) void cControl::Launch(cControl *Control)
{ {
cMutexLock MutexLock(&mutex); cMutexLock MutexLock(&mutex);
cControl *c = control; // keeps control from pointing to uninitialized memory TODO obsolete once DEPRECATED_CCONTROL is gone delete control;
control = Control; control = Control;
delete c;
} }
void cControl::Attach(void) void cControl::Attach(void)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: player.h 5.2 2024/07/15 14:42:22 kls Exp $ * $Id: player.h 5.6 2024/10/13 09:47:18 kls Exp $
*/ */
#ifndef __PLAYER_H #ifndef __PLAYER_H
@ -54,6 +54,8 @@ public:
bool IsAttached(void) { return device != NULL; } bool IsAttached(void) { return device != NULL; }
virtual double FramesPerSecond(void) { return DEFAULTFRAMESPERSECOND; } virtual double FramesPerSecond(void) { return DEFAULTFRAMESPERSECOND; }
// Returns the number of frames per second of the currently played material. // Returns the number of frames per second of the currently played material.
virtual const cErrors *GetErrors(void) { return NULL; }
// Returns the frame indexes of errors in the recording (if any).
virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return false; } virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { return false; }
// Returns the current and total frame index, optionally snapped to the // Returns the current and total frame index, optionally snapped to the
// nearest I-frame. // nearest I-frame.
@ -115,17 +117,6 @@ public:
static void Launch(cControl *Control); static void Launch(cControl *Control);
static void Attach(void); static void Attach(void);
static void Shutdown(void); static void Shutdown(void);
#ifndef DEPRECATED_CCONTROL
#define DEPRECATED_CCONTROL 0
#endif
#if DEPRECATED_CCONTROL
static cControl *Control(bool Hidden = false);
///< Old version of this function, for backwards compatibility with plugins.
///< Plugins should be changed to use the new version below, which does
///< proper locking.
///< Use of this function may result in program crashes in case replay is
///< stopped immediately after starting it.
#endif
static cControl *Control(cMutexLock &MutexLock, bool Hidden = false); static cControl *Control(cMutexLock &MutexLock, bool Hidden = false);
///< Returns the current replay control (if any) in case it is currently ///< Returns the current replay control (if any) in case it is currently
///< visible. If Hidden is true, the control will be returned even if it is ///< visible. If Hidden is true, the control will be returned even if it is

View File

@ -4,9 +4,10 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: plugin.c 4.4 2020/12/16 11:54:06 kls Exp $ * $Id: plugin.c 5.1 2025/02/12 22:22:20 kls Exp $
*/ */
#define MUTE_DEPRECATED_MAINTHREADHOOK
#include "plugin.h" #include "plugin.h"
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: plugin.h 4.1 2020/06/29 09:29:06 kls Exp $ * $Id: plugin.h 5.1 2025/02/12 22:22:20 kls Exp $
*/ */
#ifndef __PLUGIN_H #ifndef __PLUGIN_H
@ -43,6 +43,9 @@ public:
virtual bool Start(void); virtual bool Start(void);
virtual void Stop(void); virtual void Stop(void);
virtual void Housekeeping(void); virtual void Housekeeping(void);
#ifndef MUTE_DEPRECATED_MAINTHREADHOOK
[[deprecated("use proper locking instead")]]
#endif
virtual void MainThreadHook(void); virtual void MainThreadHook(void);
virtual cString Active(void); virtual cString Active(void);
virtual time_t WakeupTime(void); virtual time_t WakeupTime(void);

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2008-10-16 11:16-0400\n" "PO-Revision-Date: 2008-10-16 11:16-0400\n"
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n" "Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
"Language-Team: Arabic <ar@li.org>\n" "Language-Team: Arabic <ar@li.org>\n"
@ -134,8 +134,8 @@ msgstr "News Magazine"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentary" msgstr "Documentary"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussion/Inverview/Debate" msgstr "Discussion/Interview/Debate"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
msgstr "Show/Game Show" msgstr "Show/Game Show"
@ -212,8 +212,8 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Serious/Classical Music" msgstr "Serious/Classical Music"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/Tradional Music" msgstr "Folk/Traditional Music"
msgid "Content$Jazz" msgid "Content$Jazz"
msgstr "Jazz" msgstr "Jazz"
@ -1578,9 +1578,13 @@ msgstr "Recording - restart anyway?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "restart anyway?" msgstr "restart anyway?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "الصوت " msgstr "الصوت "

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2008-03-02 19:02+0100\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n"
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n" "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
"Language-Team: Catalan <vdr@linuxtv.org>\n" "Language-Team: Catalan <vdr@linuxtv.org>\n"
@ -133,7 +133,7 @@ msgstr "Revista de not
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documental" msgstr "Documental"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussió/Entrevista/Debat" msgstr "Discussió/Entrevista/Debat"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -211,7 +211,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Música clàssica/seriosa" msgstr "Música clàssica/seriosa"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Música folklòrica/tradicional" msgstr "Música folklòrica/tradicional"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1577,9 +1577,13 @@ msgstr "Gravant - Reiniciar de totes maneres?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "Reiniciar de totes maneres?" msgstr "Reiniciar de totes maneres?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volum " msgstr "Volum "

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2010-05-06 11:00+0200\n" "PO-Revision-Date: 2010-05-06 11:00+0200\n"
"Last-Translator: Aleš Juřík <ajurik@quick.cz>\n" "Last-Translator: Aleš Juřík <ajurik@quick.cz>\n"
"Language-Team: Czech <vdr@linuxtv.org>\n" "Language-Team: Czech <vdr@linuxtv.org>\n"
@ -133,7 +133,7 @@ msgstr "zprávy"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "dokumentární" msgstr "dokumentární"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "diskuze/rozhovor/debata" msgstr "diskuze/rozhovor/debata"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -211,7 +211,7 @@ msgstr "rock/pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "vážná/klasická hudba" msgstr "vážná/klasická hudba"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "folk/country" msgstr "folk/country"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1577,9 +1577,13 @@ msgstr "Nahrávání - přesto restartovat?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "přesto restartovat?" msgstr "přesto restartovat?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Hlasitost " msgstr "Hlasitost "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n" "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
"Language-Team: Danish <vdr@linuxtv.org>\n" "Language-Team: Danish <vdr@linuxtv.org>\n"
@ -130,7 +130,7 @@ msgstr ""
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "" msgstr ""
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "" msgstr ""
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -208,7 +208,7 @@ msgstr ""
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "" msgstr ""
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "" msgstr ""
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1574,9 +1574,13 @@ msgstr "Optagelse igang - genstart alligevel?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "genstart alligevel?" msgstr "genstart alligevel?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Lydstyrke " msgstr "Lydstyrke "

View File

@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-10 13:45+0100\n" "PO-Revision-Date: 2015-02-10 13:45+0100\n"
"Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n" "Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
"Language-Team: German <vdr@linuxtv.org>\n" "Language-Team: German <vdr@linuxtv.org>\n"
@ -132,7 +132,7 @@ msgstr "Nachrichtenmagazin"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentation" msgstr "Dokumentation"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskussion/Interview/Debatte" msgstr "Diskussion/Interview/Debatte"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -142,7 +142,7 @@ msgid "Content$Game Show/Quiz/Contest"
msgstr "Spielshow/Quiz/Wettbewerb" msgstr "Spielshow/Quiz/Wettbewerb"
msgid "Content$Variety Show" msgid "Content$Variety Show"
msgstr "Variete-Show" msgstr "Varieté-Show"
msgid "Content$Talk Show" msgid "Content$Talk Show"
msgstr "Talkshow" msgstr "Talkshow"
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Ernste/Klassische Musik" msgstr "Ernste/Klassische Musik"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Volks/Traditionelle Musik" msgstr "Volks/Traditionelle Musik"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1576,9 +1576,13 @@ msgstr "Aufnahme l
msgid "restart anyway?" msgid "restart anyway?"
msgstr "trotzdem neu starten?" msgstr "trotzdem neu starten?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "Fehler" msgstr "Fehler"
msgid "error"
msgstr "Fehler"
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Lautstärke " msgstr "Lautstärke "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n" "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
"Language-Team: Greek <vdr@linuxtv.org>\n" "Language-Team: Greek <vdr@linuxtv.org>\n"
@ -130,7 +130,7 @@ msgstr ""
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "" msgstr ""
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "" msgstr ""
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -208,7 +208,7 @@ msgstr ""
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "" msgstr ""
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "" msgstr ""
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1574,9 +1574,13 @@ msgstr "
msgid "restart anyway?" msgid "restart anyway?"
msgstr "ÔåëéêÜ íá ãßíåé åðáíåêêßíçóç?" msgstr "ÔåëéêÜ íá ãßíåé åðáíåêêßíçóç?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "¸íôáóç " msgstr "¸íôáóç "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-19 23:00+0100\n" "PO-Revision-Date: 2015-02-19 23:00+0100\n"
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n" "Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n" "Language-Team: Spanish <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Revista de noticias"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documental" msgstr "Documental"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discusión/Entrevista/Debate" msgstr "Discusión/Entrevista/Debate"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Musica clásica" msgstr "Musica clásica"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Musica Folk/Tradicional" msgstr "Musica Folk/Tradicional"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Grabaci
msgid "restart anyway?" msgid "restart anyway?"
msgstr "¿reiniciar igualmente?" msgstr "¿reiniciar igualmente?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volumen " msgstr "Volumen "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n" "Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
"Language-Team: Estonian <vdr@linuxtv.org>\n" "Language-Team: Estonian <vdr@linuxtv.org>\n"
@ -130,7 +130,7 @@ msgstr "Uudisteajakiri"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentaal" msgstr "Dokumentaal"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskussioon/intervjuu/debatt" msgstr "Diskussioon/intervjuu/debatt"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -208,7 +208,7 @@ msgstr "Rock/pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Klassikaline muusika" msgstr "Klassikaline muusika"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/rahvamuusika" msgstr "Folk/rahvamuusika"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1574,9 +1574,13 @@ msgstr "Salvestamine aktiivne - taaskäivitada ikkagi?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "taaskäivitada ikkagi?" msgstr "taaskäivitada ikkagi?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Helitugevus " msgstr "Helitugevus "

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2007-08-15 15:52+0200\n" "PO-Revision-Date: 2007-08-15 15:52+0200\n"
"Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n" "Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n" "Language-Team: Finnish <vdr@linuxtv.org>\n"
@ -134,7 +134,7 @@ msgstr "Uutismakasiini"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentti" msgstr "Dokumentti"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Keskustelu/haastattelu/väittely" msgstr "Keskustelu/haastattelu/väittely"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -212,7 +212,7 @@ msgstr "Rock/pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Vakava/klassinen musiikki" msgstr "Vakava/klassinen musiikki"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/kansanmusiikki" msgstr "Folk/kansanmusiikki"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -797,7 +797,7 @@ msgid "Folder"
msgstr "Kansio" msgstr "Kansio"
msgid "Size" msgid "Size"
msgstr "" msgstr "Koko"
msgid "This folder is currently in use - no changes are possible!" msgid "This folder is currently in use - no changes are possible!"
msgstr "Kansio on käytössä - muokkaukset eivät mahdollisia!" msgstr "Kansio on käytössä - muokkaukset eivät mahdollisia!"
@ -846,7 +846,7 @@ msgid "Edited version already exists - overwrite?"
msgstr "Muokattava versio on jo olemassa - ylikirjoitetaanko?" msgstr "Muokattava versio on jo olemassa - ylikirjoitetaanko?"
msgid "Not enough free disk space to start editing process!" msgid "Not enough free disk space to start editing process!"
msgstr "" msgstr "Tallennustila ei riitä muokkaamiseen!"
msgid "Error while queueing recording for cutting!" msgid "Error while queueing recording for cutting!"
msgstr "Tallenteen lisääminen leikkausjonoon epäonnistui!" msgstr "Tallenteen lisääminen leikkausjonoon epäonnistui!"
@ -1526,7 +1526,7 @@ msgid "Button$Insert"
msgstr "Lisää" msgstr "Lisää"
msgid "Button$Macro" msgid "Button$Macro"
msgstr "" msgstr "Makro"
msgid "Plugin" msgid "Plugin"
msgstr "Laajennos" msgstr "Laajennos"
@ -1535,7 +1535,7 @@ msgid "Up/Dn for new location - OK to move"
msgstr "'Ylös/Alas' uusi paikka - 'OK' hyväksy" msgstr "'Ylös/Alas' uusi paikka - 'OK' hyväksy"
msgid "Primary device has no MPEG decoder, can't attach player!" msgid "Primary device has no MPEG decoder, can't attach player!"
msgstr "" msgstr "Ensisijaissa laitteessa ei ole MPEG-toistinta!"
msgid "Low disk space!" msgid "Low disk space!"
msgstr "Tallennustila loppumassa!" msgstr "Tallennustila loppumassa!"
@ -1578,8 +1578,12 @@ msgstr "Tallennus kesken - käynnistetäänkö uudelleen?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "käynnistetäänkö uudelleen?" msgstr "käynnistetäänkö uudelleen?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr "virhettä"
msgid "error"
msgstr "virhe"
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "

View File

@ -18,7 +18,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2018-04-14 10:16+0100\n" "PO-Revision-Date: 2018-04-14 10:16+0100\n"
"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n" "Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
"Language-Team: French <vdr@linuxtv.org>\n" "Language-Team: French <vdr@linuxtv.org>\n"
@ -141,7 +141,7 @@ msgstr "Magazine d'information"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentaire" msgstr "Documentaire"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussion/Interview/Débat" msgstr "Discussion/Interview/Débat"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -219,7 +219,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Sérieux/Musique Classique" msgstr "Sérieux/Musique Classique"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/Musique Traditionnelle" msgstr "Folk/Musique Traditionnelle"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1585,9 +1585,13 @@ msgstr "Enregistrement en cours - confirmer redémarrage ?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "confirmer redémarrage ?" msgstr "confirmer redémarrage ?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volume " msgstr "Volume "

View File

@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2008-03-17 19:00+0100\n" "PO-Revision-Date: 2008-03-17 19:00+0100\n"
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n" "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
"Language-Team: Croatian <vdr@linuxtv.org>\n" "Language-Team: Croatian <vdr@linuxtv.org>\n"
@ -132,7 +132,7 @@ msgstr ""
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "" msgstr ""
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "" msgstr ""
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -210,7 +210,7 @@ msgstr ""
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "" msgstr ""
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "" msgstr ""
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1576,9 +1576,13 @@ msgstr "Snimanje je u tijeku - ponovno pokreni unato
msgid "restart anyway?" msgid "restart anyway?"
msgstr "ponovno pokreni unatoè?" msgstr "ponovno pokreni unatoè?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Glasnoæa " msgstr "Glasnoæa "

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.6\n" "Project-Id-Version: VDR 2.6.6\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2024-02-09 06:29+0000\n" "PO-Revision-Date: 2024-02-09 06:29+0000\n"
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n" "Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
"Language-Team: Hungarian\n" "Language-Team: Hungarian\n"
@ -138,7 +138,7 @@ msgstr "Hírmagazin"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentumfilm" msgstr "Dokumentumfilm"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Beszélgetés/Interjú/Vita" msgstr "Beszélgetés/Interjú/Vita"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -216,7 +216,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Komolyzene/Klasszikus zene" msgstr "Komolyzene/Klasszikus zene"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Népzene/Hagyományos zene" msgstr "Népzene/Hagyományos zene"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1582,9 +1582,13 @@ msgstr "Felvétel folyamatban - mégis újraindítani?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "mégis újraindítani?" msgstr "mégis újraindítani?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "hiba" msgstr "hiba"
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Hangerő " msgstr "Hangerő "

View File

@ -11,9 +11,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2024-04-02 23:20+0200\n" "PO-Revision-Date: 2024-10-15 00:35+0200\n"
"Last-Translator: Gringo <vdr-italian@tiscali.it>\n" "Last-Translator: Gringo <openpli@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n" "Language-Team: Italian <vdr@linuxtv.org>\n"
"Language: it\n" "Language: it\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -136,7 +136,7 @@ msgstr "Settimanale di attualità"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentario" msgstr "Documentario"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussione/Intervista/Dibattito" msgstr "Discussione/Intervista/Dibattito"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Musica Classica/Seria" msgstr "Musica Classica/Seria"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Musica Tradizionale/Folclore" msgstr "Musica Tradizionale/Folclore"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1537,7 +1537,7 @@ msgid "Up/Dn for new location - OK to move"
msgstr "Su/Giù per nuova posizione - OK per spostare" msgstr "Su/Giù per nuova posizione - OK per spostare"
msgid "Primary device has no MPEG decoder, can't attach player!" msgid "Primary device has no MPEG decoder, can't attach player!"
msgstr "" msgstr "Il dispositivo primario non ha un decoder MPEG, impossibile collegare il lettore!"
msgid "Low disk space!" msgid "Low disk space!"
msgstr "Poco spazio su disco!" msgstr "Poco spazio su disco!"
@ -1580,9 +1580,13 @@ msgstr "Registrazione in corso - riavviare comunque?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "riavviare comunque?" msgstr "riavviare comunque?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "errori" msgstr "errori"
msgid "error"
msgstr "errore"
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volume " msgstr "Volume "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-11 14:02+0200\n" "PO-Revision-Date: 2015-02-11 14:02+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n" "Language-Team: Lithuanian <vdr@linuxtv.org>\n"
@ -130,7 +130,7 @@ msgstr "Naujienų žurnalas"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentika" msgstr "Dokumentika"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskusijos/Interviu" msgstr "Diskusijos/Interviu"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -208,7 +208,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Klasikinė muzika" msgstr "Klasikinė muzika"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/Tradizinė muzika" msgstr "Folk/Tradizinė muzika"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1574,9 +1574,13 @@ msgstr "Įrašinėjama - perkrauti vistiek?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "perkrati bet kokiu atveju?" msgstr "perkrati bet kokiu atveju?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Garsas " msgstr "Garsas "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2018-03-31 21:47+0100\n" "PO-Revision-Date: 2018-03-31 21:47+0100\n"
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n" "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
"Language-Team: Macedonian <kde-i18n-doc@kde.org>\n" "Language-Team: Macedonian <kde-i18n-doc@kde.org>\n"
@ -132,7 +132,7 @@ msgstr "Магазин"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Документарен" msgstr "Документарен"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Дискусија/Интервју/Дебата" msgstr "Дискусија/Интервју/Дебата"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -210,7 +210,7 @@ msgstr "Рок/Поп"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Сериозна/Класична музика" msgstr "Сериозна/Класична музика"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Фолк/Народна музика" msgstr "Фолк/Народна музика"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1576,9 +1576,13 @@ msgstr "Снимање во тек - рестартирај?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "сеедно рестартирај?" msgstr "сеедно рестартирај?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Глас " msgstr "Глас "

View File

@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-10 19:43+0100\n" "PO-Revision-Date: 2015-02-10 19:43+0100\n"
"Last-Translator: Erik Oomen <oomen.e@gmail.com>\n" "Last-Translator: Erik Oomen <oomen.e@gmail.com>\n"
"Language-Team: Dutch <vdr@linuxtv.org>\n" "Language-Team: Dutch <vdr@linuxtv.org>\n"
@ -136,7 +136,7 @@ msgstr "Nieuwsbulletin"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentaire" msgstr "Documentaire"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussie/Interview/Debat" msgstr "Discussie/Interview/Debat"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Serieuze muziek/Klassieke muziek" msgstr "Serieuze muziek/Klassieke muziek"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/Traditionele muziek" msgstr "Folk/Traditionele muziek"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1580,9 +1580,13 @@ msgstr "Opname loopt - toch opnieuw starten?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "toch opnieuw starten?" msgstr "toch opnieuw starten?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volume " msgstr "Volume "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2007-08-12 14:17+0200\n" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n" "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n" "Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr ""
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "" msgstr ""
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "" msgstr ""
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr ""
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "" msgstr ""
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "" msgstr ""
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Gj
msgid "restart anyway?" msgid "restart anyway?"
msgstr "starte på nytt likevel?" msgstr "starte på nytt likevel?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volum " msgstr "Volum "

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2018-02-19 00:42+0100\n" "PO-Revision-Date: 2018-02-19 00:42+0100\n"
"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n" "Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n" "Language-Team: Polish <vdr@linuxtv.org>\n"
@ -135,7 +135,7 @@ msgstr "Magazyny informacyjne"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentalne" msgstr "Dokumentalne"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Dyskusje/Wywiady/Debaty" msgstr "Dyskusje/Wywiady/Debaty"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Muzyka klasyczna" msgstr "Muzyka klasyczna"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folk/Muzyka tradycyjna" msgstr "Folk/Muzyka tradycyjna"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1579,9 +1579,13 @@ msgstr "Trwa nagrywanie - zrestartować mimo to?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "zrestartować mimo to?" msgstr "zrestartować mimo to?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Głośność " msgstr "Głośność "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2010-03-28 22:49+0100\n" "PO-Revision-Date: 2010-03-28 22:49+0100\n"
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n" "Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
"Language-Team: Portuguese <vdr@linuxtv.org>\n" "Language-Team: Portuguese <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Notici
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentário" msgstr "Documentário"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discussão/Entrevista/Debate" msgstr "Discussão/Entrevista/Debate"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Música Clássica/Séria" msgstr "Música Clássica/Séria"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Música Tradicional/Folclore" msgstr "Música Tradicional/Folclore"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Grava
msgid "restart anyway?" msgid "restart anyway?"
msgstr "reiniciar mesmo assim?" msgstr "reiniciar mesmo assim?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volume " msgstr "Volume "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-11 22:26+0100\n" "PO-Revision-Date: 2015-02-11 22:26+0100\n"
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n" "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
"Language-Team: Romanian <vdr@linuxtv.org>\n" "Language-Team: Romanian <vdr@linuxtv.org>\n"
@ -132,7 +132,7 @@ msgstr "Magazin de ştiri"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Documentar" msgstr "Documentar"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Discuţie/Interviu/Dezbatere" msgstr "Discuţie/Interviu/Dezbatere"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Muzică clasică/serioasă" msgstr "Muzică clasică/serioasă"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Muzică folk/tradiţională" msgstr "Muzică folk/tradiţională"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1576,9 +1576,13 @@ msgstr "Tocmai se înregistrează - repornesc, totuşi?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "repornesc, totuşi?" msgstr "repornesc, totuşi?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volum " msgstr "Volum "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2016-12-27 17:13+0100\n" "PO-Revision-Date: 2016-12-27 17:13+0100\n"
"Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n" "Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n" "Language-Team: Russian <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Новостной журнал"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Документальные" msgstr "Документальные"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Дискуссия/Интервью/Дебаты" msgstr "Дискуссия/Интервью/Дебаты"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Классическая музыка" msgstr "Классическая музыка"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Фольклор/Традиционная музыка" msgstr "Фольклор/Традиционная музыка"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Идёт запись - действительно перезапуст
msgid "restart anyway?" msgid "restart anyway?"
msgstr "действительно перезапустить?" msgstr "действительно перезапустить?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Громкость " msgstr "Громкость "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-17 18:59+0100\n" "PO-Revision-Date: 2015-02-17 18:59+0100\n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <vdr@linuxtv.org>\n" "Language-Team: Slovak <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Spravodajsk
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentárny" msgstr "Dokumentárny"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskusia/Rozhovor/debata" msgstr "Diskusia/Rozhovor/debata"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Vá¾na/Klasická hudba" msgstr "Vá¾na/Klasická hudba"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "¥udová/Tradièná Hudba" msgstr "¥udová/Tradièná Hudba"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Nahr
msgid "restart anyway?" msgid "restart anyway?"
msgstr "Urèite re¹tartova»?" msgstr "Urèite re¹tartova»?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Hlasitos» " msgstr "Hlasitos» "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2013-03-04 12:46+0100\n" "PO-Revision-Date: 2013-03-04 12:46+0100\n"
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n" "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
"Language-Team: Slovenian <vdr@linuxtv.org>\n" "Language-Team: Slovenian <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Poro
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentarec" msgstr "Dokumentarec"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskudija/Interviju/Debata" msgstr "Diskudija/Interviju/Debata"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Resna/Klasièna Glasba" msgstr "Resna/Klasièna Glasba"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Narodna/Tradicionalna glasba" msgstr "Narodna/Tradicionalna glasba"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Snemanje - zares ponoven zagon?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "zares ponoven zagon?" msgstr "zares ponoven zagon?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Glasnost " msgstr "Glasnost "

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2013-03-16 15:05+0100\n" "PO-Revision-Date: 2013-03-16 15:05+0100\n"
"Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n" "Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n"
"Language-Team: Serbian <vdr@linuxtv.org>\n" "Language-Team: Serbian <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Novosti"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentarni" msgstr "Dokumentarni"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskusija/Razgovor/Debata" msgstr "Diskusija/Razgovor/Debata"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Ozbiljna/Klasièna muzika" msgstr "Ozbiljna/Klasièna muzika"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Narodna/Izvorna muzika" msgstr "Narodna/Izvorna muzika"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Snimanje u toku - svejedno ponovno pokreni!!!?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "svejedno ponovo pokreni?" msgstr "svejedno ponovo pokreni?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Jaèina " msgstr "Jaèina "

View File

@ -12,7 +12,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2015-02-12 21:58+0100\n" "PO-Revision-Date: 2015-02-12 21:58+0100\n"
"Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n" "Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n"
"Language-Team: Swedish <vdr@linuxtv.org>\n" "Language-Team: Swedish <vdr@linuxtv.org>\n"
@ -135,7 +135,7 @@ msgstr "Nyhetsmagasin"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Dokumentär" msgstr "Dokumentär"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Diskussion/Intervju/Debatt" msgstr "Diskussion/Intervju/Debatt"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Klassisk musik" msgstr "Klassisk musik"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Folkmusik" msgstr "Folkmusik"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1579,9 +1579,13 @@ msgstr "Inspelning p
msgid "restart anyway?" msgid "restart anyway?"
msgstr "vill du starta om ändå?" msgstr "vill du starta om ändå?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volym " msgstr "Volym "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2008-02-28 00:33+0100\n" "PO-Revision-Date: 2008-02-28 00:33+0100\n"
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n" "Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
"Language-Team: Turkish <vdr@linuxtv.org>\n" "Language-Team: Turkish <vdr@linuxtv.org>\n"
@ -130,7 +130,7 @@ msgstr ""
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "" msgstr ""
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "" msgstr ""
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -208,7 +208,7 @@ msgstr ""
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "" msgstr ""
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "" msgstr ""
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1574,9 +1574,13 @@ msgstr "Kay
msgid "restart anyway?" msgid "restart anyway?"
msgstr "buna raðmen yeniden baþlat?" msgstr "buna raðmen yeniden baþlat?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Volüm " msgstr "Volüm "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2018-03-18 20:00+0100\n" "PO-Revision-Date: 2018-03-18 20:00+0100\n"
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n" "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
"Language-Team: Ukrainian <vdr@linuxtv.org>\n" "Language-Team: Ukrainian <vdr@linuxtv.org>\n"
@ -131,7 +131,7 @@ msgstr "Журнал новин"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "Документальне" msgstr "Документальне"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "Дискусія/Інтерв’ю/Дебати" msgstr "Дискусія/Інтерв’ю/Дебати"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "Серйозна/Класична музика" msgstr "Серйозна/Класична музика"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "Фльк/Традиційна музика" msgstr "Фльк/Традиційна музика"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1575,9 +1575,13 @@ msgstr "Йде запис - дійсно перезапустити?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "дійсно перезапустити?" msgstr "дійсно перезапустити?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "Гучність " msgstr "Гучність "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 2.6.0\n" "Project-Id-Version: VDR 2.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
"POT-Creation-Date: 2024-07-16 14:13+0200\n" "POT-Creation-Date: 2024-09-21 12:45+0200\n"
"PO-Revision-Date: 2013-03-04 14:52+0800\n" "PO-Revision-Date: 2013-03-04 14:52+0800\n"
"Last-Translator: NFVDR <nfvdr@live.com>\n" "Last-Translator: NFVDR <nfvdr@live.com>\n"
"Language-Team: Chinese (simplified) <nfvdr@live.com>\n" "Language-Team: Chinese (simplified) <nfvdr@live.com>\n"
@ -132,8 +132,8 @@ msgstr "新闻杂志"
msgid "Content$Documentary" msgid "Content$Documentary"
msgstr "记录片" msgstr "记录片"
msgid "Content$Discussion/Inverview/Debate" msgid "Content$Discussion/Interview/Debate"
msgstr "讨论/ Inverview/辩论" msgstr "讨论/ Interview/辩论"
msgid "Content$Show/Game Show" msgid "Content$Show/Game Show"
msgstr "显示/游戏展" msgstr "显示/游戏展"
@ -210,7 +210,7 @@ msgstr "摇滚/流行"
msgid "Content$Serious/Classical Music" msgid "Content$Serious/Classical Music"
msgstr "严重/古典音乐" msgstr "严重/古典音乐"
msgid "Content$Folk/Tradional Music" msgid "Content$Folk/Traditional Music"
msgstr "民谣/传统体育专业课程设置的音乐" msgstr "民谣/传统体育专业课程设置的音乐"
msgid "Content$Jazz" msgid "Content$Jazz"
@ -1576,9 +1576,13 @@ msgstr "录像中,是否现在重启?"
msgid "restart anyway?" msgid "restart anyway?"
msgstr "现在重启?" msgstr "现在重启?"
#. TRANSLATORS: note the plural/singular!
msgid "errors" msgid "errors"
msgstr "" msgstr ""
msgid "error"
msgstr ""
#. TRANSLATORS: note the trailing blank! #. TRANSLATORS: note the trailing blank!
msgid "Volume " msgid "Volume "
msgstr "音量调整 " msgstr "音量调整 "

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recorder.c 5.6 2024/01/20 20:04:03 kls Exp $ * $Id: recorder.c 5.10 2024/09/19 09:49:02 kls Exp $
*/ */
#include "recorder.h" #include "recorder.h"
@ -19,160 +19,18 @@
#define MINFREEDISKSPACE (512) // MB #define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds #define DISKCHECKINTERVAL 100 // seconds
static bool DebugChecks = false;
// cTsChecker and cFrameChecker are used to detect errors in the recorded data stream.
// While cTsChecker checks the continuity counter of the incoming TS packets, cFrameChecker
// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
// all expected frames arrive. The resulting number of errors is not a precise value.
// If it is zero, the recording can be safely considered error free. The higher the value,
// the more damaged the recording is.
// --- cTsChecker ------------------------------------------------------------
#define TS_CC_UNKNOWN 0xFF
class cTsChecker {
private:
uchar counter[MAXPID];
int errors;
void Report(int Pid, const char *Message);
public:
cTsChecker(void);
void CheckTs(const uchar *Data, int Length);
int Errors(void) { return errors; }
};
cTsChecker::cTsChecker(void)
{
memset(counter, TS_CC_UNKNOWN, sizeof(counter));
errors = 0;
}
void cTsChecker::Report(int Pid, const char *Message)
{
errors++;
if (DebugChecks)
fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
}
void cTsChecker::CheckTs(const uchar *Data, int Length)
{
int Pid = TsPid(Data);
uchar Cc = TsContinuityCounter(Data);
if (TsHasPayload(Data)) {
if (TsError(Data))
Report(Pid, "tei");
else if (TsIsScrambled(Data))
Report(Pid, "scrambled");
else {
uchar OldCc = counter[Pid];
if (OldCc != TS_CC_UNKNOWN) {
uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
if (Cc != NewCc)
Report(Pid, "continuity");
}
}
}
counter[Pid] = Cc;
}
// --- cFrameChecker ---------------------------------------------------------
#define MAX_BACK_REFS 32
class cFrameChecker {
private:
int frameDelta;
int64_t lastPts;
uint32_t backRefs;
int lastFwdRef;
int errors;
void Report(const char *Message, int NumErrors = 1);
public:
cFrameChecker(void);
void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
void CheckFrame(const uchar *Data, int Length);
void ReportBroken(void);
int Errors(void) { return errors; }
};
cFrameChecker::cFrameChecker(void)
{
frameDelta = PTSTICKS / DEFAULTFRAMESPERSECOND;
lastPts = -1;
backRefs = 0;
lastFwdRef = 0;
errors = 0;
}
void cFrameChecker::Report(const char *Message, int NumErrors)
{
errors += NumErrors;
if (DebugChecks)
fprintf(stderr, "%s: frame error #%d (%s)\n", *TimeToString(time(NULL)), errors, Message);
}
void cFrameChecker::CheckFrame(const uchar *Data, int Length)
{
int64_t Pts = TsGetPts(Data, Length);
if (Pts >= 0) {
if (lastPts >= 0) {
int Diff = int(round((PtsDiff(lastPts, Pts) / double(frameDelta))));
if (Diff > 0) {
if (Diff <= MAX_BACK_REFS) {
if (lastFwdRef > 1) {
if (backRefs != uint32_t((1 << (lastFwdRef - 1)) - 1))
Report("missing backref");
}
}
else
Report("missed", Diff);
backRefs = 0;
lastFwdRef = Diff;
lastPts = Pts;
}
else if (Diff < 0) {
Diff = -Diff;
if (Diff <= MAX_BACK_REFS) {
int b = 1 << (Diff - 1);
if ((backRefs & b) != 0)
Report("duplicate backref");
backRefs |= b;
}
else
Report("rev diff too big");
}
else
Report("zero diff");
}
else
lastPts = Pts;
}
else
Report("no PTS");
}
void cFrameChecker::ReportBroken(void)
{
int MissedFrames = MAXBROKENTIMEOUT / 1000 * PTSTICKS / frameDelta;
Report("missed", MissedFrames);
}
// --- cRecorder ------------------------------------------------------------- // --- cRecorder -------------------------------------------------------------
cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority) cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
:cReceiver(Channel, Priority) :cReceiver(Channel, Priority)
,cThread("recording") ,cThread("recording")
{ {
tsChecker = new cTsChecker;
frameChecker = new cFrameChecker;
recordingName = strdup(FileName); recordingName = strdup(FileName);
recordingInfo = new cRecordingInfo(recordingName); recordingInfo = new cRecordingInfo(recordingName);
recordingInfo->Read(); recordingInfo->Read();
oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording
errors = oldErrors; errors = 0;
lastErrors = errors; lastErrors = 0;
firstIframeSeen = false; firstIframeSeen = false;
// Make sure the disk is up and running: // Make sure the disk is up and running:
@ -220,8 +78,6 @@ cRecorder::~cRecorder()
delete fileName; delete fileName;
delete frameDetector; delete frameDetector;
delete ringBuffer; delete ringBuffer;
delete frameChecker;
delete tsChecker;
free(recordingName); free(recordingName);
} }
@ -231,18 +87,15 @@ void cRecorder::HandleErrors(bool Force)
{ {
// We don't log every single error separately, to avoid spamming the log file: // We don't log every single error separately, to avoid spamming the log file:
if (Force || time(NULL) - lastErrorLog >= ERROR_LOG_DELTA) { if (Force || time(NULL) - lastErrorLog >= ERROR_LOG_DELTA) {
errors = tsChecker->Errors() + frameChecker->Errors();
if (errors > lastErrors) { if (errors > lastErrors) {
int d = errors - lastErrors; int d = errors - lastErrors;
if (DebugChecks) esyslog("%s: %d new error%s (total %d)", recordingName, d, d > 1 ? "s" : "", oldErrors + errors);
fprintf(stderr, "%s: %s: %d new error%s (total %d)\n", *TimeToString(time(NULL)), recordingName, d, d > 1 ? "s" : "", errors);
esyslog("%s: %d new error%s (total %d)", recordingName, d, d > 1 ? "s" : "", errors);
recordingInfo->SetErrors(oldErrors + errors); recordingInfo->SetErrors(oldErrors + errors);
recordingInfo->Write(); recordingInfo->Write();
LOCK_RECORDINGS_WRITE; LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(recordingName); Recordings->UpdateByName(recordingName);
}
lastErrors = errors; lastErrors = errors;
}
lastErrorLog = time(NULL); lastErrorLog = time(NULL);
} }
} }
@ -307,8 +160,6 @@ void cRecorder::Receive(const uchar *Data, int Length)
int p = ringBuffer->Put(Data, Length); int p = ringBuffer->Put(Data, Length);
if (p != Length && Running()) if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p); ringBuffer->ReportOverflow(Length - p);
else if (firstIframeSeen) // we ignore any garbage before the first I-frame
tsChecker->CheckTs(Data, Length);
} }
} }
@ -316,6 +167,15 @@ void cRecorder::Action(void)
{ {
cTimeMs t(MAXBROKENTIMEOUT); cTimeMs t(MAXBROKENTIMEOUT);
bool InfoWritten = false; bool InfoWritten = false;
bool pendIndependentFrame = false;
uint16_t pendNumber = 0;
off_t pendFileSize = 0;
bool pendErrors = false;
bool pendMissing = false;
// Check if this is a resumed recording, in which case we definitely missed frames:
NextFile();
if (fileName->Number() > 1 || oldErrors)
frameDetector->SetMissing();
while (Running()) { while (Running()) {
int r; int r;
uchar *b = ringBuffer->Get(r); uchar *b = ringBuffer->Get(r);
@ -338,17 +198,27 @@ void cRecorder::Action(void)
} }
InfoWritten = true; InfoWritten = true;
cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName); cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName);
frameChecker->SetFrameDelta(PTSTICKS / frameDetector->FramesPerSecond());
} }
if (firstIframeSeen || frameDetector->IndependentFrame()) { if (firstIframeSeen || frameDetector->IndependentFrame()) {
firstIframeSeen = true; // start recording with the first I-frame firstIframeSeen = true; // start recording with the first I-frame
if (!NextFile()) if (!NextFile())
break; break;
if (frameDetector->NewFrame()) { int PreviousErrors = 0;
if (index) int MissingFrames = 0;
index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize); if (frameDetector->NewFrame(&PreviousErrors, &MissingFrames)) {
if (frameChecker) if (index) {
frameChecker->CheckFrame(b, Count); if (pendNumber > 0)
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
pendIndependentFrame = frameDetector->IndependentFrame();
pendNumber = fileName->Number();
pendFileSize = fileSize;
pendErrors = PreviousErrors;
pendMissing = MissingFrames;
}
if (PreviousErrors)
errors++;
if (MissingFrames)
errors++;
} }
if (frameDetector->IndependentFrame()) { if (frameDetector->IndependentFrame()) {
recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE); recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE);
@ -372,12 +242,17 @@ void cRecorder::Action(void)
} }
} }
if (t.TimedOut()) { if (t.TimedOut()) {
frameChecker->ReportBroken(); if (pendNumber > 0)
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
frameDetector->SetMissing();
errors += MAXBROKENTIMEOUT / 1000 * frameDetector->FramesPerSecond();
HandleErrors(true); HandleErrors(true);
esyslog("ERROR: video data stream broken"); esyslog("ERROR: video data stream broken");
ShutdownHandler.RequestEmergencyExit(); ShutdownHandler.RequestEmergencyExit();
t.Set(MAXBROKENTIMEOUT); t.Set(MAXBROKENTIMEOUT);
} }
} }
if (pendNumber > 0)
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
HandleErrors(true); HandleErrors(true);
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recorder.h 5.1 2021/05/19 11:22:20 kls Exp $ * $Id: recorder.h 5.3 2024/09/17 09:39:50 kls Exp $
*/ */
#ifndef __RECORDER_H #ifndef __RECORDER_H
@ -16,13 +16,8 @@
#include "ringbuffer.h" #include "ringbuffer.h"
#include "thread.h" #include "thread.h"
class cTsChecker;
class cFrameChecker;
class cRecorder : public cReceiver, cThread { class cRecorder : public cReceiver, cThread {
private: private:
cTsChecker *tsChecker;
cFrameChecker *frameChecker;
cRingBufferLinear *ringBuffer; cRingBufferLinear *ringBuffer;
cFrameDetector *frameDetector; cFrameDetector *frameDetector;
cPatPmtGenerator patPmtGenerator; cPatPmtGenerator patPmtGenerator;
@ -56,6 +51,9 @@ public:
virtual ~cRecorder(); virtual ~cRecorder();
int Errors(void) { return oldErrors + errors; }; int Errors(void) { return oldErrors + errors; };
///< Returns the number of errors that were detected during recording. ///< Returns the number of errors that were detected during recording.
///< Each frame that is missing or contains (any number of) errors counts as one error.
///< If this is a resumed recording, this includes errors that occurred
///< in the previous parts.
}; };
#endif //__RECORDER_H #endif //__RECORDER_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.c 5.30 2024/09/01 20:43:40 kls Exp $ * $Id: recording.c 5.37 2025/01/18 20:57:06 kls Exp $
*/ */
#include "recording.h" #include "recording.h"
@ -356,6 +356,7 @@ void cResumeFile::Delete(void)
cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event) cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
{ {
modified = 0;
channelID = Channel ? Channel->GetChannelID() : tChannelID::InvalidID; channelID = Channel ? Channel->GetChannelID() : tChannelID::InvalidID;
channelName = Channel ? strdup(Channel->Name()) : NULL; channelName = Channel ? strdup(Channel->Name()) : NULL;
ownEvent = Event ? NULL : new cEvent(0); ownEvent = Event ? NULL : new cEvent(0);
@ -420,6 +421,7 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
cRecordingInfo::cRecordingInfo(const char *FileName) cRecordingInfo::cRecordingInfo(const char *FileName)
{ {
modified = 0;
channelID = tChannelID::InvalidID; channelID = tChannelID::InvalidID;
channelName = NULL; channelName = NULL;
ownEvent = new cEvent(0); ownEvent = new cEvent(0);
@ -488,6 +490,12 @@ void cRecordingInfo::SetErrors(int Errors)
bool cRecordingInfo::Read(FILE *f) bool cRecordingInfo::Read(FILE *f)
{ {
if (ownEvent) { if (ownEvent) {
struct stat st;
if (fstat(fileno(f), &st))
return false;
if (modified == st.st_mtime)
return true;
modified = st.st_mtime;
cReadLine ReadLine; cReadLine ReadLine;
char *s; char *s;
int line = 0; int line = 0;
@ -1346,6 +1354,7 @@ bool cRecording::ChangeName(const char *NewName)
fileName = strdup(OldFileName); fileName = strdup(OldFileName);
return false; return false;
} }
info->SetFileName(NewFileName);
isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
ClearSortName(); ClearSortName();
} }
@ -2550,6 +2559,12 @@ void cIndexFileGenerator::Action(void)
uint16_t FileNumber = 1; uint16_t FileNumber = 1;
off_t FileOffset = 0; off_t FileOffset = 0;
int Last = -1; int Last = -1;
bool pendIndependentFrame = false;
uint16_t pendNumber = 0;
off_t pendFileSize = 0;
bool pendErrors = false;
bool pendMissing = false;
int Errors = 0;
if (update) { if (update) {
// Look for current index and position to end of it if present: // Look for current index and position to end of it if present:
bool Independent; bool Independent;
@ -2585,11 +2600,24 @@ void cIndexFileGenerator::Action(void)
FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame
int Processed = FrameDetector.Analyze(Data, Length); int Processed = FrameDetector.Analyze(Data, Length);
if (Processed > 0) { if (Processed > 0) {
if (FrameDetector.NewFrame()) { int PreviousErrors = 0;
if (IndexFileWritten || Last < 0) // check for first frame and do not write if in update mode int MissingFrames = 0;
IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize); if (FrameDetector.NewFrame(&PreviousErrors, &MissingFrames)) {
if (IndexFileWritten || Last < 0) { // check for first frame and do not write if in update mode
if (pendNumber > 0)
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
pendIndependentFrame = FrameDetector.IndependentFrame();
pendNumber = FileName.Number();
pendFileSize = FrameOffset >= 0 ? FrameOffset : FileSize;
pendErrors = PreviousErrors;
pendMissing = MissingFrames;
}
FrameOffset = -1; FrameOffset = -1;
IndexFileWritten = true; IndexFileWritten = true;
if (PreviousErrors)
Errors++;
if (MissingFrames)
Errors++;
} }
FileSize += Processed; FileSize += Processed;
Buffer.Del(Processed); Buffer.Del(Processed);
@ -2597,7 +2625,7 @@ void cIndexFileGenerator::Action(void)
} }
else if (PatPmtParser.Completed()) { else if (PatPmtParser.Completed()) {
// Step 2 - sync FrameDetector: // Step 2 - sync FrameDetector:
int Processed = FrameDetector.Analyze(Data, Length); int Processed = FrameDetector.Analyze(Data, Length, false);
if (Processed > 0) { if (Processed > 0) {
if (FrameDetector.Synced()) { if (FrameDetector.Synced()) {
// Synced FrameDetector, so rewind for actual processing: // Synced FrameDetector, so rewind for actual processing:
@ -2655,6 +2683,8 @@ void cIndexFileGenerator::Action(void)
} }
// Recording has been processed: // Recording has been processed:
else { else {
if (pendNumber > 0)
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
IndexFileComplete = true; IndexFileComplete = true;
break; break;
} }
@ -2667,9 +2697,11 @@ void cIndexFileGenerator::Action(void)
if ((FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) || if ((FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) ||
FrameDetector.FrameWidth() != RecordingInfo.FrameWidth() || FrameDetector.FrameWidth() != RecordingInfo.FrameWidth() ||
FrameDetector.FrameHeight() != RecordingInfo.FrameHeight() || FrameDetector.FrameHeight() != RecordingInfo.FrameHeight() ||
FrameDetector.AspectRatio() != RecordingInfo.AspectRatio()) { FrameDetector.AspectRatio() != RecordingInfo.AspectRatio() ||
Errors != RecordingInfo.Errors()) {
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond()); RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
RecordingInfo.SetFrameParams(FrameDetector.FrameWidth(), FrameDetector.FrameHeight(), FrameDetector.ScanType(), FrameDetector.AspectRatio()); RecordingInfo.SetFrameParams(FrameDetector.FrameWidth(), FrameDetector.FrameHeight(), FrameDetector.ScanType(), FrameDetector.AspectRatio());
RecordingInfo.SetErrors(Errors);
RecordingInfo.Write(); RecordingInfo.Write();
LOCK_RECORDINGS_WRITE; LOCK_RECORDINGS_WRITE;
Recordings->UpdateByName(recordingName); Recordings->UpdateByName(recordingName);
@ -2702,13 +2734,17 @@ struct __attribute__((packed)) tIndexPes {
struct __attribute__((packed)) tIndexTs { struct __attribute__((packed)) tIndexTs {
uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!) uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!)
int reserved:7; // reserved for future use int reserved:5; // reserved for future use
int errors:1; // 1=this frame contains errors
int missing:1; // 1=there are frames missing after this one
int independent:1; // marks frames that can be displayed by themselves (for trick modes) int independent:1; // marks frames that can be displayed by themselves (for trick modes)
uint16_t number:16; // up to 64K files per recording uint16_t number:16; // up to 64K files per recording
tIndexTs(off_t Offset, bool Independent, uint16_t Number) tIndexTs(off_t Offset, bool Independent, uint16_t Number, bool Errors, bool Missing)
{ {
offset = Offset; offset = Offset;
reserved = 0; reserved = 0;
errors = Errors;
missing = Missing;
independent = Independent; independent = Independent;
number = Number; number = Number;
} }
@ -2724,6 +2760,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
f = -1; f = -1;
size = 0; size = 0;
last = -1; last = -1;
lastErrorIndex = last;
index = NULL; index = NULL;
isPesRecording = IsPesRecording; isPesRecording = IsPesRecording;
indexFileGenerator = NULL; indexFileGenerator = NULL;
@ -2901,10 +2938,10 @@ bool cIndexFile::CatchUp(int Index)
return index != NULL; return index != NULL;
} }
bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset) bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors, bool Missing)
{ {
if (f >= 0) { if (f >= 0) {
tIndexTs i(FileOffset, Independent, FileNumber); tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
if (isPesRecording) if (isPesRecording)
ConvertToPes(&i, 1); ConvertToPes(&i, 1);
if (safe_write(f, &i, sizeof(i)) < 0) { if (safe_write(f, &i, sizeof(i)) < 0) {
@ -2918,7 +2955,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
return f >= 0; return f >= 0;
} }
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length) bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length, bool *Errors, bool *Missing)
{ {
if (CatchUp(Index)) { if (CatchUp(Index)) {
if (Index >= 0 && Index <= last) { if (Index >= 0 && Index <= last) {
@ -2938,12 +2975,27 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
else else
*Length = -1; *Length = -1;
} }
if (Errors)
*Errors = index[Index].errors;
if (Missing)
*Missing = index[Index].missing;
return true; return true;
} }
} }
return false; return false;
} }
const cErrors *cIndexFile::GetErrors(void)
{
for (int Index = lastErrorIndex + 1; Index <= last; Index++) {
tIndexTs *p = &index[Index];
if (p->errors || p->missing)
errors.Append(Index);
}
lastErrorIndex = last;
return &errors;
}
int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off_t *FileOffset, int *Length) int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off_t *FileOffset, int *Length)
{ {
if (CatchUp()) { if (CatchUp()) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: recording.h 5.8 2024/06/13 09:31:11 kls Exp $ * $Id: recording.h 5.12 2025/01/15 10:50:29 kls Exp $
*/ */
#ifndef __RECORDING_H #ifndef __RECORDING_H
@ -64,6 +64,7 @@ public:
class cRecordingInfo { class cRecordingInfo {
friend class cRecording; friend class cRecording;
private: private:
time_t modified;
tChannelID channelID; tChannelID channelID;
char *channelName; char *channelName;
const cEvent *event; const cEvent *event;
@ -446,6 +447,9 @@ public:
cMark *GetNextEnd(const cMark *BeginMark) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->GetNextEnd(BeginMark)); } cMark *GetNextEnd(const cMark *BeginMark) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->GetNextEnd(BeginMark)); }
}; };
class cErrors : public cVector<int> {
};
#define RUC_BEFORERECORDING "before" #define RUC_BEFORERECORDING "before"
#define RUC_STARTRECORDING "started" #define RUC_STARTRECORDING "started"
#define RUC_AFTERRECORDING "after" #define RUC_AFTERRECORDING "after"
@ -486,9 +490,11 @@ private:
int f; int f;
cString fileName; cString fileName;
int size, last; int size, last;
int lastErrorIndex;
tIndexTs *index; tIndexTs *index;
bool isPesRecording; bool isPesRecording;
cResumeFile resumeFile; cResumeFile resumeFile;
cErrors errors;
cIndexFileGenerator *indexFileGenerator; cIndexFileGenerator *indexFileGenerator;
cMutex mutex; cMutex mutex;
void ConvertFromPes(tIndexTs *IndexTs, int Count); void ConvertFromPes(tIndexTs *IndexTs, int Count);
@ -498,8 +504,10 @@ public:
cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false, bool Update = false); cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false, bool Update = false);
~cIndexFile(); ~cIndexFile();
bool Ok(void) { return index != NULL; } bool Ok(void) { return index != NULL; }
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset); bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset, bool Errors = false, bool Missing = false);
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL); bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL, bool *Errors = NULL, bool *Missing = NULL);
const cErrors *GetErrors(void);
///< Returns the frame indexes of errors in the recording (if any).
int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL); int GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber = NULL, off_t *FileOffset = NULL, int *Length = NULL);
int GetClosestIFrame(int Index); int GetClosestIFrame(int Index);
///< Returns the index of the I-frame that is closest to the given Index (or Index itself, ///< Returns the index of the I-frame that is closest to the given Index (or Index itself,

199
remux.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remux.c 5.7 2024/01/23 19:33:45 kls Exp $ * $Id: remux.c 5.18 2024/12/05 10:37:15 kls Exp $
*/ */
#include "remux.h" #include "remux.h"
@ -287,6 +287,8 @@ uchar cTsPayload::GetByte(void)
if (TsPid(p) == pid) { // only handle TS packets for the initial PID if (TsPid(p) == pid) { // only handle TS packets for the initial PID
if (++numPacketsPid > MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION) if (++numPacketsPid > MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION)
return SetEof(); return SetEof();
if (TsError(p))
return SetEof(); // don't parse TS packets with errors
if (TsHasPayload(p)) { if (TsHasPayload(p)) {
if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
return SetEof(); return SetEof();
@ -1743,7 +1745,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
uint8_t sub_layer_profile_present_flag[8]; uint8_t sub_layer_profile_present_flag[8];
uint8_t sub_layer_level_present_flag[8]; uint8_t sub_layer_level_present_flag[8];
GetBits(4); // sps_video_parameter_set_id GetBits(4); // sps_video_parameter_set_id
int sps_max_sub_layers_minus1 = GetBits(3); // sps_max_sub_layers_minus1 int sps_max_sub_layers_minus1 = GetBits(3) & 7; // sps_max_sub_layers_minus1 ("& 7" to silence a compiler warning with gcc 14.1.0)
GetBit(); // sps_temporal_id_nesting_flag GetBit(); // sps_temporal_id_nesting_flag
// begin profile_tier_level(1, sps_max_sub_layers_minus1) // begin profile_tier_level(1, sps_max_sub_layers_minus1)
GetByte(); GetByte();
@ -1931,6 +1933,163 @@ void cH265Parser::ParseSequenceParameterSet(void)
} }
} }
static bool DebugChecks = false;
// cTsChecker and cFrameChecker are used to detect errors in the recorded data stream.
// While cTsChecker checks the continuity counter of the incoming TS packets, cFrameChecker
// works on entire frames, checking their PTS (Presentation Time Stamps) to see whether
// all expected frames arrive. The resulting number of errors is not a precise value.
// If it is zero, the recording can be safely considered error free. The higher the value,
// the more damaged the recording is.
// --- cTsChecker ------------------------------------------------------------
#define TS_CC_UNKNOWN 0xFF
class cTsChecker {
private:
uchar counter[MAXPID];
int errors;
void Report(int Pid, const char *Message);
public:
cTsChecker(void);
void CheckTs(const uchar *Data, int Length);
int Errors(void) { return errors; }
void Clear(void) { errors = 0; }
};
cTsChecker::cTsChecker(void)
{
memset(counter, TS_CC_UNKNOWN, sizeof(counter));
errors = 0;
}
void cTsChecker::Report(int Pid, const char *Message)
{
errors++;
if (DebugChecks)
fprintf(stderr, "%s: TS error #%d on PID %d (%s)\n", *TimeToString(time(NULL)), errors, Pid, Message);
}
void cTsChecker::CheckTs(const uchar *Data, int Length)
{
while (Length >= TS_SIZE) {
int Pid = TsPid(Data);
uchar Cc = TsContinuityCounter(Data);
if (TsHasPayload(Data)) {
if (TsError(Data))
Report(Pid, "tei");
else if (TsIsScrambled(Data))
Report(Pid, "scrambled");
else {
uchar OldCc = counter[Pid];
if (OldCc != TS_CC_UNKNOWN) {
uchar NewCc = (OldCc + 1) & TS_CONT_CNT_MASK;
if (Cc != NewCc)
Report(Pid, "continuity");
}
}
}
counter[Pid] = Cc;
Data += TS_SIZE;
Length -= TS_SIZE;
}
}
// --- cFrameChecker ---------------------------------------------------------
#define MAX_BACK_REFS 32
class cFrameChecker {
private:
cTsChecker tsChecker;
int frameDelta;
int64_t lastPts;
uint32_t backRefs;
int lastFwdRef;
int errors;
int previousErrors;
int missingFrames;
void Report(const char *Message, int NumErrors = 1);
public:
cFrameChecker(void);
void SetMissing(void) { missingFrames++; }
void SetFrameDelta(int FrameDelta) { frameDelta = FrameDelta; }
void CheckTs(const uchar *Data, int Length);
void CheckFrame(const uchar *Data, int Length, bool IndependentFrame);
int PreviousErrors(void) { return previousErrors; }
int MissingFrames(void) { return missingFrames; }
};
cFrameChecker::cFrameChecker(void)
{
frameDelta = PTSTICKS / DEFAULTFRAMESPERSECOND;
lastPts = -1;
backRefs = 0;
lastFwdRef = 0;
errors = 0;
previousErrors = 0;
missingFrames = 0;
}
void cFrameChecker::Report(const char *Message, int NumErrors)
{
errors += NumErrors;
if (DebugChecks)
fprintf(stderr, "%s: frame error #%d (%s)\n", *TimeToString(time(NULL)), errors, Message);
}
void cFrameChecker::CheckTs(const uchar *Data, int Length)
{
tsChecker.CheckTs(Data, Length);
}
void cFrameChecker::CheckFrame(const uchar *Data, int Length, bool IndependentFrame)
{
previousErrors = tsChecker.Errors();
missingFrames = errors;
errors = 0;
tsChecker.Clear();
int64_t Pts = TsGetPts(Data, Length);
if (Pts >= 0) {
if (lastPts >= 0) {
int Diff = int(round((PtsDiff(lastPts, Pts) / double(frameDelta))));
if (Diff > 0) {
if (Diff <= MAX_BACK_REFS) {
if (lastFwdRef > 1) {
if (backRefs != uint32_t((1 << (lastFwdRef - 1)) - 1))
Report("missing backref");
}
}
else
Report("missed", Diff);
backRefs = 0;
lastFwdRef = Diff;
lastPts = Pts;
}
else if (Diff < 0) {
Diff = -Diff;
if (Diff <= MAX_BACK_REFS) {
int b = 1 << (Diff - 1);
if ((backRefs & b) != 0)
Report("duplicate backref");
backRefs |= b;
}
else {
Report("rev diff too big");
lastPts = Pts;
}
}
else
Report("zero diff");
}
else if (IndependentFrame)
lastPts = Pts;
}
else
Report("no PTS");
}
// --- cFrameDetector -------------------------------------------------------- // --- cFrameDetector --------------------------------------------------------
const char *ScanTypeChars = "-pi"; // index is eScanType const char *ScanTypeChars = "-pi"; // index is eScanType
@ -1958,6 +2117,13 @@ cFrameDetector::cFrameDetector(int Pid, int Type)
aspectRatio = arUnknown; aspectRatio = arUnknown;
framesInPayloadUnit = framesPerPayloadUnit = 0; framesInPayloadUnit = framesPerPayloadUnit = 0;
scanning = false; scanning = false;
firstIframeSeen = false;
frameChecker = new cFrameChecker;
}
cFrameDetector::~cFrameDetector()
{
delete frameChecker;
} }
static int CmpUint32(const void *p1, const void *p2) static int CmpUint32(const void *p1, const void *p2)
@ -1986,7 +2152,23 @@ void cFrameDetector::SetPid(int Pid, int Type)
esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid); esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
} }
int cFrameDetector::Analyze(const uchar *Data, int Length) void cFrameDetector::SetMissing(void)
{
frameChecker->SetMissing();
}
bool cFrameDetector::NewFrame(int *PreviousErrors, int *MissingFrames)
{
if (newFrame) {
if (PreviousErrors)
*PreviousErrors = frameChecker->PreviousErrors();
if (MissingFrames)
*MissingFrames = frameChecker->MissingFrames();
}
return newFrame;
}
int cFrameDetector::Analyze(const uchar *Data, int Length, bool ErrorCheck)
{ {
if (!parser) if (!parser)
return 0; return 0;
@ -2016,7 +2198,10 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
if (parser->NewFrame()) { if (parser->NewFrame()) {
newFrame = true; newFrame = true;
independentFrame = parser->IndependentFrame(); independentFrame = parser->IndependentFrame();
firstIframeSeen |= independentFrame;
if (synced) { if (synced) {
if (ErrorCheck)
frameChecker->CheckFrame(Data, n, independentFrame);
if (framesPerPayloadUnit <= 1) if (framesPerPayloadUnit <= 1)
scanning = false; scanning = false;
} }
@ -2027,6 +2212,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
frameHeight = parser->FrameHeight(); frameHeight = parser->FrameHeight();
scanType = parser->ScanType(); scanType = parser->ScanType();
aspectRatio = parser->AspectRatio(); aspectRatio = parser->AspectRatio();
frameChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
synced = true; synced = true;
parser->SetDebug(false); parser->SetDebug(false);
} }
@ -2043,7 +2229,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
if (framesPerSecond <= 0.0) { if (framesPerSecond <= 0.0) {
// frame rate unknown, so collect a sequence of PTS values: // frame rate unknown, so collect a sequence of PTS values:
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!) if (newFrame) { // only take PTS values at the beginning of a frame (in case of fields!)
const uchar *Pes = Data + TsPayloadOffset(Data); const uchar *Pes = Data + TsPayloadOffset(Data);
if (numIFrames && PesHasPts(Pes)) { if (numIFrames && PesHasPts(Pes)) {
ptsValues[numPtsValues] = PesGetPts(Pes); ptsValues[numPtsValues] = PesGetPts(Pes);
@ -2085,11 +2271,12 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
framesPerSecond = 60.0 / 1.001; framesPerSecond = 60.0 / 1.001;
else { else {
framesPerSecond = DEFAULTFRAMESPERSECOND; framesPerSecond = DEFAULTFRAMESPERSECOND;
dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND); dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, framesPerSecond);
} }
} }
else // audio else // audio
framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing
frameChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset()); dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d TRO = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1, parser->IFrameTemporalReferenceOffset());
synced = true; synced = true;
parser->SetDebug(false); parser->SetDebug(false);
@ -2100,6 +2287,8 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
else if (Pid == PATPID && synced && Processed) else if (Pid == PATPID && synced && Processed)
return Processed; // allow the caller to see any PAT packets return Processed; // allow the caller to see any PAT packets
} }
if (firstIframeSeen && ErrorCheck)
frameChecker->CheckTs(Data, Handled);
Data += Handled; Data += Handled;
Length -= Handled; Length -= Handled;
Processed += Handled; Processed += Handled;

22
remux.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remux.h 5.3 2023/12/27 09:30:42 kls Exp $ * $Id: remux.h 5.8 2024/12/04 14:33:22 kls Exp $
*/ */
#ifndef __REMUX_H #ifndef __REMUX_H
@ -523,6 +523,8 @@ enum eAspectRatio {
extern const char *ScanTypeChars; extern const char *ScanTypeChars;
extern const char *AspectRatioTexts[]; extern const char *AspectRatioTexts[];
class cFrameChecker;
class cFrameDetector { class cFrameDetector {
private: private:
enum { MaxPtsValues = 150 }; enum { MaxPtsValues = 150 };
@ -544,25 +546,37 @@ private:
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1), int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
// while others put an entire GOP into one payload unit (> 1). // while others put an entire GOP into one payload unit (> 1).
bool scanning; bool scanning;
bool firstIframeSeen;
cFrameParser *parser; cFrameParser *parser;
public: public:
cFrameChecker *frameChecker;
cFrameDetector(int Pid = 0, int Type = 0); cFrameDetector(int Pid = 0, int Type = 0);
///< Sets up a frame detector for the given Pid and stream Type. ///< Sets up a frame detector for the given Pid and stream Type.
///< If no Pid and Type is given, they need to be set by a separate ///< If no Pid and Type is given, they need to be set by a separate
///< call to SetPid(). ///< call to SetPid().
~cFrameDetector();
void SetPid(int Pid, int Type); void SetPid(int Pid, int Type);
///< Sets the Pid and stream Type to detect frames for. ///< Sets the Pid and stream Type to detect frames for.
int Analyze(const uchar *Data, int Length); void SetMissing(void);
///< Call if this is a resumed recording, which has missing frames.
int Analyze(const uchar *Data, int Length, bool ErrorCheck = true);
///< Analyzes the TS packets pointed to by Data. Length is the number of ///< Analyzes the TS packets pointed to by Data. Length is the number of
///< bytes Data points to, and must be a multiple of TS_SIZE. ///< bytes Data points to, and must be a multiple of TS_SIZE.
///< Returns the number of bytes that have been analyzed. ///< Returns the number of bytes that have been analyzed.
///< If the return value is 0, the data was not sufficient for analyzing and ///< If the return value is 0, the data was not sufficient for analyzing and
///< Analyze() needs to be called again with more actual data. ///< Analyze() needs to be called again with more actual data.
///< Error checks can be turned off by setting ErrorCheck to false. This is
///< used when regenerating the index file, where the first chunk of data
///< is scanned for the PAT/PMT and then a rewind is done on the file.
bool Synced(void) { return synced; } bool Synced(void) { return synced; }
///< Returns true if the frame detector has synced on the data stream. ///< Returns true if the frame detector has synced on the data stream.
bool NewFrame(void) { return newFrame; } bool NewFrame(int *PreviousErrors = NULL, int *MissingFrames = NULL);
///< Returns true if the data given to the last call to Analyze() started a ///< Returns true if the data given to the last call to Analyze() started a
///< new frame. ///< new frame. If PreviousErrors is given, it will be set to the number of errors in
///< the previous frame. If MissingFrames is given, it will be set to the number of
///< missing frames between the previous frame and this one.
///< The results returned in PreviousErrors and MissingFrames are only valid if the
///< function returns true.
bool IndependentFrame(void) { return independentFrame; } bool IndependentFrame(void) { return independentFrame; }
///< Returns true if a new frame was detected and this is an independent frame ///< Returns true if a new frame was detected and this is an independent frame
///< (i.e. one that can be displayed by itself, without using data from any ///< (i.e. one that can be displayed by itself, without using data from any

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skinclassic.c 5.2 2023/12/29 10:48:40 kls Exp $ * $Id: skinclassic.c 5.4 2024/09/21 10:53:07 kls Exp $
*/ */
#include "skinclassic.h" #include "skinclassic.h"
@ -71,6 +71,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
THEME_CLR(Theme, clrReplayProgressSelected, clrRed); THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
THEME_CLR(Theme, clrReplayProgressMark, clrBlack); THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed); THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
// --- cSkinClassicDisplayChannel -------------------------------------------- // --- cSkinClassicDisplayChannel --------------------------------------------
@ -402,7 +403,8 @@ void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording)
xt -= w + 5; xt -= w + 5;
} }
if (Info->Errors() > 0) { if (Info->Errors() > 0) {
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors")); // TRANSLATORS: note the plural/singular!
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
const cFont *font = cFont::GetFont(fontSml); const cFont *font = cFont::GetFont(fontSml);
int w = font->Width(buffer); int w = font->Width(buffer);
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVpsFg), Theme.Color(clrMenuEventVpsBg), font, w); osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVpsFg), Theme.Color(clrMenuEventVpsBg), font, w);
@ -534,7 +536,7 @@ void cSkinClassicDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
void cSkinClassicDisplayReplay::SetProgress(int Current, int Total) void cSkinClassicDisplayReplay::SetProgress(int Current, int Total)
{ {
cProgressBar pb(x1 - x0, y2 - y1, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); cProgressBar pb(x1 - x0, y2 - y1, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
osd->DrawBitmap(x0, y1, pb); osd->DrawBitmap(x0, y1, pb);
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skinlcars.c 5.5 2024/07/13 15:25:22 kls Exp $ * $Id: skinlcars.c 5.8 2024/12/02 12:40:56 kls Exp $
*/ */
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures, // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures,
@ -188,6 +188,7 @@ THEME_CLR(Theme, clrReplayProgressRest, RgbShade(CLR_WHITE, -0.2));
THEME_CLR(Theme, clrReplayProgressSelected, CLR_EXPOSED); THEME_CLR(Theme, clrReplayProgressSelected, CLR_EXPOSED);
THEME_CLR(Theme, clrReplayProgressMark, CLR_BLACK); THEME_CLR(Theme, clrReplayProgressMark, CLR_BLACK);
THEME_CLR(Theme, clrReplayProgressCurrent, CLR_EXPOSED); THEME_CLR(Theme, clrReplayProgressCurrent, CLR_EXPOSED);
THEME_CLR(Theme, clrReplayProgressError, CLR_BLACK);
// Track display: // Track display:
@ -1258,6 +1259,7 @@ void cSkinLCARSDisplayMenu::DrawTimers(void)
int NumDevices = 0; int NumDevices = 0;
int y = ys04; int y = ys04;
// Timers and recording devices: // Timers and recording devices:
LOCK_SCHEDULES_READ;
while (1) { while (1) {
int NumTimers = 0; int NumTimers = 0;
const cDevice *Device = NULL; const cDevice *Device = NULL;
@ -1702,7 +1704,8 @@ void cSkinLCARSDisplayMenu::SetRecording(const cRecording *Recording)
xt -= w + xi02 - xi01; xt -= w + xi02 - xi01;
} }
if (Info->Errors() > 0) { if (Info->Errors() > 0) {
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors")); // TRANSLATORS: note the plural/singular!
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
const cFont *font = cFont::GetFont(fontSml); const cFont *font = cFont::GetFont(fontSml);
int w = font->Width(buffer); int w = font->Width(buffer);
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuFrameFg), frameColor, font, w); osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuFrameFg), frameColor, font, w);
@ -1928,7 +1931,7 @@ void cSkinLCARSDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
void cSkinLCARSDisplayReplay::SetProgress(int Current, int Total) void cSkinLCARSDisplayReplay::SetProgress(int Current, int Total)
{ {
cProgressBar pb(xp13 - xp03, lineHeight, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); cProgressBar pb(xp13 - xp03, lineHeight, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
osd->DrawBitmap(xp03, yp02, pb); osd->DrawBitmap(xp03, yp02, pb);
} }

42
skins.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skins.c 5.2 2024/07/13 09:12:18 kls Exp $ * $Id: skins.c 5.5 2025/02/12 21:18:53 kls Exp $
*/ */
#include "skins.h" #include "skins.h"
@ -147,7 +147,12 @@ const cFont *cSkinDisplayMenu::GetTextAreaFont(bool FixedFont) const
// --- cSkinDisplayReplay::cProgressBar -------------------------------------- // --- cSkinDisplayReplay::cProgressBar --------------------------------------
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent) cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent)
:cBitmap(Width, Height, 2) :cSkinDisplayReplay::cProgressBar::cProgressBar(Width, Height, Current, Total, Marks, NULL, ColorSeen, ColorRest, ColorSelected, ColorMark, ColorCurrent, clrBlack)
{
}
cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, const cErrors *Errors, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent, tColor ColorError)
:cBitmap(Width, Height, 4)
{ {
total = Total; total = Total;
if (total > 0) { if (total > 0) {
@ -168,6 +173,16 @@ cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Curren
Start = !Start; Start = !Start;
} }
} }
if (Errors) {
int LastPos = -1;
for (int i = 0; i < Errors->Size(); i++) {
int p1 = Errors->At(i);
if (p1 != LastPos) {
Error(Pos(Errors->At(i)), ColorError);
LastPos = p1;
}
}
}
} }
} }
@ -181,11 +196,25 @@ void cSkinDisplayReplay::cProgressBar::Mark(int x, bool Start, bool Current, tCo
} }
} }
void cSkinDisplayReplay::cProgressBar::Error(int x, tColor ColorError)
{
const int d = (Height() / 9) & ~0x01; // must be even
const int h = Height() / 2;
const int e = Height() / 4;
DrawRectangle(x, e, x, Height() -e - 1, ColorError);
DrawRectangle(x - d, h, x + d, h, ColorError);
for (int i = 1; i <= d; i++) {
DrawRectangle(x - d + i, h - i, x + d - i, h - i, ColorError);
DrawRectangle(x - d + i, h + i, x + d - i, h + i, ColorError);
}
}
// --- cSkinDisplayReplay ---------------------------------------------------- // --- cSkinDisplayReplay ----------------------------------------------------
cSkinDisplayReplay::cSkinDisplayReplay(void) cSkinDisplayReplay::cSkinDisplayReplay(void)
{ {
marks = NULL; marks = NULL;
errors = NULL;
} }
void cSkinDisplayReplay::SetRecording(const cRecording *Recording) void cSkinDisplayReplay::SetRecording(const cRecording *Recording)
@ -198,6 +227,11 @@ void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
marks = Marks; marks = Marks;
} }
void cSkinDisplayReplay::SetErrors(const cErrors *Errors)
{
errors = Errors;
}
// --- cSkin ----------------------------------------------------------------- // --- cSkin -----------------------------------------------------------------
cSkin::cSkin(const char *Name, cTheme *Theme) cSkin::cSkin(const char *Name, cTheme *Theme)
@ -271,7 +305,7 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
} }
cSkinDisplay::Current()->SetMessage(Type, s); cSkinDisplay::Current()->SetMessage(Type, s);
cSkinDisplay::Current()->Flush(); cSkinDisplay::Current()->Flush();
cStatus::MsgOsdStatusMessage(s); cStatus::MsgOsdStatusMessage(Type, s);
eKeys k = kNone; eKeys k = kNone;
if (Type != mtStatus) { if (Type != mtStatus) {
k = Interface->Wait(Seconds); k = Interface->Wait(Seconds);
@ -282,7 +316,7 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
} }
else { else {
cSkinDisplay::Current()->SetMessage(Type, NULL); cSkinDisplay::Current()->SetMessage(Type, NULL);
cStatus::MsgOsdStatusMessage(NULL); cStatus::MsgOsdStatusMessage(Type, NULL);
} }
} }
else if (!s && displayMessage) { else if (!s && displayMessage) {

14
skins.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skins.h 5.4 2024/07/15 14:42:22 kls Exp $ * $Id: skins.h 5.6 2024/09/19 09:49:02 kls Exp $
*/ */
#ifndef __SKINS_H #ifndef __SKINS_H
@ -311,23 +311,29 @@ class cSkinDisplayReplay : public cSkinDisplay {
///< a recording. ///< a recording.
protected: protected:
const cMarks *marks; const cMarks *marks;
const cErrors *errors;
class cProgressBar : public cBitmap { class cProgressBar : public cBitmap {
protected: protected:
int total; int total;
int Pos(int p) { return int(int64_t(p) * Width() / total); } int Pos(int p) { return int(int64_t(p) * Width() / total); }
void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent); void Mark(int x, bool Start, bool Current, tColor ColorMark, tColor ColorCurrent);
void Error(int x, tColor ColorError);
public: public:
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent); cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent); // for backwards compatibility
cProgressBar(int Width, int Height, int Current, int Total, const cMarks *Marks, const cErrors *Errors, tColor ColorSeen, tColor ColorRest, tColor ColorSelected, tColor ColorMark, tColor ColorCurrent, tColor ColorError);
}; };
public: public:
cSkinDisplayReplay(void); cSkinDisplayReplay(void);
virtual void SetMarks(const cMarks *Marks); virtual void SetMarks(const cMarks *Marks);
///< Sets the editing marks to Marks, which shall be used to display the ///< Sets the editing marks to Marks, which shall be used to display the
///< progress bar through a cProgressBar object. ///< progress bar through a cProgressBar object.
virtual void SetErrors(const cErrors *Errors);
///< Sets the errors found in the recording to Errors, which shall be used to display the
///< progress bar through a cProgressBar object.
virtual void SetRecording(const cRecording *Recording); virtual void SetRecording(const cRecording *Recording);
///< Sets the recording that is currently being played. ///< Sets the recording that is currently being played.
///< The default implementation calls SetTitle() with the title and short ///< The default implementation calls SetTitle() with the title
///< text of the Recording. A derived class can use any information provided ///< of the Recording. A derived class can use any information provided
///< by the given Recording and display it. ///< by the given Recording and display it.
virtual void SetTitle(const char *Title) = 0; virtual void SetTitle(const char *Title) = 0;
///< Sets the title of the recording. ///< Sets the title of the recording.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: skinsttng.c 5.2 2023/12/29 10:48:40 kls Exp $ * $Id: skinsttng.c 5.4 2024/09/21 10:53:07 kls Exp $
*/ */
// "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures // "Star Trek: The Next Generation"(R) is a registered trademark of Paramount Pictures
@ -122,6 +122,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
THEME_CLR(Theme, clrReplayProgressSelected, clrRed); THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
THEME_CLR(Theme, clrReplayProgressMark, clrBlack); THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed); THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
// --- cSkinSTTNGDisplayChannel ---------------------------------------------- // --- cSkinSTTNGDisplayChannel ----------------------------------------------
@ -718,7 +719,8 @@ void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording)
xt -= w + x5 - x4; xt -= w + x5 - x4;
} }
if (Info->Errors() > 0) { if (Info->Errors() > 0) {
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), tr("errors")); // TRANSLATORS: note the plural/singular!
cString buffer = cString::sprintf(" %d %s ", Info->Errors(), Info->Errors() > 1 ? tr("errors") : tr("error"));
const cFont *font = cFont::GetFont(fontSml); const cFont *font = cFont::GetFont(fontSml);
int w = font->Width(buffer); int w = font->Width(buffer);
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVps), frameColor, font, w); osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVps), frameColor, font, w);
@ -904,7 +906,7 @@ void cSkinSTTNGDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
void cSkinSTTNGDisplayReplay::SetProgress(int Current, int Total) void cSkinSTTNGDisplayReplay::SetProgress(int Current, int Total)
{ {
cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent)); cProgressBar pb(x4 - x3, y4 - y3, Current, Total, marks, errors, Theme.Color(clrReplayProgressSeen), Theme.Color(clrReplayProgressRest), Theme.Color(clrReplayProgressSelected), Theme.Color(clrReplayProgressMark), Theme.Color(clrReplayProgressCurrent), Theme.Color(clrReplayProgressError));
osd->DrawBitmap(x3, y3, pb); osd->DrawBitmap(x3, y3, pb);
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: sources.c 3.6 2014/03/09 12:05:42 kls Exp $ * $Id: sources.c 5.1 2024/10/09 10:36:16 kls Exp $
*/ */
#include "sources.h" #include "sources.h"
@ -46,10 +46,7 @@ bool cSource::Matches(int Code1, int Code2)
int cSource::Position(int Code) int cSource::Position(int Code)
{ {
int n = (Code & st_Pos); return int16_t(Code & st_Pos);
if (n > 0x00007FFF)
n |= 0xFFFF0000;
return n;
} }
cString cSource::ToString(int Code) cString cSource::ToString(int Code)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: status.c 4.1 2018/01/29 13:36:53 kls Exp $ * $Id: status.c 5.3 2025/02/12 21:18:53 kls Exp $
*/ */
#include "status.h" #include "status.h"
@ -95,10 +95,10 @@ void cStatus::MsgOsdTitle(const char *Title)
sm->OsdTitle(Title); sm->OsdTitle(Title);
} }
void cStatus::MsgOsdStatusMessage(const char *Message) void cStatus::MsgOsdStatusMessage(eMessageType Type, const char *Message)
{ {
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdStatusMessage(Message); sm->OsdStatusMessage2(Type, Message);
} }
void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
@ -107,16 +107,16 @@ void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yel
sm->OsdHelpKeys(Red, Green, Yellow, Blue); sm->OsdHelpKeys(Red, Green, Yellow, Blue);
} }
void cStatus::MsgOsdItem(const char *Text, int Index) void cStatus::MsgOsdItem(const char *Text, int Index, bool Selectable)
{ {
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdItem(Text, Index); sm->OsdItem2(Text, Index, Selectable);
} }
void cStatus::MsgOsdCurrentItem(const char *Text) void cStatus::MsgOsdCurrentItem(const char *Text, int Index)
{ {
for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm)) for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdCurrentItem(Text); sm->OsdCurrentItem2(Text, Index);
} }
void cStatus::MsgOsdTextItem(const char *Text, bool Scroll) void cStatus::MsgOsdTextItem(const char *Text, bool Scroll)

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: status.h 4.4 2018/01/29 13:42:17 kls Exp $ * $Id: status.h 5.3 2025/02/12 21:18:53 kls Exp $
*/ */
#ifndef __STATUS_H #ifndef __STATUS_H
@ -13,6 +13,7 @@
#include "config.h" #include "config.h"
#include "device.h" #include "device.h"
#include "player.h" #include "player.h"
#include "skins.h"
#include "tools.h" #include "tools.h"
// Several member functions of the following classes are called with a pointer to // Several member functions of the following classes are called with a pointer to
@ -81,14 +82,21 @@ protected:
virtual void OsdTitle(const char *Title) {} virtual void OsdTitle(const char *Title) {}
// Title has been displayed in the title line of the menu. // Title has been displayed in the title line of the menu.
virtual void OsdStatusMessage(const char *Message) {} virtual void OsdStatusMessage(const char *Message) {}
virtual void OsdStatusMessage2(eMessageType Type, const char *Message) { OsdStatusMessage(Message); }
// Message has been displayed in the status line of the menu. // Message has been displayed in the status line of the menu.
// If Message is NULL, the status line has been cleared. // If Message is NULL, the status line has been cleared.
virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {} virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue) {}
// The help keys have been set to the given values (may be NULL). // The help keys have been set to the given values (may be NULL).
virtual void OsdItem(const char *Text, int Index) {} virtual void OsdItem(const char *Text, int Index) {}
// The OSD displays the given single line Text as menu item at Index. // The OSD displays the given single line Text as menu item at Index.
virtual void OsdItem2(const char *Text, int Index, bool Selectable) { OsdItem(Text, Index); }
// The OSD displays the given single line Text as menu item at Index.
// Selectable is true if this item can be selected.
virtual void OsdCurrentItem(const char *Text) {} virtual void OsdCurrentItem(const char *Text) {}
// The OSD displays the given single line Text as the current menu item. // The OSD displays the given single line Text as the current menu item.
virtual void OsdCurrentItem2(const char *Text, int Index) { OsdCurrentItem(Text); }
// The OSD displays the given single line Text as the current menu item.
// Index is the one that was given in OsdItem[2]() for this item.
virtual void OsdTextItem(const char *Text, bool Scroll) {} virtual void OsdTextItem(const char *Text, bool Scroll) {}
// The OSD displays the given multi line text. If Text points to an // The OSD displays the given multi line text. If Text points to an
// actual string, that text shall be displayed and Scroll has no // actual string, that text shall be displayed and Scroll has no
@ -115,10 +123,12 @@ public:
static void MsgSetSubtitleTrack(int Index, const char * const *Tracks); static void MsgSetSubtitleTrack(int Index, const char * const *Tracks);
static void MsgOsdClear(void); static void MsgOsdClear(void);
static void MsgOsdTitle(const char *Title); static void MsgOsdTitle(const char *Title);
static void MsgOsdStatusMessage(const char *Message); [[deprecated("use MsgOsdStatusMessage(eMessageType Type, const char *Message) instead")]]
static void MsgOsdStatusMessage(const char *Message) { MsgOsdStatusMessage(mtStatus, Message); }
static void MsgOsdStatusMessage(eMessageType Type, const char *Message);
static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue); static void MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
static void MsgOsdItem(const char *Text, int Index); static void MsgOsdItem(const char *Text, int Index, bool Selectable = true);
static void MsgOsdCurrentItem(const char *Text); static void MsgOsdCurrentItem(const char *Text, int Index = -1);
static void MsgOsdTextItem(const char *Text, bool Scroll = false); static void MsgOsdTextItem(const char *Text, bool Scroll = false);
static void MsgOsdChannel(const char *Text); static void MsgOsdChannel(const char *Text);
static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle); static void MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);

View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 5.9 2024/08/30 09:55:15 kls Exp $ * $Id: svdrp.c 5.10 2024/09/09 13:39:05 kls Exp $
*/ */
#include "svdrp.h" #include "svdrp.h"
@ -1246,7 +1246,7 @@ void cSVDRPServer::CmdAUDI(const char *Option)
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(o)); const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(o));
if (TrackId && TrackId->id) { if (TrackId && TrackId->id) {
cDevice::PrimaryDevice()->SetCurrentAudioTrack(eTrackType(o)); cDevice::PrimaryDevice()->SetCurrentAudioTrack(eTrackType(o));
Reply(250, cString::sprintf("%d %s %s", eTrackType(o), *TrackId->language ? TrackId->language : "---", *TrackId->description ? TrackId->description : "-")); Reply(250, "%d %s %s", eTrackType(o), *TrackId->language ? TrackId->language : "---", *TrackId->description ? TrackId->description : "-");
} }
else else
Reply(501, "Audio track \"%s\" not available", Option); Reply(501, "Audio track \"%s\" not available", Option);
@ -1265,12 +1265,12 @@ void cSVDRPServer::CmdAUDI(const char *Option)
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i)); const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
if (TrackId && TrackId->id) { if (TrackId && TrackId->id) {
if (*s) if (*s)
Reply(-250, s); Reply(-250, "%s", *s);
s = cString::sprintf("%d %s %s%s", eTrackType(i), *TrackId->language ? TrackId->language : "---", i == CurrentAudioTrack ? "*" : "", *TrackId->description ? TrackId->description : "-"); s = cString::sprintf("%d %s %s%s", eTrackType(i), *TrackId->language ? TrackId->language : "---", i == CurrentAudioTrack ? "*" : "", *TrackId->description ? TrackId->description : "-");
} }
} }
if (*s) if (*s)
Reply(250, s); Reply(250, "%s", *s);
else else
Reply(550, "No audio tracks available"); Reply(550, "No audio tracks available");
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: thread.c 5.2 2024/01/18 13:01:07 kls Exp $ * $Id: thread.c 5.3 2025/01/15 08:43:12 kls Exp $
*/ */
#include "thread.h" #include "thread.h"
@ -597,6 +597,7 @@ cStateLockLog::cStateLockLog(void)
void cStateLockLog::Dump(const char *Name, tThreadId ThreadId) void cStateLockLog::Dump(const char *Name, tThreadId ThreadId)
{ {
dsyslog("--- begin invalid lock sequence report"); dsyslog("--- begin invalid lock sequence report");
dsyslog("TID T C R DR S ST");
int LastFlags = 0; int LastFlags = 0;
for (int i = 0; i < SLL_SIZE; i++) { for (int i = 0; i < SLL_SIZE; i++) {
if (tThreadId tid = logThreadIds[logIndex]) { if (tThreadId tid = logThreadIds[logIndex]) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: timers.c 5.20 2024/03/06 14:37:15 kls Exp $ * $Id: timers.c 5.25 2025/01/13 14:44:18 kls Exp $
*/ */
#include "timers.h" #include "timers.h"
@ -185,6 +185,8 @@ cTimer::cTimer(const cEvent *Event, const char *FileName, const cTimer *PatternT
deferred = 0; deferred = 0;
pending = inVpsMargin = false; pending = inVpsMargin = false;
flags = tfActive; flags = tfActive;
if (PatternTimer)
SetFlags(tfSpawned);
*pattern = 0; *pattern = 0;
*file = 0; *file = 0;
aux = NULL; aux = NULL;
@ -284,11 +286,13 @@ void cTimer::CalcMargins(int &MarginStart, int &MarginStop, const cEvent *Event)
MarginStop = Setup.MarginStop * 60; MarginStop = Setup.MarginStop * 60;
// To make sure the timer gets assigned to the correct event, we must // To make sure the timer gets assigned to the correct event, we must
// make sure that this is the only event that overlaps 100%: // make sure that this is the only event that overlaps 100%:
if (HasFlags(tfSpawned)) {
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev())) if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev()))
MarginStart = max(0, min(MarginStart, e->Duration() - 60)); MarginStart = max(0, min(MarginStart, e->Duration() - 60));
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next())) if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next()))
MarginStop = max(0, min(MarginStop, e->Duration() - 60)); MarginStop = max(0, min(MarginStop, e->Duration() - 60));
} }
}
int cTimer::Compare(const cListObject &ListObject) const int cTimer::Compare(const cListObject &ListObject) const
{ {
@ -612,7 +616,7 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
return false; return false;
deferred = 0; deferred = 0;
if (HasFlags(tfActive)) { if (HasFlags(tfActive) && !Remote()) {
if (event) { if (event) {
if (HasFlags(tfVps)) { if (HasFlags(tfVps)) {
if (event->Vps()) { if (event->Vps()) {
@ -620,7 +624,8 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
startTime = event->StartTime(); startTime = event->StartTime();
stopTime = event->EndTime(); stopTime = event->EndTime();
if (!Margin) { // this is an actual check if (!Margin) { // this is an actual check
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events... const cSchedule *Schedule = event->Schedule();
if (Schedule && Schedule->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events...
if (!vpsActive) { if (!vpsActive) {
vpsActive = true; vpsActive = true;
if (Recording()) if (Recording())
@ -636,7 +641,7 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
return running || time(NULL) < vpsNotRunning + VPSGRACE; return running || time(NULL) < vpsNotRunning + VPSGRACE;
} }
if (Recording()) { if (Recording()) {
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGGRACE)) if (Schedule && Schedule->PresentSeenWithin(EITPRESENTFOLLOWINGGRACE))
return event->IsRunning(true); // give it a chance to recover - worst case: the recording will be 60 seconds too long return event->IsRunning(true); // give it a chance to recover - worst case: the recording will be 60 seconds too long
if (vpsActive) { if (vpsActive) {
vpsActive = false; vpsActive = false;
@ -726,10 +731,39 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
bool cTimer::Expired(void) const bool cTimer::Expired(void) const
{ {
if (IsSingleEvent() && !Recording()) { if (IsSingleEvent() && !Recording()) {
time_t Now = time(NULL);
time_t ExpireTime = StopTimeEvent(); time_t ExpireTime = StopTimeEvent();
if (HasFlags(tfVps)) if (HasFlags(tfVps)) {
ExpireTime += EXPIRELATENCY; ExpireTime += EXPIRELATENCY;
return ExpireTime <= time(NULL); if (ExpireTime <= Now) {
LOCK_SCHEDULES_READ;
const cSchedule *Schedule = event ? event->Schedule() : NULL;
const cEvent *FirstEvent = event;
if (Schedule)
FirstEvent = Schedule->Events()->Next(FirstEvent);
else if ((Schedule = Schedules->GetSchedule(Channel())) != NULL) {
FirstEvent = Schedule->Events()->First();
if (FirstEvent)
dsyslog("timer %s had no event, got %s from channel/schedule", *ToDescr(), *FirstEvent->ToDescr());
}
if (FirstEvent) {
if (Schedule) {
for (const cEvent *e = FirstEvent; e; e = Schedule->Events()->Next(e)) {
if (e->Vps() == startTime) {
ExpireTime = e->EndTime() + EXPIRELATENCY;
dsyslog("timer %s is waiting for next VPS event %s", *ToDescr(), *e->ToDescr());
// no break here - let's play it safe and look at *all* events
}
}
}
}
else {
dsyslog("timer %s has no event, setting expiration to +24h", *ToDescr());
ExpireTime += 3600 * 24;
}
}
}
return ExpireTime <= Now;
} }
return false; return false;
} }
@ -783,7 +817,6 @@ cTimer *cTimer::SpawnPatternTimer(const cEvent *Event, cTimers *Timers)
cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File()); cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File());
isyslog("spawning timer %s for event %s", *ToDescr(), *Event->ToDescr()); isyslog("spawning timer %s for event %s", *ToDescr(), *Event->ToDescr());
cTimer *t = new cTimer(Event, FileName, this); cTimer *t = new cTimer(Event, FileName, this);
t->SetFlags(tfSpawned);
if (startswith(Pattern(), TIMERPATTERN_AVOID)) if (startswith(Pattern(), TIMERPATTERN_AVOID))
t->SetFlags(tfAvoid); t->SetFlags(tfAvoid);
Timers->Add(t); Timers->Add(t);
@ -1128,6 +1161,7 @@ const cTimer *cTimers::GetMatch(time_t t) const
{ {
static int LastPending = -1; static int LastPending = -1;
const cTimer *t0 = NULL; const cTimer *t0 = NULL;
LOCK_SCHEDULES_READ;
for (const cTimer *ti = First(); ti; ti = Next(ti)) { for (const cTimer *ti = First(); ti; ti = Next(ti)) {
if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) { if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) {
if (ti->Pending()) { if (ti->Pending()) {

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.c 5.14 2024/09/01 20:43:40 kls Exp $ * $Id: tools.c 5.15 2025/01/15 08:57:45 kls Exp $
*/ */
#include "tools.h" #include "tools.h"
@ -131,8 +131,11 @@ char *strcpyrealloc(char *dest, const char *src)
char *strn0cpy(char *dest, const char *src, size_t n) char *strn0cpy(char *dest, const char *src, size_t n)
{ {
char *s = dest; char *s = dest;
if (dest && n) {
if (src)
for ( ; --n && (*dest = *src) != 0; dest++, src++) ; for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
*dest = 0; *dest = 0;
}
return s; return s;
} }
@ -914,6 +917,8 @@ char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
if (cCharSetConv::SystemCharacterTable()) if (cCharSetConv::SystemCharacterTable())
return strn0cpy(Dest, Src, n); return strn0cpy(Dest, Src, n);
char *d = Dest; char *d = Dest;
if (Dest && n > 0) {
if (Src) {
while (*Src) { while (*Src) {
int sl = Utf8CharLen(Src); int sl = Utf8CharLen(Src);
n -= sl; n -= sl;
@ -924,7 +929,9 @@ char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
else else
break; break;
} }
}
*d = 0; *d = 0;
}
return Dest; return Dest;
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: tools.h 5.10 2024/09/01 20:43:40 kls Exp $ * $Id: tools.h 5.11 2025/01/13 13:18:42 kls Exp $
*/ */
#ifndef __TOOLS_H #ifndef __TOOLS_H
@ -140,7 +140,7 @@ int Utf8FromArray(const uint *a, char *s, int Size, int Max = -1);
// When allocating buffer space, make sure we reserve enough space to hold // When allocating buffer space, make sure we reserve enough space to hold
// a string in UTF-8 representation: // a string in UTF-8 representation:
#define Utf8BufSize(s) ((s) * 4) #define Utf8BufSize(s) ((s) * 4 + 1)
// The following macros automatically use the correct versions of the character // The following macros automatically use the correct versions of the character
// class functions: // class functions:

8
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.5 5.10 2024/09/09 10:58:55 kls Exp $ .\" $Id: vdr.5 5.12 2024/09/20 21:34:18 kls Exp $
.\" .\"
.TH vdr 5 "27 Dec 2021" "2.7" "Video Disk Recorder Files" .TH vdr 5 "27 Dec 2021" "2.7" "Video Disk Recorder Files"
.SH NAME .SH NAME
@ -824,8 +824,10 @@ l l.
The 'O' tag contains the number of errors that occurred during recording. The 'O' tag contains the number of errors that occurred during recording.
If it is zero, the recording can be safely considered error free. The higher the value, If it is zero, the recording can be safely considered error free. The higher the value,
the more damaged the recording is. the more damaged the recording is.
If this is an edited recording, the number of errors is that of the original If this is an edited recording, the number of errors is that of the edited
recording. recording, if the index of the original recording contains error indicators
(i.e. the original recording's index was created with VDR version 2.7.2 or
later). Otherwise it's the number of errors in the original recording.
.SS RESUME .SS RESUME
The file \fIresume\fR (if present in a recording directory) contains The file \fIresume\fR (if present in a recording directory) contains
the position within the recording where the last replay session left off. the position within the recording where the last replay session left off.

5
vdr.c
View File

@ -20,9 +20,9 @@
* *
* The author can be reached at vdr@tvdr.de * The author can be reached at vdr@tvdr.de
* *
* The project's page is at http://www.tvdr.de * The project's page is at https://www.tvdr.de
* *
* $Id: vdr.c 5.16 2024/03/29 21:46:50 kls Exp $ * $Id: vdr.c 5.18 2024/12/02 12:40:56 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -1140,6 +1140,7 @@ int main(int argc, char *argv[])
if (Timer->Matches(Now, true, Setup.VpsMargin)) if (Timer->Matches(Now, true, Setup.VpsMargin))
InVpsMargin = true; InVpsMargin = true;
else if (Timer->Event()) { else if (Timer->Event()) {
LOCK_SCHEDULES_READ;
InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime(); InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime();
NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME); NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME);
} }