First stab at porting mplex

Original commit message from CVS:
First stab at porting mplex
This commit is contained in:
Wim Taymans 2002-10-24 22:37:51 +00:00
parent 440801dd9e
commit 1496394c0f
46 changed files with 10194 additions and 0 deletions

6
ext/mplex/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
Makefile
Makefile.in
mplex
.deps
.libs

339
ext/mplex/COPYING Normal file
View file

@ -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.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
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.

67
ext/mplex/INSTRUCT Normal file
View file

@ -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 |
+---------------------------------------+--------------------------------+

38
ext/mplex/Makefile.am Normal file
View file

@ -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

58
ext/mplex/README Normal file
View file

@ -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

41
ext/mplex/TODO Normal file
View file

@ -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.

382
ext/mplex/ac3strm_in.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

163
ext/mplex/audiostrm.hh Normal file
View file

@ -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.
*/
#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__
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

144
ext/mplex/audiostrm_out.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

41
ext/mplex/aunit.hh Normal file
View file

@ -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__

358
ext/mplex/bits.cc Normal file
View file

@ -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);
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

82
ext/mplex/bits.hh Normal file
View file

@ -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__

118
ext/mplex/buffer.cc Normal file
View file

@ -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;
}

73
ext/mplex/buffer.hh Normal file
View file

@ -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.
*/
#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__
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

32
ext/mplex/fastintfns.h Normal file
View file

@ -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

38
ext/mplex/format_codes.h Normal file
View file

@ -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
*/
#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__ */

464
ext/mplex/gstmplex.cc Normal file
View file

@ -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
};

97
ext/mplex/gstmplex.h Normal file
View file

@ -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__ */

249
ext/mplex/inputstrm.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

278
ext/mplex/inputstrm.hh Normal file
View file

@ -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.
*/
#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__
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

196
ext/mplex/interact.cc Normal file
View file

@ -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

303
ext/mplex/lpcmstrm_in.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

239
ext/mplex/mjpeg_logging.cc Normal file
View file

@ -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
*/
#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);
}
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

79
ext/mplex/mjpeg_logging.h Normal file
View file

@ -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
*/
#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__ */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

120
ext/mplex/mjpeg_types.h Normal file
View file

@ -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
*/
#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__ */
/*
* Local variables:
* c-file-style: "gnu"
* tab-width: 8
* indent-tabs-mode: nil
* End:
*/

329
ext/mplex/mpastrm_in.cc Normal file
View file

@ -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.
*/
#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]);
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

427
ext/mplex/mpegconsts.cc Normal file
View file

@ -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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

149
ext/mplex/mpegconsts.h Normal file
View file

@ -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__ */

83
ext/mplex/mplexconsts.hh Normal file
View file

@ -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__

1120
ext/mplex/multplex.cc Normal file

File diff suppressed because it is too large Load diff

198
ext/mplex/outputstream.hh Normal file
View file

@ -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__

59
ext/mplex/padstrm.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

73
ext/mplex/padstrm.hh Normal file
View file

@ -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.
*/
#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__
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

193
ext/mplex/stillsstream.cc Normal file
View file

@ -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.
*/
#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;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

107
ext/mplex/stillsstream.hh Normal file
View file

@ -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.
*/
#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;
};
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

761
ext/mplex/systems.cc Normal file
View file

@ -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:
*/

131
ext/mplex/systems.hh Normal file
View file

@ -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__
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

23
ext/mplex/vector.cc Normal file
View file

@ -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;
}

71
ext/mplex/vector.hh Normal file
View file

@ -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__

155
ext/mplex/videostrm.hh Normal file
View file

@ -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.
*/
#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__
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

429
ext/mplex/videostrm_in.cc Normal file
View file

@ -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.
*/
#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;
}
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

276
ext/mplex/videostrm_out.cc Normal file
View file

@ -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.
*/
#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 ();
}
/*
* Local variables:
* c-file-style: "stroustrup"
* tab-width: 4
* indent-tabs-mode: nil
* End:
*/

880
ext/mplex/yuv4mpeg.cc Normal file
View file

@ -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";
}
}

473
ext/mplex/yuv4mpeg.h Normal file
View file

@ -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__ */

View file

@ -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__ */

167
ext/mplex/yuv4mpeg_ratio.cc Normal file
View file

@ -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;
}