mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Compare commits
139 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dd71a004e2 | ||
|
45091fbd72 | ||
|
988d5aebfa | ||
|
8c3671fae6 | ||
|
2a12af481a | ||
|
7817e64695 | ||
|
ebbaa39098 | ||
|
d3dcbbd4f2 | ||
|
3045995bbc | ||
|
1b4233d6ad | ||
|
34aa8fe8b4 | ||
|
baa97e9ff1 | ||
|
03afc4a353 | ||
|
ef4ebeb7ee | ||
|
80d8851e62 | ||
|
ead135f716 | ||
|
49dc61a92c | ||
|
af0309cc40 | ||
|
4ed7421b1c | ||
|
3058354dba | ||
|
20a8c5d240 | ||
|
0749a34342 | ||
|
e595eed57d | ||
|
a7576f0b6c | ||
|
657e5dda5d | ||
|
8fb6a2b24b | ||
|
53cac302d8 | ||
|
2c6c014dd8 | ||
|
a7071f580e | ||
|
de5327a048 | ||
|
7ab94c7bcb | ||
|
0f80fc5e86 | ||
|
d169f30e5c | ||
|
7a1842cba6 | ||
|
e4e9d7a55f | ||
|
ccbef6ce6c | ||
|
7461a1ba3a | ||
|
bb55e3036e | ||
|
bbf2cca198 | ||
|
8ce034d124 | ||
|
4030698007 | ||
|
66fea5c9f1 | ||
|
285574eeaa | ||
|
55cfb057e0 | ||
|
b4c538cff7 | ||
|
5a626fef9f | ||
|
2bcd8ba8f3 | ||
|
2dacc776bd | ||
|
a91d687a1a | ||
|
0d3882d43e | ||
|
72ad601328 | ||
|
2c6fd804f6 | ||
|
c590444b7d | ||
|
4805af7915 | ||
|
171b20a80d | ||
|
d00ae923ab | ||
|
d8ab5dc5c6 | ||
|
292af5d4f4 | ||
|
3d6b31b115 | ||
|
9e523073aa | ||
|
32d8e473fb | ||
|
5cd25df60c | ||
|
52c4816c9c | ||
|
6f6b05ffcb | ||
|
6dd5854b7a | ||
|
83425df0b6 | ||
|
82b09eaa8e | ||
|
ec5b1aadab | ||
|
f786510ba2 | ||
|
f006884e57 | ||
|
c0a005b3cd | ||
|
0c91893643 | ||
|
796da9e0f6 | ||
|
5d539be071 | ||
|
1df138d876 | ||
|
71b0140003 | ||
|
a33adf365d | ||
|
8d82b05071 | ||
|
930e3b4200 | ||
|
2543f2c486 | ||
|
d919817c35 | ||
|
6bbb596968 | ||
|
8aec1974bb | ||
|
b3ad9ec699 | ||
|
87410442b6 | ||
|
305735a886 | ||
|
41b7e1546a | ||
|
f3972e4795 | ||
|
e7ea087a6e | ||
|
749ba57dcc | ||
|
0360b0d0e7 | ||
|
2b495e0f87 | ||
|
32b11e1a53 | ||
|
db81c07b27 | ||
|
1c2401eb6c | ||
|
5828d347f7 | ||
|
9c64622718 | ||
|
f9260d0141 | ||
|
746cdaff01 | ||
|
179d5b87fc | ||
|
51dca45a0c | ||
|
62ad9b41dd | ||
|
8b87a6968a | ||
|
faf562fd4e | ||
|
6a09a2fbd6 | ||
|
bc32ffe2f9 | ||
|
ff16bbd777 | ||
|
f7f8a6b131 | ||
|
a3310e2954 | ||
|
0b08666310 | ||
|
42db3fbee0 | ||
|
bfa25d6276 | ||
|
824c495d33 | ||
|
561be36958 | ||
|
8bd0437497 | ||
|
18c9cef1ea | ||
|
2c66d57d4b | ||
|
29200d040e | ||
|
385738cadd | ||
|
a2591d6e98 | ||
|
fe97a38e77 | ||
|
552f5fc4e7 | ||
|
21d3d489fd | ||
|
78b7e4e252 | ||
|
5d984b606e | ||
|
88b1e30494 | ||
|
939071bf25 | ||
|
655682b5d2 | ||
|
b1418b6bcd | ||
|
35c8b3d22c | ||
|
e5ae02e3fa | ||
|
f0da21ea13 | ||
|
eb35faaf7d | ||
|
05f03d6e38 | ||
|
0d4284df29 | ||
|
cade92cda1 | ||
|
f0bbf64da0 | ||
|
6458f8b581 | ||
|
1770a18598 |
76
CONTRIBUTORS
76
CONTRIBUTORS
@ -2210,6 +2210,8 @@ Marko M
|
||||
for avoiding unnecessary processing in cDvbSubtitleConverter::FinishPage() if there
|
||||
are no areas
|
||||
for avoiding a zero sized array in cDevice::GetDevice()
|
||||
for reporting a crash when deleting a recording that is currently being edited, and
|
||||
then immediately deleting the edited version, too
|
||||
|
||||
Patrick Rother <krd-vdr@gulu.net>
|
||||
for reporting a bug in defining timers that only differ in the day of week
|
||||
@ -2468,6 +2470,10 @@ Christoph Haubrich <christoph1.haubrich@arcor.de>
|
||||
for fixing handling zero bytes in cH264Parser
|
||||
for implementing parsing frame rate and image size for MPEG2, H.264 and H.265
|
||||
for reporting a bug in generating the index file in the cutter
|
||||
for adding the frame width, height, scan type and aspect ratio of a recording to the 'F'
|
||||
tag of the 'info' file
|
||||
for adding a workaround in detecting frame height for channels with wrong crop parameters
|
||||
for reporting duplicate component entries in the info of an ongoing recording
|
||||
|
||||
Pekka Mauno <pekka.mauno@iki.fi>
|
||||
for fixing cSchedule::GetFollowingEvent() in case there is currently no present
|
||||
@ -2552,6 +2558,44 @@ Markus Ehrnsperger <markus.ehrnsperger@googlemail.com>
|
||||
for suggesting to add a chapter about locking to PLUGINS.html
|
||||
for fixing unnecessary interruption of ongoing recordings if timers avoided the
|
||||
transfer mode receiver device
|
||||
for reporting broken video data streams on systems without output device when switching
|
||||
live channel to a different transponder while recording
|
||||
for fixing a possible crash in cDevice::StopSectionHandler()
|
||||
for making using a dummy OSD if no OSD provider is available not be considered an error
|
||||
any more
|
||||
for removing syslog calls in child process after fork()
|
||||
for adding the move constructor to cString for better performance
|
||||
for fixing handling primary device on headless systems
|
||||
for adding a 15 second grace period before actually stopping a VPS timer
|
||||
for making the primary device no longer start unnecessary threads if it doesn't have
|
||||
a decoder
|
||||
for improving handling present/following data for VPS timers
|
||||
for making logging event status changes also show the previous status
|
||||
for making a device always being kept occupied if a timer is in VPS margin or needs the
|
||||
transponder
|
||||
for suggesting to enable unused devices to be put into a power save mode
|
||||
for a patch that was used to implement power save mode for cDvbDevice
|
||||
for suggesting to add 'lnbPowerTurnedOn = false' to cDvbTuner::ProvidesFrontend()
|
||||
for reporting a crash in strreplace() for multiple replacements with strings of
|
||||
different lengths
|
||||
for suggesting to add the lines from 'Fixed a timeout in cDvbDevice while tuning after
|
||||
the frontend has been reopened' to cDvbTuner::ProvidesFrontend()
|
||||
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>
|
||||
for reporting a bug in handling the cPluginManager::Active() result when pressing
|
||||
@ -2809,6 +2853,13 @@ Winfried K
|
||||
for fixing default values for DVB-T
|
||||
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 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>
|
||||
for adding the command line option --userdump to enable core dumps in case VDR
|
||||
@ -2979,6 +3030,7 @@ Manuel Reimer <Manuel.Reimer@gmx.de>
|
||||
number is given
|
||||
for reporting that LSTE doesn't work after PUTE in case a channel didn't already
|
||||
have EPG data
|
||||
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
|
||||
|
||||
Rene van den Braken <rene@vandenbraken.name>
|
||||
for reporting a bug in writing the PCR pid into the PMT in
|
||||
@ -3032,6 +3084,7 @@ Lars Hanisch <dvb@flensrocker.de>
|
||||
for fixing a typo in the description of cTimers::GetTimersRead()
|
||||
for suggesting to use dynamic buffering in handling CA descriptors to avoid a
|
||||
possible buffer overflow
|
||||
for suggesting to make APIVERSION a simple number, independent from VDRVERSION
|
||||
|
||||
Alex Lasnier <alex@fepg.org>
|
||||
for adding tuning support for ATSC devices
|
||||
@ -3321,6 +3374,16 @@ Mike Hay <mike.hay@linenshorts.com>
|
||||
Stefan Hofmann <stefan.hofmann@t-online.de>
|
||||
for suggesting to implement support for remote controls that only have a combined
|
||||
"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>
|
||||
for suggesting to automatically display the progress display whenever replay of a
|
||||
@ -3386,6 +3449,11 @@ Matthias Senzel <matthias.senzel@t-online.de>
|
||||
for reporting that if an error occurs while recording, the respective entry in the list
|
||||
of recordings was not updated immediately
|
||||
for fixing restoring the volume at program start
|
||||
for fixing height calculation in progress display
|
||||
for fixing an unnecessary double display of the current menu item in page up/down
|
||||
for fixing an unnecessary double display of menu items in the Recordings menu
|
||||
for reporting a bug in handling cSkinDisplayMenu::GetTextAreaFont()
|
||||
for reporting characters being cut off while editing in the LCARS skin
|
||||
|
||||
Marek Nazarko <mnazarko@gmail.com>
|
||||
for translating OSD texts to the Polish language
|
||||
@ -3581,6 +3649,9 @@ Stefan Herdler <herdler@gmx.de>
|
||||
for adding failsafe defaults for 'make LCLBLD=1' to the Makefile
|
||||
for reporting the index file of a recording not being regenerated in case it is
|
||||
present, but empty
|
||||
for reporting a missing check for self-assignment in the move assignment operator
|
||||
for modifying handling channel names with source to make it thread safe
|
||||
for fixing setting T2 system ID from NIT
|
||||
|
||||
Tobias Faust <tobias.faust@gmx.de>
|
||||
for the original "jumpingseconds" patch
|
||||
@ -3742,3 +3813,8 @@ Timo Weing
|
||||
|
||||
Jose Angel <joseangelpp@gmail.com>
|
||||
for reporting a bug in default values for DVB-T
|
||||
|
||||
Andreas Baierl <post@andreasbaierl.de>
|
||||
for implementing scaling images
|
||||
for reporting a problem in the progress display when switching from "pause" to
|
||||
"slow back"
|
||||
|
238
HISTORY
238
HISTORY
@ -9851,3 +9851,241 @@ Video Disk Recorder Revision History
|
||||
- Fixed unnecessary interruption of ongoing recordings if timers avoided the transfer
|
||||
mode receiver device (thanks to Markus Ehrnsperger).
|
||||
- Revised support for kernel based LIRC driver (thanks to Marko Mäkelä).
|
||||
|
||||
2023-12-30: Version 2.6.5
|
||||
|
||||
- Fixed broken video data streams on systems without output device when switching live
|
||||
channel to a different transponder while recording (reported by Markus Ehrnsperger).
|
||||
- The frame width, height, scan type and aspect ratio of a recording are now stored in
|
||||
the 'info' file under the 'F' tag (thanks to Christoph Haubrich).
|
||||
- Added the function cRecordingInfo::FrameParams(), which can be used to get a nicely
|
||||
formatted string with all the available frame data.
|
||||
- The recording info of the default skins now shows the frame parameters of the
|
||||
recording at the end of the description (if such information is available).
|
||||
|
||||
2024-01-24: Version 2.6.6
|
||||
|
||||
- Changed installing config files to handle potentially broken 'cp -n'.
|
||||
- Fixed height calculation in progress display (thanks to Matthias Senzel).
|
||||
- Fixed a possible crash in cDevice::StopSectionHandler() (thanks to Markus
|
||||
Ehrnsperger).
|
||||
- Using a dummy OSD if no OSD provider is available is not considered an error any
|
||||
more (thanks to Markus Ehrnsperger).
|
||||
- Implemented scaling images (thanks to Andreas Baierl).
|
||||
- Removed syslog calls in child process after fork() (thanks to Markus Ehrnsperger).
|
||||
- Fixed an unnecessary double display of the current menu item in page up/down
|
||||
(thanks to Matthias Senzel).
|
||||
- Fixed an unnecessary double display of menu items in the Recordings menu (thanks to
|
||||
Matthias Senzel).
|
||||
- Added the move constructor to cString for better performance (thanks to Markus
|
||||
Ehrnsperger).
|
||||
- Added the total number of errors when logging new recording errors.
|
||||
- Added '/' to the list of fuzzy characters for pattern timers.
|
||||
- Fixed handling primary device on headless systems (thanks to Markus Ehrnsperger).
|
||||
- Workaround in detecting frame height for channels with wrong crop parameters
|
||||
(thanks to Christoph Haubrich).
|
||||
- Fixed possible duplicate component entries in the info of an ongoing recording
|
||||
(reported by Christoph Haubrich).
|
||||
|
||||
2024-04-02: Version 2.6.7
|
||||
|
||||
- Fixed the move assignment operator to check for self-assignment (suggested by
|
||||
Stefan Herdler).
|
||||
- Added missing initialization of cChannel::nameSourceMode (thanks to Winfried Köhler).
|
||||
- Modified handling channel names with source to make it thread safe (thanks to
|
||||
Stefan Herdler).
|
||||
- Adapted "Setup/Miscellaneous/Show channel names with source" to the new handling
|
||||
in cChannel.
|
||||
- Added a 15 second grace period before actually stopping a VPS timer (thanks to
|
||||
Markus Ehrnsperger).
|
||||
- The primary device no longer starts unnecessary threads if it doesn't have a decoder
|
||||
(thanks to Markus Ehrnsperger).
|
||||
- The info file of a recording is now re-read if an update of the video directory
|
||||
is triggered, to make sure modifications from other VDRs are adopted.
|
||||
- Updated the Hungarian OSD texts (thanks to István Füley).
|
||||
- The new setup parameters "EPG scan max. channel number" and "EPG pause after scan"
|
||||
can be used to tune the behavior of the EPG scan (see MANUAL for details).
|
||||
- Improved handling present/following data for VPS timers (thanks to Markus Ehrnsperger).
|
||||
- Logging event status changes now also shows the previous status (thanks to Markus
|
||||
Ehrnsperger).
|
||||
- Fixed logging when a timer has entered the VPS margin.
|
||||
- The EIT scan no longer deletes the scanList if no device was switched in this pass.
|
||||
- The EIT scan now skips scanList entries if a device is already tuned to that
|
||||
transponder.
|
||||
- The EIT scan is no longer inhibited if a timer is in VPS margin or needs the
|
||||
transponder.
|
||||
- If the current channel is no longer available because of a VPS timer entering the
|
||||
VPS margin, live view now switches to the channel of that timer.
|
||||
- A device is now always kept occupied if a timer is in VPS margin or needs the
|
||||
transponder (thanks to Markus Ehrnsperger).
|
||||
|
||||
2024-07-08: Version 2.6.8
|
||||
|
||||
- Updated the Italian OSD texts (thanks to Diego Pierotto).
|
||||
- Fixed a possible access of a deleted object in the EIT scanner.
|
||||
- Fixed setting T2 system ID from NIT (thanks to Stefan Herdler).
|
||||
- When starting an editing process, VDR now first checks whether there is enough
|
||||
free disk space to take up the edited version of the recording.
|
||||
- Added an EPG bugfix for broadcasters who put literal "\n" strings in their EPG.
|
||||
Note that the string version of strreplace() has been modified, so that it
|
||||
replaces all occurrences of the search string, not just the first one.
|
||||
- Removed leftover cMenuRecordings::SetPath().
|
||||
- The EIT scanner now checks whether there is a proper device before adding a
|
||||
channel to the scan list.
|
||||
- Unused devices can now be put into a power save mode (suggested by Markus
|
||||
Ehrnsperger). Device plugins need to implement the new function
|
||||
cDevice::SetPowerSaveMode() to make this work.
|
||||
- Implemented power save mode for cDvbDevice (based on a patch from Markus
|
||||
Ehrnsperger).
|
||||
- Added 'lnbPowerTurnedOn = false' to cDvbTuner::ProvidesFrontend() (suggested by
|
||||
Markus Ehrnsperger).
|
||||
|
||||
2024-07-15: Version 2.6.9
|
||||
|
||||
- Fixed a crash in strreplace() for multiple replacements with strings of different
|
||||
lengths (reported by Markus Ehrnsperger).
|
||||
- Fixed handling of cSkinDisplayMenu::GetTextAreaFont() (reported by Matthias Senzel).
|
||||
- Fixed a timeout in cDvbDevice while tuning after the frontend has been reopened.
|
||||
- Fixed setting the editable width in the LCARS skin (reported by Matthias Senzel).
|
||||
- Fixed restarting the EPG scan and keeping the frequency of calls to
|
||||
Device->SetPowerSaveIfUnused() low.
|
||||
- Added the lines from 'Fixed a timeout in cDvbDevice while tuning after the frontend
|
||||
has been reopened' to cDvbTuner::ProvidesFrontend() (suggested by Markus Ehrnsperger).
|
||||
|
||||
2024-09-09: Version 2.7.1
|
||||
|
||||
- Removed deprecated function cDevice::SetCurrentChannel(const cChannel *Channel).
|
||||
- Removed deprecated function cSkinDisplayMenu::SetItemEvent(const cEvent *Event, int Index,
|
||||
bool Current, bool Selectable, const cChannel *Channel, bool WithDate, eTimerMatch TimerMatch).
|
||||
- Removed deprecated functions from cFile.
|
||||
- The default for DEPRECATED_SCHEDULE_GET_EVENT has been set to 0, which means that
|
||||
the function GetEvent(tEventID EventID, time_t StartTime = 0) is no longer available.
|
||||
You can add 'DEPRECATED_SCHEDULE_GET_EVENT=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use GetEventById() and GetEventByTime()
|
||||
instead.
|
||||
- The default for DEPRECATED_SECTIONSYNCER_SYNC_REPEAT has been set to 0, which means that
|
||||
the functions Repeat() and Sync() are no longer available.
|
||||
You can add 'DEPRECATED_SECTIONSYNCER_SYNC_REPEAT=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use Check() and Processed() instead.
|
||||
- The default for DEPRECATED_CCONTROL has been set to 0, which means that
|
||||
the function Control(bool Hidden) is no longer available.
|
||||
You can add 'DEPRECATED_CCONTROL=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use Control(cMutexLock &MutexLock, bool Hidden)
|
||||
instead.
|
||||
- Changed the error message when trying to attach a player to a primary device without
|
||||
an MPEG decoder.
|
||||
- The new SVDRP command 'AUDI' can be used to list the currently available audio tracks
|
||||
and select one of them.
|
||||
- Fixed a crash when deleting a recording that is currently being edited, and then
|
||||
immediately deleting the edited version, too (reported by Marko Mäkelä).
|
||||
- The '.update' file in the video directory is now created if it doesn't already exist.
|
||||
- Improved the error message when closing a frontend (thanks to Markus Ehrnsperger).
|
||||
- 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
|
||||
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.
|
||||
|
2
INSTALL
2
INSTALL
@ -1,7 +1,7 @@
|
||||
Installation of the Video Disk Recorder
|
||||
---------------------------------------
|
||||
|
||||
Version 2.6
|
||||
Version 2.7
|
||||
-----------
|
||||
|
||||
Compiling and running the program:
|
||||
|
17
MANUAL
17
MANUAL
@ -1,7 +1,7 @@
|
||||
Video Disk Recorder User's Manual
|
||||
---------------------------------
|
||||
|
||||
Version 2.6
|
||||
Version 2.7
|
||||
-----------
|
||||
|
||||
* Remote Control Keys
|
||||
@ -50,8 +50,8 @@ Version 2.6
|
||||
Next Next/previous channel group (in live tv mode)
|
||||
Prev or next/previous editing mark (in replay mode)
|
||||
|
||||
Channel+ channel up
|
||||
Channel- channel down
|
||||
Channel+ channel up (live view), next error (replay)
|
||||
Channel- channel down (live view), previous error (replay)
|
||||
PrevChannel previous channel
|
||||
|
||||
Power shutdown
|
||||
@ -645,6 +645,7 @@ following the matching pattern. There are three macros that can be used here:
|
||||
As of VDR version 2.5.2, the characters ' ' (blank), ':' and '-' are ignored
|
||||
when checking whether a particular recording has already been made by a pattern
|
||||
timer, making "TITLE - EPISODE" and "TITLE: EPISODE" the same.
|
||||
VDR version 2.6.6 added '/' to this list.
|
||||
|
||||
* Managing folders
|
||||
|
||||
@ -828,6 +829,16 @@ timer, making "TITLE - EPISODE" and "TITLE: EPISODE" the same.
|
||||
A value of '0' completely turns off scanning on both single
|
||||
and multiple card systems.
|
||||
|
||||
EPG scan max. channel number = 0
|
||||
The EPG scan will only tune to transponders of channels with
|
||||
numbers below this limit. By default all transponders will
|
||||
be scanned.
|
||||
|
||||
EPG pause after scan = no
|
||||
After a complete scan of all transponders (optionally limited
|
||||
by "EPG scan max. channel number") the EPG scan pauses for
|
||||
"EPG scan timeout" hours if this option is set to "yes".
|
||||
|
||||
EPG bugfix level = 3 Some tv stations transmit weirdly formatted EPG data.
|
||||
VDR attempts to fix these bugs up to the given level:
|
||||
0 = no EPG fixing
|
||||
|
@ -6,7 +6,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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:
|
||||
|
||||
@ -33,14 +33,15 @@ endif
|
||||
# Default directories (adjust as necessary or desired):
|
||||
|
||||
#PREFIX = /usr/local
|
||||
#BINDIR = $(PREFIX)/bin
|
||||
#INCDIR = $(PREFIX)/include
|
||||
#LIBDIR = $(PREFIX)/lib/vdr
|
||||
#LOCDIR = $(PREFIX)/share/locale
|
||||
#MANDIR = $(PREFIX)/share/man
|
||||
#PCDIR = $(PREFIX)/lib/pkgconfig
|
||||
#RESDIR = $(PREFIX)/share/vdr
|
||||
#DVBDIR = /usr/src/v4l-dvb/linux/include/uapi
|
||||
#VDRROOT = $(PREFIX)
|
||||
#BINDIR = $(VDRROOT)/bin
|
||||
#INCDIR = $(VDRROOT)/include
|
||||
#LIBDIR = $(VDRROOT)/lib
|
||||
#LOCDIR = $(VDRROOT)/locale
|
||||
#MANDIR = $(VDRROOT)/man
|
||||
#PCDIR = $(VDRROOT)/pkgconfig
|
||||
#RESDIR = $(VDRROOT)/share
|
||||
#DVBDIR = /usr/include
|
||||
|
||||
#VIDEODIR = /srv/vdr/video
|
||||
#CONFDIR = /var/lib/vdr
|
||||
|
33
Makefile
33
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# how to reach the author.
|
||||
#
|
||||
# $Id: Makefile 5.1 2020/12/31 11:11:53 kls Exp $
|
||||
# $Id: Makefile 5.4 2024/10/21 19:01:16 kls Exp $
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
@ -46,13 +46,14 @@ ARGSDIR ?= /etc/vdr/conf.d
|
||||
CACHEDIR ?= /var/cache/vdr
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
INCDIR ?= $(PREFIX)/include
|
||||
LIBDIR ?= $(PREFIX)/lib/vdr
|
||||
LOCDIR ?= $(PREFIX)/share/locale
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
PCDIR ?= $(PREFIX)/lib/pkgconfig
|
||||
RESDIR ?= $(PREFIX)/share/vdr
|
||||
VDRROOT ?= $(PREFIX)
|
||||
BINDIR ?= $(VDRROOT)/bin
|
||||
INCDIR ?= $(VDRROOT)/include
|
||||
LIBDIR ?= $(VDRROOT)/lib/vdr
|
||||
LOCDIR ?= $(VDRROOT)/share/locale
|
||||
MANDIR ?= $(VDRROOT)/share/man
|
||||
PCDIR ?= $(VDRROOT)/lib/pkgconfig
|
||||
RESDIR ?= $(VDRROOT)/share/vdr
|
||||
|
||||
# Source documentation
|
||||
|
||||
@ -169,7 +170,9 @@ make-libsi: # empty rule makes sure the sub-make for libsi is always called
|
||||
|
||||
.PHONY: vdr.pc
|
||||
vdr.pc:
|
||||
@echo "bindir=$(BINDIR)" > $@
|
||||
@echo "vdrrootdir=$(VDRROOT)" > $@
|
||||
@echo "bindir=$(BINDIR)" >> $@
|
||||
@echo "incdir=$(INCDIR)" >> $@
|
||||
@echo "mandir=$(MANDIR)" >> $@
|
||||
@echo "videodir=$(VIDEODIR)" >> $@
|
||||
@echo "configdir=$(CONFDIR)" >> $@
|
||||
@ -185,7 +188,7 @@ vdr.pc:
|
||||
@echo "" >> $@
|
||||
@echo "Name: VDR" >> $@
|
||||
@echo "Description: Video Disk Recorder" >> $@
|
||||
@echo "URL: http://www.tvdr.de/" >> $@
|
||||
@echo "URL: https://www.tvdr.de/" >> $@
|
||||
@echo "Version: $(VDRVERSION)" >> $@
|
||||
@echo "Cflags: \$${cflags}" >> $@
|
||||
|
||||
@ -300,7 +303,12 @@ install-dirs:
|
||||
@mkdir -p $(DESTDIR)$(RESDIR)
|
||||
|
||||
install-conf: install-dirs
|
||||
@cp -pn *.conf $(DESTDIR)$(CONFDIR)
|
||||
# 'cp -n' may be broken, so let's do it the hard way
|
||||
@for i in *.conf; do\
|
||||
if ! [ -e $(DESTDIR)$(CONFDIR)/$$i ] ; then\
|
||||
cp -p $$i $(DESTDIR)$(CONFDIR);\
|
||||
fi\
|
||||
done
|
||||
|
||||
# Documentation:
|
||||
|
||||
@ -315,6 +323,7 @@ install-doc:
|
||||
|
||||
install-plugins: plugins
|
||||
@-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;\
|
||||
done
|
||||
@if [ -d $(PLUGINDIR)/lib ] ; then\
|
||||
@ -351,7 +360,7 @@ srcdoc:
|
||||
clean:
|
||||
@$(MAKE) --no-print-directory -C $(LSIDIR) clean
|
||||
@-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 srcdoc
|
||||
CLEAN: clean
|
||||
|
54
PLUGINS.html
54
PLUGINS.html
@ -31,11 +31,11 @@ modified {
|
||||
<div class="center">
|
||||
<h1>The VDR Plugin System</h1>
|
||||
|
||||
<b>Version 2.6</b>
|
||||
<b>Version 2.7</b>
|
||||
<p>
|
||||
Copyright © 2021 Klaus Schmidinger<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>
|
||||
<p>
|
||||
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="#User interaction">User interaction</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="#Wakeup">Wakeup</a>
|
||||
<li><a href="#Setup parameters">Setup parameters</a>
|
||||
@ -166,7 +165,7 @@ is used:
|
||||
VDR/PLUGINS/src
|
||||
VDR/PLUGINS/src/hello
|
||||
VDR/PLUGINS/lib
|
||||
VDR/PLUGINS/lib/libvdr-hello.so.1.1.0
|
||||
VDR/PLUGINS/lib/libvdr-hello.so.1
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
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
|
||||
<p>
|
||||
<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>
|
||||
</table>
|
||||
<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
|
||||
recompiled.
|
||||
<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
|
||||
is not used, the path to the plugin directory has be be given to VDR through the
|
||||
<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.
|
||||
<p>
|
||||
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
|
||||
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.
|
||||
version <tt>1.0.0</tt> once it is completely operative and well tested.
|
||||
|
||||
<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.
|
||||
</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>
|
||||
|
||||
<div class="blurb">Now is not a good time!</div><p>
|
||||
@ -2117,6 +2094,17 @@ new cMyDeviceHook;
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
and shall not delete this object. It will be automatically deleted when the program ends.
|
||||
<p>
|
||||
<b>Power management</b>
|
||||
<p>
|
||||
A device that can be put into a power save mode can implement the function
|
||||
|
||||
<p><table><tr><td class="code"><pre>
|
||||
virtual void SetPowerSaveMode(bool On);
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
If On is true, power save mode shall be activated, if it is false,
|
||||
normal operating mode shall be restored.
|
||||
|
||||
<hr><h2><a name="Positioners">Positioners</a></h2>
|
||||
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -76,3 +76,10 @@ VDR Plugin 'status' Revision History
|
||||
2021-12-27: Version 2.6.0
|
||||
|
||||
- Official release.
|
||||
|
||||
2025-02-10: Version 2.6.1
|
||||
|
||||
- Added cStatus::OsdItem2().
|
||||
- Activated logging of OsdItem2().
|
||||
- Added cStatus::OsdCurrentItem2().
|
||||
- Added cStatus::OsdStatusMessage2().
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -3,13 +3,13 @@
|
||||
*
|
||||
* 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/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 *MAINMENUENTRY = NULL;
|
||||
|
||||
@ -27,10 +27,10 @@ protected:
|
||||
virtual void SetSubtitleTrack(int Index, const char * const *Tracks);
|
||||
virtual void OsdClear(void);
|
||||
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 OsdItem(const char *Text, int Index);
|
||||
virtual void OsdCurrentItem(const char *Text);
|
||||
virtual void OsdItem2(const char *Text, int Index, bool Selectable);
|
||||
virtual void OsdCurrentItem2(const char *Text, int Index);
|
||||
virtual void OsdTextItem(const char *Text, bool Scroll);
|
||||
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);
|
||||
@ -86,9 +86,9 @@ void cStatusTest::OsdTitle(const char *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)
|
||||
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -2,9 +2,9 @@ This is a "plugin" for the Video Disk Recorder (VDR).
|
||||
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
4
README
4
README
@ -4,7 +4,7 @@ Video Disk Recorder ('VDR')
|
||||
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).
|
||||
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
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@ Plugins:
|
||||
- Implemented a universal plugin interface. See the file PLUGINS.html
|
||||
for a detailed description. The man page vdr(1) describes the new options '-L'
|
||||
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
|
||||
additional types of remote controls (see PLUGINS.html, section "Remote Control").
|
||||
The previously used files 'keys.conf' and 'keys-pc.conf' have been replaced
|
||||
|
55
channels.c
55
channels.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: channels.c 5.1 2021/05/21 09:38:34 kls Exp $
|
||||
* $Id: channels.c 5.3 2024/03/02 16:21:16 kls Exp $
|
||||
*/
|
||||
|
||||
#include "channels.h"
|
||||
@ -97,23 +97,32 @@ cChannel& cChannel::operator= (const cChannel &Channel)
|
||||
provider = strcpyrealloc(provider, Channel.provider);
|
||||
portalName = strcpyrealloc(portalName, Channel.portalName);
|
||||
memcpy(&__BeginData__, &Channel.__BeginData__, (char *)&Channel.__EndData__ - (char *)&Channel.__BeginData__);
|
||||
nameSource = NULL; // these will be recalculated automatically
|
||||
nameSourceMode = 0;
|
||||
shortNameSource = NULL;
|
||||
UpdateNameSource();
|
||||
parameters = Channel.parameters;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void cChannel::UpdateNameSource(void)
|
||||
{
|
||||
if (Setup.ShowChannelNamesWithSource == 0) {
|
||||
nameSource = NULL;
|
||||
shortNameSource = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Setup.ShowChannelNamesWithSource == 1)
|
||||
nameSource = cString::sprintf("%s (%c)", name, cSource::ToChar(source));
|
||||
else
|
||||
nameSource = cString::sprintf("%s (%s)", name, *cSource::ToString(source));
|
||||
|
||||
shortNameSource = cString::sprintf("%s (%c)", shortName, cSource::ToChar(source));
|
||||
}
|
||||
|
||||
const char *cChannel::Name(void) const
|
||||
{
|
||||
if (Setup.ShowChannelNamesWithSource && !groupSep) {
|
||||
if (isempty(nameSource) || nameSourceMode != Setup.ShowChannelNamesWithSource) {
|
||||
if (Setup.ShowChannelNamesWithSource == 1)
|
||||
nameSource = cString::sprintf("%s (%c)", name, cSource::ToChar(source));
|
||||
else
|
||||
nameSource = cString::sprintf("%s (%s)", name, *cSource::ToString(source));
|
||||
}
|
||||
return nameSource;
|
||||
if (!isempty(nameSource))
|
||||
return nameSource;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@ -123,9 +132,8 @@ const char *cChannel::ShortName(bool OrName) const
|
||||
if (OrName && isempty(shortName))
|
||||
return Name();
|
||||
if (Setup.ShowChannelNamesWithSource && !groupSep) {
|
||||
if (isempty(shortNameSource))
|
||||
shortNameSource = cString::sprintf("%s (%c)", shortName, cSource::ToChar(source));
|
||||
return shortNameSource;
|
||||
if (!isempty(shortNameSource))
|
||||
return shortNameSource;
|
||||
}
|
||||
return shortName;
|
||||
}
|
||||
@ -203,9 +211,7 @@ bool cChannel::SetTransponderData(int Source, int Frequency, int Srate, const ch
|
||||
srate = Srate;
|
||||
parameters = Parameters;
|
||||
schedule = NULL;
|
||||
nameSource = NULL;
|
||||
nameSourceMode = 0;
|
||||
shortNameSource = NULL;
|
||||
UpdateNameSource();
|
||||
if (Number() && !Quiet) {
|
||||
dsyslog("changing transponder data of channel %d (%s) from %s to %s", Number(), name, *OldTransponderData, *TransponderDataToString());
|
||||
modification |= CHANNELMOD_TRANSP;
|
||||
@ -270,15 +276,12 @@ bool cChannel::SetName(const char *Name, const char *ShortName, const char *Prov
|
||||
dsyslog("changing name of channel %d from '%s,%s;%s' to '%s,%s;%s'", Number(), name, shortName, provider, Name, ShortName, Provider);
|
||||
modification |= CHANNELMOD_NAME;
|
||||
}
|
||||
if (nn) {
|
||||
if (nn)
|
||||
name = strcpyrealloc(name, Name);
|
||||
nameSource = NULL;
|
||||
nameSourceMode = 0;
|
||||
}
|
||||
if (ns) {
|
||||
if (ns)
|
||||
shortName = strcpyrealloc(shortName, ShortName);
|
||||
shortNameSource = NULL;
|
||||
}
|
||||
if (nn || ns)
|
||||
UpdateNameSource();
|
||||
if (np)
|
||||
provider = strcpyrealloc(provider, Provider);
|
||||
return true;
|
||||
@ -804,9 +807,7 @@ bool cChannel::Parse(const char *s)
|
||||
free(tpidbuf);
|
||||
free(caidbuf);
|
||||
free(namebuf);
|
||||
nameSource = NULL;
|
||||
nameSourceMode = 0;
|
||||
shortNameSource = NULL;
|
||||
UpdateNameSource();
|
||||
if (!GetChannelID().Valid()) {
|
||||
esyslog("ERROR: channel data results in invalid ID!");
|
||||
return false;
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: channels.h 5.2 2021/05/21 09:38:34 kls Exp $
|
||||
* $Id: channels.h 5.3 2024/03/02 16:21:16 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CHANNELS_H
|
||||
@ -87,6 +87,7 @@ class cChannels;
|
||||
class cChannel : public cListObject {
|
||||
friend class cSchedules;
|
||||
friend class cMenuEditChannel;
|
||||
friend class cMenuSetupMisc;
|
||||
friend class cDvbSourceParam;
|
||||
private:
|
||||
static cString ToText(const cChannel *Channel);
|
||||
@ -123,9 +124,8 @@ private:
|
||||
int number; // Sequence number assigned on load
|
||||
bool groupSep;
|
||||
int __EndData__;
|
||||
mutable cString nameSource;
|
||||
mutable int nameSourceMode;
|
||||
mutable cString shortNameSource;
|
||||
cString nameSource;
|
||||
cString shortNameSource;
|
||||
cString parameters;
|
||||
mutable int modification;
|
||||
time_t seen; // When this channel was last seen in the SDT of its transponder
|
||||
@ -133,6 +133,7 @@ private:
|
||||
cLinkChannels *linkChannels;
|
||||
cChannel *refChannel;
|
||||
cString TransponderDataToString(void) const;
|
||||
void UpdateNameSource(void);
|
||||
public:
|
||||
cChannel(void);
|
||||
cChannel(const cChannel &Channel);
|
||||
|
8
config.c
8
config.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.c 4.8 2018/02/15 14:40:36 kls Exp $
|
||||
* $Id: config.c 5.1 2024/03/04 21:13:58 kls Exp $
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
@ -409,6 +409,8 @@ cSetup::cSetup(void)
|
||||
SubtitleBgTransparency = 0;
|
||||
EPGLanguages[0] = -1;
|
||||
EPGScanTimeout = 5;
|
||||
EPGScanMaxChannel = 0;
|
||||
EPGPauseAfterScan = 0;
|
||||
EPGBugfixLevel = 3;
|
||||
EPGLinger = 0;
|
||||
SVDRPTimeout = 300;
|
||||
@ -637,6 +639,8 @@ bool cSetup::Parse(const char *Name, const char *Value)
|
||||
else if (!strcasecmp(Name, "SubtitleBgTransparency")) SubtitleBgTransparency = atoi(Value);
|
||||
else if (!strcasecmp(Name, "EPGLanguages")) return ParseLanguages(Value, EPGLanguages);
|
||||
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
|
||||
else if (!strcasecmp(Name, "EPGScanMaxChannel")) EPGScanMaxChannel = atoi(Value);
|
||||
else if (!strcasecmp(Name, "EPGPauseAfterScan")) EPGPauseAfterScan = atoi(Value);
|
||||
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
|
||||
else if (!strcasecmp(Name, "EPGLinger")) EPGLinger = atoi(Value);
|
||||
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
|
||||
@ -769,6 +773,8 @@ bool cSetup::Save(void)
|
||||
Store("SubtitleBgTransparency", SubtitleBgTransparency);
|
||||
StoreLanguages("EPGLanguages", EPGLanguages);
|
||||
Store("EPGScanTimeout", EPGScanTimeout);
|
||||
Store("EPGScanMaxChannel", EPGScanMaxChannel);
|
||||
Store("EPGPauseAfterScan", EPGPauseAfterScan);
|
||||
Store("EPGBugfixLevel", EPGBugfixLevel);
|
||||
Store("EPGLinger", EPGLinger);
|
||||
Store("SVDRPTimeout", SVDRPTimeout);
|
||||
|
22
config.h
22
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: config.h 5.12 2023/02/17 12:04:45 kls Exp $
|
||||
* $Id: config.h 5.26 2025/02/26 10:35:03 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __CONFIG_H
|
||||
@ -22,19 +22,21 @@
|
||||
|
||||
// VDR's own version number:
|
||||
|
||||
#define VDRVERSION "2.6.4"
|
||||
#define VDRVERSNUM 20604 // Version * 10000 + Major * 100 + Minor
|
||||
#define VDRVERSION "2.7.4"
|
||||
#define VDRVERSNUM 20704 // Version * 10000 + Major * 100 + Minor
|
||||
|
||||
// The plugin API's version number:
|
||||
|
||||
#define APIVERSION "2.6.3"
|
||||
#define APIVERSNUM 20603 // Version * 10000 + Major * 100 + Minor
|
||||
#define APIVERSION "6"
|
||||
#define APIVERSNUM 30006
|
||||
|
||||
// When loading plugins, VDR searches them by their APIVERSION, which
|
||||
// may be smaller than VDRVERSION in case there have been no changes to
|
||||
// VDR header files since the last APIVERSION. This allows compiled
|
||||
// When loading plugins, VDR searches files by their APIVERSION, which
|
||||
// is different from VDRVERSION. APIVERSION is a plain number, incremented
|
||||
// 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
|
||||
// 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 MINPRIORITY (-MAXPRIORITY)
|
||||
@ -291,6 +293,8 @@ public:
|
||||
int SubtitleOffset;
|
||||
int SubtitleFgTransparency, SubtitleBgTransparency;
|
||||
int EPGLanguages[I18N_MAX_LANGUAGES + 1];
|
||||
int EPGScanMaxChannel;
|
||||
int EPGPauseAfterScan;
|
||||
int EPGScanTimeout;
|
||||
int EPGBugfixLevel;
|
||||
int EPGLinger;
|
||||
|
64
cutter.c
64
cutter.c
@ -4,12 +4,11 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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 "menu.h"
|
||||
#include "recording.h"
|
||||
#include "remux.h"
|
||||
#include "videodir.h"
|
||||
|
||||
@ -232,6 +231,10 @@ private:
|
||||
int numSequences;
|
||||
off_t maxVideoFileSize;
|
||||
off_t fileSize;
|
||||
int frameErrors;
|
||||
time_t lastErrorHandling;
|
||||
cString editedRecordingName;
|
||||
cRecordingInfo *recordingInfo;
|
||||
bool suspensionLogged;
|
||||
int sequence; // cutting sequence
|
||||
int delta; // time between two frames (PTS ticks)
|
||||
@ -246,7 +249,7 @@ private:
|
||||
cPatPmtParser patPmtParser;
|
||||
bool Throttled(void);
|
||||
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);
|
||||
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
|
||||
// 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.
|
||||
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
|
||||
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
||||
void HandleErrors(bool Force = false);
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cCuttingThread(const char *FromFileName, const char *ToFileName);
|
||||
cCuttingThread(const char *FromFileName, const char *ToFileName, cRecordingInfo *RecordingInfo);
|
||||
virtual ~cCuttingThread();
|
||||
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)
|
||||
{
|
||||
error = NULL;
|
||||
@ -274,6 +278,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||
framesPerSecond = Recording.FramesPerSecond();
|
||||
suspensionLogged = false;
|
||||
fileSize = 0;
|
||||
frameErrors = 0;
|
||||
lastErrorHandling = 0;
|
||||
editedRecordingName = ToFileName;
|
||||
recordingInfo = RecordingInfo;
|
||||
sequence = 0;
|
||||
delta = int(round(PTSTICKS / framesPerSecond));
|
||||
lastVidPts = -1;
|
||||
@ -294,6 +302,10 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
|
||||
if (isPesRecording && 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();
|
||||
}
|
||||
else
|
||||
@ -328,11 +340,11 @@ bool cCuttingThread::Throttled(void)
|
||||
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;
|
||||
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);
|
||||
if (fromFile) {
|
||||
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++) {
|
||||
bool Independent;
|
||||
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:
|
||||
AssertFreeDiskSpace(-1);
|
||||
bool CutIn = !SeamlessBegin && Index == BeginIndex;
|
||||
@ -572,10 +586,12 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
||||
return false;
|
||||
}
|
||||
// Write index:
|
||||
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
|
||||
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize, Errors, Missing)) {
|
||||
error = "toIndex";
|
||||
return false;
|
||||
}
|
||||
frameErrors += Errors + Missing;
|
||||
HandleErrors();
|
||||
// Write data:
|
||||
if (toFile->Write(Buffer, Length) < 0) {
|
||||
error = "safe_write";
|
||||
@ -596,6 +612,27 @@ bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIn
|
||||
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)
|
||||
{
|
||||
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
|
||||
@ -604,6 +641,7 @@ void cCuttingThread::Action(void)
|
||||
if (!fromFile || !toFile)
|
||||
return;
|
||||
int LastEndIndex = -1;
|
||||
HandleErrors(true); // to make sure an initially reset error count is displayed correctly
|
||||
while (BeginMark && Running()) {
|
||||
// Suspend cutting if we have severe throughput problems:
|
||||
if (Throttled()) {
|
||||
@ -634,6 +672,7 @@ void cCuttingThread::Action(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
HandleErrors(true);
|
||||
}
|
||||
else
|
||||
esyslog("no editing marks found!");
|
||||
@ -642,6 +681,7 @@ void cCuttingThread::Action(void)
|
||||
// --- cCutter ---------------------------------------------------------------
|
||||
|
||||
cCutter::cCutter(const char *FileName)
|
||||
:recordingInfo(FileName)
|
||||
{
|
||||
cuttingThread = NULL;
|
||||
error = false;
|
||||
@ -676,9 +716,11 @@ bool cCutter::Start(void)
|
||||
if (strcmp(originalVersionName, editedVersionName) != 0) { // names must be different!
|
||||
cRecordingUserCommand::InvokeCommand(RUC_EDITINGRECORDING, editedVersionName, originalVersionName);
|
||||
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));
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
4
cutter.h
4
cutter.h
@ -4,12 +4,13 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
#define __CUTTER_H
|
||||
|
||||
#include "recording.h"
|
||||
#include "thread.h"
|
||||
#include "tools.h"
|
||||
|
||||
@ -19,6 +20,7 @@ class cCutter {
|
||||
private:
|
||||
cString originalVersionName;
|
||||
cString editedVersionName;
|
||||
cRecordingInfo recordingInfo;
|
||||
cCuttingThread *cuttingThread;
|
||||
bool error;
|
||||
public:
|
||||
|
97
device.c
97
device.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: device.c 5.7 2023/02/16 14:53:38 kls Exp $
|
||||
* $Id: device.c 5.14 2024/07/06 11:19:21 kls Exp $
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
@ -95,7 +95,9 @@ cDevice::cDevice(void)
|
||||
|
||||
camSlot = NULL;
|
||||
|
||||
occupiedFrom = 0;
|
||||
occupiedTimeout = 0;
|
||||
occupiedPriority = MINPRIORITY;
|
||||
|
||||
player = NULL;
|
||||
isPlayingVideo = false;
|
||||
@ -284,8 +286,12 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
continue; // no CAM is able to decrypt this channel and the device uses vdr handled CAMs
|
||||
if (NumUsableSlots && !HasInternalCam && !CamSlots.Get(j)->Assign(device[i], true))
|
||||
continue; // CAM slot can't be used with this device
|
||||
bool ndr;
|
||||
if (device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job
|
||||
bool ndr = false;
|
||||
bool TunedToTransponder = device[i]->IsTunedToTransponder(Channel);
|
||||
if (TunedToTransponder || device[i]->ProvidesChannel(Channel, Priority, &ndr)) { // this device is basically able to do the job
|
||||
bool OccupiedOtherTransponder = !TunedToTransponder && device[i]->Occupied();
|
||||
if (OccupiedOtherTransponder)
|
||||
ndr = true;
|
||||
if (NumUsableSlots && !HasInternalCam) {
|
||||
if (cCamSlot *csi = device[i]->CamSlot()) {
|
||||
cCamSlot *csj = CamSlots.Get(j);
|
||||
@ -303,7 +309,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
imp <<= 1; imp |= (LiveView && NumUsableSlots && !HasInternalCam) ? !ChannelCamRelations.CamDecrypt(Channel->GetChannelID(), CamSlots.Get(j)->MasterSlotNumber()) || ndr : 0; // prefer CAMs that are known to decrypt this channel for live viewing, if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= LiveView ? !device[i]->IsPrimaryDevice() || ndr : 0; // prefer the primary device for live viewing if we don't need to detach existing receivers
|
||||
imp <<= 1; imp |= !device[i]->Receiving() && (device[i] != cTransferControl::ReceiverDevice() || device[i]->IsPrimaryDevice()) || ndr; // use receiving devices if we don't need to detach existing receivers, but avoid primary device in local transfer mode
|
||||
imp <<= 1; imp |= device[i]->Receiving(); // avoid devices that are receiving
|
||||
imp <<= 1; imp |= device[i]->Receiving() || OccupiedOtherTransponder; // avoid devices that are receiving
|
||||
imp <<= 5; imp |= GetClippedNumProvidedSystems(5, device[i]) - 1; // avoid cards which support multiple delivery systems
|
||||
imp <<= 8; imp |= device[i]->Priority() - IDLEPRIORITY; // use the device with the lowest priority (- IDLEPRIORITY to assure that values -100..99 can be used)
|
||||
imp <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
@ -321,6 +327,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
if (NumUsableSlots && !HasInternalCam)
|
||||
s = CamSlots.Get(j);
|
||||
}
|
||||
//dsyslog("device %d provides channel %d prio %d ndr %d imp %.8X", device[i]->DeviceNumber() + 1, Channel->Number(), Priority, ndr, imp);
|
||||
}
|
||||
}
|
||||
if (!NumUsableSlots)
|
||||
@ -427,7 +434,8 @@ cDevice *cDevice::GetDeviceForTransponder(const cChannel *Channel, int Priority)
|
||||
if (d->ProvidesTransponder(Channel)) {
|
||||
if (d->MaySwitchTransponder(Channel))
|
||||
return d; // this device may switch to the transponder without disturbing any receiver or live view
|
||||
else if (!d->Occupied() && !d->IsBonded()) { // MaySwitchTransponder() implicitly calls Occupied()
|
||||
else if (!d->Occupied(Priority) && !d->IsBonded() && d->Priority(true) < LIVEPRIORITY) { // MaySwitchTransponder() implicitly calls Occupied()
|
||||
// we select only devices with priority < LIVEPRIORITY, so device can be switched without impact on recordings or live viewing
|
||||
if (d->Priority() < Priority && (!Device || d->Priority() < Device->Priority()))
|
||||
Device = d; // use this one only if no other with less impact can be found
|
||||
}
|
||||
@ -673,11 +681,11 @@ void cDevice::StartSectionHandler(void)
|
||||
void cDevice::StopSectionHandler(void)
|
||||
{
|
||||
if (sectionHandler) {
|
||||
delete sectionHandler; // automatically detaches filters
|
||||
delete nitFilter;
|
||||
delete sdtFilter;
|
||||
delete patFilter;
|
||||
delete eitFilter;
|
||||
delete sectionHandler;
|
||||
nitFilter = NULL;
|
||||
sdtFilter = NULL;
|
||||
patFilter = NULL;
|
||||
@ -801,7 +809,17 @@ bool cDevice::IsTunedToTransponder(const cChannel *Channel) const
|
||||
|
||||
bool cDevice::MaySwitchTransponder(const cChannel *Channel) const
|
||||
{
|
||||
return time(NULL) > occupiedTimeout && !Receiving() && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);
|
||||
return !Occupied() && !Receiving() && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid);
|
||||
}
|
||||
|
||||
void cDevice::SetPowerSaveMode(bool On)
|
||||
{
|
||||
}
|
||||
|
||||
void cDevice::SetPowerSaveIfUnused(void)
|
||||
{
|
||||
if (!Occupied() && !Receiving() && !(pidHandles[ptAudio].pid || pidHandles[ptVideo].pid || pidHandles[ptDolby].pid))
|
||||
SetPowerSaveMode(true);
|
||||
}
|
||||
|
||||
bool cDevice::SwitchChannel(const cChannel *Channel, bool LiveView)
|
||||
@ -876,7 +894,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
DELETENULL(dvbSubtitleConverter);
|
||||
}
|
||||
|
||||
cDevice *Device = (LiveView && IsPrimaryDevice()) ? GetDevice(Channel, LIVEPRIORITY, true) : this;
|
||||
cDevice *Device = (LiveView && IsPrimaryDevice(false)) ? GetDevice(Channel, LIVEPRIORITY, true) : this;
|
||||
|
||||
bool NeedsTransferMode = LiveView && Device != PrimaryDevice();
|
||||
// If the CAM slot wants the TS data, we need to switch to Transfer Mode:
|
||||
@ -908,6 +926,7 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
// channel to it, for possible later decryption:
|
||||
if (camSlot)
|
||||
camSlot->AddChannel(Channel);
|
||||
SetPowerSaveMode(false);
|
||||
if (SetChannelDevice(Channel, LiveView)) {
|
||||
// Start section handling:
|
||||
if (sectionHandler) {
|
||||
@ -923,23 +942,26 @@ eSetChannelResult cDevice::SetChannel(const cChannel *Channel, bool LiveView)
|
||||
}
|
||||
|
||||
if (Result == scrOk) {
|
||||
if (LiveView && IsPrimaryDevice()) {
|
||||
if (patFilter) // this is only for FF DVB cards!
|
||||
patFilter->Request(Channel->Sid());
|
||||
currentChannel = Channel->Number();
|
||||
// Set the available audio tracks:
|
||||
ClrAvailableTracks();
|
||||
for (int i = 0; i < MAXAPIDS; i++)
|
||||
SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
|
||||
if (Setup.UseDolbyDigital) {
|
||||
for (int i = 0; i < MAXDPIDS; i++)
|
||||
SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
|
||||
if (LiveView) {
|
||||
if (IsPrimaryDevice(false))
|
||||
currentChannel = Channel->Number();
|
||||
if (IsPrimaryDevice()) {
|
||||
if (patFilter) // this is only for FF DVB cards!
|
||||
patFilter->Request(Channel->Sid());
|
||||
// Set the available audio tracks:
|
||||
ClrAvailableTracks();
|
||||
for (int i = 0; i < MAXAPIDS; i++)
|
||||
SetAvailableTrack(ttAudio, i, Channel->Apid(i), Channel->Alang(i));
|
||||
if (Setup.UseDolbyDigital) {
|
||||
for (int i = 0; i < MAXDPIDS; i++)
|
||||
SetAvailableTrack(ttDolby, i, Channel->Dpid(i), Channel->Dlang(i));
|
||||
}
|
||||
for (int i = 0; i < MAXSPIDS; i++)
|
||||
SetAvailableTrack(ttSubtitle, i, Channel->Spid(i), Channel->Slang(i));
|
||||
if (!NeedsTransferMode)
|
||||
EnsureAudioTrack(true);
|
||||
EnsureSubtitleTrack();
|
||||
}
|
||||
for (int i = 0; i < MAXSPIDS; i++)
|
||||
SetAvailableTrack(ttSubtitle, i, Channel->Spid(i), Channel->Slang(i));
|
||||
if (!NeedsTransferMode)
|
||||
EnsureAudioTrack(true);
|
||||
EnsureSubtitleTrack();
|
||||
}
|
||||
cStatus::MsgChannelSwitch(this, Channel->Number(), LiveView); // only report status if channel switch successful
|
||||
}
|
||||
@ -951,21 +973,34 @@ void cDevice::ForceTransferMode(void)
|
||||
{
|
||||
if (!cTransferControl::ReceiverDevice()) {
|
||||
LOCK_CHANNELS_READ;
|
||||
if (const cChannel *Channel = Channels->GetByNumber(CurrentChannel()))
|
||||
if (const cChannel *Channel = Channels->GetByNumber(CurrentChannel())) {
|
||||
SetPowerSaveMode(false);
|
||||
SetChannelDevice(Channel, false); // this implicitly starts Transfer Mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int cDevice::Occupied(void) const
|
||||
int cDevice::Occupied(int Priority) const
|
||||
{
|
||||
if (Priority > occupiedPriority)
|
||||
return 0;
|
||||
int Seconds = occupiedTimeout - time(NULL);
|
||||
return Seconds > 0 ? Seconds : 0;
|
||||
}
|
||||
|
||||
void cDevice::SetOccupied(int Seconds)
|
||||
void cDevice::SetOccupied(int Seconds, int Priority, time_t From)
|
||||
{
|
||||
if (Seconds >= 0)
|
||||
occupiedTimeout = time(NULL) + min(Seconds, MAXOCCUPIEDTIMEOUT);
|
||||
if (Seconds < 0)
|
||||
return;
|
||||
if (From == 0)
|
||||
From = time(NULL);
|
||||
if (From == occupiedFrom)
|
||||
occupiedPriority = max(Priority, occupiedPriority);
|
||||
else {
|
||||
occupiedPriority = Priority;
|
||||
occupiedFrom = From;
|
||||
}
|
||||
occupiedTimeout = From + min(Seconds, MAXOCCUPIEDTIMEOUT);
|
||||
}
|
||||
|
||||
bool cDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
||||
@ -1657,11 +1692,13 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
|
||||
return Played;
|
||||
}
|
||||
|
||||
int cDevice::Priority(void) const
|
||||
int cDevice::Priority(bool IgnoreOccupied) const
|
||||
{
|
||||
int priority = IDLEPRIORITY;
|
||||
if (IsPrimaryDevice() && !Replaying() && HasProgramme())
|
||||
priority = TRANSFERPRIORITY; // we use the same value here, no matter whether it's actual Transfer Mode or real live viewing
|
||||
if (!IgnoreOccupied && time(NULL) <= occupiedTimeout && occupiedPriority > priority)
|
||||
priority = occupiedPriority - 1; // so timers with occupiedPriority can start
|
||||
cMutexLock MutexLock(&mutexReceiver);
|
||||
for (int i = 0; i < MAXRECEIVERS; i++) {
|
||||
if (receiver[i])
|
||||
|
37
device.h
37
device.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: device.h 5.2 2022/01/24 16:53:45 kls Exp $
|
||||
* $Id: device.h 5.6 2024/07/15 14:42:22 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DEVICE_H
|
||||
@ -178,6 +178,9 @@ public:
|
||||
///< the transponder of the given Channel, without disturbing any receiver
|
||||
///< at priorities higher or equal to Priority.
|
||||
///< If no such device is currently available, NULL will be returned.
|
||||
///< Devices recording (Device->Priority(true) >= LIVEPRIORITY) will not be returned,
|
||||
///< even if Priority >= LIVEPRIORITY. Such higher priorities are only used to
|
||||
///< override occupied.
|
||||
static void Shutdown(void);
|
||||
///< Closes down all devices.
|
||||
///< Must be called at the end of the program.
|
||||
@ -217,7 +220,7 @@ protected:
|
||||
///< May be dropped in a future version, if a better solution is found.
|
||||
///< Do not use otherwise!
|
||||
public:
|
||||
bool IsPrimaryDevice(void) const { return this == primaryDevice && HasDecoder(); }
|
||||
bool IsPrimaryDevice(bool CheckDecoder = true) const { return this == primaryDevice && (!CheckDecoder || HasDecoder()); }
|
||||
int CardIndex(void) const { return cardIndex; }
|
||||
///< Returns the card index of this device (0 ... MAXDEVICES - 1).
|
||||
int DeviceNumber(void) const;
|
||||
@ -259,7 +262,9 @@ public:
|
||||
|
||||
private:
|
||||
mutable cMutex mutexChannel;
|
||||
time_t occupiedFrom;
|
||||
time_t occupiedTimeout;
|
||||
int occupiedPriority;
|
||||
protected:
|
||||
static int currentChannel;
|
||||
public:
|
||||
@ -341,6 +346,14 @@ public:
|
||||
///< device, without disturbing any other activities. If an occupied timeout
|
||||
///< has been set for this device, and that timeout has not yet expired,
|
||||
///< this function returns false.
|
||||
virtual void SetPowerSaveMode(bool On);
|
||||
///< Puts the device into power save mode, if applicable.
|
||||
///< If On is true, power save mode shall be activated, if it is false,
|
||||
///< normal operating mode shall be restored.
|
||||
///< The default implementation does nothing.
|
||||
void SetPowerSaveIfUnused(void);
|
||||
///< Sets this device into a power save mode if it is not currently used and
|
||||
///< has implemented SetPowerSaveMode().
|
||||
bool SwitchChannel(const cChannel *Channel, bool LiveView);
|
||||
///< Switches the device to the given Channel, initiating transfer mode
|
||||
///< if necessary.
|
||||
@ -357,21 +370,16 @@ protected:
|
||||
public:
|
||||
static int CurrentChannel(void) { return primaryDevice ? currentChannel : 0; }
|
||||
///< Returns the number of the current channel on the primary device.
|
||||
#ifndef DEPRECATED_SETCURRENTCHANNEL
|
||||
#define DEPRECATED_SETCURRENTCHANNEL 0
|
||||
#endif
|
||||
#if DEPRECATED_SETCURRENTCHANNEL
|
||||
static void SetCurrentChannel(const cChannel *Channel) { currentChannel = Channel ? Channel->Number() : 0; }
|
||||
#endif
|
||||
static void SetCurrentChannel(int ChannelNumber) { currentChannel = ChannelNumber; }
|
||||
///< Sets the number of the current channel on the primary device, without
|
||||
///< actually switching to it. This can be used to correct the current
|
||||
///< channel number while replaying.
|
||||
void ForceTransferMode(void);
|
||||
///< Forces the device into transfermode for the current channel.
|
||||
int Occupied(void) const;
|
||||
///< Returns the number of seconds this device is still occupied for.
|
||||
void SetOccupied(int Seconds);
|
||||
int Occupied(int Priority = MINPRIORITY) const;
|
||||
///< Returns the number of seconds this device is still occupied for
|
||||
///< with a priority >= Priority.
|
||||
void SetOccupied(int Seconds, int Priority = MINPRIORITY, time_t From = 0);
|
||||
///< Sets the occupied timeout for this device to the given number of
|
||||
///< Seconds, This can be used to tune a device to a particular transponder
|
||||
///< and make sure it will stay there for a certain amount of time, for
|
||||
@ -379,6 +387,10 @@ public:
|
||||
///< after the device has been successfully tuned to the requested transponder.
|
||||
///< Seconds will be silently limited to MAXOCCUPIEDTIMEOUT. Values less than
|
||||
///< 0 will be silently ignored.
|
||||
///< The timeout is counted from the given From time (by default the current time).
|
||||
///< Calling this function several times with the same From time will set the
|
||||
///< priority to the maximum of the given values.
|
||||
///< Priority() may return a value >= Priority until the timeout.
|
||||
virtual bool HasLock(int TimeoutMs = 0) const;
|
||||
///< Returns true if the device has a lock on the requested transponder.
|
||||
///< Default is true, a specific device implementation may return false
|
||||
@ -833,9 +845,10 @@ private:
|
||||
mutable cMutex mutexReceiver;
|
||||
cReceiver *receiver[MAXRECEIVERS];
|
||||
public:
|
||||
int Priority(void) const;
|
||||
int Priority(bool IgnoreOccupied = false) const;
|
||||
///< Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),
|
||||
///< or IDLEPRIORITY if no receiver is currently active.
|
||||
///< If IgnoreOccupied is true, a priority set with SetOccupied() is ignored.
|
||||
protected:
|
||||
virtual bool OpenDvr(void);
|
||||
///< Opens the DVR of this device and prepares it to deliver a Transport
|
||||
|
55
dvbdevice.c
55
dvbdevice.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbdevice.c 5.3 2022/12/05 14:04:10 kls Exp $
|
||||
* $Id: dvbdevice.c 5.8 2024/09/09 08:53:57 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbdevice.h"
|
||||
@ -394,7 +394,7 @@ void cDvbFrontend::Close(void)
|
||||
{
|
||||
if (fd_frontend >= 0) {
|
||||
if (close(fd_frontend) != 0)
|
||||
esyslog("ERROR: frontend %d/%d", adapter, frontend);
|
||||
esyslog("ERROR: frontend %d/%d: %m (%s:%d)", adapter, frontend, __FILE__, __LINE__);
|
||||
fd_frontend = -1;
|
||||
}
|
||||
}
|
||||
@ -549,12 +549,12 @@ private:
|
||||
mutable uint32_t lastUncDelta;
|
||||
mutable time_t lastUncChange;
|
||||
cChannel channel;
|
||||
const cDiseqc *lastDiseqc;
|
||||
mutable const cDiseqc *lastDiseqc;
|
||||
int diseqcOffset;
|
||||
int lastSource;
|
||||
mutable int lastSource;
|
||||
cPositioner *positioner;
|
||||
const cScr *scr;
|
||||
bool lnbPowerTurnedOn;
|
||||
mutable bool lnbPowerTurnedOn;
|
||||
eTunerStatus tunerStatus;
|
||||
mutable cMutex mutex;
|
||||
cCondVar locked;
|
||||
@ -593,6 +593,7 @@ public:
|
||||
bool GetSignalStats(int &Valid, double *Strength = NULL, double *Cnr = NULL, double *BerPre = NULL, double *BerPost = NULL, double *Per = NULL, int *Status = NULL) const;
|
||||
int GetSignalStrength(void) const;
|
||||
int GetSignalQuality(void) const;
|
||||
void SetPowerSaveMode(bool On);
|
||||
};
|
||||
|
||||
cMutex cDvbTuner::bondMutex;
|
||||
@ -696,9 +697,12 @@ bool cDvbTuner::ProvidesFrontend(const cChannel *Channel, bool Activate) const
|
||||
fd_frontend = dvbFrontend->Open();
|
||||
frontend = i;
|
||||
dsyslog("using frontend %d/%d", adapter, frontend);
|
||||
lastDiseqc = NULL;
|
||||
lastSource = 0;
|
||||
lastUncValue = 0;
|
||||
lastUncDelta = 0;
|
||||
lastUncChange = 0;
|
||||
lnbPowerTurnedOn = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -859,6 +863,8 @@ void cDvbTuner::ClearEventQueue(void) const
|
||||
|
||||
bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
|
||||
{
|
||||
if (fd_frontend == -1)
|
||||
return false;
|
||||
ClearEventQueue();
|
||||
Status = (fe_status_t)0; // initialize here to fix buggy drivers
|
||||
while (1) {
|
||||
@ -876,6 +882,8 @@ bool cDvbTuner::GetFrontendStatus(fe_status_t &Status) const
|
||||
|
||||
bool cDvbTuner::GetSignalStats(int &Valid, double *Strength, double *Cnr, double *BerPre, double *BerPost, double *Per, int *Status) const
|
||||
{
|
||||
if (fd_frontend == -1)
|
||||
return false;
|
||||
ClearEventQueue();
|
||||
fe_status_t FeStatus = (fe_status_t)0; // initialize here to fix buggy drivers
|
||||
dtv_property Props[MAXFRONTENDCMDS];
|
||||
@ -1226,6 +1234,8 @@ int SignalToSQI(const cChannel *Channel, int Signal, int Ber, int FeModulation,
|
||||
|
||||
int cDvbTuner::GetSignalStrength(void) const
|
||||
{
|
||||
if (fd_frontend == -1)
|
||||
return 0;
|
||||
ClearEventQueue();
|
||||
// Try DVB API 5:
|
||||
for (int i = 0; i < 1; i++) { // just a trick to break out with 'continue' ;-)
|
||||
@ -1291,6 +1301,8 @@ int cDvbTuner::GetSignalStrength(void) const
|
||||
|
||||
int cDvbTuner::GetSignalQuality(void) const
|
||||
{
|
||||
if (fd_frontend == -1)
|
||||
return 0;
|
||||
// Try DVB API 5:
|
||||
for (int i = 0; i < 1; i++) { // just a trick to break out with 'continue' ;-)
|
||||
dtv_property Props[MAXFRONTENDCMDS];
|
||||
@ -1536,6 +1548,8 @@ void cDvbTuner::ExecuteDiseqc(const cDiseqc *Diseqc, int *Frequency)
|
||||
|
||||
void cDvbTuner::ResetToneAndVoltage(void)
|
||||
{
|
||||
if (fd_frontend == -1)
|
||||
return;
|
||||
CHECK(ioctl(fd_frontend, FE_SET_VOLTAGE, bondedTuner ? SEC_VOLTAGE_OFF : SEC_VOLTAGE_13));
|
||||
CHECK(ioctl(fd_frontend, FE_SET_TONE, SEC_TONE_OFF));
|
||||
}
|
||||
@ -1768,6 +1782,32 @@ void cDvbTuner::Action(void)
|
||||
}
|
||||
}
|
||||
|
||||
void cDvbTuner::SetPowerSaveMode(bool On)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
if (On) {
|
||||
if (fd_frontend != -1) {
|
||||
dsyslog("closing frontend %d/%d", adapter, frontend);
|
||||
tunerStatus = tsIdle;
|
||||
dvbFrontend->Close();
|
||||
fd_frontend = -1;
|
||||
channel = cChannel();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (fd_frontend == -1) {
|
||||
dsyslog("opening frontend %d/%d", adapter, frontend);
|
||||
fd_frontend = dvbFrontend->Open();
|
||||
lastDiseqc = NULL;
|
||||
lastSource = 0;
|
||||
lastUncValue = 0;
|
||||
lastUncDelta = 0;
|
||||
lastUncChange = 0;
|
||||
lnbPowerTurnedOn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- cDvbSourceParam -------------------------------------------------------
|
||||
|
||||
class cDvbSourceParam : public cSourceParam {
|
||||
@ -2300,6 +2340,11 @@ bool cDvbDevice::MaySwitchTransponder(const cChannel *Channel) const
|
||||
return BondingOk(Channel, true) && cDevice::MaySwitchTransponder(Channel);
|
||||
}
|
||||
|
||||
void cDvbDevice::SetPowerSaveMode(bool On)
|
||||
{
|
||||
dvbTuner->SetPowerSaveMode(On);
|
||||
}
|
||||
|
||||
bool cDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
|
||||
{
|
||||
if (dvbTuner->ProvidesFrontend(Channel, true)) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: dvbdevice.h 4.7 2020/06/27 10:24:46 kls Exp $
|
||||
* $Id: dvbdevice.h 5.1 2024/07/08 09:34:33 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __DVBDEVICE_H
|
||||
@ -244,6 +244,7 @@ public:
|
||||
virtual const cChannel *GetCurrentlyTunedTransponder(void) const;
|
||||
virtual bool IsTunedToTransponder(const cChannel *Channel) const;
|
||||
virtual bool MaySwitchTransponder(const cChannel *Channel) const;
|
||||
virtual void SetPowerSaveMode(bool On);
|
||||
protected:
|
||||
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
|
||||
public:
|
||||
|
48
dvbplayer.c
48
dvbplayer.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -36,7 +36,7 @@ public:
|
||||
bool IsEmpty(void);
|
||||
void Put(uint32_t Pts, int Index, bool Independent);
|
||||
int FindIndex(uint32_t Pts);
|
||||
int FindFrameNumber(uint32_t Pts);
|
||||
int FindFrameNumber(uint32_t Pts, bool Forward);
|
||||
};
|
||||
|
||||
cPtsIndex::cPtsIndex(void)
|
||||
@ -90,30 +90,30 @@ int cPtsIndex::FindIndex(uint32_t Pts)
|
||||
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);
|
||||
if (w == r)
|
||||
return lastFound; // replay always starts at an I frame
|
||||
bool Valid = false;
|
||||
int d;
|
||||
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; ) {
|
||||
d = Pts - pi[i].pts;
|
||||
if (d > 0x7FFFFFFF)
|
||||
d = 0xFFFFFFFF - d; // handle rollover
|
||||
if (d > 0) {
|
||||
int32_t d = int32_t(Pts - pi[i].pts); // typecast handles rollover
|
||||
if (d >= 0) {
|
||||
if (pi[i].independent) {
|
||||
FrameNumber = pi[i].index; // an I frame's index represents its frame number
|
||||
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
|
||||
FrameNumber++; // for every played non-I frame, increase frame number
|
||||
}
|
||||
else
|
||||
if (pi[i].independent)
|
||||
--UnplayedIFrame;
|
||||
else if (pi[i].independent)
|
||||
--UnplayedIFrame;
|
||||
if (++i >= PTSINDEX_ENTRIES)
|
||||
i = 0;
|
||||
}
|
||||
@ -283,6 +283,7 @@ public:
|
||||
void Goto(int Position, bool Still = false);
|
||||
virtual double FramesPerSecond(void) { return framesPerSecond; }
|
||||
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 GetFrameNumber(int &Current, int &Total);
|
||||
virtual bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
|
||||
@ -792,15 +793,17 @@ void cDvbPlayer::Forward(void)
|
||||
Pause();
|
||||
break;
|
||||
}
|
||||
Empty();
|
||||
// run into pmPause
|
||||
case pmStill:
|
||||
case pmPause:
|
||||
case pmPause: {
|
||||
LOCK_THREAD;
|
||||
Empty();
|
||||
DeviceMute();
|
||||
playMode = pmSlow;
|
||||
playDir = pdForward;
|
||||
trickSpeed = NORMAL_SPEED;
|
||||
TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
|
||||
}
|
||||
break;
|
||||
default: esyslog("ERROR: unknown playMode %d (%s)", playMode, __FUNCTION__);
|
||||
}
|
||||
@ -841,7 +844,6 @@ void cDvbPlayer::Backward(void)
|
||||
Pause();
|
||||
break;
|
||||
}
|
||||
Empty();
|
||||
// run into pmPause
|
||||
case pmStill:
|
||||
case pmPause: {
|
||||
@ -940,6 +942,13 @@ void cDvbPlayer::SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
|
||||
resyncAfterPause = true;
|
||||
}
|
||||
|
||||
const cErrors *cDvbPlayer::GetErrors(void)
|
||||
{
|
||||
if (index)
|
||||
return index->GetErrors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
{
|
||||
if (index) {
|
||||
@ -959,7 +968,7 @@ bool cDvbPlayer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
bool cDvbPlayer::GetFrameNumber(int &Current, int &Total)
|
||||
{
|
||||
if (index) {
|
||||
Current = ptsIndex.FindFrameNumber(DeviceGetSTC());
|
||||
Current = ptsIndex.FindFrameNumber(DeviceGetSTC(), playDir == pdForward);
|
||||
Total = index->Last();
|
||||
return true;
|
||||
}
|
||||
@ -1047,6 +1056,13 @@ int cDvbPlayerControl::SkipFrames(int Frames)
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cErrors *cDvbPlayerControl::GetErrors(void)
|
||||
{
|
||||
if (player)
|
||||
return player->GetErrors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool cDvbPlayerControl::GetIndex(int &Current, int &Total, bool SnapToIFrame)
|
||||
{
|
||||
if (player) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -47,6 +47,8 @@ public:
|
||||
// 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
|
||||
// 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);
|
||||
// Returns the current and total frame index, optionally snapped to the
|
||||
// nearest I-frame.
|
||||
|
62
eitscan.c
62
eitscan.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: eitscan.c 4.3 2019/03/12 11:46:05 kls Exp $
|
||||
* $Id: eitscan.c 5.7 2024/07/13 20:12:24 kls Exp $
|
||||
*/
|
||||
|
||||
#include "eitscan.h"
|
||||
@ -44,11 +44,23 @@ int cScanData::Compare(const cListObject &ListObject) const
|
||||
// --- cScanList -------------------------------------------------------------
|
||||
|
||||
class cScanList : public cList<cScanData> {
|
||||
private:
|
||||
bool HasDeviceForChannelEIT(const cChannel *Channel) const;
|
||||
public:
|
||||
void AddTransponders(const cList<cChannel> *Channels);
|
||||
void AddTransponder(const cChannel *Channel);
|
||||
};
|
||||
|
||||
bool cScanList::HasDeviceForChannelEIT(const cChannel *Channel) const
|
||||
{
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
cDevice *Device = cDevice::GetDevice(i);
|
||||
if (Device && Device->ProvidesEIT() && Device->ProvidesTransponder(Channel))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void cScanList::AddTransponders(const cList<cChannel> *Channels)
|
||||
{
|
||||
for (const cChannel *ch = Channels->First(); ch; ch = Channels->Next(ch))
|
||||
@ -58,7 +70,9 @@ void cScanList::AddTransponders(const cList<cChannel> *Channels)
|
||||
|
||||
void cScanList::AddTransponder(const cChannel *Channel)
|
||||
{
|
||||
if (Channel->Source() && Channel->Transponder()) {
|
||||
if (Channel->Source() && Channel->Transponder() && (Setup.EPGScanMaxChannel <= 0 || Channel->Number() < Setup.EPGScanMaxChannel)) {
|
||||
if (!HasDeviceForChannelEIT(Channel))
|
||||
return;
|
||||
for (cScanData *sd = First(); sd; sd = Next(sd)) {
|
||||
if (sd->Source() == Channel->Source() && ISTRANSPONDER(sd->Transponder(), Channel->Transponder()))
|
||||
return;
|
||||
@ -91,9 +105,11 @@ cEITScanner EITScanner;
|
||||
|
||||
cEITScanner::cEITScanner(void)
|
||||
{
|
||||
lastScan = lastActivity = time(NULL);
|
||||
paused = false;
|
||||
lastScan = 0;
|
||||
lastActivity = time(NULL);
|
||||
currentChannel = 0;
|
||||
scanList = NULL;
|
||||
scanList = new cScanList;
|
||||
transponderList = NULL;
|
||||
}
|
||||
|
||||
@ -130,25 +146,46 @@ void cEITScanner::Process(void)
|
||||
if (Setup.EPGScanTimeout || !lastActivity) { // !lastActivity means a scan was forced
|
||||
time_t now = time(NULL);
|
||||
if (now - lastScan > ScanTimeout && now - lastActivity > ActivityTimeout) {
|
||||
if (Setup.EPGPauseAfterScan && scanList->Count() == 0 && lastActivity && lastScan && now - lastScan < Setup.EPGScanTimeout * 3600) {
|
||||
if (!paused) {
|
||||
dsyslog("pause EPG scan");
|
||||
paused = true;
|
||||
}
|
||||
// Allow unused devices to go into power save mode:
|
||||
if ((now - lastScan) % 10 == 0) { // let's not do this too often
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
if (cDevice *Device = cDevice::GetDevice(i))
|
||||
Device->SetPowerSaveIfUnused();
|
||||
}
|
||||
}
|
||||
return; // pause for Setup.EPGScanTimeout hours
|
||||
}
|
||||
else if (paused) {
|
||||
dsyslog("start EPG scan");
|
||||
paused = false;
|
||||
}
|
||||
cStateKey StateKey;
|
||||
if (const cChannels *Channels = cChannels::GetChannelsRead(StateKey, 10)) {
|
||||
if (!scanList) {
|
||||
scanList = new cScanList;
|
||||
if (scanList->Count() == 0) {
|
||||
if (transponderList) {
|
||||
scanList->AddTransponders(transponderList);
|
||||
delete transponderList;
|
||||
transponderList = NULL;
|
||||
}
|
||||
scanList->AddTransponders(Channels);
|
||||
//dsyslog("EIT scan: %d scanList entries", scanList->Count());
|
||||
}
|
||||
bool AnyDeviceSwitched = false;
|
||||
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
||||
cDevice *Device = cDevice::GetDevice(i);
|
||||
if (Device && Device->ProvidesEIT()) {
|
||||
for (cScanData *ScanData = scanList->First(); ScanData; ScanData = scanList->Next(ScanData)) {
|
||||
cScanData *Next = NULL;
|
||||
for (cScanData *ScanData = scanList->First(); ScanData; ScanData = Next) {
|
||||
Next = scanList->Next(ScanData);
|
||||
const cChannel *Channel = ScanData->GetChannel();
|
||||
if (Channel) {
|
||||
if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
if (Device->IsTunedToTransponder(Channel))
|
||||
scanList->Del(ScanData);
|
||||
else if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
if (Device->ProvidesTransponder(Channel)) {
|
||||
if (Device->Priority() < 0) {
|
||||
if (const cPositioner *Positioner = Device->Positioner()) {
|
||||
@ -164,10 +201,9 @@ void cEITScanner::Process(void)
|
||||
Skins.Message(mtInfo, tr("Starting EPG scan"));
|
||||
}
|
||||
}
|
||||
//dsyslog("EIT scan: device %d source %-8s tp %5d", Device->DeviceNumber() + 1, *cSource::ToString(Channel->Source()), Channel->Transponder());
|
||||
//dsyslog("EIT scan: %d device %d source %-8s tp %5d", scanList->Count(), Device->DeviceNumber() + 1, *cSource::ToString(Channel->Source()), Channel->Transponder());
|
||||
Device->SwitchChannel(Channel, false);
|
||||
scanList->Del(ScanData);
|
||||
AnyDeviceSwitched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -177,9 +213,7 @@ void cEITScanner::Process(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!AnyDeviceSwitched) {
|
||||
delete scanList;
|
||||
scanList = NULL;
|
||||
if (scanList->Count() == 0) {
|
||||
if (lastActivity == 0) // this was a triggered scan
|
||||
Activity();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: eitscan.h 2.1 2012/03/07 14:16:57 kls Exp $
|
||||
* $Id: eitscan.h 5.1 2024/07/06 11:19:21 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __EITSCAN_H
|
||||
@ -23,6 +23,7 @@ private:
|
||||
enum { ActivityTimeout = 60,
|
||||
ScanTimeout = 20
|
||||
};
|
||||
bool paused;
|
||||
time_t lastScan, lastActivity;
|
||||
int currentChannel;
|
||||
cScanList *scanList;
|
||||
|
83
epg.c
83
epg.c
@ -7,7 +7,7 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.c 5.7 2022/11/22 14:33:48 kls Exp $
|
||||
* $Id: epg.c 5.13 2024/11/30 14:30:46 kls Exp $
|
||||
*/
|
||||
|
||||
#include "epg.h"
|
||||
@ -145,7 +145,10 @@ cEvent::~cEvent()
|
||||
int cEvent::Compare(const cListObject &ListObject) const
|
||||
{
|
||||
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
|
||||
@ -177,7 +180,7 @@ void cEvent::SetVersion(uchar Version)
|
||||
void cEvent::SetRunningStatus(int RunningStatus, const cChannel *Channel)
|
||||
{
|
||||
if (Channel && runningStatus != RunningStatus && (RunningStatus > SI::RunningStatusNotRunning || runningStatus > SI::RunningStatusUndefined) && schedule && schedule->HasTimer())
|
||||
isyslog("channel %d (%s) event %s status %d", Channel->Number(), Channel->Name(), *ToDescr(), RunningStatus);
|
||||
isyslog("channel %d (%s) event %s status %d->%d", Channel->Number(), Channel->Name(), *ToDescr(), runningStatus, RunningStatus);
|
||||
runningStatus = RunningStatus;
|
||||
}
|
||||
|
||||
@ -300,7 +303,7 @@ const char *cEvent::ContentToString(uchar Content)
|
||||
case 0x01: return tr("Content$News/Weather Report");
|
||||
case 0x02: return tr("Content$News Magazine");
|
||||
case 0x03: return tr("Content$Documentary");
|
||||
case 0x04: return tr("Content$Discussion/Inverview/Debate");
|
||||
case 0x04: return tr("Content$Discussion/Interview/Debate");
|
||||
}
|
||||
break;
|
||||
case ecgShow:
|
||||
@ -346,7 +349,7 @@ const char *cEvent::ContentToString(uchar Content)
|
||||
case 0x00: return tr("Content$Music/Ballet/Dance");
|
||||
case 0x01: return tr("Content$Rock/Pop");
|
||||
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 0x05: return tr("Content$Musical/Opera");
|
||||
case 0x06: return tr("Content$Ballet");
|
||||
@ -885,6 +888,23 @@ void cEvent::FixEpgBugs(void)
|
||||
|
||||
Final:
|
||||
|
||||
// And then there are the specially gifted people who put a literal "\n" string where there should be
|
||||
// a '\n' character:
|
||||
if (shortText) {
|
||||
if (char *p = strstr(shortText, "\\n")) {
|
||||
*p = 0;
|
||||
p += 2;
|
||||
char *s = strdup(shortText);
|
||||
char *d = strdup(cString::sprintf("%s\n\n%s", p, description));
|
||||
free(shortText);
|
||||
free(description);
|
||||
shortText = s;
|
||||
description = d;
|
||||
EpgBugFixStat(12, ChannelID());
|
||||
}
|
||||
}
|
||||
description = strreplace(description, "\\n", " \n");
|
||||
|
||||
// VDR can't usefully handle newline characters in the title, shortText or component description of EPG
|
||||
// data, so let's always convert them to blanks (independent of the setting of EPGBugfixLevel):
|
||||
strreplace(title, '\n', ' ');
|
||||
@ -911,7 +931,6 @@ cSchedule::cSchedule(tChannelID ChannelID)
|
||||
channelID = ChannelID;
|
||||
events.SetUseGarbageCollector();
|
||||
numTimers = 0;
|
||||
hasRunning = false;
|
||||
modified = 0;
|
||||
onActualTp = false;
|
||||
presentSeen = 0;
|
||||
@ -1011,18 +1030,6 @@ const cEvent *cSchedule::GetFollowingEvent(void) const
|
||||
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
|
||||
{
|
||||
return eventsHashID.Get(EventID);
|
||||
@ -1051,7 +1058,6 @@ const cEvent *cSchedule::GetEventAround(time_t Time) const
|
||||
|
||||
void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChannel *Channel)
|
||||
{
|
||||
hasRunning = false;
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p == Event) {
|
||||
if (p->RunningStatus() > SI::RunningStatusNotRunning || RunningStatus > SI::RunningStatusNotRunning) {
|
||||
@ -1061,24 +1067,19 @@ void cSchedule::SetRunningStatus(cEvent *Event, int RunningStatus, const cChanne
|
||||
}
|
||||
else if (RunningStatus >= SI::RunningStatusPausing && p->StartTime() < Event->StartTime())
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing)
|
||||
hasRunning = true;
|
||||
}
|
||||
SetPresentSeen();
|
||||
}
|
||||
|
||||
void cSchedule::ClrRunningStatus(cChannel *Channel)
|
||||
{
|
||||
if (hasRunning) {
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing) {
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
hasRunning = false;
|
||||
SetModified();
|
||||
break;
|
||||
}
|
||||
for (cEvent *p = events.First(); p; p = events.Next(p)) {
|
||||
if (p->RunningStatus() >= SI::RunningStatusPausing) {
|
||||
p->SetRunningStatus(SI::RunningStatusNotRunning, Channel);
|
||||
SetModified();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cSchedule::ResetVersions(void)
|
||||
@ -1090,14 +1091,6 @@ void cSchedule::ResetVersions(void)
|
||||
void cSchedule::Sort(void)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1124,6 +1117,20 @@ void cSchedule::DropOutdated(time_t SegmentStart, time_t SegmentEnd, uchar Table
|
||||
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)
|
||||
|
7
epg.h
7
epg.h
@ -7,7 +7,7 @@
|
||||
* Original version (as used in VDR before 1.3.0) written by
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
*
|
||||
* $Id: epg.h 5.3 2022/12/24 11:37:17 kls Exp $
|
||||
* $Id: epg.h 5.8 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __EPG_H
|
||||
@ -157,7 +157,6 @@ private:
|
||||
cHash<cEvent> eventsHashID;
|
||||
cHash<cEvent> eventsHashStartTime;
|
||||
mutable u_int16_t numTimers;// The number of timers that use this schedule
|
||||
bool hasRunning;
|
||||
bool onActualTp;
|
||||
int modified;
|
||||
time_t presentSeen;
|
||||
@ -187,10 +186,6 @@ public:
|
||||
const cList<cEvent> *Events(void) const { return &events; }
|
||||
const cEvent *GetPresentEvent(void) const;
|
||||
const cEvent *GetFollowingEvent(void) const;
|
||||
#define DEPRECATED_SCHEDULE_GET_EVENT 1
|
||||
#if DEPRECATED_SCHEDULE_GET_EVENT
|
||||
const cEvent *GetEvent(tEventID EventID, time_t StartTime = 0) const;
|
||||
#endif
|
||||
const cEvent *GetEventById(tEventID EventID) const;
|
||||
const cEvent *GetEventByTime(time_t StartTime) const;
|
||||
const cEvent *GetEventAround(time_t Time) const;
|
||||
|
31
filter.c
31
filter.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -73,35 +73,6 @@ bool cSectionSyncer::Processed(int SectionNumber, int LastSectionNumber, int Seg
|
||||
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(void)
|
||||
|
8
filter.h
8
filter.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: filter.h 5.1 2021/03/16 15:10:54 kls Exp $
|
||||
* $Id: filter.h 5.5 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __FILTER_H
|
||||
@ -13,8 +13,6 @@
|
||||
#include <sys/types.h>
|
||||
#include "tools.h"
|
||||
|
||||
#define DEPRECATED_SECTIONSYNCER_SYNC_REPEAT 1
|
||||
|
||||
class cSectionSyncer {
|
||||
private:
|
||||
int currentVersion;
|
||||
@ -49,10 +47,6 @@ public:
|
||||
///< Returns true if all sections have been processed.
|
||||
bool Complete(void) { return complete; }
|
||||
///< 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 {
|
||||
|
4
font.c
4
font.c
@ -6,7 +6,7 @@
|
||||
*
|
||||
* 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"
|
||||
@ -618,7 +618,7 @@ void cTextWrapper::Set(const char *Text, const cFont *Font, int Width)
|
||||
}
|
||||
}
|
||||
w += cw;
|
||||
if (strchr("-.,:;!?_", *p)) {
|
||||
if (strchr("-.,:;!?_~", *p)) {
|
||||
Delim = p;
|
||||
Blank = NULL;
|
||||
}
|
||||
|
147
menu.c
147
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.c 5.9 2022/12/01 13:09:04 kls Exp $
|
||||
* $Id: menu.c 5.23 2025/02/25 15:53:43 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
@ -342,6 +342,7 @@ void cMenuChannelItem::Set(void)
|
||||
|
||||
void cMenuChannelItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
if (!DisplayMenu->SetItemChannel(channel, Index, Current, Selectable, sortMode == csmProvider))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -443,8 +444,9 @@ eOSState cMenuChannels::Number(eKeys Key)
|
||||
number = number * 10 + Key - k0;
|
||||
for (cMenuChannelItem *ci = (cMenuChannelItem *)First(); ci; ci = (cMenuChannelItem *)ci->Next()) {
|
||||
if (!ci->Channel()->GroupSep() && ci->Channel()->Number() == number) {
|
||||
DisplayCurrent(false);
|
||||
SetCurrent(ci);
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1263,6 +1265,7 @@ void cMenuTimerItem::Set(void)
|
||||
}
|
||||
const char *File = timer->Pattern();
|
||||
if (!*File) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (timer->HasFlags(tfSpawned) && timer->Event() && timer->Event()->Title())
|
||||
File = timer->Event()->Title();
|
||||
else {
|
||||
@ -1291,6 +1294,7 @@ void cMenuTimerItem::Set(void)
|
||||
|
||||
void cMenuTimerItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_TIMERS_READ;
|
||||
if (!DisplayMenu->SetItemTimer(timer, Index, Current, Selectable))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -1334,12 +1338,15 @@ void cMenuTimers::Set(void)
|
||||
const cTimer *CurrentTimer = GetTimer();
|
||||
cMenuTimerItem *CurrentItem = NULL;
|
||||
Clear();
|
||||
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
|
||||
cMenuTimerItem *Item = new cMenuTimerItem(Timer);
|
||||
Add(Item);
|
||||
if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
|
||||
CurrentItem = Item;
|
||||
}
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
|
||||
cMenuTimerItem *Item = new cMenuTimerItem(Timer);
|
||||
Add(Item);
|
||||
if (CurrentTimer && Timer->Id() == CurrentTimer->Id() && (!Timer->Remote() && !CurrentTimer->Remote() || Timer->Remote() && CurrentTimer->Remote() && strcmp(Timer->Remote(), CurrentTimer->Remote()) == 0))
|
||||
CurrentItem = Item;
|
||||
}
|
||||
}
|
||||
Sort();
|
||||
SetCurrent(CurrentItem ? CurrentItem : First());
|
||||
SetHelpKeys();
|
||||
@ -1454,6 +1461,7 @@ eOSState cMenuTimers::Info(void)
|
||||
return osContinue;
|
||||
LOCK_TIMERS_READ;
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
cTimer *Timer = GetTimer();
|
||||
if (Timer && 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)
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
cOsdMenu::Display();
|
||||
DisplayMenu()->SetEvent(event);
|
||||
if (event->Description())
|
||||
@ -1599,6 +1608,8 @@ static const char *TimerMatchChars = " tT iI";
|
||||
|
||||
bool cMenuScheduleItem::Update(const cTimers *Timers, bool Force)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
eTimerMatch OldTimerMatch = timerMatch;
|
||||
bool OldTimerActive = timerActive;
|
||||
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)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (!DisplayMenu->SetItemEvent(event, Index, Current, Selectable, channel, withDate, timerMatch, timerActive))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -1671,7 +1684,6 @@ cMenuWhatsOn::cMenuWhatsOn(const cTimers *Timers, const cChannels *Channels, con
|
||||
}
|
||||
}
|
||||
currentChannel = CurrentChannelNr;
|
||||
Display();
|
||||
SetHelpKeys(Channels);
|
||||
}
|
||||
|
||||
@ -1772,10 +1784,8 @@ eOSState cMenuWhatsOn::Record(void)
|
||||
if (HasSubMenu())
|
||||
CloseSubMenu();
|
||||
}
|
||||
if (Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (Update())
|
||||
Display();
|
||||
}
|
||||
LOCK_CHANNELS_READ;
|
||||
SetHelpKeys(Channels);
|
||||
return osContinue;
|
||||
@ -1795,6 +1805,7 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
case kGreen: {
|
||||
cMenuScheduleItem *mi = (cMenuScheduleItem *)Get(Current());
|
||||
if (mi) {
|
||||
LOCK_CHANNELS_READ;
|
||||
scheduleEvent = mi->event;
|
||||
currentChannel = mi->channel->Number();
|
||||
}
|
||||
@ -1807,14 +1818,12 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
case kChanUp:
|
||||
case kChanDn|k_Repeat:
|
||||
case kChanDn: if (!HasSubMenu()) {
|
||||
LOCK_CHANNELS_READ;
|
||||
for (cOsdItem *item = First(); item; item = Next(item)) {
|
||||
if (((cMenuScheduleItem *)item)->channel->Number() == cDevice::CurrentChannel()) {
|
||||
DisplayCurrent(false);
|
||||
SetCurrent(item);
|
||||
{
|
||||
LOCK_SCHEDULES_READ;
|
||||
Display();
|
||||
}
|
||||
LOCK_CHANNELS_READ;
|
||||
DisplayCurrent(true);
|
||||
SetHelpKeys(Channels);
|
||||
break;
|
||||
}
|
||||
@ -1832,10 +1841,8 @@ eOSState cMenuWhatsOn::ProcessKey(eKeys Key)
|
||||
}
|
||||
}
|
||||
else if (!HasSubMenu()) {
|
||||
if (HadSubMenu && Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (HadSubMenu && Update())
|
||||
Display();
|
||||
}
|
||||
if (Key != kNone) {
|
||||
LOCK_CHANNELS_READ;
|
||||
SetHelpKeys(Channels);
|
||||
@ -2083,10 +2090,8 @@ eOSState cMenuSchedule::Record(void)
|
||||
if (HasSubMenu())
|
||||
CloseSubMenu();
|
||||
}
|
||||
if (Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (Update())
|
||||
Display();
|
||||
}
|
||||
SetHelpKeys();
|
||||
return osContinue;
|
||||
}
|
||||
@ -2180,10 +2185,8 @@ eOSState cMenuSchedule::ProcessKey(eKeys Key)
|
||||
Set(Timers, Channels, Channel, true);
|
||||
}
|
||||
}
|
||||
else if (HadSubMenu && Update()) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
else if (HadSubMenu && Update())
|
||||
Display();
|
||||
}
|
||||
if (Key != kNone)
|
||||
SetHelpKeys();
|
||||
}
|
||||
@ -2777,7 +2780,9 @@ eOSState cMenuRecordingEdit::Action(void)
|
||||
RecordingsHandler.Del(recording->FileName());
|
||||
else if (doCut) {
|
||||
if (access(cCutter::EditedFileName(recording->FileName()), F_OK) != 0 || Interface->Confirm(tr("Edited version already exists - overwrite?"))) {
|
||||
if (!RecordingsHandler.Add(ruCut, recording->FileName()))
|
||||
if (!EnoughFreeDiskSpaceForEdit(recording->FileName()))
|
||||
Skins.Message(mtError, tr("Not enough free disk space to start editing process!"));
|
||||
else if (!RecordingsHandler.Add(ruCut, recording->FileName()))
|
||||
Skins.Message(mtError, tr("Error while queueing recording for cutting!"));
|
||||
}
|
||||
}
|
||||
@ -3040,6 +3045,7 @@ void cMenuRecordingItem::IncrementCounter(bool New)
|
||||
|
||||
void cMenuRecordingItem::SetMenuItem(cSkinDisplayMenu *DisplayMenu, int Index, bool Current, bool Selectable)
|
||||
{
|
||||
LOCK_RECORDINGS_READ;
|
||||
if (!DisplayMenu->SetItemRecording(recording, Index, Current, Selectable, level, totalEntries, newEntries))
|
||||
DisplayMenu->SetItem(Text(), Index, Current, Selectable);
|
||||
}
|
||||
@ -3067,7 +3073,6 @@ cMenuRecordings::cMenuRecordings(const char *Base, int Level, bool OpenSubMenus,
|
||||
return;
|
||||
}
|
||||
}
|
||||
Display();
|
||||
SetHelpKeys();
|
||||
}
|
||||
|
||||
@ -3160,11 +3165,6 @@ void cMenuRecordings::Set(bool Refresh)
|
||||
}
|
||||
}
|
||||
|
||||
void cMenuRecordings::SetPath(const char *Path)
|
||||
{
|
||||
path = Path;
|
||||
}
|
||||
|
||||
void cMenuRecordings::SetRecording(const char *FileName)
|
||||
{
|
||||
fileName = FileName;
|
||||
@ -3294,8 +3294,11 @@ eOSState cMenuRecordings::Delete(void)
|
||||
if (RecordingsHandler.GetUsage(FileName)) {
|
||||
if (!Interface->Confirm(tr("Recording is being edited - really delete?")))
|
||||
return osContinue;
|
||||
SetNeedsFastResponse(true); // makes sure the edited version is removed from the menu ASAP
|
||||
}
|
||||
}
|
||||
else
|
||||
return osContinue; // recording has already been deleted
|
||||
}
|
||||
RecordingsHandler.Del(FileName); // must do this w/o holding a lock, because the cleanup section in cDirCopier::Action() might request one!
|
||||
if (cReplayControl::NowReplaying() && strcmp(cReplayControl::NowReplaying(), FileName) == 0)
|
||||
@ -3654,6 +3657,8 @@ void cMenuSetupEPG::Setup(void)
|
||||
Clear();
|
||||
|
||||
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan timeout (h)"), &data.EPGScanTimeout));
|
||||
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG scan max. channel number (0=all)"), &data.EPGScanMaxChannel));
|
||||
Add(new cMenuEditBoolItem(tr("Setup.EPG$EPG pause after scan"), &data.EPGPauseAfterScan));
|
||||
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG bugfix level"), &data.EPGBugfixLevel, 0, MAXEPGBUGFIXLEVEL));
|
||||
Add(new cMenuEditIntItem( tr("Setup.EPG$EPG linger time (min)"), &data.EPGLinger, 0));
|
||||
Add(new cMenuEditBoolItem(tr("Setup.EPG$Set system time"), &data.SetSystemTime));
|
||||
@ -4286,9 +4291,17 @@ eOSState cMenuSetupMisc::ProcessKey(eKeys Key)
|
||||
{
|
||||
bool OldSVDRPPeering = data.SVDRPPeering;
|
||||
bool ModifiedSVDRPSettings = false;
|
||||
if (Key == kOk)
|
||||
bool ModifiedShowChannelNamesWithSource = false;
|
||||
if (Key == kOk) {
|
||||
ModifiedSVDRPSettings = data.SVDRPPeering != Setup.SVDRPPeering || strcmp(data.SVDRPHostName, Setup.SVDRPHostName);
|
||||
ModifiedShowChannelNamesWithSource = data.ShowChannelNamesWithSource != Setup.ShowChannelNamesWithSource;
|
||||
}
|
||||
eOSState state = cMenuSetupBase::ProcessKey(Key);
|
||||
if (ModifiedShowChannelNamesWithSource) {
|
||||
LOCK_CHANNELS_WRITE;
|
||||
for (cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel))
|
||||
Channel->UpdateNameSource();
|
||||
}
|
||||
if (data.SVDRPPeering != OldSVDRPPeering)
|
||||
Set();
|
||||
if (ModifiedSVDRPSettings) {
|
||||
@ -4657,21 +4670,22 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (!HasSubMenu() && Update(HadSubMenu))
|
||||
Display();
|
||||
bool DoDisplay = Update();
|
||||
if (Key != kNone) {
|
||||
if (I18nCurrentLanguage() != osdLanguage) {
|
||||
Set();
|
||||
if (!HasSubMenu())
|
||||
Display();
|
||||
DoDisplay = true;
|
||||
}
|
||||
}
|
||||
if (DoDisplay)
|
||||
Display();
|
||||
return state;
|
||||
}
|
||||
|
||||
// --- SetTrackDescriptions --------------------------------------------------
|
||||
|
||||
static void SetTrackDescriptions(int LiveChannel)
|
||||
void SetTrackDescriptions(int LiveChannel)
|
||||
{
|
||||
cDevice::PrimaryDevice()->ClrAvailableTracks(true);
|
||||
const cComponents *Components = NULL;
|
||||
@ -5700,6 +5714,7 @@ cReplayControl::cReplayControl(bool PauseLive)
|
||||
displayReplay = NULL;
|
||||
marksModified = false;
|
||||
visible = modeOnly = shown = displayFrames = false;
|
||||
lastErrors = 0;
|
||||
lastCurrent = lastTotal = -1;
|
||||
lastPlay = lastForward = false;
|
||||
lastSpeed = -2; // an invalid value
|
||||
@ -5873,6 +5888,7 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
if (!visible) {
|
||||
displayReplay = Skins.Current()->DisplayReplay(modeOnly);
|
||||
displayReplay->SetMarks(&marks);
|
||||
displayReplay->SetErrors(GetErrors());
|
||||
SetNeedsFastResponse(true);
|
||||
visible = true;
|
||||
}
|
||||
@ -5884,7 +5900,9 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
}
|
||||
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) {
|
||||
int Index = Total;
|
||||
if (Setup.ShowRemainingTime)
|
||||
@ -5893,10 +5911,12 @@ bool cReplayControl::ShowProgress(bool Initial)
|
||||
}
|
||||
displayReplay->SetProgress(Current, Total);
|
||||
displayReplay->SetCurrent(IndexToHMSF(Current, displayFrames, FramesPerSecond()));
|
||||
displayReplay->SetErrors(Errors);
|
||||
displayReplay->Flush();
|
||||
lastCurrent = Current;
|
||||
lastTotal = Total;
|
||||
lastErrors = NumErrors;
|
||||
}
|
||||
lastTotal = Total;
|
||||
ShowMode();
|
||||
updateTimer.Set(PROGRESSTIMEOUT);
|
||||
return true;
|
||||
@ -6082,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)
|
||||
{
|
||||
if (*fileName) {
|
||||
@ -6093,6 +6154,8 @@ void cReplayControl::EditCut(void)
|
||||
Skins.Message(mtError, tr("No editing sequences defined!"));
|
||||
else if (access(cCutter::EditedFileName(fileName), F_OK) == 0 && !Interface->Confirm(tr("Edited version already exists - overwrite?")))
|
||||
;
|
||||
else if (!EnoughFreeDiskSpaceForEdit(fileName))
|
||||
Skins.Message(mtError, tr("Not enough free disk space to start editing process!"));
|
||||
else if (!RecordingsHandler.Add(ruCut, fileName))
|
||||
Skins.Message(mtError, tr("Can't start editing process!"));
|
||||
else
|
||||
@ -6224,6 +6287,10 @@ eOSState cReplayControl::ProcessKey(eKeys Key)
|
||||
case kMarkSkipBack: MarkMove(-adaptiveSkipper.GetValue(RAWKEY(Key)), false); break;
|
||||
case kMarkSkipForward|k_Repeat:
|
||||
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 kEditTest: EditTest(); break;
|
||||
default: {
|
||||
|
7
menu.h
7
menu.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menu.h 5.1 2020/12/26 15:49:01 kls Exp $
|
||||
* $Id: menu.h 5.5 2024/10/11 14:10:50 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MENU_H
|
||||
@ -231,7 +231,6 @@ public:
|
||||
cMenuRecordings(const char *Base = NULL, int Level = 0, bool OpenSubMenus = false, const cRecordingFilter *Filter = NULL);
|
||||
~cMenuRecordings();
|
||||
virtual eOSState ProcessKey(eKeys Key);
|
||||
static void SetPath(const char *Path);
|
||||
static void SetRecording(const char *FileName);
|
||||
};
|
||||
|
||||
@ -298,6 +297,7 @@ private:
|
||||
cMarks marks;
|
||||
bool marksModified;
|
||||
bool visible, modeOnly, shown, displayFrames;
|
||||
int lastErrors;
|
||||
int lastCurrent, lastTotal;
|
||||
bool lastPlay, lastForward;
|
||||
int lastSpeed;
|
||||
@ -316,6 +316,7 @@ private:
|
||||
void MarkToggle(void);
|
||||
void MarkJump(bool Forward);
|
||||
void MarkMove(int Frames, bool MarkRequired);
|
||||
void ErrorJump(bool Forward);
|
||||
void EditCut(void);
|
||||
void EditTest(void);
|
||||
public:
|
||||
@ -335,4 +336,6 @@ public:
|
||||
static void ClearLastReplayed(const char *FileName);
|
||||
};
|
||||
|
||||
void SetTrackDescriptions(int LiveChannel);
|
||||
|
||||
#endif //__MENU_H
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: menuitems.c 5.1 2020/12/26 15:49:01 kls Exp $
|
||||
* $Id: menuitems.c 5.3 2025/01/29 10:20:17 kls Exp $
|
||||
*/
|
||||
|
||||
#include "menuitems.h"
|
||||
@ -38,7 +38,6 @@ void cMenuEditItem::SetValue(const char *Value)
|
||||
{
|
||||
cString buffer = cString::sprintf("%s:\t%s", name, Value);
|
||||
SetText(buffer);
|
||||
cStatus::MsgOsdCurrentItem(buffer);
|
||||
}
|
||||
|
||||
void cMenuEditItem::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue)
|
||||
@ -497,10 +496,7 @@ void cMenuEditStrItem::AdvancePos(void)
|
||||
void cMenuEditStrItem::Set(void)
|
||||
{
|
||||
if (InEditMode()) {
|
||||
// This is an ugly hack to make editing strings work with the 'skincurses' plugin.
|
||||
const cFont *font = dynamic_cast<cSkinDisplayMenu *>(cSkinDisplay::Current())->GetTextAreaFont(false);
|
||||
if (!font || font->Width("W") != 1) // all characters have width == 1 in the font used by 'skincurses'
|
||||
font = cFont::GetFont(fontOsd);
|
||||
|
||||
int width = cSkinDisplay::Current()->EditableWidth();
|
||||
width -= font->Width("[]");
|
||||
|
@ -12,7 +12,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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";
|
||||
|
||||
@ -216,7 +216,6 @@ public:
|
||||
virtual bool Start(void);
|
||||
virtual void Stop(void);
|
||||
virtual void Housekeeping(void);
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
|
||||
@ -274,12 +273,6 @@ void cPlugin${PLUGIN_CLASS}::Housekeeping(void)
|
||||
// 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)
|
||||
{
|
||||
// Return a message string if shutdown should be postponed
|
||||
|
4
nit.c
4
nit.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: nit.c 5.3 2022/11/30 12:02:00 kls Exp $
|
||||
* $Id: nit.c 5.4 2024/05/29 11:13:34 kls Exp $
|
||||
*/
|
||||
|
||||
#include "nit.h"
|
||||
@ -257,7 +257,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
|
||||
cDvbTransponderParameters dtpc(Channel->Parameters());
|
||||
dtp.SetSystem(dtpc.System());
|
||||
dtp.SetStreamId(dtpc.StreamId());
|
||||
dtp.SetT2SystemId(dtp.T2SystemId());
|
||||
dtp.SetT2SystemId(dtpc.T2SystemId());
|
||||
dtp.SetSisoMiso(dtpc.SisoMiso());
|
||||
dtp.SetBandwidth(dtpc.Bandwidth());
|
||||
dtp.SetGuard(dtpc.Guard());
|
||||
|
79
osd.c
79
osd.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osd.c 4.11 2020/12/18 23:02:47 kls Exp $
|
||||
* $Id: osd.c 5.2 2024/01/18 12:04:57 kls Exp $
|
||||
*/
|
||||
|
||||
#include "osd.h"
|
||||
@ -1139,6 +1139,49 @@ void cImage::Fill(tColor Color)
|
||||
data[i] = Color;
|
||||
}
|
||||
|
||||
cImage *cImage::Scaled(double FactorX, double FactorY, bool AntiAlias) const
|
||||
{
|
||||
int w = max(1, int(round(Width() * FactorX)));
|
||||
int h = max(1, int(round(Height() * FactorY)));
|
||||
cImage *i = new cImage(cSize(w, h));
|
||||
int RatioX = (Width() << 16) / i->Width();
|
||||
int RatioY = (Height() << 16) / i->Height();
|
||||
|
||||
if (!AntiAlias || FactorX <= 1.0 && FactorY <= 1.0) {
|
||||
// Downscaling - no anti-aliasing:
|
||||
int SourceY = 0;
|
||||
for (int y = 0; y < i->Height(); y++) {
|
||||
int SourceX = 0;
|
||||
for (int x = 0; x < i->Width(); x++) {
|
||||
tColor c1 = GetPixel(cPoint(SourceX >> 16, SourceY >> 16));
|
||||
i->SetPixel(cPoint(x, y), c1);
|
||||
SourceX += RatioX;
|
||||
}
|
||||
SourceY += RatioY;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Upscaling - anti-aliasing:
|
||||
int SourceY = 0;
|
||||
for (int y = 0; y < i->Height(); y++) {
|
||||
int SourceX = 0;
|
||||
int sy = min(SourceY >> 16, Height() - 2);
|
||||
uint8_t BlendY = 0xFF - ((SourceY >> 8) & 0xFF);
|
||||
for (int x = 0; x < i->Width(); x++) {
|
||||
int sx = min(SourceX >> 16, Width() - 2);
|
||||
uint8_t BlendX = 0xFF - ((SourceX >> 8) & 0xFF);
|
||||
tColor c1 = AlphaBlend(GetPixel(cPoint(sx, sy)), GetPixel(cPoint(sx + 1, sy)), BlendX);
|
||||
tColor c2 = AlphaBlend(GetPixel(cPoint(sx, sy + 1)), GetPixel(cPoint(sx + 1, sy + 1)), BlendX);
|
||||
tColor c3 = AlphaBlend(c1, c2, BlendY);
|
||||
i->SetPixel(cPoint(x, y), c3);
|
||||
SourceX += RatioX;
|
||||
}
|
||||
SourceY += RatioY;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// --- cPixmapMemory ---------------------------------------------------------
|
||||
|
||||
cPixmapMemory::cPixmapMemory(void)
|
||||
@ -1259,6 +1302,26 @@ void cPixmapMemory::DrawImage(const cPoint &Point, int ImageHandle)
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void cPixmapMemory::DrawScaledImage(const cPoint &Point, const cImage &Image, double FactorX, double FactorY, bool AntiAlias)
|
||||
{
|
||||
Lock();
|
||||
const cImage *i = &Image;
|
||||
if (!DoubleEqual(FactorX, 1.0) || !DoubleEqual(FactorY, 1.0))
|
||||
i = i->Scaled(FactorX, FactorY, AntiAlias);
|
||||
DrawImage(Point, *i);
|
||||
if (i != &Image)
|
||||
delete i;
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void cPixmapMemory::DrawScaledImage(const cPoint &Point, int ImageHandle, double FactorX, double FactorY, bool AntiAlias)
|
||||
{
|
||||
Lock();
|
||||
if (const cImage *Image = cOsdProvider::GetImageData(ImageHandle))
|
||||
DrawScaledImage(Point, *Image, FactorX, FactorY, AntiAlias);
|
||||
Unlock();
|
||||
}
|
||||
|
||||
void cPixmapMemory::DrawPixel(const cPoint &Point, tColor Color)
|
||||
{
|
||||
Lock();
|
||||
@ -2118,6 +2181,18 @@ void cOsd::DrawImage(const cPoint &Point, int ImageHandle)
|
||||
pixmaps[0]->DrawImage(Point, ImageHandle);
|
||||
}
|
||||
|
||||
void cOsd::DrawScaledImage(const cPoint &Point, const cImage &Image, double FactorX, double FactorY, bool AntiAlias)
|
||||
{
|
||||
if (isTrueColor)
|
||||
pixmaps[0]->DrawScaledImage(Point, Image, FactorX, FactorY, AntiAlias);
|
||||
}
|
||||
|
||||
void cOsd::DrawScaledImage(const cPoint &Point, int ImageHandle, double FactorX, double FactorY, bool AntiAlias)
|
||||
{
|
||||
if (isTrueColor)
|
||||
pixmaps[0]->DrawScaledImage(Point, ImageHandle, FactorX, FactorY, AntiAlias);
|
||||
}
|
||||
|
||||
void cOsd::DrawPixel(int x, int y, tColor Color)
|
||||
{
|
||||
if (isTrueColor)
|
||||
@ -2228,7 +2303,7 @@ cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level)
|
||||
return Osd;
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: no OSD provider available - using dummy OSD!");
|
||||
isyslog("no OSD provider available - using dummy OSD!");
|
||||
return new cOsd(Left, Top, 999); // create a dummy cOsd, so that access won't result in a segfault
|
||||
}
|
||||
|
||||
|
39
osd.h
39
osd.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osd.h 5.1 2021/05/21 12:54:08 kls Exp $
|
||||
* $Id: osd.h 5.2 2024/01/18 12:04:57 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __OSD_H
|
||||
@ -447,6 +447,11 @@ public:
|
||||
///< Clears the image data by setting all pixels to be fully transparent.
|
||||
void Fill(tColor Color);
|
||||
///< Fills the image data with the given Color.
|
||||
cImage *Scaled(double FactorX, double FactorY, bool AntiAlias = false) const;
|
||||
///< Creates a copy of this image, scaled by the given factors.
|
||||
///< If AntiAlias is true and either of the factors is greater than 1.0,
|
||||
///< anti-aliasing is applied.
|
||||
///< The caller must delete the returned image once it is no longer used.
|
||||
};
|
||||
|
||||
#define MAXPIXMAPLAYERS 8
|
||||
@ -602,6 +607,23 @@ public:
|
||||
///< the given Point. ImageHandle must be a value that has previously been
|
||||
///< returned by a call to cOsdProvider::StoreImage(). If ImageHandle
|
||||
///< has an invalid value, nothing happens.
|
||||
virtual void DrawScaledImage(const cPoint &Point, const cImage &Image, double FactorX, double FactorY, bool AntiAlias = false) {};
|
||||
///< Draws the given Image into this pixmap at the given Point and scales it.
|
||||
///< If AntiAlias is true and either of the factors is greater than
|
||||
///< 1.0, anti-aliasing is applied.
|
||||
///< This function should be 'pure', just like all the others. However, since it was
|
||||
///< introduced when this class was already in widespread use, the default was made
|
||||
///< empty, so that existing code will still compile.
|
||||
virtual void DrawScaledImage(const cPoint &Point, int ImageHandle, double FactorX, double FactorY, bool AntiAlias = false) {};
|
||||
///< Draws the image referenced by the given ImageHandle into this pixmap at
|
||||
///< the given Point and scales it. ImageHandle must be a value that has
|
||||
///< previously been returned by a call to cOsdProvider::StoreImage().
|
||||
///< If ImageHandle has an invalid value, nothing happens.
|
||||
///< If AntiAlias is true and either of the factors is greater than
|
||||
///< 1.0, anti-aliasing is applied.
|
||||
///< This function should be 'pure', just like all the others. However, since it was
|
||||
///< introduced when this class was already in widespread use, the default was made
|
||||
///< empty, so that existing code will still compile.
|
||||
virtual void DrawPixel(const cPoint &Point, tColor Color) = 0;
|
||||
///< Sets the pixel at the given Point to the given Color, which is
|
||||
///< a full 32 bit ARGB value. If the alpha value of Color is not 0xFF
|
||||
@ -700,6 +722,8 @@ public:
|
||||
virtual void Fill(tColor Color);
|
||||
virtual void DrawImage(const cPoint &Point, const cImage &Image);
|
||||
virtual void DrawImage(const cPoint &Point, int ImageHandle);
|
||||
virtual void DrawScaledImage(const cPoint &Point, const cImage &Image, double FactorX, double FactorY, bool AntiAlias = false);
|
||||
virtual void DrawScaledImage(const cPoint &Point, int ImageHandle, double FactorX, double FactorY, bool AntiAlias = false);
|
||||
virtual void DrawPixel(const cPoint &Point, tColor Color);
|
||||
virtual void DrawBlendedPixel(const cPoint &Point, tColor Color, uint8_t AlphaLayer = ALPHA_OPAQUE);
|
||||
virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false);
|
||||
@ -857,6 +881,19 @@ public:
|
||||
///< returned by a call to cOsdProvider::StoreImage(). If ImageHandle
|
||||
///< has an invalid value, nothing happens.
|
||||
///< If this is not a true color OSD, this function does nothing.
|
||||
virtual void DrawScaledImage(const cPoint &Point, const cImage &Image, double FactorX, double FactorY, bool AntiAlias = false);
|
||||
///< Draws the given Image on this OSD at the given Point and scales it.
|
||||
///< If AntiAlias is true and either of the factors is greater than
|
||||
///< 1.0, anti-aliasing is applied.
|
||||
///< If this is not a true color OSD, this function does nothing.
|
||||
virtual void DrawScaledImage(const cPoint &Point, int ImageHandle, double FactorX, double FactorY, bool AntiAlias = false);
|
||||
///< Draws the image referenced by the given ImageHandle on this OSD at
|
||||
///< the given Point and scales it. ImageHandle must be a value that has
|
||||
///< previously been returned by a call to cOsdProvider::StoreImage().
|
||||
///< If ImageHandle has an invalid value, nothing happens.
|
||||
///< If AntiAlias is true and either of the factors is greater than
|
||||
///< 1.0, anti-aliasing is applied.
|
||||
///< If this is not a true color OSD, this function does nothing.
|
||||
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
|
||||
///< Checks whether the OSD can display the given set of sub-areas.
|
||||
///< The return value indicates whether a call to SetAreas() with this
|
||||
|
90
osdbase.c
90
osdbase.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: osdbase.c 4.5 2018/03/24 11:47:45 kls Exp $
|
||||
* $Id: osdbase.c 5.9 2025/02/17 10:49:10 kls Exp $
|
||||
*/
|
||||
|
||||
#include "osdbase.h"
|
||||
@ -78,12 +78,16 @@ void cOsdObject::Show(void)
|
||||
cSkinDisplayMenu *cOsdMenu::displayMenu = NULL;
|
||||
int cOsdMenu::displayMenuCount = 0;
|
||||
int cOsdMenu::osdState = 0;
|
||||
cOsdMenu *cOsdMenu::topMenu = NULL;
|
||||
|
||||
cOsdMenu::cOsdMenu(const char *Title, int c0, int c1, int c2, int c3, int c4)
|
||||
{
|
||||
isMenu = true;
|
||||
digit = 0;
|
||||
hasHotkeys = false;
|
||||
if (!topMenu)
|
||||
topMenu = this;
|
||||
active = this == topMenu;
|
||||
displayMenuItems = 0;
|
||||
title = NULL;
|
||||
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);
|
||||
first = 0;
|
||||
lastOffset = -1;
|
||||
conveyStatus = true;
|
||||
current = marked = -1;
|
||||
subMenu = NULL;
|
||||
helpRed = helpGreen = helpYellow = helpBlue = NULL;
|
||||
@ -113,6 +118,8 @@ cOsdMenu::~cOsdMenu()
|
||||
cStatus::MsgOsdClear();
|
||||
if (!--displayMenuCount)
|
||||
DELETENULL(displayMenu);
|
||||
if (this == topMenu)
|
||||
topMenu = NULL;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetMenuCategory(eMenuCategory MenuCategory)
|
||||
@ -125,6 +132,11 @@ void cOsdMenu::SetMenuSortMode(eMenuSortMode MenuSortMode)
|
||||
menuSortMode = MenuSortMode;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetActive(bool Active)
|
||||
{
|
||||
active = Active;
|
||||
}
|
||||
|
||||
void cOsdMenu::SetDisplayMenu(void)
|
||||
{
|
||||
if (displayMenu) {
|
||||
@ -169,6 +181,7 @@ void cOsdMenu::SetStatus(const char *s)
|
||||
free(status);
|
||||
status = s ? strdup(s) : NULL;
|
||||
displayMenu->SetMessage(mtStatus, s);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, s);
|
||||
}
|
||||
|
||||
void cOsdMenu::SetTitle(const char *Title)
|
||||
@ -181,7 +194,8 @@ void cOsdMenu::DisplayHelp(bool Force)
|
||||
{
|
||||
if (!helpDisplayed || Force) {
|
||||
displayMenu->SetButtons(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdHelpKeys(helpRed, helpGreen, helpYellow, helpBlue);
|
||||
helpDisplayed = true;
|
||||
}
|
||||
}
|
||||
@ -224,17 +238,28 @@ void cOsdMenu::Ins(cOsdItem *Item, bool Current, cOsdItem *Before)
|
||||
current = Item->Index();
|
||||
}
|
||||
|
||||
void cOsdMenu::DisplayNoStatus(void)
|
||||
{
|
||||
conveyStatus = false;
|
||||
Display();
|
||||
conveyStatus = true;
|
||||
}
|
||||
|
||||
void cOsdMenu::Display(void)
|
||||
{
|
||||
if (subMenu) {
|
||||
subMenu->Display();
|
||||
return;
|
||||
}
|
||||
if (!active)
|
||||
return;
|
||||
if (cOsdProvider::OsdSizeChanged(osdState))
|
||||
SetDisplayMenu();
|
||||
displayMenu->SetMessage(mtStatus, NULL);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, NULL);
|
||||
displayMenu->Clear();
|
||||
cStatus::MsgOsdClear();
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdClear();
|
||||
if (menuCategory != displayMenu->MenuCategory())
|
||||
displayMenu->SetMenuCategory(menuCategory);
|
||||
displayMenu->SetMenuSortMode(menuSortMode);
|
||||
@ -242,13 +267,15 @@ void cOsdMenu::Display(void)
|
||||
displayMenuItems = displayMenu->MaxItems();
|
||||
displayMenu->SetTabs(cols[0], cols[1], cols[2], cols[3], cols[4]);//XXX
|
||||
displayMenu->SetTitle(title);
|
||||
cStatus::MsgOsdTitle(title);
|
||||
if (conveyStatus)
|
||||
cStatus::MsgOsdTitle(title);
|
||||
DisplayHelp(true);
|
||||
int count = Count();
|
||||
if (count > 0) {
|
||||
int ni = 0;
|
||||
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())
|
||||
current = item->Index();
|
||||
}
|
||||
@ -267,16 +294,18 @@ void cOsdMenu::Display(void)
|
||||
for (cOsdItem *item = Get(first); item; item = Next(item)) {
|
||||
bool CurrentSelectable = (i == current) && item->Selectable();
|
||||
item->SetMenuItem(displayMenu, i - first, CurrentSelectable, item->Selectable());
|
||||
if (CurrentSelectable)
|
||||
cStatus::MsgOsdCurrentItem(item->Text());
|
||||
if (CurrentSelectable) // not checking conveyStatus here!
|
||||
cStatus::MsgOsdCurrentItem(item->Text(), i);
|
||||
if (++n == displayMenuItems)
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
displayMenu->SetScrollbar(count, first);
|
||||
if (!isempty(status))
|
||||
if (!isempty(status)) {
|
||||
displayMenu->SetMessage(mtStatus, status);
|
||||
cStatus::MsgOsdStatusMessage(mtStatus, status);
|
||||
}
|
||||
}
|
||||
|
||||
void cOsdMenu::SetCurrent(cOsdItem *Item)
|
||||
@ -299,9 +328,13 @@ void cOsdMenu::DisplayCurrent(bool Current)
|
||||
cOsdItem *item = Get(current);
|
||||
if (item) {
|
||||
item->SetMenuItem(displayMenu, current - first, Current && item->Selectable(), item->Selectable());
|
||||
if (Current && item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(item->Text());
|
||||
if (!Current)
|
||||
if (Current) {
|
||||
if (current - first >= displayMenuItems || current < first)
|
||||
DisplayNoStatus();
|
||||
else if (item->Selectable())
|
||||
cStatus::MsgOsdCurrentItem(item->Text(), current);
|
||||
}
|
||||
else
|
||||
item->SetFresh(true); // leaving the current item resets 'fresh'
|
||||
if (cMenuEditItem *MenuEditItem = dynamic_cast<cMenuEditItem *>(item)) {
|
||||
if (!MenuEditItem->DisplayHelp(Current))
|
||||
@ -321,7 +354,7 @@ void cOsdMenu::DisplayItem(cOsdItem *Item)
|
||||
bool Current = Index == current;
|
||||
Item->SetMenuItem(displayMenu, Offset, Current && Item->Selectable(), 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) {
|
||||
// make non-selectable items at the beginning visible:
|
||||
first = 0;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
return;
|
||||
}
|
||||
if (Setup.MenuScrollWrap)
|
||||
@ -371,11 +404,11 @@ void cOsdMenu::CursorUp(void)
|
||||
current = tmpCurrent;
|
||||
if (current < first) {
|
||||
first = Setup.MenuScrollPage ? max(0, current - displayMenuItems + 1) : current;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else if (current > lastOnScreen) {
|
||||
first = max(0, current - displayMenuItems + 1);
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else
|
||||
DisplayCurrent(true);
|
||||
@ -393,7 +426,7 @@ void cOsdMenu::CursorDown(void)
|
||||
if (first < last - displayMenuItems) {
|
||||
// make non-selectable items at the end visible:
|
||||
first = last - displayMenuItems + 1;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
return;
|
||||
}
|
||||
if (Setup.MenuScrollWrap)
|
||||
@ -411,11 +444,11 @@ void cOsdMenu::CursorDown(void)
|
||||
first = Setup.MenuScrollPage ? current : max(0, current - displayMenuItems + 1);
|
||||
if (first + displayMenuItems > last)
|
||||
first = max(0, last - displayMenuItems + 1);
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else if (current < first) {
|
||||
first = current;
|
||||
Display();
|
||||
DisplayNoStatus();
|
||||
}
|
||||
else
|
||||
DisplayCurrent(true);
|
||||
@ -447,10 +480,8 @@ void cOsdMenu::PageUp(void)
|
||||
else if (current - first >= displayMenuItems)
|
||||
first = current - displayMenuItems + 1;
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst) {
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst)
|
||||
DisplayNoStatus();
|
||||
else if (Setup.MenuScrollWrap)
|
||||
CursorUp();
|
||||
}
|
||||
@ -481,10 +512,8 @@ void cOsdMenu::PageDown(void)
|
||||
else if (current - first >= displayMenuItems)
|
||||
first = current - displayMenuItems + 1;
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst) {
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
}
|
||||
if (current != oldCurrent || first != oldFirst)
|
||||
DisplayNoStatus();
|
||||
else if (Setup.MenuScrollWrap)
|
||||
CursorDown();
|
||||
}
|
||||
@ -503,9 +532,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
|
||||
const char *s = item->Text();
|
||||
if (s && (s = skipspace(s)) != NULL) {
|
||||
if (*s == Key - k1 + '1') {
|
||||
DisplayCurrent(false);
|
||||
current = item->Index();
|
||||
RefreshCurrent();
|
||||
Display();
|
||||
DisplayCurrent(true);
|
||||
cRemote::Put(kOk, true);
|
||||
break;
|
||||
}
|
||||
@ -516,8 +546,10 @@ eOSState cOsdMenu::HotKey(eKeys Key)
|
||||
|
||||
eOSState cOsdMenu::AddSubMenu(cOsdMenu *SubMenu)
|
||||
{
|
||||
SetActive(false);
|
||||
delete subMenu;
|
||||
subMenu = SubMenu;
|
||||
subMenu->SetActive(true);
|
||||
subMenu->Display();
|
||||
return osContinue; // convenience return value
|
||||
}
|
||||
@ -526,6 +558,7 @@ eOSState cOsdMenu::CloseSubMenu(bool ReDisplay)
|
||||
{
|
||||
delete subMenu;
|
||||
subMenu = NULL;
|
||||
SetActive(true);
|
||||
if (ReDisplay) {
|
||||
RefreshCurrent();
|
||||
Display();
|
||||
@ -546,7 +579,8 @@ eOSState cOsdMenu::ProcessKey(eKeys Key)
|
||||
if (marked < 0 && item) {
|
||||
eOSState state = item->ProcessKey(Key);
|
||||
if (state != osUnknown) {
|
||||
DisplayCurrent(true);
|
||||
if (Key != kNone)
|
||||
DisplayCurrent(true);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -87,11 +87,13 @@ private:
|
||||
static cSkinDisplayMenu *displayMenu;
|
||||
static int displayMenuCount;
|
||||
static int osdState;
|
||||
static cOsdMenu *topMenu;
|
||||
int displayMenuItems;
|
||||
char *title;
|
||||
int cols[cSkinDisplayMenu::MaxTabs];
|
||||
int first, current, marked;
|
||||
int lastOffset;
|
||||
bool conveyStatus;
|
||||
eMenuCategory menuCategory;
|
||||
eMenuSortMode menuSortMode;
|
||||
eMenuOrientation menuOrientation;
|
||||
@ -101,7 +103,10 @@ private:
|
||||
char *status;
|
||||
int digit;
|
||||
bool hasHotkeys;
|
||||
bool active;
|
||||
void SetActive(bool Active);
|
||||
void DisplayHelp(bool Force = false);
|
||||
void DisplayNoStatus(void);
|
||||
protected:
|
||||
void SetDisplayMenu(void);
|
||||
cSkinDisplayMenu *DisplayMenu(void) { return displayMenu; }
|
||||
|
15
player.c
15
player.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: player.c 4.1 2020/05/18 16:47:29 kls Exp $
|
||||
* $Id: player.c 5.2 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#include "player.h"
|
||||
@ -70,14 +70,6 @@ cString cControl::GetHeader(void)
|
||||
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)
|
||||
{
|
||||
MutexLock.Lock(&mutex);
|
||||
@ -87,9 +79,8 @@ cControl *cControl::Control(cMutexLock &MutexLock, bool Hidden)
|
||||
void cControl::Launch(cControl *Control)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
cControl *c = control; // keeps control from pointing to uninitialized memory TODO obsolete once DEPRECATED_CCONTROL is gone
|
||||
delete control;
|
||||
control = Control;
|
||||
delete c;
|
||||
}
|
||||
|
||||
void cControl::Attach(void)
|
||||
@ -99,7 +90,7 @@ void cControl::Attach(void)
|
||||
if (cDevice::PrimaryDevice()->AttachPlayer(control->player))
|
||||
control->attached = true;
|
||||
else {
|
||||
Skins.Message(mtError, tr("Channel locked (recording)!"));
|
||||
Skins.Message(mtError, tr("Primary device has no MPEG decoder, can't attach player!"));
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
13
player.h
13
player.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: player.h 5.1 2022/12/05 14:45:51 kls Exp $
|
||||
* $Id: player.h 5.6 2024/10/13 09:47:18 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __PLAYER_H
|
||||
@ -54,6 +54,8 @@ public:
|
||||
bool IsAttached(void) { return device != NULL; }
|
||||
virtual double FramesPerSecond(void) { return DEFAULTFRAMESPERSECOND; }
|
||||
// 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; }
|
||||
// Returns the current and total frame index, optionally snapped to the
|
||||
// nearest I-frame.
|
||||
@ -115,15 +117,6 @@ public:
|
||||
static void Launch(cControl *Control);
|
||||
static void Attach(void);
|
||||
static void Shutdown(void);
|
||||
#define DEPRECATED_CCONTROL 1
|
||||
#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);
|
||||
///< 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
|
||||
|
3
plugin.c
3
plugin.c
@ -4,9 +4,10 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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 <ctype.h>
|
||||
#include <dirent.h>
|
||||
|
5
plugin.h
5
plugin.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -43,6 +43,9 @@ public:
|
||||
virtual bool Start(void);
|
||||
virtual void Stop(void);
|
||||
virtual void Housekeeping(void);
|
||||
#ifndef MUTE_DEPRECATED_MAINTHREADHOOK
|
||||
[[deprecated("use proper locking instead")]]
|
||||
#endif
|
||||
virtual void MainThreadHook(void);
|
||||
virtual cString Active(void);
|
||||
virtual time_t WakeupTime(void);
|
||||
|
27
po/ar.po
27
po/ar.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-10-16 11:16-0400\n"
|
||||
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
|
||||
"Language-Team: Arabic <ar@li.org>\n"
|
||||
@ -134,8 +134,8 @@ msgstr "News Magazine"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentary"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgstr "Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussion/Interview/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
msgstr "Show/Game Show"
|
||||
@ -212,8 +212,8 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Serious/Classical Music"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgstr "Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Traditional Music"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
msgstr "Jazz"
|
||||
@ -845,6 +845,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1028,6 +1031,12 @@ msgstr "بحث"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "اﻻنتهاء من البحث على الدليل الالكترونى للقنوات"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "معدل اصلاح اخطاء الدليل الالكترونى"
|
||||
|
||||
@ -1525,8 +1534,8 @@ msgstr "الملحق"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "اعلى او اسفل ل الموضع الجديد و موافق للتحريك"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "القناة مقفلة للتسجيل"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "مساحة القرص غير كافية"
|
||||
@ -1569,9 +1578,13 @@ msgstr "Recording - restart anyway?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "restart anyway?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "الصوت "
|
||||
|
23
po/ca_ES.po
23
po/ca_ES.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-03-02 19:02+0100\n"
|
||||
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
||||
"Language-Team: Catalan <vdr@linuxtv.org>\n"
|
||||
@ -133,7 +133,7 @@ msgstr "Revista de not
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documental"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussió/Entrevista/Debat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -211,7 +211,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Música clàssica/seriosa"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Música folklòrica/tradicional"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -844,6 +844,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1027,6 +1030,12 @@ msgstr "Escaneig"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Màxim d'Hores a cercar per la Guia"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivell de correcció de la Guia"
|
||||
|
||||
@ -1524,8 +1533,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Amunt/Avall per una nova localització - OK per moure"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal bloquejat (gravant)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disc gairebé ple!"
|
||||
@ -1568,9 +1577,13 @@ msgstr "Gravant - Reiniciar de totes maneres?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "Reiniciar de totes maneres?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
23
po/cs_CZ.po
23
po/cs_CZ.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2010-05-06 11:00+0200\n"
|
||||
"Last-Translator: Aleš Juřík <ajurik@quick.cz>\n"
|
||||
"Language-Team: Czech <vdr@linuxtv.org>\n"
|
||||
@ -133,7 +133,7 @@ msgstr "zprávy"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "dokumentární"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "diskuze/rozhovor/debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -211,7 +211,7 @@ msgstr "rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "vážná/klasická hudba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "folk/country"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -844,6 +844,9 @@ msgstr "Nahrávka zmizela!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Editovaná verze již existuje - přepsat?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Chyba při vložení nahrávky do fronty pro střih"
|
||||
|
||||
@ -1027,6 +1030,12 @@ msgstr "Snímat"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Časový limit pro snímání EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG úroveň chyb"
|
||||
|
||||
@ -1524,8 +1533,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Nahoru/Dolu pro novou pozici - Ok přesune"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanál je blokovaný (nahrává se)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disk bude brzy zaplněn!"
|
||||
@ -1568,9 +1577,13 @@ msgstr "Nahrávání - přesto restartovat?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "přesto restartovat?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Hlasitost "
|
||||
|
23
po/da_DK.po
23
po/da_DK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
|
||||
"Language-Team: Danish <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -841,6 +841,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1024,6 +1027,12 @@ msgstr "Skan"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Tid før EPG skanning (t)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Niveau for EPG fejlrettelse"
|
||||
|
||||
@ -1521,8 +1530,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Op/Ned for ny placering - OK for at flytte"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal blokeret (optagelse i gang)"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kun lidt diskplads tilbage!"
|
||||
@ -1565,9 +1574,13 @@ msgstr "Optagelse igang - genstart alligevel?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "genstart alligevel?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Lydstyrke "
|
||||
|
25
po/de_DE.po
25
po/de_DE.po
@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 14:54+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-10 13:45+0100\n"
|
||||
"Last-Translator: Klaus Schmidinger <vdr@tvdr.de>\n"
|
||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Nachrichtenmagazin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussion/Interview/Debatte"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -142,7 +142,7 @@ msgid "Content$Game Show/Quiz/Contest"
|
||||
msgstr "Spielshow/Quiz/Wettbewerb"
|
||||
|
||||
msgid "Content$Variety Show"
|
||||
msgstr "Variete-Show"
|
||||
msgstr "Varieté-Show"
|
||||
|
||||
msgid "Content$Talk Show"
|
||||
msgstr "Talkshow"
|
||||
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Ernste/Klassische Musik"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Volks/Traditionelle Musik"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -843,6 +843,9 @@ msgstr "Aufnahme verschwunden!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Geschnittene Version existiert bereits - überschreiben?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr "Nicht genug freier Plattenplatz zum Schneiden!"
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Fehler beim Hinzufügen der Aufnahme zur Schnittwarteschlange"
|
||||
|
||||
@ -1026,6 +1029,12 @@ msgstr "Scan"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Zeit bis zur EPG-Aktualisierung (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr "Maximaler Kanal für EPG-Scan (0=alle)"
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr "Pause nach EPG-Scan"
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG-Fehlerbereinigungsstufe"
|
||||
|
||||
@ -1523,8 +1532,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Auf/Ab für neue Position - dann OK"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal blockiert (zeichnet auf)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Primäres Device hat keinen MPEG-Decoder, Wiedergabe nicht möglich!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Platte beinahe voll!"
|
||||
@ -1567,9 +1576,13 @@ msgstr "Aufnahme l
|
||||
msgid "restart anyway?"
|
||||
msgstr "trotzdem neu starten?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "Fehler"
|
||||
|
||||
msgid "error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Lautstärke "
|
||||
|
23
po/el_GR.po
23
po/el_GR.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
|
||||
"Language-Team: Greek <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -841,6 +841,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1024,6 +1027,12 @@ msgstr "
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "×ñüíïò äéÜñêåéáò åîÝôáóçò EPG óå þñåò"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Âáèìüò äéüñèùóçò ïäçãïý EPG"
|
||||
|
||||
@ -1521,8 +1530,8 @@ msgstr "
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "ÐÜíù/ÊÜôù ãéÜ íÝá èÝóç. ÌåôÜ ÏÊ"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Ôï êáíÜëé åßíáé áðïó÷ïëéìÝíï (Ãßíåôå åããñáöÞ)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Ï óêëçñüò êïíôåýåé íÜ ãåìßóåé!"
|
||||
@ -1565,9 +1574,13 @@ msgstr "
|
||||
msgid "restart anyway?"
|
||||
msgstr "ÔåëéêÜ íá ãßíåé åðáíåêêßíçóç?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "¸íôáóç "
|
||||
|
23
po/es_ES.po
23
po/es_ES.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-19 23:00+0100\n"
|
||||
"Last-Translator: Gabriel Bonich <gbonich@gmail.com>\n"
|
||||
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Revista de noticias"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documental"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discusión/Entrevista/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Musica clásica"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Musica Folk/Tradicional"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr "Grabaci
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Se está editando una versión - sobreescribir?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Error mientras la grabación está en cola!"
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Escanear"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Tiempo de exploración de EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivel de corrección de EPG"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Arriba/Abajo para mover - OK para confirmar"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "¡Canal bloqueado (grabando)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "¡Poco espacio en disco!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Grabaci
|
||||
msgid "restart anyway?"
|
||||
msgstr "¿reiniciar igualmente?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volumen "
|
||||
|
23
po/et_EE.po
23
po/et_EE.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
|
||||
"Language-Team: Estonian <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr "Uudisteajakiri"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentaal"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussioon/intervjuu/debatt"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr "Rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klassikaline muusika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/rahvamuusika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -841,6 +841,9 @@ msgstr "Salvestis puudub!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Muudetud versioon juba olemas - kirjutada üle?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Salvestise lisamine lõikamiseks ebaõnnestus!"
|
||||
|
||||
@ -1024,6 +1027,12 @@ msgstr "Uuenda"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "EPG skaneerimise viide (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG veaparandustase"
|
||||
|
||||
@ -1521,8 +1530,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Üles/Alla' uus asukoht - 'OK' kinnitus"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal lukus (salvestamine aktiivne)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kõvaketas täis!"
|
||||
@ -1565,9 +1574,13 @@ msgstr "Salvestamine aktiivne - taaskäivitada ikkagi?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "taaskäivitada ikkagi?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Helitugevus "
|
||||
|
29
po/fi_FI.po
29
po/fi_FI.po
@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-15 15:52+0200\n"
|
||||
"Last-Translator: Matti Lehtimäki <matti.lehtimaki@gmail.com>\n"
|
||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||
@ -134,7 +134,7 @@ msgstr "Uutismakasiini"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentti"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Keskustelu/haastattelu/väittely"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -212,7 +212,7 @@ msgstr "Rock/pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Vakava/klassinen musiikki"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/kansanmusiikki"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -797,7 +797,7 @@ msgid "Folder"
|
||||
msgstr "Kansio"
|
||||
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
msgstr "Koko"
|
||||
|
||||
msgid "This folder is currently in use - no changes are possible!"
|
||||
msgstr "Kansio on käytössä - muokkaukset eivät mahdollisia!"
|
||||
@ -845,6 +845,9 @@ msgstr "Tallenne katosi!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Muokattava versio on jo olemassa - ylikirjoitetaanko?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr "Tallennustila ei riitä muokkaamiseen!"
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Tallenteen lisääminen leikkausjonoon epäonnistui!"
|
||||
|
||||
@ -1028,6 +1031,12 @@ msgstr "Päivitä"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Ohjelmaoppaan taustapäivitys (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Ohjelmaoppaan korjaustaso"
|
||||
|
||||
@ -1517,7 +1526,7 @@ msgid "Button$Insert"
|
||||
msgstr "Lisää"
|
||||
|
||||
msgid "Button$Macro"
|
||||
msgstr ""
|
||||
msgstr "Makro"
|
||||
|
||||
msgid "Plugin"
|
||||
msgstr "Laajennos"
|
||||
@ -1525,8 +1534,8 @@ msgstr "Laajennos"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Ylös/Alas' uusi paikka - 'OK' hyväksy"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanava lukittu (tallennus käynnissä)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Ensisijaissa laitteessa ei ole MPEG-toistinta!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Tallennustila loppumassa!"
|
||||
@ -1569,8 +1578,12 @@ msgstr "Tallennus kesken - käynnistetäänkö uudelleen?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "käynnistetäänkö uudelleen?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
msgstr "virhettä"
|
||||
|
||||
msgid "error"
|
||||
msgstr "virhe"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
|
23
po/fr_FR.po
23
po/fr_FR.po
@ -18,7 +18,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-04-14 10:16+0100\n"
|
||||
"Last-Translator: Bernard Jaulin <bernard.jaulin@gmail.com>\n"
|
||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||
@ -141,7 +141,7 @@ msgstr "Magazine d'information"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentaire"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussion/Interview/Débat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -219,7 +219,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Sérieux/Musique Classique"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Musique Traditionnelle"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -852,6 +852,9 @@ msgstr "L'enregistrement a disparu !"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "La version éditée existe déjà - écraser ?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Erreur lors de la mise en file d'attente pour la coupe !"
|
||||
|
||||
@ -1035,6 +1038,12 @@ msgstr "Scanner"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Inactivité avant mise à jour EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Niveau de correction du guide-EPG"
|
||||
|
||||
@ -1532,8 +1541,8 @@ msgstr "Module"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Haut/Bas -> nouvelle place - OK -> déplacer"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Chaîne verrouillée (enregistrement en cours) !"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Disque presque plein !"
|
||||
@ -1576,9 +1585,13 @@ msgstr "Enregistrement en cours - confirmer redémarrage ?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "confirmer redémarrage ?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
23
po/hr_HR.po
23
po/hr_HR.po
@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-03-17 19:00+0100\n"
|
||||
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
|
||||
"Language-Team: Croatian <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -843,6 +843,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1026,6 +1029,12 @@ msgstr "Pretra
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Vrijeme do EPG pregleda (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Razina popravka EPG gre¹aka"
|
||||
|
||||
@ -1523,8 +1532,8 @@ msgstr "Dodatak"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gore/Dolje na novu poziciju - OK za potvdru"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Program zakljuèan (snimanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Malo prostora na disku!"
|
||||
@ -1567,9 +1576,13 @@ msgstr "Snimanje je u tijeku - ponovno pokreni unato
|
||||
msgid "restart anyway?"
|
||||
msgstr "ponovno pokreni unatoè?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Glasnoæa "
|
||||
|
66
po/hu_HU.po
66
po/hu_HU.po
@ -4,22 +4,25 @@
|
||||
# Istvan Koenigsberger <istvnko@hotmail.com>, 2002, 2003, 2006
|
||||
# Guido Josten <guido.josten@t-online.de>, 2002, 2003, 2006
|
||||
# Thomas Günther <tom@toms-cafe.de>, 2007
|
||||
# István Füley <ifuley@tigercomp.ro>, 2007, 2012, 2013, 2015, 2018
|
||||
# István Füley <ifuley@tigercomp.ro>, 2007, 2012, 2013, 2015, 2018, 2024
|
||||
# Albert Danis <a.danis@gmx.de>, 2015
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Project-Id-Version: VDR 2.6.6\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"PO-Revision-Date: 2018-04-09 21:42+0300\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2024-02-09 06:29+0000\n"
|
||||
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
|
||||
"Language-Team: Hungarian <vdr@linuxtv.org>\n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Loco-Source-Locale: hu_RO\n"
|
||||
"X-Generator: Loco https://localise.biz/\n"
|
||||
"X-Loco-Parser: loco_parse_po\n"
|
||||
|
||||
msgid "*** Invalid Channel ***"
|
||||
msgstr "*** Érvénytelen csatorna ***"
|
||||
@ -135,7 +138,7 @@ msgstr "Hírmagazin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentumfilm"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Beszélgetés/Interjú/Vita"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -213,7 +216,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Komolyzene/Klasszikus zene"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Népzene/Hagyományos zene"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -349,28 +352,28 @@ msgid "LanguageCode$eng"
|
||||
msgstr "hun"
|
||||
|
||||
msgid "LanguageName$original language (qaa)"
|
||||
msgstr ""
|
||||
msgstr "eredeti nyelv"
|
||||
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
msgstr "audio leírás"
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
msgstr "csak beszéd (qks)"
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
msgstr "kód nélküli nyelv (mis)"
|
||||
|
||||
msgid "LanguageName$multiple languages (mul)"
|
||||
msgstr ""
|
||||
msgstr "többnyelvű (mul)"
|
||||
|
||||
msgid "LanguageName$narrative (nar)"
|
||||
msgstr ""
|
||||
msgstr "elbeszélő (nar)"
|
||||
|
||||
msgid "LanguageName$undetermined (und)"
|
||||
msgstr ""
|
||||
msgstr "meghatározatlan (und)"
|
||||
|
||||
msgid "LanguageName$no linguistic content (zxx)"
|
||||
msgstr ""
|
||||
msgstr "nincs nyelvi tartalom (zxx)"
|
||||
|
||||
msgid "Phase 1: Detecting RC code type"
|
||||
msgstr "Első lépés: távirányító kódjának meghatározása"
|
||||
@ -687,7 +690,7 @@ msgid "File"
|
||||
msgstr "Fájl"
|
||||
|
||||
msgid "Record on"
|
||||
msgstr "Felvétel be"
|
||||
msgstr "Felvétel itt"
|
||||
|
||||
msgid "Button$Folder"
|
||||
msgstr "Könyvtár"
|
||||
@ -699,19 +702,19 @@ msgid "Button$Repeating"
|
||||
msgstr "Ismétlődő"
|
||||
|
||||
msgid "Button$Regular"
|
||||
msgstr ""
|
||||
msgstr "Szabályos"
|
||||
|
||||
msgid "Button$Pattern"
|
||||
msgstr ""
|
||||
msgstr "Minta"
|
||||
|
||||
msgid "First day"
|
||||
msgstr "Első nap"
|
||||
|
||||
msgid "Timer is recording!"
|
||||
msgstr ""
|
||||
msgstr "Az időzítő felvételt készít!"
|
||||
|
||||
msgid "Pattern"
|
||||
msgstr ""
|
||||
msgstr "Minta"
|
||||
|
||||
msgid "Error while accessing remote timer"
|
||||
msgstr "Távoli időzítő nem elérhető"
|
||||
@ -798,7 +801,7 @@ msgid "Folder"
|
||||
msgstr "Könyvtár"
|
||||
|
||||
msgid "Size"
|
||||
msgstr ""
|
||||
msgstr "Méret"
|
||||
|
||||
msgid "This folder is currently in use - no changes are possible!"
|
||||
msgstr "Ez a könyvtár használatban van - módosítás nem lehetséges!"
|
||||
@ -846,6 +849,9 @@ msgstr "Ez a felvétel eltűnt!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "A szerkesztett változat már létezik - felülírjam?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Hiba a felvétel vágásra előkészítése közben!"
|
||||
|
||||
@ -1029,6 +1035,12 @@ msgstr "EPG keresés"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Fennmaradt idő az EPG-frissítésig (ó)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Hibaelhárítás szintje"
|
||||
|
||||
@ -1518,7 +1530,7 @@ msgid "Button$Insert"
|
||||
msgstr "Beillesztés"
|
||||
|
||||
msgid "Button$Macro"
|
||||
msgstr ""
|
||||
msgstr "Makró"
|
||||
|
||||
msgid "Plugin"
|
||||
msgstr "Plugin"
|
||||
@ -1526,8 +1538,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Fel/Le egy új pozícióért - aztán OK"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Csatorna blokkolva (felvétel)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Adattároló majdnem tele!"
|
||||
@ -1570,7 +1582,11 @@ msgstr "Felvétel folyamatban - mégis újraindítani?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "mégis újraindítani?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "hiba"
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
|
27
po/it_IT.po
27
po/it_IT.po
@ -5,14 +5,14 @@
|
||||
# Antonio Ospite <ospite@studenti.unina.it>, 2003, 2006
|
||||
# Sean Carlos <seanc@libero.it>, 2005
|
||||
# Nino Gerbino <ngerb@interfree.it>, 2006, 2015
|
||||
# Diego Pierotto <vdr-italian@tiscali.it>, 2007-2010, 2012, 2013, 2015, 2018, 2020, 2022
|
||||
# Diego Pierotto <vdr-italian@tiscali.it>, 2007-2010, 2012, 2013, 2015, 2018, 2020, 2022, 2024
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"PO-Revision-Date: 2022-12-15 20:22+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2024-10-15 00:35+0200\n"
|
||||
"Last-Translator: Gringo <openpli@tiscali.it>\n"
|
||||
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||
"Language: it\n"
|
||||
@ -136,7 +136,7 @@ msgstr "Settimanale di attualità"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentario"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussione/Intervista/Dibattito"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Musica Classica/Seria"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Musica Tradizionale/Folclore"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -847,6 +847,9 @@ msgstr "Registrazione sparita!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Versione modificata già esistente. Sovrascrivere?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr "Spazio libero su disco insufficiente per avviare il processo di modifica!"
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Errore durante l'accodamento della registrazione per il taglio!"
|
||||
|
||||
@ -1030,6 +1033,12 @@ msgstr "Scansione"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Scadenza aggiorn. EPG (ore)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr "Numero massimo canali scansione EPG (0=tutti)"
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr "Pausa EPG dopo scansione"
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Livello correzione EPG"
|
||||
|
||||
@ -1527,8 +1536,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Su/Giù per nuova posizione - OK per spostare"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canale bloccato (in registrazione)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr "Il dispositivo primario non ha un decoder MPEG, impossibile collegare il lettore!"
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Poco spazio su disco!"
|
||||
@ -1571,9 +1580,13 @@ msgstr "Registrazione in corso - riavviare comunque?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "riavviare comunque?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr "errori"
|
||||
|
||||
msgid "error"
|
||||
msgstr "errore"
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
23
po/lt_LT.po
23
po/lt_LT.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-11 14:02+0200\n"
|
||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr "Naujienų žurnalas"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentika"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusijos/Interviu"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klasikinė muzika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Tradizinė muzika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -841,6 +841,9 @@ msgstr "Įrašai išvalyti!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Tokia koreguota versija jau yra, vistiek perašyti?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Klaida sustatant įrašus į karpymo eilę!"
|
||||
|
||||
@ -1024,6 +1027,12 @@ msgstr "Skanuoti"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "EPG skanavimo užlaikymas (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG klaidų taisymo lygis"
|
||||
|
||||
@ -1521,8 +1530,8 @@ msgstr "Įskiepas"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Spauskite mygtukus į viršų / į apačią ir OK kad perkelti į kitą vietą"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalas užblokuotas (įrašinėjama)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Mažai vietos diske!"
|
||||
@ -1565,9 +1574,13 @@ msgstr "Įrašinėjama - perkrauti vistiek?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "perkrati bet kokiu atveju?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Garsas "
|
||||
|
23
po/mk_MK.po
23
po/mk_MK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-03-31 21:47+0100\n"
|
||||
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
|
||||
"Language-Team: Macedonian <kde-i18n-doc@kde.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Магазин"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документарен"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискусија/Интервју/Дебата"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Сериозна/Класична музика"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фолк/Народна музика"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -843,6 +843,9 @@ msgstr "Снимката исчезна!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Уредена верзија веќе постои - пребриши?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Грешка при редење на снимка за сечење!"
|
||||
|
||||
@ -1026,6 +1029,12 @@ msgstr "Барај"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Време до барање на EPG (ч)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Ниво на поправка на EPG грешки"
|
||||
|
||||
@ -1523,8 +1532,8 @@ msgstr "Додаток"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Горе/Доле за нова позиција - OK за потврда"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Каналот е заклучен (снимање)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Малку простор на дискот!"
|
||||
@ -1567,9 +1576,13 @@ msgstr "Снимање во тек - рестартирај?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "сеедно рестартирај?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Глас "
|
||||
|
23
po/nl_NL.po
23
po/nl_NL.po
@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-10 19:43+0100\n"
|
||||
"Last-Translator: Erik Oomen <oomen.e@gmail.com>\n"
|
||||
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
||||
@ -136,7 +136,7 @@ msgstr "Nieuwsbulletin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentaire"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussie/Interview/Debat"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -214,7 +214,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Serieuze muziek/Klassieke muziek"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Traditionele muziek"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -847,6 +847,9 @@ msgstr "Opname verdwenen!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Bewerkte versie bestaat al - overschrijven?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Kan opname voor knippen niet in wachtrij plaatsen!"
|
||||
|
||||
@ -1030,6 +1033,12 @@ msgstr "Scan"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "EPG-scan Timeout (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG foutcorrectieniveau"
|
||||
|
||||
@ -1527,8 +1536,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gebruik Omhoog/Omlaag - daarna Ok"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanaal geblokkeerd (neemt op)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Hardeschijf bijna vol!"
|
||||
@ -1571,9 +1580,13 @@ msgstr "Opname loopt - toch opnieuw starten?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "toch opnieuw starten?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
23
po/nn_NO.po
23
po/nn_NO.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
|
||||
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr ""
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Ledig tid før EPG-søk (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivå for EPG-feilretting"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Opp/Ned for ny plass - OK for å flytte"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalen er låst (opptak)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Lite ledig diskplass!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Gj
|
||||
msgid "restart anyway?"
|
||||
msgstr "starte på nytt likevel?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
23
po/pl_PL.po
23
po/pl_PL.po
@ -10,7 +10,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-02-19 00:42+0100\n"
|
||||
"Last-Translator: Tomasz Maciej Nowak <tmn505@gmail.com>\n"
|
||||
"Language-Team: Polish <vdr@linuxtv.org>\n"
|
||||
@ -135,7 +135,7 @@ msgstr "Magazyny informacyjne"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentalne"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Dyskusje/Wywiady/Debaty"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Muzyka klasyczna"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folk/Muzyka tradycyjna"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -846,6 +846,9 @@ msgstr "Nagranie zniknęło!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Edytowana wersja już istnieje - nadpisać?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Błąd podczas zakolejkowania nagrania do przycinania!"
|
||||
|
||||
@ -1029,6 +1032,12 @@ msgstr "Skanuj"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Czas skanowania EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Stopień poprawek błędów EPG"
|
||||
|
||||
@ -1526,8 +1535,8 @@ msgstr "Wtyczka"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Do góry/Na dół na nową pozycję - Ok zatwierdza"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanał zablokowany (trwa nagrywanie)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Mało miejsca na dysku!"
|
||||
@ -1570,9 +1579,13 @@ msgstr "Trwa nagrywanie - zrestartować mimo to?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "zrestartować mimo to?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Głośność "
|
||||
|
23
po/pt_PT.po
23
po/pt_PT.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2010-03-28 22:49+0100\n"
|
||||
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
|
||||
"Language-Team: Portuguese <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Notici
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentário"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discussão/Entrevista/Debate"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Música Clássica/Séria"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Música Tradicional/Folclore"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Procurar"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Procura do EPG termina após (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nível de correcção do EPG"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Cima/Baixo para nova posição - OK para mover"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal bloqueado (a gravar)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Espaço em disco baixo!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Grava
|
||||
msgid "restart anyway?"
|
||||
msgstr "reiniciar mesmo assim?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volume "
|
||||
|
23
po/ro_RO.po
23
po/ro_RO.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-11 22:26+0100\n"
|
||||
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
|
||||
"Language-Team: Romanian <vdr@linuxtv.org>\n"
|
||||
@ -132,7 +132,7 @@ msgstr "Magazin de ştiri"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Documentar"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Discuţie/Interviu/Dezbatere"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -210,7 +210,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Muzică clasică/serioasă"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Muzică folk/tradiţională"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -843,6 +843,9 @@ msgstr "Înregistrarea a dispărut!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Deja există o versiune editată - o suprascriu?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Eroare la punerea în coada pentru tăiere!"
|
||||
|
||||
@ -1026,6 +1029,12 @@ msgstr "Căutare canale"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Interval achiziţie EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivel corecţie EPG"
|
||||
|
||||
@ -1523,8 +1532,8 @@ msgstr "Plugin"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Sus/Jos pentru noua locaţie - OK pentru a muta"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Canal blocat (înregistrare)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Spaţiul pe disc e foarte scăzut!"
|
||||
@ -1567,9 +1576,13 @@ msgstr "Tocmai se înregistrează - repornesc, totuşi?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "repornesc, totuşi?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volum "
|
||||
|
23
po/ru_RU.po
23
po/ru_RU.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2016-12-27 17:13+0100\n"
|
||||
"Last-Translator: Pridvorov Andrey <ua0lnj@bk.ru>\n"
|
||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Новостной журнал"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документальные"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискуссия/Интервью/Дебаты"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Классическая музыка"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фольклор/Традиционная музыка"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr "Запись исчезла!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Изменённая версия уже есть - переписать?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Ошибка очереди записи для обрезки!"
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Сканировать"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Задержка сканирования телегида (ч)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Уровень коррекции ошибок"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Модуль"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Нажимайте \"Вверх\"/\"Вниз\" для выбора позиции, а затем \"OK\""
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Канал заблокирован (идёт запись)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Недостаточно места на диске!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Идёт запись - действительно перезапуст
|
||||
msgid "restart anyway?"
|
||||
msgstr "действительно перезапустить?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Громкость "
|
||||
|
23
po/sk_SK.po
23
po/sk_SK.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-17 18:59+0100\n"
|
||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||
"Language-Team: Slovak <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Spravodajsk
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentárny"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusia/Rozhovor/debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Vá¾na/Klasická hudba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "¥udová/Tradièná Hudba"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr "Z
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Upravená verzia u¾ existuje - prepísa»?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Chyba v poradí pri zostrihu nahrávky!"
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Skenova
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Interval snímania EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Úroveò opravy EPG chýb"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Hore/Dole na novú pozíciu - Ok presune"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanál je zamknutý (nahráva sa)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Nedostatok miesta na disku!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Nahr
|
||||
msgid "restart anyway?"
|
||||
msgstr "Urèite re¹tartova»?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Hlasitos» "
|
||||
|
23
po/sl_SI.po
23
po/sl_SI.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-04 12:46+0100\n"
|
||||
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
|
||||
"Language-Team: Slovenian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Poro
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentarec"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskudija/Interviju/Debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Resna/Klasièna Glasba"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Narodna/Tradicionalna glasba"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "I
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Èas do EPG pregleda (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivo za popravilo EPG napak"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Vstavek"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gor/Dol za novo poz. - Ok za premik"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Zaklenjen kanal (snemanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Premalo prostora na disku!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Snemanje - zares ponoven zagon?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "zares ponoven zagon?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Glasnost "
|
||||
|
23
po/sr_RS.po
23
po/sr_RS.po
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-16 15:05+0100\n"
|
||||
"Last-Translator: Zoran Turalija <zoran.turalija@gmail.com>\n"
|
||||
"Language-Team: Serbian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Novosti"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentarni"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskusija/Razgovor/Debata"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Rok/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Ozbiljna/Klasièna muzika"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Narodna/Izvorna muzika"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Pretra
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Interval do a¾uriranja EPG (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivo EPG korekcije gre¹aka"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Dodatak"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Gore/Dole za novu poziciju - OK za potvrdu"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal zakljuèan (snimanje)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Malo prostora na disku!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Snimanje u toku - svejedno ponovno pokreni!!!?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "svejedno ponovo pokreni?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Jaèina "
|
||||
|
23
po/sv_SE.po
23
po/sv_SE.po
@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2015-02-12 21:58+0100\n"
|
||||
"Last-Translator: Magnus Sirviö <sirwio@hotmail.com>\n"
|
||||
"Language-Team: Swedish <vdr@linuxtv.org>\n"
|
||||
@ -135,7 +135,7 @@ msgstr "Nyhetsmagasin"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Dokumentär"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Diskussion/Intervju/Debatt"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -213,7 +213,7 @@ msgstr "Rock/Pop"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Klassisk musik"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Folkmusik"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -846,6 +846,9 @@ msgstr "Inspelningen f
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "En redigerad version finns redan - skall den ersättas?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Ett fel upstod när inspelningen köades för klippning!"
|
||||
|
||||
@ -1029,6 +1032,12 @@ msgstr "Avs
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Tidsgräns för EPG-sökning (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Nivå för EPG bugfix"
|
||||
|
||||
@ -1526,8 +1535,8 @@ msgstr "Modul"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Upp/Ner för nya platsen - därefter OK för att flytta"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanalen är låst (inspelning pågår)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Lågt diskutrymme!"
|
||||
@ -1570,9 +1579,13 @@ msgstr "Inspelning p
|
||||
msgid "restart anyway?"
|
||||
msgstr "vill du starta om ändå?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volym "
|
||||
|
23
po/tr_TR.po
23
po/tr_TR.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2008-02-28 00:33+0100\n"
|
||||
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
|
||||
"Language-Team: Turkish <vdr@linuxtv.org>\n"
|
||||
@ -130,7 +130,7 @@ msgstr ""
|
||||
msgid "Content$Documentary"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -208,7 +208,7 @@ msgstr ""
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr ""
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -841,6 +841,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1024,6 +1027,12 @@ msgstr "Tara"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "EPG tarama zaman aþýmý (sa)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "EPG hata çözümleme ölçüsü"
|
||||
|
||||
@ -1521,8 +1530,8 @@ msgstr "Eklenti"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "'Yukarý/Aþaðý' yeni posizyon seç - 'Tamam' taþý"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Kanal geçersiz (kayýt ediliyor)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Kayýt kapasitesi az!"
|
||||
@ -1565,9 +1574,13 @@ msgstr "Kay
|
||||
msgid "restart anyway?"
|
||||
msgstr "buna raðmen yeniden baþlat?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Volüm "
|
||||
|
23
po/uk_UA.po
23
po/uk_UA.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2018-03-18 20:00+0100\n"
|
||||
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
|
||||
"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
|
||||
@ -131,7 +131,7 @@ msgstr "Журнал новин"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "Документальне"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "Дискусія/Інтерв’ю/Дебати"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
@ -209,7 +209,7 @@ msgstr "Рок/Поп"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "Серйозна/Класична музика"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "Фльк/Традиційна музика"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -842,6 +842,9 @@ msgstr "Запис зник!"
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr "Вже існує редагована версія - перезаписати?"
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr "Помилка перешуковування запису для обрізання!"
|
||||
|
||||
@ -1025,6 +1028,12 @@ msgstr "Сканувати"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "Затримка сканування телегіда (г)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "Рівень корекції помилок EPG"
|
||||
|
||||
@ -1522,8 +1531,8 @@ msgstr "Модуль"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "Натискайте \"Вверх\"/\"Вниз\" для вибору позиції, а потім \"OK\""
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "Канал заблоковано (йде запис)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "Недостатньо місця на диску!"
|
||||
@ -1566,9 +1575,13 @@ msgstr "Йде запис - дійсно перезапустити?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "дійсно перезапустити?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "Гучність "
|
||||
|
25
po/zh_CN.po
25
po/zh_CN.po
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2022-12-01 21:45+0100\n"
|
||||
"POT-Creation-Date: 2024-09-21 12:45+0200\n"
|
||||
"PO-Revision-Date: 2013-03-04 14:52+0800\n"
|
||||
"Last-Translator: NFVDR <nfvdr@live.com>\n"
|
||||
"Language-Team: Chinese (simplified) <nfvdr@live.com>\n"
|
||||
@ -132,8 +132,8 @@ msgstr "新闻杂志"
|
||||
msgid "Content$Documentary"
|
||||
msgstr "记录片"
|
||||
|
||||
msgid "Content$Discussion/Inverview/Debate"
|
||||
msgstr "讨论/ Inverview/辩论"
|
||||
msgid "Content$Discussion/Interview/Debate"
|
||||
msgstr "讨论/ Interview/辩论"
|
||||
|
||||
msgid "Content$Show/Game Show"
|
||||
msgstr "显示/游戏展"
|
||||
@ -210,7 +210,7 @@ msgstr "摇滚/流行"
|
||||
msgid "Content$Serious/Classical Music"
|
||||
msgstr "严重/古典音乐"
|
||||
|
||||
msgid "Content$Folk/Tradional Music"
|
||||
msgid "Content$Folk/Traditional Music"
|
||||
msgstr "民谣/传统体育专业课程设置的音乐"
|
||||
|
||||
msgid "Content$Jazz"
|
||||
@ -843,6 +843,9 @@ msgstr ""
|
||||
msgid "Edited version already exists - overwrite?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Not enough free disk space to start editing process!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error while queueing recording for cutting!"
|
||||
msgstr ""
|
||||
|
||||
@ -1026,6 +1029,12 @@ msgstr "扫描"
|
||||
msgid "Setup.EPG$EPG scan timeout (h)"
|
||||
msgstr "节目单扫描超时 (h)"
|
||||
|
||||
msgid "Setup.EPG$EPG scan max. channel number (0=all)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG pause after scan"
|
||||
msgstr ""
|
||||
|
||||
msgid "Setup.EPG$EPG bugfix level"
|
||||
msgstr "节目单问题修复级别"
|
||||
|
||||
@ -1523,8 +1532,8 @@ msgstr "插件"
|
||||
msgid "Up/Dn for new location - OK to move"
|
||||
msgstr "上/下 新的定位 - OK 移动"
|
||||
|
||||
msgid "Channel locked (recording)!"
|
||||
msgstr "频道已经锁定 (录像)!"
|
||||
msgid "Primary device has no MPEG decoder, can't attach player!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Low disk space!"
|
||||
msgstr "磁盘空间不足!"
|
||||
@ -1567,9 +1576,13 @@ msgstr "录像中,是否现在重启?"
|
||||
msgid "restart anyway?"
|
||||
msgstr "现在重启?"
|
||||
|
||||
#. TRANSLATORS: note the plural/singular!
|
||||
msgid "errors"
|
||||
msgstr ""
|
||||
|
||||
msgid "error"
|
||||
msgstr ""
|
||||
|
||||
#. TRANSLATORS: note the trailing blank!
|
||||
msgid "Volume "
|
||||
msgstr "音量调整 "
|
||||
|
203
recorder.c
203
recorder.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.c 5.4 2021/06/19 14:21:16 kls Exp $
|
||||
* $Id: recorder.c 5.10 2024/09/19 09:49:02 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recorder.h"
|
||||
@ -19,160 +19,18 @@
|
||||
#define MINFREEDISKSPACE (512) // MB
|
||||
#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(const char *FileName, const cChannel *Channel, int Priority)
|
||||
:cReceiver(Channel, Priority)
|
||||
,cThread("recording")
|
||||
{
|
||||
tsChecker = new cTsChecker;
|
||||
frameChecker = new cFrameChecker;
|
||||
recordingName = strdup(FileName);
|
||||
recordingInfo = new cRecordingInfo(recordingName);
|
||||
recordingInfo->Read();
|
||||
oldErrors = max(0, recordingInfo->Errors()); // in case this is a re-started recording
|
||||
errors = oldErrors;
|
||||
lastErrors = errors;
|
||||
errors = 0;
|
||||
lastErrors = 0;
|
||||
firstIframeSeen = false;
|
||||
|
||||
// Make sure the disk is up and running:
|
||||
@ -220,8 +78,6 @@ cRecorder::~cRecorder()
|
||||
delete fileName;
|
||||
delete frameDetector;
|
||||
delete ringBuffer;
|
||||
delete frameChecker;
|
||||
delete tsChecker;
|
||||
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:
|
||||
if (Force || time(NULL) - lastErrorLog >= ERROR_LOG_DELTA) {
|
||||
errors = tsChecker->Errors() + frameChecker->Errors();
|
||||
if (errors > lastErrors) {
|
||||
int d = errors - lastErrors;
|
||||
if (DebugChecks)
|
||||
fprintf(stderr, "%s: %s: %d error%s\n", *TimeToString(time(NULL)), recordingName, d, d > 1 ? "s" : "");
|
||||
esyslog("%s: %d error%s", recordingName, d, d > 1 ? "s" : "");
|
||||
esyslog("%s: %d new error%s (total %d)", recordingName, d, d > 1 ? "s" : "", oldErrors + errors);
|
||||
recordingInfo->SetErrors(oldErrors + errors);
|
||||
recordingInfo->Write();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
lastErrors = errors;
|
||||
}
|
||||
lastErrors = errors;
|
||||
lastErrorLog = time(NULL);
|
||||
}
|
||||
}
|
||||
@ -307,8 +160,6 @@ void cRecorder::Receive(const uchar *Data, int Length)
|
||||
int p = ringBuffer->Put(Data, Length);
|
||||
if (p != Length && Running())
|
||||
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);
|
||||
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()) {
|
||||
int r;
|
||||
uchar *b = ringBuffer->Get(r);
|
||||
@ -326,25 +186,39 @@ void cRecorder::Action(void)
|
||||
break;
|
||||
if (frameDetector->Synced()) {
|
||||
if (!InfoWritten) {
|
||||
if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(recordingInfo->FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(recordingInfo->FramesPerSecond(), frameDetector->FramesPerSecond())) {
|
||||
if ((frameDetector->FramesPerSecond() > 0 && DoubleEqual(recordingInfo->FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(recordingInfo->FramesPerSecond(), frameDetector->FramesPerSecond())) ||
|
||||
frameDetector->FrameWidth() != recordingInfo->FrameWidth() ||
|
||||
frameDetector->FrameHeight() != recordingInfo->FrameHeight() ||
|
||||
frameDetector->AspectRatio() != recordingInfo->AspectRatio()) {
|
||||
recordingInfo->SetFramesPerSecond(frameDetector->FramesPerSecond());
|
||||
recordingInfo->SetFrameParams(frameDetector->FrameWidth(), frameDetector->FrameHeight(), frameDetector->ScanType(), frameDetector->AspectRatio());
|
||||
recordingInfo->Write();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
}
|
||||
InfoWritten = true;
|
||||
cRecordingUserCommand::InvokeCommand(RUC_STARTRECORDING, recordingName);
|
||||
frameChecker->SetFrameDelta(PTSTICKS / frameDetector->FramesPerSecond());
|
||||
}
|
||||
if (firstIframeSeen || frameDetector->IndependentFrame()) {
|
||||
firstIframeSeen = true; // start recording with the first I-frame
|
||||
if (!NextFile())
|
||||
break;
|
||||
if (frameDetector->NewFrame()) {
|
||||
if (index)
|
||||
index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize);
|
||||
if (frameChecker)
|
||||
frameChecker->CheckFrame(b, Count);
|
||||
int PreviousErrors = 0;
|
||||
int MissingFrames = 0;
|
||||
if (frameDetector->NewFrame(&PreviousErrors, &MissingFrames)) {
|
||||
if (index) {
|
||||
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()) {
|
||||
recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE);
|
||||
@ -368,12 +242,17 @@ void cRecorder::Action(void)
|
||||
}
|
||||
}
|
||||
if (t.TimedOut()) {
|
||||
frameChecker->ReportBroken();
|
||||
if (pendNumber > 0)
|
||||
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
frameDetector->SetMissing();
|
||||
errors += MAXBROKENTIMEOUT / 1000 * frameDetector->FramesPerSecond();
|
||||
HandleErrors(true);
|
||||
esyslog("ERROR: video data stream broken");
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
t.Set(MAXBROKENTIMEOUT);
|
||||
}
|
||||
}
|
||||
if (pendNumber > 0)
|
||||
index->Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
HandleErrors(true);
|
||||
}
|
||||
|
10
recorder.h
10
recorder.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -16,13 +16,8 @@
|
||||
#include "ringbuffer.h"
|
||||
#include "thread.h"
|
||||
|
||||
class cTsChecker;
|
||||
class cFrameChecker;
|
||||
|
||||
class cRecorder : public cReceiver, cThread {
|
||||
private:
|
||||
cTsChecker *tsChecker;
|
||||
cFrameChecker *frameChecker;
|
||||
cRingBufferLinear *ringBuffer;
|
||||
cFrameDetector *frameDetector;
|
||||
cPatPmtGenerator patPmtGenerator;
|
||||
@ -56,6 +51,9 @@ public:
|
||||
virtual ~cRecorder();
|
||||
int Errors(void) { return oldErrors + errors; };
|
||||
///< 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
|
||||
|
274
recording.c
274
recording.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.c 5.22 2023/02/15 14:59:25 kls Exp $
|
||||
* $Id: recording.c 5.37 2025/01/18 20:57:06 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recording.h"
|
||||
@ -24,7 +24,6 @@
|
||||
#include "i18n.h"
|
||||
#include "interface.h"
|
||||
#include "menu.h"
|
||||
#include "remux.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "skins.h"
|
||||
#include "svdrp.h"
|
||||
@ -357,12 +356,17 @@ void cResumeFile::Delete(void)
|
||||
|
||||
cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
|
||||
{
|
||||
modified = 0;
|
||||
channelID = Channel ? Channel->GetChannelID() : tChannelID::InvalidID;
|
||||
channelName = Channel ? strdup(Channel->Name()) : NULL;
|
||||
ownEvent = Event ? NULL : new cEvent(0);
|
||||
event = ownEvent ? ownEvent : Event;
|
||||
aux = NULL;
|
||||
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
scanType = stUnknown;
|
||||
aspectRatio = arUnknown;
|
||||
priority = MAXPRIORITY;
|
||||
lifetime = MAXLIFETIME;
|
||||
fileName = NULL;
|
||||
@ -417,6 +421,7 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
|
||||
|
||||
cRecordingInfo::cRecordingInfo(const char *FileName)
|
||||
{
|
||||
modified = 0;
|
||||
channelID = tChannelID::InvalidID;
|
||||
channelName = NULL;
|
||||
ownEvent = new cEvent(0);
|
||||
@ -424,6 +429,10 @@ cRecordingInfo::cRecordingInfo(const char *FileName)
|
||||
aux = NULL;
|
||||
errors = -1;
|
||||
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
scanType = stUnknown;
|
||||
aspectRatio = arUnknown;
|
||||
priority = MAXPRIORITY;
|
||||
lifetime = MAXLIFETIME;
|
||||
fileName = strdup(cString::sprintf("%s%s", FileName, INFOFILESUFFIX));
|
||||
@ -458,6 +467,14 @@ void cRecordingInfo::SetFramesPerSecond(double FramesPerSecond)
|
||||
framesPerSecond = FramesPerSecond;
|
||||
}
|
||||
|
||||
void cRecordingInfo::SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio)
|
||||
{
|
||||
frameWidth = FrameWidth;
|
||||
frameHeight = FrameHeight;
|
||||
scanType = ScanType;
|
||||
aspectRatio = AspectRatio;
|
||||
}
|
||||
|
||||
void cRecordingInfo::SetFileName(const char *FileName)
|
||||
{
|
||||
bool IsPesRecording = fileName && endswith(fileName, ".vdr");
|
||||
@ -473,6 +490,12 @@ void cRecordingInfo::SetErrors(int Errors)
|
||||
bool cRecordingInfo::Read(FILE *f)
|
||||
{
|
||||
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;
|
||||
char *s;
|
||||
int line = 0;
|
||||
@ -504,10 +527,39 @@ bool cRecordingInfo::Read(FILE *f)
|
||||
ownEvent->SetDuration(Duration);
|
||||
ownEvent->SetTableID(uchar(TableID));
|
||||
ownEvent->SetVersion(uchar(Version));
|
||||
ownEvent->SetComponents(NULL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'F': framesPerSecond = atod(t);
|
||||
case 'F': {
|
||||
char *fpsBuf = NULL;
|
||||
char scanTypeCode;
|
||||
char *arBuf = NULL;
|
||||
int n = sscanf(t, "%m[^ ] %hu %hu %c %m[^\n]", &fpsBuf, &frameWidth, &frameHeight, &scanTypeCode, &arBuf);
|
||||
if (n >= 1) {
|
||||
framesPerSecond = atod(fpsBuf);
|
||||
if (n >= 4) {
|
||||
scanType = stUnknown;
|
||||
for (int st = stUnknown + 1; st < stMax; st++) {
|
||||
if (ScanTypeChars[st] == scanTypeCode) {
|
||||
scanType = eScanType(st);
|
||||
break;
|
||||
}
|
||||
}
|
||||
aspectRatio = arUnknown;
|
||||
if (n == 5) {
|
||||
for (int ar = arUnknown + 1; ar < arMax; ar++) {
|
||||
if (strcmp(arBuf, AspectRatioTexts[ar]) == 0) {
|
||||
aspectRatio = eAspectRatio(ar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(fpsBuf);
|
||||
free(arBuf);
|
||||
}
|
||||
break;
|
||||
case 'L': lifetime = atoi(t);
|
||||
break;
|
||||
@ -536,7 +588,10 @@ bool cRecordingInfo::Write(FILE *f, const char *Prefix) const
|
||||
if (channelID.Valid())
|
||||
fprintf(f, "%sC %s%s%s\n", Prefix, *channelID.ToString(), channelName ? " " : "", channelName ? channelName : "");
|
||||
event->Dump(f, Prefix, true);
|
||||
fprintf(f, "%sF %s\n", Prefix, *dtoa(framesPerSecond, "%.10g"));
|
||||
if (frameWidth > 0 && frameHeight > 0)
|
||||
fprintf(f, "%sF %s %s %s %c %s\n", Prefix, *dtoa(framesPerSecond, "%.10g"), *itoa(frameWidth), *itoa(frameHeight), ScanTypeChars[scanType], AspectRatioTexts[aspectRatio]);
|
||||
else
|
||||
fprintf(f, "%sF %s\n", Prefix, *dtoa(framesPerSecond, "%.10g"));
|
||||
fprintf(f, "%sP %d\n", Prefix, priority);
|
||||
fprintf(f, "%sL %d\n", Prefix, lifetime);
|
||||
fprintf(f, "%sO %d\n", Prefix, errors);
|
||||
@ -579,6 +634,27 @@ bool cRecordingInfo::Write(void) const
|
||||
return Result;
|
||||
}
|
||||
|
||||
cString cRecordingInfo::FrameParams(void) const
|
||||
{
|
||||
cString s;
|
||||
if (frameWidth && frameHeight) {
|
||||
s = cString::sprintf("%dx%d", frameWidth, frameHeight);
|
||||
if (framesPerSecond > 0) {
|
||||
if (*s)
|
||||
s.Append("/");
|
||||
s.Append(dtoa(framesPerSecond, "%.2g"));
|
||||
if (scanType != stUnknown)
|
||||
s.Append(ScanTypeChar());
|
||||
}
|
||||
if (aspectRatio != arUnknown) {
|
||||
if (*s)
|
||||
s.Append(" ");
|
||||
s.Append(AspectRatioText());
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// --- cRecording ------------------------------------------------------------
|
||||
|
||||
#define RESUME_NOT_INITIALIZED (-2)
|
||||
@ -1278,6 +1354,7 @@ bool cRecording::ChangeName(const char *NewName)
|
||||
fileName = strdup(OldFileName);
|
||||
return false;
|
||||
}
|
||||
info->SetFileName(NewFileName);
|
||||
isOnVideoDirectoryFileSystem = -1; // it might have been moved to a different file system
|
||||
ClearSortName();
|
||||
}
|
||||
@ -1379,6 +1456,17 @@ int cRecording::NumFrames(void) const
|
||||
return numFrames;
|
||||
}
|
||||
|
||||
int cRecording::NumFramesAfterEdit(void) const
|
||||
{
|
||||
int IndexLength = cIndexFile::GetLength(fileName, isPesRecording);
|
||||
if (IndexLength > 0) {
|
||||
cMarks Marks;
|
||||
if (Marks.Load(fileName, framesPerSecond, isPesRecording))
|
||||
return Marks.GetFrameAfterEdit(IndexLength - 1, IndexLength - 1);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cRecording::LengthInSeconds(void) const
|
||||
{
|
||||
int nf = NumFrames();
|
||||
@ -1387,6 +1475,14 @@ int cRecording::LengthInSeconds(void) const
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cRecording::LengthInSecondsAfterEdit(void) const
|
||||
{
|
||||
int nf = NumFramesAfterEdit();
|
||||
if (nf >= 0)
|
||||
return int(nf / FramesPerSecond());
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cRecording::FileSizeMB(void) const
|
||||
{
|
||||
if (fileSizeMB < 0) {
|
||||
@ -1475,7 +1571,8 @@ void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLev
|
||||
dsyslog("activated name checking for initial read of video directory");
|
||||
initial = false;
|
||||
}
|
||||
if (Recordings == deletedRecordings || initial || !Recordings->GetByName(buffer)) {
|
||||
cRecording *Recording = NULL;
|
||||
if (Recordings == deletedRecordings || initial || !(Recording = Recordings->GetByName(buffer))) {
|
||||
cRecording *r = new cRecording(buffer);
|
||||
if (r->Name()) {
|
||||
r->NumFrames(); // initializes the numFrames member
|
||||
@ -1489,6 +1586,8 @@ void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLev
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
else if (Recording)
|
||||
Recording->ReadInfo();
|
||||
StateKey.Remove();
|
||||
}
|
||||
else
|
||||
@ -1541,7 +1640,7 @@ const char *cRecordings::UpdateFileName(void)
|
||||
void cRecordings::TouchUpdate(void)
|
||||
{
|
||||
bool needsUpdate = NeedsUpdate();
|
||||
TouchFile(UpdateFileName());
|
||||
TouchFile(UpdateFileName(), true);
|
||||
if (!needsUpdate)
|
||||
lastUpdate = time(NULL); // make sure we don't trigger ourselves
|
||||
}
|
||||
@ -1988,8 +2087,10 @@ void cRecordingsHandlerEntry::Cleanup(cRecordings *Recordings)
|
||||
delete cutter;
|
||||
cutter = NULL;
|
||||
}
|
||||
cVideoDirectory::RemoveVideoFile(fileNameDst);
|
||||
if (cRecording *Recording = Recordings->GetByName(fileNameDst))
|
||||
Recording->Delete();
|
||||
Recordings->DelByName(fileNameDst);
|
||||
Recordings->SetModified();
|
||||
}
|
||||
}
|
||||
if ((usage & (ruMove | ruCopy)) // this was a move/copy operation...
|
||||
@ -2000,10 +2101,12 @@ void cRecordingsHandlerEntry::Cleanup(cRecordings *Recordings)
|
||||
delete copier;
|
||||
copier = NULL;
|
||||
}
|
||||
cVideoDirectory::RemoveVideoFile(fileNameDst);
|
||||
if (cRecording *Recording = Recordings->GetByName(fileNameDst))
|
||||
Recording->Delete();
|
||||
if ((usage & ruMove) != 0)
|
||||
Recordings->AddByName(fileNameSrc);
|
||||
Recordings->DelByName(fileNameDst);
|
||||
Recordings->SetModified();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2114,6 +2217,24 @@ int cRecordingsHandler::GetUsage(const char *FileName)
|
||||
return ruNone;
|
||||
}
|
||||
|
||||
int cRecordingsHandler::GetRequiredDiskSpaceMB(const char *FileName)
|
||||
{
|
||||
int RequiredDiskSpaceMB = 0;
|
||||
for (cRecordingsHandlerEntry *r = operations.First(); r; r = operations.Next(r)) {
|
||||
if ((r->Usage() & ruCanceled) != 0)
|
||||
continue;
|
||||
if ((r->Usage() & ruCut) != 0) {
|
||||
if (!FileName || EntriesOnSameFileSystem(FileName, r->FileNameDst()))
|
||||
RequiredDiskSpaceMB += FileSizeMBafterEdit(r->FileNameSrc());
|
||||
}
|
||||
else if ((r->Usage() & (ruMove | ruCopy)) != 0) {
|
||||
if (!FileName || EntriesOnSameFileSystem(FileName, r->FileNameDst()))
|
||||
RequiredDiskSpaceMB += DirSizeMB(r->FileNameSrc());
|
||||
}
|
||||
}
|
||||
return RequiredDiskSpaceMB;
|
||||
}
|
||||
|
||||
bool cRecordingsHandler::Finished(bool &Error)
|
||||
{
|
||||
cMutexLock MutexLock(&mutex);
|
||||
@ -2344,6 +2465,38 @@ int cMarks::GetNumSequences(void) const
|
||||
return NumSequences;
|
||||
}
|
||||
|
||||
int cMarks::GetFrameAfterEdit(int Frame, int LastFrame) const
|
||||
{
|
||||
if (Count() == 0 || LastFrame < 0 || Frame < 0 || Frame > LastFrame)
|
||||
return -1;
|
||||
int EditedFrame = 0;
|
||||
int PrevPos = -1;
|
||||
bool InEdit = false;
|
||||
for (const cMark *mi = First(); mi; mi = Next(mi)) {
|
||||
int p = mi->Position();
|
||||
if (InEdit) {
|
||||
EditedFrame += p - PrevPos;
|
||||
InEdit = false;
|
||||
if (Frame <= p) {
|
||||
EditedFrame -= p - Frame;
|
||||
return EditedFrame;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Frame <= p)
|
||||
return EditedFrame;
|
||||
PrevPos = p;
|
||||
InEdit = true;
|
||||
}
|
||||
}
|
||||
if (InEdit) {
|
||||
EditedFrame += LastFrame - PrevPos; // the last sequence had no actual "end" mark
|
||||
if (Frame < LastFrame)
|
||||
EditedFrame -= LastFrame - Frame;
|
||||
}
|
||||
return EditedFrame;
|
||||
}
|
||||
|
||||
// --- cRecordingUserCommand -------------------------------------------------
|
||||
|
||||
const char *cRecordingUserCommand::command = NULL;
|
||||
@ -2406,6 +2559,12 @@ void cIndexFileGenerator::Action(void)
|
||||
uint16_t FileNumber = 1;
|
||||
off_t FileOffset = 0;
|
||||
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) {
|
||||
// Look for current index and position to end of it if present:
|
||||
bool Independent;
|
||||
@ -2441,11 +2600,24 @@ void cIndexFileGenerator::Action(void)
|
||||
FrameOffset = FileSize; // the PAT/PMT is at the beginning of an I-frame
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.NewFrame()) {
|
||||
if (IndexFileWritten || Last < 0) // check for first frame and do not write if in update mode
|
||||
IndexFile.Write(FrameDetector.IndependentFrame(), FileName.Number(), FrameOffset >= 0 ? FrameOffset : FileSize);
|
||||
int PreviousErrors = 0;
|
||||
int MissingFrames = 0;
|
||||
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;
|
||||
IndexFileWritten = true;
|
||||
if (PreviousErrors)
|
||||
Errors++;
|
||||
if (MissingFrames)
|
||||
Errors++;
|
||||
}
|
||||
FileSize += Processed;
|
||||
Buffer.Del(Processed);
|
||||
@ -2453,7 +2625,7 @@ void cIndexFileGenerator::Action(void)
|
||||
}
|
||||
else if (PatPmtParser.Completed()) {
|
||||
// Step 2 - sync FrameDetector:
|
||||
int Processed = FrameDetector.Analyze(Data, Length);
|
||||
int Processed = FrameDetector.Analyze(Data, Length, false);
|
||||
if (Processed > 0) {
|
||||
if (FrameDetector.Synced()) {
|
||||
// Synced FrameDetector, so rewind for actual processing:
|
||||
@ -2511,6 +2683,8 @@ void cIndexFileGenerator::Action(void)
|
||||
}
|
||||
// Recording has been processed:
|
||||
else {
|
||||
if (pendNumber > 0)
|
||||
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
IndexFileComplete = true;
|
||||
break;
|
||||
}
|
||||
@ -2520,8 +2694,14 @@ void cIndexFileGenerator::Action(void)
|
||||
if (IndexFileWritten) {
|
||||
cRecordingInfo RecordingInfo(recordingName);
|
||||
if (RecordingInfo.Read()) {
|
||||
if (FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) {
|
||||
if ((FrameDetector.FramesPerSecond() > 0 && !DoubleEqual(RecordingInfo.FramesPerSecond(), FrameDetector.FramesPerSecond())) ||
|
||||
FrameDetector.FrameWidth() != RecordingInfo.FrameWidth() ||
|
||||
FrameDetector.FrameHeight() != RecordingInfo.FrameHeight() ||
|
||||
FrameDetector.AspectRatio() != RecordingInfo.AspectRatio() ||
|
||||
Errors != RecordingInfo.Errors()) {
|
||||
RecordingInfo.SetFramesPerSecond(FrameDetector.FramesPerSecond());
|
||||
RecordingInfo.SetFrameParams(FrameDetector.FrameWidth(), FrameDetector.FrameHeight(), FrameDetector.ScanType(), FrameDetector.AspectRatio());
|
||||
RecordingInfo.SetErrors(Errors);
|
||||
RecordingInfo.Write();
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->UpdateByName(recordingName);
|
||||
@ -2554,13 +2734,17 @@ struct __attribute__((packed)) tIndexPes {
|
||||
|
||||
struct __attribute__((packed)) tIndexTs {
|
||||
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)
|
||||
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;
|
||||
reserved = 0;
|
||||
errors = Errors;
|
||||
missing = Missing;
|
||||
independent = Independent;
|
||||
number = Number;
|
||||
}
|
||||
@ -2576,6 +2760,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
|
||||
f = -1;
|
||||
size = 0;
|
||||
last = -1;
|
||||
lastErrorIndex = last;
|
||||
index = NULL;
|
||||
isPesRecording = IsPesRecording;
|
||||
indexFileGenerator = NULL;
|
||||
@ -2753,10 +2938,10 @@ bool cIndexFile::CatchUp(int Index)
|
||||
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) {
|
||||
tIndexTs i(FileOffset, Independent, FileNumber);
|
||||
tIndexTs i(FileOffset, Independent, FileNumber, Errors, Missing);
|
||||
if (isPesRecording)
|
||||
ConvertToPes(&i, 1);
|
||||
if (safe_write(f, &i, sizeof(i)) < 0) {
|
||||
@ -2770,7 +2955,7 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
|
||||
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 (Index >= 0 && Index <= last) {
|
||||
@ -2790,12 +2975,27 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
|
||||
else
|
||||
*Length = -1;
|
||||
}
|
||||
if (Errors)
|
||||
*Errors = index[Index].errors;
|
||||
if (Missing)
|
||||
*Missing = index[Index].missing;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (CatchUp()) {
|
||||
@ -3157,7 +3357,7 @@ void cDoneRecordings::Append(const char *Title)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *FuzzyChars = " -:";
|
||||
static const char *FuzzyChars = " -:/";
|
||||
|
||||
static const char *SkipFuzzyChars(const char *s)
|
||||
{
|
||||
@ -3308,3 +3508,39 @@ cString GetRecordingTimerId(const char *Directory)
|
||||
}
|
||||
return Id;
|
||||
}
|
||||
|
||||
// --- Disk space calculation for editing ------------------------------------
|
||||
|
||||
int FileSizeMBafterEdit(const char *FileName)
|
||||
{
|
||||
int FileSizeMB = DirSizeMB(FileName);
|
||||
if (FileSizeMB > 0) {
|
||||
cRecording r(FileName);
|
||||
int NumFramesOrg = r.NumFrames();
|
||||
if (NumFramesOrg > 0) {
|
||||
int NumFramesEdit = r.NumFramesAfterEdit();
|
||||
if (NumFramesEdit > 0)
|
||||
return max(1, int(FileSizeMB * (double(NumFramesEdit) / NumFramesOrg)));
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool EnoughFreeDiskSpaceForEdit(const char *FileName)
|
||||
{
|
||||
int FileSizeMB = FileSizeMBafterEdit(FileName);
|
||||
if (FileSizeMB > 0) {
|
||||
int FreeDiskMB;
|
||||
cVideoDirectory::VideoDiskSpace(&FreeDiskMB);
|
||||
cString EditedFileName = cCutter::EditedFileName(FileName);
|
||||
if (access(EditedFileName, F_OK)) {
|
||||
int ExistingEditedSizeMB = DirSizeMB(EditedFileName);
|
||||
if (ExistingEditedSizeMB > 0)
|
||||
FreeDiskMB += ExistingEditedSizeMB;
|
||||
}
|
||||
FreeDiskMB -= RecordingsHandler.GetRequiredDiskSpaceMB(FileName);
|
||||
FreeDiskMB -= MINDISKSPACE;
|
||||
return FileSizeMB < FreeDiskMB;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
45
recording.h
45
recording.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recording.h 5.5 2021/05/23 15:03:17 kls Exp $
|
||||
* $Id: recording.h 5.12 2025/01/15 10:50:29 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __RECORDING_H
|
||||
@ -17,6 +17,7 @@
|
||||
#include "thread.h"
|
||||
#include "timers.h"
|
||||
#include "tools.h"
|
||||
#include "remux.h"
|
||||
|
||||
#define FOLDERDELIMCHAR '~'
|
||||
|
||||
@ -63,12 +64,17 @@ public:
|
||||
class cRecordingInfo {
|
||||
friend class cRecording;
|
||||
private:
|
||||
time_t modified;
|
||||
tChannelID channelID;
|
||||
char *channelName;
|
||||
const cEvent *event;
|
||||
cEvent *ownEvent;
|
||||
char *aux;
|
||||
double framesPerSecond;
|
||||
uint16_t frameWidth;
|
||||
uint16_t frameHeight;
|
||||
eScanType scanType;
|
||||
eAspectRatio aspectRatio;
|
||||
int priority;
|
||||
int lifetime;
|
||||
char *fileName;
|
||||
@ -87,7 +93,15 @@ public:
|
||||
const cComponents *Components(void) const { return event->Components(); }
|
||||
const char *Aux(void) const { return aux; }
|
||||
double FramesPerSecond(void) const { return framesPerSecond; }
|
||||
uint16_t FrameWidth(void) const { return frameWidth; }
|
||||
uint16_t FrameHeight(void) const { return frameHeight; }
|
||||
eScanType ScanType(void) const { return scanType; }
|
||||
char ScanTypeChar(void) const { return ScanTypeChars[scanType]; }
|
||||
eAspectRatio AspectRatio(void) const { return aspectRatio; }
|
||||
const char *AspectRatioText(void) const { return AspectRatioTexts[aspectRatio]; }
|
||||
cString FrameParams(void) const;
|
||||
void SetFramesPerSecond(double FramesPerSecond);
|
||||
void SetFrameParams(uint16_t FrameWidth, uint16_t FrameHeight, eScanType ScanType, eAspectRatio AspectRatio);
|
||||
void SetFileName(const char *FileName);
|
||||
int Errors(void) const { return errors; } // returns -1 if undefined
|
||||
void SetErrors(int Errors);
|
||||
@ -161,8 +175,15 @@ public:
|
||||
int NumFrames(void) const;
|
||||
///< Returns the number of frames in this recording.
|
||||
///< If the number of frames is unknown, -1 will be returned.
|
||||
int NumFramesAfterEdit(void) const;
|
||||
///< Returns the number of frames in the edited version of this recording.
|
||||
///< If there are no editing marks, 0 will be returned.
|
||||
///< If the number of frames is unknown, -1 will be returned.
|
||||
int LengthInSeconds(void) const;
|
||||
///< Returns the length (in seconds) of this recording, or -1 in case of error.
|
||||
int LengthInSecondsAfterEdit(void) const;
|
||||
///< Returns the length (in seconds) of the edited version of this recording, or -1 in case of error.
|
||||
///< If there are no editing marks, 0 will be returned.
|
||||
int FileSizeMB(void) const;
|
||||
///< Returns the total file size of this recording (in MB), or -1 if the file
|
||||
///< size is unknown.
|
||||
@ -340,6 +361,10 @@ public:
|
||||
///< Deletes/terminates all operations.
|
||||
int GetUsage(const char *FileName);
|
||||
///< Returns the usage type for the given FileName.
|
||||
int GetRequiredDiskSpaceMB(const char *FileName = NULL);
|
||||
///< Returns the total disk space required to process all actions.
|
||||
///< If FileName is given, only the drive that contains that file is taken
|
||||
///< into account.
|
||||
bool Finished(bool &Error);
|
||||
///< Returns true if all operations in the list have been finished.
|
||||
///< If there have been any errors, Errors will be set to true.
|
||||
@ -411,6 +436,10 @@ public:
|
||||
///< 0 (the beginning of the recording), and there is no "end" mark, the
|
||||
///< return value is 0, which means that the result is the same as the original
|
||||
///< recording.
|
||||
int GetFrameAfterEdit(int Frame, int LastFrame) const;
|
||||
///< Returns the number of the given Frame within the region covered by begin/end sequences.
|
||||
///< LastFrame must be given by the caller.
|
||||
///< If there are no editing marks or in case of an error -1 is returned.
|
||||
cMark *Get(int Position) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->Get(Position)); }
|
||||
cMark *GetPrev(int Position) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->GetPrev(Position)); }
|
||||
cMark *GetNext(int Position) { return const_cast<cMark *>(static_cast<const cMarks *>(this)->GetNext(Position)); }
|
||||
@ -418,6 +447,9 @@ public:
|
||||
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_STARTRECORDING "started"
|
||||
#define RUC_AFTERRECORDING "after"
|
||||
@ -458,9 +490,11 @@ private:
|
||||
int f;
|
||||
cString fileName;
|
||||
int size, last;
|
||||
int lastErrorIndex;
|
||||
tIndexTs *index;
|
||||
bool isPesRecording;
|
||||
cResumeFile resumeFile;
|
||||
cErrors errors;
|
||||
cIndexFileGenerator *indexFileGenerator;
|
||||
cMutex mutex;
|
||||
void ConvertFromPes(tIndexTs *IndexTs, int Count);
|
||||
@ -470,8 +504,10 @@ public:
|
||||
cIndexFile(const char *FileName, bool Record, bool IsPesRecording = false, bool PauseLive = false, bool Update = false);
|
||||
~cIndexFile();
|
||||
bool Ok(void) { return index != NULL; }
|
||||
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
|
||||
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL);
|
||||
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 *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 GetClosestIFrame(int Index);
|
||||
///< Returns the index of the I-frame that is closest to the given Index (or Index itself,
|
||||
@ -557,4 +593,7 @@ void IncRecordingsSortMode(const char *Directory);
|
||||
void SetRecordingTimerId(const char *Directory, const char *TimerId);
|
||||
cString GetRecordingTimerId(const char *Directory);
|
||||
|
||||
int FileSizeMBafterEdit(const char *FileName);
|
||||
bool EnoughFreeDiskSpaceForEdit(const char *FileName);
|
||||
|
||||
#endif //__RECORDING_H
|
||||
|
259
remux.c
259
remux.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.c 5.5 2022/11/30 14:38:46 kls Exp $
|
||||
* $Id: remux.c 5.18 2024/12/05 10:37:15 kls Exp $
|
||||
*/
|
||||
|
||||
#include "remux.h"
|
||||
@ -287,6 +287,8 @@ uchar cTsPayload::GetByte(void)
|
||||
if (TsPid(p) == pid) { // only handle TS packets for the initial PID
|
||||
if (++numPacketsPid > MAX_TS_PACKETS_FOR_VIDEO_FRAME_DETECTION)
|
||||
return SetEof();
|
||||
if (TsError(p))
|
||||
return SetEof(); // don't parse TS packets with errors
|
||||
if (TsHasPayload(p)) {
|
||||
if (index > 0 && TsPayloadStart(p)) // checking index to not skip the very first TS packet
|
||||
return SetEof();
|
||||
@ -1177,7 +1179,8 @@ protected:
|
||||
uint16_t frameWidth;
|
||||
uint16_t frameHeight;
|
||||
double framesPerSecond;
|
||||
bool progressive;
|
||||
eScanType scanType;
|
||||
eAspectRatio aspectRatio;
|
||||
public:
|
||||
cFrameParser(void);
|
||||
virtual ~cFrameParser() {};
|
||||
@ -1195,7 +1198,8 @@ public:
|
||||
uint16_t FrameWidth(void) { return frameWidth; }
|
||||
uint16_t FrameHeight(void) { return frameHeight; }
|
||||
double FramesPerSecond(void) { return framesPerSecond; }
|
||||
bool Progressive(void) { return progressive; }
|
||||
eScanType ScanType(void) { return scanType; }
|
||||
eAspectRatio AspectRatio(void) { return aspectRatio; }
|
||||
};
|
||||
|
||||
cFrameParser::cFrameParser(void)
|
||||
@ -1207,7 +1211,8 @@ cFrameParser::cFrameParser(void)
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
framesPerSecond = 0.0;
|
||||
progressive = false;
|
||||
scanType = stUnknown;
|
||||
aspectRatio = arUnknown;
|
||||
}
|
||||
|
||||
// --- cAudioParser ----------------------------------------------------------
|
||||
@ -1322,17 +1327,24 @@ int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||
uchar b = tsPayload.GetByte(); // ignoring two MSB of width and height in sequence extension
|
||||
frameWidth |= b >> 4; // as 12 Bit = max 4095 should be sufficient for all available MPEG2 streams
|
||||
frameHeight = (b & 0x0F) << 8 | tsPayload.GetByte();
|
||||
b = tsPayload.GetByte();
|
||||
b = tsPayload.GetByte(); // hi: aspect ratio info, lo: frame rate code
|
||||
switch (b >> 4) {
|
||||
case 1: aspectRatio = ar_1_1; break;
|
||||
case 2: aspectRatio = ar_4_3; break;
|
||||
case 3: aspectRatio = ar_16_9; break;
|
||||
case 4: aspectRatio = ar_2_21_1; break;
|
||||
default: aspectRatio = arUnknown;
|
||||
}
|
||||
uchar frame_rate_value = b & 0x0F;
|
||||
if (frame_rate_value > 0 && frame_rate_value <= 8)
|
||||
framesPerSecond = frame_rate_table[frame_rate_value];
|
||||
}
|
||||
else if (!seenScanType && scanner == 0x000001B5) { // Extension start code
|
||||
if ((tsPayload.GetByte() & 0xF0) == 0x10) { // Sequence Extension
|
||||
progressive = (tsPayload.GetByte() & 0x40) != 0;
|
||||
scanType = (tsPayload.GetByte() & 0x40) ? stProgressive : stInterlaced;
|
||||
seenScanType = true;
|
||||
if (debug) {
|
||||
cString s = cString::sprintf("MPEG2: %d x %d%c %.2f fps", frameWidth, frameHeight, progressive ? 'p' : 'i', framesPerSecond);
|
||||
cString s = cString::sprintf("MPEG2: %d x %d%c %.2f fps %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, AspectRatioTexts[aspectRatio]);
|
||||
dsyslog("%s", *s);
|
||||
dbgframes("\n%s", *s);
|
||||
}
|
||||
@ -1553,7 +1565,7 @@ void cH264Parser::ParseSequenceParameterSet(void)
|
||||
uint16_t frame_Height = 16 * (1 + GetGolombUe()); // pic_height_in_map_units_minus1
|
||||
frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag
|
||||
if (frameWidth == 0) {
|
||||
progressive = frame_mbs_only_flag;
|
||||
scanType = frame_mbs_only_flag ? stProgressive : stInterlaced;
|
||||
if (!frame_mbs_only_flag) {
|
||||
GetBit(); // mb_adaptive_frame_field_flag
|
||||
frame_Height *= 2;
|
||||
@ -1577,6 +1589,8 @@ void cH264Parser::ParseSequenceParameterSet(void)
|
||||
}
|
||||
frame_Width -= CropUnitX * (frame_crop_left_offset + frame_crop_right_offset);
|
||||
frame_Height -= CropUnitY * (frame_crop_top_offset + frame_crop_bottom_offset);
|
||||
if (frame_Height > 1080 && frame_Height <= 1090) // workaround for channels with wrong crop parameters
|
||||
frame_Height = 1080;
|
||||
}
|
||||
frameWidth = frame_Width;
|
||||
frameHeight = frame_Height;
|
||||
@ -1584,8 +1598,11 @@ void cH264Parser::ParseSequenceParameterSet(void)
|
||||
if (GetBit()) { // vui_parameters_present_flag
|
||||
if (GetBit()) { // aspect_ratio_info_present
|
||||
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
|
||||
if (aspect_ratio_idc == 255)
|
||||
GetBits(32);
|
||||
if (aspect_ratio_idc == 255) // EXTENDED_SAR
|
||||
GetBits(32); // sar_width, sar_height
|
||||
else if (frameHeight >= 720 && (aspect_ratio_idc == 1 || aspect_ratio_idc == 14 || aspect_ratio_idc == 15 || aspect_ratio_idc == 16))
|
||||
aspectRatio = ar_16_9;
|
||||
// implement decoding of other aspect_ratio_idc values when they are required
|
||||
}
|
||||
if (GetBit()) // overscan_info_present_flag
|
||||
GetBit(); // overscan_approriate_flag
|
||||
@ -1606,7 +1623,7 @@ void cH264Parser::ParseSequenceParameterSet(void)
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
|
||||
cString s = cString::sprintf("H.264: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
|
||||
dsyslog("%s", *s);
|
||||
dbgframes("\n%s", *s);
|
||||
}
|
||||
@ -1728,7 +1745,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
uint8_t sub_layer_profile_present_flag[8];
|
||||
uint8_t sub_layer_level_present_flag[8];
|
||||
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
|
||||
// begin profile_tier_level(1, sps_max_sub_layers_minus1)
|
||||
GetByte();
|
||||
@ -1737,7 +1754,7 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
GetByte();
|
||||
GetByte();
|
||||
bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
|
||||
progressive = general_progressive_source_flag;
|
||||
scanType = general_progressive_source_flag ? stProgressive : stInterlaced;
|
||||
GetBit(); // general_interlaced_source_flag
|
||||
GetBits(6);
|
||||
GetByte();
|
||||
@ -1880,6 +1897,9 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
int aspect_ratio_idc = GetBits(8); // aspect_ratio_idc
|
||||
if (aspect_ratio_idc == 255) // EXTENDED_SAR
|
||||
GetBits(32); // sar_width, sar_height
|
||||
else if (aspect_ratio_idc == 1 || aspect_ratio_idc == 14)
|
||||
aspectRatio = ar_16_9;
|
||||
// implement decoding of other aspect_ratio_idc values when they are required
|
||||
}
|
||||
if (GetBit()) // overscan_info_present_flag
|
||||
GetBit(); // overscan_appropriate_flag
|
||||
@ -1907,14 +1927,181 @@ void cH265Parser::ParseSequenceParameterSet(void)
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit", frameWidth, frameHeight, progressive ? 'p':'i', framesPerSecond, bitDepth);
|
||||
cString s = cString::sprintf("H.265: %d x %d%c %.2f fps %d Bit %s", frameWidth, frameHeight, ScanTypeChars[scanType], framesPerSecond, bitDepth, AspectRatioTexts[aspectRatio]);
|
||||
dsyslog("%s", *s);
|
||||
dbgframes("\n%s", *s);
|
||||
}
|
||||
}
|
||||
|
||||
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 --------------------------------------------------------
|
||||
|
||||
const char *ScanTypeChars = "-pi"; // index is eScanType
|
||||
const char *AspectRatioTexts[] = { // index is eAspectRatio
|
||||
"-",
|
||||
"1:1",
|
||||
"4:3",
|
||||
"16:9",
|
||||
"2.21:1",
|
||||
NULL
|
||||
};
|
||||
|
||||
cFrameDetector::cFrameDetector(int Pid, int Type)
|
||||
{
|
||||
parser = NULL;
|
||||
@ -1924,8 +2111,19 @@ cFrameDetector::cFrameDetector(int Pid, int Type)
|
||||
numPtsValues = 0;
|
||||
numIFrames = 0;
|
||||
framesPerSecond = 0;
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
scanType = stUnknown;
|
||||
aspectRatio = arUnknown;
|
||||
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
||||
scanning = false;
|
||||
firstIframeSeen = false;
|
||||
frameChecker = new cFrameChecker;
|
||||
}
|
||||
|
||||
cFrameDetector::~cFrameDetector()
|
||||
{
|
||||
delete frameChecker;
|
||||
}
|
||||
|
||||
static int CmpUint32(const void *p1, const void *p2)
|
||||
@ -1954,7 +2152,23 @@ void cFrameDetector::SetPid(int Pid, int Type)
|
||||
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)
|
||||
return 0;
|
||||
@ -1984,13 +2198,21 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
if (parser->NewFrame()) {
|
||||
newFrame = true;
|
||||
independentFrame = parser->IndependentFrame();
|
||||
firstIframeSeen |= independentFrame;
|
||||
if (synced) {
|
||||
if (ErrorCheck)
|
||||
frameChecker->CheckFrame(Data, n, independentFrame);
|
||||
if (framesPerPayloadUnit <= 1)
|
||||
scanning = false;
|
||||
}
|
||||
else {
|
||||
if (parser->FramesPerSecond() > 0.0) {
|
||||
framesPerSecond = parser->FramesPerSecond();
|
||||
frameWidth = parser->FrameWidth();
|
||||
frameHeight = parser->FrameHeight();
|
||||
scanType = parser->ScanType();
|
||||
aspectRatio = parser->AspectRatio();
|
||||
frameChecker->SetFrameDelta(PTSTICKS / framesPerSecond);
|
||||
synced = true;
|
||||
parser->SetDebug(false);
|
||||
}
|
||||
@ -2007,7 +2229,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
if (framesPerSecond <= 0.0) {
|
||||
// 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 (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);
|
||||
if (numIFrames && PesHasPts(Pes)) {
|
||||
ptsValues[numPtsValues] = PesGetPts(Pes);
|
||||
@ -2049,11 +2271,12 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
framesPerSecond = 60.0 / 1.001;
|
||||
else {
|
||||
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
|
||||
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());
|
||||
synced = true;
|
||||
parser->SetDebug(false);
|
||||
@ -2064,6 +2287,8 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||
else if (Pid == PATPID && synced && Processed)
|
||||
return Processed; // allow the caller to see any PAT packets
|
||||
}
|
||||
if (firstIframeSeen && ErrorCheck)
|
||||
frameChecker->CheckTs(Data, Handled);
|
||||
Data += Handled;
|
||||
Length -= Handled;
|
||||
Processed += Handled;
|
||||
|
53
remux.h
53
remux.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: remux.h 5.2 2021/12/25 14:11:39 kls Exp $
|
||||
* $Id: remux.h 5.8 2024/12/04 14:33:22 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __REMUX_H
|
||||
@ -504,6 +504,27 @@ void PesDump(const char *Name, const u_char *Data, int Length);
|
||||
|
||||
class cFrameParser;
|
||||
|
||||
enum eScanType {
|
||||
stUnknown = 0,
|
||||
stProgressive = 1,
|
||||
stInterlaced = 2,
|
||||
stMax
|
||||
};
|
||||
|
||||
enum eAspectRatio {
|
||||
arUnknown = 0,
|
||||
ar_1_1 = 1,
|
||||
ar_4_3 = 2,
|
||||
ar_16_9 = 3,
|
||||
ar_2_21_1 = 4,
|
||||
arMax
|
||||
};
|
||||
|
||||
extern const char *ScanTypeChars;
|
||||
extern const char *AspectRatioTexts[];
|
||||
|
||||
class cFrameChecker;
|
||||
|
||||
class cFrameDetector {
|
||||
private:
|
||||
enum { MaxPtsValues = 150 };
|
||||
@ -517,29 +538,45 @@ private:
|
||||
int numIFrames;
|
||||
bool isVideo;
|
||||
double framesPerSecond;
|
||||
uint16_t frameWidth;
|
||||
uint16_t frameHeight;
|
||||
eScanType scanType;
|
||||
eAspectRatio aspectRatio;
|
||||
int framesInPayloadUnit;
|
||||
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
||||
// while others put an entire GOP into one payload unit (> 1).
|
||||
bool scanning;
|
||||
bool firstIframeSeen;
|
||||
cFrameParser *parser;
|
||||
public:
|
||||
cFrameChecker *frameChecker;
|
||||
cFrameDetector(int Pid = 0, int Type = 0);
|
||||
///< 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
|
||||
///< call to SetPid().
|
||||
~cFrameDetector();
|
||||
void SetPid(int Pid, int Type);
|
||||
///< 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
|
||||
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
||||
///< Returns the number of bytes that have been analyzed.
|
||||
///< If the return value is 0, the data was not sufficient for analyzing and
|
||||
///< 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; }
|
||||
///< 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
|
||||
///< 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; }
|
||||
///< 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
|
||||
@ -547,6 +584,14 @@ public:
|
||||
double FramesPerSecond(void) { return framesPerSecond; }
|
||||
///< Returns the number of frames per second, or 0 if this information is not
|
||||
///< available.
|
||||
uint16_t FrameWidth(void) { return frameWidth; }
|
||||
///< Returns the frame width, or 0 if this information is not available.
|
||||
uint16_t FrameHeight(void) { return frameHeight; }
|
||||
///< Returns the frame height, or 0 if this information is not available.
|
||||
eScanType ScanType(void) { return scanType; }
|
||||
///< Returns the scan type, or stUnknown if this information is not available.
|
||||
eAspectRatio AspectRatio(void) { return aspectRatio; }
|
||||
///< Returns the aspect ratio, or arUnknown if this information is not available.
|
||||
};
|
||||
|
||||
#endif // __REMUX_H
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinclassic.c 5.1 2021/07/01 15:40:46 kls Exp $
|
||||
* $Id: skinclassic.c 5.4 2024/09/21 10:53:07 kls Exp $
|
||||
*/
|
||||
|
||||
#include "skinclassic.h"
|
||||
@ -71,6 +71,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
|
||||
|
||||
// --- cSkinClassicDisplayChannel --------------------------------------------
|
||||
|
||||
@ -402,7 +403,8 @@ void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + 5;
|
||||
}
|
||||
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);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVpsFg), Theme.Color(clrMenuEventVpsBg), font, w);
|
||||
@ -421,7 +423,13 @@ void cSkinClassicDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
}
|
||||
y += font->Height();
|
||||
if (!isempty(Info->Description())) {
|
||||
textScroller.Set(osd, x1, y, x2 - x1, y3 - y, Info->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
|
||||
cString d = Info->Description();
|
||||
cString f = Info->FrameParams();
|
||||
if (*f) {
|
||||
d.Append("\n\n");
|
||||
d.Append(f);
|
||||
}
|
||||
textScroller.Set(osd, x1, y, x2 - x1, y3 - y, d, font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
|
||||
SetTextScrollbar();
|
||||
}
|
||||
}
|
||||
@ -528,7 +536,7 @@ void cSkinClassicDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
19
skinlcars.c
19
skinlcars.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinlcars.c 5.3 2021/07/01 15:40:46 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,
|
||||
@ -188,6 +188,7 @@ THEME_CLR(Theme, clrReplayProgressRest, RgbShade(CLR_WHITE, -0.2));
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, CLR_EXPOSED);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, CLR_BLACK);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, CLR_EXPOSED);
|
||||
THEME_CLR(Theme, clrReplayProgressError, CLR_BLACK);
|
||||
|
||||
// Track display:
|
||||
|
||||
@ -1258,6 +1259,7 @@ void cSkinLCARSDisplayMenu::DrawTimers(void)
|
||||
int NumDevices = 0;
|
||||
int y = ys04;
|
||||
// Timers and recording devices:
|
||||
LOCK_SCHEDULES_READ;
|
||||
while (1) {
|
||||
int NumTimers = 0;
|
||||
const cDevice *Device = NULL;
|
||||
@ -1627,7 +1629,7 @@ void cSkinLCARSDisplayMenu::SetItem(const char *Text, int Index, bool Current, b
|
||||
if (!Tab(i + 1))
|
||||
break;
|
||||
}
|
||||
SetEditableWidth(xi02 - xi00 - TextSpacing - Tab(1));
|
||||
SetEditableWidth(xi01 - xi00 - TextSpacing - Tab(1));
|
||||
}
|
||||
|
||||
void cSkinLCARSDisplayMenu::SetScrollbar(int Total, int Offset)
|
||||
@ -1702,7 +1704,8 @@ void cSkinLCARSDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + xi02 - xi01;
|
||||
}
|
||||
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);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuFrameFg), frameColor, font, w);
|
||||
@ -1729,7 +1732,13 @@ void cSkinLCARSDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
if (!isempty(Info->Description())) {
|
||||
int yt = y;
|
||||
int yb = yi01;
|
||||
textScroller.Set(osd, xl, yt, xi01 - xl, yb - yt, Info->Description(), font, Theme.Color(clrEventDescription), Theme.Color(clrBackground));
|
||||
cString d = Info->Description();
|
||||
cString f = Info->FrameParams();
|
||||
if (*f) {
|
||||
d.Append("\n\n");
|
||||
d.Append(f);
|
||||
}
|
||||
textScroller.Set(osd, xl, yt, xi01 - xl, yb - yt, d, font, Theme.Color(clrEventDescription), Theme.Color(clrBackground));
|
||||
DrawTextScrollbar();
|
||||
}
|
||||
}
|
||||
@ -1922,7 +1931,7 @@ void cSkinLCARSDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
48
skins.c
48
skins.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skins.c 4.2 2019/05/29 16:43:09 kls Exp $
|
||||
* $Id: skins.c 5.5 2025/02/12 21:18:53 kls Exp $
|
||||
*/
|
||||
|
||||
#include "skins.h"
|
||||
@ -139,15 +139,20 @@ int cSkinDisplayMenu::GetTextAreaWidth(void) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool) const
|
||||
const cFont *cSkinDisplayMenu::GetTextAreaFont(bool FixedFont) const
|
||||
{
|
||||
return NULL;
|
||||
return cFont::GetFont(FixedFont ? fontFix : fontOsd);
|
||||
}
|
||||
|
||||
// --- 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)
|
||||
: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;
|
||||
if (total > 0) {
|
||||
@ -162,12 +167,22 @@ cSkinDisplayReplay::cProgressBar::cProgressBar(int Width, int Height, int Curren
|
||||
const cMark *m2 = Marks->Next(m);
|
||||
int p2 = Pos(m2 ? m2->Position() : total);
|
||||
int h = Height / 3;
|
||||
DrawRectangle(p1, h, p2, Height - h, ColorSelected);
|
||||
DrawRectangle(p1, h, p2, Height - h - 1, ColorSelected);
|
||||
}
|
||||
Mark(p1, Start, m->Position() == Current, ColorMark, ColorCurrent);
|
||||
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(void)
|
||||
{
|
||||
marks = NULL;
|
||||
errors = NULL;
|
||||
}
|
||||
|
||||
void cSkinDisplayReplay::SetRecording(const cRecording *Recording)
|
||||
@ -198,6 +227,11 @@ void cSkinDisplayReplay::SetMarks(const cMarks *Marks)
|
||||
marks = Marks;
|
||||
}
|
||||
|
||||
void cSkinDisplayReplay::SetErrors(const cErrors *Errors)
|
||||
{
|
||||
errors = Errors;
|
||||
}
|
||||
|
||||
// --- cSkin -----------------------------------------------------------------
|
||||
|
||||
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()->Flush();
|
||||
cStatus::MsgOsdStatusMessage(s);
|
||||
cStatus::MsgOsdStatusMessage(Type, s);
|
||||
eKeys k = kNone;
|
||||
if (Type != mtStatus) {
|
||||
k = Interface->Wait(Seconds);
|
||||
@ -282,7 +316,7 @@ eKeys cSkins::Message(eMessageType Type, const char *s, int Seconds)
|
||||
}
|
||||
else {
|
||||
cSkinDisplay::Current()->SetMessage(Type, NULL);
|
||||
cStatus::MsgOsdStatusMessage(NULL);
|
||||
cStatus::MsgOsdStatusMessage(Type, NULL);
|
||||
}
|
||||
}
|
||||
else if (!s && displayMessage) {
|
||||
|
30
skins.h
30
skins.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skins.h 5.1 2021/05/21 13:03:42 kls Exp $
|
||||
* $Id: skins.h 5.6 2024/09/19 09:49:02 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __SKINS_H
|
||||
@ -244,15 +244,6 @@ public:
|
||||
///< If the skin displays the Event item in its own way, it shall return true.
|
||||
///< The default implementation does nothing and returns false, which results in
|
||||
///< a call to SetItem() with a proper text.
|
||||
#ifndef DEPRECATED_SKIN_SETITEMEVENT
|
||||
#define DEPRECATED_SKIN_SETITEMEVENT 0
|
||||
#endif
|
||||
#if DEPRECATED_SKIN_SETITEMEVENT
|
||||
virtual bool SetItemEvent(const cEvent *Event, int Index, bool Current, bool Selectable, const cChannel *Channel, bool WithDate, eTimerMatch TimerMatch) { return SetItemEvent(Event, Index, Current, Selectable, Channel, WithDate, TimerMatch, true); }
|
||||
///< This function is here for comaptibility with older plugins and may be removed
|
||||
///< in a future version. Use the above version of SetItemEvent() with the TimerActive
|
||||
///< parameter instead.
|
||||
#endif
|
||||
virtual bool SetItemTimer(const cTimer *Timer, int Index, bool Current, bool Selectable) { return false; }
|
||||
///< Sets the item at the given Index to Timer. See SetItem() for more information.
|
||||
///< If a derived skin class implements this function, it can display a Timer item
|
||||
@ -310,8 +301,7 @@ public:
|
||||
virtual const cFont *GetTextAreaFont(bool FixedFont) const;
|
||||
///< Returns a pointer to the font which is used to display text with SetText().
|
||||
///< The parameter FixedFont has the same meaning as in SetText(). The default
|
||||
///< implementation returns NULL. Therefore a caller of this method must be
|
||||
///< prepared to receive NULL if the plugin doesn't implement this method.
|
||||
///< implementation returns the font defined in the setup.
|
||||
///< The returned pointer is valid a long as the instance of cSkinDisplayMenu
|
||||
///< exists.
|
||||
};
|
||||
@ -321,23 +311,29 @@ class cSkinDisplayReplay : public cSkinDisplay {
|
||||
///< a recording.
|
||||
protected:
|
||||
const cMarks *marks;
|
||||
const cErrors *errors;
|
||||
class cProgressBar : public cBitmap {
|
||||
protected:
|
||||
int 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 Error(int x, tColor ColorError);
|
||||
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:
|
||||
cSkinDisplayReplay(void);
|
||||
virtual void SetMarks(const cMarks *Marks);
|
||||
///< Sets the editing marks to Marks, which shall be used to display the
|
||||
///< 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);
|
||||
///< Sets the recording that is currently being played.
|
||||
///< The default implementation calls SetTitle() with the title and short
|
||||
///< text of the Recording. A derived class can use any information provided
|
||||
///< The default implementation calls SetTitle() with the title
|
||||
///< of the Recording. A derived class can use any information provided
|
||||
///< by the given Recording and display it.
|
||||
virtual void SetTitle(const char *Title) = 0;
|
||||
///< Sets the title of the recording.
|
||||
@ -351,13 +347,13 @@ public:
|
||||
///< used to display a progress indicator.
|
||||
virtual void SetCurrent(const char *Current) = 0;
|
||||
///< Sets the current position within the recording, as a user readable
|
||||
///< string if the form "h:mm:ss.ff". The ".ff" part, indicating the
|
||||
///< string in the form "h:mm:ss.ff". The ".ff" part, indicating the
|
||||
///< frame number, is optional and the actual implementation needs to
|
||||
///< take care that it is erased from the display when a Current string
|
||||
///< _with_ ".ff" is followed by one without it.
|
||||
virtual void SetTotal(const char *Total) = 0;
|
||||
///< Sets the total length of the recording, as a user readable
|
||||
///< string if the form "h:mm:ss".
|
||||
///< string in the form "h:mm:ss".
|
||||
virtual void SetJump(const char *Jump) = 0;
|
||||
///< Sets the prompt that allows the user to enter a jump point.
|
||||
///< Jump is a string of the form "Jump: mm:ss". The actual implementation
|
||||
|
16
skinsttng.c
16
skinsttng.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: skinsttng.c 5.1 2021/07/01 15:40:46 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
|
||||
@ -122,6 +122,7 @@ THEME_CLR(Theme, clrReplayProgressRest, clrWhite);
|
||||
THEME_CLR(Theme, clrReplayProgressSelected, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressMark, clrBlack);
|
||||
THEME_CLR(Theme, clrReplayProgressCurrent, clrRed);
|
||||
THEME_CLR(Theme, clrReplayProgressError, clrBlack);
|
||||
|
||||
// --- cSkinSTTNGDisplayChannel ----------------------------------------------
|
||||
|
||||
@ -718,7 +719,8 @@ void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
xt -= w + x5 - x4;
|
||||
}
|
||||
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);
|
||||
int w = font->Width(buffer);
|
||||
osd->DrawText(xt - w, y, buffer, Theme.Color(clrMenuEventVps), frameColor, font, w);
|
||||
@ -745,7 +747,13 @@ void cSkinSTTNGDisplayMenu::SetRecording(const cRecording *Recording)
|
||||
if (!isempty(Info->Description())) {
|
||||
int yt = y;
|
||||
int yb = y4 - Roundness;
|
||||
textScroller.Set(osd, xl, yt, x4 - xl, yb - yt, Info->Description(), font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
|
||||
cString d = Info->Description();
|
||||
cString f = Info->FrameParams();
|
||||
if (*f) {
|
||||
d.Append("\n\n");
|
||||
d.Append(f);
|
||||
}
|
||||
textScroller.Set(osd, xl, yt, x4 - xl, yb - yt, d, font, Theme.Color(clrMenuEventDescription), Theme.Color(clrBackground));
|
||||
yb = yt + textScroller.Height();
|
||||
osd->DrawEllipse (x1, yt - Roundness, x2, yt, frameColor, -3);
|
||||
osd->DrawRectangle(x1, yt, x2, yb, frameColor);
|
||||
@ -898,7 +906,7 @@ void cSkinSTTNGDisplayReplay::SetMode(bool Play, bool Forward, int Speed)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -46,10 +46,7 @@ bool cSource::Matches(int Code1, int Code2)
|
||||
|
||||
int cSource::Position(int Code)
|
||||
{
|
||||
int n = (Code & st_Pos);
|
||||
if (n > 0x00007FFF)
|
||||
n |= 0xFFFF0000;
|
||||
return n;
|
||||
return int16_t(Code & st_Pos);
|
||||
}
|
||||
|
||||
cString cSource::ToString(int Code)
|
||||
|
14
status.c
14
status.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -95,10 +95,10 @@ void cStatus::MsgOsdTitle(const char *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))
|
||||
sm->OsdStatusMessage(Message);
|
||||
sm->OsdStatusMessage2(Type, Message);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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))
|
||||
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))
|
||||
sm->OsdCurrentItem(Text);
|
||||
sm->OsdCurrentItem2(Text, Index);
|
||||
}
|
||||
|
||||
void cStatus::MsgOsdTextItem(const char *Text, bool Scroll)
|
||||
|
18
status.h
18
status.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -13,6 +13,7 @@
|
||||
#include "config.h"
|
||||
#include "device.h"
|
||||
#include "player.h"
|
||||
#include "skins.h"
|
||||
#include "tools.h"
|
||||
|
||||
// Several member functions of the following classes are called with a pointer to
|
||||
@ -81,14 +82,21 @@ protected:
|
||||
virtual void OsdTitle(const char *Title) {}
|
||||
// Title has been displayed in the title line of the menu.
|
||||
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.
|
||||
// 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) {}
|
||||
// The help keys have been set to the given values (may be NULL).
|
||||
virtual void OsdItem(const char *Text, int 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) {}
|
||||
// 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) {}
|
||||
// The OSD displays the given multi line text. If Text points to an
|
||||
// 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 MsgOsdClear(void);
|
||||
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 MsgOsdItem(const char *Text, int Index);
|
||||
static void MsgOsdCurrentItem(const char *Text);
|
||||
static void MsgOsdItem(const char *Text, int Index, bool Selectable = true);
|
||||
static void MsgOsdCurrentItem(const char *Text, int Index = -1);
|
||||
static void MsgOsdTextItem(const char *Text, bool Scroll = false);
|
||||
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);
|
||||
|
57
svdrp.c
57
svdrp.c
@ -10,7 +10,7 @@
|
||||
* and interact with the Video Disk Recorder - or write a full featured
|
||||
* graphical interface that sits on top of an SVDRP connection.
|
||||
*
|
||||
* $Id: svdrp.c 5.7 2023/02/16 17:20:09 kls Exp $
|
||||
* $Id: svdrp.c 5.10 2024/09/09 13:39:05 kls Exp $
|
||||
*/
|
||||
|
||||
#include "svdrp.h"
|
||||
@ -823,6 +823,14 @@ bool cPUTEhandler::Process(const char *s)
|
||||
// adjust the help for CLRE accordingly if changing this!
|
||||
|
||||
const char *HelpPages[] = {
|
||||
"AUDI [ <number> ]\n"
|
||||
" Lists the currently available audio tracks in the format 'number language description'.\n"
|
||||
" The number indicates the track type (1..32 = MP2, 33..48 = Dolby).\n"
|
||||
" The currently selected track has its description prefixed with '*'.\n"
|
||||
" If a number is given (which must be one of the track numbers listed)\n"
|
||||
" audio is switched to that track.\n"
|
||||
" Note that the list may not be fully available or current immediately after\n"
|
||||
" switching the channel or starting a replay.",
|
||||
"CHAN [ + | - | <number> | <name> | <id> ]\n"
|
||||
" Switch channel up, down or to the given channel number, name or id.\n"
|
||||
" Without option (or after successfully switching to the channel)\n"
|
||||
@ -1071,6 +1079,7 @@ private:
|
||||
bool Send(const char *s);
|
||||
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||
void PrintHelpTopics(const char **hp);
|
||||
void CmdAUDI(const char *Option);
|
||||
void CmdCHAN(const char *Option);
|
||||
void CmdCLRE(const char *Option);
|
||||
void CmdCONN(const char *Option);
|
||||
@ -1228,6 +1237,45 @@ void cSVDRPServer::PrintHelpTopics(const char **hp)
|
||||
}
|
||||
}
|
||||
|
||||
void cSVDRPServer::CmdAUDI(const char *Option)
|
||||
{
|
||||
if (*Option) {
|
||||
if (isnumber(Option)) {
|
||||
int o = strtol(Option, NULL, 10);
|
||||
if (o >= ttAudioFirst && o <= ttDolbyLast) {
|
||||
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(o));
|
||||
if (TrackId && TrackId->id) {
|
||||
cDevice::PrimaryDevice()->SetCurrentAudioTrack(eTrackType(o));
|
||||
Reply(250, "%d %s %s", eTrackType(o), *TrackId->language ? TrackId->language : "---", *TrackId->description ? TrackId->description : "-");
|
||||
}
|
||||
else
|
||||
Reply(501, "Audio track \"%s\" not available", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Invalid audio track \"%s\"", Option);
|
||||
}
|
||||
else
|
||||
Reply(501, "Error in audio track \"%s\"", Option);
|
||||
}
|
||||
else {
|
||||
SetTrackDescriptions(!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring() ? cDevice::CurrentChannel() : 0);
|
||||
eTrackType CurrentAudioTrack = cDevice::PrimaryDevice()->GetCurrentAudioTrack();
|
||||
cString s;
|
||||
for (int i = ttAudioFirst; i <= ttDolbyLast; i++) {
|
||||
const tTrackId *TrackId = cDevice::PrimaryDevice()->GetTrack(eTrackType(i));
|
||||
if (TrackId && TrackId->id) {
|
||||
if (*s)
|
||||
Reply(-250, "%s", *s);
|
||||
s = cString::sprintf("%d %s %s%s", eTrackType(i), *TrackId->language ? TrackId->language : "---", i == CurrentAudioTrack ? "*" : "", *TrackId->description ? TrackId->description : "-");
|
||||
}
|
||||
}
|
||||
if (*s)
|
||||
Reply(250, "%s", *s);
|
||||
else
|
||||
Reply(550, "No audio tracks available");
|
||||
}
|
||||
}
|
||||
|
||||
void cSVDRPServer::CmdCHAN(const char *Option)
|
||||
{
|
||||
LOCK_CHANNELS_READ;
|
||||
@ -1505,7 +1553,9 @@ void cSVDRPServer::CmdEDIT(const char *Option)
|
||||
if (const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
|
||||
cMarks Marks;
|
||||
if (Marks.Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.Count()) {
|
||||
if (RecordingsHandler.Add(ruCut, Recording->FileName()))
|
||||
if (!EnoughFreeDiskSpaceForEdit(Recording->FileName()))
|
||||
Reply(550, "Not enough free disk space to start editing process");
|
||||
else if (RecordingsHandler.Add(ruCut, Recording->FileName()))
|
||||
Reply(250, "Editing recording \"%s\" [%s]", Option, Recording->Title());
|
||||
else
|
||||
Reply(554, "Can't start editing process");
|
||||
@ -2569,7 +2619,8 @@ void cSVDRPServer::Execute(char *Cmd)
|
||||
if (*s)
|
||||
*s++ = 0;
|
||||
s = skipspace(s);
|
||||
if (CMD("CHAN")) CmdCHAN(s);
|
||||
if (CMD("AUDI")) CmdAUDI(s);
|
||||
else if (CMD("CHAN")) CmdCHAN(s);
|
||||
else if (CMD("CLRE")) CmdCLRE(s);
|
||||
else if (CMD("CONN")) CmdCONN(s);
|
||||
else if (CMD("DELC")) CmdDELC(s);
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH "svdrpsend" "1" "27 Dec 2021" "2.6" "Video Disk Recorder"
|
||||
.TH "svdrpsend" "1" "27 Dec 2021" "2.7" "Video Disk Recorder"
|
||||
.SH NAME
|
||||
.LP
|
||||
svdrpsend \- sends commands to VDR
|
||||
|
23
thread.c
23
thread.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: thread.c 5.1 2022/11/13 15:25:52 kls Exp $
|
||||
* $Id: thread.c 5.3 2025/01/15 08:43:12 kls Exp $
|
||||
*/
|
||||
|
||||
#include "thread.h"
|
||||
@ -597,6 +597,7 @@ cStateLockLog::cStateLockLog(void)
|
||||
void cStateLockLog::Dump(const char *Name, tThreadId ThreadId)
|
||||
{
|
||||
dsyslog("--- begin invalid lock sequence report");
|
||||
dsyslog("TID T C R DR S ST");
|
||||
int LastFlags = 0;
|
||||
for (int i = 0; i < SLL_SIZE; i++) {
|
||||
if (tThreadId tid = logThreadIds[logIndex]) {
|
||||
@ -949,11 +950,11 @@ bool cPipe::Open(const char *Command, const char *Mode)
|
||||
int fd[2];
|
||||
|
||||
if (pipe(fd) < 0) {
|
||||
LOG_ERROR;
|
||||
LOG_ERROR_STR(Command);
|
||||
return false;
|
||||
}
|
||||
if ((pid = fork()) < 0) { // fork failed
|
||||
LOG_ERROR;
|
||||
LOG_ERROR_STR(Command);
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
return false;
|
||||
@ -969,7 +970,7 @@ bool cPipe::Open(const char *Command, const char *Mode)
|
||||
}
|
||||
close(fd[iopipe]);
|
||||
if ((f = fdopen(fd[1 - iopipe], mode)) == NULL) {
|
||||
LOG_ERROR;
|
||||
LOG_ERROR_STR(Command);
|
||||
close(fd[1 - iopipe]);
|
||||
}
|
||||
return f != NULL;
|
||||
@ -982,7 +983,6 @@ bool cPipe::Open(const char *Command, const char *Mode)
|
||||
}
|
||||
close(fd[iopipe]);
|
||||
if (dup2(fd[1 - iopipe], iofd) == -1) { // now redirect
|
||||
LOG_ERROR;
|
||||
close(fd[1 - iopipe]);
|
||||
_exit(-1);
|
||||
}
|
||||
@ -991,7 +991,6 @@ bool cPipe::Open(const char *Command, const char *Mode)
|
||||
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
||||
close(i); //close all dup'ed filedescriptors
|
||||
if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
|
||||
LOG_ERROR_STR(Command);
|
||||
close(fd[1 - iopipe]);
|
||||
_exit(-1);
|
||||
}
|
||||
@ -1044,14 +1043,14 @@ int SystemExec(const char *Command, bool Detached)
|
||||
pid_t pid;
|
||||
|
||||
if ((pid = fork()) < 0) { // fork failed
|
||||
LOG_ERROR;
|
||||
LOG_ERROR_STR(Command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid > 0) { // parent process
|
||||
int status = 0;
|
||||
if (waitpid(pid, &status, 0) < 0) {
|
||||
LOG_ERROR;
|
||||
LOG_ERROR_STR(Command);
|
||||
return -1;
|
||||
}
|
||||
return status;
|
||||
@ -1064,19 +1063,17 @@ int SystemExec(const char *Command, bool Detached)
|
||||
// Start a new session
|
||||
pid_t sid = setsid();
|
||||
if (sid < 0)
|
||||
LOG_ERROR;
|
||||
_exit(-1);
|
||||
// close STDIN and re-open as /dev/null
|
||||
int devnull = open("/dev/null", O_RDONLY);
|
||||
if (devnull < 0 || dup2(devnull, 0) < 0)
|
||||
LOG_ERROR;
|
||||
_exit(-1);
|
||||
}
|
||||
int MaxPossibleFileDescriptors = getdtablesize();
|
||||
for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
|
||||
close(i); //close all dup'ed filedescriptors
|
||||
if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
|
||||
LOG_ERROR_STR(Command);
|
||||
if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1)
|
||||
_exit(-1);
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
}
|
||||
|
90
timers.c
90
timers.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.c 5.18 2022/11/20 10:57:31 kls Exp $
|
||||
* $Id: timers.c 5.25 2025/01/13 14:44:18 kls Exp $
|
||||
*/
|
||||
|
||||
#include "timers.h"
|
||||
@ -21,6 +21,8 @@
|
||||
// format characters in order to allow any number of blanks after a numeric
|
||||
// value!
|
||||
|
||||
#define VPSGRACE 15 // seconds we still record after the running status of a VPS event has changed to "not running"
|
||||
|
||||
// --- cTimer ----------------------------------------------------------------
|
||||
|
||||
cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
|
||||
@ -47,6 +49,8 @@ cTimer::cTimer(bool Instant, bool Pause, const cChannel *Channel)
|
||||
weekdays = 0;
|
||||
start = now->tm_hour * 100 + now->tm_min;
|
||||
stop = 0;
|
||||
vpsNotRunning = 0;
|
||||
vpsActive = false;
|
||||
if (!Setup.InstantRecordTime && channel && (Instant || Pause)) {
|
||||
LOCK_SCHEDULES_READ;
|
||||
if (const cSchedule *Schedule = Schedules->GetSchedule(channel)) {
|
||||
@ -181,11 +185,15 @@ cTimer::cTimer(const cEvent *Event, const char *FileName, const cTimer *PatternT
|
||||
deferred = 0;
|
||||
pending = inVpsMargin = false;
|
||||
flags = tfActive;
|
||||
if (PatternTimer)
|
||||
SetFlags(tfSpawned);
|
||||
*pattern = 0;
|
||||
*file = 0;
|
||||
aux = NULL;
|
||||
remote = NULL;
|
||||
event = NULL;
|
||||
vpsNotRunning = 0;
|
||||
vpsActive = false;
|
||||
if (!PatternTimer || PatternTimer->HasFlags(tfVps)) {
|
||||
if (Event->Vps() && (PatternTimer || Setup.UseVps))
|
||||
SetFlags(tfVps);
|
||||
@ -255,6 +263,8 @@ cTimer& cTimer::operator= (const cTimer &Timer)
|
||||
stop = Timer.stop;
|
||||
priority = Timer.priority;
|
||||
lifetime = Timer.lifetime;
|
||||
vpsNotRunning = 0;
|
||||
vpsActive = false;
|
||||
strncpy(pattern, Timer.pattern, sizeof(pattern));
|
||||
strncpy(file, Timer.file, sizeof(file));
|
||||
free(aux);
|
||||
@ -276,10 +286,12 @@ void cTimer::CalcMargins(int &MarginStart, int &MarginStop, const cEvent *Event)
|
||||
MarginStop = Setup.MarginStop * 60;
|
||||
// To make sure the timer gets assigned to the correct event, we must
|
||||
// make sure that this is the only event that overlaps 100%:
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev()))
|
||||
MarginStart = max(0, min(MarginStart, e->Duration() - 60));
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next()))
|
||||
MarginStop = max(0, min(MarginStop, e->Duration() - 60));
|
||||
if (HasFlags(tfSpawned)) {
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Prev()))
|
||||
MarginStart = max(0, min(MarginStart, e->Duration() - 60));
|
||||
if (const cEvent *e = dynamic_cast<const cEvent *>(Event->Next()))
|
||||
MarginStop = max(0, min(MarginStop, e->Duration() - 60));
|
||||
}
|
||||
}
|
||||
|
||||
int cTimer::Compare(const cListObject &ListObject) const
|
||||
@ -556,6 +568,7 @@ void cTimer::SetFile(const char *File)
|
||||
}
|
||||
|
||||
#define EITPRESENTFOLLOWINGRATE 10 // max. seconds between two occurrences of the "EIT present/following table for the actual multiplex" (2s by the standard, using some more for safety)
|
||||
#define EITPRESENTFOLLOWINGGRACE 60 // max. seconds before reporting a loss of VPS control of an ongoing recording
|
||||
|
||||
bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
{
|
||||
@ -603,7 +616,7 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
return false;
|
||||
deferred = 0;
|
||||
|
||||
if (HasFlags(tfActive)) {
|
||||
if (HasFlags(tfActive) && !Remote()) {
|
||||
if (event) {
|
||||
if (HasFlags(tfVps)) {
|
||||
if (event->Vps()) {
|
||||
@ -611,9 +624,33 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
||||
startTime = event->StartTime();
|
||||
stopTime = event->EndTime();
|
||||
if (!Margin) { // this is an actual check
|
||||
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events...
|
||||
return event->IsRunning(true);
|
||||
// ...otherwise we fall back to normal timer handling below (note: Margin == 0!)
|
||||
const cSchedule *Schedule = event->Schedule();
|
||||
if (Schedule && Schedule->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events...
|
||||
if (!vpsActive) {
|
||||
vpsActive = true;
|
||||
if (Recording())
|
||||
dsyslog("timer %s regained VPS control", *ToDescr());
|
||||
}
|
||||
bool running = event->IsRunning(true);
|
||||
if (!running) {
|
||||
if (Recording() && vpsNotRunning == 0)
|
||||
vpsNotRunning = time(NULL);
|
||||
}
|
||||
else
|
||||
vpsNotRunning = 0;
|
||||
return running || time(NULL) < vpsNotRunning + VPSGRACE;
|
||||
}
|
||||
if (Recording()) {
|
||||
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
|
||||
if (vpsActive) {
|
||||
vpsActive = false;
|
||||
esyslog("ERROR: timer %s lost VPS control", *ToDescr());
|
||||
}
|
||||
// ...otherwise we fall back to normal timer handling below (note: Margin == 0!)
|
||||
}
|
||||
else
|
||||
return false; // relying on vdr.c to ensure that a transponder is tuned to this channel
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -694,10 +731,39 @@ eTimerMatch cTimer::Matches(const cEvent *Event, int *Overlap) const
|
||||
bool cTimer::Expired(void) const
|
||||
{
|
||||
if (IsSingleEvent() && !Recording()) {
|
||||
time_t Now = time(NULL);
|
||||
time_t ExpireTime = StopTimeEvent();
|
||||
if (HasFlags(tfVps))
|
||||
if (HasFlags(tfVps)) {
|
||||
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;
|
||||
}
|
||||
@ -751,7 +817,6 @@ cTimer *cTimer::SpawnPatternTimer(const cEvent *Event, cTimers *Timers)
|
||||
cString FileName = MakePatternFileName(Pattern(), Event->Title(), Event->ShortText(), File());
|
||||
isyslog("spawning timer %s for event %s", *ToDescr(), *Event->ToDescr());
|
||||
cTimer *t = new cTimer(Event, FileName, this);
|
||||
t->SetFlags(tfSpawned);
|
||||
if (startswith(Pattern(), TIMERPATTERN_AVOID))
|
||||
t->SetFlags(tfAvoid);
|
||||
Timers->Add(t);
|
||||
@ -1096,6 +1161,7 @@ const cTimer *cTimers::GetMatch(time_t t) const
|
||||
{
|
||||
static int LastPending = -1;
|
||||
const cTimer *t0 = NULL;
|
||||
LOCK_SCHEDULES_READ;
|
||||
for (const cTimer *ti = First(); ti; ti = Next(ti)) {
|
||||
if (!ti->Remote() && !ti->Recording() && ti->Matches(t)) {
|
||||
if (ti->Pending()) {
|
||||
|
4
timers.h
4
timers.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: timers.h 5.9 2022/11/20 10:57:31 kls Exp $
|
||||
* $Id: timers.h 5.11 2024/03/06 14:37:15 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __TIMERS_H
|
||||
@ -37,6 +37,8 @@ private:
|
||||
int scheduleStateSpawn;
|
||||
int scheduleStateAdjust;
|
||||
mutable time_t deferred; ///< Matches(time_t, ...) will return false if the current time is before this value
|
||||
mutable time_t vpsNotRunning; ///< the time when a VPS event's running status changed to "not running"
|
||||
mutable bool vpsActive; ///< true if this is a VPS timer and the event is current
|
||||
bool pending, inVpsMargin;
|
||||
uint flags;
|
||||
const cChannel *channel;
|
||||
|
167
tools.c
167
tools.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.c 5.7 2022/11/22 14:33:48 kls Exp $
|
||||
* $Id: tools.c 5.15 2025/01/15 08:57:45 kls Exp $
|
||||
*/
|
||||
|
||||
#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 *s = dest;
|
||||
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
|
||||
*dest = 0;
|
||||
if (dest && n) {
|
||||
if (src)
|
||||
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
|
||||
*dest = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -151,26 +154,31 @@ char *strreplace(char *s, char c1, char c2)
|
||||
|
||||
char *strreplace(char *s, const char *s1, const char *s2)
|
||||
{
|
||||
if (!s || !s1 || !s2)
|
||||
if (!s || !s1 || !s2 || strcmp(s1, s2) == 0)
|
||||
return s;
|
||||
char *p = strstr(s, s1);
|
||||
if (p) {
|
||||
int of = p - s;
|
||||
char *q = s;
|
||||
if (char *p = strstr(s, s1)) {
|
||||
int l = strlen(s);
|
||||
int l1 = strlen(s1);
|
||||
int l2 = strlen(s2);
|
||||
if (l2 > l1) {
|
||||
if (char *NewBuffer = (char *)realloc(s, l + l2 - l1 + 1))
|
||||
s = NewBuffer;
|
||||
else {
|
||||
esyslog("ERROR: out of memory");
|
||||
return s;
|
||||
do {
|
||||
int of = p - s;
|
||||
if (l2 > l1) {
|
||||
if (char *NewBuffer = (char *)realloc(s, l + l2 - l1 + 1))
|
||||
s = NewBuffer;
|
||||
else {
|
||||
esyslog("ERROR: out of memory");
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
char *sof = s + of;
|
||||
if (l2 != l1)
|
||||
memmove(sof + l2, sof + l1, l - of - l1 + 1);
|
||||
memcpy(sof, s2, l2);
|
||||
char *sof = s + of;
|
||||
if (l2 != l1) {
|
||||
memmove(sof + l2, sof + l1, l - of - l1 + 1);
|
||||
l += l2 - l1;
|
||||
}
|
||||
memcpy(sof, s2, l2);
|
||||
q = sof + l2;
|
||||
} while (p = strstr(q, s1));
|
||||
}
|
||||
return s;
|
||||
}
|
||||
@ -714,9 +722,17 @@ bool SpinUpDisk(const char *FileName)
|
||||
return false;
|
||||
}
|
||||
|
||||
void TouchFile(const char *FileName)
|
||||
void TouchFile(const char *FileName, bool Create)
|
||||
{
|
||||
if (utime(FileName, NULL) == -1 && errno != ENOENT)
|
||||
if (Create && access(FileName, F_OK) != 0) { // the file does not exist
|
||||
isyslog("creating file '%s'", FileName);
|
||||
int f = open(FileName, O_WRONLY | O_CREAT, DEFFILEMODE);
|
||||
if (f >= 0)
|
||||
close(f);
|
||||
else
|
||||
LOG_ERROR_STR(FileName);
|
||||
}
|
||||
if (utime(FileName, NULL) == -1 && errno != ENOENT)
|
||||
LOG_ERROR_STR(FileName);
|
||||
}
|
||||
|
||||
@ -901,17 +917,21 @@ char *Utf8Strn0Cpy(char *Dest, const char *Src, int n)
|
||||
if (cCharSetConv::SystemCharacterTable())
|
||||
return strn0cpy(Dest, Src, n);
|
||||
char *d = Dest;
|
||||
while (*Src) {
|
||||
int sl = Utf8CharLen(Src);
|
||||
n -= sl;
|
||||
if (n > 0) {
|
||||
while (sl--)
|
||||
*d++ = *Src++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
if (Dest && n > 0) {
|
||||
if (Src) {
|
||||
while (*Src) {
|
||||
int sl = Utf8CharLen(Src);
|
||||
n -= sl;
|
||||
if (n > 0) {
|
||||
while (sl--)
|
||||
*d++ = *Src++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
*d = 0;
|
||||
*d = 0;
|
||||
}
|
||||
return Dest;
|
||||
}
|
||||
|
||||
@ -1106,6 +1126,16 @@ cString &cString::operator=(const cString &String)
|
||||
return *this;
|
||||
}
|
||||
|
||||
cString &cString::operator=(cString &&String)
|
||||
{
|
||||
if (this != &String) {
|
||||
free(s);
|
||||
s = String.s;
|
||||
String.s = NULL;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
cString &cString::operator=(const char *String)
|
||||
{
|
||||
if (s == String)
|
||||
@ -1130,6 +1160,22 @@ cString &cString::Append(const char *String)
|
||||
return *this;
|
||||
}
|
||||
|
||||
cString &cString::Append(char c)
|
||||
{
|
||||
if (c) {
|
||||
int l1 = s ? strlen(s) : 0;
|
||||
int l2 = 1;
|
||||
if (char *p = (char *)realloc(s, l1 + l2 + 1)) {
|
||||
s = p;
|
||||
*(s + l1) = c;
|
||||
*(s + l1 + 1) = 0;
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: out of memory");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
cString &cString::Truncate(int Index)
|
||||
{
|
||||
int l = strlen(s);
|
||||
@ -1633,11 +1679,6 @@ bool cFileNameList::Load(const char *Directory, bool DirsOnly)
|
||||
|
||||
// --- cFile -----------------------------------------------------------------
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::files[FD_SETSIZE] = { false };
|
||||
int cFile::maxFiles = 0;
|
||||
#endif
|
||||
|
||||
cFile::cFile(void)
|
||||
{
|
||||
f = -1;
|
||||
@ -1659,24 +1700,8 @@ bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
|
||||
bool cFile::Open(int FileDes)
|
||||
{
|
||||
if (FileDes >= 0) {
|
||||
if (!IsOpen()) {
|
||||
if (!IsOpen())
|
||||
f = FileDes;
|
||||
#if DEPRECATED_CFILE
|
||||
if (f >= 0) {
|
||||
if (f < FD_SETSIZE) {
|
||||
if (f >= maxFiles)
|
||||
maxFiles = f + 1;
|
||||
if (!files[f])
|
||||
files[f] = true;
|
||||
else
|
||||
esyslog("ERROR: file descriptor %d already in files[]", f);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
esyslog("ERROR: attempt to re-open file descriptor %d", FileDes);
|
||||
}
|
||||
@ -1687,9 +1712,6 @@ void cFile::Close(void)
|
||||
{
|
||||
if (f >= 0) {
|
||||
close(f);
|
||||
#if DEPRECATED_CFILE
|
||||
files[f] = false;
|
||||
#endif
|
||||
f = -1;
|
||||
}
|
||||
}
|
||||
@ -1699,26 +1721,6 @@ bool cFile::Ready(bool Wait)
|
||||
return f >= 0 && FileReady(f, Wait ? 1000 : 0);
|
||||
}
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
for (int i = 0; i < maxFiles; i++) {
|
||||
if (files[i])
|
||||
FD_SET(i, &set);
|
||||
}
|
||||
if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
|
||||
FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
|
||||
if (TimeoutMs == 0)
|
||||
TimeoutMs = 10; // load gets too heavy with 0
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = TimeoutMs / 1000;
|
||||
timeout.tv_usec = (TimeoutMs % 1000) * 1000;
|
||||
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
|
||||
}
|
||||
#endif
|
||||
|
||||
bool cFile::FileReady(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
@ -1734,21 +1736,6 @@ bool cFile::FileReady(int FileDes, int TimeoutMs)
|
||||
return select(FD_SETSIZE, &set, NULL, NULL, (TimeoutMs >= 0) ? &timeout : NULL) > 0 && FD_ISSET(FileDes, &set);
|
||||
}
|
||||
|
||||
#if DEPRECATED_CFILE
|
||||
bool cFile::FileReadyForWriting(int FileDes, int TimeoutMs)
|
||||
{
|
||||
fd_set set;
|
||||
struct timeval timeout;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(FileDes, &set);
|
||||
if (TimeoutMs < 100)
|
||||
TimeoutMs = 100;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = TimeoutMs * 1000;
|
||||
return select(FD_SETSIZE, NULL, &set, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- cSafeFile -------------------------------------------------------------
|
||||
|
||||
cSafeFile::cSafeFile(const char *FileName)
|
||||
|
22
tools.h
22
tools.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: tools.h 5.6 2021/05/26 13:37:53 kls Exp $
|
||||
* $Id: tools.h 5.11 2025/01/13 13:18:42 kls Exp $
|
||||
*/
|
||||
|
||||
#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
|
||||
// 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
|
||||
// class functions:
|
||||
@ -182,13 +182,16 @@ public:
|
||||
cString(const char *S = NULL, bool TakePointer = false);
|
||||
cString(const char *S, const char *To); ///< Copies S up to To (exclusive). To must be a valid pointer into S. If To is NULL, everything is copied.
|
||||
cString(const cString &String);
|
||||
cString(cString &&String): s(String.s) { String.s = NULL; }
|
||||
virtual ~cString();
|
||||
operator const void * () const { return s; } // to catch cases where operator*() should be used
|
||||
operator const char * () const { return s; } // for use in (const char *) context
|
||||
const char * operator*() const { return s; } // for use in (const void *) context (printf() etc.)
|
||||
cString &operator=(const cString &String);
|
||||
cString &operator=(cString &&String);
|
||||
cString &operator=(const char *String);
|
||||
cString &Append(const char *String);
|
||||
cString &Append(char c);
|
||||
cString &Truncate(int Index); ///< Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
|
||||
cString &CompactChars(char c); ///< Compact any sequence of characters 'c' to a single character, and strip all of them from the beginning and end of this string.
|
||||
static cString sprintf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
@ -316,7 +319,7 @@ bool RemoveEmptyDirectories(const char *DirName, bool RemoveThis = false, const
|
||||
int DirSizeMB(const char *DirName); ///< returns the total size of the files in the given directory, or -1 in case of an error
|
||||
char *ReadLink(const char *FileName); ///< returns a new string allocated on the heap, which the caller must delete (or NULL in case of an error)
|
||||
bool SpinUpDisk(const char *FileName);
|
||||
void TouchFile(const char *FileName);
|
||||
void TouchFile(const char *FileName, bool Create = false);
|
||||
time_t LastModifiedTime(const char *FileName);
|
||||
off_t FileSize(const char *FileName); ///< returns the size of the given file, or -1 in case of an error (e.g. if the file doesn't exist)
|
||||
cString WeekDayName(int WeekDay);
|
||||
@ -457,15 +460,8 @@ public:
|
||||
struct dirent *Next(void);
|
||||
};
|
||||
|
||||
#ifndef DEPRECATED_CFILE
|
||||
#define DEPRECATED_CFILE 0
|
||||
#endif
|
||||
class cFile {
|
||||
private:
|
||||
#if DEPRECATED_CFILE
|
||||
static bool files[];
|
||||
static int maxFiles;
|
||||
#endif
|
||||
int f;
|
||||
public:
|
||||
cFile(void);
|
||||
@ -476,13 +472,7 @@ public:
|
||||
void Close(void);
|
||||
bool IsOpen(void) { return f >= 0; }
|
||||
bool Ready(bool Wait = true);
|
||||
#if DEPRECATED_CFILE
|
||||
static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000);
|
||||
#endif
|
||||
static bool FileReady(int FileDes, int TimeoutMs = 1000);
|
||||
#if DEPRECATED_CFILE
|
||||
static bool FileReadyForWriting(int FileDes, int TimeoutMs = 1000);
|
||||
#endif
|
||||
};
|
||||
|
||||
class cSafeFile {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user