summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2002-06-16 13:26:00 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2002-06-16 13:26:00 +0200
commit3ab746babd6d879f05b4cbc76662c65cf4146397 (patch)
tree0312baea9a9cef76689a86d6e74ffed68320e1cc
parenta4bfddd2f995ad03409de005bc3015437c10aa06 (diff)
downloadvdr-3ab746babd6d879f05b4cbc76662c65cf4146397.tar.gz
vdr-3ab746babd6d879f05b4cbc76662c65cf4146397.tar.bz2
Changed 'cStatusMonitor' to 'cStatus'1.1.3
-rw-r--r--HISTORY2
-rw-r--r--PLUGINS.html10
-rw-r--r--PLUGINS/src/status/COPYING340
-rw-r--r--PLUGINS/src/status/HISTORY6
-rw-r--r--PLUGINS/src/status/Makefile79
-rw-r--r--PLUGINS/src/status/README11
-rw-r--r--PLUGINS/src/status/status.c179
-rw-r--r--device.c8
-rw-r--r--dvbapi.c2564
-rw-r--r--dvbapi.h283
-rw-r--r--interface.c10
-rw-r--r--menu.c14
-rw-r--r--menuitems.c8
-rw-r--r--osd.c6
-rw-r--r--status.c58
-rw-r--r--status.h10
16 files changed, 678 insertions, 2910 deletions
diff --git a/HISTORY b/HISTORY
index 36df4217..b15541c5 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1319,7 +1319,7 @@ Video Disk Recorder Revision History
accessed (suggested by Stefan Huelswitt).
- Rearranged OSD class names to make 'cOsd' available for the main OSD interface.
- Completely moved OSD handling out of the cDvbApi class, into the new cOsd.
-- Implemented cStatusMonitor to allow plugins to set up a status monitor.
+- Implemented cStatus to allow plugins to set up a status monitor.
See PLUGINS.html for details.
- Moved the cEITScanner out of dvbapi.h/.c, into the new eitscan.h/.c.
- Added Swedish language texts (thanks to Tomas Prybil).
diff --git a/PLUGINS.html b/PLUGINS.html
index a0f7ebc9..a3adc937 100644
--- a/PLUGINS.html
+++ b/PLUGINS.html
@@ -857,12 +857,12 @@ plugin's name, and <tt>0.0.1</tt> will be your plugin's current version number.
<center><i><b>A piece of the action</b></i></center><p>
If a plugin wants to get informed on various events in VDR, it can derive a class from
-<tt>cStatusMonitor</tt>, as in
+<tt>cStatus</tt>, as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
#include &lt;vdr/status.h&gt;
-class cMyStatusMonitor : public cStatusMonitor {
+class cMyStatusMonitor : public cStatus {
protected:
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
};
@@ -914,15 +914,15 @@ Note that the actual object is created in the <tt>Start()</tt> function, not in
constructor! It is also important to delete the object in the destructor, in order to
avoid memory leaks.
<p>
-A Plugin can implement any number of <tt>cStatusMonitor</tt> derived objects, and once
+A Plugin can implement any number of <tt>cStatus</tt> derived objects, and once
the plugin has been started it may create and delete them as necessary.
-No further action apart from creating an object derived from <tt>cStatusMonitor</tt>
+No further action apart from creating an object derived from <tt>cStatus</tt>
is necessary. VDR will automatically hook it into a list of status monitors, with
their individual virtual member functions being called in the same sequence as the
objects were created.
<p>
See the file <tt>status.h</tt> for detailed information on which status monitor
-member functions are available in <tt>cStatusMonitor</tt>. You only need to implement
+member functions are available in <tt>cStatus</tt>. You only need to implement
the functions you actually want to use.
<!--X1.1.3--></td></tr></table>
diff --git a/PLUGINS/src/status/COPYING b/PLUGINS/src/status/COPYING
new file mode 100644
index 00000000..5b6e7c66
--- /dev/null
+++ b/PLUGINS/src/status/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/status/HISTORY b/PLUGINS/src/status/HISTORY
new file mode 100644
index 00000000..27929362
--- /dev/null
+++ b/PLUGINS/src/status/HISTORY
@@ -0,0 +1,6 @@
+VDR Plugin 'status' Revision History
+------------------------------------
+
+2002-05-18: Version 0.0.1
+
+- Initial revision.
diff --git a/PLUGINS/src/status/Makefile b/PLUGINS/src/status/Makefile
new file mode 100644
index 00000000..b583e7b5
--- /dev/null
+++ b/PLUGINS/src/status/Makefile
@@ -0,0 +1,79 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id: Makefile 1.1 2002/06/16 13:26:00 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 = status
+
+### 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 directory environment:
+
+DVBDIR = ../../../../DVB/ost/include
+VDRDIR = ../../..
+VDRINC = $(VDRDIR)/include
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### 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$(VDRINC) -I$(DVBDIR)
+
+DEFINES = -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o
+
+### The C++ compiler and options:
+
+CXX = g++
+CXXFLAGS = -g -O2 -Wall -Woverloaded-virtual
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -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)
+
+package: 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/status/README b/PLUGINS/src/status/README
new file mode 100644
index 00000000..356f1f8d
--- /dev/null
+++ b/PLUGINS/src/status/README
@@ -0,0 +1,11 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Klaus Schmidinger <kls@cadsoft.de>
+
+Project's homepage: www.cadsoft.de/people/kls/vdr
+
+Latest version available at: www.cadsoft.de/people/kls/vdr/software.htm
+
+See the file COPYING for license information.
+
+Description: This is an example that shows the use of cStatus.
diff --git a/PLUGINS/src/status/status.c b/PLUGINS/src/status/status.c
new file mode 100644
index 00000000..2d5f42f3
--- /dev/null
+++ b/PLUGINS/src/status/status.c
@@ -0,0 +1,179 @@
+/*
+ * status.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id: status.c 1.1 2002/06/16 13:26:00 kls Exp $
+ */
+
+#include <vdr/plugin.h>
+#include <vdr/status.h>
+
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Status monitor test";
+static const char *MAINMENUENTRY = NULL;
+
+// ---
+
+class cStatusTest : public cStatus {
+protected:
+ virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
+ virtual void Recording(const cDevice *Device, const char *Name);
+ virtual void Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name);
+ virtual void SetVolume(int Volume, bool Absolute);
+ virtual void OsdClear(void);
+ virtual void OsdTitle(const char *Title);
+ virtual void OsdStatusMessage(const char *Message);
+ virtual void OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue);
+ virtual void OsdCurrentItem(const char *Text);
+ virtual void OsdTextItem(const char *Text, bool Scroll);
+ virtual void OsdChannel(const char *Text);
+ virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle);
+ };
+
+void cStatusTest::ChannelSwitch(const cDevice *Device, int ChannelNumber)
+{
+ dsyslog("status: cStatusTest::ChannelSwitch %d %d", Device->CardIndex(), ChannelNumber);
+}
+
+void cStatusTest::Recording(const cDevice *Device, const char *Name)
+{
+ dsyslog("status: cStatusTest::Recording %d %s", Device->CardIndex(), Name);
+}
+
+void cStatusTest::Replaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
+{
+ dsyslog("status: cStatusTest::Replaying %s", Name);
+}
+
+void cStatusTest::SetVolume(int Volume, bool Absolute)
+{
+ dsyslog("status: cStatusTest::SetVolume %d %d", Volume, Absolute);
+}
+
+void cStatusTest::OsdClear(void)
+{
+ dsyslog("status: cStatusTest::OsdClear");
+}
+
+void cStatusTest::OsdTitle(const char *Title)
+{
+ dsyslog("status: cStatusTest::OsdTitle '%s'", Title);
+}
+
+void cStatusTest::OsdStatusMessage(const char *Message)
+{
+ dsyslog("status: cStatusTest::OsdStatusMessage '%s'", Message);
+}
+
+void cStatusTest::OsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+{
+ dsyslog("status: cStatusTest::OsdHelpKeys %s - %s - %s - %s", Red, Green, Yellow, Blue);
+}
+
+void cStatusTest::OsdCurrentItem(const char *Text)
+{
+ dsyslog("status: cStatusTest::OsdCurrentItem %s", Text);
+}
+
+void cStatusTest::OsdTextItem(const char *Text, bool Scroll)
+{
+ dsyslog("status: cStatusTest::OsdTextItem %s %d", Text, Scroll);
+}
+
+void cStatusTest::OsdChannel(const char *Text)
+{
+ dsyslog("status: cStatusTest::OsdChannel %s", Text);
+}
+
+void cStatusTest::OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
+{
+ char buffer[25];
+ struct tm tm_r;
+ dsyslog("status: cStatusTest::OsdProgramme");
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&PresentTime, &tm_r));
+ dsyslog("%5s %s", buffer, PresentTitle);
+ dsyslog("%5s %s", "", PresentSubtitle);
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&FollowingTime, &tm_r));
+ dsyslog("%5s %s", buffer, FollowingTitle);
+ dsyslog("%5s %s", "", FollowingSubtitle);
+}
+
+// ---
+
+class cPluginStatus : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+ cStatusTest *statusTest;
+public:
+ cPluginStatus(void);
+ virtual ~cPluginStatus();
+ virtual const char *Version(void) { return VERSION; }
+ virtual const char *Description(void) { return DESCRIPTION; }
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Start(void);
+ virtual void Housekeeping(void);
+ virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
+ virtual cOsdMenu *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ };
+
+cPluginStatus::cPluginStatus(void)
+{
+ // Initialize any member varaiables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ statusTest = NULL;
+}
+
+cPluginStatus::~cPluginStatus()
+{
+ // Clean up after yourself!
+ delete statusTest;
+}
+
+const char *cPluginStatus::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool cPluginStatus::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginStatus::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ statusTest = new cStatusTest;
+ return true;
+}
+
+void cPluginStatus::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+cOsdMenu *cPluginStatus::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ return NULL;
+}
+
+cMenuSetupPage *cPluginStatus::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return NULL;
+}
+
+bool cPluginStatus::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return false;
+}
+
+VDRPLUGINCREATOR(cPluginStatus); // Don't touch this!
diff --git a/device.c b/device.c
index 8da7fdde..fe756592 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.1 2002/06/16 12:29:09 kls Exp $
+ * $Id: device.c 1.2 2002/06/16 13:23:31 kls Exp $
*/
#include "device.h"
@@ -459,7 +459,7 @@ eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Pol
//XXX+StopTransfer();
//XXX+StopReplay();
- cStatusMonitor::MsgChannelSwitch(this, 0);
+ cStatus::MsgChannelSwitch(this, 0);
// Must set this anyway to avoid getting stuck when switching through
// channels with 'Up' and 'Down' keys:
@@ -639,7 +639,7 @@ eSetChannelResult cDevice::SetChannel(int ChannelNumber, int Frequency, char Pol
if (Result == scrOk && siProcessor)
siProcessor->SetCurrentTransponder(Frequency);
- cStatusMonitor::MsgChannelSwitch(this, ChannelNumber);
+ cStatus::MsgChannelSwitch(this, ChannelNumber);
return Result;
}
@@ -660,7 +660,7 @@ void cDevice::SetVolume(int Volume, bool Absolute)
audioMixer_t am;
am.volume_left = am.volume_right = volume;
CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
- cStatusMonitor::MsgSetVolume(volume, Absolute);
+ cStatus::MsgSetVolume(volume, Absolute);
if (volume > 0)
mute = false;
}
diff --git a/dvbapi.c b/dvbapi.c
deleted file mode 100644
index aa4f50e3..00000000
--- a/dvbapi.c
+++ /dev/null
@@ -1,2564 +0,0 @@
-/*
- * dvbapi.c: Interface to the DVB driver
- *
- * See the main source file 'vdr.c' for copyright information and
- * how to reach the author.
- *
- * $Id: dvbapi.c 1.180 2002/05/20 10:58:33 kls Exp $
- */
-
-#include "dvbapi.h"
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-extern "C" {
-#define HAVE_BOOLEAN
-#include <jpeglib.h>
-}
-#include <stdlib.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include "config.h"
-#include "recording.h"
-#include "remux.h"
-#include "ringbuffer.h"
-#include "status.h"
-#include "tools.h"
-#include "videodir.h"
-
-#define DEV_VIDEO "/dev/video"
-#define DEV_OST_OSD "/dev/ost/osd"
-#define DEV_OST_FRONTEND "/dev/ost/frontend"
-#define DEV_OST_SEC "/dev/ost/sec"
-#define DEV_OST_DVR "/dev/ost/dvr"
-#define DEV_OST_DEMUX "/dev/ost/demux"
-#define DEV_OST_VIDEO "/dev/ost/video"
-#define DEV_OST_AUDIO "/dev/ost/audio"
-
-// The size of the array used to buffer video data:
-// (must be larger than MINVIDEODATA - see remux.h)
-#define VIDEOBUFSIZE MEGABYTE(1)
-
-// The maximum size of a single frame:
-#define MAXFRAMESIZE KILOBYTE(192)
-
-#define MAXFILESPERRECORDING 255
-
-#define MINFREEDISKSPACE (512) // MB
-#define DISKCHECKINTERVAL 100 // seconds
-
-#define INDEXFILESUFFIX "/index.vdr"
-#define RECORDFILESUFFIX "/%03d.vdr"
-#define RECORDFILESUFFIXLEN 20 // some additional bytes for safety...
-
-// The number of frames to back up when resuming an interrupted replay session:
-#define RESUMEBACKUP (10 * FRAMESPERSEC)
-
-// The maximum time we wait before assuming that a recorded video data stream
-// is broken:
-#define MAXBROKENTIMEOUT 30 // seconds
-
-// The maximum time to wait before giving up while catching up on an index file:
-#define MAXINDEXCATCHUP 2 // seconds
-
-// The default priority for non-primary DVB cards:
-#define DEFAULTPRIORITY -2
-
-#define CHECK(s) { if ((s) < 0) LOG_ERROR; } // used for 'ioctl()' calls
-
-#define FATALERRNO (errno != EAGAIN && errno != EINTR)
-
-typedef unsigned char uchar;
-
-const char *IndexToHMSF(int Index, bool WithFrame)
-{
- static char buffer[16];
- int f = (Index % FRAMESPERSEC) + 1;
- int s = (Index / FRAMESPERSEC);
- int m = s / 60 % 60;
- int h = s / 3600;
- s %= 60;
- snprintf(buffer, sizeof(buffer), WithFrame ? "%d:%02d:%02d.%02d" : "%d:%02d:%02d", h, m, s, f);
- return buffer;
-}
-
-int HMSFToIndex(const char *HMSF)
-{
- int h, m, s, f = 0;
- if (3 <= sscanf(HMSF, "%d:%d:%d.%d", &h, &m, &s, &f))
- return (h * 3600 + m * 60 + s) * FRAMESPERSEC + f - 1;
- return 0;
-}
-
-// --- cIndexFile ------------------------------------------------------------
-
-class cIndexFile {
-private:
- struct tIndex { int offset; uchar type; uchar number; short reserved; };
- int f;
- char *fileName;
- int size, last;
- tIndex *index;
- cResumeFile resumeFile;
- bool CatchUp(int Index = -1);
-public:
- cIndexFile(const char *FileName, bool Record);
- ~cIndexFile();
- bool Ok(void) { return index != NULL; }
- bool Write(uchar PictureType, uchar FileNumber, int FileOffset);
- bool Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType = NULL, int *Length = NULL);
- int GetNextIFrame(int Index, bool Forward, uchar *FileNumber = NULL, int *FileOffset = NULL, int *Length = NULL, bool StayOffEnd = false);
- int Get(uchar FileNumber, int FileOffset);
- int Last(void) { CatchUp(); return last; }
- int GetResume(void) { return resumeFile.Read(); }
- bool StoreResume(int Index) { return resumeFile.Save(Index); }
- };
-
-cIndexFile::cIndexFile(const char *FileName, bool Record)
-:resumeFile(FileName)
-{
- f = -1;
- fileName = NULL;
- size = 0;
- last = -1;
- index = NULL;
- if (FileName) {
- fileName = new char[strlen(FileName) + strlen(INDEXFILESUFFIX) + 1];
- if (fileName) {
- strcpy(fileName, FileName);
- char *pFileExt = fileName + strlen(fileName);
- strcpy(pFileExt, INDEXFILESUFFIX);
- int delta = 0;
- if (access(fileName, R_OK) == 0) {
- struct stat buf;
- if (stat(fileName, &buf) == 0) {
- delta = buf.st_size % sizeof(tIndex);
- if (delta) {
- delta = sizeof(tIndex) - delta;
- esyslog("ERROR: invalid file size (%ld) in '%s'", buf.st_size, fileName);
- }
- last = (buf.st_size + delta) / sizeof(tIndex) - 1;
- if (!Record && last >= 0) {
- size = last + 1;
- index = new tIndex[size];
- if (index) {
- f = open(fileName, O_RDONLY);
- if (f >= 0) {
- if ((int)safe_read(f, index, buf.st_size) != buf.st_size) {
- esyslog("ERROR: can't read from file '%s'", fileName);
- delete index;
- index = NULL;
- close(f);
- f = -1;
- }
- // we don't close f here, see CatchUp()!
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't allocate %d bytes for index '%s'", size * sizeof(tIndex), fileName);
- }
- }
- else
- LOG_ERROR;
- }
- else if (!Record)
- isyslog("missing index file %s", fileName);
- if (Record) {
- if ((f = open(fileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) >= 0) {
- if (delta) {
- esyslog("ERROR: padding index file with %d '0' bytes", delta);
- while (delta--)
- writechar(f, 0);
- }
- }
- else
- LOG_ERROR_STR(fileName);
- }
- }
- else
- esyslog("ERROR: can't copy file name '%s'", FileName);
- }
-}
-
-cIndexFile::~cIndexFile()
-{
- if (f >= 0)
- close(f);
- delete fileName;
- delete index;
-}
-
-bool cIndexFile::CatchUp(int Index)
-{
- if (index && f >= 0) {
- for (int i = 0; i <= MAXINDEXCATCHUP && (Index < 0 || Index >= last); i++) {
- struct stat buf;
- if (fstat(f, &buf) == 0) {
- int newLast = buf.st_size / sizeof(tIndex) - 1;
- if (newLast > last) {
- if (size <= newLast) {
- size *= 2;
- if (size <= newLast)
- size = newLast + 1;
- }
- index = (tIndex *)realloc(index, size * sizeof(tIndex));
- if (index) {
- int offset = (last + 1) * sizeof(tIndex);
- int delta = (newLast - last) * sizeof(tIndex);
- if (lseek(f, offset, SEEK_SET) == offset) {
- if (safe_read(f, &index[last + 1], delta) != delta) {
- esyslog("ERROR: can't read from index");
- delete index;
- index = NULL;
- close(f);
- f = -1;
- break;
- }
- last = newLast;
- }
- else
- LOG_ERROR_STR(fileName);
- }
- else
- esyslog("ERROR: can't realloc() index");
- }
- }
- else
- LOG_ERROR_STR(fileName);
- if (Index >= last)
- sleep(1);
- else
- return true;
- }
- }
- return false;
-}
-
-bool cIndexFile::Write(uchar PictureType, uchar FileNumber, int FileOffset)
-{
- if (f >= 0) {
- tIndex i = { FileOffset, PictureType, FileNumber, 0 };
- if (safe_write(f, &i, sizeof(i)) < 0) {
- LOG_ERROR_STR(fileName);
- close(f);
- f = -1;
- return false;
- }
- last++;
- }
- return f >= 0;
-}
-
-bool cIndexFile::Get(int Index, uchar *FileNumber, int *FileOffset, uchar *PictureType, int *Length)
-{
- if (index) {
- CatchUp(Index);
- if (Index >= 0 && Index <= last) {
- *FileNumber = index[Index].number;
- *FileOffset = index[Index].offset;
- if (PictureType)
- *PictureType = index[Index].type;
- if (Length) {
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else
- *Length = -1; // this means "everything up to EOF" (the buffer's Read function will act accordingly)
- }
- return true;
- }
- }
- return false;
-}
-
-int cIndexFile::GetNextIFrame(int Index, bool Forward, uchar *FileNumber, int *FileOffset, int *Length, bool StayOffEnd)
-{
- if (index) {
- CatchUp();
- int d = Forward ? 1 : -1;
- for (;;) {
- Index += d;
- if (Index >= 0 && Index < last - ((Forward && StayOffEnd) ? 100 : 0)) {
- if (index[Index].type == I_FRAME) {
- if (FileNumber)
- *FileNumber = index[Index].number;
- else
- FileNumber = &index[Index].number;
- if (FileOffset)
- *FileOffset = index[Index].offset;
- else
- FileOffset = &index[Index].offset;
- if (Length) {
- // all recordings end with a non-I_FRAME, so the following should be safe:
- int fn = index[Index + 1].number;
- int fo = index[Index + 1].offset;
- if (fn == *FileNumber)
- *Length = fo - *FileOffset;
- else {
- esyslog("ERROR: 'I' frame at end of file #%d", *FileNumber);
- *Length = -1;
- }
- }
- return Index;
- }
- }
- else
- break;
- }
- }
- return -1;
-}
-
-int cIndexFile::Get(uchar FileNumber, int FileOffset)
-{
- if (index) {
- CatchUp();
- //TODO implement binary search!
- int i;
- for (i = 0; i < last; i++) {
- if (index[i].number > FileNumber || (index[i].number == FileNumber) && index[i].offset >= FileOffset)
- break;
- }
- return i;
- }
- return -1;
-}
-
-// --- cFileName -------------------------------------------------------------
-
-class cFileName {
-private:
- int file;
- int fileNumber;
- char *fileName, *pFileNumber;
- bool record;
- bool blocking;
-public:
- cFileName(const char *FileName, bool Record, bool Blocking = false);
- ~cFileName();
- const char *Name(void) { return fileName; }
- int Number(void) { return fileNumber; }
- int Open(void);
- void Close(void);
- int SetOffset(int Number, int Offset = 0);
- int NextFile(void);
- };
-
-cFileName::cFileName(const char *FileName, bool Record, bool Blocking)
-{
- file = -1;
- fileNumber = 0;
- record = Record;
- blocking = Blocking;
- // Prepare the file name:
- fileName = new char[strlen(FileName) + RECORDFILESUFFIXLEN];
- if (!fileName) {
- esyslog("ERROR: can't copy file name '%s'", fileName);
- return;
- }
- strcpy(fileName, FileName);
- pFileNumber = fileName + strlen(fileName);
- SetOffset(1);
-}
-
-cFileName::~cFileName()
-{
- Close();
- delete fileName;
-}
-
-int cFileName::Open(void)
-{
- if (file < 0) {
- int BlockingFlag = blocking ? 0 : O_NONBLOCK;
- if (record) {
- dsyslog("recording to '%s'", fileName);
- file = OpenVideoFile(fileName, O_RDWR | O_CREAT | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else {
- if (access(fileName, R_OK) == 0) {
- dsyslog("playing '%s'", fileName);
- file = open(fileName, O_RDONLY | BlockingFlag);
- if (file < 0)
- LOG_ERROR_STR(fileName);
- }
- else if (errno != ENOENT)
- LOG_ERROR_STR(fileName);
- }
- }
- return file;
-}
-
-void cFileName::Close(void)
-{
- if (file >= 0) {
- if ((record && CloseVideoFile(file) < 0) || (!record && close(file) < 0))
- LOG_ERROR_STR(fileName);
- file = -1;
- }
-}
-
-int cFileName::SetOffset(int Number, int Offset)
-{
- if (fileNumber != Number)
- Close();
- if (0 < Number && Number <= MAXFILESPERRECORDING) {
- fileNumber = Number;
- sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
- if (record) {
- if (access(fileName, F_OK) == 0) // file exists, let's try next suffix
- return SetOffset(Number + 1);
- else if (errno != ENOENT) { // something serious has happened
- LOG_ERROR_STR(fileName);
- return -1;
- }
- // found a non existing file suffix
- }
- if (Open() >= 0) {
- if (!record && Offset >= 0 && lseek(file, Offset, SEEK_SET) != Offset) {
- LOG_ERROR_STR(fileName);
- return -1;
- }
- }
- return file;
- }
- esyslog("ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
- return -1;
-}
-
-int cFileName::NextFile(void)
-{
- return SetOffset(fileNumber + 1);
-}
-
-// --- cRecordBuffer ---------------------------------------------------------
-
-class cRecordBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- cFileName fileName;
- cIndexFile *index;
- cRemux remux;
- uchar pictureType;
- int fileSize;
- int videoDev;
- int recordFile;
- bool recording;
- time_t lastDiskSpaceCheck;
- bool RunningLowOnDiskSpace(void);
- bool NextFile(void);
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2);
- virtual ~cRecordBuffer();
- };
-
-cRecordBuffer::cRecordBuffer(cDvbApi *DvbApi, const char *FileName, int VPid, int APid1, int APid2, int DPid1, int DPid2)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,fileName(FileName, true)
-,remux(VPid, APid1, APid2, DPid1, DPid2, true)
-{
- dvbApi = DvbApi;
- index = NULL;
- pictureType = NO_PICTURE;
- fileSize = 0;
- recordFile = fileName.Open();
- recording = false;
- lastDiskSpaceCheck = time(NULL);
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, true);
- if (!index)
- esyslog("ERROR: can't allocate index");
- // let's continue without index, so we'll at least have the recording
- videoDev = dvbApi->SetModeRecord();
- Start();
-}
-
-cRecordBuffer::~cRecordBuffer()
-{
- Stop();
- dvbApi->SetModeNormal(true);
- delete index;
-}
-
-bool cRecordBuffer::RunningLowOnDiskSpace(void)
-{
- if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
- int Free = FreeDiskSpaceMB(fileName.Name());
- lastDiskSpaceCheck = time(NULL);
- if (Free < MINFREEDISKSPACE) {
- dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
- return true;
- }
- }
- return false;
-}
-
-bool cRecordBuffer::NextFile(void)
-{
- if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
- recordFile = fileName.NextFile();
- fileSize = 0;
- }
- }
- return recordFile >= 0;
-}
-
-void cRecordBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- time_t t = time(NULL);
- recording = true;
- for (;;) {
- if (cFile::FileReady(videoDev, 100)) {
- int r = read(videoDev, b, sizeof(b));
- if (r > 0) {
- uchar *p = b;
- while (r > 0) {
- int w = Put(p, r);
- p += w;
- r -= w;
- if (r > 0)
- usleep(1); // this keeps the CPU load low
- }
- t = time(NULL);
- }
- else if (r < 0) {
- if (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- if (time(NULL) - t > MAXBROKENTIMEOUT) {
- esyslog("ERROR: video data stream broken");
- cThread::EmergencyExit(true);
- t = time(NULL);
- }
- if (!recording)
- break;
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cRecordBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int r = 0;
- for (;;) {
- int g = Get(b + r, sizeof(b) - r);
- if (g > 0)
- r += g;
- if (r > 0) {
- int Count = r, Result;
- const uchar *p = remux.Process(b, Count, Result, &pictureType);
- if (p) {
- if (!Busy() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
- break;
- if (NextFile()) {
- if (index && pictureType != NO_PICTURE)
- index->Write(pictureType, fileName.Number(), fileSize);
- if (safe_write(recordFile, p, Result) < 0) {
- LOG_ERROR_STR(fileName.Name());
- recording = false;
- return;
- }
- fileSize += Result;
- }
- else
- break;
- }
- if (Count > 0) {
- r -= Count;
- memmove(b, b + Count, r);
- }
- if (!recording)
- break;
- }
- else
- usleep(1); // this keeps the CPU load low
- }
- recording = false;
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-// --- ReadFrame -------------------------------------------------------------
-
-int ReadFrame(int f, uchar *b, int Length, int Max)
-{
- if (Length == -1)
- Length = Max; // this means we read up to EOF (see cIndex)
- else if (Length > Max) {
- esyslog("ERROR: frame larger than buffer (%d > %d)", Length, Max);
- Length = Max;
- }
- int r = safe_read(f, b, Length);
- if (r < 0)
- LOG_ERROR;
- return r;
-}
-
-// --- cBackTrace ----------------------------------------------------------
-
-#define AVG_FRAME_SIZE 15000 // an assumption about the average frame size
-#define DVB_BUF_SIZE (256 * 1024) // an assumption about the dvb firmware buffer size
-#define BACKTRACE_ENTRIES (DVB_BUF_SIZE / AVG_FRAME_SIZE + 20) // how many entries are needed to backtrace buffer contents
-
-class cBackTrace {
-private:
- int index[BACKTRACE_ENTRIES];
- int length[BACKTRACE_ENTRIES];
- int pos, num;
-public:
- cBackTrace(void);
- void Clear(void);
- void Add(int Index, int Length);
- int Get(bool Forward);
- };
-
-cBackTrace::cBackTrace(void)
-{
- Clear();
-}
-
-void cBackTrace::Clear(void)
-{
- pos = num = 0;
-}
-
-void cBackTrace::Add(int Index, int Length)
-{
- index[pos] = Index;
- length[pos] = Length;
- if (++pos >= BACKTRACE_ENTRIES)
- pos = 0;
- if (num < BACKTRACE_ENTRIES)
- num++;
-}
-
-int cBackTrace::Get(bool Forward)
-{
- int p = pos;
- int n = num;
- int l = DVB_BUF_SIZE + (Forward ? 0 : 256 * 1024); //XXX (256 * 1024) == DVB_BUF_SIZE ???
- int i = -1;
-
- while (n && l > 0) {
- if (--p < 0)
- p = BACKTRACE_ENTRIES - 1;
- i = index[p] - 1;
- l -= length[p];
- n--;
- }
- return i;
-}
-
-// --- cPlayBuffer ---------------------------------------------------------
-
-#define MAX_VIDEO_SLOWMOTION 63 // max. arg to pass to VIDEO_SLOWMOTION // TODO is this value correct?
-
-class cPlayBuffer : public cRingBufferFrame {
-private:
- cBackTrace backTrace;
-protected:
- enum ePlayModes { pmPlay, pmPause, pmSlow, pmFast, pmStill };
- enum ePlayDirs { pdForward, pdBackward };
- static int Speeds[];
- cDvbApi *dvbApi;
- int videoDev, audioDev;
- cPipe dolbyDev;
- int blockInput, blockOutput;
- ePlayModes playMode;
- ePlayDirs playDir;
- int trickSpeed;
- int readIndex, writeIndex;
- bool canDoTrickMode;
- bool canToggleAudioTrack;
- bool skipAC3bytes;
- uchar audioTrack;
- void TrickSpeed(int Increment);
- virtual void Empty(bool Block = false);
- virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00) {}
- virtual void PlayExternalDolby(const uchar *b, int MaxLength);
- virtual void Output(void);
- void putFrame(cFrame *Frame);
- void putFrame(unsigned char *Data, int Length, eFrameType Type = ftUnknown);
-public:
- cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev);
- virtual ~cPlayBuffer();
- virtual void Pause(void);
- virtual void Play(void);
- virtual void Forward(void);
- virtual void Backward(void);
- virtual int SkipFrames(int Frames) { return -1; }
- virtual void SkipSeconds(int Seconds) {}
- virtual void Goto(int Position, bool Still = false) {}
- virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false) { Current = Total = -1; }
- bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
- bool CanToggleAudioTrack(void) { return canToggleAudioTrack; };
- virtual void ToggleAudioTrack(void);
- };
-
-#define NORMAL_SPEED 4 // the index of the '1' entry in the following array
-#define MAX_SPEEDS 3 // the offset of the maximum speed from normal speed in either direction
-#define SPEED_MULT 12 // the speed multiplier
-int cPlayBuffer::Speeds[] = { 0, -2, -4, -8, 1, 2, 4, 12, 0 };
-
-cPlayBuffer::cPlayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev)
-:cRingBufferFrame(VIDEOBUFSIZE)
-{
- dvbApi = DvbApi;
- videoDev = VideoDev;
- audioDev = AudioDev;
- blockInput = blockOutput = false;
- playMode = pmPlay;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- readIndex = writeIndex = -1;
- canDoTrickMode = false;
- canToggleAudioTrack = false;
- skipAC3bytes = false;
- audioTrack = 0xC0;
-}
-
-cPlayBuffer::~cPlayBuffer()
-{
-}
-
-void cPlayBuffer::PlayExternalDolby(const uchar *b, int MaxLength)
-{
- if (cDvbApi::AudioCommand()) {
- if (!dolbyDev && !dolbyDev.Open(cDvbApi::AudioCommand(), "w")) {
- esyslog("ERROR: can't open pipe to audio command '%s'", cDvbApi::AudioCommand());
- return;
- }
- if (b[0] == 0x00 && b[1] == 0x00 && b[2] == 0x01) {
- if (b[3] == 0xBD) { // dolby
- int l = b[4] * 256 + b[5] + 6;
- int written = b[8] + (skipAC3bytes ? 13 : 9); // skips the PES header
- int n = min(l - written, MaxLength);
- while (n > 0) {
- int w = fwrite(&b[written], 1, n, dolbyDev);
- if (w < 0) {
- LOG_ERROR;
- break;
- }
- n -= w;
- written += w;
- }
- }
- }
- }
-}
-
-void cPlayBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- while (Busy()) {
- if (blockOutput) {
- if (blockOutput > 1)
- blockOutput = 1;
- continue;
- }
- const cFrame *frame = Get();
- if (frame) {
- if (frame->Type() == ftDolby)
- PlayExternalDolby(frame->Data(), frame->Count());
- else {
- StripAudioPackets((uchar *)frame->Data(), frame->Count(), (playMode == pmFast || playMode == pmSlow) ? 0x00 : audioTrack);//XXX
- const uchar *p = frame->Data();
- int r = frame->Count();
- while (r > 0 && Busy() && !blockOutput) {
- if (cFile::FileReadyForWriting(videoDev, 100)) {
- int w = write(videoDev, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- writeIndex = frame->Index();
- backTrace.Add(frame->Index(), frame->Count());
- }
- Drop(frame);
- }
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-void cPlayBuffer::putFrame(cFrame *Frame)
-{
- while (Busy() && !blockInput) {
- if (Put(Frame))
- return;
- }
- delete Frame; // caller relies on frame being put, otherwise this would be a memory leak!
-}
-
-void cPlayBuffer::putFrame(unsigned char *Data, int Length, eFrameType Type)
-{
- putFrame(new cFrame(Data, Length, Type));
-}
-
-void cPlayBuffer::TrickSpeed(int Increment)
-{
- int nts = trickSpeed + Increment;
- if (Speeds[nts] == 1) {
- trickSpeed = nts;
- if (playMode == pmFast)
- Play();
- else
- Pause();
- }
- else if (Speeds[nts]) {
- trickSpeed = nts;
- int Mult = (playMode == pmSlow && playDir == pdForward) ? 1 : SPEED_MULT;
- int sp = (Speeds[nts] > 0) ? Mult / Speeds[nts] : -Speeds[nts] * Mult;
- if (sp > MAX_VIDEO_SLOWMOTION)
- sp = MAX_VIDEO_SLOWMOTION;
- CHECK(ioctl(videoDev, VIDEO_SLOWMOTION, sp));
- }
-}
-
-void cPlayBuffer::Empty(bool Block)
-{
- if (!(blockInput || blockOutput)) {
- blockInput = blockOutput = 2;
- EnablePut();
- EnableGet();
- time_t t0 = time(NULL);
- while ((blockInput > 1 || blockOutput > 1) && time(NULL) - t0 < 2)
- usleep(1);
- Lock();
- if ((readIndex = backTrace.Get(playDir == pdForward)) < 0)
- readIndex = writeIndex;
- cRingBufferFrame::Clear();
- CHECK(ioctl(videoDev, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(audioDev, AUDIO_CLEAR_BUFFER));
- }
- if (!Block) {
- blockInput = blockOutput = 0;
- backTrace.Clear();
- Unlock();
- }
-}
-
-void cPlayBuffer::Pause(void)
-{
- if (playMode == pmPause || playMode == pmStill)
- Play();
- else {
- bool empty = (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(videoDev, VIDEO_FREEZE));
- playMode = pmPause;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Play(void)
-{
- if (playMode != pmPlay) {
- bool empty = (playMode == pmStill || playMode == pmFast || (playMode == pmSlow && playDir == pdBackward));
- if (empty)
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- if (empty)
- Empty(false);
- }
-}
-
-void cPlayBuffer::Forward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? 1 : -1);
- break;
- }
- else if (playDir == pdForward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdForward ? -1 : 1);
- break;
- }
- else if (playDir == pdForward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdForward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- break;
- }
- }
-}
-
-void cPlayBuffer::Backward(void)
-{
- if (canDoTrickMode) {
- switch (playMode) {
- case pmFast:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? 1 : -1);
- break;
- }
- else if (playDir == pdBackward) {
- Play();
- break;
- }
- // run into pmPlay
- case pmPlay:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmFast;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? 1 : MAX_SPEEDS);
- Empty(false);
- break;
- case pmSlow:
- if (Setup.MultiSpeedMode) {
- TrickSpeed(playDir == pdBackward ? -1 : 1);
- break;
- }
- else if (playDir == pdBackward) {
- Pause();
- break;
- }
- // run into pmPause
- case pmStill:
- case pmPause:
- Empty(true);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- playMode = pmSlow;
- playDir = pdBackward;
- trickSpeed = NORMAL_SPEED;
- TrickSpeed(Setup.MultiSpeedMode ? -1 : -MAX_SPEEDS);
- Empty(false);
- break;
- }
- }
-}
-
-bool cPlayBuffer::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- Play = (playMode == pmPlay || playMode == pmFast);
- Forward = (playDir == pdForward);
- if (playMode == pmFast || playMode == pmSlow)
- Speed = Setup.MultiSpeedMode ? abs(trickSpeed - NORMAL_SPEED) : 0;
- else
- Speed = -1;
- return true;
-}
-
-void cPlayBuffer::ToggleAudioTrack(void)
-{
- if (CanToggleAudioTrack()) {
- audioTrack = (audioTrack == 0xC0) ? 0xC1 : 0xC0;
- Empty();
- }
-}
-
-// --- cReplayBuffer ---------------------------------------------------------
-
-class cReplayBuffer : public cPlayBuffer {
-private:
- cIndexFile *index;
- cFileName fileName;
- int replayFile;
- bool eof;
- bool NextFile(uchar FileNumber = 0, int FileOffset = -1);
- void Close(void);
- virtual void StripAudioPackets(uchar *b, int Length, uchar Except = 0x00);
- void DisplayFrame(uchar *b, int Length);
- int Resume(void);
- bool Save(void);
-protected:
- virtual void Input(void);
-public:
- cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName);
- virtual ~cReplayBuffer();
- virtual int SkipFrames(int Frames);
- virtual void SkipSeconds(int Seconds);
- virtual void Goto(int Position, bool Still = false);
- virtual void GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
- };
-
-cReplayBuffer::cReplayBuffer(cDvbApi *DvbApi, int VideoDev, int AudioDev, const char *FileName)
-:cPlayBuffer(DvbApi, VideoDev, AudioDev)
-,fileName(FileName, false)
-{
- index = NULL;
- replayFile = fileName.Open();
- eof = false;
- if (!fileName.Name())
- return;
- // Create the index file:
- index = new cIndexFile(FileName, false);
- if (!index)
- esyslog("ERROR: can't allocate index");
- else if (!index->Ok()) {
- delete index;
- index = NULL;
- }
- canDoTrickMode = index != NULL;
- dvbApi->SetModeReplay();
- Start();
-}
-
-cReplayBuffer::~cReplayBuffer()
-{
- Stop();
- Save();
- Close();
- dvbApi->SetModeNormal(false);
- delete index;
-}
-
-void cReplayBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- readIndex = Resume();
- if (readIndex >= 0)
- isyslog("resuming replay at index %d (%s)", readIndex, IndexToHMSF(readIndex, true));
-
- uchar b[MAXFRAMESIZE];
- while (Busy() && (blockInput || NextFile())) {
- if (blockInput) {
- if (blockInput > 1)
- blockInput = 1;
- continue;
- }
- if (playMode != pmStill) {
- int r = 0;
- if (playMode == pmFast || (playMode == pmSlow && playDir == pdBackward)) {
- uchar FileNumber;
- int FileOffset, Length;
- int Index = index->GetNextIFrame(readIndex, playDir == pdForward, &FileNumber, &FileOffset, &Length, true);
- if (Index >= 0) {
- if (!NextFile(FileNumber, FileOffset))
- break;
- }
- else {
- // can't call Play() here, because those functions may only be
- // called from the foreground thread - and we also don't need
- // to empty the buffer here
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- playMode = pmPlay;
- playDir = pdForward;
- continue;
- }
- readIndex = Index;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- // must call StripAudioPackets() here because the buffer is not emptied
- // when falling back from "fast forward" to "play" (see above)
- StripAudioPackets(b, r);
- }
- else if (index) {
- uchar FileNumber;
- int FileOffset, Length;
- readIndex++;
- if (!(index->Get(readIndex, &FileNumber, &FileOffset, NULL, &Length) && NextFile(FileNumber, FileOffset)))
- break;
- r = ReadFrame(replayFile, b, Length, sizeof(b));
- }
- else // allows replay even if the index file is missing
- r = read(replayFile, b, sizeof(b));
- if (r > 0)
- putFrame(new cFrame(b, r, ftUnknown, readIndex));
- else if (r == 0)
- eof = true;
- else if (r < 0 && FATALERRNO) {
- LOG_ERROR;
- break;
- }
- }
- else//XXX
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cReplayBuffer::StripAudioPackets(uchar *b, int Length, uchar Except)
-{
- if (canDoTrickMode) {
- for (int i = 0; i < Length - 6; i++) {
- if (b[i] == 0x00 && b[i + 1] == 0x00 && b[i + 2] == 0x01) {
- uchar c = b[i + 3];
- int l = b[i + 4] * 256 + b[i + 5] + 6;
- switch (c) {
- case 0xBD: // dolby
- if (Except)
- PlayExternalDolby(&b[i], Length - i);
- // continue with deleting the data - otherwise it disturbs DVB replay
- case 0xC0 ... 0xC1: // audio
- if (c == 0xC1)
- canToggleAudioTrack = true;
- if (!Except || c != Except) {
- int n = l;
- for (int j = i; j < Length && n--; j++)
- b[j] = 0x00;
- }
- break;
- case 0xE0 ... 0xEF: // video
- break;
- default:
- //esyslog("ERROR: unexpected packet id %02X", c);
- l = 0;
- }
- if (l)
- i += l - 1; // the loop increments, too!
- }
- /*XXX
- else
- esyslog("ERROR: broken packet header");
- XXX*/
- }
- }
-}
-
-void cReplayBuffer::DisplayFrame(uchar *b, int Length)
-{
- StripAudioPackets(b, Length);
- CHECK(ioctl(audioDev, AUDIO_SET_AV_SYNC, false));
- CHECK(ioctl(audioDev, AUDIO_SET_MUTE, true));
-/* Using the VIDEO_STILLPICTURE ioctl call would be the
- correct way to display a still frame, but unfortunately this
- doesn't work with frames from VDR. So let's do pretty much the
- same here as in DVB/driver/dvb.c's play_iframe() - I have absolutely
- no idea why it works this way, but doesn't work with VIDEO_STILLPICTURE.
- If anybody ever finds out what could be changed so that VIDEO_STILLPICTURE
- could be used, please let me know!
- kls 2002-03-23
-*/
-//#define VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
-#ifdef VIDEO_STILLPICTURE_WORKS_WITH_VDR_FRAMES
- videoDisplayStillPicture sp = { (char *)b, Length };
- CHECK(ioctl(videoDev, VIDEO_STILLPICTURE, &sp));
-#else
-#define MIN_IFRAME 400000
- for (int i = MIN_IFRAME / Length + 1; i > 0; i--) {
- safe_write(videoDev, b, Length);
- usleep(1); // allows the buffer to be displayed in case the progress display is active
- }
-#endif
-}
-
-void cReplayBuffer::Close(void)
-{
- if (replayFile >= 0) {
- fileName.Close();
- replayFile = -1;
- }
-}
-
-int cReplayBuffer::Resume(void)
-{
- if (index) {
- int Index = index->GetResume();
- if (Index >= 0) {
- uchar FileNumber;
- int FileOffset;
- if (index->Get(Index, &FileNumber, &FileOffset) && NextFile(FileNumber, FileOffset))
- return Index;
- }
- }
- return -1;
-}
-
-bool cReplayBuffer::Save(void)
-{
- if (index) {
- int Index = writeIndex;
- if (Index >= 0) {
- Index -= RESUMEBACKUP;
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false);
- else
- Index = 0;
- if (Index >= 0)
- return index->StoreResume(Index);
- }
- }
- return false;
-}
-
-int cReplayBuffer::SkipFrames(int Frames)
-{
- if (index && Frames) {
- int Current, Total;
- GetIndex(Current, Total, true);
- int OldCurrent = Current;
- Current = index->GetNextIFrame(Current + Frames, Frames > 0);
- return Current >= 0 ? Current : OldCurrent;
- }
- return -1;
-}
-
-void cReplayBuffer::SkipSeconds(int Seconds)
-{
- if (index && Seconds) {
- Empty(true);
- int Index = writeIndex;
- if (Index >= 0) {
- Index = max(Index + Seconds * FRAMESPERSEC, 0);
- if (Index > 0)
- Index = index->GetNextIFrame(Index, false, NULL, NULL, NULL, true);
- if (Index >= 0)
- readIndex = writeIndex = Index - 1; // Input() will first increment it!
- }
- Empty(false);
- Play();
- }
-}
-
-void cReplayBuffer::Goto(int Index, bool Still)
-{
- if (index) {
- Empty(true);
- if (++Index <= 0)
- Index = 1; // not '0', to allow GetNextIFrame() below to work!
- uchar FileNumber;
- int FileOffset, Length;
- Index = index->GetNextIFrame(Index, false, &FileNumber, &FileOffset, &Length);
- if (Index >= 0 && NextFile(FileNumber, FileOffset) && Still) {
- uchar b[MAXFRAMESIZE];
- int r = ReadFrame(replayFile, b, Length, sizeof(b));
- if (r > 0) {
- if (playMode == pmPause)
- CHECK(ioctl(videoDev, VIDEO_CONTINUE));
- DisplayFrame(b, r);
- }
- playMode = pmStill;
- }
- readIndex = writeIndex = Index;
- Empty(false);
- }
-}
-
-void cReplayBuffer::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (index) {
- if (playMode == pmStill)
- Current = max(readIndex, 0);
- else {
- Current = max(writeIndex, 0);
- if (SnapToIFrame) {
- int i1 = index->GetNextIFrame(Current + 1, false);
- int i2 = index->GetNextIFrame(Current, true);
- Current = (abs(Current - i1) <= abs(Current - i2)) ? i1 : i2;
- }
- }
- Total = index->Last();
- }
- else
- Current = Total = -1;
-}
-
-bool cReplayBuffer::NextFile(uchar FileNumber, int FileOffset)
-{
- if (FileNumber > 0)
- replayFile = fileName.SetOffset(FileNumber, FileOffset);
- else if (replayFile >= 0 && eof) {
- Close();
- replayFile = fileName.NextFile();
- }
- eof = false;
- return replayFile >= 0;
-}
-
-// --- cTransferBuffer -------------------------------------------------------
-
-class cTransferBuffer : public cRingBufferLinear {
-private:
- cDvbApi *dvbApi;
- int fromDevice, toDevice;
- bool gotBufferReserve;
- cRemux remux;
-protected:
- virtual void Input(void);
- virtual void Output(void);
-public:
- cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid);
- virtual ~cTransferBuffer();
- void SetAudioPid(int APid);
- };
-
-cTransferBuffer::cTransferBuffer(cDvbApi *DvbApi, int ToDevice, int VPid, int APid)
-:cRingBufferLinear(VIDEOBUFSIZE, true)
-,remux(VPid, APid, 0, 0, 0)
-{
- dvbApi = DvbApi;
- fromDevice = dvbApi->SetModeRecord();
- toDevice = ToDevice;
- gotBufferReserve = false;
- Start();
-}
-
-cTransferBuffer::~cTransferBuffer()
-{
- Stop();
- dvbApi->SetModeNormal(true);
-}
-
-void cTransferBuffer::SetAudioPid(int APid)
-{
- Clear();
- //XXX we may need to have access to the audio device, too, in order to clear it
- CHECK(ioctl(toDevice, VIDEO_CLEAR_BUFFER));
- gotBufferReserve = false;
- remux.SetAudioPid(APid);
-}
-
-void cTransferBuffer::Input(void)
-{
- dsyslog("input thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- int n = 0;
- while (Busy()) {
- if (cFile::FileReady(fromDevice, 100)) {
- int r = read(fromDevice, b + n, sizeof(b) - n);
- if (r > 0) {
- n += r;
- int Count = n, Result;
- const uchar *p = remux.Process(b, Count, Result);
- if (p) {
- while (Result > 0 && Busy()) {
- int w = Put(p, Result);
- p += w;
- Result -= w;
- }
- }
- if (Count > 0) {
- n -= Count;
- memmove(b, b + Count, n);
- }
- }
- else if (r < 0) {
- if (FATALERRNO) {
- if (errno == EBUFFEROVERFLOW) // this error code is not defined in the library
- esyslog("ERROR (%s,%d): DVB driver buffer overflow", __FILE__, __LINE__);
- else {
- LOG_ERROR;
- break;
- }
- }
- }
- }
- }
-
- dsyslog("input thread ended (pid=%d)", getpid());
-}
-
-void cTransferBuffer::Output(void)
-{
- dsyslog("output thread started (pid=%d)", getpid());
-
- uchar b[MINVIDEODATA];
- while (Busy()) {
- if (!gotBufferReserve) {
- if (Available() < MAXFRAMESIZE) {
- usleep(100000); // allow the buffer to collect some reserve
- continue;
- }
- else
- gotBufferReserve = true;
- }
- int r = Get(b, sizeof(b));
- if (r > 0) {
- uchar *p = b;
- while (r > 0 && Busy() && cFile::FileReadyForWriting(toDevice, 100)) {
- int w = write(toDevice, p, r);
- if (w > 0) {
- p += w;
- r -= w;
- }
- else if (w < 0 && FATALERRNO) {
- LOG_ERROR;
- Stop();
- return;
- }
- }
- }
- else
- usleep(1); // this keeps the CPU load low
- }
-
- dsyslog("output thread ended (pid=%d)", getpid());
-}
-
-// --- cCuttingBuffer --------------------------------------------------------
-
-class cCuttingBuffer : public cThread {
-private:
- const char *error;
- bool active;
- int fromFile, toFile;
- cFileName *fromFileName, *toFileName;
- cIndexFile *fromIndex, *toIndex;
- cMarks fromMarks, toMarks;
-protected:
- virtual void Action(void);
-public:
- cCuttingBuffer(const char *FromFileName, const char *ToFileName);
- virtual ~cCuttingBuffer();
- const char *Error(void) { return error; }
- };
-
-cCuttingBuffer::cCuttingBuffer(const char *FromFileName, const char *ToFileName)
-{
- error = NULL;
- active = false;
- fromFile = toFile = -1;
- fromFileName = toFileName = NULL;
- fromIndex = toIndex = NULL;
- if (fromMarks.Load(FromFileName) && fromMarks.Count()) {
- fromFileName = new cFileName(FromFileName, false, true);
- toFileName = new cFileName(ToFileName, true, true);
- fromIndex = new cIndexFile(FromFileName, false);
- toIndex = new cIndexFile(ToFileName, true);
- toMarks.Load(ToFileName); // doesn't actually load marks, just sets the file name
- Start();
- }
- else
- esyslog("no editing marks found for %s", FromFileName);
-}
-
-cCuttingBuffer::~cCuttingBuffer()
-{
- active = false;
- Cancel(3);
- delete fromFileName;
- delete toFileName;
- delete fromIndex;
- delete toIndex;
-}
-
-void cCuttingBuffer::Action(void)
-{
- dsyslog("video cutting thread started (pid=%d)", getpid());
-
- cMark *Mark = fromMarks.First();
- if (Mark) {
- fromFile = fromFileName->Open();
- toFile = toFileName->Open();
- active = fromFile >= 0 && toFile >= 0;
- int Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- int FileSize = 0;
- int CurrentFileNumber = 0;
- int LastIFrame = 0;
- toMarks.Add(0);
- toMarks.Save();
- uchar buffer[MAXFRAMESIZE];
- while (active) {
- uchar FileNumber;
- int FileOffset, Length;
- uchar PictureType;
-
- // Make sure there is enough disk space:
-
- AssertFreeDiskSpace();
-
- // Read one frame:
-
- if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &PictureType, &Length)) {
- if (FileNumber != CurrentFileNumber) {
- fromFile = fromFileName->SetOffset(FileNumber, FileOffset);
- CurrentFileNumber = FileNumber;
- }
- if (fromFile >= 0) {
- int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer));
- if (len < 0) {
- error = "ReadFrame";
- break;
- }
- if (len != Length) {
- CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer
- Length = len;
- }
- }
- else {
- error = "fromFile";
- break;
- }
- }
- else
- break;
-
- // Write one frame:
-
- if (PictureType == I_FRAME) { // every file shall start with an I_FRAME
- if (!Mark) // edited version shall end before next I-frame
- break;
- if (FileSize > MEGABYTE(Setup.MaxVideoFileSize)) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 1";
- break;
- }
- FileSize = 0;
- }
- LastIFrame = 0;
- }
- if (safe_write(toFile, buffer, Length) < 0) {
- error = "safe_write";
- break;
- }
- if (!toIndex->Write(PictureType, toFileName->Number(), FileSize)) {
- error = "toIndex";
- break;
- }
- FileSize += Length;
- if (!LastIFrame)
- LastIFrame = toIndex->Last();
-
- // Check editing marks:
-
- if (Mark && Index >= Mark->position) {
- Mark = fromMarks.Next(Mark);
- toMarks.Add(LastIFrame);
- if (Mark)
- toMarks.Add(toIndex->Last() + 1);
- toMarks.Save();
- if (Mark) {
- Index = Mark->position;
- Mark = fromMarks.Next(Mark);
- CurrentFileNumber = 0; // triggers SetOffset before reading next frame
- if (Setup.SplitEditedFiles) {
- toFile = toFileName->NextFile();
- if (toFile < 0) {
- error = "toFile 2";
- break;
- }
- FileSize = 0;
- }
- }
- // the 'else' case (i.e. 'final end mark reached') is handled above
- // in 'Write one frame', so that the edited version will end right
- // before the next I-frame.
- }
- }
- }
- else
- esyslog("no editing marks found!");
- dsyslog("end video cutting thread");
-}
-
-// --- cVideoCutter ----------------------------------------------------------
-
-char *cVideoCutter::editedVersionName = NULL;
-cCuttingBuffer *cVideoCutter::cuttingBuffer = NULL;
-bool cVideoCutter::error = false;
-bool cVideoCutter::ended = false;
-
-bool cVideoCutter::Start(const char *FileName)
-{
- if (!cuttingBuffer) {
- error = false;
- ended = false;
- cRecording Recording(FileName);
- const char *evn = Recording.PrefixFileName('%');
- if (evn && RemoveVideoFile(evn) && MakeDirs(evn, true)) {
- // XXX this can be removed once RenameVideoFile() follows symlinks (see videodir.c)
- // remove a possible deleted recording with the same name to avoid symlink mixups:
- char *s = strdup(evn);
- char *e = strrchr(s, '.');
- if (e) {
- if (strcmp(e, ".rec") == 0) {
- strcpy(e, ".del");
- RemoveVideoFile(s);
- }
- }
- delete s;
- // XXX
- editedVersionName = strdup(evn);
- Recording.WriteSummary();
- cuttingBuffer = new cCuttingBuffer(FileName, editedVersionName);
- return true;
- }
- }
- return false;
-}
-
-void cVideoCutter::Stop(void)
-{
- bool Interrupted = cuttingBuffer && cuttingBuffer->Active();
- const char *Error = cuttingBuffer ? cuttingBuffer->Error() : NULL;
- delete cuttingBuffer;
- cuttingBuffer = NULL;
- if ((Interrupted || Error) && editedVersionName) {
- if (Interrupted)
- isyslog("editing process has been interrupted");
- if (Error)
- esyslog("ERROR: '%s' during editing process", Error);
- RemoveVideoFile(editedVersionName); //XXX what if this file is currently being replayed?
- }
-}
-
-bool cVideoCutter::Active(void)
-{
- if (cuttingBuffer) {
- if (cuttingBuffer->Active())
- return true;
- error = cuttingBuffer->Error();
- Stop();
- if (!error)
- cRecordingUserCommand::InvokeCommand(RUC_EDITEDRECORDING, editedVersionName);
- delete editedVersionName;
- editedVersionName = NULL;
- ended = true;
- }
- return false;
-}
-
-bool cVideoCutter::Error(void)
-{
- bool result = error;
- error = false;
- return result;
-}
-
-bool cVideoCutter::Ended(void)
-{
- bool result = ended;
- ended = false;
- return result;
-}
-
-// --- cDvbApi ---------------------------------------------------------------
-
-static const char *OstName(const char *Name, int n)
-{
- static char buffer[_POSIX_PATH_MAX];
- snprintf(buffer, sizeof(buffer), "%s%d", Name, n);
- return buffer;
-}
-
-static int OstOpen(const char *Name, int n, int Mode, bool ReportError = false)
-{
- const char *FileName = OstName(Name, n);
- int fd = open(FileName, Mode);
- if (fd < 0 && ReportError)
- LOG_ERROR_STR(FileName);
- return fd;
-}
-
-int cDvbApi::NumDvbApis = 0;
-int cDvbApi::useDvbApi = 0;
-cDvbApi *cDvbApi::dvbApi[MAXDVBAPI] = { NULL };
-cDvbApi *cDvbApi::PrimaryDvbApi = NULL;
-char *cDvbApi::audioCommand = NULL;
-
-cDvbApi::cDvbApi(int n)
-{
- frontendType = FrontendType(-1); // don't know how else to initialize this - there is no FE_UNKNOWN
- vPid = aPid1 = aPid2 = dPid1 = dPid2 = 0;
- siProcessor = NULL;
- recordBuffer = NULL;
- replayBuffer = NULL;
- transferBuffer = NULL;
- transferringFromDvbApi = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- cardIndex = n;
-
- // Devices that are only present on DVB-C or DVB-S cards:
-
- fd_frontend = OstOpen(DEV_OST_FRONTEND, n, O_RDWR);
- fd_sec = OstOpen(DEV_OST_SEC, n, O_RDWR);
-
- // Devices that all DVB cards must have:
-
- fd_demuxv = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxa1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxa2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxd1 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxd2 = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
- fd_demuxt = OstOpen(DEV_OST_DEMUX, n, O_RDWR | O_NONBLOCK, true);
-
- // Devices not present on "budget" cards:
-
- fd_osd = OstOpen(DEV_OST_OSD, n, O_RDWR);
- fd_video = OstOpen(DEV_OST_VIDEO, n, O_RDWR | O_NONBLOCK);
- fd_audio = OstOpen(DEV_OST_AUDIO, n, O_RDWR | O_NONBLOCK);
-
- // Devices that will be dynamically opened and closed when necessary:
-
- fd_dvr = -1;
-
- // Video format:
-
- SetVideoFormat(Setup.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
-
- // We only check the devices that must be present - the others will be checked before accessing them:
-
- if (fd_frontend >= 0 && fd_demuxv >= 0 && fd_demuxa1 >= 0 && fd_demuxa2 >= 0 && fd_demuxd1 >= 0 && fd_demuxd2 >= 0 && fd_demuxt >= 0) {
- siProcessor = new cSIProcessor(OstName(DEV_OST_DEMUX, n));
- FrontendInfo feinfo;
- CHECK(ioctl(fd_frontend, FE_GET_INFO, &feinfo));
- frontendType = feinfo.type;
- }
- else
- esyslog("ERROR: can't open video device %d", n);
-
- currentChannel = 1;
- mute = false;
- volume = Setup.CurrentVolume;
-}
-
-cDvbApi::~cDvbApi()
-{
- delete siProcessor;
- StopReplay();
- StopRecord();
- StopTransfer();
- // We're not explicitly closing any device files here, since this sometimes
- // caused segfaults. Besides, the program is about to terminate anyway...
-}
-
-void cDvbApi::SetUseDvbApi(int n)
-{
- if (n < MAXDVBAPI)
- useDvbApi |= (1 << n);
-}
-
-bool cDvbApi::SetPrimaryDvbApi(int n)
-{
- n--;
- if (0 <= n && n < NumDvbApis && dvbApi[n]) {
- isyslog("setting primary DVB to %d", n + 1);
- PrimaryDvbApi = dvbApi[n];
- return true;
- }
- esyslog("invalid DVB interface: %d", n + 1);
- return false;
-}
-
-int cDvbApi::CanShift(int Ca, int Priority, int UsedCards)
-{
- // Test whether a recording on this DVB device can be shifted to another one
- // in order to perform a new recording with the given Ca and Priority on this device:
- int ShiftLevel = -1; // default means this device can't be shifted
- if (UsedCards & (1 << CardIndex()) != 0)
- return ShiftLevel; // otherwise we would get into a loop
- if (Recording()) {
- if (ProvidesCa(Ca) // this device provides the requested Ca
- && (Ca != this->Ca() // the requested Ca is different from the one currently used...
- || Priority > this->Priority())) { // ...or the request comes from a higher priority
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- UsedCards |= (1 << CardIndex());
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(this->Ca())) != 0) { // this device is basicly able to do the job
- if (dvbApi[i] != this) { // it is not _this_ device
- int sl = dvbApi[i]->CanShift(this->Ca(), Priority, UsedCards); // this is the original Priority!
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i];
- ShiftLevel = sl;
- }
- }
- }
- }
- if (ShiftLevel >= 0)
- ShiftLevel++; // adds the device's own shift
- }
- }
- else if (Priority > this->Priority())
- ShiftLevel = 0; // no shifting necessary, this device can do the job
- return ShiftLevel;
-}
-
-cDvbApi *cDvbApi::GetDvbApi(int Ca, int Priority)
-{
- cDvbApi *d = NULL;
- int Provides[MAXDVBAPI];
- // Check which devices provide Ca:
- for (int i = 0; i < NumDvbApis; i++) {
- if ((Provides[i] = dvbApi[i]->ProvidesCa(Ca)) != 0) { // this device is basicly able to do the job
- if (Priority > dvbApi[i]->Priority() // Priority is high enough to use this device
- && (!d // we don't have a device yet, or...
- || dvbApi[i]->Priority() < d->Priority() // ...this one has an even lower Priority
- || (dvbApi[i]->Priority() == d->Priority() // ...same Priority...
- && Provides[i] < Provides[d->CardIndex()]))) // ...but this one provides fewer Ca values
- d = dvbApi[i];
- }
- }
- if (!d && Ca > MAXDVBAPI) {
- // We didn't find one the easy way, so now we have to try harder:
- int ShiftLevel = -1;
- for (int i = 0; i < NumDvbApis; i++) {
- if (Provides[i]) { // this device is basicly able to do the job, but for some reason we didn't get it above
- int sl = dvbApi[i]->CanShift(Ca, Priority); // asks this device to shift its job to another device
- if (sl >= 0 && (ShiftLevel < 0 || sl < ShiftLevel)) {
- d = dvbApi[i]; // found one that can be shifted with the fewest number of subsequent shifts
- ShiftLevel = sl;
- }
- }
- }
- }
- return d;
-}
-
-void cDvbApi::SetCaCaps(void)
-{
- for (int d = 0; d < NumDvbApis; d++) {
- for (int i = 0; i < MAXCACAPS; i++)
- dvbApi[d]->caCaps[i] = Setup.CaCaps[dvbApi[d]->CardIndex()][i];
- }
-}
-
-int cDvbApi::ProvidesCa(int Ca)
-{
- if (Ca == CardIndex() + 1)
- return 1; // exactly _this_ card was requested
- if (Ca && Ca <= MAXDVBAPI)
- return 0; // a specific card was requested, but not _this_ one
- int result = Ca ? 0 : 1; // by default every card can provide FTA
- int others = Ca ? 1 : 0;
- for (int i = 0; i < MAXCACAPS; i++) {
- if (caCaps[i]) {
- if (caCaps[i] == Ca)
- result = 1;
- else
- others++;
- }
- }
- return result ? result + others : 0;
-}
-
-bool cDvbApi::Probe(const char *FileName)
-{
- if (access(FileName, F_OK) == 0) {
- dsyslog("probing %s", FileName);
- int f = open(FileName, O_RDONLY);
- if (f >= 0) {
- close(f);
- return true;
- }
- else if (errno != ENODEV && errno != EINVAL)
- LOG_ERROR_STR(FileName);
- }
- else if (errno != ENOENT)
- LOG_ERROR_STR(FileName);
- return false;
-}
-
-bool cDvbApi::Initialize(void)
-{
- NumDvbApis = 0;
- for (int i = 0; i < MAXDVBAPI; i++) {
- if (useDvbApi == 0 || (useDvbApi & (1 << i)) != 0) {
- if (Probe(OstName(DEV_OST_FRONTEND, i)))
- dvbApi[NumDvbApis++] = new cDvbApi(i);
- else
- break;
- }
- }
- PrimaryDvbApi = dvbApi[0];
- if (NumDvbApis > 0)
- isyslog("found %d video device%s", NumDvbApis, NumDvbApis > 1 ? "s" : "");
- else
- esyslog("ERROR: no video device found, giving up!");
- SetCaCaps();
- return NumDvbApis > 0;
-}
-
-void cDvbApi::Shutdown(void)
-{
- for (int i = 0; i < NumDvbApis; i++) {
- delete dvbApi[i];
- dvbApi[i] = NULL;
- }
- PrimaryDvbApi = NULL;
-}
-
-bool cDvbApi::GrabImage(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
-{
- int videoDev = OstOpen(DEV_VIDEO, CardIndex(), O_RDWR, true);
- if (videoDev >= 0) {
- int result = 0;
- struct video_mbuf mbuf;
- result |= ioctl(videoDev, VIDIOCGMBUF, &mbuf);
- if (result == 0) {
- int msize = mbuf.size;
- unsigned char *mem = (unsigned char *)mmap(0, msize, PROT_READ | PROT_WRITE, MAP_SHARED, videoDev, 0);
- if (mem && mem != (unsigned char *)-1) {
- // set up the size and RGB
- struct video_capability vc;
- result |= ioctl(videoDev, VIDIOCGCAP, &vc);
- struct video_mmap vm;
- vm.frame = 0;
- if ((SizeX > 0) && (SizeX <= vc.maxwidth) &&
- (SizeY > 0) && (SizeY <= vc.maxheight)) {
- vm.width = SizeX;
- vm.height = SizeY;
- }
- else {
- vm.width = vc.maxwidth;
- vm.height = vc.maxheight;
- }
- vm.format = VIDEO_PALETTE_RGB24;
- result |= ioctl(videoDev, VIDIOCMCAPTURE, &vm);
- result |= ioctl(videoDev, VIDIOCSYNC, &vm.frame);
- // make RGB out of BGR:
- int memsize = vm.width * vm.height;
- unsigned char *mem1 = mem;
- for (int i = 0; i < memsize; i++) {
- unsigned char tmp = mem1[2];
- mem1[2] = mem1[0];
- mem1[0] = tmp;
- mem1 += 3;
- }
-
- if (Quality < 0)
- Quality = 255; //XXX is this 'best'???
-
- isyslog("grabbing to %s (%s %d %d %d)", FileName, Jpeg ? "JPEG" : "PNM", Quality, vm.width, vm.height);
- FILE *f = fopen(FileName, "wb");
- if (f) {
- if (Jpeg) {
- // write JPEG file:
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- jpeg_stdio_dest(&cinfo, f);
- cinfo.image_width = vm.width;
- cinfo.image_height = vm.height;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_RGB;
-
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, Quality, true);
- jpeg_start_compress(&cinfo, true);
-
- int rs = vm.width * 3;
- JSAMPROW rp[vm.height];
- for (int k = 0; k < vm.height; k++)
- rp[k] = &mem[rs * k];
- jpeg_write_scanlines(&cinfo, rp, vm.height);
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
- }
- else {
- // write PNM file:
- if (fprintf(f, "P6\n%d\n%d\n255\n", vm.width, vm.height) < 0 ||
- fwrite(mem, vm.width * vm.height * 3, 1, f) < 0) {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- }
- fclose(f);
- }
- else {
- LOG_ERROR_STR(FileName);
- result |= 1;
- }
- munmap(mem, msize);
- }
- else
- result |= 1;
- }
- close(videoDev);
- return result == 0;
- }
- return false;
-}
-
-int cDvbApi::Priority(void)
-{
- return (this == PrimaryDvbApi && !Recording()) ? Setup.PrimaryLimit - 1 : priority;
-}
-
-int cDvbApi::SetModeRecord(void)
-{
- // Sets up the DVB device for recording
-
- SetPids(true);
- if (fd_dvr >= 0)
- close(fd_dvr);
- fd_dvr = OstOpen(DEV_OST_DVR, CardIndex(), O_RDONLY | O_NONBLOCK);
- if (fd_dvr < 0)
- LOG_ERROR;
- return fd_dvr;
-}
-
-void cDvbApi::SetModeReplay(void)
-{
- // Sets up the DVB device for replay
-
- if (fd_video >= 0 && fd_audio >= 0) {
- if (siProcessor)
- siProcessor->SetStatus(false);
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
- CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY));
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(fd_audio, AUDIO_PLAY));
- CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY));
- CHECK(ioctl(fd_video, VIDEO_PLAY));
- }
-}
-
-void cDvbApi::SetModeNormal(bool FromRecording)
-{
- // Puts the DVB device back into "normal" viewing mode (after replay or recording)
-
- if (FromRecording) {
- close(fd_dvr);
- fd_dvr = -1;
- SetPids(false);
- }
- else {
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_video, VIDEO_STOP, true));
- CHECK(ioctl(fd_audio, AUDIO_STOP, true));
- CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
- CHECK(ioctl(fd_video, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX));
- CHECK(ioctl(fd_audio, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX));
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
- if (siProcessor)
- siProcessor->SetStatus(true);
- }
- }
-}
-
-void cDvbApi::SetVideoFormat(videoFormat_t Format)
-{
- if (fd_video >= 0)
- CHECK(ioctl(fd_video, VIDEO_SET_FORMAT, Format));
-}
-
-bool cDvbApi::SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output)
-{
- if (Pid) {
- CHECK(ioctl(fd, DMX_STOP));
- if (Pid != 0x1FFF) {
- dmxPesFilterParams pesFilterParams;
- pesFilterParams.pid = Pid;
- pesFilterParams.input = DMX_IN_FRONTEND;
- pesFilterParams.output = Output;
- pesFilterParams.pesType = PesType;
- pesFilterParams.flags = DMX_IMMEDIATE_START;
- if (ioctl(fd, DMX_SET_PES_FILTER, &pesFilterParams) < 0) {
- LOG_ERROR;
- return false;
- }
- }
- }
- return true;
-}
-
-bool cDvbApi::SetPids(bool ForRecording)
-{
- return SetVpid(vPid, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
- SetApid1(aPid1, ForRecording ? DMX_OUT_TS_TAP : DMX_OUT_DECODER) &&
- SetApid2(ForRecording ? aPid2 : 0, DMX_OUT_TS_TAP) && (!Setup.RecordDolbyDigital ||
- SetDpid1(ForRecording ? dPid1 : 0, DMX_OUT_TS_TAP) &&
- SetDpid2(ForRecording ? dPid2 : 0, DMX_OUT_TS_TAP));
-}
-
-eSetChannelResult cDvbApi::SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr)
-{
- StopTransfer();
- StopReplay();
-
- cStatusMonitor::MsgChannelSwitch(this, 0);
-
- // Must set this anyway to avoid getting stuck when switching through
- // channels with 'Up' and 'Down' keys:
- currentChannel = ChannelNumber;
- vPid = Vpid;
- aPid1 = Apid1;
- aPid2 = Apid2;
- dPid1 = Dpid1;
- dPid2 = Dpid2;
-
- // Avoid noise while switching:
-
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, true));
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, true));
- CHECK(ioctl(fd_video, VIDEO_CLEAR_BUFFER));
- CHECK(ioctl(fd_audio, AUDIO_CLEAR_BUFFER));
- }
-
- // Stop setting system time:
-
- if (siProcessor)
- siProcessor->SetCurrentTransponder(0);
-
- // If this card can't receive this channel, we must not actually switch
- // the channel here, because that would irritate the driver when we
- // start replaying in Transfer Mode immediately after switching the channel:
- bool NeedsTransferMode = (this == PrimaryDvbApi && !ProvidesCa(Ca));
-
- if (!NeedsTransferMode) {
-
- // Turn off current PIDs:
-
- SetVpid( 0x1FFF, DMX_OUT_DECODER);
- SetApid1(0x1FFF, DMX_OUT_DECODER);
- SetApid2(0x1FFF, DMX_OUT_DECODER);
- SetDpid1(0x1FFF, DMX_OUT_DECODER);
- SetDpid2(0x1FFF, DMX_OUT_DECODER);
- SetTpid( 0x1FFF, DMX_OUT_DECODER);
-
- FrontendParameters Frontend;
-
- switch (frontendType) {
- case FE_QPSK: { // DVB-S
-
- // Frequency offsets:
-
- unsigned int freq = Frequency;
- int tone = SEC_TONE_OFF;
-
- if (freq < (unsigned int)Setup.LnbSLOF) {
- freq -= Setup.LnbFrequLo;
- tone = SEC_TONE_OFF;
- }
- else {
- freq -= Setup.LnbFrequHi;
- tone = SEC_TONE_ON;
- }
-
- Frontend.Frequency = freq * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qpsk.SymbolRate = Srate * 1000UL;
- Frontend.u.qpsk.FEC_inner = FEC_AUTO;
-
- int volt = (Polarization == 'v' || Polarization == 'V') ? SEC_VOLTAGE_13 : SEC_VOLTAGE_18;
-
- // DiseqC:
-
- secCommand scmd;
- scmd.type = 0;
- scmd.u.diseqc.addr = 0x10;
- scmd.u.diseqc.cmd = 0x38;
- scmd.u.diseqc.numParams = 1;
- scmd.u.diseqc.params[0] = 0xF0 | ((Diseqc * 4) & 0x0F) | (tone == SEC_TONE_ON ? 1 : 0) | (volt == SEC_VOLTAGE_18 ? 2 : 0);
-
- secCmdSequence scmds;
- scmds.voltage = volt;
- scmds.miniCommand = SEC_MINI_NONE;
- scmds.continuousTone = tone;
- scmds.numCommands = Setup.DiSEqC ? 1 : 0;
- scmds.commands = &scmd;
-
- CHECK(ioctl(fd_sec, SEC_SEND_SEQUENCE, &scmds));
- }
- break;
- case FE_QAM: { // DVB-C
-
- // Frequency and symbol rate:
-
- Frontend.Frequency = Frequency * 1000000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.qam.SymbolRate = Srate * 1000UL;
- Frontend.u.qam.FEC_inner = FEC_AUTO;
- Frontend.u.qam.QAM = QAM_64;
- }
- break;
- case FE_OFDM: { // DVB-T
-
- // Frequency and OFDM paramaters:
-
- Frontend.Frequency = Frequency * 1000UL;
- Frontend.Inversion = INVERSION_AUTO;
- Frontend.u.ofdm.bandWidth=BANDWIDTH_8_MHZ;
- Frontend.u.ofdm.HP_CodeRate=FEC_2_3;
- Frontend.u.ofdm.LP_CodeRate=FEC_1_2;
- Frontend.u.ofdm.Constellation=QAM_64;
- Frontend.u.ofdm.TransmissionMode=TRANSMISSION_MODE_2K;
- Frontend.u.ofdm.guardInterval=GUARD_INTERVAL_1_32;
- Frontend.u.ofdm.HierarchyInformation=HIERARCHY_NONE;
- }
- break;
- default:
- esyslog("ERROR: attempt to set channel with unknown DVB frontend type");
- return scrFailed;
- }
-
- // Tuning:
-
- CHECK(ioctl(fd_frontend, FE_SET_FRONTEND, &Frontend));
-
- // Wait for channel sync:
-
- if (cFile::FileReady(fd_frontend, 5000)) {
- FrontendEvent event;
- int res = ioctl(fd_frontend, FE_GET_EVENT, &event);
- if (res >= 0) {
- if (event.type != FE_COMPLETION_EV) {
- esyslog("ERROR: channel %d not sync'ed on DVB card %d!", ChannelNumber, CardIndex() + 1);
- if (this == PrimaryDvbApi)
- cThread::RaisePanic();
- return scrFailed;
- }
- }
- else
- esyslog("ERROR %d in frontend get event (channel %d, card %d)", res, ChannelNumber, CardIndex() + 1);
- }
- else
- esyslog("ERROR: timeout while tuning");
-
- // PID settings:
-
- if (!SetPids(false)) {
- esyslog("ERROR: failed to set PIDs for channel %d", ChannelNumber);
- return scrFailed;
- }
- SetTpid(Tpid, DMX_OUT_DECODER);
- if (fd_audio >= 0)
- CHECK(ioctl(fd_audio, AUDIO_SET_AV_SYNC, true));
- }
-
- if (this == PrimaryDvbApi && siProcessor)
- siProcessor->SetCurrentServiceID(Pnr);
-
- eSetChannelResult Result = scrOk;
-
- // If this DVB card can't receive this channel, let's see if we can
- // use the card that actually can receive it and transfer data from there:
-
- if (NeedsTransferMode) {
- cDvbApi *CaDvbApi = GetDvbApi(Ca, 0);
- if (CaDvbApi && !CaDvbApi->Recording()) {
- if ((Result = CaDvbApi->SetChannel(ChannelNumber, Frequency, Polarization, Diseqc, Srate, Vpid, Apid1, Apid2, Dpid1, Dpid2, Tpid, Ca, Pnr)) == scrOk) {
- SetModeReplay();
- transferringFromDvbApi = CaDvbApi->StartTransfer(fd_video);
- }
- }
- else
- Result = scrNoTransfer;
- }
-
- if (fd_video >= 0 && fd_audio >= 0) {
- CHECK(ioctl(fd_audio, AUDIO_SET_MUTE, false));
- CHECK(ioctl(fd_video, VIDEO_SET_BLANK, false));
- }
-
- // Start setting system time:
-
- if (Result == scrOk && siProcessor)
- siProcessor->SetCurrentTransponder(Frequency);
-
- cStatusMonitor::MsgChannelSwitch(this, ChannelNumber);
-
- return Result;
-}
-
-bool cDvbApi::Transferring(void)
-{
- return transferBuffer;
-}
-
-cDvbApi *cDvbApi::StartTransfer(int TransferToVideoDev)
-{
- StopTransfer();
- transferBuffer = new cTransferBuffer(this, TransferToVideoDev, vPid, aPid1);
- return this;
-}
-
-void cDvbApi::StopTransfer(void)
-{
- if (transferBuffer) {
- delete transferBuffer;
- transferBuffer = NULL;
- }
- if (transferringFromDvbApi) {
- transferringFromDvbApi->StopTransfer();
- transferringFromDvbApi = NULL;
- }
-}
-
-int cDvbApi::SecondsToFrames(int Seconds)
-{
- return Seconds * FRAMESPERSEC;
-}
-
-bool cDvbApi::Recording(void)
-{
- if (recordBuffer && !recordBuffer->Active())
- StopRecord();
- return recordBuffer != NULL;
-}
-
-bool cDvbApi::Replaying(void)
-{
- if (replayBuffer && !replayBuffer->Active())
- StopReplay();
- return replayBuffer != NULL;
-}
-
-bool cDvbApi::StartRecord(const char *FileName, int Ca, int Priority)
-{
- if (Recording()) {
- esyslog("ERROR: StartRecord() called while recording - ignored!");
- return false;
- }
-
- StopTransfer();
-
- StopReplay(); // TODO: remove this if the driver is able to do record and replay at the same time
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartRecord: file name is (null)");
- return false;
- }
- isyslog("record %s", FileName);
-
- // Create directories if necessary:
-
- if (!MakeDirs(FileName, true))
- return false;
-
- // Make sure the disk is up and running:
-
- SpinUpDisk(FileName);
-
- // Create recording buffer:
-
- recordBuffer = new cRecordBuffer(this, FileName, vPid, aPid1, aPid2, dPid1, dPid2);
-
- if (recordBuffer) {
- ca = Ca;
- priority = Priority;
- return true;
- }
- else
- esyslog("ERROR: can't allocate recording buffer");
-
- return false;
-}
-
-void cDvbApi::StopRecord(void)
-{
- if (recordBuffer) {
- delete recordBuffer;
- recordBuffer = NULL;
- ca = -1;
- priority = DEFAULTPRIORITY;
- }
-}
-
-bool cDvbApi::StartReplay(const char *FileName)
-{
- if (Recording()) {
- esyslog("ERROR: StartReplay() called while recording - ignored!");
- return false;
- }
- StopTransfer();
- StopReplay();
- if (fd_video >= 0 && fd_audio >= 0) {
-
- // Check FileName:
-
- if (!FileName) {
- esyslog("ERROR: StartReplay: file name is (null)");
- return false;
- }
- isyslog("replay %s", FileName);
-
- // Create replay buffer:
-
- replayBuffer = new cReplayBuffer(this, fd_video, fd_audio, FileName);
- if (replayBuffer)
- return true;
- else
- esyslog("ERROR: can't allocate replaying buffer");
- }
- return false;
-}
-
-void cDvbApi::StopReplay(void)
-{
- if (replayBuffer) {
- delete replayBuffer;
- replayBuffer = NULL;
- if (this == PrimaryDvbApi) {
- // let's explicitly switch the channel back in case it was in Transfer Mode:
- cChannel *Channel = Channels.GetByNumber(currentChannel);
- if (Channel) {
- Channel->Switch(this, false);
- usleep(100000); // allow driver to sync in case a new replay will start immediately
- }
- }
- }
-}
-
-void cDvbApi::Pause(void)
-{
- if (replayBuffer)
- replayBuffer->Pause();
-}
-
-void cDvbApi::Play(void)
-{
- if (replayBuffer)
- replayBuffer->Play();
-}
-
-void cDvbApi::Forward(void)
-{
- if (replayBuffer)
- replayBuffer->Forward();
-}
-
-void cDvbApi::Backward(void)
-{
- if (replayBuffer)
- replayBuffer->Backward();
-}
-
-void cDvbApi::SkipSeconds(int Seconds)
-{
- if (replayBuffer)
- replayBuffer->SkipSeconds(Seconds);
-}
-
-int cDvbApi::SkipFrames(int Frames)
-{
- if (replayBuffer)
- return replayBuffer->SkipFrames(Frames);
- return -1;
-}
-
-bool cDvbApi::GetIndex(int &Current, int &Total, bool SnapToIFrame)
-{
- if (replayBuffer) {
- replayBuffer->GetIndex(Current, Total, SnapToIFrame);
- return true;
- }
- return false;
-}
-
-bool cDvbApi::GetReplayMode(bool &Play, bool &Forward, int &Speed)
-{
- return replayBuffer && replayBuffer->GetReplayMode(Play, Forward, Speed);
-}
-
-void cDvbApi::Goto(int Position, bool Still)
-{
- if (replayBuffer)
- replayBuffer->Goto(Position, Still);
-}
-
-bool cDvbApi::CanToggleAudioTrack(void)
-{
- return replayBuffer ? replayBuffer->CanToggleAudioTrack() : (aPid1 && aPid2 && aPid1 != aPid2);
-}
-
-bool cDvbApi::ToggleAudioTrack(void)
-{
- if (replayBuffer) {
- replayBuffer->ToggleAudioTrack();
- return true;
- }
- else {
- int a = aPid2;
- aPid2 = aPid1;
- aPid1 = a;
- if (transferringFromDvbApi)
- return transferringFromDvbApi->ToggleAudioTrack();
- else {
- if (transferBuffer)
- transferBuffer->SetAudioPid(aPid1);
- return SetPids(transferBuffer != NULL);
- }
- }
- return false;
-}
-
-bool cDvbApi::ToggleMute(void)
-{
- int OldVolume = volume;
- mute = !mute;
- SetVolume(0, mute);
- volume = OldVolume;
- return mute;
-}
-
-void cDvbApi::SetVolume(int Volume, bool Absolute)
-{
- if (fd_audio >= 0) {
- volume = min(max(Absolute ? Volume : volume + Volume, 0), MAXVOLUME);
- audioMixer_t am;
- am.volume_left = am.volume_right = volume;
- CHECK(ioctl(fd_audio, AUDIO_SET_MIXER, &am));
- cStatusMonitor::MsgSetVolume(volume, Absolute);
- if (volume > 0)
- mute = false;
- }
-}
-
-void cDvbApi::SetAudioCommand(const char *Command)
-{
- delete audioCommand;
- audioCommand = strdup(Command);
-}
diff --git a/dvbapi.h b/dvbapi.h
deleted file mode 100644
index 036ded85..00000000
--- a/dvbapi.h
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * dvbapi.h: Interface to the DVB driver
- *
- * See the main source file 'vdr.c' for copyright information and
- * how to reach the author.
- *
- * $Id: dvbapi.h 1.72 2002/05/20 10:58:20 kls Exp $
- */
-
-#ifndef __DVBAPI_H
-#define __DVBAPI_H
-
-#include <stdlib.h> // FIXME: this is apparently necessary for the ost/... header files
- // FIXME: shouldn't every header file include ALL the other header
- // FIXME: files it depends on? The sequence in which header files
- // FIXME: are included here should not matter - and it should NOT
- // FIXME: be necessary to include <stdlib.h> here!
-#include <linux/videodev.h>
-#include <ost/dmx.h>
-#include <ost/sec.h>
-#include <ost/frontend.h>
-#include <ost/video.h>
-#include <ost/audio.h>
-#include <stdio.h>
-#include "eit.h"
-#include "thread.h"
-
-#define FRAMESPERSEC 25
-
-// The maximum file size is limited by the range that can be covered
-// with 'int'. 4GB might be possible (if the range is considered
-// 'unsigned'), 2GB should be possible (even if the range is considered
-// 'signed'), so let's use 2000MB for absolute safety (the actual file size
-// may be slightly higher because we stop recording only before the next
-// 'I' frame, to have a complete Group Of Pictures):
-#define MAXVIDEOFILESIZE 2000 // MB
-#define MINVIDEOFILESIZE 100 // MB
-
-#define MAXVOLUME 255
-#define VOLUMEDELTA 5 // used to increase/decrease the volume
-
-const char *IndexToHMSF(int Index, bool WithFrame = false);
- // Converts the given index to a string, optionally containing the frame number.
-int HMSFToIndex(const char *HMSF);
- // Converts the given string (format: "hh:mm:ss.ff") to an index.
-
-enum eSetChannelResult { scrOk, scrNoTransfer, scrFailed };
-
-class cChannel;
-
-class cRecordBuffer;
-class cPlayBuffer;
-class cReplayBuffer;
-class cTransferBuffer;
-class cCuttingBuffer;
-
-class cVideoCutter {
-private:
- static char *editedVersionName;
- static cCuttingBuffer *cuttingBuffer;
- static bool error;
- static bool ended;
-public:
- static bool Start(const char *FileName);
- static void Stop(void);
- static bool Active(void);
- static bool Error(void);
- static bool Ended(void);
- };
-
-class cDvbApi {
- friend class cOsd;
- friend class cRecordBuffer;
- friend class cReplayBuffer;
- friend class cTransferBuffer;
-private:
- FrontendType frontendType;
- int fd_osd, fd_frontend, fd_sec, fd_dvr, fd_audio, fd_video, fd_demuxa1, fd_demuxa2, fd_demuxd1, fd_demuxd2, fd_demuxv, fd_demuxt;
- int vPid, aPid1, aPid2, dPid1, dPid2;
- int OsdDeviceHandle(void) { return fd_osd; }
- bool SetPid(int fd, dmxPesType_t PesType, int Pid, dmxOutput_t Output);
- bool SetVpid(int Vpid, dmxOutput_t Output) { return SetPid(fd_demuxv, DMX_PES_VIDEO, Vpid, Output); }
- bool SetApid1(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa1, DMX_PES_AUDIO, Apid, Output); }
- bool SetApid2(int Apid, dmxOutput_t Output) { return SetPid(fd_demuxa2, DMX_PES_OTHER, Apid, Output); }
- bool SetDpid1(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd1, DMX_PES_OTHER, Dpid, Output); }
- bool SetDpid2(int Dpid, dmxOutput_t Output) { return SetPid(fd_demuxd2, DMX_PES_OTHER, Dpid, Output); }
- bool SetTpid(int Tpid, dmxOutput_t Output) { return SetPid(fd_demuxt, DMX_PES_TELETEXT, Tpid, Output); }
- bool SetPids(bool ForRecording);
- cDvbApi(int n);
-public:
- ~cDvbApi();
-
-#define MAXDVBAPI 4 // the maximum number of DVB cards in the system
-#define MAXCACAPS 16 // the maximum number of different CA values per DVB card
-
- static int NumDvbApis;
-private:
- static cDvbApi *dvbApi[MAXDVBAPI];
- static int useDvbApi;
- int cardIndex;
- int caCaps[MAXCACAPS];
- int CanShift(int Ca, int Priority, int UsedCards = 0);
-public:
- static cDvbApi *PrimaryDvbApi;
- static void SetUseDvbApi(int n);
- // Sets the 'useDvbApi' flag of the given DVB device.
- // If this function is not called before Initialize(), all DVB devices
- // will be used.
- static bool SetPrimaryDvbApi(int n);
- // Sets the primary DVB device to 'n' (which must be in the range
- // 1...NumDvbApis) and returns true if this was possible.
- static cDvbApi *GetDvbApi(int Ca, int Priority);
- // Selects a free DVB device, avoiding the PrimaryDvbApi if possible.
- // If Ca is not 0, the device with the given number will be returned
- // in case Ca is <= MAXDVBAPI, or the device that provides the given
- // value in its caCaps.
- // If all DVB devices are currently recording, the one recording the
- // lowest priority timer (if any) that is lower than the given Priority
- // will be returned.
- // The caller must check whether the returned DVB device is actually
- // recording and stop recording if necessary.
- int CardIndex(void) const { return cardIndex; }
- // Returns the card index of this DvbApi (0 ... MAXDVBAPI - 1).
- static void SetCaCaps(void);
- // Sets the CaCaps of all DVB devices according to the Setup data.
- int ProvidesCa(int Ca);
- // Checks whether this DVB device provides the given value in its
- // caCaps. Returns 0 if the value is not provided, 1 if only this
- // value is provided, and > 1 if this and other values are provided.
- // If the given value is equal to the number of this DVB device,
- // 1 is returned. If it is 0 (FTA), 1 plus the number of other values
- // in caCaps is returned.
- static bool Probe(const char *FileName);
- // Probes for existing DVB devices.
- static bool Initialize(void);
- // Initializes the DVB API.
- // Must be called before accessing any DVB functions.
- static void Shutdown(void);
- // Closes down all DVB devices.
- // Must be called at the end of the program.
-
- // EIT facilities
-
-private:
- cSIProcessor *siProcessor;
-public:
- // Image Grab facilities
-
- bool GrabImage(const char *FileName, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1);
-
- // Video format facilities:
-
- void SetVideoFormat(videoFormat_t Format);
-
- // Channel facilities
-
-private:
- int currentChannel;
-public:
- eSetChannelResult SetChannel(int ChannelNumber, int Frequency, char Polarization, int Diseqc, int Srate, int Vpid, int Apid1, int Apid2, int Dpid1, int Dpid2, int Tpid, int Ca, int Pnr);
- static int CurrentChannel(void) { return PrimaryDvbApi ? PrimaryDvbApi->currentChannel : 0; }
- int Channel(void) { return currentChannel; }
-
- // Transfer facilities
-
-private:
- cTransferBuffer *transferBuffer;
- cDvbApi *transferringFromDvbApi;
-public:
- bool Transferring(void);
- // Returns true if we are currently transferring video data.
-private:
- cDvbApi *StartTransfer(int TransferToVideoDev);
- // Starts transferring video data from this DVB device to TransferToVideoDev.
- void StopTransfer(void);
- // Stops transferring video data (in case a transfer is currently active).
-
- // Record/Replay facilities
-
-private:
- cRecordBuffer *recordBuffer;
- cPlayBuffer *replayBuffer;
- int ca;
- int priority;
- int Priority(void);
- // Returns the priority of the current recording session (0..MAXPRIORITY),
- // or -1 if no recording is currently active. The primary DVB device will
- // always return at least Setup.PrimaryLimit-1.
- int SetModeRecord(void);
- // Initiates recording mode and returns the file handle to read from.
- void SetModeReplay(void);
- void SetModeNormal(bool FromRecording);
-public:
- int Ca(void) { return ca; }
- // Returns the ca of the current recording session.
- int SecondsToFrames(int Seconds);
- // Returns the number of frames corresponding to the given number of seconds.
- bool Recording(void);
- // Returns true if we are currently recording.
- bool Replaying(void);
- // Returns true if we are currently replaying.
- bool StartRecord(const char *FileName, int Ca, int Priority);
- // Starts recording the current channel into the given file, with
- // the given ca and priority.
- // In order to be able to record longer movies,
- // a numerical suffix will be appended to the file name. The inital
- // value of that suffix will be larger than any existing file under
- // the given name, thus allowing an interrupted recording to continue
- // gracefully.
- // Returns true if recording was started successfully.
- // If there is already a recording session active, false will be
- // returned.
- void StopRecord(void);
- // Stops the current recording session (if any).
- bool StartReplay(const char *FileName);
- // Starts replaying the given file.
- // If there is already a replay session active, it will be stopped
- // and the new file will be played back.
- void StopReplay(void);
- // Stops the current replay session (if any).
- void Pause(void);
- // Pauses the current replay session, or resumes a paused session.
- void Play(void);
- // Resumes normal replay mode.
- void Forward(void);
- // Runs the current replay session forward at a higher speed.
- void Backward(void);
- // Runs the current replay session backwards at a higher speed.
- void SkipSeconds(int Seconds);
- // Skips the given number of seconds in the current replay session.
- // The sign of 'Seconds' determines the direction in which to skip.
- // Use a very large negative value to go all the way back to the
- // beginning of the recording.
- int SkipFrames(int Frames);
- // Returns the new index into the current replay session after skipping
- // the given number of frames (no actual repositioning is done!).
- // The sign of 'Frames' determines the direction in which to skip.
- bool GetIndex(int &Current, int &Total, bool SnapToIFrame = false);
- // Returns the current and total frame index, optionally snapped to the
- // nearest I-frame.
- bool GetReplayMode(bool &Play, bool &Forward, int &Speed);
- // Returns the current replay mode (if applicable).
- // 'Play' tells whether we are playing or pausing, 'Forward' tells whether
- // we are going forward or backward and 'Speed' is -1 if this is normal
- // play/pause mode, 0 if it is single speed fast/slow forward/back mode
- // and >0 if this is multi speed mode.
- void Goto(int Index, bool Still = false);
- // Positions to the given index and displays that frame as a still picture
- // if Still is true.
-
- // Audio track facilities
-
-public:
- bool CanToggleAudioTrack(void);
- // Returns true if we are currently replaying and this recording has two
- // audio tracks, or if the current channel has two audio PIDs.
- bool ToggleAudioTrack(void);
- // Toggles the audio track if possible.
-
- // Dolby Digital audio facilities
-
-private:
- static char *audioCommand;
-public:
- static void SetAudioCommand(const char *Command);
- static const char *AudioCommand(void) { return audioCommand; }
-
- // Volume facilities:
-
-private:
- bool mute;
- int volume;
-public:
- bool IsMute(void) { return mute; }
- bool ToggleMute(void);
- // Turns the volume off or on and returns the new mute state.
- void SetVolume(int Volume, bool Absolute = false);
- // Sets the volume to the given value, either absolutely or relative to
- // the current volume.
- static int CurrentVolume(void) { return PrimaryDvbApi ? PrimaryDvbApi->volume : 0; }
- };
-
-#endif //__DVBAPI_H
diff --git a/interface.c b/interface.c
index ed9d0b1e..fa36b4cb 100644
--- a/interface.c
+++ b/interface.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: interface.c 1.51 2002/06/10 16:30:00 kls Exp $
+ * $Id: interface.c 1.52 2002/06/16 13:23:40 kls Exp $
*/
#include "interface.h"
@@ -128,7 +128,7 @@ void cInterface::Clear(void)
{
if (open)
cOsd::Clear();
- cStatusMonitor::MsgOsdClear();
+ cStatus::MsgOsdClear();
}
void cInterface::ClearEol(int x, int y, eDvbColor Color)
@@ -289,7 +289,7 @@ void cInterface::Title(const char *s)
x = 0;
Write(x, 0, s, clrBlack, clrCyan);
}
- cStatusMonitor::MsgOsdTitle(s);
+ cStatus::MsgOsdTitle(s);
}
void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
@@ -302,7 +302,7 @@ void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
x = 0;
Write(x, Line, s, FgColor, BgColor);
}
- cStatusMonitor::MsgOsdStatusMessage(s);
+ cStatus::MsgOsdStatusMessage(s);
}
void cInterface::Info(const char *s)
@@ -358,7 +358,7 @@ void cInterface::Help(const char *Red, const char *Green, const char *Yellow, co
HelpButton(1, Green, clrBlack, clrGreen);
HelpButton(2, Yellow, clrBlack, clrYellow);
HelpButton(3, Blue, clrWhite, clrBlue);
- cStatusMonitor::MsgOsdHelpKeys(Red, Green, Yellow, Blue);
+ cStatus::MsgOsdHelpKeys(Red, Green, Yellow, Blue);
}
void cInterface::QueryKeys(void)
diff --git a/menu.c b/menu.c
index 7f063348..ff63fbcd 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.196 2002/06/16 12:11:02 kls Exp $
+ * $Id: menu.c 1.197 2002/06/16 13:23:51 kls Exp $
*/
#include "menu.h"
@@ -2169,7 +2169,7 @@ void cDisplayChannel::DisplayChannel(const cChannel *Channel)
Interface->Write(0, 0, buffer);
const char *date = DayDateTime();
Interface->Write(-strlen(date), 0, date);
- cStatusMonitor::MsgOsdChannel(buffer);
+ cStatus::MsgOsdChannel(buffer);
}
void cDisplayChannel::DisplayInfo(void)
@@ -2223,7 +2223,7 @@ void cDisplayChannel::DisplayInfo(void)
Interface->Flush();
lines = Lines;
lastTime = time_ms();
- cStatusMonitor::MsgOsdProgramme(Present ? Present->GetTime() : 0, PresentTitle, PresentSubtitle, Following ? Following->GetTime() : 0, FollowingTitle, FollowingSubtitle);
+ cStatus::MsgOsdProgramme(Present ? Present->GetTime() : 0, PresentTitle, PresentSubtitle, Following ? Following->GetTime() : 0, FollowingTitle, FollowingSubtitle);
}
}
}
@@ -2445,7 +2445,7 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
recorder = new cRecorder(fileName, ch->ca, timer->priority, ch->vpid, ch->apid1, ch->apid2, ch->dpid1, ch->dpid2);
if (device->Attach(recorder)) {
Recording.WriteSummary();
- cStatusMonitor::MsgRecording(device, fileName);
+ cStatus::MsgRecording(device, fileName);
Interface->DisplayRecording(device->CardIndex(), true);
}
else
@@ -2492,7 +2492,7 @@ bool cRecordControl::GetEventInfo(void)
void cRecordControl::Stop(bool KeepInstant)
{
if (timer) {
- cStatusMonitor::MsgRecording(device, NULL);
+ cStatus::MsgRecording(device, NULL);
DELETENULL(recorder);
timer->SetRecording(false);
if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && !timer->Matches())) {
@@ -2694,14 +2694,14 @@ cReplayControl::cReplayControl(void)
if (!Start(fileName))
Interface->Error(tr("Channel locked (recording)!"));//XXX+
else
- cStatusMonitor::MsgReplaying(this, fileName);
+ cStatus::MsgReplaying(this, fileName);
}
}
cReplayControl::~cReplayControl()
{
Hide();
- cStatusMonitor::MsgReplaying(this, NULL);
+ cStatus::MsgReplaying(this, NULL);
Stop();
}
diff --git a/menuitems.c b/menuitems.c
index a66cbc30..6a54a86d 100644
--- a/menuitems.c
+++ b/menuitems.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: menuitems.c 1.5 2002/05/19 12:34:33 kls Exp $
+ * $Id: menuitems.c 1.6 2002/06/16 13:23:56 kls Exp $
*/
#include "menuitems.h"
@@ -398,7 +398,7 @@ void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
// scroll indicators use inverted color scheme!
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
- cStatusMonitor::MsgOsdTextItem(text);
+ cStatus::MsgOsdTextItem(text);
}
void cMenuTextItem::ScrollUp(bool Page)
@@ -408,7 +408,7 @@ void cMenuTextItem::ScrollUp(bool Page)
offset = max(offset - (Page ? h : 1), 0);
Display();
}
- cStatusMonitor::MsgOsdTextItem(NULL, true);
+ cStatus::MsgOsdTextItem(NULL, true);
}
void cMenuTextItem::ScrollDown(bool Page)
@@ -418,7 +418,7 @@ void cMenuTextItem::ScrollDown(bool Page)
offset = min(offset + (Page ? h : 1), lines - h);
Display();
}
- cStatusMonitor::MsgOsdTextItem(NULL, false);
+ cStatus::MsgOsdTextItem(NULL, false);
}
eOSState cMenuTextItem::ProcessKey(eKeys Key)
diff --git a/osd.c b/osd.c
index 22093e24..1092187b 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 1.28 2002/06/10 16:30:00 kls Exp $
+ * $Id: osd.c 1.29 2002/06/16 13:24:00 kls Exp $
*/
#include "osd.h"
@@ -434,7 +434,7 @@ void cOsdMenu::Display(void)
if (item) {
item->Display(i - first, i == current ? clrBlack : clrWhite, i == current ? clrCyan : clrBackground);
if (i == current)
- cStatusMonitor::MsgOsdCurrentItem(item->Text());
+ cStatus::MsgOsdCurrentItem(item->Text());
}
if (++n == MAXOSDITEMS) //TODO get this from Interface!!!
break;
@@ -462,7 +462,7 @@ void cOsdMenu::DisplayCurrent(bool Current)
if (item) {
item->Display(current - first, Current ? clrBlack : clrWhite, Current ? clrCyan : clrBackground);
if (Current)
- cStatusMonitor::MsgOsdCurrentItem(item->Text());
+ cStatus::MsgOsdCurrentItem(item->Text());
}
}
diff --git a/status.c b/status.c
index 5f3ae7d4..76dc8404 100644
--- a/status.c
+++ b/status.c
@@ -4,93 +4,93 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: status.c 1.2 2002/06/16 12:10:44 kls Exp $
+ * $Id: status.c 1.3 2002/06/16 13:24:36 kls Exp $
*/
#include "status.h"
-// --- cStatusMonitor --------------------------------------------------------
+// --- cStatus ---------------------------------------------------------------
-cList<cStatusMonitor> cStatusMonitor::statusMonitors;
+cList<cStatus> cStatus::statusMonitors;
-cStatusMonitor::cStatusMonitor(void)
+cStatus::cStatus(void)
{
statusMonitors.Add(this);
}
-cStatusMonitor::~cStatusMonitor()
+cStatus::~cStatus()
{
statusMonitors.Del(this, false);
}
-void cStatusMonitor::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)
+void cStatus::MsgChannelSwitch(const cDevice *Device, int ChannelNumber)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->ChannelSwitch(Device, ChannelNumber);
}
-void cStatusMonitor::MsgRecording(const cDevice *Device, const char *Name)
+void cStatus::MsgRecording(const cDevice *Device, const char *Name)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->Recording(Device, Name);
}
-void cStatusMonitor::MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
+void cStatus::MsgReplaying(const cDvbPlayerControl *DvbPlayerControl, const char *Name)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->Replaying(DvbPlayerControl, Name);
}
-void cStatusMonitor::MsgSetVolume(int Volume, bool Absolute)
+void cStatus::MsgSetVolume(int Volume, bool Absolute)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->SetVolume(Volume, Absolute);
}
-void cStatusMonitor::MsgOsdClear(void)
+void cStatus::MsgOsdClear(void)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdClear();
}
-void cStatusMonitor::MsgOsdTitle(const char *Title)
+void cStatus::MsgOsdTitle(const char *Title)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdTitle(Title);
}
-void cStatusMonitor::MsgOsdStatusMessage(const char *Message)
+void cStatus::MsgOsdStatusMessage(const char *Message)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdStatusMessage(Message);
}
-void cStatusMonitor::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
+void cStatus::MsgOsdHelpKeys(const char *Red, const char *Green, const char *Yellow, const char *Blue)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdHelpKeys(Red, Green, Yellow, Blue);
}
-void cStatusMonitor::MsgOsdCurrentItem(const char *Text)
+void cStatus::MsgOsdCurrentItem(const char *Text)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdCurrentItem(Text);
}
-void cStatusMonitor::MsgOsdTextItem(const char *Text, bool Scroll)
+void cStatus::MsgOsdTextItem(const char *Text, bool Scroll)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdTextItem(Text, Scroll);
}
-void cStatusMonitor::MsgOsdChannel(const char *Text)
+void cStatus::MsgOsdChannel(const char *Text)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdChannel(Text);
}
-void cStatusMonitor::MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
+void cStatus::MsgOsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle)
{
- for (cStatusMonitor *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
+ for (cStatus *sm = statusMonitors.First(); sm; sm = statusMonitors.Next(sm))
sm->OsdProgramme(PresentTime, PresentTitle, PresentSubtitle, FollowingTime, FollowingTitle, FollowingSubtitle);
}
diff --git a/status.h b/status.h
index e478ee1d..d68f23ad 100644
--- a/status.h
+++ b/status.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: status.h 1.2 2002/06/16 12:09:55 kls Exp $
+ * $Id: status.h 1.3 2002/06/16 13:24:50 kls Exp $
*/
#ifndef __STATUS_H
@@ -15,9 +15,9 @@
#include "dvbplayer.h"
#include "tools.h"
-class cStatusMonitor : public cListObject {
+class cStatus : public cListObject {
private:
- static cList<cStatusMonitor> statusMonitors;
+ static cList<cStatus> statusMonitors;
protected:
// These functions can be implemented by derived classes to receive status information:
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber) {}
@@ -55,8 +55,8 @@ protected:
virtual void OsdProgramme(time_t PresentTime, const char *PresentTitle, const char *PresentSubtitle, time_t FollowingTime, const char *FollowingTitle, const char *FollowingSubtitle) {}
// The OSD displays the given programme information.
public:
- cStatusMonitor(void);
- virtual ~cStatusMonitor();
+ cStatus(void);
+ virtual ~cStatus();
// These functions are called whenever the related status information changes:
static void MsgChannelSwitch(const cDevice *Device, int ChannelNumber);
static void MsgRecording(const cDevice *Device, const char *Name);