diff options
69 files changed, 2091 insertions, 1136 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index a275edb..9e2317f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1757,6 +1757,9 @@ Udo Richter <udo_richter@gmx.de> for adding the option --outputonly to the dvbsddevice plugin for adding a missing template specification to the c'tor of cSortedTimers for contributing to a patch that implements FHS support + for suggesting to check cIoThrottle::Engaged() in cRemoveDeletedRecordingsThread::Action() + for suggesting to shift editing marks that don't point to an I-frame towards the next I-frame + when a recording is played Sven Kreiensen <svenk@kammer.uni-hannover.de> for his help in keeping 'channels.conf.terr' up to date @@ -2305,6 +2308,8 @@ Christoph Haubrich <christoph1.haubrich@arcor.de> for fixing cRecording::LengthInSeconds(), which wrongfully rounded the result to full minutes for suggesting to check for NULL in cOsd::AddPixmap() + for making modified editing marks be written to disk whenever the replay progress + display gets hidden Pekka Mauno <pekka.mauno@iki.fi> for fixing cSchedule::GetFollowingEvent() in case there is currently no present @@ -2314,6 +2319,8 @@ Alexander Wenzel <hondansx@gmx.de> for fixing the shutdown timeout for making the script given to VDR with the '-r' option be also called whenever a recording is deleted + for making pressing the Play key during normal live viewing mode open the Recordings + menu if there is no "last viewed" recording Jan Lenz <email@JanLenz.de> for reporting a bug in deleting recordings that have been removed externally when @@ -2618,6 +2625,7 @@ Sundararaj Reel <sundararaj.reel@googlemail.com> for reporting a problem with the function cString::sprintf(const char *fmt, va_list &ap), that might inadvertently be called with a 'char *' as the second argument on some compilers and cause a crash + for reporting a possible memory leak in SI::StructureLoop::getNextAsPointer() Ales Jurik <ajurik@quick.cz> for reporting broken SI data on Czech/Slovak channels after changing the default @@ -2692,6 +2700,7 @@ Derek Kelly (user.vdr@gmail.com) several payloads for reporting a problem with getting the maximum short channel name length in case there are no short names at all + for reporting an incompatible change from DTV_DVBT2_PLP_ID to DTV_STREAM_ID in DVB API 5.8 Marcel Unbehaun <frostworks@gmx.de> for adding cRecordingInfo::GetEvent() @@ -2895,6 +2904,7 @@ Torsten Lang <info@torstenlang.de> in case buffers run full for suggesting to increase the size of the TS buffer to 5MB and that of the Recorder buffer to 20MB to better handle HD recordings + for fixing setting the video format in the dvbhdffdevice Christian Ruppert <idl0r@gentoo.org> for some improvements to the Makefiles @@ -2960,3 +2970,6 @@ Sören Moch <smoch@web.de> for a patch that was used to move cleaning up the EPG data and writing the epg.data file into a separate thread to avoid sluggish response to user input on slow systems for fixing sorting folders before recordings in case of UTF-8 + +Peter Münster <pmlists@free.fr> + for fixing 'make install' to not overwrite existing configuration files @@ -7271,3 +7271,70 @@ Video Disk Recorder Revision History whether a directory is empty. This allows users to continue to use files such as ".keep" to prevent a directory from being deleted when it is empty. Currently the only file name that is ignored is ".sort". + +2012-11-18: Version 1.7.32 + +- Pressing the Play key during normal live viewing mode now opens the Recordings menu + if there is no "last viewed" recording (thanks to Alexander Wenzel). + The same behavior has been implemented for the Blue key in the main menu. +- cIoThrottle::Engaged() is now also checked in cRemoveDeletedRecordingsThread::Action(), + to suspend removing deleted recordings in case this is necessary to make room for + new, ongoing recordings (suggested by Udo Richter). +- The cThread constructor now has an additional boolean parameter that can be set to + true to have this thread run at a lower priority. Plugin authors that use low + priority threads may want to use this instead of the calls to SetPriority(19) and + SetIOPriority(7). The priority of a thread ("low" or "high") is now logged when the + thread starts. +- Changed DTV_DVBT2_PLP_ID to DTV_STREAM_ID in dvbdevice.c to adapt to an incompatible + change in DVB API 5.8 (reported by Derek Kelly). + Removed the meanwhile obsolete definition of FE_CAN_TURBO_FEC. +- Fixed some compiler warnings under gcc version 4.7.1. +- Fixed setting the video format in the dvbhdffdevice (thanks to Torsten Lang). +- Fixed 'make install' to not overwrite existing configuration files (thanks to Peter + Münster). +- Added including the Make.global and Make.config files to the dvbdhffdevice's + libhdffcmd/Makefile. +- Added options to build a 32-bit version of VDR on a 64-bit machine to + Make.config.template. +- Fixed handling VPS timers in case the running status of an event goes to '1' (not + running) and later goes to '4' (running). +- If a frame position in the 'marks' file of a recording doesn't point to an I-frame, + it will now be shifted towards the next I-frame, either up or down, whichever is + closer (suggested by Udo Richter). +- Fixed a possible memory leak in SI::StructureLoop::getNextAsPointer() (reported by + Sundararaj Reel). +- Fixed handling timers in case an event is modified and "phased out" while the timer + is recording. +- Improved frame detection by parsing just far enough into the MPEG-4 NAL units to get + the necessary information about frames and slices. +- The initial syncing of the frame detector is now done immediately after the first + complete GOP has been seen. This makes recordings and especially pausing live video + start up to twice as fast as before. +- Updated the Romanian OSD texts (thanks to Lucian Muresan). +- Fixed handling the very last entry in a recording index. +- The return type of cMarks::Add() has been changed to void, since due to the sorting + of the list of marks the returned pointer might have pointed to a totally different + mark. Besides, the return value was never actually used. +- Improved editing TS recordings by + + stripping dangling TS packets from the beginning of a sequence + + including pending TS packets at the end of a sequence + + fixing all timestamps and continuity counters + + generating editing marks for the edited version in such a way that each cutting + point is marked by an "end" and "begin" mark with the same offset + + no longer generating an editing mark at the "end" of the edited recording (this + was actually generated at the beginning of the last GOP, so that a subsequent + edit would have cut off the last GOP) + + no longer generating any editing marks if the edited recording results on just + one single sequence + + ignoring pairs of editing marks that are placed at exactly the same position of + a recording when actually cutting the recording + + not doing anything if the editing marks in place would result in the edited + version being the same as the original recording +- Editing marks can now be placed directly on top of each other, in which case they + simply mark a position, but have no effect on the actual cutting process. +- When positioned at an offset where two (or more) editing marks are placed on top + of each other, the '4' key moves the first one of them to the left, while the '6' + key moves the last one of them to the right. The '7' and '9' key handle multiple + marks at the same place as if it were one single mark. +- Modified editing marks are now written to disk whenever the replay progress display + gets hidden (thanks to Christoph Haubrich). @@ -36,6 +36,10 @@ and type 'make'. This should produce an executable file named 'vdr', which can be run after the DVB driver has been installed. +If you want to build a 32-bit version of VDR on a 64-bit machine, you can +use 'make M32=1' to do so. Note that you also need to have a Make.config file +(derived from Make.config.template) to make this work. + IMPORTANT: See "Configuration files" below for information on how ========= to set up the configuration files at the proper location! @@ -367,13 +367,13 @@ Version 1.6 - 7, 9 Jump back and forward between editing marks. Replay goes into still mode after jumping to a mark. - 8 Positions replay at a point 3 seconds before the current or next - "start" mark and starts replay. + "begin" mark and starts replay. - 2 Start the actual cutting process. Editing marks are represented by black, vertical lines in the progress display. - A small black triangle at the top of the mark means that this is a "start" + A small black triangle at the top of the mark means that this is a "begin" mark, and a triangle at the bottom means that this is an "end" mark. - The cutting process will save all video data between "start" and "end" marks + The cutting process will save all video data between "begin" and "end" marks into a new file (the original recording remains untouched). The new file will have the same name as the original recording, preceded with a '%' character (imagine the '%' somehow looking like a pair of scissors ;-). Red bars in the @@ -382,7 +382,7 @@ Version 1.6 The video sequences to be saved by the cutting process are determined by an "even/odd" algorithm. This means that every odd numbered editing mark (i.e. - 1, 3, 5,...) represents a "start" mark, while every even numbered mark (2, 4, + 1, 3, 5,...) represents a "begin" mark, while every even numbered mark (2, 4, 6,...) is an "end" mark. Inserting or toggling a mark on or off automatically adjusts the sequence to the right side of that mark. @@ -395,11 +395,13 @@ Version 1.6 version of the recording you can use the '8' key to jump to a point just before the next cut and have a look at the resulting sequence. - Currently editing marks can only be set at I-frames, which typically is - every 12th frame. So editing can be done with a resolution of roughly half - a second. A "start" mark marks the first frame of a resulting video - sequence, and an "end" mark marks the last frame of that sequence. - + Currently editing marks can only be set at I-frames, which typically appear + every half of a second to a second. A "begin" mark marks the first frame of + a resulting video sequence, and an "end" mark marks the last frame of that + sequence. Note that the actual frame indicated by the an "end" mark will + not be included in the edited version of the recording. That's because every + recording (and every sequence of an edited recording) begins with an I-frame + and ends right before the next I-frame. An edited recording (indicated by the '%' character) will never be deleted automatically in case the disk runs full (no matter what "lifetime" it has). diff --git a/Make.config.template b/Make.config.template index ef463b1..08f9ece 100644 --- a/Make.config.template +++ b/Make.config.template @@ -6,7 +6,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Make.config.template 2.9 2012/09/01 10:31:33 kls Exp $ +# $Id: Make.config.template 2.10 2012/10/09 10:32:32 kls Exp $ ### The C compiler and options: @@ -21,6 +21,12 @@ CFLAGS += -fPIC CXXFLAGS += -fPIC endif +# Use 'make M32=1 ...' to build a 32-bit version of VDR on a 64-bit machine: +ifdef M32 +CFLAGS += -m32 +CXXFLAGS += -m32 +endif + ### The directory environment: PREFIX = $(DESTDIR)/usr/local @@ -4,7 +4,7 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 2.29 2012/09/07 14:11:37 kls Exp $ +# $Id: Makefile 2.30 2012/10/09 08:32:25 kls Exp $ .DELETE_ON_ERROR: @@ -211,7 +211,7 @@ install-dirs: @mkdir -p $(DESTDIR)$(RESDIRDEF) install-conf: - @cp *.conf $(DESTDIR)$(CONFDIRDEF) + @cp -n *.conf $(DESTDIR)$(CONFDIRDEF) # Documentation: diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c index f8b47bd..845fa6b 100644 --- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.c +++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: dvbhdffdevice.c 1.43 2012/05/08 11:40:32 kls Exp $ + * $Id: dvbhdffdevice.c 1.46 2012/11/15 09:19:10 kls Exp $ */ #include <stdint.h> @@ -69,6 +69,7 @@ cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend) mHdffCmdIf->CmdAvSetAudioDelay(gHdffSetup.AudioDelay); mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) gHdffSetup.AudioDownmix); + mHdffCmdIf->CmdAvSetSyncShift(gHdffSetup.AvSyncShift); mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) gHdffSetup.AnalogueVideo); mHdffCmdIf->CmdHdmiSetVideoMode(gHdffSetup.GetVideoMode()); @@ -136,7 +137,7 @@ void cDvbHdFfDevice::SetVideoFormat(bool VideoFormat16_9) { HdffVideoFormat_t videoFormat; videoFormat.AutomaticEnabled = true; - videoFormat.AfdEnabled = true; + videoFormat.AfdEnabled = false; videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat; videoFormat.VideoConversion = (HdffVideoConversion_t) gHdffSetup.VideoConversion; mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat); @@ -375,62 +376,86 @@ bool cDvbHdFfDevice::CanReplay(void) const bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode) { - if (PlayMode == pmNone) { - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - - mHdffCmdIf->CmdAvEnableVideoAfterStop(0, false); - mHdffCmdIf->CmdAvSetPcrPid(0, 0); - mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); - mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); + if (PlayMode == pmNone) { + if (fd_video == -1) + fd_video = DvbOpen(DEV_DVB_VIDEO, adapter, frontend, O_RDWR | O_NONBLOCK); + if (fd_audio == -1) + fd_audio = DvbOpen(DEV_DVB_AUDIO, adapter, frontend, O_RDWR | O_NONBLOCK); - ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); - mHdffCmdIf->CmdAvSetDecoderInput(0, 0); - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvSetPlayMode(0, true); - } - else { - if (playMode == pmNone) - TurnOffLiveMode(true); + mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); + mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - mHdffCmdIf->CmdAvSetPlayMode(1, Transferring() || (cTransferControl::ReceiverDevice() == this)); - mHdffCmdIf->CmdAvSetStc(0, 100000); - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true); + mHdffCmdIf->CmdAvEnableVideoAfterStop(0, false); + mHdffCmdIf->CmdAvSetPcrPid(0, 0); + mHdffCmdIf->CmdAvSetVideoPid(0, 0, HDFF_VIDEO_STREAM_MPEG1); + mHdffCmdIf->CmdAvSetAudioPid(0, 0, HDFF_AUDIO_STREAM_MPEG1); - playVideoPid = -1; - playAudioPid = -1; - audioCounter = 0; - videoCounter = 0; - freezed = false; - trickMode = false; + ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX); + mHdffCmdIf->CmdAvSetDecoderInput(0, 0); + mHdffCmdIf->CmdAvEnableSync(0, true); + mHdffCmdIf->CmdAvSetPlayMode(0, true); + } + else { + if (playMode == pmNone) + TurnOffLiveMode(true); - mHdffCmdIf->CmdAvSetDecoderInput(0, 2); - ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); - } - playMode = PlayMode; - return true; + if (PlayMode == pmExtern_THIS_SHOULD_BE_AVOIDED) + { + close(fd_video); + fd_video = -1; + close(fd_audio); + fd_audio = -1; + } + else + { + mHdffCmdIf->CmdAvSetPlayMode(1, Transferring() || (cTransferControl::ReceiverDevice() == this)); + mHdffCmdIf->CmdAvSetStc(0, 100000); + mHdffCmdIf->CmdAvEnableSync(0, false); + mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true); + + playVideoPid = -1; + playAudioPid = -1; + audioCounter = 0; + videoCounter = 0; + freezed = false; + trickMode = false; + isPlayingVideo = false; + + mHdffCmdIf->CmdAvSetDecoderInput(0, 2); + ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY); + } + } + playMode = PlayMode; + return true; } int64_t cDvbHdFfDevice::GetSTC(void) { - if (fd_video >= 0) { - uint64_t pts; - if (ioctl(fd_video, VIDEO_GET_PTS, &pts) == -1) { - esyslog("ERROR: pts %d: %m", CardIndex() + 1); - return -1; + if (isPlayingVideo) + { + if (fd_video >= 0) { + uint64_t pts; + if (ioctl(fd_video, VIDEO_GET_PTS, &pts) == -1) { + esyslog("ERROR: pts %d: %m", CardIndex() + 1); + return -1; + } + //printf("video PTS %lld\n", pts); + return pts; } - return pts; - } - if (fd_audio >= 0) { - uint64_t pts; - if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) { - esyslog("ERROR: pts %d: %m", CardIndex() + 1); - return -1; + } + else + { + if (fd_audio >= 0) { + uint64_t pts; + if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) { + esyslog("ERROR: pts %d: %m", CardIndex() + 1); + return -1; + } + //printf("audio PTS %lld\n", pts); + return pts; } - return pts; - } - return -1; + } + return -1; } void cDvbHdFfDevice::TrickSpeed(int Speed) @@ -456,12 +481,13 @@ void cDvbHdFfDevice::Clear(void) void cDvbHdFfDevice::Play(void) { - freezed = false; - trickMode = false; - mHdffCmdIf->CmdAvEnableSync(0, true); - mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); - mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); - cDevice::Play(); + freezed = false; + trickMode = false; + if (isPlayingVideo) + mHdffCmdIf->CmdAvEnableSync(0, true); + mHdffCmdIf->CmdAvSetVideoSpeed(0, 100); + mHdffCmdIf->CmdAvSetAudioSpeed(0, 100); + cDevice::Play(); } void cDvbHdFfDevice::Freeze(void) @@ -631,6 +657,11 @@ int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length) { if (freezed) return -1; + if (!isPlayingVideo) + { + mHdffCmdIf->CmdAvEnableSync(0, true); + isPlayingVideo = true; + } //TODO: support greater Length uint8_t tsBuffer[188 * 16]; uint32_t tsLength; @@ -651,8 +682,6 @@ int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id) { if (freezed) return -1; - if (trickMode) - return Length; uint8_t streamId; uint8_t tsBuffer[188 * 16]; uint32_t tsLength; @@ -702,17 +731,23 @@ int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id) int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length) { - if (freezed) - return -1; - int pid = TsPid(Data); - if (pid != playVideoPid) { - PatPmtParser(); - if (pid == PatPmtParser()->Vpid()) { - playVideoPid = pid; - mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, MapVideoStreamTypes(PatPmtParser()->Vtype()), true); + if (freezed) + return -1; + if (!isPlayingVideo) + { + mHdffCmdIf->CmdAvEnableSync(0, true); + isPlayingVideo = true; + } + + int pid = TsPid(Data); + if (pid != playVideoPid) { + PatPmtParser(); + if (pid == PatPmtParser()->Vpid()) { + playVideoPid = pid; + mHdffCmdIf->CmdAvSetVideoPid(0, playVideoPid, MapVideoStreamTypes(PatPmtParser()->Vtype()), true); } - } - return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); + } + return WriteAllOrNothing(fd_video, Data, Length, 1000, 10); } static HdffAudioStreamType_t MapAudioStreamTypes(int Atype) @@ -732,8 +767,6 @@ int cDvbHdFfDevice::PlayTsAudio(const uchar *Data, int Length) { if (freezed) return -1; - if (trickMode) - return Length; int pid = TsPid(Data); if (pid != playAudioPid) { playAudioPid = pid; diff --git a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h index 439ec9b..344f3dc 100644 --- a/PLUGINS/src/dvbhddevice/dvbhdffdevice.h +++ b/PLUGINS/src/dvbhddevice/dvbhdffdevice.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: dvbhdffdevice.h 1.7 2011/09/10 10:17:32 kls Exp $ + * $Id: dvbhdffdevice.h 1.8 2012/11/05 08:49:29 kls Exp $ */ #ifndef __DVBHDFFDEVICE_H @@ -81,6 +81,7 @@ private: int playAudioPid; bool freezed; bool trickMode; + bool isPlayingVideo; // Pes2Ts conversion stuff uint8_t videoCounter; diff --git a/PLUGINS/src/dvbhddevice/hdffcmd.c b/PLUGINS/src/dvbhddevice/hdffcmd.c index a6a41d9..1532cc2 100644 --- a/PLUGINS/src/dvbhddevice/hdffcmd.c +++ b/PLUGINS/src/dvbhddevice/hdffcmd.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: hdffcmd.c 1.25 2012/06/16 11:16:38 kls Exp $ + * $Id: hdffcmd.c 1.26 2012/11/15 09:19:47 kls Exp $ */ #include <stdint.h> @@ -170,6 +170,11 @@ void cHdffCmdIf::CmdAvSetAudioChannel(uint8_t AudioChannel) HdffCmdAvSetAudioChannel(mOsdDev, AudioChannel); } +void cHdffCmdIf::CmdAvSetSyncShift(int16_t SyncShift) +{ + HdffCmdAvSetSyncShift(mOsdDev, SyncShift); +} + void cHdffCmdIf::CmdOsdConfigure(const HdffOsdConfig_t * pConfig) { diff --git a/PLUGINS/src/dvbhddevice/hdffcmd.h b/PLUGINS/src/dvbhddevice/hdffcmd.h index f33fe08..58ed138 100644 --- a/PLUGINS/src/dvbhddevice/hdffcmd.h +++ b/PLUGINS/src/dvbhddevice/hdffcmd.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: hdffcmd.h 1.20 2012/06/16 11:16:52 kls Exp $ + * $Id: hdffcmd.h 1.21 2012/11/15 09:20:05 kls Exp $ */ #ifndef _HDFF_CMD_H_ @@ -47,6 +47,7 @@ public: void CmdAvSetAudioDelay(int16_t Delay); void CmdAvSetAudioDownmix(HdffAudioDownmixMode_t DownmixMode); void CmdAvSetAudioChannel(uint8_t AudioChannel); + void CmdAvSetSyncShift(int16_t SyncShift); void CmdOsdConfigure(const HdffOsdConfig_t * pConfig); void CmdOsdReset(void); diff --git a/PLUGINS/src/dvbhddevice/hdffmsgdef.h b/PLUGINS/src/dvbhddevice/hdffmsgdef.h deleted file mode 100644 index d63e88a..0000000 --- a/PLUGINS/src/dvbhddevice/hdffmsgdef.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * hdffmsgdef.h: TODO(short description) - * - * See the README file for copyright information and how to reach the author. - * - * $Id: hdffmsgdef.h 1.13 2011/08/27 09:34:43 kls Exp $ - */ - -#ifndef _HDFF_MSGDEF_H_ -#define _HDFF_MSGDEF_H_ - -#define MAX_CMD_LEN 1536 - -namespace HDFF -{ - -typedef enum _eMessageType -{ - msgTypeCommand, - msgTypeAnswer, - msgTypeResult, - msgTypeEvent -} eMessageType; - -typedef enum _eMessageGroup -{ - msgGroupGeneric, - msgGroupAvDec, - msgGroupAvMux, - msgGroupFrontend, - msgGroupOsd, - msgGroupHdmi, - msgGroupRemoteControl -} eMessageGroup; - -typedef enum _eMessageId -{ - msgGenGetFirmwareVersion = 0, - msgGenGetInterfaceVersion, - msgGenGetCopyrights, - - msgAvSetAudioPid = 0, - msgAvSetVideoPid, - msgAvSetPcrPid, - msgAvSetTeletextPid, - msgAvShowStillImage, - msgAvSetVideoWindow, - msgAvSetDecoderInput, - msgAvSetDemultiplexerInput, - msgAvSetVideoFormat, - msgAvSetVideoOutputMode, - msgAvSetStc, - msgAvFlushBuffer, - msgAvEnableSync, - msgAvSetVideoSpeed, - msgAvSetAudioSpeed, - msgAvEnableVideoAfterStop, - msgAvGetVideoFormatInfo, - msgAvSetAudioDelay, - msgAvSetAudioDownmix, - msgAvSetAudioChannel, - msgAvSetPlayMode, - - msgMuxSetVideoOut = 0, - msgMuxSetSlowBlank, - msgMuxSetFastBlank, - msgMuxSetVolume, - msgMuxSetAudioMute, - - msgOsdConfigure = 0, - msgOsdReset, - msgOsdCreateDisplay = 10, - msgOsdDeleteDisplay, - msgOsdEnableDisplay, - msgOsdSetDisplayOutputRectangle, - msgOsdSetDisplayClippingArea, - msgOsdRenderDisplay, - msgOsdSaveRegion, - msgOsdRestoreRegion, - msgOsdCreatePalette = 30, - msgOsdDeletePalette, - msgOsdSetDisplayPalette, - msgOsdSetPaletteColors, - msgOsdCreateFontFace = 50, - msgOsdDeleteFontFace, - msgOsdCreateFont, - msgOsdDeleteFont, - msgOsdDrawPixel = 70, - msgOsdDrawRectangle, - msgOsdDrawCircle, - msgOsdDrawEllipse, - msgOsdDrawSlope, - msgOsdDrawText, - msgOsdDrawTextW, - msgOsdDrawBitmap, - - msgHdmiEnableOutput = 0, - msgHdmiSetVideoMode, - msgHdmiConfigure, - msgHdmiIsDisplayConnected, - msgHdmiGetDisplayInfo, - msgHdmiGetVideoMode, - msgHdmiSendCecCommand, - - msgRemoteSetProtocol = 0, - msgRemoteSetAddressFilter, - msgRemoteKeyEvent -} eMessageId; - - -// AvDec definitions - -typedef enum _eAVContainerType -{ - avContainerPes, - avContainerPesDvd, - avContainerMaxValue -} eAVContainerType; - -typedef enum _eAudioStreamType -{ - audioStreamMpeg1, - audioStreamMpeg2, - audioStreamAc3, - audioStreamAac, - audioStreamHeAac, - audioStreamPcm, - audioStreamEAc3, - audioStreamDts, - audioStreamMaxValue -} eAudioStreamType; - -typedef enum _eVideoStreamType -{ - videoStreamMpeg1, - videoStreamMpeg2, - videoStreamH264, - videoStreamMpeg4Asp, - videoStreamVc1, - videoStreamMaxValue -} eVideoStreamType; - - -typedef enum _eTvFormat -{ - tvFormat4by3, - tvFormat16by9, - tvFormatMaxValue -} eTvFormat; - -typedef enum _eVideoConversion -{ - videoConversionAutomatic, - videoConversionLetterbox16by9, - videoConversionLetterbox14by9, - videoConversionPillarbox, - videoConversionCentreCutOut, - videoConversionAlways16by9, - videoConversionMaxValue -} eVideoConversion; - -typedef struct _tVideoFormat -{ - bool AutomaticEnabled; - bool AfdEnabled; - eTvFormat TvFormat; - eVideoConversion VideoConversion; -} tVideoFormat; - -typedef enum _eVideoOutputMode -{ - videoOutputClone, - videoOutputDualView, - videoOutputMaxValue -} eVideoOutputMode; - -typedef enum _eDownmixMode -{ - downmixOff, - downmixAnalog, - downmixAlways, - downmixAutomatic, - downmixHdmi -} eDownmixMode; - -// AvMux definitions - -typedef enum _eVideoOut -{ - videoOutDisabled, - videoOutRgb, - videoOutCvbsYuv, - videoOutYc, - videoOutMaxValue -} eVideoOut; - -typedef enum _eSlowBlank -{ - slowBlankOff, - slowBlank16by9, - slowBlank4by3, - slowBlankMaxValue -} eSlowBlank; - -typedef enum _eFastBlank -{ - fastBlankCvbs, - fastBlankRgb, - fastBlankMaxValue -} eFastBlank; - - -// OSD definitions - -#define InvalidHandle 0xFFFFFFFF -#define ScreenDisplayHandle 0xFFFFFFFE - -#define PositionScreenCentered 0xFFFF - -#define SizeFullScreen 0xFFFF -#define SizeSameAsSource 0xFFFE - -#define FontFaceTiresias 0x00000000 - -typedef enum _eColorType -{ - colorTypeClut1, - colorTypeClut2, - colorTypeClut4, - colorTypeClut8, - colorTypeARGB8888, - colorTypeARGB8565, - colorTypeARGB4444, - colorTypeARGB1555, -} eColorType; - -typedef enum _eColorFormat -{ - colorFormatARGB, - colorFormatACbYCr, -} eColorFormat; - -typedef enum _eDrawingFlags -{ - drawFull, - drawHalfTop, - drawHalfLeft, - drawHalfBottom, - drawHalfRight, - drawQuarterTopLeft, - drawQuarterTopRight, - drawQuarterBottomLeft, - drawQuarterBottomRight, - drawQuarterTopLeftInverted, - drawQuarterTopRightInverted, - drawQuarterBottomLeftInverted, - drawQuarterBottomRightInverted -} eDrawingFlags; - -typedef struct _tOsdConfig -{ - bool FontAntialiasing; - bool FontKerning; -} tOsdConfig; - -// HDMI definitions - -typedef enum _eHdmiVideoMode -{ - videoMode576p50 = 18, - videoMode720p50 = 19, - videoMode1080i50 = 20, - videoMode576i50 = 22, - videoModeMaxValue -} eHdmiVideoMode; - -typedef enum _eVideoModeAdaption -{ - videoModeAdaptOff, - videoModeAdaptFrameRate, - videoModeAdaptOnlyForHd, - videoModeAdaptAlways -} eVideoModeAdaption; - -typedef enum _eCecCommand -{ - cecCommandTvOn, - cecCommandTvOff, - cecCommandActiveSource, - cecCommandInactiveSource -} eCecCommand; - -typedef struct _tHdmiConfig -{ - bool TransmitAudio; - bool ForceDviMode; - bool CecEnabled; - eVideoModeAdaption VideoModeAdaption; -} tHdmiConfig; - -// Remote control definitions - -typedef enum _eRemoteProtocol -{ - remoteProtocolNone, - remoteProtocolRc5, - remoteProtocolRc6, - remoteProtocolMaxValue -} eRemoteProtocol; - -} // end of namespace - -#endif diff --git a/PLUGINS/src/dvbhddevice/hdffosd.c b/PLUGINS/src/dvbhddevice/hdffosd.c index 427a3fb..5d55c95 100644 --- a/PLUGINS/src/dvbhddevice/hdffosd.c +++ b/PLUGINS/src/dvbhddevice/hdffosd.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: hdffosd.c 1.17 2012/06/16 11:17:11 kls Exp $ + * $Id: hdffosd.c 1.18 2012/11/15 09:20:24 kls Exp $ */ #include "hdffosd.h" @@ -390,11 +390,13 @@ void cHdffOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor Colo } } } +#if 0 if (mSupportsUtf8Text) { mHdffCmdIf->CmdOsdDrawUtf8Text(mDisplay, pFont->Handle, x + mLeft, y + mTop + h, s, ColorFg); } else +#endif { uint16_t tmp[1000]; uint16_t len = 0; diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile b/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile index 8b25f66..babe79b 100644 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile +++ b/PLUGINS/src/dvbhddevice/libhdffcmd/Makefile @@ -24,6 +24,18 @@ CFLAGS ?= -g -O2 -fPIC -Wall AR ?= ar ARFLAGS ?= r +### The directory environment: + +VDRDIR ?= ../../../.. + +### Make sure that necessary options are included: + +include $(VDRDIR)/Make.global + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + ### Implicit rules: %.o: %.c diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c index 413f3e2..3ae1fd7 100644 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c +++ b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.c @@ -449,3 +449,22 @@ int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel) osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); } + +int HdffCmdAvSetSyncShift(int OsdDevice, int16_t SyncShift) +{ + uint8_t cmdData[16]; + BitBuffer_t cmdBuf; + osd_raw_cmd_t osd_cmd; + + BitBuffer_Init(&cmdBuf, cmdData, sizeof(cmdData)); + memset(&osd_cmd, 0, sizeof(osd_raw_cmd_t)); + osd_cmd.cmd_data = cmdData; + HdffCmdBuildHeader(&cmdBuf, HDFF_MSG_TYPE_COMMAND, + HDFF_MSG_GROUP_AV_DECODER, + HDFF_MSG_AV_SET_OPTIONS); + BitBuffer_SetBits(&cmdBuf, 1, 1); + BitBuffer_SetBits(&cmdBuf, 31, 0); // reserved + BitBuffer_SetBits(&cmdBuf, 16, SyncShift); + osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf); + return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd); +} diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h index 0e77c79..bfed59d 100644 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h +++ b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_av.h @@ -149,4 +149,6 @@ int HdffCmdAvSetAudioDownmix(int OsdDevice, int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel); +int HdffCmdAvSetSyncShift(int OsdDevice, int16_t SyncShift); + #endif /* HDFFCMD_AV_H */ diff --git a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h index 322c256..5ead270 100644 --- a/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h +++ b/PLUGINS/src/dvbhddevice/libhdffcmd/hdffcmd_defs.h @@ -70,6 +70,7 @@ typedef enum HdffMessageId_t HDFF_MSG_AV_SET_AUDIO_DOWNMIX, HDFF_MSG_AV_SET_AUDIO_CHANNEL, HDFF_MSG_AV_SET_PLAY_MODE, + HDFF_MSG_AV_SET_OPTIONS, HDFF_MSG_MUX_SET_VIDEO_OUT = 0, HDFF_MSG_MUX_SET_SLOW_BLANK, diff --git a/PLUGINS/src/dvbhddevice/po/de_DE.po b/PLUGINS/src/dvbhddevice/po/de_DE.po index ac58c0c..b263f08 100644 --- a/PLUGINS/src/dvbhddevice/po/de_DE.po +++ b/PLUGINS/src/dvbhddevice/po/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR \n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2012-02-07 20:13+0100\n" +"POT-Creation-Date: 2012-11-13 20:08+0100\n" "PO-Revision-Date: 2011-04-25 21:44+0200\n" "Last-Translator: Christoph Haubrich\n" "Language-Team: <see README>\n" @@ -91,6 +91,9 @@ msgstr "Audio Verzĥgerung (ms)" msgid "Audio Downmix" msgstr "Audio Downmix" +msgid "A/V Sync Shift (ms)" +msgstr "A/V-Sync Verschiebung (ms)" + msgid "OSD Size" msgstr "OSD Grĥe" diff --git a/PLUGINS/src/dvbhddevice/po/fi_FI.po b/PLUGINS/src/dvbhddevice/po/fi_FI.po index f8e480b..a971773 100644 --- a/PLUGINS/src/dvbhddevice/po/fi_FI.po +++ b/PLUGINS/src/dvbhddevice/po/fi_FI.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR \n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2012-02-07 20:13+0100\n" +"POT-Creation-Date: 2012-11-13 20:08+0100\n" "PO-Revision-Date: 2011-04-25 21:44+0200\n" "Last-Translator: Rolf Ahrenberg\n" "Language-Team: Finnish <vdr@linuxtv.org>\n" @@ -91,6 +91,9 @@ msgstr "¤nen viive (ms)" msgid "Audio Downmix" msgstr "¤nen alasmiksaus" +msgid "A/V Sync Shift (ms)" +msgstr "" + msgid "OSD Size" msgstr "Kuvaruutun¤ytĥn koko" diff --git a/PLUGINS/src/dvbhddevice/po/it_IT.po b/PLUGINS/src/dvbhddevice/po/it_IT.po index def0389..56b987a 100644 --- a/PLUGINS/src/dvbhddevice/po/it_IT.po +++ b/PLUGINS/src/dvbhddevice/po/it_IT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR\n" "Report-Msgid-Bugs-To: <see README>\n" -"POT-Creation-Date: 2012-02-07 20:13+0100\n" +"POT-Creation-Date: 2012-11-13 20:08+0100\n" "PO-Revision-Date: 2011-07-10 00:23+0100\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Language-Team: <see README>\n" @@ -94,6 +94,9 @@ msgstr "Ritardo audio (ms)" msgid "Audio Downmix" msgstr "Scala Audio" +msgid "A/V Sync Shift (ms)" +msgstr "" + msgid "OSD Size" msgstr "Dimensione OSD" diff --git a/PLUGINS/src/dvbhddevice/setup.c b/PLUGINS/src/dvbhddevice/setup.c index b6525a0..fcc1648 100644 --- a/PLUGINS/src/dvbhddevice/setup.c +++ b/PLUGINS/src/dvbhddevice/setup.c @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: setup.c 1.17 2012/02/08 15:14:01 kls Exp $ + * $Id: setup.c 1.18 2012/11/15 09:20:50 kls Exp $ */ #include "setup.h" @@ -26,6 +26,7 @@ cHdffSetup::cHdffSetup(void) AnalogueVideo = HDFF_VIDEO_OUT_CVBS_YUV; AudioDelay = 0; AudioDownmix = HDFF_AUDIO_DOWNMIX_AUTOMATIC; + AvSyncShift = 0; OsdSize = 0; CecEnabled = 1; CecTvOn = 1; @@ -46,6 +47,7 @@ bool cHdffSetup::SetupParse(const char *Name, const char *Value) else if (strcmp(Name, "AnalogueVideo") == 0) AnalogueVideo = atoi(Value); else if (strcmp(Name, "AudioDelay") == 0) AudioDelay = atoi(Value); else if (strcmp(Name, "AudioDownmix") == 0) AudioDownmix = atoi(Value); + else if (strcmp(Name, "AvSyncShift") == 0) AvSyncShift = atoi(Value); else if (strcmp(Name, "OsdSize") == 0) OsdSize = atoi(Value); else if (strcmp(Name, "CecEnabled") == 0) CecEnabled = atoi(Value); else if (strcmp(Name, "CecTvOn") == 0) CecTvOn = atoi(Value); @@ -256,6 +258,7 @@ cHdffSetupPage::cHdffSetupPage(HDFF::cHdffCmdIf * pHdffCmdIf) Add(new cMenuEditStraItem(tr("Analogue Video"), &mNewHdffSetup.AnalogueVideo, kAnalogueVideos, AnalogueVideoItems)); Add(new cMenuEditIntItem(tr("Audio Delay (ms)"), &mNewHdffSetup.AudioDelay, 0, 500)); Add(new cMenuEditStraItem(tr("Audio Downmix"), &mNewHdffSetup.AudioDownmix, kAudioDownmixes, AudioDownmixItems)); + Add(new cMenuEditIntItem(tr("A/V Sync Shift (ms)"), &mNewHdffSetup.AvSyncShift, -500, 500)); Add(new cMenuEditStraItem(tr("OSD Size"), &mNewHdffSetup.OsdSize, kOsdSizes, OsdSizeItems)); Add(new cMenuEditBoolItem(tr("HDMI CEC"), &mNewHdffSetup.CecEnabled)); Add(new cMenuEditBoolItem(tr("CEC: Switch TV on"), &mNewHdffSetup.CecTvOn)); @@ -385,6 +388,7 @@ void cHdffSetupPage::Store(void) SetupStore("AnalogueVideo", mNewHdffSetup.AnalogueVideo); SetupStore("AudioDelay", mNewHdffSetup.AudioDelay); SetupStore("AudioDownmix", mNewHdffSetup.AudioDownmix); + SetupStore("AvSyncShift", mNewHdffSetup.AvSyncShift); SetupStore("OsdSize", mNewHdffSetup.OsdSize); SetupStore("CecEnabled", mNewHdffSetup.CecEnabled); SetupStore("CecTvOn", mNewHdffSetup.CecTvOn); @@ -412,6 +416,7 @@ void cHdffSetupPage::Store(void) mHdffCmdIf->CmdAvSetAudioDelay(mNewHdffSetup.AudioDelay); mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) mNewHdffSetup.AudioDownmix); + mHdffCmdIf->CmdAvSetSyncShift(mNewHdffSetup.AvSyncShift); mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) mNewHdffSetup.AnalogueVideo); diff --git a/PLUGINS/src/dvbhddevice/setup.h b/PLUGINS/src/dvbhddevice/setup.h index 64033e1..011411d 100644 --- a/PLUGINS/src/dvbhddevice/setup.h +++ b/PLUGINS/src/dvbhddevice/setup.h @@ -3,7 +3,7 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: setup.h 1.12 2012/02/08 15:14:56 kls Exp $ + * $Id: setup.h 1.13 2012/11/15 09:21:07 kls Exp $ */ #ifndef _HDFF_SETUP_H_ @@ -28,6 +28,7 @@ struct cHdffSetup int AnalogueVideo; int AudioDelay; int AudioDownmix; + int AvSyncShift; int OsdSize; int CecEnabled; int CecTvOn; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ci.c 2.9 2012/05/29 11:13:40 kls Exp $ + * $Id: ci.c 2.10 2012/10/07 11:11:18 kls Exp $ */ #include "ci.h" @@ -845,9 +845,9 @@ void cCiDateTime::SendDateTime(void) int D = tm_gmt.tm_mday; int L = (M == 1 || M == 2) ? 1 : 0; int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001); -#define DEC2BCD(d) (((d / 10) << 4) + (d % 10)) +#define DEC2BCD(d) uint8_t(((d / 10) << 4) + (d % 10)) struct tTime { uint16_t mjd; uint8_t h, m, s; short offset; }; - tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : htons(tm_loc.tm_gmtoff / 60) }; + tTime T = { mjd : htons(MJD), h : DEC2BCD(tm_gmt.tm_hour), m : DEC2BCD(tm_gmt.tm_min), s : DEC2BCD(tm_gmt.tm_sec), offset : short(htons(tm_loc.tm_gmtoff / 60)) }; bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer; DumpTPDUDataTransfer &= DumpDateTime; if (DumpDateTime) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 2.53 2012/09/15 11:51:54 kls Exp $ + * $Id: config.h 2.54 2012/10/03 09:59:34 kls Exp $ */ #ifndef __CONFIG_H @@ -22,13 +22,13 @@ // VDR's own version number: -#define VDRVERSION "1.7.31" -#define VDRVERSNUM 10731 // Version * 10000 + Major * 100 + Minor +#define VDRVERSION "1.7.32" +#define VDRVERSNUM 10732 // Version * 10000 + Major * 100 + Minor // The plugin API's version number: -#define APIVERSION "1.7.31" -#define APIVERSNUM 10731 // Version * 10000 + Major * 100 + Minor +#define APIVERSION "1.7.32" +#define APIVERSNUM 10732 // Version * 10000 + Major * 100 + Minor // When loading plugins, VDR searches them by their APIVERSION, which // may be smaller than VDRVERSION in case there have been no changes to @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: cutter.c 2.14 2012/09/20 09:12:47 kls Exp $ + * $Id: cutter.c 2.16 2012/11/18 12:09:00 kls Exp $ */ #include "cutter.h" @@ -13,17 +13,261 @@ #include "remux.h" #include "videodir.h" +// --- cPacketBuffer --------------------------------------------------------- + +class cPacketBuffer { +private: + uchar *data; + int size; + int length; +public: + cPacketBuffer(void); + ~cPacketBuffer(); + void Append(uchar *Data, int Length); + ///< Appends Length bytes of Data to this packet buffer. + void Flush(uchar *Data, int &Length, int MaxLength); + ///< Flushes the content of this packet buffer into the given Data, starting + ///< at position Length, and clears the buffer afterwards. Length will be + ///< incremented accordingly. If Length plus the total length of the stored + ///< packets would exceed MaxLength, nothing is copied. + }; + +cPacketBuffer::cPacketBuffer(void) +{ + data = NULL; + size = length = 0; +} + +cPacketBuffer::~cPacketBuffer() +{ + free(data); +} + +void cPacketBuffer::Append(uchar *Data, int Length) +{ + if (length + Length >= size) { + int NewSize = (length + Length) * 3 / 2; + if (uchar *p = (uchar *)realloc(data, NewSize)) { + data = p; + size = NewSize; + } + else + return; // out of memory + } + memcpy(data + length, Data, Length); + length += Length; +} + +void cPacketBuffer::Flush(uchar *Data, int &Length, int MaxLength) +{ + if (Data && length > 0 && Length + length <= MaxLength) { + memcpy(Data + Length, data, length); + Length += length; + } + length = 0; +} + +// --- cPacketStorage -------------------------------------------------------- + +class cPacketStorage { +private: + cPacketBuffer *buffers[MAXPID]; +public: + cPacketStorage(void); + ~cPacketStorage(); + void Append(int Pid, uchar *Data, int Length); + void Flush(int Pid, uchar *Data, int &Length, int MaxLength); + }; + +cPacketStorage::cPacketStorage(void) +{ + for (int i = 0; i < MAXPID; i++) + buffers[i] = NULL; +} + +cPacketStorage::~cPacketStorage() +{ + for (int i = 0; i < MAXPID; i++) + delete buffers[i]; +} + +void cPacketStorage::Append(int Pid, uchar *Data, int Length) +{ + if (!buffers[Pid]) + buffers[Pid] = new cPacketBuffer; + buffers[Pid]->Append(Data, Length); +} + +void cPacketStorage::Flush(int Pid, uchar *Data, int &Length, int MaxLength) +{ + if (buffers[Pid]) + buffers[Pid]->Flush(Data, Length, MaxLength); +} + +// --- cDanglingPacketStripper ----------------------------------------------- + +class cDanglingPacketStripper { +private: + bool processed[MAXPID]; + cPatPmtParser patPmtParser; +public: + cDanglingPacketStripper(void); + bool Process(uchar *Data, int Length, int64_t FirstPts); + ///< Scans the frame given in Data and hides the payloads of any TS packets + ///< that either didn't start within this frame, or have a PTS that is + ///< before FirstPts. The TS packets in question are not physically removed + ///< from Data in order to keep any frame counts and PCR timestamps intact. + ///< Returns true if any dangling packets have been found. + }; + +cDanglingPacketStripper::cDanglingPacketStripper(void) +{ + memset(processed, 0x00, sizeof(processed)); +} + +bool cDanglingPacketStripper::Process(uchar *Data, int Length, int64_t FirstPts) +{ + bool Found = false; + while (Length >= TS_SIZE && *Data == TS_SYNC_BYTE) { + int Pid = TsPid(Data); + if (Pid == PATPID) + patPmtParser.ParsePat(Data, TS_SIZE); + else if (Pid == patPmtParser.PmtPid()) + patPmtParser.ParsePmt(Data, TS_SIZE); + else { + int64_t Pts = TsGetPts(Data, TS_SIZE); + if (Pts >= 0) + processed[Pid] = PtsDiff(FirstPts, Pts) >= 0; // Pts is at or after FirstPts + if (!processed[Pid]) { + TsHidePayload(Data); + Found = true; + } + } + Length -= TS_SIZE; + Data += TS_SIZE; + } + return Found; +} + +// --- cPtsFixer ------------------------------------------------------------- + +class cPtsFixer { +private: + int delta; // time between two frames + int64_t last; // the last (i.e. highest) video PTS value seen + int64_t offset; // offset to add to PTS values + bool fixCounters; // controls fixing the TS continuity counters (only from the second CutIn up) + uchar counter[MAXPID]; // the TS continuity counter for each PID + cPatPmtParser patPmtParser; +public: + cPtsFixer(void); + void Setup(double FramesPerSecond); + void Fix(uchar *Data, int Length, bool CutIn); + }; + +cPtsFixer::cPtsFixer(void) +{ + delta = 0; + last = -1; + offset = -1; + fixCounters = false; + memset(counter, 0x00, sizeof(counter)); +} + +void cPtsFixer::Setup(double FramesPerSecond) +{ + delta = int(round(PTSTICKS / FramesPerSecond)); +} + +void cPtsFixer::Fix(uchar *Data, int Length, bool CutIn) +{ + if (!patPmtParser.Vpid()) { + if (!patPmtParser.ParsePatPmt(Data, Length)) + return; + } + // Determine the PTS offset at the beginning of each sequence (except the first one): + if (CutIn && last >= 0) { + int64_t Pts = TsGetPts(Data, Length); + if (Pts >= 0) { + // offset is calculated so that Pts + offset results in last + delta: + offset = Pts - PtsAdd(last, delta); + if (offset <= 0) + offset = -offset; + else + offset = MAX33BIT + 1 - offset; + } + fixCounters = true; + } + // Keep track of the highest video PTS: + uchar *p = Data; + int len = Length; + while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { + int Pid = TsPid(p); + if (Pid == patPmtParser.Vpid()) { + int64_t Pts = PtsAdd(TsGetPts(p, TS_SIZE), offset); // offset is taken into account here, to make last have the "new" value already! + if (Pts >= 0 && (last < 0 || PtsDiff(last, Pts) > 0)) + last = Pts; + } + // Adjust the TS continuity counter: + if (fixCounters) { + counter[Pid] = (counter[Pid] + 1) & TS_CONT_CNT_MASK; + TsSetContinuityCounter(p, counter[Pid]); + } + else + counter[Pid] = TsGetContinuityCounter(p); // collect initial counters + p += TS_SIZE; + len -= TS_SIZE; + } + // Apply the PTS offset: + if (offset > 0) { + uchar *p = Data; + int len = Length; + while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { + // Adjust the various timestamps: + int64_t Pts = TsGetPts(p, TS_SIZE); + if (Pts >= 0) + TsSetPts(p, TS_SIZE, PtsAdd(Pts, offset)); + int64_t Dts = TsGetDts(p, TS_SIZE); + if (Dts >= 0) + TsSetDts(p, TS_SIZE, PtsAdd(Dts, offset)); + int64_t Pcr = TsGetPcr(p); + if (Pcr >= 0) { + int64_t NewPcr = Pcr + offset * PCRFACTOR; + if (NewPcr >= MAX27MHZ) + NewPcr -= MAX27MHZ + 1; + TsSetPcr(p, NewPcr); + } + p += TS_SIZE; + len -= TS_SIZE; + } + } +} + // --- cCuttingThread -------------------------------------------------------- class cCuttingThread : public cThread { private: const char *error; bool isPesRecording; + double framesPerSecond; cUnbufferedFile *fromFile, *toFile; cFileName *fromFileName, *toFileName; cIndexFile *fromIndex, *toIndex; cMarks fromMarks, toMarks; + int numSequences; off_t maxVideoFileSize; + off_t fileSize; + cPtsFixer ptsFixer; + bool suspensionLogged; + bool Throttled(void); + bool SwitchFile(bool Force = false); + bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length); + bool FramesAreEqual(int Index1, int Index2); + void GetPendingPackets(uchar *Buffer, int &Length, int Index, int64_t LastPts); + // Gather all non-video TS packets from Index upward that either belong to + // payloads that started before Index, or have a PTS that is before LastPts, + // and add them to the end of the given Data. + bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex); protected: virtual void Action(void); public: @@ -33,7 +277,7 @@ public: }; cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName) -:cThread("video cutting") +:cThread("video cutting", true) { error = NULL; fromFile = toFile = NULL; @@ -41,16 +285,25 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName) fromIndex = toIndex = NULL; cRecording Recording(FromFileName); isPesRecording = Recording.IsPesRecording(); - if (fromMarks.Load(FromFileName, Recording.FramesPerSecond(), isPesRecording) && fromMarks.Count()) { - fromFileName = new cFileName(FromFileName, false, true, isPesRecording); - toFileName = new cFileName(ToFileName, true, true, isPesRecording); - fromIndex = new cIndexFile(FromFileName, false, isPesRecording); - toIndex = new cIndexFile(ToFileName, true, isPesRecording); - toMarks.Load(ToFileName, Recording.FramesPerSecond(), isPesRecording); // doesn't actually load marks, just sets the file name - maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); - if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) - maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); - Start(); + framesPerSecond = Recording.FramesPerSecond(); + suspensionLogged = false; + fileSize = 0; + ptsFixer.Setup(framesPerSecond); + if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) { + numSequences = fromMarks.GetNumSequences(); + if (numSequences > 0) { + fromFileName = new cFileName(FromFileName, false, true, isPesRecording); + toFileName = new cFileName(ToFileName, true, true, isPesRecording); + fromIndex = new cIndexFile(FromFileName, false, isPesRecording); + toIndex = new cIndexFile(ToFileName, true, isPesRecording); + toMarks.Load(ToFileName, framesPerSecond, isPesRecording); // doesn't actually load marks, just sets the file name + maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); + if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) + maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); + Start(); + } + else + esyslog("no editing sequences found for %s", FromFileName); } else esyslog("no editing marks found for %s", FromFileName); @@ -65,171 +318,236 @@ cCuttingThread::~cCuttingThread() delete toIndex; } +bool cCuttingThread::Throttled(void) +{ + if (cIoThrottle::Engaged()) { + if (!suspensionLogged) { + dsyslog("suspending cutter thread"); + suspensionLogged = true; + } + return true; + } + else if (suspensionLogged) { + dsyslog("resuming cutter thread"); + suspensionLogged = false; + } + return false; +} + +bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length) +{ + uint16_t FileNumber; + off_t FileOffset; + if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) { + fromFile = fromFileName->SetOffset(FileNumber, FileOffset); + if (fromFile) { + fromFile->SetReadAhead(MEGABYTE(20)); + int len = ReadFrame(fromFile, Buffer, Length, MAXFRAMESIZE); + if (len < 0) + error = "ReadFrame"; + else if (len != Length) + Length = len; + return error == NULL; + } + else + error = "fromFile"; + } + return false; +} + +bool cCuttingThread::SwitchFile(bool Force) +{ + if (fileSize > maxVideoFileSize || Force) { + toFile = toFileName->NextFile(); + if (!toFile) { + error = "toFile"; + return false; + } + fileSize = 0; + } + return true; +} + +bool cCuttingThread::FramesAreEqual(int Index1, int Index2) +{ + bool Independent; + uchar Buffer1[MAXFRAMESIZE]; + uchar Buffer2[MAXFRAMESIZE]; + int Length1; + int Length2; + if (LoadFrame(Index1, Buffer1, Independent, Length1) && LoadFrame(Index2, Buffer2, Independent, Length2)) { + if (Length1 == Length2) { + int Diffs = 0; + for (int i = 0; i < Length1; i++) { + if (Buffer1[i] != Buffer2[i]) { + if (Diffs++ > 10) // the continuity counters of the PAT/PMT packets may differ + return false; + } + } + return true; + } + } + return false; +} + +void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int64_t LastPts) +{ + bool Processed[MAXPID] = { false }; + int NumIndependentFrames = 0; + cPatPmtParser PatPmtParser; + cPacketStorage PacketStorage; + for (; NumIndependentFrames < 2; Index++) { + uchar Buffer[MAXFRAMESIZE]; + bool Independent; + int len; + if (LoadFrame(Index, Buffer, Independent, len)) { + if (Independent) + NumIndependentFrames++; + uchar *p = Buffer; + while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { + int Pid = TsPid(p); + if (Pid == PATPID) + PatPmtParser.ParsePat(p, TS_SIZE); + else if (Pid == PatPmtParser.PmtPid()) + PatPmtParser.ParsePmt(p, TS_SIZE); + else if (!Processed[Pid]) { + int64_t Pts = TsGetPts(p, TS_SIZE); + if (Pts >= 0) { + int64_t d = PtsDiff(LastPts, Pts); + if (d <= 0) // Pts is before or at LastPts + PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE); + if (d >= 0) { // Pts is at or after LastPts + NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets + Processed[Pid] = true; + } + } + if (!Processed[Pid]) + PacketStorage.Append(Pid, p, TS_SIZE); + } + len -= TS_SIZE; + p += TS_SIZE; + } + } + else + break; + } +} + +bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex) +{ + // Check for seamless connections: + bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex); + bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex); + // Process all frames from BeginIndex (included) to EndIndex (excluded): + cDanglingPacketStripper DanglingPacketStripper; + int NumIndependentFrames = 0; + int64_t FirstPts = -1; + int64_t LastPts = -1; + for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) { + uchar Buffer[MAXFRAMESIZE]; + bool Independent; + int Length; + if (LoadFrame(Index, Buffer, Independent, Length)) { + if (!isPesRecording) { + int64_t Pts = TsGetPts(Buffer, Length); + if (FirstPts < 0) + FirstPts = Pts; // the PTS of the first frame in the sequence + else if (LastPts < 0 || PtsDiff(LastPts, Pts) > 0) + LastPts = Pts; // the PTS of the frame that is displayed as the very last one of the sequence + } + // Fixup data at the beginning of the sequence: + if (!SeamlessBegin) { + if (isPesRecording) { + if (Index == BeginIndex) + cRemux::SetBrokenLink(Buffer, Length); + } + else if (NumIndependentFrames < 2) { + if (DanglingPacketStripper.Process(Buffer, Length, FirstPts)) + NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more dangling packets + } + } + // Fixup data at the end of the sequence: + if (!SeamlessEnd) { + if (Index == EndIndex - 1) { + if (!isPesRecording) + GetPendingPackets(Buffer, Length, EndIndex, LastPts + int(round(PTSTICKS / framesPerSecond))); // adding one frame length to fully cover the very last frame + } + } + // Fixup timestamps and continuity counters: + if (!isPesRecording) { + if (numSequences > 1) + ptsFixer.Fix(Buffer, Length, !SeamlessBegin && Index == BeginIndex); + } + // Every file shall start with an independent frame: + if (Independent) { + NumIndependentFrames++; + if (!SwitchFile()) + return false; + } + // Write index: + if (!toIndex->Write(Independent, toFileName->Number(), fileSize)) { + error = "toIndex"; + return false; + } + // Write data: + if (toFile->Write(Buffer, Length) < 0) { + error = "safe_write"; + return false; + } + fileSize += Length; + // Generate marks at the editing points in the edited recording: + if (numSequences > 0 && Index == BeginIndex) { + if (toMarks.Count() > 0) + toMarks.Add(toIndex->Last()); + toMarks.Add(toIndex->Last()); + toMarks.Save(); + } + } + else + return false; + } + return true; +} + void cCuttingThread::Action(void) { - cMark *Mark = fromMarks.First(); - if (Mark) { - SetPriority(19); - SetIOPriority(7); + if (cMark *BeginMark = fromMarks.GetNextBegin()) { fromFile = fromFileName->Open(); toFile = toFileName->Open(); if (!fromFile || !toFile) return; - fromFile->SetReadAhead(MEGABYTE(20)); - int Index = Mark->Position(); - Mark = fromMarks.Next(Mark); - off_t FileSize = 0; - int CurrentFileNumber = 0; - int LastIFrame = 0; - toMarks.Add(0); - toMarks.Save(); - uchar buffer[MAXFRAMESIZE], buffer2[MAXFRAMESIZE]; - int Length2; - bool CheckForSeamlessStream = false; - bool LastMark = false; - bool cutIn = true; - bool suspensionLogged = false; - while (Running()) { - uint16_t FileNumber; - off_t FileOffset; - int Length; - bool Independent; - + int LastEndIndex = -1; + while (BeginMark && Running()) { // Suspend cutting if we have severe throughput problems: - - if (cIoThrottle::Engaged()) { - if (!suspensionLogged) { - dsyslog("suspending cutter thread"); - suspensionLogged = true; - } + if (Throttled()) { cCondWait::SleepMs(100); continue; } - else if (suspensionLogged) { - dsyslog("resuming cutter thread"); - suspensionLogged = false; - } - // Make sure there is enough disk space: - AssertFreeDiskSpace(-1); - - // Read one frame: - - if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) { - if (FileNumber != CurrentFileNumber) { - fromFile = fromFileName->SetOffset(FileNumber, FileOffset); - if (fromFile) - fromFile->SetReadAhead(MEGABYTE(20)); - CurrentFileNumber = FileNumber; - } - if (fromFile) { - int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer)); - if (len < 0) { - error = "ReadFrame"; - break; - } - if (len != Length) { - CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer - Length = len; - } - } - else { - error = "fromFile"; - break; - } + // Determine the actual begin and end marks, skipping any marks at the same position: + cMark *EndMark = fromMarks.GetNextEnd(BeginMark); + // Process the current sequence: + int EndIndex = EndMark ? EndMark->Position() : fromIndex->Last() + 1; + int NextBeginIndex = -1; + if (EndMark) { + if (cMark *NextBeginMark = fromMarks.GetNextBegin(EndMark)) + NextBeginIndex = NextBeginMark->Position(); } - else { - // Error, unless we're past the last cut-in and there's no cut-out - if (Mark || LastMark) - error = "index"; + if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex)) break; - } - - // Write one frame: - - if (Independent) { // every file shall start with an independent frame - if (LastMark) // edited version shall end before next I-frame - break; - if (FileSize > maxVideoFileSize) { - toFile = toFileName->NextFile(); - if (!toFile) { - error = "toFile 1"; + if (!EndMark) + break; // reached EOF + LastEndIndex = EndIndex; + // Switch to the next sequence: + BeginMark = fromMarks.GetNextBegin(EndMark); + if (BeginMark) { + // Split edited files: + if (Setup.SplitEditedFiles) { + if (!SwitchFile(true)) break; - } - FileSize = 0; - } - LastIFrame = 0; - // Compare the current frame with the previously stored one, to see if this is a seamlessly merged recording of the same stream: - if (CheckForSeamlessStream) { - if (Length == Length2) { - int diffs = 0; - for (int i = 0; i < Length; i++) { - if (buffer[i] != buffer2[i]) { - if (diffs++ > 10) - break; - } - } - if (diffs < 10) // the continuity counters of the PAT/PMT packets may differ - cutIn = false; // it's apparently a seamless stream, so no need for "broken" handling - } - CheckForSeamlessStream = false; - } - if (cutIn) { - if (isPesRecording) - cRemux::SetBrokenLink(buffer, Length); - else - TsSetTeiOnBrokenPackets(buffer, Length); - cutIn = false; } } - if (toFile->Write(buffer, Length) < 0) { - error = "safe_write"; - break; - } - if (!toIndex->Write(Independent, toFileName->Number(), FileSize)) { - error = "toIndex"; - break; - } - FileSize += Length; - if (!LastIFrame) - LastIFrame = toIndex->Last(); - - // Check editing marks: - - if (Mark && Index >= Mark->Position()) { - Mark = fromMarks.Next(Mark); - toMarks.Add(LastIFrame); - if (Mark) - toMarks.Add(toIndex->Last() + 1); - toMarks.Save(); - if (Mark) { - // Read the next frame, for later comparison with the first frame at this mark: - if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length2)) { - if (FileNumber != CurrentFileNumber) - fromFile = fromFileName->SetOffset(FileNumber, FileOffset); - if (fromFile) { - int len = ReadFrame(fromFile, buffer2, Length2, sizeof(buffer2)); - if (len >= 0 && len == Length2) - CheckForSeamlessStream = true; - } - } - Index = Mark->Position(); - Mark = fromMarks.Next(Mark); - CurrentFileNumber = 0; // triggers SetOffset before reading next frame - cutIn = true; - if (Setup.SplitEditedFiles) { - toFile = toFileName->NextFile(); - if (!toFile) { - error = "toFile 2"; - break; - } - FileSize = 0; - } - } - else - LastMark = true; - } } Recordings.TouchUpdate(); } @@ -257,7 +575,7 @@ bool cCutter::Start(const char *FileName) cMarks FromMarks; FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()); - if (cMark *First = FromMarks.First()) + if (cMark *First = FromMarks.GetNextBegin()) Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60); const char *evn = Recording.PrefixFileName('%'); @@ -345,13 +663,17 @@ bool CutRecording(const char *FileName) if (Recording.Name()) { cMarks Marks; if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) { - if (cCutter::Start(FileName)) { - while (cCutter::Active()) - cCondWait::SleepMs(CUTTINGCHECKINTERVAL); - return true; + if (Marks.GetNumSequences()) { + if (cCutter::Start(FileName)) { + while (cCutter::Active()) + cCondWait::SleepMs(CUTTINGCHECKINTERVAL); + return true; + } + else + fprintf(stderr, "can't start editing process\n"); } else - fprintf(stderr, "can't start editing process\n"); + fprintf(stderr, "'%s' has no editing sequences\n", FileName); } else fprintf(stderr, "'%s' has no editing marks\n", FileName); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 2.68 2012/09/20 09:32:26 kls Exp $ + * $Id: device.c 2.69 2012/11/13 09:11:43 kls Exp $ */ #include "device.h" @@ -1147,7 +1147,7 @@ void cDevice::StillPicture(const uchar *Data, int Length) int Size = 0; while (Length >= TS_SIZE) { int Pid = TsPid(Data); - if (Pid == 0) + if (Pid == PATPID) patPmtParser.ParsePat(Data, TS_SIZE); else if (Pid == patPmtParser.PmtPid()) patPmtParser.ParsePmt(Data, TS_SIZE); @@ -1484,7 +1484,7 @@ int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly) if (TsHasPayload(Data)) { // silently ignore TS packets w/o payload int PayloadOffset = TsPayloadOffset(Data); if (PayloadOffset < TS_SIZE) { - if (Pid == 0) + if (Pid == PATPID) patPmtParser.ParsePat(Data, TS_SIZE); else if (Pid == patPmtParser.PmtPid()) patPmtParser.ParsePmt(Data, TS_SIZE); diff --git a/dvbdevice.c b/dvbdevice.c index 9320da6..d3ade75 100644 --- a/dvbdevice.c +++ b/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 2.72 2012/09/20 10:07:54 kls Exp $ + * $Id: dvbdevice.c 2.74 2012/10/07 11:11:30 kls Exp $ */ #include "dvbdevice.h" @@ -21,7 +21,9 @@ #include "menuitems.h" #include "sourceparams.h" -#define FE_CAN_TURBO_FEC 0x8000000 // TODO: remove this once it is defined in the driver +#if (DVB_API_VERSION << 8 | DVB_API_VERSION_MINOR) < 0x0508 +#define DTV_STREAM_ID DTV_DVBT2_PLP_ID +#endif #define DVBS_TUNE_TIMEOUT 9000 //ms #define DVBS_LOCK_TIMEOUT 2000 //ms @@ -408,7 +410,7 @@ cString cDvbTuner::GetBondingParams(const cChannel *Channel) const return diseqc->Commands(); } else { - bool ToneOff = Channel->Frequency() < (unsigned int)Setup.LnbSLOF; + bool ToneOff = Channel->Frequency() < Setup.LnbSLOF; bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R'; return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V'); } @@ -574,40 +576,52 @@ int cDvbTuner::GetSignalQuality(void) const return 3; return 4; } +#ifdef DEBUG_SIGNALQUALITY bool HasSnr = true; +#endif uint16_t Snr; while (1) { if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1) break; if (errno == EOPNOTSUPP) { Snr = 0xFFFF; +#ifdef DEBUG_SIGNALQUALITY HasSnr = false; +#endif break; } if (errno != EINTR) return -1; } +#ifdef DEBUG_SIGNALQUALITY bool HasBer = true; +#endif uint32_t Ber; while (1) { if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1) break; if (errno == EOPNOTSUPP) { Ber = 0; +#ifdef DEBUG_SIGNALQUALITY HasBer = false; +#endif break; } if (errno != EINTR) return -1; } +#ifdef DEBUG_SIGNALQUALITY bool HasUnc = true; +#endif uint32_t Unc; while (1) { if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1) break; if (errno == EOPNOTSUPP) { Unc = 0; +#ifdef DEBUG_SIGNALQUALITY HasUnc = false; +#endif break; } if (errno != EINTR) @@ -810,7 +824,7 @@ bool cDvbTuner::SetFrontend(void) SETCMD(DTV_HIERARCHY, dtp.Hierarchy()); if (frontendType == SYS_DVBT2) { // DVB-T2 - SETCMD(DTV_DVBT2_PLP_ID, dtp.PlpId()); + SETCMD(DTV_STREAM_ID, dtp.PlpId()); } tuneTimeout = DVBT_TUNE_TIMEOUT; @@ -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 2.21 2012/09/29 14:29:49 kls Exp $ + * $Id: epg.c 2.22 2012/10/04 12:21:24 kls Exp $ */ #include "epg.h" @@ -1148,14 +1148,12 @@ public: }; cEpgDataWriter::cEpgDataWriter(void) -:cThread("epg data writer") +:cThread("epg data writer", true) { } void cEpgDataWriter::Action(void) { - SetPriority(19); - SetIOPriority(7); Perform(); } @@ -6,7 +6,7 @@ * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * $Id: si.h 2.5 2012/01/11 11:35:17 kls Exp $ + * $Id: si.h 2.6 2012/10/15 11:56:06 kls Exp $ * * ***************************************************************************/ @@ -347,8 +347,10 @@ public: T *ret=new T(); ret->setData(d); ret->CheckParse(); - if (!checkSize(ret->getLength())) + if (!checkSize(ret->getLength())) { + delete ret; return 0; + } it.i+=ret->getLength(); return ret; } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 2.61 2012/09/15 11:45:28 kls Exp $ + * $Id: menu.c 2.65 2012/11/18 13:07:53 kls Exp $ */ #include "menu.h" @@ -3418,7 +3418,7 @@ bool cMenuMain::Update(bool Force) stopReplayItem = NULL; } // Color buttons: - SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : NULL); + SetHelp(!replaying ? tr("Button$Record") : NULL, tr("Button$Audio"), replaying ? NULL : tr("Button$Pause"), replaying ? tr("Button$Stop") : cReplayControl::LastReplayed() ? tr("Button$Resume") : tr("Button$Play")); result = true; } @@ -3516,7 +3516,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) state = replaying ? osContinue : osPause; break; case kBlue: if (!HadSubMenu) - state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue; + state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osRecordings; break; default: break; } @@ -4482,10 +4482,6 @@ cReplayControl::~cReplayControl() Hide(); cStatus::MsgReplaying(this, NULL, fileName, false); Stop(); - if (marksModified) { - marks.Save(); - marksModified = false; - } if (currentReplayControl == this) currentReplayControl = NULL; } @@ -4573,6 +4569,10 @@ void cReplayControl::Hide(void) timeSearchActive = false; timeoutShow = 0; } + if (marksModified) { + marks.Save(); + marksModified = false; + } } void cReplayControl::ShowMode(void) @@ -4771,12 +4771,12 @@ void cReplayControl::MarkMove(bool Forward) int p = SkipFrames(Forward ? 1 : -1); cMark *m2; if (Forward) { - if ((m2 = marks.Next(m)) != NULL && m2->Position() <= p) - return; + while ((m2 = marks.Next(m)) != NULL && m2->Position() == m->Position()) + m = m2; } else { - if ((m2 = marks.Prev(m)) != NULL && m2->Position() >= p) - return; + while ((m2 = marks.Prev(m)) != NULL && m2->Position() == m->Position()) + m = m2; } m->SetPosition(p); Goto(m->Position(), true); @@ -4789,13 +4789,11 @@ void cReplayControl::EditCut(void) { if (*fileName) { Hide(); - if (marksModified) { - marks.Save(); - marksModified = false; - } if (!cCutter::Active()) { if (!marks.Count()) Skins.Message(mtError, tr("No editing marks defined!")); + else if (!marks.GetNumSequences()) + Skins.Message(mtError, tr("No editing sequences defined!")); else if (!cCutter::Start(fileName)) Skins.Message(mtError, tr("Can't start editing process!")); else @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1241,6 +1241,9 @@ msgstr "Ĝ§ÙÙĜ² Ĝ§ÙÙ " msgid "No editing marks defined!" msgstr "ÙĜ§ĜŞÙĜĴĜŻ ĜıÙĜ§Ù
Ĝ§ĜŞ ĜŞĜıĜŻÙÙ Ù
ĜıĜħÙĜİ" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "ÙĜ§ ÙÙ
ÙÙ Ĝ§ÙĜ¨ĜŻĜĦ ÙÙ ĜıÙ
ÙÙĜİ Ĝ§ÙĜŞĜıĜŻÙÙ" diff --git a/po/ca_ES.po b/po/ca_ES.po index c67265b..f08cd57 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1216,6 +1216,9 @@ msgstr "Salta a:" msgid "No editing marks defined!" msgstr "No hi ha marques d'edició definides" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "No puc iniciar el procés d'edició!" diff --git a/po/cs_CZ.po b/po/cs_CZ.po index 55053f9..1a62102 100644 --- a/po/cs_CZ.po +++ b/po/cs_CZ.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.14\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2010-05-06 11:00+0200\n" "Last-Translator: Radek Ċ Ċastn½ <dedkus@gmail.com>\n" "Language-Team: Czech <vdr@linuxtv.org>\n" @@ -1215,6 +1215,9 @@ msgstr "Skok: " msgid "No editing marks defined!" msgstr "Nejsou definovĦny editaÄn znaÄky!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Nelze zaÄt editaÄn proces!" diff --git a/po/da_DK.po b/po/da_DK.po index fac5800..597539f 100644 --- a/po/da_DK.po +++ b/po/da_DK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1213,6 +1213,9 @@ msgstr "Hop: " msgid "No editing marks defined!" msgstr "Der er ikke sat nogen redigeringsmĉrker!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Kan ikke starte redigeringsprocessen!" diff --git a/po/de_DE.po b/po/de_DE.po index a321b50..c958758 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2010-01-16 16:46+0100\n" "Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n" "Language-Team: German <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Springen: " msgid "No editing marks defined!" msgstr "Keine Schnittmarken gesetzt!" +msgid "No editing sequences defined!" +msgstr "Keine Schnittsequenzen definiert!" + msgid "Can't start editing process!" msgstr "Schnitt kann nicht gestartet werden!" diff --git a/po/el_GR.po b/po/el_GR.po index 8936408..1347812 100644 --- a/po/el_GR.po +++ b/po/el_GR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1213,6 +1213,9 @@ msgstr "ÔïïèŬôçóç: " msgid "No editing marks defined!" msgstr "ÄŬí Ŭ÷ïġí ïñéóôċß óçìċßá ċċîċñáóßáò" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Áäġíáìßá ċêêßíçóçò ôçò ċċîċñáóßáò!" diff --git a/po/es_ES.po b/po/es_ES.po index bc80a11..8e76063 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-03-02 19:02+0100\n" "Last-Translator: Luca Olivetti <luca@ventoso.org>\n" "Language-Team: Spanish <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Saltar: " msgid "No editing marks defined!" msgstr "ĦNo se definieron marcas de edición!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "ĦNo se puede iniciar el proceso de edición!" diff --git a/po/et_EE.po b/po/et_EE.po index 0dea999..12c1f25 100644 --- a/po/et_EE.po +++ b/po/et_EE.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1213,6 +1213,9 @@ msgstr "Hĵpe: " msgid "No editing marks defined!" msgstr "Redigeerimise markerid puuduvad!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Redigeerimise start nurjus!" diff --git a/po/fi_FI.po b/po/fi_FI.po index daad22a..901328a 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-13 13:15+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2007-08-15 15:52+0200\n" "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n" "Language-Team: Finnish <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Siirry: " msgid "No editing marks defined!" msgstr "Muokkausmerkinn¤t puuttuvat!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Muokkauksen aloitus ep¤onnistui!" diff --git a/po/fr_FR.po b/po/fr_FR.po index 3ca3c06..a0995ae 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-02-27 18:14+0100\n" "Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n" "Language-Team: French <vdr@linuxtv.org>\n" @@ -1219,6 +1219,9 @@ msgstr "Accès direct : " msgid "No editing marks defined!" msgstr "Pas de marques d'édition définies !" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Impossible de commencer le montage !" diff --git a/po/hr_HR.po b/po/hr_HR.po index dcb1a21..44cfd09 100644 --- a/po/hr_HR.po +++ b/po/hr_HR.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1215,6 +1215,9 @@ msgstr "Skoèi: " msgid "No editing marks defined!" msgstr "Nijedna toèka rezanja nije odreena!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Ne mogu zapoèeti ureivanje!" diff --git a/po/hu_HU.po b/po/hu_HU.po index c391b67..30ab8ab 100644 --- a/po/hu_HU.po +++ b/po/hu_HU.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2012-01-02 11:54+0200\n" "Last-Translator: István Füley <ifuley@tigercomp.ro>\n" "Language-Team: Hungarian <vdr@linuxtv.org>\n" @@ -1217,6 +1217,9 @@ msgstr "Ugrás ide: " msgid "No editing marks defined!" msgstr "Nincs vágópont kijelölve" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "A vágás nem indítható!" diff --git a/po/it_IT.po b/po/it_IT.po index b63d93d..1258f23 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2012-06-06 22:50+0100\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Language-Team: Italian <vdr@linuxtv.org>\n" @@ -1220,6 +1220,9 @@ msgstr "Vai a: " msgid "No editing marks defined!" msgstr "Nessun marcatore di modifica definito!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Impossibile avviare il processo di modifica!" diff --git a/po/lt_LT.po b/po/lt_LT.po index 95f8e83..b53c762 100644 --- a/po/lt_LT.po +++ b/po/lt_LT.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.16\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2010-10-30 11:55+0200\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Language-Team: Lithuanian <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "PerĊĦokti: " msgid "No editing marks defined!" msgstr "Nenustatytos koregavimo ĊymÄs!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Negali pradÄti koregavimo!" diff --git a/po/mk_MK.po b/po/mk_MK.po index fd16ec8..89e8b73 100644 --- a/po/mk_MK.po +++ b/po/mk_MK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR-1.7.14\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2010-03-11 00:54+0100\n" "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n" "Language-Team: Macedonian <en@li.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Ħşş½¸:" msgid "No editing marks defined!" msgstr "µĵ° ´Ñµ´µ½ ·½°ş¸ ·° ѵѵѵ!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "µ ĵĥµ ´° żÑ½µ Ñѵ´Ñ²°Ñµ!" diff --git a/po/nl_NL.po b/po/nl_NL.po index 6c3825d..5e515c3 100644 --- a/po/nl_NL.po +++ b/po/nl_NL.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-02-26 17:20+0100\n" "Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n" "Language-Team: Dutch <vdr@linuxtv.org>\n" @@ -1217,6 +1217,9 @@ msgstr "Springen: " msgid "No editing marks defined!" msgstr "Geen bewerkingsmarkeringen gedefinieerd!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Kan niet beginnen met bewerken!" diff --git a/po/nn_NO.po b/po/nn_NO.po index 41ce5c3..f366260 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1214,6 +1214,9 @@ msgstr "Hopp: " msgid "No editing marks defined!" msgstr "" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Kan ikke starte redigeringsprosessen!" diff --git a/po/pl_PL.po b/po/pl_PL.po index f4c1876..fbe8a86 100644 --- a/po/pl_PL.po +++ b/po/pl_PL.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-03-09 12:59+0100\n" "Last-Translator: Michael Rakowski <mrak@gmx.de>\n" "Language-Team: Polish <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Skok: " msgid "No editing marks defined!" msgstr "Nie zdefiniowano znaczników montażu!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Nie można uruchomiĉ procesu edycji!" diff --git a/po/pt_PT.po b/po/pt_PT.po index 5cfc203..d69e069 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.15\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1214,6 +1214,9 @@ msgstr "Saltar: " msgid "No editing marks defined!" msgstr "Marcas de ediço no foram definidas!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Impossível iniciar processo de ediço!" diff --git a/po/ro_RO.po b/po/ro_RO.po index cd638dc..03ae8f0 100644 --- a/po/ro_RO.po +++ b/po/ro_RO.po @@ -2,19 +2,19 @@ # Copyright (C) 2008 Klaus Schmidinger <kls@tvdr.de> # This file is distributed under the same license as the VDR package. # Paul Lacatus <paul@campina.iiruc.ro>, 2002 -# Lucian Muresan <lucianm@users.sourceforge.net>, 2004, 2005, 2006, 2008, 2010, 2011 +# Lucian Muresan <lucianm@users.sourceforge.net>, 2004, 2005, 2006, 2008, 2010, 2011, 2012 # msgid "" msgstr "" "Project-Id-Version: VDR 1.7.12\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" -"PO-Revision-Date: 2011-03-10 23:52+0100\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" +"PO-Revision-Date: 2012-11-05 01:28+0100\n" "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n" "Language-Team: Romanian <vdr@linuxtv.org>\n" "Language: ro\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Poedit-Language: Romanian\n" "X-Poedit-Country: ROMANIA\n" @@ -47,7 +47,7 @@ msgid "System" msgstr "Sistem" msgid "Srate" -msgstr "Rat simboluri" +msgstr "RatÄ simboluri" msgid "Inversion" msgstr "Inversiune" @@ -59,10 +59,10 @@ msgid "CoderateL" msgstr "CoderateL" msgid "Modulation" -msgstr "Modulaŝie" +msgstr "ModulaĊ£ie" msgid "Bandwidth" -msgstr "Lrgime de band" +msgstr "LÄrgime de bandÄ" msgid "Transmission" msgstr "Transmisie" @@ -77,52 +77,52 @@ msgid "Rolloff" msgstr "Rolloff" msgid "PlpId" -msgstr "" +msgstr "Identificator PLP" msgid "Starting EPG scan" -msgstr "Pornesc achiziŝia EPG" +msgstr "Pornesc achiziĊ£ia EPG" msgid "Content$Movie/Drama" -msgstr "Film/Dram" +msgstr "Film/DramÄ" msgid "Content$Detective/Thriller" -msgstr "Poliŝist/Suspense" +msgstr "PoliĊ£ist/Suspense" msgid "Content$Adventure/Western/War" -msgstr "Aventuri/Western/Rzboi" +msgstr "Aventuri/Western/RÄzboi" msgid "Content$Science Fiction/Fantasy/Horror" -msgstr "Science-Fiction/Fantastic/Groaz" +msgstr "Science-Fiction/Fantastic/GroazÄ" msgid "Content$Comedy" msgstr "Comedie" msgid "Content$Soap/Melodrama/Folkloric" -msgstr "Telenovel/Melodram/Folclor" +msgstr "TelenovelÄ/MelodramÄ/Folclor" msgid "Content$Romance" msgstr "Film romantic" msgid "Content$Serious/Classical/Religious/Historical Movie/Drama" -msgstr "Film serios/clasic/religios/istoric/Dram" +msgstr "Film serios/clasic/religios/istoric/DramÄ" msgid "Content$Adult Movie/Drama" -msgstr "Film pentru adulŝi/Dram" +msgstr "Film pentru adulĊ£i/DramÄ" msgid "Content$News/Current Affairs" -msgstr "Ştiri/Actualitŝi" +msgstr "Ċtiri/ActualitÄĊ£i" msgid "Content$News/Weather Report" -msgstr "Ştiri/Buletin meteorologic" +msgstr "Ċtiri/Buletin meteorologic" msgid "Content$News Magazine" -msgstr "Magazin de ştiri" +msgstr "Magazin de Ċtiri" msgid "Content$Documentary" msgstr "Documentar" msgid "Content$Discussion/Inverview/Debate" -msgstr "Discuŝie/Interviu/Dezbatere" +msgstr "DiscuĊ£ie/Interviu/Dezbatere" msgid "Content$Show/Game Show" msgstr "Show/Jocuri" @@ -131,7 +131,7 @@ msgid "Content$Game Show/Quiz/Contest" msgstr "Emisiune jocuri/quiz/concurs" msgid "Content$Variety Show" -msgstr "Varietŝi" +msgstr "VarietÄĊ£i" msgid "Content$Talk Show" msgstr "Talk Show" @@ -152,7 +152,7 @@ msgid "Content$Tennis/Squash" msgstr "Tenis/Squash" msgid "Content$Team Sports" -msgstr "Sporturi de echip" +msgstr "Sporturi de echipÄ" msgid "Content$Athletics" msgstr "Atletism" @@ -164,19 +164,19 @@ msgid "Content$Water Sport" msgstr "Sporturi acvatice" msgid "Content$Winter Sports" -msgstr "Sporturi de iarn" +msgstr "Sporturi de iarnÄ" msgid "Content$Equestrian" -msgstr "Echitaŝie" +msgstr "EchitaĊ£ie" msgid "Content$Martial Sports" -msgstr "Arte marŝiale" +msgstr "Arte marĊ£iale" msgid "Content$Children's/Youth Programme" msgstr "Emisiune pentru copii/tineret" msgid "Content$Pre-school Children's Programme" -msgstr "Emisiune pentru copii preşcolari" +msgstr "Emisiune pentru copii preĊcolari" msgid "Content$Entertainment Programme for 6 to 14" msgstr "Divertisment pentru copii intre 6 - 16 ani" @@ -185,34 +185,34 @@ msgid "Content$Entertainment Programme for 10 to 16" msgstr "Divertisment pentru copii intre 10 - 16 ani" msgid "Content$Informational/Educational/School Programme" -msgstr "Informaŝional/Educaŝional/Emisiune şcoal" +msgstr "InformaĊ£ional/EducaĊ£ional/Emisiune ĊcoalÄ" msgid "Content$Cartoons/Puppets" -msgstr "Desene animate/Teatru de ppuşi" +msgstr "Desene animate/Teatru de pÄpuĊi" msgid "Content$Music/Ballet/Dance" -msgstr "Muzic/Balet/Dans" +msgstr "MuzicÄ/Balet/Dans" msgid "Content$Rock/Pop" msgstr "Rock/Pop" msgid "Content$Serious/Classical Music" -msgstr "Muzic clasic/serioas" +msgstr "MuzicÄ clasicÄ/serioasÄ" msgid "Content$Folk/Tradional Music" -msgstr "Muzic folk/tradiŝional" +msgstr "MuzicÄ folk/tradiĊ£ionalÄ" msgid "Content$Jazz" msgstr "Jazz" msgid "Content$Musical/Opera" -msgstr "Teatru muzical/Oper/Operet" +msgstr "Teatru muzical/OperÄ/OperetÄ" msgid "Content$Ballet" msgstr "Balet" msgid "Content$Arts/Culture" -msgstr "Art/Cultur" +msgstr "ArtÄ/CulturÄ" msgid "Content$Performing Arts" msgstr "Spectacole" @@ -224,10 +224,10 @@ msgid "Content$Religion" msgstr "Religie" msgid "Content$Popular Culture/Traditional Arts" -msgstr "Cultur Pop/Arte tradiŝionale" +msgstr "CulturÄ Pop/Arte tradiĊ£ionale" msgid "Content$Literature" -msgstr "Literatur" +msgstr "LiteraturÄ" msgid "Content$Film/Cinema" msgstr "Film/Cinema" @@ -236,16 +236,16 @@ msgid "Content$Experimental Film/Video" msgstr "Film experimental/Video" msgid "Content$Broadcasting/Press" -msgstr "Radiodifuziune/Pres" +msgstr "Radiodifuziune/PresÄ" msgid "Content$New Media" msgstr "Medii noi" msgid "Content$Arts/Culture Magazine" -msgstr "Art/Magazin cultural" +msgstr "ArtÄ/Magazin cultural" msgid "Content$Fashion" -msgstr "Mod" +msgstr "ModÄ" msgid "Content$Social/Political/Economics" msgstr "Social/Politic/Economie" @@ -254,28 +254,28 @@ msgid "Content$Magazine/Report/Documentary" msgstr "Magazin/Reportaj/Documentar" msgid "Content$Economics/Social Advisory" -msgstr "Economie/Consiliere social" +msgstr "Economie/Consiliere socialÄ" msgid "Content$Remarkable People" msgstr "Oameni remarcabili" msgid "Content$Education/Science/Factual" -msgstr "Educaŝie/Ştiinŝ/Practic" +msgstr "EducaĊ£ie/ĊtiinĊ£Ä/Practic" msgid "Content$Nature/Animals/Environment" -msgstr "Natur/Animale/Mediu" +msgstr "NaturÄ/Animale/Mediu" msgid "Content$Technology/Natural Sciences" -msgstr "Tehnologie/Ştiinŝe naturale" +msgstr "Tehnologie/ĊtiinĊ£e naturale" msgid "Content$Medicine/Physiology/Psychology" -msgstr "Medicin/Fiziologie/Psihologie" +msgstr "MedicinÄ/Fiziologie/Psihologie" msgid "Content$Foreign Countries/Expeditions" -msgstr "Ŝri strine/Expediŝii" +msgstr "Ċ˘Äri strÄine/ExpediĊ£ii" msgid "Content$Social/Spiritual Sciences" -msgstr "Social/Ştiinŝe spirituale" +msgstr "Social/ĊtiinĊ£e spirituale" msgid "Content$Further Education" msgstr "Cursuri de aprofundare" @@ -287,28 +287,28 @@ msgid "Content$Leisure/Hobbies" msgstr "Timp liber/Hobby" msgid "Content$Tourism/Travel" -msgstr "Turism/Cltorie" +msgstr "Turism/CÄlÄtorie" msgid "Content$Handicraft" -msgstr "Meşteşug" +msgstr "MeĊteĊug" msgid "Content$Motoring" msgstr "Motoare" msgid "Content$Fitness & Health" -msgstr "Mişcare & Sntate" +msgstr "MiĊcare & SÄnÄtate" msgid "Content$Cooking" -msgstr "Gtit" +msgstr "GÄtit" msgid "Content$Advertisement/Shopping" -msgstr "Publicitate/Cumprturi" +msgstr "Publicitate/CumpÄrÄturi" msgid "Content$Gardening" -msgstr "Grdinrit" +msgstr "GrÄdinÄrit" msgid "Content$Original Language" -msgstr "Limba original" +msgstr "Limba originalÄ" msgid "Content$Black & White" msgstr "Alb-Negru" @@ -317,65 +317,65 @@ msgid "Content$Unpublished" msgstr "Nepublicat" msgid "Content$Live Broadcast" -msgstr "Transmisie în direct" +msgstr "Transmisie n direct" #, c-format msgid "ParentalRating$from %d" msgstr "de la %d ani" msgid "No title" -msgstr "Fr titlu" +msgstr "FÄrÄ titlu" #. TRANSLATORS: The name of the language, as written natively msgid "LanguageName$English" -msgstr "Român" +msgstr "Rom˘nÄ" #. TRANSLATORS: The 3-letter code of the language msgid "LanguageCode$eng" msgstr "rom" msgid "Phase 1: Detecting RC code type" -msgstr "Faza 1: Detecŝia tipului telecomenzii" +msgstr "Faza 1: DetecĊ£ia tipului telecomenzii" msgid "Press any key on the RC unit" -msgstr "Apsaŝi o tast pe telecomand" +msgstr "ApÄsaĊ£i o tastÄ pe telecomandÄ" msgid "RC code detected!" msgstr "S-a detectat tipul telecomenzii!" msgid "Do not press any key..." -msgstr "Nu apsaŝi nici o tast..." +msgstr "Nu apÄsaĊ£i nici o tastÄ..." msgid "Phase 2: Learning specific key codes" -msgstr "Faza 2: Învŝarea codurilor anumitor taste" +msgstr "Faza 2: nvÄĊ£area codurilor anumitor taste" #, c-format msgid "Press key for '%s'" -msgstr "Apsaŝi tasta pentru '%s'" +msgstr "ApÄsaĊ£i tasta pentru '%s'" msgid "Press 'Up' to confirm" -msgstr "Apsaŝi 'Sus' pentru confirmare" +msgstr "ApÄsaĊ£i 'Sus' pentru confirmare" msgid "Press 'Down' to continue" -msgstr "Apsaŝi 'Jos' pentru continuare" +msgstr "ApÄsaĊ£i 'Jos' pentru continuare" msgid "(press 'Up' to go back)" -msgstr "(Apsaŝi 'Sus' pentru revenire)" +msgstr "(ApÄsaĊ£i 'Sus' pentru revenire)" msgid "(press 'Down' to end key definition)" -msgstr "(Apsaŝi 'Jos' pentru terminare)" +msgstr "(ApÄsaĊ£i 'Jos' pentru terminare)" msgid "(press 'Menu' to skip this key)" -msgstr "Apsaŝi 'Meniu' pentru a sri peste aceast tast" +msgstr "ApÄsaĊ£i 'Meniu' pentru a sÄri peste aceastÄ tastÄ" msgid "Learning Remote Control Keys" -msgstr "Învŝare taste telecomand" +msgstr "nvÄĊ£are taste telecomandÄ" msgid "Phase 3: Saving key codes" msgstr "Faza 3: Salvarea codurilor de taste" msgid "Press 'Up' to save, 'Down' to cancel" -msgstr "Apsaŝi 'Sus' pentru salvare, 'Jos' pentru anulare" +msgstr "ApÄsaĊ£i 'Sus' pentru salvare, 'Jos' pentru anulare" msgid "Key$Up" msgstr "Sus" @@ -390,16 +390,16 @@ msgid "Key$Ok" msgstr "OK" msgid "Key$Back" -msgstr "Înapoi" +msgstr "napoi" msgid "Key$Left" -msgstr "Stânga" +msgstr "St˘nga" msgid "Key$Right" msgstr "Dreapta" msgid "Key$Red" -msgstr "Roşu" +msgstr "RoĊu" msgid "Key$Green" msgstr "Verde" @@ -417,28 +417,28 @@ msgid "Key$Play" msgstr "Redare" msgid "Key$Pause" -msgstr "Pauz" +msgstr "PauzÄ" msgid "Key$Stop" msgstr "Stop" msgid "Key$Record" -msgstr "Înregistrare" +msgstr "nregistrare" msgid "Key$FastFwd" -msgstr "Derulare înainte" +msgstr "Derulare nainte" msgid "Key$FastRew" -msgstr "Derulare înapoi" +msgstr "Derulare napoi" msgid "Key$Next" -msgstr "Urmtor" +msgstr "UrmÄtor" msgid "Key$Prev" msgstr "Anterior" msgid "Key$Power" -msgstr "Închidere" +msgstr "nchidere" msgid "Key$Channel+" msgstr "Canal+" @@ -456,7 +456,7 @@ msgid "Key$Volume-" msgstr "Volum-" msgid "Key$Mute" -msgstr "Fr sunet" +msgstr "FÄrÄ sunet" msgid "Key$Audio" msgstr "Sunet" @@ -474,10 +474,10 @@ msgid "Key$Timers" msgstr "Timer-e" msgid "Key$Recordings" -msgstr "Înregistrri" +msgstr "nregistrÄri" msgid "Key$Setup" -msgstr "Configuraŝie" +msgstr "ConfiguraĊ£ie" msgid "Key$Commands" msgstr "Comenzi" @@ -525,10 +525,10 @@ msgid "Name" msgstr "Nume" msgid "Source" -msgstr "Surs" +msgstr "SursÄ" msgid "Frequency" -msgstr "Frecvenŝ" +msgstr "FrecvenĊ£Ä" msgid "Vpid" msgstr "PID Video" @@ -558,7 +558,7 @@ msgid "Tpid" msgstr "PID Teletext" msgid "CA" -msgstr "CA (Acces Condiŝional)" +msgstr "CA (Acces CondiĊ£ional)" msgid "Sid" msgstr "Sid" @@ -570,25 +570,25 @@ msgid "Channels" msgstr "Canale" msgid "Button$Edit" -msgstr "Modific" +msgstr "ModificÄ" msgid "Button$New" msgstr "Nou" msgid "Button$Delete" -msgstr "Şterge" +msgstr "Ċterge" msgid "Button$Mark" -msgstr "Marcheaz" +msgstr "MarcheazÄ" msgid "Channel is being used by a timer!" msgstr "Canalul este utilizat de un timer!" msgid "Delete channel?" -msgstr "Şterg canalul?" +msgstr "Ċterg canalul?" msgid "Edit folder" -msgstr "Editeaz directorul" +msgstr "EditeazÄ directorul" msgid "New folder" msgstr "Director nou" @@ -597,20 +597,20 @@ msgid "Sub folder" msgstr "Sub-director" msgid "Folder name already exists!" -msgstr "Un director cu acelaşi nume exist!" +msgstr "Un director cu acelaĊi nume existÄ!" #, c-format msgid "Folder name must not contain '%c'!" -msgstr "Numele directorului nu poate s conŝin '%c'!" +msgstr "Numele directorului nu poate sÄ conĊ£inÄ '%c'!" msgid "Button$Select" -msgstr "Selecteaz" +msgstr "SelecteazÄ" msgid "Delete folder and all sub folders?" -msgstr "Şterg directorul şi toate sub-directoarele?" +msgstr "Ċterg directorul Ċi toate sub-directoarele?" msgid "Delete folder?" -msgstr "Şterg directorul?" +msgstr "Ċterg directorul?" msgid "Edit timer" msgstr "Modificare timer" @@ -625,10 +625,10 @@ msgid "Day" msgstr "Ziua" msgid "Start" -msgstr "Început" +msgstr "nceput" msgid "Stop" -msgstr "Sfârşit" +msgstr "Sf˘rĊit" msgid "VPS" msgstr "VPS" @@ -637,25 +637,25 @@ msgid "Priority" msgstr "Prioritate" msgid "Lifetime" -msgstr "Timp de pstrare" +msgstr "Timp de pÄstrare" msgid "File" -msgstr "Fişier" +msgstr "FiĊier" msgid "Button$Folder" msgstr "Director" msgid "Button$Single" -msgstr "" +msgstr "OdatÄ" msgid "Button$Repeating" -msgstr "" +msgstr "Repetitiv" msgid "First day" msgstr "Prima zi" msgid "Select folder" -msgstr "Selecteaz directorul" +msgstr "SelecteazÄ directorul" msgid "Timers" msgstr "Timer-e" @@ -667,10 +667,10 @@ msgid "Button$Info" msgstr "Info" msgid "Delete timer?" -msgstr "Şterg timer-ul?" +msgstr "Ċterg timer-ul?" msgid "Timer still recording - really delete?" -msgstr "Timer-ul tocmai înregistreaz - şterg, totuşi?" +msgstr "Timer-ul tocmai nregistreazÄ - Ċterg, totuĊi?" msgid "Event" msgstr "Emisiune" @@ -679,19 +679,19 @@ msgid "Button$Timer" msgstr "Timer" msgid "Button$Record" -msgstr "Înregistr." +msgstr "nregistr." msgid "Button$Switch" -msgstr "Comut" +msgstr "ComutÄ" msgid "What's on now?" msgstr "Ce emisiuni sunt acum?" msgid "What's on next?" -msgstr "Ce emisiuni urmeaz?" +msgstr "Ce emisiuni urmeazÄ?" msgid "Button$Next" -msgstr "Urmtor" +msgstr "UrmÄtor" msgid "Button$Now" msgstr "Acum" @@ -708,32 +708,32 @@ msgstr "Programul canalului %s" #, c-format msgid "This event - %s" -msgstr "Aceast emisiune - %s" +msgstr "AceastÄ emisiune - %s" msgid "This event - all channels" -msgstr "Aceast emisiune - toate canalele" +msgstr "AceastÄ emisiune - toate canalele" msgid "All events - all channels" msgstr "Toate emisiunile - toate canalele" #, c-format msgid "Please enter %d digits!" -msgstr "V rog introduceŝi %d cifre!" +msgstr "VÄ rog introduceĊ£i %d cifre!" msgid "CAM not responding!" -msgstr "CAM-ul nu reacŝioneaz!" +msgstr "CAM-ul nu reacĊ£ioneazÄ!" msgid "Recording info" -msgstr "Detaliile înregistrrii" +msgstr "Detaliile nregistrÄrii" msgid "Button$Play" msgstr "Redare" msgid "Button$Rewind" -msgstr "Înapoi" +msgstr "napoi" msgid "Recordings" -msgstr "Înregistrri" +msgstr "nregistrÄri" msgid "Button$Open" msgstr "Deschide" @@ -742,28 +742,28 @@ msgid "Commands" msgstr "Comenzi" msgid "Error while accessing recording!" -msgstr "Eroare la accesarea înregistrrii" +msgstr "Eroare la accesarea nregistrÄrii" msgid "Delete recording?" -msgstr "Şterg înregistrarea?" +msgstr "Ċterg nregistrarea?" msgid "Recording is being edited - really delete?" -msgstr "" +msgstr "Montajul nregistrÄrii e n curs de desfÄÈurare - Èterg totuÈi?" msgid "Error while deleting recording!" -msgstr "Eroare la ştergerea înregistrrii!" +msgstr "Eroare la Ċtergerea nregistrÄrii!" msgid "Recording commands" -msgstr "Comenzi pentru înregistrri" +msgstr "Comenzi pentru nregistrÄri" msgid "never" -msgstr "niciodat" +msgstr "niciodatÄ" msgid "skin dependent" msgstr "dep. de skin" msgid "always" -msgstr "întotdeauna" +msgstr "ntotdeauna" msgid "OSD" msgstr "OSD" @@ -775,22 +775,22 @@ msgid "Setup.OSD$Skin" msgstr "Skin" msgid "Setup.OSD$Theme" -msgstr "Tem" +msgstr "TemÄ" msgid "Setup.OSD$Left (%)" -msgstr "Stânga (%)" +msgstr "St˘nga (%)" msgid "Setup.OSD$Top (%)" msgstr "Sus (%)" msgid "Setup.OSD$Width (%)" -msgstr "Lŝime OSD (%)" +msgstr "LÄĊ£ime OSD (%)" msgid "Setup.OSD$Height (%)" -msgstr "Înlŝime OSD (%)" +msgstr "nÄlĊ£ime OSD (%)" msgid "Setup.OSD$Message time (s)" -msgstr "Timp afişare mesaje (sec)" +msgstr "Timp afiĊare mesaje (sec)" msgid "Setup.OSD$Use small font" msgstr "Utilizare fonturi mici" @@ -805,19 +805,19 @@ msgid "Setup.OSD$Small font" msgstr "Font mic" msgid "Setup.OSD$Fixed font" -msgstr "Font cu lŝime fix" +msgstr "Font cu lÄĊ£ime fixÄ" msgid "Setup.OSD$Default font size (%)" -msgstr "Mrimea implicit a fontului (%)" +msgstr "MÄrimea implicitÄ a fontului (%)" msgid "Setup.OSD$Small font size (%)" -msgstr "Mrimea 'mic' a fontului (%)" +msgstr "MÄrimea 'micÄ' a fontului (%)" msgid "Setup.OSD$Fixed font size (%)" -msgstr "Mrimea 'fix' a fontului (%)" +msgstr "MÄrimea 'fixÄ' a fontului (%)" msgid "Setup.OSD$Channel info position" -msgstr "Poziŝia informaŝiilor despre canal" +msgstr "PoziĊ£ia informaĊ£iilor despre canal" msgid "bottom" msgstr "jos" @@ -826,61 +826,61 @@ msgid "top" msgstr "sus" msgid "Setup.OSD$Channel info time (s)" -msgstr "Durata afişrii info-canal (s)" +msgstr "Durata afiĊÄrii info-canal (s)" msgid "Setup.OSD$Info on channel switch" -msgstr "Informaŝii la comutarea canalului" +msgstr "InformaĊ£ii la comutarea canalului" msgid "Setup.OSD$Timeout requested channel info" -msgstr "Durata afişrii informaŝii canal" +msgstr "Durata afiĊÄrii informaĊ£ii canal" msgid "Setup.OSD$Scroll pages" -msgstr "Deruleaz pagini" +msgstr "DeruleazÄ pagini" msgid "Setup.OSD$Scroll wraps" -msgstr "Derulare circular" +msgstr "Derulare circularÄ" msgid "Setup.OSD$Menu key closes" -msgstr "Tasta 'Meniu' închide" +msgstr "Tasta 'Meniu' nchide" msgid "Setup.OSD$Recording directories" -msgstr "Directoare înregistrri" +msgstr "Directoare nregistrÄri" msgid "Setup.OSD$Folders in timer menu" -msgstr "Directoare în meniul de timer-e" +msgstr "Directoare n meniul de timer-e" msgid "Setup.OSD$Number keys for characters" msgstr "Caractere pe tastele numerice" msgid "Setup.OSD$Color key 0" -msgstr "" +msgstr "Primul buton colorat" msgid "Setup.OSD$Color key 1" -msgstr "" +msgstr "Al 2-lea buton colorat" msgid "Setup.OSD$Color key 2" -msgstr "" +msgstr "Al 3-lea buton colorat" msgid "Setup.OSD$Color key 3" -msgstr "" +msgstr "Al 4-lea buton colorat" msgid "EPG" msgstr "EPG" msgid "Button$Scan" -msgstr "Cutare canale" +msgstr "CÄutare canale" msgid "Setup.EPG$EPG scan timeout (h)" -msgstr "Interval achiziŝie EPG (h)" +msgstr "Interval achiziĊ£ie EPG (h)" msgid "Setup.EPG$EPG bugfix level" -msgstr "Nivel corecŝie EPG" +msgstr "Nivel corecĊ£ie EPG" msgid "Setup.EPG$EPG linger time (min)" msgstr "Date EPG expirate cel mult (min)" msgid "Setup.EPG$Set system time" -msgstr "Potriveşte ceasul sistem" +msgstr "PotriveĊte ceasul sistem" msgid "Setup.EPG$Use time from transponder" msgstr "Preia ora din transponder" @@ -891,7 +891,7 @@ msgstr "Limbi preferate" #. TRANSLATORS: note the singular! msgid "Setup.EPG$Preferred language" -msgstr "Limba preferat" +msgstr "Limba preferatÄ" msgid "pan&scan" msgstr "pan&scan" @@ -915,10 +915,10 @@ msgid "names and PIDs" msgstr "nume si PID-uri" msgid "add new channels" -msgstr "adugare canale noi" +msgstr "adÄugare canale noi" msgid "add new transponders" -msgstr "adugare transpondere noi" +msgstr "adÄugare transpondere noi" msgid "DVB" msgstr "Dispozitiv DVB" @@ -930,16 +930,16 @@ msgid "Button$Subtitles" msgstr "Subtitrare" msgid "Setup.DVB$Primary DVB interface" -msgstr "Dispozitiv DVB primar" +msgstr "InterfaÈÄ DVB primarÄ" msgid "Setup.DVB$Standard compliance" -msgstr "" +msgstr "Standard de recepÈie" msgid "Setup.DVB$Video format" msgstr "Format video" msgid "Setup.DVB$Video display format" -msgstr "Formatul redrii video" +msgstr "Formatul redÄrii video" msgid "Setup.DVB$Use Dolby Digital" msgstr "Sunet Dolby Digital" @@ -954,22 +954,22 @@ msgid "Setup.DVB$Audio language" msgstr "Limba sunetului" msgid "Setup.DVB$Display subtitles" -msgstr "Afişeaz subtitrare" +msgstr "AfiĊeazÄ subtitrare" msgid "Setup.DVB$Subtitle languages" msgstr "Limbi subtitrare" msgid "Setup.DVB$Subtitle language" -msgstr "Limb subtitrare" +msgstr "LimbÄ subtitrare" msgid "Setup.DVB$Subtitle offset" msgstr "Offset subtitrare" msgid "Setup.DVB$Subtitle foreground transparency" -msgstr "Transparenŝa prim-planului subtitrrii" +msgstr "TransparenĊ£a prim-planului subtitrÄrii" msgid "Setup.DVB$Subtitle background transparency" -msgstr "Transparenŝa fundalului subtitrrii" +msgstr "TransparenĊ£a fundalului subtitrÄrii" msgid "LNB" msgstr "LNB" @@ -978,20 +978,20 @@ msgid "Setup.LNB$Use DiSEqC" msgstr "Utilizare DiSEqC" msgid "Setup.LNB$SLOF (MHz)" -msgstr "Frecvenŝ comutare band, SLOF (MHz)" +msgstr "FrecvenĊ£Ä comutare bandÄ, SLOF (MHz)" msgid "Setup.LNB$Low LNB frequency (MHz)" -msgstr "Frecvnŝ LNB inferioar (Mhz)" +msgstr "FrecvnĊ£Ä LNB inferioarÄ (Mhz)" msgid "Setup.LNB$High LNB frequency (MHz)" -msgstr "Frecvnŝ LNB superioar (MHz)" +msgstr "FrecvnĊ£Ä LNB superioarÄ (MHz)" #, c-format msgid "Setup.LNB$Device %d connected to sat cable" -msgstr "" +msgstr "Receptorul %d conectat la cablul de satelit" msgid "Setup.LNB$own" -msgstr "" +msgstr "propriu" msgid "CAM reset" msgstr "Resetare CAM" @@ -1000,7 +1000,7 @@ msgid "CAM present" msgstr "CAM prezent" msgid "CAM ready" -msgstr "CAM pregtit" +msgstr "CAM pregÄtit" msgid "CAM" msgstr "CAM" @@ -1018,19 +1018,19 @@ msgid "Can't open CAM menu!" msgstr "Nu pot deschide meniul CAM" msgid "CAM is in use - really reset?" -msgstr "CAM-ul este in folosinŝ - totuşi resetez?" +msgstr "CAM-ul este in folosinĊ£Ä - totuĊi resetez?" msgid "Can't reset CAM!" msgstr "Nu pot reseta CAM" msgid "do not pause live video" -msgstr "nu înregistra emisiunea" +msgstr "nu nregistra emisiunea" msgid "confirm pause live video" -msgstr "confirm înregistrarea emisiunii" +msgstr "confirmÄ nregistrarea emisiunii" msgid "pause live video" -msgstr "înregistreaz emisiunea" +msgstr "nregistreazÄ emisiunea" msgid "confirm" msgstr "confirmare" @@ -1039,70 +1039,70 @@ msgid "yes" msgstr "da" msgid "Recording" -msgstr "Înregistrare" +msgstr "nregistrare" msgid "Setup.Recording$Margin at start (min)" -msgstr "Marj la pornire (min)" +msgstr "MarjÄ la pornire (min)" msgid "Setup.Recording$Margin at stop (min)" -msgstr "Marj la oprire (min)" +msgstr "MarjÄ la oprire (min)" msgid "Setup.Recording$Default priority" -msgstr "Prioritate implicit" +msgstr "Prioritate implicitÄ" msgid "Setup.Recording$Default lifetime (d)" -msgstr "Timp de pstrare predefinit (zile)" +msgstr "Timp de pÄstrare predefinit (zile)" msgid "Setup.Recording$Pause key handling" -msgstr "Funcŝia tastei 'pauz'" +msgstr "FuncĊ£ia tastei 'pauzÄ'" msgid "Setup.Recording$Pause priority" -msgstr "Prioritate pauz" +msgstr "Prioritate pauzÄ" msgid "Setup.Recording$Pause lifetime (d)" -msgstr "Pstrarea emisiunilor 'pauzate' (zile)" +msgstr "PÄstrarea emisiunilor 'pauzate' (zile)" msgid "Setup.Recording$Use episode name" -msgstr "Utilizeaz numele episodului" +msgstr "UtilizeazÄ numele episodului" msgid "Setup.Recording$Use VPS" -msgstr "Utilizeaz VPS" +msgstr "UtilizeazÄ VPS" msgid "Setup.Recording$VPS margin (s)" -msgstr "Marj de timp la utilizare VPS (s)" +msgstr "MarjÄ de timp la utilizare VPS (s)" msgid "Setup.Recording$Mark instant recording" -msgstr "Marcheaz înregistrare imediat" +msgstr "MarcheazÄ nregistrare imediatÄ" msgid "Setup.Recording$Name instant recording" -msgstr "Nume înregistrare imediat" +msgstr "Nume nregistrare imediatÄ" msgid "Setup.Recording$Instant rec. time (min)" -msgstr "Timpul de înregistare imediat (min)" +msgstr "Timpul de nregistare imediatÄ (min)" msgid "Setup.Recording$present event" -msgstr "" +msgstr "eveniment curent" msgid "Setup.Recording$Max. video file size (MB)" -msgstr "Dimensiune maxim a fişierului video (MB)" +msgstr "Dimensiune maximÄ a fiĊierului video (MB)" msgid "Setup.Recording$Split edited files" -msgstr "Separare fişiere montate" +msgstr "Separare fiĊiere montate" msgid "Setup.Recording$Delete timeshift recording" -msgstr "Şterge înregistrarea pentru vizionare decalat" +msgstr "Ċterge nregistrarea pentru vizionare decalatÄ" msgid "Replay" msgstr "Redare" msgid "Setup.Replay$Multi speed mode" -msgstr "Mod multi-vitez" +msgstr "Mod multi-vitezÄ" msgid "Setup.Replay$Show replay mode" -msgstr "Afişeaz redarea" +msgstr "AfiĊeazÄ redarea" msgid "Setup.Replay$Show remaining time" -msgstr "" +msgstr "AratÄ timpul rÄmas" msgid "Setup.Replay$Resume ID" msgstr "Identificator continuare" @@ -1111,10 +1111,10 @@ msgid "Miscellaneous" msgstr "Diverse" msgid "Setup.Miscellaneous$Min. event timeout (min)" -msgstr "Durat minim emisiuni (min)" +msgstr "DuratÄ minimÄ emisiuni (min)" msgid "Setup.Miscellaneous$Min. user inactivity (min)" -msgstr "Durata minim de inactivitate (min)" +msgstr "Durata minimÄ de inactivitate (min)" msgid "Setup.Miscellaneous$SVDRP timeout (s)" msgstr "Timeout SVDRP (sec)" @@ -1129,28 +1129,28 @@ msgid "Setup.Miscellaneous$Initial channel" msgstr "Canalul de pornire" msgid "Setup.Miscellaneous$as before" -msgstr "ca mai înainte" +msgstr "ca mai nainte" msgid "Setup.Miscellaneous$Initial volume" msgstr "Volumul la pornire" msgid "Setup.Miscellaneous$Channels wrap" -msgstr "Lista de canale în bucl" +msgstr "Lista de canale n buclÄ" msgid "Setup.Miscellaneous$Show channel names with source" -msgstr "" +msgstr "AratÄ numele canalelor cu sursa" msgid "Setup.Miscellaneous$Emergency exit" -msgstr "Oprire de urgenŝ" +msgstr "Oprire de urgenĊ£Ä" msgid "Plugins" msgstr "Plugin-uri" msgid "This plugin has no setup parameters!" -msgstr "Acest plugin nu se configureaz!" +msgstr "Acest plugin nu se configureazÄ!" msgid "Setup" -msgstr "Configuraŝie" +msgstr "ConfiguraĊ£ie" msgid "Restart" msgstr "Repornire" @@ -1160,17 +1160,17 @@ msgstr "Sigur repornesc?" #. TRANSLATORS: note the leading and trailing blanks! msgid " Stop recording " -msgstr " Opreşte înregistrarea " +msgstr " OpreĊte nregistrarea " msgid "Schedule" msgstr "Program (EPG)" #. TRANSLATORS: note the leading blank! msgid " Stop replaying" -msgstr " Opreşte redarea" +msgstr " OpreĊte redarea" msgid "Button$Pause" -msgstr "Pauz" +msgstr "PauzÄ" msgid "Button$Stop" msgstr "Stop" @@ -1180,56 +1180,59 @@ msgstr "Continuare" #. TRANSLATORS: note the leading blank! msgid " Cancel editing" -msgstr " Opreşte montajul înregistrrii" +msgstr " OpreĊte montajul nregistrÄrii" msgid "Stop recording?" -msgstr "Opresc înregistrarea?" +msgstr "Opresc nregistrarea?" msgid "Cancel editing?" -msgstr "Opresc montajul înregistrrii?" +msgstr "Opresc montajul nregistrÄrii?" msgid "No audio available!" -msgstr "Lipseşte sunetul!" +msgstr "LipseĊte sunetul!" msgid "No subtitles" -msgstr "Nu afişeaz subtirare" +msgstr "Nu afiĊeazÄ subtirare" msgid "No subtitles available!" -msgstr "Subtitrare indisponibil!" +msgstr "Subtitrare indisponibilÄ!" msgid "Not enough disk space to start recording!" -msgstr "Insuficient spaŝiul pe disc pentru înregistrare!" +msgstr "Insuficient spaĊ£iul pe disc pentru nregistrare!" msgid "No free DVB device to record!" -msgstr "Nu mai sunt dispozitive DVB disponibile pentru înregistrare!" +msgstr "Nu mai sunt receptoare DVB disponibile pentru nregistrare!" msgid "Pausing live video..." -msgstr "Trec în pauz emisiunea transmis..." +msgstr "Trec n pauzÄ emisiunea transmisÄ..." msgid "Delete timeshift recording?" -msgstr "Şterg înregistrarea pentru vizionare decalat?" +msgstr "Ċterg nregistrarea pentru vizionare decalatÄ?" #. TRANSLATORS: note the trailing blank! msgid "Jump: " msgstr "Salt la: " msgid "No editing marks defined!" -msgstr "Nu s-au pus marcaje de montaj pentru aceast înregistrare" +msgstr "Nu s-au pus marcaje de montaj pentru aceastÄ nregistrare" + +msgid "No editing sequences defined!" +msgstr "" msgid "Can't start editing process!" -msgstr "Nu pot porni montajul înregistrrii!" +msgstr "Nu pot porni montajul nregistrÄrii!" msgid "Editing process started" -msgstr "Montajul înregistrrii a început" +msgstr "Montajul nregistrÄrii a nceput" msgid "Editing process already active!" -msgstr "Montajul înregistrrii este deja activ!" +msgstr "Montajul nregistrÄrii este deja activ!" msgid "FileNameChars$ abcdefghijklmnopqrstuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&" -msgstr " aâbcdefghiîjklmnopqrsştŝuvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&" +msgstr " aĢbcdefghijklmnopqrsĊtĊ£uvwxyz0123456789-.,#~\\^$[]|()*+?{}/:%@&" msgid "CharMap$ 0\t-.,1#~\\^$[]|()*+?{}/:%@&\tabc2\tdef3\tghi4\tjkl5\tmno6\tpqrs7\ttuv8\twxyz9" -msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\taâbc2\tdef3\tghiî4\tjkl5\tmno6\tpqrsş7\ttŝuv8\twxyz9" +msgstr " 0\t-.,1#~\\^$[]|()*+?{}/:%@&\taĢbc2\tdef3\tghi4\tjkl5\tmno6\tpqrsĊ7\ttĊ£uv8\twxyz9" msgid "Button$ABC/abc" msgstr "ABC/abc" @@ -1238,19 +1241,19 @@ msgid "Button$Overwrite" msgstr "Suprascrie" msgid "Button$Insert" -msgstr "Insereaz" +msgstr "InsereazÄ" msgid "Plugin" -msgstr "Plugin (modul adiŝional)" +msgstr "Plugin (modul adiĊ£ional)" msgid "Up/Dn for new location - OK to move" -msgstr "Sus/Jos pentru noua locaŝie - OK pentru a muta" +msgstr "Sus/Jos pentru noua locaĊ£ie - OK pentru a muta" msgid "Channel locked (recording)!" -msgstr "Canal blocat (înregistrare)!" +msgstr "Canal blocat (nregistrare)!" msgid "Low disk space!" -msgstr "Spaŝiul pe disc e foarte sczut!" +msgstr "SpaĊ£iul pe disc e foarte scÄzut!" msgid "Regenerating index file" msgstr "Generez index" @@ -1259,36 +1262,36 @@ msgid "Index file regeneration complete" msgstr "Generarea indexului s-a incheiat" msgid "Index file regeneration failed!" -msgstr "" +msgstr "Generarea indexului a eÈuat!" msgid "Can't shutdown - option '-s' not given!" -msgstr "Nu pot închide - vezi opŝiunea '-s'" +msgstr "Nu pot nchide - vezi opĊ£iunea '-s'" msgid "Editing - shut down anyway?" -msgstr "Montajul tocmai se efectueaz - închid, totuşi?" +msgstr "Montajul tocmai se efectueazÄ - nchid, totuĊi?" msgid "Recording - shut down anyway?" -msgstr "Tocmai se înregistreaz - închid, totuşi?" +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?" +msgstr "nregistrez peste %ld minute - nchid, totuĊi?" msgid "shut down anyway?" -msgstr "închid, totuşi?" +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 "Editing - restart anyway?" -msgstr "Montajul tocmai se efectueaz - repornesc, totuşi?" +msgstr "Montajul tocmai se efectueazÄ - repornesc, totuĊi?" msgid "Recording - restart anyway?" -msgstr "Tocmai se înregistreaz - repornesc, totuşi?" +msgstr "Tocmai se nregistreazÄ - repornesc, totuĊi?" msgid "restart anyway?" -msgstr "repornesc, totuşi?" +msgstr "repornesc, totuĊi?" #. TRANSLATORS: note the trailing blank! msgid "Volume " @@ -1298,22 +1301,22 @@ msgid "Classic VDR" msgstr "VDR clasic" msgid "DISK" -msgstr "" +msgstr "HARD-DISC" msgid "LOAD" -msgstr "" +msgstr "SARCINÄ" msgid "TIMERS" -msgstr "" +msgstr "TIMER-E" msgid "DEVICES" -msgstr "" +msgstr "RECEPTOARE" msgid "LIVE" -msgstr "" +msgstr "LIVE" msgid "PLAY" -msgstr "" +msgstr "REDARE" msgid "ST:TNG Panels" msgstr "Cons. ST:TNG" @@ -1324,13 +1327,13 @@ msgstr "LMMJVSD" #. TRANSLATORS: abbreviated weekdays, beginning with monday (must all be 3 letters!) msgid "MonTueWedThuFriSatSun" -msgstr "LunMarMieJoiVinSâmDum" +msgstr "LunMarMieJoiVinS˘mDum" msgid "Monday" msgstr "Luni" msgid "Tuesday" -msgstr "Marŝi" +msgstr "MarĊ£i" msgid "Wednesday" msgstr "Miercuri" @@ -1342,41 +1345,41 @@ msgid "Friday" msgstr "Vineri" msgid "Saturday" -msgstr "Sâmbt" +msgstr "S˘mbÄtÄ" msgid "Sunday" -msgstr "Duminic" +msgstr "DuminicÄ" msgid "Upcoming recording!" -msgstr "Urmeaz o înregistrare!" +msgstr "UrmeazÄ o nregistrare!" msgid "Pause live video?" -msgstr "Înregistrez emisiunea?" +msgstr "nregistrez emisiunea?" msgid "Recording started" -msgstr "A început înregistrarea" +msgstr "A nceput nregistrarea" msgid "VDR will shut down later - press Power to force" -msgstr "VDR se va închide mai târziu - apsaŝi 'Power' pentru a forŝa" +msgstr "VDR se va nchide mai t˘rziu - apÄsaĊ£i 'Power' pentru a forĊ£a" msgid "Press any key to cancel shutdown" -msgstr "Apas orice tast pentru a anula închiderea" +msgstr "ApasÄ orice tastÄ pentru a anula nchiderea" msgid "Switching primary DVB..." msgstr "Comut dispozitiv DVB primar..." msgid "Editing process failed!" -msgstr "Montajul înregistrrii a eşuat" +msgstr "Montajul nregistrÄrii a eĊuat" msgid "Editing process finished" -msgstr "Montajul înregistrrii s-a terminat" +msgstr "Montajul nregistrÄrii s-a terminat" msgid "Press any key to cancel restart" -msgstr "Apsaŝi orice tast pentru a anula repornirea" +msgstr "ApÄsaĊ£i orice tastÄ pentru a anula repornirea" #, c-format msgid "VDR will shut down in %s minutes" -msgstr "VDR se va închide în %s minute" +msgstr "VDR se va nchide n %s minute" msgid "Disk" msgstr "Disc" diff --git a/po/ru_RU.po b/po/ru_RU.po index d8e1e8e..b58fd9a 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-12-15 14:37+0100\n" "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" "Language-Team: Russian <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "żĠàĠÙâĜ: " msgid "No editing marks defined!" msgstr "½Ġ ×ÔŬë ÜĠâÚĜ ÔÛï ÜŜŬâÖ!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "½ĠÒŜ×ÜŜÖŬŜ Ŭçâì ÜŜŬâÖ ×ßĜáĜ!" diff --git a/po/sk_SK.po b/po/sk_SK.po index 0de2d06..ab5035e 100644 --- a/po/sk_SK.po +++ b/po/sk_SK.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.16\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2011-02-15 16:29+0100\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Language-Team: Slovak <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Skok: " msgid "No editing marks defined!" msgstr "Nie sú urèené znaèky úprav!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Nemôe zaèağ spracovanie úprav!" diff --git a/po/sl_SI.po b/po/sl_SI.po index 9556868..9326d85 100644 --- a/po/sl_SI.po +++ b/po/sl_SI.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-02-28 19:44+0100\n" "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n" "Language-Team: Slovenian <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Skoèi: " msgid "No editing marks defined!" msgstr "Nobena prekinitvena toèka ni definirana!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Ne morem zaèeti urejanja!" diff --git a/po/sr_SR.po b/po/sr_SR.po index c4149dd..c7b97d5 100644 --- a/po/sr_SR.po +++ b/po/sr_SR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.1\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2011-01-09 15:57+0100\n" "Last-Translator: Milan Cvijanoviĉ <elcom_cvijo@hotmail.com>\n" "Language-Team: Serbian <vdr@linuxtv.org>\n" @@ -1239,6 +1239,9 @@ msgstr "Skoèi: " msgid "No editing marks defined!" msgstr "Nijedna taèka rezanja nije odreena!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Ne mogu zapoèeti ureivanje!" diff --git a/po/sv_SE.po b/po/sv_SE.po index 951dd04..dc4d620 100644 --- a/po/sv_SE.po +++ b/po/sv_SE.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2008-03-12 18:25+0100\n" "Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n" "Language-Team: Swedish <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Hopp: " msgid "No editing marks defined!" msgstr "Det finns inga redigeringsmärken" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Kan inte starta redigering!" diff --git a/po/tr_TR.po b/po/tr_TR.po index 8b161b3..1f61944 100644 --- a/po/tr_TR.po +++ b/po/tr_TR.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\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" @@ -1213,6 +1213,9 @@ msgstr "Atla: " msgid "No editing marks defined!" msgstr "Kesim iŝaretleri belirtilmemiŝ!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "Kesim baŝlatŭlamŭyor!" diff --git a/po/uk_UA.po b/po/uk_UA.po index 45e57ee..72fe84c 100644 --- a/po/uk_UA.po +++ b/po/uk_UA.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.7.7\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2010-04-25 16:35+0200\n" "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n" "Language-Team: Ukrainian <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "µÑµıѸ: " msgid "No editing marks defined!" msgstr "µ ·°´°½ ĵÑÑş ´ğÑ ĵ½Ñ°ĥÑ!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "µĵĥ𸲠żÑ°Ñ¸ ĵ½Ñ°ĥ ·°ż¸ÑÑ!" diff --git a/po/zh_CN.po b/po/zh_CN.po index 4e2b951..56b2de0 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: VDR 1.6.0\n" "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n" "PO-Revision-Date: 2009-09-23 23:50+0800\n" "Last-Translator: Nan Feng <nfgx@21cn.com>\n" "Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "è·³èż: " msgid "No editing marks defined!" msgstr "ĉ çĵèĉ è°ċäı!" +msgid "No editing sequences defined!" +msgstr "" + msgid "Can't start editing process!" msgstr "ä¸è½ċĵċ§çĵèċ¤ç" diff --git a/recording.c b/recording.c index 1eeb82d..497bf2d 100644 --- a/recording.c +++ b/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 2.64 2012/09/30 13:05:14 kls Exp $ + * $Id: recording.c 2.73 2012/11/13 13:46:49 kls Exp $ */ #include "recording.h" @@ -83,20 +83,20 @@ public: }; cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void) -:cThread("remove deleted recordings") +:cThread("remove deleted recordings", true) { } void cRemoveDeletedRecordingsThread::Action(void) { - SetPriority(19); - SetIOPriority(7); // Make sure only one instance of VDR does this: cLockFile LockFile(VideoDirectory); if (LockFile.Lock()) { bool deleted = false; cThreadLock DeletedRecordingsLock(&DeletedRecordings); for (cRecording *r = DeletedRecordings.First(); r; ) { + if (cIoThrottle::Engaged()) + return; if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) { cRecording *next = DeletedRecordings.Next(r); r->Remove(); @@ -1358,8 +1358,10 @@ bool cMark::Save(FILE *f) bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording) { + recordingFileName = RecordingFileName; fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX); framesPerSecond = FramesPerSecond; + isPesRecording = IsPesRecording; nextUpdate = 0; lastFileTime = -1; // the first call to Load() must take place! lastChange = 0; @@ -1388,6 +1390,7 @@ bool cMarks::Update(void) cMutexLock MutexLock(&MutexMarkFramesPerSecond); MarkFramesPerSecond = framesPerSecond; if (cConfig<cMark>::Load(fileName)) { + Align(); Sort(); return true; } @@ -1396,6 +1399,18 @@ bool cMarks::Update(void) return false; } +void cMarks::Align(void) +{ + cIndexFile IndexFile(recordingFileName, false, isPesRecording); + for (cMark *m = First(); m; m = Next(m)) { + int p = IndexFile.GetClosestIFrame(m->Position()); + if (int d = m->Position() - p) { + isyslog("aligned editing mark %s to %s (off by %d frame%s)", *IndexToHMSF(m->Position(), true, framesPerSecond), *IndexToHMSF(p, true, framesPerSecond), d, abs(d) > 1 ? "s" : ""); + m->SetPosition(p); + } + } +} + void cMarks::Sort(void) { for (cMark *m1 = First(); m1; m1 = Next(m1)) { @@ -1408,14 +1423,10 @@ void cMarks::Sort(void) } } -cMark *cMarks::Add(int Position) +void cMarks::Add(int Position) { - cMark *m = Get(Position); - if (!m) { - cConfig<cMark>::Add(m = new cMark(Position, NULL, framesPerSecond)); - Sort(); - } - return m; + cConfig<cMark>::Add(new cMark(Position, NULL, framesPerSecond)); + Sort(); } cMark *cMarks::Get(int Position) @@ -1445,6 +1456,54 @@ cMark *cMarks::GetNext(int Position) return NULL; } +cMark *cMarks::GetNextBegin(cMark *EndMark) +{ + cMark *BeginMark = EndMark ? Next(EndMark) : First(); + if (BeginMark) { + while (cMark *NextMark = Next(BeginMark)) { + if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position + if (!(BeginMark = Next(NextMark))) + break; + } + else + break; + } + } + return BeginMark; +} + +cMark *cMarks::GetNextEnd(cMark *BeginMark) +{ + if (!BeginMark) + return NULL; + cMark *EndMark = Next(BeginMark); + if (EndMark) { + while (cMark *NextMark = Next(EndMark)) { + if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position + if (!(EndMark = Next(NextMark))) + break; + } + else + break; + } + } + return EndMark; +} + +int cMarks::GetNumSequences(void) +{ + int NumSequences = 0; + if (cMark *BeginMark = GetNextBegin()) { + while (cMark *EndMark = GetNextEnd(BeginMark)) { + NumSequences++; + BeginMark = GetNextBegin(EndMark); + } + if (NumSequences == 0 && BeginMark->Position() > 0) + NumSequences = 1; // there is only one actual "begin" mark at a non-zero offset, and no actual "end" mark + } + return NumSequences; +} + // --- cRecordingUserCommand ------------------------------------------------- const char *cRecordingUserCommand::command = NULL; @@ -1535,7 +1594,6 @@ void cIndexFileGenerator::Action(void) if (Processed > 0) { if (FrameDetector.Synced()) { // Synced FrameDetector, so rewind for actual processing: - FrameDetector.Reset(); Rewind = true; } Buffer.Del(Processed); @@ -1546,7 +1604,7 @@ void cIndexFileGenerator::Action(void) uchar *p = Data; while (Length >= TS_SIZE) { int Pid = TsPid(p); - if (Pid == 0) + if (Pid == PATPID) PatPmtParser.ParsePat(p, TS_SIZE); else if (Pid == PatPmtParser.PmtPid()) PatPmtParser.ParsePmt(p, TS_SIZE); @@ -1570,6 +1628,7 @@ void cIndexFileGenerator::Action(void) ReplayFile = FileName.NextFile(); FileSize = 0; FrameOffset = -1; + Buffer.Clear(); } } // Recording has been processed: @@ -1753,7 +1812,7 @@ bool cIndexFile::CatchUp(int Index) // returns true unless something really goes wrong, so that 'index' becomes NULL if (index && f >= 0) { cMutexLock MutexLock(&mutex); - for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) { + for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index > last); i++) { struct stat buf; if (fstat(f, &buf) == 0) { if (!IsInIndexList(this)) { @@ -1798,7 +1857,7 @@ bool cIndexFile::CatchUp(int Index) } else LOG_ERROR_STR(*fileName); - if (Index < last) + if (Index <= last) break; cCondWait::SleepMs(1000); } @@ -1826,18 +1885,22 @@ bool cIndexFile::Write(bool Independent, uint16_t FileNumber, off_t FileOffset) bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length) { if (CatchUp(Index)) { - if (Index >= 0 && Index < last) { + if (Index >= 0 && Index <= last) { *FileNumber = index[Index].number; *FileOffset = index[Index].offset; if (Independent) *Independent = index[Index].independent; if (Length) { - uint16_t fn = index[Index + 1].number; - off_t fo = index[Index + 1].offset; - if (fn == *FileNumber) - *Length = int(fo - *FileOffset); + if (Index < last) { + uint16_t fn = index[Index + 1].number; + off_t fo = index[Index + 1].offset; + if (fn == *FileNumber) + *Length = int(fo - *FileOffset); + else + *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly) + } else - *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly) + *Length = -1; } return true; } @@ -1851,7 +1914,7 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off int d = Forward ? 1 : -1; for (;;) { Index += d; - if (Index >= 0 && Index < last) { + if (Index >= 0 && Index <= last) { if (index[Index].independent) { uint16_t fn; if (!FileNumber) @@ -1882,12 +1945,40 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off return -1; } +int cIndexFile::GetClosestIFrame(int Index) +{ + if (last > 0) { + Index = constrain(Index, 0, last); + if (index[Index].independent) + return Index; + int il = Index - 1; + int ih = Index + 1; + for (;;) { + if (il >= 0) { + if (index[il].independent) + return il; + il--; + } + else if (ih > last) + break; + if (ih <= last) { + if (index[ih].independent) + return ih; + ih++; + } + else if (il < 0) + break; + } + } + return 0; +} + int cIndexFile::Get(uint16_t FileNumber, off_t FileOffset) { if (CatchUp()) { //TODO implement binary search! int i; - for (i = 0; i < last; i++) { + for (i = 0; i <= last; i++) { if (index[i].number > FileNumber || (index[i].number == FileNumber) && off_t(index[i].offset) >= FileOffset) break; } @@ -2039,7 +2130,7 @@ bool cFileName::GetLastPatPmtVersions(int &PatVersion, int &PmtVersion) while (read(fd, buf, sizeof(buf)) == sizeof(buf)) { if (buf[0] == TS_SYNC_BYTE) { int Pid = TsPid(buf); - if (Pid == 0) + if (Pid == PATPID) PatPmtParser.ParsePat(buf, sizeof(buf)); else if (Pid == PatPmtParser.PmtPid()) { PatPmtParser.ParsePmt(buf, sizeof(buf)); diff --git a/recording.h b/recording.h index ac44ad5..9ae9b1e 100644 --- a/recording.h +++ b/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 2.37 2012/09/17 08:53:23 kls Exp $ + * $Id: recording.h 2.40 2012/11/13 11:43:59 kls Exp $ */ #ifndef __RECORDING_H @@ -222,19 +222,35 @@ public: class cMarks : public cConfig<cMark> { private: + cString recordingFileName; cString fileName; double framesPerSecond; + bool isPesRecording; time_t nextUpdate; time_t lastFileTime; time_t lastChange; public: bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false); bool Update(void); + void Align(void); void Sort(void); - cMark *Add(int Position); + void Add(int Position); cMark *Get(int Position); cMark *GetPrev(int Position); cMark *GetNext(int Position); + cMark *GetNextBegin(cMark *EndMark = NULL); + ///< Returns the next "begin" mark after EndMark, skipping any marks at the + ///< same position as EndMark. If EndMark is NULL, the first actual "begin" + ///< will be returned (if any). + cMark *GetNextEnd(cMark *BeginMark); + ///< Returns the next "end" mark after BeginMark, skipping any marks at the + ///< same position as BeginMark. + int GetNumSequences(void); + ///< Returns the actual number of sequences to be cut from the recording. + ///< If there is only one actual "begin" mark, and it is positioned at index + ///< 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. }; #define RUC_BEFORERECORDING "before" @@ -291,8 +307,14 @@ public: 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); 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, + ///< if it already points to an I-frame). Index may be any value, even outside the current + ///< range of frame indexes. + ///< If there is no actual index data available, 0 is returned. int Get(uint16_t FileNumber, off_t FileOffset); int Last(void) { CatchUp(); return last; } + ///< Returns the index of the last entry in this file, or -1 if the file is empty. int GetResume(void) { return resumeFile.Read(); } bool StoreResume(int Index) { return resumeFile.Save(Index); } bool IsStillRecording(void); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.c 2.67 2012/09/19 10:28:42 kls Exp $ + * $Id: remux.c 2.71 2012/11/18 12:18:08 kls Exp $ */ #include "remux.h" @@ -23,6 +23,8 @@ static bool DebugFrames = false; #define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a) #define dbgframes(a...) if (DebugFrames) fprintf(stderr, a) +#define EMPTY_SCANNER (0xFFFFFFFF) + ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader) { if (Count < 7) @@ -112,6 +114,32 @@ void cRemux::SetBrokenLink(uchar *Data, int Length) // --- Some TS handling tools ------------------------------------------------ +void TsHidePayload(uchar *p) +{ + p[1] &= ~TS_PAYLOAD_START; + p[3] |= TS_ADAPT_FIELD_EXISTS; + p[3] &= ~TS_PAYLOAD_EXISTS; + p[4] = TS_SIZE - 5; + p[5] = 0x00; + memset(p + 6, 0xFF, TS_SIZE - 6); +} + +void TsSetPcr(uchar *p, int64_t Pcr) +{ + if (TsHasAdaptationField(p)) { + if (p[4] >= 7 && (p[5] & TS_ADAPT_PCR)) { + int64_t b = Pcr / PCRFACTOR; + int e = Pcr % PCRFACTOR; + p[ 6] = b >> 25; + p[ 7] = b >> 17; + p[ 8] = b >> 9; + p[ 9] = b >> 1; + p[10] = (b << 7) | (p[10] & 0x7E) | ((e >> 8) & 0x01); + p[11] = e; + } + } +} + int64_t TsGetPts(const uchar *p, int l) { // Find the first packet with a PTS and use it: @@ -125,27 +153,165 @@ int64_t TsGetPts(const uchar *p, int l) return -1; } -void TsSetTeiOnBrokenPackets(uchar *p, int l) +int64_t TsGetDts(const uchar *p, int l) { - bool Processed[MAXPID] = { false }; - while (l >= TS_SIZE) { - if (*p != TS_SYNC_BYTE) - break; - int Pid = TsPid(p); - if (!Processed[Pid]) { - if (!TsPayloadStart(p)) - p[1] |= TS_ERROR; - else { - Processed[Pid] = true; - int offs = TsPayloadOffset(p); - cRemux::SetBrokenLink(p + offs, TS_SIZE - offs); - } + // Find the first packet with a DTS and use it: + while (l > 0) { + const uchar *d = p; + if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) + return PesGetDts(d); + p += TS_SIZE; + l -= TS_SIZE; + } + return -1; +} + +void TsSetPts(uchar *p, int l, int64_t Pts) +{ + // Find the first packet with a PTS and use it: + while (l > 0) { + const uchar *d = p; + if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d)) { + PesSetPts(const_cast<uchar *>(d), Pts); + return; } + p += TS_SIZE; l -= TS_SIZE; + } +} + +void TsSetDts(uchar *p, int l, int64_t Dts) +{ + // Find the first packet with a DTS and use it: + while (l > 0) { + const uchar *d = p; + if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) { + PesSetDts(const_cast<uchar *>(d), Dts); + return; + } p += TS_SIZE; + l -= TS_SIZE; } } +// --- Some PES handling tools ----------------------------------------------- + +void PesSetPts(uchar *p, int64_t Pts) +{ + p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1); + p[10] = Pts >> 22; + p[11] = ((Pts >> 14) & 0xFE) | 0x01; + p[12] = Pts >> 7; + p[13] = ((Pts << 1) & 0xFE) | 0x01; +} + +void PesSetDts(uchar *p, int64_t Dts) +{ + p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1); + p[15] = Dts >> 22; + p[16] = ((Dts >> 14) & 0xFE) | 0x01; + p[17] = Dts >> 7; + p[18] = ((Dts << 1) & 0xFE) | 0x01; +} + +int64_t PtsDiff(int64_t Pts1, int64_t Pts2) +{ + int64_t d = Pts2 - Pts1; + if (d > MAX33BIT / 2) + return d - (MAX33BIT + 1); + if (d < -MAX33BIT / 2) + return d + (MAX33BIT + 1); + return d; +} + +// --- cTsPayload ------------------------------------------------------------ + +cTsPayload::cTsPayload(void) +{ + data = NULL; + length = 0; + pid = -1; + index = 0; +} + +cTsPayload::cTsPayload(uchar *Data, int Length, int Pid) +{ + Setup(Data, Length, Pid); +} + +void cTsPayload::Setup(uchar *Data, int Length, int Pid) +{ + data = Data; + length = Length; + pid = Pid >= 0 ? Pid : TsPid(Data); + index = 0; +} + +uchar cTsPayload::GetByte(void) +{ + if (!Eof()) { + if (index % TS_SIZE == 0) { // encountered the next TS header + for (;; index += TS_SIZE) { + if (data[index] == TS_SYNC_BYTE && index + TS_SIZE <= length) { // to make sure we are at a TS header start and drop incomplete TS packets at the end + uchar *p = data + index; + if (TsPid(p) == pid) { // only handle TS packets for the initial PID + if (TsHasPayload(p)) { + if (index > 0 && TsPayloadStart(p)) { // checking index to not skip the very first TS packet + length = index; // triggers EOF + return 0x00; + } + index += TsPayloadOffset(p); + break; + } + } + } + else { + length = index; // triggers EOF + return 0x00; + } + } + } + return data[index++]; + } + return 0x00; +} + +bool cTsPayload::SkipBytes(int Bytes) +{ + while (Bytes-- > 0) + GetByte(); + return !Eof(); +} + +bool cTsPayload::SkipPesHeader(void) +{ + return SkipBytes(PesPayloadOffset(data + TsPayloadOffset(data))); +} + +int cTsPayload::GetLastIndex(void) +{ + return index - 1; +} + +void cTsPayload::SetByte(uchar Byte, int Index) +{ + if (Index >= 0 && Index < length) + data[Index] = Byte; +} + +bool cTsPayload::Find(uint32_t Code) +{ + int OldIndex = index; + uint32_t Scanner = EMPTY_SCANNER; + while (!Eof()) { + Scanner = (Scanner << 8) | GetByte(); + if (Scanner == Code) + return true; + } + index = OldIndex; + return false; +} + // --- cPatPmtGenerator ------------------------------------------------------ cPatPmtGenerator::cPatPmtGenerator(const cChannel *Channel) @@ -665,6 +831,25 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length) pmtSize = 0; } +bool cPatPmtParser::ParsePatPmt(const uchar *Data, int Length) +{ + while (Length >= TS_SIZE) { + if (*Data != TS_SYNC_BYTE) + break; // just for safety + int Pid = TsPid(Data); + if (Pid == PATPID) + ParsePat(Data, TS_SIZE); + else if (Pid == PmtPid()) { + ParsePmt(Data, TS_SIZE); + if (patVersion >= 0 && pmtVersion >= 0) + return true; + } + Data += TS_SIZE; + Length -= TS_SIZE; + } + return false; +} + bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const { PatVersion = patVersion; @@ -809,23 +994,365 @@ void PesDump(const char *Name, const u_char *Data, int Length) TsDump(Name, Data, Length); } -// --- cFrameDetector -------------------------------------------------------- +// --- cFrameParser ---------------------------------------------------------- + +class cFrameParser { +protected: + bool debug; + bool newFrame; + bool independentFrame; +public: + cFrameParser(void); + virtual ~cFrameParser() {}; + virtual int Parse(const uchar *Data, int Length, int Pid) = 0; + ///< Parses the given Data, which is a sequence of Length bytes of TS packets. + ///< The payload in the TS packets with the given Pid is searched for just + ///< enough information to determine the beginning and type of the next video + ///< frame. + ///< Returns the number of bytes parsed. Upon return, the functions NewFrame() + ///< and IndependentFrame() can be called to retrieve the required information. + void SetDebug(bool Debug) { debug = Debug; } + bool NewFrame(void) { return newFrame; } + bool IndependentFrame(void) { return independentFrame; } + }; + +cFrameParser::cFrameParser(void) +{ + debug = true; + newFrame = false; + independentFrame = false; +} -#define EMPTY_SCANNER (0xFFFFFFFF) +// --- cAudioParser ---------------------------------------------------------- + +class cAudioParser : public cFrameParser { +public: + cAudioParser(void); + virtual int Parse(const uchar *Data, int Length, int Pid); + }; + +cAudioParser::cAudioParser(void) +{ +} + +int cAudioParser::Parse(const uchar *Data, int Length, int Pid) +{ + if (TsPayloadStart(Data)) { + newFrame = independentFrame = true; + if (debug) + dbgframes("/"); + } + else + newFrame = independentFrame = false; + return TS_SIZE; +} + +// --- cMpeg2Parser ---------------------------------------------------------- + +class cMpeg2Parser : public cFrameParser { +private: + uint32_t scanner; + bool seenIndependentFrame; +public: + cMpeg2Parser(void); + virtual int Parse(const uchar *Data, int Length, int Pid); + }; + +cMpeg2Parser::cMpeg2Parser(void) +{ + scanner = EMPTY_SCANNER; + seenIndependentFrame = false; +} + +int cMpeg2Parser::Parse(const uchar *Data, int Length, int Pid) +{ + newFrame = independentFrame = false; + bool SeenPayloadStart = false; + cTsPayload tsPayload(const_cast<uchar *>(Data), Length, Pid); + if (TsPayloadStart(Data)) { + SeenPayloadStart = true; + tsPayload.SkipPesHeader(); + scanner = EMPTY_SCANNER; + if (debug && seenIndependentFrame) + dbgframes("/"); + } + uint32_t OldScanner = scanner; // need to remember it in case of multiple frames per payload + for (;;) { + if (!SeenPayloadStart && tsPayload.AtTsStart()) + OldScanner = scanner; + scanner = (scanner << 8) | tsPayload.GetByte(); + if (scanner == 0x00000100) { // Picture Start Code + if (!SeenPayloadStart && tsPayload.GetLastIndex() > TS_SIZE) { + scanner = OldScanner; + return tsPayload.Used() - TS_SIZE; + } + newFrame = true; + tsPayload.GetByte(); + uchar FrameType = (tsPayload.GetByte() >> 3) & 0x07; + independentFrame = FrameType == 1; // I-Frame + if (debug) { + seenIndependentFrame |= independentFrame; + if (seenIndependentFrame) { + static const char FrameTypes[] = "?IPBD???"; + dbgframes("%c", FrameTypes[FrameType]); + } + } + break; + } + if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary + || (tsPayload.Available() < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit... + && (tsPayload.Available() <= 0 || tsPayload.AtTsStart()))) // ...but only if there is no more data at all, or if we are at a TS boundary + break; + } + return tsPayload.Used(); +} + +// --- cMpeg4Parser ---------------------------------------------------------- + +class cMpeg4Parser : public cFrameParser { +private: + enum eNalUnitType { + nutCodedSliceNonIdr = 1, + nutCodedSliceIdr = 5, + nutSequenceParameterSet = 7, + nutAccessUnitDelimiter = 9, + }; + cTsPayload tsPayload; + uchar byte; // holds the current byte value in case of bitwise access + int bit; // the bit index into the current byte (-1 if we're not in bit reading mode) + int zeroBytes; // the number of consecutive zero bytes (to detect 0x000003) + uint32_t scanner; + // Identifiers written in '_' notation as in "ITU-T H.264": + bool separate_colour_plane_flag; + int log2_max_frame_num; + bool frame_mbs_only_flag; + // + bool gotAccessUnitDelimiter; + bool gotSequenceParameterSet; + uchar GetByte(bool Raw = false); + ///< Gets the next data byte. If Raw is true, no filtering will be done. + ///< With Raw set to false, if the byte sequence 0x000003 is encountered, + ///< the byte with 0x03 will be skipped. + uchar GetBit(void); + uint32_t GetBits(int Bits); + uint32_t GetGolombUe(void); + int32_t GetGolombSe(void); + void ParseAccessUnitDelimiter(void); + void ParseSequenceParameterSet(void); + void ParseSliceHeader(void); +public: + cMpeg4Parser(void); + ///< Sets up a new MPEG-4 parser. + ///< This class parses only the data absolutely necessary to determine the + ///< frame borders and field count of the given H264 material. + virtual int Parse(const uchar *Data, int Length, int Pid); + }; + +cMpeg4Parser::cMpeg4Parser(void) +{ + byte = 0; + bit = -1; + zeroBytes = 0; + scanner = EMPTY_SCANNER; + separate_colour_plane_flag = false; + log2_max_frame_num = 0; + frame_mbs_only_flag = false; + gotAccessUnitDelimiter = false; + gotSequenceParameterSet = false; +} + +uchar cMpeg4Parser::GetByte(bool Raw) +{ + uchar b = tsPayload.GetByte(); + if (!Raw) { + // If we encounter the byte sequence 0x000003, we need to skip the 0x03: + if (b == 0x00) + zeroBytes++; + else { + if (b == 0x03 && zeroBytes >= 2) + b = tsPayload.GetByte(); + zeroBytes = 0; + } + } + else + zeroBytes = 0; + bit = -1; + return b; +} + +uchar cMpeg4Parser::GetBit(void) +{ + if (bit < 0) { + byte = GetByte(); + bit = 7; + } + return (byte & (1 << bit--)) ? 1 : 0; +} + +uint32_t cMpeg4Parser::GetBits(int Bits) +{ + uint32_t b = 0; + while (Bits--) + b |= GetBit() << Bits; + return b; +} + +uint32_t cMpeg4Parser::GetGolombUe(void) +{ + int z = -1; + for (int b = 0; !b; z++) + b = GetBit(); + return (1 << z) - 1 + GetBits(z); +} + +int32_t cMpeg4Parser::GetGolombSe(void) +{ + uint32_t v = GetGolombUe(); + if (v) { + if ((v & 0x01) != 0) + return (v + 1) / 2; // fails for v == 0xFFFFFFFF, but that will probably never happen + else + return -int32_t(v / 2); + } + return v; +} + +int cMpeg4Parser::Parse(const uchar *Data, int Length, int Pid) +{ + newFrame = independentFrame = false; + tsPayload.Setup(const_cast<uchar *>(Data), Length, Pid); + if (TsPayloadStart(Data)) { + tsPayload.SkipPesHeader(); + scanner = EMPTY_SCANNER; + if (debug && gotSequenceParameterSet) { + dbgframes("/"); + } + } + for (;;) { + scanner = (scanner << 8) | GetByte(true); + if ((scanner & 0xFFFFFF00) == 0x00000100) { // NAL unit start + uchar NalUnitType = scanner & 0x1F; + switch (NalUnitType) { + case nutAccessUnitDelimiter: ParseAccessUnitDelimiter(); + gotAccessUnitDelimiter = true; + break; + case nutSequenceParameterSet: ParseSequenceParameterSet(); + gotSequenceParameterSet = true; + break; + case nutCodedSliceNonIdr: + case nutCodedSliceIdr: if (gotAccessUnitDelimiter && gotSequenceParameterSet) { + ParseSliceHeader(); + gotAccessUnitDelimiter = false; + return tsPayload.Used(); + } + break; + default: ; + } + } + if (tsPayload.AtPayloadStart() // stop at any new payload start to have the buffer refilled if necessary + || (tsPayload.Available() < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE // stop if the available data is below the limit... + && (tsPayload.Available() <= 0 || tsPayload.AtTsStart()))) // ...but only if there is no more data at all, or if we are at a TS boundary + break; + } + return tsPayload.Used(); +} + +void cMpeg4Parser::ParseAccessUnitDelimiter(void) +{ + if (debug && gotSequenceParameterSet) + dbgframes("A"); + GetByte(); // primary_pic_type +} + +void cMpeg4Parser::ParseSequenceParameterSet(void) +{ + 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 + if (chroma_format_idc == 3) + separate_colour_plane_flag = GetBit(); + GetGolombUe(); // bit_depth_luma_minus8 + GetGolombUe(); // bit_depth_chroma_minus8 + GetBit(); // qpprime_y_zero_transform_bypass_flag + if (GetBit()) { // seq_scaling_matrix_present_flag + for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++) { + if (GetBit()) { // seq_scaling_list_present_flag + int SizeOfScalingList = (i < 6) ? 16 : 64; + int LastScale = 8; + int NextScale = 8; + for (int j = 0; j < SizeOfScalingList; j++) { + if (NextScale) + NextScale = (LastScale + GetGolombSe() + 256) % 256; // delta_scale + if (NextScale) + LastScale = NextScale; + } + } + } + } + } + log2_max_frame_num = GetGolombUe() + 4; // log2_max_frame_num_minus4 + int pic_order_cnt_type = GetGolombUe(); // pic_order_cnt_type + if (pic_order_cnt_type == 0) + GetGolombUe(); // log2_max_pic_order_cnt_lsb_minus4 + else if (pic_order_cnt_type == 1) { + GetBit(); // delta_pic_order_always_zero_flag + GetGolombSe(); // offset_for_non_ref_pic + GetGolombSe(); // offset_for_top_to_bottom_field + for (int i = GetGolombUe(); i--; ) // num_ref_frames_in_pic_order_cnt_cycle + GetGolombSe(); // offset_for_ref_frame + } + 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 + frame_mbs_only_flag = GetBit(); // frame_mbs_only_flag + if (debug) { + if (gotAccessUnitDelimiter && !gotSequenceParameterSet) + dbgframes("A"); // just for completeness + dbgframes(frame_mbs_only_flag ? "S" : "s"); + } +} + +void cMpeg4Parser::ParseSliceHeader(void) +{ + newFrame = true; + GetGolombUe(); // first_mb_in_slice + int slice_type = GetGolombUe(); // slice_type, 0 = P, 1 = B, 2 = I, 3 = SP, 4 = SI + independentFrame = (slice_type % 5) == 2; + if (debug) { + static const char SliceTypes[] = "PBIpi"; + dbgframes("%c", SliceTypes[slice_type % 5]); + } + if (frame_mbs_only_flag) + return; // don't need the rest - a frame is complete + GetGolombUe(); // pic_parameter_set_id + if (separate_colour_plane_flag) + GetBits(2); // colour_plane_id + GetBits(log2_max_frame_num); // frame_num + if (!frame_mbs_only_flag) { + if (GetBit()) // field_pic_flag + newFrame = !GetBit(); // bottom_field_flag + if (debug) + dbgframes(newFrame ? "t" : "b"); + } +} + +// --- cFrameDetector -------------------------------------------------------- cFrameDetector::cFrameDetector(int Pid, int Type) { + parser = NULL; SetPid(Pid, Type); synced = false; newFrame = independentFrame = false; numPtsValues = 0; - numFrames = 0; numIFrames = 0; framesPerSecond = 0; framesInPayloadUnit = framesPerPayloadUnit = 0; - payloadUnitOfFrame = 0; scanning = false; - scanner = EMPTY_SCANNER; } static int CmpUint32(const void *p1, const void *p2) @@ -840,42 +1367,26 @@ void cFrameDetector::SetPid(int Pid, int Type) pid = Pid; type = Type; isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or 4 -} - -void cFrameDetector::Reset(void) -{ - newFrame = independentFrame = false; - payloadUnitOfFrame = 0; - scanning = false; - scanner = EMPTY_SCANNER; -} - -int cFrameDetector::SkipPackets(const uchar *&Data, int &Length, int &Processed, int &FrameTypeOffset) -{ - if (!synced) - dbgframes("%d>", FrameTypeOffset); - while (Length >= TS_SIZE) { - // switch to the next TS packet, but skip those that have a different PID: - Data += TS_SIZE; - Length -= TS_SIZE; - Processed += TS_SIZE; - if (TsPid(Data) == pid) - break; - else if (Length < TS_SIZE) - esyslog("ERROR: out of data while skipping TS packets in cFrameDetector"); - } - FrameTypeOffset -= TS_SIZE; - FrameTypeOffset += TsPayloadOffset(Data); - return FrameTypeOffset; + delete parser; + parser = NULL; + if (type == 0x01 || type == 0x02) + parser = new cMpeg2Parser; + else if (type == 0x1B) + parser = new cMpeg4Parser; + else if (type == 0x04 || type == 0x06) // MPEG audio or AC3 audio + parser = new cAudioParser; + else if (type != 0) + esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid); } int cFrameDetector::Analyze(const uchar *Data, int Length) { - bool SeenPayloadStart = false; - bool SeenAccessUnitDelimiter = false; + if (!parser) + return 0; int Processed = 0; newFrame = independentFrame = false; - while (Length >= TS_SIZE) { + while (Length >= MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE) { // makes sure we are looking at enough data, in case the frame type is not stored in the first TS packet + // Sync on TS packet borders: if (Data[0] != TS_SYNC_BYTE) { int Skipped = 1; while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE)) @@ -883,186 +1394,99 @@ int cFrameDetector::Analyze(const uchar *Data, int Length) esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped); return Processed + Skipped; } + // Handle one TS packet: + int Handled = TS_SIZE; if (TsHasPayload(Data) && !TsIsScrambled(Data)) { int Pid = TsPid(Data); if (Pid == pid) { + if (Processed) + return Processed; + if (TsPayloadStart(Data)) + scanning = true; + if (scanning) { + // Detect the beginning of a new frame: + if (TsPayloadStart(Data)) { + if (!framesPerPayloadUnit) + framesPerPayloadUnit = framesInPayloadUnit; + } + int n = parser->Parse(Data, Length, pid); + if (n > 0) { + if (parser->NewFrame()) { + newFrame = true; + independentFrame = parser->IndependentFrame(); + if (synced) { + if (framesPerPayloadUnit <= 1) + scanning = false; + } + else { + framesInPayloadUnit++; + if (independentFrame) + numIFrames++; + } + } + Handled = n; + } + } if (TsPayloadStart(Data)) { - SeenPayloadStart = true; - if (synced && Processed) - return Processed; - if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE) - return Processed; // need more data, in case the frame type is not stored in the first TS packet + // Determine the frame rate from the PTS values in the PES headers: 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 - const uchar *Pes = Data + TsPayloadOffset(Data); - if (numIFrames && PesHasPts(Pes)) { - ptsValues[numPtsValues] = PesGetPts(Pes); - // check for rollover: - if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) { - dbgframes("#"); - numPtsValues = 0; - numIFrames = 0; - numFrames = 0; + if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!) + const uchar *Pes = Data + TsPayloadOffset(Data); + if (numIFrames && PesHasPts(Pes)) { + ptsValues[numPtsValues] = PesGetPts(Pes); + // check for rollover: + if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) { + dbgframes("#"); + numPtsValues = 0; + numIFrames = 0; + } + else + numPtsValues++; } - else - numPtsValues++; } } - else { + if (numPtsValues >= 2 && numIFrames >= 2) { // find the smallest PTS delta: qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32); numPtsValues--; for (int i = 0; i < numPtsValues; i++) ptsValues[i] = ptsValues[i + 1] - ptsValues[i]; qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32); - uint32_t Delta = ptsValues[0]; + uint32_t Delta = ptsValues[0] / framesPerPayloadUnit; // determine frame info: if (isVideo) { if (abs(Delta - 3600) <= 1) framesPerSecond = 25.0; else if (Delta % 3003 == 0) framesPerSecond = 30.0 / 1.001; - else if (abs(Delta - 1800) <= 1) { - if (numFrames > 50) { - // this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame" - framesPerSecond = 25.0; - framesPerPayloadUnit = -2; - } - else - framesPerSecond = 50.0; - } + else if (abs(Delta - 1800) <= 1) + framesPerSecond = 50.0; else if (Delta == 1501) - if (numFrames > 50) { - // this is a "best guess": if there are more than 50 frames between two I-frames, we assume each "frame" actually contains a "field", so two "fields" make one "frame" - framesPerSecond = 30.0 / 1.001; - framesPerPayloadUnit = -2; - } - else - framesPerSecond = 60.0 / 1.001; + framesPerSecond = 60.0 / 1.001; else { framesPerSecond = DEFAULTFRAMESPERSECOND; dsyslog("unknown frame delta (%d), assuming %5.2f fps", Delta, DEFAULTFRAMESPERSECOND); } } else // audio - framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing - dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numFrames); + framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing + dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1); + synced = true; + parser->SetDebug(false); } } - scanner = EMPTY_SCANNER; - scanning = true; - } - if (scanning) { - int PayloadOffset = TsPayloadOffset(Data); - if (TsPayloadStart(Data)) { - PayloadOffset += PesPayloadOffset(Data + PayloadOffset); - if (!framesPerPayloadUnit) - framesPerPayloadUnit = framesInPayloadUnit; - if (DebugFrames && !synced) - dbgframes("/"); - } - for (int i = PayloadOffset; scanning && i < TS_SIZE; i++) { - scanner <<= 8; - scanner |= Data[i]; - switch (type) { - case 0x01: // MPEG 1 video - case 0x02: // MPEG 2 video - if (scanner == 0x00000100) { // Picture Start Code - scanner = EMPTY_SCANNER; - if (synced && !SeenPayloadStart && Processed) - return Processed; // flush everything before this new frame - int FrameTypeOffset = i + 2; - if (FrameTypeOffset >= TS_SIZE) // the byte to check is in the next TS packet - i = SkipPackets(Data, Length, Processed, FrameTypeOffset); - newFrame = true; - uchar FrameType = (Data[FrameTypeOffset] >> 3) & 0x07; - independentFrame = FrameType == 1; // I-Frame - if (synced) { - if (framesPerPayloadUnit <= 1) - scanning = false; - } - else { - framesInPayloadUnit++; - if (independentFrame) - numIFrames++; - if (numIFrames == 1) - numFrames++; - dbgframes("%u ", FrameType); - } - if (synced) - return Processed + TS_SIZE; // flag this new frame - } - break; - case 0x1B: // MPEG 4 video - if (scanner == 0x00000109) { // Access Unit Delimiter - scanner = EMPTY_SCANNER; - if (synced && !SeenPayloadStart && Processed) - return Processed; // flush everything before this new frame - SeenAccessUnitDelimiter = true; - } - else if (SeenAccessUnitDelimiter && scanner == 0x00000001) { // NALU start - SeenAccessUnitDelimiter = false; - int FrameTypeOffset = i + 1; - if (FrameTypeOffset >= TS_SIZE) // the byte to check is in the next TS packet - i = SkipPackets(Data, Length, Processed, FrameTypeOffset); - newFrame = true; - uchar FrameType = Data[FrameTypeOffset] & 0x1F; - independentFrame = FrameType == 0x07; - if (synced) { - if (framesPerPayloadUnit < 0) { - payloadUnitOfFrame = (payloadUnitOfFrame + 1) % -framesPerPayloadUnit; - if (payloadUnitOfFrame != 0 && independentFrame) - payloadUnitOfFrame = 0; - if (payloadUnitOfFrame) - newFrame = false; - } - if (framesPerPayloadUnit <= 1) - scanning = false; - } - else { - framesInPayloadUnit++; - if (independentFrame) - numIFrames++; - if (numIFrames == 1) - numFrames++; - dbgframes("%02X ", FrameType); - } - if (synced) - return Processed + TS_SIZE; // flag this new frame - } - break; - case 0x04: // MPEG audio - case 0x06: // AC3 audio - if (synced && Processed) - return Processed; - newFrame = true; - independentFrame = true; - if (!synced) { - framesInPayloadUnit = 1; - if (TsPayloadStart(Data)) - numIFrames++; - } - scanning = false; - break; - default: esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid); - pid = 0; // let's just ignore any further data - } - } - if (!synced && framesPerSecond > 0.0 && independentFrame) { - synced = true; - dbgframes("*\n"); - Reset(); - return Processed + TS_SIZE; - } } } else if (Pid == PATPID && synced && Processed) return Processed; // allow the caller to see any PAT packets } - Data += TS_SIZE; - Length -= TS_SIZE; - Processed += TS_SIZE; + Data += Handled; + Length -= Handled; + Processed += Handled; + if (newFrame) + break; } return Processed; } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remux.h 2.32 2011/09/04 12:48:26 kls Exp $ + * $Id: remux.h 2.35 2012/11/18 12:17:23 kls Exp $ */ #ifndef __REMUX_H @@ -52,6 +52,11 @@ public: #define PATPID 0x0000 // PAT PID (constant 0) #define MAXPID 0x2000 // for arrays that use a PID as the index +#define PTSTICKS 90000 // number of PTS ticks per second +#define PCRFACTOR 300 // conversion from 27MHz PCR extension to 90kHz PCR base +#define MAX33BIT 0x00000001FFFFFFFFLL // max. possible value with 33 bit +#define MAX27MHZ ((MAX33BIT + 1) * PCRFACTOR - 1) // max. possible PCR value + inline bool TsHasPayload(const uchar *p) { return p[3] & TS_PAYLOAD_EXISTS; @@ -82,6 +87,16 @@ inline bool TsIsScrambled(const uchar *p) return p[3] & TS_SCRAMBLING_CONTROL; } +inline uchar TsGetContinuityCounter(const uchar *p) +{ + return p[3] & TS_CONT_CNT_MASK; +} + +inline void TsSetContinuityCounter(uchar *p, uchar Counter) +{ + p[3] = (p[3] & ~TS_CONT_CNT_MASK) | (Counter & TS_CONT_CNT_MASK); +} + inline int TsPayloadOffset(const uchar *p) { int o = TsHasAdaptationField(p) ? p[4] + 5 : 4; @@ -103,15 +118,31 @@ inline int TsContinuityCounter(const uchar *p) return p[3] & TS_CONT_CNT_MASK; } -inline int TsGetAdaptationField(const uchar *p) +inline int64_t TsGetPcr(const uchar *p) { - return TsHasAdaptationField(p) ? p[5] : 0x00; + if (TsHasAdaptationField(p)) { + if (p[4] >= 7 && (p[5] & TS_ADAPT_PCR)) { + return ((((int64_t)p[ 6]) << 25) | + (((int64_t)p[ 7]) << 17) | + (((int64_t)p[ 8]) << 9) | + (((int64_t)p[ 9]) << 1) | + (((int64_t)p[10]) >> 7)) * PCRFACTOR + + (((((int)p[10]) & 0x01) << 8) | + ( ((int)p[11]))); + } + } + return -1; } +void TsHidePayload(uchar *p); +void TsSetPcr(uchar *p, int64_t Pcr); + // The following functions all take a pointer to a sequence of complete TS packets. int64_t TsGetPts(const uchar *p, int l); -void TsSetTeiOnBrokenPackets(uchar *p, int l); +int64_t TsGetDts(const uchar *p, int l); +void TsSetPts(uchar *p, int l, int64_t Pts); +void TsSetDts(uchar *p, int l, int64_t Dts); // Some PES handling tools: // The following functions that take a pointer to PES data all assume that @@ -142,6 +173,11 @@ inline bool PesHasPts(const uchar *p) return (p[7] & 0x80) && p[8] >= 5; } +inline bool PesHasDts(const uchar *p) +{ + return (p[7] & 0x40) && p[8] >= 10; +} + inline int64_t PesGetPts(const uchar *p) { return ((((int64_t)p[ 9]) & 0x0E) << 29) | @@ -151,6 +187,88 @@ inline int64_t PesGetPts(const uchar *p) ((((int64_t)p[13]) & 0xFE) >> 1); } +inline int64_t PesGetDts(const uchar *p) +{ + return ((((int64_t)p[14]) & 0x0E) << 29) | + (( (int64_t)p[15]) << 22) | + ((((int64_t)p[16]) & 0xFE) << 14) | + (( (int64_t)p[17]) << 7) | + ((((int64_t)p[18]) & 0xFE) >> 1); +} + +void PesSetPts(uchar *p, int64_t Pts); +void PesSetDts(uchar *p, int64_t Dts); + +// PTS handling: + +inline int64_t PtsAdd(int64_t Pts1, int64_t Pts2) { return (Pts1 + Pts2) & MAX33BIT; } + ///< Adds the given PTS values, taking into account the 33bit wrap around. +int64_t PtsDiff(int64_t Pts1, int64_t Pts2); + ///< Returns the difference between two PTS values. The result of Pts2 - Pts1 + ///< is the actual number of 90kHz time ticks that pass from Pts1 to Pts2, + ///< properly taking into account the 33bit wrap around. If Pts2 is "before" + ///< Pts1, the result is negative. + +// A transprent TS payload handler: + +class cTsPayload { +private: + uchar *data; + int length; + int pid; + int index; // points to the next byte to process +public: + cTsPayload(void); + cTsPayload(uchar *Data, int Length, int Pid = -1); + ///< Creates a new TS payload handler and calls Setup() with the given Data. + void Setup(uchar *Data, int Length, int Pid = -1); + ///< Sets up this TS payload handler with the given Data, which points to a + ///< sequence of Length bytes of complete TS packets. Any incomplete TS + ///< packet at the end will be ignored. + ///< If Pid is given, only TS packets with data for that PID will be processed. + ///< Otherwise the PID of the first TS packet defines which payload will be + ///< delivered. + ///< Any intermediate TS packets with different PIDs will be skipped. + bool AtTsStart(void) { return index < length && (index % TS_SIZE) == 0; } + ///< Returns true if this payload handler is currently pointing to first byte + ///< of a TS packet. + bool AtPayloadStart(void) { return AtTsStart() && TsPayloadStart(data + index); } + ///< Returns true if this payload handler is currently pointing to the first byte + ///< of a TS packet that starts a new payload. + int Available(void) { return length - index; } + ///< Returns the number of raw bytes (including any TS headers) still available + ///< in the TS payload handler. + int Used(void) { return (index + TS_SIZE - 1) / TS_SIZE * TS_SIZE; } + ///< Returns the number of raw bytes that have already been used (e.g. by calling + ///< GetByte()). Any TS packet of which at least a single byte has been delivered + ///< is counted with its full size. + bool Eof(void) const { return index >= length; } + ///< Returns true if all available bytes of the TS payload have been processed. + uchar GetByte(void); + ///< Gets the next byte of the TS payload, skipping any intermediate TS header data. + bool SkipBytes(int Bytes); + ///< Skips the given number of bytes in the payload and returns true if there + ///< is still data left to read. + bool SkipPesHeader(void); + ///< Skips all bytes belonging to the PES header of the payload. + int GetLastIndex(void); + ///< Returns the index into the TS data of the payload byte that has most recently + ///< been read. If no byte has been read yet, -1 will be returned. + void SetByte(uchar Byte, int Index); + ///< Sets the TS data byte at the given Index to the value Byte. + ///< Index should be one that has been retrieved by a previous call to GetIndex(), + ///< otherwise the behaviour is undefined. The current read index will not be + ///< altered by a call to this function. + bool Find(uint32_t Code); + ///< Searches for the four byte sequence given in Code and returns true if it + ///< was found within the payload data. The next call to GetByte() will return the + ///< value immediately following the Code. If the code was not found, the read + ///< index will remain the same as before this call, so that several calls to + ///< Find() can be performed starting at the same index.. + ///< The special code 0xFFFFFFFF can not be searched, because this value is used + ///< to initialize the scanner. + }; + // PAT/PMT Generator: #define MAX_SECTION_SIZE 4096 // maximum size of an SI section @@ -248,6 +366,10 @@ public: ///< are delivered to the parser through several subsequent calls to ///< ParsePmt(). The whole PMT data will be processed once the last packet ///< has been received. + bool ParsePatPmt(const uchar *Data, int Length); + ///< Parses the given Data (which may consist of several TS packets, typically + ///< an entire frame) and extracts the PAT and PMT. + ///< Returns true if a valid PAT/PMT has been detected. bool GetVersions(int &PatVersion, int &PmtVersion) const; ///< Returns true if a valid PAT/PMT has been parsed and stores ///< the current version numbers in the given variables. @@ -338,6 +460,8 @@ void PesDump(const char *Name, const u_char *Data, int Length); #define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 5 +class cFrameParser; + class cFrameDetector { private: enum { MaxPtsValues = 150 }; @@ -354,12 +478,9 @@ private: double framesPerSecond; int framesInPayloadUnit; int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1), - // some put an entire GOP into one payload unit (> 1), and - // some spread a single frame over several payload units (< 0). - int payloadUnitOfFrame; + // while others put an entire GOP into one payload unit (> 1). bool scanning; - uint32_t scanner; - int SkipPackets(const uchar *&Data, int &Length, int &Processed, int &FrameTypeOffset); + cFrameParser *parser; public: cFrameDetector(int Pid = 0, int Type = 0); ///< Sets up a frame detector for the given Pid and stream Type. @@ -367,9 +488,6 @@ public: ///< call to SetPid(). void SetPid(int Pid, int Type); ///< Sets the Pid and stream Type to detect frames for. - void Reset(void); - ///< Resets any counters and flags used while syncing and prepares - ///< the frame detector for actual work. int Analyze(const uchar *Data, int Length); ///< 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. @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: sections.c 2.1 2012/08/26 12:53:39 kls Exp $ + * $Id: sections.c 2.2 2012/10/04 12:21:59 kls Exp $ */ #include "sections.h" @@ -40,7 +40,7 @@ public: // --- cSectionHandler ------------------------------------------------------- cSectionHandler::cSectionHandler(cDevice *Device) -:cThread("section handler") +:cThread("section handler", true) { shp = new cSectionHandlerPrivate; device = Device; @@ -164,7 +164,6 @@ void cSectionHandler::SetStatus(bool On) void cSectionHandler::Action(void) { - SetPriority(19); while (Running()) { Lock(); diff --git a/skinlcars.h b/skinlcars.h index 71117f6..7306d12 100644 --- a/skinlcars.h +++ b/skinlcars.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: skinlcars.h 2.1 2012/04/15 13:17:35 kls Exp $ + * $Id: skinlcars.h 1.1 2012/04/15 13:17:35 kls Exp $ */ #ifndef __SKINLCARS_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: themes.h 2.0 2007/08/05 14:10:22 kls Exp $ + * $Id: themes.h 2.1 2012/10/07 11:11:43 kls Exp $ */ #ifndef __THEMES_H @@ -56,7 +56,7 @@ public: }; // A helper macro that simplifies defining theme colors. -#define THEME_CLR(Theme, Subject, Color) static const int Subject = Theme.AddColor(#Subject, Color) +#define THEME_CLR(Theme, Subject, Color) static const tColor Subject = Theme.AddColor(#Subject, Color) class cThemes { private: @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.c 2.5 2012/09/20 09:05:50 kls Exp $ + * $Id: thread.c 2.6 2012/10/04 12:20:43 kls Exp $ */ #include "thread.h" @@ -204,7 +204,7 @@ void cMutex::Unlock(void) tThreadId cThread::mainThreadId = 0; -cThread::cThread(const char *Description) +cThread::cThread(const char *Description, bool LowPriority) { active = running = false; childTid = 0; @@ -212,6 +212,7 @@ cThread::cThread(const char *Description) description = NULL; if (Description) SetDescription("%s", Description); + lowPriority = LowPriority; } cThread::~cThread() @@ -248,12 +249,16 @@ void *cThread::StartThread(cThread *Thread) { Thread->childThreadId = ThreadId(); if (Thread->description) { - dsyslog("%s thread started (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); + dsyslog("%s thread started (pid=%d, tid=%d, prio=%s)", Thread->description, getpid(), Thread->childThreadId, Thread->lowPriority ? "low" : "high"); #ifdef PR_SET_NAME if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0) esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); #endif } + if (Thread->lowPriority) { + Thread->SetPriority(19); + Thread->SetIOPriority(7); + } Thread->Action(); if (Thread->description) dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.h 2.2 2012/09/20 08:46:27 kls Exp $ + * $Id: thread.h 2.3 2012/10/04 12:15:39 kls Exp $ */ #ifndef __THREAD_H @@ -83,6 +83,7 @@ private: tThreadId childThreadId; cMutex mutex; char *description; + bool lowPriority; static tThreadId mainThreadId; static void *StartThread(cThread *Thread); protected: @@ -106,11 +107,13 @@ protected: ///< If WaitSeconds is -1, only 'running' is set to false and Cancel() ///< returns immediately, without killing the thread. public: - cThread(const char *Description = NULL); + cThread(const char *Description = NULL, bool LowPriority = false); ///< Creates a new thread. ///< If Description is present, a log file entry will be made when ///< the thread starts and stops. The Start() function must be called ///< to actually start the thread. + ///< LowPriority can be set to true to make this thread run at a lower + ///< priority. virtual ~cThread(); void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3))); bool Start(void); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 2.12 2012/09/15 13:34:03 kls Exp $ + * $Id: timers.c 2.14 2012/10/16 08:22:39 kls Exp $ */ #include "timers.h" @@ -465,10 +465,11 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const startTime = event->StartTime(); stopTime = event->EndTime(); if (!Margin) { // this is an actual check - if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events... - return event->IsRunning(true); - else - return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling + if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) { // VPS control can only work with up-to-date events... + if (event->StartTime() > 0) // checks for "phased out" events + return event->IsRunning(true); + } + return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling } } } @@ -549,8 +550,12 @@ void cTimer::SetEventFromSchedule(const cSchedules *Schedules) lastSetEvent = now; const cEvent *Event = NULL; if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) { - if (event && Recording()) - return; // let the recording end first + if (event && event->StartTime() > 0) { // checks for "phased out" events + if (Recording()) + return; // let the recording end first + if (now <= event->EndTime() || Matches(0, true)) + return; // stay with the old event until the timer has completely expired + } // VPS timers only match if their start time exactly matches the event's VPS time: for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) { if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events @@ -562,8 +567,6 @@ void cTimer::SetEventFromSchedule(const cSchedules *Schedules) } } } - if (!Event && event && (now <= event->EndTime() || Matches(0, true))) - return; // stay with the old event until the timer has completely expired } else { // Normal timers match the event they have the most overlap with: @@ -8,7 +8,7 @@ .\" License as specified in the file COPYING that comes with the .\" vdr distribution. .\" -.\" $Id: vdr.5 2.29 2012/03/10 14:56:01 kls Exp $ +.\" $Id: vdr.5 2.30 2012/10/15 10:50:23 kls Exp $ .\" .TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files" .SH NAME @@ -815,13 +815,13 @@ least one blank. The lines in this file need not necessarily appear in the correct temporal sequence, they will be automatically sorted by time index. +If a frame position doesn't point to an I-frame of the corresponding recording, +it will be shifted towards the next I-frame (either up or down, whichever is +closer). + \fBCURRENT RESTRICTIONS:\fR -\ the comment is currently not used by VDR -.br --\ marks must have a frame number, and that frame MUST be an I-frame (this -means that only marks generated by VDR itself can be used, since they -will always be guaranteed to mark I-frames). .SS EPG DATA The file \fIepg.data\fR contains the EPG data in an easily parsable format. The first character of each line defines what kind of data this line contains. @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 2.40 2012/09/24 12:43:04 kls Exp $ + * $Id: vdr.c 2.42 2012/10/13 12:48:56 kls Exp $ */ #include <getopt.h> @@ -892,7 +892,7 @@ int main(int argc, char *argv[]) Timer->SetInVpsMargin(InVpsMargin); } else if (Timer->Event()) { - InVpsMargin = Timer->Event()->StartTime() <= Now && Timer->Event()->RunningStatus() == SI::RunningStatusUndefined; + InVpsMargin = Timer->Event()->StartTime() <= Now && Now < Timer->Event()->EndTime(); NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME); } else { @@ -1238,6 +1238,8 @@ int main(int argc, char *argv[]) cControl::Shutdown(); cControl::Launch(new cReplayControl); } + else + DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu break; default: break; } |