diff options
| author | anbr <vdr07@deltab.de> | 2011-10-06 17:52:03 +0200 |
|---|---|---|
| committer | anbr <vdr07@deltab.de> | 2011-10-06 17:52:03 +0200 |
| commit | 3fc884edbb4a9d6cb6b4c5e554895302377b325b (patch) | |
| tree | d4bcff6cafc0916de074a0a27e0c8d92ff2adad6 | |
| parent | a7c0d42f96dd548fc808ac9d9963e9152b46856d (diff) | |
| parent | f279ccbfdfcaa4e7301b56ade829cb974aeb5636 (diff) | |
| download | xxv-3fc884edbb4a9d6cb6b4c5e554895302377b325b.tar.gz xxv-3fc884edbb4a9d6cb6b4c5e554895302377b325b.tar.bz2 | |
Merge branch 'vdr2jpeg'
| -rw-r--r-- | contrib/vdr2jpeg/COPYING | 339 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/HISTORY | 56 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/LIESMICH | 60 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/Makefile | 135 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/README | 59 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/ffm.cpp | 1347 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/ffm.h | 28 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/gop.cpp | 751 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/gop.h | 111 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/mpegdec.cpp | 383 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/mpegdec.h | 17 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/tools.cpp | 41 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/tools.h | 18 | ||||
| -rw-r--r-- | contrib/vdr2jpeg/vdr2jpeg.cpp | 230 |
14 files changed, 3575 insertions, 0 deletions
diff --git a/contrib/vdr2jpeg/COPYING b/contrib/vdr2jpeg/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/contrib/vdr2jpeg/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General +Public License instead of this License. diff --git a/contrib/vdr2jpeg/HISTORY b/contrib/vdr2jpeg/HISTORY new file mode 100644 index 0000000..b4aa835 --- /dev/null +++ b/contrib/vdr2jpeg/HISTORY @@ -0,0 +1,56 @@ +0.1.9 + Support new vdr filestructure and fileformat(since VDR-1.7.3) +0.1.1 + small fixes to build gcc 4.3 + update to build with ffmpeg-svn (r15589) + remove predefined include to ffmpeg libraries, unneeded by pkg-config +0.1.0 + update to build with ffmpeg-svn (r12129) + refactoring modules +0.0.12 + show versionsnumber on help screen + small fix to build with ffmpeg-svn (r10260) +0.0.11 + update to build with ffmpeg-svn (r9303) + add options for frame span +0.0.10 + add rules for debian package + update to build with ffmpeg-svn (r6898) +0.0.9 + update VERSION checking to build with ffmpeg-svn (r5812) + COPYING missed inside package + cleanup code + static linking failed, reorder library for linking + strip debugging symbols on non-DEBUG target + change Makefile to default build without FFMDIR must defined +0.0.8b + add missing header math.h (thanks to Tobias Grimm) + add missing header errno.h +0.0.8 + now wantet frame grabed, instead of i-frame only grabbing + * if frame extraction failed neighbor image are duplicate. + * change name schematic to pic (8) + Reimplement encoding based on ffmpeg-main-0.4.9-cvs + enlarge own self-protection memory limit to support bigger HD Recordings from 1,5M to 16M + add check if wanted and readed chunk size has same value + add testfunction for reread dumped gob-file, only present if compile with DEBUG=1 +0.0.7 + add Make.config for user defined compile settings + keep now aspect ratio, if nothing or only one od scaled width or height of output image are defined + fix name schematic gob/pic from 4/4 to 8/2 for better numerical sorting + fix wrong size detection on files over symbolic links (thanks to HFlor-at-vdrportal-de) +0.0.6 + fix parse commandline +0.0.5 + first public release + some cleanup +0.0.4 + Feature Scaling + Reimplement encoding based on ffmpeg-main +0.0.3 + Fix handle of more then 001.vdr (thanks to Peter Sebbel) + Fix a other Compile probs with 2.95 +0.0.2 + Fix Compile probs with 2.95 +0.0.1 + Initial release based on vdrsync.pl and ffmpeg/Libavformat API example diff --git a/contrib/vdr2jpeg/LIESMICH b/contrib/vdr2jpeg/LIESMICH new file mode 100644 index 0000000..a1cf75d --- /dev/null +++ b/contrib/vdr2jpeg/LIESMICH @@ -0,0 +1,60 @@ + +Das ist eine einfaches c++ Programm um jpeg Bilder von VDR-Aufnahmen zu erstellen. +Es basiert auf vdrsync.pl und ffmain, ist eine eigentlich nur ein kleine +Designstudie zur Erweiterung von XXV. vdr2jpeg wird von xxv verwendet, +um Vorschaubilder von Aufnahmen zu generieren. + +Erfordernisse +------------- +ffmpeg-svn (getestet mit r15589) + +Empfohlen +------------ +ffmpeg als shared libary und mit Software Scaler + +$ cd ffmpeg && \ + ./configure --enable-shared --enable-swscale && \ + make && \ + make install + +Installation +------------ + +$ make all +Für die Installation in /usr/bin/local : +$ make install + +um mehr Info zu erhalten +$ make all DEBUG=1 + +um ein statisches Programm zu erhalten +$ make all STATIC=1 +=> es werden keine externen Bibliotheken zur Laufzeit benötigt. + + +Verwendung +---------- +$ ./vdr2jpeg +Usage: ./vdr2jpeg + -r recordings : VDR Aufnahmeverzeichnis + -f frame : Gesuchtes Frame (Auflösung in PAL 1/25s) + -o outdirectory : Ausgabeverzeichnis + -x 160 : Skalierte Breite des Ausgabebildes + -y 120 : Skalierte Höhe des Ausgabebildes + -s 500 : Bereich der Frames, beginned mit dem gesuchten Frame (z.B 500 : 20s) + -i 25 : Abstand zwischen Frames im gewählten Bereich (resolution at PAL - 1/25s) + -c 5 : Anzahl der extrahierten Bilder einer Aufnahme oder im gewählten Bereich + + +Um Frames 5000,10000,15000 zu extrahieren (~ 3,6,9 Minute) + +$ vdr2jpeg -r /video/Auf_der_Flucht/2004-12-28.22:10.50.99.rec \ + -f 5000 -f 10000 -f 15000 + +Selbst Verhalten mittels Auswahl des Bereich + +$ vdr2jpeg -r /video/Auf_der_Flucht/2004-12-28.22:10.50.99.rec \ + -f 5000 -s 10000 -i 5000 + + + diff --git a/contrib/vdr2jpeg/Makefile b/contrib/vdr2jpeg/Makefile new file mode 100644 index 0000000..a113a1b --- /dev/null +++ b/contrib/vdr2jpeg/Makefile @@ -0,0 +1,135 @@ +################################################################################ +# +# Makefile -- for building vdr2jpeg +# +# Copyright (c) 2005-2010 Andreas Brachold +# +# This code is distributed under the terms and conditions of the +# GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. +# + + +# You can change the compile options here +# or add options at own file Make.config + + +# Build ffmpeg e.g. with ./configure --enable-shared --enable-swscale +#FFMDIR = /usr/local/include/ffmpeg + +# Build with debugging symbol and lots of dumped messages +#DEBUG = 1 + +# Build full static paket, if ffmpeg configured without --enable-shared +# you should defined FFMDIR +#STATIC = 1 + +# Place where vdr2jpeg should installed +INSTALLBINDIR ?= /usr/local/bin + +# Place where should package created, need some space. +TMPDIR = /tmp + +################################################################################ +# +# there none user configurable options below this point +# +################################################################################ +ifdef DESTDIR +INSTALLDIR = $(DESTDIR)/$(INSTALLBINDIR) +else +INSTALLDIR = $(INSTALLBINDIR) +endif +################################################################################ +# Compiler/linker settings +CXX ?= g++ +STRIP ?= strip +PKG-CONFIG ?= pkg-config + +# Allow user defined options to overwrite defaults: +-include Make.config + +# General settings: + +ifdef DEBUG + CXXFLAGS ?= -O0 + CXXFLAGS += -g -ggdb + LDFLAGS += -g -ggdb + DEFINES += -DDEBUG +else + CXXFLAGS ?= -O2 +endif +CXXFLAGS += -fPIC -Wall -Woverloaded-virtual + +ifdef FFMDIR +INCLUDES += -I$(FFMDIR) +LIBS += -L$(FFMDIR) -L$(FFMDIR)/libavformat -L$(FFMDIR)/libavcodec -L$(FFMDIR)/libavutil -L$(FFMDIR)/libswscale +LIBS += -lavformat -lavcodec -lavutil -lswscale +else +ifeq ($(shell $(PKG-CONFIG) --exists libavformat && echo 1), 1) +INCLUDES += $(shell $(PKG-CONFIG) --cflags libavformat libavcodec libavutil libswscale) +LIBS += $(shell $(PKG-CONFIG) --libs libavformat libavcodec libavutil libswscale) +endif +endif + + +ifdef STATIC + CXXFLAGS += -static +endif + +################################################################################ +# Target configuration + +PRGNAME = vdr2jpeg +VERSION = $(shell grep 'static const char \*VERSION *=' vdr2jpeg.cpp | awk '{ print $$6 }' | sed -e 's/[";]//g') +ARCHIVE = $(PRGNAME)-$(VERSION) +DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS +FILES = README LIESMICH HISTORY COPYING Makefile \ + vdr2jpeg.cpp \ + gop.cpp gop.h \ + mpegdec.cpp mpegdec.h \ + ffm.cpp ffm.h \ + tools.cpp tools.h +OBJS = vdr2jpeg.o tools.o gop.o mpegdec.o ffm.o + +################################################################################ +# Implicit rules: + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + + +################################################################################ +# Main targets : + +all: vdr2jpeg +.PHONY: all + +vdr2jpeg: $(OBJS) + $(CXX) $(CXXFLAGS) $(OBJS) $(LIBS) -o vdr2jpeg +ifndef DEBUG + $(STRIP) $@ +endif + +install: all + install -d $(INSTALLDIR) + install -m 755 -o root -g root -s vdr2jpeg $(INSTALLDIR) + +uninstall: + rm -f $(INSTALLDIR)/vdr2jpeg + +clean: + @-rm -f vdr2jpeg a.out *.o *.tgz core* *~ + +distclean: clean + @-rm -f *.jpg *.mpv + +dist: distclean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir -p $(TMPDIR)/$(ARCHIVE) + @cp -a $(FILES) $(TMPDIR)/$(ARCHIVE) + @find $(TMPDIR)/$(ARCHIVE) -type d -exec chmod 755 {} \; + @find $(TMPDIR)/$(ARCHIVE) -type f -exec chmod 644 {} \; + @chown root.root -R $(TMPDIR)/$(ARCHIVE)/* + @tar czfh $(ARCHIVE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(ARCHIVE).tgz diff --git a/contrib/vdr2jpeg/README b/contrib/vdr2jpeg/README new file mode 100644 index 0000000..3c1f5ba --- /dev/null +++ b/contrib/vdr2jpeg/README @@ -0,0 +1,59 @@ + +This is a simple c++ program to grab jpeg images from VDR-Recordings. +It's based on vdrsync.pl and ffmain, and was design as little study to enhance xxv. +vdr2jpeg are used from xxv to generated preview images of recordings. + +Requirements +------------- +ffmpeg-svn (tested with r15589) + +Recommended +------------ +ffmpeg as shared libary and with software scaler + +$ cd ffmpeg && \ + ./configure --enable-shared --enable-swscale && \ + make && \ + make install + +Installation +------------ + +to build, type simple +$ make all + +for install on /usr/bin/local : +$ make install + +or build with more diagnostic +$ make all DEBUG=1 + +or to link vdr2jpeg static, +if your want that no external libraries are needed at run-time +$ make all STATIC=1 + + +Usage +---------- +$ ./vdr2jpeg +Usage: ./vdr2jpeg + -r recordings : VDR recording folder + -f frame : wanted frame (resolution at PAL - 1/25s) + -o outdirectory : output folder + -x 160 : scaled width of output image + -y 120 : scaled height of output image + -s 5000 : frame range, started at wanted frame (e.g 500 : 20s) + -i 25 : space beetween frames at selected range (resolution at PAL - 1/25s) + -c 5 : number of extracted frames of an recording or within the selected range + + +e.g. to extract frames 5000,10000,15000 (at 3,6,9 Minutes) + +$ vdr2jpeg -r /video/Auf_der_Flucht/2004-12-28.22:10.50.99.rec \ + -f 5000 -f 10000 -f 15000 + + +same using with selected span of frames + +$ vdr2jpeg -r /video/Auf_der_Flucht/2004-12-28.22:10.50.99.rec \ + -f 5000 -s 10000 -i 5000 diff --git a/contrib/vdr2jpeg/ffm.cpp b/contrib/vdr2jpeg/ffm.cpp new file mode 100644 index 0000000..66b76bf --- /dev/null +++ b/contrib/vdr2jpeg/ffm.cpp @@ -0,0 +1,1347 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright (c) 2005-2008 Andreas Brachold + * + * based on FFmpeg main Copyright (c) 2000-2003 Fabrice Bellard + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <limits.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <math.h> +#include <string.h> + +#include "ffm.h" + +extern "C" { +#include <libavutil/avutil.h> +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libswscale/swscale.h> +} + +static int frame_width = 0; +static int frame_height = 0; +static float frame_aspect_ratio = 0; +static enum PixelFormat frame_pix_fmt = PIX_FMT_YUV420P; +static int frame_rate = 25; +static int frame_rate_base = 1; +static int keep_aspect_ratio = 1; + +/** select an input file for an output file */ +#define MAX_FILES 1 + +static AVFormatContext *input_files[MAX_FILES]; +static int64_t input_files_ts_offset[MAX_FILES]; +static unsigned int nb_input_files = 0; + +static AVFormatContext *output_files[MAX_FILES]; +static unsigned int nb_output_files = 0; + +static AVInputFormat *file_iformat; +static AVOutputFormat *file_oformat; + +static int max_frames[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX}; +static int video_qdiff = 3; + + +static const char *video_rc_eq="tex^qComp"; +static int me_method = ME_EPZS; + +static int same_quality = 1; + +static int top_field_first = -1; + + + + +static float mux_preload= 0.5; +static float mux_max_delay= 0.7; + +static int64_t input_ts_offset = 0; + +static int video_sync_method= 1; +static int opt_shortest = 0; // + +static int verbose = +#ifdef DEBUG + 2; +#else + -1; +#endif + +static int nb_frames_dup = 0; +static int nb_frames_drop = 0; +static int input_sync; + +static int pgmyuv_compatibility_hack=0; +static int dts_delta_threshold = 10; + +AVCodecContext *avctx_opts; +AVFormatContext *avformat_opts; +//static int64_t timer_start = 0; + +struct AVInputStream; + +typedef struct AVOutputStream { + unsigned int file_index; /* file index */ + int index; /* stream index in the output file */ + int source_index; /* AVInputStream index */ + AVStream *st; /* stream in the output file */ + int encoding_needed; /* true if encoding needed for this stream */ + int frame_number; + /* input pts and corresponding output pts + for A/V sync */ + //double sync_ipts; /* dts from the AVPacket of the demuxer in second units */ + struct AVInputStream *sync_ist; /* input stream to sync against */ + int64_t sync_opts; /* output frame counter, could be changed to some true timestamp */ //FIXME look at frame_number + /* video only */ + int video_resample; + AVFrame pict_tmp; /* temporary image for resampling */ + struct SwsContext *img_resample_ctx; /* for image resampling */ + int resample_height; + +} AVOutputStream; + +typedef struct AVInputStream { + unsigned int file_index; + int index; + AVStream *st; + int discard; /* true if stream data should be discarded */ + int decoding_needed; /* true if the packets must be decoded in 'raw_fifo' */ + int64_t sample_index; /* current sample */ + + int64_t start; /* time when read started */ + unsigned long frame; /* current frame */ + int64_t next_pts; /* synthetic pts for cases where pkt.pts + is not defined */ + int64_t pts; /* current pts */ + int is_start; /* is 1 at the start and after a discontinuity */ +} AVInputStream; + +typedef struct AVInputFile { + int eof_reached; /* true if eof reached */ + int ist_index; /* index of first stream in ist_table */ + int buffer_size; /* current total buffer size */ + unsigned int nb_streams; /* nb streams we are aware of */ +} AVInputFile; + +static double +get_sync_ipts(const AVOutputStream *ost) +{ + const AVInputStream *ist = ost->sync_ist; + return (double)(ist->pts + input_files_ts_offset[ist->file_index] )/AV_TIME_BASE; +} + +static int bit_buffer_size= 1024*256; +static uint8_t *bit_buffer= NULL; + +static bool do_video_out(AVFormatContext *s, + AVOutputStream *ost, + AVInputStream *ist, + AVFrame *in_picture, + int *frame_size) +{ + int nb_frames, i, ret; + AVFrame *final_picture, *formatted_picture, *resampling_dst, *padding_src; + AVFrame picture_crop_temp, picture_pad_temp; + AVCodecContext *enc, *dec; + + avcodec_get_frame_defaults(&picture_crop_temp); + avcodec_get_frame_defaults(&picture_pad_temp); + + enc = ost->st->codec; + dec = ist->st->codec; + + /* by default, we output a single frame */ + nb_frames = 1; + + *frame_size = 0; + + if(video_sync_method){ + double vdelta; + vdelta = get_sync_ipts(ost) / av_q2d(enc->time_base) - ost->sync_opts; + //FIXME set to 0.5 after we fix some dts/pts bugs like in avidec.c + if (vdelta < -1.1) + nb_frames = 0; + else if (vdelta > 1.1) + nb_frames = lrintf(vdelta); +//fprintf(stderr, "vdelta:%f, ost->sync_opts:%"PRId64", ost->sync_ipts:%f nb_frames:%d\n", vdelta, ost->sync_opts, ost->sync_ipts, nb_frames); + if (nb_frames == 0){ + ++nb_frames_drop; + if (verbose>2) + fprintf(stderr, "*** drop!\n"); + }else if (nb_frames > 1) { + nb_frames_dup += nb_frames; + if (verbose>2) + fprintf(stderr, "*** %d dup!\n", nb_frames-1); + } + }else + ost->sync_opts= lrintf(get_sync_ipts(ost) / av_q2d(enc->time_base)); + + nb_frames= FFMIN(nb_frames, max_frames[CODEC_TYPE_VIDEO] - ost->frame_number); + if (nb_frames <= 0) + return true; + + { + formatted_picture = in_picture; + } + + final_picture = formatted_picture; + padding_src = formatted_picture; + resampling_dst = &ost->pict_tmp; + + if (ost->video_resample) { + padding_src = NULL; + final_picture = &ost->pict_tmp; + sws_scale(ost->img_resample_ctx, formatted_picture->data, formatted_picture->linesize, + 0, ost->resample_height, resampling_dst->data, resampling_dst->linesize); + } + + /* duplicates frame if needed */ + for(i=0;i<nb_frames;i++) { + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index= ost->index; + + /*if (s->oformat->flags & AVFMT_RAWPICTURE) { + // raw pictures are written as AVPicture structure to + // avoid any copies. We support temorarily the older + // method. + AVFrame* old_frame = enc->coded_frame; + enc->coded_frame = dec->coded_frame; //FIXME/XXX remove this hack + pkt.data= (uint8_t *)final_picture; + pkt.size= sizeof(AVPicture); + if(dec->coded_frame && enc->coded_frame->pts != AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); + if(dec->coded_frame && dec->coded_frame->key_frame) + pkt.flags |= PKT_FLAG_KEY; + + av_interleaved_write_frame(s, &pkt); + enc->coded_frame = old_frame; + } else*/ { + AVFrame big_picture; + + big_picture= *final_picture; + /* better than nothing: use input picture interlaced + settings */ + big_picture.interlaced_frame = in_picture->interlaced_frame; + if(avctx_opts->flags & (CODEC_FLAG_INTERLACED_DCT|CODEC_FLAG_INTERLACED_ME)){ + if(top_field_first == -1) + big_picture.top_field_first = in_picture->top_field_first; + else + big_picture.top_field_first = top_field_first; + } + + /* handles sameq here. This is not correct because it may + not be a global option */ + if (same_quality) { + big_picture.quality = (int)ist->st->quality; + }else + big_picture.quality = (int)ost->st->quality; + big_picture.pict_type = 0; +// big_picture.pts = AV_NOPTS_VALUE; + big_picture.pts= ost->sync_opts; +// big_picture.pts= av_rescale(ost->sync_opts, AV_TIME_BASE*(int64_t)enc->time_base.num, enc->time_base.den); +//av_log(NULL, AV_LOG_DEBUG, "%"PRId64" -> encoder\n", ost->sync_opts); + ret = avcodec_encode_video(enc, + bit_buffer, bit_buffer_size, + &big_picture); + if (ret == -1) { + fprintf(stderr, "Video encoding failed\n"); + return false; + } + //enc->frame_number = enc->real_pict_num; + if(ret>0){ + pkt.data= bit_buffer; + pkt.size= ret; + if(enc->coded_frame && enc->coded_frame->pts != (int64_t)AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); +/*av_log(NULL, AV_LOG_DEBUG, "encoder -> %"PRId64"/%"PRId64"\n", + pkt.pts != AV_NOPTS_VALUE ? av_rescale(pkt.pts, enc->time_base.den, AV_TIME_BASE*(int64_t)enc->time_base.num) : -1, + pkt.dts != AV_NOPTS_VALUE ? av_rescale(pkt.dts, enc->time_base.den, AV_TIME_BASE*(int64_t)enc->time_base.num) : -1);*/ + + if(enc->coded_frame && enc->coded_frame->key_frame) + pkt.flags |= PKT_FLAG_KEY; + av_interleaved_write_frame(s, &pkt); + *frame_size = ret; + } + } + ost->sync_opts++; + ost->frame_number++; + } + return true; +} + +/* pkt = NULL means EOF (needed to flush decoder buffers) */ +static int output_packet(AVInputStream *ist, int ist_index, + AVOutputStream **ost_table, int nb_ostreams, + const AVPacket *pkt) +{ + AVFormatContext *os; + AVOutputStream *ost; + uint8_t *ptr; + int len, ret, i; + uint8_t *data_buf; + int data_size, got_picture; + AVFrame picture; + + if(!pkt){ + ist->pts= ist->next_pts; // needed for last packet if vsync=0 + } else if (pkt->dts != (int64_t)AV_NOPTS_VALUE) { //FIXME seems redundant, as libavformat does this too + ist->next_pts = ist->pts = av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); + } else { +// assert(ist->pts == ist->next_pts); + } + + if (pkt == NULL) { + /* EOF handling */ + ptr = NULL; + len = 0; + goto handle_eof; + } + + len = pkt->size; + ptr = pkt->data; + while (len > 0) { + handle_eof: + /* decode the packet if needed */ + data_buf = NULL; /* fail safe */ + data_size = 0; + if (ist->decoding_needed) { + switch(ist->st->codec->codec_type) { + case CODEC_TYPE_VIDEO: + data_size = (ist->st->codec->width * ist->st->codec->height * 3) / 2; + /* XXX: allocate picture correctly */ + avcodec_get_frame_defaults(&picture); + + ret = avcodec_decode_video(ist->st->codec, + &picture, &got_picture, ptr, len); + ist->st->quality= picture.quality; + if (ret < 0) + goto fail_decode; + if (!got_picture) { + /* no picture yet */ + goto discard_packet; + } + if (ist->st->codec->time_base.num != 0) { + ist->next_pts += ((int64_t)AV_TIME_BASE * + ist->st->codec->time_base.num) / + ist->st->codec->time_base.den; + } + len = 0; + break; + default: + goto fail_decode; + } + } else { + if(ist->st->codec->codec_type == CODEC_TYPE_VIDEO) { + if (ist->st->codec->time_base.num != 0) { + ist->next_pts += ((int64_t)AV_TIME_BASE * + ist->st->codec->time_base.num) / + ist->st->codec->time_base.den; + } + } + data_buf = ptr; + data_size = len; + ret = len; + len = 0; + } + + /* frame rate emulation */ + if (ist->st->codec->rate_emu) { + int64_t pts = av_rescale((int64_t) ist->frame * ist->st->codec->time_base.num, 1000000, ist->st->codec->time_base.den); + int64_t now = av_gettime() - ist->start; + if (pts > now) + usleep(pts - now); + + ist->frame++; + } + +#if 0 + /* mpeg PTS deordering : if it is a P or I frame, the PTS + is the one of the next displayed one */ + /* XXX: add mpeg4 too ? */ + if (ist->st->codec->codec_id == CODEC_ID_MPEG1VIDEO) { + if (ist->st->codec->pict_type != B_TYPE) { + int64_t tmp; + tmp = ist->last_ip_pts; + ist->last_ip_pts = ist->frac_pts.val; + ist->frac_pts.val = tmp; + } + } +#endif + /* if output time reached then transcode raw format, + encode packets and output them */ + if (1 || ist->pts >= 0) + for(i=0;i<nb_ostreams;i++) { + int frame_size; + + ost = ost_table[i]; + if (ost->source_index == ist_index) { + os = output_files[ost->file_index]; + +#if 0 + printf("%d: got pts=%0.3f %0.3f\n", i, + (double)pkt->pts / AV_TIME_BASE, + ((double)ist->pts / AV_TIME_BASE) - + ((double)ost->st->pts.val * ost->st->time_base.num / ost->st->time_base.den)); +#endif + /* set the input output pts pairs */ + //ost->sync_ipts = (double)(ist->pts + input_files_ts_offset[ist->file_index])/ AV_TIME_BASE; + + if (ost->encoding_needed) { + switch(ost->st->codec->codec_type) { + case CODEC_TYPE_VIDEO: + if(!do_video_out(os, ost, ist, &picture, &frame_size)) + return -1; + break; + default: + return -1; + } + } else { + AVFrame avframe; //FIXME/XXX remove this + AVPacket opkt; + av_init_packet(&opkt); + + /* no reencoding needed : output the packet directly */ + /* force the input stream PTS */ + + avcodec_get_frame_defaults(&avframe); + ost->st->codec->coded_frame= &avframe; + avframe.key_frame = pkt->flags & PKT_FLAG_KEY; + + if (ost->st->codec->codec_type == CODEC_TYPE_VIDEO) { + ost->sync_opts++; + } + + opkt.stream_index= ost->index; + if(pkt->pts != (int64_t)AV_NOPTS_VALUE) + opkt.pts= av_rescale_q(av_rescale_q(pkt->pts, ist->st->time_base, AV_TIME_BASE_Q) + input_files_ts_offset[ist->file_index], AV_TIME_BASE_Q, ost->st->time_base); + else + opkt.pts= AV_NOPTS_VALUE; + + { + int64_t dts; + if (pkt->dts == (int64_t)AV_NOPTS_VALUE) + dts = ist->next_pts; + else + dts= av_rescale_q(pkt->dts, ist->st->time_base, AV_TIME_BASE_Q); + opkt.dts= av_rescale_q(dts + input_files_ts_offset[ist->file_index], AV_TIME_BASE_Q, ost->st->time_base); + } + opkt.flags= pkt->flags; + + //FIXME remove the following 2 lines they shall be replaced by the bitstream filters + if(av_parser_change(ist->st->parser, ost->st->codec, &opkt.data, &opkt.size, data_buf, data_size, pkt->flags & PKT_FLAG_KEY)) + opkt.destruct= av_destruct_packet; + + av_interleaved_write_frame(os, &opkt); + ost->st->codec->frame_number++; + ost->frame_number++; + av_free_packet(&opkt); + } + } + } + } + discard_packet: + if (pkt == NULL) { + /* EOF handling */ + + for(i=0;i<nb_ostreams;i++) { + ost = ost_table[i]; + if (ost->source_index == ist_index) { + AVCodecContext *enc= ost->st->codec; + os = output_files[ost->file_index]; + + if(ost->st->codec->codec_type == CODEC_TYPE_VIDEO && (os->oformat->flags & AVFMT_RAWPICTURE)) + continue; + + if (ost->encoding_needed) { + for(;;) { + AVPacket pkt; + av_init_packet(&pkt); + pkt.stream_index= ost->index; + + switch(ost->st->codec->codec_type) { + case CODEC_TYPE_VIDEO: + ret = avcodec_encode_video(enc, bit_buffer, bit_buffer_size, NULL); + if(enc->coded_frame && enc->coded_frame->key_frame) + pkt.flags |= PKT_FLAG_KEY; + break; + default: + ret=-1; + } + + if(ret<=0) + break; + pkt.data= bit_buffer; + pkt.size= ret; + if(enc->coded_frame && enc->coded_frame->pts != (int64_t)AV_NOPTS_VALUE) + pkt.pts= av_rescale_q(enc->coded_frame->pts, enc->time_base, ost->st->time_base); + av_interleaved_write_frame(os, &pkt); + } + } + } + } + } + + return 0; + fail_decode: + return -1; +} + + +/* + * The following code is the main loop of the file converter + */ +static bool av_encode(AVFormatContext **output_files, + unsigned int nb_output_files, + AVFormatContext **input_files, + unsigned int nb_input_files) +{ + unsigned int i, j, k, n, nb_istreams = 0, nb_ostreams = 0; + AVFormatContext *is, *os; + AVCodecContext *codec, *icodec; + AVOutputStream *ost, **ost_table = NULL; + AVInputStream *ist, **ist_table = NULL; + AVInputFile *file_table; + bool ret; + + file_table= (AVInputFile*) av_mallocz(nb_input_files * sizeof(AVInputFile)); + if (!file_table) + goto fail; + + /* input stream init */ + j = 0; + for(i=0;i<nb_input_files;i++) { + is = input_files[i]; + file_table[i].ist_index = j; + file_table[i].nb_streams = is->nb_streams; + j += is->nb_streams; + } + nb_istreams = j; + + ist_table = (AVInputStream**)av_mallocz(nb_istreams * sizeof(AVInputStream *)); + if (!ist_table) + goto fail; + + for(i=0;i<nb_istreams;i++) { + ist = (AVInputStream*)av_mallocz(sizeof(AVInputStream)); + if (!ist) + goto fail; + ist_table[i] = ist; + } + j = 0; + for(i=0;i<nb_input_files;i++) { + is = input_files[i]; + for(k=0;k<is->nb_streams;k++) { + ist = ist_table[j++]; + ist->st = is->streams[k]; + ist->file_index = i; + ist->index = k; + ist->discard = 1; /* the stream is discarded by default + (changed later) */ + + if (ist->st->codec->rate_emu) { + ist->start = av_gettime(); + ist->frame = 0; + } + } + } + + /* output stream init */ + nb_ostreams = 0; + for(i=0;i<nb_output_files;i++) { + os = output_files[i]; + if (!os->nb_streams) { + fprintf(stderr, "Output file does not contain any stream\n"); + return false; + } + nb_ostreams += os->nb_streams; + } + + ost_table = (AVOutputStream**)av_mallocz(sizeof(AVOutputStream *) * nb_ostreams); + if (!ost_table) + goto fail; + for(i=0;i<nb_ostreams;i++) { + ost = (AVOutputStream*)av_mallocz(sizeof(AVOutputStream)); + if (!ost) + goto fail; + ost_table[i] = ost; + } + + n = 0; + for(k=0;k<nb_output_files;k++) { + os = output_files[k]; + for(i=0;i<os->nb_streams;i++) { + int found; + ost = ost_table[n++]; + ost->file_index = k; + ost->index = i; + ost->st = os->streams[i]; + { + /* get corresponding input stream index : we select the first one with the right type */ + found = 0; + for(j=0;j<nb_istreams;j++) { + ist = ist_table[j]; + if (ist->discard && + ist->st->codec->codec_type == ost->st->codec->codec_type) { + ost->source_index = j; + found = 1; + break; + } + } + + if (!found) { + /* try again and reuse existing stream */ + for(j=0;j<nb_istreams;j++) { + ist = ist_table[j]; + if (ist->st->codec->codec_type == ost->st->codec->codec_type) { + ost->source_index = j; + found = 1; + } + } + if (!found) { + fprintf(stderr, "Could not find input stream matching output stream #%d.%d\n", + ost->file_index, ost->index); + return false; + } + } + } + ist = ist_table[ost->source_index]; + ist->discard = 0; + ost->sync_ist = ist; + } + } + + /* for each output stream, we compute the right encoding parameters */ + for(i=0;i<nb_ostreams;i++) { + ost = ost_table[i]; + ist = ist_table[ost->source_index]; + + codec = ost->st->codec; + icodec = ist->st->codec; + + if (ost->st->stream_copy) { + /* if stream_copy is selected, no need to decode or encode */ + codec->codec_id = icodec->codec_id; + codec->codec_type = icodec->codec_type; + if(!codec->codec_tag) codec->codec_tag = icodec->codec_tag; + codec->bit_rate = icodec->bit_rate; + codec->extradata= icodec->extradata; + codec->extradata_size= icodec->extradata_size; + if(av_q2d(icodec->time_base) > av_q2d(ist->st->time_base) && av_q2d(ist->st->time_base) < 1.0/1000) + codec->time_base = icodec->time_base; + else + codec->time_base = ist->st->time_base; + switch(codec->codec_type) { + case CODEC_TYPE_VIDEO: + codec->pix_fmt = icodec->pix_fmt; + codec->width = icodec->width; + codec->height = icodec->height; + codec->has_b_frames = icodec->has_b_frames; + break; + case CODEC_TYPE_SUBTITLE: + break; + default: + return false; + } + } else { + switch(codec->codec_type) { + case CODEC_TYPE_VIDEO: + ost->video_resample = ((codec->width != icodec->width) || + (codec->height != icodec->height) || + (codec->pix_fmt != icodec->pix_fmt)); + if (ost->video_resample) { + avcodec_get_frame_defaults(&ost->pict_tmp); + if( avpicture_alloc( (AVPicture*)&ost->pict_tmp, codec->pix_fmt, + codec->width, codec->height ) ) { + fprintf(stderr, "Cannot allocate temp picture, check pix fmt\n"); + return false; + } + ost->img_resample_ctx = sws_getContext( + icodec->width, + icodec->height, + icodec->pix_fmt, + codec->width, + codec->height, + codec->pix_fmt, + SWS_FAST_BILINEAR, NULL, NULL, NULL); + if (ost->img_resample_ctx == NULL) { + fprintf(stderr, "Cannot get resampling context\n"); + return false; + } + ost->resample_height = icodec->height; + } + ost->encoding_needed = 1; + ist->decoding_needed = 1; + break; + default: + return false; + } + } + if(codec->codec_type == CODEC_TYPE_VIDEO){ + int size= codec->width * codec->height; + bit_buffer_size= FFMAX(bit_buffer_size, 4*size); + } + } + + if (!bit_buffer) + bit_buffer = (uint8_t*)av_malloc(bit_buffer_size); + if (!bit_buffer) + goto fail; + + /* dump the file output parameters - cannot be done before in case + of stream copy */ + for(i=0;i<nb_output_files;i++) { + dump_format(output_files[i], i, output_files[i]->filename, 1); + } + + /* open each encoder */ + for(i=0;i<nb_ostreams;i++) { + ost = ost_table[i]; + if (ost->encoding_needed) { + AVCodec *codec; + codec = avcodec_find_encoder(ost->st->codec->codec_id); + if (!codec) { + fprintf(stderr, "Unsupported codec for output stream #%d.%d\n", + ost->file_index, ost->index); + return false; + } + if (avcodec_open(ost->st->codec, codec) < 0) { + fprintf(stderr, "Error while opening codec for output stream #%d.%d - maybe incorrect parameters such as bit_rate, rate, width or height\n", + ost->file_index, ost->index); + return false; + } + } + } + + /* open each decoder */ + for(i=0;i<nb_istreams;i++) { + ist = ist_table[i]; + if (ist->decoding_needed) { + AVCodec *codec; + codec = avcodec_find_decoder(ist->st->codec->codec_id); + if (!codec) { + fprintf(stderr, "Unsupported codec (id=%d) for input stream #%d.%d\n", + ist->st->codec->codec_id, ist->file_index, ist->index); + return false; + } + if (avcodec_open(ist->st->codec, codec) < 0) { + fprintf(stderr, "Error while opening codec for input stream #%d.%d\n", + ist->file_index, ist->index); + return false; + } + //if (ist->st->codec->codec_type == CODEC_TYPE_VIDEO) + // ist->st->codec->flags |= CODEC_FLAG_REPEAT_FIELD; + } + } + + /* init pts */ + for(i=0;i<nb_istreams;i++) { + ist = ist_table[i]; + is = input_files[ist->file_index]; + ist->pts = 0; + ist->next_pts = av_rescale_q(ist->st->start_time, ist->st->time_base, AV_TIME_BASE_Q); + if(ist->st->start_time == (int64_t)AV_NOPTS_VALUE) + ist->next_pts=0; + if(input_files_ts_offset[ist->file_index]) + ist->next_pts= AV_NOPTS_VALUE; + ist->is_start = 1; + } + + /* open files and write file headers */ + for(i=0;i<nb_output_files;i++) { + os = output_files[i]; + if (av_write_header(os) < 0) { + fprintf(stderr, "Could not write header for output file #%d (incorrect codec parameters ?)\n", i); + ret = false; //AVERROR(EINVAL); + goto fail; + } + } + + //timer_start = av_gettime(); + + while( 1 ) { + unsigned int file_index, ist_index; + AVPacket pkt; + double ipts_min; + double opts_min; + + redo: + ipts_min= 1e100; + opts_min= 1e100; + + /* select the stream that we must read now by looking at the + smallest output pts */ + file_index = (unsigned int)-1; + for(i=0;i<nb_ostreams;i++) { + double ipts, opts; + ost = ost_table[i]; + os = output_files[ost->file_index]; + ist = ist_table[ost->source_index]; + if(ost->st->codec->codec_type == CODEC_TYPE_VIDEO) + opts = ost->sync_opts * av_q2d(ost->st->codec->time_base); + else + opts = ost->st->pts.val * av_q2d(ost->st->time_base); + ipts = (double)ist->pts; + if (!file_table[ist->file_index].eof_reached){ + if(ipts < ipts_min) { + ipts_min = ipts; + if(input_sync ) file_index = ist->file_index; + } + if(opts < opts_min) { + opts_min = opts; + if(!input_sync) file_index = ist->file_index; + } + } + if(ost->frame_number >= max_frames[ost->st->codec->codec_type]){ + file_index= (unsigned int)-1; + break; + } + } + /* if none, if is finished */ + if (file_index == (unsigned int)-1) { + break; + } + + /* read a frame from it and output it in the fifo */ + is = input_files[file_index]; + if (av_read_frame(is, &pkt) < 0) { + file_table[file_index].eof_reached = 1; + if (opt_shortest) break; else continue; // + } + + /* the following test is needed in case new streams appear + dynamically in stream : we ignore them */ + if ((unsigned int)pkt.stream_index >= file_table[file_index].nb_streams) + goto discard_packet; + ist_index = file_table[file_index].ist_index + pkt.stream_index; + ist = ist_table[ist_index]; + if (ist->discard) + goto discard_packet; + +// fprintf(stderr, "next:%"PRId64" dts:%"PRId64" off:%"PRId64" %d\n", ist->next_pts, pkt.dts, input_files_ts_offset[ist->file_index], ist->st->codec->codec_type); + if (pkt.dts != (int64_t)AV_NOPTS_VALUE && ist->next_pts != (int64_t)AV_NOPTS_VALUE) { + int64_t delta= av_rescale_q(pkt.dts, ist->st->time_base, AV_TIME_BASE_Q) - ist->next_pts; + if(FFABS(delta) > 1LL*dts_delta_threshold*AV_TIME_BASE){ + input_files_ts_offset[ist->file_index]-= delta; +// if (verbose > 2) +// fprintf(stderr, "timestamp discontinuity %"PRId64", new offset= %"PRId64"\n", delta, input_files_ts_offset[ist->file_index]); + for(i=0; i<file_table[file_index].nb_streams; i++){ + int index= file_table[file_index].ist_index + i; + ist_table[index]->next_pts += delta; + ist_table[index]->is_start=1; + } + } + } + + //fprintf(stderr,"read #%d.%d size=%d\n", ist->file_index, ist->index, pkt.size); + if (output_packet(ist, ist_index, ost_table, nb_ostreams, &pkt) < 0) { + + if (verbose >= 0) + fprintf(stderr, "Error while decoding stream #%d.%d\n", + ist->file_index, ist->index); + + av_free_packet(&pkt); + goto redo; + } + + discard_packet: + av_free_packet(&pkt); + + } + + /* at the end of stream, we must flush the decoder buffers */ + for(i=0;i<nb_istreams;i++) { + ist = ist_table[i]; + if (ist->decoding_needed) { + output_packet(ist, i, ost_table, nb_ostreams, NULL); + } + } + + /* write the trailer if needed and close file */ + for(i=0;i<nb_output_files;i++) { + os = output_files[i]; + av_write_trailer(os); + } + + /* close each encoder */ + for(i=0;i<nb_ostreams;i++) { + ost = ost_table[i]; + if (ost->encoding_needed) { + av_freep(&ost->st->codec->stats_in); + avcodec_close(ost->st->codec); + } + } + + /* close each decoder */ + for(i=0;i<nb_istreams;i++) { + ist = ist_table[i]; + if (ist->decoding_needed) { + avcodec_close(ist->st->codec); + } + } + + /* finished ! */ + + ret = true; + fail1: + av_freep(&bit_buffer); + av_free(file_table); + + if (ist_table) { + for(i=0;i<nb_istreams;i++) { + ist = ist_table[i]; + av_free(ist); + } + av_free(ist_table); + } + if (ost_table) { + for(i=0;i<nb_ostreams;i++) { + ost = ost_table[i]; + if (ost) { + av_free(ost->pict_tmp.data[0]); + if (ost->video_resample) + sws_freeContext(ost->img_resample_ctx); + av_free(ost); + } + } + av_free(ost_table); + } + return ret; + fail: + ret = false; //AVERROR(ENOMEM); + goto fail1; +} + +void print_error(const char *filename, int err) +{ + switch(err) { + case AVERROR_NUMEXPECTED: + fprintf(stderr, "%s: Incorrect image filename syntax.\n", + filename); + break; + case AVERROR_INVALIDDATA: + fprintf(stderr, "%s: Error while parsing header\n", filename); + break; + case AVERROR_NOFMT: + fprintf(stderr, "%s: Unknown format\n", filename); + break; + case AVERROR_IO: + fprintf(stderr, "%s: I/O error occured\n" + "Usually that means that input file is truncated and/or corrupted.\n", + filename); + break; + case AVERROR_NOMEM: + fprintf(stderr, "%s: memory allocation error occured\n", filename); + break; + default: + fprintf(stderr, "%s: Error while opening file\n", filename); + break; + } +} + +static bool opt_input_file(const char *filename) +{ + AVFormatContext *ic; + AVFormatParameters params, *ap = ¶ms; + int err, ret, rfps, rfps_base; + unsigned int i; + int64_t timestamp; + + /* get default parameters from command line */ + ic = avformat_alloc_context(); + + memset(ap, 0, sizeof(*ap)); + ap->prealloced_context = 1; + ap->time_base.den = frame_rate; + ap->time_base.num = frame_rate_base; + ap->width = frame_width + 0 + 0; + ap->height = frame_height + 0 + 0; + ap->pix_fmt = frame_pix_fmt; + ap->channel = 0; + ap->standard = 0; + ap->video_codec_id = CODEC_ID_NONE; + if(pgmyuv_compatibility_hack) + ap->video_codec_id= CODEC_ID_PGMYUV; + + /* open the input file with generic libav function */ + err = av_open_input_file(&ic, filename, file_iformat, 0, ap); + if (err < 0) { + print_error(filename, err); + return 0; + } + + ic->loop_input = 0; + + /* If not enough info to get the stream parameters, we decode the + first frames to get it. (used in mpeg case for example) */ + ret = av_find_stream_info(ic); + if (ret < 0 && verbose >= 0) { + fprintf(stderr, "%s: could not find codec parameters\n", filename); + return false; + } + + timestamp = 0; + /* add the stream start time */ + if (ic->start_time != (int64_t)AV_NOPTS_VALUE) + timestamp += ic->start_time; + + /* update the current parameters so that they match the one of the input stream */ + for(i=0;i<ic->nb_streams;i++) { + AVCodecContext *enc = ic->streams[i]->codec; + enc->thread_count= 1; + switch(enc->codec_type) { + case CODEC_TYPE_VIDEO: + frame_height = enc->height; + frame_width = enc->width; + frame_aspect_ratio = av_q2d(enc->sample_aspect_ratio) * enc->width / enc->height; + frame_pix_fmt = enc->pix_fmt; + rfps = ic->streams[i]->r_frame_rate.num; + rfps_base = ic->streams[i]->r_frame_rate.den; + if(enc->lowres) enc->flags |= CODEC_FLAG_EMU_EDGE; + + if (enc->time_base.den != rfps || enc->time_base.num != rfps_base) { + + if (verbose >= 0) + fprintf(stderr,"\nSeems stream %d codec frame rate differs from container frame rate: %2.2f (%d/%d) -> %2.2f (%d/%d)\n", + i, (float)enc->time_base.den / enc->time_base.num, enc->time_base.den, enc->time_base.num, + + (float)rfps / rfps_base, rfps, rfps_base); + } + /* update the current frame rate to match the stream frame rate */ + frame_rate = rfps; + frame_rate_base = rfps_base; + + enc->rate_emu = 0; + break; + case CODEC_TYPE_DATA: + break; + case CODEC_TYPE_SUBTITLE: + break; + case CODEC_TYPE_UNKNOWN: + break; + default: + //av_abort(); + return false; + } + } + + input_files[nb_input_files] = ic; + input_files_ts_offset[nb_input_files] = input_ts_offset - timestamp; + /* dump the file content */ + if (verbose >= 0) + dump_format(ic, nb_input_files, filename, 0); + + nb_input_files++; + file_iformat = NULL; + file_oformat = NULL; + return true; +} + +static void check_video_inputs(int *has_video_ptr) +{ + int has_video; + unsigned int i, j; + AVFormatContext *ic; + + has_video = 0; + for(j=0;j<nb_input_files;j++) { + ic = input_files[j]; + for(i=0;i<ic->nb_streams;i++) { + AVCodecContext *enc = ic->streams[i]->codec; + switch(enc->codec_type) { + case CODEC_TYPE_VIDEO: + has_video = 1; + break; + case CODEC_TYPE_DATA: + case CODEC_TYPE_UNKNOWN: + case CODEC_TYPE_SUBTITLE: + break; + default: + //av_abort(); + return; + } + } + } + *has_video_ptr = has_video; +} + + +static bool new_video_stream(AVFormatContext *oc) +{ + AVStream *st; + AVCodecContext *video_enc; + CodecID codec_id; + + st = av_new_stream(oc, oc->nb_streams); + if (!st) { + fprintf(stderr, "Could not alloc stream\n"); + return false; + } + avcodec_get_context_defaults2(st->codec, CODEC_TYPE_VIDEO); + + video_enc = st->codec; + + { + AVCodec *codec; + + codec_id = av_guess_codec(oc->oformat, NULL, oc->filename, NULL, CODEC_TYPE_VIDEO); + + video_enc->codec_id = codec_id; + codec = avcodec_find_encoder(codec_id); + + video_enc->time_base.den = frame_rate; + video_enc->time_base.num = frame_rate_base; +/* if(codec && codec->supported_framerates){ + const AVRational *p= codec->supported_framerates; + AVRational req= (AVRational){frame_rate, frame_rate_base}; + const AVRational *best=NULL; + AVRational best_error= (AVRational){INT_MAX, 1}; + for(; p->den!=0; p++){ + AVRational error= av_sub_q(req, *p); + if(error.num <0) error.num *= -1; + if(av_cmp_q(error, best_error) < 0){ + best_error= error; + best= p; + } + } + video_enc->time_base.den= best->num; + video_enc->time_base.num= best->den; + }*/ + + if((keep_aspect_ratio & 2) == keep_aspect_ratio) { // Nur Höhe wurde definiert + video_enc->width = ((int)((float)frame_height * frame_aspect_ratio)) & 0x7ffffff8; + video_enc->height = frame_height; + } else if((keep_aspect_ratio & 1) == keep_aspect_ratio) { // Nur Weite wurde definiert + video_enc->width = frame_width; + video_enc->height = ((int)((float)frame_width / frame_aspect_ratio)) & 0x7ffffff8; + } else { + video_enc->width = frame_width; + video_enc->height = frame_height; + } + + video_enc->sample_aspect_ratio = av_d2q(frame_aspect_ratio*video_enc->height/video_enc->width, 255); + video_enc->pix_fmt = frame_pix_fmt; + st->sample_aspect_ratio = video_enc->sample_aspect_ratio; + + if(codec && codec->pix_fmts){ + const enum PixelFormat *p= codec->pix_fmts; + for(; *p!=-1; p++){ + if(*p == video_enc->pix_fmt) + break; + } + if(*p == -1) + video_enc->pix_fmt = codec->pix_fmts[0]; + } + +/* if (intra_only) + video_enc->gop_size = 0;*/ + if (same_quality) { + video_enc->flags |= CODEC_FLAG_QSCALE; + st->quality = FF_QP2LAMBDA; + video_enc->global_quality= (int)st->quality; + } + + video_enc->max_qdiff = video_qdiff; + video_enc->rc_eq = video_rc_eq; + video_enc->thread_count = 1; + video_enc->rc_override_count=0; + if (!video_enc->rc_initial_buffer_occupancy) + video_enc->rc_initial_buffer_occupancy = video_enc->rc_buffer_size*3/4; + video_enc->me_threshold= 0; + video_enc->intra_dc_precision= 0; + video_enc->strict_std_compliance = 0; + + video_enc->me_method = me_method; + + } + + return true; +} + +/** + * Copy the string str to buf. If str length is bigger than buf_size - + * 1 then it is clamped to buf_size - 1. + * NOTE: this function does what strncpy should have done to be + * useful. NEVER use strncpy. + * + * @param buf destination buffer + * @param buf_size size of destination buffer + * @param str source string + */ +void pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} + +static bool opt_output_file(const char *filename) +{ + AVFormatContext *oc; + int use_video, input_has_video = 0; + AVFormatParameters params, *ap = ¶ms; + + oc = avformat_alloc_context(); + + if (!file_oformat) { + file_oformat = guess_format(NULL, filename, NULL); + if (!file_oformat) { + fprintf(stderr, "Unable for find a suitable output format for '%s'\n", + filename); + return false; + } + } + + oc->oformat = file_oformat; + pstrcpy(oc->filename, sizeof(oc->filename), filename); + + { + use_video = file_oformat->video_codec != CODEC_ID_NONE; + + /* disable if no corresponding type found and at least one + input file */ + if (nb_input_files > 0) { + check_video_inputs(&input_has_video); + if (!input_has_video) + use_video = 0; + } + + if (use_video) { + if(!new_video_stream(oc)) + return false; + } + + oc->timestamp = 0; + + } + + output_files[nb_output_files++] = oc; + + /* check filename in case of an image number is expected */ + if (oc->oformat->flags & AVFMT_NEEDNUMBER) { + if (!av_filename_number_test(oc->filename)) { + print_error(oc->filename, AVERROR_NUMEXPECTED); + return false; + } + } + + memset(ap, 0, sizeof(*ap)); + if (av_set_parameters(oc, ap) < 0) { + fprintf(stderr, "%s: Invalid encoding parameters\n", + oc->filename); + return false; + } + + oc->preload= (int)(mux_preload*AV_TIME_BASE); + oc->max_delay= (int)(mux_max_delay*AV_TIME_BASE); + oc->loop_output = AVFMT_NOOUTPUTLOOP; + + /* reset some options */ + file_oformat = NULL; + file_iformat = NULL; + return true; +} + + +void ffm_initalize(void) +{ + av_log_set_level(verbose); + av_register_all(); + avctx_opts= avcodec_alloc_context(); +} + +void ffm_deinitalize(void) +{ + //av_free_static(); +} + + + +bool decode (const char* szMPVfile, /* const tPackedList & packed, */ + const char* szTmpMask, + int width, int height) +{ + unsigned int i,j; + nb_input_files = 0; + nb_output_files = 0; + + frame_width = 0; + frame_height = 0; + frame_aspect_ratio = 0; + frame_pix_fmt = PIX_FMT_YUV420P; + frame_rate = 25; + frame_rate_base = 1; + keep_aspect_ratio = 1; + + /* parse options */ + if(!opt_input_file(szMPVfile)) + return false; + + if (width != -1 || height != -1) { + if (width != -1) { + frame_width = width; + keep_aspect_ratio |= 1; + } else { + keep_aspect_ratio &= ~1; + } + + if (height != -1) { + frame_height = height; + keep_aspect_ratio |= 2; + } else { + keep_aspect_ratio &= ~2; + } + } + + if(!opt_output_file (szTmpMask)) + return false; + + /* file converter / grab */ + if (nb_output_files <= 0) { + fprintf(stderr, "Must supply at least one output file\n"); + return false; + } + + if (nb_input_files <= 0) { + fprintf(stderr, "Must supply at least one input file\n"); + return false; + } + + bool bRet = av_encode(output_files, nb_output_files, input_files, nb_input_files); + + /* close files */ + for(i=0;i<nb_output_files;i++) { + /* maybe av_close_output_file ??? */ + AVFormatContext *s = output_files[i]; + + if (!(s->oformat->flags & AVFMT_NOFILE)) + url_fclose(s->pb); + for(j=0;j<s->nb_streams;j++) + av_free(s->streams[j]); + av_free(s); + } + for(i=0;i<nb_input_files;i++) + av_close_input_file(input_files[i]); + + return bRet; +} + diff --git a/contrib/vdr2jpeg/ffm.h b/contrib/vdr2jpeg/ffm.h new file mode 100644 index 0000000..21fab77 --- /dev/null +++ b/contrib/vdr2jpeg/ffm.h @@ -0,0 +1,28 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright (c) 2005-2008 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +extern void ffm_initalize(void); +extern void ffm_deinitalize(void); + +extern bool decode(const char* szMPVfile, + const char* szTmpMask, int width, int height); + + +// Helperclass for proper init/deinit ffmpeg +struct ffminit +{ + ffminit() { + ffm_initalize(); + } + + virtual ~ffminit() { + ffm_deinitalize(); + } +}; diff --git a/contrib/vdr2jpeg/gop.cpp b/contrib/vdr2jpeg/gop.cpp new file mode 100644 index 0000000..56f44e2 --- /dev/null +++ b/contrib/vdr2jpeg/gop.cpp @@ -0,0 +1,751 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2005-2010 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <time.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <algorithm> +#include "ffm.h" +#include "gop.h" +#include "mpegdec.h" + +bool operator >>(std::istream & i, tFrame & x) +{ + i.read((x.u.rawdata), sizeof(x.u.rawdata)); // Read all data in one step + if(i.gcount() == sizeof(x.u.rawdata)) { + return true; + } else { + return false; + } +} + +std::ostream & operator <<(std::ostream & o, const tFrame & x) +{ + o << " I-Frame = " << x.nIFrame; +//#ifdef DEBUG +// o << " Offset = " << x.u.pes.offset; +// o << " File = " <<(int) x.u.pes.number; +// o << " Type = " <<(int) x.u.pes.type; +// o << " Reserved : " << x.u.pes.reserved; +//#endif // DEBUG + return o; +} + + +bool ReadIndexFile(const std::string & szFile, int nIndexVersion, + const std::vector < int >&nFrames, + std::vector < std::pair<tFrame,tFrame> > &nGOP) +{ + if(szFile.empty()) { + std::cerr << "Missing index file" << std::endl; + return false; + } + + if(nFrames.empty()) { + std::cerr << "Missing wanted frames" << std::endl; + return false; + } + + try + { + std::ifstream f(szFile.c_str(), std::ifstream::in | std::ifstream::binary); + + if(f.fail()) { + std::cerr << "Can't open file : " << szFile << std::endl; + return false; + } + std::vector < int >::const_iterator i = nFrames.begin(); + std::vector < int >::const_iterator e = nFrames.end(); + for(; i != e && f.good(); ++i) { + std::pair<tFrame,tFrame> gop; + tFrame x( *i ); + f.seekg(x.nIFrame * 8, std::ifstream::beg); + do + { + if(!f.good()) + { + std::cerr << "Seek behind end of file : "; + std::cerr << szFile; + std::cerr << " at first frame "; + std::cerr << x.nIFrame << std::endl; + return false; + } + if(!(f >> x)) { + std::cerr << "Incomplete struct : "; + std::cerr << szFile; + std::cerr << " at first frame "; + std::cerr << x.nIFrame << std::endl; + return false; + } + if(!x.bIsIFrame(nIndexVersion)) + { + f.seekg(8 * 2 * -1, std::ifstream::cur); + if(x.nIFrame > 0) + --x.nIFrame; + else { + std::cerr << "Missing start struct : "; + std::cerr << szFile; + std::cerr << " at first frame "; + std::cerr << x.nIFrame << std::endl; + return false; + } + } + } + while(!x.bIsIFrame(nIndexVersion)); // loop until found I-Frame + + gop.first = x; //Remember I-Frame + do + { + ++x.nIFrame; + if(!f.good()) + { + std::cerr << "Seek behind end of file : "; + std::cerr << szFile; + std::cerr << " at second frame "; + std::cerr << x.nIFrame << std::endl; + return false; + } + if(!(f >> x)) { +#ifdef DEBUG + std::cerr << "Incomplete struct : "; + std::cerr << szFile; + std::cerr << " at Frame "; + std::cerr << x.nIFrame << std::endl; +#endif + return true; + } + } + while(f.good() && !x.bIsIFrame(nIndexVersion)); // Build IBBP..I -> break on next I-Frame + gop.second = x; + nGOP.push_back(gop); //Remember I-Frame + } + f.close(); + return true; + } + catch(...) { + std::cerr << "Something fail at read " << szFile << std::endl; + return false; + } +} + +bool ReadIndexFileFull(const std::string & szFile, int nIndexVersion, + std::vector < std::pair<tFrame,tFrame> > &nGOP, + bool bIntraOnly, + unsigned int &nFirst, unsigned int nLimit) +{ + if(szFile.empty()) { + std::cerr << "Missing index file" << std::endl; + return false; + } + + try + { + std::ifstream f(szFile.c_str(), std::ifstream::in | std::ifstream::binary); + + if(f.fail()) { + std::cerr << "Can't open file : " << szFile << std:: + endl; + return false; + } + + std::pair<tFrame,tFrame> gop; + tFrame x(nFirst); + f.seekg(nFirst * 8, std::ifstream::beg); + + // Search first I-Frame + while(bIntraOnly && f.good()) { + if(!(f >> x)) { + std::cerr << + "Incomplete struct : "; + std::cerr << szFile; + std::cerr << " at Frame "; + std::cerr << x.nIFrame << std::endl; + return false; + } + if(x.bIsIFrame(nIndexVersion)) { + break; + } + ++x.nFrame; + ++x.nIFrame; + } + // Read all frames + while(f.good()) { + + gop.first = x; //First I-Frame + + do // Build IBBP..I -> break on next I-Frame + { + if(!(f >> x)) { +#ifdef DEBUG + std::cerr << + "Incomplete struct : "; + std::cerr << szFile; + std::cerr << " at Frame "; + std::cerr << x.nIFrame << std::endl; +#endif + return true; + } + ++x.nFrame; + ++x.nIFrame; + if(!bIntraOnly || x.bIsIFrame(nIndexVersion)) { + break; + } + } while(f.good()); + + gop.second = x; //Last I-Frame + +// std::cerr << x << std::endl; + nGOP.push_back(gop); //Remember GOP IBBP..I + if(x.nFrame >= (nFirst + nLimit)) { + nFirst = x.nFrame; + break; + } + } + f.close(); + return true; + } + catch(...) { + std::cerr << "Something fail at read " << szFile << std::endl; + return false; + } +} + +int copy(const char * inn, const char * outn ) +{ + char buffer[8192]; + int count,readed = 0,written = 0; + int in,out; + + in = open(inn,O_RDONLY); + if(in == -1) + return -1; + out = open(outn,O_CREAT|O_TRUNC|O_WRONLY,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if(out == -1) + return -1; + + while(0 < (count = read(in,buffer,sizeof(buffer)))) { + readed += count; + written += write(out,buffer,count); + if(readed != written) + break; + } + return ((readed == written) ? 0 : -1); +} + +bool temppath(const std::string & szOutPath, std::string & szTmpBase ) +{ + unsigned int nT = time(0); + srand(nT); + + std::stringstream so; + so << szOutPath; + if('/' != *szOutPath.rbegin()) + so << '/'; + so << "tmp-vdr2jpeg-"; + so.fill('0'); + so.width(8); + so << std::hex; + so << rand(); + szTmpBase = so.str(); + return true; +} + +void unlinkTmpfiles(const std::string & szTmpBase, bool bJPEG ) +{ + std::stringstream so; + struct stat64 ds; + for(unsigned int i = 1 ; i <= 99 ; ++i) + { + so.str(""); + so << szTmpBase; + so.fill('0'); + so.width(2); + so << i; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szTmp(so.str()); + + if(stat64(szTmp.c_str(), &ds)) { + break; + } + if(-1 == unlink(szTmp.c_str())) { + perror(szTmp.c_str()); + break; + } + } + so.str(""); + so << szTmpBase; + so << ".mpv"; + so << std::ends; + std::string szTmpMVP(so.str()); + if(stat64(szTmpMVP.c_str(), &ds)) { + return; + } + if(-1 == unlink(szTmpMVP.c_str())) { + perror(szTmpMVP.c_str()); + return; + } +} + +bool linkTmpfile(const std::string & szTmp, const std::string & szOutPath, + int nFrame, bool bJPEG) +{ + struct stat64 ds; + std::stringstream so; + + so << szOutPath; + if('/' != *szOutPath.rbegin()) + so << '/'; + so.fill('0'); + so.width(8); + so << nFrame; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szFile(so.str()); + + + if(!stat64(szFile.c_str(), &ds)) { + if(-1 == unlink(szFile.c_str())) { + perror(szFile.c_str()); + return false; + } + } + + int nErr = link(szTmp.c_str(),szFile.c_str()); + if(-1 == nErr && (errno == EXDEV || errno == EPERM )) + { + nErr = copy(szTmp.c_str(),szFile.c_str()); + } + if(0 != nErr || 0 != chmod(szFile.c_str(),S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ) + { + if(errno == EEXIST || errno == EACCES) + perror(szFile.c_str()); + else + perror(szTmp.c_str()); + return false; + } + return true; +} + +bool handleTmpfile(const std::string & szTmpBase, const std::string & szOutPath, + int nFrame, int nFrameBase, bool exact, bool bJPEG, bool bLink, + unsigned int nWidth,unsigned int nHeight) +{ + struct stat64 ds; + + std::string szTmp; + std::stringstream so; + // started from wantet frame, avoid skipped duplicated frames + // look backward + for(unsigned int n = (nFrame - nFrameBase + 1) ; n > 0 ; --n) + { + so.str(""); + so << szTmpBase; + so.fill('0'); + so.width(2); + so << n; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szTmpTry = so.str(); + + if(stat64(szTmpTry.c_str(), &ds)) { + if(exact) { + std::cerr << "Can't find tmp file : " << szTmpTry.c_str() << std::endl; + return false; + } + } + else + { + szTmp = szTmpTry; + break; + } + } + + if(szTmp.length() == 0) + { + // look forward backward + for(unsigned int n = (nFrame - nFrameBase + 1) ; n <= 99 ; ++n) + { + so.str(""); + so << szTmpBase; + so.fill('0'); + so.width(2); + so << n; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szTmpTry = so.str(); + + if(stat64(szTmpTry.c_str(), &ds)) { + if(exact) { + std::cerr << "Can't find tmp file : " << szTmpTry.c_str() << std::endl; + return false; + } + } + else + { + szTmp = szTmpTry; + break; + } + } + } + if(szTmp.length() == 0) + { + std::cerr << "Can't find any tmp file" << std::endl; + return false; + } + if(bLink) { + return linkTmpfile(szTmp,szOutPath, nFrame, bJPEG ); + } + return false; +} + + +bool ReadGOP(const std::string & szFile, int nBegin, int nLength, char *pMem ) +{ + std::ifstream f(szFile.c_str(), + std::ifstream::in | std::ifstream::binary); + if(f.fail()) { + std::cerr << "Can't open file : " << szFile << std::endl; + return false; + } + + if(nBegin < 0) { + std::cerr << "Negativ seek at file : " << szFile << std::endl; + return false; + } + + if(nLength < 0) { + std::cerr << "Negativ length at file : " << szFile << std::endl; + return false; + } + + if(nBegin != 0) { + f.seekg(nBegin, std::ifstream::beg); // Seek to I-Frame + } + + f.read(pMem, nLength); + if (f.gcount() != nLength) { + std::cerr << "Can't read all from file : " << szFile << std::endl; + return false; + } + return true; +} + +bool ReadRecordings(const std::string & szFolder, int nIndexVersion, + const std::string & szOutPath, const std::string & szTempPath, + const std::vector < std::pair<tFrame,tFrame> > &nGOP, int width, int height, + bool exact, bool bJPEG) +{ + if(szFolder.empty()) + { + std::cerr << "Missing VDR-recording folder" << std::endl; + return false; + } + + if(nGOP.empty()) + { + std::cerr << "Missing wanted GOP" << std::endl; + return false; + } + + unsigned int nError = 0; + std::vector < std::pair<tFrame,tFrame> >::const_iterator i = nGOP.begin(); + std::vector < std::pair<tFrame,tFrame> >::const_iterator e = nGOP.end(); + + for(; i != e; ++i) + { +#ifdef DEBUG + std::cout << "Extract GOP : "; + std::cout << i->first << " / " << i->second << std::endl; +#endif //DEBUG + + //Skip empty frames (most at end) + if(i->first.GetOffset(nIndexVersion) == i->second.GetOffset(nIndexVersion)) { + continue; + } + + std::stringstream ss; + std::string szTmpBase; + if(!temppath(szTempPath,szTmpBase)) + { + std::cerr << "Can't get tempname" << std::endl; + return false; + } + + ss << szTmpBase; + ss << ".mpv"; + ss << std::ends; + std::string szTmpMVP(ss.str()); + + try + { + char *pMem = NULL; + off_t nSize = 0; + + // Offset bigger then current 00x.vdr files, goto next + if((i->first.GetOffset(nIndexVersion) >= i->second.GetOffset(nIndexVersion) + || i->first.GetFileNr(nIndexVersion) != i->second.GetFileNr(nIndexVersion)) + && i->second.GetOffset(nIndexVersion) != 0) { + + ss.str(""); + ss << szFolder; + if('/' != *szFolder.rbegin()) + ss << '/'; + i->first.buildFilename(ss,nIndexVersion); // Filenumber 001.vdr ... + ss << std::ends; + std::string szFile(ss.str()); + + struct stat64 ds; + if(stat64(szFile.c_str(), &ds)) { + std::cerr << "Can't find file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + if(i->first.GetOffset(nIndexVersion) >= (ds.st_size)) { + std::cerr << "Offset bigger than current file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + nSize = ds.st_size - i->first.GetOffset(nIndexVersion) + i->second.GetOffset(nIndexVersion); + if(nSize <= 0 + || nSize >=(1 << 24) + || NULL ==(pMem = new char[nSize])) { + std::cerr << "Can't alloc memory for file : " << szFile << std::endl; + std::cerr << "want :" << nSize << " bytes" << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + if(!ReadGOP(szFile, i->first.GetOffset(nIndexVersion), ds.st_size - i->first.GetOffset(nIndexVersion), pMem)) { + delete[]pMem; + return false; + } + + ss.str(""); + ss << szFolder; + if('/' != *szFolder.rbegin()) + ss << '/'; + i->second.buildFilename(ss,nIndexVersion); // Filenumber 002.vdr ... + ss << std::ends; + szFile = ss.str(); + + if(stat64(szFile.c_str(), &ds)) { + std::cerr << "Can't find file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + if(i->second.GetOffset(nIndexVersion) >=(ds.st_size)) { + std::cerr << "Offset bigger than current file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + if(!ReadGOP(szFile, 0, i->second.GetOffset(nIndexVersion), pMem + i->first.GetOffset(nIndexVersion))) { + delete[]pMem; + return false; + } + + } else { + + ss.str(""); + ss << szFolder; + if('/' != *szFolder.rbegin()) + ss << '/'; + i->first.buildFilename(ss,nIndexVersion); // Filenumber 001.vdr ... + ss << std::ends; + std::string szFile(ss.str()); + + struct stat64 ds; + if(stat64(szFile.c_str(), &ds)) + { + std::cerr << "Can't find file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + if(i->first.GetOffset(nIndexVersion) >=(ds.st_size)) { + std::cerr << "Offset bigger than current file : " << szFile << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + // size of GOP + nSize = i->second.GetOffset(nIndexVersion) + ? i->second.GetOffset(nIndexVersion) - i->first.GetOffset(nIndexVersion) + : ds.st_size - i->first.GetOffset(nIndexVersion); + if(nSize <= 0 + || nSize >=(1 << 24) + || NULL ==(pMem = new char[nSize])) + { + std::cerr << "Can't alloc memory for file : " << szFile << std::endl; + std::cerr << "want :" << nSize << " bytes" << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + if(!ReadGOP(szFile, i->first.GetOffset(nIndexVersion), nSize, pMem)) { + delete[]pMem; + return false; + } + } + + +/* +00 00 01 // PES Startcode +e4 // Streamtype +07 fa // Length => 2042 +84 c0 // notused +0b // header_length +*/ + int nMagicFormat = -1; + static const unsigned char pesMagic[3] = { 0x00, 0x00, 0x01 }; + static const unsigned char tsMagic[3] = { 0x47, 0x40, 0x00 }; + if(0 == memcmp(pMem, pesMagic, sizeof(pesMagic))) { + nMagicFormat = 1; + } else if(0 == memcmp(pMem, tsMagic, sizeof(tsMagic))) { + nMagicFormat = 2; + } else { + std::cerr << "No valid magic found, at current pes packet, for file : " << szFolder << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; +#ifdef DEBUG +#if 0 + for(unsigned int b = 0; b < nSize; ++b) { + if(0 == b % 16) { + std::cerr << std::endl; + std::cerr.fill('0'); + std::cerr.width(4); + std::cerr << std::hex << b; + std::cerr << " "; + } +#else + std::cerr << "magic and streamcode are : "; + for(unsigned int b = 0; b < 4 && b < nSize; ++b) { +#endif + std::cerr.fill('0'); + std::cerr.width(2); + std::cerr << std::hex << (unsigned int)(*(pMem + b) & 0xFF); + std::cerr << " "; + } + std::cerr << std::endl; +#endif + delete[]pMem; + return false; + } + + bool bRet = false; + if(nMagicFormat == 1) { + demux_reset(); + bRet = 0 == demuxPES ((uint8_t*)pMem,(uint8_t*)pMem + nSize, szTmpMVP.c_str(), 0); + } else { + demux_reset(); + bRet = 0 == demuxTS ((uint8_t*)pMem,(uint8_t*)pMem + nSize, szTmpMVP.c_str(), 0); + } + if(bRet) + { + std::stringstream so; + so << szTmpBase; + so << "%2d"; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szTmpMask(so.str()); + bRet = decode(szTmpMVP.c_str(), szTmpMask.c_str(), width, height); + if(bRet) + { + if(!handleTmpfile(szTmpBase, szOutPath,i->first.nFrame, + i->first.nIFrame, exact, bJPEG, bJPEG,width, height)) { + std::cerr << "Can't handle file : " << szFolder << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + return false; + } + + // look for more picture from same GOP + std::vector < std::pair<tFrame,tFrame> >::const_iterator j = i + 1; + std::vector < std::pair<tFrame,tFrame> >::const_iterator e = nGOP.end(); + for(;j != e; ++j ,++i) + { + if((i->first.nIFrame != j->first.nIFrame ) + ||(!handleTmpfile(szTmpBase, szOutPath,i->first.nFrame, + i->first.nIFrame, exact, bJPEG, bJPEG, width, height))) + break; + } + } + } + unlinkTmpfiles(szTmpBase, bJPEG); + + delete[]pMem; + if(!bRet) + { + std::cerr << "decode failed, for file : " << szFolder << std::endl; + std::cerr << i->first << " / " << i->second << std::endl; + nError += 1; + } + } + catch(...) + { + std::cerr << "Something fail at read " << szFolder << std::endl; + return false; + } + } + return (nError == 0); +} + +bool decodegop(const std::string & szFile, + const std::string & szOutPath, + int nFrame, int width, int height, + bool exact, bool bJPEG) +{ + struct stat64 ds; + if(stat64(szFile.c_str(), &ds)) { + std::cerr << "Can't stat file : " << szFile << std::endl; + return false; + } + try + { + std::string szTmpBase; + if(!temppath(szOutPath, szTmpBase)) + { + std::cerr << "Can't get tempname" << std::endl; + return false; + } + + std::stringstream so; + so << szTmpBase; + so << "%2d"; + so << (bJPEG ? ".jpg" : ".ppm"); + so << std::ends; + std::string szTmpMask(so.str()); + + bool bRet = decode(szFile.c_str(), szTmpMask.c_str(), width, height) + && handleTmpfile(szTmpBase, szOutPath, exact ? nFrame : 0 , 0, exact, bJPEG, true, width, height ); + + unlinkTmpfiles(szTmpBase, bJPEG); + + return bRet; + + } + catch(...) + { + std::cerr << "Something fail at read " << szFile << std::endl; + } + return false; +} + diff --git a/contrib/vdr2jpeg/gop.h b/contrib/vdr2jpeg/gop.h new file mode 100644 index 0000000..410ae4a --- /dev/null +++ b/contrib/vdr2jpeg/gop.h @@ -0,0 +1,111 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2005-2010 Andreas Brachold + * + * pes demux ported from vdrsync.pl : Copyright(c) 2005 Peter Sebbel + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <iostream> +#include <fstream> +#include <vector> +#include <stdint.h> + +struct tFrame +{ + unsigned int nFrame; + unsigned int nIFrame; + union uIndex + { + struct tIndexPES { + int32_t offset; + unsigned char type; + unsigned char number; + int16_t reserved; + } pes; + struct tIndexTS { + uint64_t offset:40; // up to 1TB per file (not using off_t here - must definitely be exactly 64 bit!) + int reserved:7; // reserved for future use + int independent:1; // marks frames that can be displayed by themselves (for trick modes) + uint16_t number:16; // up to 64K files per recording + } ts; + char rawdata[8]; + } u; + tFrame() + { + }; + tFrame(int n) + { + nFrame = n; + nIFrame = n - 6; + if(n <= 6) + nIFrame = 0; + }; + bool bIsIFrame(int nVersion) const { + if(nVersion == 0) { + return u.pes.type == 1; + } else { + return u.ts.independent != 0; + } + }; + off_t GetOffset(int nVersion) const { + if(nVersion == 0) { + return u.pes.offset; + } else { + return u.ts.offset; + } + }; + unsigned int GetFileNr(int nVersion) const { + if(nVersion == 0) { + return u.pes.number; + } else { + return u.ts.number; + } + }; + void buildFilename(std::ostream & o, int nVersion) const { + o.fill('0'); + o.width(nVersion == 0 ? 3 : 5); + o << GetFileNr(nVersion); // Filenumber 002.vdr ... + if(nVersion == 0) + o << ".vdr"; + else + o << ".ts"; + }; +}; + +bool operator >> (std::istream & i, tFrame & x); +std::ostream & operator <<(std::ostream & o, const tFrame & x); + + +bool ReadIndexFile(const std::string & szFile, int nIndexVersion, + const std::vector < int >&nFrames, std::vector < std::pair<tFrame,tFrame> > &nGOP); + +bool ReadIndexFileFull(const std::string & szFile, int nIndexVersion, + std::vector < std::pair<tFrame,tFrame> > &nGOP, + bool bIntraOnly, + unsigned int &nFirst, unsigned int nLimit); + +int copy(const char * inn, const char * outn ); + +bool temppath(const std::string & szOutPath, std::string & szTmpBase ); + +void unlinkTmpfiles(const std::string & szTmpBase, bool bJPEG ); + +bool handleTmpfile(const std::string & szTmpBase, const std::string & szOutPath, + int nFrame, int nFrameBase, bool exact, bool bJPEG, bool bLink, + unsigned int nWidth,unsigned int nHeight); + +bool ReadRecordings(const std::string & szFolder, int nIndexVersion, + const std::string & szOutPath, const std::string & szTempPath, + const std::vector < std::pair<tFrame,tFrame> > &nGOP, int width, int height, + bool exact, bool bJPEG = true); + +bool decodegop(const std::string & szFile, + const std::string & szOutPath, + int nFrame, int width, int height, + bool exact, bool bJPEG = true); + diff --git a/contrib/vdr2jpeg/mpegdec.cpp b/contrib/vdr2jpeg/mpegdec.cpp new file mode 100644 index 0000000..bd26ebd --- /dev/null +++ b/contrib/vdr2jpeg/mpegdec.cpp @@ -0,0 +1,383 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2010 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + */ +/* + the demuxer is based on the original demuxer from mpeg2dec.c + Copyright (C) 2000-2003 Michel Lespinasse <walken@zoy.org> + Copyright (C) 1999-2000 Aaron Holtzman <aholtzma@ess.engr.uvic.ca> +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <inttypes.h> +#include "mpegdec.h" + + +#define DEMUX_HEADER 0 +#define DEMUX_DATA 1 +#define DEMUX_SKIP 2 + +#define DEMUX_PAYLOAD_START 1 +#define DEMUX_RESET 2 + +static int state = DEMUX_SKIP; +static int state_bytes = 0; +static int demux_pid = 0; +static int demux_track = 0xE0; + +void demux_reset(void) +{ + state = DEMUX_SKIP; + state_bytes = 0; + demux_pid = 0; + demux_track = 0xE0; +} + +static bool store_mpeg2 (unsigned char * current, unsigned char * end, const char* f) +{ + FILE * d; + d = fopen (f, "ab"); + if (!d) { + fprintf (stderr, "Could not open file \"%s\".\n", f); + demux_reset(); + return false; + } + + fwrite (current, 1, end-current, d); + fclose (d); + return true; +} + + +// from mpeg2dec.cpp +int demuxPES (unsigned char * buf, unsigned char * end, const char* f, int flags) +{ + int iRet = 0; + static int mpeg1_skip_table[16] = + { + 0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + /* + * the demuxer keeps some state between calls: + * if "state" = DEMUX_HEADER, then "head_buf" contains the first + * "bytes" bytes from some header. + * if "state" == DEMUX_DATA, then we need to copy "bytes" bytes + * of ES data before the next header. + * if "state" == DEMUX_SKIP, then we need to skip "bytes" bytes + * of data before the next header. + * + * NEEDBYTES makes sure we have the requested number of bytes for a + * header. If we dont, it copies what we have into head_buf and returns, + * so that when we come back with more data we finish decoding this header. + * + * DONEBYTES updates "buf" to point after the header we just parsed. + */ + + + static unsigned char head_buf[264]; + + unsigned char * header; + int bytes; + int len; + +#define NEEDBYTES(x) \ + do { \ + int missing; \ + \ + missing = (x) - bytes; \ + if (missing > 0) { \ + if (header == head_buf) { \ + if (missing <= end - buf) { \ + memcpy (header + bytes, buf, missing); \ + buf += missing; \ + bytes = (x); \ + } else { \ + memcpy (header + bytes, buf, end - buf); \ + state_bytes = bytes + end - buf; \ + return iRet; \ + } \ + } else { \ + memcpy (head_buf, header, bytes); \ + state = DEMUX_HEADER; \ + state_bytes = bytes; \ + return iRet; \ + } \ + } \ + } while (0) + +#define DONEBYTES(x) \ + do { \ + if (header != head_buf) \ + buf = header + (x); \ + } while (0) + + if (flags & DEMUX_RESET) + { + state = DEMUX_SKIP; + state_bytes = 0; + } + if (flags & DEMUX_PAYLOAD_START) + { + state = DEMUX_SKIP; + goto payload_start; + } + switch (state) + { + case DEMUX_HEADER: + if (state_bytes > 0) + { + header = head_buf; + bytes = state_bytes; + goto continue_header; + } + break; + + case DEMUX_DATA: + if (demux_pid || (state_bytes > end - buf)) + { + if(!store_mpeg2 (buf, end, f)) + if(state == DEMUX_SKIP) return 0; + state_bytes -= end - buf; + return iRet; + } + if(!store_mpeg2 (buf, buf + state_bytes, f)) + if(state == DEMUX_SKIP) return 0; + buf += state_bytes; + break; + + case DEMUX_SKIP: + if (demux_pid || (state_bytes > end - buf)) + { + state_bytes -= end - buf; + return iRet; + } + buf += state_bytes; + break; + } + + while (1) + { + if (demux_pid) + { + state = DEMUX_SKIP; + return iRet; + } + + payload_start: + header = buf; + bytes = end - buf; + + continue_header: + NEEDBYTES (4); + if (header[0] || header[1] || (header[2] != 1)) + { + if (demux_pid) + { + state = DEMUX_SKIP; + return iRet; + } + else if (header != head_buf) + { + buf++; + goto payload_start; + } + else + { + header[0] = header[1]; + header[1] = header[2]; + header[2] = header[3]; + bytes = 3; + goto continue_header; + } + } + if (demux_pid) + { + if ((header[3] >= 0xe0) && (header[3] <= 0xef)) + goto pes; + fprintf (stderr, "bad stream id %x\n", header[3]); + return -1; + } + switch (header[3]) + { + case 0xb9: /* program end code */ + /* DONEBYTES (4); */ + /* break; */ + return 1; + + case 0xba: /* pack header */ + NEEDBYTES (12); + if ((header[4] & 0xc0) == 0x40) + { /* mpeg2 */ + NEEDBYTES (14); + len = 14 + (header[13] & 7); + NEEDBYTES (len); + DONEBYTES (len); + /* header points to the mpeg2 pack header */ + } + else if ((header[4] & 0xf0) == 0x20) + { /* mpeg1 */ + DONEBYTES (12); + /* header points to the mpeg1 pack header */ + } + else + { + fprintf (stderr, "weird pack header\n"); + return -1; + } + break; + + case 0xbd: // private stream 1 + case 0xc0: // audio stream 0 + NEEDBYTES (9); + len = header[4]*256+header[5]+6; + NEEDBYTES(len); + DONEBYTES(len); + break; + default: + + if (header[3] == demux_track) + { + pes: + NEEDBYTES (7); + if ((header[6] & 0xc0) == 0x80) + { /* mpeg2 */ + NEEDBYTES (9); + len = 9 + header[8]; + NEEDBYTES (len); + } + else + { /* mpeg1 */ + int len_skip; + len = 7; + while (header[len - 1] == 0xff) + { + len++; + NEEDBYTES (len); + if (len == 23) + { + fprintf (stderr, "too much stuffing\n"); + break; + } + } + if ((header[len - 1] & 0xc0) == 0x40) + { + len += 2; + NEEDBYTES (len); + } + len_skip = len; + len += mpeg1_skip_table[header[len - 1] >> 4]; + NEEDBYTES (len); + } + DONEBYTES (len); + bytes = 6 + (header[4] << 8) + header[5] - len; + if (demux_pid || (bytes > end - buf)) + { + if(!store_mpeg2 (buf, end, f)) + if(state == DEMUX_SKIP) return 0; + state = DEMUX_DATA; + state_bytes = bytes - (end - buf); + return 0; + } + else if (bytes > 0) + { + if(!store_mpeg2 (buf, buf + bytes, f)) + if(state == DEMUX_SKIP) return 0; + buf += bytes; + } + } + else if (header[3] < 0xb9) + { + fprintf (stderr, "looks like a video stream, not system stream\n"); + return -1; + } + else + { + //fprintf(stderr, "header[3] is %02X\n",header[3]); + NEEDBYTES (6); + DONEBYTES (6); + bytes = (header[4] << 8) + header[5]; + //skip: + if (bytes > end - buf) + { + state = DEMUX_SKIP; + state_bytes = bytes - (end - buf); + return iRet; + } + buf += bytes; + } + } + } + + return iRet; +} + +int demuxTS(unsigned char * buffer, unsigned char * end, const char* f, int flags ) +{ + unsigned char * buf; + unsigned char * nextbuf; + unsigned char * data; + int pid; + buf = buffer; + int demuxflag = flags; + int iRet = -1; + + int r = end - buf; + int i = 0; + while( i < r ) + { + if (buf[i] != 0x47) + { + fprintf (stderr, "bad sync byte\n"); + i++; + continue; + } + demux_pid = ((buf[i+1] << 8) + buf[i+2]) & 0x1fff; + i+=188; + } + + for (; (nextbuf = buf + 188) <= end; buf = nextbuf) + { + if (*buf != 0x47) + { + fprintf (stderr, "bad sync byte\n"); + nextbuf = buf + 1; + continue; + } + pid = ((buf[1] << 8) + buf[2]) & 0x1fff; + if (pid != demux_pid) + continue; + data = buf + 4; + if (buf[3] & 0x20) + { // buf contains an adaptation field + data = buf + 5 + buf[4]; + if (data > nextbuf) + continue; + } + if (buf[3] & 0x10) + { + if( (demuxflag & DEMUX_RESET) == DEMUX_RESET ) + { + if( buf[1] & 0x40 ) + { + iRet = demuxPES (data, nextbuf, f, DEMUX_PAYLOAD_START|demuxflag); + if(state == DEMUX_SKIP) return 0; + demuxflag = 0; + } + } + else + { + iRet = demuxPES (data, nextbuf, f, (buf[1] & 0x40) ? DEMUX_PAYLOAD_START|demuxflag : demuxflag); + if(state == DEMUX_SKIP) return 0; + demuxflag = 0; + } + } + } + return iRet; +} diff --git a/contrib/vdr2jpeg/mpegdec.h b/contrib/vdr2jpeg/mpegdec.h new file mode 100644 index 0000000..b28c32d --- /dev/null +++ b/contrib/vdr2jpeg/mpegdec.h @@ -0,0 +1,17 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2010 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + */ + +#ifndef __MPEGDEC_H__ +#define __MPEGDEC_H__ + +void demux_reset(void); +int demuxPES (unsigned char * buf, unsigned char * end, const char* szFileName, int flags); +int demuxTS(unsigned char * buffer, unsigned char * end, const char* szFileName, int flags=0 ); + +#endif diff --git a/contrib/vdr2jpeg/tools.cpp b/contrib/vdr2jpeg/tools.cpp new file mode 100644 index 0000000..9e73352 --- /dev/null +++ b/contrib/vdr2jpeg/tools.cpp @@ -0,0 +1,41 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2007-2008 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <string.h> +#include "tools.h" + +int option(int argc, char *argv[], const char opt, bool bParam, + std::string & param, int n) +{ + for(int i = n; i < argc; ++i) + { + if(argv[i] && strlen(argv[i]) > 1) + { + switch(*(argv[i] + 0)) + { + case '-': + { + if(*(argv[i] + 1) == opt) + { + if(!bParam) + return i + 1; + if(i + 1 < argc) + { + param = argv[i + 1]; + return i + 2; + } + } + } + } + } + } + return -1; +} + diff --git a/contrib/vdr2jpeg/tools.h b/contrib/vdr2jpeg/tools.h new file mode 100644 index 0000000..609362e --- /dev/null +++ b/contrib/vdr2jpeg/tools.h @@ -0,0 +1,18 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2007-2010 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <string> + +int option(int argc, char *argv[], const char opt, bool bParam, + std::string & param, int n = 1); + + + + diff --git a/contrib/vdr2jpeg/vdr2jpeg.cpp b/contrib/vdr2jpeg/vdr2jpeg.cpp new file mode 100644 index 0000000..f8f7f48 --- /dev/null +++ b/contrib/vdr2jpeg/vdr2jpeg.cpp @@ -0,0 +1,230 @@ +/* + * Simple program to grab images from VDR Recording + * + * Copyright(c) 2005-2010 Andreas Brachold + * + * This code is distributed under the terms and conditions of the + * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <iostream> +#include <fstream> +#include <sstream> +#include <vector> +#include <algorithm> +#include "tools.h" +#include "ffm.h" +#include "gop.h" + +static const char *VERSION = "0.1.9"; + +void help(int argc, char *argv[]) +{ + std::cerr << "Usage: " << argv[0] << " (" << VERSION << ")" << std::endl; + std::cerr << " -r recordings : VDR recording folder" << std::endl; + std::cerr << " -f frame : wanted frame (resolution at PAL - 1/25s)" << std::endl; + std::cerr << " -o directory : output folder" << std::endl; + std::cerr << " -t directory : temporary folder" << std::endl; + std::cerr << " -x 160 : scaled width of output image" << std::endl; + std::cerr << " -y 120 : scaled height of output image" << std::endl; + std::cerr << " -e : exact frame grab, instead of duplicate frame" << std::endl; + std::cerr << " -s 500 : frame range, started at wanted frame (e.g 500 : 20s)" << std::endl; + std::cerr << " -i 25 : space beetween frames at selected range (resolution at PAL - 1/25s)" << std::endl; + std::cerr << " -c 5 : number of extracted frames of an recording or within selected range" << std::endl; +#ifdef DEBUG + std::cerr << " -g gobfile : reread demuxed gobfile" << std::endl; +#endif + std::cerr << std::endl << std::endl; +} + +int main(int argc, char *argv[]) +{ + int width = -1; + int height = -1; + bool exact = false; + std::string szGOP, szFolder, szOutPath, szTempPath, s; + + + if(-1 != option(argc, argv, 'x', true, s)) + { + width =((atoi(s.c_str()) >> 1) << 1); // Only /2 + } + if(-1 != option(argc, argv, 'y', true, s)) + { + height =((atoi(s.c_str()) >> 1) << 1); + } + if(-1 != option(argc, argv, 'e', false, s)) + { + exact = true; + } + + szOutPath = ("."); + option(argc, argv, 'o', true, szOutPath); + szTempPath = szOutPath; + option(argc, argv, 't', true, szTempPath); + + std::vector < int >nFrame; + int n = 0; + std::string f; + + while(-1 !=(n = option(argc, argv, 'f', true, f, n))) { + int frame = atoi(f.c_str()); + if(frame < 0) { + std::cerr << "ignore negative frame" << std::endl << std::endl; + } else { + nFrame.push_back(frame); + } + } + + if(!nFrame.empty()) { + std::sort(nFrame.begin(), nFrame.end()); + } + + if(-1 == option(argc, argv, 'r', true, szFolder)) { + std::cerr << "missing vdr recording folder" << std::endl << std::endl; + help(argc, argv); + return 1; + } + + static const char* files[] = { "index.vdr", "index" }; + + struct stat64 ds; + std::string szIndex; + int nIndexVersion = -1; + for(unsigned int l = 0; l < 2; ++l) { + std::stringstream ss; + ss << szFolder; + if('/' != *szFolder.rbegin()) + ss << '/'; + ss << files[l]; + ss << std::ends; + szIndex = ss.str(); + + if(!stat64(szIndex.c_str(), &ds)) { + nIndexVersion = l; + break; + } + } + if(-1 == nIndexVersion) { + std::cerr << "Can't find index file at " << szFolder << std::endl; + return 1; + } + + int nTotalFrames = (ds.st_size / 8); + if(nTotalFrames <= 0) { + std::cerr << "Empty index file : " << szIndex << std::endl; + return 1; + } + + if(-1 != option(argc, argv, 's', true, s)) { + + int range = atoi(s.c_str()); + if(range <= 0) { + std::cerr << "none or negative frame range" << std::endl << std::endl; + return 1; + } + + int inter = 25; + if(-1 != option(argc, argv, 'i', true, s)) { + inter = atoi(s.c_str()); + if(inter <= 0) { + std::cerr << "none or negative frame space" << std::endl << std::endl; + return 1; + } + } + + + if(-1 != option(argc, argv, 'c', true, s)) { + int count = atoi(s.c_str()); + if(count <= 0) { + std::cerr << "none or negative frame count" << std::endl << std::endl; + return 1; + } + inter = range / count; + if(inter<=0) + { + std::cerr << "count bigger then selected frame range, limit interval to 1" << std::endl << std::endl; + inter = 1; + } + } + + int nLastFrame = 0; + if(!nFrame.empty()) + nLastFrame = *nFrame.rbegin(); + + int begin = nLastFrame; + int end = nLastFrame + range; + + if(end > nTotalFrames) { + end = nTotalFrames; + std::cerr << "wanted end frame from selected range, bigger then available frames. Limit end frame to " << end << std::endl << std::endl; + } + + for(int i = begin; i < end; i += inter) { + nFrame.push_back(i); + } + } else { + if(-1 != option(argc, argv, 'c', true, s)) { + int count = atoi(s.c_str()); + if(count <= 0) { + std::cerr << "none or negative frame count" << std::endl << std::endl; + return 1; + } + int nLastFrame = 0; + if(!nFrame.empty()) + nLastFrame = *nFrame.rbegin(); + + int begin = nLastFrame; + int inter = (nTotalFrames - nLastFrame) / count; + if(inter<=0) + { + std::cerr << "count bigger then frame selected range, limit interval to 1" << std::endl << std::endl; + inter = 1; + } + + for(int i = begin + (inter/2); i < nTotalFrames; i += inter) { + nFrame.push_back(i); + } + } + } + + if(nFrame.empty()) { + std::cerr << "none frame defined, use first frame" << std::endl << std::endl; + nFrame.push_back(0); + } + +#ifdef DEBUG + std::cout << "Want Frames : "; + std::vector < int >::const_iterator i = nFrame.begin(); + std::vector < int >::const_iterator e = nFrame.end(); + for(; i != e; ++i) { + std::cout << (*i) << " "; + } + std::cout << std::endl; + + if(-1 != option(argc, argv, 'g', true, szGOP)) { + ffminit x; + std::vector < int >::const_iterator i = nFrame.begin(); + std::vector < int >::const_iterator e = nFrame.end(); + for(; i != e; ++i) + if(!decodegop(szGOP,szOutPath,*i, width, height,exact)); + return 1; + return 0; // Success + } +#endif //DEBUG + std::vector < std::pair<tFrame,tFrame> > nGOP; + if(ReadIndexFile(szIndex, nIndexVersion, nFrame, nGOP)) { + ffminit x; + if(ReadRecordings(szFolder, nIndexVersion, + szOutPath, szTempPath, + nGOP, width, height, exact, true)) { + return 0; // Success + } + } + return 1; +} |
