mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
Version 1.7.32
VDR developer version 1.7.32 is now available at ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.32.tar.bz2 A 'diff' against the previous version is available at ftp://ftp.tvdr.de/vdr/Developer/vdr-1.7.31-1.7.32.diff MD5 checksums: 068ba78fd427694dcc480fe3b2d07148 vdr-1.7.32.tar.bz2 222f1e9b4d4edaa6fe57286409614cc7 vdr-1.7.31-1.7.32.diff WARNING: ======== This is a developer version. Even though I use it in my productive environment. I strongly recommend that you only use it under controlled conditions and for testing and debugging. The main focus of this version is on an improved frame detection code, and improvements to the cutting process. When cutting a recording, VDR now removes any "dangling" TS packets from the beginning of an editing sequence and pulls in any "pending" TS packets at the end of a sequence. It also fixes all timestamps and continuity counters. However, while the results look much better now in, for instance, Kaffeine, the TT S2-6400 still shows some video artifacts at the editing points, and the Mac video player sometimes totally chokes on edited material. I did spend a lot of time trying to find out what could still be wrong here, but couldn't come up with any new ideas. So I think it's now time to invite others to test this new cutting code, read the source code and try to find out what's still going wrong here. Maybe (hopefully ;-) it's just some stupid little error... ;-) From the HISTORY file: - 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).
This commit is contained in:
parent
fa6845328c
commit
beffcabc81
13
CONTRIBUTORS
13
CONTRIBUTORS
@ -1757,6 +1757,9 @@ Udo Richter <udo_richter@gmx.de>
|
|||||||
for adding the option --outputonly to the dvbsddevice plugin
|
for adding the option --outputonly to the dvbsddevice plugin
|
||||||
for adding a missing template specification to the c'tor of cSortedTimers
|
for adding a missing template specification to the c'tor of cSortedTimers
|
||||||
for contributing to a patch that implements FHS support
|
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>
|
Sven Kreiensen <svenk@kammer.uni-hannover.de>
|
||||||
for his help in keeping 'channels.conf.terr' up to date
|
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
|
for fixing cRecording::LengthInSeconds(), which wrongfully rounded the result to full
|
||||||
minutes
|
minutes
|
||||||
for suggesting to check for NULL in cOsd::AddPixmap()
|
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>
|
Pekka Mauno <pekka.mauno@iki.fi>
|
||||||
for fixing cSchedule::GetFollowingEvent() in case there is currently no present
|
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 fixing the shutdown timeout
|
||||||
for making the script given to VDR with the '-r' option be also called whenever a
|
for making the script given to VDR with the '-r' option be also called whenever a
|
||||||
recording is deleted
|
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>
|
Jan Lenz <email@JanLenz.de>
|
||||||
for reporting a bug in deleting recordings that have been removed externally when
|
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),
|
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
|
that might inadvertently be called with a 'char *' as the second argument on some
|
||||||
compilers and cause a crash
|
compilers and cause a crash
|
||||||
|
for reporting a possible memory leak in SI::StructureLoop::getNextAsPointer()
|
||||||
|
|
||||||
Ales Jurik <ajurik@quick.cz>
|
Ales Jurik <ajurik@quick.cz>
|
||||||
for reporting broken SI data on Czech/Slovak channels after changing the default
|
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
|
several payloads
|
||||||
for reporting a problem with getting the maximum short channel name length in case there
|
for reporting a problem with getting the maximum short channel name length in case there
|
||||||
are no short names at all
|
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>
|
Marcel Unbehaun <frostworks@gmx.de>
|
||||||
for adding cRecordingInfo::GetEvent()
|
for adding cRecordingInfo::GetEvent()
|
||||||
@ -2895,6 +2904,7 @@ Torsten Lang <info@torstenlang.de>
|
|||||||
in case buffers run full
|
in case buffers run full
|
||||||
for suggesting to increase the size of the TS buffer to 5MB and that of the Recorder
|
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
|
buffer to 20MB to better handle HD recordings
|
||||||
|
for fixing setting the video format in the dvbhdffdevice
|
||||||
|
|
||||||
Christian Ruppert <idl0r@gentoo.org>
|
Christian Ruppert <idl0r@gentoo.org>
|
||||||
for some improvements to the Makefiles
|
for some improvements to the Makefiles
|
||||||
@ -2960,3 +2970,6 @@ S
|
|||||||
for a patch that was used to move cleaning up the EPG data and writing the epg.data
|
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
|
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
|
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
|
||||||
|
67
HISTORY
67
HISTORY
@ -7271,3 +7271,70 @@ Video Disk Recorder Revision History
|
|||||||
whether a directory is empty. This allows users to continue to use files such as
|
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
|
".keep" to prevent a directory from being deleted when it is empty. Currently the
|
||||||
only file name that is ignored is ".sort".
|
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).
|
||||||
|
4
INSTALL
4
INSTALL
@ -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
|
named 'vdr', which can be run after the DVB driver has been
|
||||||
installed.
|
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
|
IMPORTANT: See "Configuration files" below for information on how
|
||||||
========= to set up the configuration files at the proper location!
|
========= to set up the configuration files at the proper location!
|
||||||
|
|
||||||
|
20
MANUAL
20
MANUAL
@ -367,13 +367,13 @@ Version 1.6
|
|||||||
- 7, 9 Jump back and forward between editing marks. Replay goes into still
|
- 7, 9 Jump back and forward between editing marks. Replay goes into still
|
||||||
mode after jumping to a mark.
|
mode after jumping to a mark.
|
||||||
- 8 Positions replay at a point 3 seconds before the current or next
|
- 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.
|
- 2 Start the actual cutting process.
|
||||||
|
|
||||||
Editing marks are represented by black, vertical lines in the progress display.
|
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.
|
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
|
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
|
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
|
(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
|
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.
|
"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
|
6,...) is an "end" mark. Inserting or toggling a mark on or off automatically
|
||||||
adjusts the sequence to the right side of that mark.
|
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
|
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.
|
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
|
Currently editing marks can only be set at I-frames, which typically appear
|
||||||
every 12th frame. So editing can be done with a resolution of roughly half
|
every half of a second to a second. A "begin" mark marks the first frame of
|
||||||
a second. A "start" mark marks the first frame of a resulting video
|
a resulting video sequence, and an "end" mark marks the last frame of that
|
||||||
sequence, and an "end" mark marks the last frame of that sequence.
|
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
|
An edited recording (indicated by the '%' character) will never be deleted
|
||||||
automatically in case the disk runs full (no matter what "lifetime" it has).
|
automatically in case the disk runs full (no matter what "lifetime" it has).
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Make.config.template 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:
|
### The C compiler and options:
|
||||||
|
|
||||||
@ -21,6 +21,12 @@ CFLAGS += -fPIC
|
|||||||
CXXFLAGS += -fPIC
|
CXXFLAGS += -fPIC
|
||||||
endif
|
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:
|
### The directory environment:
|
||||||
|
|
||||||
PREFIX = $(DESTDIR)/usr/local
|
PREFIX = $(DESTDIR)/usr/local
|
||||||
|
4
Makefile
4
Makefile
@ -4,7 +4,7 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Makefile 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:
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
@ -211,7 +211,7 @@ install-dirs:
|
|||||||
@mkdir -p $(DESTDIR)$(RESDIRDEF)
|
@mkdir -p $(DESTDIR)$(RESDIRDEF)
|
||||||
|
|
||||||
install-conf:
|
install-conf:
|
||||||
@cp *.conf $(DESTDIR)$(CONFDIRDEF)
|
@cp -n *.conf $(DESTDIR)$(CONFDIRDEF)
|
||||||
|
|
||||||
|
|
||||||
# Documentation:
|
# Documentation:
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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>
|
#include <stdint.h>
|
||||||
@ -69,6 +69,7 @@ cDvbHdFfDevice::cDvbHdFfDevice(int Adapter, int Frontend)
|
|||||||
|
|
||||||
mHdffCmdIf->CmdAvSetAudioDelay(gHdffSetup.AudioDelay);
|
mHdffCmdIf->CmdAvSetAudioDelay(gHdffSetup.AudioDelay);
|
||||||
mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) gHdffSetup.AudioDownmix);
|
mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) gHdffSetup.AudioDownmix);
|
||||||
|
mHdffCmdIf->CmdAvSetSyncShift(gHdffSetup.AvSyncShift);
|
||||||
mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) gHdffSetup.AnalogueVideo);
|
mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) gHdffSetup.AnalogueVideo);
|
||||||
mHdffCmdIf->CmdHdmiSetVideoMode(gHdffSetup.GetVideoMode());
|
mHdffCmdIf->CmdHdmiSetVideoMode(gHdffSetup.GetVideoMode());
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ void cDvbHdFfDevice::SetVideoFormat(bool VideoFormat16_9)
|
|||||||
{
|
{
|
||||||
HdffVideoFormat_t videoFormat;
|
HdffVideoFormat_t videoFormat;
|
||||||
videoFormat.AutomaticEnabled = true;
|
videoFormat.AutomaticEnabled = true;
|
||||||
videoFormat.AfdEnabled = true;
|
videoFormat.AfdEnabled = false;
|
||||||
videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat;
|
videoFormat.TvFormat = (HdffTvFormat_t) gHdffSetup.TvFormat;
|
||||||
videoFormat.VideoConversion = (HdffVideoConversion_t) gHdffSetup.VideoConversion;
|
videoFormat.VideoConversion = (HdffVideoConversion_t) gHdffSetup.VideoConversion;
|
||||||
mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
|
mHdffCmdIf->CmdAvSetVideoFormat(0, &videoFormat);
|
||||||
@ -376,6 +377,11 @@ bool cDvbHdFfDevice::CanReplay(void) const
|
|||||||
bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode)
|
bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode)
|
||||||
{
|
{
|
||||||
if (PlayMode == pmNone) {
|
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);
|
||||||
|
|
||||||
mHdffCmdIf->CmdAvSetVideoSpeed(0, 100);
|
mHdffCmdIf->CmdAvSetVideoSpeed(0, 100);
|
||||||
mHdffCmdIf->CmdAvSetAudioSpeed(0, 100);
|
mHdffCmdIf->CmdAvSetAudioSpeed(0, 100);
|
||||||
|
|
||||||
@ -393,9 +399,18 @@ bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode)
|
|||||||
if (playMode == pmNone)
|
if (playMode == pmNone)
|
||||||
TurnOffLiveMode(true);
|
TurnOffLiveMode(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->CmdAvSetPlayMode(1, Transferring() || (cTransferControl::ReceiverDevice() == this));
|
||||||
mHdffCmdIf->CmdAvSetStc(0, 100000);
|
mHdffCmdIf->CmdAvSetStc(0, 100000);
|
||||||
mHdffCmdIf->CmdAvEnableSync(0, true);
|
mHdffCmdIf->CmdAvEnableSync(0, false);
|
||||||
mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true);
|
mHdffCmdIf->CmdAvEnableVideoAfterStop(0, true);
|
||||||
|
|
||||||
playVideoPid = -1;
|
playVideoPid = -1;
|
||||||
@ -404,15 +419,19 @@ bool cDvbHdFfDevice::SetPlayMode(ePlayMode PlayMode)
|
|||||||
videoCounter = 0;
|
videoCounter = 0;
|
||||||
freezed = false;
|
freezed = false;
|
||||||
trickMode = false;
|
trickMode = false;
|
||||||
|
isPlayingVideo = false;
|
||||||
|
|
||||||
mHdffCmdIf->CmdAvSetDecoderInput(0, 2);
|
mHdffCmdIf->CmdAvSetDecoderInput(0, 2);
|
||||||
ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
|
ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
playMode = PlayMode;
|
playMode = PlayMode;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t cDvbHdFfDevice::GetSTC(void)
|
int64_t cDvbHdFfDevice::GetSTC(void)
|
||||||
|
{
|
||||||
|
if (isPlayingVideo)
|
||||||
{
|
{
|
||||||
if (fd_video >= 0) {
|
if (fd_video >= 0) {
|
||||||
uint64_t pts;
|
uint64_t pts;
|
||||||
@ -420,16 +439,22 @@ int64_t cDvbHdFfDevice::GetSTC(void)
|
|||||||
esyslog("ERROR: pts %d: %m", CardIndex() + 1);
|
esyslog("ERROR: pts %d: %m", CardIndex() + 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
//printf("video PTS %lld\n", pts);
|
||||||
return pts;
|
return pts;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (fd_audio >= 0) {
|
if (fd_audio >= 0) {
|
||||||
uint64_t pts;
|
uint64_t pts;
|
||||||
if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) {
|
if (ioctl(fd_audio, AUDIO_GET_PTS, &pts) == -1) {
|
||||||
esyslog("ERROR: pts %d: %m", CardIndex() + 1);
|
esyslog("ERROR: pts %d: %m", CardIndex() + 1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
//printf("audio PTS %lld\n", pts);
|
||||||
return pts;
|
return pts;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,6 +483,7 @@ void cDvbHdFfDevice::Play(void)
|
|||||||
{
|
{
|
||||||
freezed = false;
|
freezed = false;
|
||||||
trickMode = false;
|
trickMode = false;
|
||||||
|
if (isPlayingVideo)
|
||||||
mHdffCmdIf->CmdAvEnableSync(0, true);
|
mHdffCmdIf->CmdAvEnableSync(0, true);
|
||||||
mHdffCmdIf->CmdAvSetVideoSpeed(0, 100);
|
mHdffCmdIf->CmdAvSetVideoSpeed(0, 100);
|
||||||
mHdffCmdIf->CmdAvSetAudioSpeed(0, 100);
|
mHdffCmdIf->CmdAvSetAudioSpeed(0, 100);
|
||||||
@ -631,6 +657,11 @@ int cDvbHdFfDevice::PlayVideo(const uchar *Data, int Length)
|
|||||||
{
|
{
|
||||||
if (freezed)
|
if (freezed)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (!isPlayingVideo)
|
||||||
|
{
|
||||||
|
mHdffCmdIf->CmdAvEnableSync(0, true);
|
||||||
|
isPlayingVideo = true;
|
||||||
|
}
|
||||||
//TODO: support greater Length
|
//TODO: support greater Length
|
||||||
uint8_t tsBuffer[188 * 16];
|
uint8_t tsBuffer[188 * 16];
|
||||||
uint32_t tsLength;
|
uint32_t tsLength;
|
||||||
@ -651,8 +682,6 @@ int cDvbHdFfDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
|
|||||||
{
|
{
|
||||||
if (freezed)
|
if (freezed)
|
||||||
return -1;
|
return -1;
|
||||||
if (trickMode)
|
|
||||||
return Length;
|
|
||||||
uint8_t streamId;
|
uint8_t streamId;
|
||||||
uint8_t tsBuffer[188 * 16];
|
uint8_t tsBuffer[188 * 16];
|
||||||
uint32_t tsLength;
|
uint32_t tsLength;
|
||||||
@ -704,6 +733,12 @@ int cDvbHdFfDevice::PlayTsVideo(const uchar *Data, int Length)
|
|||||||
{
|
{
|
||||||
if (freezed)
|
if (freezed)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (!isPlayingVideo)
|
||||||
|
{
|
||||||
|
mHdffCmdIf->CmdAvEnableSync(0, true);
|
||||||
|
isPlayingVideo = true;
|
||||||
|
}
|
||||||
|
|
||||||
int pid = TsPid(Data);
|
int pid = TsPid(Data);
|
||||||
if (pid != playVideoPid) {
|
if (pid != playVideoPid) {
|
||||||
PatPmtParser();
|
PatPmtParser();
|
||||||
@ -732,8 +767,6 @@ int cDvbHdFfDevice::PlayTsAudio(const uchar *Data, int Length)
|
|||||||
{
|
{
|
||||||
if (freezed)
|
if (freezed)
|
||||||
return -1;
|
return -1;
|
||||||
if (trickMode)
|
|
||||||
return Length;
|
|
||||||
int pid = TsPid(Data);
|
int pid = TsPid(Data);
|
||||||
if (pid != playAudioPid) {
|
if (pid != playAudioPid) {
|
||||||
playAudioPid = pid;
|
playAudioPid = pid;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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
|
#ifndef __DVBHDFFDEVICE_H
|
||||||
@ -81,6 +81,7 @@ private:
|
|||||||
int playAudioPid;
|
int playAudioPid;
|
||||||
bool freezed;
|
bool freezed;
|
||||||
bool trickMode;
|
bool trickMode;
|
||||||
|
bool isPlayingVideo;
|
||||||
|
|
||||||
// Pes2Ts conversion stuff
|
// Pes2Ts conversion stuff
|
||||||
uint8_t videoCounter;
|
uint8_t videoCounter;
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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>
|
#include <stdint.h>
|
||||||
@ -170,6 +170,11 @@ void cHdffCmdIf::CmdAvSetAudioChannel(uint8_t AudioChannel)
|
|||||||
HdffCmdAvSetAudioChannel(mOsdDev, AudioChannel);
|
HdffCmdAvSetAudioChannel(mOsdDev, AudioChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cHdffCmdIf::CmdAvSetSyncShift(int16_t SyncShift)
|
||||||
|
{
|
||||||
|
HdffCmdAvSetSyncShift(mOsdDev, SyncShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void cHdffCmdIf::CmdOsdConfigure(const HdffOsdConfig_t * pConfig)
|
void cHdffCmdIf::CmdOsdConfigure(const HdffOsdConfig_t * pConfig)
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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_
|
#ifndef _HDFF_CMD_H_
|
||||||
@ -47,6 +47,7 @@ public:
|
|||||||
void CmdAvSetAudioDelay(int16_t Delay);
|
void CmdAvSetAudioDelay(int16_t Delay);
|
||||||
void CmdAvSetAudioDownmix(HdffAudioDownmixMode_t DownmixMode);
|
void CmdAvSetAudioDownmix(HdffAudioDownmixMode_t DownmixMode);
|
||||||
void CmdAvSetAudioChannel(uint8_t AudioChannel);
|
void CmdAvSetAudioChannel(uint8_t AudioChannel);
|
||||||
|
void CmdAvSetSyncShift(int16_t SyncShift);
|
||||||
|
|
||||||
void CmdOsdConfigure(const HdffOsdConfig_t * pConfig);
|
void CmdOsdConfigure(const HdffOsdConfig_t * pConfig);
|
||||||
void CmdOsdReset(void);
|
void CmdOsdReset(void);
|
||||||
|
@ -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
|
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#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)
|
if (mSupportsUtf8Text)
|
||||||
{
|
{
|
||||||
mHdffCmdIf->CmdOsdDrawUtf8Text(mDisplay, pFont->Handle, x + mLeft, y + mTop + h, s, ColorFg);
|
mHdffCmdIf->CmdOsdDrawUtf8Text(mDisplay, pFont->Handle, x + mLeft, y + mTop + h, s, ColorFg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
uint16_t tmp[1000];
|
uint16_t tmp[1000];
|
||||||
uint16_t len = 0;
|
uint16_t len = 0;
|
||||||
|
@ -24,6 +24,18 @@ CFLAGS ?= -g -O2 -fPIC -Wall
|
|||||||
AR ?= ar
|
AR ?= ar
|
||||||
ARFLAGS ?= r
|
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:
|
### Implicit rules:
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
@ -449,3 +449,22 @@ int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel)
|
|||||||
osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf);
|
osd_cmd.cmd_len = HdffCmdSetLength(&cmdBuf);
|
||||||
return ioctl(OsdDevice, OSD_RAW_CMD, &osd_cmd);
|
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);
|
||||||
|
}
|
||||||
|
@ -149,4 +149,6 @@ int HdffCmdAvSetAudioDownmix(int OsdDevice,
|
|||||||
|
|
||||||
int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel);
|
int HdffCmdAvSetAudioChannel(int OsdDevice, uint8_t AudioChannel);
|
||||||
|
|
||||||
|
int HdffCmdAvSetSyncShift(int OsdDevice, int16_t SyncShift);
|
||||||
|
|
||||||
#endif /* HDFFCMD_AV_H */
|
#endif /* HDFFCMD_AV_H */
|
||||||
|
@ -70,6 +70,7 @@ typedef enum HdffMessageId_t
|
|||||||
HDFF_MSG_AV_SET_AUDIO_DOWNMIX,
|
HDFF_MSG_AV_SET_AUDIO_DOWNMIX,
|
||||||
HDFF_MSG_AV_SET_AUDIO_CHANNEL,
|
HDFF_MSG_AV_SET_AUDIO_CHANNEL,
|
||||||
HDFF_MSG_AV_SET_PLAY_MODE,
|
HDFF_MSG_AV_SET_PLAY_MODE,
|
||||||
|
HDFF_MSG_AV_SET_OPTIONS,
|
||||||
|
|
||||||
HDFF_MSG_MUX_SET_VIDEO_OUT = 0,
|
HDFF_MSG_MUX_SET_VIDEO_OUT = 0,
|
||||||
HDFF_MSG_MUX_SET_SLOW_BLANK,
|
HDFF_MSG_MUX_SET_SLOW_BLANK,
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR \n"
|
"Project-Id-Version: VDR \n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\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"
|
"PO-Revision-Date: 2011-04-25 21:44+0200\n"
|
||||||
"Last-Translator: Christoph Haubrich\n"
|
"Last-Translator: Christoph Haubrich\n"
|
||||||
"Language-Team: <see README>\n"
|
"Language-Team: <see README>\n"
|
||||||
@ -91,6 +91,9 @@ msgstr "Audio Verzögerung (ms)"
|
|||||||
msgid "Audio Downmix"
|
msgid "Audio Downmix"
|
||||||
msgstr "Audio Downmix"
|
msgstr "Audio Downmix"
|
||||||
|
|
||||||
|
msgid "A/V Sync Shift (ms)"
|
||||||
|
msgstr "A/V-Sync Verschiebung (ms)"
|
||||||
|
|
||||||
msgid "OSD Size"
|
msgid "OSD Size"
|
||||||
msgstr "OSD Größe"
|
msgstr "OSD Größe"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR \n"
|
"Project-Id-Version: VDR \n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\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"
|
"PO-Revision-Date: 2011-04-25 21:44+0200\n"
|
||||||
"Last-Translator: Rolf Ahrenberg\n"
|
"Last-Translator: Rolf Ahrenberg\n"
|
||||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
@ -91,6 +91,9 @@ msgstr "Äänen viive (ms)"
|
|||||||
msgid "Audio Downmix"
|
msgid "Audio Downmix"
|
||||||
msgstr "Äänen alasmiksaus"
|
msgstr "Äänen alasmiksaus"
|
||||||
|
|
||||||
|
msgid "A/V Sync Shift (ms)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OSD Size"
|
msgid "OSD Size"
|
||||||
msgstr "Kuvaruutunäytön koko"
|
msgstr "Kuvaruutunäytön koko"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR\n"
|
"Project-Id-Version: VDR\n"
|
||||||
"Report-Msgid-Bugs-To: <see README>\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"
|
"PO-Revision-Date: 2011-07-10 00:23+0100\n"
|
||||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||||
"Language-Team: <see README>\n"
|
"Language-Team: <see README>\n"
|
||||||
@ -94,6 +94,9 @@ msgstr "Ritardo audio (ms)"
|
|||||||
msgid "Audio Downmix"
|
msgid "Audio Downmix"
|
||||||
msgstr "Scala Audio"
|
msgstr "Scala Audio"
|
||||||
|
|
||||||
|
msgid "A/V Sync Shift (ms)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "OSD Size"
|
msgid "OSD Size"
|
||||||
msgstr "Dimensione OSD"
|
msgstr "Dimensione OSD"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "setup.h"
|
||||||
@ -26,6 +26,7 @@ cHdffSetup::cHdffSetup(void)
|
|||||||
AnalogueVideo = HDFF_VIDEO_OUT_CVBS_YUV;
|
AnalogueVideo = HDFF_VIDEO_OUT_CVBS_YUV;
|
||||||
AudioDelay = 0;
|
AudioDelay = 0;
|
||||||
AudioDownmix = HDFF_AUDIO_DOWNMIX_AUTOMATIC;
|
AudioDownmix = HDFF_AUDIO_DOWNMIX_AUTOMATIC;
|
||||||
|
AvSyncShift = 0;
|
||||||
OsdSize = 0;
|
OsdSize = 0;
|
||||||
CecEnabled = 1;
|
CecEnabled = 1;
|
||||||
CecTvOn = 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, "AnalogueVideo") == 0) AnalogueVideo = atoi(Value);
|
||||||
else if (strcmp(Name, "AudioDelay") == 0) AudioDelay = atoi(Value);
|
else if (strcmp(Name, "AudioDelay") == 0) AudioDelay = atoi(Value);
|
||||||
else if (strcmp(Name, "AudioDownmix") == 0) AudioDownmix = 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, "OsdSize") == 0) OsdSize = atoi(Value);
|
||||||
else if (strcmp(Name, "CecEnabled") == 0) CecEnabled = atoi(Value);
|
else if (strcmp(Name, "CecEnabled") == 0) CecEnabled = atoi(Value);
|
||||||
else if (strcmp(Name, "CecTvOn") == 0) CecTvOn = 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 cMenuEditStraItem(tr("Analogue Video"), &mNewHdffSetup.AnalogueVideo, kAnalogueVideos, AnalogueVideoItems));
|
||||||
Add(new cMenuEditIntItem(tr("Audio Delay (ms)"), &mNewHdffSetup.AudioDelay, 0, 500));
|
Add(new cMenuEditIntItem(tr("Audio Delay (ms)"), &mNewHdffSetup.AudioDelay, 0, 500));
|
||||||
Add(new cMenuEditStraItem(tr("Audio Downmix"), &mNewHdffSetup.AudioDownmix, kAudioDownmixes, AudioDownmixItems));
|
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 cMenuEditStraItem(tr("OSD Size"), &mNewHdffSetup.OsdSize, kOsdSizes, OsdSizeItems));
|
||||||
Add(new cMenuEditBoolItem(tr("HDMI CEC"), &mNewHdffSetup.CecEnabled));
|
Add(new cMenuEditBoolItem(tr("HDMI CEC"), &mNewHdffSetup.CecEnabled));
|
||||||
Add(new cMenuEditBoolItem(tr("CEC: Switch TV on"), &mNewHdffSetup.CecTvOn));
|
Add(new cMenuEditBoolItem(tr("CEC: Switch TV on"), &mNewHdffSetup.CecTvOn));
|
||||||
@ -385,6 +388,7 @@ void cHdffSetupPage::Store(void)
|
|||||||
SetupStore("AnalogueVideo", mNewHdffSetup.AnalogueVideo);
|
SetupStore("AnalogueVideo", mNewHdffSetup.AnalogueVideo);
|
||||||
SetupStore("AudioDelay", mNewHdffSetup.AudioDelay);
|
SetupStore("AudioDelay", mNewHdffSetup.AudioDelay);
|
||||||
SetupStore("AudioDownmix", mNewHdffSetup.AudioDownmix);
|
SetupStore("AudioDownmix", mNewHdffSetup.AudioDownmix);
|
||||||
|
SetupStore("AvSyncShift", mNewHdffSetup.AvSyncShift);
|
||||||
SetupStore("OsdSize", mNewHdffSetup.OsdSize);
|
SetupStore("OsdSize", mNewHdffSetup.OsdSize);
|
||||||
SetupStore("CecEnabled", mNewHdffSetup.CecEnabled);
|
SetupStore("CecEnabled", mNewHdffSetup.CecEnabled);
|
||||||
SetupStore("CecTvOn", mNewHdffSetup.CecTvOn);
|
SetupStore("CecTvOn", mNewHdffSetup.CecTvOn);
|
||||||
@ -412,6 +416,7 @@ void cHdffSetupPage::Store(void)
|
|||||||
|
|
||||||
mHdffCmdIf->CmdAvSetAudioDelay(mNewHdffSetup.AudioDelay);
|
mHdffCmdIf->CmdAvSetAudioDelay(mNewHdffSetup.AudioDelay);
|
||||||
mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) mNewHdffSetup.AudioDownmix);
|
mHdffCmdIf->CmdAvSetAudioDownmix((HdffAudioDownmixMode_t) mNewHdffSetup.AudioDownmix);
|
||||||
|
mHdffCmdIf->CmdAvSetSyncShift(mNewHdffSetup.AvSyncShift);
|
||||||
|
|
||||||
mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) mNewHdffSetup.AnalogueVideo);
|
mHdffCmdIf->CmdMuxSetVideoOut((HdffVideoOut_t) mNewHdffSetup.AnalogueVideo);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* See the README file for copyright information and how to reach the author.
|
* See the README file for copyright information and how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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_
|
#ifndef _HDFF_SETUP_H_
|
||||||
@ -28,6 +28,7 @@ struct cHdffSetup
|
|||||||
int AnalogueVideo;
|
int AnalogueVideo;
|
||||||
int AudioDelay;
|
int AudioDelay;
|
||||||
int AudioDownmix;
|
int AudioDownmix;
|
||||||
|
int AvSyncShift;
|
||||||
int OsdSize;
|
int OsdSize;
|
||||||
int CecEnabled;
|
int CecEnabled;
|
||||||
int CecTvOn;
|
int CecTvOn;
|
||||||
|
6
ci.c
6
ci.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "ci.h"
|
||||||
@ -845,9 +845,9 @@ void cCiDateTime::SendDateTime(void)
|
|||||||
int D = tm_gmt.tm_mday;
|
int D = tm_gmt.tm_mday;
|
||||||
int L = (M == 1 || M == 2) ? 1 : 0;
|
int L = (M == 1 || M == 2) ? 1 : 0;
|
||||||
int MJD = 14956 + D + int((Y - L) * 365.25) + int((M + 1 + L * 12) * 30.6001);
|
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; };
|
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;
|
bool OldDumpTPDUDataTransfer = DumpTPDUDataTransfer;
|
||||||
DumpTPDUDataTransfer &= DumpDateTime;
|
DumpTPDUDataTransfer &= DumpDateTime;
|
||||||
if (DumpDateTime)
|
if (DumpDateTime)
|
||||||
|
10
config.h
10
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.h 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
|
#ifndef __CONFIG_H
|
||||||
@ -22,13 +22,13 @@
|
|||||||
|
|
||||||
// VDR's own version number:
|
// VDR's own version number:
|
||||||
|
|
||||||
#define VDRVERSION "1.7.31"
|
#define VDRVERSION "1.7.32"
|
||||||
#define VDRVERSNUM 10731 // Version * 10000 + Major * 100 + Minor
|
#define VDRVERSNUM 10732 // Version * 10000 + Major * 100 + Minor
|
||||||
|
|
||||||
// The plugin API's version number:
|
// The plugin API's version number:
|
||||||
|
|
||||||
#define APIVERSION "1.7.31"
|
#define APIVERSION "1.7.32"
|
||||||
#define APIVERSNUM 10731 // Version * 10000 + Major * 100 + Minor
|
#define APIVERSNUM 10732 // Version * 10000 + Major * 100 + Minor
|
||||||
|
|
||||||
// When loading plugins, VDR searches them by their APIVERSION, which
|
// When loading plugins, VDR searches them by their APIVERSION, which
|
||||||
// may be smaller than VDRVERSION in case there have been no changes to
|
// may be smaller than VDRVERSION in case there have been no changes to
|
||||||
|
584
cutter.c
584
cutter.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "cutter.h"
|
||||||
@ -13,17 +13,261 @@
|
|||||||
#include "remux.h"
|
#include "remux.h"
|
||||||
#include "videodir.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 --------------------------------------------------------
|
// --- cCuttingThread --------------------------------------------------------
|
||||||
|
|
||||||
class cCuttingThread : public cThread {
|
class cCuttingThread : public cThread {
|
||||||
private:
|
private:
|
||||||
const char *error;
|
const char *error;
|
||||||
bool isPesRecording;
|
bool isPesRecording;
|
||||||
|
double framesPerSecond;
|
||||||
cUnbufferedFile *fromFile, *toFile;
|
cUnbufferedFile *fromFile, *toFile;
|
||||||
cFileName *fromFileName, *toFileName;
|
cFileName *fromFileName, *toFileName;
|
||||||
cIndexFile *fromIndex, *toIndex;
|
cIndexFile *fromIndex, *toIndex;
|
||||||
cMarks fromMarks, toMarks;
|
cMarks fromMarks, toMarks;
|
||||||
|
int numSequences;
|
||||||
off_t maxVideoFileSize;
|
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:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
public:
|
public:
|
||||||
@ -33,7 +277,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
||||||
:cThread("video cutting")
|
:cThread("video cutting", true)
|
||||||
{
|
{
|
||||||
error = NULL;
|
error = NULL;
|
||||||
fromFile = toFile = NULL;
|
fromFile = toFile = NULL;
|
||||||
@ -41,17 +285,26 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
|||||||
fromIndex = toIndex = NULL;
|
fromIndex = toIndex = NULL;
|
||||||
cRecording Recording(FromFileName);
|
cRecording Recording(FromFileName);
|
||||||
isPesRecording = Recording.IsPesRecording();
|
isPesRecording = Recording.IsPesRecording();
|
||||||
if (fromMarks.Load(FromFileName, Recording.FramesPerSecond(), isPesRecording) && fromMarks.Count()) {
|
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);
|
fromFileName = new cFileName(FromFileName, false, true, isPesRecording);
|
||||||
toFileName = new cFileName(ToFileName, true, true, isPesRecording);
|
toFileName = new cFileName(ToFileName, true, true, isPesRecording);
|
||||||
fromIndex = new cIndexFile(FromFileName, false, isPesRecording);
|
fromIndex = new cIndexFile(FromFileName, false, isPesRecording);
|
||||||
toIndex = new cIndexFile(ToFileName, true, isPesRecording);
|
toIndex = new cIndexFile(ToFileName, true, isPesRecording);
|
||||||
toMarks.Load(ToFileName, Recording.FramesPerSecond(), isPesRecording); // doesn't actually load marks, just sets the file name
|
toMarks.Load(ToFileName, framesPerSecond, isPesRecording); // doesn't actually load marks, just sets the file name
|
||||||
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
|
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize);
|
||||||
if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
|
if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES))
|
||||||
maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
|
maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES);
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
esyslog("no editing sequences found for %s", FromFileName);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
esyslog("no editing marks found for %s", FromFileName);
|
esyslog("no editing marks found for %s", FromFileName);
|
||||||
}
|
}
|
||||||
@ -65,170 +318,235 @@ cCuttingThread::~cCuttingThread()
|
|||||||
delete toIndex;
|
delete toIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCuttingThread::Action(void)
|
bool cCuttingThread::Throttled(void)
|
||||||
{
|
{
|
||||||
cMark *Mark = fromMarks.First();
|
|
||||||
if (Mark) {
|
|
||||||
SetPriority(19);
|
|
||||||
SetIOPriority(7);
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Suspend cutting if we have severe throughput problems:
|
|
||||||
|
|
||||||
if (cIoThrottle::Engaged()) {
|
if (cIoThrottle::Engaged()) {
|
||||||
if (!suspensionLogged) {
|
if (!suspensionLogged) {
|
||||||
dsyslog("suspending cutter thread");
|
dsyslog("suspending cutter thread");
|
||||||
suspensionLogged = true;
|
suspensionLogged = true;
|
||||||
}
|
}
|
||||||
cCondWait::SleepMs(100);
|
return true;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if (suspensionLogged) {
|
else if (suspensionLogged) {
|
||||||
dsyslog("resuming cutter thread");
|
dsyslog("resuming cutter thread");
|
||||||
suspensionLogged = false;
|
suspensionLogged = false;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure there is enough disk space:
|
bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length)
|
||||||
|
{
|
||||||
AssertFreeDiskSpace(-1);
|
uint16_t FileNumber;
|
||||||
|
off_t FileOffset;
|
||||||
// Read one frame:
|
if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) {
|
||||||
|
|
||||||
if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) {
|
|
||||||
if (FileNumber != CurrentFileNumber) {
|
|
||||||
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
|
fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
|
||||||
if (fromFile)
|
if (fromFile) {
|
||||||
fromFile->SetReadAhead(MEGABYTE(20));
|
fromFile->SetReadAhead(MEGABYTE(20));
|
||||||
CurrentFileNumber = FileNumber;
|
int len = ReadFrame(fromFile, Buffer, Length, MAXFRAMESIZE);
|
||||||
}
|
if (len < 0)
|
||||||
if (fromFile) {
|
|
||||||
int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
|
|
||||||
if (len < 0) {
|
|
||||||
error = "ReadFrame";
|
error = "ReadFrame";
|
||||||
break;
|
else if (len != Length)
|
||||||
}
|
|
||||||
if (len != Length) {
|
|
||||||
CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer
|
|
||||||
Length = len;
|
Length = len;
|
||||||
|
return error == NULL;
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
error = "fromFile";
|
error = "fromFile";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
else {
|
|
||||||
// Error, unless we're past the last cut-in and there's no cut-out
|
|
||||||
if (Mark || LastMark)
|
|
||||||
error = "index";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write one frame:
|
bool cCuttingThread::SwitchFile(bool Force)
|
||||||
|
{
|
||||||
if (Independent) { // every file shall start with an independent frame
|
if (fileSize > maxVideoFileSize || Force) {
|
||||||
if (LastMark) // edited version shall end before next I-frame
|
|
||||||
break;
|
|
||||||
if (FileSize > maxVideoFileSize) {
|
|
||||||
toFile = toFileName->NextFile();
|
toFile = toFileName->NextFile();
|
||||||
if (!toFile) {
|
if (!toFile) {
|
||||||
error = "toFile 1";
|
error = "toFile";
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
FileSize = 0;
|
fileSize = 0;
|
||||||
}
|
}
|
||||||
LastIFrame = 0;
|
return true;
|
||||||
// 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) {
|
bool cCuttingThread::FramesAreEqual(int Index1, int Index2)
|
||||||
int diffs = 0;
|
{
|
||||||
for (int i = 0; i < Length; i++) {
|
bool Independent;
|
||||||
if (buffer[i] != buffer2[i]) {
|
uchar Buffer1[MAXFRAMESIZE];
|
||||||
if (diffs++ > 10)
|
uchar Buffer2[MAXFRAMESIZE];
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (diffs < 10) // the continuity counters of the PAT/PMT packets may differ
|
return true;
|
||||||
cutIn = false; // it's apparently a seamless stream, so no need for "broken" handling
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
CheckForSeamlessStream = false;
|
|
||||||
}
|
}
|
||||||
if (cutIn) {
|
|
||||||
if (isPesRecording)
|
|
||||||
cRemux::SetBrokenLink(buffer, Length);
|
|
||||||
else
|
else
|
||||||
TsSetTeiOnBrokenPackets(buffer, Length);
|
|
||||||
cutIn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toFile->Write(buffer, Length) < 0) {
|
|
||||||
error = "safe_write";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!toIndex->Write(Independent, toFileName->Number(), FileSize)) {
|
}
|
||||||
|
|
||||||
|
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";
|
error = "toIndex";
|
||||||
break;
|
return false;
|
||||||
}
|
}
|
||||||
FileSize += Length;
|
// Write data:
|
||||||
if (!LastIFrame)
|
if (toFile->Write(Buffer, Length) < 0) {
|
||||||
LastIFrame = toIndex->Last();
|
error = "safe_write";
|
||||||
|
return false;
|
||||||
// Check editing marks:
|
}
|
||||||
|
fileSize += Length;
|
||||||
if (Mark && Index >= Mark->Position()) {
|
// Generate marks at the editing points in the edited recording:
|
||||||
Mark = fromMarks.Next(Mark);
|
if (numSequences > 0 && Index == BeginIndex) {
|
||||||
toMarks.Add(LastIFrame);
|
if (toMarks.Count() > 0)
|
||||||
if (Mark)
|
toMarks.Add(toIndex->Last());
|
||||||
toMarks.Add(toIndex->Last() + 1);
|
toMarks.Add(toIndex->Last());
|
||||||
toMarks.Save();
|
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
|
else
|
||||||
LastMark = true;
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cCuttingThread::Action(void)
|
||||||
|
{
|
||||||
|
if (cMark *BeginMark = fromMarks.GetNextBegin()) {
|
||||||
|
fromFile = fromFileName->Open();
|
||||||
|
toFile = toFileName->Open();
|
||||||
|
if (!fromFile || !toFile)
|
||||||
|
return;
|
||||||
|
int LastEndIndex = -1;
|
||||||
|
while (BeginMark && Running()) {
|
||||||
|
// Suspend cutting if we have severe throughput problems:
|
||||||
|
if (Throttled()) {
|
||||||
|
cCondWait::SleepMs(100);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Make sure there is enough disk space:
|
||||||
|
AssertFreeDiskSpace(-1);
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
|
||||||
|
break;
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Recordings.TouchUpdate();
|
Recordings.TouchUpdate();
|
||||||
@ -257,7 +575,7 @@ bool cCutter::Start(const char *FileName)
|
|||||||
|
|
||||||
cMarks FromMarks;
|
cMarks FromMarks;
|
||||||
FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording());
|
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);
|
Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);
|
||||||
|
|
||||||
const char *evn = Recording.PrefixFileName('%');
|
const char *evn = Recording.PrefixFileName('%');
|
||||||
@ -345,6 +663,7 @@ bool CutRecording(const char *FileName)
|
|||||||
if (Recording.Name()) {
|
if (Recording.Name()) {
|
||||||
cMarks Marks;
|
cMarks Marks;
|
||||||
if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
|
if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
|
||||||
|
if (Marks.GetNumSequences()) {
|
||||||
if (cCutter::Start(FileName)) {
|
if (cCutter::Start(FileName)) {
|
||||||
while (cCutter::Active())
|
while (cCutter::Active())
|
||||||
cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
|
cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
|
||||||
@ -353,6 +672,9 @@ bool CutRecording(const char *FileName)
|
|||||||
else
|
else
|
||||||
fprintf(stderr, "can't start editing process\n");
|
fprintf(stderr, "can't start editing process\n");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "'%s' has no editing sequences\n", FileName);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
fprintf(stderr, "'%s' has no editing marks\n", FileName);
|
fprintf(stderr, "'%s' has no editing marks\n", FileName);
|
||||||
}
|
}
|
||||||
|
6
device.c
6
device.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "device.h"
|
||||||
@ -1147,7 +1147,7 @@ void cDevice::StillPicture(const uchar *Data, int Length)
|
|||||||
int Size = 0;
|
int Size = 0;
|
||||||
while (Length >= TS_SIZE) {
|
while (Length >= TS_SIZE) {
|
||||||
int Pid = TsPid(Data);
|
int Pid = TsPid(Data);
|
||||||
if (Pid == 0)
|
if (Pid == PATPID)
|
||||||
patPmtParser.ParsePat(Data, TS_SIZE);
|
patPmtParser.ParsePat(Data, TS_SIZE);
|
||||||
else if (Pid == patPmtParser.PmtPid())
|
else if (Pid == patPmtParser.PmtPid())
|
||||||
patPmtParser.ParsePmt(Data, TS_SIZE);
|
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
|
if (TsHasPayload(Data)) { // silently ignore TS packets w/o payload
|
||||||
int PayloadOffset = TsPayloadOffset(Data);
|
int PayloadOffset = TsPayloadOffset(Data);
|
||||||
if (PayloadOffset < TS_SIZE) {
|
if (PayloadOffset < TS_SIZE) {
|
||||||
if (Pid == 0)
|
if (Pid == PATPID)
|
||||||
patPmtParser.ParsePat(Data, TS_SIZE);
|
patPmtParser.ParsePat(Data, TS_SIZE);
|
||||||
else if (Pid == patPmtParser.PmtPid())
|
else if (Pid == patPmtParser.PmtPid())
|
||||||
patPmtParser.ParsePmt(Data, TS_SIZE);
|
patPmtParser.ParsePmt(Data, TS_SIZE);
|
||||||
|
22
dvbdevice.c
22
dvbdevice.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "dvbdevice.h"
|
||||||
@ -21,7 +21,9 @@
|
|||||||
#include "menuitems.h"
|
#include "menuitems.h"
|
||||||
#include "sourceparams.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_TUNE_TIMEOUT 9000 //ms
|
||||||
#define DVBS_LOCK_TIMEOUT 2000 //ms
|
#define DVBS_LOCK_TIMEOUT 2000 //ms
|
||||||
@ -408,7 +410,7 @@ cString cDvbTuner::GetBondingParams(const cChannel *Channel) const
|
|||||||
return diseqc->Commands();
|
return diseqc->Commands();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bool ToneOff = Channel->Frequency() < (unsigned int)Setup.LnbSLOF;
|
bool ToneOff = Channel->Frequency() < Setup.LnbSLOF;
|
||||||
bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R';
|
bool VoltOff = dtp.Polarization() == 'V' || dtp.Polarization() == 'R';
|
||||||
return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V');
|
return cString::sprintf("%c %c", ToneOff ? 't' : 'T', VoltOff ? 'v' : 'V');
|
||||||
}
|
}
|
||||||
@ -574,40 +576,52 @@ int cDvbTuner::GetSignalQuality(void) const
|
|||||||
return 3;
|
return 3;
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
bool HasSnr = true;
|
bool HasSnr = true;
|
||||||
|
#endif
|
||||||
uint16_t Snr;
|
uint16_t Snr;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
|
if (ioctl(fd_frontend, FE_READ_SNR, &Snr) != -1)
|
||||||
break;
|
break;
|
||||||
if (errno == EOPNOTSUPP) {
|
if (errno == EOPNOTSUPP) {
|
||||||
Snr = 0xFFFF;
|
Snr = 0xFFFF;
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
HasSnr = false;
|
HasSnr = false;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
bool HasBer = true;
|
bool HasBer = true;
|
||||||
|
#endif
|
||||||
uint32_t Ber;
|
uint32_t Ber;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
|
if (ioctl(fd_frontend, FE_READ_BER, &Ber) != -1)
|
||||||
break;
|
break;
|
||||||
if (errno == EOPNOTSUPP) {
|
if (errno == EOPNOTSUPP) {
|
||||||
Ber = 0;
|
Ber = 0;
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
HasBer = false;
|
HasBer = false;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
bool HasUnc = true;
|
bool HasUnc = true;
|
||||||
|
#endif
|
||||||
uint32_t Unc;
|
uint32_t Unc;
|
||||||
while (1) {
|
while (1) {
|
||||||
if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
|
if (ioctl(fd_frontend, FE_READ_UNCORRECTED_BLOCKS, &Unc) != -1)
|
||||||
break;
|
break;
|
||||||
if (errno == EOPNOTSUPP) {
|
if (errno == EOPNOTSUPP) {
|
||||||
Unc = 0;
|
Unc = 0;
|
||||||
|
#ifdef DEBUG_SIGNALQUALITY
|
||||||
HasUnc = false;
|
HasUnc = false;
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
@ -810,7 +824,7 @@ bool cDvbTuner::SetFrontend(void)
|
|||||||
SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
|
SETCMD(DTV_HIERARCHY, dtp.Hierarchy());
|
||||||
if (frontendType == SYS_DVBT2) {
|
if (frontendType == SYS_DVBT2) {
|
||||||
// DVB-T2
|
// DVB-T2
|
||||||
SETCMD(DTV_DVBT2_PLP_ID, dtp.PlpId());
|
SETCMD(DTV_STREAM_ID, dtp.PlpId());
|
||||||
}
|
}
|
||||||
|
|
||||||
tuneTimeout = DVBT_TUNE_TIMEOUT;
|
tuneTimeout = DVBT_TUNE_TIMEOUT;
|
||||||
|
6
epg.c
6
epg.c
@ -7,7 +7,7 @@
|
|||||||
* Original version (as used in VDR before 1.3.0) written by
|
* Original version (as used in VDR before 1.3.0) written by
|
||||||
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
* Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
|
||||||
*
|
*
|
||||||
* $Id: epg.c 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"
|
#include "epg.h"
|
||||||
@ -1148,14 +1148,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cEpgDataWriter::cEpgDataWriter(void)
|
cEpgDataWriter::cEpgDataWriter(void)
|
||||||
:cThread("epg data writer")
|
:cThread("epg data writer", true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void cEpgDataWriter::Action(void)
|
void cEpgDataWriter::Action(void)
|
||||||
{
|
{
|
||||||
SetPriority(19);
|
|
||||||
SetIOPriority(7);
|
|
||||||
Perform();
|
Perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* the Free Software Foundation; either version 2 of the License, or *
|
* the Free Software Foundation; either version 2 of the License, or *
|
||||||
* (at your option) any later version. *
|
* (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();
|
T *ret=new T();
|
||||||
ret->setData(d);
|
ret->setData(d);
|
||||||
ret->CheckParse();
|
ret->CheckParse();
|
||||||
if (!checkSize(ret->getLength()))
|
if (!checkSize(ret->getLength())) {
|
||||||
|
delete ret;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
it.i+=ret->getLength();
|
it.i+=ret->getLength();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
28
menu.c
28
menu.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: menu.c 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"
|
#include "menu.h"
|
||||||
@ -3418,7 +3418,7 @@ bool cMenuMain::Update(bool Force)
|
|||||||
stopReplayItem = NULL;
|
stopReplayItem = NULL;
|
||||||
}
|
}
|
||||||
// Color buttons:
|
// 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;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3516,7 +3516,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
|||||||
state = replaying ? osContinue : osPause;
|
state = replaying ? osContinue : osPause;
|
||||||
break;
|
break;
|
||||||
case kBlue: if (!HadSubMenu)
|
case kBlue: if (!HadSubMenu)
|
||||||
state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osContinue;
|
state = replaying ? osStopReplay : cReplayControl::LastReplayed() ? osReplay : osRecordings;
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
@ -4482,10 +4482,6 @@ cReplayControl::~cReplayControl()
|
|||||||
Hide();
|
Hide();
|
||||||
cStatus::MsgReplaying(this, NULL, fileName, false);
|
cStatus::MsgReplaying(this, NULL, fileName, false);
|
||||||
Stop();
|
Stop();
|
||||||
if (marksModified) {
|
|
||||||
marks.Save();
|
|
||||||
marksModified = false;
|
|
||||||
}
|
|
||||||
if (currentReplayControl == this)
|
if (currentReplayControl == this)
|
||||||
currentReplayControl = NULL;
|
currentReplayControl = NULL;
|
||||||
}
|
}
|
||||||
@ -4573,6 +4569,10 @@ void cReplayControl::Hide(void)
|
|||||||
timeSearchActive = false;
|
timeSearchActive = false;
|
||||||
timeoutShow = 0;
|
timeoutShow = 0;
|
||||||
}
|
}
|
||||||
|
if (marksModified) {
|
||||||
|
marks.Save();
|
||||||
|
marksModified = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cReplayControl::ShowMode(void)
|
void cReplayControl::ShowMode(void)
|
||||||
@ -4771,12 +4771,12 @@ void cReplayControl::MarkMove(bool Forward)
|
|||||||
int p = SkipFrames(Forward ? 1 : -1);
|
int p = SkipFrames(Forward ? 1 : -1);
|
||||||
cMark *m2;
|
cMark *m2;
|
||||||
if (Forward) {
|
if (Forward) {
|
||||||
if ((m2 = marks.Next(m)) != NULL && m2->Position() <= p)
|
while ((m2 = marks.Next(m)) != NULL && m2->Position() == m->Position())
|
||||||
return;
|
m = m2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ((m2 = marks.Prev(m)) != NULL && m2->Position() >= p)
|
while ((m2 = marks.Prev(m)) != NULL && m2->Position() == m->Position())
|
||||||
return;
|
m = m2;
|
||||||
}
|
}
|
||||||
m->SetPosition(p);
|
m->SetPosition(p);
|
||||||
Goto(m->Position(), true);
|
Goto(m->Position(), true);
|
||||||
@ -4789,13 +4789,11 @@ void cReplayControl::EditCut(void)
|
|||||||
{
|
{
|
||||||
if (*fileName) {
|
if (*fileName) {
|
||||||
Hide();
|
Hide();
|
||||||
if (marksModified) {
|
|
||||||
marks.Save();
|
|
||||||
marksModified = false;
|
|
||||||
}
|
|
||||||
if (!cCutter::Active()) {
|
if (!cCutter::Active()) {
|
||||||
if (!marks.Count())
|
if (!marks.Count())
|
||||||
Skins.Message(mtError, tr("No editing marks defined!"));
|
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))
|
else if (!cCutter::Start(fileName))
|
||||||
Skins.Message(mtError, tr("Can't start editing process!"));
|
Skins.Message(mtError, tr("Can't start editing process!"));
|
||||||
else
|
else
|
||||||
|
5
po/ar.po
5
po/ar.po
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.0\n"
|
"Project-Id-Version: VDR 1.7.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-10-16 11:16-0400\n"
|
||||||
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
|
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
|
||||||
"Language-Team: Arabic <ar@li.org>\n"
|
"Language-Team: Arabic <ar@li.org>\n"
|
||||||
@ -1241,6 +1241,9 @@ msgstr "اقفز الى "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "لاتوجد علامات تعديل معرفة"
|
msgstr "لاتوجد علامات تعديل معرفة"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "لا يمكن البدء فى عملية التعديل"
|
msgstr "لا يمكن البدء فى عملية التعديل"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-03-02 19:02+0100\n"
|
||||||
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
||||||
"Language-Team: Catalan <vdr@linuxtv.org>\n"
|
"Language-Team: Catalan <vdr@linuxtv.org>\n"
|
||||||
@ -1216,6 +1216,9 @@ msgstr "Salta a:"
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "No hi ha marques d'edició definides"
|
msgstr "No hi ha marques d'edició definides"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "No puc iniciar el procés d'edició!"
|
msgstr "No puc iniciar el procés d'edició!"
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.14\n"
|
"Project-Id-Version: VDR 1.7.14\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-05-06 11:00+0200\n"
|
||||||
"Last-Translator: Radek Šťastný <dedkus@gmail.com>\n"
|
"Last-Translator: Radek Šťastný <dedkus@gmail.com>\n"
|
||||||
"Language-Team: Czech <vdr@linuxtv.org>\n"
|
"Language-Team: Czech <vdr@linuxtv.org>\n"
|
||||||
@ -1215,6 +1215,9 @@ msgstr "Skok: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nejsou definovány editační značky!"
|
msgstr "Nejsou definovány editační značky!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Nelze začít editační proces!"
|
msgstr "Nelze začít editační proces!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||||
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
|
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
|
||||||
"Language-Team: Danish <vdr@linuxtv.org>\n"
|
"Language-Team: Danish <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Hop: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Der er ikke sat nogen redigeringsmærker!"
|
msgstr "Der er ikke sat nogen redigeringsmærker!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Kan ikke starte redigeringsprocessen!"
|
msgstr "Kan ikke starte redigeringsprocessen!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-01-16 16:46+0100\n"
|
||||||
"Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n"
|
"Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n"
|
||||||
"Language-Team: German <vdr@linuxtv.org>\n"
|
"Language-Team: German <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Springen: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Keine Schnittmarken gesetzt!"
|
msgstr "Keine Schnittmarken gesetzt!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr "Keine Schnittsequenzen definiert!"
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Schnitt kann nicht gestartet werden!"
|
msgstr "Schnitt kann nicht gestartet werden!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||||
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
|
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
|
||||||
"Language-Team: Greek <vdr@linuxtv.org>\n"
|
"Language-Team: Greek <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "ÄÝí Ý÷ïõí ïñéóôåß óçìåßá åðåîåñãáóßáò"
|
msgstr "ÄÝí Ý÷ïõí ïñéóôåß óçìåßá åðåîåñãáóßáò"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Áäõíáìßá åêêßíçóçò ôçò åðåîåñãáóßáò!"
|
msgstr "Áäõíáìßá åêêßíçóçò ôçò åðåîåñãáóßáò!"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-03-02 19:02+0100\n"
|
||||||
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
|
||||||
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
"Language-Team: Spanish <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Saltar: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "¡No se definieron marcas de edición!"
|
msgstr "¡No se definieron marcas de edición!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "¡No se puede iniciar el proceso de edición!"
|
msgstr "¡No se puede iniciar el proceso de edición!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||||
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
|
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
|
||||||
"Language-Team: Estonian <vdr@linuxtv.org>\n"
|
"Language-Team: Estonian <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Hüpe: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Redigeerimise markerid puuduvad!"
|
msgstr "Redigeerimise markerid puuduvad!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Redigeerimise start nurjus!"
|
msgstr "Redigeerimise start nurjus!"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2007-08-15 15:52+0200\n"
|
||||||
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
|
||||||
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
"Language-Team: Finnish <vdr@linuxtv.org>\n"
|
||||||
@ -1216,6 +1216,9 @@ msgstr "Siirry: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Muokkausmerkinnät puuttuvat!"
|
msgstr "Muokkausmerkinnät puuttuvat!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Muokkauksen aloitus epäonnistui!"
|
msgstr "Muokkauksen aloitus epäonnistui!"
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-02-27 18:14+0100\n"
|
||||||
"Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n"
|
"Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n"
|
||||||
"Language-Team: French <vdr@linuxtv.org>\n"
|
"Language-Team: French <vdr@linuxtv.org>\n"
|
||||||
@ -1219,6 +1219,9 @@ msgstr "Acc
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Pas de marques d'édition définies !"
|
msgstr "Pas de marques d'édition définies !"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Impossible de commencer le montage !"
|
msgstr "Impossible de commencer le montage !"
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-03-17 19:00+0100\n"
|
||||||
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
|
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
|
||||||
"Language-Team: Croatian <vdr@linuxtv.org>\n"
|
"Language-Team: Croatian <vdr@linuxtv.org>\n"
|
||||||
@ -1215,6 +1215,9 @@ msgstr "Sko
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nijedna toèka rezanja nije odreðena!"
|
msgstr "Nijedna toèka rezanja nije odreðena!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Ne mogu zapoèeti ureðivanje!"
|
msgstr "Ne mogu zapoèeti ureðivanje!"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2012-01-02 11:54+0200\n"
|
||||||
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
|
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
|
||||||
"Language-Team: Hungarian <vdr@linuxtv.org>\n"
|
"Language-Team: Hungarian <vdr@linuxtv.org>\n"
|
||||||
@ -1217,6 +1217,9 @@ msgstr "Ugr
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nincs vágópont kijelölve"
|
msgstr "Nincs vágópont kijelölve"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "A vágás nem indítható!"
|
msgstr "A vágás nem indítható!"
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2012-06-06 22:50+0100\n"
|
||||||
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
|
||||||
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
"Language-Team: Italian <vdr@linuxtv.org>\n"
|
||||||
@ -1220,6 +1220,9 @@ msgstr "Vai a: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nessun marcatore di modifica definito!"
|
msgstr "Nessun marcatore di modifica definito!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Impossibile avviare il processo di modifica!"
|
msgstr "Impossibile avviare il processo di modifica!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.16\n"
|
"Project-Id-Version: VDR 1.7.16\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-10-30 11:55+0200\n"
|
||||||
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
|
||||||
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
"Language-Team: Lithuanian <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Peršokti: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nenustatytos koregavimo žymės!"
|
msgstr "Nenustatytos koregavimo žymės!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Negali pradėti koregavimo!"
|
msgstr "Negali pradėti koregavimo!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR-1.7.14\n"
|
"Project-Id-Version: VDR-1.7.14\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-03-11 00:54+0100\n"
|
||||||
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
|
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
|
||||||
"Language-Team: Macedonian <en@li.org>\n"
|
"Language-Team: Macedonian <en@li.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Скокни:"
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Нема одредено ознаки за сечење!"
|
msgstr "Нема одредено ознаки за сечење!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Не може да почне уредување!"
|
msgstr "Не може да почне уредување!"
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-02-26 17:20+0100\n"
|
||||||
"Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n"
|
"Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n"
|
||||||
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
"Language-Team: Dutch <vdr@linuxtv.org>\n"
|
||||||
@ -1217,6 +1217,9 @@ msgstr "Springen: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Geen bewerkingsmarkeringen gedefinieerd!"
|
msgstr "Geen bewerkingsmarkeringen gedefinieerd!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Kan niet beginnen met bewerken!"
|
msgstr "Kan niet beginnen met bewerken!"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2007-08-12 14:17+0200\n"
|
||||||
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
|
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
|
||||||
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
|
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Hopp: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Kan ikke starte redigeringsprosessen!"
|
msgstr "Kan ikke starte redigeringsprosessen!"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-03-09 12:59+0100\n"
|
||||||
"Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
|
"Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
|
||||||
"Language-Team: Polish <vdr@linuxtv.org>\n"
|
"Language-Team: Polish <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Skok: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nie zdefiniowano znaczników monta¿u!"
|
msgstr "Nie zdefiniowano znaczników monta¿u!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Nie mo¿na uruchomiæ procesu edycji!"
|
msgstr "Nie mo¿na uruchomiæ procesu edycji!"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.15\n"
|
"Project-Id-Version: VDR 1.7.15\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-03-28 22:49+0100\n"
|
||||||
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
|
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
|
||||||
"Language-Team: Portuguese <vdr@linuxtv.org>\n"
|
"Language-Team: Portuguese <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Saltar: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Marcas de edição não foram definidas!"
|
msgstr "Marcas de edição não foram definidas!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Impossível iniciar processo de edição!"
|
msgstr "Impossível iniciar processo de edição!"
|
||||||
|
|
||||||
|
495
po/ro_RO.po
495
po/ro_RO.po
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-12-15 14:37+0100\n"
|
||||||
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
|
||||||
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
"Language-Team: Russian <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "½Õ ×ÐÔÐÝë ÜÕâÚØ ÔÛï ÜÞÝâÐÖÐ!"
|
msgstr "½Õ ×ÐÔÐÝë ÜÕâÚØ ÔÛï ÜÞÝâÐÖÐ!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "½ÕÒÞ×ÜÞÖÝÞ ÝÐçÐâì ÜÞÝâÐÖ ×ÐßØáØ!"
|
msgstr "½ÕÒÞ×ÜÞÖÝÞ ÝÐçÐâì ÜÞÝâÐÖ ×ÐßØáØ!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.16\n"
|
"Project-Id-Version: VDR 1.7.16\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2011-02-15 16:29+0100\n"
|
||||||
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
|
||||||
"Language-Team: Slovak <vdr@linuxtv.org>\n"
|
"Language-Team: Slovak <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Skok: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nie sú urèené znaèky úprav!"
|
msgstr "Nie sú urèené znaèky úprav!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Nemô¾e zaèa» spracovanie úprav!"
|
msgstr "Nemô¾e zaèa» spracovanie úprav!"
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-02-28 19:44+0100\n"
|
||||||
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
|
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
|
||||||
"Language-Team: Slovenian <vdr@linuxtv.org>\n"
|
"Language-Team: Slovenian <vdr@linuxtv.org>\n"
|
||||||
@ -1214,6 +1214,9 @@ msgstr "Sko
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nobena prekinitvena toèka ni definirana!"
|
msgstr "Nobena prekinitvena toèka ni definirana!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Ne morem zaèeti urejanja!"
|
msgstr "Ne morem zaèeti urejanja!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.1\n"
|
"Project-Id-Version: VDR 1.7.1\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2011-01-09 15:57+0100\n"
|
||||||
"Last-Translator: Milan Cvijanoviæ <elcom_cvijo@hotmail.com>\n"
|
"Last-Translator: Milan Cvijanoviæ <elcom_cvijo@hotmail.com>\n"
|
||||||
"Language-Team: Serbian <vdr@linuxtv.org>\n"
|
"Language-Team: Serbian <vdr@linuxtv.org>\n"
|
||||||
@ -1239,6 +1239,9 @@ msgstr "Sko
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Nijedna taèka rezanja nije odreðena!"
|
msgstr "Nijedna taèka rezanja nije odreðena!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Ne mogu zapoèeti ureðivanje!"
|
msgstr "Ne mogu zapoèeti ureðivanje!"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-03-12 18:25+0100\n"
|
||||||
"Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n"
|
"Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n"
|
||||||
"Language-Team: Swedish <vdr@linuxtv.org>\n"
|
"Language-Team: Swedish <vdr@linuxtv.org>\n"
|
||||||
@ -1216,6 +1216,9 @@ msgstr "Hopp: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Det finns inga redigeringsmärken"
|
msgstr "Det finns inga redigeringsmärken"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Kan inte starta redigering!"
|
msgstr "Kan inte starta redigering!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2008-02-28 00:33+0100\n"
|
||||||
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
|
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
|
||||||
"Language-Team: Turkish <vdr@linuxtv.org>\n"
|
"Language-Team: Turkish <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Atla: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Kesim iþaretleri belirtilmemiþ!"
|
msgstr "Kesim iþaretleri belirtilmemiþ!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Kesim baþlatýlamýyor!"
|
msgstr "Kesim baþlatýlamýyor!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.7.7\n"
|
"Project-Id-Version: VDR 1.7.7\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2010-04-25 16:35+0200\n"
|
||||||
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
|
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
|
||||||
"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
|
"Language-Team: Ukrainian <vdr@linuxtv.org>\n"
|
||||||
@ -1213,6 +1213,9 @@ msgstr "Перейти: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "Не задано міток для монтажу!"
|
msgstr "Не задано міток для монтажу!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "Неможливо почати монтаж запису!"
|
msgstr "Неможливо почати монтаж запису!"
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: VDR 1.6.0\n"
|
"Project-Id-Version: VDR 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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"
|
"PO-Revision-Date: 2009-09-23 23:50+0800\n"
|
||||||
"Last-Translator: Nan Feng <nfgx@21cn.com>\n"
|
"Last-Translator: Nan Feng <nfgx@21cn.com>\n"
|
||||||
"Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n"
|
"Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n"
|
||||||
@ -1216,6 +1216,9 @@ msgstr "跳过: "
|
|||||||
msgid "No editing marks defined!"
|
msgid "No editing marks defined!"
|
||||||
msgstr "无编辑标记定义!"
|
msgstr "无编辑标记定义!"
|
||||||
|
|
||||||
|
msgid "No editing sequences defined!"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Can't start editing process!"
|
msgid "Can't start editing process!"
|
||||||
msgstr "不能开始编辑处理"
|
msgstr "不能开始编辑处理"
|
||||||
|
|
||||||
|
127
recording.c
127
recording.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.c 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"
|
#include "recording.h"
|
||||||
@ -83,20 +83,20 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void)
|
cRemoveDeletedRecordingsThread::cRemoveDeletedRecordingsThread(void)
|
||||||
:cThread("remove deleted recordings")
|
:cThread("remove deleted recordings", true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void cRemoveDeletedRecordingsThread::Action(void)
|
void cRemoveDeletedRecordingsThread::Action(void)
|
||||||
{
|
{
|
||||||
SetPriority(19);
|
|
||||||
SetIOPriority(7);
|
|
||||||
// Make sure only one instance of VDR does this:
|
// Make sure only one instance of VDR does this:
|
||||||
cLockFile LockFile(VideoDirectory);
|
cLockFile LockFile(VideoDirectory);
|
||||||
if (LockFile.Lock()) {
|
if (LockFile.Lock()) {
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
cThreadLock DeletedRecordingsLock(&DeletedRecordings);
|
||||||
for (cRecording *r = DeletedRecordings.First(); r; ) {
|
for (cRecording *r = DeletedRecordings.First(); r; ) {
|
||||||
|
if (cIoThrottle::Engaged())
|
||||||
|
return;
|
||||||
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
|
if (r->Deleted() && time(NULL) - r->Deleted() > DELETEDLIFETIME) {
|
||||||
cRecording *next = DeletedRecordings.Next(r);
|
cRecording *next = DeletedRecordings.Next(r);
|
||||||
r->Remove();
|
r->Remove();
|
||||||
@ -1358,8 +1358,10 @@ bool cMark::Save(FILE *f)
|
|||||||
|
|
||||||
bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording)
|
bool cMarks::Load(const char *RecordingFileName, double FramesPerSecond, bool IsPesRecording)
|
||||||
{
|
{
|
||||||
|
recordingFileName = RecordingFileName;
|
||||||
fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
|
fileName = AddDirectory(RecordingFileName, IsPesRecording ? MARKSFILESUFFIX ".vdr" : MARKSFILESUFFIX);
|
||||||
framesPerSecond = FramesPerSecond;
|
framesPerSecond = FramesPerSecond;
|
||||||
|
isPesRecording = IsPesRecording;
|
||||||
nextUpdate = 0;
|
nextUpdate = 0;
|
||||||
lastFileTime = -1; // the first call to Load() must take place!
|
lastFileTime = -1; // the first call to Load() must take place!
|
||||||
lastChange = 0;
|
lastChange = 0;
|
||||||
@ -1388,6 +1390,7 @@ bool cMarks::Update(void)
|
|||||||
cMutexLock MutexLock(&MutexMarkFramesPerSecond);
|
cMutexLock MutexLock(&MutexMarkFramesPerSecond);
|
||||||
MarkFramesPerSecond = framesPerSecond;
|
MarkFramesPerSecond = framesPerSecond;
|
||||||
if (cConfig<cMark>::Load(fileName)) {
|
if (cConfig<cMark>::Load(fileName)) {
|
||||||
|
Align();
|
||||||
Sort();
|
Sort();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1396,6 +1399,18 @@ bool cMarks::Update(void)
|
|||||||
return false;
|
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)
|
void cMarks::Sort(void)
|
||||||
{
|
{
|
||||||
for (cMark *m1 = First(); m1; m1 = Next(m1)) {
|
for (cMark *m1 = First(); m1; m1 = Next(m1)) {
|
||||||
@ -1408,15 +1423,11 @@ void cMarks::Sort(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cMark *cMarks::Add(int Position)
|
void cMarks::Add(int Position)
|
||||||
{
|
{
|
||||||
cMark *m = Get(Position);
|
cConfig<cMark>::Add(new cMark(Position, NULL, framesPerSecond));
|
||||||
if (!m) {
|
|
||||||
cConfig<cMark>::Add(m = new cMark(Position, NULL, framesPerSecond));
|
|
||||||
Sort();
|
Sort();
|
||||||
}
|
}
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
cMark *cMarks::Get(int Position)
|
cMark *cMarks::Get(int Position)
|
||||||
{
|
{
|
||||||
@ -1445,6 +1456,54 @@ cMark *cMarks::GetNext(int Position)
|
|||||||
return NULL;
|
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 -------------------------------------------------
|
// --- cRecordingUserCommand -------------------------------------------------
|
||||||
|
|
||||||
const char *cRecordingUserCommand::command = NULL;
|
const char *cRecordingUserCommand::command = NULL;
|
||||||
@ -1535,7 +1594,6 @@ void cIndexFileGenerator::Action(void)
|
|||||||
if (Processed > 0) {
|
if (Processed > 0) {
|
||||||
if (FrameDetector.Synced()) {
|
if (FrameDetector.Synced()) {
|
||||||
// Synced FrameDetector, so rewind for actual processing:
|
// Synced FrameDetector, so rewind for actual processing:
|
||||||
FrameDetector.Reset();
|
|
||||||
Rewind = true;
|
Rewind = true;
|
||||||
}
|
}
|
||||||
Buffer.Del(Processed);
|
Buffer.Del(Processed);
|
||||||
@ -1546,7 +1604,7 @@ void cIndexFileGenerator::Action(void)
|
|||||||
uchar *p = Data;
|
uchar *p = Data;
|
||||||
while (Length >= TS_SIZE) {
|
while (Length >= TS_SIZE) {
|
||||||
int Pid = TsPid(p);
|
int Pid = TsPid(p);
|
||||||
if (Pid == 0)
|
if (Pid == PATPID)
|
||||||
PatPmtParser.ParsePat(p, TS_SIZE);
|
PatPmtParser.ParsePat(p, TS_SIZE);
|
||||||
else if (Pid == PatPmtParser.PmtPid())
|
else if (Pid == PatPmtParser.PmtPid())
|
||||||
PatPmtParser.ParsePmt(p, TS_SIZE);
|
PatPmtParser.ParsePmt(p, TS_SIZE);
|
||||||
@ -1570,6 +1628,7 @@ void cIndexFileGenerator::Action(void)
|
|||||||
ReplayFile = FileName.NextFile();
|
ReplayFile = FileName.NextFile();
|
||||||
FileSize = 0;
|
FileSize = 0;
|
||||||
FrameOffset = -1;
|
FrameOffset = -1;
|
||||||
|
Buffer.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Recording has been processed:
|
// 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
|
// returns true unless something really goes wrong, so that 'index' becomes NULL
|
||||||
if (index && f >= 0) {
|
if (index && f >= 0) {
|
||||||
cMutexLock MutexLock(&mutex);
|
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;
|
struct stat buf;
|
||||||
if (fstat(f, &buf) == 0) {
|
if (fstat(f, &buf) == 0) {
|
||||||
if (!IsInIndexList(this)) {
|
if (!IsInIndexList(this)) {
|
||||||
@ -1798,7 +1857,7 @@ bool cIndexFile::CatchUp(int Index)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
LOG_ERROR_STR(*fileName);
|
LOG_ERROR_STR(*fileName);
|
||||||
if (Index < last)
|
if (Index <= last)
|
||||||
break;
|
break;
|
||||||
cCondWait::SleepMs(1000);
|
cCondWait::SleepMs(1000);
|
||||||
}
|
}
|
||||||
@ -1826,12 +1885,13 @@ 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)
|
bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent, int *Length)
|
||||||
{
|
{
|
||||||
if (CatchUp(Index)) {
|
if (CatchUp(Index)) {
|
||||||
if (Index >= 0 && Index < last) {
|
if (Index >= 0 && Index <= last) {
|
||||||
*FileNumber = index[Index].number;
|
*FileNumber = index[Index].number;
|
||||||
*FileOffset = index[Index].offset;
|
*FileOffset = index[Index].offset;
|
||||||
if (Independent)
|
if (Independent)
|
||||||
*Independent = index[Index].independent;
|
*Independent = index[Index].independent;
|
||||||
if (Length) {
|
if (Length) {
|
||||||
|
if (Index < last) {
|
||||||
uint16_t fn = index[Index + 1].number;
|
uint16_t fn = index[Index + 1].number;
|
||||||
off_t fo = index[Index + 1].offset;
|
off_t fo = index[Index + 1].offset;
|
||||||
if (fn == *FileNumber)
|
if (fn == *FileNumber)
|
||||||
@ -1839,6 +1899,9 @@ bool cIndexFile::Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *I
|
|||||||
else
|
else
|
||||||
*Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
|
*Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
*Length = -1;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1851,7 +1914,7 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off
|
|||||||
int d = Forward ? 1 : -1;
|
int d = Forward ? 1 : -1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Index += d;
|
Index += d;
|
||||||
if (Index >= 0 && Index < last) {
|
if (Index >= 0 && Index <= last) {
|
||||||
if (index[Index].independent) {
|
if (index[Index].independent) {
|
||||||
uint16_t fn;
|
uint16_t fn;
|
||||||
if (!FileNumber)
|
if (!FileNumber)
|
||||||
@ -1882,12 +1945,40 @@ int cIndexFile::GetNextIFrame(int Index, bool Forward, uint16_t *FileNumber, off
|
|||||||
return -1;
|
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)
|
int cIndexFile::Get(uint16_t FileNumber, off_t FileOffset)
|
||||||
{
|
{
|
||||||
if (CatchUp()) {
|
if (CatchUp()) {
|
||||||
//TODO implement binary search!
|
//TODO implement binary search!
|
||||||
int i;
|
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)
|
if (index[i].number > FileNumber || (index[i].number == FileNumber) && off_t(index[i].offset) >= FileOffset)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2039,7 +2130,7 @@ bool cFileName::GetLastPatPmtVersions(int &PatVersion, int &PmtVersion)
|
|||||||
while (read(fd, buf, sizeof(buf)) == sizeof(buf)) {
|
while (read(fd, buf, sizeof(buf)) == sizeof(buf)) {
|
||||||
if (buf[0] == TS_SYNC_BYTE) {
|
if (buf[0] == TS_SYNC_BYTE) {
|
||||||
int Pid = TsPid(buf);
|
int Pid = TsPid(buf);
|
||||||
if (Pid == 0)
|
if (Pid == PATPID)
|
||||||
PatPmtParser.ParsePat(buf, sizeof(buf));
|
PatPmtParser.ParsePat(buf, sizeof(buf));
|
||||||
else if (Pid == PatPmtParser.PmtPid()) {
|
else if (Pid == PatPmtParser.PmtPid()) {
|
||||||
PatPmtParser.ParsePmt(buf, sizeof(buf));
|
PatPmtParser.ParsePmt(buf, sizeof(buf));
|
||||||
|
26
recording.h
26
recording.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.h 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
|
#ifndef __RECORDING_H
|
||||||
@ -222,19 +222,35 @@ public:
|
|||||||
|
|
||||||
class cMarks : public cConfig<cMark> {
|
class cMarks : public cConfig<cMark> {
|
||||||
private:
|
private:
|
||||||
|
cString recordingFileName;
|
||||||
cString fileName;
|
cString fileName;
|
||||||
double framesPerSecond;
|
double framesPerSecond;
|
||||||
|
bool isPesRecording;
|
||||||
time_t nextUpdate;
|
time_t nextUpdate;
|
||||||
time_t lastFileTime;
|
time_t lastFileTime;
|
||||||
time_t lastChange;
|
time_t lastChange;
|
||||||
public:
|
public:
|
||||||
bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false);
|
bool Load(const char *RecordingFileName, double FramesPerSecond = DEFAULTFRAMESPERSECOND, bool IsPesRecording = false);
|
||||||
bool Update(void);
|
bool Update(void);
|
||||||
|
void Align(void);
|
||||||
void Sort(void);
|
void Sort(void);
|
||||||
cMark *Add(int Position);
|
void Add(int Position);
|
||||||
cMark *Get(int Position);
|
cMark *Get(int Position);
|
||||||
cMark *GetPrev(int Position);
|
cMark *GetPrev(int Position);
|
||||||
cMark *GetNext(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"
|
#define RUC_BEFORERECORDING "before"
|
||||||
@ -291,8 +307,14 @@ public:
|
|||||||
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
|
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset);
|
||||||
bool Get(int Index, uint16_t *FileNumber, off_t *FileOffset, bool *Independent = NULL, int *Length = NULL);
|
bool 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 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 Get(uint16_t FileNumber, off_t FileOffset);
|
||||||
int Last(void) { CatchUp(); return last; }
|
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(); }
|
int GetResume(void) { return resumeFile.Read(); }
|
||||||
bool StoreResume(int Index) { return resumeFile.Save(Index); }
|
bool StoreResume(int Index) { return resumeFile.Save(Index); }
|
||||||
bool IsStillRecording(void);
|
bool IsStillRecording(void);
|
||||||
|
792
remux.c
792
remux.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: remux.c 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"
|
#include "remux.h"
|
||||||
@ -23,6 +23,8 @@ static bool DebugFrames = false;
|
|||||||
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
|
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
|
||||||
#define dbgframes(a...) if (DebugFrames) 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)
|
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
|
||||||
{
|
{
|
||||||
if (Count < 7)
|
if (Count < 7)
|
||||||
@ -112,6 +114,32 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
|
|||||||
|
|
||||||
// --- Some TS handling tools ------------------------------------------------
|
// --- 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)
|
int64_t TsGetPts(const uchar *p, int l)
|
||||||
{
|
{
|
||||||
// Find the first packet with a PTS and use it:
|
// Find the first packet with a PTS and use it:
|
||||||
@ -125,25 +153,163 @@ int64_t TsGetPts(const uchar *p, int l)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TsSetTeiOnBrokenPackets(uchar *p, int l)
|
int64_t TsGetDts(const uchar *p, int l)
|
||||||
{
|
{
|
||||||
bool Processed[MAXPID] = { false };
|
// Find the first packet with a DTS and use it:
|
||||||
while (l >= TS_SIZE) {
|
while (l > 0) {
|
||||||
if (*p != TS_SYNC_BYTE)
|
const uchar *d = p;
|
||||||
break;
|
if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d))
|
||||||
int Pid = TsPid(p);
|
return PesGetDts(d);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l -= TS_SIZE;
|
|
||||||
p += TS_SIZE;
|
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 ------------------------------------------------------
|
||||||
@ -665,6 +831,25 @@ void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
|
|||||||
pmtSize = 0;
|
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
|
bool cPatPmtParser::GetVersions(int &PatVersion, int &PmtVersion) const
|
||||||
{
|
{
|
||||||
PatVersion = patVersion;
|
PatVersion = patVersion;
|
||||||
@ -809,23 +994,365 @@ void PesDump(const char *Name, const u_char *Data, int Length)
|
|||||||
TsDump(Name, Data, Length);
|
TsDump(Name, Data, Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cFrameDetector --------------------------------------------------------
|
// --- cFrameParser ----------------------------------------------------------
|
||||||
|
|
||||||
#define EMPTY_SCANNER (0xFFFFFFFF)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 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)
|
cFrameDetector::cFrameDetector(int Pid, int Type)
|
||||||
{
|
{
|
||||||
|
parser = NULL;
|
||||||
SetPid(Pid, Type);
|
SetPid(Pid, Type);
|
||||||
synced = false;
|
synced = false;
|
||||||
newFrame = independentFrame = false;
|
newFrame = independentFrame = false;
|
||||||
numPtsValues = 0;
|
numPtsValues = 0;
|
||||||
numFrames = 0;
|
|
||||||
numIFrames = 0;
|
numIFrames = 0;
|
||||||
framesPerSecond = 0;
|
framesPerSecond = 0;
|
||||||
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
framesInPayloadUnit = framesPerPayloadUnit = 0;
|
||||||
payloadUnitOfFrame = 0;
|
|
||||||
scanning = false;
|
scanning = false;
|
||||||
scanner = EMPTY_SCANNER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int CmpUint32(const void *p1, const void *p2)
|
static int CmpUint32(const void *p1, const void *p2)
|
||||||
@ -840,42 +1367,26 @@ void cFrameDetector::SetPid(int Pid, int Type)
|
|||||||
pid = Pid;
|
pid = Pid;
|
||||||
type = Type;
|
type = Type;
|
||||||
isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or 4
|
isVideo = type == 0x01 || type == 0x02 || type == 0x1B; // MPEG 1, 2 or 4
|
||||||
}
|
delete parser;
|
||||||
|
parser = NULL;
|
||||||
void cFrameDetector::Reset(void)
|
if (type == 0x01 || type == 0x02)
|
||||||
{
|
parser = new cMpeg2Parser;
|
||||||
newFrame = independentFrame = false;
|
else if (type == 0x1B)
|
||||||
payloadUnitOfFrame = 0;
|
parser = new cMpeg4Parser;
|
||||||
scanning = false;
|
else if (type == 0x04 || type == 0x06) // MPEG audio or AC3 audio
|
||||||
scanner = EMPTY_SCANNER;
|
parser = new cAudioParser;
|
||||||
}
|
else if (type != 0)
|
||||||
|
esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cFrameDetector::Analyze(const uchar *Data, int Length)
|
int cFrameDetector::Analyze(const uchar *Data, int Length)
|
||||||
{
|
{
|
||||||
bool SeenPayloadStart = false;
|
if (!parser)
|
||||||
bool SeenAccessUnitDelimiter = false;
|
return 0;
|
||||||
int Processed = 0;
|
int Processed = 0;
|
||||||
newFrame = independentFrame = false;
|
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) {
|
if (Data[0] != TS_SYNC_BYTE) {
|
||||||
int Skipped = 1;
|
int Skipped = 1;
|
||||||
while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE))
|
while (Skipped < Length && (Data[Skipped] != TS_SYNC_BYTE || Length - Skipped > TS_SIZE && Data[Skipped + TS_SIZE] != TS_SYNC_BYTE))
|
||||||
@ -883,18 +1394,45 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
|||||||
esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
|
esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
|
||||||
return Processed + Skipped;
|
return Processed + Skipped;
|
||||||
}
|
}
|
||||||
|
// Handle one TS packet:
|
||||||
|
int Handled = TS_SIZE;
|
||||||
if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
|
if (TsHasPayload(Data) && !TsIsScrambled(Data)) {
|
||||||
int Pid = TsPid(Data);
|
int Pid = TsPid(Data);
|
||||||
if (Pid == pid) {
|
if (Pid == pid) {
|
||||||
if (TsPayloadStart(Data)) {
|
if (Processed)
|
||||||
SeenPayloadStart = true;
|
|
||||||
if (synced && Processed)
|
|
||||||
return Processed;
|
return Processed;
|
||||||
if (Length < MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE)
|
if (TsPayloadStart(Data))
|
||||||
return Processed; // need more data, in case the frame type is not stored in the first TS packet
|
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)) {
|
||||||
|
// Determine the frame rate from the PTS values in the PES headers:
|
||||||
if (framesPerSecond <= 0.0) {
|
if (framesPerSecond <= 0.0) {
|
||||||
// frame rate unknown, so collect a sequence of PTS values:
|
// frame rate unknown, so collect a sequence of PTS values:
|
||||||
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
|
if (numPtsValues < 2 || numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
|
||||||
|
if (newFrame) { // only take PTS values at the beginning of a frame (in case if fields!)
|
||||||
const uchar *Pes = Data + TsPayloadOffset(Data);
|
const uchar *Pes = Data + TsPayloadOffset(Data);
|
||||||
if (numIFrames && PesHasPts(Pes)) {
|
if (numIFrames && PesHasPts(Pes)) {
|
||||||
ptsValues[numPtsValues] = PesGetPts(Pes);
|
ptsValues[numPtsValues] = PesGetPts(Pes);
|
||||||
@ -903,42 +1441,29 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
|||||||
dbgframes("#");
|
dbgframes("#");
|
||||||
numPtsValues = 0;
|
numPtsValues = 0;
|
||||||
numIFrames = 0;
|
numIFrames = 0;
|
||||||
numFrames = 0;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
numPtsValues++;
|
numPtsValues++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
|
if (numPtsValues >= 2 && numIFrames >= 2) {
|
||||||
// find the smallest PTS delta:
|
// find the smallest PTS delta:
|
||||||
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
||||||
numPtsValues--;
|
numPtsValues--;
|
||||||
for (int i = 0; i < numPtsValues; i++)
|
for (int i = 0; i < numPtsValues; i++)
|
||||||
ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
|
ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
|
||||||
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
|
||||||
uint32_t Delta = ptsValues[0];
|
uint32_t Delta = ptsValues[0] / framesPerPayloadUnit;
|
||||||
// determine frame info:
|
// determine frame info:
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
if (abs(Delta - 3600) <= 1)
|
if (abs(Delta - 3600) <= 1)
|
||||||
framesPerSecond = 25.0;
|
framesPerSecond = 25.0;
|
||||||
else if (Delta % 3003 == 0)
|
else if (Delta % 3003 == 0)
|
||||||
framesPerSecond = 30.0 / 1.001;
|
framesPerSecond = 30.0 / 1.001;
|
||||||
else if (abs(Delta - 1800) <= 1) {
|
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;
|
framesPerSecond = 50.0;
|
||||||
}
|
|
||||||
else if (Delta == 1501)
|
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 {
|
else {
|
||||||
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
framesPerSecond = DEFAULTFRAMESPERSECOND;
|
||||||
@ -946,123 +1471,22 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else // audio
|
else // audio
|
||||||
framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing
|
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, numFrames);
|
dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1);
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
synced = true;
|
||||||
dbgframes("*\n");
|
parser->SetDebug(false);
|
||||||
Reset();
|
}
|
||||||
return Processed + TS_SIZE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Pid == PATPID && synced && Processed)
|
else if (Pid == PATPID && synced && Processed)
|
||||||
return Processed; // allow the caller to see any PAT packets
|
return Processed; // allow the caller to see any PAT packets
|
||||||
}
|
}
|
||||||
Data += TS_SIZE;
|
Data += Handled;
|
||||||
Length -= TS_SIZE;
|
Length -= Handled;
|
||||||
Processed += TS_SIZE;
|
Processed += Handled;
|
||||||
|
if (newFrame)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return Processed;
|
return Processed;
|
||||||
}
|
}
|
||||||
|
142
remux.h
142
remux.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: remux.h 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
|
#ifndef __REMUX_H
|
||||||
@ -52,6 +52,11 @@ public:
|
|||||||
#define PATPID 0x0000 // PAT PID (constant 0)
|
#define PATPID 0x0000 // PAT PID (constant 0)
|
||||||
#define MAXPID 0x2000 // for arrays that use a PID as the index
|
#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)
|
inline bool TsHasPayload(const uchar *p)
|
||||||
{
|
{
|
||||||
return p[3] & TS_PAYLOAD_EXISTS;
|
return p[3] & TS_PAYLOAD_EXISTS;
|
||||||
@ -82,6 +87,16 @@ inline bool TsIsScrambled(const uchar *p)
|
|||||||
return p[3] & TS_SCRAMBLING_CONTROL;
|
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)
|
inline int TsPayloadOffset(const uchar *p)
|
||||||
{
|
{
|
||||||
int o = TsHasAdaptationField(p) ? p[4] + 5 : 4;
|
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;
|
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.
|
// The following functions all take a pointer to a sequence of complete TS packets.
|
||||||
|
|
||||||
int64_t TsGetPts(const uchar *p, int l);
|
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:
|
// Some PES handling tools:
|
||||||
// The following functions that take a pointer to PES data all assume that
|
// 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;
|
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)
|
inline int64_t PesGetPts(const uchar *p)
|
||||||
{
|
{
|
||||||
return ((((int64_t)p[ 9]) & 0x0E) << 29) |
|
return ((((int64_t)p[ 9]) & 0x0E) << 29) |
|
||||||
@ -151,6 +187,88 @@ inline int64_t PesGetPts(const uchar *p)
|
|||||||
((((int64_t)p[13]) & 0xFE) >> 1);
|
((((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:
|
// PAT/PMT Generator:
|
||||||
|
|
||||||
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
|
#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
|
///< are delivered to the parser through several subsequent calls to
|
||||||
///< ParsePmt(). The whole PMT data will be processed once the last packet
|
///< ParsePmt(). The whole PMT data will be processed once the last packet
|
||||||
///< has been received.
|
///< 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;
|
bool GetVersions(int &PatVersion, int &PmtVersion) const;
|
||||||
///< Returns true if a valid PAT/PMT has been parsed and stores
|
///< Returns true if a valid PAT/PMT has been parsed and stores
|
||||||
///< the current version numbers in the given variables.
|
///< 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
|
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR 5
|
||||||
|
|
||||||
|
class cFrameParser;
|
||||||
|
|
||||||
class cFrameDetector {
|
class cFrameDetector {
|
||||||
private:
|
private:
|
||||||
enum { MaxPtsValues = 150 };
|
enum { MaxPtsValues = 150 };
|
||||||
@ -354,12 +478,9 @@ private:
|
|||||||
double framesPerSecond;
|
double framesPerSecond;
|
||||||
int framesInPayloadUnit;
|
int framesInPayloadUnit;
|
||||||
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
|
||||||
// some put an entire GOP into one payload unit (> 1), and
|
// while others put an entire GOP into one payload unit (> 1).
|
||||||
// some spread a single frame over several payload units (< 0).
|
|
||||||
int payloadUnitOfFrame;
|
|
||||||
bool scanning;
|
bool scanning;
|
||||||
uint32_t scanner;
|
cFrameParser *parser;
|
||||||
int SkipPackets(const uchar *&Data, int &Length, int &Processed, int &FrameTypeOffset);
|
|
||||||
public:
|
public:
|
||||||
cFrameDetector(int Pid = 0, int Type = 0);
|
cFrameDetector(int Pid = 0, int Type = 0);
|
||||||
///< Sets up a frame detector for the given Pid and stream Type.
|
///< Sets up a frame detector for the given Pid and stream Type.
|
||||||
@ -367,9 +488,6 @@ public:
|
|||||||
///< call to SetPid().
|
///< call to SetPid().
|
||||||
void SetPid(int Pid, int Type);
|
void SetPid(int Pid, int Type);
|
||||||
///< Sets the Pid and stream Type to detect frames for.
|
///< Sets the Pid and stream Type to detect frames for.
|
||||||
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);
|
int Analyze(const uchar *Data, int Length);
|
||||||
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
///< Analyzes the TS packets pointed to by Data. Length is the number of
|
||||||
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
///< bytes Data points to, and must be a multiple of TS_SIZE.
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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"
|
#include "sections.h"
|
||||||
@ -40,7 +40,7 @@ public:
|
|||||||
// --- cSectionHandler -------------------------------------------------------
|
// --- cSectionHandler -------------------------------------------------------
|
||||||
|
|
||||||
cSectionHandler::cSectionHandler(cDevice *Device)
|
cSectionHandler::cSectionHandler(cDevice *Device)
|
||||||
:cThread("section handler")
|
:cThread("section handler", true)
|
||||||
{
|
{
|
||||||
shp = new cSectionHandlerPrivate;
|
shp = new cSectionHandlerPrivate;
|
||||||
device = Device;
|
device = Device;
|
||||||
@ -164,7 +164,6 @@ void cSectionHandler::SetStatus(bool On)
|
|||||||
|
|
||||||
void cSectionHandler::Action(void)
|
void cSectionHandler::Action(void)
|
||||||
{
|
{
|
||||||
SetPriority(19);
|
|
||||||
while (Running()) {
|
while (Running()) {
|
||||||
|
|
||||||
Lock();
|
Lock();
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: skinlcars.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
|
#ifndef __SKINLCARS_H
|
||||||
|
4
themes.h
4
themes.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: 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
|
#ifndef __THEMES_H
|
||||||
@ -56,7 +56,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// A helper macro that simplifies defining theme colors.
|
// 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 {
|
class cThemes {
|
||||||
private:
|
private:
|
||||||
|
11
thread.c
11
thread.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: thread.c 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"
|
#include "thread.h"
|
||||||
@ -204,7 +204,7 @@ void cMutex::Unlock(void)
|
|||||||
|
|
||||||
tThreadId cThread::mainThreadId = 0;
|
tThreadId cThread::mainThreadId = 0;
|
||||||
|
|
||||||
cThread::cThread(const char *Description)
|
cThread::cThread(const char *Description, bool LowPriority)
|
||||||
{
|
{
|
||||||
active = running = false;
|
active = running = false;
|
||||||
childTid = 0;
|
childTid = 0;
|
||||||
@ -212,6 +212,7 @@ cThread::cThread(const char *Description)
|
|||||||
description = NULL;
|
description = NULL;
|
||||||
if (Description)
|
if (Description)
|
||||||
SetDescription("%s", Description);
|
SetDescription("%s", Description);
|
||||||
|
lowPriority = LowPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
cThread::~cThread()
|
cThread::~cThread()
|
||||||
@ -248,12 +249,16 @@ void *cThread::StartThread(cThread *Thread)
|
|||||||
{
|
{
|
||||||
Thread->childThreadId = ThreadId();
|
Thread->childThreadId = ThreadId();
|
||||||
if (Thread->description) {
|
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
|
#ifdef PR_SET_NAME
|
||||||
if (prctl(PR_SET_NAME, Thread->description, 0, 0, 0) < 0)
|
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);
|
esyslog("%s thread naming failed (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
if (Thread->lowPriority) {
|
||||||
|
Thread->SetPriority(19);
|
||||||
|
Thread->SetIOPriority(7);
|
||||||
|
}
|
||||||
Thread->Action();
|
Thread->Action();
|
||||||
if (Thread->description)
|
if (Thread->description)
|
||||||
dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
|
dsyslog("%s thread ended (pid=%d, tid=%d)", Thread->description, getpid(), Thread->childThreadId);
|
||||||
|
7
thread.h
7
thread.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: thread.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
|
#ifndef __THREAD_H
|
||||||
@ -83,6 +83,7 @@ private:
|
|||||||
tThreadId childThreadId;
|
tThreadId childThreadId;
|
||||||
cMutex mutex;
|
cMutex mutex;
|
||||||
char *description;
|
char *description;
|
||||||
|
bool lowPriority;
|
||||||
static tThreadId mainThreadId;
|
static tThreadId mainThreadId;
|
||||||
static void *StartThread(cThread *Thread);
|
static void *StartThread(cThread *Thread);
|
||||||
protected:
|
protected:
|
||||||
@ -106,11 +107,13 @@ protected:
|
|||||||
///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
|
///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
|
||||||
///< returns immediately, without killing the thread.
|
///< returns immediately, without killing the thread.
|
||||||
public:
|
public:
|
||||||
cThread(const char *Description = NULL);
|
cThread(const char *Description = NULL, bool LowPriority = false);
|
||||||
///< Creates a new thread.
|
///< Creates a new thread.
|
||||||
///< If Description is present, a log file entry will be made when
|
///< If Description is present, a log file entry will be made when
|
||||||
///< the thread starts and stops. The Start() function must be called
|
///< the thread starts and stops. The Start() function must be called
|
||||||
///< to actually start the thread.
|
///< to actually start the thread.
|
||||||
|
///< LowPriority can be set to true to make this thread run at a lower
|
||||||
|
///< priority.
|
||||||
virtual ~cThread();
|
virtual ~cThread();
|
||||||
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
|
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
|
||||||
bool Start(void);
|
bool Start(void);
|
||||||
|
15
timers.c
15
timers.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: timers.c 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"
|
#include "timers.h"
|
||||||
@ -465,9 +465,10 @@ bool cTimer::Matches(time_t t, bool Directly, int Margin) const
|
|||||||
startTime = event->StartTime();
|
startTime = event->StartTime();
|
||||||
stopTime = event->EndTime();
|
stopTime = event->EndTime();
|
||||||
if (!Margin) { // this is an actual check
|
if (!Margin) { // this is an actual check
|
||||||
if (event->Schedule()->PresentSeenWithin(EITPRESENTFOLLOWINGRATE)) // VPS control can only work with up-to-date events...
|
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 event->IsRunning(true);
|
||||||
else
|
}
|
||||||
return startTime <= t && t < stopTime; // ...otherwise we fall back to normal timer handling
|
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;
|
lastSetEvent = now;
|
||||||
const cEvent *Event = NULL;
|
const cEvent *Event = NULL;
|
||||||
if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
|
if (HasFlags(tfVps) && Schedule->Events()->First()->Vps()) {
|
||||||
if (event && Recording())
|
if (event && event->StartTime() > 0) { // checks for "phased out" events
|
||||||
|
if (Recording())
|
||||||
return; // let the recording end first
|
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:
|
// 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)) {
|
for (const cEvent *e = Schedule->Events()->First(); e; e = Schedule->Events()->Next(e)) {
|
||||||
if (e->StartTime() && e->RunningStatus() != SI::RunningStatusNotRunning) { // skip outdated events
|
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 {
|
else {
|
||||||
// Normal timers match the event they have the most overlap with:
|
// Normal timers match the event they have the most overlap with:
|
||||||
|
10
vdr.5
10
vdr.5
@ -8,7 +8,7 @@
|
|||||||
.\" License as specified in the file COPYING that comes with the
|
.\" License as specified in the file COPYING that comes with the
|
||||||
.\" vdr distribution.
|
.\" vdr distribution.
|
||||||
.\"
|
.\"
|
||||||
.\" $Id: vdr.5 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"
|
.TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@ -815,13 +815,13 @@ least one blank.
|
|||||||
The lines in this file need not necessarily appear in the correct temporal
|
The lines in this file need not necessarily appear in the correct temporal
|
||||||
sequence, they will be automatically sorted by time index.
|
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
|
\fBCURRENT RESTRICTIONS:\fR
|
||||||
|
|
||||||
-\ the comment is currently not used by VDR
|
-\ 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
|
.SS EPG DATA
|
||||||
The file \fIepg.data\fR contains the EPG data in an easily parsable format.
|
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.
|
The first character of each line defines what kind of data this line contains.
|
||||||
|
6
vdr.c
6
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.tvdr.de
|
* 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>
|
#include <getopt.h>
|
||||||
@ -892,7 +892,7 @@ int main(int argc, char *argv[])
|
|||||||
Timer->SetInVpsMargin(InVpsMargin);
|
Timer->SetInVpsMargin(InVpsMargin);
|
||||||
}
|
}
|
||||||
else if (Timer->Event()) {
|
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);
|
NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1238,6 +1238,8 @@ int main(int argc, char *argv[])
|
|||||||
cControl::Shutdown();
|
cControl::Shutdown();
|
||||||
cControl::Launch(new cReplayControl);
|
cControl::Launch(new cReplayControl);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
DirectMainFunction(osRecordings); // no last viewed recording, so enter the Recordings menu
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user