summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS16
-rw-r--r--HISTORY30
-rw-r--r--PLUGINS.html127
-rw-r--r--PLUGINS/src/svdrpdemo/COPYING340
-rw-r--r--PLUGINS/src/svdrpdemo/HISTORY6
-rw-r--r--PLUGINS/src/svdrpdemo/Makefile82
-rw-r--r--PLUGINS/src/svdrpdemo/README13
-rw-r--r--PLUGINS/src/svdrpdemo/svdrpdemo.c60
-rw-r--r--channels.conf18
-rw-r--r--config.h6
-rw-r--r--device.c7
-rw-r--r--eitscan.c4
-rw-r--r--i18n.c14
-rw-r--r--menu.c128
-rw-r--r--menu.h26
-rwxr-xr-xnewplugin16
-rw-r--r--plugin.c12
-rw-r--r--plugin.h4
-rw-r--r--remux.c749
-rw-r--r--remux.h11
-rw-r--r--sdt.c11
-rw-r--r--svdrp.c253
-rw-r--r--svdrp.h6
-rw-r--r--tools.c12
-rw-r--r--tools.h3
25 files changed, 1616 insertions, 338 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ff546dd..0a9c8ae 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -976,6 +976,8 @@ Reinhard Nissl <rnissl@gmx.de>
for making cDvbPlayer::Goto() append a Sequence End Code to get the image shown
immediately with softdevices
for fixing cDvbSpuBitmap::putPixel()
+ for implementing cAudioRepacker in remux.c
+ for modifying handling of audio packets for radio channels in remux.c
Richard Robson <richard_robson@beeb.net>
for reporting freezing replay if a timer starts while in Transfer Mode from the
@@ -1212,6 +1214,7 @@ Marco Schlüßler <marco@lordzodiac.de>
is waiting
for improving resetting CAM connections
for fixing handling EPG data for time shifted events
+ for fixing detecting short channel names for "Kabel Deutschland"
Jürgen Schmitz <j.schmitz@web.de>
for reporting a bug in displaying the current channel when switching via the SVDRP
@@ -1436,7 +1439,20 @@ Klaus Heppenheimer <klaus@reel-multimedia.com>
Thomas Günther <tom1@toms-cafe.de>
for fixing handling the frame number display if '7' is pressed before the first
editing mark, or '9' after the last one
+ for suggesting to move cMenuEditTimer and cMenuEvent to menu.h so that plugins
+ can use it
David Woodhouse <dwmw2@infradead.org>
for his help in replacing the get/put_unaligned() macros from asm/unaligned.h with
own inline functions to avoid problems on platforms that don't provide these
+
+Marcus Hilbrich <s4440288@mail.inf.tu-dresden.de>
+ for a bug report that lead to fixing the EPG scan, so that it doesn't use the
+ primary device if that is currently in Transfer-Mode from itself
+
+Hardy Flor <HFlor@web.de>
+ for a patch that was used as a base to implement SVDRP commands for plugins
+ for implementing the SVDRP command PLAY
+
+Harald Milz <hm@seneca.muc.de>
+ for his CUTR patch, which was used as a base to implement the SVDRP command EDIT
diff --git a/HISTORY b/HISTORY
index 016b6f5..dda7c86 100644
--- a/HISTORY
+++ b/HISTORY
@@ -3716,3 +3716,33 @@ Video Disk Recorder Revision History
- Replaced the get/put_unaligned() macros from <asm/unaligned.h> with own inline
functions to avoid problems on platforms that don't provide these (thanks to
David Woodhouse for his help).
+
+2005-08-28: Version 1.3.31
+
+- Added missing German OSD texts for 'Audio language'.
+- The Setup/CICAM menu now only contains the devices that actually have a CI and
+ dynamically detects the number of slots a CI provides.
+- Implemented cAudioRepacker for better handling of audio PES packets (thanks to
+ Reinhard Nissl).
+- Modified handling of audio packets for radio channels in remux.c (thanks to
+ Reinhard Nissl).
+- Updated the Danish OSD texts (thanks to Mogens Elneff).
+- Fixed the EPG scan, so that it doesn't use the primary device if that is
+ currently in Transfer-Mode from itself (thanks to Marcus Hilbrich for a bug
+ report that lead to this).
+- Removed the TUNER_LOCK_TIMEOUT in cDevice::AttachReceiver() since it caused more
+ trouble than it fixed.
+- Fixed detecting short channel names for "Kabel Deutschland", who uses a comma
+ as delimiter (thanks to Marco Schlüßler).
+- Moved cMenuEditTimer and cMenuEvent to menu.h so that plugins can use it (suggested
+ by Thomas Günther).
+- The new static function cString::sprintf() can be used to easily create a formatted
+ string.
+- Plugins can now implement their own SVDRP commands (based on a patch from Hardy
+ Flor). See PLUGINS.html, section "SVDRP commands" for details. The SVDRP commands
+ of a plugin are accessed through the new SVDRP command PLUG.
+ See PLUGINS/src/svdrpdemo for an example of how to use this feature.
+- The new SVDRP command PLAY can be used to start replaying a recording (thanks to
+ Hardy Flor).
+- The new SVDRP command EDIT can be used to start the editing process of a recording
+ (based on the CUTR patch by Harald Milz).
diff --git a/PLUGINS.html b/PLUGINS.html
index 29d8351..203d870 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -14,18 +14,18 @@ Copyright &copy; 2005 Klaus Schmidinger<br>
<a href="http://www.cadsoft.de/vdr">www.cadsoft.de/vdr</a>
</center>
<p>
-<!--X1.3.19--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
-Important modifications introduced in version 1.3.19 are marked like this.
-<!--X1.3.19--></td></tr></table>
-<!--X1.3.20--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.20 are marked like this.
<!--X1.3.20--></td></tr></table>
-<!--X1.3.21--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.21 are marked like this.
<!--X1.3.21--></td></tr></table>
-<!--X1.3.30--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.3.30 are marked like this.
<!--X1.3.30--></td></tr></table>
+<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+Important modifications introduced in version 1.3.31 are marked like this.
+<!--X1.3.31--></td></tr></table>
<p>
VDR provides an easy to use plugin interface that allows additional functionality
to be added to the program by implementing a dynamically loadable library file.
@@ -58,7 +58,7 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Command line arguments">Command line arguments</a>
<li><a href="#Command line help">Command line help</a>
<li><a href="#Getting started">Getting started</a>
-<!--X1.3.20--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<li><a href="#Shutting down">Shutting down</a>
<!--X1.3.20--></td></tr></table>
<li><a href="#Main menu entry">Main menu entry</a>
@@ -68,9 +68,12 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#The Setup menu">The Setup menu</a>
<li><a href="#Configuration files">Configuration files</a>
<li><a href="#Internationalization">Internationalization</a>
-<!--X1.3.30--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<li><a href="#Custom services">Custom services</a>
<!--X1.3.30--></td></tr></table>
+<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<li><a href="#SVDRP commands">SVDRP commands</a>
+<!--X1.3.31--></td></tr></table>
<li><a href="#Loading plugins into VDR">Loading plugins into VDR</a>
<li><a href="#Building the distribution package">Building the distribution package</a>
</ul>
@@ -84,7 +87,7 @@ structures and allows it to hook itself into specific areas to perform special a
<li><a href="#Skins">Skins</a>
<li><a href="#Themes">Themes</a>
<li><a href="#Devices">Devices</a>
-<!--X1.3.21--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<li><a href="#Audio">Audio</a>
<!--X1.3.21--></td></tr></table>
<li><a href="#Remote Control">Remote Control</a>
@@ -311,7 +314,7 @@ since VDR, for instance, has to create the plugin objects in order to get their
command line help - and after that immediately destroys them again.
<p>
The <b>destructor</b> has to clean up any data created by the plugin.
-<!--X1.3.20--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Any threads the plugin may have created shall be stopped in the
<a href="#Shutting down"><tt>Stop()</tt></a> function.
<!--X1.3.20--></td></tr></table>
@@ -509,7 +512,7 @@ VDR to exit.
If the plugin doesn't implement any background functionality or internationalized
texts, it doesn't need to implement either of these functions.
-<!--X1.3.20--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
+<!--X1.3.20--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<a name="Shutting down"><hr><h2>Shutting down</h2>
<center><i><b>Stop it, right there!</b></i></center><p>
@@ -869,7 +872,7 @@ Texts are first searched for in the <i>Phrases</i> registered for this plugin (i
and then in the global VDR texts. So a plugin can make use of texts defined by the
core VDR code.
-<!--X1.3.30--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<!--X1.3.30--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<a name="Custom services"><hr><h2>Custom services</h2>
<center><i><b>What can I do for you?</b></i></center><p>
@@ -934,12 +937,106 @@ will send the request to all plugins until one plugin handles it.
The function returns a pointer to the plugin that handled the request, or <tt>NULL</tt>
if no plugin handled it.
<p>
-To send a messages to all plugins, a plugin can call the function
+To send a message to all plugins, a plugin can call the function
<tt>cPluginManager::CallAllServices()</tt>. This function returns <tt>true</tt> if
any plugin handled the request, or <tt>false</tt> if no plugin handled the request.
<!--X1.3.30--></td></tr></table>
+<!--X1.3.31--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
+<a name="SVDRP commands"><hr><h2>SVDRP commands</h2>
+
+<center><i><b>Infinite Diversity in Infinite Combinations</b></i></center><p>
+
+A plugin can implement its own SVDRP commands through the two functions
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+virtual const char **SVDRPHelpPages(void);
+virtual cString SVDRPCommand(const char *Cmd, const char *Option, int &ReplyCode);
+</pre></td></tr></table><p>
+
+The <tt>SVDRPHelpPages()</tt> function must return a pointer to a list of help
+strings for all of the plugin's SVDRP commands, like this
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+const char **cPluginSvdrpdemo::SVDRPHelpPages(void)
+{
+ static const char *HelpPages[] = {
+ "DATE\n"
+ " Print the current date.",
+ "TIME [ raw ]\n"
+ " Print the current time.\n"
+ " If the optional keyword 'raw' is given, the result will be the\n"
+ " raw time_t data.",
+ NULL
+ };
+ return HelpPages;
+}
+</pre></td></tr></table><p>
+
+Note that the first line of each entry contains the actual command and its
+parameters, while the following lines explain what the command does and what
+the parameters (if any) mean. All lines of the explanation shall be indented
+by exactly 4 blanks (no tabs), and none of them shall be longer than 79 characters
+(to avoid messy output on 80 character wide terminals). The last entry in the
+list must be NULL.
+<p>
+The command names <tt>HELP</tt> and <tt>MAIN</tt> are reserverd and cannot
+be used by a plugin.
+<p>
+The actual processing of SVDRP commands for a plugin is done in its
+<tt>SVDRPCommand()</tt> function.
+Here's an example of such a function, which implements the commands advertised in
+the above help texts:
+
+<p><table><tr><td bgcolor=#F0F0F0><pre>
+cString cPluginSvdrpdemo::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ if (strcasecmp(Command, "DATE") == 0) {
+ // we use the default reply code here
+ return DateString(time(NULL));
+ }
+ else if (strcasecmp(Command, "TIME") == 0) {
+ ReplyCode = 901;
+ if (*Option) {
+ if (strcasecmp(Option, "RAW") == 0)
+ return cString::sprintf("%ld\nThis is the number of seconds since the epoch\n"
+ "and a demo of a multi-line reply", time(NULL));
+ else {
+ ReplyCode = 504;
+ return cString::sprintf("Unknown option: \"%s\"", Option);
+ }
+ }
+ return TimeString(time(NULL));
+ }
+ return NULL;
+}
+</pre></td></tr></table><p>
+
+The command is given to this function in the <tt>Command</tt> parameter, and any optional parameters
+are given in the <tt>Option</tt> string. <tt>Command</tt> always points to an actual, non-empty string, while
+<tt>Option</tt> may point to an empty string (it is never NULL, though).
+<p>
+If a plugin doesn't implement the given command, it shall return NULL, and VDR will
+automatically issue a proper error message. If it encounters an unknown or invalid
+option, it shall set the <tt>ReplyCode</tt> to one of the codes defined in <tt>VDR/svdrp.c</tt>
+and return a proper error message.
+<p>
+The default <tt>ReplyCode</tt> is 900, and if the plugin doesn't care about reply
+codes, it doesn't have to set it to anything else (unless there is an error, of
+course). The codes in the range 901..999 are reserved for plugins that want
+to use special reply codes. Any plugin can use any of these values and doesn't
+have to coordinate this with any other plugin, since the caller knows which
+plugin was called, and will therefore process the values according to the
+particular plugin's definitions.
+<p>
+The returned string may consist of several lines, separated by the newline character
+('<tt>\n</tt>'). Each of these lines will be preceeded with the <tt>ReplyCode</tt>
+when presenting them to the caller, and the continuation character ('<tt>-</tt>')
+will be set for all but the last one.
+
+<!--X1.3.31--></td></tr></table>
+
<a name="Loading plugins into VDR"><hr><h2>Loading plugins into VDR</h2>
<center><i><b>Saddling up!</b></i></center><p>
@@ -1276,9 +1373,7 @@ public:
};
cMyReceiver::cMyReceiver(int Pid)
-<!--X1.3.19--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
:cReceiver(0, -1, Pid)
-<!--X1.3.19--></td></tr></table>
{
}
@@ -1735,7 +1830,7 @@ private:
virtual void Action(void);
public:
cMyAudio(void);
-<!--X1.3.21--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
+<!--X1.3.21--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
virtual void Play(const uchar *Data, int Length, uchar Id);
<!--X1.3.21--></td></tr></table>
virtual void Mute(bool On);
diff --git a/PLUGINS/src/svdrpdemo/COPYING b/PLUGINS/src/svdrpdemo/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/PLUGINS/src/svdrpdemo/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/PLUGINS/src/svdrpdemo/HISTORY b/PLUGINS/src/svdrpdemo/HISTORY
new file mode 100644
index 0000000..78014f2
--- /dev/null
+++ b/PLUGINS/src/svdrpdemo/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'svdrpdemo' Revision History
+---------------------------------------
+
+2005-08-27: Version 0.0.1
+
+- Initial revision.
diff --git a/PLUGINS/src/svdrpdemo/Makefile b/PLUGINS/src/svdrpdemo/Makefile
new file mode 100644
index 0000000..5714bdc
--- /dev/null
+++ b/PLUGINS/src/svdrpdemo/Makefile
@@ -0,0 +1,82 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile 1.1 2005/08/27 11:26:49 kls Exp $
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = svdrpdemo
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -O2 -Wall -Woverloaded-virtual
+
+### The directory environment:
+
+DVBDIR = ../../../../DVB
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g')
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+INCLUDES += -I$(VDRDIR)/include -I$(DVBDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+all: libvdr-$(PLUGIN).so
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
+ @cp $@ $(LIBDIR)/$@.$(VDRVERSION)
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~
diff --git a/PLUGINS/src/svdrpdemo/README b/PLUGINS/src/svdrpdemo/README
new file mode 100644
index 0000000..27b767a
--- /dev/null
+++ b/PLUGINS/src/svdrpdemo/README
@@ -0,0 +1,13 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Klaus Schmidinger <Klaus.Schmidinger@cadsoft.de>
+
+Project's homepage: http://www.cadsoft.de/vdr
+
+Latest version available at: http://www.cadsoft.de/vdr
+
+See the file COPYING for license information.
+
+Description:
+
+This plugin shows how to add SVDRP support to a plugin.
diff --git a/PLUGINS/src/svdrpdemo/svdrpdemo.c b/PLUGINS/src/svdrpdemo/svdrpdemo.c
new file mode 100644
index 0000000..b6ee9a7
--- /dev/null
+++ b/PLUGINS/src/svdrpdemo/svdrpdemo.c
@@ -0,0 +1,60 @@
+/*
+ * svdrpdemo.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: svdrpdemo.c 1.1 2005/08/27 16:28:58 kls Exp $
+ */
+
+#include <vdr/plugin.h>
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "How to add SVDRP support to a plugin";
+static const char *MAINMENUENTRY = NULL;
+
+class cPluginSvdrpdemo : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+public:
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+ };
+
+const char **cPluginSvdrpdemo::SVDRPHelpPages(void)
+{
+ static const char *HelpPages[] = {
+ "DATE\n"
+ " Print the current date.",
+ "TIME [ raw ]\n"
+ " Print the current time.\n"
+ " If the optional keyword 'raw' is given, the result will be the\n"
+ " raw time_t data.",
+ NULL
+ };
+ return HelpPages;
+}
+
+cString cPluginSvdrpdemo::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ if (strcasecmp(Command, "DATE") == 0) {
+ // we use the default reply code here
+ return DateString(time(NULL));
+ }
+ else if (strcasecmp(Command, "TIME") == 0) {
+ ReplyCode = 901;
+ if (*Option) {
+ if (strcasecmp(Option, "RAW") == 0)
+ return cString::sprintf("%ld\nThis is the number of seconds since the epoch\nand a demo of a multi-line reply", time(NULL));
+ else {
+ ReplyCode = 504;
+ return cString::sprintf("Unknown option: \"%s\"", Option);
+ }
+ }
+ return TimeString(time(NULL));
+ }
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginSvdrpdemo); // Don't touch this!
diff --git a/channels.conf b/channels.conf
index 872bbbe..a6034e2 100644
--- a/channels.conf
+++ b/channels.conf
@@ -5,18 +5,18 @@ RTL2;RTL World:12187:hC34:S19.2E:27500:166:128=deu:68:0:12020:1:1089:0
Das Erste;ARD:11836:hC34:S19.2E:27500:101:102=deu:104:0:28106:1:1101:0
Bayerisches FS;ARD:11836:hC34:S19.2E:27500:201:202=deu:204:0:28107:1:1101:0
hr-fernsehen;ARD:11836:hC34:S19.2E:27500:301:302=deu:304:0:28108:1:1101:0
-NDR FS MV;ARD:12109:hC34:S19.2E:27500:2401:2402=deu:2404:0:28224:1:1073:0
+NDR FS MV;ARD:12109:hC34:S19.2E:27500:301:302=deu:2404:0:28224:1:1073:0
SR SÜDWEST Ferns.;ARD:11836:hC34:S19.2E:27500:501:502=deu:504:0:28110:1:1101:0
WDR Köln;ARD:11836:hC34:S19.2E:27500:601:602=deu:604:0:28111:1:1101:0
-BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu;703:704:0:28112:1:1101:0
+BR-alpha;ARD:11836:hC34:S19.2E:27500:701:702=deu:704:0:28112:1:1101:0
SÜDWEST Ferns. BW;ARD:11836:hC34:S19.2E:27500:801:802=deu:804:0:28113:1:1101:0
Phoenix;ARD:11836:hC34:S19.2E:27500:901:902=deu:904:0:28114:1:1101:0
ZDF;ZDFvision:11953:hC34:S19.2E:27500:110:120=deu,121=2ch;125=dd:130:0:28006:1:1079:0
-3sat;ZDFvision:11953:hC34:S19.2E:27500:210:220=deu,221=2ch:230:0:28007:1:1079:0
+3sat;ZDFvision:11953:hC34:S19.2E:27500:210:220=deu,221=2ch;225=dd:230:0:28007:1:1079:0
KiKa;ZDFvision:11953:hC34:S19.2E:27500:310:320=deu:330:0:28008:1:1079:0
arte;ARD:11836:hC34:S19.2E:27500:401:402=deu,403=fra:404:0:28109:1:1101:0
-ORF1;ORF:12692:hC56:S19.2E:22000:160:161=deu;163=deu:165:1762,D05,1702,1801:13001:1:1117:0
-ORF2;ORF:12692:hC56:S19.2E:22000:500:501=deu;503=deu:505:1762,D05,1702,1801:13002:1:1117:0
+ORF1;ORF:12692:hC56:S19.2E:22000:160:161=deu;163=deu:165:3:13001:1:1117:0
+ORF2;ORF:12692:hC56:S19.2E:22000:500:501=deu;503=deu:505:3:13002:1:1117:0
ZDFinfokanal;ZDFvision:11953:hC34:S19.2E:27500:610:620=deu:130:0:28011:1:1079:0
CNN Int.;CNN:11778:vC34:S19.2E:27500:165:100=eng:47:0:28522:1:1068:0
Super RTL,S RTL;RTL World:12187:hC34:S19.2E:27500:165:120=deu:65:0:12040:1:1089:0
@@ -27,7 +27,7 @@ DSF;BetaDigital:12480:vC34:S19.2E:27500:1023:1024=deu:39:0:900:133:33:0
HSE24,HSE24;BetaDigital:12480:vC34:S19.2E:27500:1279:1280=deu:37:0:40:133:33:0
Bloomberg TV Germany;Bloomberg:12551:vC56:S19.2E:22000:162:99=deu:0:0:12160:1:1108:0
EURONEWS;CSAT:11817:vC34:S19.2E:27500:163:92=fra,93=eng,94=ita,95=esl,91=rus,98=por,99=deu:0:0:8004:1:1070:0
-rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28205:1:1073:0
+rbb Brandenburg;ARD:12109:hC34:S19.2E:27500:501:502=deu:504:0:28205:1:1073:0
Sky News:11597:vC56:S19.2E:22000:305+131:306=eng:0:0:28707:1:1026:0
Veronica/JETIX;CANALDIGITAAL:12574:hC56:S19.2E:22000:518+8190:92=dut:38:622,100:5020:53:1109:0
BVN;CANALDIGITAAL:12574:hC56:S19.2E:22000:515+8190:96=dut:36:0:5025:53:1109:0
@@ -44,9 +44,9 @@ MDR FERNSEHEN;ARD:12109:hC34:S19.2E:27500:401:402=deu:404:0:28204:1:1073:0
rbb Berlin;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0
:Premiere World
PREMIERE START,START;PREMIERE:11797:hC34:S19.2E:27500:255:256=deu:32:1:8:133:2:0
-PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:32:1722,1702,1801:10:133:2:0
+PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:32:1:10:133:2:0
PREMIERE 2,PREM 2;PREMIERE:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:32:1801,1722,1702:11:133:2:0
-PREMIERE 3,PREM 3;PREMIERE:11797:hC34:S19.2E:27500:2303:2304=deu:32:1722,1801,1702:43:133:2:0
+PREMIERE 3,PREM 3;PREMIERE:11797:hC34:S19.2E:27500:2303:2304=deu,2305=deu:32:1722,1801,1702:43:133:2:0
PREMIERE 4,PREM 4;PREMIERE:11797:hC34:S19.2E:27500:767:768=deu:32:1801,1722,1702:9:133:2:0
PREMIERE 5,PREM 5;PREMIERE:11797:hC34:S19.2E:27500:1279:1280=deu:32:1722,1801,1702:29:133:2:0
PREMIERE 6,PREM 6;PREMIERE:11797:hC34:S19.2E:27500:1535:1536=deu:32:1702,1801,1722:41:133:2:0
@@ -108,7 +108,7 @@ Sky Cinema 1;BSkyB:12285:vC23:S28.2E:27500:519+8190:647=eng,667=NAR:583:960,961:
Sky Cinema 2;BSkyB:12285:vC23:S28.2E:27500:517+8190:645=eng,665=NAR:581:960,961:4802:2:2030:0
:@900 Some 'seed' channels
Chelsea TV;BskyB:11778:vC23:S28.2E:27500:2308+2304:2309=eng:0:960,961:9307:2:2004:0
-WDR Münster;ARD:12421:hC34:S19.2E:27500:101:102=deu:104:0:28310:1:1201:0
+WDR Münster;ARD:12421:hC34:S19.2E:27500:701:702=deu:104:0:28310:1:1201:0
Animal Plnt+;BSkyB:12070:hC23:S28.2E:27500:2314+2307:2315=eng:0:960,961:50002:2:2019:0
S1T;BSkyB:12285:vC23:S28.2E:27500:513+8190:641=eng,661=NAR:577:960,961:4409:2:2030:0
CNN;BSkyB:12051:vC23:S28.2E:27500:2313:2315=eng:2314:0:7140:2:2018:0
diff --git a/config.h b/config.h
index a2e6c35..8bfbc85 100644
--- a/config.h
+++ b/config.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: config.h 1.224 2005/08/20 10:29:35 kls Exp $
+ * $Id: config.h 1.225 2005/08/26 12:28:40 kls Exp $
*/
#ifndef __CONFIG_H
@@ -20,8 +20,8 @@
#include "i18n.h"
#include "tools.h"
-#define VDRVERSION "1.3.30"
-#define VDRVERSNUM 10330 // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION "1.3.31"
+#define VDRVERSNUM 10331 // Version * 10000 + Major * 100 + Minor
#define MAXPRIORITY 99
#define MAXLIFETIME 99
diff --git a/device.c b/device.c
index 2f61498..2380803 100644
--- a/device.c
+++ b/device.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: device.c 1.106 2005/08/21 08:56:49 kls Exp $
+ * $Id: device.c 1.107 2005/08/27 09:01:09 kls Exp $
*/
#include "device.h"
@@ -139,7 +139,6 @@ int cPesAssembler::PacketSize(const uchar *data)
// The default priority for non-primary devices:
#define DEFAULTPRIORITY -1
-#define TUNER_LOCK_TIMEOUT 5000 // ms
int cDevice::numDevices = 0;
int cDevice::useDevice = 0;
@@ -1186,10 +1185,6 @@ bool cDevice::AttachReceiver(cReceiver *Receiver)
return false;
if (Receiver->device == this)
return true;
- if (!HasLock(TUNER_LOCK_TIMEOUT)) {
- esyslog("ERROR: device %d has no lock, can't attach receiver!", CardIndex() + 1);
- return false;
- }
cMutexLock MutexLock(&mutexReceiver);
for (int i = 0; i < MAXRECEIVERS; i++) {
if (!receiver[i]) {
diff --git a/eitscan.c b/eitscan.c
index 3894bca..10739c3 100644
--- a/eitscan.c
+++ b/eitscan.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: eitscan.c 1.27 2005/08/07 11:29:54 kls Exp $
+ * $Id: eitscan.c 1.28 2005/08/26 15:37:06 kls Exp $
*/
#include "eitscan.h"
@@ -150,7 +150,7 @@ void cEITScanner::Process(void)
if (!Channel->Ca() || Channel->Ca() == Device->DeviceNumber() + 1 || Channel->Ca() >= 0x0100) {
if (Device->ProvidesTransponder(Channel)) {
if (!Device->Receiving()) {
- bool IsPrimaryDeviceReplaying = Device == cDevice::PrimaryDevice() && Device->Replaying();
+ bool IsPrimaryDeviceReplaying = Device == cDevice::PrimaryDevice() && Device->Replaying() && cTransferControl::ReceiverDevice() != cDevice::PrimaryDevice();
if (Device != cDevice::ActualDevice() || (Device->ProvidesTransponderExclusively(Channel) && (IsPrimaryDeviceReplaying || now - lastActivity > Setup.EPGScanTimeout * 3600))) {
if (!IsPrimaryDeviceReplaying && Device == cDevice::ActualDevice() && !currentChannel) {
if (cTransferControl::ReceiverDevice())
diff --git a/i18n.c b/i18n.c
index d7c6873..346ad86 100644
--- a/i18n.c
+++ b/i18n.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: i18n.c 1.198 2005/08/14 12:03:47 kls Exp $
+ * $Id: i18n.c 1.200 2005/08/26 13:39:07 kls Exp $
*
* Translations provided by:
*
@@ -271,7 +271,7 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"",// TODO
"",// TODO
- "",// TODO
+ "Optagelses info",
},
{ "Setup",
"Einstellungen",
@@ -2924,7 +2924,7 @@ const tI18nPhrase Phrases[] = {
"¿àÞÚàãâÚÐ áâàÐÝØæ ÜÕÝî",
"Listaj po stranicama",
"Lehekülje kerimine",
- "Rul sider",
+ "Scroll sidevis",
},
{ "Setup.OSD$Scroll wraps",
"Rundum scrollen",
@@ -2945,7 +2945,7 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"S kraja skoèi na poèetak",
"",// TODO
- "",// TODO
+ "Scroll rundt",
},
{ "Setup.OSD$Sort timers",
"Timer sortieren",
@@ -3389,7 +3389,7 @@ const tI18nPhrase Phrases[] = {
"tilf. ny transp.",
},
{ "Setup.DVB$Audio languages", // note the plural
- "",//TODO
+ "Audio Sprachen",
"",//TODO
"",//TODO
"Audio talen",
@@ -3410,7 +3410,7 @@ const tI18nPhrase Phrases[] = {
"Audio sprog (ant.)",
},
{ "Setup.DVB$Audio language", // note the singular
- "",//TODO
+ "Audio Sprache",
"",//TODO
"",//TODO
"Audio taal",
@@ -5407,7 +5407,7 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"Bez titla",
"",// TODO
- "",// TODO
+ "Ingen titel",
},
{ NULL }
};
diff --git a/menu.c b/menu.c
index 3385707..1e51474 100644
--- a/menu.c
+++ b/menu.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.c 1.355 2005/08/14 15:14:29 kls Exp $
+ * $Id: menu.c 1.357 2005/08/27 09:37:23 kls Exp $
*/
#include "menu.h"
@@ -19,7 +19,6 @@
#include "eitscan.h"
#include "i18n.h"
#include "interface.h"
-#include "menuitems.h"
#include "plugin.h"
#include "recording.h"
#include "remote.h"
@@ -605,20 +604,6 @@ eOSState cMenuText::ProcessKey(eKeys Key)
// --- cMenuEditTimer --------------------------------------------------------
-class cMenuEditTimer : public cOsdMenu {
-private:
- cTimer *timer;
- cTimer data;
- int channel;
- bool addIfConfirmed;
- cMenuEditDateItem *firstday;
- void SetFirstDayItem(void);
-public:
- cMenuEditTimer(cTimer *Timer, bool New = false);
- virtual ~cMenuEditTimer();
- virtual eOSState ProcessKey(eKeys Key);
- };
-
cMenuEditTimer::cMenuEditTimer(cTimer *Timer, bool New)
:cOsdMenu(tr("Edit timer"), 12)
{
@@ -904,15 +889,6 @@ eOSState cMenuTimers::ProcessKey(eKeys Key)
// --- cMenuEvent ------------------------------------------------------------
-class cMenuEvent : public cOsdMenu {
-private:
- const cEvent *event;
-public:
- cMenuEvent(const cEvent *Event, bool CanSwitch = false);
- virtual void Display(void);
- virtual eOSState ProcessKey(eKeys Key);
-};
-
cMenuEvent::cMenuEvent(const cEvent *Event, bool CanSwitch)
:cOsdMenu(tr("Event"))
{
@@ -2122,11 +2098,28 @@ eOSState cMenuSetupLNB::ProcessKey(eKeys Key)
// --- cMenuSetupCICAM -------------------------------------------------------
+class cMenuSetupCICAMItem : public cOsdItem {
+private:
+ cCiHandler *ciHandler;
+ int slot;
+public:
+ cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot);
+ cCiHandler *CiHandler(void) { return ciHandler; }
+ int Slot(void) { return slot; }
+ };
+
+cMenuSetupCICAMItem::cMenuSetupCICAMItem(int Device, cCiHandler *CiHandler, int Slot)
+{
+ ciHandler = CiHandler;
+ slot = Slot;
+ char buffer[32];
+ const char *CamName = CiHandler->GetCamName(slot);
+ snprintf(buffer, sizeof(buffer), "%s%d %d\t%s", tr("Setup.CICAM$CICAM DVB"), Device + 1, slot + 1, CamName ? CamName : "-");
+ SetText(buffer);
+}
+
class cMenuSetupCICAM : public cMenuSetupBase {
private:
- int helpKeys;
- void SetHelpKeys(void);
- cCiHandler *GetCurrentCiHandler(int *Slot = NULL);
eOSState Menu(void);
eOSState Reset(void);
public:
@@ -2136,66 +2129,43 @@ public:
cMenuSetupCICAM::cMenuSetupCICAM(void)
{
- helpKeys = -1;
SetSection(tr("CICAM"));
for (int d = 0; d < cDevice::NumDevices(); d++) {
cDevice *Device = cDevice::GetDevice(d);
- cCiHandler *CiHandler = Device->CiHandler();
- for (int Slot = 0; Slot < 2; Slot++) {
- char buffer[32];
- int CardIndex = Device->CardIndex();
- const char *CamName = CiHandler ? CiHandler->GetCamName(Slot) : NULL;
- if (!CamName)
- CamName = "-";
- snprintf(buffer, sizeof(buffer), "%s%d %d\t%s", tr("Setup.CICAM$CICAM DVB"), CardIndex + 1, Slot + 1, CamName);
- Add(new cOsdItem(buffer));
- }
+ if (Device) {
+ cCiHandler *CiHandler = Device->CiHandler();
+ if (CiHandler) {
+ for (int Slot = 0; Slot < CiHandler->NumSlots(); Slot++)
+ Add(new cMenuSetupCICAMItem(Device->CardIndex(), CiHandler, Slot));
+ }
+ }
}
- SetHelpKeys();
-}
-
-cCiHandler *cMenuSetupCICAM::GetCurrentCiHandler(int *Slot)
-{
- cDevice *Device = cDevice::GetDevice(Current() / 2);
- if (Slot)
- *Slot = Current() % 2;
- return Device ? Device->CiHandler() : NULL;
-}
-
-void cMenuSetupCICAM::SetHelpKeys(void)
-{
- int NewHelpKeys = helpKeys;
- NewHelpKeys = GetCurrentCiHandler() ? 1 : 0;
- if (NewHelpKeys != helpKeys) {
- switch (NewHelpKeys) {
- case 0: SetHelp(NULL); break;
- case 1: SetHelp(tr("Menu"), tr("Reset"));
- }
- helpKeys = NewHelpKeys;
- }
+ SetHelp(tr("Menu"), tr("Reset"));
}
eOSState cMenuSetupCICAM::Menu(void)
{
- int Slot = 0;
- cCiHandler *CiHandler = GetCurrentCiHandler(&Slot);
- if (CiHandler && CiHandler->EnterMenu(Slot))
- return osEnd; // the CAM menu will be executed explicitly from the main loop
- else
- Skins.Message(mtError, tr("Can't open CAM menu!"));
+ cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
+ if (item) {
+ if (item->CiHandler()->EnterMenu(item->Slot()))
+ return osEnd; // the CAM menu will be executed explicitly from the main loop
+ else
+ Skins.Message(mtError, tr("Can't open CAM menu!"));
+ }
return osContinue;
}
eOSState cMenuSetupCICAM::Reset(void)
{
- int Slot = 0;
- cCiHandler *CiHandler = GetCurrentCiHandler(&Slot);
- if (CiHandler && CiHandler->Reset(Slot)) {
- Skins.Message(mtInfo, tr("CAM has been reset"));
- return osEnd;
+ cMenuSetupCICAMItem *item = (cMenuSetupCICAMItem *)Get(Current());
+ if (item) {
+ if (item->CiHandler()->Reset(item->Slot())) {
+ Skins.Message(mtInfo, tr("CAM has been reset"));
+ return osEnd;
+ }
+ else
+ Skins.Message(mtError, tr("Can't reset CAM!"));
}
- else
- Skins.Message(mtError, tr("Can't reset CAM!"));
return osContinue;
}
@@ -2205,17 +2175,11 @@ eOSState cMenuSetupCICAM::ProcessKey(eKeys Key)
if (state == osUnknown) {
switch (Key) {
- case kRed: if (helpKeys == 1)
- return Menu();
- break;
- case kGreen: if (helpKeys == 1)
- return Reset();
- break;
+ case kRed: return Menu();
+ case kGreen: return Reset();
default: break;
}
}
- if (Key != kNone)
- SetHelpKeys();
return state;
}
diff --git a/menu.h b/menu.h
index 93712e4..f327811 100644
--- a/menu.h
+++ b/menu.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menu.h 1.70 2005/05/15 14:34:54 kls Exp $
+ * $Id: menu.h 1.71 2005/08/27 09:37:33 kls Exp $
*/
#ifndef __MENU_H
@@ -15,6 +15,7 @@
#include "epg.h"
#include "osdbase.h"
#include "dvbplayer.h"
+#include "menuitems.h"
#include "recorder.h"
#include "skins.h"
@@ -29,6 +30,29 @@ public:
virtual eOSState ProcessKey(eKeys Key);
};
+class cMenuEditTimer : public cOsdMenu {
+private:
+ cTimer *timer;
+ cTimer data;
+ int channel;
+ bool addIfConfirmed;
+ cMenuEditDateItem *firstday;
+ void SetFirstDayItem(void);
+public:
+ cMenuEditTimer(cTimer *Timer, bool New = false);
+ virtual ~cMenuEditTimer();
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+class cMenuEvent : public cOsdMenu {
+private:
+ const cEvent *event;
+public:
+ cMenuEvent(const cEvent *Event, bool CanSwitch = false);
+ virtual void Display(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
class cMenuMain : public cOsdMenu {
private:
time_t lastActivity;
diff --git a/newplugin b/newplugin
index 8badddf..90d6113 100755
--- a/newplugin
+++ b/newplugin
@@ -12,7 +12,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
-# $Id: newplugin 1.19 2005/08/21 09:32:08 kls Exp $
+# $Id: newplugin 1.20 2005/08/27 16:13:29 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
@@ -171,6 +171,8 @@ public:
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
virtual bool Service(const char *Id, void *Data = NULL);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
};
cPlugin${PLUGIN_CLASS}::cPlugin$PLUGIN_CLASS(void)
@@ -243,6 +245,18 @@ bool cPlugin${PLUGIN_CLASS}::Service(const char *Id, void *Data)
return false;
}
+const char **cPlugin${PLUGIN_CLASS}::SVDRPHelpPages(vo<F3>id)
+{
+ // Return help text for SVDRP commands this plugin implements
+ return NULL;
+}
+
+const char *cPlugin${PLUGIN_CLASS}::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ // Process SVDRP commands this plugin implements
+ return NULL;
+}
+
VDRPLUGINCREATOR(cPlugin$PLUGIN_CLASS); // Don't touch this!
};
diff --git a/plugin.c b/plugin.c
index 8a316cc..9916313 100644
--- a/plugin.c
+++ b/plugin.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: plugin.c 1.14 2005/08/21 09:35:28 kls Exp $
+ * $Id: plugin.c 1.15 2005/08/27 16:13:24 kls Exp $
*/
#include "plugin.h"
@@ -104,6 +104,16 @@ bool cPlugin::Service(const char *Id, void *Data)
return false;
}
+const char **cPlugin::SVDRPHelpPages(void)
+{
+ return NULL;
+}
+
+cString cPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ return NULL;
+}
+
void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases)
{
I18nRegister(Phrases, Name());
diff --git a/plugin.h b/plugin.h
index 3befbbb..2d9f983 100644
--- a/plugin.h
+++ b/plugin.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: plugin.h 1.9 2005/08/21 09:32:08 kls Exp $
+ * $Id: plugin.h 1.10 2005/08/27 16:13:17 kls Exp $
*/
#ifndef __PLUGIN_H
@@ -51,6 +51,8 @@ public:
void RegisterI18n(const tI18nPhrase * const Phrases);
virtual bool Service(const char *Id, void *Data = NULL);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
static void SetConfigDirectory(const char *Dir);
static const char *ConfigDirectory(const char *PluginName = NULL);
diff --git a/remux.c b/remux.c
index c980bd8..1028050 100644
--- a/remux.c
+++ b/remux.c
@@ -8,10 +8,10 @@
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
* VDR's needs.
*
- * The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
+ * The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
*
- * $Id: remux.c 1.37 2005/08/21 08:58:58 kls Exp $
+ * $Id: remux.c 1.42 2005/08/28 11:46:44 kls Exp $
*/
#include "remux.h"
@@ -20,49 +20,23 @@
#include "thread.h"
#include "tools.h"
-// --- cRepacker -------------------------------------------------------------
-
-class cRepacker {
-protected:
- int maxPacketSize;
- uint8_t subStreamId;
- static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
- static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
- {
- int n = ResultBuffer->Put(Data, Count);
- if (n != Count)
- esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
- return n;
- }
- static int AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = 0);
-public:
- cRepacker(void) { maxPacketSize = 6 + 65535; subStreamId = 0; }
- virtual ~cRepacker() {}
- virtual void Reset(void) {}
- virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
- virtual int BreakAt(const uchar *Data, int Count) = 0;
- virtual int QuerySnoopSize(void) { return 0; }
- void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
- void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
- };
-
-int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
+ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
{
if (Count < 7)
- return -1; // too short
+ return phNeedMoreData; // too short
if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
if (Count < 9)
- return -1; // too short
+ return phNeedMoreData; // too short
PesPayloadOffset = 6 + 3 + Data[8];
if (Count < PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
if (ContinuationHeader)
*ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
- return 2; // MPEG 2
+ return phMPEG2; // MPEG 2
}
// check for MPEG 1 ...
@@ -74,7 +48,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
break;
if (Count <= ++PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
}
// skip STD_buffer_scale/size
@@ -82,7 +56,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
PesPayloadOffset += 2;
if (Count <= PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
}
if (ContinuationHeader)
@@ -104,18 +78,50 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
*ContinuationHeader = true;
}
else
- return 0; // unknown
+ return phInvalid; // unknown
if (Count < PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
- return 1; // MPEG 1
+ return phMPEG1; // MPEG 1
}
-// --- cVideoRepacker --------------------------------------------------------
+// --- cRepacker -------------------------------------------------------------
-class cVideoRepacker : public cRepacker {
-private:
+class cRepacker {
+protected:
+ bool initiallySyncing;
+ int maxPacketSize;
+ uint8_t subStreamId;
+ static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
+public:
+ static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ cRepacker(void) { initiallySyncing = true; maxPacketSize = 6 + 65535; subStreamId = 0; }
+ virtual ~cRepacker() {}
+ virtual void Reset(void) { /* initiallySyncing = true; */ }
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
+ virtual int BreakAt(const uchar *Data, int Count) = 0;
+ virtual int QuerySnoopSize(void) { return 0; }
+ void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
+ void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
+ };
+
+int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
+{
+ if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
+ esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
+ return 0;
+ }
+ int n = ResultBuffer->Put(Data, Count);
+ if (n != Count)
+ esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
+ return n;
+}
+
+// --- cCommonRepacker --------------------------------------------------------
+
+class cCommonRepacker : public cRepacker {
+protected:
int skippedBytes;
int packetTodo;
uchar fragmentData[6 + 65535 + 3];
@@ -125,40 +131,25 @@ private:
uchar pesHeaderBackup[6 + 3 + 255];
int pesHeaderBackupLen;
uint32_t scanner;
- enum eState {
- syncing,
- findPicture,
- scanPicture
- } state;
uint32_t localScanner;
int localStart;
bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
-public:
- cVideoRepacker(void);
- virtual void Reset(void);
- virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
- virtual int BreakAt(const uchar *Data, int Count);
virtual int QuerySnoopSize() { return 4; }
+ virtual void Reset(void);
};
-cVideoRepacker::cVideoRepacker(void)
-{
- Reset();
-}
-
-void cVideoRepacker::Reset(void)
+void cCommonRepacker::Reset(void)
{
+ cRepacker::Reset();
skippedBytes = 0;
packetTodo = maxPacketSize - 6 - 3;
fragmentLen = 0;
pesHeaderLen = 0;
pesHeaderBackupLen = 0;
- scanner = 0xFFFFFFFF;
- state = syncing;
localStart = -1;
}
-bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// enter packet length into PES header ...
if (fragmentLen > 0) { // ... which is contained in the fragment buffer
@@ -166,11 +157,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = fragmentLen + Count - 6;
fragmentData[ 4 ] = PacketLen >> 8;
fragmentData[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid)
+ esyslog("cCommonRepacker: invalid PES packet encountered in fragment buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset)
+ return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = Put(ResultBuffer, fragmentData, Bite);
+ int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen);
fragmentLen = 0;
if (n != Bite)
return false;
@@ -179,11 +176,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = pesHeaderLen + Count - 6;
pesHeader[ 4 ] = PacketLen >> 8;
pesHeader[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid)
+ esyslog("cCommonRepacker: invalid PES packet encountered in header buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset)
+ return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = Put(ResultBuffer, pesHeader, Bite);
+ int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen);
pesHeaderLen = 0;
if (n != Bite)
return false;
@@ -193,7 +196,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
// amount of data to put into result buffer
int Bite = Count;
// put data into result buffer
- int n = Put(ResultBuffer, Data, Bite);
+ int n = Put(ResultBuffer, Data, Bite, Bite);
if (n != Bite)
return false;
}
@@ -201,6 +204,34 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
return true;
}
+// --- cVideoRepacker --------------------------------------------------------
+
+class cVideoRepacker : public cCommonRepacker {
+private:
+ enum eState {
+ syncing,
+ findPicture,
+ scanPicture
+ } state;
+public:
+ cVideoRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+cVideoRepacker::cVideoRepacker(void)
+{
+ Reset();
+}
+
+void cVideoRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0xFFFFFFFF;
+ state = syncing;
+}
+
void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// synchronisation is detected some bytes after frame start.
@@ -211,8 +242,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
int pesPayloadOffset = 0;
bool continuationHeader = false;
- int mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
- if (mpegLevel <= 0) {
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
DroppedData("cVideoRepacker: no valid PES packet header found", Count);
return;
}
@@ -264,16 +295,14 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
// the byte count get's negative then the current buffer ends in a
// partitial start code that must be stripped off, as it shall be put
// in the next packet.
- if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload)) {
- DroppedData("cVideoRepacker: result buffer overflow", Count - (done - 3));
- return;
- }
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
// go on with syncing to the next picture
state = syncing;
}
if (state == syncing) {
- // report that syncing dropped some bytes
- if (skippedBytes > SkippedBytesLimit)
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// if there is a PES header available, then use it ...
@@ -298,7 +327,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- if (mpegLevel == 2) {
+ if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
@@ -379,10 +408,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
const uchar *excessData = fragmentData + fragmentLen + bite;
// a negative byte count means to drop some bytes from the current
// fragment's tail, to not exceed the maximum packet size.
- if (!PushOutPacket(ResultBuffer, payload, bite)) {
- DroppedData("cVideoRepacker: result buffer overflow", Count - done);
- return;
- }
+ PushOutPacket(ResultBuffer, payload, bite);
// create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
@@ -392,7 +418,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- if (mpegLevel == 2) {
+ if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
@@ -434,7 +460,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
}
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit) {
- esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ if (!initiallySyncing) // omit report for the typical initial case
+ esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
skippedBytes = SkippedBytesLimit;
}
}
@@ -443,7 +470,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
{
int PesPayloadOffset = 0;
- if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= 0)
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
return -1; // not enough data for test
// just detect end of picture
@@ -475,6 +502,381 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
return PesPayloadOffset + packetTodo + 4;
}
+// --- cAudioRepacker --------------------------------------------------------
+
+class cAudioRepacker : public cCommonRepacker {
+private:
+ static int bitRates[2][3][16];
+ enum eState {
+ syncing,
+ scanFrame
+ } state;
+ int frameTodo;
+ int frameSize;
+ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize = NULL);
+public:
+ cAudioRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+int cAudioRepacker::bitRates[2][3][16] = { // all values are specified as kbits/s
+ // MPEG 1, Layer I
+ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
+ // MPEG 1, Layer II
+ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
+ // MPEG 1, Layer III
+ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1,
+ // MPEG 2, Layer I
+ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1,
+ // MPEG 2, Layer II/III
+ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1,
+ // MPEG 2, Layer II/III
+ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1
+ };
+
+cAudioRepacker::cAudioRepacker(void)
+{
+ Reset();
+}
+
+void cAudioRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0;
+ state = syncing;
+ frameTodo = 0;
+ frameSize = 0;
+}
+
+bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, bool Mpeg2, int *FrameSize)
+{
+ int syncword = (Header & 0xFFF00000) >> 20;
+ int id = (Header & 0x00080000) >> 19;
+ int layer = (Header & 0x00060000) >> 17;
+//int protection_bit = (Header & 0x00010000) >> 16;
+ int bitrate_index = (Header & 0x0000F000) >> 12;
+ int sampling_frequency = (Header & 0x00000C00) >> 10;
+ int padding_bit = (Header & 0x00000200) >> 9;
+//int private_bit = (Header & 0x00000100) >> 8;
+//int mode = (Header & 0x000000C0) >> 6;
+//int mode_extension = (Header & 0x00000030) >> 4;
+//int copyright = (Header & 0x00000008) >> 3;
+//int orignal_copy = (Header & 0x00000004) >> 2;
+ int emphasis = (Header & 0x00000003);
+
+ if (syncword != 0xFFF)
+ return false;
+
+ if (id == 0 && !Mpeg2) // reserved in MPEG 1
+ return false;
+
+ if (layer == 0) // reserved
+ return false;
+
+ if (bitrate_index == 0xF) // forbidden
+ return false;
+
+ if (sampling_frequency == 3) // reserved
+ return false;
+
+ if (emphasis == 2) // reserved
+ return false;
+
+ if (FrameSize) {
+ if (bitrate_index == 0)
+ *FrameSize = 0;
+ else {
+ static int samplingFrequencies[2][4] = { // all values are specified in Hz
+ // MPEG 1
+ 44100, 48000, 32000, -1,
+ // MPEG 2
+ 22050, 24000, 16000, -1
+ };
+
+ static int slots_per_frame[2][3] = {
+ // MPEG 1, Layer I, II, III
+ 12, 144, 144,
+ // MPEG 2, Layer I, II, III
+ 12, 144, 72
+ };
+
+ int mpegIndex = 1 - id;
+ int layerIndex = 3 - layer;
+
+ // Layer I (i. e., layerIndex == 0) has a larger slot size
+ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+
+ int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
+ int sf = samplingFrequencies[mpegIndex][sampling_frequency];
+
+ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+
+ *FrameSize = (N + padding_bit) * slotSize; // bytes
+ }
+ }
+
+ return true;
+}
+
+void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // reset local scanner
+ localStart = -1;
+
+ int pesPayloadOffset = 0;
+ bool continuationHeader = false;
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
+ DroppedData("cAudioRepacker: no valid PES packet header found", Count);
+ return;
+ }
+ if (!continuationHeader) {
+ // backup PES header
+ pesHeaderBackupLen = pesPayloadOffset;
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = pesPayloadOffset;
+ int todo = Count - done;
+ const uchar *data = Data + done;
+ // remember start of the data
+ const uchar *payload = data;
+
+ while (todo > 0) {
+ // collect number of skipped bytes while syncing
+ if (state <= syncing)
+ skippedBytes++;
+ else if (frameTodo > 0) {
+ frameTodo--;
+ if (frameTodo == 0 && state == scanFrame) {
+ // the current audio frame is is done now. So push out the packet to
+ // start a new packet for the next audio frame.
+ PushOutPacket(ResultBuffer, payload, data - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ }
+ // did we reach an audio frame header?
+ scanner <<= 8;
+ scanner |= *data;
+ if ((scanner & 0xFFF00000) == 0xFFF00000) {
+ if (frameTodo <= 0 && IsValidAudioHeader(scanner, mpegLevel == phMPEG2, &frameSize)) {
+ if (state == scanFrame) {
+ // As a new audio frame starts here, the previous one is done. So push
+ // out the packet to start a new packet for the next audio frame. If
+ // the byte count gets negative then the current buffer ends in a
+ // partitial audio frame header that must be stripped off, as it shall
+ // be put in the next packet.
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ esyslog("cAudioRepacker: skipped %d bytes to sync on next audio frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // if there is a PES header available, then use it ...
+ if (pesHeaderBackupLen > 0) {
+ // ISO 13818-1 says:
+ // In the case of audio, if a PTS is present in a PES packet header
+ // it shall refer to the access unit commencing in the PES packet. An
+ // audio access unit commences in a PES packet if the first byte of
+ // the audio access unit is present in the PES packet.
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ }
+ else {
+ // ... otherwise create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+ }
+ // append the first three bytes of the audio frame header
+ pesHeader[pesHeaderLen++] = 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 16) & 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 8) & 0xFF;
+ // the next packet's payload will begin with the fourth byte of
+ // the audio frame header (= the actual byte)
+ payload = data;
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // expected remainder of audio frame: so far we have read 3 bytes from the frame header
+ frameTodo = frameSize - 3;
+ // go on with collecting the frame's data
+ ((int &)state)++;
+ }
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as there is no more space left?
+ if (--packetTodo <= 0) {
+ // We connot start a new packet here if the current might end in an audio
+ // frame header and this header shall possibly be put in the next packet. So
+ // overfill the current packet until we can safely detect that we won't
+ // break an audio frame header into pieces:
+ //
+ // A) the last four bytes were an audio frame header.
+ // B) the last three bytes introduce an audio frame header.
+ // C) the last two bytes introduce an audio frame header.
+ // D) the last byte introduces an audio frame header.
+ //
+ // Todo : Data : Rule : Result
+ // -----:-------------------------------:------:-------
+ // : XX XX FF Fz zz zz|YY YY YY YY : :
+ // 0 : ^^| : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX FF Fz zz|zz YY YY YY : :
+ // 0 : ^^| : B : wait
+ // -1 : |^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX FF Fz|zz zz YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : B : wait
+ // -2 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX XX FF|Fz zz zz YY : :
+ // 0 : ^^| : D : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : B : wait
+ // -3 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ bool A = ((scanner & 0xFFF00000) == 0xFFF00000);
+ bool B = ((scanner & 0xFFF000) == 0xFFF000);
+ bool C = ((scanner & 0xFFF0) == 0xFFF0);
+ bool D = ((scanner & 0xFF) == 0xFF);
+ if (A || (!B && !C && !D)) {
+ // Actually we cannot push out an overfull packet. So we'll have to
+ // adjust the byte count and payload start as necessary. If the byte
+ // count gets negative we'll have to append the excess from fragment's
+ // tail to the next PES header.
+ int bite = data + packetTodo - payload;
+ const uchar *excessData = fragmentData + fragmentLen + bite;
+ // A negative byte count means to drop some bytes from the current
+ // fragment's tail, to not exceed the maximum packet size.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+
+ // copy any excess data
+ while (bite++ < 0) {
+ // append the excess data here
+ pesHeader[pesHeaderLen++] = *excessData++;
+ packetTodo++;
+ }
+ // the next packet's payload will begin here
+ payload = data + packetTodo;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo += maxPacketSize - pesHeaderLen;
+ }
+ }
+ }
+ // The packet is done. Now store any remaining data into fragment buffer
+ // if we are no longer syncing.
+ if (state != syncing) {
+ // append the PES header ...
+ int bite = pesHeaderLen;
+ pesHeaderLen = 0;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, pesHeader, bite);
+ fragmentLen += bite;
+ }
+ // append payload. It may contain part of an audio frame header at it's
+ // end, which will be removed when the next packet gets processed.
+ bite = data - payload;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, payload, bite);
+ fragmentLen += bite;
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ esyslog("cAudioRepacker: skipped %d bytes while syncing on next audio frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cAudioRepacker::BreakAt(const uchar *Data, int Count)
+{
+ int PesPayloadOffset = 0;
+
+ ePesHeader MpegLevel = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (MpegLevel <= phInvalid)
+ return -1; // not enough data for test
+
+ // determine amount of data to fill up packet and to append next audio frame header
+ int packetRemainder = PesPayloadOffset + packetTodo + 4;
+
+ // just detect end of an audio frame
+ if (state == scanFrame) {
+ // when remaining audio frame size is known, then omit scanning
+ if (frameTodo > 0) {
+ // determine amount of data to fill up audio frame and to append next audio frame header
+ int remaining = PesPayloadOffset + frameTodo + 4;
+ if (remaining < packetRemainder)
+ return remaining;
+ return packetRemainder;
+ }
+ // setup local scanner
+ if (localStart < 0) {
+ localScanner = scanner;
+ localStart = 0;
+ }
+ // start where we've stopped at the last run
+ const uchar *data = Data + PesPayloadOffset + localStart;
+ const uchar *limit = Data + Count;
+ // scan data
+ while (data < limit) {
+ localStart++;
+ localScanner <<= 8;
+ localScanner |= *data++;
+ // check whether the next audio frame follows
+ if (((localScanner & 0xFFF00000) == 0xFFF00000) && IsValidAudioHeader(localScanner, MpegLevel == phMPEG2))
+ return data - Data;
+ }
+ }
+ // just fill up packet and append next audio frame header
+ return packetRemainder;
+}
+
// --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker {
@@ -501,8 +903,8 @@ private:
int skippedBytes;
void ResetPesHeader(bool ContinuationFrame = false);
void AppendSubStreamID(bool ContinuationFrame = false);
- bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite);
- bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite);
+ bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+ bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
public:
cDolbyRepacker(void);
virtual void Reset(void);
@@ -569,6 +971,7 @@ void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame)
void cDolbyRepacker::Reset(void)
{
+ cRepacker::Reset();
ResetPesHeader();
state = find_0b;
ac3todo = 0;
@@ -580,26 +983,24 @@ void cDolbyRepacker::Reset(void)
skippedBytes = 0;
}
-bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
+bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
{
+ bool success = true;
// enough data available to put PES packet into buffer?
if (fragmentTodo <= Todo) {
// output a previous fragment first
if (fragmentLen > 0) {
Bite = fragmentLen;
- int n = Put(ResultBuffer, fragmentData, Bite);
- if (Bite != n) {
- Reset();
- return false;
- }
+ int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
+ if (Bite != n)
+ success = false;
fragmentLen = 0;
}
Bite = fragmentTodo;
- int n = Put(ResultBuffer, Data, Bite);
- if (Bite != n) {
- Reset();
- Done += n;
- return false;
+ if (success) {
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
}
fragmentTodo = 0;
// ac3 frame completely processed?
@@ -609,19 +1010,16 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
else {
// copy the fragment into separate buffer for later processing
Bite = Todo;
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, Data, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
}
- return true;
+ return success;
}
-bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
+bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
{
+ bool success = true;
int packetLen = pesHeaderLen + ac3todo;
// limit packet to maximum size
if (packetLen > maxPacketSize)
@@ -631,17 +1029,14 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
Bite = pesHeaderLen;
// enough data available to put PES packet into buffer?
if (packetLen - pesHeaderLen <= Todo) {
- int n = Put(ResultBuffer, pesHeader, Bite);
- if (Bite != n) {
- Reset();
- return false;
- }
+ int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
+ if (Bite != n)
+ success = false;
Bite = packetLen - pesHeaderLen;
- n = Put(ResultBuffer, Data, Bite);
- if (Bite != n) {
- Reset();
- Done += n;
- return false;
+ if (success) {
+ n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
}
// ac3 frame completely processed?
if (Bite >= ac3todo)
@@ -650,24 +1045,16 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
else {
fragmentTodo = packetLen;
// copy the pesheader into separate buffer for later processing
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, pesHeader, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
// copy the fragment into separate buffer for later processing
Bite = Todo;
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, Data, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
}
- return true;
+ return success;
}
void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
@@ -761,8 +1148,9 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
state = find_0b;
continue;
}
- // report that syncing dropped some bytes
- if (skippedBytes > SkippedBytesLimit)
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
esyslog("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// append read data to header for common output processing
@@ -776,18 +1164,11 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
case output_packet: {
int bite = 0;
// finish remainder of ac3 frame?
- if (fragmentTodo > 0) {
- if (!FinishRemainder(ResultBuffer, data, todo, done, bite)) {
- DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
- return;
- }
- }
+ if (fragmentTodo > 0)
+ FinishRemainder(ResultBuffer, data, todo, bite);
else {
// start a new packet
- if (!StartNewPacket(ResultBuffer, data, todo, done, bite)) {
- DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
- return;
- }
+ StartNewPacket(ResultBuffer, data, todo, bite);
// prepare for next (continuation) packet
ResetPesHeader(state == output_packet);
}
@@ -800,7 +1181,8 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
}
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit) {
- esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
+ if (!initiallySyncing) // omit report for the typical initial case
+ esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
skippedBytes = SkippedBytesLimit;
}
}
@@ -890,7 +1272,8 @@ private:
uint8_t hlength;
int mpeg;
uint8_t check;
- int which;
+ int mpeg1_required;
+ int mpeg1_stuffing;
bool done;
cRingBufferLinear *resultBuffer;
int tsErrors;
@@ -956,11 +1339,8 @@ void cTS2PES::store(uint8_t *Data, int Count)
{
if (repacker)
repacker->Repack(resultBuffer, Data, Count);
- else {
- int n = resultBuffer->Put(Data, Count);
- if (n != Count)
- esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
- }
+ else
+ cRepacker::Put(resultBuffer, Data, Count, Count);
}
void cTS2PES::reset_ipack(void)
@@ -973,7 +1353,8 @@ void cTS2PES::reset_ipack(void)
hlength = 0;
mpeg = 0;
check = 0;
- which = 0;
+ mpeg1_required = 0;
+ mpeg1_stuffing = 0;
done = false;
count = 0;
}
@@ -1057,7 +1438,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
{
int c = 0;
- while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
switch (found ) {
case 0:
case 1:
@@ -1103,6 +1484,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
plength = ntohs(*pl);
c += 2;
found += 2;
+ mpeg1_stuffing = 0;
}
else {
plen[0] = Buf[c];
@@ -1115,32 +1497,54 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
unsigned short *pl = (unsigned short *)plen;
plength = ntohs(*pl);
found++;
+ mpeg1_stuffing = 0;
}
break;
case 6:
if (!done) {
flag1 = Buf[c++];
found++;
- if ((flag1 & 0xC0) == 0x80 )
- mpeg = 2;
- else {
- hlength = 0;
- which = 0;
- mpeg = 1;
- flag2 = 0;
+ if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level
+ if ((flag1 & 0xC0) == 0x80)
+ mpeg = 2;
+ else {
+ mpeg = 1;
+ mpeg1_required = 7;
+ }
+ }
+ if (mpeg == 1) {
+ if (flag1 == 0xFF) { // MPEG1 stuffing
+ if (++mpeg1_stuffing > 16)
+ found = 0; // invalid MPEG1 header
+ else { // ignore stuffing
+ found--;
+ if (plength > 0)
+ plength--;
+ }
+ }
+ else if ((flag1 & 0xC0) == 0x40) // STD_buffer_scale/size
+ mpeg1_required += 2;
+ else if (flag1 != 0x0F && (flag1 & 0xF0) != 0x20 && (flag1 & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
+ else {
+ flag2 = 0;
+ hlength = 0;
+ }
}
}
break;
case 7:
- if (!done && mpeg == 2) {
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
flag2 = Buf[c++];
found++;
}
break;
case 8:
- if (!done && mpeg == 2) {
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
hlength = Buf[c++];
found++;
+ if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
}
break;
default:
@@ -1151,7 +1555,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
if (!plength)
plength = MMAX_PLENGTH - 6;
- if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
+ if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= mpeg1_required))) {
switch (cid) {
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
@@ -1163,8 +1567,13 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
write_ipack(&hlength, 1);
}
- if (mpeg == 1 && found == 7)
+ if (mpeg == 1 && found == mpeg1_required) {
write_ipack(&flag1, 1);
+ if (mpeg1_required > 7) {
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+ }
if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
while (c < Count && found < 14) {
@@ -1277,7 +1686,12 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
if (APids) {
int n = 0;
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
+#define TEST_cAudioRepacker
+#ifdef TEST_cAudioRepacker
+ ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++, 0x00, new cAudioRepacker);
+#else
ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
+#endif
}
if (DPids) {
int n = 0;
@@ -1321,10 +1735,9 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
// If the return value is -1 the packet was not completely in the buffer.
int Length = GetPacketLength(Data, Count, Offset);
if (Length > 0) {
- if (Length >= 8) {
- int i = Offset + 8; // the minimum length of the video packet header
- i += Data[i] + 1; // possible additional header bytes
- for (; i < Offset + Length - 5; i++) {
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
+ for (int i = Offset + PesPayloadOffset; i < Offset + Length - 5; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
switch (Data[i + 3]) {
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
@@ -1409,19 +1822,6 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
return resultBuffer->Get(Count);
#endif
- // Special VPID case to enable recording radio channels:
-
- if (isRadio) {
- // XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
- // XXX also allowing 0x1FFF to not break Michael Paar's original patch,
- // XXX but it would probably be best to only use '0'
- // Force syncing of radio channels to avoid "no useful data" error
- synced = true;
- if (PictureType)
- *PictureType = I_FRAME;
- return resultBuffer->Get(Count);
- }
-
// Check for frame borders:
if (PictureType)
@@ -1466,6 +1866,18 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
l = GetPacketLength(data, resultCount, i);
if (l < 0)
return resultData;
+ if (isRadio) {
+ if (!synced) {
+ if (PictureType)
+ *PictureType = I_FRAME;
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
+ }
+ else if (Count)
+ return resultData;
+ else if (PictureType)
+ *PictureType = I_FRAME;
+ }
}
if (synced) {
if (!Count)
@@ -1499,8 +1911,9 @@ void cRemux::Clear(void)
void cRemux::SetBrokenLink(uchar *Data, int Length)
{
- if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
- for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
+ for (int i = PesPayloadOffset; i < Length - 7; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
Data[i + 7] |= 0x20;
diff --git a/remux.h b/remux.h
index 0cd570e..ab29655 100644
--- a/remux.h
+++ b/remux.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.h 1.14 2005/08/07 10:28:07 kls Exp $
+ * $Id: remux.h 1.15 2005/08/26 13:22:19 kls Exp $
*/
#ifndef __REMUX_H
@@ -15,6 +15,15 @@
#include "ringbuffer.h"
#include "tools.h"
+enum ePesHeader {
+ phNeedMoreData = -1,
+ phInvalid = 0,
+ phMPEG1 = 1,
+ phMPEG2 = 2
+ };
+
+ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL);
+
// Picture types:
#define NO_PICTURE 0
#define I_FRAME 1
diff --git a/sdt.c b/sdt.c
index dcf6dc1..74c5503 100644
--- a/sdt.c
+++ b/sdt.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: sdt.c 1.14 2005/05/14 09:39:46 kls Exp $
+ * $Id: sdt.c 1.15 2005/08/27 09:27:47 kls Exp $
*/
#include "sdt.h"
@@ -64,11 +64,14 @@ void cSdtFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
char *ps = compactspace(ShortNameBuf);
if (!*ps && cSource::IsCable(Source())) {
// Some cable providers don't mark short channel names according to the
- // standard, but rather go their own way and use "name>short name":
- char *p = strchr(pn, '>');
+ // standard, but rather go their own way and use "name>short name" or
+ // "name, short name":
+ char *p = strchr(pn, '>'); // fix for UPC Wien
+ if (!p)
+ p = strchr(pn, ','); // fix for "Kabel Deutschland"
if (p && p > pn) {
*p++ = 0;
- strcpy(ShortNameBuf, p);
+ strcpy(ShortNameBuf, skipspace(p));
}
}
sd->providerName.getText(ProviderNameBuf, sizeof(ProviderNameBuf));
diff --git a/svdrp.c b/svdrp.c
index 093bd99..889ffe4 100644
--- a/svdrp.c
+++ b/svdrp.c
@@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
- * $Id: svdrp.c 1.74 2005/08/07 14:20:41 kls Exp $
+ * $Id: svdrp.c 1.77 2005/08/28 14:12:00 kls Exp $
*/
#include "svdrp.h"
@@ -28,10 +28,12 @@
#include <unistd.h>
#include "channels.h"
#include "config.h"
+#include "cutter.h"
#include "device.h"
#include "eitscan.h"
#include "keys.h"
#include "menu.h"
+#include "plugin.h"
#include "remote.h"
#include "timers.h"
#include "tools.h"
@@ -194,6 +196,10 @@ const char *HelpPages[] = {
" RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING!",
"DELT <number>\n"
" Delete timer.",
+ "EDIT <number>\n"
+ " Edit the recording with the given number. Before a recording can be\n"
+ " edited, an LSTR command must have been executed in order to retrieve\n"
+ " the recording numbers.",
"GRAB <filename> [ jpeg | pnm [ <quality> [ <sizex> <sizey> ] ] ]\n"
" Grab the current frame and save it to the given file. Images can\n"
" be stored as JPEG (default) or PNM, at the given quality (default\n"
@@ -253,6 +259,22 @@ const char *HelpPages[] = {
" zero, this means that the timer is currently recording and has started\n"
" at the given time. The first value in the resulting line is the number\n"
" of the timer.",
+ "PLAY <number> [ begin | <position> ]\n"
+ " Play the recording with the given number. Before a recording can be\n"
+ " played, an LSTR command must have been executed in order to retrieve\n"
+ " the recording numbers.\n"
+ " The keyword 'begin' plays the recording from its very beginning, while\n"
+ " a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that\n"
+ " position. If neither 'begin' nor a <position> are given, replay is resumed\n"
+ " at the position where any previous replay was stopped, or from the beginning\n"
+ " by default. To control or stop the replay session, use the usual remote\n"
+ " control keypresses via the HITK command.",
+ "PLUG <name> [ <command> [ <options> ]]\n"
+ " Send a command to a plugin.\n"
+ " The PLUG command without any parameters lists all plugins.\n"
+ " If only a name is given, all commands known to that plugin are listed.\n"
+ " If a command is given (optionally followed by parameters), that command\n"
+ " is sent to the plugin, and the result will be displayed.",
"PUTE\n"
" Put data into the EPG list. The data entered has to strictly follow the\n"
" format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n"
@@ -294,6 +316,8 @@ const char *HelpPages[] = {
504 Command parameter not implemented
550 Requested action not taken
554 Transaction failed
+ 900 Default plugin reply code
+ 901..999 Plugin specific reply codes
*/
@@ -315,15 +339,16 @@ const char *GetHelpTopic(const char *HelpPage)
return NULL;
}
-const char *GetHelpPage(const char *Cmd)
+const char *GetHelpPage(const char *Cmd, const char **p)
{
- const char **p = HelpPages;
- while (*p) {
- const char *t = GetHelpTopic(*p);
- if (strcasecmp(Cmd, t) == 0)
- return *p;
- p++;
- }
+ if (p) {
+ while (*p) {
+ const char *t = GetHelpTopic(*p);
+ if (strcasecmp(Cmd, t) == 0)
+ return *p;
+ p++;
+ }
+ }
return NULL;
}
@@ -376,14 +401,14 @@ void cSVDRP::Reply(int Code, const char *fmt, ...)
va_start(ap, fmt);
char *buffer;
vasprintf(&buffer, fmt, ap);
- char *nl = strchr(buffer, '\n');
- if (Code > 0 && nl && *(nl + 1)) // trailing newlines don't count!
- Code = -Code;
- char number[16];
- sprintf(number, "%03d%c", abs(Code), Code < 0 ? '-' : ' ');
const char *s = buffer;
while (s && *s) {
const char *n = strchr(s, '\n');
+ char cont = ' ';
+ if (Code < 0 || n && *(n + 1)) // trailing newlines don't count!
+ cont = '-';
+ char number[16];
+ sprintf(number, "%03d%c", abs(Code), cont);
if (!(Send(number) && Send(s, n ? n - s : -1) && Send("\r\n"))) {
Close();
break;
@@ -400,6 +425,32 @@ void cSVDRP::Reply(int Code, const char *fmt, ...)
}
}
+void cSVDRP::PrintHelpTopics(const char **hp)
+{
+ int NumPages = 0;
+ if (hp) {
+ while (*hp) {
+ NumPages++;
+ hp++;
+ }
+ hp -= NumPages;
+ }
+ const int TopicsPerLine = 5;
+ int x = 0;
+ for (int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
+ char buffer[TopicsPerLine * MAXHELPTOPIC + 5];
+ char *q = buffer;
+ q += sprintf(q, " ");
+ for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
+ const char *topic = GetHelpTopic(hp[(y * TopicsPerLine + x)]);
+ if (topic)
+ q += sprintf(q, "%*s", -MAXHELPTOPIC, topic);
+ }
+ x = 0;
+ Reply(-214, buffer);
+ }
+}
+
void cSVDRP::CmdCHAN(const char *Option)
{
if (*Option) {
@@ -561,6 +612,36 @@ void cSVDRP::CmdDELT(const char *Option)
Reply(501, "Missing timer number");
}
+void cSVDRP::CmdEDIT(const char *Option)
+{
+ if (*Option) {
+ if (isnumber(Option)) {
+ cRecording *recording = Recordings.Get(strtol(Option, NULL, 10) - 1);
+ if (recording) {
+ cMarks Marks;
+ if (Marks.Load(recording->FileName()) && Marks.Count()) {
+ if (!cCutter::Active()) {
+ if (cCutter::Start(recording->FileName()))
+ Reply(250, "Editing recording \"%s\" [%s]", Option, recording->Title());
+ else
+ Reply(554, "Can't start editing process");
+ }
+ else
+ Reply(554, "Editing process already active");
+ }
+ else
+ Reply(554, "No editing marks defined");
+ }
+ else
+ Reply(550, "Recording \"%s\" not found%s", Option, Recordings.Count() ? "" : " (use LSTR before editing)");
+ }
+ else
+ Reply(501, "Error in recording number \"%s\"", Option);
+ }
+ else
+ Reply(501, "Missing recording number");
+}
+
void cSVDRP::CmdGRAB(const char *Option)
{
char *FileName = NULL;
@@ -626,7 +707,7 @@ void cSVDRP::CmdGRAB(const char *Option)
void cSVDRP::CmdHELP(const char *Option)
{
if (*Option) {
- const char *hp = GetHelpPage(Option);
+ const char *hp = GetHelpPage(Option, HelpPages);
if (hp)
Reply(214, hp);
else {
@@ -637,24 +718,13 @@ void cSVDRP::CmdHELP(const char *Option)
else {
Reply(-214, "This is VDR version %s", VDRVERSION);
Reply(-214, "Topics:");
- const char **hp = HelpPages;
- int NumPages = 0;
- while (*hp) {
- NumPages++;
- hp++;
- }
- const int TopicsPerLine = 5;
- int x = 0;
- for (int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
- char buffer[TopicsPerLine * (MAXHELPTOPIC + 5)];
- char *q = buffer;
- for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
- const char *topic = GetHelpTopic(HelpPages[(y * TopicsPerLine + x)]);
- if (topic)
- q += sprintf(q, " %s", topic);
- }
- x = 0;
- Reply(-214, buffer);
+ PrintHelpTopics(HelpPages);
+ cPlugin *plugin;
+ for (int i = 0; (plugin = cPluginManager::GetPlugin(i)) != NULL; i++) {
+ const char **hp = plugin->SVDRPHelpPages();
+ if (hp)
+ Reply(-214, "Plugin %s v%s - %s", plugin->Name(), plugin->Version(), plugin->Description());
+ PrintHelpTopics(hp);
}
Reply(-214, "To report bugs in the implementation send email to");
Reply(-214, " vdr-bugs@cadsoft.de");
@@ -1042,6 +1112,120 @@ void cSVDRP::CmdNEXT(const char *Option)
Reply(550, "No active timers");
}
+void cSVDRP::CmdPLAY(const char *Option)
+{
+ if (*Option) {
+ char *opt = strdup(Option);
+ char *num = skipspace(opt);
+ char *option = num;
+ while (*option && !isspace(*option))
+ option++;
+ char c = *option;
+ *option = 0;
+ if (isnumber(num)) {
+ cRecording *recording = Recordings.Get(strtol(num, NULL, 10) - 1);
+ if (recording) {
+ if (c)
+ option = skipspace(++option);
+ cReplayControl::SetRecording(NULL, NULL);
+ cControl::Shutdown();
+ if (*option) {
+ int pos = 0;
+ if (strcasecmp(option, "BEGIN") != 0) {
+ int h, m = 0, s = 0, f = 1;
+ int x = sscanf(option, "%d:%d:%d.%d", &h, &m, &s, &f);
+ if (x == 1)
+ pos = h;
+ else if (x >= 3)
+ pos = (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
+ }
+ cResumeFile resume(recording->FileName());
+ if (pos <= 0)
+ resume.Delete();
+ else
+ resume.Save(pos);
+ }
+ cReplayControl::SetRecording(recording->FileName(), recording->Title());
+ cControl::Launch(new cReplayControl);
+ cControl::Attach();
+ Reply(250, "Playing recording \"%s\" [%s]", num, recording->Title());
+ }
+ else
+ Reply(550, "Recording \"%s\" not found%s", num, Recordings.Count() ? "" : " (use LSTR before playing)");
+ }
+ else
+ Reply(501, "Error in recording number \"%s\"", num);
+ free(opt);
+ }
+ else
+ Reply(501, "Missing recording number");
+}
+
+void cSVDRP::CmdPLUG(const char *Option)
+{
+ if (*Option) {
+ char *opt = strdup(Option);
+ char *name = skipspace(opt);
+ char *option = name;
+ while (*option && !isspace(*option))
+ option++;
+ char c = *option;
+ *option = 0;
+ cPlugin *plugin = cPluginManager::GetPlugin(name);
+ if (plugin) {
+ if (c)
+ option = skipspace(++option);
+ char *cmd = option;
+ while (*option && !isspace(*option))
+ option++;
+ if (*option) {
+ *option++ = 0;
+ option = skipspace(option);
+ }
+ if (!*cmd || strcasecmp(cmd, "HELP") == 0) {
+ if (*cmd && *option) {
+ const char *hp = GetHelpPage(option, plugin->SVDRPHelpPages());
+ if (hp) {
+ Reply(-214, hp);
+ Reply(214, "End of HELP info");
+ }
+ else
+ Reply(504, "HELP topic \"%s\" for plugin \"%s\" unknown", option, plugin->Name());
+ }
+ else {
+ Reply(-214, "Plugin %s v%s - %s", plugin->Name(), plugin->Version(), plugin->Description());
+ const char **hp = plugin->SVDRPHelpPages();
+ if (hp) {
+ Reply(-214, "SVDRP commands:");
+ PrintHelpTopics(hp);
+ Reply(214, "End of HELP info");
+ }
+ else
+ Reply(214, "This plugin has no SVDRP commands");
+ }
+ }
+ else {
+ int ReplyCode = 900;
+ cString s = plugin->SVDRPCommand(cmd, option, ReplyCode);
+ if (s)
+ Reply(abs(ReplyCode), *s);
+ else
+ Reply(500, "Command unrecognized: \"%s\"", cmd);
+ }
+ }
+ else
+ Reply(550, "Plugin \"%s\" not found (use PLUG for a list of plugins)", name);
+ free(opt);
+ }
+ else {
+ Reply(-214, "Available plugins:");
+ cPlugin *plugin;
+ for (int i = 0; (plugin = cPluginManager::GetPlugin(i)) != NULL; i++)
+ Reply(-214, "%s v%s - %s", plugin->Name(), plugin->Version(), plugin->Description());
+ Reply(214, "End of plugin list");
+ }
+}
+
void cSVDRP::CmdPUTE(const char *Option)
{
delete PUTEhandler;
@@ -1152,6 +1336,7 @@ void cSVDRP::Execute(char *Cmd)
else if (CMD("DELC")) CmdDELC(s);
else if (CMD("DELR")) CmdDELR(s);
else if (CMD("DELT")) CmdDELT(s);
+ else if (CMD("EDIT")) CmdEDIT(s);
else if (CMD("GRAB")) CmdGRAB(s);
else if (CMD("HELP")) CmdHELP(s);
else if (CMD("HITK")) CmdHITK(s);
@@ -1167,6 +1352,8 @@ void cSVDRP::Execute(char *Cmd)
else if (CMD("NEWC")) CmdNEWC(s);
else if (CMD("NEWT")) CmdNEWT(s);
else if (CMD("NEXT")) CmdNEXT(s);
+ else if (CMD("PLAY")) CmdPLAY(s);
+ else if (CMD("PLUG")) CmdPLUG(s);
else if (CMD("PUTE")) CmdPUTE(s);
else if (CMD("SCAN")) CmdSCAN(s);
else if (CMD("STAT")) CmdSTAT(s);
diff --git a/svdrp.h b/svdrp.h
index 815c91e..baa4fb3 100644
--- a/svdrp.h
+++ b/svdrp.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: svdrp.h 1.20 2004/01/17 13:30:52 kls Exp $
+ * $Id: svdrp.h 1.23 2005/08/28 14:10:32 kls Exp $
*/
#ifndef __SVDRP_H
@@ -52,11 +52,13 @@ private:
void Close(bool Timeout = false);
bool Send(const char *s, int length = -1);
void Reply(int Code, const char *fmt, ...);
+ void PrintHelpTopics(const char **hp);
void CmdCHAN(const char *Option);
void CmdCLRE(const char *Option);
void CmdDELC(const char *Option);
void CmdDELR(const char *Option);
void CmdDELT(const char *Option);
+ void CmdEDIT(const char *Option);
void CmdGRAB(const char *Option);
void CmdHELP(const char *Option);
void CmdHITK(const char *Option);
@@ -72,6 +74,8 @@ private:
void CmdNEWC(const char *Option);
void CmdNEWT(const char *Option);
void CmdNEXT(const char *Option);
+ void CmdPLAY(const char *Option);
+ void CmdPLUG(const char *Option);
void CmdPUTE(const char *Option);
void CmdSCAN(const char *Option);
void CmdSTAT(const char *Option);
diff --git a/tools.c b/tools.c
index ca4b0b8..46d3ce2 100644
--- a/tools.c
+++ b/tools.c
@@ -4,13 +4,14 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.96 2005/08/06 09:53:21 kls Exp $
+ * $Id: tools.c 1.97 2005/08/27 14:43:55 kls Exp $
*/
#include "tools.h"
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/vfs.h>
@@ -530,6 +531,15 @@ cString &cString::operator=(const cString &String)
return *this;
}
+cString cString::sprintf(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ char *buffer;
+ vasprintf(&buffer, fmt, ap);
+ return cString(buffer, true);
+}
+
cString WeekDayName(int WeekDay)
{
char buffer[4];
diff --git a/tools.h b/tools.h
index 0a8ee8b..41a0049 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.74 2005/08/21 14:06:38 kls Exp $
+ * $Id: tools.h 1.75 2005/08/27 14:40:08 kls Exp $
*/
#ifndef __TOOLS_H
@@ -81,6 +81,7 @@ public:
operator const char * () const { return s; } // for use in (const char *) context
const char * operator*() const { return s; } // for use in (const void *) context (printf() etc.)
cString &operator=(const cString &String);
+ static cString sprintf(const char *fmt, ...);
};
ssize_t safe_read(int filedes, void *buffer, size_t size);