summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranbr <vdr07@deltab.de>2011-10-06 17:52:03 +0200
committeranbr <vdr07@deltab.de>2011-10-06 17:52:03 +0200
commit3fc884edbb4a9d6cb6b4c5e554895302377b325b (patch)
treed4bcff6cafc0916de074a0a27e0c8d92ff2adad6
parenta7c0d42f96dd548fc808ac9d9963e9152b46856d (diff)
parentf279ccbfdfcaa4e7301b56ade829cb974aeb5636 (diff)
downloadxxv-3fc884edbb4a9d6cb6b4c5e554895302377b325b.tar.gz
xxv-3fc884edbb4a9d6cb6b4c5e554895302377b325b.tar.bz2
Merge branch 'vdr2jpeg'
-rw-r--r--contrib/vdr2jpeg/COPYING339
-rw-r--r--contrib/vdr2jpeg/HISTORY56
-rw-r--r--contrib/vdr2jpeg/LIESMICH60
-rw-r--r--contrib/vdr2jpeg/Makefile135
-rw-r--r--contrib/vdr2jpeg/README59
-rw-r--r--contrib/vdr2jpeg/ffm.cpp1347
-rw-r--r--contrib/vdr2jpeg/ffm.h28
-rw-r--r--contrib/vdr2jpeg/gop.cpp751
-rw-r--r--contrib/vdr2jpeg/gop.h111
-rw-r--r--contrib/vdr2jpeg/mpegdec.cpp383
-rw-r--r--contrib/vdr2jpeg/mpegdec.h17
-rw-r--r--contrib/vdr2jpeg/tools.cpp41
-rw-r--r--contrib/vdr2jpeg/tools.h18
-rw-r--r--contrib/vdr2jpeg/vdr2jpeg.cpp230
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 = &params;
+ 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 = &params;
+
+ 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;
+}