diff options
author | Jochen Dolze <vdr@dolze.de> | 2009-09-20 20:30:22 +0200 |
---|---|---|
committer | Jochen Dolze <vdr@dolze.de> | 2009-09-20 20:30:22 +0200 |
commit | 27b768a40f33b229ee832d22f24696565b812f98 (patch) | |
tree | d03fec9e719028d9805e143956b38180c1b5920e | |
download | vdr-plugin-markad-27b768a40f33b229ee832d22f24696565b812f98.tar.gz vdr-plugin-markad-27b768a40f33b229ee832d22f24696565b812f98.tar.bz2 |
First commit
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | HISTORY | 12 | ||||
-rw-r--r-- | INSTALL | 167 | ||||
-rw-r--r-- | Makefile | 131 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 17 | ||||
-rw-r--r-- | TODO | 0 | ||||
-rw-r--r-- | audio.cpp | 88 | ||||
-rw-r--r-- | audio.h | 38 | ||||
-rw-r--r-- | common.cpp | 95 | ||||
-rw-r--r-- | common.h | 36 | ||||
-rw-r--r-- | decoder.cpp | 585 | ||||
-rw-r--r-- | decoder.h | 71 | ||||
-rw-r--r-- | demux.cpp | 96 | ||||
-rw-r--r-- | demux.h | 30 | ||||
-rw-r--r-- | global.h | 94 | ||||
-rw-r--r-- | markad-standalone.cpp | 94 | ||||
-rw-r--r-- | markad-standalone.h | 25 | ||||
-rw-r--r-- | markad.cpp | 121 | ||||
-rw-r--r-- | markad.h | 45 | ||||
-rw-r--r-- | pes2audioes.cpp | 273 | ||||
-rw-r--r-- | pes2audioes.h | 132 | ||||
-rw-r--r-- | po/de_DE.po | 19 | ||||
-rw-r--r-- | recv.cpp | 332 | ||||
-rw-r--r-- | recv.h | 90 | ||||
-rw-r--r-- | status.cpp | 79 | ||||
-rw-r--r-- | status.h | 27 | ||||
-rw-r--r-- | ts2pes.cpp | 233 | ||||
-rw-r--r-- | ts2pes.h | 72 | ||||
-rw-r--r-- | video.cpp | 266 | ||||
-rw-r--r-- | video.h | 53 |
33 files changed, 3662 insertions, 0 deletions
@@ -0,0 +1 @@ +Jochen Dolze <vdr@dolze.de> @@ -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 @@ -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. @@ -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 @@ -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/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; +} + @@ -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; +} + @@ -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; +} + + @@ -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; +} @@ -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 |