First stab at porting mplex
authorWim Taymans <wim.taymans@gmail.com>
Thu, 24 Oct 2002 22:37:51 +0000 (22:37 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Thu, 24 Oct 2002 22:37:51 +0000 (22:37 +0000)
Original commit message from CVS:
First stab at porting mplex

46 files changed:
ext/mplex/.gitignore [new file with mode: 0644]
ext/mplex/COPYING [new file with mode: 0644]
ext/mplex/INSTRUCT [new file with mode: 0644]
ext/mplex/Makefile.am [new file with mode: 0644]
ext/mplex/README [new file with mode: 0644]
ext/mplex/TODO [new file with mode: 0644]
ext/mplex/ac3strm_in.cc [new file with mode: 0644]
ext/mplex/audiostrm.hh [new file with mode: 0644]
ext/mplex/audiostrm_out.cc [new file with mode: 0644]
ext/mplex/aunit.hh [new file with mode: 0644]
ext/mplex/bits.cc [new file with mode: 0644]
ext/mplex/bits.hh [new file with mode: 0644]
ext/mplex/buffer.cc [new file with mode: 0644]
ext/mplex/buffer.hh [new file with mode: 0644]
ext/mplex/fastintfns.h [new file with mode: 0644]
ext/mplex/format_codes.h [new file with mode: 0644]
ext/mplex/gstmplex.cc [new file with mode: 0644]
ext/mplex/gstmplex.h [new file with mode: 0644]
ext/mplex/inputstrm.cc [new file with mode: 0644]
ext/mplex/inputstrm.hh [new file with mode: 0644]
ext/mplex/interact.cc [new file with mode: 0644]
ext/mplex/lpcmstrm_in.cc [new file with mode: 0644]
ext/mplex/mjpeg_logging.cc [new file with mode: 0644]
ext/mplex/mjpeg_logging.h [new file with mode: 0644]
ext/mplex/mjpeg_types.h [new file with mode: 0644]
ext/mplex/mpastrm_in.cc [new file with mode: 0644]
ext/mplex/mpegconsts.cc [new file with mode: 0644]
ext/mplex/mpegconsts.h [new file with mode: 0644]
ext/mplex/mplexconsts.hh [new file with mode: 0644]
ext/mplex/multplex.cc [new file with mode: 0644]
ext/mplex/outputstream.hh [new file with mode: 0644]
ext/mplex/padstrm.cc [new file with mode: 0644]
ext/mplex/padstrm.hh [new file with mode: 0644]
ext/mplex/stillsstream.cc [new file with mode: 0644]
ext/mplex/stillsstream.hh [new file with mode: 0644]
ext/mplex/systems.cc [new file with mode: 0644]
ext/mplex/systems.hh [new file with mode: 0644]
ext/mplex/vector.cc [new file with mode: 0644]
ext/mplex/vector.hh [new file with mode: 0644]
ext/mplex/videostrm.hh [new file with mode: 0644]
ext/mplex/videostrm_in.cc [new file with mode: 0644]
ext/mplex/videostrm_out.cc [new file with mode: 0644]
ext/mplex/yuv4mpeg.cc [new file with mode: 0644]
ext/mplex/yuv4mpeg.h [new file with mode: 0644]
ext/mplex/yuv4mpeg_intern.h [new file with mode: 0644]
ext/mplex/yuv4mpeg_ratio.cc [new file with mode: 0644]

diff --git a/ext/mplex/.gitignore b/ext/mplex/.gitignore
new file mode 100644 (file)
index 0000000..684bfe5
--- /dev/null
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+mplex
+.deps
+.libs
+
diff --git a/ext/mplex/COPYING b/ext/mplex/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+       Appendix: 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) 19yy  <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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ext/mplex/INSTRUCT b/ext/mplex/INSTRUCT
new file mode 100644 (file)
index 0000000..e75d4cf
--- /dev/null
@@ -0,0 +1,67 @@
+//////////////////////////////////////////////////////////////////////////
+//                                                                     //
+//     INSTRUCTIONS FOR MPLEX - THE MPEG1/SYSTEMS MULTIPLEXER          //
+//                                                                     //
+//////////////////////////////////////////////////////////////////////////
+
+
+Please note that I do not have a comprehensive instruction manual for this
+release. I suggest you try the program out with some default values and
+learn something more about ISO/IEC 11172-1 (aka MPEG1/Systems).
+
+For those of you that can read *German*, you can download a postscript
+paper discussing implementation and problems of this software, with
+introductions to MPEG1/Audio, MPEG1/Video and MPEG1/Systems.
+You should find the paper with the same distribution you got this
+program from.
+
+If not, you should find the postscript version of this 40-page paper
+on
+
+ftp.informatik.tu-muenchen.de in /pub/comp/graphics/mpeg/mplex
+
+(121822 bytes, Jun 30 , 1994 , mpeg_systems_paper_0.99.ps.gz)
+
+If you have any questions you cannot figure out by running the
+program, feel free to ask me.
+
+--------------------------------------------------------------------------
+
+One more thing that might save me many emails:
+
+when asked about the startup packet delay, try something like
+half the video buffer size divided by your sector size. Say you
+have a 40 kByte video buffer and a 2324 Byte Sector size, then
+a startup delay of 8 sectors will work just fine.
+
+What does the above parameter mean?
+
+Normally, the Decoding/Presentation Time Stamp of the first access
+unit is set to the clock value that will happen exactly after the last
+packet containig data from this first unit arrives into the system
+target decoder. This works fine if the video/audio streams are of
+*very perfectly constant* or the packet size are *very* small
+(ideally: the size of one access unit, that would mean variable
+packet length).
+Anyway: this parameter allows you to say that the System Target
+Decoder should start decoding the first access unit after he
+gets (startup_packet_delay + size_of_first_access_units[av])
+packets of data.
+This guarantees that the buffers are conveniently filled up.
+Note that both the video stream offset and audio stream offset (ms)
+add up even more bytes to this startup delay, but you can
+tell conveniently that audio should start so many ms after video,
+for example.
+
+Sorry for no further doc, enjoy multiplexing A/V :)
+
+Christoph.
+
+moar@heaven.zfe.siemens.de
++---------------------------------------+--------------------------------+
+| http://www.informatik.tu-muenchen.de/ |                 Christoph Moar |
+| cgi-bin/nph-gateway/hphalle6/~moar/   |                Kaulbachstr.29a |
+| index.html                            |                   80539 Munich |
+| email:moar@informatik.tu-muenchen.de  |   voice: ++49 - 89 -  23862874 |
++---------------------------------------+--------------------------------+
+
diff --git a/ext/mplex/Makefile.am b/ext/mplex/Makefile.am
new file mode 100644 (file)
index 0000000..c2d672c
--- /dev/null
@@ -0,0 +1,38 @@
+plugindir = $(libdir)/gst
+
+CC = g++
+
+EXTRA_DIST = \
+       README          INSTRUCT        TODO \
+       .cvsignore
+
+MAINTAINERCLEANFILES = Makefile.in
+
+gstmplex_SOURCES = \
+       bits.cc                 bits.hh \
+       buffer.cc               buffer.hh \
+       videostrm.hh    audiostrm.hh    padstrm.hh \
+       videostrm_in.cc  padstrm.cc \
+       lpcmstrm_in.cc mpastrm_in.cc    ac3strm_in.cc \
+       videostrm_out.cc        audiostrm_out.cc        \
+       multplex.cc     outputstream.hh \
+       systems.cc              systems.hh \
+       vector.cc       vector.hh       mplexconsts.hh \
+       inputstrm.cc inputstrm.hh aunit.hh \
+       stillsstream.cc stillsstream.hh \
+       mjpeg_logging.cc mjpeg_logging.h \
+       yuv4mpeg.cc yuv4mpeg_ratio.cc yuv4mpeg.h \
+       mpegconsts.cc mpegconsts.h
+
+plugin_LTLIBRARIES = libgstmplex.la
+
+libgstmplex_la_SOURCES = gstmplex.cc \
+                        $(gstmplex_SOURCES)
+                           
+libgstmplex_la_CXXFLAGS = $(GST_CFLAGS) 
+libgstmplex_la_LIBADD = -lstdc++ $(LIBM_LIBS)
+libgstmplex_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = gstmplex.h
+
+
diff --git a/ext/mplex/README b/ext/mplex/README
new file mode 100644 (file)
index 0000000..c060c1f
--- /dev/null
@@ -0,0 +1,58 @@
+
+*  mplex-2 - MPEG1/2 SYSTEMS/PROGRAM stream multiplexer
+*
+* Orginally based on mplex
+*  Copyright (C) 1994 1995 Christoph Moar 
+* Reengineered version in C++
+*  Copyright (C) 2000,2001, 2002 Andrew Stevens
+*
+*  as@comlab.ox.ac.uk
+               (Andrew Stevens)
+*  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 in the file COPYING 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+
+MODIFICATIONS TO ORIGINAL
+
+July 2000 Andrew Stevens
+        Trivial modifications to support catenated audio stremas and
+         non-intractive use.
+August 2000 Andrew Stevens
+       Modifed to support multiplexing of variable bit-rate
+       video streams. Seems to work well.
+August 2000 Andrew Stevens
+       Removed use of temporary files for storing stream data.
+       Increased performance through the use of a more efficient bitstream 
+       library.
+       Eliminated arithmetic overflow errors for large streams.
+       Initial support for multiplexing variable bit-rate streams.
+
+Novermber 2000 Andrew Stevens
+       Clean up code to suit modern compilers with 64-bit int support.
+       Cleaned up packet size calculations to make the code more flexible.
+       Initial MPEG-2 support
+       Support for splitting output streams (not yet completely implemented)
+       Support for multiplexing for VCD.
+
+Jan 2001-
+       
+       Reengineered in C++
+       Support for SVCD.
+       Support for basic DVD
+       VBR Audio as well as video.
+       Clean stream splitting support.
+       Class structure to simplify adding new stream types
+
+       Encapsulation and modularistion
diff --git a/ext/mplex/TODO b/ext/mplex/TODO
new file mode 100644 (file)
index 0000000..8b39301
--- /dev/null
@@ -0,0 +1,41 @@
+TODO
+
+- Check if video and MPEG audio streams have the same eof bug as I found
+in AC3 audio.
+
+- Need to add general facility for enforcing max STD buffer delay for audio
+  and for warning if constraints for particular formats are exceeded.
+
+- Make VBR more efficient (a skip for long periods where no sector is emitted).
+
+
+- Complete tidying up the systems.cc structure.   Non-duplication of the
+       header generation stuff would be neat if it can be managed...
+
+
+- Add checking for changed sequence parameters in mid-sequence sequence headers.
+
+
+- Currently the VCD HR Stills muxing stuff assumes *all* HR stills
+  are the same size which is given in the initial vbv_buffer_size...
+       This will work with mpeg2enc (which does this) but will fail fail fail
+       with other streams.
+
+- Rebuild initial delay / sequence splitting DTS adjustment stuff so
+       different streams can have different starting delays based on
+       *stream* parameters.  I.e. delay should be delegated to the elementary
+       streams with only a sector_prefix offset set centrally.
+
+- Tidy code so Elementary streams handle their mux parameter initialisation
+       from cmd-line parameters *not* the output stream.
+
+
+
+
+Eventually:
+
+- Full SVCD (MPEG audio extension) support.
+
+- DVD muxing and generation of info for .IFO's etc.
+
+
diff --git a/ext/mplex/ac3strm_in.cc b/ext/mplex/ac3strm_in.cc
new file mode 100644 (file)
index 0000000..5dfc22d
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ *  ac3strm_in.c: AC3 Audio strem class members handling scanning and
+ *  buffering raw input stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *  Copyright (C) 2000,2001 Brent Byeler for original header-structure
+ *                          parsing code.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "audiostrm.hh"
+#include "outputstream.hh"
+
+
+
+#define AC3_SYNCWORD            0x0b77
+#define AC3_PACKET_SAMPLES      1536
+
+/// table for the available AC3 bitrates
+static const unsigned int ac3_bitrate_index[32] = 
+{ 
+  32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192,
+  224, 256, 320, 384, 448, 512, 576, 640,
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const unsigned int ac3_frame_size[3][32] = 
+{
+  {64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,
+   448, 512, 640, 768, 896, 1024, 1152, 1280,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+  {69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417,
+   487, 557, 696, 835, 975, 1114, 1253, 1393,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+  {96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576,
+   672, 768, 960, 1152, 1344, 1536, 1728, 1920,
+   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/// table for the available AC3 frequencies
+static const unsigned int ac3_frequency[4] = { 48000, 44100, 32000, 0 };
+
+
+AC3Stream::AC3Stream (IBitStream & ibs, OutputStream & into):
+AudioStream (ibs, into)
+{
+}
+
+bool
+AC3Stream::Probe (IBitStream & bs)
+{
+  return bs.getbits (16) == AC3_SYNCWORD;
+}
+
+
+/*************************************************************************
+ *
+ * Reads initial stream parameters and displays feedback banner to users
+ * @param stream_num AC3 substream ID
+ *************************************************************************/
+
+
+void
+AC3Stream::Init (const int stream_num)
+{
+  unsigned int framesize_code;
+
+  this->stream_num = stream_num;
+
+  MuxStream::Init (PRIVATE_STR_1, 1,   // Buffer scale
+                  default_buffer_size,
+                  muxinto.vcd_zero_stuffing,
+                  muxinto.buffers_in_audio, muxinto.always_buffers_in_audio);
+  mjpeg_info ("Scanning for header info: AC3 Audio stream %02x", stream_num);
+
+  InitAUbuffer ();
+  AU_start = bs.bitcount ();
+  if (bs.getbits (16) == AC3_SYNCWORD) {
+    num_syncword++;
+    bs.getbits (16);           // CRC field
+    frequency = bs.getbits (2);        // Sample rate code
+    framesize_code = bs.getbits (6);   // Frame size code
+    framesize = ac3_frame_size[frequency][framesize_code >> 1];
+    framesize = (framesize_code & 1) && frequency == 1 ? (framesize + 1) << 1 : (framesize << 1);
+
+
+    size_frames[0] = framesize;
+    size_frames[1] = framesize;
+    num_frames[0]++;
+    access_unit.start = AU_start;
+    access_unit.length = framesize;
+    bit_rate = ac3_bitrate_index[framesize_code >> 1];
+    samples_per_second = ac3_frequency[frequency];
+
+
+    /* Presentation time-stamping  */
+    access_unit.PTS = static_cast < clockticks > (decoding_order) *
+      static_cast < clockticks > (AC3_PACKET_SAMPLES) *
+      static_cast < clockticks > (CLOCKS) / samples_per_second;
+    access_unit.DTS = access_unit.PTS;
+    access_unit.dorder = decoding_order;
+    ++decoding_order;
+    aunits.append (access_unit);
+
+  } else {
+    mjpeg_error ("Invalid AC3 Audio stream header.");
+    exit (1);
+  }
+
+  OutputHdrInfo ();
+}
+
+/// @returns the current bitrate
+unsigned int
+AC3Stream::NominalBitRate ()
+{
+  return bit_rate;
+}
+
+/// Prefills the internal buffer for output multiplexing.
+/// @param frames_to_buffer the number of audio frames to read ahead
+void
+AC3Stream::FillAUbuffer (unsigned int frames_to_buffer)
+{
+  unsigned int framesize_code;
+
+  last_buffered_AU += frames_to_buffer;
+  mjpeg_debug ("Scanning %d MPEG audio frames to frame %d", frames_to_buffer, last_buffered_AU);
+
+  static int header_skip = 5;  // Initially skipped past  5 bytes of header 
+  int skip;
+  bool bad_last_frame = false;
+
+  while (!bs.eos () &&
+        decoding_order < last_buffered_AU) {
+    skip = access_unit.length - header_skip;
+    if (skip & 0x1)
+      bs.getbits (8);
+    if (skip & 0x2)
+      bs.getbits (16);
+    skip = skip >> 2;
+
+    for (int i = 0; i < skip; i++) {
+      bs.getbits (32);
+    }
+
+    prev_offset = AU_start;
+    AU_start = bs.bitcount ();
+    if (AU_start - prev_offset != access_unit.length * 8) {
+      bad_last_frame = true;
+      break;
+    }
+
+    /* Check we have reached the end of have  another catenated 
+       stream to process before finishing ... */
+    if ((syncword = bs.getbits (16)) != AC3_SYNCWORD) {
+      if (!bs.eos ()) {
+       mjpeg_error_exit1 ("Can't find next AC3 frame - broken bit-stream?");
+      }
+      break;
+    }
+
+    bs.getbits (16);           // CRC field
+    bs.getbits (2);            // Sample rate code TOOD: check for change!
+    framesize_code = bs.getbits (6);
+    framesize = ac3_frame_size[frequency][framesize_code >> 1];
+    framesize = (framesize_code & 1) && frequency == 1 ? (framesize + 1) << 1 : (framesize << 1);
+
+    access_unit.start = AU_start;
+    access_unit.length = framesize;
+    access_unit.PTS = static_cast < clockticks > (decoding_order) *
+      static_cast < clockticks > (AC3_PACKET_SAMPLES) *
+      static_cast < clockticks > (CLOCKS) / samples_per_second;;
+    access_unit.DTS = access_unit.PTS;
+    access_unit.dorder = decoding_order;
+    decoding_order++;
+    aunits.append (access_unit);
+    num_frames[0]++;
+
+    num_syncword++;
+
+
+#ifdef DEBUG_AC3_HEADERS
+    /* Some stuff to generate frame-header information */
+    printf ("bsid       = %d\n", bs.getbits (5));
+    printf ("bsmode     = 0x%1x\n", bs.getbits (3));
+    int acmode = bs.getbits (3);
+
+    printf ("acmode     = 0x%1x\n", acmode);
+    if ((acmode & 0x1) && (acmode != 1))
+      printf ("cmixlev   = %d\n", bs.getbits (2));
+    if ((acmode & 0x4))
+      printf ("smixlev   = %d\n", bs.getbits (2));
+    if (acmode == 2)
+      printf ("dsurr     = %d\n", bs.getbits (2));
+    printf ("lfeon      = %d\n", bs.getbits (1));
+    printf ("dialnorm   = %02d\n", bs.getbits (5));
+    int compre = bs.getbits (1);
+
+    printf ("compre     = %d\n", compre);
+    if (compre)
+      printf ("compr    = %02d\n", bs.getbits (8));
+    int langcode = bs.getbits (1);
+
+    printf ("langcode     = %d\n", langcode);
+    if (langcode)
+      printf ("langcod  = 0x%02x\n", bs.getbits (8));
+
+    while (bs.bitcount () % 8 != 0)
+      bs.getbits (1);
+    header_skip = (bs.bitcount () - AU_start) / 8;
+#endif
+    if (num_syncword >= old_frames + 10) {
+      mjpeg_debug ("Got %d frame headers.", num_syncword);
+      old_frames = num_syncword;
+    }
+
+
+  }
+  if (bad_last_frame) {
+    mjpeg_error_exit1 ("Last AC3 frame ended prematurely!\n");
+  }
+  last_buffered_AU = decoding_order;
+  eoscan = bs.eos ();
+
+}
+
+
+/// Closes the AC3 stream and prints some statistics.
+void
+AC3Stream::Close ()
+{
+  stream_length = AU_start >> 3;
+  mjpeg_info ("AUDIO_STATISTICS: %02x", stream_id);
+  mjpeg_info ("Audio stream length %lld bytes.", stream_length);
+  mjpeg_info ("Syncwords      : %8u", num_syncword);
+  mjpeg_info ("Frames         : %8u padded", num_frames[0]);
+  mjpeg_info ("Frames         : %8u unpadded", num_frames[1]);
+
+  bs.close ();
+}
+
+/*************************************************************************
+       OutputAudioInfo
+       gibt gesammelte Informationen zu den Audio Access Units aus.
+
+       Prints information on audio access units
+*************************************************************************/
+
+void
+AC3Stream::OutputHdrInfo ()
+{
+  mjpeg_info ("AC3 AUDIO STREAM:");
+
+  mjpeg_info ("Bit rate       : %8u bytes/sec (%3u kbit/sec)", bit_rate * 128, bit_rate);
+
+  if (frequency == 3)
+    mjpeg_info ("Frequency      : reserved");
+  else
+    mjpeg_info ("Frequency      :     %d Hz", ac3_frequency[frequency]);
+
+}
+
+/**
+Reads the bytes neccessary to complete the current packet payload. 
+@param to_read number of bytes to read
+@param dst byte buffer pointer to read to 
+@returns the number of bytes read
+ */
+unsigned int
+AC3Stream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  static unsigned int aus = 0;
+  static unsigned int rd = 0;
+
+  unsigned int bytes_read = bs.read_buffered_bytes (dst + 4, to_read - 4);
+
+  rd += bytes_read;
+  clockticks decode_time;
+
+  unsigned int first_header = (new_au_next_sec || au_unsent > bytes_read)
+    ? 0 : au_unsent;
+
+  // BUG BUG BUG: how do we set the 1st header pointer if we have
+  // the *middle* part of a large frame?
+  assert (first_header <= to_read - 2);
+
+
+  unsigned int syncwords = 0;
+  unsigned int bytes_muxed = bytes_read;
+
+  if (bytes_muxed == 0 || MuxCompleted ()) {
+    goto completion;
+  }
+
+
+  /* Work through what's left of the current AU and the following AU's
+     updating the info until we reach a point where an AU had to be
+     split between packets.
+     NOTE: It *is* possible for this loop to iterate. 
+
+     The DTS/PTS field for the packet in this case would have been
+     given the that for the first AU to start in the packet.
+
+   */
+
+  decode_time = RequiredDTS ();
+  while (au_unsent < bytes_muxed) {
+    // BUG BUG BUG: if we ever had odd payload / packet size we might
+    // split an AC3 frame in the middle of the syncword!
+    assert (bytes_muxed > 1);
+    bufmodel.Queued (au_unsent, decode_time);
+    bytes_muxed -= au_unsent;
+    if (new_au_next_sec)
+      ++syncwords;
+    aus += au->length;
+    if (!NextAU ()) {
+      goto completion;
+    }
+    new_au_next_sec = true;
+    decode_time = RequiredDTS ();
+  };
+
+  // We've now reached a point where the current AU overran or
+  // fitted exactly.  We need to distinguish the latter case
+  // so we can record whether the next packet starts with an
+  // existing AU or not - info we need to decide what PTS/DTS
+  // info to write at the start of the next packet.
+
+  if (au_unsent > bytes_muxed) {
+    if (new_au_next_sec)
+      ++syncwords;
+    bufmodel.Queued (bytes_muxed, decode_time);
+    au_unsent -= bytes_muxed;
+    new_au_next_sec = false;
+  } else                       //  if (au_unsent == bytes_muxed)
+  {
+    bufmodel.Queued (bytes_muxed, decode_time);
+    if (new_au_next_sec)
+      ++syncwords;
+    aus += au->length;
+    new_au_next_sec = NextAU ();
+  }
+completion:
+  // Generate the AC3 header...
+  // Note the index counts from the low byte of the offset so
+  // the smallest value is 1!
+
+  dst[0] = AC3_SUB_STR_0 + stream_num;
+  dst[1] = syncwords;
+  dst[2] = (first_header + 1) >> 8;
+  dst[3] = (first_header + 1) & 0xff;
+
+  return bytes_read + 4;
+}
+\f
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/audiostrm.hh b/ext/mplex/audiostrm.hh
new file mode 100644 (file)
index 0000000..bc0167a
--- /dev/null
@@ -0,0 +1,163 @@
+
+/*
+ *  inptstrm.hh:  Input stream classes for MPEG multiplexing
+ *  TODO: Split into the base classes and the different types of
+ *  actual input stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#ifndef __AUDIOSTRM_H__
+#define __AUDIOSTRM_H__
+
+#include "inputstrm.hh"
+
+class AudioStream:public ElementaryStream
+{
+public:
+  AudioStream (IBitStream & ibs, OutputStream & into);
+  virtual void Init (const int stream_num) = 0;
+  virtual void Close () = 0;
+
+  void OutputSector ();
+  bool RunOutComplete ();
+  virtual unsigned int NominalBitRate () = 0;
+
+  unsigned int num_syncword;
+  unsigned int num_frames[2];
+  unsigned int size_frames[2];
+  unsigned int version_id;
+  unsigned int layer;
+  unsigned int protection;
+  unsigned int bit_rate_code;
+  unsigned int frequency;
+  unsigned int mode;
+  unsigned int mode_extension;
+  unsigned int copyright;
+  unsigned int original_copy;
+  unsigned int emphasis;
+
+protected:
+    virtual bool AUBufferNeedsRefill ();
+  virtual void FillAUbuffer (unsigned int frames_to_buffer) = 0;
+  void InitAUbuffer ();
+
+  /* State variables for scanning source bit-stream */
+  unsigned int framesize;
+  unsigned int skip;
+  unsigned int samples_per_second;
+  unsigned long long int length_sum;
+  AAunit access_unit;
+};
+
+class MPAStream:public AudioStream
+{
+public:
+  MPAStream (IBitStream & ibs, OutputStream & into);
+  virtual void Init (const int stream_num);
+  static bool Probe (IBitStream & bs);
+  virtual void Close ();
+  virtual unsigned int NominalBitRate ();
+
+
+private:
+  void OutputHdrInfo ();
+  unsigned int SizeFrame (int bit_rate, int padding_bit);
+  virtual void FillAUbuffer (unsigned int frames_to_buffer);
+
+
+  /* State variables for scanning source bit-stream */
+  unsigned int framesize;
+  unsigned int skip;
+  unsigned int samples_per_second;
+};
+
+
+
+class AC3Stream:public AudioStream
+{
+public:
+  AC3Stream (IBitStream & ibs, OutputStream & into);
+  virtual void Init (const int stream_num);
+  static bool Probe (IBitStream & bs);
+  virtual void Close ();
+  virtual unsigned int NominalBitRate ();
+
+  virtual unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+  virtual unsigned int StreamHeaderSize ()
+  {
+    return 4;
+  }
+
+
+private:
+  void OutputHdrInfo ();
+  virtual void FillAUbuffer (unsigned int frames_to_buffer);
+
+  static const unsigned int default_buffer_size = 16 * 1024;
+
+  /* State variables for scanning source bit-stream */
+  unsigned int framesize;
+  unsigned int samples_per_second;
+  unsigned int bit_rate;
+  unsigned int stream_num;
+};
+
+class LPCMStream:public AudioStream
+{
+public:
+  LPCMStream (IBitStream & ibs, OutputStream & into);
+  virtual void Init (const int stream_num);
+  static bool Probe (IBitStream & bs);
+  virtual void Close ();
+  virtual unsigned int NominalBitRate ();
+
+  virtual unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+  virtual unsigned int StreamHeaderSize ()
+  {
+    return 7;
+  }
+
+
+private:
+  void OutputHdrInfo ();
+  virtual void FillAUbuffer (unsigned int frames_to_buffer);
+
+  static const unsigned int default_buffer_size = 232 * 1024;
+  static const unsigned int ticks_per_frame_90kHz = 150;
+
+  /* State variables for scanning source bit-stream */
+  unsigned int stream_num;
+  unsigned int samples_per_second;
+  unsigned int channels;
+  unsigned int bits_per_sample;
+  unsigned int bytes_per_frame;
+  unsigned int frame_index;
+  unsigned int dynamic_range_code;
+};
+
+
+#endif // __AUDIOSTRM_H__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/audiostrm_out.cc b/ext/mplex/audiostrm_out.cc
new file mode 100644 (file)
index 0000000..5ef358a
--- /dev/null
@@ -0,0 +1,144 @@
+
+/*
+ *  inptstrm.c:  Members of audi stream classes related to muxing out into
+ *               the output stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include <config.h>
+#include <assert.h>
+#include "fastintfns.h"
+#include "audiostrm.hh"
+#include "outputstream.hh"
+
+
+
+AudioStream::AudioStream (IBitStream & ibs, OutputStream & into):
+ElementaryStream (ibs, into, ElementaryStream::audio), num_syncword (0)
+{
+  FRAME_CHUNK = 24;
+  for (int i = 0; i < 2; ++i)
+    num_frames[i] = size_frames[i] = 0;
+}
+
+void
+AudioStream::InitAUbuffer ()
+{
+  unsigned int i;
+
+  for (i = 0; i < aunits.BUF_SIZE; ++i)
+    aunits.init (new AAunit);
+}
+
+
+
+/*********************************
+ * Signals when audio stream has completed mux run-out specified
+ * in associated mux stream. 
+ *********************************/
+
+bool
+AudioStream::RunOutComplete ()
+{
+  return (au_unsent == 0 || (muxinto.running_out && RequiredPTS () >= muxinto.runout_PTS));
+}
+
+bool
+AudioStream::AUBufferNeedsRefill ()
+{
+  return
+    !eoscan
+    && (aunits.current () + FRAME_CHUNK > last_buffered_AU
+       || bs.buffered_bytes () < muxinto.sector_size);
+}
+
+/******************************************************************
+       Output_Audio
+       generates Pack/Sys Header/Packet information from the
+       audio stream and saves them into the sector
+******************************************************************/
+
+void
+AudioStream::OutputSector ()
+{
+  clockticks PTS;
+  unsigned int max_packet_data;
+  unsigned int actual_payload;
+  unsigned int old_au_then_new_payload;
+
+  PTS = RequiredDTS ();
+  old_au_then_new_payload = muxinto.PacketPayload (*this, buffers_in_header, false, false);
+
+  max_packet_data = 0;
+  if (muxinto.running_out && NextRequiredPTS () > muxinto.runout_PTS) {
+    /* We're now in the last AU of a segment.  So we don't want to
+       go beyond it's end when writing sectors. Hence we limit
+       packet payload size to (remaining) AU length.
+     */
+    max_packet_data = au_unsent;
+  }
+
+  /* CASE: packet starts with new access unit                     */
+
+  if (new_au_next_sec) {
+    actual_payload =
+      muxinto.WritePacket (max_packet_data, *this, buffers_in_header, PTS, 0, TIMESTAMPBITS_PTS);
+
+  }
+
+
+  /* CASE: packet starts with old access unit, no new one */
+  /*       starts in this very same packet                        */
+  else if (!(new_au_next_sec) && (au_unsent >= old_au_then_new_payload)) {
+    actual_payload =
+      muxinto.WritePacket (max_packet_data, *this, buffers_in_header, 0, 0, TIMESTAMPBITS_NO);
+  }
+
+
+  /* CASE: packet starts with old access unit, a new one  */
+  /*       starts in this very same packet                        */
+  else {                       /* !(new_au_next_sec) &&  (au_unsent < old_au_then_new_payload)) */
+
+    /* is there another access unit anyway ? */
+    if (Lookahead () != 0) {
+      PTS = NextRequiredDTS ();
+      actual_payload =
+       muxinto.WritePacket (max_packet_data, *this, buffers_in_header, PTS, 0, TIMESTAMPBITS_PTS);
+
+    } else {
+      actual_payload = muxinto.WritePacket (0, *this, buffers_in_header, 0, 0, TIMESTAMPBITS_NO);
+    };
+
+  }
+
+  ++nsec;
+
+  buffers_in_header = always_buffers_in_header;
+
+}
+\f
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/aunit.hh b/ext/mplex/aunit.hh
new file mode 100644 (file)
index 0000000..0876887
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __AUNIT_H__
+#define __AUNIT_H__
+
+#include <config.h>
+#include "mjpeg_types.h"
+#include "bits.hh"
+
+typedef int64_t clockticks;    // This value *must* be signed
+
+                               // because we frequently compute *offsets*
+
+class Aunit
+{
+public:
+  Aunit ():length (0), PTS (0), DTS (0)
+  {
+  }
+  void markempty ()
+  {
+    length = 0;
+  }
+  bitcount_t start;
+  unsigned int length;
+  clockticks PTS;
+  int dorder;
+
+  // Used only for video AU's but otherwise
+  // you have to go crazy on templates.
+  clockticks DTS;
+  int porder;
+  unsigned int type;
+  bool seq_header;
+  bool end_seq;
+
+};
+
+typedef Aunit VAunit;
+
+typedef Aunit AAunit;
+
+#endif // __AUNIT_H__
diff --git a/ext/mplex/bits.cc b/ext/mplex/bits.cc
new file mode 100644 (file)
index 0000000..84d0591
--- /dev/null
@@ -0,0 +1,358 @@
+/** @file bits.cc, bit-level output                                              */
+
+/* Copyright (C) 2001, Andrew Stevens <andrew.stevens@philips.com> *
+
+ *
+ * Disclaimer of Warranty
+ *
+ * These software programs are available to the user without any license fee or
+ * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
+ * any and all warranties, whether express, implied, or statuary, including any
+ * implied warranties or merchantability or of fitness for a particular
+ * purpose.  In no event shall the copyright-holder be liable for any
+ * incidental, punitive, or consequential damages of any kind whatsoever
+ * arising from the use of these programs.
+ *
+ * This disclaimer of warranty extends to the user of these programs and user's
+ * customers, employees, agents, transferees, successors, and assigns.
+ *
+ * The MPEG Software Simulation Group does not represent or warrant that the
+ * programs furnished hereunder are free of infringement of any third-party
+ * patents.
+ *
+ * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
+ * are subject to royalty fees to patent holders.  Many of these patents are
+ * general enough such that they are unavoidable regardless of implementation
+ * design.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/param.h>
+#include <assert.h>
+#include "mjpeg_logging.h"
+#include "bits.hh"
+
+
+/// Initializes the bitstream, sets internal variables.
+// TODO: The buffer size should be set dynamically to sensible sizes.
+//
+BitStream::BitStream ():
+user_data (NULL)
+{
+  totbits = 0LL;
+  buffer_start = 0LL;
+  eobs = true;
+  readpos = 0LL;
+  bfr = 0;
+  bfr_size = 0;
+}
+
+/// Deconstructor. Deletes the internal buffer. 
+BitStream::~BitStream ()
+{
+  delete bfr;
+}
+
+/**
+   Refills an IBitStream's input buffer based on the internal variables bufcount and bfr_size. 
+ */
+bool
+IBitStream::refill_buffer ()
+{
+  size_t i;
+
+  if (bufcount >= bfr_size) {
+    SetBufSize (bfr_size + 4096);
+  }
+
+  i = read_callback (this, bfr + bufcount, static_cast < size_t > (bfr_size - bufcount), user_data);
+  bufcount += i;
+
+  if (i == 0) {
+    eobs = true;
+    return false;
+  }
+  return true;
+}
+
+/**
+  Flushes all read input up-to *but not including* bit
+  unbuffer_upto.
+@param flush_to the number of bits to flush
+*/
+
+void
+IBitStream::flush (bitcount_t flush_upto)
+{
+  if (flush_upto > buffer_start + bufcount)
+    mjpeg_error_exit1 ("INTERNAL ERROR: attempt to flush input beyond buffered amount");
+
+  if (flush_upto < buffer_start)
+    mjpeg_error_exit1
+      ("INTERNAL ERROR: attempt to flush input stream before  first buffered byte %d last is %d",
+       (int) flush_upto, (int) buffer_start);
+  unsigned int bytes_to_flush = static_cast < unsigned int >(flush_upto - buffer_start);
+
+  //
+  // Don't bother actually flushing until a good fraction of a buffer
+  // will be cleared.
+  //
+
+  if (bytes_to_flush < bfr_size * 3 / 4)
+    return;
+
+  bufcount -= bytes_to_flush;
+  buffer_start = flush_upto;
+  byteidx -= bytes_to_flush;
+  memmove (bfr, bfr + bytes_to_flush, static_cast < size_t > (bufcount));
+}
+
+
+/**
+  Undo scanning / reading
+  N.b buffer *must not* be flushed between prepareundo and undochanges.
+  @param undo handle to store the undo information
+*/
+void
+IBitStream::prepareundo (BitStreamUndo & undo)
+{
+  undo = *(static_cast < BitStreamUndo * >(this));
+}
+
+/**
+Undoes changes committed to an IBitStream. 
+@param undo handle to retrieve the undo information   
+ */
+void
+IBitStream::undochanges (BitStreamUndo & undo)
+{
+  *(static_cast < BitStreamUndo * >(this)) = undo;
+}
+
+/**
+   Read a number bytes over an IBitStream, using the buffer. 
+   @param dst buffer to read to 
+   @param length the number of bytes to read
+ */
+unsigned int
+IBitStream::read_buffered_bytes (uint8_t * dst, unsigned int length)
+{
+  unsigned int to_read = length;
+
+  if (readpos < buffer_start)
+    mjpeg_error_exit1
+      ("INTERNAL ERROR: access to input stream buffer @ %d: before first buffered byte (%d)",
+       (int) readpos, (int) buffer_start);
+
+  if (readpos + length > buffer_start + bufcount) {
+  /*
+    if (!feof (fileh)) {
+      mjpeg_error
+       ("INTERNAL ERROR: access to input stream buffer beyond last buffered byte @POS=%lld END=%d REQ=%lld + %d bytes",
+        readpos, bufcount, readpos - (bitcount_t) buffer_start, length);
+      abort ();
+    }
+    */
+    to_read = static_cast < unsigned int >((buffer_start + bufcount) - readpos);
+  }
+  memcpy (dst, bfr + (static_cast < unsigned int >(readpos - buffer_start)), to_read);
+  // We only ever flush up to the start of a read as we
+  // have only scanned up to a header *beginning* a block that is then
+  // read
+  flush (readpos);
+  readpos += to_read;
+  return to_read;
+}
+
+/** open the device to read the bit stream from it 
+@param bs_filename filename to open
+@param buf_size size of the internal buffer 
+*/
+void
+IBitStream::open (ReadCallback read_callback, void *user_data, unsigned int buf_size)
+{
+  this->read_callback = read_callback;
+  this->user_data = user_data;
+
+  bfr_size = buf_size;
+  if (bfr == NULL)
+    bfr = new uint8_t[buf_size];
+  else {
+    delete bfr;
+
+    bfr = new uint8_t[buf_size];
+  }
+
+  byteidx = 0;
+  bitidx = 8;
+  totbits = 0LL;
+  bufcount = 0;
+  eobs = false;
+  if (!refill_buffer ()) {
+    if (bufcount == 0) {
+      mjpeg_error_exit1 ("Unable to read.");
+    }
+  }
+}
+
+
+/** sets the internal buffer size. 
+    @param new_buf_size the new internal buffer size 
+*/
+void
+IBitStream::SetBufSize (unsigned int new_buf_size)
+{
+  assert (bfr != NULL);                // Must be open first!
+  assert (new_buf_size >= bfr_size);   // Can only be increased in size...
+
+  if (bfr_size != new_buf_size) {
+    uint8_t *new_buf = new uint8_t[new_buf_size];
+
+    memcpy (new_buf, bfr, static_cast < size_t > (bfr_size));
+    delete bfr;
+
+    bfr_size = new_buf_size;
+    bfr = new_buf;
+  }
+
+}
+
+/**
+   close the device containing the bit stream after a read process
+*/
+void
+IBitStream::close ()
+{
+}
+
+
+// TODO replace with shift ops!
+
+uint8_t
+  IBitStream::masks[8] = {
+    0x1,
+    0x2,
+    0x4,
+    0x8,
+    0x10,
+    0x20,
+    0x40,
+0x80 };
+
+/*read 1 bit from the bit stream 
+@returns the read bit, 0 on EOF */
+uint32_t
+IBitStream::get1bit ()
+{
+  unsigned int bit;
+
+  if (eobs)
+    return 0;
+
+  bit = (bfr[byteidx] & masks[bitidx - 1]) >> (bitidx - 1);
+  totbits++;
+  bitidx--;
+  if (!bitidx) {
+    bitidx = 8;
+    byteidx++;
+    if (byteidx == bufcount) {
+      refill_buffer ();
+    }
+  }
+
+  return bit;
+}
+
+/*read N bits from the bit stream 
+@returns the read bits, 0 on EOF */
+uint32_t
+IBitStream::getbits (int N)
+{
+  uint32_t val = 0;
+  int i = N;
+  unsigned int j;
+
+  // Optimize: we are on byte boundary and want to read multiple of bytes!
+  if ((bitidx == 8) && ((N & 7) == 0)) {
+    i = N >> 3;
+    while (i > 0) {
+      if (eobs)
+       return 0;
+      val = (val << 8) | bfr[byteidx];
+      byteidx++;
+      totbits += 8;
+      if (byteidx == bufcount) {
+       refill_buffer ();
+      }
+      i--;
+    }
+  } else {
+    while (i > 0) {
+      if (eobs)
+       return 0;
+
+      j = (bfr[byteidx] & masks[bitidx - 1]) >> (bitidx - 1);
+      totbits++;
+      bitidx--;
+      if (!bitidx) {
+       bitidx = 8;
+       byteidx++;
+       if (byteidx == bufcount) {
+         refill_buffer ();
+       }
+      }
+      val = (val << 1) | j;
+      i--;
+    }
+  }
+  return val;
+}
+
+
+/** This function seeks for a byte aligned sync word (max 32 bits) in the bit stream and
+    places the bit stream pointer right after the sync.
+    This function returns 1 if the sync was found otherwise it returns 0
+@param sync the sync word to search for
+@param N the number of bits to retrieve
+@param lim number of bytes to search through
+@returns false on error */
+
+bool
+IBitStream::seek_sync (uint32_t sync, int N, int lim)
+{
+  uint32_t val, val1;
+  uint32_t maxi = ((1U << N) - 1); /* pow(2.0, (double)N) - 1 */ ;
+  if (maxi == 0) {
+    maxi = 0xffffffff;
+  }
+  while (bitidx != 8) {
+    get1bit ();
+  }
+
+  val = getbits (N);
+  if (eobs)
+    return false;
+  while ((val & maxi) != sync && --lim) {
+    val <<= 8;
+    val1 = getbits (8);
+    val |= val1;
+    if (eobs)
+      return false;
+  }
+
+  return (!!lim);
+}
+\f
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/bits.hh b/ext/mplex/bits.hh
new file mode 100644 (file)
index 0000000..275b8c0
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef __BITS_H__
+#define __BITS_H__
+
+#include <config.h>
+#include <stdio.h>
+
+typedef uint64_t bitcount_t;
+
+
+class BitStreamUndo
+{
+//protected:
+public:
+  uint8_t outbyte;
+  unsigned int byteidx;
+  int bitidx;
+  unsigned int bufcount;
+  fpos_t actpos;
+  bitcount_t totbits;
+  bitcount_t buffer_start;
+  bitcount_t readpos;
+  uint8_t *bfr;
+  unsigned int bfr_size;
+public:
+  bool eobs;
+};
+
+typedef class BitStream _BitStream;
+
+typedef size_t (*ReadCallback)         (_BitStream *bs, uint8_t *dest, size_t size, void *user_data);
+
+class BitStream : public BitStreamUndo
+{
+public:
+  void *user_data;
+  static const unsigned int BUFFER_SIZE = 4 * 1024;
+public:
+    BitStream ();
+   ~BitStream ();
+  inline bitcount_t bitcount ()
+  {
+    return totbits;
+  }
+  inline bool eos ()
+  {
+    return eobs;
+  }
+};
+
+
+//
+// Input bit stream class.   Supports the "scanning" of a stream
+// into a large buffer which is flushed once it has been read.
+// N.b. if you scan ahead a long way and don't read its your
+// responsibility to flush manually...
+//
+
+class IBitStream:public BitStream
+{
+public:
+  void open (ReadCallback read_callback, void *user_data, unsigned int buf_size = BUFFER_SIZE);
+  void SetBufSize (unsigned int buf_size);
+  void close ();
+  uint32_t get1bit ();
+  uint32_t getbits (int N);
+  void prepareundo (BitStreamUndo & undobuf);
+  void undochanges (BitStreamUndo & undobuf);
+  bool seek_sync (unsigned int sync, int N, int lim);
+  void flush (bitcount_t bitposition);
+  inline unsigned int buffered_bytes ()
+  {
+    return (buffer_start + bufcount - readpos);
+  }
+  unsigned int read_buffered_bytes (uint8_t * dst, unsigned int length_bytes);
+
+private:
+  bool refill_buffer ();
+  static uint8_t masks[8];
+  ReadCallback read_callback;
+};
+
+#endif // __BITS_H__
diff --git a/ext/mplex/buffer.cc b/ext/mplex/buffer.cc
new file mode 100644 (file)
index 0000000..2b29180
--- /dev/null
@@ -0,0 +1,118 @@
+#include "buffer.hh"
+#include <stdlib.h>
+
+/******************************************************************
+ *     Remove entries from FIFO buffer list, if their DTS is less than
+ *     actual SCR. These packet data have been already decoded and have
+ *     been removed from the system target decoder's elementary stream
+ *     buffer.
+ *****************************************************************/
+
+void
+BufferModel::Cleaned (clockticks SCR)
+{
+  BufferQueue *pointer;
+
+  while ((first != NULL) && first->DTS < SCR) {
+    pointer = first;
+    first = first->next;
+    delete pointer;
+  }
+}
+
+/******************************************************************
+ * Return the SCR when there will next be some change in the
+ * buffer.
+ * If the buffer is empty return a zero timestamp.
+ *****************************************************************/
+
+clockticks
+BufferModel::NextChange ()
+{
+  if (first == NULL)
+    return static_cast < clockticks > (0);
+  else
+    return first->DTS;
+}
+
+
+/******************************************************************
+ *
+ *     Remove all entries from FIFO buffer list, if their DTS is less
+ *     than actual SCR. These packet data have been already decoded and
+ *     have been removed from the system target decoder's elementary
+ *     stream buffer.
+ *****************************************************************/
+
+void
+BufferModel::Flushed ()
+{
+  BufferQueue *pointer;
+
+  while (first != NULL) {
+    pointer = first;
+    first = first->next;
+    delete pointer;
+  }
+}
+
+/******************************************************************
+       BufferModel::Space
+
+       returns free space in the buffer
+******************************************************************/
+
+unsigned int
+BufferModel::Space ()
+{
+  unsigned int used_bytes;
+  BufferQueue *pointer;
+
+  pointer = first;
+  used_bytes = 0;
+
+  while (pointer != NULL) {
+    used_bytes += pointer->size;
+    pointer = pointer->next;
+  }
+
+  return (max_size - used_bytes);
+
+}
+
+/******************************************************************
+       Queue_Buffer
+
+       adds entry into the buffer FIFO queue
+******************************************************************/
+
+void
+BufferModel::Queued (unsigned int bytes, clockticks TS)
+{
+  BufferQueue *pointer;
+
+  pointer = first;
+  if (pointer == NULL) {
+    first = new BufferQueue;
+    first->size = bytes;
+    first->next = NULL;
+    first->DTS = TS;
+  } else {
+    while ((pointer->next) != NULL) {
+      pointer = pointer->next;
+    }
+
+    pointer->next = (BufferQueue *) malloc (sizeof (BufferQueue));
+    pointer->next->size = bytes;
+    pointer->next->next = NULL;
+    pointer->next->DTS = TS;
+  }
+}
+
+
+void
+BufferModel::Init (unsigned int size)
+{
+  max_size = size;
+  first = 0;
+}
diff --git a/ext/mplex/buffer.hh b/ext/mplex/buffer.hh
new file mode 100644 (file)
index 0000000..a7bdba9
--- /dev/null
@@ -0,0 +1,73 @@
+
+/*
+ *  buffer.hh:  Classes for decoder buffer models for mux despatch
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#ifndef __BUFFER_H__
+#define __BUFFER_H__
+
+#include <config.h>
+#include "aunit.hh"
+
+class BufferQueue
+{
+#include <config.h>
+#
+public:
+  unsigned int size;           /* als verkettete Liste implementiert   */
+  clockticks DTS;
+  BufferQueue *next;
+};
+
+
+class BufferModel
+{
+public:
+  BufferModel ():max_size (0), first (0)
+  {
+  }
+  void Init (unsigned int size);
+
+  void Cleaned (clockticks timenow);
+  clockticks NextChange ();
+  void Flushed ();
+  unsigned int Space ();
+  void Queued (unsigned int bytes, clockticks removaltime);
+  inline unsigned int Size ()
+  {
+    return max_size;
+  }
+private:
+  unsigned int max_size;
+  BufferQueue *first;
+};
+
+
+
+#endif // __BUFFER_H__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/fastintfns.h b/ext/mplex/fastintfns.h
new file mode 100644 (file)
index 0000000..db78af1
--- /dev/null
@@ -0,0 +1,32 @@
+/* fast int primitives. min,max,abs,samesign
+ *
+ * WARNING: Assumes 2's complement arithmetic.
+ *
+ */
+
+
+static __inline__ int intmax( register int x, register int y )
+{
+       return x < y ? y : x;
+}
+
+static __inline__ int intmin( register int x, register int y )
+{
+       return x < y ? x : y;
+}
+
+static __inline__ int intabs( register int x )
+{
+       return x < 0 ? -x : x;
+}
+
+#define fabsshift ((8*sizeof(unsigned int))-1)
+
+#define signmask(x) (((int)x)>>fabsshift)
+static __inline__ int intsamesign(int x, int y)
+{
+       return (y+(signmask(x) & -(y<<1)));
+}
+#undef signmask
+#undef fabsshift
+
diff --git a/ext/mplex/format_codes.h b/ext/mplex/format_codes.h
new file mode 100644 (file)
index 0000000..32f668f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    $Id$
+
+    Copyright (C) 2001 Andrew Stevens <andrew.stevens@planet-interkom.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+\f
+#ifndef __FORMAT_CODES_H__
+#define __FORMAT_CODES_H__
+
+#define MPEG_FORMAT_MPEG1   0
+#define MPEG_FORMAT_VCD     1
+#define MPEG_FORMAT_VCD_NSR 2
+#define MPEG_FORMAT_MPEG2   3
+#define MPEG_FORMAT_SVCD     4
+#define MPEG_FORMAT_SVCD_NSR 5
+#define MPEG_FORMAT_VCD_STILL 6
+#define MPEG_FORMAT_SVCD_STILL 7
+#define MPEG_FORMAT_DVD      8
+
+#define MPEG_FORMAT_FIRST 0
+#define MPEG_FORMAT_LAST 8
+
+#define MPEG_STILLS_FORMAT(x) (x==MPEG_FORMAT_VCD_STILL||x==MPEG_FORMAT_SVCD_STILL)
+#endif /* __FORMAT_CODES_H__ */
diff --git a/ext/mplex/gstmplex.cc b/ext/mplex/gstmplex.cc
new file mode 100644 (file)
index 0000000..73d78f0
--- /dev/null
@@ -0,0 +1,464 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <string.h>
+
+#include "gstmplex.h"
+
+#include "videostrm.hh"
+#include "audiostrm.hh"
+
+
+/* elementfactory information */
+static GstElementDetails gst_mplex_details = {
+  "MPlex multiplexer",
+  "Codec/Audio/Decoder",
+  "GPL",
+  "multiplex mpeg audio and video into a system stream",
+  VERSION,
+  "Wim Taymans <wim.taymans@chello.be> ",
+  "(C) 2002",
+};
+
+/* Sidec signals and args */
+enum {
+  /* FILL ME */
+  LAST_SIGNAL
+};
+
+enum {
+  ARG_0,
+  ARG_MUX_FORMAT,
+  ARG_MUX_BITRATE,
+  ARG_VIDEO_BUFFER,
+  ARG_SYNC_OFFSET,
+  ARG_SECTOR_SIZE,
+  ARG_VBR,
+  ARG_PACKETS_PER_PACK,
+  ARG_SYSTEM_HEADERS,
+  /* FILL ME */
+};
+
+GST_PAD_TEMPLATE_FACTORY (src_factory,
+  "src",
+  GST_PAD_SRC,
+  GST_PAD_ALWAYS,
+  GST_CAPS_NEW (
+    "src_video",
+    "video/mpeg",
+      "mpegversion",    GST_PROPS_INT_RANGE (1, 2),
+      "systemstream",   GST_PROPS_BOOLEAN (TRUE)
+  )
+)
+
+GST_PAD_TEMPLATE_FACTORY (video_sink_factory,
+  "video_%d",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  GST_CAPS_NEW (
+    "sink_video",
+    "video/mpeg",
+      "mpegversion",    GST_PROPS_INT_RANGE (1, 2),
+      "systemstream",   GST_PROPS_BOOLEAN (FALSE)
+  )
+)
+  
+GST_PAD_TEMPLATE_FACTORY (audio_sink_factory,
+  "audio_%d",
+  GST_PAD_SINK,
+  GST_PAD_REQUEST,
+  GST_CAPS_NEW (
+    "sink_audio",
+    "audio/mp3",
+       NULL
+  )
+)
+
+#define GST_TYPE_MPLEX_MUX_FORMAT (gst_mplex_mux_format_get_type())
+static GType
+gst_mplex_mux_format_get_type (void) 
+{
+  static GType mplex_mux_format_type = 0;
+  static GEnumValue mplex_mux_format[] = {
+    { MPEG_FORMAT_MPEG1,      "0", "Generic MPEG1" },
+    { MPEG_FORMAT_VCD,        "1", "VCD" },
+    { MPEG_FORMAT_VCD_NSR,    "2", "user-rate VCD" },
+    { MPEG_FORMAT_MPEG2,      "3", "Generic MPEG2" },
+    { MPEG_FORMAT_SVCD,       "4", "SVCD" },
+    { MPEG_FORMAT_SVCD_NSR,   "5", "user-rate SVCD" },
+    { MPEG_FORMAT_VCD_STILL,  "6", "VCD Stills" },
+    { MPEG_FORMAT_SVCD_STILL, "7", "SVCD Stills" },
+    { MPEG_FORMAT_DVD,        "8", "DVD" },
+    {0, NULL, NULL},
+  };
+  if (!mplex_mux_format_type) {
+    mplex_mux_format_type = g_enum_register_static("GstMPlexMuxFormat", mplex_mux_format);
+  }
+  return mplex_mux_format_type;
+}
+
+static void    gst_mplex_class_init            (GstMPlex *klass);
+static void    gst_mplex_init                  (GstMPlex *mplex);
+
+static GstPad*         gst_mplex_request_new_pad       (GstElement     *element,
+                                                GstPadTemplate *templ,
+                                                const gchar    *req_name);
+static void    gst_mplex_loop                  (GstElement *element);
+static size_t  gst_mplex_read_callback         (BitStream *bitstream, 
+                                                uint8_t *dest, size_t size, 
+                                                void *user_data);
+static size_t  gst_mplex_write_callback        (PS_Stream *stream, 
+                                                uint8_t *data, size_t size, 
+                                                void *user_data);
+
+static void     gst_mplex_get_property                 (GObject *object, guint prop_id, 
+                                                GValue *value, GParamSpec *pspec);
+static void     gst_mplex_set_property                 (GObject *object, guint prop_id, 
+                                                const GValue *value, GParamSpec *pspec);
+
+static GstElementClass *parent_class = NULL;
+//static guint gst_mplex_signals[LAST_SIGNAL] = { 0 };
+
+GType
+gst_mplex_get_type (void) 
+{
+  static GType mplex_type = 0;
+
+  if (!mplex_type) {
+    static const GTypeInfo mplex_info = {
+      sizeof(GstMPlexClass),      
+      NULL,
+      NULL,
+      (GClassInitFunc) gst_mplex_class_init,
+      NULL,
+      NULL,
+      sizeof(GstMPlex),
+      0,
+      (GInstanceInitFunc) gst_mplex_init,
+      NULL
+    };
+    mplex_type = g_type_register_static (GST_TYPE_ELEMENT, "GstMPlex", &mplex_info, (GTypeFlags)0);
+  }
+
+  return mplex_type;
+}
+
+static void
+gst_mplex_class_init (GstMPlex *klass) 
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass*)klass;
+  gstelement_class = (GstElementClass*)klass;
+
+  parent_class = GST_ELEMENT_CLASS (g_type_class_ref (GST_TYPE_ELEMENT));
+
+  gobject_class->set_property = gst_mplex_set_property;
+  gobject_class->get_property = gst_mplex_get_property;
+
+  g_object_class_install_property (gobject_class, ARG_MUX_FORMAT,
+    g_param_spec_enum ("mux_format", "Mux format", "Set defaults for particular MPEG profiles",
+                       GST_TYPE_MPLEX_MUX_FORMAT, MPEG_FORMAT_MPEG1, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_MUX_BITRATE,
+    g_param_spec_int ("mux_bitrate", "Mux bitrate", "Specify data rate of output stream in kbit/sec"
+                     "(0 = Compute from source streams)",
+                      0, G_MAXINT, 0, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_VIDEO_BUFFER,
+    g_param_spec_int ("video_buffer", "Video buffer", "Specifies decoder buffers size in kB",
+                      20, 2000, 20, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_SYNC_OFFSET,
+    g_param_spec_int ("sync_offset", "Sync offset", "Specify offset of timestamps (video-audio) in mSec",
+                      0, G_MAXINT, 0, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_SECTOR_SIZE,
+    g_param_spec_int ("sector_size", "Sector size", "Specify sector size in bytes for generic formats",
+                      256, 16384, 2028, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_VBR,
+    g_param_spec_boolean ("vbr", "VBR", "Multiplex variable bit-rate video",
+                          TRUE, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_PACKETS_PER_PACK,
+      g_param_spec_int ("packets_per_pack", "Packets per pack",
+                        "Number of packets per pack generic formats",
+                       1, 100, 1, (GParamFlags) G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_class, ARG_SYSTEM_HEADERS,
+    g_param_spec_boolean ("system_headers", "System headers", 
+                          " Create System header in every pack in generic formats",
+                          TRUE, (GParamFlags) G_PARAM_READWRITE));
+
+  gstelement_class->request_new_pad = gst_mplex_request_new_pad;
+}
+
+static void 
+gst_mplex_init (GstMPlex *mplex) 
+{
+  mplex->srcpad = gst_pad_new_from_template (
+               GST_PAD_TEMPLATE_GET (src_factory), "src");
+  gst_element_add_pad (GST_ELEMENT (mplex), mplex->srcpad);
+
+  gst_element_set_loop_function (GST_ELEMENT (mplex), gst_mplex_loop);
+
+  mplex->ostrm = new OutputStream ();
+  mplex->strms = new vector<ElementaryStream *>();
+
+  mplex->state = GST_MPLEX_OPEN_STREAMS;
+
+  mplex->ostrm->opt_mux_format = MPEG_FORMAT_DVD;
+
+  (void)mjpeg_default_handler_verbosity(mplex->ostrm->opt_verbosity);
+}
+
+static GstPad*
+gst_mplex_request_new_pad (GstElement     *element,
+                           GstPadTemplate *templ,
+                          const gchar    *req_name) 
+{
+  GstMPlexStream *stream;
+  GstMPlex *mplex;
+  GstPad *pad = NULL;
+
+  mplex = GST_MPLEX (element);
+  
+  stream = g_new0 (GstMPlexStream, 1);
+
+  if (!strncmp (templ->name_template, "audio", 5)) {
+    pad = gst_pad_new ("audio_sink", GST_PAD_SINK);
+
+    stream->type = GST_MPLEX_STREAM_AC3;
+  }
+  else if (!strncmp (templ->name_template, "video", 5)) {
+    pad = gst_pad_new ("video_sink", GST_PAD_SINK);
+
+    stream->type = GST_MPLEX_STREAM_DVD_VIDEO;
+  }
+  
+  if (pad) {
+    stream->pad = pad;
+    stream->bitstream = new IBitStream();
+    stream->bytestream = gst_bytestream_new (pad);
+
+    mplex->streams = g_list_prepend (mplex->streams, stream);
+
+    gst_element_add_pad (element, pad);
+  }
+
+  return pad;
+}
+
+
+static size_t
+gst_mplex_read_callback (BitStream *bitstream, uint8_t *dest, size_t size, void *user_data)
+{
+  GstMPlexStream *stream;
+  guint8 *data;
+  guint32 len;
+
+  stream = (GstMPlexStream *) user_data;
+
+  len = gst_bytestream_peek_bytes (stream->bytestream, &data, size);
+  if (len < size) {
+    g_print ("got %d bytes out of %d\n", len, size);
+  }
+
+  memcpy (dest, data, len);
+  
+  gst_bytestream_flush_fast (stream->bytestream, len);
+
+  return len;
+}
+
+static size_t
+gst_mplex_write_callback (PS_Stream *stream, uint8_t *data, size_t size, void *user_data)
+{
+  GstMPlex *mplex;
+  GstBuffer *outbuf;
+
+  mplex = GST_MPLEX (user_data);
+
+  if (GST_PAD_IS_USABLE (mplex->srcpad)) {
+    outbuf = gst_buffer_new_and_alloc (size);
+    memcpy (GST_BUFFER_DATA (outbuf), data, size);
+
+    gst_pad_push (mplex->srcpad, outbuf);
+  }
+
+  return size;
+}
+
+static void 
+gst_mplex_loop (GstElement *element)
+{
+  GstMPlex *mplex;
+
+  mplex = GST_MPLEX (element);
+
+  switch (mplex->state) {
+    case GST_MPLEX_OPEN_STREAMS:
+    {
+      mplex->ostrm->InitSyntaxParameters();
+
+      GList *walk = mplex->streams;
+      while (walk) {
+        GstMPlexStream *stream = (GstMPlexStream *) walk->data;
+
+        stream->bitstream->open (gst_mplex_read_callback, stream);
+
+       switch (stream->type) {
+         case GST_MPLEX_STREAM_AC3:
+         {
+           AC3Stream *ac3stream;
+
+            ac3stream = new AC3Stream(*stream->bitstream, *mplex->ostrm);
+            ac3stream->Init(0);
+            stream->elem_stream = ac3stream;
+           break;
+         }
+         case GST_MPLEX_STREAM_DVD_VIDEO:
+         {
+           DVDVideoStream *dvdstream;
+
+            dvdstream = new DVDVideoStream(*stream->bitstream, *mplex->ostrm);
+            dvdstream->Init(0);
+            stream->elem_stream = dvdstream;
+           break;
+         }
+         default:
+           break;
+       }
+        mplex->strms->push_back(stream->elem_stream);
+       
+        walk = g_list_next (walk);
+      }
+
+      mplex->ps_stream = new PS_Stream (gst_mplex_write_callback, mplex);
+      mplex->ostrm->Init (mplex->strms, mplex->ps_stream);
+      
+      /* move to running state after this */
+      mplex->state = GST_MPLEX_RUN;
+      break;
+    }
+    case GST_MPLEX_RUN:
+      if (!mplex->ostrm->OutputMultiplex()) {
+        mplex->state = GST_MPLEX_END;
+      }
+      break;
+    case GST_MPLEX_END:
+    {
+      mplex->ostrm->Close ();
+      gst_pad_push (mplex->srcpad, GST_BUFFER (gst_event_new (GST_EVENT_EOS)));
+      gst_element_set_eos (element);
+      break;
+    }
+    default:
+      break;
+  }
+
+}
+
+static void 
+gst_mplex_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+  GstMPlex *mplex;
+
+  mplex = GST_MPLEX(object);
+
+  switch(prop_id) {
+    case ARG_MUX_FORMAT:
+      break;
+    case ARG_MUX_BITRATE:
+      break;
+    case ARG_VIDEO_BUFFER:
+      break;
+    case ARG_SYNC_OFFSET:
+      break;
+    case ARG_SECTOR_SIZE:
+      break;
+    case ARG_VBR:
+      break;
+    case ARG_PACKETS_PER_PACK:
+      break;
+    case ARG_SYSTEM_HEADERS:
+      break;
+    default:
+      //G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      return;
+  }
+}
+
+static void 
+gst_mplex_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+  GstMPlex *mplex;
+
+  mplex = GST_MPLEX(object);
+
+  switch(prop_id) {
+    case ARG_MUX_FORMAT:
+      break;
+    case ARG_MUX_BITRATE:
+      break;
+    case ARG_VIDEO_BUFFER:
+      break;
+    case ARG_SYNC_OFFSET:
+      break;
+    case ARG_SECTOR_SIZE:
+      break;
+    case ARG_VBR:
+      break;
+    case ARG_PACKETS_PER_PACK:
+      break;
+    case ARG_SYSTEM_HEADERS:
+      break;
+    default:
+      //G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+plugin_init (GModule *module, GstPlugin *plugin)
+{
+  GstElementFactory *factory;
+
+  /* this filter needs the bytestream package */
+  if (!gst_library_load ("gstbytestream"))
+    return FALSE;
+
+  /* create an elementfactory for the avi_demux element */
+  factory = gst_element_factory_new ("mplex",GST_TYPE_MPLEX,
+                                    &gst_mplex_details);
+  g_return_val_if_fail (factory != NULL, FALSE);
+  gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY);
+
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_factory));
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (audio_sink_factory));
+  gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (video_sink_factory));
+
+  gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+  return TRUE;
+}
+
+GstPluginDesc plugin_desc = {
+  GST_VERSION_MAJOR,
+  GST_VERSION_MINOR,
+  "mplex",
+  plugin_init
+};
+
diff --git a/ext/mplex/gstmplex.h b/ext/mplex/gstmplex.h
new file mode 100644 (file)
index 0000000..9266eb1
--- /dev/null
@@ -0,0 +1,97 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __GST_MPLEX_H__
+#define __GST_MPLEX_H__
+
+
+#include <config.h>
+#include <stdlib.h>
+
+#include <gst/gst.h>
+#include <gst/bytestream/bytestream.h>
+
+#include "outputstream.hh"
+#include "bits.hh"
+
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_MPLEX \
+  (gst_mplex_get_type())
+#define GST_MPLEX(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MPLEX,GstMPlex))
+#define GST_MPLEX_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MPLEX,GstMPlex))
+#define GST_IS_MPLEX(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MPLEX))
+#define GST_IS_MPLEX_CLASS(obj) \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MPLEX))
+
+typedef enum {
+  GST_MPLEX_INIT,
+  GST_MPLEX_OPEN_STREAMS,
+  GST_MPLEX_RUN,
+  GST_MPLEX_END
+} GstMPlexState;
+
+typedef enum {
+  GST_MPLEX_STREAM_UNKOWN,
+  GST_MPLEX_STREAM_AC3,
+  GST_MPLEX_STREAM_MPA,
+  GST_MPLEX_STREAM_LPCM,
+  GST_MPLEX_STREAM_VIDEO,
+  GST_MPLEX_STREAM_DVD_VIDEO,
+} GstMPlexStreamType;
+
+typedef struct _GstMPlex GstMPlex;
+typedef struct _GstMPlexStream GstMPlexStream;
+typedef struct _GstMPlexClass GstMPlexClass;
+
+struct _GstMPlexStream {
+  IBitStream           *bitstream;
+  ElementaryStream     *elem_stream;
+  GstPad               *pad;
+  GstMPlexStreamType    type;
+  GstByteStream                *bytestream;
+};
+
+struct _GstMPlex {
+  GstElement    element;
+
+  GstMPlexState         state;
+  /* pads */
+  GstPad       *srcpad;
+  GList                *streams;
+
+  vector<ElementaryStream *> *strms;
+  OutputStream         *ostrm;
+  PS_Stream    *ps_stream;
+};
+
+struct _GstMPlexClass {
+  GstElementClass parent_class;
+};
+
+GType gst_mplex_get_type (void);
+       
+G_END_DECLS
+
+#endif /* __GST_MPLEX_H__ */
diff --git a/ext/mplex/inputstrm.cc b/ext/mplex/inputstrm.cc
new file mode 100644 (file)
index 0000000..4a19724
--- /dev/null
@@ -0,0 +1,249 @@
+
+/*
+ *  inputstrm.c:  Base classes related to muxing out input streams into
+ *                the output stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include <config.h>
+#include <assert.h>
+#include "fastintfns.h"
+#include "inputstrm.hh"
+#include "outputstream.hh"
+
+MuxStream::MuxStream ():init (false)
+{
+}
+
+
+void
+MuxStream::Init (const int strm_id,
+                const unsigned int _buf_scale,
+                const unsigned int buf_size,
+                const unsigned int _zero_stuffing, bool bufs_in_first, bool always_bufs)
+{
+  stream_id = strm_id;
+  nsec = 0;
+  zero_stuffing = _zero_stuffing;
+  buffer_scale = _buf_scale;
+  buffer_size = buf_size;
+  bufmodel.Init (buf_size);
+  buffers_in_header = bufs_in_first;
+  always_buffers_in_header = always_bufs;
+  new_au_next_sec = true;
+  init = true;
+}
+
+
+
+unsigned int
+MuxStream::BufferSizeCode ()
+{
+  if (buffer_scale == 1)
+    return buffer_size / 1024;
+  else if (buffer_scale == 0)
+    return buffer_size / 128;
+  else
+    assert (false);
+}
+
+
+
+ElementaryStream::ElementaryStream (IBitStream & ibs, OutputStream & into, stream_kind _kind):
+InputStream (ibs), muxinto (into), kind (_kind), buffer_min (INT_MAX), buffer_max (1)
+{
+}
+
+
+bool ElementaryStream::NextAU ()
+{
+  Aunit *
+    p_au =
+    next ();
+
+  if (p_au != NULL) {
+    au = p_au;
+    au_unsent = p_au->length;
+    return true;
+  } else {
+    au_unsent = 0;
+    return false;
+  }
+}
+
+
+Aunit *
+ElementaryStream::Lookahead ()
+{
+  return aunits.lookahead ();
+}
+
+unsigned int
+ElementaryStream::BytesToMuxAUEnd (unsigned int sector_transport_size)
+{
+  return (au_unsent / min_packet_data) * sector_transport_size +
+    (au_unsent % min_packet_data) + (sector_transport_size - min_packet_data);
+}
+
+
+/******************************************************************
+ *     ElementaryStream::ReadPacketPayload
+ *
+ *  Reads the stream data from actual input stream, updates decode
+ *  buffer model and current access unit information from the
+ *  look-ahead scanning buffer to account for bytes_muxed bytes being
+ *  muxed out.  Particular important is the maintenance of "au_unsent"
+ *  the count of how much data in the current AU remains umuxed.  It
+ *  not only allows us to keep track of AU's but is also used for
+ *  generating substream headers
+ *
+ *  Unless we need to over-ride it to handle sub-stream headers
+ * The packet payload for an elementary stream is simply the parsed and
+ * spliced buffered stream data..
+ *
+ ******************************************************************/
+
+
+
+unsigned int
+ElementaryStream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  unsigned int actually_read = bs.read_buffered_bytes (dst, to_read);
+
+  Muxed (actually_read);
+  return actually_read;
+}
+
+
+
+
+void
+ElementaryStream::Muxed (unsigned int bytes_muxed)
+{
+  clockticks decode_time;
+
+  if (bytes_muxed == 0 || MuxCompleted ())
+    return;
+
+
+  /* Work through what's left of the current AU and the following AU's
+     updating the info until we reach a point where an AU had to be
+     split between packets.
+     NOTE: It *is* possible for this loop to iterate. 
+
+     The DTS/PTS field for the packet in this case would have been
+     given the that for the first AU to start in the packet.
+     Whether Joe-Blow's hardware VCD player handles this properly is
+     another matter of course!
+   */
+
+  decode_time = RequiredDTS ();
+  while (au_unsent < bytes_muxed) {
+
+    bufmodel.Queued (au_unsent, decode_time);
+    bytes_muxed -= au_unsent;
+    if (!NextAU ())
+      return;
+    new_au_next_sec = true;
+    decode_time = RequiredDTS ();
+  };
+
+  // We've now reached a point where the current AU overran or
+  // fitted exactly.  We need to distinguish the latter case
+  // so we can record whether the next packet starts with an
+  // existing AU or not - info we need to decide what PTS/DTS
+  // info to write at the start of the next packet.
+
+  if (au_unsent > bytes_muxed) {
+
+    bufmodel.Queued (bytes_muxed, decode_time);
+    au_unsent -= bytes_muxed;
+    new_au_next_sec = false;
+  } else                       //  if (au_unsent == bytes_muxed)
+  {
+    bufmodel.Queued (bytes_muxed, decode_time);
+    if (!NextAU ())
+      return;
+    new_au_next_sec = true;
+  }
+
+}
+
+bool
+ElementaryStream::MuxPossible (clockticks currentSCR)
+{
+  return (!RunOutComplete () && bufmodel.Space () > max_packet_data);
+}
+
+
+void
+ElementaryStream::UpdateBufferMinMax ()
+{
+  buffer_min = buffer_min < (int) bufmodel.Space ()? buffer_min : bufmodel.Space ();
+  buffer_max = buffer_max > (int) bufmodel.Space ()? buffer_max : bufmodel.Space ();
+}
+
+
+
+void
+ElementaryStream::AllDemuxed ()
+{
+  bufmodel.Flushed ();
+}
+
+void
+ElementaryStream::DemuxedTo (clockticks SCR)
+{
+  bufmodel.Cleaned (SCR);
+}
+
+bool
+ElementaryStream::MuxCompleted ()
+{
+  return au_unsent == 0;
+}
+
+void
+ElementaryStream::SetSyncOffset (clockticks sync_offset)
+{
+  timestamp_delay = sync_offset;
+}
+
+Aunit *
+ElementaryStream::next ()
+{
+  Aunit *res;
+
+  while (AUBufferNeedsRefill ()) {
+    FillAUbuffer (FRAME_CHUNK);
+  }
+  res = aunits.next ();
+  return res;
+}
+\f
+
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/inputstrm.hh b/ext/mplex/inputstrm.hh
new file mode 100644 (file)
index 0000000..a424749
--- /dev/null
@@ -0,0 +1,278 @@
+
+/*
+ *  inptstrm.hh:  Input stream classes for MPEG multiplexing
+ *  TODO: Split into the base classes and the different types of
+ *  actual input stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#ifndef __INPUTSTRM_H__
+#define __INPUTSTRM_H__
+
+#include <config.h>
+#include <stdio.h>
+#include <vector>
+#include <sys/stat.h>
+
+#include "mjpeg_types.h"
+#include "mpegconsts.h"
+#include "format_codes.h"
+#include "mjpeg_logging.h"
+
+#include "mplexconsts.hh"
+#include "bits.hh"
+#include "aunit.hh"
+#include "vector.hh"
+#include "buffer.hh"
+
+
+class InputStream
+{
+public:
+  InputStream (IBitStream & istream):bs (istream),
+    eoscan (false), stream_length (0), last_buffered_AU (0), decoding_order (0), old_frames (0)
+  {
+  }
+
+  void SetBufSize (unsigned int buf_size)
+  {
+    bs.SetBufSize (buf_size);
+  }
+
+protected:
+  IBitStream & bs;
+  bool eoscan;
+  bitcount_t stream_length;
+  off_t file_length;
+
+  unsigned int last_buffered_AU;       // decode seq num of last buffered frame + 1
+  bitcount_t AU_start;
+  uint32_t syncword;
+  bitcount_t prev_offset;
+  unsigned int decoding_order;
+  unsigned int old_frames;
+
+};
+
+//
+// Abstract forward reference...
+//
+
+class OutputStream;
+
+
+class MuxStream
+{
+public:
+  MuxStream ();
+
+  void Init (const int strm_id,
+            const unsigned int _buf_scale,
+            const unsigned int buf_size,
+            const unsigned int _zero_stuffing, const bool bufs_in_first, const bool always_bufs);
+
+  unsigned int BufferSizeCode ();
+  inline unsigned int BufferSize ()
+  {
+    return buffer_size;
+  }
+  inline unsigned int BufferScale ()
+  {
+    return buffer_scale;
+  }
+
+
+  inline void SetMaxPacketData (unsigned int max)
+  {
+    max_packet_data = max;
+  }
+  inline void SetMinPacketData (unsigned int min)
+  {
+    min_packet_data = min;
+  }
+  inline unsigned int MaxPacketData ()
+  {
+    return max_packet_data;
+  }
+  inline unsigned int MinPacketData ()
+  {
+    return min_packet_data;
+  }
+  inline bool NewAUNextSector ()
+  {
+    return new_au_next_sec;
+  }
+
+  //
+  //  Read the next packet payload (sub-stream headers plus
+  //  parsed and spliced stream data) for a packet with the
+  //  specified payload capacity.  Update the AU info.
+  //
+
+  virtual unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read) = 0;
+
+  //
+  // Return the size of the substream headers...
+  //
+  virtual unsigned int StreamHeaderSize ()
+  {
+    return 0;
+  }
+
+public:                        // TODO should go protected once encapsulation complete
+  int stream_id;
+  unsigned int buffer_scale;
+  unsigned int buffer_size;
+  BufferModel bufmodel;
+  unsigned int max_packet_data;
+  unsigned int min_packet_data;
+  unsigned int zero_stuffing;
+  unsigned int nsec;
+  bool buffers_in_header;
+  bool always_buffers_in_header;
+  bool new_au_next_sec;
+  bool init;
+};
+
+class DummyMuxStream:public MuxStream
+{
+public:
+  DummyMuxStream (const int strm_id, const unsigned int buf_scale, unsigned int buf_size)
+  {
+    stream_id = strm_id;
+    buffer_scale = buf_scale;
+    buffer_size = buf_size;
+  }
+
+  unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+  {
+    abort ();
+    return 0;
+  }
+};
+
+
+class ElementaryStream:public InputStream, public MuxStream
+{
+public:
+  enum stream_kind
+  { audio, video, dummy };
+    ElementaryStream (IBitStream & ibs, OutputStream & into, stream_kind kind);
+  virtual void Close () = 0;
+
+  bool NextAU ();
+  Aunit *Lookahead ();
+  unsigned int BytesToMuxAUEnd (unsigned int sector_transport_size);
+  bool MuxCompleted ();
+  virtual bool MuxPossible (clockticks currentSCR);
+  void DemuxedTo (clockticks SCR);
+  void SetTSOffset (clockticks baseTS);
+  void AllDemuxed ();
+  inline stream_kind Kind ()
+  {
+    return kind;
+  }
+  inline int BufferMin ()
+  {
+    return buffer_min;
+  }
+  inline int BufferMax ()
+  {
+    return buffer_max;
+  }
+  inline clockticks RequiredDTS ()
+  {
+    return au->DTS + timestamp_delay;
+  };
+  inline clockticks RequiredPTS ()
+  {
+    return au->PTS + timestamp_delay;
+  };
+  inline clockticks NextRequiredDTS ()
+  {
+    Aunit *next = Lookahead ();
+
+    if (next != 0)
+      return next->DTS + timestamp_delay;
+    else
+      return 0;
+  };
+  inline clockticks NextRequiredPTS ()
+  {
+    Aunit *next = Lookahead ();
+
+    if (next != 0)
+      return next->PTS + timestamp_delay;
+    else
+      return 0;
+  };
+
+  void UpdateBufferMinMax ();
+
+  void SetSyncOffset (clockticks timestamp_delay);
+
+
+  inline bool BuffersInHeader ()
+  {
+    return buffers_in_header;
+  }
+  virtual unsigned int NominalBitRate () = 0;
+  virtual bool RunOutComplete () = 0;
+  virtual void OutputSector () = 0;
+
+  virtual unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+
+
+protected:
+  virtual void FillAUbuffer (unsigned int frames_to_buffer) = 0;
+  virtual void InitAUbuffer () = 0;
+  virtual bool AUBufferNeedsRefill () = 0;
+  AUStream aunits;
+  void Muxed (unsigned int bytes_muxed);
+
+public:                        // TODO should go protected once encapsulation complete
+  // N.b. currently length=0 is used to indicate an ended
+  // stream.
+  // au itself should simply disappear
+  Aunit * au;
+  clockticks timestamp_delay;
+
+protected:
+  unsigned int au_unsent;
+  Aunit *next ();
+
+  OutputStream & muxinto;
+  stream_kind kind;
+  int buffer_min;
+  int buffer_max;
+  int FRAME_CHUNK;
+
+};
+
+
+
+#endif // __INPUTSTRM_H__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/interact.cc b/ext/mplex/interact.cc
new file mode 100644 (file)
index 0000000..2191d55
--- /dev/null
@@ -0,0 +1,196 @@
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#include <sys/stat.h>
+
+#include <mjpeg_logging.h>
+#include <format_codes.h>
+
+#include "interact.hh"
+#include "videostrm.hh"
+#include "audiostrm.hh"
+#include "mplexconsts.hh"
+
+
+#if 0
+int opt_verbosity = 1;
+int opt_buffer_size = 46;
+int opt_data_rate = 0;         /* 3486 = 174300B/sec would be right for VCD */
+int opt_video_offset = 0;
+int opt_audio_offset = 0;
+int opt_sector_size = 2324;
+int opt_VBR = 0;
+int opt_mpeg = 1;
+int opt_mux_format = 0;                /* Generic MPEG-1 stream as default */
+int opt_multifile_segment = 0;
+int opt_always_system_headers = 0;
+int opt_packets_per_pack = 20;
+bool opt_ignore_underrun = false;
+off_t opt_max_segment_size = 0;
+
+/*************************************************************************
+    Startbildschirm und Anzahl der Argumente
+
+    Intro Screen and argument check
+*************************************************************************/
+
+static void
+Usage (char *str)
+{
+  fprintf (stderr,
+          "mjpegtools mplex version " VERSION "\n"
+          "Usage: %s [params] -o <output filename pattern> <input file>... \n"
+          "         %%d in the output file name is by segment count\n"
+          "  where possible params are:\n"
+          "--verbose|-v num\n"
+          "  Level of verbosity. 0 = quiet, 1 = normal 2 = verbose/debug\n"
+          "--format|-f fmt\n"
+          "  Set defaults for particular MPEG profiles\n"
+          "  [0 = Generic MPEG1, 1 = VCD, 2 = user-rate VCD, 3 = Generic MPEG2,\n"
+          "   4 = SVCD, 5 = user-rate SVCD\n"
+          "   6 = VCD Stills, 7 = SVCD Stills, 8 = DVD]\n"
+          "--mux-bitrate|-r num\n"
+          "  Specify data rate of output stream in kbit/sec\n"
+          "    (default 0=Compute from source streams)\n"
+          "--video-buffer|-b num\n"
+          "  Specifies decoder buffers size in kB.  [ 20...2000]\n"
+          "--mux-limit|-l num\n"
+          "  Multiplex only num seconds of material (default 0=multiplex all)\n"
+          "--sync-offset|-O num\n"
+          "  Specify offset of timestamps (video-audio) in mSec\n"
+          "--sector-size|-s num\n"
+          "  Specify sector size in bytes for generic formats [256..16384]\n"
+          "--vbr|-V\n"
+          "  Multiplex variable bit-rate video\n"
+          "--packets-per-pack|-p num\n"
+          "  Number of packets per pack generic formats [1..100]\n"
+          "--system-headers|-h\n"
+          "  Create System header in every pack in generic formats\n"
+          "--max-segment-size|-S size\n"
+          "  Maximum size of output file(s) in Mbyte (default: 2000) (0 = no limit)\n"
+          "--split-segment|-M\n"
+          "  Simply split a sequence across files rather than building run-out/run-in\n"
+          "--help|-?\n" "  Print this lot out!\n", str);
+  exit (1);
+}
+
+static const char short_options[] = "o:b:r:O:v:m:f:l:s:S:q:p:VXMeh";
+
+#if defined(HAVE_GETOPT_LONG)
+static struct option long_options[] = {
+  {"verbose", 1, 0, 'v'},
+  {"format", 1, 0, 'f'},
+  {"mux-bitrate", 1, 0, 'r'},
+  {"video-buffer", 1, 0, 'b'},
+  {"output", 1, 0, 'o'},
+  {"sync-offset", 1, 0, 'O'},
+  {"vbr", 1, 0, 'V'},
+  {"system-headers", 1, 0, 'h'},
+  {"split-segment", 0, &opt_multifile_segment, 1},
+  {"max-segment-size", 1, 0, 'S'},
+  {"mux-upto", 1, 0, 'l'},
+  {"packets-per-pack", 1, 0, 'p'},
+  {"sector-size", 1, 0, 's'},
+  {"help", 0, 0, '?'},
+  {0, 0, 0, 0}
+};
+#endif
+
+int
+intro_and_options (int argc, char *argv[], char **multplex_outfile)
+{
+  int n;
+
+#if defined(HAVE_GETOPT_LONG)
+  while ((n = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)
+#else
+  while ((n = getopt (argc, argv, short_options)) != -1)
+#endif
+  {
+    switch (n) {
+      case 0:
+       break;
+      case 'm':
+       opt_mpeg = atoi (optarg);
+       if (opt_mpeg < 1 || opt_mpeg > 2)
+         Usage (argv[0]);
+
+       break;
+      case 'v':
+       opt_verbosity = atoi (optarg);
+       if (opt_verbosity < 0 || opt_verbosity > 2)
+         Usage (argv[0]);
+       break;
+
+      case 'V':
+       opt_VBR = 1;
+       break;
+
+      case 'h':
+       opt_always_system_headers = 1;
+       break;
+
+      case 'b':
+       opt_buffer_size = atoi (optarg);
+       if (opt_buffer_size < 0 || opt_buffer_size > 1000)
+         Usage (argv[0]);
+       break;
+
+      case 'r':
+       opt_data_rate = atoi (optarg);
+       if (opt_data_rate < 0)
+         Usage (argv[0]);
+       /* Convert from kbit/sec (user spec) to 50B/sec units... */
+       opt_data_rate = ((opt_data_rate * 1000 / 8 + 49) / 50) * 50;
+       break;
+
+      case 'O':
+       opt_video_offset = atoi (optarg);
+       if (opt_video_offset < 0) {
+         opt_audio_offset = -opt_video_offset;
+         opt_video_offset = 0;
+       }
+       break;
+
+      case 'p':
+       opt_packets_per_pack = atoi (optarg);
+       if (opt_packets_per_pack < 1 || opt_packets_per_pack > 100)
+         Usage (argv[0]);
+       break;
+
+
+      case 'f':
+       opt_mux_format = atoi (optarg);
+       if (opt_mux_format != MPEG_FORMAT_DVD &&
+           (opt_mux_format < MPEG_FORMAT_MPEG1 || opt_mux_format > MPEG_FORMAT_LAST)
+         )
+         Usage (argv[0]);
+       break;
+      case 's':
+       opt_sector_size = atoi (optarg);
+       if (opt_sector_size < 256 || opt_sector_size > 16384)
+         Usage (argv[0]);
+       break;
+      case 'S':
+       opt_max_segment_size = atoi (optarg);
+       if (opt_max_segment_size < 0)
+         Usage (argv[0]);
+       opt_max_segment_size *= 1024 * 1024;
+       break;
+      case 'M':
+       opt_multifile_segment = 1;
+       break;
+      case '?':
+      default:
+       Usage (argv[0]);
+       break;
+    }
+  }
+  (void) mjpeg_default_handler_verbosity (opt_verbosity);
+  mjpeg_info ("mplex version %s (%s)", MPLEX_VER, MPLEX_DATE);
+  return optind - 1;
+}
+#endif
diff --git a/ext/mplex/lpcmstrm_in.cc b/ext/mplex/lpcmstrm_in.cc
new file mode 100644 (file)
index 0000000..2e73a62
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ *  lpcmstrm_in.c: LPCM Audio strem class members handling scanning and
+ *  buffering raw input stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *  Copyright (C) 2000,2001 Brent Byeler for original header-structure
+ *                          parsing code.
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "audiostrm.hh"
+#include "outputstream.hh"
+
+
+
+
+LPCMStream::LPCMStream (IBitStream & ibs, OutputStream & into):
+AudioStream (ibs, into)
+{
+}
+
+bool LPCMStream::Probe (IBitStream & bs)
+{
+  return true;
+}
+
+
+/*************************************************************************
+ *
+ * Reads initial stream parameters and displays feedback banner to users
+ *
+ *************************************************************************/
+
+
+void
+LPCMStream::Init (const int stream_num)
+{
+
+  MuxStream::Init (PRIVATE_STR_1, 1,   // Buffer scale
+                  default_buffer_size,
+                  muxinto.vcd_zero_stuffing,
+                  muxinto.buffers_in_audio, muxinto.always_buffers_in_audio);
+  mjpeg_info ("Scanning for header info: LPCM Audio stream %02x", stream_num);
+
+  InitAUbuffer ();
+
+  AU_start = bs.bitcount ();
+
+  // This is a dummy debug version that simply assumes 48kHz
+  // two channel 16 bit sample LPCM
+  samples_per_second = 48000;
+  channels = 2;
+  bits_per_sample = 16;
+  bytes_per_frame =
+    samples_per_second * channels * bits_per_sample / 8 * ticks_per_frame_90kHz / 90000;
+  frame_index = 0;
+  dynamic_range_code = 0x80;
+
+  /* Presentation/decoding time-stamping  */
+  access_unit.start = AU_start;
+  access_unit.length = bytes_per_frame;
+  access_unit.PTS = static_cast < clockticks > (decoding_order) *
+    (CLOCKS_per_90Kth_sec * ticks_per_frame_90kHz);
+  access_unit.DTS = access_unit.PTS;
+  access_unit.dorder = decoding_order;
+  decoding_order++;
+  aunits.append (access_unit);
+
+  OutputHdrInfo ();
+}
+
+unsigned int
+LPCMStream::NominalBitRate ()
+{
+  return samples_per_second * channels * bits_per_sample;
+}
+
+
+
+void
+LPCMStream::FillAUbuffer (unsigned int frames_to_buffer)
+{
+  last_buffered_AU += frames_to_buffer;
+  mjpeg_debug ("Scanning %d MPEG LPCM audio frames to frame %d",
+              frames_to_buffer, last_buffered_AU);
+
+  static int header_skip = 0;  // Initially skipped past  5 bytes of header 
+  int skip;
+  bool bad_last_frame = false;
+
+  while (!bs.eos () &&
+        decoding_order < last_buffered_AU) {
+    skip = access_unit.length - header_skip;
+    mjpeg_debug ("Buffering frame %d (%d bytes)\n", decoding_order - 1, skip);
+    if (skip & 0x1)
+      bs.getbits (8);
+    if (skip & 0x2)
+      bs.getbits (16);
+    skip = skip >> 2;
+
+    for (int i = 0; i < skip; i++) {
+      bs.getbits (32);
+    }
+
+    prev_offset = AU_start;
+    AU_start = bs.bitcount ();
+    if (AU_start - prev_offset != access_unit.length * 8) {
+      bad_last_frame = true;
+      break;
+    }
+    // Here we would check for header data but LPCM has no headers...
+    if (bs.eos ())
+      break;
+
+    access_unit.start = AU_start;
+    access_unit.length = bytes_per_frame;
+    access_unit.PTS = static_cast < clockticks > (decoding_order) *
+      (CLOCKS_per_90Kth_sec * ticks_per_frame_90kHz);
+    access_unit.DTS = access_unit.PTS;
+    access_unit.dorder = decoding_order;
+    decoding_order++;
+    aunits.append (access_unit);
+    num_frames[0]++;
+
+    num_syncword++;
+
+    if (num_syncword >= old_frames + 10) {
+      mjpeg_debug ("Got %d frame headers.", num_syncword);
+      old_frames = num_syncword;
+    }
+    mjpeg_debug ("Got frame %d\n", decoding_order);
+
+  }
+  if (bad_last_frame) {
+    mjpeg_error_exit1 ("Last LPCM frame ended prematurely!\n");
+  }
+  last_buffered_AU = decoding_order;
+  eoscan = bs.eos ();
+
+}
+
+
+
+void
+LPCMStream::Close ()
+{
+  stream_length = AU_start / 8;
+  mjpeg_info ("AUDIO_STATISTICS: %02x", stream_id);
+  mjpeg_info ("Audio stream length %lld bytes.", stream_length);
+  mjpeg_info ("Frames         : %8u ", num_frames[0]);
+  bs.close ();
+}
+
+/*************************************************************************
+       OutputAudioInfo
+       gibt gesammelte Informationen zu den Audio Access Units aus.
+
+       Prints information on audio access units
+*************************************************************************/
+
+void
+LPCMStream::OutputHdrInfo ()
+{
+  mjpeg_info ("LPCM AUDIO STREAM:");
+
+  mjpeg_info ("Bit rate       : %8u bytes/sec (%3u kbit/sec)",
+             NominalBitRate () / 8, NominalBitRate ());
+  mjpeg_info ("Channels       :     %d\n", channels);
+  mjpeg_info ("Bits per sample:     %d\n", bits_per_sample);
+  mjpeg_info ("Frequency      :     %d Hz", samples_per_second);
+
+}
+
+
+unsigned int
+LPCMStream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  unsigned int header_size = LPCMStream::StreamHeaderSize ();
+  unsigned int bytes_read = bs.read_buffered_bytes (dst + header_size,
+                                                   to_read - header_size);
+  clockticks decode_time;
+  bool starting_frame_found = false;
+  uint8_t starting_frame_index = 0;
+
+  int starting_frame_offset = (new_au_next_sec || au_unsent > bytes_read)
+    ? 0 : au_unsent;
+
+  unsigned int frames = 0;
+  unsigned int bytes_muxed = bytes_read;
+
+  if (bytes_muxed == 0 || MuxCompleted ()) {
+    goto completion;
+  }
+
+
+  /* Work through what's left of the current frames and the
+     following frames's updating the info until we reach a point where
+     an frame had to be split between packets. 
+
+     The DTS/PTS field for the packet in this case would have been
+     given the that for the first AU to start in the packet.
+
+   */
+
+  decode_time = RequiredDTS ();
+  while (au_unsent < bytes_muxed) {
+    assert (bytes_muxed > 1);
+    bufmodel.Queued (au_unsent, decode_time);
+    bytes_muxed -= au_unsent;
+    if (new_au_next_sec) {
+      ++frames;
+      if (!starting_frame_found) {
+       starting_frame_index = static_cast < uint8_t > (au->dorder % 20);
+       starting_frame_found = true;
+      }
+    }
+    if (!NextAU ()) {
+      goto completion;
+    }
+    new_au_next_sec = true;
+    decode_time = RequiredDTS ();
+  };
+
+  // We've now reached a point where the current AU overran or
+  // fitted exactly.  We need to distinguish the latter case so we
+  // can record whether the next packet starts with the tail end of
+  // // an already started frame or a new one. We need this info to
+  // decide what PTS/DTS info to write at the start of the next
+  // packet.
+
+  if (au_unsent > bytes_muxed) {
+    if (new_au_next_sec)
+      ++frames;
+    bufmodel.Queued (bytes_muxed, decode_time);
+    au_unsent -= bytes_muxed;
+    new_au_next_sec = false;
+  } else                       //  if (au_unsent == bytes_muxed)
+  {
+    bufmodel.Queued (bytes_muxed, decode_time);
+    if (new_au_next_sec)
+      ++frames;
+    new_au_next_sec = NextAU ();
+  }
+completion:
+  // Generate the LPCM header...
+  // Note the index counts from the low byte of the offset so
+  // the smallest value is 1!
+  dst[0] = LPCM_SUB_STR_0 + stream_num;
+  dst[1] = frames;
+  dst[2] = (starting_frame_offset + 1) >> 8;
+  dst[3] = (starting_frame_offset + 1) & 0xff;
+  unsigned int bps_code;
+
+  switch (bits_per_sample) {
+    case 16:
+      bps_code = 0;
+      break;
+    case 20:
+      bps_code = 1;
+      break;
+    case 24:
+      bps_code = 2;
+      break;
+    default:
+      bps_code = 3;
+      break;
+  }
+  dst[4] = starting_frame_index;
+  unsigned int bsf_code = (samples_per_second == 48000) ? 0 : 1;
+  unsigned int channels_code = channels - 1;
+
+  dst[5] = (bps_code << 6) | (bsf_code << 4) | channels_code;
+  dst[6] = dynamic_range_code;
+  return bytes_read + header_size;
+}
+\f
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mjpeg_logging.cc b/ext/mplex/mjpeg_logging.cc
new file mode 100644 (file)
index 0000000..db4b0c0
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+    $Id$
+
+    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+\f
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+extern int fred;
+
+#include "mjpeg_logging.h"
+
+static const char _rcsid[] = "$Id: ";
+
+#define MAX_DEFAULT_ID_SIZE 16
+#define DEFAULT_DEFAULT_ID "???"
+
+#ifdef HAVE___PROGNAME
+extern const char *__progname;
+#endif
+
+static log_level_t mjpeg_log_verbosity = (log_level_t) 0;
+static char default_handler_id[MAX_DEFAULT_ID_SIZE];
+static char default_handler_id_is_set = 0;
+
+static int
+default_mjpeg_log_filter (log_level_t level)
+{
+  int verb_from_env;
+
+  if (mjpeg_log_verbosity == 0) {
+    char *mjpeg_verb_env = getenv ("MJPEG_VERBOSITY");
+
+    if (mjpeg_verb_env != NULL) {
+      verb_from_env = LOG_WARN - atoi (mjpeg_verb_env);
+      if (verb_from_env >= LOG_DEBUG && verb_from_env <= LOG_ERROR)
+       mjpeg_log_verbosity = (log_level_t) verb_from_env;
+    }
+  }
+  return (level < LOG_WARN && level < mjpeg_log_verbosity);
+}
+
+static mjpeg_log_filter_t _filter = default_mjpeg_log_filter;
+
+static void
+default_mjpeg_log_handler (log_level_t level, const char message[])
+{
+  const char *ids;
+
+  if ((*_filter) (level))
+    return;
+  if (default_handler_id_is_set) {
+    ids = default_handler_id;
+  } else {
+#ifdef HAVE___PROGNAME
+    ids = __progname;
+#else
+    ids = DEFAULT_DEFAULT_ID;
+#endif
+  }
+  switch (level) {
+    case LOG_ERROR:
+      fprintf (stderr, "**ERROR: [%s] %s\n", ids, message);
+      break;
+    case LOG_DEBUG:
+      fprintf (stderr, "--DEBUG: [%s] %s\n", ids, message);
+      break;
+    case LOG_WARN:
+      fprintf (stderr, "++ WARN: [%s] %s\n", ids, message);
+      break;
+    case LOG_INFO:
+      fprintf (stderr, "   INFO: [%s] %s\n", ids, message);
+      break;
+    default:
+      assert (0);
+  }
+}
+
+static mjpeg_log_handler_t _handler = default_mjpeg_log_handler;
+
+
+mjpeg_log_handler_t
+mjpeg_log_set_handler (mjpeg_log_handler_t new_handler)
+{
+  mjpeg_log_handler_t old_handler = _handler;
+
+  _handler = new_handler;
+
+  return old_handler;
+}
+
+/***************
+ *
+ * Set default log handlers degree of verboseity.
+ * 0 = quiet, 1 = info, 2 = debug
+ *
+ *************/
+
+int
+mjpeg_default_handler_verbosity (int verbosity)
+{
+  int prev_verb = mjpeg_log_verbosity;
+
+  mjpeg_log_verbosity = (log_level_t) (LOG_WARN - (log_level_t) verbosity);
+  return prev_verb;
+}
+
+/*
+ * Set identifier string used by default handler
+ *
+ */
+int
+mjpeg_default_handler_identifier (const char *new_id)
+{
+  const char *s;
+
+  if (new_id == NULL) {
+    default_handler_id_is_set = 0;
+    return 0;
+  }
+  /* find basename of new_id (remove any directory prefix) */
+  if ((s = strrchr (new_id, '/')) == NULL)
+    s = new_id;
+  else
+    s = s + 1;
+  strncpy (default_handler_id, s, MAX_DEFAULT_ID_SIZE);
+  default_handler_id[MAX_DEFAULT_ID_SIZE - 1] = '\0';
+  default_handler_id_is_set = 1;
+  return 0;
+}
+
+
+static void
+mjpeg_logv (log_level_t level, const char format[], va_list args)
+{
+  char buf[1024] = { 0, };
+
+  /* TODO: Original had a re-entrancy error trap to assist bug
+     finding.  To make this work with multi-threaded applications a
+     lock is needed hence delete.
+   */
+
+
+  vsnprintf (buf, sizeof (buf) - 1, format, args);
+
+  _handler (level, buf);
+}
+
+void
+mjpeg_log (log_level_t level, const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (level, format, args);
+  va_end (args);
+}
+
+void
+mjpeg_debug (const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (LOG_DEBUG, format, args);
+  va_end (args);
+}
+
+void
+mjpeg_info (const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (LOG_INFO, format, args);
+  va_end (args);
+}
+
+void
+mjpeg_warn (const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (LOG_WARN, format, args);
+  va_end (args);
+}
+
+void
+mjpeg_error (const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (LOG_ERROR, format, args);
+  va_end (args);
+}
+
+void
+mjpeg_error_exit1 (const char format[], ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  mjpeg_logv (LOG_ERROR, format, args);
+  va_end (args);
+  exit (EXIT_FAILURE);
+}
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 8
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mjpeg_logging.h b/ext/mplex/mjpeg_logging.h
new file mode 100644 (file)
index 0000000..3028f89
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+    $Id$
+
+    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+\f
+#ifndef __MJPEG_LOGGING_H__
+#define __MJPEG_LOGGING_H__
+
+#include "mjpeg_types.h"
+
+typedef enum {
+  LOG_DEBUG = 1,
+  LOG_INFO,
+  LOG_WARN,
+  LOG_ERROR
+} log_level_t;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void
+mjpeg_log(log_level_t level, const char format[], ...) GNUC_PRINTF(2, 3);
+
+typedef int(*mjpeg_log_filter_t)(log_level_t level);
+    
+typedef void(*mjpeg_log_handler_t)(log_level_t level, const char message[]);
+
+mjpeg_log_handler_t
+mjpeg_log_set_handler(mjpeg_log_handler_t new_handler);
+
+int
+mjpeg_default_handler_identifier(const char *new_id);
+
+int
+mjpeg_default_handler_verbosity(int verbosity);
+
+void
+mjpeg_debug(const char format[], ...) GNUC_PRINTF(1,2);
+
+void
+mjpeg_info(const char format[], ...) GNUC_PRINTF(1,2);
+
+void
+mjpeg_warn(const char format[], ...) GNUC_PRINTF(1,2);
+
+void
+mjpeg_error(const char format[], ...) GNUC_PRINTF(1,2);
+
+void
+mjpeg_error_exit1(const char format[], ...) GNUC_PRINTF(1,2);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __MJPEG_LOGGING_H__ */
+
+\f
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 8
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mjpeg_types.h b/ext/mplex/mjpeg_types.h
new file mode 100644 (file)
index 0000000..21edb9a
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    $Id$
+
+    Copyright (C) 2000 Herbert Valerio Riedel <hvr@gnu.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+\f
+#ifndef __MJPEG_TYPES_H__
+#define __MJPEG_TYPES_H__
+//#include <config.h>
+
+#if defined(HAVE_STDINT_H)
+# include <stdint.h>
+#elif defined(HAVE_INTTYPES_H)
+# include <inttypes.h>
+#elif defined(__CYGWIN__)
+# include <sys/types.h>
+typedef u_int8_t uint8_t;
+typedef u_int16_t uint16_t;
+typedef u_int32_t uint32_t;
+typedef u_int64_t uint64_t;
+# define INT8_C(c)     c
+# define INT16_C(c)    c
+# define INT32_C(c)    c
+# define INT64_C(c)    c ## LL
+# define UINT8_C(c)    c ## U
+# define UINT16_C(c)   c ## U
+# define UINT32_C(c)   c ## U
+# define UINT64_C(c)   c ## ULL
+#else
+/* warning ISO/IEC 9899:1999 <stdint.h> was missing and even <inttypes.h> */
+/* fixme */
+/* (Ronald) we'll just give an error now...Better solutions might come later */
+#error You don't seem to have sys/types.h, inttypes.h or stdint.h! \
+This might mean two things: \
+Either you really don't have them, in which case you should \
+install the system headers and/or C-library headers. \
+You might also have forgotten to define whether you have them. \
+You can do this by either defining their presence before including \
+mjpegtools' header files (e.g. "#define HAVE_STDINT_H"), or you can check \
+for their presence in a configure script. mjpegtools' configure \
+script is a good example of how to do this. You need to check for \
+PRId64, stdbool.h, inttypes.h, stdint.h and sys/types.h
+#endif /* HAVE_STDINT_H */
+
+#if defined(__FreeBSD__)
+#include <sys/types.h> /* FreeBSD - ssize_t */
+#endif
+
+#if defined(HAVE_STDBOOL_H) && !defined(__cplusplus)
+#include <stdbool.h>
+#else
+/* ISO/IEC 9899:1999 <stdbool.h> missing -- enabling workaround */
+
+# ifndef __cplusplus
+typedef enum
+  {
+    false = 0,
+    true = 1
+  } locBool;
+
+#  define false   false
+#  define true    true
+#  define bool locBool
+# endif
+#endif
+
+#ifndef PRId64
+#define PRId64 PRID64_STRING_FORMAT
+#endif
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define GNUC_PRINTF( format_idx, arg_idx )    \
+  __attribute__((format (printf, format_idx, arg_idx)))
+#define GNUC_SCANF( format_idx, arg_idx )     \
+  __attribute__((format (scanf, format_idx, arg_idx)))
+#define GNUC_FORMAT( arg_idx )                \
+  __attribute__((format_arg (arg_idx)))
+#define GNUC_NORETURN                         \
+  __attribute__((noreturn))
+#define GNUC_CONST                            \
+  __attribute__((const))
+#define GNUC_UNUSED                           \
+  __attribute__((unused))
+#define GNUC_PACKED                           \
+  __attribute__((packed))
+#else   /* !__GNUC__ */
+#define GNUC_PRINTF( format_idx, arg_idx )
+#define GNUC_SCANF( format_idx, arg_idx )
+#define GNUC_FORMAT( arg_idx )
+#define GNUC_NORETURN
+#define GNUC_CONST
+#define GNUC_UNUSED
+#define GNUC_PACKED
+#endif  /* !__GNUC__ */
+
+
+#endif /* __MJPEG_TYPES_H__ */
+
+\f
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 8
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mpastrm_in.cc b/ext/mplex/mpastrm_in.cc
new file mode 100644 (file)
index 0000000..ea7c32f
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ *  audiostrm_in.c: MPEG Audio strem class members handling scanning
+ *  and buffering raw input stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "audiostrm.hh"
+#include "outputstream.hh"
+
+
+static const char *mpa_audio_version[4] = {
+  "2.5",
+  "2.0",
+  "reserved",
+  "1.0"
+};
+
+static const unsigned int mpa_bitrates_kbps[4][3][16] = {
+  {                            /* MPEG audio V2.5 */
+   {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
+   {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
+   {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
+   },
+  { /*RESERVED*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+   {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+   },
+  {                            /* MPEG audio V2 */
+   {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
+   {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0},
+   {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
+   },
+  {                            /* MPEG audio V1 */
+   {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
+   {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
+   {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0}
+   }
+
+};
+
+
+static const int mpa_freq_table[4][4] = {
+  /* MPEG audio V2.5 */
+  {11025, 12000, 8000, 0},
+  /* RESERVED */
+  {0, 0, 0, 0},
+  /* MPEG audio V2 */
+  {22050, 24000, 16000, 0},
+  /* MPEG audio V1 */
+  {44100, 48000, 32000, 0}
+};
+
+static const char mpa_stereo_mode[4][15] =
+  { "stereo", "joint stereo", "dual channel", "single channel" };
+static const char mpa_copyright_status[2][20] = { "no copyright", "copyright protected" };
+static const char mpa_original_bit[2][10] = { "copy", "original" };
+static const char mpa_emphasis_mode[4][20] =
+  { "none", "50/15 microseconds", "reserved", "CCITT J.17" };
+static const unsigned int mpa_slots[4] = { 12, 144, 144, 0 };
+static const unsigned int mpa_samples[4] = { 384, 1152, 1152, 0 };
+
+
+
+MPAStream::MPAStream (IBitStream & ibs, OutputStream & into):
+AudioStream (ibs, into)
+{
+}
+
+bool
+MPAStream::Probe (IBitStream & bs)
+{
+  return bs.getbits (11) == AUDIO_SYNCWORD;
+}
+
+
+/*************************************************************************
+ *
+ * Reads initial stream parameters and displays feedback banner to users
+ *
+ *************************************************************************/
+
+
+void
+MPAStream::Init (const int stream_num)
+{
+  int padding_bit;
+
+  MuxStream::Init (AUDIO_STR_0 + stream_num, 0,        // Buffer scale
+                  muxinto.audio_buffer_size,
+                  muxinto.vcd_zero_stuffing,
+                  muxinto.buffers_in_audio, muxinto.always_buffers_in_audio);
+  mjpeg_info ("Scanning for header info: Audio stream %02x", AUDIO_STR_0 + stream_num);
+
+  InitAUbuffer ();
+
+  /* A.Stevens 2000 - update to be compatible up to  MPEG2.5
+   */
+  AU_start = bs.bitcount ();
+  if (bs.getbits (11) == AUDIO_SYNCWORD) {
+    num_syncword++;
+    version_id = bs.getbits (2);
+    layer = 3 - bs.getbits (2);        /* 0..2 not 1..3!! */
+    protection = bs.get1bit ();
+    bit_rate_code = bs.getbits (4);
+    frequency = bs.getbits (2);
+    padding_bit = bs.get1bit ();
+    bs.get1bit ();
+    mode = bs.getbits (2);
+    mode_extension = bs.getbits (2);
+    copyright = bs.get1bit ();
+    original_copy = bs.get1bit ();
+    emphasis = bs.getbits (2);
+
+    framesize =
+      mpa_bitrates_kbps[version_id][layer][bit_rate_code] *
+      mpa_slots[layer] * 1000 / mpa_freq_table[version_id][frequency];
+
+    size_frames[0] = framesize;
+    size_frames[1] = framesize + (layer == 0 ? 4 : 1);
+    num_frames[padding_bit]++;
+    access_unit.start = AU_start;
+    access_unit.length = size_frames[padding_bit];
+
+    samples_per_second = mpa_freq_table[version_id][frequency];
+
+    /* Presentation time-stamping  */
+    access_unit.PTS = static_cast < clockticks > (decoding_order) *
+      static_cast < clockticks > (mpa_samples[layer]) *
+      static_cast < clockticks > (CLOCKS) / samples_per_second;
+    access_unit.DTS = access_unit.PTS;
+    access_unit.dorder = decoding_order;
+    ++decoding_order;
+    aunits.append (access_unit);
+
+  } else {
+    mjpeg_error ("Invalid MPEG Audio stream header.");
+    exit (1);
+  }
+
+
+  OutputHdrInfo ();
+}
+
+unsigned int
+MPAStream::NominalBitRate ()
+{
+  return mpa_bitrates_kbps[version_id][layer][bit_rate_code] * 128;
+}
+
+
+unsigned int
+MPAStream::SizeFrame (int rate_code, int padding)
+{
+  return mpa_bitrates_kbps[version_id][layer][rate_code] *
+    mpa_slots[layer] * 1000 / mpa_freq_table[version_id][frequency] + padding;
+}
+
+void
+MPAStream::FillAUbuffer (unsigned int frames_to_buffer)
+{
+  unsigned int i;
+  unsigned int padding_bit;
+
+  last_buffered_AU += frames_to_buffer;
+
+  mjpeg_debug ("Scanning %d MPEG audio frames to frame %d", frames_to_buffer, last_buffered_AU);
+
+  while (!bs.eos () &&
+        decoding_order < last_buffered_AU) {
+
+    skip = access_unit.length - 4;
+    if (skip & 0x1)
+      bs.getbits (8);
+    if (skip & 0x2)
+      bs.getbits (16);
+    skip = skip >> 2;
+
+    for (i = 0; i < skip; i++) {
+      bs.getbits (32);
+    }
+    prev_offset = AU_start;
+    AU_start = bs.bitcount ();
+
+    /* Check we have reached the end of have  another catenated 
+       stream to process before finishing ... */
+    if ((syncword = bs.getbits (11)) != AUDIO_SYNCWORD) {
+      if (!bs.eobs) {
+       /* There appears to be another catenated stream... */
+       int next;
+
+       mjpeg_warn ("End of component bit-stream ... seeking next");
+       /* Catenated stream must start on byte boundary */
+       syncword = (syncword << (8 - AU_start % 8));
+       next = bs.getbits (8 - (AU_start % 8));
+       AU_start = bs.bitcount () - 11;
+       syncword = syncword | next;
+       if (syncword != AUDIO_SYNCWORD) {
+         mjpeg_warn ("Failed to find start of next stream at %lld prev %lld !", AU_start / 8,
+                     prev_offset / 8);
+         break;
+       }
+      } else
+       /* No catenated stream... finished! */
+       break;
+    }
+    // Skip version_id:2, layer:2, protection:1
+    (void) bs.getbits (5);
+    int rate_code = bs.getbits (4);
+
+    // Skip frequency
+    (void) bs.getbits (2);
+
+    padding_bit = bs.get1bit ();
+    access_unit.start = AU_start;
+    access_unit.length = SizeFrame (rate_code, padding_bit);
+    access_unit.PTS =
+      static_cast < clockticks > (decoding_order) * static_cast < clockticks >
+      (mpa_samples[layer]) * static_cast < clockticks > (CLOCKS)
+      / samples_per_second;
+    access_unit.DTS = access_unit.PTS;
+    access_unit.dorder = decoding_order;
+    decoding_order++;
+    aunits.append (access_unit);
+    num_frames[padding_bit]++;
+
+    bs.getbits (9);
+
+    num_syncword++;
+
+    if (num_syncword >= old_frames + 10) {
+      mjpeg_debug ("Got %d frame headers.", num_syncword);
+      old_frames = num_syncword;
+
+    }
+
+
+
+  }
+  last_buffered_AU = decoding_order;
+  eoscan = bs.eos ();
+
+}
+
+
+
+void
+MPAStream::Close ()
+{
+  stream_length = AU_start >> 3;
+  mjpeg_info ("AUDIO_STATISTICS: %02x", stream_id);
+  mjpeg_info ("Audio stream length %lld bytes.", stream_length);
+  mjpeg_info ("Syncwords      : %8u", num_syncword);
+  mjpeg_info ("Frames         : %8u padded", num_frames[0]);
+  mjpeg_info ("Frames         : %8u unpadded", num_frames[1]);
+
+  bs.close ();
+}
+
+/*************************************************************************
+       OutputAudioInfo
+       gibt gesammelte Informationen zu den Audio Access Units aus.
+
+       Prints information on audio access units
+*************************************************************************/
+
+void
+MPAStream::OutputHdrInfo ()
+{
+  unsigned int bitrate;
+
+  bitrate = mpa_bitrates_kbps[version_id][layer][bit_rate_code];
+
+
+  mjpeg_info ("AUDIO STREAM:");
+  mjpeg_info ("Audio version  : %s", mpa_audio_version[version_id]);
+  mjpeg_info ("Layer          : %8u", layer + 1);
+
+  if (protection == 0)
+    mjpeg_info ("CRC checksums  :      yes");
+  else
+    mjpeg_info ("CRC checksums  :       no");
+
+  if (bit_rate_code == 0)
+    mjpeg_info ("Bit rate       :     free");
+  else if (bit_rate_code == 0xf)
+    mjpeg_info ("Bit rate       : reserved");
+  else
+    mjpeg_info ("Bit rate       : %8u bytes/sec (%3u kbit/sec)", bitrate * 128, bitrate);
+
+  if (frequency == 3)
+    mjpeg_info ("Frequency      : reserved");
+  else
+    mjpeg_info ("Frequency      :     %d Hz", mpa_freq_table[version_id][frequency]);
+
+  mjpeg_info ("Mode           : %8u %s", mode, mpa_stereo_mode[mode]);
+  mjpeg_info ("Mode extension : %8u", mode_extension);
+  mjpeg_info ("Copyright bit  : %8u %s", copyright, mpa_copyright_status[copyright]);
+  mjpeg_info ("Original/Copy  : %8u %s", original_copy, mpa_original_bit[original_copy]);
+  mjpeg_info ("Emphasis       : %8u %s", emphasis, mpa_emphasis_mode[emphasis]);
+}
+\f
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mpegconsts.cc b/ext/mplex/mpegconsts.cc
new file mode 100644 (file)
index 0000000..2265da3
--- /dev/null
@@ -0,0 +1,427 @@
+
+/*
+ *  mpegconsts.c:  Video format constants for MPEG and utilities for display
+ *                 and conversion to format used for yuv4mpeg
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *  Copyright (C) 2001 Matthew Marjanovic <maddog@mir.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <config.h>
+#include "mpegconsts.h"
+#include "yuv4mpeg.h"
+#include "yuv4mpeg_intern.h"
+
+static y4m_ratio_t mpeg_framerates[] = {
+  Y4M_FPS_UNKNOWN,
+  Y4M_FPS_NTSC_FILM,
+  Y4M_FPS_FILM,
+  Y4M_FPS_PAL,
+  Y4M_FPS_NTSC,
+  Y4M_FPS_30,
+  Y4M_FPS_PAL_FIELD,
+  Y4M_FPS_NTSC_FIELD,
+  Y4M_FPS_60
+};
+
+
+#define MPEG_NUM_RATES (sizeof(mpeg_framerates)/sizeof(mpeg_framerates[0]))
+const mpeg_framerate_code_t mpeg_num_framerates = MPEG_NUM_RATES;
+
+static const char *framerate_definitions[MPEG_NUM_RATES] = {
+  "illegal",
+  "24000.0/1001.0 (NTSC 3:2 pulldown converted FILM)",
+  "24.0 (NATIVE FILM)",
+  "25.0 (PAL/SECAM VIDEO / converted FILM)",
+  "30000.0/1001.0 (NTSC VIDEO)",
+  "30.0",
+  "50.0 (PAL FIELD RATE)",
+  "60000.0/1001.0 (NTSC FIELD RATE)",
+  "60.0"
+};
+
+
+static const char *mpeg1_aspect_ratio_definitions[] = {
+  "1:1 (square pixels)",
+  "1:0.6735",
+  "1:0.7031 (16:9 Anamorphic PAL/SECAM for 720x578/352x288 images)",
+  "1:0.7615",
+  "1:0.8055",
+  "1:0.8437 (16:9 Anamorphic NTSC for 720x480/352x240 images)",
+  "1:0.8935",
+  "1:0.9375 (4:3 PAL/SECAM for 720x578/352x288 images)",
+  "1:0.9815",
+  "1:1.0255",
+  "1:1:0695",
+  "1:1.1250 (4:3 NTSC for 720x480/352x240 images)",
+  "1:1.1575",
+  "1:1.2015"
+};
+
+static const y4m_ratio_t mpeg1_aspect_ratios[] = {
+  Y4M_SAR_MPEG1_1,
+  Y4M_SAR_MPEG1_2,
+  Y4M_SAR_MPEG1_3,             /* Anamorphic 16:9 PAL */
+  Y4M_SAR_MPEG1_4,
+  Y4M_SAR_MPEG1_5,
+  Y4M_SAR_MPEG1_6,             /* Anamorphic 16:9 NTSC */
+  Y4M_SAR_MPEG1_7,
+  Y4M_SAR_MPEG1_8,             /* PAL/SECAM 4:3 */
+  Y4M_SAR_MPEG1_9,
+  Y4M_SAR_MPEG1_10,
+  Y4M_SAR_MPEG1_11,
+  Y4M_SAR_MPEG1_12,            /* NTSC 4:3 */
+  Y4M_SAR_MPEG1_13,
+  Y4M_SAR_MPEG1_14,
+};
+
+static const char *mpeg2_aspect_ratio_definitions[] = {
+  "1:1 pixels",
+  "4:3 display",
+  "16:9 display",
+  "2.21:1 display"
+};
+
+
+static const y4m_ratio_t mpeg2_aspect_ratios[] = {
+  Y4M_DAR_MPEG2_1,
+  Y4M_DAR_MPEG2_2,
+  Y4M_DAR_MPEG2_3,
+  Y4M_DAR_MPEG2_4
+};
+
+static const char **aspect_ratio_definitions[2] = {
+  mpeg1_aspect_ratio_definitions,
+  mpeg2_aspect_ratio_definitions
+};
+
+static const y4m_ratio_t *mpeg_aspect_ratios[2] = {
+  mpeg1_aspect_ratios,
+  mpeg2_aspect_ratios
+};
+
+const mpeg_aspect_code_t mpeg_num_aspect_ratios[2] = {
+  sizeof (mpeg1_aspect_ratios) / sizeof (mpeg1_aspect_ratios[0]),
+  sizeof (mpeg2_aspect_ratios) / sizeof (mpeg2_aspect_ratios[0])
+};
+
+/*
+ * Convert MPEG frame-rate code to corresponding frame-rate
+ */
+
+y4m_ratio_t
+mpeg_framerate (mpeg_framerate_code_t code)
+{
+  if (code == 0 || code > mpeg_num_framerates)
+    return y4m_fps_UNKNOWN;
+  else
+    return mpeg_framerates[code];
+}
+
+/*
+ * Look-up MPEG frame rate code for a (exact) frame rate.
+ */
+
+
+mpeg_framerate_code_t
+mpeg_framerate_code (y4m_ratio_t framerate)
+{
+  mpeg_framerate_code_t i;
+
+  y4m_ratio_reduce (&framerate);
+  for (i = 1; i < mpeg_num_framerates; ++i) {
+    if (Y4M_RATIO_EQL (framerate, mpeg_framerates[i]))
+      return i;
+  }
+  return 0;
+}
+
+
+/* small enough to distinguish 1/1000 from 1/1001 */
+#define MPEG_FPS_TOLERANCE 0.0001
+
+
+y4m_ratio_t
+mpeg_conform_framerate (double fps)
+{
+  mpeg_framerate_code_t i;
+  y4m_ratio_t result;
+
+  /* try to match it to a standard frame rate */
+  for (i = 1; i < mpeg_num_framerates; i++) {
+    double deviation = 1.0 - (Y4M_RATIO_DBL (mpeg_framerates[i]) / fps);
+
+    if ((deviation > -MPEG_FPS_TOLERANCE) && (deviation < +MPEG_FPS_TOLERANCE))
+      return mpeg_framerates[i];
+  }
+  /* no luck?  just turn it into a ratio (6 decimal place accuracy) */
+  result.n = (int) ((fps * 1000000.0) + 0.5);
+  result.d = 1000000;
+  y4m_ratio_reduce (&result);
+  return result;
+}
+
+
+
+/*
+ * Convert MPEG aspect-ratio code to corresponding aspect-ratio
+ */
+
+y4m_ratio_t
+mpeg_aspect_ratio (int mpeg_version, mpeg_aspect_code_t code)
+{
+  y4m_ratio_t ratio;
+
+  if (mpeg_version < 1 || mpeg_version > 2)
+    return y4m_sar_UNKNOWN;
+  if (code == 0 || code > mpeg_num_aspect_ratios[mpeg_version - 1])
+    return y4m_sar_UNKNOWN;
+  else {
+    ratio = mpeg_aspect_ratios[mpeg_version - 1][code - 1];
+    y4m_ratio_reduce (&ratio);
+    return ratio;
+  }
+}
+
+/*
+ * Look-up corresponding MPEG aspect ratio code given an exact aspect ratio.
+ *
+ * WARNING: The semantics of aspect ratio coding *changed* between
+ * MPEG1 and MPEG2.  In MPEG1 it is the *pixel* aspect ratio. In
+ * MPEG2 it is the (far more sensible) aspect ratio of the eventual
+ * display.
+ *
+ */
+
+mpeg_aspect_code_t
+mpeg_frame_aspect_code (int mpeg_version, y4m_ratio_t aspect_ratio)
+{
+  mpeg_aspect_code_t i;
+  y4m_ratio_t red_ratio = aspect_ratio;
+
+  y4m_ratio_reduce (&red_ratio);
+  if (mpeg_version < 1 || mpeg_version > 2)
+    return 0;
+  for (i = 1; i < mpeg_num_aspect_ratios[mpeg_version - 1]; ++i) {
+    y4m_ratio_t red_entry = mpeg_aspect_ratios[mpeg_version - 1][i - 1];
+
+    y4m_ratio_reduce (&red_entry);
+    if (Y4M_RATIO_EQL (red_entry, red_ratio))
+      return i;
+  }
+
+  return 0;
+
+}
+
+
+
+/*
+ * Guess the correct MPEG aspect ratio code,
+ *  given the true sample aspect ratio and frame size of a video stream
+ *  (and the MPEG version, 1 or 2).
+ *
+ * Returns 0 if it has no good guess.
+ *
+ */
+
+
+/* this is big enough to accommodate the difference between 720 and 704 */
+#define GUESS_ASPECT_TOLERANCE 0.03
+
+mpeg_aspect_code_t
+mpeg_guess_mpeg_aspect_code (int mpeg_version, y4m_ratio_t sampleaspect,
+                            int frame_width, int frame_height)
+{
+  if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_UNKNOWN)) {
+    return 0;
+  }
+  switch (mpeg_version) {
+    case 1:
+      if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_SQUARE)) {
+       return 1;
+      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_NTSC_CCIR601)) {
+       return 12;
+      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_NTSC_16_9)) {
+       return 6;
+      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_PAL_CCIR601)) {
+       return 8;
+      } else if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_PAL_16_9)) {
+       return 3;
+      }
+      return 0;
+      break;
+    case 2:
+      if (Y4M_RATIO_EQL (sampleaspect, y4m_sar_SQUARE)) {
+       return 1;               /* '1' means square *pixels* in MPEG-2; go figure. */
+      } else {
+       unsigned int i;
+       double true_far;        /* true frame aspect ratio */
+
+       true_far =
+         (double) (sampleaspect.n * frame_width) / (double) (sampleaspect.d * frame_height);
+       /* start at '2'... */
+       for (i = 2; i < mpeg_num_aspect_ratios[mpeg_version - 1]; i++) {
+         double ratio = true_far / Y4M_RATIO_DBL (mpeg_aspect_ratios[mpeg_version - 1][i - 1]);
+
+         if ((ratio > (1.0 - GUESS_ASPECT_TOLERANCE)) && (ratio < (1.0 + GUESS_ASPECT_TOLERANCE)))
+           return i;
+       }
+       return 0;
+      }
+      break;
+    default:
+      return 0;
+      break;
+  }
+}
+
+
+
+
+/*
+ * Guess the true sample aspect ratio of a video stream,
+ *  given the MPEG aspect ratio code and the actual frame size
+ *  (and the MPEG version, 1 or 2).
+ *
+ * Returns y4m_sar_UNKNOWN if it has no good guess.
+ *
+ */
+y4m_ratio_t
+mpeg_guess_sample_aspect_ratio (int mpeg_version,
+                               mpeg_aspect_code_t code, int frame_width, int frame_height)
+{
+  switch (mpeg_version) {
+    case 1:
+      /* MPEG-1 codes turn into SAR's, just not quite the right ones.
+         For the common/known values, we provide the ratio used in practice,
+         otherwise say we don't know. */
+      switch (code) {
+       case 1:
+         return y4m_sar_SQUARE;
+         break;
+       case 3:
+         return y4m_sar_PAL_16_9;
+         break;
+       case 6:
+         return y4m_sar_NTSC_16_9;
+         break;
+       case 8:
+         return y4m_sar_PAL_CCIR601;
+         break;
+       case 12:
+         return y4m_sar_NTSC_CCIR601;
+         break;
+       default:
+         return y4m_sar_UNKNOWN;
+         break;
+      }
+      break;
+    case 2:
+      /* MPEG-2 codes turn into Frame Aspect Ratios, though not exactly the
+         FAR's used in practice.  For common/standard frame sizes, we provide
+         the original SAR; otherwise, we say we don't know. */
+      if (code == 1) {
+       return y4m_sar_SQUARE;  /* '1' means square *pixels* in MPEG-2 */
+      } else if ((code >= 2) && (code <= 4)) {
+       return y4m_guess_sar (frame_width, frame_height, mpeg2_aspect_ratios[code - 1]);
+      } else {
+       return y4m_sar_UNKNOWN;
+      }
+      break;
+    default:
+      return y4m_sar_UNKNOWN;
+      break;
+  }
+}
+
+
+
+
+
+/*
+ * Look-up MPEG explanatory definition string for frame rate code
+ *
+ */
+
+
+const char *
+mpeg_framerate_code_definition (mpeg_framerate_code_t code)
+{
+  if (code == 0 || code >= mpeg_num_framerates)
+    return "UNDEFINED: illegal/reserved frame-rate ratio code";
+
+  return framerate_definitions[code];
+}
+
+/*
+ * Look-up MPEG explanatory definition string aspect ratio code for an
+ * aspect ratio code
+ *
+ */
+
+const char *
+mpeg_aspect_code_definition (int mpeg_version, mpeg_aspect_code_t code)
+{
+  if (mpeg_version < 1 || mpeg_version > 2)
+    return "UNDEFINED: illegal MPEG version";
+
+  if (code < 1 || code > mpeg_num_aspect_ratios[mpeg_version - 1])
+    return "UNDEFINED: illegal aspect ratio code";
+
+  return aspect_ratio_definitions[mpeg_version - 1][code - 1];
+}
+
+
+/*
+ * Look-up explanatory definition of interlace field order code
+ *
+ */
+
+const char *
+mpeg_interlace_code_definition (int yuv4m_interlace_code)
+{
+  const char *def;
+
+  switch (yuv4m_interlace_code) {
+    case Y4M_UNKNOWN:
+      def = "unknown";
+      break;
+    case Y4M_ILACE_NONE:
+      def = "none/progressive";
+      break;
+    case Y4M_ILACE_TOP_FIRST:
+      def = "top-field-first";
+      break;
+    case Y4M_ILACE_BOTTOM_FIRST:
+      def = "bottom-field-first";
+      break;
+    default:
+      def = "UNDEFINED: illegal video interlacing type-code!";
+      break;
+  }
+  return def;
+}
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/mpegconsts.h b/ext/mplex/mpegconsts.h
new file mode 100644 (file)
index 0000000..461fd2d
--- /dev/null
@@ -0,0 +1,149 @@
+
+/*
+ *  mpegconsts.c:  Video format constants for MPEG and utilities for display
+ *                 and conversion to format used for yuv4mpeg
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __MPEGCONSTS_H__
+#define __MPEGCONSTS_H__
+
+
+#include "yuv4mpeg.h"
+
+
+typedef unsigned int mpeg_framerate_code_t;
+typedef unsigned int mpeg_aspect_code_t;
+
+extern const mpeg_framerate_code_t mpeg_num_framerates;
+extern const mpeg_aspect_code_t mpeg_num_aspect_ratios[2];
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Convert MPEG frame-rate code to corresponding frame-rate
+ *  y4m_fps_UNKNOWN = { 0, 0 } = Undefined/resrerved code.
+ */
+
+y4m_ratio_t
+mpeg_framerate( mpeg_framerate_code_t code );
+
+
+/*
+ * Look-up MPEG frame rate code for a (exact) frame rate.
+ *  0 = No MPEG code defined for frame-rate
+ */
+
+mpeg_framerate_code_t 
+mpeg_framerate_code( y4m_ratio_t framerate );
+
+
+/*
+ * Convert floating-point framerate to an exact ratio.
+ *  Uses a standard MPEG rate, if it finds one within MPEG_FPS_TOLERANCE
+ *  (see mpegconsts.c), otherwise uses "fps:1000000" as the ratio.
+ */
+
+y4m_ratio_t
+mpeg_conform_framerate( double fps );
+
+
+/*
+ * Convert MPEG aspect ratio code to corresponding aspect ratio
+ *
+ * WARNING: The semantics of aspect ratio coding *changed* between
+ * MPEG1 and MPEG2.  In MPEG1 it is the *pixel* aspect ratio. In
+ * MPEG2 it is the (far more sensible) aspect ratio of the eventual
+ * display.
+ *
+ */
+
+y4m_ratio_t
+mpeg_aspect_ratio( int mpeg_version,  mpeg_aspect_code_t code );
+
+/*
+ * Look-up MPEG aspect ratio code for an aspect ratio - tolerance
+ * is Y4M_ASPECT_MULT used by YUV4MPEG (see yuv4mpeg_intern.h)
+ *
+ * WARNING: The semantics of aspect ratio coding *changed* between
+ * MPEG1 and MPEG2.  In MPEG1 it is the *pixel* aspect ratio. In
+ * MPEG2 it is the (far more sensible) aspect ratio of the eventual
+ * display.
+ *
+ */
+
+mpeg_aspect_code_t 
+mpeg_frame_aspect_code( int mpeg_version, y4m_ratio_t aspect_ratio );
+
+/*
+ * Look-up MPEG explanatory definition string aspect ratio code for an
+ * aspect ratio code
+ *
+ */
+
+const char *
+mpeg_aspect_code_definition( int mpeg_version,  mpeg_aspect_code_t code  );
+
+/*
+ * Look-up MPEG explanatory definition string aspect ratio code for an
+ * frame rate code
+ *
+ */
+
+const char *
+mpeg_framerate_code_definition( mpeg_framerate_code_t code  );
+
+const char *
+mpeg_interlace_code_definition( int yuv4m_interlace_code );
+
+
+/*
+ * Guess the correct MPEG aspect ratio code,
+ *  given the true sample aspect ratio and frame size of a video stream
+ *  (and the MPEG version, 1 or 2).
+ *
+ * Returns 0 if it has no good answer.
+ *
+ */
+mpeg_aspect_code_t 
+mpeg_guess_mpeg_aspect_code(int mpeg_version, y4m_ratio_t sampleaspect,
+                           int frame_width, int frame_height);
+
+/*
+ * Guess the true sample aspect ratio of a video stream,
+ *  given the MPEG aspect ratio code and the actual frame size
+ *  (and the MPEG version, 1 or 2).
+ *
+ * Returns y4m_sar_UNKNOWN if it has no good answer.
+ *
+ */
+y4m_ratio_t 
+mpeg_guess_sample_aspect_ratio(int mpeg_version,
+                              mpeg_aspect_code_t code,
+                              int frame_width, int frame_height);
+
+
+#ifdef __cplusplus
+};
+#endif
+
+
+
+#endif /* __MPEGCONSTS_H__ */
diff --git a/ext/mplex/mplexconsts.hh b/ext/mplex/mplexconsts.hh
new file mode 100644 (file)
index 0000000..c3fe6ce
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __MPLEXCONSTS_H__
+#define __MPLEXCONSTS_H__
+
+
+#define SEQUENCE_HEADER        0x000001b3
+#define SEQUENCE_END           0x000001b7
+#define PICTURE_START          0x00000100
+#define EXT_START_CODE 0x000001b5
+#define GROUP_START            0x000001b8
+#define SYNCWORD_START         0x000001
+
+#define IFRAME                  1
+#define PFRAME                  2
+#define BFRAME                  3
+#define DFRAME                  4
+#define NOFRAME                 5
+
+#define PIC_TOP_FIELD 1
+#define PIC_BOT_FIELD 2
+#define PIC_FRAME 3
+
+#define CODING_EXT_ID       8
+#define AUDIO_SYNCWORD         0x7ff
+
+
+#define PACK_START             0x000001ba
+#define SYS_HEADER_START       0x000001bb
+#define ISO11172_END           0x000001b9
+#define PACKET_START           0x000001
+
+#define MAX_FFFFFFFF           4294967295.0    /* = 0xffffffff in dec. */
+
+#define CLOCKS_per_90Kth_sec 300
+
+#define CLOCKS                 (CLOCKS_per_90Kth_sec*90000)
+/* MPEG-2 System Clock Hertz - we divide down by 300.0 for MPEG-1*/
+
+/* Range of sizes of the fields following the packet length field in packet header:
+       used to calculate if recieve buffers will have enough space... */
+
+#define MPEG2_BUFFERINFO_LENGTH 3
+#define MPEG1_BUFFERINFO_LENGTH 2
+#define DTS_PTS_TIMESTAMP_LENGTH 5
+#define MPEG2_AFTER_PACKET_LENGTH_MIN    3
+#define MPEG1_AFTER_PACKET_LENGTH_MIN    (0+1)
+
+       /* Sector under-size below which header stuffing rather than padding packets
+          or post-packet zero stuffing is used.  *Must* be less than 20 for VCD
+          multiplexing to work correctly!
+        */
+
+#define MINIMUM_PADDING_PACKET_SIZE 10
+
+#define PACKET_HEADER_SIZE     6
+
+#define AUDIO_STREAMS          0xb8    /* Marker Audio Streams */
+#define VIDEO_STREAMS          0xb9    /* Marker Video Streams */
+#define AUDIO_STR_0            0xc0    /* Marker Audio Stream0 */
+#define VIDEO_STR_0            0xe0    /* Marker Video Stream0 */
+#define PADDING_STR            0xbe    /* Marker Padding Stream */
+#define PRIVATE_STR_1   0xbd   /* private stream 1 */
+#define PRIVATE_STR_2   0xbf   /* private stream 2 */
+#define AC3_SUB_STR_0   0x80   /* AC3 substream id 0 */
+
+#define LPCM_SUB_STR_0  0xa0   /* LPCM substream id 0 */
+
+#define ZERO_STUFFING_BYTE     0
+#define STUFFING_BYTE          0xff
+#define RESERVED_BYTE          0xff
+#define TIMESTAMPBITS_NO               0       /* Flag NO timestamps   */
+#define TIMESTAMPBITS_PTS              2       /* Flag PTS timestamp   */
+#define TIMESTAMPBITS_DTS              1       /* Flag PTS timestamp   */
+#define TIMESTAMPBITS_PTS_DTS  (TIMESTAMPBITS_DTS|TIMESTAMPBITS_PTS)   /* Flag BOTH timestamps */
+
+#define MARKER_MPEG1_SCR               2       /* Marker SCR           */
+#define MARKER_MPEG2_SCR        1      /* These don't need to be distinct! */
+#define MARKER_JUST_PTS                        2       /* Marker only PTS      */
+#define MARKER_PTS                             3       /* Marker PTS           */
+#define MARKER_DTS                             1       /* Marker DTS           */
+#define MARKER_NO_TIMESTAMPS   0x0f    /* Marker NO timestamps */
+
+
+#endif // __MPLEXCONSTS_H__
diff --git a/ext/mplex/multplex.cc b/ext/mplex/multplex.cc
new file mode 100644 (file)
index 0000000..5a1d4b1
--- /dev/null
@@ -0,0 +1,1120 @@
+
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <mjpeg_types.h>
+#include <mjpeg_logging.h>
+#include <format_codes.h>
+
+#include "videostrm.hh"
+#include "outputstream.hh"
+
+
+/******************************************************************* 
+       Find the timecode corresponding to given position in the system stream
+   (assuming the SCR starts at 0 at the beginning of the stream 
+@param bytepos byte position in the stream
+@param ts returns the number of clockticks the bytepos is from the file start    
+****************************************************************** */
+
+void
+OutputStream::ByteposTimecode (bitcount_t bytepos, clockticks & ts)
+{
+  ts = (bytepos * CLOCKS) / static_cast < bitcount_t > (dmux_rate);
+}
+
+
+/**********
+ *
+ * UpdateSectorHeaders - Update the sector headers after a change in stream
+ *                     position / SCR
+ **********
+
+ **********
+ *
+ * NextPosAndSCR - Update nominal (may be >= actual) byte count
+ * and SCR to next output sector.
+ *
+ ********/
+
+void
+OutputStream::NextPosAndSCR ()
+{
+  bytes_output += sector_transport_size;
+  ByteposTimecode (bytes_output, current_SCR);
+  if (start_of_new_pack) {
+    psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+    pack_header_ptr = &pack_header;
+    if (include_sys_header)
+      sys_header_ptr = &sys_header;
+    else
+      sys_header_ptr = NULL;
+
+  } else
+    pack_header_ptr = NULL;
+}
+
+
+/**********
+ *
+ * NextPosAndSCR - Update nominal (may be >= actual) byte count
+ * and SCR to next output sector.
+ * @param bytepos byte position in the stream
+ ********/
+
+void
+OutputStream::SetPosAndSCR (bitcount_t bytepos)
+{
+  bytes_output = bytepos;
+  ByteposTimecode (bytes_output, current_SCR);
+  if (start_of_new_pack) {
+    psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+    pack_header_ptr = &pack_header;
+    if (include_sys_header)
+      sys_header_ptr = &sys_header;
+    else
+      sys_header_ptr = NULL;
+
+  } else
+    pack_header_ptr = NULL;
+}
+
+/* 
+   Stream syntax parameters.
+*/
+
+
+/******************************************************************
+
+       Initialisation of stream syntax paramters based on selected
+       user options.
+******************************************************************/
+
+
+// TODO: this mixes class member parameters with opt_ globals...
+
+void
+OutputStream::InitSyntaxParameters ()
+{
+  video_buffer_size = 0;
+  seg_starts_with_video = false;
+  audio_buffer_size = 4 * 1024;
+
+  switch (opt_mux_format) {
+    case MPEG_FORMAT_VCD:
+      opt_data_rate = 75 * 2352;       /* 75 raw CD sectors/sec */
+      video_buffer_size = 46 * 1024;
+      opt_VBR = 0;
+
+    case MPEG_FORMAT_VCD_NSR:  /* VCD format, non-standard rate */
+      mjpeg_info ("Selecting VCD output profile");
+      if (video_buffer_size == 0)
+       video_buffer_size = opt_buffer_size * 1024;
+      vbr = opt_VBR;
+      opt_mpeg = 1;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = 0;
+      always_sys_header_in_pack = 0;
+      sector_transport_size = 2352;    /* Each 2352 bytes with 2324 bytes payload */
+      transport_prefix_sectors = 30;
+      sector_size = 2324;
+      buffers_in_video = 1;
+      always_buffers_in_video = 0;
+      buffers_in_audio = 1;    // This is needed as otherwise we have
+      always_buffers_in_audio = 1;     //  to stuff the packer header which 
+      // must be 13 bytes for VCD audio
+      vcd_zero_stuffing = 20;  // The famous 20 zero bytes for VCD
+      // audio sectors.
+      dtspts_for_all_vau = false;
+      sector_align_iframeAUs = false;
+      timestamp_iframe_only = false;
+      seg_starts_with_video = true;
+      break;
+
+    case MPEG_FORMAT_MPEG2:
+      mjpeg_info ("Selecting generic MPEG2 output profile");
+      opt_mpeg = 2;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = 1;
+      always_sys_header_in_pack = 0;
+      sector_transport_size = 2048;    /* Each 2352 bytes with 2324 bytes payload */
+      transport_prefix_sectors = 0;
+      sector_size = 2048;
+      video_buffer_size = 234 * 1024;
+      buffers_in_video = 1;
+      always_buffers_in_video = 0;
+      buffers_in_audio = 1;
+      always_buffers_in_audio = 1;
+      vcd_zero_stuffing = 0;
+      dtspts_for_all_vau = 0;
+      sector_align_iframeAUs = false;
+      timestamp_iframe_only = false;
+      video_buffers_iframe_only = false;
+      vbr = opt_VBR;
+      break;
+
+    case MPEG_FORMAT_SVCD:
+      opt_data_rate = 150 * 2324;
+      video_buffer_size = 230 * 1024;
+
+    case MPEG_FORMAT_SVCD_NSR: /* Non-standard data-rate */
+      mjpeg_info ("Selecting SVCD output profile");
+      if (video_buffer_size == 0)
+       video_buffer_size = opt_buffer_size * 1024;
+      opt_mpeg = 2;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = 0;
+      always_sys_header_in_pack = 0;
+      sector_transport_size = 2324;
+      transport_prefix_sectors = 0;
+      sector_size = 2324;
+      vbr = true;
+      buffers_in_video = 1;
+      always_buffers_in_video = 0;
+      buffers_in_audio = 1;
+      always_buffers_in_audio = 0;
+      vcd_zero_stuffing = 0;
+      dtspts_for_all_vau = 0;
+      sector_align_iframeAUs = true;
+      seg_starts_with_video = true;
+      timestamp_iframe_only = false;
+      video_buffers_iframe_only = false;
+      break;
+
+    case MPEG_FORMAT_VCD_STILL:
+      opt_data_rate = 75 * 2352;       /* 75 raw CD sectors/sec */
+      vbr = false;
+      opt_mpeg = 1;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = 0;
+      always_sys_header_in_pack = 0;
+      sector_transport_size = 2352;    /* Each 2352 bytes with 2324 bytes payload */
+      transport_prefix_sectors = 0;
+      sector_size = 2324;
+      buffers_in_video = 1;
+      always_buffers_in_video = 0;
+      buffers_in_audio = 1;
+      always_buffers_in_audio = 0;
+      vcd_zero_stuffing = 20;
+      dtspts_for_all_vau = 1;
+      sector_align_iframeAUs = true;
+      timestamp_iframe_only = false;
+      video_buffers_iframe_only = false;
+      if (opt_buffer_size == 0)
+       opt_buffer_size = 46;
+      else if (opt_buffer_size > 220) {
+       mjpeg_error_exit1 ("VCD stills has max. permissible video buffer size of 220KB");
+      } else {
+       /* Add a margin for sequence header overheads for HR stills */
+       /* So the user simply specifies the nominal size... */
+       opt_buffer_size += 4;
+      }
+      video_buffer_size = opt_buffer_size * 1024;
+      break;
+
+    case MPEG_FORMAT_SVCD_STILL:
+      mjpeg_info ("Selecting SVCD output profile");
+      if (opt_data_rate == 0)
+       opt_data_rate = 150 * 2324;
+      video_buffer_size = 230 * 1024;
+      opt_mpeg = 2;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = 0;
+      always_sys_header_in_pack = 0;
+      sector_transport_size = 2324;
+      transport_prefix_sectors = 0;
+      sector_size = 2324;
+      vbr = true;
+      buffers_in_video = 1;
+      always_buffers_in_video = 0;
+      buffers_in_audio = 1;
+      always_buffers_in_audio = 0;
+      vcd_zero_stuffing = 0;
+      dtspts_for_all_vau = 0;
+      sector_align_iframeAUs = true;
+      timestamp_iframe_only = false;
+      video_buffers_iframe_only = false;
+      break;
+
+    case MPEG_FORMAT_DVD:
+      mjpeg_info ("Selecting DVD output profile (INCOMPLETE!!!!)");
+      opt_data_rate = 1260000;
+      opt_mpeg = 2;
+      packets_per_pack = 1;
+      sys_header_in_pack1 = false;     // Handle by control packets
+      always_sys_header_in_pack = false;
+      sector_transport_size = 2048;
+      transport_prefix_sectors = 0;
+      sector_size = 2048;
+      video_buffer_size = 232 * 1024;
+      buffers_in_video = true;
+      always_buffers_in_video = false;
+      buffers_in_audio = true;
+      always_buffers_in_audio = false;
+      vcd_zero_stuffing = 0;
+      dtspts_for_all_vau = 0;
+      sector_align_iframeAUs = true;
+      timestamp_iframe_only = true;
+      video_buffers_iframe_only = true;
+      vbr = true;
+      if (opt_max_segment_size == 0)
+       opt_max_segment_size = 2000 * 1024 * 1024;
+      break;
+
+    default:                   /* MPEG_FORMAT_MPEG1 - auto format MPEG1 */
+      mjpeg_info ("Selecting generic MPEG1 output profile");
+      opt_mpeg = 1;
+      vbr = opt_VBR;
+      packets_per_pack = opt_packets_per_pack;
+      always_sys_header_in_pack = opt_always_system_headers;
+      sys_header_in_pack1 = 1;
+      sector_transport_size = opt_sector_size;
+      transport_prefix_sectors = 0;
+      sector_size = opt_sector_size;
+      if (opt_buffer_size == 0) {
+       opt_buffer_size = 46;
+      }
+      video_buffer_size = opt_buffer_size * 1024;
+      buffers_in_video = 1;
+      always_buffers_in_video = 1;
+      buffers_in_audio = 0;
+      always_buffers_in_audio = 1;
+      vcd_zero_stuffing = 0;
+      dtspts_for_all_vau = 0;
+      sector_align_iframeAUs = false;
+      timestamp_iframe_only = false;
+      video_buffers_iframe_only = false;
+      break;
+  }
+
+}
+
+/**
+ * Compute the number of run-in sectors needed to fill up the buffers to
+ * suit the type of stream being muxed.
+ *
+ * For stills we have to ensure an entire buffer is loaded as we only
+ * ever process one frame at a time.
+ * @returns the number of run-in sectors needed to fill up the buffers to suit the type of stream being muxed.
+ */
+
+unsigned int
+OutputStream::RunInSectors ()
+{
+  vector < ElementaryStream * >::iterator str;
+  unsigned int sectors_delay = 1;
+
+  for (str = vstreams.begin (); str < vstreams.end (); ++str) {
+
+    if (MPEG_STILLS_FORMAT (opt_mux_format)) {
+      sectors_delay += (unsigned int) (1.02 * (*str)->BufferSize ()) / sector_size + 2;
+    } else if (vbr)
+      sectors_delay += 3 * (*str)->BufferSize () / (4 * sector_size);
+    else
+      sectors_delay += 5 * (*str)->BufferSize () / (6 * sector_size);
+  }
+  sectors_delay += astreams.size ();
+  return sectors_delay;
+}
+
+/**
+   Initializes the output stream. Traverses the input files and calculates their payloads.
+   Estimates the multiplex rate. Estimates the neccessary stream delay for the different substreams.
+ */
+
+
+void
+OutputStream::Init (vector < ElementaryStream * >*strms, PS_Stream *strm)
+{
+  vector < ElementaryStream * >::iterator str;
+  clockticks delay;
+  unsigned int sectors_delay;
+
+  Pack_struc dummy_pack;
+  Sys_header_struc dummy_sys_header;
+  Sys_header_struc *sys_hdr;
+  unsigned int nominal_rate_sum;
+
+  packets_left_in_pack = 0;        /* Suppress warning */
+  video_first = false;
+
+  estreams = strms;
+
+  for (str = estreams->begin (); str < estreams->end (); ++str) {
+    switch ((*str)->Kind ()) {
+      case ElementaryStream::audio:
+       astreams.push_back (*str);
+       break;
+      case ElementaryStream::video:
+       vstreams.push_back (*str);
+       break;
+      default:
+       break;
+    }
+
+    completed.push_back (false);
+  }
+
+  mjpeg_info ("SYSTEMS/PROGRAM stream:");
+  this->psstrm = strm;
+
+  psstrm->Init (opt_mpeg, sector_size, opt_max_segment_size);
+
+  /* These are used to make (conservative) decisions
+     about whether a packet should fit into the recieve buffers... 
+     Audio packets always have PTS fields, video packets needn'.      
+     TODO: Really this should be encapsulated in Elementary stream...?
+   */
+  psstrm->CreatePack (&dummy_pack, 0, mux_rate);
+  if (always_sys_header_in_pack) {
+    vector < MuxStream * >muxstreams;
+    AppendMuxStreamsOf (*estreams, muxstreams);
+    psstrm->CreateSysHeader (&dummy_sys_header, mux_rate, !vbr, 1, true, true, muxstreams);
+    sys_hdr = &dummy_sys_header;
+  } else
+    sys_hdr = NULL;
+
+  nominal_rate_sum = 0;
+  for (str = estreams->begin (); str < estreams->end (); ++str) {
+    switch ((*str)->Kind ()) {
+      case ElementaryStream::audio:
+       (*str)->SetMaxPacketData (psstrm->PacketPayload (**str, NULL, NULL, 
+                                                false, true, false));
+       (*str)->SetMinPacketData (psstrm->PacketPayload (**str, sys_hdr, &dummy_pack,
+                                                always_buffers_in_audio, true, false));
+
+       break;
+      case ElementaryStream::video:
+       (*str)->SetMaxPacketData (psstrm->PacketPayload (**str, NULL, NULL, 
+                                                false, false, false));
+       (*str)->SetMinPacketData (psstrm->PacketPayload (**str, sys_hdr, &dummy_pack,
+                                                always_buffers_in_video, true, true));
+       break;
+      default:
+       mjpeg_error_exit1 ("INTERNAL: Only audio and video payload calculations implemented!");
+
+    }
+
+    if ((*str)->NominalBitRate () == 0 && opt_data_rate == 0)
+      mjpeg_error_exit1
+       ("Variable bit-rate stream present: output stream (max) data-rate *must* be specified!");
+    nominal_rate_sum += (*str)->NominalBitRate ();
+
+  }
+
+  /* Attempt to guess a sensible mux rate for the given video and *
+     audio estreams-> This is a rough and ready guess for MPEG-1 like
+     formats. */
+
+
+  dmux_rate = static_cast < int >(1.015 * nominal_rate_sum);
+
+  dmux_rate = (dmux_rate / 50 + 25) * 50;
+
+  mjpeg_info ("rough-guess multiplexed stream data rate    : %07d", dmux_rate * 8);
+  if (opt_data_rate != 0)
+    mjpeg_info ("target data-rate specified               : %7d", opt_data_rate * 8);
+
+  if (opt_data_rate == 0) {
+    mjpeg_info ("Setting best-guess data rate.");
+  } else if (opt_data_rate >= dmux_rate) {
+    mjpeg_info ("Setting specified specified data rate: %7d", opt_data_rate * 8);
+    dmux_rate = opt_data_rate;
+  } else if (opt_data_rate < dmux_rate) {
+    mjpeg_warn ("Target data rate lower than computed requirement!");
+    mjpeg_warn ("N.b. a 20%% or so discrepancy in variable bit-rate");
+    mjpeg_warn ("streams is common and harmless provided no time-outs will occur");
+    dmux_rate = opt_data_rate;
+  }
+
+  mux_rate = dmux_rate / 50;
+
+  /* To avoid Buffer underflow, the DTS of the first video and audio AU's
+     must be offset sufficiently  forward of the SCR to allow the buffer 
+     time to fill before decoding starts. Calculate the necessary delays...
+   */
+
+  sectors_delay = RunInSectors ();
+
+  ByteposTimecode (static_cast < bitcount_t > (sectors_delay * sector_transport_size), delay);
+
+
+  video_delay = delay + static_cast < clockticks > (opt_video_offset * CLOCKS / 1000);
+  audio_delay = delay + static_cast < clockticks > (opt_audio_offset * CLOCKS / 1000);
+  mjpeg_info ("Sectors = %d Video delay = %lld Audio delay = %lld",
+             sectors_delay, video_delay / 300, audio_delay / 300);
+
+
+  //
+  // Now that all mux parameters are set we can trigger parsing
+  // of actual input stream data and calculation of associated 
+  // PTS/DTS by causing the read of the first AU's...
+  //
+  for (str = estreams->begin (); str < estreams->end (); ++str) {
+    (*str)->NextAU ();
+  }
+
+
+  //
+  // Now that we have both output and input streams initialised and
+  // data-rates set we can make a decent job of setting the maximum
+  // STD buffer delay in video streams.
+  //
+
+  for (str = vstreams.begin (); str < vstreams.end (); ++str) {
+    static_cast < VideoStream * >(*str)->SetMaxStdBufferDelay (dmux_rate);
+  }
+
+
+
+  /*  Let's try to read in unit after unit and to write it out into
+     the outputstream. The only difficulty herein lies into the
+     buffer management, and into the fact the the actual access
+     unit *has* to arrive in time, that means the whole unit
+     (better yet, packet data), has to arrive before arrival of
+     DTS. If both buffers are full we'll generate a padding packet
+
+     Of course, when we start we're starting a new segment with no
+     bytes output...
+   */
+
+  ByteposTimecode (sector_transport_size, ticks_per_sector);
+  seg_state = start_segment;
+  running_out = false;
+}
+
+/**
+   Prints the current status of the substreams. 
+   @param level the desired log level 
+ */
+void
+OutputStream::MuxStatus (log_level_t level)
+{
+  vector < ElementaryStream * >::iterator str;
+  for (str = estreams->begin (); str < estreams->end (); ++str) {
+    switch ((*str)->Kind ()) {
+      case ElementaryStream::video:
+       mjpeg_log (level,
+                  "Video %02x: buf=%7d frame=%06d sector=%08d",
+                  (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->au->dorder, (*str)->nsec);
+       break;
+      case ElementaryStream::audio:
+       mjpeg_log (level,
+                  "Audio %02x: buf=%7d frame=%06d sector=%08d",
+                  (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->au->dorder, (*str)->nsec);
+       break;
+      default:
+       mjpeg_log (level,
+                  "Other %02x: buf=%7d sector=%08d",
+                  (*str)->stream_id, (*str)->bufmodel.Space (), (*str)->nsec);
+       break;
+    }
+  }
+  if (!vbr)
+    mjpeg_log (level, "Padding : sector=%08d", pstrm.nsec);
+
+
+}
+
+
+/**
+   Append input substreams to the output multiplex stream.
+ */
+void
+OutputStream::AppendMuxStreamsOf (vector < ElementaryStream * >&elem, vector < MuxStream * >&mux)
+{
+  vector < ElementaryStream * >::iterator str;
+  for (str = elem.begin (); str < elem.end (); ++str) {
+    mux.push_back (static_cast < MuxStream * >(*str));
+  }
+}
+
+/******************************************************************
+    Program start-up packets.  Generate any irregular packets                                          
+needed at the start of the stream...
+       Note: *must* leave a sensible in-stream system header in
+       sys_header.
+       TODO: get rid of this grotty sys_header global.
+******************************************************************/
+void
+OutputStream::OutputPrefix ()
+{
+  vector < MuxStream * >vmux, amux, emux;
+  AppendMuxStreamsOf (vstreams, vmux);
+  AppendMuxStreamsOf (astreams, amux);
+  AppendMuxStreamsOf (*estreams, emux);
+
+  /* Deal with transport padding */
+  SetPosAndSCR (bytes_output + transport_prefix_sectors * sector_transport_size);
+
+  /* VCD: Two padding packets with video and audio system headers */
+  split_at_seq_end = !opt_multifile_segment;
+
+  switch (opt_mux_format) {
+    case MPEG_FORMAT_VCD:
+    case MPEG_FORMAT_VCD_NSR:
+
+      /* Annoyingly VCD generates seperate system headers for
+         audio and video ... DOH... */
+      if (astreams.size () > 1 || vstreams.size () > 1 ||
+         astreams.size () + vstreams.size () != estreams->size ()) {
+       mjpeg_error_exit1 ("VCD man only have max. 1 audio and 1 video stream");
+      }
+      /* First packet carries video-info-only sys_header */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, vmux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      OutputPadding (false);
+
+      /* Second packet carries audio-info-only sys_header */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, amux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      OutputPadding (true);
+      break;
+
+    case MPEG_FORMAT_SVCD:
+    case MPEG_FORMAT_SVCD_NSR:
+      /* First packet carries sys_header */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, true, true, true, emux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      OutputPadding (false);
+      break;
+
+    case MPEG_FORMAT_VCD_STILL:
+      split_at_seq_end = false;
+      /* First packet carries small-still sys_header */
+      /* TODO No support mixed-mode stills sequences... */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, false, false, true, true, emux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      OutputPadding (false);
+      break;
+
+    case MPEG_FORMAT_SVCD_STILL:
+      /* TODO: Video only at present */
+      /* First packet carries video-info-only sys_header */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, false, true, true, true, vmux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      OutputPadding (false);
+      break;
+
+    case MPEG_FORMAT_DVD:
+      /* A DVD System header is a weird thing.  We seem to need to
+         include buffer info about streams 0xb8, 0xb9, 0xbf even if
+         they're not physically present but the buffers for the actual
+         video streams aren't included.  
+
+         TODO: I have no idead about MPEG audio streams if present...
+       */
+    {
+      DummyMuxStream dvd_0xb9_strm_dummy (0xb9, 1, video_buffer_size);
+      DummyMuxStream dvd_0xb8_strm_dummy (0xb8, 0, 4096);
+      DummyMuxStream dvd_0xbf_strm_dummy (0xbf, 1, 2048);
+
+      vector < MuxStream * >dvdmux;
+      vector < MuxStream * >::iterator muxstr;
+      dvdmux.push_back (&dvd_0xb9_strm_dummy);
+      dvdmux.push_back (&dvd_0xb8_strm_dummy);
+      unsigned int max_priv1_buffer = 0;
+
+      for (muxstr = amux.begin (); muxstr < amux.end (); ++muxstr) {
+       // We mux *many* substreams on PRIVATE_STR_1
+       // we set the system header buffer size to the maximum
+       // of all those we find
+       if ((*muxstr)->stream_id == PRIVATE_STR_1 && (*muxstr)->BufferSize () > max_priv1_buffer) {
+         max_priv1_buffer = (*muxstr)->BufferSize ();
+       } else
+         dvdmux.push_back (*muxstr);
+      }
+
+      DummyMuxStream dvd_priv1_strm_dummy (PRIVATE_STR_1, 1, max_priv1_buffer);
+
+      if (max_priv1_buffer > 0)
+       dvdmux.push_back (&dvd_priv1_strm_dummy);
+
+      dvdmux.push_back (&dvd_0xbf_strm_dummy);
+      psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, false, true, true, dvdmux);
+      sys_header_ptr = &sys_header;
+      pack_header_ptr = &pack_header;
+      /* It is then followed up by a pair of PRIVATE_STR_2 packets which
+         we keep empty 'cos we don't know what goes there...
+       */
+    }
+      break;
+
+    default:
+      /* Create the in-stream header in case it is needed */
+      psstrm->CreateSysHeader (&sys_header, mux_rate, !vbr, false, true, true, emux);
+  }
+}
+
+/******************************************************************
+    Program shutdown packets.  Generate any irregular packets
+    needed at the end of the stream...
+   
+******************************************************************/
+
+void
+OutputStream::OutputSuffix ()
+{
+  psstrm->CreatePack (&pack_header, current_SCR, mux_rate);
+  psstrm->CreateSector (&pack_header, NULL, 0, pstrm, false, true, 0, 0, TIMESTAMPBITS_NO);
+}
+
+/******************************************************************
+
+       Main multiplex iteration.
+       Opens and closes all needed files and manages the correct
+       call od the respective Video- and Audio- packet routines.
+       The basic multiplexing is done here. Buffer capacity and 
+       Timestamp checking is also done here, decision is taken
+       wether we should genereate a Video-, Audio- or Padding-
+       packet.
+******************************************************************/
+
+
+
+bool
+OutputStream::OutputMultiplex ()
+{
+  for (;;) {
+    bool completion = true;
+
+    for (str = estreams->begin (); str < estreams->end (); ++str)
+      completion &= (*str)->MuxCompleted ();
+    if (completion)
+      break;
+
+    /* A little state-machine for handling the transition from one
+       segment to the next 
+     */
+    bool runout_incomplete;
+    VideoStream *master;
+
+    switch (seg_state) {
+
+       /* Audio and slave video access units at end of segment.
+          If there are any audio AU's whose PTS implies they
+          should be played *before* the video AU starting the
+          next segement is presented we mux them out.  Once
+          they're gone we've finished this segment so we write
+          the suffix switch file, and start muxing a new segment.
+        */
+      case runout_segment:
+       runout_incomplete = false;
+       for (str = estreams->begin (); str < estreams->end (); ++str) {
+         runout_incomplete |= !(*str)->RunOutComplete ();
+       }
+
+       if (runout_incomplete)
+         break;
+
+       /* Otherwise we write the stream suffix and start a new
+          stream file */
+       OutputSuffix ();
+       psstrm->NextFile ();
+
+       running_out = false;
+       seg_state = start_segment;
+
+       /* Starting a new segment.
+          We send the segment prefix, video and audio reciever
+          buffers are assumed to start empty.  We reset the segment
+          length count and hence the SCR.
+
+        */
+
+      case start_segment:
+       mjpeg_info ("New sequence commences...");
+       SetPosAndSCR (0);
+       MuxStatus (LOG_INFO);
+
+       for (str = estreams->begin (); str < estreams->end (); ++str) {
+         (*str)->AllDemuxed ();
+       }
+
+       packets_left_in_pack = packets_per_pack;
+       start_of_new_pack = true;
+       include_sys_header = sys_header_in_pack1;
+       buffers_in_video = always_buffers_in_video;
+       video_first = seg_starts_with_video & (vstreams.size () > 0);
+       OutputPrefix ();
+
+       /* Set the offset applied to the raw PTS/DTS of AU's to
+          make the DTS of the first AU in the master (video) stream
+          precisely the video delay plus whatever time we wasted in
+          the sequence pre-amble.
+
+          The DTS of the remaining streams are set so that
+          (modulo the relevant delay offset) they maintain the
+          same relative timing to the master stream.
+
+        */
+
+       clockticks ZeroSCR;
+
+       if (vstreams.size () != 0)
+         ZeroSCR = vstreams[0]->au->DTS;
+       else
+         ZeroSCR = (*estreams)[0]->au->DTS;
+
+       for (str = vstreams.begin (); str < vstreams.end (); ++str)
+         (*str)->SetSyncOffset (video_delay + current_SCR - ZeroSCR);
+       for (str = astreams.begin (); str < astreams.end (); ++str)
+         (*str)->SetSyncOffset (audio_delay + current_SCR - ZeroSCR);
+       pstrm.nsec = 0;
+       for (str = estreams->begin (); str < estreams->end (); ++str)
+         (*str)->nsec = 0;
+       seg_state = mid_segment;
+       //for( str = estreams->begin(); str < estreams->end(); ++str )
+       //{
+       //mjpeg_info("STREAM %02x: SCR=%lld mux=%d reqDTS=%lld", 
+       //(*str)->stream_id,
+       //current_SCR /300,
+       //(*str)->MuxPossible(current_SCR),
+       //(*str)->RequiredDTS()/300
+       //);
+       //}
+       break;
+
+      case mid_segment:
+       /* Once we exceed our file size limit, we need to
+          start a new file soon.  If we want a single stream we
+          simply switch.
+
+          Otherwise we're in the last gop of the current segment
+          (and need to start running streams out ready for a
+          clean continuation in the next segment).
+          TODO: runout_PTS really needs to be expressed in
+          sync delay adjusted units...
+        */
+
+       master = vstreams.size () > 0 ? static_cast < VideoStream * >(vstreams[0]) : 0;
+       if (psstrm->FileLimReached ()) {
+         if (opt_multifile_segment || master == 0)
+           psstrm->NextFile ();
+         else {
+           if (master->NextAUType () == IFRAME) {
+             seg_state = runout_segment;
+             runout_PTS = master->NextRequiredPTS ();
+             mjpeg_debug ("Running out to (raw) PTS %lld SCR=%lld",
+                          runout_PTS / 300, current_SCR / 300);
+             running_out = true;
+             seg_state = runout_segment;
+           }
+         }
+       } else if (master != 0 && master->EndSeq ()) {
+         if (split_at_seq_end && master->Lookahead () != 0) {
+           if (!master->SeqHdrNext () || master->NextAUType () != IFRAME) {
+             mjpeg_error_exit1 ("Sequence split detected %d but no following sequence found...",
+                                master->NextAUType ());
+           }
+
+           runout_PTS = master->NextRequiredPTS ();
+           mjpeg_debug ("Running out to %lld SCR=%lld", runout_PTS / 300, current_SCR / 300);
+           MuxStatus (LOG_INFO);
+           running_out = true;
+           seg_state = runout_segment;
+         }
+       }
+       break;
+
+    }
+
+    padding_packet = false;
+    start_of_new_pack = (packets_left_in_pack == packets_per_pack);
+
+    for (str = estreams->begin (); str < estreams->end (); ++str) {
+      (*str)->DemuxedTo (current_SCR);
+    }
+
+
+
+    //
+    // Find the ready-to-mux stream with the most urgent DTS
+    //
+    ElementaryStream *despatch = 0;
+    clockticks earliest = 0;
+
+    for (str = estreams->begin (); str < estreams->end (); ++str) {
+      /* DEBUG
+         mjpeg_info("STREAM %02x: SCR=%lld mux=%d reqDTS=%lld", 
+         (*str)->stream_id,
+         current_SCR /300,
+         (*str)->MuxPossible(current_SCR),
+         (*str)->RequiredDTS()/300
+         );
+       */
+      if ((*str)->MuxPossible (current_SCR) &&
+         (!video_first || (*str)->Kind () == ElementaryStream::video)
+       ) {
+       if (despatch == 0 || earliest > (*str)->RequiredDTS ()) {
+         despatch = *str;
+         earliest = (*str)->RequiredDTS ();
+       }
+      }
+    }
+
+    if (underrun_ignore > 0)
+      --underrun_ignore;
+
+    if (despatch) {
+      despatch->OutputSector ();
+      video_first = false;
+      if (current_SCR >= earliest && underrun_ignore == 0) {
+       mjpeg_warn ("Stream %02x: data will arrive too late sent(SCR)=%lld required(DTS)=%d",
+                   despatch->stream_id, current_SCR / 300, (int) earliest / 300);
+       MuxStatus (LOG_WARN);
+       // Give the stream a chance to recover
+       underrun_ignore = 300;
+       ++underruns;
+       if (underruns > 10 && !opt_ignore_underrun) {
+         mjpeg_error_exit1 ("Too many frame drops -exiting");
+       }
+      }
+      if (despatch->nsec > 50 && despatch->Lookahead () != 0 && !running_out)
+       despatch->UpdateBufferMinMax ();
+      padding_packet = false;
+
+    } else {
+      //
+      // If we got here no stream could be muxed out.
+      // We therefore generate padding packets if necessary
+      // usually this is because reciever buffers are likely to be
+      // full.  
+      //
+      if (vbr) {
+       //
+       // VBR: For efficiency we bump SCR up to five times or
+       // until it looks like buffer status will change
+       NextPosAndSCR ();
+       clockticks next_change = static_cast < clockticks > (0);
+
+       for (str = estreams->begin (); str < estreams->end (); ++str) {
+         clockticks change_time = (*str)->bufmodel.NextChange ();
+
+         if (next_change == 0 || change_time < next_change)
+           next_change = change_time;
+       }
+       unsigned int bumps = 5;
+
+       while (bumps > 0 && next_change > current_SCR + ticks_per_sector) {
+         NextPosAndSCR ();
+         --bumps;
+       }
+      } else {
+       // Just output a padding packet
+       OutputPadding (false);
+      }
+      padding_packet = true;
+    }
+
+    /* Update the counter for pack packets.  VBR is a tricky 
+       case as here padding packets are "virtual" */
+
+    if (!(vbr && padding_packet)) {
+      --packets_left_in_pack;
+      if (packets_left_in_pack == 0)
+       packets_left_in_pack = packets_per_pack;
+    }
+
+    MuxStatus (LOG_DEBUG);
+    /* Unless sys headers are always required we turn them off after the first
+       packet has been generated */
+    include_sys_header = always_sys_header_in_pack;
+
+    pcomp = completed.begin ();
+    str = estreams->begin ();
+    while (str < estreams->end ()) {
+      if (!(*pcomp) && (*str)->MuxCompleted ()) {
+       mjpeg_info ("STREAM %02x completed @ %d.", (*str)->stream_id, (*str)->au->dorder);
+       MuxStatus (LOG_DEBUG);
+       (*pcomp) = true;
+      }
+      ++str;
+      ++pcomp;
+    }
+
+    return true;
+  }
+
+  return false;
+}
+
+
+void
+OutputStream::Close ()
+{
+  // Tidy up
+  OutputSuffix ();
+  psstrm->Close ();
+  mjpeg_info ("Multiplex completion at SCR=%lld.", current_SCR / 300);
+  MuxStatus (LOG_INFO);
+  for (str = estreams->begin (); str < estreams->end (); ++str) {
+    (*str)->Close ();
+    if ((*str)->nsec <= 50)
+      mjpeg_info ("BUFFERING stream too short for useful statistics");
+    else
+      mjpeg_info ("BUFFERING min %d Buf max %d", (*str)->BufferMin (), (*str)->BufferMax ());
+  }
+
+  if (underruns > 0) {
+    mjpeg_error ("foo");
+    mjpeg_error_exit1 ("MUX STATUS: Frame data under-runs detected!");
+  } else {
+    mjpeg_info ("foo");
+    mjpeg_info ("MUX STATUS: no under-runs detected.");
+  }
+}
+
+/**
+   Calculate the packet payload of the output stream at a certain timestamp. 
+@param strm the output stream
+@param buffers the number of buffers
+@param PTSstamp presentation time stamp
+@param DTSstamp decoding time stamp
+ */
+unsigned int
+OutputStream::PacketPayload (MuxStream & strm, bool buffers, bool PTSstamp, bool DTSstamp)
+{
+  return psstrm->PacketPayload (strm, sys_header_ptr, pack_header_ptr, buffers, PTSstamp, DTSstamp)
+    - strm.StreamHeaderSize ();
+}
+
+/***************************************************
+
+  WritePacket - Write out a normal packet carrying data from one of
+              the elementary stream being muxed.
+@param max_packet_data_size the maximum packet data size allowed
+@param strm output mux stream
+@param buffers ?
+@param PTSstamp presentation time stamp of the packet
+@param DTSstamp decoding time stamp of the packet
+@param timestamps ?
+@param returns the written bytes/packets (?)
+***************************************************/
+
+unsigned int
+OutputStream::WritePacket (unsigned int max_packet_data_size,
+                          MuxStream & strm,
+                          bool buffers, clockticks PTS, clockticks DTS, uint8_t timestamps)
+{
+  unsigned int written = psstrm->CreateSector (pack_header_ptr,
+                                              sys_header_ptr,
+                                              max_packet_data_size,
+                                              strm,
+                                              buffers,
+                                              false,
+                                              PTS,
+                                              DTS,
+                                              timestamps);
+
+  NextPosAndSCR ();
+  return written;
+}
+
+/***************************************************
+ *
+ * WriteRawSector - Write out a packet carrying data for
+ *                    a control packet with irregular content.
+@param rawsector data for the raw sector
+@param length length of the raw sector
+ ***************************************************/
+
+void
+OutputStream::WriteRawSector (uint8_t * rawsector, unsigned int length)
+{
+  //
+  // Writing raw sectors when packs stretch over multiple sectors
+  // is a recipe for disaster!
+  //
+  assert (packets_per_pack == 1);
+  psstrm->RawWrite (rawsector, length);
+  NextPosAndSCR ();
+
+}
+
+
+
+/******************************************************************
+       OutputPadding
+
+       generates Pack/Sys Header/Packet information for a 
+       padding stream and saves the sector
+
+       We have to pass in a special flag to cope with appalling mess VCD
+       makes of audio packets (the last 20 bytes being dropped thing) 0 =
+       Fill the packet completetely.  This include "audio packets" that
+    include no actual audio, only a system header and padding.
+@param vcd_audio_pad flag for VCD audio padding
+******************************************************************/
+
+
+void
+OutputStream::OutputPadding (bool vcd_audio_pad)
+{
+  if (vcd_audio_pad)
+    psstrm->CreateSector (pack_header_ptr, sys_header_ptr,
+                         0, vcdapstrm, false, false, 0, 0, TIMESTAMPBITS_NO);
+  else
+    psstrm->CreateSector (pack_header_ptr, sys_header_ptr,
+                         0, pstrm, false, false, 0, 0, TIMESTAMPBITS_NO);
+  ++pstrm.nsec;
+  NextPosAndSCR ();
+
+}
+
+ /******************************************************************
+ *     OutputGOPControlSector
+ *  DVD System headers are carried in peculiar sectors carrying 2
+ *  PrivateStream2 packets.   We're sticking 0's in the packets
+ *  as we have no idea what's supposed to be in there.
+ *
+ * Thanks to Brent Byeler who worked out this work-around.
+ *
+ ******************************************************************/
+
+void
+OutputStream::OutputDVDPriv2 ()
+{
+  uint8_t *packet_size_field;
+  uint8_t *index;
+  uint8_t sector_buf[sector_size];
+  unsigned int tozero;
+
+  assert (sector_size == 2048);
+  PS_Stream::BufferSectorHeader (sector_buf, pack_header_ptr, &sys_header, index);
+  PS_Stream::BufferPacketHeader (index, PRIVATE_STR_2, 2,      // MPEG 2
+                                false, // No buffers
+                                0, 0, 0,       // No timestamps
+                                0, TIMESTAMPBITS_NO, packet_size_field, index);
+  tozero = sector_buf + 1024 - index;
+  memset (index, 0, tozero);
+  index += tozero;
+  PS_Stream::BufferPacketSize (packet_size_field, index);
+
+  PS_Stream::BufferPacketHeader (index, PRIVATE_STR_2, 2,      // MPEG 2
+                                false, // No buffers
+                                0, 0, 0,       // No timestamps
+                                0, TIMESTAMPBITS_NO, packet_size_field, index);
+  tozero = sector_buf + 2048 - index;
+  memset (index, 0, tozero);
+  index += tozero;
+  PS_Stream::BufferPacketSize (packet_size_field, index);
+
+  WriteRawSector (sector_buf, sector_size);
+}
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/outputstream.hh b/ext/mplex/outputstream.hh
new file mode 100644 (file)
index 0000000..3b62561
--- /dev/null
@@ -0,0 +1,198 @@
+
+#ifndef __OUTPUTSTREAM_H__
+#define __OUTPUTSTREAM_H__
+
+#include <config.h>
+#include <stdio.h>
+#include "mjpeg_types.h"
+
+#include "inputstrm.hh"
+#include "padstrm.hh"
+#include "systems.hh"
+
+typedef enum
+{ start_segment, mid_segment,
+  runout_segment
+}
+segment_state;
+
+class OutputStream
+{
+public:
+  OutputStream ()
+  {
+    underrun_ignore = 0;
+    underruns = 0;
+
+    opt_verbosity = 1;
+    opt_buffer_size = 46;
+    opt_data_rate = 0;          /* 3486 = 174300B/sec would be right for VCD */
+    opt_video_offset = 0;
+    opt_audio_offset = 0;
+    opt_sector_size = 2324;
+    opt_VBR = 0;
+    opt_mpeg = 1;
+    opt_mux_format = 0;         /* Generic MPEG-1 stream as default */
+    opt_multifile_segment = 0;
+    opt_always_system_headers = 0;
+    opt_packets_per_pack = 20;
+    opt_ignore_underrun = false;
+    opt_max_segment_size = 0;
+  }
+  bool OutputMultiplex ();
+  void Init (vector < ElementaryStream * >*strms, PS_Stream *stream);
+  void Close ();
+
+  void InitSyntaxParameters ();
+  void ByteposTimecode (bitcount_t bytepos, clockticks & ts);
+
+  inline Sys_header_struc *SystemHeader ()
+  {
+    return &sys_header;
+  }
+
+  unsigned int PacketPayload (MuxStream & strm, bool buffers, bool PTSstamp, bool DTSstamp);
+  unsigned int WritePacket (unsigned int max_packet_data_size,
+                           MuxStream & strm,
+                           bool buffers, clockticks PTS, clockticks DTS, uint8_t timestamps);
+
+  /* Special "unusual" sector types needed for particular formats 
+   */
+
+  void OutputDVDPriv2 ();
+
+  /* Syntax control parameters, public becaus they're partly referenced
+     by the input stream objects.
+   */
+
+  bool always_sys_header_in_pack;
+  bool dtspts_for_all_vau;
+  bool sys_header_in_pack1;
+  bool buffers_in_video;
+  bool always_buffers_in_video;
+  bool buffers_in_audio;
+  bool always_buffers_in_audio;
+  bool sector_align_iframeAUs;
+  bool split_at_seq_end;
+  bool seg_starts_with_video;
+  bool timestamp_iframe_only;
+  bool video_buffers_iframe_only;
+  unsigned int audio_buffer_size;
+  unsigned int video_buffer_size;
+
+  /* more profile options */
+  int opt_verbosity;
+  int opt_quiet_mode;
+  int opt_buffer_size;
+  int opt_data_rate;
+  int opt_video_offset;
+  int opt_audio_offset;
+  int opt_sector_size;
+  int opt_VBR;
+  int opt_mpeg;
+  int opt_mux_format;
+  int opt_multifile_segment;
+  int opt_always_system_headers;
+  int opt_packets_per_pack;
+  bool opt_stills;
+  bool opt_ignore_underrun;
+  int verbose;
+  off_t opt_max_segment_size;
+
+  /* Sequence run-out control */
+  bool running_out;
+  clockticks runout_PTS;
+
+
+/* In some situations the system/PES packets are embedded with
+   external transport data which has to be taken into account for SCR
+   calculations to be correct.  E.g. VCD streams. Where each 2324 byte
+   system packet is embedded in a 2352 byte CD sector and the actual
+   MPEG data is preceded by 30 empty sectors.
+*/
+
+  unsigned int sector_transport_size;
+  unsigned int transport_prefix_sectors;
+  unsigned int sector_size;
+  unsigned int vcd_zero_stuffing;      /* VCD audio sectors have 20 0 bytes :-( */
+
+  int dmux_rate;               /* Actual data mux-rate for calculations always a multiple of 50  */
+  int mux_rate;                        /* MPEG mux rate (50 byte/sec units      */
+  unsigned int packets_per_pack;
+
+private:
+
+  /* Stream packet component buffers */
+
+  Sys_header_struc sys_header;
+  Pack_struc pack_header;
+  Pack_struc *pack_header_ptr;
+  Sys_header_struc *sys_header_ptr;
+  bool start_of_new_pack;
+  bool include_sys_header;
+
+  /* Under-run error messages */
+  unsigned int underruns;
+  unsigned int underrun_ignore;
+
+  /* Output data stream... */
+  PS_Stream *psstrm;
+  bitcount_t bytes_output;
+  clockticks ticks_per_sector;
+
+public:
+  clockticks current_SCR;
+private:
+  clockticks audio_delay;
+  clockticks video_delay;
+  bool vbr;
+
+  /* Source data streams */
+  /* Note: 1st video stream is regarded as the "master" stream for
+     the purpose of splitting sequences etc...
+   */
+  vector < ElementaryStream * >*estreams;      // Complete set
+  vector < ElementaryStream * >vstreams;       // Video streams in estreams
+  vector < ElementaryStream * >astreams;       // Audio streams in estreams
+
+  PaddingStream pstrm;
+  VCDAPadStream vcdapstrm;
+  DVDPriv2Stream dvdpriv2strm;
+
+private:
+  unsigned int RunInSectors ();
+
+  void NextPosAndSCR ();
+  void SetPosAndSCR (bitcount_t bytepos);
+
+  void OutputPrefix ();
+
+  void OutputSuffix ();
+  void OutputPadding (bool vcd_audio_pad);
+  void MuxStatus (log_level_t level);
+
+  void WriteRawSector (uint8_t * rawpackets, unsigned int length);
+  void AppendMuxStreamsOf (vector < ElementaryStream * >&elem, vector < MuxStream * >&mux);
+
+  /* state var for muxing */
+  segment_state seg_state;
+
+  vector < bool > completed;
+  vector < bool >::iterator pcomp;
+  vector < ElementaryStream * >::iterator str;
+
+  unsigned int packets_left_in_pack;        /* Suppress warning */
+  bool padding_packet;
+  bool video_first;
+
+/* 
+ * Local variables:
+ *  c-file-style: "gnu"
+ *  tab-width: 8
+ *  indent-tabs-mode: nil
+ * End:
+ */
+};
+
+
+#endif //__OUTPUTSTREAM_H__
diff --git a/ext/mplex/padstrm.cc b/ext/mplex/padstrm.cc
new file mode 100644 (file)
index 0000000..95e32db
--- /dev/null
@@ -0,0 +1,59 @@
+
+/*
+ *  padstrm.cc:  Padding stream pseudo-input streams
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include "padstrm.hh"
+
+
+
+//
+// Generator for padding packets in a padding stream...
+//
+
+
+unsigned int
+PaddingStream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  memset (dst, STUFFING_BYTE, to_read);
+  return to_read;
+}
+
+unsigned int
+VCDAPadStream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  memset (dst, STUFFING_BYTE, to_read);
+  return to_read;
+}
+
+unsigned int
+DVDPriv2Stream::ReadPacketPayload (uint8_t * dst, unsigned int to_read)
+{
+  memset (dst, 0, to_read);
+  return to_read;
+}
+\f
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/padstrm.hh b/ext/mplex/padstrm.hh
new file mode 100644 (file)
index 0000000..3300e5b
--- /dev/null
@@ -0,0 +1,73 @@
+
+/*
+ *  padstrm.hh:  Padding stream pseudo input-streamsin
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#ifndef __PADSTRM_H__
+#define __PADSTRM_H__
+
+#include "inputstrm.hh"
+
+
+class PaddingStream:public MuxStream
+{
+public:
+  PaddingStream ()
+  {
+    MuxStream::Init (PADDING_STR, 0, 0, 0, false, false);
+  }
+
+  unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+};
+
+class VCDAPadStream:public MuxStream
+{
+public:
+  VCDAPadStream ()
+  {
+    Init (PADDING_STR, 0, 0, 20, false, false);
+
+  }
+
+  unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+};
+
+class DVDPriv2Stream:public MuxStream
+{
+public:
+  DVDPriv2Stream ()
+  {
+    Init (PRIVATE_STR_2, 0, 0, 0, false, false);
+
+  }
+
+  unsigned int ReadPacketPayload (uint8_t * dst, unsigned int to_read);
+};
+
+
+#endif // __PADSTRM_H__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/stillsstream.cc b/ext/mplex/stillsstream.cc
new file mode 100644 (file)
index 0000000..95c8950
--- /dev/null
@@ -0,0 +1,193 @@
+
+/*
+ *  stillsstreams.c: Class for elemenary still video streams
+ *                   Most functionality is inherited from VideoStream
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include <format_codes.h>
+
+#include "stillsstream.hh"
+#include "outputstream.hh"
+
+void
+StillsStream::Init ()
+{
+  int stream_id = 0;
+  int buffer_size = 0;
+
+  SetBufSize (4 * 1024 * 1024);
+  InitAUbuffer ();
+  ScanFirstSeqHeader ();
+
+  mjpeg_debug ("SETTING video buffer to %d", muxinto.video_buffer_size);
+  switch (opt_mux_format) {
+    case MPEG_FORMAT_VCD_STILL:
+      if (horizontal_size > 352) {
+       stream_id = VIDEO_STR_0 + 2;
+       buffer_size = vbv_buffer_size * 2048;
+       mjpeg_info ("Stills Stream %02x: high-resolution VCD stills %d KB each",
+                   stream_id, buffer_size);
+       if (buffer_size < 46 * 1024)
+         mjpeg_error_exit1
+           ("I Can't multiplex high-res stills smaller than normal res stills - sorry!");
+
+      } else {
+       stream_id = VIDEO_STR_0 + 1;
+       buffer_size = 46 * 1024;
+       mjpeg_info ("Stills Stream %02x: normal VCD stills", stream_id);
+      }
+      break;
+    case MPEG_FORMAT_SVCD_STILL:
+      if (horizontal_size > 480) {
+       stream_id = VIDEO_STR_0 + 1;
+       buffer_size = 230 * 1024;
+       mjpeg_info ("Stills Stream %02x: high-resolution SVCD stills.", stream_id);
+      } else {
+       stream_id = VIDEO_STR_0 + 1;
+       buffer_size = 230 * 1024;
+       mjpeg_info ("Stills Stream %02x: normal-resolution SVCD stills.", stream_id);
+      }
+      break;
+  }
+
+
+  MuxStream::Init (stream_id, 1,       // Buffer scale
+                  buffer_size, 0,      // Zero stuffing
+                  muxinto.buffers_in_video, muxinto.always_buffers_in_video);
+
+  /* Skip to the end of the 1st AU (*2nd* Picture start!)
+   */
+  AU_hdr = SEQUENCE_HEADER;
+  AU_pict_data = 0;
+  AU_start = 0LL;
+
+  OutputSeqhdrInfo ();
+
+}
+
+
+
+
+/*
+ * Compute DTS / PTS for a VCD/SVCD Stills sequence
+ * TODO: Very crude. Simply assumes each still stays for the specified
+ * frame interval and that enough run-in delay is present for the first
+ * frame to be loaded.
+ *
+ */
+
+void
+StillsStream::NextDTSPTS (clockticks & DTS, clockticks & PTS)
+{
+  clockticks interval = static_cast < clockticks >
+    (intervals->NextFrameInterval () * CLOCKS / frame_rate);
+  clockticks time_for_xfer;
+
+  muxinto.ByteposTimecode (BufferSize (), time_for_xfer);
+
+  DTS = current_PTS + time_for_xfer;   // This frame decoded just after
+  // Predecessor completed.
+  PTS = current_PTS + time_for_xfer + interval;
+  current_PTS = PTS;
+  current_DTS = DTS;
+}
+
+/*
+ * VCD mixed stills segment items have the constraint that both stills
+ * streams must end together.  To do this each stream has to know
+ * about its "sibling".
+ *
+ */
+
+void
+VCDStillsStream::SetSibling (VCDStillsStream * _sibling)
+{
+  assert (_sibling != 0);
+  sibling = _sibling;
+  if (sibling->stream_id == stream_id) {
+    mjpeg_error_exit1 ("VCD mixed stills stream cannot contain two streams of the same type!");
+  }
+
+}
+
+/*
+ * Check if we've reached the last sector of the last AU.  Note: that
+ * we know no PTS/DTS time-stamps will be needed because no new AU
+ * will appear in the last sector.  WARNING: We assume a still won't
+ * fit into a single secotr.
+ *
+ */
+
+bool
+VCDStillsStream::LastSectorLastAU ()
+{
+  return (Lookahead () == 0 &&
+         au_unsent <= muxinto.PacketPayload (*this, buffers_in_header, false, false)
+    );
+}
+
+
+/*
+ * The requirement that VCD mixed stills segment items constituent streams
+ * end together means we can't mux the last sector of the last AU of
+ * such streams until its sibling has already completed muxing or is
+ * also ready to mux the last sector of its last AU.
+ *
+ * NOTE: Will not work right if sector_align_iframe_AUs not set as this
+ * will allow multiple AU's in  a sector.
+ *
+ */
+
+
+bool
+VCDStillsStream::MuxPossible ()
+{
+  if (bufmodel.Size () < au_unsent) {
+    mjpeg_error_exit1
+      ("Illegal VCD still: larger than maximum permitted by its buffering parameters!");
+  }
+  if (RunOutComplete () || bufmodel.Space () < au_unsent) {
+    return false;
+  }
+
+  if (LastSectorLastAU ()) {
+    if (sibling != 0) {
+      if (!stream_mismatch_warned && sibling->NextAUType () != NOFRAME) {
+       mjpeg_warn ("One VCD stills stream runs significantly longer than the other!");
+       mjpeg_warn ("Simultaneous stream ending recommended by standard not possible");
+       return true;
+      }
+      return sibling->MuxCompleted () || sibling->LastSectorLastAU ();
+    } else
+      return true;
+  } else
+    return true;
+}
+\f
+
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/stillsstream.hh b/ext/mplex/stillsstream.hh
new file mode 100644 (file)
index 0000000..bfd2aca
--- /dev/null
@@ -0,0 +1,107 @@
+
+/*
+ *  stillsstreams.c: Class for elemenary still video streams
+ *                   Most functionality is inherited from VideoStream
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include "videostrm.hh"
+
+class FrameIntervals
+{
+public:
+  virtual int NextFrameInterval () = 0;
+};
+
+//
+// Class of sequence of frame intervals.
+//
+
+class ConstantFrameIntervals:public FrameIntervals
+{
+public:
+  ConstantFrameIntervals (int _frame_interval):frame_interval (_frame_interval)
+  {
+  }
+  int NextFrameInterval ()
+  {
+    return frame_interval;
+  };
+private:
+  int frame_interval;
+};
+
+
+//
+// Class for video stills sequence for (S)VCD non-mixed stills segment 
+// item
+//
+
+class StillsStream:public VideoStream
+{
+public:
+  StillsStream (IBitStream & ibs,
+               OutputStream & into, FrameIntervals * frame_ints):VideoStream (ibs, into),
+    current_PTS (0LL), current_DTS (0LL), intervals (frame_ints)
+  {
+  }
+  void Init ();
+
+private:
+  virtual void NextDTSPTS (clockticks & DTS, clockticks & PTS);
+  clockticks current_PTS;
+  clockticks current_DTS;
+  FrameIntervals *intervals;
+
+  int opt_mux_format;
+};
+
+//
+// Class for video stills sequence for VCD mixed stills Segment item.
+// 
+
+class VCDStillsStream:public StillsStream
+{
+public:
+  VCDStillsStream (IBitStream & ibs,
+                  OutputStream & into, FrameIntervals * frame_ints):StillsStream (ibs, into,
+                                                                                  frame_ints),
+    sibling (0), stream_mismatch_warned (false)
+  {
+  }
+
+  void SetSibling (VCDStillsStream *);
+  virtual bool MuxPossible ();
+
+private:
+  bool LastSectorLastAU ();
+  VCDStillsStream *sibling;
+  bool stream_mismatch_warned;
+};
+\f
+
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/systems.cc b/ext/mplex/systems.cc
new file mode 100644 (file)
index 0000000..8aa3937
--- /dev/null
@@ -0,0 +1,761 @@
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include "systems.hh"
+#include "mplexconsts.hh"
+
+uint8_t dummy_buf[8000];
+void
+PS_Stream::Init (unsigned _mpeg, unsigned int _sector_size, off_t max_seg_size)
+{
+  max_segment_size = max_seg_size;
+  mpeg_version = _mpeg;
+  sector_size = _sector_size;
+  segment_num = 1;
+  written = 0;
+
+  sector_buf = new uint8_t[_sector_size];
+}
+
+bool PS_Stream::FileLimReached ()
+{
+  return max_segment_size != 0 && written > max_segment_size;
+}
+
+void
+PS_Stream::NextFile ()
+{
+/*
+  char prev_filename[strlen (cur_filename) + 1];
+
+  //fclose (strm);
+  ++segment_num;
+  strcpy (prev_filename, cur_filename);
+  snprintf (cur_filename, MAXPATHLEN, filename_pat, segment_num);
+  if (strcmp (prev_filename, cur_filename) == 0) {
+    mjpeg_error_exit1
+      ("Need to split output but there appears to be no %%d in the filename pattern %s",
+       filename_pat);
+  }
+  strm = fopen (cur_filename, "wb");
+  if (strm == NULL) {
+    mjpeg_error_exit1 ("Could not open for writing: %s", cur_filename);
+  }
+  */
+}
+
+/**************************************************************
+
+        Packet payload compute how much payload a sector-sized packet with the 
+        specified headers can carry...
+     TODO: Should really be called "Sector Payload"
+**************************************************************/
+
+
+unsigned int
+PS_Stream::PacketPayload (MuxStream & strm,
+                         Sys_header_struc * sys_header,
+                         Pack_struc * pack_header, int buffers, int PTSstamp, int DTSstamp)
+{
+  int payload = sector_size - (PACKET_HEADER_SIZE + strm.zero_stuffing);
+
+  if (sys_header != NULL)
+    payload -= sys_header->length;
+  if (mpeg_version == 2) {
+    if (buffers)
+      payload -= MPEG2_BUFFERINFO_LENGTH;
+
+    payload -= MPEG2_AFTER_PACKET_LENGTH_MIN;
+    if (pack_header != NULL)
+      payload -= pack_header->length;
+    if (DTSstamp)
+      payload -= DTS_PTS_TIMESTAMP_LENGTH;
+    if (PTSstamp)
+      payload -= DTS_PTS_TIMESTAMP_LENGTH;
+
+  } else {
+    if (buffers)
+      payload -= MPEG1_BUFFERINFO_LENGTH;
+
+    payload -= MPEG1_AFTER_PACKET_LENGTH_MIN;
+    if (pack_header != NULL)
+      payload -= pack_header->length;
+    if (DTSstamp)
+      payload -= DTS_PTS_TIMESTAMP_LENGTH;
+    if (PTSstamp)
+      payload -= DTS_PTS_TIMESTAMP_LENGTH;
+    if (DTSstamp || PTSstamp)
+      payload += 1;            /* No need for nostamp marker ... */
+
+  }
+
+  return payload;
+}
+
+
+
+/*************************************************************************
+    Kopiert einen TimeCode in einen Bytebuffer. Dabei wird er nach
+    MPEG-Verfahren in bits aufgesplittet.
+
+    Makes a Copy of a TimeCode in a Buffer, splitting it into bitfields
+    for MPEG-1/2 DTS/PTS fields and MPEG-1 pack scr fields
+*************************************************************************/
+
+void
+PS_Stream::BufferDtsPtsMpeg1ScrTimecode (clockticks timecode, uint8_t marker, uint8_t ** buffer)
+{
+  clockticks thetime_base;
+  uint8_t temp;
+  unsigned int msb, lsb;
+
+  /* MPEG-1 uses a 90KHz clock, extended to 300*90KHz = 27Mhz in MPEG-2 */
+  /* For these fields we only encode to MPEG-1 90Khz resolution... */
+
+  thetime_base = timecode / 300;
+  msb = (thetime_base >> 32) & 1;
+  lsb = (thetime_base & 0xFFFFFFFFLL);
+
+  temp = (marker << 4) | (msb << 3) | ((lsb >> 29) & 0x6) | 1;
+  *((*buffer)++) = temp;
+  temp = (lsb & 0x3fc00000) >> 22;
+  *((*buffer)++) = temp;
+  temp = ((lsb & 0x003f8000) >> 14) | 1;
+  *((*buffer)++) = temp;
+  temp = (lsb & 0x7f80) >> 7;
+  *((*buffer)++) = temp;
+  temp = ((lsb & 0x007f) << 1) | 1;
+  *((*buffer)++) = temp;
+
+}
+
+/*************************************************************************
+    Makes a Copy of a TimeCode in a Buffer, splitting it into bitfields
+    for MPEG-2 pack scr fields  which use the full 27Mhz resolution
+    
+    Did they *really* need to put a 27Mhz
+    clock source into the system stream.  Does anyone really need it
+    for their decoders?  Get real... I guess they thought it might allow
+    someone somewhere to save on a proper clock circuit.
+*************************************************************************/
+
+
+void
+PS_Stream::BufferMpeg2ScrTimecode (clockticks timecode, uint8_t ** buffer)
+{
+  clockticks thetime_base;
+  unsigned int thetime_ext;
+  uint8_t temp;
+  unsigned int msb, lsb;
+
+  thetime_base = timecode / 300;
+  thetime_ext = timecode % 300;
+  msb = (thetime_base >> 32) & 1;
+  lsb = thetime_base & 0xFFFFFFFFLL;
+
+
+  temp = (MARKER_MPEG2_SCR << 6) | (msb << 5) | ((lsb >> 27) & 0x18) | 0x4 | ((lsb >> 28) & 0x3);
+  *((*buffer)++) = temp;
+  temp = (lsb & 0x0ff00000) >> 20;
+  *((*buffer)++) = temp;
+  temp = ((lsb & 0x000f8000) >> 12) | 0x4 | ((lsb & 0x00006000) >> 13);
+  *((*buffer)++) = temp;
+  temp = (lsb & 0x00001fe0) >> 5;
+  *((*buffer)++) = temp;
+  temp = ((lsb & 0x0000001f) << 3) | 0x4 | ((thetime_ext & 0x00000180) >> 7);
+  *((*buffer)++) = temp;
+  temp = ((thetime_ext & 0x0000007F) << 1) | 1;
+  *((*buffer)++) = temp;
+}
+
+/*************************************************************************
+
+BufferPaddingPacket - Insert a padding packet of the desired length
+                      into the specified Program/System stream buffer
+
+**************************************************************************/
+
+void
+PS_Stream::BufferPaddingPacket (int padding, uint8_t * &buffer)
+{
+  uint8_t *index = buffer;
+  int i;
+
+  assert ((mpeg_version == 2 && padding >= 6) || (mpeg_version == 1 && padding >= 7));
+
+  *(index++) = static_cast < uint8_t > (PACKET_START) >> 16;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x00ffff) >> 8;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x0000ff);
+  *(index++) = PADDING_STR;
+  *(index++) = static_cast < uint8_t > ((padding - 6) >> 8);
+  *(index++) = static_cast < uint8_t > ((padding - 6) & 0xff);
+  if (mpeg_version == 2) {
+    for (i = 0; i < padding - 6; i++)
+      *(index++) = static_cast < uint8_t > (STUFFING_BYTE);
+  } else {
+    *(index++) = 0x0F;
+    for (i = 0; i < padding - 7; i++)
+      *(index++) = static_cast < uint8_t > (STUFFING_BYTE);
+  }
+
+  buffer = index;
+}
+
+
+void
+PS_Stream::BufferSectorHeader (uint8_t * index,
+                              Pack_struc * pack,
+                              Sys_header_struc * sys_header, uint8_t * &header_end)
+{
+  /* Pack header if present */
+
+  if (pack != NULL) {
+    memcpy (index, pack->buf, pack->length);
+    index += pack->length;
+  }
+
+  /* System header if present */
+
+  if (sys_header != NULL) {
+    memcpy (index, sys_header->buf, sys_header->length);
+    index += sys_header->length;
+  }
+  header_end = index;
+}
+
+/******************************************
+ *
+ * BufferPacketHeader
+ * Construct and MPEG-1/2 header for a packet in the specified
+ * buffer (which *MUST* be long enough) and set points to the start of
+ * the payload and packet length fields.
+ *
+ ******************************************/
+
+
+void
+PS_Stream::BufferPacketHeader (uint8_t * buf,
+                              uint8_t type,
+                              unsigned int mpeg_version,
+                              bool buffers,
+                              unsigned int buffer_size,
+                              uint8_t buffer_scale,
+                              clockticks PTS,
+                              clockticks DTS,
+                              uint8_t timestamps, uint8_t * &size_field, uint8_t * &header_end)
+{
+
+  uint8_t *index = buf;
+  uint8_t *pes_header_len_field = 0;
+
+
+  /* konstante Packet Headerwerte eintragen */
+  /* write constant packet header data */
+
+  *(index++) = static_cast < uint8_t > (PACKET_START) >> 16;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x00ffff) >> 8;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x0000ff);
+  *(index++) = type;
+
+
+  /* we remember this offset so we can fill in the packet size field once
+     we know the actual size... */
+  size_field = index;
+  index += 2;
+
+  if (mpeg_version == 1) {
+    /* MPEG-1: buffer information */
+    if (buffers) {
+      *(index++) = static_cast < uint8_t > (0x40 | (buffer_scale << 5) | (buffer_size >> 8));
+      *(index++) = static_cast < uint8_t > (buffer_size & 0xff);
+    }
+
+    /* MPEG-1: PTS, PTS & DTS, oder gar nichts? */
+    /* should we write PTS, PTS & DTS or nothing at all ? */
+
+    switch (timestamps) {
+      case TIMESTAMPBITS_NO:
+       *(index++) = MARKER_NO_TIMESTAMPS;
+       break;
+      case TIMESTAMPBITS_PTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_JUST_PTS, &index);
+       break;
+      case TIMESTAMPBITS_PTS_DTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_PTS, &index);
+       BufferDtsPtsMpeg1ScrTimecode (DTS, MARKER_DTS, &index);
+       break;
+    }
+  } else if (type != PADDING_STR) {
+    /* MPEG-2 packet syntax header flags. */
+    /* These *DO NOT* appear in padding packets                     */
+    /* TODO: They don't appear in several others either!    */
+    /* First byte:
+       <1,0><PES_scrambling_control:2=0><PES_priority><data_alignment_ind.=0>
+       <copyright=0><original=1> */
+    *(index++) = 0x81;
+    /* Second byte: PTS PTS_DTS or neither?  Buffer info?
+       <PTS_DTS:2><ESCR=0><ES_rate=0>
+       <DSM_trick_mode:2=0><PES_CRC=0><PES_extension=(!!buffers)>
+     */
+    *(index++) = (timestamps << 6) | (!!buffers);
+    /* Third byte:
+       <PES_header_length:8> */
+    pes_header_len_field = index;      /* To fill in later! */
+    index++;
+    /* MPEG-2: the timecodes if required */
+    switch (timestamps) {
+      case TIMESTAMPBITS_PTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_JUST_PTS, &index);
+       break;
+
+      case TIMESTAMPBITS_PTS_DTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_PTS, &index);
+       BufferDtsPtsMpeg1ScrTimecode (DTS, MARKER_DTS, &index);
+       break;
+    }
+
+    /* MPEG-2 The buffer information in a PES_extension */
+    if (buffers) {
+      /* MPEG-2 PES extension header
+         <PES_private_data:1=0><pack_header_field=0>
+         <program_packet_sequence_counter=0>
+         <P-STD_buffer=1><reserved:3=1><{PES_extension_flag_2=0> */
+      *(index++) = static_cast < uint8_t > (0x1e);
+      *(index++) = static_cast < uint8_t > (0x40 | (buffer_scale << 5) | (buffer_size >> 8));
+      *(index++) = static_cast < uint8_t > (buffer_size & 0xff);
+    }
+  }
+
+  if (mpeg_version == 2 && type != PADDING_STR) {
+    *pes_header_len_field = static_cast < uint8_t > (index - (pes_header_len_field + 1));
+  }
+
+  header_end = index;
+}
+
+/*************************************************************************
+ *     CreateSector
+ *
+ *  Creates a complete sector to carry a padding packet or a packet
+ *  from one of the elementary streams.  Pack and System headers are
+ *  prepended if required.
+ *
+ *  We allow for situations where want to
+ *  deliberately reduce the payload carried by stuffing.
+ *  This allows us to deal with tricky situations where the
+ *     header overhead of adding in additional information
+ *     would exceed the remaining payload capacity.
+ *
+ *    Header stuffing and/or a padding packet is appended if the sector is
+ *    unfilled.   Zero stuffing after the end of a packet is also supported
+ *    to allow thos wretched audio packets from VCD's to be handled.
+ *
+ *  TODO: Should really be called "WriteSector"
+ *  TODO: We need to add a mechanism for sub-headers of private streams
+ *        to be generated...
+ *
+ *************************************************************************/
+
+
+unsigned int
+PS_Stream::CreateSector (Pack_struc * pack,
+                        Sys_header_struc * sys_header,
+                        unsigned int max_packet_data_size,
+                        MuxStream & strm,
+                        bool buffers,
+                        bool end_marker, clockticks PTS, clockticks DTS, uint8_t timestamps)
+{
+  unsigned int i;
+  unsigned int j;
+  uint8_t *index;
+  uint8_t *size_offset;
+  uint8_t *fixed_packet_header_end;
+  uint8_t *pes_header_len_offset = 0;
+  unsigned int target_packet_data_size;
+  unsigned int actual_packet_data_size;
+  int packet_data_to_read;
+  unsigned int bytes_short;
+  uint8_t type = strm.stream_id;
+  uint8_t buffer_scale = strm.BufferScale ();
+  unsigned int buffer_size = strm.BufferSizeCode ();
+  unsigned int sector_pack_area;
+
+  index = sector_buf;
+
+  sector_pack_area = sector_size - strm.zero_stuffing;
+  if (end_marker)
+    sector_pack_area -= 4;
+
+  BufferSectorHeader (index, pack, sys_header, index);
+
+  /* konstante Packet Headerwerte eintragen */
+  /* write constant packet header data */
+
+  *(index++) = static_cast < uint8_t > (PACKET_START) >> 16;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x00ffff) >> 8;
+  *(index++) = static_cast < uint8_t > (PACKET_START & 0x0000ff);
+  *(index++) = type;
+
+
+  /* we remember this offset so we can fill in the packet size field once
+     we know the actual size... */
+  size_offset = index;
+  index += 2;
+  fixed_packet_header_end = index;
+
+  if (mpeg_version == 1) {
+    /* MPEG-1: buffer information */
+    if (buffers) {
+      *(index++) = static_cast < uint8_t > (0x40 | (buffer_scale << 5) | (buffer_size >> 8));
+      *(index++) = static_cast < uint8_t > (buffer_size & 0xff);
+    }
+
+    /* MPEG-1: PTS, PTS & DTS, oder gar nichts? */
+    /* should we write PTS, PTS & DTS or nothing at all ? */
+
+    switch (timestamps) {
+      case TIMESTAMPBITS_NO:
+       *(index++) = MARKER_NO_TIMESTAMPS;
+       break;
+      case TIMESTAMPBITS_PTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_JUST_PTS, &index);
+       break;
+      case TIMESTAMPBITS_PTS_DTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_PTS, &index);
+       BufferDtsPtsMpeg1ScrTimecode (DTS, MARKER_DTS, &index);
+       break;
+    }
+  } else if (type != PADDING_STR) {
+    /* MPEG-2 packet syntax header flags. */
+    /* These *DO NOT* appear in padding packets                     */
+    /* TODO: They don't appear in several others either!    */
+    /* First byte:
+       <1,0><PES_scrambling_control:2=0><PES_priority><data_alignment_ind.=0>
+       <copyright=0><original=1> */
+    *(index++) = 0x81;
+    /* Second byte: PTS PTS_DTS or neither?  Buffer info?
+       <PTS_DTS:2><ESCR=0><ES_rate=0>
+       <DSM_trick_mode:2=0><PES_CRC=0><PES_extension=(!!buffers)>
+     */
+    *(index++) = (timestamps << 6) | (!!buffers);
+    /* Third byte:
+       <PES_header_length:8> */
+    pes_header_len_offset = index;     /* To fill in later! */
+    index++;
+    /* MPEG-2: the timecodes if required */
+    switch (timestamps) {
+      case TIMESTAMPBITS_PTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_JUST_PTS, &index);
+       break;
+
+      case TIMESTAMPBITS_PTS_DTS:
+       BufferDtsPtsMpeg1ScrTimecode (PTS, MARKER_PTS, &index);
+       BufferDtsPtsMpeg1ScrTimecode (DTS, MARKER_DTS, &index);
+       break;
+    }
+
+    /* MPEG-2 The buffer information in a PES_extension */
+    if (buffers) {
+      /* MPEG-2 PES extension header
+         <PES_private_data:1=0><pack_header_field=0>
+         <program_packet_sequence_counter=0>
+         <P-STD_buffer=1><reserved:3=1><{PES_extension_flag_2=0> */
+      *(index++) = static_cast < uint8_t > (0x1e);
+      *(index++) = static_cast < uint8_t > (0x40 | (buffer_scale << 5) | (buffer_size >> 8));
+      *(index++) = static_cast < uint8_t > (buffer_size & 0xff);
+    }
+  }
+#ifdef MUX_DEBUG
+  // DVD MPEG2: AC3 in PRIVATE_STR_1
+  if (type == PRIVATE_STR_1) {
+    ac3_header = index;
+    // TODO: should allow multiple AC3 streams...
+    //ac3_header[0] = AC3_SUB_STR_1; // byte: Audio stream number
+    // byte: num of AC3 syncwords
+    // byte: Offset first AC3 syncword (hi)
+    // byte: Offset 2nd AC2 syncword (lo)
+    //index += 4;
+    //subheader_size = 4;
+    subheader_size = 0;
+  } else
+#endif
+
+
+    /* MPEG-1, MPEG-2: data available to be filled is packet_size less
+     * header and MPEG-1 trailer... */
+
+    target_packet_data_size = sector_pack_area - (index - sector_buf);
+
+
+  /* DEBUG: A handy consistency check when we're messing around */
+#ifdef MUX_DEBUG
+  if (type != PADDING_STR && (end_marker ? target_packet_data_size + 4 : target_packet_data_size)
+      !=
+      PacketPayload (strm, sys_header, pack, buffers,
+                    timestamps & TIMESTAMPBITS_PTS, timestamps & TIMESTAMPBITS_DTS))
+  {
+    printf ("\nPacket size calculation error %d S%d P%d B%d %d %d!\n ",
+           timestamps,
+           sys_header != 0, pack != 0, buffers,
+           target_packet_data_size,
+           PacketPayload (strm, sys_header, pack, buffers,
+                          timestamps & TIMESTAMPBITS_PTS, timestamps & TIMESTAMPBITS_DTS));
+    exit (1);
+  }
+#endif
+
+  /* If a maximum payload data size is specified (!=0) and is
+     smaller than the space available thats all we read (the
+     remaining space is stuffed) */
+  if (max_packet_data_size != 0 && max_packet_data_size < target_packet_data_size) {
+    packet_data_to_read = max_packet_data_size;
+  } else
+    packet_data_to_read = target_packet_data_size;
+
+
+  /* MPEG-1, MPEG-2: read in available packet data ... */
+
+  actual_packet_data_size = strm.ReadPacketPayload (index, packet_data_to_read);
+
+  // DVD MPEG2: AC3 in PRIVATE_STR_1: fill in syncword count and offset
+#ifdef MUX_DEBUG
+  if (type == PRIVATE_STR_1) {
+    unsigned int syncwords_found;
+
+    for (i = 0; i < actual_packet_data_size; ++i) {
+      if (index[i + 4] == 0x0b && i + 5 < actual_packet_data_size && index[i + 5] == 0x77) {
+       if (syncwords_found == 0) {
+         if (ac3_header[2] != static_cast < uint8_t > ((i + 1) >> 8) ||
+             ac3_header[3] != static_cast < uint8_t > ((i + 1) & 0xff))
+           printf ("BROKEN HEADER %2x %2x (%2x %2x)\n",
+                   ac3_header[2],
+                   ac3_header[3],
+                   static_cast < uint8_t > ((i + 1) >> 8),
+                   static_cast < uint8_t > ((i + 1) & 0xff));
+       }
+       ++syncwords_found;
+      }
+    }
+  }
+#endif
+  bytes_short = target_packet_data_size - actual_packet_data_size;
+
+  /* Handle the situations where we don't have enough data to fill
+     the packet size fully ...
+     Small shortfalls are dealt with by stuffing, big ones by inserting
+     padding packets.
+   */
+
+
+  if (bytes_short < MINIMUM_PADDING_PACKET_SIZE && bytes_short > 0) {
+    if (mpeg_version == 1) {
+      /* MPEG-1 stuffing happens *before* header data fields. */
+      memmove (fixed_packet_header_end + bytes_short,
+              fixed_packet_header_end, actual_packet_data_size + (index - fixed_packet_header_end)
+       );
+      for (j = 0; j < bytes_short; ++j)
+       fixed_packet_header_end[j] = static_cast < uint8_t > (STUFFING_BYTE);
+    } else {
+      memmove (index + bytes_short, index, actual_packet_data_size);
+      for (j = 0; j < bytes_short; ++j)
+       *(index + j) = static_cast < uint8_t > (STUFFING_BYTE);
+    }
+    index += bytes_short;
+    bytes_short = 0;
+  }
+
+
+  /* MPEG-2: we now know the header length... but we mustn't forget
+     to take into account any non-MPEG headers we've included.
+     Currently this only happens for AC3 audio, but who knows...
+   */
+  if (mpeg_version == 2 && type != PADDING_STR) {
+    unsigned int pes_header_len = index - (pes_header_len_offset + 1);
+
+    *pes_header_len_offset = static_cast < uint8_t > (pes_header_len);
+  }
+  index += actual_packet_data_size;
+  /* MPEG-1, MPEG-2: Now we know that actual packet size */
+  size_offset[0] = static_cast < uint8_t > ((index - size_offset - 2) >> 8);
+  size_offset[1] = static_cast < uint8_t > ((index - size_offset - 2) & 0xff);
+
+  /* The case where we have fallen short enough to allow it to be
+     dealt with by inserting a stuffing packet... */
+  if (bytes_short != 0) {
+    *(index++) = static_cast < uint8_t > (PACKET_START) >> 16;
+    *(index++) = static_cast < uint8_t > (PACKET_START & 0x00ffff) >> 8;
+    *(index++) = static_cast < uint8_t > (PACKET_START & 0x0000ff);
+    *(index++) = PADDING_STR;
+    *(index++) = static_cast < uint8_t > ((bytes_short - 6) >> 8);
+    *(index++) = static_cast < uint8_t > ((bytes_short - 6) & 0xff);
+    if (mpeg_version == 2) {
+      for (i = 0; i < bytes_short - 6; i++)
+       *(index++) = static_cast < uint8_t > (STUFFING_BYTE);
+    } else {
+      *(index++) = 0x0F;       /* TODO: A.Stevens 2000 Why is this here? */
+      for (i = 0; i < bytes_short - 7; i++)
+       *(index++) = static_cast < uint8_t > (STUFFING_BYTE);
+    }
+
+    bytes_short = 0;
+  }
+
+  if (end_marker) {
+    *(index++) = static_cast < uint8_t > ((ISO11172_END) >> 24);
+    *(index++) = static_cast < uint8_t > ((ISO11172_END & 0x00ff0000) >> 16);
+    *(index++) = static_cast < uint8_t > ((ISO11172_END & 0x0000ff00) >> 8);
+    *(index++) = static_cast < uint8_t > (ISO11172_END & 0x000000ff);
+  }
+
+  for (i = 0; i < strm.zero_stuffing; i++)
+    *(index++) = static_cast < uint8_t > (0);
+
+
+  /* At this point padding or stuffing will have ensured the packet
+     is filled to target_packet_data_size
+   */
+  RawWrite (sector_buf, sector_size);
+  return actual_packet_data_size;
+}
+
+
+
+
+/*************************************************************************
+       Create_Pack
+       erstellt in einem Buffer die spezifischen Pack-Informationen.
+       Diese werden dann spaeter von der Sector-Routine nochmals
+       in dem Sektor kopiert.
+
+       writes specifical pack header information into a buffer
+       later this will be copied from the sector routine into
+       the sector buffer
+*************************************************************************/
+
+void
+PS_Stream::CreatePack (Pack_struc * pack, clockticks SCR, unsigned int mux_rate)
+{
+  uint8_t *index;
+
+  index = pack->buf;
+
+  *(index++) = static_cast < uint8_t > ((PACK_START) >> 24);
+  *(index++) = static_cast < uint8_t > ((PACK_START & 0x00ff0000) >> 16);
+  *(index++) = static_cast < uint8_t > ((PACK_START & 0x0000ff00) >> 8);
+  *(index++) = static_cast < uint8_t > (PACK_START & 0x000000ff);
+
+  if (mpeg_version == 2) {
+    /* Annoying: MPEG-2's SCR pack header time is different from
+       all the rest... */
+    BufferMpeg2ScrTimecode (SCR, &index);
+    *(index++) = static_cast < uint8_t > (mux_rate >> 14);
+    *(index++) = static_cast < uint8_t > (0xff & (mux_rate >> 6));
+    *(index++) = static_cast < uint8_t > (0x03 | ((mux_rate & 0x3f) << 2));
+    *(index++) = static_cast < uint8_t > (RESERVED_BYTE << 3 | 0);     /* No pack stuffing */
+  } else {
+    BufferDtsPtsMpeg1ScrTimecode (SCR, MARKER_MPEG1_SCR, &index);
+    *(index++) = static_cast < uint8_t > (0x80 | (mux_rate >> 15));
+    *(index++) = static_cast < uint8_t > (0xff & (mux_rate >> 7));
+    *(index++) = static_cast < uint8_t > (0x01 | ((mux_rate & 0x7f) << 1));
+  }
+  pack->SCR = SCR;
+  pack->length = index - pack->buf;
+}
+
+
+/*************************************************************************
+       Create_Sys_Header
+       erstelle in einem Buffer die spezifischen Sys_Header
+       Informationen. Diese werden spaeter von der Sector-Routine
+       nochmals zum Sectorbuffer kopiert.
+
+       writes specifical system header information into a buffer
+       later this will be copied from the sector routine into
+       the sector buffer
+       RETURN: Length of header created...
+*************************************************************************/
+
+void
+PS_Stream::CreateSysHeader (Sys_header_struc * sys_header,
+                           unsigned int rate_bound,
+                           bool fixed,
+                           int CSPS,
+                           bool audio_lock, bool video_lock, vector < MuxStream * >&streams)
+{
+  uint8_t *index;
+  uint8_t *len_index;
+  int system_header_size;
+
+  index = sys_header->buf;
+  int video_bound = 0;
+  int audio_bound = 0;
+
+  vector < MuxStream * >::iterator str;
+  for (str = streams.begin (); str < streams.end (); ++str) {
+    switch (((*str)->stream_id & 0xe0)) {
+      case 0xe0:               // MPEG Video
+       ++video_bound;
+       break;
+      case 0xb9:               // DVD seems to use this stream id in
+       ++video_bound;          // system headers for video buffer size
+       break;
+      case 0xc0:
+       ++audio_bound;          // MPEG Audio
+       break;
+      default:
+       break;
+    }
+  }
+
+  /* if we are not using both streams, we should clear some
+     options here */
+
+  *(index++) = static_cast < uint8_t > ((SYS_HEADER_START) >> 24);
+  *(index++) = static_cast < uint8_t > ((SYS_HEADER_START & 0x00ff0000) >> 16);
+  *(index++) = static_cast < uint8_t > ((SYS_HEADER_START & 0x0000ff00) >> 8);
+  *(index++) = static_cast < uint8_t > (SYS_HEADER_START & 0x000000ff);
+
+  len_index = index;           /* Skip length field for now... */
+  index += 2;
+
+  *(index++) = static_cast < uint8_t > (0x80 | (rate_bound >> 15));
+  *(index++) = static_cast < uint8_t > (0xff & (rate_bound >> 7));
+  *(index++) = static_cast < uint8_t > (0x01 | ((rate_bound & 0x7f) << 1));
+  *(index++) = static_cast < uint8_t > ((audio_bound << 2) | (fixed << 1) | CSPS);
+  *(index++) = static_cast < uint8_t > ((audio_lock << 7) | (video_lock << 6) | 0x20 | video_bound);
+
+  *(index++) = static_cast < uint8_t > (RESERVED_BYTE);
+  for (str = streams.begin (); str < streams.end (); ++str) {
+    *(index++) = (*str)->stream_id;
+    *(index++) = static_cast < uint8_t >
+      (0xc0 | ((*str)->BufferScale () << 5) | ((*str)->BufferSizeCode () >> 8));
+    *(index++) = static_cast < uint8_t > ((*str)->BufferSizeCode () & 0xff);
+  }
+
+
+  system_header_size = (index - sys_header->buf);
+  len_index[0] = static_cast < uint8_t > ((system_header_size - 6) >> 8);
+  len_index[1] = static_cast < uint8_t > ((system_header_size - 6) & 0xff);
+  sys_header->length = system_header_size;
+}
+
+
+void
+PS_Stream::RawWrite (uint8_t * buf, unsigned int len)
+{
+  if (callback (this, buf, len, user_data) != len) {
+    mjpeg_error_exit1 ("Failed write");
+  }
+  written += len;
+}
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/systems.hh b/ext/mplex/systems.hh
new file mode 100644 (file)
index 0000000..bbad92f
--- /dev/null
@@ -0,0 +1,131 @@
+#ifndef __SYSTEMS_HH__
+#define __SYSTEMS_HH__
+
+#include "inputstrm.hh"
+
+#include <vector>
+
+using std::vector;
+
+/* Buffer size parameters */
+
+#define MAX_SECTOR_SIZE         16384
+#define MAX_PACK_HEADER_SIZE   255
+#define MAX_SYS_HEADER_SIZE     255
+
+
+typedef class PS_Stream _PS_Stream;
+
+typedef size_t (*WriteCallback) (_PS_Stream *str, uint8_t *data, size_t size, void *user_data);
+
+
+typedef struct sector_struc    /* Ein Sektor, kann Pack, Sys Header    */
+/* und Packet enthalten.               */
+{
+  unsigned char buf[MAX_SECTOR_SIZE];
+  unsigned int length_of_packet_data;
+  //clockticks TS                ;
+}
+Sector_struc;
+
+typedef struct pack_struc      /* Pack Info                            */
+{
+  unsigned char buf[MAX_PACK_HEADER_SIZE];
+  int length;
+  clockticks SCR;
+}
+Pack_struc;
+
+typedef struct sys_header_struc        /* System Header Info                   */
+{
+  unsigned char buf[MAX_SYS_HEADER_SIZE];
+  int length;
+}
+Sys_header_struc;
+
+
+class PS_Stream {
+public:
+  PS_Stream (WriteCallback _callback, void *_user_data):
+    callback (_callback),
+    user_data (_user_data)
+    {
+    }
+
+  void Init (unsigned _mpeg, unsigned int _sector_sizen, off_t max_segment_size);      // 0 = No Limit
+
+  bool FileLimReached ();
+  void NextFile ();
+  unsigned int PacketPayload (MuxStream & strm,
+                Sys_header_struc * sys_header,
+                Pack_struc * pack_header, int buffers, int PTSstamp, int DTSstamp);
+
+  unsigned int CreateSector (Pack_struc * pack,
+               Sys_header_struc * sys_header,
+               unsigned int max_packet_data_size,
+               MuxStream & strm,
+               bool buffers, bool end_marker, clockticks PTS, clockticks DTS, uint8_t timestamps);
+  void RawWrite (uint8_t * data, unsigned int len);
+  static void BufferSectorHeader (uint8_t * buf,
+                     Pack_struc * pack, Sys_header_struc * sys_header, uint8_t * &header_end);
+  static void BufferPacketHeader (uint8_t * buf,
+                     uint8_t type,
+                     unsigned int mpeg_version,
+                     bool buffers,
+                     unsigned int buffer_size,
+                     uint8_t buffer_scale,
+                     clockticks PTS,
+                     clockticks DTS,
+                     uint8_t timestamps, uint8_t * &size_field, uint8_t * &header_end);
+
+  static inline void BufferPacketSize (uint8_t * size_field, uint8_t * packet_end)
+  {
+    unsigned int
+      packet_size =
+      packet_end -
+      size_field -
+      2;
+
+    size_field[0] = static_cast < uint8_t > (packet_size >> 8);
+    size_field[1] = static_cast < uint8_t > (packet_size & 0xff);
+
+  }
+
+  void CreatePack (Pack_struc * pack, clockticks SCR, unsigned int mux_rate);
+  void CreateSysHeader (Sys_header_struc * sys_header,
+                  unsigned int rate_bound,
+                  bool fixed,
+                  int CSPS, bool audio_lock, bool video_lock, vector < MuxStream * >&streams);
+
+  void Close ()
+  {
+  }
+
+private:
+
+  /* TODO: Replace **'s with *&'s */
+  static void BufferDtsPtsMpeg1ScrTimecode (clockticks timecode, uint8_t marker, uint8_t ** buffer);
+  static void BufferMpeg2ScrTimecode (clockticks timecode, uint8_t ** buffer);
+  void BufferPaddingPacket (int padding, uint8_t * &buffer);
+
+private:
+  unsigned int mpeg_version;
+  unsigned int sector_size;
+  int segment_num;
+
+  off_t max_segment_size;
+  uint8_t * sector_buf;
+  WriteCallback callback;
+  void *user_data;
+  off_t written;
+};
+#endif // __SYSTEMS_HH__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/vector.cc b/ext/mplex/vector.cc
new file mode 100644 (file)
index 0000000..343b628
--- /dev/null
@@ -0,0 +1,23 @@
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "mjpeg_types.h"
+#include "vector.hh"
+
+
+AUStream::AUStream ():
+cur_rd (0), cur_wr (0), totalctr (0), size (0), buf (0)
+{
+  buf = new (Aunit *)[AUStream::BUF_SIZE];
+}
+
+
+void
+AUStream::init (Aunit * rec)
+{
+  buf[cur_wr] = rec;
+  ++cur_wr;
+  cur_wr = cur_wr >= AUStream::BUF_SIZE ? 0 : cur_wr;
+  cur_rd = cur_wr;
+}
diff --git a/ext/mplex/vector.hh b/ext/mplex/vector.hh
new file mode 100644 (file)
index 0000000..9a4f041
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef __AUSTREAM_H__
+#define __AUSTREAM_H__
+
+#include <config.h>
+#include <deque>
+#include "mjpeg_logging.h"
+#include "aunit.hh"
+
+class AUStream
+{
+public:
+  AUStream ();
+
+  void init (Aunit * rec);
+
+  void append (Aunit & rec)
+  {
+    if (size == BUF_SIZE)
+      mjpeg_error_exit1 ("INTERNAL ERROR: AU buffer overflow");
+    *buf[cur_wr] = rec;
+    ++size;
+    ++cur_wr;
+    cur_wr = cur_wr >= BUF_SIZE ? 0 : cur_wr;
+  }
+
+  inline Aunit *next ()
+  {
+    if (size == 0) {
+      return 0;
+    } else {
+      Aunit *ret;
+
+      ret = buf[cur_rd];
+      ++cur_rd;
+      ++totalctr;
+      --size;
+      cur_rd = cur_rd >= BUF_SIZE ? 0 : cur_rd;
+      return ret;
+    }
+  }
+
+  inline Aunit *lookahead ()
+  {
+    return size == 0 ? 0 : buf[cur_rd];
+  }
+
+  inline Aunit *last ()
+  {
+    int i = cur_wr - 1 < 0 ? BUF_SIZE - 1 : cur_wr - 1;
+
+    return buf[i];
+  }
+
+  static const unsigned int BUF_SIZE = 128;
+
+  inline unsigned int current ()
+  {
+    return totalctr;
+  }
+//private:
+  unsigned int cur_rd;
+  unsigned int cur_wr;
+  unsigned int totalctr;
+  unsigned int size;
+  Aunit **buf;
+};
+
+
+
+
+#endif // __AUSTREAM_H__
diff --git a/ext/mplex/videostrm.hh b/ext/mplex/videostrm.hh
new file mode 100644 (file)
index 0000000..6eeac47
--- /dev/null
@@ -0,0 +1,155 @@
+
+/*
+ *  videostrm.hh:  Video stream elementary input stream
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#ifndef __VIDEOSTRM_H__
+#define __VIDEOSTRM_H__
+
+#include "inputstrm.hh"
+
+class VideoStream:public ElementaryStream
+{
+public:
+  VideoStream (IBitStream & ibs, OutputStream & into);
+  void Init (const int stream_num);
+  static bool Probe (IBitStream & bs);
+
+  void Close ();
+
+  inline int AUType ()
+  {
+    return au->type;
+  }
+
+  inline bool EndSeq ()
+  {
+    return au->end_seq;
+  }
+
+  inline int NextAUType ()
+  {
+    VAunit *p_au = Lookahead ();
+
+    if (p_au != NULL)
+      return p_au->type;
+    else
+      return NOFRAME;
+  }
+
+  inline bool SeqHdrNext ()
+  {
+    VAunit *p_au = Lookahead ();
+
+    return p_au != NULL && p_au->seq_header;
+  }
+
+  virtual unsigned int NominalBitRate ()
+  {
+    return bit_rate * 50;
+  }
+
+  virtual void OutputGOPControlSector ();
+  bool RunOutComplete ();
+  virtual bool MuxPossible (clockticks currentSCR);
+  void SetMaxStdBufferDelay (unsigned int demux_rate);
+  void OutputSector ();
+
+protected:
+  void OutputSeqhdrInfo ();
+  virtual bool AUBufferNeedsRefill ();
+  virtual void FillAUbuffer (unsigned int frames_to_buffer);
+  virtual void InitAUbuffer ();
+  virtual void NextDTSPTS (clockticks & DTS, clockticks & PTS);
+  void ScanFirstSeqHeader ();
+  uint8_t NewAUTimestamps (int AUtype);
+  bool NewAUBuffers (int AUtype);
+
+public:
+  unsigned int num_sequence;
+  unsigned int num_seq_end;
+  unsigned int num_pictures;
+  unsigned int num_groups;
+  unsigned int num_frames[4];
+  unsigned int avg_frames[4];
+
+  unsigned int horizontal_size;
+  unsigned int vertical_size;
+  unsigned int aspect_ratio;
+  unsigned int picture_rate;
+  unsigned int bit_rate;
+  unsigned int comp_bit_rate;
+  unsigned int peak_bit_rate;
+  unsigned int vbv_buffer_size;
+  unsigned int CSPF;
+  double secs_per_frame;
+
+
+  bool dtspts_for_all_au;
+  bool gop_control_packet;
+
+protected:
+
+  /* State variables for scanning source bit-stream */
+  VAunit access_unit;
+  unsigned int fields_presented;
+  unsigned int group_order;
+  unsigned int group_start_pic;
+  unsigned int group_start_field;
+  int temporal_reference;
+  unsigned int pict_rate;
+  unsigned int pict_struct;
+  int pulldown_32;
+  int repeat_first_field;
+  int prev_temp_ref;
+  double frame_rate;
+  unsigned int max_bits_persec;
+  int AU_pict_data;
+  int AU_hdr;
+  clockticks max_PTS;
+  clockticks max_STD_buffer_delay;
+
+  int opt_mpeg;
+  int opt_multifile_segment;
+};
+
+//
+// DVD's generate control sectors for each GOP...
+//
+
+class DVDVideoStream:public VideoStream
+{
+public:
+  DVDVideoStream (IBitStream & ibs, OutputStream & into):VideoStream (ibs, into)
+  {
+    gop_control_packet = true;
+  }
+  void OutputGOPControlSector ();
+};
+
+#endif // __INPUTSTRM_H__
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/videostrm_in.cc b/ext/mplex/videostrm_in.cc
new file mode 100644 (file)
index 0000000..91e6bb3
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ *  inptstrm.c:  Members of video stream class related to raw stream
+ *               scanning and buffering.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+#include <config.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "videostrm.hh"
+#include "outputstream.hh"
+
+
+
+static void
+marker_bit (IBitStream & bs, unsigned int what)
+{
+  if (what != bs.get1bit ()) {
+    mjpeg_error ("Illegal MPEG stream at offset (bits) %lld: supposed marker bit not found.",
+                bs.bitcount ());
+    exit (1);
+  }
+}
+
+
+void
+VideoStream::ScanFirstSeqHeader ()
+{
+  if (bs.getbits (32) == SEQUENCE_HEADER) {
+    num_sequence++;
+    horizontal_size = bs.getbits (12);
+    vertical_size = bs.getbits (12);
+    aspect_ratio = bs.getbits (4);
+    pict_rate = bs.getbits (4);
+    picture_rate = pict_rate;
+    bit_rate = bs.getbits (18);
+    marker_bit (bs, 1);
+    vbv_buffer_size = bs.getbits (10);
+    CSPF = bs.get1bit ();
+
+  } else {
+    mjpeg_error ("Invalid MPEG Video stream header.");
+    exit (1);
+  }
+
+  if (pict_rate > 0 && pict_rate <= mpeg_num_framerates) {
+    frame_rate = Y4M_RATIO_DBL (mpeg_framerate (pict_rate));
+  } else {
+    frame_rate = 25.0;
+  }
+
+}
+
+
+
+
+void
+VideoStream::Init (const int stream_num)
+{
+  mjpeg_debug ("SETTING video buffer to %d", muxinto.video_buffer_size);
+  MuxStream::Init (VIDEO_STR_0 + stream_num, 1,        // Buffer scale
+                  muxinto.video_buffer_size, 0,        // Zero stuffing
+                  muxinto.buffers_in_video, muxinto.always_buffers_in_video);
+  mjpeg_info ("Scanning for header info: Video stream %02x ", VIDEO_STR_0 + stream_num);
+  InitAUbuffer ();
+
+  ScanFirstSeqHeader ();
+
+  /* Skip to the end of the 1st AU (*2nd* Picture start!)
+   */
+  AU_hdr = SEQUENCE_HEADER;
+  AU_pict_data = 0;
+  AU_start = 0LL;
+
+  OutputSeqhdrInfo ();
+}
+
+//
+// Set the Maximum STD buffer delay for this video stream.
+// By default we set 1 second but if we have specified a video
+// buffer that can hold more than 1.0 seconds demuxed data we
+// set the delay to the time to fill the buffer.
+//
+
+void
+VideoStream::SetMaxStdBufferDelay (unsigned int dmux_rate)
+{
+  double max_delay = CLOCKS;
+
+  if (static_cast < double >(BufferSize ()) / dmux_rate > 1.0)
+    max_delay *= static_cast < double >(BufferSize ()) / dmux_rate;
+
+  //
+  // To enforce a maximum STD buffer residency the
+  // calculation is a bit tricky as when we decide to mux we may
+  // (but not always) have some of the *previous* picture left to
+  // mux in which case it is the timestamp of the next picture that counts.
+  // For simplicity we simply reduce the limit by 1.5 frame intervals
+  // and use the timestamp for the current picture.
+  //
+  if (frame_rate > 10.0)
+    max_STD_buffer_delay = static_cast < clockticks > (max_delay * (frame_rate - 1.5) / frame_rate);
+  else
+    max_STD_buffer_delay = static_cast < clockticks > (10.0 * max_delay / frame_rate);
+
+}
+
+//
+// Return whether AU buffer needs refilling.  There are two cases:
+// 1. We have less than our look-ahead "FRAME_CHUNK" buffer AU's
+// buffered 2. AU's are very small and we could have less than 1
+// sector's worth of data buffered.
+//
+
+bool
+VideoStream::AUBufferNeedsRefill ()
+{
+  return
+    !eoscan
+    && (aunits.current () + FRAME_CHUNK > last_buffered_AU
+       || bs.buffered_bytes () < muxinto.sector_size);
+}
+
+//
+// Refill the AU unit buffer setting  AU PTS DTS from the scanned
+// header information...
+//
+
+void
+VideoStream::FillAUbuffer (unsigned int frames_to_buffer)
+{
+  if (eoscan)
+    return;
+
+  last_buffered_AU += frames_to_buffer;
+  mjpeg_debug ("Scanning %d video frames to frame %d", frames_to_buffer, last_buffered_AU);
+
+  // We set a limit of 2M to seek before we give up.
+  // This is intentionally very high because some heavily
+  // padded still frames may have a loooong gap before
+  // a following sequence end marker.
+  while (!bs.eos () &&
+        bs.seek_sync (SYNCWORD_START, 24, 2 * 1024 * 1024) &&
+        decoding_order < last_buffered_AU)
+  {
+    syncword = (SYNCWORD_START << 8) + bs.getbits (8);
+    if (AU_pict_data) {
+
+      /* Handle the header *ending* an AU...
+         If we have the AU picture data an AU and have now
+         reached a header marking the end of an AU fill in the
+         the AU length and append it to the list of AU's and
+         start a new AU.  I.e. sequence and gop headers count as
+         part of the AU of the corresponding picture
+       */
+      stream_length = bs.bitcount () - 32LL;
+      switch (syncword) {
+       case SEQUENCE_HEADER:
+       case GROUP_START:
+       case PICTURE_START:
+         access_unit.start = AU_start;
+         access_unit.length = (int) (stream_length - AU_start) >> 3;
+         access_unit.end_seq = 0;
+         avg_frames[access_unit.type - 1] += access_unit.length;
+         aunits.append (access_unit);
+         mjpeg_debug ("Found AU %d: DTS=%d", access_unit.dorder, (int) access_unit.DTS / 300);
+         AU_hdr = syncword;
+         AU_start = stream_length;
+         AU_pict_data = 0;
+         break;
+       case SEQUENCE_END:
+         access_unit.length = ((stream_length - AU_start) >> 3) + 4;
+         access_unit.end_seq = 1;
+         aunits.append (access_unit);
+         mjpeg_info ("Scanned to end AU %d", access_unit.dorder);
+         avg_frames[access_unit.type - 1] += access_unit.length;
+
+         /* Do we have a sequence split in the video stream? */
+         if (!bs.eos () && bs.getbits (32) == SEQUENCE_HEADER) {
+           stream_length = bs.bitcount () - 32LL;
+           AU_start = stream_length;
+           syncword = AU_hdr = SEQUENCE_HEADER;
+           AU_pict_data = 0;
+           if (opt_multifile_segment)
+             mjpeg_warn
+               ("Sequence end marker found in video stream but single-segment splitting specified!");
+         } else {
+           if (!bs.eos () && !opt_multifile_segment)
+             mjpeg_warn ("No seq. header starting new sequence after seq. end!");
+         }
+
+         num_seq_end++;
+         break;
+      }
+    }
+
+    /* Handle the headers starting an AU... */
+    switch (syncword) {
+      case SEQUENCE_HEADER:
+       /* TODO: Really we should update the info here so we can handle
+          streams where parameters change on-the-fly... */
+       num_sequence++;
+       break;
+
+      case GROUP_START:
+       num_groups++;
+       group_order = 0;
+       break;
+
+      case PICTURE_START:
+       /* We have reached AU's picture data... */
+       AU_pict_data = 1;
+
+       prev_temp_ref = temporal_reference;
+       temporal_reference = bs.getbits (10);
+       access_unit.type = bs.getbits (3);
+
+       /* Now scan forward a little for an MPEG-2 picture coding extension
+          so we can get pulldown info (if present) */
+       if (bs.seek_sync (EXT_START_CODE, 32, 64) && bs.getbits (4) == CODING_EXT_ID) {
+         /* Skip: 4 F-codes (4)... */
+         (void) bs.getbits (16);
+         /* Skip: DC Precision(2) */
+         (void) bs.getbits (2);
+         pict_struct = bs.getbits (2);
+         /* Skip: topfirst (1) frame pred dct (1),
+            concealment_mv(1), q_scale_type (1), */
+         (void) bs.getbits (4);
+         /* Skip: intra_vlc_format(1), alternate_scan (1) */
+         (void) bs.getbits (2);
+         repeat_first_field = bs.getbits (1);
+         pulldown_32 |= repeat_first_field;
+
+       } else {
+         repeat_first_field = 0;
+         pict_struct = PIC_FRAME;
+       }
+
+       if (access_unit.type == IFRAME) {
+         unsigned int bits_persec =
+           (unsigned int) (((double) (stream_length - prev_offset)) *
+                           2 * frame_rate / ((double) (2 + fields_presented - group_start_field)));
+
+         if (bits_persec > max_bits_persec) {
+           max_bits_persec = bits_persec;
+         }
+         prev_offset = stream_length;
+         group_start_pic = decoding_order;
+         group_start_field = fields_presented;
+       }
+
+       NextDTSPTS (access_unit.DTS, access_unit.PTS);
+
+       access_unit.dorder = decoding_order;
+       access_unit.porder = temporal_reference + group_start_pic;
+       access_unit.seq_header = (AU_hdr == SEQUENCE_HEADER);
+
+       decoding_order++;
+       group_order++;
+
+       if ((access_unit.type > 0) && (access_unit.type < 5)) {
+         num_frames[access_unit.type - 1]++;
+       }
+
+
+       if (decoding_order >= old_frames + 1000) {
+         mjpeg_debug ("Got %d picture headers.", decoding_order);
+         old_frames = decoding_order;
+       }
+
+       break;
+
+
+
+    }
+  }
+  last_buffered_AU = decoding_order;
+  num_pictures = decoding_order;
+  eoscan = bs.eos ();
+}
+
+void
+VideoStream::Close ()
+{
+
+  bs.close ();
+  stream_length = (unsigned int) (AU_start / 8);
+  for (int i = 0; i < 4; i++) {
+    avg_frames[i] /= num_frames[i] == 0 ? 1 : num_frames[i];
+  }
+
+  comp_bit_rate = (unsigned int)
+    ((((double) stream_length) / ((double) fields_presented)) * 2.0
+     * ((double) frame_rate) + 25.0) / 50;
+
+  /* Peak bit rate in 50B/sec units... */
+  peak_bit_rate = ((max_bits_persec / 8) / 50);
+  mjpeg_info ("VIDEO_STATISTICS: %02x", stream_id);
+  mjpeg_info ("Video Stream length: %11llu bytes", stream_length / 8);
+  mjpeg_info ("Sequence headers: %8u", num_sequence);
+  mjpeg_info ("Sequence ends   : %8u", num_seq_end);
+  mjpeg_info ("No. Pictures    : %8u", num_pictures);
+  mjpeg_info ("No. Groups      : %8u", num_groups);
+  mjpeg_info ("No. I Frames    : %8u avg. size%6u bytes", num_frames[0], avg_frames[0]);
+  mjpeg_info ("No. P Frames    : %8u avg. size%6u bytes", num_frames[1], avg_frames[1]);
+  mjpeg_info ("No. B Frames    : %8u avg. size%6u bytes", num_frames[2], avg_frames[2]);
+  mjpeg_info ("No. D Frames    : %8u avg. size%6u bytes", num_frames[3], avg_frames[3]);
+  mjpeg_info ("Average bit-rate : %8u bits/sec", comp_bit_rate * 400);
+  mjpeg_info ("Peak bit-rate    : %8u  bits/sec", peak_bit_rate * 400);
+
+}
+
+
+
+
+/*************************************************************************
+       OutputSeqHdrInfo
+     Display sequence header parameters
+*************************************************************************/
+
+void
+VideoStream::OutputSeqhdrInfo ()
+{
+  const char *str;
+
+  mjpeg_info ("VIDEO STREAM: %02x", stream_id);
+
+  mjpeg_info ("Frame width     : %u", horizontal_size);
+  mjpeg_info ("Frame height    : %u", vertical_size);
+  if (aspect_ratio <= mpeg_num_aspect_ratios[opt_mpeg - 1])
+    str = mpeg_aspect_code_definition (opt_mpeg, aspect_ratio);
+  else
+    str = "forbidden";
+  mjpeg_info ("Aspect ratio    : %s", str);
+
+
+  if (picture_rate == 0)
+    mjpeg_info ("Picture rate    : forbidden");
+  else if (picture_rate <= mpeg_num_framerates)
+    mjpeg_info ("Picture rate    : %2.3f frames/sec",
+               Y4M_RATIO_DBL (mpeg_framerate (picture_rate)));
+  else
+    mjpeg_info ("Picture rate    : %x reserved", picture_rate);
+
+  if (bit_rate == 0x3ffff) {
+    bit_rate = 0;
+    mjpeg_info ("Bit rate        : variable");
+  } else if (bit_rate == 0)
+    mjpeg_info ("Bit rate       : forbidden");
+  else
+    mjpeg_info ("Bit rate        : %u bits/sec", bit_rate * 400);
+
+  mjpeg_info ("Vbv buffer size : %u bytes", vbv_buffer_size * 2048);
+  mjpeg_info ("CSPF            : %u", CSPF);
+}
+
+//
+// Compute PTS DTS of current AU in the video sequence being
+// scanned.  This is is the PTS/DTS calculation for normal video only.
+// It is virtual and over-ridden for non-standard streams (Stills
+// etc!).
+//
+
+void
+VideoStream::NextDTSPTS (clockticks & DTS, clockticks & PTS)
+{
+  if (pict_struct != PIC_FRAME) {
+    DTS = static_cast < clockticks > (fields_presented * (double) (CLOCKS / 2) / frame_rate);
+    int dts_fields = temporal_reference * 2 + group_start_field + 1;
+
+    if (temporal_reference == prev_temp_ref)
+      dts_fields += 1;
+    PTS = static_cast < clockticks > (dts_fields * (double) (CLOCKS / 2) / frame_rate);
+    access_unit.porder = temporal_reference + group_start_pic;
+    fields_presented += 1;
+  } else if (pulldown_32) {
+    int frames2field;
+    int frames3field;
+
+    DTS = static_cast < clockticks > (fields_presented * (double) (CLOCKS / 2) / frame_rate);
+    if (repeat_first_field) {
+      frames2field = (temporal_reference + 1) / 2;
+      frames3field = temporal_reference / 2;
+      fields_presented += 3;
+    } else {
+      frames2field = (temporal_reference) / 2;
+      frames3field = (temporal_reference + 1) / 2;
+      fields_presented += 2;
+    }
+    PTS = static_cast < clockticks >
+      ((frames2field * 2 + frames3field * 3 + group_start_field +
+       1) * (double) (CLOCKS / 2) / frame_rate);
+    access_unit.porder = temporal_reference + group_start_pic;
+  } else {
+    DTS = static_cast < clockticks > (decoding_order * (double) CLOCKS / frame_rate);
+    PTS = static_cast < clockticks >
+      ((temporal_reference + group_start_pic + 1) * (double) CLOCKS / frame_rate);
+    fields_presented += 2;
+  }
+
+}
+\f
+
+
+
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/videostrm_out.cc b/ext/mplex/videostrm_out.cc
new file mode 100644 (file)
index 0000000..a70bd07
--- /dev/null
@@ -0,0 +1,276 @@
+
+/*
+ *  inptstrm.c:  Members of input stream classes related to muxing out into
+ *               the output stream.
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+\f
+
+#include <config.h>
+#include <assert.h>
+#include "fastintfns.h"
+#include "videostrm.hh"
+#include "outputstream.hh"
+
+
+VideoStream::VideoStream (IBitStream & ibs, OutputStream & into):
+ElementaryStream (ibs, into, ElementaryStream::video),
+num_sequence (0),
+num_seq_end (0),
+num_pictures (0),
+num_groups (0), dtspts_for_all_au (into.dtspts_for_all_vau), gop_control_packet (false)
+{
+  prev_offset = 0;
+  decoding_order = 0;
+  fields_presented = 0;
+  group_order = 0;
+  group_start_pic = 0;
+  group_start_field = 0;
+  temporal_reference = 0;
+  pulldown_32 = 0;
+  temporal_reference = -1;     // Needed to recognise 2nd field of 1st
+  // frame in a field pic sequence
+  last_buffered_AU = 0;
+  max_bits_persec = 0;
+  AU_hdr = SEQUENCE_HEADER;    /* GOP or SEQ Header starting AU? */
+  for (int i = 0; i < 4; ++i)
+    num_frames[i] = avg_frames[i] = 0;
+  FRAME_CHUNK = 6;
+
+  this->opt_mpeg = into.opt_mpeg;
+  this->opt_multifile_segment = into.opt_multifile_segment;
+}
+
+bool
+VideoStream::Probe (IBitStream & bs)
+{
+  return bs.getbits (32) == 0x1b3;
+}
+
+void
+VideoStream::InitAUbuffer ()
+{
+  unsigned int i;
+
+  for (i = 0; i < aunits.BUF_SIZE; ++i)
+    aunits.init (new VAunit);
+}
+
+
+/*********************************
+ * Signals when video stream has completed mux run-out specified
+ * in associated mux stream.   Run-out is always to complete GOP's.
+ *********************************/
+
+bool
+VideoStream::RunOutComplete ()
+{
+
+  return (au_unsent == 0 ||
+         (muxinto.running_out && au->type == IFRAME && RequiredPTS () >= muxinto.runout_PTS));
+}
+
+/*********************************
+ * Signals if it is permissible/possible to Mux out a sector from the Stream.
+ * The universal constraints that muxing should not be complete and that
+ * that the reciever buffer should have sufficient it also insists that
+ * the muxed data won't hang around in the receiver buffer for more than
+ * one second.  This is mainly for the benefit of (S)VCD and DVD applications
+ * where long delays mess up random access.
+ *******************************/
+
+
+bool
+VideoStream::MuxPossible (clockticks currentSCR)
+{
+
+  return (ElementaryStream::MuxPossible (currentSCR) &&
+         RequiredDTS () < currentSCR + max_STD_buffer_delay);
+}
+
+/*********************************
+ * Work out the timestamps to be set in the header of sectors starting
+ * new AU's.
+ *********************************/
+
+uint8_t
+VideoStream::NewAUTimestamps (int AUtype)
+{
+  uint8_t timestamps;
+
+  if (AUtype == BFRAME)
+    timestamps = TIMESTAMPBITS_PTS;
+  else
+    timestamps = TIMESTAMPBITS_PTS_DTS;
+
+  if (muxinto.timestamp_iframe_only && AUtype != IFRAME)
+    timestamps = TIMESTAMPBITS_NO;
+  return timestamps;
+}
+
+/*********************************
+ * Work out the buffer records to be set in the header of sectors
+ * starting new AU's.
+ *********************************/
+
+bool
+VideoStream::NewAUBuffers (int AUtype)
+{
+  return buffers_in_header & !(muxinto.video_buffers_iframe_only && AUtype != IFRAME);
+}
+
+/******************************************************************
+       Output_Video
+       generiert Pack/Sys_Header/Packet Informationen aus dem
+       Video Stream und speichert den so erhaltenen Sektor ab.
+
+       generates Pack/Sys_Header/Packet information from the
+       video stream and writes out the new sector
+******************************************************************/
+
+void
+VideoStream::OutputSector ()
+{
+
+  unsigned int max_packet_payload;
+  unsigned int actual_payload;
+  unsigned int old_au_then_new_payload;
+  clockticks DTS, PTS;
+  int autype;
+
+  max_packet_payload = 0;      /* 0 = Fill sector */
+  /*      
+     We're now in the last AU of a segment. 
+     So we don't want to go beyond it's end when filling
+     sectors. Hence we limit packet payload size to (remaining) AU length.
+     The same applies when we wish to ensure sequence headers starting
+     ACCESS-POINT AU's in (S)VCD's etc are sector-aligned.
+   */
+  int nextAU = NextAUType ();
+
+  if ((muxinto.running_out && nextAU == IFRAME &&
+       NextRequiredPTS () > muxinto.runout_PTS) ||
+      (muxinto.sector_align_iframeAUs && nextAU == IFRAME)
+    ) {
+    max_packet_payload = au_unsent;
+  }
+
+  /* Figure out the threshold payload size below which we can fit more
+     than one AU into a packet N.b. because fitting more than one in
+     imposses an overhead of additional header fields so there is a
+     dead spot where we *have* to stuff the packet rather than start
+     fitting in an extra AU.  Slightly over-conservative in the case
+     of the last packet...  */
+
+  old_au_then_new_payload = muxinto.PacketPayload (*this, buffers_in_header, true, true);
+
+  /* CASE: Packet starts with new access unit                     */
+  if (new_au_next_sec) {
+    autype = AUType ();
+    //
+    // Some types of output format (e.g. DVD) require special
+    // control sectors before the sector starting a new GOP
+    // N.b. this implies muxinto.sector_align_iframeAUs
+    //
+    if (gop_control_packet && autype == IFRAME) {
+      OutputGOPControlSector ();
+    }
+
+    if (dtspts_for_all_au && max_packet_payload == 0)
+      max_packet_payload = au_unsent;
+
+    PTS = RequiredPTS ();
+    DTS = RequiredDTS ();
+    actual_payload =
+      muxinto.WritePacket (max_packet_payload,
+                          *this, NewAUBuffers (autype), PTS, DTS, NewAUTimestamps (autype));
+
+  }
+
+  /* CASE: Packet begins with old access unit, no new one */
+  /*           can begin in the very same packet                                  */
+
+  else if (au_unsent >= old_au_then_new_payload ||
+          (max_packet_payload != 0 && au_unsent >= max_packet_payload)) {
+    actual_payload = muxinto.WritePacket (au_unsent, *this, false, 0, 0, TIMESTAMPBITS_NO);
+  }
+
+  /* CASE: Packet begins with old access unit, a new one  */
+  /*           could begin in the very same packet                        */
+  else {                       /* if ( !new_au_next_sec  && 
+                                  (au_unsent < old_au_then_new_payload)) */
+    /* Is there a new access unit ? */
+    if (Lookahead () != 0) {
+      autype = NextAUType ();
+      if (dtspts_for_all_au && max_packet_payload == 0)
+       max_packet_payload = au_unsent + Lookahead ()->length;
+
+      PTS = NextRequiredPTS ();
+      DTS = NextRequiredDTS ();
+
+      actual_payload =
+       muxinto.WritePacket (max_packet_payload,
+                            *this, NewAUBuffers (autype), PTS, DTS, NewAUTimestamps (autype));
+    } else {
+      actual_payload = muxinto.WritePacket (0, *this, false, 0, 0, TIMESTAMPBITS_NO);
+    }
+
+  }
+  ++nsec;
+  buffers_in_header = always_buffers_in_header;
+}
+
+
+/***********************************************
+   OutputControlSector - Write control sectors prefixing a GOP
+   For "normal" video streams this doesn't happen and so represents
+   a bug and triggers an abort.
+
+   In DVD's these sectors carry a system header and what is
+   presumably indexing and/or sub-title information in
+   private_stream_2 packets.  I have no idea what to put in here so we
+   simply pad the sector out.
+***********************************************/
+
+void
+VideoStream::OutputGOPControlSector ()
+{
+  abort ();
+}
+
+ /******************************************************************
+ *     OutputGOPControlSector
+ *  DVD System headers are carried in peculiar sectors carrying 2
+ *  PrivateStream2 packets.   We're sticking 0's in the packets
+ *  as we have no idea what's supposed to be in there.
+ ******************************************************************/
+
+void
+DVDVideoStream::OutputGOPControlSector ()
+{
+  muxinto.OutputDVDPriv2 ();
+}
+\f
+
+/* 
+ * Local variables:
+ *  c-file-style: "stroustrup"
+ *  tab-width: 4
+ *  indent-tabs-mode: nil
+ * End:
+ */
diff --git a/ext/mplex/yuv4mpeg.cc b/ext/mplex/yuv4mpeg.cc
new file mode 100644 (file)
index 0000000..a9e1aeb
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+ *  yuv4mpeg.c:  Functions for reading and writing "new" YUV4MPEG streams
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "yuv4mpeg.h"
+#include "yuv4mpeg_intern.h"
+#include "mjpeg_logging.h"
+
+
+static int _y4mparam_allow_unknown_tags = 1;   /* default is forgiveness */
+
+static void *(*_y4m_alloc) (size_t bytes) = malloc;
+static void (*_y4m_free) (void *ptr) = free;
+
+
+
+int
+y4m_allow_unknown_tags (int yn)
+{
+  int old = _y4mparam_allow_unknown_tags;
+
+  if (yn >= 0)
+    _y4mparam_allow_unknown_tags = (yn) ? 1 : 0;
+  return old;
+}
+
+
+
+/*************************************************************************
+ *
+ * Convenience functions for fd read/write
+ *
+ *   - guaranteed to transfer entire payload (or fail)
+ *   - returns:
+ *               0 on complete success
+ *               +(# of remaining bytes) on eof (for y4m_read)
+ *               -(# of rem. bytes) on error (and ERRNO should be set)
+ *     
+ *************************************************************************/
+
+
+ssize_t
+y4m_read (int fd, void *buf, size_t len)
+{
+  ssize_t n;
+  uint8_t *ptr = (uint8_t *) buf;
+
+  while (len > 0) {
+    n = read (fd, ptr, len);
+    if (n <= 0) {
+      /* return amount left to read */
+      if (n == 0)
+       return len;             /* n == 0 --> eof */
+      else
+       return -len;            /* n < 0 --> error */
+    }
+    ptr += n;
+    len -= n;
+  }
+  return 0;
+}
+
+
+ssize_t
+y4m_write (int fd, const void *buf, size_t len)
+{
+  ssize_t n;
+  const uint8_t *ptr = (const uint8_t *) buf;
+
+  while (len > 0) {
+    n = write (fd, ptr, len);
+    if (n <= 0)
+      return -len;             /* return amount left to write */
+    ptr += n;
+    len -= n;
+  }
+  return 0;
+}
+
+
+
+/*************************************************************************
+ *
+ * "Extra tags" handling
+ *
+ *************************************************************************/
+
+
+static char *
+y4m_new_xtag (void)
+{
+  return (char *) _y4m_alloc (Y4M_MAX_XTAG_SIZE * sizeof (char));
+}
+
+
+void
+y4m_init_xtag_list (y4m_xtag_list_t * xtags)
+{
+  int i;
+
+  xtags->count = 0;
+  for (i = 0; i < Y4M_MAX_XTAGS; i++) {
+    xtags->tags[i] = NULL;
+  }
+}
+
+
+void
+y4m_fini_xtag_list (y4m_xtag_list_t * xtags)
+{
+  int i;
+
+  for (i = 0; i < Y4M_MAX_XTAGS; i++) {
+    if (xtags->tags[i] != NULL) {
+      _y4m_free (xtags->tags[i]);
+      xtags->tags[i] = NULL;
+    }
+  }
+  xtags->count = 0;
+}
+
+
+void
+y4m_copy_xtag_list (y4m_xtag_list_t * dest, const y4m_xtag_list_t * src)
+{
+  int i;
+
+  for (i = 0; i < src->count; i++) {
+    if (dest->tags[i] == NULL)
+      dest->tags[i] = y4m_new_xtag ();
+    strncpy (dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
+  }
+  dest->count = src->count;
+}
+
+
+
+static int
+y4m_snprint_xtags (char *s, int maxn, const y4m_xtag_list_t * xtags)
+{
+  int i, room;
+
+  for (i = 0, room = maxn - 1; i < xtags->count; i++) {
+    int n = snprintf (s, room + 1, " %s", xtags->tags[i]);
+
+    if ((n < 0) || (n > room))
+      return Y4M_ERR_HEADER;
+    s += n;
+    room -= n;
+  }
+  s[0] = '\n';                 /* finish off header with newline */
+  s[1] = '\0';                 /* ...and end-of-string           */
+  return Y4M_OK;
+}
+
+
+int
+y4m_xtag_count (const y4m_xtag_list_t * xtags)
+{
+  return xtags->count;
+}
+
+
+const char *
+y4m_xtag_get (const y4m_xtag_list_t * xtags, int n)
+{
+  if (n >= xtags->count)
+    return NULL;
+  else
+    return xtags->tags[n];
+}
+
+
+int
+y4m_xtag_add (y4m_xtag_list_t * xtags, const char *tag)
+{
+  if (xtags->count >= Y4M_MAX_XTAGS)
+    return Y4M_ERR_XXTAGS;
+  if (xtags->tags[xtags->count] == NULL)
+    xtags->tags[xtags->count] = y4m_new_xtag ();
+  strncpy (xtags->tags[xtags->count], tag, Y4M_MAX_XTAG_SIZE);
+  (xtags->count)++;
+  return Y4M_OK;
+}
+
+
+int
+y4m_xtag_remove (y4m_xtag_list_t * xtags, int n)
+{
+  int i;
+  char *q;
+
+  if ((n < 0) || (n >= xtags->count))
+    return Y4M_ERR_RANGE;
+  q = xtags->tags[n];
+  for (i = n; i < (xtags->count - 1); i++)
+    xtags->tags[i] = xtags->tags[i + 1];
+  xtags->tags[i] = q;
+  (xtags->count)--;
+  return Y4M_OK;
+}
+
+
+int
+y4m_xtag_clearlist (y4m_xtag_list_t * xtags)
+{
+  xtags->count = 0;
+  return Y4M_OK;
+}
+
+
+int
+y4m_xtag_addlist (y4m_xtag_list_t * dest, const y4m_xtag_list_t * src)
+{
+  int i, j;
+
+  if ((dest->count + src->count) > Y4M_MAX_XTAGS)
+    return Y4M_ERR_XXTAGS;
+  for (i = dest->count, j = 0; j < src->count; i++, j++) {
+    if (dest->tags[i] == NULL)
+      dest->tags[i] = y4m_new_xtag ();
+    strncpy (dest->tags[i], src->tags[i], Y4M_MAX_XTAG_SIZE);
+  }
+  dest->count += src->count;
+  return Y4M_OK;
+}
+
+
+/*************************************************************************
+ *
+ * Creators/destructors for y4m_*_info_t structures
+ *
+ *************************************************************************/
+
+
+void
+y4m_init_stream_info (y4m_stream_info_t * info)
+{
+  if (info == NULL)
+    return;
+  /* initialize info */
+  info->width = Y4M_UNKNOWN;
+  info->height = Y4M_UNKNOWN;
+  info->interlace = Y4M_UNKNOWN;
+  info->framerate = y4m_fps_UNKNOWN;
+  info->sampleaspect = y4m_sar_UNKNOWN;
+  y4m_init_xtag_list (&(info->x_tags));
+}
+
+
+void
+y4m_copy_stream_info (y4m_stream_info_t * dest, const y4m_stream_info_t * src)
+{
+  if ((dest == NULL) || (src == NULL))
+    return;
+  /* copy info */
+  dest->width = src->width;
+  dest->height = src->height;
+  dest->interlace = src->interlace;
+  dest->framerate = src->framerate;
+  dest->sampleaspect = src->sampleaspect;
+  dest->framelength = src->framelength;
+  y4m_copy_xtag_list (&(dest->x_tags), &(src->x_tags));
+}
+
+
+void
+y4m_fini_stream_info (y4m_stream_info_t * info)
+{
+  if (info == NULL)
+    return;
+  y4m_fini_xtag_list (&(info->x_tags));
+}
+
+
+void
+y4m_si_set_width (y4m_stream_info_t * si, int width)
+{
+  si->width = width;
+  si->framelength = (si->height * si->width) * 3 / 2;
+}
+
+int
+y4m_si_get_width (const y4m_stream_info_t * si)
+{
+  return si->width;
+}
+
+void
+y4m_si_set_height (y4m_stream_info_t * si, int height)
+{
+  si->height = height;
+  si->framelength = (si->height * si->width) * 3 / 2;
+}
+
+int
+y4m_si_get_height (const y4m_stream_info_t * si)
+{
+  return si->height;
+}
+
+void
+y4m_si_set_interlace (y4m_stream_info_t * si, int interlace)
+{
+  si->interlace = interlace;
+}
+
+int
+y4m_si_get_interlace (const y4m_stream_info_t * si)
+{
+  return si->interlace;
+}
+
+void
+y4m_si_set_framerate (y4m_stream_info_t * si, y4m_ratio_t framerate)
+{
+  si->framerate = framerate;
+}
+
+y4m_ratio_t
+y4m_si_get_framerate (const y4m_stream_info_t * si)
+{
+  return si->framerate;
+}
+
+void
+y4m_si_set_sampleaspect (y4m_stream_info_t * si, y4m_ratio_t sar)
+{
+  si->sampleaspect = sar;
+}
+
+y4m_ratio_t
+y4m_si_get_sampleaspect (const y4m_stream_info_t * si)
+{
+  return si->sampleaspect;
+}
+
+int
+y4m_si_get_framelength (const y4m_stream_info_t * si)
+{
+  return si->framelength;
+}
+
+y4m_xtag_list_t *
+y4m_si_xtags (y4m_stream_info_t * si)
+{
+  return &(si->x_tags);
+}
+
+
+
+void
+y4m_init_frame_info (y4m_frame_info_t * info)
+{
+  if (info == NULL)
+    return;
+  /* initialize info */
+  y4m_init_xtag_list (&(info->x_tags));
+}
+
+
+void
+y4m_copy_frame_info (y4m_frame_info_t * dest, const y4m_frame_info_t * src)
+{
+  if ((dest == NULL) || (src == NULL))
+    return;
+  /* copy info */
+  y4m_copy_xtag_list (&(dest->x_tags), &(src->x_tags));
+}
+
+
+void
+y4m_fini_frame_info (y4m_frame_info_t * info)
+{
+  if (info == NULL)
+    return;
+  y4m_fini_xtag_list (&(info->x_tags));
+}
+
+
+
+/*************************************************************************
+ *
+ * Tag parsing 
+ *
+ *************************************************************************/
+
+int
+y4m_parse_stream_tags (char *s, y4m_stream_info_t * i)
+{
+  char *token, *value;
+  char tag;
+  int err;
+
+  /* parse fields */
+  for (token = strtok (s, Y4M_DELIM); token != NULL; token = strtok (NULL, Y4M_DELIM)) {
+    if (token[0] == '\0')
+      continue;                        /* skip empty strings */
+    tag = token[0];
+    value = token + 1;
+    switch (tag) {
+      case 'W':                /* width */
+       i->width = atoi (value);
+       if (i->width <= 0)
+         return Y4M_ERR_RANGE;
+       break;
+      case 'H':                /* height */
+       i->height = atoi (value);
+       if (i->height <= 0)
+         return Y4M_ERR_RANGE;
+       break;
+      case 'F':                /* frame rate (fps) */
+       if ((err = y4m_parse_ratio (&(i->framerate), value)) != Y4M_OK)
+         return err;
+       if (i->framerate.n < 0)
+         return Y4M_ERR_RANGE;
+       break;
+      case 'I':                /* interlacing */
+       switch (value[0]) {
+         case 'p':
+           i->interlace = Y4M_ILACE_NONE;
+           break;
+         case 't':
+           i->interlace = Y4M_ILACE_TOP_FIRST;
+           break;
+         case 'b':
+           i->interlace = Y4M_ILACE_BOTTOM_FIRST;
+           break;
+         case '?':
+         default:
+           i->interlace = Y4M_UNKNOWN;
+           break;
+       }
+       break;
+      case 'A':                /* sample (pixel) aspect ratio */
+       if ((err = y4m_parse_ratio (&(i->sampleaspect), value)) != Y4M_OK)
+         return err;
+       if (i->sampleaspect.n < 0)
+         return Y4M_ERR_RANGE;
+       break;
+      case 'X':                /* 'X' meta-tag */
+       if ((err = y4m_xtag_add (&(i->x_tags), token)) != Y4M_OK)
+         return err;
+       break;
+      default:
+       /* possible error on unknown options */
+       if (_y4mparam_allow_unknown_tags) {
+         /* unknown tags ok:  store in xtag list and warn... */
+         if ((err = y4m_xtag_add (&(i->x_tags), token)) != Y4M_OK)
+           return err;
+         mjpeg_warn ("Unknown stream tag encountered:  '%s'", token);
+       } else {
+         /* unknown tags are *not* ok */
+         return Y4M_ERR_BADTAG;
+       }
+       break;
+    }
+  }
+  /* Error checking... width and height must be known since we can't
+   * parse without them
+   */
+  if (i->width == Y4M_UNKNOWN || i->height == Y4M_UNKNOWN)
+    return Y4M_ERR_HEADER;
+  /* ta da!  done. */
+  return Y4M_OK;
+}
+
+
+
+static int
+y4m_parse_frame_tags (char *s, y4m_frame_info_t * i)
+{
+  char *token, *value;
+  char tag;
+  int err;
+
+  /* parse fields */
+  for (token = strtok (s, Y4M_DELIM); token != NULL; token = strtok (NULL, Y4M_DELIM)) {
+    if (token[0] == '\0')
+      continue;                        /* skip empty strings */
+    tag = token[0];
+    value = token + 1;
+    switch (tag) {
+      case 'X':                /* 'X' meta-tag */
+       if ((err = y4m_xtag_add (&(i->x_tags), token)) != Y4M_OK)
+         return err;
+       break;
+      default:
+       /* possible error on unknown options */
+       if (_y4mparam_allow_unknown_tags) {
+         /* unknown tags ok:  store in xtag list and warn... */
+         if ((err = y4m_xtag_add (&(i->x_tags), token)) != Y4M_OK)
+           return err;
+         mjpeg_warn ("Unknown frame tag encountered:  '%s'", token);
+       } else {
+         /* unknown tags are *not* ok */
+         return Y4M_ERR_BADTAG;
+       }
+       break;
+    }
+  }
+  /* ta da!  done. */
+  return Y4M_OK;
+}
+
+
+
+
+
+/*************************************************************************
+ *
+ * Read/Write stream header
+ *
+ *************************************************************************/
+
+
+int
+y4m_read_stream_header (int fd, y4m_stream_info_t * i)
+{
+  char line[Y4M_LINE_MAX];
+  char *p;
+  int n;
+  int err;
+
+  /* read the header line */
+  for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
+    if (read (fd, p, 1) < 1)
+      return Y4M_ERR_SYSTEM;
+    if (*p == '\n') {
+      *p = '\0';               /* Replace linefeed by end of string */
+      break;
+    }
+  }
+  if (n >= Y4M_LINE_MAX)
+    return Y4M_ERR_HEADER;
+  /* look for keyword in header */
+  if (strncmp (line, Y4M_MAGIC, strlen (Y4M_MAGIC)))
+    return Y4M_ERR_MAGIC;
+  if ((err = y4m_parse_stream_tags (line + strlen (Y4M_MAGIC), i)) != Y4M_OK)
+    return err;
+
+  i->framelength = (i->height * i->width) * 3 / 2;
+  return Y4M_OK;
+}
+
+
+
+int
+y4m_write_stream_header (int fd, const y4m_stream_info_t * i)
+{
+  char s[Y4M_LINE_MAX + 1];
+  int n;
+  int err;
+  y4m_ratio_t rate = i->framerate;
+  y4m_ratio_t aspect = i->sampleaspect;
+
+  y4m_ratio_reduce (&rate);
+  y4m_ratio_reduce (&aspect);
+  n = snprintf (s, sizeof (s), "%s W%d H%d F%d:%d I%s A%d:%d",
+               Y4M_MAGIC,
+               i->width,
+               i->height,
+               rate.n, rate.d,
+               (i->interlace == Y4M_ILACE_NONE) ? "p" :
+               (i->interlace == Y4M_ILACE_TOP_FIRST) ? "t" :
+               (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "b" : "?", aspect.n, aspect.d);
+  if ((n < 0) || (n > Y4M_LINE_MAX))
+    return Y4M_ERR_HEADER;
+  if ((err = y4m_snprint_xtags (s + n, sizeof (s) - n - 1, &(i->x_tags)))
+      != Y4M_OK)
+    return err;
+  /* non-zero on error */
+  return (y4m_write (fd, s, strlen (s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
+}
+
+
+
+
+
+/*************************************************************************
+ *
+ * Read/Write frame header
+ *
+ *************************************************************************/
+
+int
+y4m_read_frame_header (int fd, y4m_frame_info_t * i)
+{
+  char line[Y4M_LINE_MAX];
+  char *p;
+  int n;
+  ssize_t remain;
+
+  /* This is more clever than read_stream_header...
+     Try to read "FRAME\n" all at once, and don't try to parse
+     if nothing else is there...
+   */
+  remain = y4m_read (fd, line, sizeof (Y4M_FRAME_MAGIC) - 1 + 1);      /* -'\0', +'\n' */
+  if (remain < 0)
+    return Y4M_ERR_SYSTEM;
+  if (remain > 0) {
+    /* A clean EOF should end exactly at a frame-boundary */
+    if (remain == sizeof (Y4M_FRAME_MAGIC))
+      return Y4M_ERR_EOF;
+    else
+      return Y4M_ERR_BADEOF;
+  }
+  if (strncmp (line, Y4M_FRAME_MAGIC, sizeof (Y4M_FRAME_MAGIC) - 1))
+    return Y4M_ERR_MAGIC;
+  if (line[sizeof (Y4M_FRAME_MAGIC) - 1] == '\n')
+    return Y4M_OK;             /* done -- no tags:  that was the end-of-line. */
+
+  if (line[sizeof (Y4M_FRAME_MAGIC) - 1] != Y4M_DELIM[0]) {
+    return Y4M_ERR_MAGIC;      /* wasn't a space -- what was it? */
+  }
+
+  /* proceed to get the tags... (overwrite the magic) */
+  for (n = 0, p = line; n < Y4M_LINE_MAX; n++, p++) {
+    if (y4m_read (fd, p, 1))
+      return Y4M_ERR_SYSTEM;
+    if (*p == '\n') {
+      *p = '\0';               /* Replace linefeed by end of string */
+      break;
+    }
+  }
+  if (n >= Y4M_LINE_MAX)
+    return Y4M_ERR_HEADER;
+  /* non-zero on error */
+  return y4m_parse_frame_tags (line, i);
+}
+
+
+int
+y4m_write_frame_header (int fd, const y4m_frame_info_t * i)
+{
+  char s[Y4M_LINE_MAX + 1];
+  int n;
+  int err;
+
+  n = snprintf (s, sizeof (s), "%s", Y4M_FRAME_MAGIC);
+  if ((n < 0) || (n > Y4M_LINE_MAX))
+    return Y4M_ERR_HEADER;
+  if ((err = y4m_snprint_xtags (s + n, sizeof (s) - n - 1, &(i->x_tags)))
+      != Y4M_OK)
+    return err;
+  /* non-zero on error */
+  return (y4m_write (fd, s, strlen (s)) ? Y4M_ERR_SYSTEM : Y4M_OK);
+}
+
+
+
+/*************************************************************************
+ *
+ * Read/Write entire frame
+ *
+ *************************************************************************/
+
+int
+y4m_read_frame (int fd, const y4m_stream_info_t * si, y4m_frame_info_t * fi, uint8_t * const yuv[3])
+{
+  int err;
+  int w = si->width;
+  int h = si->height;
+
+  /* Read frame header */
+  if ((err = y4m_read_frame_header (fd, fi)) != Y4M_OK)
+    return err;
+  /* Read luminance scanlines */
+  if (y4m_read (fd, yuv[0], w * h))
+    return Y4M_ERR_SYSTEM;
+  /* Read chrominance scanlines */
+  if (y4m_read (fd, yuv[1], w * h / 4))
+    return Y4M_ERR_SYSTEM;
+  if (y4m_read (fd, yuv[2], w * h / 4))
+    return Y4M_ERR_SYSTEM;
+
+  return Y4M_OK;
+}
+
+
+
+
+int
+y4m_write_frame (int fd, const y4m_stream_info_t * si,
+                const y4m_frame_info_t * fi, uint8_t * const yuv[3])
+{
+  int err;
+  int w = si->width;
+  int h = si->height;
+
+  /* Write frame header */
+  if ((err = y4m_write_frame_header (fd, fi)) != Y4M_OK)
+    return err;
+  /* Write luminance,chrominance scanlines */
+  if (y4m_write (fd, yuv[0], w * h) ||
+      y4m_write (fd, yuv[1], w * h / 4) || y4m_write (fd, yuv[2], w * h / 4))
+    return Y4M_ERR_SYSTEM;
+  return Y4M_OK;
+}
+
+
+
+/*************************************************************************
+ *
+ * Read/Write entire frame, (de)interleaved (to)from two separate fields
+ *
+ *************************************************************************/
+
+
+int
+y4m_read_fields (int fd, const y4m_stream_info_t * si, y4m_frame_info_t * fi,
+                uint8_t * const upper_field[3], uint8_t * const lower_field[3])
+{
+  int i, y, err;
+  int width = si->width;
+  int height = si->height;
+
+  /* Read frame header */
+  if ((err = y4m_read_frame_header (fd, fi)) != Y4M_OK)
+    return err;
+  /* Read Y', Cb, and Cr planes */
+  for (i = 0; i < 3; i++) {
+    uint8_t *srctop = upper_field[i];
+    uint8_t *srcbot = lower_field[i];
+
+    /* alternately write one line from each */
+    for (y = 0; y < height; y += 2) {
+      if (y4m_read (fd, srctop, width))
+       return Y4M_ERR_SYSTEM;
+      srctop += width;
+      if (y4m_read (fd, srcbot, width))
+       return Y4M_ERR_SYSTEM;
+      srcbot += width;
+    }
+    /* for chroma, width/height are half as big */
+    if (i == 0) {
+      width /= 2;
+      height /= 2;
+    }
+  }
+  return Y4M_OK;
+}
+
+
+
+int
+y4m_write_fields (int fd, const y4m_stream_info_t * si,
+                 const y4m_frame_info_t * fi,
+                 uint8_t * const upper_field[3], uint8_t * const lower_field[3])
+{
+  int i, y, err;
+  int width = si->width;
+  int height = si->height;
+
+  /* Write frame header */
+  if ((err = y4m_write_frame_header (fd, fi)) != Y4M_OK)
+    return err;
+  /* Write Y', Cb, and Cr planes */
+  for (i = 0; i < 3; i++) {
+    uint8_t *srctop = upper_field[i];
+    uint8_t *srcbot = lower_field[i];
+
+    /* alternately write one line from each */
+    for (y = 0; y < height; y += 2) {
+      if (y4m_write (fd, srctop, width))
+       return Y4M_ERR_SYSTEM;
+      srctop += width;
+      if (y4m_write (fd, srcbot, width))
+       return Y4M_ERR_SYSTEM;
+      srcbot += width;
+    }
+    /* for chroma, width/height are half as big */
+    if (i == 0) {
+      width /= 2;
+      height /= 2;
+    }
+  }
+  return Y4M_OK;
+}
+
+
+
+/*************************************************************************
+ *
+ * Handy logging of stream info
+ *
+ *************************************************************************/
+
+void
+y4m_log_stream_info (log_level_t level, const char *prefix, const y4m_stream_info_t * i)
+{
+  char s[256];
+
+  snprintf (s, sizeof (s), "  frame size:  ");
+  if (i->width == Y4M_UNKNOWN)
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "(?)x");
+  else
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "%dx", i->width);
+  if (i->height == Y4M_UNKNOWN)
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "(?) pixels ");
+  else
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "%d pixels ", i->height);
+  if (i->framelength == Y4M_UNKNOWN)
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "(? bytes)");
+  else
+    snprintf (s + strlen (s), sizeof (s) - strlen (s), "(%d bytes)", i->framelength);
+  mjpeg_log (level, "%s%s", prefix, s);
+  if ((i->framerate.n == 0) && (i->framerate.d == 0))
+    mjpeg_log (level, "%s  frame rate:  ??? fps", prefix);
+  else
+    mjpeg_log (level, "%s  frame rate:  %d/%d fps (~%f)", prefix,
+              i->framerate.n, i->framerate.d, (double) i->framerate.n / (double) i->framerate.d);
+  mjpeg_log (level, "%s   interlace:  %s", prefix,
+            (i->interlace == Y4M_ILACE_NONE) ? "none/progressive" :
+            (i->interlace == Y4M_ILACE_TOP_FIRST) ? "top-field-first" :
+            (i->interlace == Y4M_ILACE_BOTTOM_FIRST) ? "bottom-field-first" : "anyone's guess");
+  if ((i->sampleaspect.n == 0) && (i->sampleaspect.d == 0))
+    mjpeg_log (level, "%ssample aspect ratio:  ?:?", prefix);
+  else
+    mjpeg_log (level, "%ssample aspect ratio:  %d:%d", prefix,
+              i->sampleaspect.n, i->sampleaspect.d);
+}
+
+
+/*************************************************************************
+ *
+ * Convert error code to string
+ *
+ *************************************************************************/
+
+const char *
+y4m_strerr (int err)
+{
+  switch (err) {
+    case Y4M_OK:
+      return "no error";
+    case Y4M_ERR_RANGE:
+      return "parameter out of range";
+    case Y4M_ERR_SYSTEM:
+      return "system error (failed read/write)";
+    case Y4M_ERR_HEADER:
+      return "bad stream or frame header";
+    case Y4M_ERR_BADTAG:
+      return "unknown header tag";
+    case Y4M_ERR_MAGIC:
+      return "bad header magic";
+    case Y4M_ERR_XXTAGS:
+      return "too many xtags";
+    case Y4M_ERR_EOF:
+      return "end-of-file";
+    case Y4M_ERR_BADEOF:
+      return "stream ended unexpectedly (EOF)";
+    default:
+      return "unknown error code";
+  }
+}
diff --git a/ext/mplex/yuv4mpeg.h b/ext/mplex/yuv4mpeg.h
new file mode 100644 (file)
index 0000000..6028ea8
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ *  yuv4mpeg.h:  Functions for reading and writing "new" YUV4MPEG2 streams.
+ *
+ *               Stream format is described at the end of this file.
+ *
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __YUV4MPEG_H__
+#define __YUV4MPEG_H__
+
+#include <stdlib.h>
+#include <mjpeg_types.h>
+#include <unistd.h>
+#include <mjpeg_logging.h>
+
+
+/************************************************************************
+ *  error codes returned by y4m_* functions
+ ************************************************************************/
+#define Y4M_OK          0
+#define Y4M_ERR_RANGE   1  /* argument or tag value out of range */
+#define Y4M_ERR_SYSTEM  2  /* failed system call, check errno */
+#define Y4M_ERR_HEADER  3  /* illegal/malformed header */
+#define Y4M_ERR_BADTAG  4  /* illegal tag character */
+#define Y4M_ERR_MAGIC   5  /* bad header magic */
+#define Y4M_ERR_EOF     6  /* end-of-file (clean) */
+#define Y4M_ERR_XXTAGS  7  /* too many xtags */
+#define Y4M_ERR_BADEOF  8  /* unexpected end-of-file */
+
+
+/* generic 'unknown' value for integer parameters (e.g. interlace, height) */
+#define Y4M_UNKNOWN -1
+
+
+
+/************************************************************************
+ *  'ratio' datatype, for rational numbers
+ *                                     (see 'ratio' functions down below)
+ ************************************************************************/
+typedef struct _y4m_ratio {
+  int n;  /* numerator   */
+  int d;  /* denominator */
+} y4m_ratio_t;
+
+
+/************************************************************************
+ *  useful standard framerates (as ratios)
+ ************************************************************************/
+extern const y4m_ratio_t y4m_fps_UNKNOWN;
+extern const y4m_ratio_t y4m_fps_NTSC_FILM;  /* 24000/1001 film (in NTSC)  */
+extern const y4m_ratio_t y4m_fps_FILM;       /* 24fps film                 */
+extern const y4m_ratio_t y4m_fps_PAL;        /* 25fps PAL                  */
+extern const y4m_ratio_t y4m_fps_NTSC;       /* 30000/1001 NTSC            */
+extern const y4m_ratio_t y4m_fps_30;         /* 30fps                      */
+extern const y4m_ratio_t y4m_fps_PAL_FIELD;  /* 50fps PAL field rate       */
+extern const y4m_ratio_t y4m_fps_NTSC_FIELD; /* 60000/1001 NTSC field rate */
+extern const y4m_ratio_t y4m_fps_60;         /* 60fps                      */
+
+/************************************************************************
+ *  useful standard sample (pixel) aspect ratios (W:H)
+ ************************************************************************/
+extern const y4m_ratio_t y4m_sar_UNKNOWN; 
+extern const y4m_ratio_t y4m_sar_SQUARE;        /* square pixels */
+extern const y4m_ratio_t y4m_sar_NTSC_CCIR601;  /* 525-line (NTSC) Rec.601 */
+extern const y4m_ratio_t y4m_sar_NTSC_16_9;     /* 16:9 NTSC/Rec.601       */
+extern const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3; /* NTSC SVCD 4:3           */
+extern const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9;/* NTSC SVCD 16:9          */
+extern const y4m_ratio_t y4m_sar_PAL_CCIR601;   /* 625-line (PAL) Rec.601  */
+extern const y4m_ratio_t y4m_sar_PAL_16_9;      /* 16:9 PAL/Rec.601        */
+extern const y4m_ratio_t y4m_sar_PAL_SVCD_4_3;  /* PAL SVCD 4:3            */
+extern const y4m_ratio_t y4m_sar_PAL_SVCD_16_9; /* PAL SVCD 16:9           */
+extern const y4m_ratio_t y4m_sar_SQR_ANA16_9;   /* anamorphic 16:9 sampled */
+                                            /* from 4:3 with square pixels */
+
+/************************************************************************
+ *  useful standard display aspect ratios (W:H)
+ ************************************************************************/
+extern const y4m_ratio_t y4m_dar_UNKNOWN; 
+extern const y4m_ratio_t y4m_dar_4_3;     /* standard TV   */
+extern const y4m_ratio_t y4m_dar_16_9;    /* widescreen TV */
+extern const y4m_ratio_t y4m_dar_221_100; /* word-to-your-mother TV */
+
+
+/************************************************************************
+ *  'xtag_list' --- list of unparsed and/or meta/X header tags
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_xtag_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+#define Y4M_MAX_XTAGS 32        /* maximum number of xtags in list       */
+#define Y4M_MAX_XTAG_SIZE 32    /* max length of an xtag (including 'X') */
+typedef struct _y4m_xtag_list {
+  int count;
+  char *tags[Y4M_MAX_XTAGS];
+} y4m_xtag_list_t;
+
+
+
+/************************************************************************
+ *  'stream_info' --- stream header information
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_si_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+typedef struct _y4m_stream_info {
+  /* values from header */
+  int width;
+  int height;
+  int interlace;            /* see Y4M_ILACE_* definitions below   */
+  y4m_ratio_t framerate;    /* frames-per-second;   0:0 == unknown */
+  y4m_ratio_t sampleaspect; /* pixel width/height;  0:0 == unknown */
+  /* computed/derivative values */
+  int framelength;    /* bytes of data per frame (not including header) */
+  /* mystical X tags */
+  y4m_xtag_list_t x_tags;
+} y4m_stream_info_t;
+
+/* possible options for the interlace parameter */
+#define Y4M_ILACE_NONE          0   /* non-interlaced, progressive frame */
+#define Y4M_ILACE_TOP_FIRST     1   /* interlaced, top-field first       */
+#define Y4M_ILACE_BOTTOM_FIRST  2   /* interlaced, bottom-field first    */
+
+
+/************************************************************************
+ *  'frame_info' --- frame header information
+ *
+ *     Do not touch this structure directly!
+ *
+ *     Use the y4m_fi_*() functions (see below).
+ *     You must initialize/finalize this structure before/after use.
+ ************************************************************************/
+typedef struct _y4m_frame_info {
+  /* mystical X tags */
+  y4m_xtag_list_t x_tags;
+} y4m_frame_info_t;
+
+
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#endif
+
+
+/************************************************************************
+ *  'ratio' functions
+ ************************************************************************/
+
+/* 'normalize' a ratio (remove common factors) */
+void y4m_ratio_reduce(y4m_ratio_t *r);
+
+/* parse "nnn:ddd" into a ratio (returns Y4M_OK or Y4M_ERR_RANGE) */
+int y4m_parse_ratio(y4m_ratio_t *r, const char *s);
+
+/* quick test of two ratios for equality (i.e. identical components) */
+#define Y4M_RATIO_EQL(a,b) ( ((a).n == (b).n) && ((a).d == (b).d) )
+
+/* quick conversion of a ratio to a double (no divide-by-zero check!) */
+#define Y4M_RATIO_DBL(r) ((double)(r).n / (double)(r).d)
+
+/*************************************************************************
+ *
+ * Guess the true SAR (sample aspect ratio) from a list of commonly 
+ * encountered values, given the "suggested" display aspect ratio (DAR),
+ * and the true frame width and height.
+ *
+ * Returns y4m_sar_UNKNOWN if no match is found.
+ *
+ *************************************************************************/
+y4m_ratio_t y4m_guess_sar(int width, int height, y4m_ratio_t dar);
+
+
+
+/************************************************************************
+ *  'xtag' functions
+ *
+ * o Before using an xtag_list (but after the structure/memory has been
+ *    allocated), you must initialize it via y4m_init_xtag_list().
+ * o After using an xtag_list (but before the structure is released),
+ *    call y4m_fini_xtag_list() to free internal memory.
+ *
+ ************************************************************************/
+
+/* initialize an xtag_list structure */
+void y4m_init_xtag_list(y4m_xtag_list_t *xtags);
+
+/* finalize an xtag_list structure */
+void y4m_fini_xtag_list(y4m_xtag_list_t *xtags);
+
+/* make one xtag_list into a copy of another */
+void y4m_copy_xtag_list(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src);
+
+/* return number of tags in an xtag_list */
+int y4m_xtag_count(const y4m_xtag_list_t *xtags);
+
+/* access n'th tag in an xtag_list */
+const char *y4m_xtag_get(const y4m_xtag_list_t *xtags, int n);
+
+/* append a new tag to an xtag_list
+    returns:          Y4M_OK - success
+              Y4M_ERR_XXTAGS - list is already full */
+int y4m_xtag_add(y4m_xtag_list_t *xtags, const char *tag);
+
+/* remove a tag from an xtag_list 
+    returns:         Y4M_OK - success
+              Y4M_ERR_RANGE - n is out of range */
+int y4m_xtag_remove(y4m_xtag_list_t *xtags, int n);
+
+/* remove all tags from an xtag_list 
+    returns:   Y4M_OK - success       */
+int y4m_xtag_clearlist(y4m_xtag_list_t *xtags);
+
+/* append copies of tags from src list to dest list
+    returns:          Y4M_OK - success
+              Y4M_ERR_XXTAGS - operation would overfill dest list */
+int y4m_xtag_addlist(y4m_xtag_list_t *dest, const y4m_xtag_list_t *src);
+
+
+
+/************************************************************************
+ *  '*_info' functions
+ *
+ * o Before using a *_info structure (but after the structure/memory has
+ *    been allocated), you must initialize it via y4m_init_*_info().
+ * o After using a *_info structure (but before the structure is released),
+ *    call y4m_fini_*_info() to free internal memory.
+ * o Use the 'set' and 'get' accessors to modify or access the fields in
+ *    the structures; don't touch the structure directly.  (Ok, so there
+ *    is no really convenient C syntax to prevent you from doing this,
+ *    but we are all responsible programmers here, so just don't do it!)
+ *
+ ************************************************************************/
+
+/* initialize a stream_info structure */
+void y4m_init_stream_info(y4m_stream_info_t *i);
+
+/* finalize a stream_info structure */
+void y4m_fini_stream_info(y4m_stream_info_t *i);
+
+/* make one stream_info into a copy of another */
+void y4m_copy_stream_info(y4m_stream_info_t *dest,
+                         const y4m_stream_info_t *src);
+
+/* access or set stream_info fields */
+void y4m_si_set_width(y4m_stream_info_t *si, int width);
+int y4m_si_get_width(const y4m_stream_info_t *si);
+void y4m_si_set_height(y4m_stream_info_t *si, int height);
+int y4m_si_get_height(const y4m_stream_info_t *si);
+void y4m_si_set_interlace(y4m_stream_info_t *si, int interlace);
+int y4m_si_get_interlace(const y4m_stream_info_t *si);
+void y4m_si_set_framerate(y4m_stream_info_t *si, y4m_ratio_t framerate);
+y4m_ratio_t y4m_si_get_framerate(const y4m_stream_info_t *si);
+void y4m_si_set_sampleaspect(y4m_stream_info_t *si, y4m_ratio_t sar);
+y4m_ratio_t y4m_si_get_sampleaspect(const y4m_stream_info_t *si);
+int y4m_si_get_framelength(const y4m_stream_info_t *si);
+
+/* access stream_info xtag_list */
+y4m_xtag_list_t *y4m_si_xtags(y4m_stream_info_t *si);
+
+
+/* initialize a frame_info structure */
+void y4m_init_frame_info(y4m_frame_info_t *i);
+
+/* finalize a frame_info structure */
+void y4m_fini_frame_info(y4m_frame_info_t *i);
+
+/* make one frame_info into a copy of another */
+void y4m_copy_frame_info(y4m_frame_info_t *dest,
+                        const y4m_frame_info_t *src);
+
+/* access frame_info xtag_list */
+y4m_xtag_list_t *y4m_fi_xtags(y4m_frame_info_t *fi);
+
+
+
+/************************************************************************
+ *  blocking read and write functions
+ *
+ *  o guaranteed to transfer entire payload (or fail)
+ *  o return values:
+ *                         0 (zero)   complete success
+ *          -(# of remaining bytes)   error (and errno left set)
+ *          +(# of remaining bytes)   EOF (for y4m_read only)
+ *
+ ************************************************************************/
+
+/* read len bytes from fd into buf */
+ssize_t y4m_read(int fd, void *buf, size_t len);
+
+/* write len bytes from fd into buf */
+ssize_t y4m_write(int fd, const void *buf, size_t len);
+
+
+
+/************************************************************************
+ *  stream header processing functions
+ *  
+ *  o return values:
+ *                   Y4M_OK - success
+ *                Y4M_ERR_* - error (see y4m_strerr() for descriptions)
+ *
+ ************************************************************************/
+
+/* parse a string of stream header tags */
+int y4m_parse_stream_tags(char *s, y4m_stream_info_t *i);
+
+/* read a stream header from file descriptor fd */
+int y4m_read_stream_header(int fd, y4m_stream_info_t *i);
+
+/* write a stream header to file descriptor fd */
+int y4m_write_stream_header(int fd, const y4m_stream_info_t *i);
+
+
+
+/************************************************************************
+ *  frame processing functions
+ *  
+ *  o return values:
+ *                   Y4M_OK - success
+ *                Y4M_ERR_* - error (see y4m_strerr() for descriptions)
+ *
+ ************************************************************************/
+
+/* read a frame header from file descriptor fd */
+int y4m_read_frame_header(int fd, y4m_frame_info_t *i);
+
+/* write a frame header to file descriptor fd */
+int y4m_write_frame_header(int fd, const y4m_frame_info_t *i);
+
+/* read a complete frame (header + data)
+   o yuv[3] points to three buffers, one each for Y, U, V planes */
+int y4m_read_frame(int fd, const y4m_stream_info_t *si, 
+                  y4m_frame_info_t *fi, uint8_t * const yuv[3]);
+
+/* write a complete frame (header + data)
+   o yuv[3] points to three buffers, one each for Y, U, V planes */
+int y4m_write_frame(int fd, const y4m_stream_info_t *si, 
+                   const y4m_frame_info_t *fi, uint8_t * const yuv[3]);
+
+
+/* read a complete frame (header + data), but de-interleave fields
+    into two separate buffers
+   o upper_field[3] same as yuv[3] above, but for upper field
+   o lower_field[3] same as yuv[3] above, but for lower field
+*/
+int y4m_read_fields(int fd, const y4m_stream_info_t *si, 
+                   y4m_frame_info_t *fi,
+                   uint8_t * const upper_field[3], 
+                   uint8_t * const lower_field[3]);
+
+/* write a complete frame (header + data), but interleave fields
+    from two separate buffers
+   o upper_field[3] same as yuv[3] above, but for upper field
+   o lower_field[3] same as yuv[3] above, but for lower field
+*/
+int y4m_write_fields(int fd, const y4m_stream_info_t *si, 
+                    const y4m_frame_info_t *fi,
+                    uint8_t * const upper_field[3], 
+                    uint8_t * const lower_field[3]);
+
+
+
+/************************************************************************
+ *  miscellaneous functions
+ ************************************************************************/
+
+/* convenient dump of stream header info via mjpeg_log facility
+ *  - each logged/printed line is prefixed by 'prefix'
+ */
+void y4m_log_stream_info(log_level_t level, const char *prefix,
+                        const y4m_stream_info_t *i);
+
+/* convert a Y4M_ERR_* error code into mildly explanatory string */
+const char *y4m_strerr(int err);
+
+/* set 'allow_unknown_tag' flag for library...
+    o yn = 0 :  unknown header tags will produce a parsing error
+    o yn = 1 :  unknown header tags/values will produce a warning, but
+                 are otherwise passed along via the xtags list
+    o yn = -1:  don't change, just return current setting
+
+   return value:  previous setting of flag
+*/
+int y4m_allow_unknown_tags(int yn);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/************************************************************************
+ ************************************************************************
+
+  Description of the (new!, forever?) YUV4MPEG2 stream format:
+
+  STREAM consists of
+    o one '\n' terminated STREAM-HEADER
+    o unlimited number of FRAMEs
+
+  FRAME consists of
+    o one '\n' terminated FRAME-HEADER
+    o "length" octets of planar YCrCb 4:2:0 image data
+        (if frame is interlaced, then the two fields are interleaved)
+
+
+  STREAM-HEADER consists of
+     o string "YUV4MPEG2 "  (note the space after the '2')
+     o unlimited number of ' ' separated TAGGED-FIELDs
+     o '\n' line terminator
+
+  FRAME-HEADER consists of
+     o string "FRAME "  (note the space after the 'E')
+     o unlimited number of ' ' separated TAGGED-FIELDs
+     o '\n' line terminator
+
+
+  TAGGED-FIELD consists of
+     o single ascii character tag
+     o VALUE (which does not contain whitespace)
+
+  VALUE consists of
+     o integer (base 10 ascii representation)
+  or o RATIO
+  or o single ascii character
+  or o generic ascii string
+
+  RATIO consists of
+     o numerator (integer)
+     o ':' (a colon)
+     o denominator (integer)
+
+
+  The currently supported tags for the STREAM-HEADER:
+     W - [integer] frame width, pixels, should be > 0
+     H - [integer] frame height, pixels, should be > 0
+     I - [char] interlacing:  p - progressive (none)
+                            t - top-field-first
+                            b - bottom-field-first
+                           ? - unknown
+     F - [ratio] frame-rate, 0:0 == unknown
+     A - [ratio] sample (pixel) aspect ratio, 0:0 == unknown
+     X - [character string] 'metadata' (unparsed, but passed around)
+
+  The currently supported tags for the FRAME-HEADER:
+     X - character string 'metadata' (unparsed, but passed around)
+
+ ************************************************************************
+ ************************************************************************/
+
+#endif /* __YUV4MPEG_H__ */
+
+
diff --git a/ext/mplex/yuv4mpeg_intern.h b/ext/mplex/yuv4mpeg_intern.h
new file mode 100644 (file)
index 0000000..140f9d6
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ *  yuv4mpeg_intern.h:  Internal constants for "new" YUV4MPEG streams
+ *
+ *  Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com>
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of version 2 of the GNU General Public License
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#ifndef __YUV4MPEG_INTERN_H__
+#define __YUV4MPEG_INTERN_H__
+
+
+#define Y4M_MAGIC "YUV4MPEG2"
+#define Y4M_FRAME_MAGIC "FRAME"
+
+#define Y4M_DELIM " "  /* single-character(space) separating tagged fields */
+
+#define Y4M_LINE_MAX 256   /* max number of characters in a header line
+                               (including the '\n', but not the '\0') */
+
+
+/* standard framerate ratios */
+#define Y4M_FPS_UNKNOWN    { 0, 0 }
+#define Y4M_FPS_NTSC_FILM  { 24000, 1001 }
+#define Y4M_FPS_FILM       { 24, 1 }
+#define Y4M_FPS_PAL        { 25, 1 }
+#define Y4M_FPS_NTSC       { 30000, 1001 }
+#define Y4M_FPS_30         { 30, 1 }
+#define Y4M_FPS_PAL_FIELD  { 50, 1 }
+#define Y4M_FPS_NTSC_FIELD { 60000, 1001 }
+#define Y4M_FPS_60         { 60, 1 }
+
+/* standard sample/pixel aspect ratios */
+#define Y4M_SAR_UNKNOWN        {   0, 0  }
+#define Y4M_SAR_SQUARE         {   1, 1  }
+#define Y4M_SAR_SQR_ANA_16_9   {   4, 3  }
+#define Y4M_SAR_NTSC_CCIR601   {  10, 11 }
+#define Y4M_SAR_NTSC_16_9      {  40, 33 }
+#define Y4M_SAR_NTSC_SVCD_4_3  {  15, 11 }
+#define Y4M_SAR_NTSC_SVCD_16_9 {  20, 11 }
+#define Y4M_SAR_PAL_CCIR601    {  59, 54 }
+#define Y4M_SAR_PAL_16_9       { 118, 81 }
+#define Y4M_SAR_PAL_SVCD_4_3   {  59, 36 }
+#define Y4M_SAR_PAL_SVCD_16_9  {  59, 27 }
+
+#define Y4M_SAR_MPEG1_1 Y4M_SAR_SQUARE
+#define Y4M_SAR_MPEG1_2  { 10000, 6735 }
+#define Y4M_SAR_MPEG1_3  { 10000, 7031 } /* Anamorphic 16:9 PAL */
+#define Y4M_SAR_MPEG1_4  { 10000, 7615 }
+#define Y4M_SAR_MPEG1_5  { 10000, 8055 }
+#define Y4M_SAR_MPEG1_6  { 10000, 8437 } /* Anamorphic 16:9 NTSC */
+#define Y4M_SAR_MPEG1_7  { 10000, 8935 } 
+#define Y4M_SAR_MPEG1_8  { 10000, 9375 } /* PAL/SECAM 4:3 */
+#define Y4M_SAR_MPEG1_9  { 10000, 9815 }
+#define Y4M_SAR_MPEG1_10 { 10000, 10255 }
+#define Y4M_SAR_MPEG1_11 { 10000, 10695 }
+#define Y4M_SAR_MPEG1_12 { 10000, 11250 } /* NTSC 4:3 */
+#define Y4M_SAR_MPEG1_13 { 10000, 11575 }
+#define Y4M_SAR_MPEG1_14 { 10000, 12015 }
+
+#define Y4M_DAR_UNKNOWN        {   0, 0  }
+#define Y4M_DAR_4_3      {   4, 3   }
+#define Y4M_DAR_16_9     {  16, 9   }
+#define Y4M_DAR_221_100  { 221, 100 }
+
+#define Y4M_DAR_MPEG2_1 {   1, 1   }
+#define Y4M_DAR_MPEG2_2 {   4, 3   }
+#define Y4M_DAR_MPEG2_3 {  16, 9   }
+#define Y4M_DAR_MPEG2_4 { 221, 100 }
+
+#endif /* __YUV4MPEG_INTERN_H__ */
+
diff --git a/ext/mplex/yuv4mpeg_ratio.cc b/ext/mplex/yuv4mpeg_ratio.cc
new file mode 100644 (file)
index 0000000..a20a237
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ *  yuv4mpeg_ratio.c:  Functions for dealing with y4m_ratio_t datatype.
+ *
+ *  Copyright (C) 2001 Matthew J. Marjanovic <maddog@mir.com>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include "yuv4mpeg.h"
+#include "yuv4mpeg_intern.h"
+
+
+/* useful list of standard framerates */
+const y4m_ratio_t y4m_fps_UNKNOWN = Y4M_FPS_UNKNOWN;
+const y4m_ratio_t y4m_fps_NTSC_FILM = Y4M_FPS_NTSC_FILM;
+const y4m_ratio_t y4m_fps_FILM = Y4M_FPS_FILM;
+const y4m_ratio_t y4m_fps_PAL = Y4M_FPS_PAL;
+const y4m_ratio_t y4m_fps_NTSC = Y4M_FPS_NTSC;
+const y4m_ratio_t y4m_fps_30 = Y4M_FPS_30;
+const y4m_ratio_t y4m_fps_PAL_FIELD = Y4M_FPS_PAL_FIELD;
+const y4m_ratio_t y4m_fps_NTSC_FIELD = Y4M_FPS_NTSC_FIELD;
+const y4m_ratio_t y4m_fps_60 = Y4M_FPS_60;
+
+/* useful list of standard sample aspect ratios */
+const y4m_ratio_t y4m_sar_UNKNOWN = Y4M_SAR_UNKNOWN;
+const y4m_ratio_t y4m_sar_SQUARE = Y4M_SAR_SQUARE;
+const y4m_ratio_t y4m_sar_SQR_ANA_16_9 = Y4M_SAR_SQR_ANA_16_9;
+const y4m_ratio_t y4m_sar_NTSC_CCIR601 = Y4M_SAR_NTSC_CCIR601;
+const y4m_ratio_t y4m_sar_NTSC_16_9 = Y4M_SAR_NTSC_16_9;
+const y4m_ratio_t y4m_sar_NTSC_SVCD_4_3 = Y4M_SAR_NTSC_SVCD_4_3;
+const y4m_ratio_t y4m_sar_NTSC_SVCD_16_9 = Y4M_SAR_NTSC_SVCD_16_9;
+const y4m_ratio_t y4m_sar_PAL_CCIR601 = Y4M_SAR_PAL_CCIR601;
+const y4m_ratio_t y4m_sar_PAL_16_9 = Y4M_SAR_PAL_16_9;
+const y4m_ratio_t y4m_sar_PAL_SVCD_4_3 = Y4M_SAR_PAL_SVCD_4_3;
+const y4m_ratio_t y4m_sar_PAL_SVCD_16_9 = Y4M_SAR_PAL_SVCD_16_9;
+
+/* useful list of standard display aspect ratios */
+const y4m_ratio_t y4m_dar_4_3 = Y4M_DAR_4_3;
+const y4m_ratio_t y4m_dar_16_9 = Y4M_DAR_16_9;
+const y4m_ratio_t y4m_dar_221_100 = Y4M_DAR_221_100;
+
+/*
+ *  Euler's algorithm for greatest common divisor
+ */
+
+static int
+gcd (int a, int b)
+{
+  a = (a >= 0) ? a : -a;
+  b = (b >= 0) ? b : -b;
+
+  while (b > 0) {
+    int x = b;
+
+    b = a % b;
+    a = x;
+  }
+  return a;
+}
+
+
+/*************************************************************************
+ *
+ * Remove common factors from a ratio
+ *
+ *************************************************************************/
+
+
+void
+y4m_ratio_reduce (y4m_ratio_t * r)
+{
+  int d;
+
+  if ((r->n == 0) && (r->d == 0))
+    return;                    /* "unknown" */
+  d = gcd (r->n, r->d);
+  r->n /= d;
+  r->d /= d;
+}
+
+
+
+/*************************************************************************
+ *
+ * Parse "nnn:ddd" into a ratio
+ *
+ * returns:         Y4M_OK  - success
+ *           Y4M_ERR_RANGE  - range error 
+ *
+ *************************************************************************/
+
+int
+y4m_parse_ratio (y4m_ratio_t * r, const char *s)
+{
+  char *t = strchr (s, ':');
+
+  if (t == NULL)
+    return Y4M_ERR_RANGE;
+  r->n = atoi (s);
+  r->d = atoi (t + 1);
+  if (r->d < 0)
+    return Y4M_ERR_RANGE;
+  /* 0:0 == unknown, so that is ok, otherwise zero denominator is bad */
+  if ((r->d == 0) && (r->n != 0))
+    return Y4M_ERR_RANGE;
+  y4m_ratio_reduce (r);
+  return Y4M_OK;
+}
+
+
+
+/*************************************************************************
+ *
+ * Guess the true SAR (sample aspect ratio) from a list of commonly 
+ * encountered values, given the "suggested" display aspect ratio, and
+ * the true frame width and height.
+ *
+ * Returns y4m_sar_UNKNOWN if no match is found.
+ *
+ *************************************************************************/
+
+/* this is big enough to accommodate the difference between 720 and 704 */
+#define GUESS_ASPECT_TOLERANCE 0.03
+
+y4m_ratio_t
+y4m_guess_sar (int width, int height, y4m_ratio_t dar)
+{
+  int i;
+  double implicit_sar = (double) (dar.n * height) / (double) (dar.d * width);
+  y4m_ratio_t sarray[] = {
+    y4m_sar_SQUARE,
+    y4m_sar_NTSC_CCIR601,
+    y4m_sar_NTSC_16_9,
+    y4m_sar_NTSC_SVCD_4_3,
+    y4m_sar_NTSC_SVCD_16_9,
+    y4m_sar_PAL_CCIR601,
+    y4m_sar_PAL_16_9,
+    y4m_sar_PAL_SVCD_4_3,
+    y4m_sar_PAL_SVCD_16_9,
+    y4m_sar_UNKNOWN
+  };
+
+  for (i = 0; !(Y4M_RATIO_EQL (sarray[i], y4m_sar_UNKNOWN)); i++) {
+    double ratio = implicit_sar / Y4M_RATIO_DBL (sarray[i]);
+
+    if ((ratio > (1.0 - GUESS_ASPECT_TOLERANCE)) && (ratio < (1.0 + GUESS_ASPECT_TOLERANCE)))
+      return sarray[i];
+  }
+  return y4m_sar_UNKNOWN;
+}