summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--COPYING340
-rw-r--r--ChangeLog0
-rw-r--r--HISTORY12
-rw-r--r--INSTALL167
-rw-r--r--Makefile131
-rw-r--r--NEWS0
-rw-r--r--README17
-rw-r--r--TODO0
-rw-r--r--audio.cpp88
-rw-r--r--audio.h38
-rw-r--r--common.cpp95
-rw-r--r--common.h36
-rw-r--r--decoder.cpp585
-rw-r--r--decoder.h71
-rw-r--r--demux.cpp96
-rw-r--r--demux.h30
-rw-r--r--global.h94
-rw-r--r--markad-standalone.cpp94
-rw-r--r--markad-standalone.h25
-rw-r--r--markad.cpp121
-rw-r--r--markad.h45
-rw-r--r--pes2audioes.cpp273
-rw-r--r--pes2audioes.h132
-rw-r--r--po/de_DE.po19
-rw-r--r--recv.cpp332
-rw-r--r--recv.h90
-rw-r--r--status.cpp79
-rw-r--r--status.h27
-rw-r--r--ts2pes.cpp233
-rw-r--r--ts2pes.h72
-rw-r--r--video.cpp266
-rw-r--r--video.h53
33 files changed, 3662 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..8d8e8df
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Jochen Dolze <vdr@dolze.de>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5b6e7c6
--- /dev/null
+++ b/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/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ChangeLog
diff --git a/HISTORY b/HISTORY
new file mode 100644
index 0000000..7545157
--- /dev/null
+++ b/HISTORY
@@ -0,0 +1,12 @@
+VDR Plugin 'noad' Revision History
+----------------------------------
+
+2009-09-11: Version 0.0.2
+
+- Fixed H264 decoder
+- Added mp2 decoder
+- Added ac3 decoder
+
+2009-08-26: Version 0.0.1
+
+- Initial revision.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..02a4a07
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+ These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+ The file `configure.in' is used to create `configure' by a program
+called `autoconf'. You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes a while. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 4. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. You can give `configure'
+initial values for variables by setting them in the environment. Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+ CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+ env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory. After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+ By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc. You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on. Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+ CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+ If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+ Use and save the results of the tests in FILE instead of
+ `./config.cache'. Set FILE to `/dev/null' to disable caching, for
+ debugging `configure'.
+
+`--help'
+ Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--version'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5530737
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,131 @@
+#
+# Makefile for a Video Disk Recorder plugin
+#
+# $Id$
+
+# 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.
+# IMPORTANT: the presence of this macro is important for the Make.config
+# file. So it must be defined, even if it is not used here!
+#
+PLUGIN = markad
+
+### ffmpeg usage ####
+#
+# Set this to 0, if you don't want to use ffmpeg
+AVCODEC = 0
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).h | awk '{ print $$6 }' | sed -e 's/[";]//g')
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -fPIC -g -O0 -Wall -Woverloaded-virtual -Wno-parentheses
+PKG-CONFIG ?= pkg-config
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+TMPDIR = /tmp
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR's plugin API (taken from VDR's "config.h"):
+
+APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h)
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### Includes and Defines (add further entries here):
+
+PKG-LIBS += libavcodec
+PKG-INCLUDES += libavcodec
+
+INCLUDES += -I$(VDRDIR)/include
+
+DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+
+ifeq ($(AVCODEC),1)
+DEFINES += DHAVE_AVCODEC
+INCLUDES += $(shell $(PKG-CONFIG) --cflags $(PKG-INCLUDES))
+LIBS += $(shell $(PKG-CONFIG) --libs $(PKG-LIBS))
+endif
+### The object files (add further files here):
+
+OBJS-CMD = markad-standalone.o
+OBJS-COMMON = demux.o video.o audio.o decoder.o common.o ts2pes.o pes2audioes.o
+OBJS = $(PLUGIN).o recv.o status.o $(OBJS-COMMON)
+
+### The main target:
+
+all: libvdr-$(PLUGIN).so $(PLUGIN) i18n
+
+### Implicit rules:
+
+%.o: %.cpp
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Dependencies:
+
+MAKEDEP = $(CXX) -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): Makefile
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@
+ @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS-CMD:%.o=%.cpp) >> $@
+
+-include $(DEPFILE)
+
+### Internationalization (I18N):
+
+PODIR = po
+LOCALEDIR = $(VDRDIR)/locale
+I18Npo = $(wildcard $(PODIR)/*.po)
+I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
+I18Npot = $(PODIR)/$(PLUGIN).pot
+
+%.mo: %.po
+ msgfmt -c -o $@ $<
+
+$(I18Npot): $(wildcard *.cpp *.h)
+ xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o $@ $^
+
+%.po: $(I18Npot)
+ msgmerge -U --no-wrap --no-location --backup=none -q $@ $<
+ @touch $@
+
+$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
+ @mkdir -p $(dir $@)
+ cp $< $@
+
+.PHONY: i18n
+i18n: $(I18Nmsgs) $(I18Npot)
+
+### Targets:
+
+libvdr-$(PLUGIN).so: $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
+ @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION)
+
+$(PLUGIN): $(OBJS-COMMON) $(OBJS-CMD)
+ $(CXX) $(CXXFLAGS) $(OBJS-COMMON) $(OBJS-CMD) $(LIBS) -o $@
+
+dist: clean
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a *.cpp *.h $(TMPDIR)/$(ARCHIVE)
+ @cp -a po COPYING HISTORY README Makefile $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE).tgz -C $(TMPDIR) --exclude debian --exclude CVS --exclude .svn --exclude markad.kdevelop --exclude markad.kdevelop.filelist --exclude markad.kdevelop.pcs --exclude markad.kdevses --exclude Doxyfile --exclude test $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tgz
+
+clean:
+ @-rm -f $(OBJS) $(OBJS-COMMON) $(OBJS-CMD) $(DEPFILE) $(PLUGIN) *.so *.so.* *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/NEWS
diff --git a/README b/README
new file mode 100644
index 0000000..f58695c
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+Written by: Jochen Dolze <vdr@dolze.de>
+
+Project's homepage: vdr-portal
+
+Latest version available at: vdr-portal
+
+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.
+See the file COPYING for more information.
+
+Description:
+
+Noad marks advertisements in VDR recordings.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TODO
diff --git a/audio.cpp b/audio.cpp
new file mode 100644
index 0000000..d34ceae
--- /dev/null
+++ b/audio.cpp
@@ -0,0 +1,88 @@
+/*
+ * audio.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "audio.h"
+
+cMarkAdAudio::cMarkAdAudio(int RecvNumber,MarkAdContext *maContext)
+{
+ macontext=maContext;
+ recvnumber=RecvNumber;
+ mark.Comment=NULL;
+ mark.Position=0;
+ channels=0;
+}
+
+cMarkAdAudio::~cMarkAdAudio()
+{
+ ResetMark();
+}
+
+void cMarkAdAudio::ResetMark()
+{
+ if (mark.Comment) free(mark.Comment);
+ mark.Comment=NULL;
+ mark.Position=0;
+}
+
+bool cMarkAdAudio::AddMark(int Position, const char *Comment)
+{
+ if (!Comment) return false;
+ if (mark.Comment)
+ {
+ int oldlen=strlen(mark.Comment);
+ mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment));
+ if (!mark.Comment)
+ {
+ mark.Position=0;
+ return false;
+ }
+ strcat(mark.Comment," [");
+ strcat(mark.Comment,Comment);
+ strcat(mark.Comment,"]");
+ }
+ else
+ {
+ mark.Comment=strdup(Comment);
+ }
+ mark.Position=Position;
+ return true;
+}
+
+bool cMarkAdAudio::ChannelChange(int a, int b)
+{
+ if ((a==0) || (b==0)) return false;
+ if (a!=b) return true;
+ return false;
+}
+
+MarkAdMark *cMarkAdAudio::Process(int LastIFrame)
+{
+ ResetMark();
+ if (!LastIFrame) return NULL;
+
+
+ if (macontext->State.ContentStarted)
+ {
+ if (ChannelChange(macontext->Audio.Info.Channels,channels))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"audio channel change from %i to %i (%i)", channels,
+ macontext->Audio.Info.Channels,LastIFrame);
+ if (buf)
+ {
+ isyslog("markad [%i]: %s",recvnumber, buf);
+ AddMark(LastIFrame,buf);
+ free(buf);
+ }
+ }
+ }
+
+ channels=macontext->Audio.Info.Channels;
+ return &mark;
+}
+
diff --git a/audio.h b/audio.h
new file mode 100644
index 0000000..3287f7d
--- /dev/null
+++ b/audio.h
@@ -0,0 +1,38 @@
+/*
+ * audio.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __audio_h_
+#define __audio_h_
+
+#include <vdr/tools.h> // needed for (d/e/i)syslog
+#include "global.h"
+
+class cMarkAdAudio
+{
+private:
+ int lastiframe;
+ int recvnumber;
+ MarkAdContext *macontext;
+
+ MarkAdMark mark;
+ void ResetMark();
+ bool AddMark(int Position, const char *Comment);
+
+ int channels;
+
+private:
+ bool ChannelChange(int a, int b);
+
+public:
+ cMarkAdAudio(int RecvNumber,MarkAdContext *maContext);
+ ~cMarkAdAudio();
+ MarkAdMark *Process(int LastIFrame);
+};
+
+
+#endif
diff --git a/common.cpp b/common.cpp
new file mode 100644
index 0000000..dcddfd9
--- /dev/null
+++ b/common.cpp
@@ -0,0 +1,95 @@
+/*
+ * common.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "common.h"
+
+cMarkAdCommon::cMarkAdCommon(int RecvNumber,MarkAdContext *maContext)
+{
+ macontext=maContext;
+ recvnumber=RecvNumber;
+ mark.Comment=NULL;
+ mark.Position=0;
+}
+
+cMarkAdCommon::~cMarkAdCommon()
+{
+ ResetMark();
+}
+
+void cMarkAdCommon::ResetMark()
+{
+ if (mark.Comment) free(mark.Comment);
+ mark.Comment=NULL;
+ mark.Position=0;
+}
+
+bool cMarkAdCommon::AddMark(int Position, const char *Comment)
+{
+ if (!Comment) return false;
+ if (mark.Comment)
+ {
+ int oldlen=strlen(mark.Comment);
+ mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment));
+ if (!mark.Comment)
+ {
+ mark.Position=0;
+ return false;
+ }
+ strcat(mark.Comment," [");
+ strcat(mark.Comment,Comment);
+ strcat(mark.Comment,"]");
+ }
+ else
+ {
+ mark.Comment=strdup(Comment);
+ }
+ mark.Position=Position;
+ return true;
+}
+
+void cMarkAdCommon::SetTimerMarks(int LastIFrame)
+{
+ if (!macontext) return;
+
+ if ((time(NULL)>macontext->General.StartTime) && (!macontext->State.ContentStarted))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"start of %s content (%i)",
+ macontext->General.ManualRecording ? "user" : "event",LastIFrame);
+ if (buf)
+ {
+ isyslog("markad [%i]: %s",recvnumber,buf);
+ AddMark(LastIFrame,buf);
+ free(buf);
+ }
+ macontext->State.ContentStarted=LastIFrame;
+ }
+
+ if ((time(NULL)>macontext->General.EndTime) &&
+ (macontext->State.ContentStarted) && (!macontext->State.ContentStopped))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"stop of %s content (%i)",
+ macontext->General.ManualRecording ? "user" : "event",LastIFrame);
+ if (buf)
+ {
+ isyslog("markad [%i]: %s",recvnumber,buf);
+ AddMark(LastIFrame,buf);
+ free(buf);
+ }
+ macontext->State.ContentStopped=LastIFrame;
+ }
+}
+
+MarkAdMark *cMarkAdCommon::Process(int LastIFrame)
+{
+ ResetMark();
+ if (!LastIFrame) return NULL;
+ SetTimerMarks(LastIFrame);
+ return &mark;
+}
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..8a61879
--- /dev/null
+++ b/common.h
@@ -0,0 +1,36 @@
+/*
+ * common.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __common_h_
+#define __common_h_
+
+#include <vdr/tools.h> // needed for (d/e/i)syslog
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "global.h"
+
+class cMarkAdCommon
+{
+private:
+ int recvnumber;
+ MarkAdContext *macontext;
+ MarkAdMark mark;
+
+ void ResetMark();
+ bool AddMark(int Position, const char *Comment);
+ void SetTimerMarks(int LastIFrame);
+public:
+ MarkAdMark *Process(int LastIFrame);
+ cMarkAdCommon(int RecvNumber,MarkAdContext *maContext);
+ ~cMarkAdCommon();
+};
+
+#endif \ No newline at end of file
diff --git a/decoder.cpp b/decoder.cpp
new file mode 100644
index 0000000..0cbaa9a
--- /dev/null
+++ b/decoder.cpp
@@ -0,0 +1,585 @@
+/*
+ * decoder.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "decoder.h"
+
+void cMarkAdDecoder::FindAC3AudioInfos(MarkAdContext *maContext, uchar *espkt, int eslen)
+{
+ if ((!maContext) || (!espkt)) return;
+
+#pragma pack(1)
+ struct AC3HDR
+ {
+unsigned Sync1:
+ 8;
+unsigned Sync2:
+ 8;
+unsigned CrcH:
+ 8;
+unsigned CrcL:
+ 8;
+unsigned FrameSizeIndex:
+ 6;
+unsigned SampleRateIndex:
+ 2;
+unsigned BsMod:
+ 3;
+unsigned BsID:
+ 5;
+unsigned LFE_Mix_VarField:
+ 5;
+unsigned AcMod:
+ 3;
+ };
+#pragma pack()
+
+ struct AC3HDR *ac3hdr = (struct AC3HDR *) espkt;
+
+ if ((ac3hdr->Sync1==0x0b) && (ac3hdr->Sync2==0x77))
+ {
+ // some extra checks
+ if (ac3hdr->SampleRateIndex==3) return; // reserved
+ if (ac3hdr->FrameSizeIndex>=38) return; // reserved
+
+ maContext->Audio.Info.Channels=0;
+ int lfe_bitmask = 0x0;
+
+ switch (ac3hdr->AcMod)
+ {
+ case 0:
+ maContext->Audio.Info.Channels=2;
+ lfe_bitmask=0x10;
+ break;
+ case 1:
+ maContext->Audio.Info.Channels=1;
+ lfe_bitmask=0x10;
+ break;
+ case 2:
+ maContext->Audio.Info.Channels=2;
+ lfe_bitmask=0x4;
+ break;
+ case 3:
+ maContext->Audio.Info.Channels=3;
+ lfe_bitmask=0x4;
+ break;
+ case 4:
+ maContext->Audio.Info.Channels=3;
+ lfe_bitmask=0x4;
+ break;
+ case 5:
+ maContext->Audio.Info.Channels=4;
+ lfe_bitmask=0x1;
+ break;
+ case 6:
+ maContext->Audio.Info.Channels=4;
+ lfe_bitmask=0x4;
+ break;
+ case 7:
+ maContext->Audio.Info.Channels=5;
+ lfe_bitmask=0x1;
+ break;
+ }
+
+ if ((ac3hdr->LFE_Mix_VarField & lfe_bitmask)==lfe_bitmask)
+ maContext->Audio.Info.Channels++;
+ }
+
+}
+
+void cMarkAdDecoder::FindH264VideoInfos(MarkAdContext *maContext, uchar *pespkt, int peslen)
+{
+ if ((!maContext) || (!pespkt) || (!peslen)) return;
+
+// TODO: here i need some help from someone who is able to parse an H264 Picture
+// Parameter Set (ID 0x68 or 0x28) or an H264 Sequence Parameter Set (ID 0x67 or 0x27)
+}
+
+void cMarkAdDecoder::FindH262VideoInfos(MarkAdContext *maContext, uchar *pespkt, int peslen)
+{
+ if ((!maContext) || (!pespkt) || (!peslen)) return;
+
+ struct H262_SequenceHdr
+ {
+unsigned Sync1:
+ 8;
+unsigned Sync2:
+ 8;
+unsigned Sync3:
+ 8;
+unsigned Sync4:
+ 8;
+unsigned WidthH:
+ 8;
+unsigned HeightH:
+ 4;
+unsigned WidthL:
+ 4;
+unsigned HeightL:
+ 8;
+unsigned FrameRateIndex:
+ 4;
+unsigned AspectRatioIndex:
+ 4;
+ };
+
+ struct H262_PictureHdr
+ {
+unsigned Sync1:
+ 8;
+unsigned Sync2:
+ 8;
+unsigned Sync3:
+ 8;
+unsigned Sync4:
+ 8;
+unsigned TemporalReferenceH:
+ 8;
+unsigned VBVDelay:
+ 3;
+unsigned CodingType:
+ 3;
+unsigned TemporalReferenceL:
+ 8;
+ };
+
+ struct H262_SequenceHdr *seqhdr = (struct H262_SequenceHdr *) pespkt;
+ struct H262_PictureHdr *pichdr = (struct H262_PictureHdr *) pespkt;
+
+ if (pichdr->Sync1==0 && pichdr->Sync2==0 && pichdr->Sync3==1 && pichdr->Sync4==0)
+ {
+ switch (pichdr->CodingType)
+ {
+ case 1:
+ maContext->Video.Info.Pict_Type=MA_I_TYPE;
+ break;
+ case 2:
+ maContext->Video.Info.Pict_Type=MA_P_TYPE;
+ break;
+ case 3:
+ maContext->Video.Info.Pict_Type=MA_B_TYPE;
+ break;
+ case 4:
+ maContext->Video.Info.Pict_Type=MA_D_TYPE;
+ break;
+ default:
+ maContext->Video.Info.Pict_Type=0;
+ break;
+ }
+ }
+
+ if (seqhdr->Sync1==0 && seqhdr->Sync2==0 && seqhdr->Sync3==1 && seqhdr->Sync4==0xb3)
+ {
+
+ maContext->Video.Info.Height=(seqhdr->HeightH<<8)+seqhdr->HeightL;
+ maContext->Video.Info.Width=(seqhdr->WidthH<<4)+seqhdr->WidthL;
+
+ switch (seqhdr->AspectRatioIndex)
+ {
+ case 1:
+ maContext->Video.Info.AspectRatio.Num=1;
+ maContext->Video.Info.AspectRatio.Den=1;
+ break;
+ case 2:
+ maContext->Video.Info.AspectRatio.Num=4;
+ maContext->Video.Info.AspectRatio.Den=3;
+ break;
+ case 3:
+ maContext->Video.Info.AspectRatio.Num=16;
+ maContext->Video.Info.AspectRatio.Den=9;
+ break;
+ case 4:
+ maContext->Video.Info.AspectRatio.Num=11; // actually 2.21:1
+ maContext->Video.Info.AspectRatio.Den=5;
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+cMarkAdDecoder::cMarkAdDecoder(int RecvNumber, bool useH264, bool hasAC3)
+{
+ recvnumber=RecvNumber;
+#ifdef HAVE_AVCODEC
+ avcodec_init();
+ avcodec_register_all();
+
+ cpu_set_t cpumask;
+ uint len = sizeof(cpumask);
+ int cpucount;
+ if (sched_getaffinity(0,len,&cpumask)<0)
+ {
+ cpucount=1;
+ }
+ else
+ {
+ cpucount=CPU_COUNT(&cpumask);
+ }
+
+ CodecID mp2_codecid=CODEC_ID_MP2;
+ AVCodec *mp2_codec= avcodec_find_decoder(mp2_codecid);
+ if (mp2_codec)
+ {
+ mp2_context = avcodec_alloc_context();
+ if (mp2_context)
+ {
+ mp2_context->thread_count=cpucount;
+ if (avcodec_open(mp2_context, mp2_codec) < 0)
+ {
+ esyslog("noad [%i]: could not open codec 0x%05x",recvnumber,mp2_codecid);
+ av_free(mp2_context);
+ mp2_context=NULL;
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: could not allocate mp2 context",recvnumber);
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: codec 0x%05x not found",recvnumber,mp2_codecid);
+ mp2_context=NULL;
+ }
+
+ if (hasAC3)
+ {
+ CodecID ac3_codecid=CODEC_ID_AC3;
+ AVCodec *ac3_codec= avcodec_find_decoder(ac3_codecid);
+ if (ac3_codec)
+ {
+ ac3_context = avcodec_alloc_context();
+ if (ac3_context)
+ {
+ ac3_context->thread_count=cpucount;
+ if (avcodec_open(ac3_context, ac3_codec) < 0)
+ {
+ esyslog("noad [%i]: could not open codec 0x%05x",recvnumber,ac3_codecid);
+ av_free(ac3_context);
+ ac3_context=NULL;
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: could not allocate ac3 context",recvnumber);
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: codec 0x%05x not found",recvnumber,ac3_codecid);
+ ac3_context=NULL;
+ }
+ }
+ else
+ {
+ ac3_context=NULL;
+ }
+
+ AVCodec *video_codec=NULL;
+ CodecID video_codecid;
+
+ if (useH264)
+ {
+ video_codecid=CODEC_ID_H264;
+ }
+ else
+ {
+ video_codecid=CODEC_ID_MPEG2VIDEO;
+ }
+
+ video_codec = avcodec_find_decoder(video_codecid);
+ if (video_codec)
+ {
+ video_context = avcodec_alloc_context();
+ if (video_context)
+ {
+ video_context->thread_count=cpucount;
+ if (video_codec->capabilities & CODEC_CAP_TRUNCATED)
+ video_context->flags|=CODEC_FLAG_TRUNCATED; // we do not send complete frames
+ video_context->flags|=CODEC_FLAG_EMU_EDGE; // now linesize should be the same as width
+ video_context->flags2|=CODEC_FLAG2_CHUNKS; // needed for H264!
+ video_context->flags2|=CODEC_FLAG2_FAST; // really?
+
+ if (avcodec_open(video_context, video_codec) < 0)
+ {
+ esyslog("noad [%i]: could not open codec 0x%05x",recvnumber,video_codecid);
+ av_free(video_context);
+ video_context=NULL;
+ }
+ else
+ {
+ video_frame = avcodec_alloc_frame();
+ if (!video_frame)
+ {
+ esyslog("noad [%i]: could not allocate frame",recvnumber);
+ avcodec_close(video_context);
+ av_free(video_context);
+ video_context=NULL;
+ }
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: could not allocate video context",recvnumber);
+ }
+ }
+ else
+ {
+ esyslog("noad [%i]: codec 0x%05x not found",recvnumber,video_codecid);
+ video_context=NULL;
+ }
+ memset(temp_pictureplane,0,sizeof(temp_pictureplane));
+#endif
+}
+
+cMarkAdDecoder::~cMarkAdDecoder()
+{
+#ifdef HAVE_AVCODEC
+ if (video_context)
+ {
+ avcodec_close(video_context);
+ av_free(video_context);
+ av_free(video_frame);
+ }
+
+ if (ac3_context)
+ {
+ avcodec_close(ac3_context);
+ av_free(ac3_context);
+ }
+
+ if (mp2_context)
+ {
+ avcodec_close(mp2_context);
+ av_free(mp2_context);
+ }
+ SetVideoInfos(NULL,NULL,NULL,NULL);
+#endif
+}
+
+bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen)
+{
+#ifdef HAVE_AVCODEC
+ if (!mp2_context) return false;
+ AVPacket avpkt;
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0)
+ av_init_packet(&avpkt);
+#else
+ memset(&avpkt,0,sizeof(avpkt));
+ avpkt.pts = avpkt.dts = AV_NOPTS_VALUE;
+ avpkt.pos = -1;
+#endif
+ avpkt.data=espkt;
+ avpkt.size=eslen;
+
+ DECLARE_ALIGNED(16,char,outbuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]);
+ int outbuf_size=AVCODEC_MAX_AUDIO_FRAME_SIZE;
+ int ret=false;
+ while (avpkt.size>0)
+ {
+#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0)
+ int len=avcodec_decode_audio2(mp2_context,(short *) &outbuf,&outbuf_size,
+ avpkt.data,avpkt.size);
+#else
+ int len=avcodec_decode_audio3(mp2_context,(short *) &outbuf,&outbuf_size,&avpkt);
+#endif
+ if (len<0)
+ {
+ esyslog("noad [%i]: error decoding mp2",recvnumber);
+ break;
+ }
+ else
+ {
+ SetAudioInfos(maContext,ac3_context);
+ ret=true;
+ avpkt.size-=len;
+ avpkt.data+=len;
+ }
+ }
+ return ret;
+#else
+ return true;
+#endif
+}
+
+#ifdef HAVE_AVCODEC
+bool cMarkAdDecoder::SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context)
+{
+ if ((!maContext) || (!Audio_Context)) return false;
+
+ maContext->Audio.Info.Channels = Audio_Context->channels;
+ return true;
+}
+#endif
+
+bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen)
+{
+#ifdef HAVE_AVCODEC
+ if (!ac3_context) return false;
+ AVPacket avpkt;
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0)
+ av_init_packet(&avpkt);
+#else
+ memset(&avpkt,0,sizeof(avpkt));
+ avpkt.pts = avpkt.dts = AV_NOPTS_VALUE;
+ avpkt.pos = -1;
+#endif
+ avpkt.data=espkt;
+ avpkt.size=eslen;
+
+ DECLARE_ALIGNED(16,char,outbuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]);
+ int outbuf_size=AVCODEC_MAX_AUDIO_FRAME_SIZE;
+ int ret=false;
+ while (avpkt.size>0)
+ {
+#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0)
+ int len=avcodec_decode_audio2(ac3_context,(short *) &outbuf,&outbuf_size,
+ avpkt.data,avpkt.size);
+#else
+ int len=avcodec_decode_audio3(ac3_context,(short *) &outbuf,&outbuf_size,&avpkt);
+#endif
+ if (len<0)
+ {
+ esyslog("noad [%i]: error decoding ac3",recvnumber);
+ break;
+ }
+ else
+ {
+ SetAudioInfos(maContext,ac3_context,);
+ ret=true;
+ avpkt.size-=len;
+ avpkt.data+=len;
+ }
+ }
+ return ret;
+#else
+ return true;
+#endif
+}
+
+#ifdef HAVE_AVCODEC
+void cMarkAdDecoder::PAR2DAR(AVRational a, AVRational *erg)
+{
+ av_reduce(&erg->num,&erg->den,video_context->width*a.num,
+ video_context->height*a.den,1024*1024);
+}
+
+bool cMarkAdDecoder::SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Video_Context, AVFrame *Video_Frame, AVRational *DAR)
+{
+ for (int i=0; i<4; i++)
+ {
+ if (temp_pictureplane[i])
+ {
+ free(temp_pictureplane[i]);
+ temp_pictureplane[i]=NULL;
+ }
+ }
+
+ if ((!maContext) || (!Video_Context) || (!Video_Frame)) return false;
+ maContext->Video.Data.Valid=false;
+ for (int i=0; i<4; i++)
+ {
+ if (Video_Frame->data[i])
+ {
+ temp_pictureplane[i]=(uchar *) malloc(Video_Frame->linesize[i]);
+ if (!temp_pictureplane[i]) return false;
+ memcpy(temp_pictureplane[i],Video_Frame->data[i],Video_Frame->linesize[i]);
+ maContext->Video.Data.Plane[i]=temp_pictureplane[i];
+ maContext->Video.Data.PlaneLinesize[i]=Video_Frame->linesize[i];
+ }
+ }
+ maContext->Video.Info.Height=Video_Context->height;
+ maContext->Video.Info.Width=Video_Context->width;
+
+ switch (Video_Frame->pict_type)
+ {
+ case FF_I_TYPE:
+ maContext->Video.Info.Pict_Type=MA_I_TYPE;
+ break;
+ case FF_P_TYPE:
+ maContext->Video.Info.Pict_Type=MA_P_TYPE;
+ break;
+ case FF_B_TYPE:
+ maContext->Video.Info.Pict_Type=MA_B_TYPE;
+ break;
+ case FF_S_TYPE:
+ maContext->Video.Info.Pict_Type=MA_S_TYPE;
+ break;
+ case FF_SI_TYPE:
+ maContext->Video.Info.Pict_Type=MA_SI_TYPE;
+ break;
+ case FF_SP_TYPE:
+ maContext->Video.Info.Pict_Type=MA_SP_TYPE;
+ break;
+ case FF_BI_TYPE:
+ maContext->Video.Info.Pict_Type=MA_BI_TYPE;
+ break;
+ default:
+ maContext->Video.Info.Pict_Type=0;
+ }
+
+ if (DAR)
+ {
+ maContext->Video.Info.AspectRatio.Num=DAR->num;
+ maContext->Video.Info.AspectRatio.Den=DAR->den;
+ }
+
+ maContext->Video.Data.Valid=true;
+ return true;
+}
+#endif
+
+bool cMarkAdDecoder::DecodeVideo(MarkAdContext *maContext,uchar *pespkt, int peslen)
+{
+#ifdef HAVE_AVCODEC
+ AVPacket avpkt;
+#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0)
+ av_init_packet(&avpkt);
+#else
+ memset(&avpkt,0,sizeof(avpkt));
+ avpkt.pts = avpkt.dts = AV_NOPTS_VALUE;
+ avpkt.pos = -1;
+#endif
+ avpkt.data=pespkt;
+ avpkt.size=peslen;
+
+ // decode video
+ int video_frame_ready=0;
+ int len,ret=false;
+ while (avpkt.size>0)
+ {
+#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0)
+ len=avcodec_decode_video(video_context,video_frame,&video_frame_ready,
+ avpkt.data,avpkt.size);
+#else
+ len=avcodec_decode_video2(video_context,video_frame,&video_frame_ready,
+ &avpkt);
+#endif
+ if (len<0)
+ {
+ esyslog("noad [%i]: error decoding video",recvnumber);
+ break;
+ }
+ else
+ {
+ avpkt.size-=len;
+ avpkt.data+=len;
+ }
+ if (video_frame_ready)
+ {
+ AVRational dar;
+ PAR2DAR(video_context->sample_aspect_ratio,&dar);
+ if (SetVideoInfos(maContext,video_context,video_frame,&dar)) ret=true;
+ }
+ }
+ return ret;
+#else
+ return true;
+#endif
+}
diff --git a/decoder.h b/decoder.h
new file mode 100644
index 0000000..dbcf91c
--- /dev/null
+++ b/decoder.h
@@ -0,0 +1,71 @@
+/*
+ * decoder.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __decoder_h_
+#define __decoder_h_
+
+#define __STDC_CONSTANT_MACROS
+
+#include <vdr/tools.h> // needed for (d/e/i)syslog
+#include <stdint.h>
+#include <sched.h>
+
+#ifndef DECLARE_ALIGNED
+#define DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n)))
+#endif
+
+#ifndef CPU_COUNT
+#define CPU_COUNT(i) 1 // very crude ;)
+#endif
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#ifdef HAVE_AVCODEC
+extern "C"
+{
+#include <libavcodec/avcodec.h>
+
+#if LIBAVCODEC_VERSION_INT < ((52<<16)+(23<<8)+0)
+#include <libavformat/avformat.h>
+#endif
+}
+#endif
+
+#include "global.h"
+
+class cMarkAdDecoder
+{
+private:
+ int recvnumber;
+#ifdef HAVE_AVCODEC
+ AVCodecContext *ac3_context;
+ AVCodecContext *mp2_context;
+ AVCodecContext *video_context;
+ AVFrame *video_frame;
+ uchar *temp_pictureplane[4];
+
+ bool SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context);
+
+ void PAR2DAR(AVRational a, AVRational *erg);
+ bool SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Video_Context,
+ AVFrame *Video_Frame, AVRational *DAR);
+#endif
+public:
+ void FindAC3AudioInfos(MarkAdContext *maContext, uchar *espkt, int eslen);
+ void FindH264VideoInfos(MarkAdContext *maContext, uchar *pespkt, int peslen);
+ void FindH262VideoInfos(MarkAdContext *maContext, uchar *pespkt, int peslen);
+ bool DecodeVideo(MarkAdContext *maContext, uchar *pespkt, int peslen);
+ bool DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen);
+ bool DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen);
+ cMarkAdDecoder(int recvnumber, bool useH264, bool hasAC3);
+ ~cMarkAdDecoder();
+};
+
+#endif
diff --git a/demux.cpp b/demux.cpp
new file mode 100644
index 0000000..792618b
--- /dev/null
+++ b/demux.cpp
@@ -0,0 +1,96 @@
+/*
+ * demux.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "demux.h"
+
+cMarkAdDemux::cMarkAdDemux()
+{
+ ts2pes=new cMarkAdTS2PES();
+ pes2audioes=NULL;
+ pespkt=NULL;
+ pesptr=NULL;
+ peslen=0;
+}
+
+cMarkAdDemux::~cMarkAdDemux()
+{
+ if (ts2pes) delete ts2pes;
+ if (pes2audioes) delete pes2audioes;
+}
+
+int cMarkAdDemux::Process(int Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen)
+{
+ if ((!Data) && (!Count) && (!ts2pes) && (!pes2audioes) ||
+ (!Pkt) || (!PktLen) || (!Pid)) return -1;
+ *Pkt=NULL;
+ *PktLen=0;
+
+ int len=-1; // we don't want loops
+
+ if (!peslen)
+ {
+ len=ts2pes->Process(Pid,Data,Count,&pespkt,&peslen);
+ }
+ if (pespkt)
+ {
+
+ if ((((pespkt[3]>=0xc0) && (pespkt[3]<=0xDF)) || (pespkt[3]==0xBD))
+ && (!pesptr))
+ {
+ if (!pes2audioes)
+ {
+ pes2audioes=new cMarkAdPES2AudioES();
+ }
+ if (pes2audioes)
+ {
+ pesptr=pespkt;
+ }
+ else
+ {
+ pesptr=NULL;
+ }
+ }
+ if (pesptr)
+ {
+
+ if (len==-1) len=0;
+ uchar *esdata;
+ int essize;
+ while (peslen>0)
+ {
+ int len2=pes2audioes->Process(pesptr,peslen,&esdata,&essize);
+ if (len2<0)
+ {
+ break;
+ }
+ else
+ {
+ if (esdata)
+ {
+ *Pkt=esdata;
+ *PktLen=essize;
+ }
+ pesptr+=len2;
+ peslen-=len2;
+ if (!peslen) pesptr=NULL;
+ break;
+ }
+ }
+
+ }
+ else
+ {
+ *Pkt=pespkt;
+ *PktLen=peslen;
+ pespkt=pesptr=NULL;
+ peslen=0;
+ }
+ }
+ return len;
+}
+
diff --git a/demux.h b/demux.h
new file mode 100644
index 0000000..11e0b91
--- /dev/null
+++ b/demux.h
@@ -0,0 +1,30 @@
+/*
+ * demux.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __demux_h_
+#define __demux_h_
+
+#include "global.h"
+#include "ts2pes.h"
+#include "pes2audioes.h"
+
+class cMarkAdDemux
+{
+private:
+ cMarkAdTS2PES *ts2pes;
+ cMarkAdPES2AudioES *pes2audioes;
+ uchar *pespkt;
+ uchar *pesptr; // pointer into pespkt
+ int peslen;
+public:
+ cMarkAdDemux();
+ ~cMarkAdDemux();
+ int Process(int Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen);
+};
+
+#endif
diff --git a/global.h b/global.h
new file mode 100644
index 0000000..7762a83
--- /dev/null
+++ b/global.h
@@ -0,0 +1,94 @@
+/*
+ * global.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __global_h_
+#define __global_h_
+
+#include <time.h>
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#define MA_I_TYPE 1
+#define MA_P_TYPE 2
+#define MA_B_TYPE 3
+#define MA_S_TYPE 4
+#define MA_SI_TYPE 5
+#define MA_SP_TYPE 6
+#define MA_BI_TYPE 7
+#define MA_D_TYPE 80
+
+typedef struct MarkAdMark
+{
+ int Position;
+ char *Comment;
+} MarkAdMark;
+
+typedef struct MarkAdAspectRatio
+{
+ int Num;
+ int Den;
+} MarkAdAspectRatio;
+
+typedef struct MarkAdContext
+{
+ struct General
+ {
+ time_t StartTime;
+ time_t EndTime;
+ bool ManualRecording;
+ bool H264;
+ int VPid;
+ int APid;
+ int DPid;
+ } General;
+
+ struct State
+ {
+ int ContentStarted;
+ int ContentStopped;
+ } State;
+
+ struct Video
+ {
+ struct Info
+ {
+ int Width; // width of pic
+ int Height; // height of pic
+ int Pict_Type; // picture type (I,P,B,S,SI,SP,BI)
+ MarkAdAspectRatio AspectRatio;
+ } Info;
+
+ struct Data
+ {
+ bool Valid; // flag, if true data is valid
+ uchar *Plane[4]; // picture planes (YUV420)
+ int PlaneLinesize[4]; // size int bytes of each picture plane line
+ } Data;
+ } Video;
+
+ struct Audio
+ {
+ struct Info
+ {
+ int Channels; // number of audio channels
+ } Info;
+ struct Data
+ {
+ bool Valid;
+ uchar *SampleBufAC3;
+ int SampleBufLenAC3;
+ uchar *SampleBufMP2;
+ int SampleBufLenMP2;
+ } Data;
+ } Audio;
+
+} MarkAdContext;
+
+#endif
diff --git a/markad-standalone.cpp b/markad-standalone.cpp
new file mode 100644
index 0000000..6837807
--- /dev/null
+++ b/markad-standalone.cpp
@@ -0,0 +1,94 @@
+/*
+ * markad-standalone.cpp: A program for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "markad-standalone.h"
+
+#define MAXSYSLOGBUF 255
+void syslog_with_tid(int priority, const char *format, ...)
+{
+ va_list ap;
+ char fmt[MAXSYSLOGBUF];
+ snprintf(fmt, sizeof(fmt), "[%d] %s", getpid(), format);
+ va_start(ap, format);
+ vsyslog(priority, fmt, ap);
+ va_end(ap);
+}
+
+int main(int argc, char *argv[])
+{
+ uchar data[35344];
+ int datalen;
+
+ //int f=open("/var/lib/video/031.ts",O_RDWR);
+ //int f=open("/root/frames_515.dat",O_RDWR);
+ //int f=open("/root/VDR/PLUGINS/markad/test/ANIXEHD-Spiderman3-h.264.ts",O_RDWR);
+ int f=open("/tmp/input0.ts",O_RDWR);
+ if (f==-1) return -1;
+
+ MarkAdContext macontext;
+ memset(&macontext,0,sizeof(macontext));
+
+ macontext.General.StartTime=0;
+ macontext.General.EndTime=0xFFFFFFFF;
+ macontext.General.VPid=0x100;
+ //macontext.General.DPid=0x403;
+// macontext.General.DPid=0x203;
+ //macontext.General.APid=0x101;
+
+ cMarkAdDemux *demux = new cMarkAdDemux();
+ cMarkAdDecoder *decoder = new cMarkAdDecoder(255,false,false);
+ cMarkAdVideo *video = new cMarkAdVideo(255,&macontext);
+ MarkAdMark *mark;
+
+ int lastiframe=1;
+ while (datalen=read(f,&data,sizeof(data)))
+ {
+ uchar *pkt;
+ int pktlen;
+
+ uchar *tspkt = data;
+ int tslen = datalen;
+
+ while (tslen>0)
+ {
+ int len=demux->Process(macontext.General.VPid,tspkt,tslen,&pkt,&pktlen);
+ if (len<0)
+ {
+ break;
+ }
+ else
+ {
+
+ if (pkt)
+ {
+
+ decoder->FindH262VideoInfos(&macontext,pkt,pktlen);
+ if (decoder->DecodeVideo(&macontext,pkt,pktlen))
+ {
+ mark=video->Process(lastiframe++);
+ // AddMark(mark);
+
+ }
+
+ printf("Pktlen=%i\n", pktlen);
+ }
+
+ tspkt+=len;
+ tslen-=len;
+ }
+ }
+ }
+ delete demux;
+ delete decoder;
+ delete video;
+ close(f);
+
+
+ return 0;
+
+}
diff --git a/markad-standalone.h b/markad-standalone.h
new file mode 100644
index 0000000..1f39d39
--- /dev/null
+++ b/markad-standalone.h
@@ -0,0 +1,25 @@
+/*
+ * markad-standalone.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __markad_standalone_h_
+#define __markad_standalone_h_
+
+#include <syslog.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "demux.h"
+#include "global.h"
+#include "decoder.h"
+#include "video.h"
+
+int SysLogLevel=3;
+
+#endif \ No newline at end of file
diff --git a/markad.cpp b/markad.cpp
new file mode 100644
index 0000000..6685fbc
--- /dev/null
+++ b/markad.cpp
@@ -0,0 +1,121 @@
+/*
+ * markad.c: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include <vdr/plugin.h>
+
+#include "markad.h"
+
+cPluginMarkAd::cPluginMarkAd(void)
+{
+ // Initialize any member variables here.
+ // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
+ // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ statusMonitor=NULL;
+}
+
+cPluginMarkAd::~cPluginMarkAd()
+{
+ // Clean up after yourself!
+ if (statusMonitor) delete statusMonitor;
+}
+
+const char *cPluginMarkAd::CommandLineHelp(void)
+{
+ // Return a string that describes all known command line options.
+ return NULL;
+}
+
+bool cPluginMarkAd::ProcessArgs(int argc, char *argv[])
+{
+ // Implement command line argument processing here if applicable.
+ return true;
+}
+
+bool cPluginMarkAd::Initialize(void)
+{
+ // Initialize any background activities the plugin shall perform.
+ return true;
+}
+
+bool cPluginMarkAd::Start(void)
+{
+ // Start any background activities the plugin shall perform.
+ statusMonitor = new cStatusMarkAd();
+ return true;
+}
+
+void cPluginMarkAd::Stop(void)
+{
+ // Stop any background activities the plugin is performing.
+}
+
+void cPluginMarkAd::Housekeeping(void)
+{
+ // Perform any cleanup or other regular tasks.
+}
+
+const char *cPluginMarkAd::MainMenuEntry(void)
+{
+ return NULL;
+}
+
+void cPluginMarkAd::MainThreadHook(void)
+{
+ // Perform actions in the context of the main program thread.
+ // WARNING: Use with great care - see PLUGINS.html!
+}
+
+cString cPluginMarkAd::Active(void)
+{
+ // Return a message string if shutdown should be postponed
+ return NULL;
+}
+
+time_t cPluginMarkAd::WakeupTime(void)
+{
+ // Return custom wakeup time for shutdown script
+ return 0;
+}
+
+cOsdObject *cPluginMarkAd::MainMenuAction(void)
+{
+ // Perform the action when selected from the main VDR menu.
+ return NULL;
+}
+
+cMenuSetupPage *cPluginMarkAd::SetupMenu(void)
+{
+ // Return a setup menu in case the plugin supports one.
+ return NULL;
+}
+
+bool cPluginMarkAd::SetupParse(const char *Name, const char *Value)
+{
+ // Parse your own setup parameters and store their values.
+ return false;
+}
+
+bool cPluginMarkAd::Service(const char *Id, void *Data)
+{
+ // Handle custom service requests from other plugins
+ return false;
+}
+
+const char **cPluginMarkAd::SVDRPHelpPages(void)
+{
+ // Return help text for SVDRP commands this plugin implements
+ return NULL;
+}
+
+cString cPluginMarkAd::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ // Process SVDRP commands this plugin implements
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cPluginMarkAd); // Don't touch this!
diff --git a/markad.h b/markad.h
new file mode 100644
index 0000000..f1b40e8
--- /dev/null
+++ b/markad.h
@@ -0,0 +1,45 @@
+
+/*
+ * markad.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __markad_h_
+#define __markad_h_
+
+#include "status.h"
+
+static const char *VERSION = "0.0.3";
+static const char *DESCRIPTION = trNOOP("Mark advertisements");
+
+class cPluginMarkAd : public cPlugin {
+private:
+ // Add any member variables or functions you may need here.
+ cStatusMarkAd *statusMonitor;
+public:
+ cPluginMarkAd(void);
+ virtual ~cPluginMarkAd();
+ 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 Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void);
+ virtual void MainThreadHook(void);
+ virtual cString Active(void);
+ virtual time_t WakeupTime(void);
+ virtual const char *MainMenuEntry(void);
+ virtual cOsdObject *MainMenuAction(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ virtual bool Service(const char *Id, void *Data = NULL);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+ };
+
+#endif
diff --git a/pes2audioes.cpp b/pes2audioes.cpp
new file mode 100644
index 0000000..7614cd0
--- /dev/null
+++ b/pes2audioes.cpp
@@ -0,0 +1,273 @@
+/*
+ * pes2audioes.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "pes2audioes.h"
+
+#include <stdio.h>
+
+cMarkAdPES2AudioES::cMarkAdPES2AudioES()
+{
+ esdata=NULL;
+ esdatalast=NULL;
+ essize=0;
+ data_left=false;
+}
+
+cMarkAdPES2AudioES::~cMarkAdPES2AudioES()
+{
+ if (esdata) free(esdata);
+ if (esdatalast) free(esdatalast);
+}
+
+void cMarkAdPES2AudioES::Reset()
+{
+ if (esdata) free(esdata);
+ esdata=NULL;
+ essize=0;
+ data_left=false;
+}
+
+int cMarkAdPES2AudioES::FindAudioHeader(uchar *ESData, int ESSize)
+{
+ unsigned short scanner=0x0;
+ int i;
+ for (i=0; i<ESSize; i++)
+ {
+ scanner|=ESData[i];
+ if ((scanner==0x0B77) || ((scanner & 0xFFE0)==0xFFE0))
+ {
+ break;
+ }
+ scanner<<=8;
+
+ }
+ if (i!=ESSize)
+ {
+ return i-1;
+ }
+ return -1;
+}
+
+
+bool cMarkAdPES2AudioES::IsValidAudioHeader(uchar *Data,int *FrameSize)
+{
+ struct MP2HDR *mp2hdr = (struct MP2HDR *) Data;
+ struct AC3HDR *ac3hdr = (struct AC3HDR *) Data;
+
+ if ((ac3hdr->Sync1==0x0b) && (ac3hdr->Sync2==0x77))
+ {
+ if (ac3hdr->SampleRateIndex==3) return false; // reserved
+ if (ac3hdr->FrameSizeIndex>=38) return false; // reserved
+
+ if (FrameSize)
+ {
+ int bitRatesAC3[3][38] = // all values are specified as kbits/s
+ {
+ { 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192,
+ 224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512,
+ 640, 640, 768, 768, 896, 896, 1024, 1024, 1152, 1152, 1280, 1280 }, // 48kHz
+
+ { 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209,
+ 243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558,
+ 696, 697, 835, 836, 975, 976, 1114, 1115, 1253, 1254, 1393, 1394 }, // 44.1kHz
+
+ { 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288,
+ 288, 336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768,
+ 768, 960, 960, 1152, 1152, 1344, 1344, 1536, 1536, 1728, 1728, 1920,1920 } // 32kHz
+ };
+
+ *FrameSize=2*bitRatesAC3[ac3hdr->SampleRateIndex][ac3hdr->FrameSizeIndex];
+ }
+
+ }
+ else if ((mp2hdr->Sync1==0xFF) && (mp2hdr->Sync2==7))
+ {
+ if (mp2hdr->MpegID==1) return false; // reserved
+ if (mp2hdr->Layer==0) return false; // reserved
+ if (mp2hdr->BitRateIndex==0xF) return false; // forbidden
+ if (mp2hdr->SampleRateIndex==3) return false; //reserved
+ if (mp2hdr->Emphasis==2) return false; // reserved
+
+ if (FrameSize)
+ {
+ int bitRates[3][3][16] = // all values are specified as kbits/s
+ {
+ {
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // M1, L1
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // M1, L2
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // M1, L3
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2, L1
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2, L2
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2, L3
+ },
+ {
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2.5, L1
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2.5, L2
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2.5, L3
+ }
+ };
+
+ int samplingFrequencies[3][4] = // all values are specified in Hz
+ {
+ { 44100, 48000, 32000, -1 }, // MPEG 1
+ { 22050, 24000, 16000, -1 }, // MPEG 2
+ { 32000, 16000, 8000, -1 } // MPEG 2.5
+ };
+
+
+ int slots_per_frame[3][3] =
+ {
+ { 12, 144, 144 }, // MPEG 1, Layer I, II, III
+ { 12, 144, 72 }, // MPEG 2, Layer I, II, III
+ { 12, 144, 72 } // MPEG 2.5, Layer I, II, III
+ };
+
+ int mpegIndex;
+ switch (mp2hdr->MpegID)
+ {
+ case 0:
+ mpegIndex=2;
+ break;
+ case 2:
+ mpegIndex=1;
+ break;
+ case 3:
+ mpegIndex=0;
+ break;
+ default:
+ mpegIndex=0; // just to get rid of compiler warnings ;)
+ }
+ int layerIndex = 3 - mp2hdr->Layer;
+
+ // Layer I (i. e., layerIndex == 0) has a larger slot size
+ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+ int sf = samplingFrequencies[mpegIndex][mp2hdr->SampleRateIndex];
+
+ if (mp2hdr->BitRateIndex == 0)
+ *FrameSize = 0; // "free" Bitrate -> we don't support this!
+ else
+ {
+ int br = 1000 * bitRates[mpegIndex][layerIndex][mp2hdr->BitRateIndex]; // bits/s
+ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+
+ *FrameSize = (N + mp2hdr->Padding) * slotSize; // bytes
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+int cMarkAdPES2AudioES::Process(uchar *PESData, int PESSize, uchar **ESData, int *ESSize)
+{
+ if ((!ESData) || (!ESSize)) return -1;
+ *ESData=NULL;
+ *ESSize=0;
+
+ int bytes_processed;
+
+ if (!data_left)
+ {
+ struct PESHDR *peshdr=(struct PESHDR *) PESData;
+
+ // first check some simple things
+ if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1))
+ {
+ Reset();
+ return PESSize;
+ }
+
+ if ((peshdr->StreamID<0xC0) && (peshdr->StreamID>0xDF))
+ {
+ if (peshdr->StreamID!=0xBD) return PESSize; // no audio
+ }
+
+ int Length=(peshdr->LenH<<8)+peshdr->LenL;
+ if (Length>PESSize)
+ {
+ Reset();
+ return PESSize;
+ }
+
+ struct PESHDROPT *peshdropt=(struct PESHDROPT *) &PESData[sizeof(struct PESHDR)];
+
+ uchar *buf;
+ int buflen;
+
+ if (peshdropt->MarkerBits==0x2)
+ {
+ // we have an optional PES header
+ int bpos=sizeof(struct PESHDR)+sizeof(struct PESHDROPT)+
+ peshdropt->Length;
+ buf=&PESData[bpos];
+ buflen=PESSize-bpos;
+ bytes_processed=bpos;
+ }
+ else
+ {
+ int bpos=sizeof(struct PESHDR);
+ buf=&PESData[bpos];
+ buflen=PESSize-bpos;
+ bytes_processed=bpos;
+ }
+ esdata=(uchar *) realloc(esdata,essize+buflen);
+ if (!esdata)
+ {
+ return -1;
+ essize=0;
+ }
+ memcpy(esdata+essize,buf,buflen);
+ essize+=buflen;
+ bytes_processed+=buflen;
+ }
+ else
+ {
+ bytes_processed=essize;
+ data_left=false;
+ }
+
+ int audiohdr=FindAudioHeader(esdata,essize);
+ if (audiohdr!=-1)
+ {
+ int framesize;
+ if (IsValidAudioHeader(&esdata[audiohdr],&framesize))
+ {
+ if ((essize-audiohdr)>=framesize)
+ {
+ *ESData=&esdata[audiohdr];
+ *ESSize=framesize;
+ if (esdatalast) free(esdatalast);
+ esdatalast=esdata;
+
+ int size=(essize-framesize-audiohdr);
+
+ esdata=NULL;
+ essize=0;
+
+ if (size)
+ {
+ void *ptr=malloc(size);
+ if (!ptr) return -1;
+
+ memcpy(ptr,(*ESData)+framesize,size);
+ bytes_processed-=size;
+
+ esdata=(uchar *) ptr;
+ essize=size;
+ data_left=true;
+ }
+ }
+ }
+ }
+ return bytes_processed;
+}
diff --git a/pes2audioes.h b/pes2audioes.h
new file mode 100644
index 0000000..13ebcc2
--- /dev/null
+++ b/pes2audioes.h
@@ -0,0 +1,132 @@
+/*
+ * pes2audioes.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __pes2audioes_h_
+#define __pes2audioes_h_
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+class cMarkAdPES2AudioES
+{
+private:
+ struct PESHDR
+ {
+ uchar Sync1;
+ uchar Sync2;
+ uchar Sync3;
+ uchar StreamID;
+ uchar LenH;
+ uchar LenL;
+ };
+
+#pragma pack(1)
+ struct PESHDROPT
+ {
+unsigned OOC:
+ 1;
+unsigned CY:
+ 1;
+unsigned DAI:
+ 1;
+unsigned PESP:
+ 1;
+unsigned PESSC:
+ 2;
+unsigned MarkerBits:
+ 2;
+unsigned EXT:
+ 1;
+unsigned CRC:
+ 1;
+unsigned ACI:
+ 1;
+unsigned TM:
+ 1;
+unsigned RATE:
+ 1;
+unsigned ESCR:
+ 1;
+unsigned TSF:
+ 2;
+unsigned Length:
+ 8;
+ };
+#pragma pack()
+
+ struct MP2HDR
+ {
+unsigned Sync1:
+ 8;
+unsigned Protection:
+ 1;
+unsigned Layer:
+ 2;
+unsigned MpegID:
+ 2;
+unsigned Sync2:
+ 3;
+unsigned Private:
+ 1;
+unsigned Padding:
+ 1;
+unsigned SampleRateIndex:
+ 2;
+unsigned BitRateIndex:
+ 4;
+unsigned Emphasis:
+ 2;
+unsigned Original:
+ 1;
+unsigned Copyright:
+ 1;
+unsigned ModeExt:
+ 2;
+unsigned Mode:
+ 2;
+ };
+
+#pragma pack(1)
+ struct AC3HDR
+ {
+unsigned Sync1:
+ 8;
+unsigned Sync2:
+ 8;
+unsigned CRC1:
+ 8;
+unsigned CRC2:
+ 8;
+unsigned FrameSizeIndex:
+ 6;
+unsigned SampleRateIndex:
+ 2;
+ };
+#pragma pack()
+
+ uchar *esdatalast;
+ uchar *esdata;
+ int essize;
+ bool data_left;
+
+ int FindAudioHeader(uchar *PESData, int PESSize);
+ bool IsValidAudioHeader(uchar *Data, int *FrameSize);
+ void Reset();
+public:
+ cMarkAdPES2AudioES();
+ ~cMarkAdPES2AudioES();
+ int Process(uchar *PESData, int PESSize, uchar **ESData, int *ESSize);
+};
+
+
+#endif
diff --git a/po/de_DE.po b/po/de_DE.po
new file mode 100644
index 0000000..d34fd78
--- /dev/null
+++ b/po/de_DE.po
@@ -0,0 +1,19 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: vdr\n"
+"Report-Msgid-Bugs-To: <see README>\n"
+"POT-Creation-Date: 2009-08-27 21:50+0200\n"
+"PO-Revision-Date: 2009-08-27 14:18+0200\n"
+"Last-Translator: Jochen Dolze <noad@dolze.de>\n"
+"Language-Team: <vdr@linuxtv.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+msgid "Mark advertisements"
+msgstr "Markiere Werbung"
diff --git a/recv.cpp b/recv.cpp
new file mode 100644
index 0000000..b66cf93
--- /dev/null
+++ b/recv.cpp
@@ -0,0 +1,332 @@
+/*
+ * recv.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "recv.h"
+
+cMarkAdReceiver::cMarkAdReceiver(int RecvNumber, const char *Filename, cTimer *Timer)
+ :
+ cReceiver(Timer->Channel()->GetChannelID(), -1,
+ Timer->Channel()->Vpid(),Timer->Channel()->Apids(),
+ Timer->Channel()->Dpids()),cThread("markad"),
+ buffer(MEGABYTE(3)), running(false) // 3MB Buffer
+{
+ if ((!Filename) || (!Timer)) return;
+
+ recvnumber=RecvNumber;
+ filename=strdup(Filename);
+
+ // 10 ms timeout on getting TS frames
+ buffer.SetTimeouts(0, 10);
+
+ bool useH264=false;
+#if APIVERSNUM >= 10700 && APIVERSNUM < 10702
+ if (Timer->Channel()->System()==DVBFE_DELSYS_DVBS2) useH264=true;
+#endif
+
+#if APIVERSNUM >= 10702
+ if (Timer->Channel()->System()==SYS_DVBS2) useH264=true;
+#endif
+ memset(&macontext,0,sizeof(macontext));
+ if (Timer->Event())
+ {
+ macontext.General.StartTime=Timer->Event()->StartTime();
+ macontext.General.EndTime=Timer->Event()->EndTime();
+ }
+ else
+ {
+ macontext.General.StartTime=Timer->StartTime();
+ macontext.General.EndTime=Timer->StopTime();
+ macontext.General.ManualRecording=true;
+ }
+
+ macontext.General.VPid=Timer->Channel()->Vpid();
+// macontext.General.APid=Timer->Channel()->Apid(0); // TODO ... better solution?
+ macontext.General.DPid=Timer->Channel()->Dpid(0); // TODO ... better solution?
+
+ macontext.General.H264=useH264;
+
+ if (macontext.General.VPid)
+ {
+ video=new cMarkAdVideo(RecvNumber,&macontext);
+ video_demux = new cMarkAdDemux();
+ }
+ else
+ {
+ video=NULL;
+ video_demux=NULL;
+ }
+
+ if (macontext.General.APid)
+ {
+ mp2_demux = new cMarkAdDemux();
+ }
+ else
+ {
+ mp2_demux = NULL;
+ }
+
+ if (macontext.General.DPid)
+ {
+ ac3_demux = new cMarkAdDemux();
+ }
+ else
+ {
+ ac3_demux=NULL;
+ }
+
+ if ((macontext.General.APid) || (macontext.General.DPid))
+ {
+ audio=new cMarkAdAudio(RecvNumber,&macontext);
+ }
+ else
+ {
+ audio=NULL;
+ }
+
+ decoder=new cMarkAdDecoder(RecvNumber,useH264,macontext.General.DPid!=0);
+ common=new cMarkAdCommon(RecvNumber,&macontext);
+
+ marks.Load(Filename);
+ Index=NULL;
+ lastiframe=0;
+}
+
+cMarkAdReceiver::~cMarkAdReceiver()
+{
+ cReceiver::Detach();
+ if (running)
+ {
+ running=false;
+ buffer.Signal();
+ Cancel(2);
+ }
+ buffer.Clear();
+
+ if ((!macontext.State.ContentStopped) && (lastiframe))
+ {
+ MarkAdMark tempmark;
+ tempmark.Position=lastiframe;
+ tempmark.Comment=strdup("stop of content (user)");
+ AddMark(&tempmark);
+ isyslog("markad [%i]: stop of content (user)",recvnumber);
+ free(tempmark.Comment);
+ }
+
+ if (video_demux) delete video_demux;
+ if (mp2_demux) delete mp2_demux;
+ if (ac3_demux) delete ac3_demux;
+ if (decoder) delete decoder;
+ if (video) delete video;
+ if (audio) delete audio;
+ if (common) delete common;
+ if (filename) free(filename);
+}
+
+int cMarkAdReceiver::LastIFrame()
+{
+ if (!Index)
+ {
+ Index = new cIndexFile(filename,false);
+ if (!Index)
+ {
+ esyslog("markad [%i]: ERROR can't allocate index",recvnumber);
+ return -1;
+ }
+ else if (!Index->Ok())
+ {
+ // index file is not ready till now, try it later
+ delete Index;
+ Index=NULL;
+ return -1;
+ }
+ }
+ return Index->GetNextIFrame(Index->Last(),false,NULL,NULL,NULL,true);
+}
+
+void cMarkAdReceiver::Activate(bool On)
+{
+ if (On)
+ {
+ if (!running)
+ {
+ running=true;
+ Start();
+ }
+ }
+ else if (running)
+ {
+ marks.Save();
+ if (Index) delete Index;
+ running = false;
+ buffer.Signal();
+ Cancel(2);
+ }
+}
+
+void cMarkAdReceiver::Receive(uchar *Data, int Length)
+{
+ int len = Length;
+
+ if (!buffer.Check(len))
+ {
+ // Buffer overrun
+ esyslog("markad [%i]: buffer overrun (Check)",recvnumber);
+ buffer.Signal();
+ return;
+ }
+
+ cFrame *frame=new cFrame(Data, len);
+ if (frame && !buffer.Put(frame))
+ {
+ // Buffer overrun
+ esyslog("markad [%i]: buffer overrun (Put)",recvnumber);
+ delete frame;
+ buffer.Signal();
+ }
+}
+void cMarkAdReceiver::AddMark(MarkAdMark *mark)
+{
+ if (!mark) return;
+ if (!mark->Position) return;
+ if (!mark->Comment) return;
+ cMark *newmark=marks.Add(mark->Position);
+ if (newmark)
+ {
+ if (newmark->comment) free(newmark->comment);
+ newmark->comment=strdup(mark->Comment);
+ }
+ marks.Save();
+}
+
+void cMarkAdReceiver::Action()
+{
+ while (running)
+ {
+ cFrame *frame=buffer.Get();
+ if (frame)
+ {
+ lastiframe=LastIFrame();
+ MarkAdMark *mark;
+
+ if (common)
+ {
+ mark=common->Process(lastiframe);
+ AddMark(mark);
+ }
+
+ if ((video_demux) && (decoder) && (video))
+ {
+ cTimeMs t;
+ uchar *pkt;
+ int pktlen;
+
+ uchar *tspkt = frame->Data();
+ int tslen = frame->Count();
+
+ while (tslen>0)
+ {
+ int len=video_demux->Process(macontext.General.VPid,tspkt,tslen,&pkt,&pktlen);
+ if (len<0)
+ {
+ break;
+ }
+ else
+ {
+ if (pkt)
+ {
+ decoder->FindH262VideoInfos(&macontext,pkt,pktlen);
+ if (decoder->DecodeVideo(&macontext,pkt,pktlen))
+ {
+ mark=video->Process(lastiframe);
+ AddMark(mark);
+
+ }
+ }
+ tspkt+=len;
+ tslen-=len;
+ }
+ }
+ if (t.Elapsed()>100)
+ {
+ isyslog("markad [%i]: 100ms exceeded -> %Li",
+ recvnumber,t.Elapsed());
+ }
+ }
+
+ if ((mp2_demux) && (decoder) && (audio))
+ {
+ uchar *pkt;
+ int pktlen;
+
+ uchar *tspkt = frame->Data();
+ int tslen = frame->Count();
+
+ while (tslen>0)
+ {
+ int len=mp2_demux->Process(macontext.General.APid,tspkt,tslen,&pkt,&pktlen);
+ if (len<0)
+ {
+ break;
+ }
+ else
+ {
+ if (pkt)
+ {
+ if (decoder->DecodeMP2(&macontext,pkt,pktlen))
+ {
+ mark=audio->Process(lastiframe);
+ AddMark(mark);
+ }
+ }
+ tspkt+=len;
+ tslen-=len;
+ }
+ }
+ }
+
+ if ((ac3_demux) && (decoder) && (audio))
+ {
+ uchar *pkt;
+ int pktlen;
+
+ uchar *tspkt = frame->Data();
+ int tslen = frame->Count();
+
+ while (tslen>0)
+ {
+ int len=ac3_demux->Process(macontext.General.APid,tspkt,tslen,&pkt,&pktlen);
+ if (len<0)
+ {
+ break;
+ }
+ else
+ {
+ if (pkt)
+ {
+ decoder->FindAC3AudioInfos(&macontext,pkt,pktlen);
+ if (decoder->DecodeAC3(&macontext,pkt,pktlen))
+ {
+ mark=audio->Process(lastiframe);
+ }
+ }
+ tspkt+=len;
+ tslen-=len;
+ }
+ }
+ }
+
+ buffer.Drop(frame);
+ }
+ else
+ buffer.Wait();
+ }
+ buffer.Clear();
+ running=false;
+}
+
+
diff --git a/recv.h b/recv.h
new file mode 100644
index 0000000..fb7e5a0
--- /dev/null
+++ b/recv.h
@@ -0,0 +1,90 @@
+/*
+ * recv.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __recv_h_
+#define __recv_h_
+
+#include <vdr/receiver.h>
+#include <vdr/filter.h>
+#include <vdr/thread.h>
+#include <vdr/ringbuffer.h>
+#include <vdr/recording.h>
+
+#include "demux.h"
+#include "decoder.h"
+#include "audio.h"
+#include "video.h"
+#include "common.h"
+
+#if (APIVERSNUM >= 10700)
+#include <linux/dvb/frontend.h>
+#endif
+
+class cMarkAdRingBuffer : public cRingBufferFrame
+{
+private:
+ int pid;
+public:
+ cMarkAdRingBuffer(int Size) : cRingBufferFrame(Size, true) {};
+ ~cMarkAdRingBuffer()
+ {
+ Clear();
+ }
+ void Wait(void)
+ {
+ WaitForGet();
+ }
+ void Signal(void)
+ {
+ EnableGet();
+ }
+ bool Check(int Size)
+ {
+ return (Free() >= Size);
+ }
+};
+
+class cMarkAdReceiver : public cReceiver, public cThread
+{
+private:
+ int recvnumber;
+ char *filename;
+ int lastiframe;
+
+ cMarks marks;
+ cIndexFile *Index;
+
+ int LastIFrame();
+ MarkAdContext macontext;
+
+ cMarkAdDecoder *decoder;
+ cMarkAdCommon *common;
+ cMarkAdAudio *audio;
+ cMarkAdVideo *video;
+
+ cMarkAdDemux *video_demux;
+ cMarkAdDemux *mp2_demux;
+ cMarkAdDemux *ac3_demux;
+
+ void AddMark(MarkAdMark *mark);
+protected:
+ virtual void Activate(bool On);
+ virtual void Receive(uchar *Data, int Length);
+ void Action();
+ cMarkAdRingBuffer buffer;
+ bool running;
+public:
+ cMarkAdReceiver(int RecvNumber, const char *Filename, cTimer *Timer);
+ const char *FileName()
+ {
+ return (const char *) filename;
+ }
+ virtual ~cMarkAdReceiver();
+};
+
+#endif
diff --git a/status.cpp b/status.cpp
new file mode 100644
index 0000000..bb308c9
--- /dev/null
+++ b/status.cpp
@@ -0,0 +1,79 @@
+/*
+ * status.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "status.h"
+
+cStatusMarkAd::cStatusMarkAd()
+{
+ memset(recv,0,sizeof(recv));
+}
+
+int cStatusMarkAd::GetFreeReceiver()
+{
+ for (int i=0; i<(MAXDEVICES*MAXRECEIVERS); i++)
+ {
+ if (recv[i]==NULL) return i;
+ }
+ return -1;
+}
+
+int cStatusMarkAd::FindReceiver(const char *FileName)
+{
+ for (int i=0; i<(MAXDEVICES*MAXRECEIVERS); i++)
+ {
+ if (recv[i])
+ {
+ if (!strcmp(recv[i]->FileName(),FileName)) return i;
+ }
+ }
+ return -1;
+}
+
+void cStatusMarkAd::Recording(const cDevice *Device, const char *Name, const char *FileName, bool On)
+{
+ if (!Device) return; // just to be safe
+ if (!FileName) return; // we cannot operate without a filename
+
+ int recvnumber;
+
+ if (On)
+ {
+ if (!Name) return; // we cannot operate without name ;)
+
+ cTimer *timer=NULL;
+
+ for (cTimer *Timer = Timers.First(); Timer; Timer=Timers.Next(Timer))
+ {
+ if (Timer->Recording() && (!strcmp(Timer->File(),Name)))
+ {
+ timer=Timer;
+ break;
+ }
+ }
+
+ if (!timer) return;
+
+ recvnumber=GetFreeReceiver();
+ if (recvnumber<0) return;
+
+ recv[recvnumber] = new cMarkAdReceiver(recvnumber,FileName,timer);
+ dsyslog("markad [%i]: start recording %s ",recvnumber,FileName);
+ ((cDevice *) Device)->AttachReceiver(recv[recvnumber]);
+ }
+ else
+ {
+ recvnumber=FindReceiver(FileName);
+ if (recvnumber<0) return;
+
+ dsyslog("markad [%i]: stop recording %s ",recvnumber,FileName);
+ ((cDevice *) Device)->Detach(recv[recvnumber]);
+ delete recv[recvnumber];
+ recv[recvnumber]=NULL;
+ }
+
+}
diff --git a/status.h b/status.h
new file mode 100644
index 0000000..7f1dadb
--- /dev/null
+++ b/status.h
@@ -0,0 +1,27 @@
+/*
+ * status.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+#ifndef __status_h_
+#define __status_h_
+
+#include <vdr/status.h>
+#include "recv.h"
+
+// --- cStatusMarkAd
+class cStatusMarkAd : public cStatus
+{
+private:
+ cMarkAdReceiver *recv[MAXDEVICES*MAXRECEIVERS];
+ int FindReceiver(const char *FileName);
+ int GetFreeReceiver();
+protected:
+ virtual void Recording(const cDevice *Device, const char *Name, const char *FileName, bool On);
+public:
+ cStatusMarkAd();
+};
+
+#endif
diff --git a/ts2pes.cpp b/ts2pes.cpp
new file mode 100644
index 0000000..c75b672
--- /dev/null
+++ b/ts2pes.cpp
@@ -0,0 +1,233 @@
+/*
+ * ts2pes.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "ts2pes.h"
+
+#include <stdio.h>
+
+cMarkAdTS2PES::cMarkAdTS2PES()
+{
+ pesdata=NULL;
+ pesdatalast=NULL;
+ Reset();
+}
+
+cMarkAdTS2PES::~cMarkAdTS2PES()
+{
+ if (pesdata) free(pesdata);
+ if (pesdatalast) free(pesdatalast);
+}
+
+void cMarkAdTS2PES::Reset()
+{
+ if (pesdata) free(pesdata);
+ pesdata=NULL;
+ pessize=0;
+ data_left=false;
+ streamsize=0;
+ counter=-1;
+ sync=false;
+}
+
+int cMarkAdTS2PES::FindPESHeader(uchar *TSData, int TSSize, int *StreamSize)
+{
+ unsigned long scanner=0xFFFFFFFF;
+ int i;
+ for (i=0; i<TSSize; i++)
+ {
+ scanner<<=8;
+ if (scanner==0x00000100)
+ {
+ break;
+ }
+ scanner|=TSData[i];
+ }
+ if (i!=TSSize)
+ {
+ if (StreamSize)
+ {
+ if (TSData[i]>=0xBC)
+ {
+ *StreamSize=(TSData[i+1]<<8)+TSData[i+2];
+ if (*StreamSize) (*StreamSize)+=6; // 6 Byte PES-Header
+ }
+ }
+ return i-3;
+ }
+ return -1;
+}
+
+int cMarkAdTS2PES::Process(int Pid, uchar *TSData, int TSSize, uchar **PESData, int *PESSize)
+{
+ if ((!PESData) || (!PESSize)) return -1;
+ *PESData=NULL;
+ *PESSize=0;
+
+ int buflen=TS_SIZE+1;
+ uchar *buf=NULL;
+
+ int bytes_processed;
+
+ if (!data_left)
+ {
+ // search for TS packet sync
+ int i;
+ for (i=0; i<TSSize; i++)
+ {
+ if (TSData[i]==0x47) break;
+ }
+ if (i==TSSize)
+ {
+ Reset();
+ return TSSize;
+ }
+ TSData+=i;
+ TSSize-=i;
+
+ struct TSHDR *tshdr = (struct TSHDR *) TSData;
+
+ int pid = (tshdr->PidH << 8) | tshdr->PidL;
+ if (Pid!=pid)
+ {
+ return TS_SIZE; // not for us
+ }
+
+ if ((tshdr->PayloadStart==0) && (!sync))
+ {
+ return TS_SIZE;
+ }
+ else
+ {
+ sync=true;
+ }
+
+ if ((counter!=-1) && (((counter+1) & 0xF)!=tshdr->Counter))
+ {
+ // sequence error
+ Reset();
+ return TS_SIZE;
+ }
+ counter=tshdr->Counter;
+
+ if ((tshdr->AFC<=0) || (tshdr->AFC>3))
+ {
+ Reset();
+ return TS_SIZE;
+ }
+
+ // we just ignore the infos in the adaption field (e.g. OPCR/PCR)
+ if ((tshdr->AFC!=1) && (tshdr->AFC!=3))
+ {
+ return TS_SIZE;
+ }
+
+ if (tshdr->AFC==1)
+ {
+ // payload only
+ buflen=TS_SIZE-sizeof(struct TSHDR);
+ buf=&TSData[sizeof(struct TSHDR)];
+ }
+
+ if (tshdr->AFC==3)
+ {
+ // adaption field + payload
+ struct TSADAPT *tsadapt = (struct TSADAPT *) &TSData[4];
+ int alen=tsadapt->Len+1;
+ buflen=TS_SIZE-(sizeof(struct TSHDR)+alen);
+ buf=&TSData[sizeof(struct TSHDR)+alen];
+ }
+
+ if (buflen>TS_SIZE)
+ {
+ // size to large
+ Reset();
+ return TS_SIZE;
+ }
+ bytes_processed=TS_SIZE-buflen;
+
+ pesdata=(uchar *) realloc(pesdata,pessize+buflen);
+ if (!pesdata)
+ {
+ pessize=0;
+ return -1;
+ }
+ memcpy(pesdata+pessize,buf,buflen);
+ pessize+=buflen;
+ bytes_processed+=buflen;
+ }
+ else
+ {
+ bytes_processed=pessize;
+ data_left=false;
+ }
+
+ int peshdr=FindPESHeader(pesdata, pessize, &streamsize);
+ if (peshdr==0)
+ {
+ if (!streamsize)
+ {
+ peshdr=FindPESHeader(pesdata+3,pessize-3,NULL);
+ if (peshdr>0) peshdr+=3;
+ }
+ else
+ {
+ if (pessize>streamsize)
+ {
+ int size=pessize-streamsize;
+
+ *PESData=pesdata;
+ *PESSize=streamsize;
+ if (pesdatalast) free(pesdatalast);
+ pesdatalast=pesdata;
+ pesdata=NULL;
+ pessize=0;
+
+ void *ptr=malloc(size);
+ if (!ptr) return -1;
+ memcpy(ptr,(*PESData)+streamsize,size);
+ bytes_processed-=size;
+ pessize=size;
+ pesdata=(uchar *) ptr;
+ data_left=true;
+ streamsize=0;
+ }
+ }
+ }
+ if (peshdr>0)
+ {
+ // start of next PES paket found
+ if (pesdata)
+ {
+ // return old data
+ *PESData=pesdata;
+ *PESSize=peshdr;
+ if (pesdatalast) free(pesdatalast);
+ pesdatalast=pesdata;
+ int size=pessize-peshdr;
+ pesdata=NULL;
+ pessize=0;
+
+ if (size>0)
+ {
+ void *ptr=malloc(size);
+ if (!ptr) return -1;
+ memcpy(ptr,(*PESData)+peshdr,size);
+ bytes_processed-=size;
+ pessize=size;
+ pesdata=(uchar *) ptr;
+ data_left=true;
+ }
+ else
+ {
+ // TODO: not sure if this is ok
+ bytes_processed-=size;
+ }
+ }
+ }
+ return bytes_processed;
+}
diff --git a/ts2pes.h b/ts2pes.h
new file mode 100644
index 0000000..a3596be
--- /dev/null
+++ b/ts2pes.h
@@ -0,0 +1,72 @@
+/*
+ * ts2pes.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __ts2pes_h_
+#define __ts2pes_h_
+
+#ifndef TS_SIZE
+#define TS_SIZE 188
+#endif
+
+#ifndef uchar
+typedef unsigned char uchar;
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+class cMarkAdTS2PES
+{
+private:
+ struct TSHDR
+ {
+unsigned Sync:
+ 8;
+unsigned PidH:
+ 5;
+unsigned Priority:
+ 1;
+unsigned PayloadStart:
+ 1;
+unsigned TError:
+ 1;
+unsigned PidL:
+ 8;
+unsigned Counter:
+ 4;
+unsigned AFC:
+ 2;
+unsigned TSC:
+ 2;
+ };
+
+ struct TSADAPT
+ {
+unsigned Len:
+ 8;
+unsigned Flags:
+ 8;
+ };
+
+ uchar *pesdatalast;
+ uchar *pesdata;
+ int pessize;
+ int streamsize;
+ bool data_left;
+ int counter;
+ bool sync;
+
+ void Reset();
+ int FindPESHeader(uchar *TSData, int TSSize, int *StreamSize);
+public:
+ cMarkAdTS2PES();
+ ~cMarkAdTS2PES();
+ int Process(int Pid,uchar *TSData, int TSSize, uchar **PESData, int *PESSize);
+};
+
+#endif
diff --git a/video.cpp b/video.cpp
new file mode 100644
index 0000000..fb956fd
--- /dev/null
+++ b/video.cpp
@@ -0,0 +1,266 @@
+/*
+ * video.cpp: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "video.h"
+
+cMarkAdBlackBordersHoriz::cMarkAdBlackBordersHoriz(int RecvNumber, MarkAdContext *maContext)
+{
+ macontext=maContext;
+
+ borderstatus=false;
+ borderiframe=-1;
+ borderstarttime=0;
+}
+
+void cMarkAdBlackBordersHoriz::SaveFrame(int LastIFrame)
+{
+ if (!macontext) return;
+ if (!macontext->Video.Data.Valid) return;
+
+ if (macontext->Video.Data.PlaneLinesize[0]!=macontext->Video.Info.Width) return;
+
+ FILE *pFile;
+ char szFilename[32];
+
+ // Open file
+ sprintf(szFilename, "frame%06d.pgm", LastIFrame);
+ pFile=fopen(szFilename, "wb");
+ if (pFile==NULL)
+ return;
+
+ // Write header
+ fprintf(pFile, "P5\n%d %d\n255\n", macontext->Video.Info.Width,
+ macontext->Video.Info.Width);
+
+ // Write pixel data
+ fwrite(macontext->Video.Data.Plane[0],1,macontext->Video.Info.Width*
+ macontext->Video.Info.Height,pFile);
+ // Close file
+ fclose(pFile);
+}
+
+int cMarkAdBlackBordersHoriz::Process(int LastIFrame, int *BorderIFrame)
+{
+#define CHECKHEIGHT 20
+#define BRIGHTNESS 20
+ if (!macontext) return 0;
+ if (!macontext->Video.Data.Valid) return 0;
+
+ *BorderIFrame=borderiframe;
+
+ int x,y;
+ bool ftop=true,fbottom=true;
+
+ if (macontext->Video.Data.PlaneLinesize[0]!=macontext->Video.Info.Width)
+ {
+ // slow (?) method
+ for (y=(macontext->Video.Info.Height-CHECKHEIGHT); y<macontext->Video.Info.Height; y++)
+ {
+ for (x=0; x<macontext->Video.Info.Width; x++)
+ {
+ if (macontext->Video.Data.Plane[0][y*macontext->Video.Data.PlaneLinesize[0]+x]>
+ BRIGHTNESS)
+ {
+ fbottom=false;
+ y=macontext->Video.Info.Height;
+ break;
+ }
+ }
+ }
+
+ if (fbottom)
+ {
+ for (y=0; y<CHECKHEIGHT; y++)
+ {
+ for (x=0; x<macontext->Video.Info.Width; x++)
+ {
+ if (macontext->Video.Data.Plane[0][y*macontext->Video.Data.PlaneLinesize[0]+x]
+ >BRIGHTNESS)
+ {
+ ftop=false;
+ y=CHECKHEIGHT;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // "fast" method
+ for (x=(macontext->Video.Info.Height-CHECKHEIGHT)*macontext->Video.Info.Width;
+ x<macontext->Video.Info.Height*macontext->Video.Info.Width; x++)
+ {
+ if (macontext->Video.Data.Plane[0][x]>BRIGHTNESS) fbottom=false;
+ }
+
+ if (fbottom)
+ {
+ for (x=0; x<(macontext->Video.Info.Width*CHECKHEIGHT); x++)
+ {
+ if (macontext->Video.Data.Plane[0][x]>BRIGHTNESS) ftop=false;
+ }
+ }
+ }
+
+ if ((fbottom) && (ftop))
+ {
+ if (!borderstatus)
+ {
+ if (!borderstarttime)
+ {
+ borderiframe=LastIFrame;
+ borderstarttime=time(NULL);
+ borderstatus=false;
+ }
+ else
+ {
+ if ((time(NULL)>(borderstarttime+20)))
+ {
+ borderstatus=true;
+ return 1; // detected black border
+ }
+ }
+ }
+ }
+ else
+ {
+ if (borderstatus)
+ {
+ borderiframe=LastIFrame;
+ borderstarttime=0;
+ borderstatus=false;
+ return -1;
+ }
+ else
+ {
+ borderiframe=-1;
+ borderstarttime=0;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+
+cMarkAdVideo::cMarkAdVideo(int RecvNumber,MarkAdContext *maContext)
+{
+ macontext=maContext;
+ recvnumber=RecvNumber;
+
+ aspectratio.Num=0;
+ aspectratio.Den=0;
+ mark.Comment=NULL;
+ mark.Position=0;
+
+ hborder=new cMarkAdBlackBordersHoriz(RecvNumber,maContext);
+}
+
+cMarkAdVideo::~cMarkAdVideo()
+{
+ ResetMark();
+ delete hborder;
+}
+
+void cMarkAdVideo::ResetMark()
+{
+ if (mark.Comment) free(mark.Comment);
+ mark.Comment=NULL;
+ mark.Position=0;
+}
+
+bool cMarkAdVideo::AddMark(int Position, const char *Comment)
+{
+ if (!Comment) return false;
+ if (mark.Comment)
+ {
+ int oldlen=strlen(mark.Comment);
+ mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment));
+ if (!mark.Comment)
+ {
+ mark.Position=0;
+ return false;
+ }
+ strcat(mark.Comment," [");
+ strcat(mark.Comment,Comment);
+ strcat(mark.Comment,"]");
+ }
+ else
+ {
+ mark.Comment=strdup(Comment);
+ }
+ mark.Position=Position;
+ return true;
+}
+
+bool cMarkAdVideo::AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b)
+{
+ if ((!a) || (!b)) return false;
+
+ if (a->Num==0 || a->Den==0 || b->Num==0 || b->Den==0) return false;
+ if ((a->Num!=b->Num) && (a->Den!=b->Den)) return true;
+ return false;
+
+}
+
+
+MarkAdMark *cMarkAdVideo::Process(int LastIFrame)
+{
+ ResetMark();
+ if (!LastIFrame) return NULL;
+
+ if (macontext->State.ContentStarted)
+ {
+ int borderiframe;
+ int hret=hborder->Process(LastIFrame,&borderiframe);
+
+ if ((hret>0) && (borderiframe))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"detected start of horiz. borders (%i)",borderiframe);
+ if (buf)
+ {
+ dsyslog("markad [%i]: %s",recvnumber,buf);
+ AddMark(borderiframe,buf);
+ free(buf);
+ }
+ }
+
+ if ((hret<0) && (borderiframe))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"detected stop of horiz. borders (%i)",borderiframe);
+ if (buf)
+ {
+ dsyslog("markad [%i]: %s",recvnumber,buf);
+ AddMark(borderiframe,buf);
+ free(buf);
+ }
+ }
+ }
+
+ if (AspectRatioChange(&macontext->Video.Info.AspectRatio,&aspectratio))
+ {
+ char *buf=NULL;
+ asprintf(&buf,"aspect ratio change from %i:%i to %i:%i (%i)",
+ aspectratio.Num,aspectratio.Den,
+ macontext->Video.Info.AspectRatio.Num,
+ macontext->Video.Info.AspectRatio.Den,LastIFrame);
+ if (buf)
+ {
+ isyslog("markad [%i]: %s",recvnumber, buf);
+ AddMark(LastIFrame,buf);
+ free(buf);
+ }
+ }
+
+ aspectratio.Num=macontext->Video.Info.AspectRatio.Num;
+ aspectratio.Den=macontext->Video.Info.AspectRatio.Den;
+
+ return &mark;
+}
diff --git a/video.h b/video.h
new file mode 100644
index 0000000..56b8f33
--- /dev/null
+++ b/video.h
@@ -0,0 +1,53 @@
+/*
+ * video.h: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#ifndef __video_h_
+#define __video_h_
+
+#include <vdr/tools.h> // needed for (d/e/i)syslog
+
+#include <time.h>
+#include <stdio.h>
+
+#include "global.h"
+
+class cMarkAdBlackBordersHoriz
+{
+private:
+ int borderstatus;
+ int borderiframe;
+ time_t borderstarttime;
+ void SaveFrame(int LastIFrame);
+ MarkAdContext *macontext;
+public:
+ cMarkAdBlackBordersHoriz(int RecvNumber, MarkAdContext *maContext);
+ int Process(int LastIFrame,int *BorderIFrame);
+};
+
+class cMarkAdVideo
+{
+private:
+ int recvnumber;
+ MarkAdContext *macontext;
+ MarkAdMark mark;
+
+ MarkAdAspectRatio aspectratio;
+ cMarkAdBlackBordersHoriz *hborder;
+
+ void ResetMark();
+ bool AddMark(int Position, const char *Comment);
+ bool AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b);
+ void SetTimerMarks(int LastIFrame);
+
+public:
+ cMarkAdVideo(int RecvNumber,MarkAdContext *maContext);
+ ~cMarkAdVideo();
+ MarkAdMark *Process(int LastIFrame);
+};
+
+#endif