mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Compare commits
188 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 | ||
|
5f136032a2 | ||
|
63efcf3927 | ||
|
468dc1115e | ||
|
d53e0fd5c3 | ||
|
14b907b01c | ||
|
e0d87da768 | ||
|
42b584e38d | ||
|
a0f79bdd5f | ||
|
4372d55dd1 | ||
|
13672280b6 | ||
|
5b134cb23e | ||
|
87cf0b7a3d | ||
|
8b14723e9e | ||
|
7fe59548cd | ||
|
15f13ac936 | ||
|
5b176f97a4 | ||
|
0bb6f87776 | ||
|
c06d2389e9 | ||
|
548a33c728 | ||
|
4336b55f4e | ||
|
c12c7378e9 | ||
|
a299d8d348 | ||
|
c7bf474a42 | ||
|
8d65cc6dc0 | ||
|
f2b9f0e8dd | ||
|
30f05ba714 | ||
|
76445411a5 | ||
|
4425918d31 | ||
|
6888ea68b6 | ||
|
abb82a2396 | ||
|
6192ca81d9 | ||
|
d06c5efa54 | ||
|
f859b8d2ae | ||
|
035d5fd5b9 | ||
|
93d578d9b8 | ||
|
d756628297 | ||
|
a4cde807bc | ||
|
83c9677899 | ||
|
b14ed38a48 | ||
|
2bf0967a47 | ||
|
7ed306d127 | ||
|
5a029eb29f | ||
|
230adc8235 | ||
|
982a9a5157 | ||
|
c8e4921a0a | ||
|
786245efe5 | ||
|
74460f22bf | ||
|
a843d03af1 | ||
|
d3f3e856e4 |
109
CONTRIBUTORS
109
CONTRIBUTORS
@ -2189,7 +2189,7 @@ Harald Milz <hm@seneca.muc.de>
|
||||
for his CUTR patch, which was used as a base to implement the SVDRP command EDIT
|
||||
for reporting a bug in parsing the '-l' command line option
|
||||
|
||||
Marko Mäkelä <marko.makela@hut.fi>
|
||||
Marko Mäkelä <marko.makela@iki.fi>
|
||||
for making repeat keys be ignored when waiting for a keypress to cancel an operation
|
||||
for reporting that a menu was automatically closed when a replay ends
|
||||
for suggesting to ignore k_Repeat when deciding whether the same key has been
|
||||
@ -2201,6 +2201,17 @@ Marko M
|
||||
for reporting some uninitialized item area coordinates in cSkinLCARSDisplayMenu
|
||||
for reporting a problem with the video directory not being set correctly with --edit
|
||||
for reporting a compiler warning about the use of strncpy() in strreplace()
|
||||
for adding support for kernel based LIRC driver
|
||||
for reporting a possible heap-use-after-free in cDvbTuner::Action()
|
||||
for reporting a flaw in initializing cDvbPlayerControl and cTransferControl
|
||||
for reporting a possible call to poll() in cSectionHandler::Action() without any
|
||||
filters
|
||||
for avoiding the memcpy() call in cGlyph::cGlyph() if the bitmap is empty
|
||||
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
|
||||
@ -2457,6 +2468,12 @@ Christoph Haubrich <christoph1.haubrich@arcor.de>
|
||||
for reporting missing '0x09=H.265 video, 0x19 = AC4 audio' in vdr.5
|
||||
for reporting a problem with the call to EpgHandlers.EndSegmentTransfer()
|
||||
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
|
||||
@ -2537,6 +2554,48 @@ Markus Ehrnsperger <markus.ehrnsperger@googlemail.com>
|
||||
Make.config
|
||||
for reporting a bug in error handling when loading a plugin
|
||||
for reporting a possible crash in cIndexFile::GetClosestIFrame()
|
||||
for reporting a missing 'const' in cTimers::GetTimerForEvent()
|
||||
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
|
||||
@ -2662,6 +2721,7 @@ J
|
||||
for suggesting to no longer log unaligned marks in cMarks::Align()
|
||||
for reporting some warnings from g++ 7.2.0 regarding fixed buffer sizes in
|
||||
cMenuEditTimeItem::Set() and cCountdown::Update()
|
||||
for fixing symmetry of Begin/EndSegmentTransfer() calls in cEIT::cEIT()
|
||||
|
||||
Peter Pinnau <vdr@unterbrecher.de>
|
||||
for reporting that 'uint32_t' requires including stdint.h in font.h on some systems
|
||||
@ -2768,7 +2828,7 @@ Benjamin Hess <benjamin.h@gmx.ch>
|
||||
for enhancing the SVDRP command CLRE to allow clearing the EPG data of a particular
|
||||
channel
|
||||
|
||||
Winfried Köhler <w_koehl@gmx.de>
|
||||
Winfried Köhler <nvdec@quantentunnel.de>
|
||||
for fixing finding new transponders
|
||||
for fixing wrong value for TableIdBAT in libsi/si.h
|
||||
for reporting a compiler warning in calculations involving FramesPerSecond()
|
||||
@ -2790,6 +2850,16 @@ Winfried K
|
||||
for using __cplusplus instead of DISABLE_TEMPLATES_COLLIDING_WITH_STL, and using
|
||||
std::min(), std::max() and std::swap() is available
|
||||
for adding some missing "AUTO" values to vdr.5
|
||||
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
|
||||
@ -2892,6 +2962,7 @@ Johann Friedrichs <johann.friedrichs@web.de>
|
||||
want the actual response strings
|
||||
for reporting a bug in handling the tfRecording flag in the SVDRP commands MODT and UPDT
|
||||
for adding a missing '-D' to the 'plugins' target of the Makefile
|
||||
for adding code for the 'qks' audio track
|
||||
|
||||
Timo Helkio <timolavi@mbnet.fi>
|
||||
for reporting a hangup when replaying a TS recording with subtitles activated
|
||||
@ -2959,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
|
||||
@ -3012,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
|
||||
@ -3301,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
|
||||
@ -3365,6 +3448,12 @@ Matthias Senzel <matthias.senzel@t-online.de>
|
||||
when Flush() was called
|
||||
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
|
||||
@ -3516,6 +3605,8 @@ Claus Muus <email@clausmuus.de>
|
||||
".../Volume linearize"
|
||||
for reporting multiple recording entries in case a recording is started during the
|
||||
initial reading of the video directory
|
||||
for reporting that getting the lock for removing deleted recordings may fail if the
|
||||
disk is full
|
||||
|
||||
Dieter Ferdinand <dieter.ferdinand@gmx.de>
|
||||
for reporting a problem with jumping to an absolute position via the Red key in
|
||||
@ -3556,6 +3647,11 @@ Stefan Herdler <herdler@gmx.de>
|
||||
".sort" file
|
||||
for reporting faulty memory handling in cString::Append()
|
||||
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
|
||||
@ -3618,6 +3714,7 @@ Onur Sent
|
||||
for fixing handling the S2SatelliteDeliverySystemDescriptor for transponders broadcasting
|
||||
in "backwards compatibility mode" according to ETSI EN 300 468
|
||||
for pointing out some potentially mistakable code in cSectionHandler::SetStatus()
|
||||
for adding periodic calls to malloc_trim(0) to reduce memory consumption
|
||||
|
||||
Helmut Binder <cco@aon.at>
|
||||
for improving calculating signal strength and quality
|
||||
@ -3713,3 +3810,11 @@ Ulf Gr
|
||||
Timo Weingärtner <timo@tiwe.de>
|
||||
for reporting an integer overflow in calculating the disk use percentage if there's
|
||||
more than 20TB of recordings
|
||||
|
||||
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"
|
||||
|
312
HISTORY
312
HISTORY
@ -9769,7 +9769,7 @@ Video Disk Recorder Revision History
|
||||
2022-02-02: Version 2.6.1
|
||||
|
||||
- Replaced strncpy() with memcpy() in strreplace() to avoid a compiler warning
|
||||
(reported by Marco Mäkelä).
|
||||
(reported by Marko Mäkelä).
|
||||
- Fixed starting replay after jumping to an editing mark.
|
||||
- Updated the Italian OSD texts (thanks to Diego Pierotto).
|
||||
- Added some missing "AUTO" values to vdr.5 (thanks to Winfried Köhler).
|
||||
@ -9779,3 +9779,313 @@ Video Disk Recorder Revision History
|
||||
- Clarified some potentially mistakable code in cSectionHandler::SetStatus() (pointed
|
||||
out by Onur Sentürk).
|
||||
- Official release.
|
||||
|
||||
2022-11-30: Version 2.6.2
|
||||
|
||||
- Added UPDATE-2.6.0, which was missing in the official 2.6.0 release.
|
||||
- Fixed unexpected calls of the '-r' script when a recording is interrupted and
|
||||
the timer has not yet finished.
|
||||
- Now dropping capabilities after opening terminal.
|
||||
- Now assuming the lock when removing deleted recordings even if the disk is full
|
||||
(reported by Claus Muus).
|
||||
- When checking whether a recording is still active, VDR no longer checks whether the
|
||||
index file is being written, but rather checks for the presence of a '.timer' file.
|
||||
The cutter now writes a dummy '.timer' file with timer ID '0' to make this work
|
||||
for recordings that are currently being edited.
|
||||
- Fixed a possible crash if an editing process is canceled while the edited
|
||||
recording is being replayed.
|
||||
- Added a warning if an attempt is made to obtain a write lock twice from the same thread.
|
||||
- Fixed default values for DVB-T (thanks to Winfried Köhler and Jose Angel).
|
||||
- Removed some unnecessary locks from SVDRPClientHandler.
|
||||
- Fixed a possible deadlock in case two SVDRP clients send each other POLL commands
|
||||
at the same time.
|
||||
- Added a missing 'const' to cTimers::GetTimerForEvent() (reported by Markus Ehrnsperger).
|
||||
- Added a chapter about locking to PLUGINS.html (suggested by Markus Ehrnsperger).
|
||||
- Implemented parsing frame rate and image size for MPEG2, H.264 and H.265 (thanks
|
||||
to Christoph Haubrich).
|
||||
- Using the frame rate parsed from the stream, with fall back to determining it from
|
||||
PTS values.
|
||||
- Fixed printing/scanning values for systems where %ld doesn't work for time_t.
|
||||
- Added support for kernel based LIRC driver (thanks to Marko Mäkelä). Use the
|
||||
command line option '--lirc=/dev/lirc0' to use this. Requires kernel version 5.10
|
||||
or above.
|
||||
- Added periodic calls to malloc_trim(0) to reduce memory consumption (thanks to
|
||||
Onur Sentürk).
|
||||
- Fixed regenerating the index file of a recording in case it is present, but empty
|
||||
(reported by Stefan Herdler).
|
||||
- Added missing rounding when dividing frequencies in processing the NIT (thanks to
|
||||
Winfried Köhler).
|
||||
|
||||
2022-12-14: Version 2.6.3
|
||||
|
||||
- Fixed a compiler warning.
|
||||
- Fixed generating the index file in the cutter (reported by Christoph Haubrich).
|
||||
- Fixed a faulty 'Timer still recording' query when canceling an editing job.
|
||||
- Added code for the 'qks' audio track (thanks to Johann Friedrichs).
|
||||
- Fixed a possible heap-use-after-free in cDvbTuner::Action() (reported by Marko Mäkelä).
|
||||
- Fixed initializing cDvbPlayerControl and cTransferControl (reported by Marko Mäkelä).
|
||||
- Now avoiding calling poll() in cSectionHandler::Action() if there are no filters
|
||||
(reported by Marko Mäkelä).
|
||||
- Now avoiding the memcpy() call in cGlyph::cGlyph() if the bitmap is empty (thanks
|
||||
to Marko Mäkelä).
|
||||
- Now avoiding unnecessary processing in cDvbSubtitleConverter::FinishPage() if there
|
||||
are no areas (thanks to Marko Mäkelä).
|
||||
- Avoiding a zero sized array in cDevice::GetDevice() (thanks to Marko Mäkelä).
|
||||
- Now checking the video directory after setting the user id.
|
||||
|
||||
2023-02-17: Version 2.6.4
|
||||
|
||||
- Updated the Italian OSD texts (thanks to Diego Pierotto).
|
||||
- Fixed restoring the volume at program start (thanks to Matthias Senzel).
|
||||
- Fixed symmetry of Begin/EndSegmentTransfer() calls in cEIT::cEIT() (thanks to
|
||||
Jörg Wendel).
|
||||
- Added a note to epg.h about not messing with event ids (suggested by Winfried Köhler).
|
||||
- Added a note to vdr.5 about event ids possibly changing when an event moves from
|
||||
one table to another.
|
||||
- Fixed initializing cDvbPlayerControl (was broken in version 2.6.3).
|
||||
- Reverted 'Fixed a possible crash if an editing process is canceled while the edited
|
||||
recording is being replayed' (introduced in version 2.6.2), because it caused a
|
||||
deadlock when moving recordings between volumes.
|
||||
- Fixed a possible crash if an editing process is canceled while the edited recording
|
||||
is being replayed (new solution).
|
||||
- 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 4.1 2017/06/02 09:29:54 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
|
||||
@ -74,6 +75,8 @@ endif
|
||||
|
||||
### The remote control:
|
||||
LIRC_DEVICE = /var/run/lirc/lircd
|
||||
### Use this for kernel based driver:
|
||||
#LIRC_DEVICE = /dev/lirc0
|
||||
|
||||
### Define if you always want to use LIRC, independent of the --lirc option:
|
||||
#REMOTE=LIRC
|
||||
|
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
|
||||
|
107
PLUGINS.html
107
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>
|
||||
@ -83,6 +82,7 @@ structures and allows it to hook itself into specific areas to perform special a
|
||||
<li><a href="#Internationalization">Internationalization</a>
|
||||
<li><a href="#Custom services">Custom services</a>
|
||||
<li><a href="#SVDRP commands">SVDRP commands</a>
|
||||
<li><a href="#Locking">Locking</a>
|
||||
<li><a href="#Loading plugins into VDR">Loading plugins into VDR</a>
|
||||
<li><a href="#Building the distribution package">Building the distribution package</a>
|
||||
</ul>
|
||||
@ -165,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
|
||||
@ -184,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>
|
||||
@ -195,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.
|
||||
@ -390,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>
|
||||
|
||||
@ -692,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>
|
||||
@ -1185,9 +1163,59 @@ when presenting them to the caller, and the continuation character ('<tt>-</tt>'
|
||||
will be set for all but the last one.
|
||||
<p>
|
||||
<b>The SVDRP functions are called from the separate "SVDRP server handler" thread.
|
||||
Therefore the plugin needs to take care of proper locking if it accesses any
|
||||
Therefore the plugin needs to take care of proper <a href="#Locking">locking</a> if it accesses any
|
||||
global data.</b>
|
||||
|
||||
<hr><h2><a name="Locking">Locking</a></h2>
|
||||
|
||||
<div class="blurb">U can't touch this</div><p>
|
||||
|
||||
When accessing global data structures, proper locking is absolutely necessary.
|
||||
<p>
|
||||
There are several macros that allow for easy locking and unlocking. They all
|
||||
follow the naming convention <tt>LOCK_*_READ|WRITE</tt>, where <tt>'*'</tt> is the name
|
||||
of the global data structure, which can be one of <tt>TIMERS</tt>, <tt>CHANNELS</tt>,
|
||||
<tt>RECORDINGS</tt> or <tt>SCHEDULES</tt>. To implicitly avoid deadlocks in case you
|
||||
need to lock more than one structure, always make sure you use these macros in
|
||||
this sequence!
|
||||
<p>
|
||||
Using one of these macros sets a lock on the structure, creates a local pointer to the
|
||||
respective structure and makes sure the lock is released at the end of the current
|
||||
block:
|
||||
|
||||
<p><table><tr><td class="code"><pre>
|
||||
if (some condition) {
|
||||
LOCK_TIMERS_READ; // creates local const cTimers *Timers
|
||||
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
|
||||
// do something with Timer
|
||||
}
|
||||
}
|
||||
</pre></td></tr></table><p>
|
||||
|
||||
Note the naming convention: TIMERS -> Timers etc.
|
||||
<p>
|
||||
The <tt>LOCK_*_READ</tt> macros create pointers that are '<tt>const</tt>', while
|
||||
the <tt>LOCK_*_WRITE</tt> macros allow modifications to the data structures.
|
||||
Both wait indefinitely to obtain the lock. However, if <tt>LOCK_*_WRITE</tt> is
|
||||
used twice in the same block, the second call will not obtain a lock and
|
||||
immediately return <tt>NULL</tt>, which may lead to a crash. In such cases a
|
||||
warning and backtrace is logged.
|
||||
<p>
|
||||
You may keep pointers to objects in such lists, even after releasing
|
||||
the lock. However, you may only access such objects if you are
|
||||
holding a proper lock again. If an object has been deleted from the list
|
||||
while you did not hold a lock (for instance by an other thread), the
|
||||
object will still be there, but no longer within this list (it is then
|
||||
stored in the ListGarbageCollector for a few seconds). That way even if you
|
||||
access the object after it has been deleted, you won't cause a segfault.
|
||||
You can call the Contains() function to check whether an object you are
|
||||
holding a pointer to is still in the list. Note that the garbage collector
|
||||
is purged when the usual housekeeping is done.
|
||||
<p>
|
||||
See tools.h, class <tt>cListBase</tt> for more documentation and information on how
|
||||
to use locking with timeouts, and thread.h, classes <tt>cStateLock</tt> and
|
||||
<tt>cStateKey</tt> on how to easily react to changes in such lists.
|
||||
|
||||
<hr><h2><a name="Loading plugins into VDR">Loading plugins into VDR</a></h2>
|
||||
|
||||
<div class="blurb">Saddling up!</div><p>
|
||||
@ -2066,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
|
||||
|
@ -116,3 +116,7 @@ VDR Plugin 'pictures' Revision History
|
||||
2021-12-27: Version 2.6.0
|
||||
|
||||
- Official release.
|
||||
|
||||
2022-12-05: Version 2.6.1
|
||||
|
||||
Fixed initializing cPictureControl.
|
||||
|
@ -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,7 +3,7 @@
|
||||
*
|
||||
* See the README file for copyright information and how to reach the author.
|
||||
*
|
||||
* $Id: pictures.c 4.3 2018/04/10 13:00:42 kls Exp $
|
||||
* $Id: pictures.c 5.1 2022/12/05 15:26:23 kls Exp $
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
@ -11,7 +11,7 @@
|
||||
#include "menu.h"
|
||||
#include "player.h"
|
||||
|
||||
static const char *VERSION = "2.4.0";
|
||||
static const char *VERSION = "2.6.1";
|
||||
static const char *DESCRIPTION = trNOOP("A simple picture viewer");
|
||||
static const char *MAINMENUENTRY = trNOOP("Pictures");
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* See the README file for copyright information and how to reach the author.
|
||||
*
|
||||
* $Id: player.c 3.1 2014/02/08 12:48:12 kls Exp $
|
||||
* $Id: player.c 5.1 2022/12/05 15:26:23 kls Exp $
|
||||
*/
|
||||
|
||||
#include "player.h"
|
||||
@ -96,8 +96,10 @@ int cPictureControl::active = 0;
|
||||
cString cPictureControl::lastDisplayed;
|
||||
|
||||
cPictureControl::cPictureControl(cPictureEntry *Pictures, const cPictureEntry *PictureEntry, bool SlideShow)
|
||||
:cControl(player = new cPicturePlayer)
|
||||
:cControl(NULL)
|
||||
{
|
||||
player = new cPicturePlayer;
|
||||
SetPlayer(player);
|
||||
pictures = Pictures;
|
||||
pictureEntry = PictureEntry;
|
||||
osd = NULL;
|
||||
|
@ -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
|
||||
|
119
UPDATE-2.6.0
Normal file
119
UPDATE-2.6.0
Normal file
@ -0,0 +1,119 @@
|
||||
This is a summary of the changes in VDR 2.6.0 since the last stable
|
||||
version 2.4.0. It only contains things that are of actual importance
|
||||
to the user and doesn't mention the many fixes and improvements that
|
||||
have been made "behind the scenes".
|
||||
|
||||
See the file HISTORY for a detailed list of all changes.
|
||||
|
||||
Timers:
|
||||
|
||||
- Implemented "Pattern Timers" (see MANUAL, vdr.1 and vdr.5 for details).
|
||||
- The margins for timer recordings are now always limited to the duration of the
|
||||
previous and next event.
|
||||
- Spawned timers that don't use VPS now automatically adjust their start/stop times
|
||||
to changes in the respective event's times.
|
||||
|
||||
EPG:
|
||||
|
||||
- Events in the past are no longer marked as having a timer in the Schedules
|
||||
menu.
|
||||
- Improved handling EPG data from the EIT tables:
|
||||
+ Table 0x4F is now completely ignored.
|
||||
+ Once a schedule has seen events from 0x5X, tables 0x6X are ignored for that
|
||||
schedule.
|
||||
+ When looking up an event in its schedule, the start time is used for tables 0x6X, and the
|
||||
event id for tables 0x4E and 0x5X.
|
||||
+ When hashing events by event id or start time, existing older entries in the hash
|
||||
tables are now deleted before entering the new ones.
|
||||
+ The function cSchedule::GetEvent() is now deprecated and may be removed in a future
|
||||
version. Use GetEventById() and GetEventByTime() instead.
|
||||
+ On channels that use proper event ids a change of the start time no longer
|
||||
causes a new event to be created, but rather modifies the existing one. This
|
||||
avoids possible interruptions in VPS recordings in case the event's start time
|
||||
is changed while the recording is already going on.
|
||||
- Fixed the timer indicator in the Schedule menu in case an event is already over, but the
|
||||
timer is still recording.
|
||||
- Fixed unlocking vs. call to EpgHandlers.EndSegmentTransfer().
|
||||
|
||||
Devices:
|
||||
|
||||
- Increased the number of possible modulation systems in cDevice::GetDevice().
|
||||
- Improved cSectionSyncer to make sure that no sections are missed, and to allow
|
||||
handling partially used segments (as in the EIT) and processing sections in random
|
||||
order. Segment syncing is now done with the two member functions Check() and
|
||||
Processed(). The old functions Sync() and Repeat() are deprecated and may be
|
||||
removed in a future version. See the comments in filter.h for a description on
|
||||
how to use these new function.
|
||||
- Added a device hook for detecting whether a device provides EIT data.
|
||||
|
||||
Recordings:
|
||||
|
||||
- Made the functions cRecordingInfo::SetData() and cRecordingInfo::SetAux() public.
|
||||
- Fixed setting the 'title' of a recording's info to the recording's name if there
|
||||
is no info file (the change in version 1.7.28 broke the fallback to the old 'summary.vdr').
|
||||
- Added some missing user command calls for copying, renaming and moving recordings.
|
||||
- Recordings are now checked for errors:
|
||||
+ On TS level, the continuity counter, transport error indicator and scramble flags are
|
||||
checked.
|
||||
+ On frame level it is checked whether there are no gaps in the PTS.
|
||||
+ The number of errors during a recording is stored in the recording's 'info' file, with
|
||||
the new tag 'O'.
|
||||
+ Spawned timers that shall avoid recording reruns only store the recording's name in
|
||||
the donerecs,data file if there were no errors during recording, and if the timer has
|
||||
actually finished.
|
||||
- The Recordings menu now marks recordings with errors with an exclamation mark ('!'),
|
||||
and the number of errors (if any) is displayed in the recording's Info menu.
|
||||
|
||||
Replay:
|
||||
|
||||
- Fixed scaling subtitles with anti-aliasing.
|
||||
|
||||
Conditional Access:
|
||||
|
||||
- Decreased the scrambling timeout for CAMs known to decrypt a certain channel, so
|
||||
that it won't collide with MAXBROKENTIMEOUT in recorder.c.
|
||||
|
||||
Skins:
|
||||
|
||||
- The new functions cTimer::Start/StopTimeEvent() are now used in the LCARS skin to display
|
||||
the start/stop times of timers in the main menu.
|
||||
|
||||
SVDRP:
|
||||
|
||||
- The SVDRP command DELC now also accepts a channel id.
|
||||
|
||||
Misc:
|
||||
|
||||
- Added support for HEVC-video and AC-4-audio.
|
||||
- EXPIRELATENCY now only applies to VPS timers.
|
||||
- Removed the macros __STL_CONFIG_H, _STL_ALGOBASE_H and _MOVE_H from tools.h. If your
|
||||
plugin insists in using "using namespace std;" you can still define
|
||||
DISABLE_TEMPLATES_COLLIDING_WITH_STL before including any VDR header files.
|
||||
- The cFile class has been partially deprecated:
|
||||
+ The handling of file handles was not thread-safe.
|
||||
+ It was only actually used in svdrp.c.
|
||||
+ cFile::Ready() now processes only its own file descriptor by calling FileReady()
|
||||
instead of AnyFileReady().
|
||||
- The transponder value of channels is now cached, because cChannel::Transponder(void)
|
||||
is called very often.
|
||||
- Added code for the 'qad' audio track.
|
||||
- The 'Edit path' dialog now also shows the total size of all recordings in that path.
|
||||
- The macro DEPRECATED_VDR_CHARSET_OVERRIDE and the related code has been removed.
|
||||
- The default for DEPRECATED_SETCURRENTCHANNEL has been set to 0, which means that
|
||||
the function SetCurrentChannel(const cChannel *Channel) is no longer available.
|
||||
You can add 'DEPRECATED_SETCURRENTCHANNEL=1' when compiling in order to restore this
|
||||
functionality. However, it is recommended to use SetCurrentChannel(int ChannelNumber)
|
||||
instead.
|
||||
- The macro DEPRECATED_GETBITMAP and the related code has been removed.
|
||||
- The default for DEPRECATED_SKIN_SETITEMEVENT has been set to 0, which means that
|
||||
the function cSkinDisplayMenu::SetItemEvent() without the TimerActive parameter is
|
||||
no longer available. You can add 'DEPRECATED_SKIN_SETITEMEVENT=1' when compiling in
|
||||
order to restore this functionality. However, it is recommended to use the function
|
||||
with the TimerActive parameter instead.
|
||||
- Now using __cplusplus instead of DISABLE_TEMPLATES_COLLIDING_WITH_STL, and using
|
||||
std::min(), std::max() and std::swap() if available.
|
||||
- No longer permanently looping through PMT PIDs, which caused problems with some
|
||||
SatIP receivers.
|
||||
- Replaced all umlauts in the example channels.conf with their ae, oe, ue substitutes
|
||||
to avoid problems on UTF-8 systems.
|
||||
- Added missing '0x09=H.265 video, 0x19 = AC4 audio' to vdr.5.
|
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.9 2022/02/02 10:56:43 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.1"
|
||||
#define VDRVERSNUM 20601 // 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.1"
|
||||
#define APIVERSNUM 20601 // 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;
|
||||
|
66
cutter.c
66
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 4.6 2018/01/18 12:19:31 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,8 +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);
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName);
|
||||
recordingInfo.Read();
|
||||
recordingInfo.SetFileName(editedVersionName);
|
||||
recordingInfo.Write();
|
||||
SetRecordingTimerId(editedVersionName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
|
||||
cuttingThread = new cCuttingThread(originalVersionName, editedVersionName, &recordingInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -693,6 +736,7 @@ void cCutter::Stop(void)
|
||||
const char *Error = cuttingThread ? cuttingThread->Error() : NULL;
|
||||
delete cuttingThread;
|
||||
cuttingThread = NULL;
|
||||
SetRecordingTimerId(editedVersionName, NULL);
|
||||
if ((Interrupted || Error) && *editedVersionName) {
|
||||
if (Interrupted)
|
||||
isyslog("editing process has been interrupted");
|
||||
|
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:
|
||||
|
101
device.c
101
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.5 2022/01/24 16:53:45 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;
|
||||
@ -249,7 +251,7 @@ cDevice *cDevice::GetDevice(const cChannel *Channel, int Priority, bool LiveView
|
||||
{
|
||||
// Collect the current priorities of all CAM slots that can decrypt the channel:
|
||||
int NumCamSlots = CamSlots.Count();
|
||||
int SlotPriority[NumCamSlots];
|
||||
int SlotPriority[NumCamSlots + 1]; // +1 to avoid a zero sized array in case there are no CAM slots
|
||||
int NumUsableSlots = 0;
|
||||
bool InternalCamNeeded = false;
|
||||
if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
|
||||
@ -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,10 +309,10 @@ 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 <<= 1; imp |= device[i] == cTransferControl::ReceiverDevice(); // avoid the Transfer Mode receiver device
|
||||
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
|
||||
imp <<= 8; imp |= ((NumUsableSlots && !HasInternalCam) ? SlotPriority[j] : IDLEPRIORITY) - IDLEPRIORITY;// use the CAM slot with the lowest priority (- IDLEPRIORITY to assure that values -100..99 can be used)
|
||||
imp <<= 1; imp |= ndr; // avoid devices if we need to detach existing receivers
|
||||
imp <<= 1; imp |= (NumUsableSlots || InternalCamNeeded) ? 0 : device[i]->HasCi(); // avoid cards with Common Interface for FTA channels
|
||||
@ -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
|
||||
|
68
dvbdevice.c
68
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.1 2021/06/09 09:12:25 kls Exp $
|
||||
* $Id: dvbdevice.c 5.8 2024/09/09 08:53:57 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbdevice.h"
|
||||
@ -23,6 +23,8 @@
|
||||
|
||||
static int DvbApiVersion = 0x0000; // the version of the DVB driver actually in use (will be determined by the first device created)
|
||||
|
||||
#define BANDWIDTH_HZ_AUTO 0 // missing in DVB API 5
|
||||
|
||||
#define DVBS_TUNE_TIMEOUT 9000 //ms
|
||||
#define DVBS_LOCK_TIMEOUT 2000 //ms
|
||||
#define DVBC_TUNE_TIMEOUT 9000 //ms
|
||||
@ -59,6 +61,7 @@ const tDvbParameterMap BandwidthValues[] = {
|
||||
{ 8, 8000000, "8 MHz" },
|
||||
{ 10, 10000000, "10 MHz" },
|
||||
{ 1712, 1712000, "1.712 MHz" },
|
||||
{ 999, BANDWIDTH_HZ_AUTO, trNOOP("auto") },
|
||||
{ -1, 0, NULL }
|
||||
};
|
||||
|
||||
@ -255,10 +258,10 @@ bool cDvbTransponderParameters::Parse(const char *s)
|
||||
{
|
||||
polarization = 0;
|
||||
inversion = INVERSION_AUTO;
|
||||
bandwidth = 8000000;
|
||||
bandwidth = BANDWIDTH_HZ_AUTO;
|
||||
coderateH = FEC_AUTO;
|
||||
coderateL = FEC_AUTO;
|
||||
modulation = QPSK;
|
||||
modulation = QAM_AUTO;
|
||||
system = DVB_SYSTEM_1;
|
||||
transmission = TRANSMISSION_MODE_AUTO;
|
||||
guard = GUARD_INTERVAL_AUTO;
|
||||
@ -391,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;
|
||||
}
|
||||
}
|
||||
@ -546,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;
|
||||
@ -590,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;
|
||||
@ -693,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;
|
||||
}
|
||||
@ -856,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) {
|
||||
@ -873,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];
|
||||
@ -1223,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' ;-)
|
||||
@ -1288,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];
|
||||
@ -1533,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));
|
||||
}
|
||||
@ -1765,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 {
|
||||
@ -1855,16 +1898,16 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend)
|
||||
|
||||
// We only check the devices that must be present - the others will be checked before accessing them://XXX
|
||||
|
||||
dvbTuner = new cDvbTuner(this, adapter, frontend);
|
||||
|
||||
StartSectionHandler();
|
||||
|
||||
dvbTuner = new cDvbTuner(this, adapter, frontend);
|
||||
}
|
||||
|
||||
cDvbDevice::~cDvbDevice()
|
||||
{
|
||||
StopSectionHandler();
|
||||
delete dvbTuner;
|
||||
delete ciAdapter;
|
||||
StopSectionHandler();
|
||||
UnBond();
|
||||
// We're not explicitly closing any device files here, since this sometimes
|
||||
// caused segfaults. Besides, the program is about to terminate anyway...
|
||||
@ -2297,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:
|
||||
|
52
dvbplayer.c
52
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.1 2022/01/13 21:41:41 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;
|
||||
}
|
||||
@ -981,8 +990,10 @@ bool cDvbPlayer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
|
||||
// --- cDvbPlayerControl -----------------------------------------------------
|
||||
|
||||
cDvbPlayerControl::cDvbPlayerControl(const char *FileName, bool PauseLive)
|
||||
:cControl(player = new cDvbPlayer(FileName, PauseLive))
|
||||
:cControl(NULL)
|
||||
{
|
||||
player = new cDvbPlayer(FileName, PauseLive);
|
||||
SetPlayer(player);
|
||||
}
|
||||
|
||||
cDvbPlayerControl::~cDvbPlayerControl()
|
||||
@ -1045,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.
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Original author: Marco Schluessler <marco@lordzodiac.de>
|
||||
* With some input from the "subtitles plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
|
||||
*
|
||||
* $Id: dvbsubtitle.c 5.1 2021/03/17 15:24:34 kls Exp $
|
||||
* $Id: dvbsubtitle.c 5.2 2022/12/06 16:57:01 kls Exp $
|
||||
*/
|
||||
|
||||
#include "dvbsubtitle.h"
|
||||
@ -1770,11 +1770,13 @@ void cDvbSubtitleConverter::FinishPage(cDvbSubtitlePage *Page)
|
||||
return;
|
||||
int NumAreas;
|
||||
tArea *Areas = Page->GetAreas(NumAreas);
|
||||
if (!Areas)
|
||||
return;
|
||||
tArea AreaCombined = Page->CombineAreas(NumAreas, Areas);
|
||||
tArea AreaOsd = Page->ScaleArea(AreaCombined, osdFactorX, osdFactorY);
|
||||
int Bpp = 8;
|
||||
bool Reduced = false;
|
||||
if (osd && NumAreas > 0) {
|
||||
if (osd) {
|
||||
while (osd->CanHandleAreas(&AreaOsd, 1) != oeOk) {
|
||||
dbgoutput("CanHandleAreas: %d<br>\n", osd->CanHandleAreas(&AreaOsd, 1));
|
||||
int HalfBpp = Bpp / 2;
|
||||
|
21
eit.c
21
eit.c
@ -8,7 +8,7 @@
|
||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||
* Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
|
||||
*
|
||||
* $Id: eit.c 5.4 2021/12/11 20:58:51 kls Exp $
|
||||
* $Id: eit.c 5.6 2022/12/23 09:47:23 kls Exp $
|
||||
*/
|
||||
|
||||
// The various ways in which broadcasters handle (or screw up) their EPG:
|
||||
@ -115,6 +115,14 @@ cEIT::cEIT(cEitTablesHash &EitTablesHash, int Source, u_char Tid, const u_char *
|
||||
return;
|
||||
}
|
||||
|
||||
cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(Channel, true);
|
||||
|
||||
if (pSchedule->OnActualTp(Tid) && (Tid & 0xF0) == 0x60) {
|
||||
SchedulesStateKey.Remove(false);
|
||||
ChannelsStateKey.Remove(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EpgHandlers.BeginSegmentTransfer(Channel)) {
|
||||
SchedulesStateKey.Remove(false);
|
||||
ChannelsStateKey.Remove(false);
|
||||
@ -123,13 +131,6 @@ cEIT::cEIT(cEitTablesHash &EitTablesHash, int Source, u_char Tid, const u_char *
|
||||
|
||||
bool ChannelsModified = false;
|
||||
bool handledExternally = EpgHandlers.HandledExternally(Channel);
|
||||
cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(Channel, true);
|
||||
|
||||
if (pSchedule->OnActualTp(Tid) && (Tid & 0xF0) == 0x60) {
|
||||
SchedulesStateKey.Remove(false);
|
||||
ChannelsStateKey.Remove(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool Empty = true;
|
||||
bool Modified = false;
|
||||
@ -469,7 +470,7 @@ cTDT::cTDT(const u_char *Data)
|
||||
timespec ts = {};
|
||||
ts.tv_sec = dvbtim;
|
||||
if (clock_settime(CLOCK_REALTIME, &ts) == 0)
|
||||
isyslog("system time changed from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(dvbtim), dvbtim);
|
||||
isyslog("system time changed from %s (%jd) to %s (%jd)", *TimeToString(loctim), intmax_t(loctim), *TimeToString(dvbtim), intmax_t(dvbtim));
|
||||
else
|
||||
esyslog("ERROR while setting system time: %m");
|
||||
}
|
||||
@ -479,7 +480,7 @@ cTDT::cTDT(const u_char *Data)
|
||||
delta.tv_sec = diff;
|
||||
delta.tv_usec = 0;
|
||||
if (adjtime(&delta, NULL) == 0)
|
||||
isyslog("system time adjustment initiated from %s (%ld) to %s (%ld)", *TimeToString(loctim), loctim, *TimeToString(dvbtim), dvbtim);
|
||||
isyslog("system time adjustment initiated from %s (%jd) to %s (%jd)", *TimeToString(loctim), intmax_t(loctim), *TimeToString(dvbtim), intmax_t(dvbtim));
|
||||
else
|
||||
esyslog("ERROR while adjusting system time: %m");
|
||||
}
|
||||
|
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;
|
||||
|
93
epg.c
93
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.6 2021/04/29 09:19:58 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");
|
||||
@ -451,7 +454,7 @@ cString cEvent::GetVpsString(void) const
|
||||
void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
|
||||
{
|
||||
if (InfoOnly || startTime + duration + EPG_LINGER_TIME >= time(NULL)) {
|
||||
fprintf(f, "%sE %u %ld %d %X %X\n", Prefix, eventID, startTime, duration, tableID, version);
|
||||
fprintf(f, "%sE %u %jd %d %X %X\n", Prefix, eventID, intmax_t(startTime), duration, tableID, version);
|
||||
if (!isempty(title))
|
||||
fprintf(f, "%sT %s\n", Prefix, title);
|
||||
if (!isempty(shortText))
|
||||
@ -476,7 +479,7 @@ void cEvent::Dump(FILE *f, const char *Prefix, bool InfoOnly) const
|
||||
}
|
||||
}
|
||||
if (vps)
|
||||
fprintf(f, "%sV %ld\n", Prefix, vps);
|
||||
fprintf(f, "%sV %jd\n", Prefix, intmax_t(vps));
|
||||
if (!InfoOnly && !isempty(aux)) {
|
||||
strreplace(aux, '\n', '|');
|
||||
fprintf(f, "%s@ %s\n", Prefix, aux);
|
||||
@ -518,7 +521,7 @@ bool cEvent::Parse(char *s)
|
||||
components = new cComponents;
|
||||
components->SetComponent(components->NumComponents(), t);
|
||||
break;
|
||||
case 'V': SetVps(atoi(t));
|
||||
case 'V': SetVps(atol(t));
|
||||
break;
|
||||
case '@': strreplace(t, '|', '\n');
|
||||
SetAux(t);
|
||||
@ -541,11 +544,11 @@ bool cEvent::Read(FILE *f, cSchedule *Schedule, int &Line)
|
||||
switch (*s) {
|
||||
case 'E': if (!Event) {
|
||||
unsigned int EventID;
|
||||
time_t StartTime;
|
||||
intmax_t StartTime; // actually time_t, but intmax_t for scanning with "%jd"
|
||||
int Duration;
|
||||
unsigned int TableID = 0;
|
||||
unsigned int Version = 0xFF; // actual value is ignored
|
||||
int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
|
||||
int n = sscanf(t, "%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
|
||||
if (n >= 3 && n <= 5) {
|
||||
Event = (cEvent *)Schedule->GetEventByTime(StartTime);
|
||||
cEvent *newEvent = NULL;
|
||||
@ -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)
|
||||
|
10
epg.h
10
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.2 2021/04/28 20:44:56 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;
|
||||
@ -274,6 +269,7 @@ public:
|
||||
///< therefore the EPG handlers have to take care of this. Otherwise the parsing of
|
||||
///< non-updates will waste a lot of resources.
|
||||
virtual bool SetEventID(cEvent *Event, tEventID EventID) { return false; }
|
||||
///< Important note: if you want VPS to work, do not mess with the event ids!
|
||||
virtual bool SetTitle(cEvent *Event, const char *Title) { return false; }
|
||||
virtual bool SetShortText(cEvent *Event, const char *ShortText) { return false; }
|
||||
virtual bool SetDescription(cEvent *Event, const char *Description) { return false; }
|
||||
@ -298,7 +294,7 @@ public:
|
||||
///< Designed to give handlers the possibility to prepare a database transaction.
|
||||
///< If any EPG handler returns false in this function, it is assumed that
|
||||
///< the EPG for the given Channel has to be handled later due to some transaction problems,
|
||||
///> therefore the processing will aborted.
|
||||
///> therefore the processing will be aborted.
|
||||
///< Dummy is for backward compatibility and may be removed in a future version.
|
||||
virtual bool EndSegmentTransfer(bool Modified, bool Dummy) { return false; } // TODO remove obsolete Dummy
|
||||
///< Called after the segment data has been processed.
|
||||
|
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 {
|
||||
|
7
font.c
7
font.c
@ -6,7 +6,7 @@
|
||||
*
|
||||
* BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya.
|
||||
*
|
||||
* $Id: font.c 5.1 2021/12/20 13:19:52 kls Exp $
|
||||
* $Id: font.c 5.3 2025/02/17 11:13:13 kls Exp $
|
||||
*/
|
||||
|
||||
#include "font.h"
|
||||
@ -74,7 +74,8 @@ cGlyph::cGlyph(uint CharCode, FT_GlyphSlotRec_ *GlyphData)
|
||||
rows = GlyphData->bitmap.rows;
|
||||
pitch = GlyphData->bitmap.pitch;
|
||||
bitmap = MALLOC(uchar, rows * pitch);
|
||||
memcpy(bitmap, GlyphData->bitmap.buffer, rows * pitch);
|
||||
if (int bytes = rows * pitch)
|
||||
memcpy(bitmap, GlyphData->bitmap.buffer, bytes);
|
||||
}
|
||||
|
||||
cGlyph::~cGlyph()
|
||||
@ -617,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;
|
||||
}
|
||||
|
3
i18n.c
3
i18n.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: i18n.c 5.1 2021/05/21 09:50:57 kls Exp $
|
||||
* $Id: i18n.c 5.2 2022/12/01 20:57:12 kls Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -81,6 +81,7 @@ struct tSpecialLc { const char *Code; const char *Name; };
|
||||
const struct tSpecialLc SpecialLanguageCodeList[] = {
|
||||
{ "qaa", trNOOP("LanguageName$original language (qaa)") },
|
||||
{ "qad", trNOOP("LanguageName$audio description (qad)") },
|
||||
{ "qks", trNOOP("LanguageName$clear speech (qks)") },
|
||||
{ "mis", trNOOP("LanguageName$uncoded languages (mis)") },
|
||||
{ "mul", trNOOP("LanguageName$multiple languages (mul)") },
|
||||
{ "nar", trNOOP("LanguageName$narrative (nar)") },
|
||||
|
@ -6,7 +6,7 @@
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* $Id: si.h 4.3 2020/05/15 12:32:51 kls Exp $
|
||||
* $Id: si.h 5.1 2023/02/16 17:20:09 kls Exp $
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
@ -47,7 +47,6 @@ enum TableId { TableIdPAT = 0x00, //program association section
|
||||
TableIdPremiereCIT = 0xA0 //premiere content information section
|
||||
};
|
||||
|
||||
|
||||
enum DescriptorTag {
|
||||
// defined by ISO/IEC 13818-1
|
||||
VideoStreamDescriptorTag = 0x02,
|
||||
|
@ -6,7 +6,7 @@
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* $Id: util.h 2.3 2012/02/26 13:58:26 kls Exp $
|
||||
* $Id: util.h 5.1 2023/02/16 17:20:09 kls Exp $
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
@ -121,8 +121,6 @@ private:
|
||||
int off;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//abstract base class
|
||||
class Parsable {
|
||||
public:
|
||||
|
150
lirc.c
150
lirc.c
@ -6,24 +6,50 @@
|
||||
*
|
||||
* LIRC support added by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
|
||||
*
|
||||
* $Id: lirc.c 4.2 2020/09/16 13:48:33 kls Exp $
|
||||
* $Id: lirc.c 5.2 2023/02/16 17:15:06 kls Exp $
|
||||
*/
|
||||
|
||||
#include "lirc.h"
|
||||
#include <linux/version.h>
|
||||
#define HAVE_KERNEL_LIRC (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0))
|
||||
// cLircUsrRemote
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
// cLircDevRemote
|
||||
#if HAVE_KERNEL_LIRC
|
||||
#include <linux/lirc.h>
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#define RECONNECTDELAY 3000 // ms
|
||||
|
||||
cLircRemote::cLircRemote(const char *DeviceName)
|
||||
:cRemote("LIRC")
|
||||
class cLircUsrRemote : public cLircRemote {
|
||||
private:
|
||||
enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
|
||||
struct sockaddr_un addr;
|
||||
bool Connect(void);
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cLircUsrRemote(const char *DeviceName);
|
||||
};
|
||||
|
||||
#if HAVE_KERNEL_LIRC
|
||||
class cLircDevRemote : public cLircRemote {
|
||||
private:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cLircDevRemote(void);
|
||||
bool Connect(const char *DeviceName);
|
||||
};
|
||||
#endif
|
||||
|
||||
// --- cLircRemote -----------------------------------------------------------
|
||||
|
||||
cLircRemote::cLircRemote(const char *Name)
|
||||
:cRemote(Name)
|
||||
,cThread("LIRC remote control")
|
||||
{
|
||||
addr.sun_family = AF_UNIX;
|
||||
strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
|
||||
if (!Connect())
|
||||
f = -1;
|
||||
Start();
|
||||
}
|
||||
|
||||
cLircRemote::~cLircRemote()
|
||||
@ -35,7 +61,28 @@ cLircRemote::~cLircRemote()
|
||||
close(fh);
|
||||
}
|
||||
|
||||
bool cLircRemote::Connect(void)
|
||||
void cLircRemote::NewLircRemote(const char *Name)
|
||||
{
|
||||
#if HAVE_KERNEL_LIRC
|
||||
cLircDevRemote *r = new cLircDevRemote();
|
||||
if (r->Connect(Name))
|
||||
return;
|
||||
delete r;
|
||||
#endif
|
||||
new cLircUsrRemote(Name);
|
||||
}
|
||||
// --- cLircUsrRemote --------------------------------------------------------
|
||||
|
||||
cLircUsrRemote::cLircUsrRemote(const char *DeviceName)
|
||||
: cLircRemote("LIRC")
|
||||
{
|
||||
addr.sun_family = AF_UNIX;
|
||||
strn0cpy(addr.sun_path, DeviceName, sizeof(addr.sun_path));
|
||||
Connect();
|
||||
Start();
|
||||
}
|
||||
|
||||
bool cLircUsrRemote::Connect(void)
|
||||
{
|
||||
if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
|
||||
if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0)
|
||||
@ -54,7 +101,7 @@ bool cLircRemote::Ready(void)
|
||||
return f >= 0;
|
||||
}
|
||||
|
||||
void cLircRemote::Action(void)
|
||||
void cLircUsrRemote::Action(void)
|
||||
{
|
||||
cTimeMs FirstTime;
|
||||
cTimeMs LastTime;
|
||||
@ -129,3 +176,86 @@ void cLircRemote::Action(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- cLircDevRemote --------------------------------------------------------
|
||||
|
||||
#if HAVE_KERNEL_LIRC
|
||||
bool cLircDevRemote::Connect(const char *DeviceName)
|
||||
{
|
||||
unsigned mode = LIRC_MODE_SCANCODE;
|
||||
f = open(DeviceName, O_RDONLY, 0);
|
||||
if (f < 0) {
|
||||
switch (errno) {
|
||||
case ENXIO:
|
||||
case ENODEV:
|
||||
// Do not complain about an attempt to open a lircd socket file.
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR_STR(DeviceName);
|
||||
}
|
||||
}
|
||||
else if (ioctl(f, LIRC_SET_REC_MODE, &mode)) {
|
||||
LOG_ERROR_STR(DeviceName);
|
||||
close(f);
|
||||
f = -1;
|
||||
}
|
||||
if (f >= 0)
|
||||
Start();
|
||||
return f >= 0;
|
||||
}
|
||||
|
||||
cLircDevRemote::cLircDevRemote(void)
|
||||
:cLircRemote("DEV_LIRC")
|
||||
{
|
||||
}
|
||||
|
||||
void cLircDevRemote::Action(void)
|
||||
{
|
||||
if (f < 0)
|
||||
return;
|
||||
uint64_t FirstTime = 0, LastTime = 0;
|
||||
uint32_t LastKeyCode = 0;
|
||||
uint16_t LastFlags = false;
|
||||
bool SeenRepeat = false;
|
||||
bool repeat = false;
|
||||
|
||||
while (Running()) {
|
||||
lirc_scancode sc;
|
||||
ssize_t ret = read(f, &sc, sizeof sc);
|
||||
|
||||
if (ret == sizeof sc) {
|
||||
const bool SameKey = sc.keycode == LastKeyCode && !((sc.flags ^ LastFlags) & LIRC_SCANCODE_FLAG_TOGGLE);
|
||||
|
||||
if (sc.flags & LIRC_SCANCODE_FLAG_REPEAT != 0)
|
||||
// Before Linux 6.0, this flag is never set for some devices.
|
||||
SeenRepeat = true;
|
||||
|
||||
if (SameKey && uint((sc.timestamp - FirstTime) / 1000000) < uint(Setup.RcRepeatDelay))
|
||||
continue; // skip keys coming in too fast
|
||||
|
||||
if (!SameKey || (SeenRepeat && !(sc.flags & LIRC_SCANCODE_FLAG_REPEAT))) {
|
||||
// This is a key-press event, not key-repeat.
|
||||
if (repeat)
|
||||
Put(LastKeyCode, false, true); // generated release for previous key
|
||||
repeat = false;
|
||||
FirstTime = sc.timestamp;
|
||||
LastKeyCode = sc.keycode;
|
||||
LastFlags = sc.flags;
|
||||
}
|
||||
else if (uint((sc.timestamp - LastTime) / 1000000) < uint(Setup.RcRepeatDelta))
|
||||
continue; // filter out too frequent key-repeat events
|
||||
else
|
||||
repeat = true;
|
||||
|
||||
LastTime = sc.timestamp;
|
||||
Put(sc.keycode, repeat);
|
||||
}
|
||||
else {
|
||||
if (repeat) // the last one was a repeat, so let's generate a release
|
||||
Put(LastKeyCode, false, true);
|
||||
repeat = false;
|
||||
LastKeyCode = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
14
lirc.h
14
lirc.h
@ -4,27 +4,23 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: lirc.h 1.4 2006/01/27 16:00:19 kls Exp $
|
||||
* $Id: lirc.h 5.1 2022/11/26 13:37:06 kls Exp $
|
||||
*/
|
||||
|
||||
#ifndef __LIRC_H
|
||||
#define __LIRC_H
|
||||
|
||||
#include <sys/un.h>
|
||||
#include "remote.h"
|
||||
#include "thread.h"
|
||||
|
||||
class cLircRemote : public cRemote, private cThread {
|
||||
private:
|
||||
enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
|
||||
class cLircRemote : public cRemote, protected cThread {
|
||||
protected:
|
||||
int f;
|
||||
struct sockaddr_un addr;
|
||||
virtual void Action(void);
|
||||
bool Connect(void);
|
||||
cLircRemote(const char *Name);
|
||||
public:
|
||||
cLircRemote(const char *DeviceName);
|
||||
virtual ~cLircRemote();
|
||||
virtual bool Ready(void);
|
||||
static void NewLircRemote(const char *Name);
|
||||
};
|
||||
|
||||
#endif //__LIRC_H
|
||||
|
155
menu.c
155
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.6 2021/05/21 10:41:31 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;
|
||||
}
|
||||
@ -2097,7 +2102,7 @@ eOSState cMenuSchedule::Switch(void)
|
||||
if (item) {
|
||||
LOCK_CHANNELS_READ;
|
||||
const cChannel *Channel = NULL;
|
||||
if (Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) {
|
||||
if ((Channel = Channels->GetByChannelID(item->event->ChannelID(), true)) != NULL) {
|
||||
if (!Channels->SwitchTo(Channel->Number()))
|
||||
Channel = NULL;
|
||||
}
|
||||
@ -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;
|
||||
@ -3250,7 +3250,7 @@ static bool TimerStillRecording(const char *FileName)
|
||||
int Id;
|
||||
char *RemoteBuf = NULL;
|
||||
cString Remote;
|
||||
if (2 == sscanf(TimerId, "%d@%m[^ \n]", &Id, &RemoteBuf)) {
|
||||
if (2 == sscanf(TimerId, "%d@%m[^ \n]", &Id, &RemoteBuf) && Id != 0) {
|
||||
Remote = RemoteBuf;
|
||||
free(RemoteBuf);
|
||||
if (Interface->Confirm(tr("Timer still recording - really delete?"))) {
|
||||
@ -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;
|
||||
@ -5418,9 +5432,9 @@ bool cRecordControl::GetEvent(void)
|
||||
void cRecordControl::Stop(bool ExecuteUserCommand)
|
||||
{
|
||||
if (timer) {
|
||||
bool Finished = timer->HasFlags(tfActive) && !timer->Matches();
|
||||
if (recorder) {
|
||||
int Errors = recorder->Errors();
|
||||
bool Finished = timer->HasFlags(tfActive) && !timer->Matches();
|
||||
isyslog("timer %s %s with %d error%s", *timer->ToDescr(), Finished ? "finished" : "stopped", Errors, Errors != 1 ? "s" : "");
|
||||
if (timer->HasFlags(tfAvoid) && Errors == 0 && Finished) {
|
||||
const char *p = strgetlast(timer->File(), FOLDERDELIMCHAR);
|
||||
@ -5432,7 +5446,7 @@ void cRecordControl::Stop(bool ExecuteUserCommand)
|
||||
timer = NULL;
|
||||
SetRecordingTimerId(fileName, NULL);
|
||||
cStatus::MsgRecording(device, NULL, fileName, false);
|
||||
if (ExecuteUserCommand)
|
||||
if (ExecuteUserCommand && Finished)
|
||||
cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
12
nit.c
12
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.2 2021/12/14 21:15:02 kls Exp $
|
||||
* $Id: nit.c 5.4 2024/05/29 11:13:34 kls Exp $
|
||||
*/
|
||||
|
||||
#include "nit.h"
|
||||
@ -81,8 +81,8 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
|
||||
for (SI::Loop::Iterator it3; fld->frequencies.hasNext(it3); ) {
|
||||
int f = fld->frequencies.getNext(it3);
|
||||
switch (ct) {
|
||||
case 1: f = BCD2INT(f) / 100; break;
|
||||
case 2: f = BCD2INT(f) / 10; break;
|
||||
case 1: f = round(BCD2INT(f) / 100.0); break;
|
||||
case 2: f = round(BCD2INT(f) / 10.0); break;
|
||||
case 3: f = f * 10; break;
|
||||
default: ;
|
||||
}
|
||||
@ -112,7 +112,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
|
||||
SI::SatelliteDeliverySystemDescriptor *sd = (SI::SatelliteDeliverySystemDescriptor *)d;
|
||||
cDvbTransponderParameters dtp;
|
||||
int Source = cSource::FromData(cSource::stSat, BCD2INT(sd->getOrbitalPosition()), sd->getWestEastFlag());
|
||||
int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 100;
|
||||
int Frequency = Frequencies[0] = round(BCD2INT(sd->getFrequency()) / 100.0);
|
||||
static char Polarizations[] = { 'H', 'V', 'L', 'R' };
|
||||
dtp.SetPolarization(Polarizations[sd->getPolarization()]);
|
||||
static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
|
||||
@ -184,7 +184,7 @@ void cNitFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
|
||||
SI::CableDeliverySystemDescriptor *sd = (SI::CableDeliverySystemDescriptor *)d;
|
||||
cDvbTransponderParameters dtp;
|
||||
int Source = cSource::FromData(cSource::stCable);
|
||||
int Frequency = Frequencies[0] = BCD2INT(sd->getFrequency()) / 10;
|
||||
int Frequency = Frequencies[0] = round(BCD2INT(sd->getFrequency()) / 10.0);
|
||||
//XXX FEC_outer???
|
||||
static int CodeRates[] = { FEC_NONE, FEC_1_2, FEC_2_3, FEC_3_4, FEC_5_6, FEC_7_8, FEC_8_9, FEC_3_5, FEC_4_5, FEC_9_10, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_AUTO, FEC_NONE };
|
||||
dtp.SetCoderateH(CodeRates[sd->getFecInner()]);
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
14
player.h
14
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 4.5 2020/05/18 16:47:29 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.
|
||||
@ -107,6 +109,7 @@ public:
|
||||
///< Deletion of the marks themselves is handled separately, calling
|
||||
///< this function merely tells the player to no longer display the
|
||||
///< marks, if it has any.
|
||||
void SetPlayer(cPlayer *Player) { player = Player; }
|
||||
double FramesPerSecond(void) const { return player ? player->FramesPerSecond() : DEFAULTFRAMESPERSECOND; }
|
||||
bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false) const { return player ? player->GetIndex(Current, Total, SnapToIFrame) : false; }
|
||||
bool GetFrameNumber(int &Current, int &Total) const { return player ? player->GetFrameNumber(Current, Total) : false; }
|
||||
@ -114,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);
|
||||
|
38
po/ar.po
38
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: 2021-07-01 17:10+0200\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"
|
||||
@ -353,6 +353,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -842,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 ""
|
||||
|
||||
@ -1025,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 "معدل اصلاح اخطاء الدليل الالكترونى"
|
||||
|
||||
@ -1522,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 "مساحة القرص غير كافية"
|
||||
@ -1547,15 +1559,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "اغلق الحاسوب"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "تم تسجيل %ld دقيقة سيتم الاغلاق على اى حال"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "تم تسجيل %jd دقيقة سيتم الاغلاق على اى حال"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "الاغلاق على اى حال"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s wakes up in %ld min, continue?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s wakes up in %jd min, continue?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Editing - restart anyway?"
|
||||
@ -1566,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 "الصوت "
|
||||
|
34
po/ca_ES.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -352,6 +352,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -841,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 ""
|
||||
|
||||
@ -1024,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"
|
||||
|
||||
@ -1521,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!"
|
||||
@ -1546,15 +1558,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Gravant - Apagar de totes maneres?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Hi ha una gravació en %ld minuts - Apagar de totes maneres?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Hi ha una gravació en %jd minuts - Apagar de totes maneres?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "Apagar de totes maneres?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "El plugin %s s'activarà en %ld minuts, continuar?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "El plugin %s s'activarà en %jd minuts, continuar?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Editant - Reiniciar de totes maneres?"
|
||||
@ -1565,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 "
|
||||
|
34
po/cs_CZ.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -352,6 +352,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -841,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"
|
||||
|
||||
@ -1024,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"
|
||||
|
||||
@ -1521,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!"
|
||||
@ -1546,15 +1558,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Systém nahrává - přesto vypnout?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Nahrávání začne za %ld minut - přesto vypnout?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Nahrávání začne za %jd minut - přesto vypnout?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "přesto vypnout?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Modul %s startuje za %ld min., pokračovat?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Modul %s startuje za %jd min., pokračovat?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Edituji - přesto restartovat?"
|
||||
@ -1565,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 "
|
||||
|
34
po/da_DK.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -349,6 +349,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -838,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 ""
|
||||
|
||||
@ -1021,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"
|
||||
|
||||
@ -1518,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!"
|
||||
@ -1543,15 +1555,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Optagelse igang - sluk alligevel?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Optagelse om %ld minutter - sluk alligevel?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Optagelse om %jd minutter - sluk alligevel?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "sluk alligevel?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s vågner om %ld min, fortsæt?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s vågner om %jd min, fortsæt?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Redigering igang - genstart alligevel?"
|
||||
@ -1562,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 "
|
||||
|
37
po/de_DE.po
37
po/de_DE.po
@ -3,12 +3,13 @@
|
||||
# This file is distributed under the same license as the VDR package.
|
||||
# Klaus Schmidinger <vdr@tvdr.de>, 2000-2015
|
||||
# Albert Danis <a.danis@gmx.de>, 2015
|
||||
# Johann Friedrichs <johann.friedrichs@web.de>, 2022
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: VDR 2.6.0\n"
|
||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n"
|
||||
"POT-Creation-Date: 2021-07-01 17:10+0200\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"
|
||||
@ -131,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"
|
||||
@ -141,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"
|
||||
@ -209,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"
|
||||
@ -350,6 +351,9 @@ msgstr "Originalton (qaa)"
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr "Audiodeskription (qad)"
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr "Klare Sprache"
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr "einzelne andere Sprachen (mis)"
|
||||
|
||||
@ -839,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"
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1557,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Aufnahme läuft - trotzdem ausschalten?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Aufnahme in %ld Minuten - trotzdem ausschalten?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Aufnahme in %jd Minuten - trotzdem ausschalten?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "trotzdem ausschalten?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s wacht in %ld Min auf, weiter?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s wacht in %jd Min auf, weiter?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Bearbeitung läuft - trotzdem neu starten?"
|
||||
@ -1563,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 "
|
||||
|
32
po/el_GR.po
32
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: 2021-07-01 17:10+0200\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"
|
||||
@ -349,6 +349,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -838,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 ""
|
||||
|
||||
@ -1021,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"
|
||||
|
||||
@ -1518,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 "Ï óêëçñüò êïíôåýåé íÜ ãåìßóåé!"
|
||||
@ -1543,14 +1555,14 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Ãßíåôáé åããñáöÞ - ÔåëéêÜ íá ãßíåé ôåñìáôéóìüò?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "ÁíáìÝíåôáé åããñáöÞ óÝ %ld ëåðôÜ - ÔåëéêÜ íá ôåñìáôéóôåß?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "ÁíáìÝíåôáé åããñáöÞ óÝ %jd ëåðôÜ - ÔåëéêÜ íá ôåñìáôéóôåß?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "ÔåëéêÜ íá ãßíåé ôåñìáôéóìüò?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
@ -1562,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 "¸íôáóç "
|
||||
|
34
po/es_ES.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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!"
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Grabación en curso - ¿apagar igualmente?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Grabación dentro de %ld minutos, ¿apagar igualmente?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Grabación dentro de %jd minutos, ¿apagar igualmente?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "¿apagar igualmente?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s inicia en %ld min, ¿continuar?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s inicia en %jd min, ¿continuar?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Editando - ¿reiniciar igualmente?"
|
||||
@ -1563,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 "
|
||||
|
34
po/et_EE.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -349,6 +349,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -838,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!"
|
||||
|
||||
@ -1021,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"
|
||||
|
||||
@ -1518,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!"
|
||||
@ -1543,15 +1555,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Salvestamine aktiivne - lülitada välja?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Salvestamine algab %ld minuti pärast - lülitada välja?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Salvestamine algab %jd minuti pärast - lülitada välja?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "lülitada välja?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s ärkab %ld minuti pärast, jätkata?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s ärkab %jd minuti pärast, jätkata?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Töötlemine aktiivne - taaskäivitada?"
|
||||
@ -1562,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 "
|
||||
|
40
po/fi_FI.po
40
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: 2021-07-01 17:10+0200\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"
|
||||
@ -353,6 +353,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -794,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!"
|
||||
@ -842,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!"
|
||||
|
||||
@ -1025,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"
|
||||
|
||||
@ -1514,7 +1526,7 @@ msgid "Button$Insert"
|
||||
msgstr "Lisää"
|
||||
|
||||
msgid "Button$Macro"
|
||||
msgstr ""
|
||||
msgstr "Makro"
|
||||
|
||||
msgid "Plugin"
|
||||
msgstr "Laajennos"
|
||||
@ -1522,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!"
|
||||
@ -1547,15 +1559,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Tallennus kesken - sammutetaanko?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Tallennus alkaa %ld min kuluttua - sammutetaanko?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Tallennus alkaa %jd min kuluttua - sammutetaanko?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "sammutetaanko?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Laajennos %s herää %ld minuutin kuluttua - sammutetaanko?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Laajennos %s herää %jd minuutin kuluttua - sammutetaanko?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Muokkaus kesken - käynnistetäänkö uudelleen?"
|
||||
@ -1566,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 "
|
||||
|
34
po/fr_FR.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -360,6 +360,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -849,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 !"
|
||||
|
||||
@ -1032,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"
|
||||
|
||||
@ -1529,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 !"
|
||||
@ -1554,15 +1566,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Enregistrement en cours - confirmer l'arrêt ?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Enregistrement dans %ld minutes - confirmer l'arrêt ?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Enregistrement dans %jd minutes - confirmer l'arrêt ?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "confirmer l'arrêt ?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Le plugin %s démarre dans %ld min, continuer ?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Le plugin %s démarre dans %jd min, continuer ?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Édition en cours - confirmer redémarrage ?"
|
||||
@ -1573,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 "
|
||||
|
34
po/hr_HR.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -351,6 +351,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -840,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 ""
|
||||
|
||||
@ -1023,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"
|
||||
|
||||
@ -1520,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!"
|
||||
@ -1545,15 +1557,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Snimanje traje - iskljuèiti unatoè?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Snimanje za %ld minuta - iskljuèiti unatoè?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Snimanje za %jd minuta - iskljuèiti unatoè?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "svejedno iskljuèiti?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Dodatak %s se budi u %ld min, nastavi?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Dodatak %s se budi u %jd min, nastavi?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Ureðivanje u tijeku - ponovno pokreni unatoè?"
|
||||
@ -1564,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 "
|
||||
|
75
po/hu_HU.po
75
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: 2021-07-01 17:10+0200\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,25 +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 "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"
|
||||
@ -684,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"
|
||||
@ -696,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ő"
|
||||
@ -795,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!"
|
||||
@ -843,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!"
|
||||
|
||||
@ -1026,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"
|
||||
|
||||
@ -1515,7 +1530,7 @@ msgid "Button$Insert"
|
||||
msgstr "Beillesztés"
|
||||
|
||||
msgid "Button$Macro"
|
||||
msgstr ""
|
||||
msgstr "Makró"
|
||||
|
||||
msgid "Plugin"
|
||||
msgstr "Plugin"
|
||||
@ -1523,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!"
|
||||
@ -1548,15 +1563,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Felvétel folyamatban - mégis leállítani?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Felvétel %ld perc múlva kezdődik - mégis leállítani?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Felvétel %jd perc múlva kezdődik - mégis leállítani?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "mégis leállítani?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "A(z) %s plugin ébreszt %ld perc múlva, folytatni?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "A(z) %s plugin ébreszt %jd perc múlva, folytatni?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Szerkesztés folyamatban - mégis újraindítani?"
|
||||
@ -1567,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!
|
||||
|
40
po/it_IT.po
40
po/it_IT.po
@ -5,15 +5,15 @@
|
||||
# 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: 2021-07-01 17:10+0200\n"
|
||||
"PO-Revision-Date: 2022-01-03 16:42+0100\n"
|
||||
"Last-Translator: Gringo <vdr-italian@tiscali.it>\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"
|
||||
"MIME-Version: 1.0\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"
|
||||
@ -355,6 +355,9 @@ msgstr "lingua originale (qaa)"
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr "descrizione audio (qad)"
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr "discorso chiaro (qks)"
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr "lingue non codificate (mis)"
|
||||
|
||||
@ -844,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!"
|
||||
|
||||
@ -1027,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"
|
||||
|
||||
@ -1524,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!"
|
||||
@ -1549,15 +1561,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Registrazione in corso - spegnere comunque?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Registrazione tra %ld minuti - spegnere comunque?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Registrazione tra %jd minuti - spegnere comunque?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "spegnere comunque?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Il plugin %s si ripristina tra %ld min, continuare?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Il plugin %s si ripristina tra %jd min, continuare?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Modifica in corso - riavviare comunque?"
|
||||
@ -1568,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 "
|
||||
|
34
po/lt_LT.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -349,6 +349,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -838,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ę!"
|
||||
|
||||
@ -1021,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"
|
||||
|
||||
@ -1518,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!"
|
||||
@ -1543,15 +1555,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Įrašinėjama - išjungti vistiek?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Įrašas prasidės po %ld minučių, išjungti vistiek?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Įrašas prasidės po %jd minučių, išjungti vistiek?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "išjungti bet kokiu atveju?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Įskiepas %s aktyvuosis po %ld min, tęsti toliau?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Įskiepas %s aktyvuosis po %jd min, tęsti toliau?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Koreguojama - perkrauti vistiek?"
|
||||
@ -1562,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 "
|
||||
|
34
po/mk_MK.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -351,6 +351,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -840,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 "Грешка при редење на снимка за сечење!"
|
||||
|
||||
@ -1023,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 грешки"
|
||||
|
||||
@ -1520,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 "Малку простор на дискот!"
|
||||
@ -1545,15 +1557,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Снимање во тек - исклучи?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Снимање уште %ld минути - исклучи?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Снимање уште %jd минути - исклучи?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "сеедно исклучи?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Додатокот %s се подига за %ld мин, продолжи?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Додатокот %s се подига за %jd мин, продолжи?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Уредување во тек - рестартирај?"
|
||||
@ -1564,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 "Глас "
|
||||
|
34
po/nl_NL.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -355,6 +355,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -844,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!"
|
||||
|
||||
@ -1027,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"
|
||||
|
||||
@ -1524,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!"
|
||||
@ -1549,15 +1561,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Opname loopt - toch uitschakelen?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Opname in %ld minuten - toch uitschakelen?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Opname in %jd minuten - toch uitschakelen?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "toch uitschakelen?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s wordt binnen %ld min. aktief, doorgaan?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s wordt binnen %jd min. aktief, doorgaan?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Bewerken aktief - toch herstarten?"
|
||||
@ -1568,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 "
|
||||
|
32
po/nn_NO.po
32
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 ""
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,14 +1556,14 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Gjør opptak - slå av likevel?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Skal gjøre opptak om %ld minutter - slå av likevel?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Skal gjøre opptak om %jd minutter - slå av likevel?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "slå av likevel?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
@ -1563,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 "
|
||||
|
34
po/pl_PL.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -354,6 +354,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -843,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!"
|
||||
|
||||
@ -1026,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"
|
||||
|
||||
@ -1523,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!"
|
||||
@ -1548,15 +1560,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Trwa nagrywanie - wyłączyć mimo to?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Nagrywanie za %ld minut - wyłączyć mimo to?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Nagrywanie za %jd minut - wyłączyć mimo to?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "wyłączyć mimo to?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Wtyczka %s obudzi się za %ld min, kontynuować?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Wtyczka %s obudzi się za %jd min, kontynuować?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Montaż w trakcie - Zrestartować mimo to?"
|
||||
@ -1567,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ść "
|
||||
|
34
po/pt_PT.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 ""
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "A gravar - desligar mesmo assim?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Gravação programada em %ld minutos - desligar mesmo assim?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Gravação programada em %jd minutos - desligar mesmo assim?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "desligar mesmo assim?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "O plugin %s acorda daqui a %ld minuots, continuar?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "O plugin %s acorda daqui a %jd minuots, continuar?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "A editar - reiniciar mesmo assim?"
|
||||
@ -1563,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 "
|
||||
|
34
po/ro_RO.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -351,6 +351,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -840,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!"
|
||||
|
||||
@ -1023,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"
|
||||
|
||||
@ -1520,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!"
|
||||
@ -1545,15 +1557,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Tocmai se înregistrează - închid, totuşi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Înregistrez peste %ld minute - închid, totuşi?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Înregistrez peste %jd minute - închid, totuşi?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "închid, totuşi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin-ul %s se va trezi +n %ld min, continui?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin-ul %s se va trezi +n %jd min, continui?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Editarea tocmai se efectuează - repornesc, totuşi?"
|
||||
@ -1564,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 "
|
||||
|
34
po/ru_RU.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 "Ошибка очереди записи для обрезки!"
|
||||
|
||||
@ -1022,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 "Уровень коррекции ошибок"
|
||||
|
||||
@ -1519,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 "Недостаточно места на диске!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Идёт запись - действительно выключить?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Через %ld минут начнётся запись - действительно выключить?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Через %jd минут начнётся запись - действительно выключить?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "действительно выключить?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s проснется через %ld минут - продолжить?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s проснется через %jd минут - продолжить?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Процесс монтажа - действительно перезапустить?"
|
||||
@ -1563,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 "Громкость "
|
||||
|
34
po/sk_SK.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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!"
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Nahráva - aj tak vypnú»?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Nahrávanie zaène za %ld minút - aj tak vypnú»?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Nahrávanie zaène za %jd minút - aj tak vypnú»?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "Urèite vypnú»?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Modul %s sa prebudí za %ld min., pokraèova»?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Modul %s sa prebudí za %jd min., pokraèova»?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Upravuje sa - aj tak re¹tartova»?"
|
||||
@ -1563,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» "
|
||||
|
34
po/sl_SI.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 ""
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Snemanje - zares izklopi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Snemanje èez %ld minut, zares izklopi?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Snemanje èez %jd minut, zares izklopi?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "zares izklopi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Vstavek %s se zbudi v %ld min, nadaljuj?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Vstavek %s se zbudi v %jd min, nadaljuj?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Urejanje - zares ponoven zagon?"
|
||||
@ -1563,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 "
|
||||
|
34
po/sr_RS.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 ""
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Snimanje u toku - svejedno iskljuèi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Snimanje za %ld minuta - svejedno iskljuèi?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Snimanje za %jd minuta - svejedno iskljuèi?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "svejedno iskljuèi?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Dodatak %s se budi za %ld min, nastavi?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Dodatak %s se budi za %jd min, nastavi?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Ureðivanje u toku - svejedno ponovno pokreni?"
|
||||
@ -1563,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 "
|
||||
|
34
po/sv_SE.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -354,6 +354,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -843,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!"
|
||||
|
||||
@ -1026,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"
|
||||
|
||||
@ -1523,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!"
|
||||
@ -1548,15 +1560,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Inspelning pågår, vill du avsluta ändå?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Inspelning startar om %ld minuter, vill du avsluta ändå?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Inspelning startar om %jd minuter, vill du avsluta ändå?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "vill du avsluta ändå?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Plugin %s aktiv om %ld min, vill du fortsätta?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Plugin %s aktiv om %jd min, vill du fortsätta?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Editering pågår, vill du starta om ändå?"
|
||||
@ -1567,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 "
|
||||
|
34
po/tr_TR.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -349,6 +349,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -838,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 ""
|
||||
|
||||
@ -1021,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ü"
|
||||
|
||||
@ -1518,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!"
|
||||
@ -1543,15 +1555,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Kayýt ediliyor - buna raðmen kapat?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Kayýt %ld dakikada baþlýyor - buna raðmen kapat?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Kayýt %jd dakikada baþlýyor - buna raðmen kapat?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "buna raðmen kapat?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Eklenti %s %ld dakikada açýlýyor, devam et?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Eklenti %s %jd dakikada açýlýyor, devam et?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Kesim aktif - buna raðmen yeniden baþlat?"
|
||||
@ -1562,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 "
|
||||
|
34
po/uk_UA.po
34
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: 2021-07-01 17:10+0200\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"
|
||||
@ -350,6 +350,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -839,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 "Помилка перешуковування запису для обрізання!"
|
||||
|
||||
@ -1022,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"
|
||||
|
||||
@ -1519,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 "Недостатньо місця на диску!"
|
||||
@ -1544,15 +1556,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "Йде запис - дійсно виключити?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "Через %ld хвилин почнеться запис - дійсно виключити?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "Через %jd хвилин почнеться запис - дійсно виключити?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "дійсно виключити?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "Модуль %s включиться через %ld хвилин - продовжити?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "Модуль %s включиться через %jd хвилин - продовжити?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "Процес монтажу - дійсно перезапустити?"
|
||||
@ -1563,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 "Гучність "
|
||||
|
36
po/zh_CN.po
36
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: 2021-07-01 17:10+0200\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"
|
||||
@ -351,6 +351,9 @@ msgstr ""
|
||||
msgid "LanguageName$audio description (qad)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$clear speech (qks)"
|
||||
msgstr ""
|
||||
|
||||
msgid "LanguageName$uncoded languages (mis)"
|
||||
msgstr ""
|
||||
|
||||
@ -840,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 ""
|
||||
|
||||
@ -1023,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 "节目单问题修复级别"
|
||||
|
||||
@ -1520,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 "磁盘空间不足!"
|
||||
@ -1545,15 +1557,15 @@ msgid "Recording - shut down anyway?"
|
||||
msgstr "录像中,是否立即关机?"
|
||||
|
||||
#, c-format
|
||||
msgid "Recording in %ld minutes, shut down anyway?"
|
||||
msgstr "录像已有 %ld 分钟,立即关机?"
|
||||
msgid "Recording in %jd minutes, shut down anyway?"
|
||||
msgstr "录像已有 %jd 分钟,立即关机?"
|
||||
|
||||
msgid "shut down anyway?"
|
||||
msgstr "是否立即关机?"
|
||||
|
||||
#, c-format
|
||||
msgid "Plugin %s wakes up in %ld min, continue?"
|
||||
msgstr "插件 %s 唤醒在 %ld 分钟,是否继续?"
|
||||
msgid "Plugin %s wakes up in %jd min, continue?"
|
||||
msgstr "插件 %s 唤醒在 %jd 分钟,是否继续?"
|
||||
|
||||
msgid "Editing - restart anyway?"
|
||||
msgstr "编辑中,是否现在重启?"
|
||||
@ -1564,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
|
||||
|
316
recording.c
316
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.14 2022/01/24 10:44:21 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"
|
||||
@ -66,7 +65,6 @@
|
||||
#define DISKCHECKDELTA 100 // seconds between checks for free disk space
|
||||
#define REMOVELATENCY 10 // seconds to wait until next check after removing a file
|
||||
#define MARKSUPDATEDELTA 10 // seconds between checks for updating editing marks
|
||||
#define MININDEXAGE 3600 // seconds before an index file is considered no longer to be written
|
||||
#define MAXREMOVETIME 10 // seconds after which to return from removing deleted recordings
|
||||
|
||||
#define MAX_LINK_LEVEL 6
|
||||
@ -313,23 +311,31 @@ bool cResumeFile::Save(int Index)
|
||||
if (safe_write(f, &Index, sizeof(Index)) < 0)
|
||||
LOG_ERROR_STR(fileName);
|
||||
close(f);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->ResetResume(fileName);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
FILE *f = fopen(fileName, "w");
|
||||
if (f) {
|
||||
fprintf(f, "I %d\n", Index);
|
||||
fclose(f);
|
||||
LOCK_RECORDINGS_WRITE;
|
||||
Recordings->ResetResume(fileName);
|
||||
}
|
||||
else
|
||||
else {
|
||||
LOG_ERROR_STR(fileName);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Not using LOCK_RECORDINGS_WRITE here, because we might already hold a lock in cRecordingsHandler::Action()
|
||||
// and end up here if an editing process is canceled while the edited recording is being replayed. The worst
|
||||
// that can happen if we don't get this lock here is that the resume info in the Recordings list is not updated,
|
||||
// but that doesn't matter because the recording is deleted, anyway.
|
||||
cStateKey StateKey;
|
||||
if (cRecordings *Recordings = cRecordings::GetRecordingsWrite(StateKey, 1)) {
|
||||
Recordings->ResetResume(fileName);
|
||||
StateKey.Remove();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -350,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;
|
||||
@ -410,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);
|
||||
@ -417,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));
|
||||
@ -451,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");
|
||||
@ -466,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;
|
||||
@ -486,21 +516,50 @@ bool cRecordingInfo::Read(FILE *f)
|
||||
break;
|
||||
case 'E': {
|
||||
unsigned int EventID;
|
||||
time_t StartTime;
|
||||
intmax_t StartTime; // actually time_t, but intmax_t for scanning with "%jd"
|
||||
int Duration;
|
||||
unsigned int TableID = 0;
|
||||
unsigned int Version = 0xFF;
|
||||
int n = sscanf(t, "%u %ld %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
|
||||
int n = sscanf(t, "%u %jd %d %X %X", &EventID, &StartTime, &Duration, &TableID, &Version);
|
||||
if (n >= 3 && n <= 5) {
|
||||
ownEvent->SetEventID(EventID);
|
||||
ownEvent->SetStartTime(StartTime);
|
||||
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;
|
||||
@ -529,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);
|
||||
@ -572,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)
|
||||
@ -1271,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();
|
||||
}
|
||||
@ -1351,6 +1435,11 @@ int cRecording::IsInUse(void) const
|
||||
return Use;
|
||||
}
|
||||
|
||||
static bool StillRecording(const char *Directory)
|
||||
{
|
||||
return access(AddDirectory(Directory, TIMERRECFILE), F_OK) == 0;
|
||||
}
|
||||
|
||||
void cRecording::ResetResume(void) const
|
||||
{
|
||||
resume = RESUME_NOT_INITIALIZED;
|
||||
@ -1360,13 +1449,24 @@ int cRecording::NumFrames(void) const
|
||||
{
|
||||
if (numFrames < 0) {
|
||||
int nf = cIndexFile::GetLength(FileName(), IsPesRecording());
|
||||
if (time(NULL) - LastModifiedTime(cIndexFile::IndexFileName(FileName(), IsPesRecording())) < MININDEXAGE)
|
||||
if (StillRecording(FileName()))
|
||||
return nf; // check again later for ongoing recordings
|
||||
numFrames = nf;
|
||||
}
|
||||
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();
|
||||
@ -1375,11 +1475,19 @@ 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) {
|
||||
int fs = DirSizeMB(FileName());
|
||||
if (time(NULL) - LastModifiedTime(cIndexFile::IndexFileName(FileName(), IsPesRecording())) < MININDEXAGE)
|
||||
if (StillRecording(FileName()))
|
||||
return fs; // check again later for ongoing recordings
|
||||
fileSizeMB = fs;
|
||||
}
|
||||
@ -1463,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
|
||||
@ -1477,6 +1586,8 @@ void cVideoDirectoryScannerThread::ScanVideoDir(const char *DirName, int LinkLev
|
||||
else
|
||||
delete r;
|
||||
}
|
||||
else if (Recording)
|
||||
Recording->ReadInfo();
|
||||
StateKey.Remove();
|
||||
}
|
||||
else
|
||||
@ -1529,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
|
||||
}
|
||||
@ -1976,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...
|
||||
@ -1988,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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2102,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);
|
||||
@ -2332,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;
|
||||
@ -2394,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;
|
||||
@ -2409,6 +2580,7 @@ void cIndexFileGenerator::Action(void)
|
||||
isyslog("generating index file");
|
||||
}
|
||||
Skins.QueueMessage(mtInfo, tr("Regenerating index file"));
|
||||
SetRecordingTimerId(recordingName, cString::sprintf("%d@%s", 0, Setup.SVDRPHostName));
|
||||
bool Stuffed = false;
|
||||
while (Running()) {
|
||||
// Rewind input file:
|
||||
@ -2428,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);
|
||||
@ -2440,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:
|
||||
@ -2498,16 +2683,25 @@ void cIndexFileGenerator::Action(void)
|
||||
}
|
||||
// Recording has been processed:
|
||||
else {
|
||||
if (pendNumber > 0)
|
||||
IndexFile.Write(pendIndependentFrame, pendNumber, pendFileSize, pendErrors, pendMissing);
|
||||
IndexFileComplete = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SetRecordingTimerId(recordingName, NULL);
|
||||
if (IndexFileComplete) {
|
||||
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);
|
||||
@ -2540,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;
|
||||
}
|
||||
@ -2562,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;
|
||||
@ -2574,7 +2773,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
|
||||
cCondWait::SleepMs(INDEXFILETESTINTERVAL);
|
||||
}
|
||||
int delta = 0;
|
||||
if (!Record && access(fileName, R_OK) != 0) {
|
||||
if (!Record && (access(fileName, R_OK) != 0 || FileSize(fileName) == 0 && time(NULL) - LastModifiedTime(fileName) > MAXWAITFORINDEXFILE)) {
|
||||
// Index file doesn't exist, so try to regenerate it:
|
||||
if (!isPesRecording) { // sorry, can only do this for TS recordings
|
||||
resumeFile.Delete(); // just in case
|
||||
@ -2610,7 +2809,7 @@ cIndexFile::cIndexFile(const char *FileName, bool Record, bool IsPesRecording, b
|
||||
}
|
||||
else if (isPesRecording)
|
||||
ConvertFromPes(index, size);
|
||||
if (!index || time(NULL) - buf.st_mtime >= MININDEXAGE) {
|
||||
if (!index || !StillRecording(FileName)) {
|
||||
close(f);
|
||||
f = -1;
|
||||
}
|
||||
@ -2739,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) {
|
||||
@ -2756,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) {
|
||||
@ -2776,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()) {
|
||||
@ -3143,7 +3357,7 @@ void cDoneRecordings::Append(const char *Title)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *FuzzyChars = " -:";
|
||||
static const char *FuzzyChars = " -:/";
|
||||
|
||||
static const char *SkipFuzzyChars(const char *s)
|
||||
{
|
||||
@ -3294,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
|
||||
|
549
remux.c
549
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.2 2022/01/18 14:24:33 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();
|
||||
@ -1174,6 +1176,11 @@ protected:
|
||||
bool newFrame;
|
||||
bool independentFrame;
|
||||
int iFrameTemporalReferenceOffset;
|
||||
uint16_t frameWidth;
|
||||
uint16_t frameHeight;
|
||||
double framesPerSecond;
|
||||
eScanType scanType;
|
||||
eAspectRatio aspectRatio;
|
||||
public:
|
||||
cFrameParser(void);
|
||||
virtual ~cFrameParser() {};
|
||||
@ -1188,6 +1195,11 @@ public:
|
||||
bool NewFrame(void) { return newFrame; }
|
||||
bool IndependentFrame(void) { return independentFrame; }
|
||||
int IFrameTemporalReferenceOffset(void) { return iFrameTemporalReferenceOffset; }
|
||||
uint16_t FrameWidth(void) { return frameWidth; }
|
||||
uint16_t FrameHeight(void) { return frameHeight; }
|
||||
double FramesPerSecond(void) { return framesPerSecond; }
|
||||
eScanType ScanType(void) { return scanType; }
|
||||
eAspectRatio AspectRatio(void) { return aspectRatio; }
|
||||
};
|
||||
|
||||
cFrameParser::cFrameParser(void)
|
||||
@ -1196,6 +1208,11 @@ cFrameParser::cFrameParser(void)
|
||||
newFrame = false;
|
||||
independentFrame = false;
|
||||
iFrameTemporalReferenceOffset = 0;
|
||||
frameWidth = 0;
|
||||
frameHeight = 0;
|
||||
framesPerSecond = 0.0;
|
||||
scanType = stUnknown;
|
||||
aspectRatio = arUnknown;
|
||||
}
|
||||
|
||||
// --- cAudioParser ----------------------------------------------------------
|
||||
@ -1229,6 +1246,18 @@ private:
|
||||
uint32_t scanner;
|
||||
bool seenIndependentFrame;
|
||||
int lastIFrameTemporalReference;
|
||||
bool seenScanType;
|
||||
const double frame_rate_table[9] = {
|
||||
0, // 0 forbidden
|
||||
24000./1001., // 1 23.976...
|
||||
24., // 2 24
|
||||
25., // 3 25
|
||||
30000./1001., // 4 29.97...
|
||||
30., // 5 30
|
||||
50., // 6 50
|
||||
60000./1001., // 7 59.94...
|
||||
60. // 8 60
|
||||
};
|
||||
public:
|
||||
cMpeg2Parser(void);
|
||||
virtual int Parse(const uchar *Data, int Length, int Pid);
|
||||
@ -1239,6 +1268,7 @@ cMpeg2Parser::cMpeg2Parser(void)
|
||||
scanner = EMPTY_SCANNER;
|
||||
seenIndependentFrame = false;
|
||||
lastIFrameTemporalReference = -1; // invalid
|
||||
seenScanType = false;
|
||||
}
|
||||
|
||||
int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||
@ -1292,6 +1322,34 @@ int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||
tsPayload.Statistics();
|
||||
break;
|
||||
}
|
||||
else if (frameWidth == 0 && scanner == 0x000001B3) { // Sequence header code
|
||||
frameWidth = tsPayload.GetByte() << 4;
|
||||
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(); // 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
|
||||
scanType = (tsPayload.GetByte() & 0x40) ? stProgressive : stInterlaced;
|
||||
seenScanType = true;
|
||||
if (debug) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
|
||||
|| tsPayload.Eof()) // or if we're out of data
|
||||
break;
|
||||
@ -1461,15 +1519,17 @@ void cH264Parser::ParseAccessUnitDelimiter(void)
|
||||
|
||||
void cH264Parser::ParseSequenceParameterSet(void)
|
||||
{
|
||||
int chroma_format_idc = 0;
|
||||
int bitDepth = 0;
|
||||
uchar profile_idc = GetByte(); // profile_idc
|
||||
GetByte(); // constraint_set[0-5]_flags, reserved_zero_2bits
|
||||
GetByte(); // level_idc
|
||||
GetGolombUe(); // seq_parameter_set_id
|
||||
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc ==118 || profile_idc == 128) {
|
||||
int chroma_format_idc = GetGolombUe(); // chroma_format_idc
|
||||
chroma_format_idc = GetGolombUe(); // chroma_format_idc
|
||||
if (chroma_format_idc == 3)
|
||||
separate_colour_plane_flag = GetBit();
|
||||
GetGolombUe(); // bit_depth_luma_minus8
|
||||
bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
|
||||
GetGolombUe(); // bit_depth_chroma_minus8
|
||||
GetBit(); // qpprime_y_zero_transform_bypass_flag
|
||||
if (GetBit()) { // seq_scaling_matrix_present_flag
|
||||
@ -1501,9 +1561,73 @@ void cH264Parser::ParseSequenceParameterSet(void)
|
||||
}
|
||||
GetGolombUe(); // max_num_ref_frames
|
||||
GetBit(); // gaps_in_frame_num_value_allowed_flag
|
||||
GetGolombUe(); // pic_width_in_mbs_minus1
|
||||
GetGolombUe(); // pic_height_in_map_units_minus1
|
||||
uint16_t frame_Width = 16 * (1 + GetGolombUe()); // pic_width_in_mbs_minus1
|
||||
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) {
|
||||
scanType = frame_mbs_only_flag ? stProgressive : stInterlaced;
|
||||
if (!frame_mbs_only_flag) {
|
||||
GetBit(); // mb_adaptive_frame_field_flag
|
||||
frame_Height *= 2;
|
||||
}
|
||||
GetBit(); // direct_8x8_inference_flag
|
||||
bool frame_cropping_flag = GetBit(); // frame_cropping_flag
|
||||
if (frame_cropping_flag) {
|
||||
uint16_t frame_crop_left_offset = GetGolombUe(); // frame_crop_left_offset
|
||||
uint16_t frame_crop_right_offset = GetGolombUe(); // frame_crop_right_offset
|
||||
uint16_t frame_crop_top_offset = GetGolombUe(); // frame_crop_top_offset
|
||||
uint16_t frame_crop_bottom_offset = GetGolombUe(); // frame_crop_bottom_offset
|
||||
uint16_t CropUnitX = 1;
|
||||
uint16_t CropUnitY = frame_mbs_only_flag ? 1 : 2;
|
||||
if (!separate_colour_plane_flag && chroma_format_idc > 0) {
|
||||
if (chroma_format_idc == 1) {
|
||||
CropUnitX = 2;
|
||||
CropUnitY *= 2;
|
||||
}
|
||||
else if (chroma_format_idc == 2)
|
||||
CropUnitX = 2;
|
||||
}
|
||||
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;
|
||||
// VUI parameters
|
||||
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) // 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
|
||||
if (GetBit()) { // video_signal_type_present_flag
|
||||
GetBits(4); // video_format, video_full_range_flag
|
||||
if (GetBit()) // colour_description_present_flag
|
||||
GetBits(24); // colour_primaries, transfer_characteristics, matrix_coefficients
|
||||
}
|
||||
if (GetBit()) { // chroma_loc_info_present_flag
|
||||
GetGolombUe(); // chroma_sample_loc_type_top_field
|
||||
GetGolombUe(); // chroma_sample_loc_type_bottom_field
|
||||
}
|
||||
if (GetBit()) { // timing_info_present_flag
|
||||
uint32_t num_units_in_tick = GetBits(32); // num_units_in_tick
|
||||
uint32_t time_scale = GetBits(32); // time_scale
|
||||
if (num_units_in_tick > 0)
|
||||
framesPerSecond = double(time_scale) / (num_units_in_tick << 1);
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
if (gotAccessUnitDelimiter && !gotSequenceParameterSet)
|
||||
dbgframes("A"); // just for completeness
|
||||
@ -1570,6 +1694,7 @@ private:
|
||||
nutUnspecified0 = 48,
|
||||
nutUnspecified7 = 55,
|
||||
};
|
||||
void ParseSequenceParameterSet(void);
|
||||
public:
|
||||
cH265Parser(void);
|
||||
virtual int Parse(const uchar *Data, int Length, int Pid);
|
||||
@ -1602,6 +1727,10 @@ int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (frameWidth == 0 && NalUnitType == nutSequenceParameterSet) {
|
||||
ParseSequenceParameterSet();
|
||||
gotSequenceParameterSet = true;
|
||||
}
|
||||
}
|
||||
if (tsPayload.AtPayloadStart() // stop at a new payload start to have the buffer refilled if necessary
|
||||
|| tsPayload.Eof()) // or if we're out of data
|
||||
@ -1610,8 +1739,369 @@ int cH265Parser::Parse(const uchar *Data, int Length, int Pid)
|
||||
return tsPayload.Used();
|
||||
}
|
||||
|
||||
void cH265Parser::ParseSequenceParameterSet(void)
|
||||
{
|
||||
int separate_colour_plane_flag = 0;
|
||||
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) & 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();
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte();
|
||||
bool general_progressive_source_flag = GetBit(); // general_progressive_source_flag
|
||||
scanType = general_progressive_source_flag ? stProgressive : stInterlaced;
|
||||
GetBit(); // general_interlaced_source_flag
|
||||
GetBits(6);
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte();
|
||||
GetByte(); // general_level_idc
|
||||
for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
|
||||
sub_layer_profile_present_flag[i] = GetBit(); // sub_layer_profile_present_flag[i]
|
||||
sub_layer_level_present_flag[i] = GetBit(); // sub_layer_level_present_flag[i]
|
||||
}
|
||||
if (sps_max_sub_layers_minus1 > 0) {
|
||||
for (int i = sps_max_sub_layers_minus1; i < 8; i++ )
|
||||
GetBits(2); // reserved_zero_2bits[i]
|
||||
}
|
||||
for (int i = 0; i < sps_max_sub_layers_minus1; i++ ) {
|
||||
if (sub_layer_profile_present_flag[i] )
|
||||
GetBits(88);
|
||||
if (sub_layer_level_present_flag[i])
|
||||
GetBits(8);
|
||||
}
|
||||
// end profile_tier_level
|
||||
GetGolombUe(); // sps_seq_parameter_set_id
|
||||
int chroma_format_idc = GetGolombUe(); // chroma_format_idc
|
||||
if (chroma_format_idc == 3)
|
||||
separate_colour_plane_flag = GetBit(); // separate_colour_plane_flag
|
||||
frameWidth = GetGolombUe(); // pic_width_in_luma_samples
|
||||
frameHeight = GetGolombUe(); // pic_height_in_luma_samples
|
||||
bool conformance_window_flag = GetBit(); // conformance_window_flag
|
||||
if (conformance_window_flag) {
|
||||
int conf_win_left_offset = GetGolombUe(); // conf_win_left_offset
|
||||
int conf_win_right_offset = GetGolombUe(); // conf_win_right_offset
|
||||
int conf_win_top_offset = GetGolombUe(); // conf_win_top_offset
|
||||
int conf_win_bottom_offset = GetGolombUe(); // conf_win_bottom_offset
|
||||
uint16_t SubWidthC = 1;
|
||||
uint16_t SubHeightC = 1;
|
||||
if (!separate_colour_plane_flag && chroma_format_idc > 0) {
|
||||
if (chroma_format_idc == 1) {
|
||||
SubWidthC = 2;
|
||||
SubHeightC = 2;
|
||||
}
|
||||
else if (chroma_format_idc == 2)
|
||||
SubWidthC = 2;
|
||||
}
|
||||
frameWidth -= SubWidthC * (conf_win_left_offset + conf_win_right_offset);
|
||||
frameHeight -= SubHeightC * (conf_win_top_offset + conf_win_bottom_offset);
|
||||
}
|
||||
int bitDepth = 8 + GetGolombUe(); // bit_depth_luma_minus8
|
||||
GetGolombUe(); // bit_depth_chroma_minus8
|
||||
int log2_max_pic_order_cnt_lsb_minus4 = GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4
|
||||
int sps_sub_layer_ordering_info_present_flag = GetBit(); // sps_sub_layer_ordering_info_present_flag
|
||||
for (int i = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1; i <= sps_max_sub_layers_minus1; ++i) {
|
||||
GetGolombUe(); // sps_max_dec_pic_buffering_minus1[i]
|
||||
GetGolombUe(); // sps_max_num_reorder_pics[i]
|
||||
GetGolombUe(); // sps_max_latency_increase_plus1[i]
|
||||
}
|
||||
GetGolombUe(); // log2_min_luma_coding_block_size_minus3
|
||||
GetGolombUe(); // log2_diff_max_min_luma_coding_block_size
|
||||
GetGolombUe(); // log2_min_luma_transform_block_size_minus2
|
||||
GetGolombUe(); // log2_diff_max_min_luma_transform_block_size
|
||||
GetGolombUe(); // max_transform_hierarchy_depth_inter
|
||||
GetGolombUe(); // max_transform_hierarchy_depth_intra
|
||||
if (GetBit()) { // scaling_list_enabled_flag
|
||||
if (GetBit()) { // sps_scaling_list_data_present_flag
|
||||
// begin scaling_list_data
|
||||
for (int sizeId = 0; sizeId < 4; ++sizeId) {
|
||||
for (int matrixId = 0; matrixId < 6; matrixId += (sizeId == 3) ? 3 : 1) {
|
||||
if (!GetBit()) // scaling_list_pred_mode_flag[sizeId][matrixId]
|
||||
GetGolombUe(); // scaling_list_pred_matrix_id_delta[sizeId][matrixId]
|
||||
else {
|
||||
int coefNum = min(64, (1 << (4 + (sizeId << 1))));
|
||||
if (sizeId > 1)
|
||||
GetGolombSe(); // scaling_list_dc_coef_minus8[sizeId−2][matrixId]
|
||||
for (int i = 0; i < coefNum; ++i)
|
||||
GetGolombSe(); // scaling_list_delta_coef
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// end scaling_list_data
|
||||
}
|
||||
GetBits(2); // amp_enabled_flag, sample_adaptive_offset_enabled_flag
|
||||
if (GetBit()) { // pcm_enabled_flag
|
||||
GetBits(8); // pcm_sample_bit_depth_luma_minus1, pcm_sample_bit_depth_chroma_minus1
|
||||
GetGolombUe(); // log2_min_pcm_luma_coding_block_size_minus3
|
||||
GetGolombUe(); // log2_diff_max_min_pcm_luma_coding_block_size
|
||||
GetBit(); // pcm_loop_filter_disabled_flag
|
||||
}
|
||||
uint32_t num_short_term_ref_pic_sets = GetGolombUe(); // num_short_term_ref_pic_sets
|
||||
uint32_t NumDeltaPocs[num_short_term_ref_pic_sets];
|
||||
for (uint32_t stRpsIdx = 0; stRpsIdx < num_short_term_ref_pic_sets; ++stRpsIdx) {
|
||||
// start of st_ref_pic_set(stRpsIdx)
|
||||
bool inter_ref_pic_set_prediction_flag = false;
|
||||
if (stRpsIdx != 0)
|
||||
inter_ref_pic_set_prediction_flag = GetBit(); // inter_ref_pic_set_prediction_flag
|
||||
if (inter_ref_pic_set_prediction_flag) {
|
||||
uint32_t RefRpsIdx, delta_idx_minus1 = 0;
|
||||
if (stRpsIdx == num_short_term_ref_pic_sets)
|
||||
delta_idx_minus1 = GetGolombUe(); // delta_idx_minus1
|
||||
GetBit(); // delta_rps_sign
|
||||
GetGolombUe(); // abs_delta_rps_minus1
|
||||
RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1);
|
||||
NumDeltaPocs[stRpsIdx] = 0;
|
||||
for (uint32_t j = 0; j <= NumDeltaPocs[RefRpsIdx]; ++j) {
|
||||
if (!GetBit()) { // used_by_curr_pic_flag[j]
|
||||
if (GetBit()) // use_delta_flag[j]
|
||||
NumDeltaPocs[stRpsIdx]++;
|
||||
}
|
||||
else
|
||||
NumDeltaPocs[stRpsIdx]++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint32_t num_negative_pics = GetGolombUe(); // num_negative_pics
|
||||
uint32_t num_positive_pics = GetGolombUe(); // num_positive_pics
|
||||
for (uint32_t j = 0; j < num_negative_pics; ++j) {
|
||||
GetGolombUe(); // delta_poc_s0_minus1[i]
|
||||
GetBit(); // used_by_curr_pic_s0_flag[i]
|
||||
}
|
||||
for (uint32_t j = 0; j < num_positive_pics; ++j) {
|
||||
GetGolombUe(); // delta_poc_s1_minus1[i]
|
||||
GetBit(); // delta_poc_s1_minus1[i]
|
||||
}
|
||||
NumDeltaPocs[stRpsIdx] = num_negative_pics + num_positive_pics;
|
||||
}
|
||||
// end of st_ref_pic_set(stRpsIdx)
|
||||
}
|
||||
if (GetBit()) { // long_term_ref_pics_present_flag
|
||||
uint32_t num_long_term_ref_pics_sps = GetGolombUe(); // num_long_term_ref_pics_sps
|
||||
for (uint32_t i = 0; i < num_long_term_ref_pics_sps; ++i) {
|
||||
GetBits(log2_max_pic_order_cnt_lsb_minus4 + 4); // lt_ref_pic_poc_lsb_sps[i]
|
||||
GetBit(); // used_by_curr_pic_lt_sps_flag[i]
|
||||
}
|
||||
}
|
||||
GetBits(2); // sps_temporal_mvp_enabled_flag, strong_intra_smoothing_enabled_flag
|
||||
if (GetBit()) { // vui_parameters_present_flag
|
||||
// begin of vui_parameters()
|
||||
if (GetBit()) { // aspect_ratio_info_present_flag
|
||||
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
|
||||
if (GetBit()) { // video_signal_type_present_flag
|
||||
GetBits(4); // video_format, video_full_range_flag
|
||||
if (GetBit()) // colour_description_present_flag
|
||||
GetBits(24); // colour_primaries, transfer_characteristics, matrix_coeffs
|
||||
}
|
||||
if (GetBit()) { // chroma_loc_info_present_flag
|
||||
GetGolombUe(); // chroma_sample_loc_type_top_field
|
||||
GetGolombUe(); // chroma_sample_loc_type_bottom_field
|
||||
}
|
||||
GetBits(3); // neutral_chroma_indication_flag, field_seq_flag, frame_field_info_present_flag
|
||||
if (GetBit()) { // default_display_window_flag
|
||||
GetGolombUe(); // def_disp_win_left_offset
|
||||
GetGolombUe(); // def_disp_win_right_offset
|
||||
GetGolombUe(); // def_disp_win_top_offset
|
||||
GetGolombUe(); // def_disp_win_bottom_offset
|
||||
}
|
||||
if (GetBit()) { // vui_timing_info_present_flag
|
||||
uint32_t vui_num_units_in_tick = GetBits(32); // vui_num_units_in_tick
|
||||
uint32_t vui_time_scale = GetBits(32); // vui_time_scale
|
||||
if (vui_num_units_in_tick > 0)
|
||||
framesPerSecond = (double)vui_time_scale / vui_num_units_in_tick;
|
||||
}
|
||||
}
|
||||
if (debug) {
|
||||
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;
|
||||
@ -1621,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)
|
||||
@ -1651,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;
|
||||
@ -1681,11 +2198,24 @@ 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);
|
||||
}
|
||||
framesInPayloadUnit++;
|
||||
if (independentFrame)
|
||||
numIFrames++;
|
||||
@ -1699,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);
|
||||
@ -1741,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);
|
||||
@ -1756,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: sections.c 5.3 2022/01/31 21:21:42 kls Exp $
|
||||
* $Id: sections.c 5.4 2022/12/06 12:25:08 kls Exp $
|
||||
*/
|
||||
|
||||
#include "sections.h"
|
||||
@ -180,6 +180,11 @@ void cSectionHandler::Action(void)
|
||||
startFilters = false;
|
||||
}
|
||||
int NumFilters = filterHandles.Count();
|
||||
if (NumFilters == 0) {
|
||||
Unlock();
|
||||
cCondWait::SleepMs(100);
|
||||
continue;
|
||||
}
|
||||
pollfd pfd[NumFilters];
|
||||
for (cFilterHandle *fh = filterHandles.First(); fh; fh = filterHandles.Next(fh)) {
|
||||
int i = fh->Index();
|
||||
|
10
shutdown.c
10
shutdown.c
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Original version written by Udo Richter <udo_richter@gmx.de>.
|
||||
*
|
||||
* $Id: shutdown.c 4.2 2018/03/23 15:39:21 kls Exp $
|
||||
* $Id: shutdown.c 5.1 2022/11/22 14:33:48 kls Exp $
|
||||
*/
|
||||
|
||||
#include "shutdown.h"
|
||||
@ -113,7 +113,7 @@ void cShutdownHandler::CheckManualStart(int ManualStart)
|
||||
}
|
||||
else {
|
||||
// Set inactive from now on
|
||||
dsyslog("scheduled wakeup time in %ld minutes, assuming automatic start of VDR", Delta / 60);
|
||||
dsyslog("scheduled wakeup time in %jd minutes, assuming automatic start of VDR", intmax_t(Delta / 60));
|
||||
SetUserInactiveTimeout(-3, true);
|
||||
}
|
||||
}
|
||||
@ -127,7 +127,7 @@ void cShutdownHandler::SetShutdownCommand(const char *ShutdownCommand)
|
||||
void cShutdownHandler::CallShutdownCommand(time_t WakeupTime, int Channel, const char *File, bool UserShutdown)
|
||||
{
|
||||
time_t Delta = WakeupTime ? WakeupTime - time(NULL) : 0;
|
||||
cString cmd = cString::sprintf("%s %ld %ld %d \"%s\" %d", shutdownCommand, WakeupTime, Delta, Channel, *strescape(File, "\\\"$"), UserShutdown);
|
||||
cString cmd = cString::sprintf("%s %jd %jd %d \"%s\" %d", shutdownCommand, intmax_t(WakeupTime), intmax_t(Delta), Channel, *strescape(File, "\\\"$"), UserShutdown);
|
||||
isyslog("executing '%s'", *cmd);
|
||||
int Status = SystemExec(cmd, true);
|
||||
if (!WIFEXITED(Status) || WEXITSTATUS(Status))
|
||||
@ -183,7 +183,7 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive)
|
||||
// Timer within Min Event Timeout
|
||||
if (!Interactive)
|
||||
return false;
|
||||
cString buf = cString::sprintf(tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
|
||||
cString buf = cString::sprintf(tr("Recording in %jd minutes, shut down anyway?"), intmax_t(Delta / 60));
|
||||
if (!Interface->Confirm(buf))
|
||||
return false;
|
||||
}
|
||||
@ -198,7 +198,7 @@ bool cShutdownHandler::ConfirmShutdown(bool Interactive)
|
||||
// Plugin wakeup within Min Event Timeout
|
||||
if (!Interactive)
|
||||
return false;
|
||||
cString buf = cString::sprintf(tr("Plugin %s wakes up in %ld min, continue?"), Plugin->Name(), Delta / 60);
|
||||
cString buf = cString::sprintf(tr("Plugin %s wakes up in %jd min, continue?"), Plugin->Name(), intmax_t(Delta / 60));
|
||||
if (!Interface->Confirm(buf))
|
||||
return false;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
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