mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-04 14:38:48 +00:00
Moved to gst-libs/ext/mplex
Original commit message from CVS: Moved to gst-libs/ext/mplex
This commit is contained in:
parent
a10432d76c
commit
d1f62e8a4f
41 changed files with 0 additions and 9406 deletions
|
@ -1,339 +0,0 @@
|
||||||
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.
|
|
|
@ -1,67 +0,0 @@
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
// //
|
|
||||||
// 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 |
|
|
||||||
+---------------------------------------+--------------------------------+
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
|
|
||||||
* 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
|
|
|
@ -1,41 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
|
@ -1,383 +0,0 @@
|
||||||
/*
|
|
||||||
* 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"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#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 %d bytes.", (int)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:
|
|
||||||
*/
|
|
|
@ -1,163 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,144 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,41 +0,0 @@
|
||||||
#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__
|
|
|
@ -1,358 +0,0 @@
|
||||||
/** @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:
|
|
||||||
*/
|
|
|
@ -1,82 +0,0 @@
|
||||||
#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__
|
|
|
@ -1,118 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,35 +0,0 @@
|
||||||
/* fast int primitives. min,max,abs,samesign
|
|
||||||
*
|
|
||||||
* WARNING: Assumes 2's complement arithmetic.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __inline__
|
|
||||||
#define __inline__ inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
$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__ */
|
|
|
@ -1,249 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,278 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,304 +0,0 @@
|
||||||
/*
|
|
||||||
* 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"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 %d bytes.", (int)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:
|
|
||||||
*/
|
|
|
@ -1,239 +0,0 @@
|
||||||
/*
|
|
||||||
$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:
|
|
||||||
*/
|
|
|
@ -1,79 +0,0 @@
|
||||||
/*
|
|
||||||
$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:
|
|
||||||
*/
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
$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:
|
|
||||||
*/
|
|
|
@ -1,329 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 %d prev %d !", (int) AU_start / 8,
|
|
||||||
(int)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 %d bytes.", (int)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:
|
|
||||||
*/
|
|
|
@ -1,427 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,149 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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__ */
|
|
|
@ -1,83 +0,0 @@
|
||||||
#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__
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,198 +0,0 @@
|
||||||
|
|
||||||
#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__
|
|
|
@ -1,59 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,73 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,194 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
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:
|
|
||||||
*/
|
|
|
@ -1,107 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,762 +0,0 @@
|
||||||
#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"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
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:
|
|
||||||
*/
|
|
|
@ -1,131 +0,0 @@
|
||||||
#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:
|
|
||||||
*/
|
|
|
@ -1,23 +0,0 @@
|
||||||
#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;
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
#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__
|
|
|
@ -1,155 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,429 +0,0 @@
|
||||||
/*
|
|
||||||
* 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) %d: supposed marker bit not found.",
|
|
||||||
(int)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: %11u bytes", (int)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:
|
|
||||||
*/
|
|
|
@ -1,276 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
* 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:
|
|
||||||
*/
|
|
|
@ -1,880 +0,0 @@
|
||||||
/*
|
|
||||||
* 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";
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,473 +0,0 @@
|
||||||
/*
|
|
||||||
* 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__ */
|
|
||||||
|
|
||||||
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* 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__ */
|
|
||||||
|
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 = (char *) 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;
|
|
||||||
}
|
|
Loading…
Reference in a new issue