diff options
author | Fabrice Bellard <fabrice@bellard.org> | 2000-12-20 00:02:47 +0000 |
---|---|---|
committer | Fabrice Bellard <fabrice@bellard.org> | 2000-12-20 00:02:47 +0000 |
commit | 9aeeeb63f7e1ab7b0b7bb839a5f258667a2d2d78 (patch) | |
tree | 133769894d45da35e05ded6ea39d33bb81e7ae18 | |
parent | 77bb6835ba752bb9335d208963a53227bbb1bc63 (diff) | |
download | ffmpeg-9aeeeb63f7e1ab7b0b7bb839a5f258667a2d2d78.tar.gz |
Initial revision
Originally committed as revision 2 to svn://svn.ffmpeg.org/ffmpeg/trunk
-rw-r--r-- | COPYING | 339 | ||||
-rw-r--r-- | Makefile | 28 | ||||
-rw-r--r-- | README | 60 | ||||
-rw-r--r-- | asfenc.c | 510 | ||||
-rw-r--r-- | doc/BUGS | 14 | ||||
-rw-r--r-- | doc/README.tech | 25 | ||||
-rw-r--r-- | doc/TODO | 19 | ||||
-rw-r--r-- | doc/ffmpeg.txt | 62 | ||||
-rw-r--r-- | doc/ffserver.conf | 214 | ||||
-rw-r--r-- | ffmpeg.c | 717 | ||||
-rw-r--r-- | ffserver.c | 1642 | ||||
-rw-r--r-- | formats.c | 373 | ||||
-rw-r--r-- | grab.c | 258 | ||||
-rw-r--r-- | jpegenc.c | 102 | ||||
-rw-r--r-- | libav/Makefile | 17 | ||||
-rw-r--r-- | libav/ac3enc.c | 1460 | ||||
-rw-r--r-- | libav/ac3enc.h | 32 | ||||
-rw-r--r-- | libav/ac3tab.h | 180 | ||||
-rw-r--r-- | libav/avcodec.h | 79 | ||||
-rw-r--r-- | libav/common.c | 174 | ||||
-rw-r--r-- | libav/common.h | 68 | ||||
-rw-r--r-- | libav/h263data.h | 151 | ||||
-rw-r--r-- | libav/h263enc.c | 229 | ||||
-rw-r--r-- | libav/jfdctfst.c | 224 | ||||
-rw-r--r-- | libav/jrevdct.c | 1584 | ||||
-rw-r--r-- | libav/mjpegenc.c | 416 | ||||
-rw-r--r-- | libav/mpegaudio.c | 754 | ||||
-rw-r--r-- | libav/mpegaudio.h | 31 | ||||
-rw-r--r-- | libav/mpegaudiotab.h | 310 | ||||
-rw-r--r-- | libav/mpegencodevlc.h | 311 | ||||
-rw-r--r-- | libav/mpegvideo.c | 1098 | ||||
-rw-r--r-- | libav/mpegvideo.h | 94 | ||||
-rw-r--r-- | libav/resample.c | 245 | ||||
-rw-r--r-- | mpegenc.h | 127 | ||||
-rw-r--r-- | mpegmux.c | 301 | ||||
-rw-r--r-- | rmenc.c | 498 | ||||
-rw-r--r-- | swfenc.c | 347 | ||||
-rw-r--r-- | udp.c | 123 |
38 files changed, 13216 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..e77696ae8d --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..590784966b --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CFLAGS= -O2 -Wall -g -I./libav +LDFLAGS= -g -L./libav + +PROG= ffmpeg ffserver + +all: lib $(PROG) + +lib: + make -C libav all + +ffmpeg: rmenc.o mpegmux.o asfenc.o jpegenc.o swfenc.o udp.o formats.o grab.o ffmpeg.o libav/libav.a + gcc $(LDFLAGS) -o $@ $^ -lav -lm + +ffserver: rmenc.o mpegmux.o asfenc.o jpegenc.o swfenc.o formats.o grab.o ffserver.o libav/libav.a + gcc $(LDFLAGS) -o $@ $^ -lav -lpthread -lm + +%.o: %.c + gcc $(CFLAGS) -c -o $@ $< + +clean: + make -C libav clean + rm -f *.o *~ gmon.out TAGS $(PROG) + +etags: + etags *.[ch] libav/*.[ch] + +tar: + (cd .. ; tar zcvf ffmpeg-0.3.tgz ffmpeg --exclude CVS --exclude TAGS ) diff --git a/README b/README new file mode 100644 index 0000000000..45029dbfad --- /dev/null +++ b/README @@ -0,0 +1,60 @@ +FFmpeg version 0.9 - (c) 2000 Gerard Lantau. + +1) Introduction +--------------- + +ffmpeg is a hyper fast realtime audio/video encoder and streaming +server. It can grab from a standard Video4Linux video source and +convert it into several file formats based on DCT/motion compensation +encoding. Sound is compressed in MPEG audio layer 2 or using an AC3 +compatible stream. + +What makes ffmpeg interesting ? + +- Innovative streaming technology : multiformat, real time encoding, + simple configuration. + +- Simple and efficient video encoder: outputs MPEG1, H263 and Real + Video(tm) compatible bitstreams using the same encoder core. + +- Real time encoding (25 fps in 352x288 on a K6 500) using the video4linux API. + +- Generates I and P frames, which means it is far better than a MJPEG + encoder. + +- Hyper fast MPEG audio layer 2 compression (50 times faster than + realtime on a K6 500). + +- Hyper fast AC3 compatible encoder. + +- simple and very small portable C source code, easy to understand and + to modify. It be may the smallest decent MPEG encoder :-) + +ffmpeg is made of two programs: + +* ffmpeg: soft VCR which encodes in real time to several formats. + +* ffserver: live broadcast streaming server based on the ffmpeg core +encoders. + +2) Documentation +---------------- + +read doc/ffmpeg.txt and doc/ffserver.txt to learn the basic features. + +read ffmpeg + +3) Licensing: +------------ + +* See the file COPYING. ffmpeg is licensed under the GNU General + Public License. + +* Source code from third parties: The DCT code comes from the Berkeley + MPEG decoder and the JPEG encoder. + +* This code should be patent free since it is very simple. I took care + to use the same video encoder core for all formats to show that they + really ARE THE SAME except for the encoding huffman codes. + +Gerard Lantau (glantau@users.sourceforge.net). diff --git a/asfenc.c b/asfenc.c new file mode 100644 index 0000000000..5b2892309f --- /dev/null +++ b/asfenc.c @@ -0,0 +1,510 @@ +/* + * ASF compatible encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include "mpegenc.h" + +#define PACKET_SIZE 3200 +#define PACKET_HEADER_SIZE 12 +#define FRAME_HEADER_SIZE 17 + +typedef struct { + AVEncodeContext *enc; + int num; + int seq; +} ASFStream; + +typedef struct { + int is_streamed; /* true if streamed */ + int seqno; + int packet_size; + + ASFStream streams[2]; + ASFStream *audio_stream, *video_stream; + int nb_streams; + /* non streamed additonnal info */ + int file_size_offset; + int data_offset; + /* packet filling */ + int packet_size_left; + int packet_timestamp_start; + int packet_timestamp_end; + int packet_nb_frames; + UINT8 packet_buf[PACKET_SIZE]; + PutByteContext pb; +} ASFContext; + +typedef struct { + UINT32 v1; + UINT16 v2; + UINT16 v3; + UINT8 v4[8]; +} GUID; + +static const GUID asf_header = { + 0x75B22630, 0x668E, 0x11CF, { 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C }, +}; + +static const GUID file_header = { + 0x8CABDCA1, 0xA947, 0x11CF, { 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 }, +}; + +static const GUID stream_header = { + 0xB7DC0791, 0xA9B7, 0x11CF, { 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 }, +}; + +static const GUID audio_stream = { + 0xF8699E40, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B }, +}; + +static const GUID audio_conceal_none = { + 0x49f1a440, 0x4ece, 0x11d0, { 0xa3, 0xac, 0x00, 0xa0, 0xc9, 0x03, 0x48, 0xf6 }, +}; + +static const GUID video_stream = { + 0xBC19EFC0, 0x5B4D, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B }, +}; + +static const GUID video_conceal_none = { + 0x20FB5700, 0x5B55, 0x11CF, { 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B }, +}; + +static const GUID comment_header = { + 0x86D15240, 0x311D, 0x11D0, { 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 }, +}; + +static const GUID data_header = { + 0x75b22636, 0x668e, 0x11cf, { 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c }, +}; + +static const GUID packet_guid = { + 0xF656CCE1, 0x03B3, 0x11D4, { 0xBE, 0xA2, 0x00, 0xA0, 0xCC, 0x3D, 0x72, 0x74 }, +}; + +/* I am not a number !!! This GUID is the one found on the PC used to + generate the stream */ +static const GUID my_guid = { + 0x12345678, 0xA947, 0x11CF, { 0x31, 0x41, 0x59, 0x26, 0x20, 0x20, 0x20, 0x20 }, +}; + +static void put_guid(PutByteContext *s, const GUID *g) +{ + int i; + + put_le32(s, g->v1); + put_le16(s, g->v2); + put_le16(s, g->v3); + for(i=0;i<8;i++) + put_byte(s, g->v4[i]); +} + +#if 0 +static void put_str16(PutByteContext *s, const char *tag) +{ + put_le16(s,strlen(tag)); + while (*tag) { + put_le16(s, *tag++); + } +} +#endif + +/* write an asf chunk (only used in streaming case) */ +static void put_chunk(AVFormatContext *s, int type, int payload_length) +{ + ASFContext *asf = s->priv_data; + PutByteContext *pb = &s->pb; + int length; + + length = payload_length + 8; + put_le16(pb, type); + put_le16(pb, length); + put_le32(pb, asf->seqno); + put_le16(pb, 0); /* unknown bytes */ + put_le16(pb, length); + asf->seqno++; +} + + +static int asf_write_header(AVFormatContext *s) +{ + PutByteContext *pb = &s->pb; + ASFContext *asf; + int header_size, n, extra_size, extra_size2, i, wav_extra_size; + AVEncodeContext *enc; + long long header_offset, cur_pos; + + asf = malloc(sizeof(ASFContext)); + if (!asf) + return -1; + memset(asf, 0, sizeof(ASFContext)); + s->priv_data = asf; + + asf->packet_size = PACKET_SIZE; + + if (!s->is_streamed) { + put_guid(pb, &asf_header); + put_le64(pb, 0); /* header length, will be patched after */ + put_le32(pb, 6); /* ??? */ + put_byte(pb, 1); /* ??? */ + put_byte(pb, 2); /* ??? */ + } else { + put_chunk(s, 0x4824, 0); /* start of stream (length will be patched later) */ + } + + /* file header */ + header_offset = put_pos(pb); + put_guid(pb, &file_header); + put_le64(pb, 24 + 80); + put_guid(pb, &my_guid); + asf->file_size_offset = put_pos(pb); + put_le64(pb, 0); /* file size (patched after if not streamed) */ + put_le64(pb, 0); /* file time : 1601 :-) */ + put_le64(pb, 0x283); /* ??? */ + put_le64(pb, 0); /* stream 0 length in us */ + put_le64(pb, 0); /* stream 1 length in us */ + put_le32(pb, 0x0c1c); /* ??? */ + put_le32(pb, 0); /* ??? */ + put_le32(pb, 2); /* ??? */ + put_le32(pb, asf->packet_size); /* packet size */ + put_le32(pb, asf->packet_size); /* ??? */ + put_le32(pb, 0x03e800); /* ??? */ + + /* stream headers */ + n = 0; + for(i=0;i<2;i++) { + if (i == 0) + enc = s->audio_enc; + else + enc = s->video_enc; + + if (!enc) + continue; + asf->streams[n].num = i; + asf->streams[n].seq = 0; + asf->streams[n].enc = enc; + + switch(enc->codec->type) { + case CODEC_TYPE_AUDIO: + asf->audio_stream = &asf->streams[n]; + wav_extra_size = 0; + extra_size = 18 + wav_extra_size; + extra_size2 = 0; + break; + default: + case CODEC_TYPE_VIDEO: + asf->video_stream = &asf->streams[n]; + wav_extra_size = 0; + extra_size = 0x33; + extra_size2 = 0; + break; + } + + put_guid(pb, &stream_header); + put_le64(pb, 24 + 16 * 2 + 22 + extra_size + extra_size2); + if (enc->codec->type == CODEC_TYPE_AUDIO) { + put_guid(pb, &audio_stream); + put_guid(pb, &audio_conceal_none); + } else { + put_guid(pb, &video_stream); + put_guid(pb, &video_conceal_none); + } + put_le64(pb, 0); /* ??? */ + put_le32(pb, extra_size); /* wav header len */ + put_le32(pb, extra_size2); /* additional data len */ + put_le16(pb, n + 1); /* stream number */ + put_le32(pb, 0); /* ??? */ + + if (enc->codec->type == CODEC_TYPE_AUDIO) { + /* WAVEFORMATEX header */ + + put_le16(pb, 0x55); /* MP3 format */ + put_le16(pb, s->audio_enc->channels); + put_le32(pb, s->audio_enc->rate); + put_le32(pb, s->audio_enc->bit_rate / 8); + put_le16(pb, 1); /* block align */ + put_le16(pb, 16); /* bits per sample */ + put_le16(pb, wav_extra_size); + + /* no addtionnal adata */ + } else { + put_le32(pb, enc->width); + put_le32(pb, enc->height); + put_byte(pb, 2); /* ??? */ + put_le16(pb, 40); /* size */ + + /* BITMAPINFOHEADER header */ + put_le32(pb, 40); /* size */ + put_le32(pb, enc->width); + put_le32(pb, enc->height); + put_le16(pb, 1); /* planes */ + put_le16(pb, 24); /* depth */ + /* compression type */ + switch(enc->codec->id) { + case CODEC_ID_H263: + put_tag(pb, "I263"); + break; + case CODEC_ID_MJPEG: + put_tag(pb, "MJPG"); + break; + default: + put_tag(pb, "XXXX"); + break; + } + put_le32(pb, enc->width * enc->height * 3); + put_le32(pb, 0); + put_le32(pb, 0); + put_le32(pb, 0); + put_le32(pb, 0); + } + n++; + } + asf->nb_streams = n; + + /* XXX: should media comments */ +#if 0 + put_guid(pb, &comment_header); + put_le64(pb, 0); +#endif + /* patch the header size fields */ + cur_pos = put_pos(pb); + header_size = cur_pos - header_offset; + if (!s->is_streamed) { + header_size += 24 + 6; + put_seek(pb, header_offset - 14, SEEK_SET); + put_le64(pb, header_size); + } else { + header_size += 8 + 50; + put_seek(pb, header_offset - 10, SEEK_SET); + put_le16(pb, header_size); + put_seek(pb, header_offset - 2, SEEK_SET); + put_le16(pb, header_size); + } + put_seek(pb, cur_pos, SEEK_SET); + + /* movie chunk, followed by packets of packet_size */ + asf->data_offset = cur_pos; + put_guid(pb, &data_header); + put_le64(pb, 24); /* will be patched after */ + put_guid(pb, &packet_guid); + put_le64(pb, 0x283); /* ??? */ + put_byte(pb, 1); /* ??? */ + put_byte(pb, 1); /* ??? */ + + put_flush_packet(&s->pb); + + asf->packet_nb_frames = 0; + asf->packet_timestamp_start = -1; + asf->packet_timestamp_end = -1; + asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE; + init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, + NULL, NULL, NULL); + + return 0; +} + +/* write a fixed size packet */ +static void put_packet(AVFormatContext *s, + unsigned int timestamp, unsigned int duration, + int nb_frames, int padsize) +{ + ASFContext *asf = s->priv_data; + PutByteContext *pb = &s->pb; + int flags; + + if (s->is_streamed) { + put_chunk(s, 0x4424, asf->packet_size); + } + + put_byte(pb, 0x82); + put_le16(pb, 0); + + flags = 0x01; /* nb segments present */ + if (padsize > 0) { + if (padsize < 256) + flags |= 0x08; + else + flags |= 0x10; + } + put_byte(pb, flags); /* flags */ + put_byte(pb, 0x5d); + if (flags & 0x10) + put_le16(pb, padsize); + if (flags & 0x08) + put_byte(pb, padsize); + put_le32(pb, timestamp); + put_le16(pb, duration); + put_byte(pb, nb_frames | 0x80); +} + +static void flush_packet(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + int hdr_size, ptr; + + put_packet(s, asf->packet_timestamp_start, + asf->packet_timestamp_end - asf->packet_timestamp_start, + asf->packet_nb_frames, asf->packet_size_left); + + /* compute padding */ + hdr_size = PACKET_HEADER_SIZE; + if (asf->packet_size_left > 0) { + /* if padding needed, don't forget to count the + padding byte in the header size */ + hdr_size++; + asf->packet_size_left--; + /* XXX: I do not test again exact limit to avoid boundary problems */ + if (asf->packet_size_left > 200) { + hdr_size++; + asf->packet_size_left--; + } + } + ptr = asf->packet_size - PACKET_HEADER_SIZE - asf->packet_size_left; + memset(asf->packet_buf + ptr, 0, asf->packet_size_left); + + put_buffer(&s->pb, asf->packet_buf, asf->packet_size - hdr_size); + + put_flush_packet(&s->pb); + asf->packet_nb_frames = 0; + asf->packet_timestamp_start = -1; + asf->packet_timestamp_end = -1; + asf->packet_size_left = asf->packet_size - PACKET_HEADER_SIZE; + init_put_byte(&asf->pb, asf->packet_buf, asf->packet_size, + NULL, NULL, NULL); +} + +static void put_frame_header(AVFormatContext *s, ASFStream *stream, int timestamp, + int payload_size, int frag_offset, int frag_len) +{ + ASFContext *asf = s->priv_data; + PutByteContext *pb = &asf->pb; + int val; + + val = stream->num; + if (stream->enc->key_frame) + val |= 0x80; + put_byte(pb, val); + put_byte(pb, stream->seq); + put_le32(pb, frag_offset); /* fragment offset */ + put_byte(pb, 0x08); /* flags */ + put_le32(pb, payload_size); + put_le32(pb, timestamp); + put_le16(pb, frag_len); +} + + +/* Output a frame. We suppose that payload_size <= PACKET_SIZE. + + It is there that you understand that the ASF format is really + crap. They have misread the MPEG Systems spec ! + */ +static void put_frame(AVFormatContext *s, ASFStream *stream, int timestamp, + UINT8 *buf, int payload_size) +{ + ASFContext *asf = s->priv_data; + int frag_pos, frag_len, frag_len1; + + frag_pos = 0; + while (frag_pos < payload_size) { + frag_len = payload_size - frag_pos; + frag_len1 = asf->packet_size_left - FRAME_HEADER_SIZE; + if (frag_len1 > 0) { + if (frag_len > frag_len1) + frag_len = frag_len1; + put_frame_header(s, stream, timestamp, payload_size, frag_pos, frag_len); + put_buffer(&asf->pb, buf, frag_len); + asf->packet_size_left -= (frag_len + FRAME_HEADER_SIZE); + asf->packet_timestamp_end = timestamp; + if (asf->packet_timestamp_start == -1) + asf->packet_timestamp_start = timestamp; + asf->packet_nb_frames++; + } else { + frag_len = 0; + } + frag_pos += frag_len; + buf += frag_len; + /* output the frame if filled */ + if (asf->packet_size_left <= FRAME_HEADER_SIZE) + flush_packet(s); + } + stream->seq++; +} + + +static int asf_write_audio(AVFormatContext *s, UINT8 *buf, int size) +{ + ASFContext *asf = s->priv_data; + int timestamp; + + timestamp = (int)((float)s->audio_enc->frame_number * s->audio_enc->frame_size * 1000.0 / + s->audio_enc->rate); + put_frame(s, asf->audio_stream, timestamp, buf, size); + return 0; +} + +static int asf_write_video(AVFormatContext *s, UINT8 *buf, int size) +{ + ASFContext *asf = s->priv_data; + int timestamp; + + timestamp = (int)((float)s->video_enc->frame_number * 1000.0 / + s->video_enc->rate); + put_frame(s, asf->audio_stream, timestamp, buf, size); + return 0; +} + +static int asf_write_trailer(AVFormatContext *s) +{ + ASFContext *asf = s->priv_data; + long long file_size; + + /* flush the current packet */ + if (asf->pb.buf_ptr > asf->pb.buffer) + flush_packet(s); + + if (s->is_streamed) { + put_chunk(s, 0x4524, 0); /* end of stream */ + } else { + /* patch the various place which depends on the file size */ + file_size = put_pos(&s->pb); + put_seek(&s->pb, asf->file_size_offset, SEEK_SET); + put_le64(&s->pb, file_size); + put_seek(&s->pb, asf->data_offset + 16, SEEK_SET); + put_le64(&s->pb, file_size - asf->data_offset); + } + + put_flush_packet(&s->pb); + + free(asf); + return 0; +} + +AVFormat asf_format = { + "asf", + "asf format", + "application/octet-stream", + "asf", + CODEC_ID_MP2, + CODEC_ID_MJPEG, + asf_write_header, + asf_write_audio, + asf_write_video, + asf_write_trailer, +}; diff --git a/doc/BUGS b/doc/BUGS new file mode 100644 index 0000000000..0c9ef63267 --- /dev/null +++ b/doc/BUGS @@ -0,0 +1,14 @@ +- Sound is only handled in mono. The fixed psycho acoustic model is + very bad, although the quality is surpringly good for such a model ! + +- the bit rate control is really not correct. + +- Only frame size multiple of 16 are handled. + +- If you want a specific format to be added (I am thinking about + MJPEG, H261) please tell me. Of course, the format you choose should + be based on MPEG to be easily integrated + +- ffmpeg can be used to generate streaming audio/video on a + server. Please tell me if you are interested. + diff --git a/doc/README.tech b/doc/README.tech new file mode 100644 index 0000000000..8ba259d27a --- /dev/null +++ b/doc/README.tech @@ -0,0 +1,25 @@ +Technical notes: +--------------- + +- To increase speed, only motion vectors (0,0) are tested + +- The decision intra/predicted macroblock is the algorithm suggested + by the mpeg 1 specification. + +- only Huffman based H263 is supported, mainly because of patent + issues. + +- Many options can be modified only in the source code. + +- I rewrote the mpeg audio layer 2 compatible encoder from scratch. It + is one of the simplest encoder you can imagine (800 lines of C code + !). It is also one of the fastest because of its simplicity. They + are still some problems of overflow. A minimal psycho acoustic model + could be added. Only mono stream can be generated. I may add stereo + if needed. + +- I rewrote the AC3 audio encoder from scratch. It is fairly naive, + but the result are quiet interesting at 64 kbit/s. It includes + extensions for low sampling rates used in some Internet + formats. Differential and coupled stereo is not handled. Stereo + channels are simply handled as two mono channels. diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 0000000000..c57a4876d6 --- /dev/null +++ b/doc/TODO @@ -0,0 +1,19 @@ +ffmpeg TODO list: + +- Add ASF format. + +- add MJPEG codec. + +- fix skip frame bug in mpeg video + +- fix G2 audio problem (bad volume in AC3 ?) + +- use non zero motion vectors. + +- test fifo & master handling + +- deny & allow + password. + +- Improve psycho acoustic model for AC3 & mpeg audio. + +- Improve the bit rate control for video codecs. diff --git a/doc/ffmpeg.txt b/doc/ffmpeg.txt new file mode 100644 index 0000000000..b7fffcdd29 --- /dev/null +++ b/doc/ffmpeg.txt @@ -0,0 +1,62 @@ +* ffmpeg can use YUV files as input : + + ffmpeg /tmp/out.mpg /tmp/test + +If will use the files: +/tmp/test0.Y, /tmp/test0.U, /tmp/test0.V, +/tmp/test1.Y, /tmp/test1.U, /tmp/test1.V, etc... + +The Y files use twice the resolution of the U and V files. They are +raw files, without header. They can be generated by all decent video +decoders. + +* ffmpeg can use a video4linux compatible video source : + + ffmpeg /tmp/out.mpg + + Note that you must activate the right video source and channel + before launching ffmpeg. You can use any TV viewer such as xawtv by + Gerd Knorr which I find very good. + +* There are some importants options to know: + +-s size set frame size [160x128] +-f format set encoding format [mpeg1] +-r fps set frame rate [25] +-b bitrate set the bitrate in kbit/s [100] +-t time set recording time in seconds [10.0] + +The frame size can also be: cif, qcif, sqcif and 4cif. +The encoding format can be mpeg1, h263 or rv10. + +Advanced options are: + +-d device set video4linux device name +-g gop_size set the group of picture size. + An 'intra' frame is generated every gop_size frames. +-i use only intra images (speed up compression, but lower quality). +-c comment set the comment string. + +Comment strings are only used for Real Video(tm) encoding. Tags are +used in the comment string. A typical comment string is: + +"+title=Test Video +author=FFMpeg +copyright=Free +comment=Generated by FFMpeg 1.0" + +The output file can be "-" to output to a pipe. This is only possible +with mpeg1 and h263 formats. + +* Tips: + +- For low bit rate application, use a low frame rate and a small gop + size. This is especially true for real video where the Linux player + does not seem to be very fast, so it can miss frames. An example is: + + ffmpeg -g 3 -r 3 -t 10 -b 50 -s qcif -f rv10 /tmp/b.rm + +- The parameter 'q' which is displayed while encoding is the current + quantizer. The value of 1 indicates that a very good quality could + be achieved. The value of 31 indicates the worst quality. If q=31 + too often, it means that the encoder cannot compress enough to meet + your bit rate. You must either increase the bit rate, decrease the + frame rate or decrease the frame size. + diff --git a/doc/ffserver.conf b/doc/ffserver.conf new file mode 100644 index 0000000000..8b5d926a12 --- /dev/null +++ b/doc/ffserver.conf @@ -0,0 +1,214 @@ +# Port on which the server is listening. You must select a different +# port from your standard http web server if it is running on the same +# computer. + +Port 8080 + +# Address on which the server is bound. Only useful if you have +# several network interfaces. + +BindAddress 0.0.0.0 + +# Host and port of the master server if you which that this server +# duplicates another existing server. Otherwise, the server does the +# audio/video grab itself. See the following options for the grab parameters + +#MasterServer http://localhost:80/index.html + +# Grab parameters + +#AudioDevice /dev/dsp +#VideoDevice /dev/video + +# Number of simultaneous requests that can be handled. Since FFServer +# is very fast, this limit is determined mainly by your Internet +# connection speed. + +MaxClients 1000 + +# Access Log file (uses standard Apache log file format) +# '-' is the standard output + +CustomLog - + +################################################################## +# Now you can define each stream which will be generated from the +# original audio and video stream. Each format has a filename (here +# 'test128.mpg'). FFServer will send this stream when answering a +# request containing this filename. + +<Stream test1.mpg> + +# Format of the stream : you can choose among: +# mpeg1 : MPEG1 multiplexed video and audio +# mpeg1video : only MPEG1 video +# mp2 : MPEG audio layer 2 +# mp3 : MPEG audio layer 3 (currently sent as layer 2) +# rm : Real Networks compatible stream. Multiplexed audio and video. +# ra : Real Networks compatible stream. Audio only. +# mpjpeg : Multipart JPEG (works with Netscape without any plugin) +# jpeg : Generate a single JPEG image. +# asf : ASF compatible stream (Windows Media Player format) +# swf : Macromedia flash(tm) compatible stream +# master : special ffmpeg stream used to duplicate a server + +Format mpeg1 + +# Bitrate for the audio stream. Codecs usually support only a few different bitrates. + +AudioBitRate 32 + +# Number of audio channels : 1 = mono, 2 = stereo + +AudioChannels 1 + +# Sampling frequency for audio. When using low bitrates, you should +# lower this frequency to 22050 or 11025. The supported frequencies +# depend on the selected audio codec. + +AudioSampleRate 44100 + +# Bitrate for the video stream. +VideoBitRate 64 + +# Number of frames per second +VideoFrameRate 3 + +# Size of the video frame : WxH +# W : width, H : height +# The following abbreviation are defined : sqcif, qcif, cif, 4cif +#VideoSize 352x240 + +# transmit only intra frames (useful for low bitrates) +VideoIntraOnly + +# If non intra only, an intra frame is transmitted every VideoGopSize +# frames Video synchronization can only begin at an I frames. +#VideoGopSize 12 + +</Stream> + +# second mpeg stream with high frame rate + +<Stream test2.mpg> +Format mpeg1video +VideoBitRate 128 +VideoFrameRate 25 +#VideoSize 352x240 +VideoGopSize 25 +</Stream> + +################################################################## +# Another stream : used to download data to another server which +# duplicates this one + +<Stream master> + +Format master + +</Stream> + +################################################################## +# Another stream : Real with audio only at 32 kbits + +<Stream test.ra> + +Format ra +AudioBitRate 32 + +</Stream> + +################################################################## +# Another stream : Real with audio and video at 64 kbits + +<Stream test.rm> + +Format rm + +AudioBitRate 32 +VideoBitRate 20 +VideoFrameRate 2 +VideoIntraOnly + +</Stream> + +################################################################## +# Another stream : Mpeg audio layer 2 at 64 kbits. + +<Stream test.mp2> + +Format mp2 +AudioBitRate 64 +AudioSampleRate 44100 + +</Stream> + +<Stream test1.mp2> + +Format mp2 +AudioBitRate 32 +AudioSampleRate 16000 + +</Stream> + +################################################################## +# Another stream : Multipart JPEG + +<Stream test.mjpg> + +Format mpjpeg + +VideoFrameRate 2 +VideoIntraOnly + +</Stream> + +################################################################## +# Another stream : Multipart JPEG + +<Stream test.jpg> + +Format jpeg + +# the parameters are choose here to take the same output as the +# Multipart JPEG one. +VideoFrameRate 2 +VideoIntraOnly + +</Stream> + +################################################################## +# Another stream : Flash + +<Stream test.swf> + +Format swf + +VideoFrameRate 2 +VideoIntraOnly + +</Stream> + + +################################################################## +# Another stream : ASF compatible + +<Stream test.asf> + +Format asf + +AudioBitRate 64 +AudioSampleRate 44100 +VideoFrameRate 2 +VideoIntraOnly + +</Stream> + +################################################################## +# Another stream : server status + +<Stream stat.html> + +Format status + +</Stream> diff --git a/ffmpeg.c b/ffmpeg.c new file mode 100644 index 0000000000..57cdd07348 --- /dev/null +++ b/ffmpeg.c @@ -0,0 +1,717 @@ +/* + * Basic user interface for ffmpeg system + * Copyright (c) 2000 Gerard Lantau + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <linux/videodev.h> +#include <linux/soundcard.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> + +#include "mpegenc.h" +#include "mpegvideo.h" + +static AVFormat *file_format; +static int frame_width = 160; +static int frame_height = 128; +static int frame_rate = 25; +static int bit_rate = 200000; +static int video_disable = 0; + +static const char *video_filename, *audio_filename; +static float recording_time = 10.0; +static int nb_frames; +static int gop_size = 12; +static int intra_only = 0; +static int audio_freq = 44100; +static int audio_bit_rate = 64000; +static int audio_disable = 0; +static int audio_channels = 1; + +static long long time_start; + + +static int file_read_picture(AVEncodeContext *s, + UINT8 *picture[3], + int width, int height, + int picture_number) +{ + FILE *f; + char buf[1024]; + static int init = 0; + static UINT8 *pict[3]; + if (!init) { + pict[0] = malloc(width * height); + pict[1] = malloc(width * height / 4); + pict[2] = malloc(width * height / 4); + init = 1; + } + + picture[0] = pict[0]; + picture[1] = pict[1]; + picture[2] = pict[2]; + + sprintf(buf, "%s%d.Y", video_filename, picture_number); + f=fopen(buf, "r"); + if (!f) { + return -1; + } + + fread(picture[0], 1, width * height, f); + fclose(f); + + sprintf(buf, "%s%d.U", video_filename, picture_number); + f=fopen(buf, "r"); + if (!f) { + perror(buf); + exit(1); + } + fread(picture[1], 1, width * height / 4, f); + fclose(f); + + sprintf(buf, "%s%d.V", video_filename, picture_number); + f=fopen(buf, "r"); + if (!f) { + perror(buf); + exit(1); + } + fread(picture[2], 1, width * height / 4, f); + fclose(f); + return 0; +} + +static void display_stats(AVEncodeContext *video_ctx, + AVEncodeContext *audio_ctx, + int batch_mode, int the_end) +{ + if (video_ctx && + ((video_ctx->frame_number % video_ctx->rate) == 0 || + the_end)) { + float ti; + + if (batch_mode) { + ti = (float)video_ctx->frame_number / video_ctx->rate; + } else { + ti = (gettime() - time_start) / 1000000.0; + if (ti < 0.1) + ti = 0.1; + } + + fprintf(stderr, + "frame=%5d size=%8dkB time=%0.1f fps=%4.1f bitrate=%6.1fkbits/s q=%2d\r", + video_ctx->frame_number, + data_out_size / 1024, + ti, + video_ctx->frame_number / ti, + (data_out_size * 8 / ti / 1000), + ((MpegEncContext *)video_ctx->priv_data)->qscale); + if (the_end) { + fprintf(stderr,"\n"); + } + fflush(stderr); + } +#if 0 + if (the_end && batch_mode && audio_ctx) { + duration = (gettime() - ti) / 1000000.0; + factor = 0; + if (ti > 0) { + factor = (float)nb_samples / s->sample_rate / duration; + } + fprintf(stderr, "%0.1f seconds compressed in %0.1f seconds (speed factor: %0.1f)\n", + (float)nb_samples / s->sample_rate, + duration, + factor); + } +#endif +} + +void raw_write_data(void *opaque, + unsigned char *buf, int size) +{ + FILE *outfile = opaque; + fwrite(buf, 1, size, outfile); + data_out_size += size; +} + +int raw_seek(void *opaque, long long offset, int whence) +{ + FILE *outfile = opaque; + fseek(outfile, offset, whence); + return 0; +} + +static void av_encode(AVFormatContext *ctx, + const char *video_filename, + const char *audio_filename) +{ + UINT8 audio_buffer[4096]; + UINT8 video_buffer[128*1024]; + char buf[256]; + short *samples; + int ret; + int audio_fd; + FILE *infile; + int sample_count; + int batch_mode; + AVEncodeContext *audio_enc, *video_enc; + int frame_size, frame_bytes; + AVEncoder *audio_encoder, *video_encoder; + UINT8 *picture[3]; + + /* audio */ + audio_enc = ctx->audio_enc; + sample_count = 0; + infile = NULL; + frame_size = 0; + samples = NULL; + audio_fd = -1; + frame_bytes = 0; + batch_mode = 0; + if (audio_filename || + video_filename) + batch_mode = 1; + + if (audio_enc) { + if (batch_mode) { + if (!audio_filename) { + fprintf(stderr, "Must give audio input file\n"); + exit(1); + } + infile = fopen(audio_filename, "r"); + if (!infile) { + fprintf(stderr, "Could not open '%s'\n", audio_filename); + exit(1); + } + audio_fd = -1; + } else { + audio_fd = audio_open(audio_enc->rate, audio_enc->channels); + if (audio_fd < 0) { + fprintf(stderr, "Could not open audio device\n"); + exit(1); + } + } + + audio_encoder = avencoder_find(ctx->format->audio_codec); + if (avencoder_open(audio_enc, audio_encoder) < 0) { + fprintf(stderr, "Audio encoder: incorrect audio frequency or bitrate\n"); + exit(1); + } + avencoder_string(buf, sizeof(buf), audio_enc); + fprintf(stderr, " %s\n", buf); + + frame_size = audio_enc->frame_size; + + frame_bytes = frame_size * 2 * audio_enc->channels; + samples = malloc(frame_bytes); + } + + /* video */ + video_enc = ctx->video_enc; + if (video_enc) { + if (batch_mode) { + if (!video_filename) { + fprintf(stderr, "Must give video input file\n"); + exit(1); + } + } else { + ret = v4l_init(video_enc->rate, video_enc->width, video_enc->height); + if (ret < 0) { + fprintf(stderr,"Could not init video 4 linux capture\n"); + exit(1); + } + } + + video_encoder = avencoder_find(ctx->format->video_codec); + if (avencoder_open(video_enc, video_encoder) < 0) { + fprintf(stderr, "Error while initializing video codec\n"); + exit(1); + } + + avencoder_string(buf, sizeof(buf), video_enc); + fprintf(stderr, " %s\n", buf); + } + + ctx->format->write_header(ctx); + time_start = gettime(); + + for(;;) { + /* read & compression audio frames */ + if (audio_enc) { + if (!batch_mode) { + for(;;) { + ret = read(audio_fd, samples, frame_bytes); + if (ret != frame_bytes) + break; + ret = avencoder_encode(audio_enc, + audio_buffer, sizeof(audio_buffer), samples); + ctx->format->write_audio_frame(ctx, audio_buffer, ret); + } + } else { + if (video_enc) + sample_count += audio_enc->rate / video_enc->rate; + else + sample_count += frame_size; + while (sample_count > frame_size) { + if (fread(samples, 1, frame_bytes, infile) == 0) + goto the_end; + + ret = avencoder_encode(audio_enc, + audio_buffer, sizeof(audio_buffer), samples); + ctx->format->write_audio_frame(ctx, audio_buffer, ret); + + sample_count -= frame_size; + } + } + } + + if (video_enc) { + /* read video image */ + if (batch_mode) { + ret = file_read_picture (video_enc, picture, + video_enc->width, video_enc->height, + video_enc->frame_number); + } else { + ret = v4l_read_picture (picture, + video_enc->width, video_enc->height, + video_enc->frame_number); + } + if (ret < 0) + break; + ret = avencoder_encode(video_enc, video_buffer, sizeof(video_buffer), picture); + ctx->format->write_video_picture(ctx, video_buffer, ret); + } + + display_stats(video_enc, NULL, batch_mode, 0); + if (video_enc && video_enc->frame_number >= nb_frames) + break; + } + the_end: + display_stats(video_enc, NULL, batch_mode, 1); + + if (video_enc) + avencoder_close(video_enc); + + if (audio_enc) + avencoder_close(audio_enc); + + ctx->format->write_trailer(ctx); + + if (!infile) { + close(audio_fd); + } else { + fclose(infile); + } +} + +typedef struct { + const char *str; + int width, height; +} SizeEntry; + +SizeEntry sizes[] = { + { "sqcif", 128, 96 }, + { "qcif", 176, 144 }, + { "cif", 352, 288 }, + { "4cif", 704, 576 }, +}; + +enum { + OPT_AR=256, + OPT_AB, + OPT_AN, + OPT_AC, + OPT_VN, +}; + +struct option long_options[] = +{ + { "ar", required_argument, NULL, OPT_AR }, + { "ab", required_argument, NULL, OPT_AB }, + { "an", no_argument, NULL, OPT_AN }, + { "ac", required_argument, NULL, OPT_AC }, + { "vn", no_argument, NULL, OPT_VN }, +}; + +enum { + OUT_FILE, + OUT_PIPE, + OUT_UDP, +}; + + +void help(void) +{ + AVFormat *f; + + printf("ffmpeg version 1.0, Copyright (c) 2000 Gerard Lantau\n" + "usage: ffmpeg [options] outfile [video_infile] [audio_infile]\n" + "Hyper fast MPEG1 video/H263/RV and AC3/MPEG audio layer 2 encoder\n" + "\n" + "Main options are:\n" + "\n" + "-L print the LICENSE\n" + "-s size set frame size [%dx%d]\n" + "-f format set encoding format [%s]\n" + "-r fps set frame rate [%d]\n" + "-b bitrate set the total bitrate in kbit/s [%d]\n" + "-t time set recording time in seconds [%0.1f]\n" + "-ar freq set the audio sampling freq [%d]\n" + "-ab bitrate set the audio bitrate in kbit/s [%d]\n" + "-ac channels set the number of audio channels [%d]\n" + "-an disable audio recording [%s]\n" + "-vn disable video recording [%s]\n" + "\n" + "Frame sizes abbreviations: sqcif qcif cif 4cif\n", + frame_width, frame_height, + file_format->name, + frame_rate, + bit_rate / 1000, + recording_time, + audio_freq, + audio_bit_rate / 1000, + audio_channels, + audio_disable ? "yes" : "no", + video_disable ? "yes" : "no"); + + printf("Encoding video formats:"); + for(f = first_format; f != NULL; f = f->next) + printf(" %s", f->name); + printf("\n"); + + printf("outfile can be a file name, - (pipe) or 'udp:host:port'\n" + "\n" + "Advanced options are:\n" + "-d device set video4linux device name\n" + "-g gop_size set the group of picture size [%d]\n" + "-i use only intra frames [%s]\n" + "-c comment set the comment string\n" + "\n", + gop_size, + intra_only ? "yes" : "no"); +} + +void licence(void) +{ + printf( + "ffmpeg version 1.0\n" + "Copyright (c) 2000 Gerard Lantau\n" + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n" + ); +} + +static unsigned char output_buffer[32768]; + +int main(int argc, char **argv) +{ + AVEncodeContext video_enc1, *video_enc = &video_enc1; + AVEncodeContext audio_enc1, *audio_enc = &audio_enc1; + UDPContext udp_ctx1, *udp_ctx = &udp_ctx1; + AVFormatContext av_ctx1, *av_ctx = &av_ctx1; + FILE *outfile; + int i, c; + char *filename; + int output_type; + int use_video, use_audio; + + register_avencoder(&ac3_encoder); + register_avencoder(&mp2_encoder); + register_avencoder(&mpeg1video_encoder); + register_avencoder(&h263_encoder); + register_avencoder(&rv10_encoder); + register_avencoder(&mjpeg_encoder); + + register_avformat(&mp2_format); + register_avformat(&ac3_format); + register_avformat(&mpeg1video_format); + register_avformat(&h263_format); + register_avformat(&mpeg_mux_format); + register_avformat(&ra_format); + register_avformat(&rm_format); + register_avformat(&asf_format); + register_avformat(&mpjpeg_format); + register_avformat(&swf_format); + + file_format = NULL; + + for(;;) { + c = getopt_long_only(argc, argv, "s:f:r:b:t:hd:g:ic:L", + long_options, NULL); + if (c == -1) + break; + switch(c) { + case 'L': + licence(); + exit(1); + case 'h': + help(); + exit(1); + case 's': + { + int n = sizeof(sizes) / sizeof(SizeEntry); + const char *p; + + for(i=0;i<n;i++) { + if (!strcmp(sizes[i].str, optarg)) { + frame_width = sizes[i].width; + frame_height = sizes[i].height; + break; + } + } + if (i == n) { + p = optarg; + frame_width = strtol(p, (char **)&p, 10); + if (*p) + p++; + frame_height = strtol(p, (char **)&p, 10); + } + } + break; + case 'f': + { + AVFormat *f; + f = first_format; + while (f != NULL && strcmp(f->name, optarg) != 0) f = f->next; + if (f == NULL) { + fprintf(stderr, "Invalid format: %s\n", optarg); + exit(1); + } + file_format = f; + } + break; + case 'r': + { + frame_rate = atoi(optarg); + } + break; + case 'b': + { + bit_rate = atoi(optarg) * 1000; + } + break; + case 't': + { + recording_time = atof(optarg); + break; + } + /* audio specific */ + case OPT_AR: + { + audio_freq = atoi(optarg); + break; + } + case OPT_AB: + { + audio_bit_rate = atoi(optarg) * 1000; + break; + } + case OPT_AN: + audio_disable = 1; + break; + case OPT_VN: + video_disable = 1; + break; + case OPT_AC: + { + audio_channels = atoi(optarg); + if (audio_channels != 1 && + audio_channels != 2) { + fprintf(stderr, "Incorrect number of channels: %d\n", audio_channels); + exit(1); + } + } + break; + /* advanced options */ + case 'd': + v4l_device = optarg; + break; + case 'g': + gop_size = atoi(optarg); + break; + case 'i': + intra_only = 1; + break; + case 'c': + comment_string = optarg; + break; + default: + exit(2); + } + } + + if (optind >= argc) { + help(); + exit(1); + } + + filename = argv[optind++]; + video_filename = NULL; + audio_filename = NULL; + + /* auto detect format */ + if (file_format == NULL) + file_format = guess_format(NULL, filename, NULL); + + if (file_format == NULL) + file_format = &mpeg_mux_format; + + /* check parameters */ + if (frame_width <= 0 || frame_height <= 0) { + fprintf(stderr, "Incorrect frame size\n"); + exit(1); + } + if ((frame_width % 16) != 0 || (frame_height % 16) != 0) { + fprintf(stderr, "Frame size must be a multiple of 16\n"); + exit(1); + } + + if (bit_rate < 5000 || bit_rate >= 10000000) { + fprintf(stderr, "Invalid bit rate\n"); + exit(1); + } + + if (frame_rate < 1 || frame_rate >= 60) { + fprintf(stderr, "Invalid frame rate\n"); + exit(1); + } + + nb_frames = (int)(recording_time * frame_rate); + if (nb_frames < 1) { + fprintf(stderr, "Invalid recording time\n"); + exit(1); + } + + use_video = file_format->video_codec != CODEC_ID_NONE; + use_audio = file_format->audio_codec != CODEC_ID_NONE; + if (audio_disable) { + use_audio = 0; + } + if (video_disable) { + use_video = 0; + } + + if (use_video == 0 && use_audio == 0) { + fprintf(stderr, "No audio or video selected\n"); + exit(1); + } + + fprintf(stderr, "Recording: %s, %0.1f seconds\n", + file_format->name, + recording_time); + + /* open output media */ + + if (strstart(filename, "udp:", NULL)) { + output_type = OUT_UDP; + outfile = NULL; + memset(udp_ctx, 0, sizeof(*udp_ctx)); + if (udp_tx_open(udp_ctx, filename, 0) < 0) { + fprintf(stderr, "Could not open UDP socket\n"); + exit(1); + } + } else if (!strcmp(filename, "-")) { + output_type = OUT_PIPE; + outfile = stdout; + } else { + output_type = OUT_FILE; + outfile = fopen(filename, "w"); + if (!outfile) { + perror(filename); + exit(1); + } + } + + av_ctx->video_enc = NULL; + av_ctx->audio_enc = NULL; + + if (output_type == OUT_UDP) { + init_put_byte(&av_ctx->pb, output_buffer, sizeof(output_buffer), + udp_ctx, udp_write_data, NULL); + } else { + init_put_byte(&av_ctx->pb, output_buffer, sizeof(output_buffer), + outfile, raw_write_data, raw_seek); + } + + if (use_video) { + if (optind < argc) { + video_filename = argv[optind++]; + } + /* init mpeg video encoding context */ + memset(video_enc, 0, sizeof(*video_enc)); + video_enc->bit_rate = bit_rate; + video_enc->rate = frame_rate; + + video_enc->width = frame_width; + video_enc->height = frame_height; + if (!intra_only) + video_enc->gop_size = gop_size; + else + video_enc->gop_size = 0; + + av_ctx->video_enc = video_enc; + av_ctx->format = file_format; + } + + if (use_audio) { + if (optind < argc) { + audio_filename = argv[optind++]; + } + audio_enc->bit_rate = audio_bit_rate; + audio_enc->rate = audio_freq; + audio_enc->channels = audio_channels; + av_ctx->audio_enc = audio_enc; + } + av_ctx->format = file_format; + av_ctx->is_streamed = 0; + + av_encode(av_ctx, video_filename, audio_filename); + + /* close output media */ + + switch(output_type) { + case OUT_FILE: + fclose(outfile); + break; + case OUT_PIPE: + break; + case OUT_UDP: + udp_tx_close(udp_ctx); + break; + } + fprintf(stderr, "\n"); + + return 0; +} + diff --git a/ffserver.c b/ffserver.c new file mode 100644 index 0000000000..4bece8c455 --- /dev/null +++ b/ffserver.c @@ -0,0 +1,1642 @@ +/* + * Multiple format streaming server + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <linux/videodev.h> +#include <linux/soundcard.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <ctype.h> +#include <pthread.h> + +#include "mpegenc.h" + +/* maximum number of simultaneous HTTP connections */ +#define HTTP_MAX_CONNECTIONS 2000 + +enum HTTPState { + HTTPSTATE_WAIT_REQUEST, + HTTPSTATE_SEND_HEADER, + HTTPSTATE_SEND_DATA_HEADER, + HTTPSTATE_SEND_DATA, + HTTPSTATE_SEND_DATA_TRAILER, +}; + +enum MasterState { + MASTERSTATE_RECEIVE_HEADER, + MASTERSTATE_RECEIVE_DATA, +}; + +#define IOBUFFER_MAX_SIZE 16384 +#define FIFO_MAX_SIZE (1024*1024) + +/* coef for exponential mean for bitrate estimation in statistics */ +#define AVG_COEF 0.9 + +/* timeouts are in ms */ +#define REQUEST_TIMEOUT (15 * 1000) +#define SYNC_TIMEOUT (10 * 1000) +#define MASTER_CONNECT_TIMEOUT (10 * 1000) + +typedef struct HTTPContext { + enum HTTPState state; + int fd; /* socket file descriptor */ + struct sockaddr_in from_addr; /* origin */ + struct pollfd *poll_entry; /* used when polling */ + long timeout; + UINT8 buffer[IOBUFFER_MAX_SIZE]; + UINT8 *buffer_ptr, *buffer_end; + int http_error; + struct HTTPContext *next; + UINT8 *rptr; /* read pointer in the fifo */ + int got_key_frame[2]; /* for each type */ + long long data_count; + long long last_http_fifo_write_count; /* used to monitor overflow in the fifo */ + /* format handling */ + struct FFStream *stream; + AVFormatContext fmt_ctx; + int last_packet_sent; /* true if last data packet was sent */ +} HTTPContext; + +/* each generated stream is described here */ +enum StreamType { + STREAM_TYPE_LIVE, + STREAM_TYPE_MASTER, + STREAM_TYPE_STATUS, +}; + +typedef struct FFStream { + enum StreamType stream_type; + char filename[1024]; + AVFormat *fmt; + AVEncodeContext *audio_enc; + AVEncodeContext *video_enc; + struct FFStream *next; +} FFStream; + +typedef struct FifoBuffer { + UINT8 *buffer; + UINT8 *rptr, *wptr, *end; +} FifoBuffer; + +/* each codec is here */ +typedef struct FFCodec { + struct FFCodec *next; + FifoBuffer fifo; /* for compression: one audio fifo per codec */ + ReSampleContext resample; /* for audio resampling */ + long long data_count; + float avg_frame_size; /* frame size averraged over last frames with exponential mean */ + AVEncodeContext enc; +} FFCodec; + +/* packet header */ +typedef struct { + UINT8 codec_type; + UINT8 codec_id; + UINT8 data[4]; + UINT16 bit_rate; + UINT16 payload_size; +} PacketHeader; + +struct sockaddr_in my_addr; +char logfilename[1024]; +HTTPContext *first_http_ctx; +FFStream *first_stream; +FFCodec *first_codec; + +/* master state */ +char master_url[1024]; +enum MasterState master_state; +UINT8 *master_wptr; +int master_count; + +long long http_fifo_write_count; +static FifoBuffer http_fifo; + +static int handle_http(HTTPContext *c, long cur_time); +static int http_parse_request(HTTPContext *c); +static int http_send_data(HTTPContext *c); +static int master_receive(int fd); +static void compute_stats(HTTPContext *c); + +int nb_max_connections; +int nb_connections; + +/* fifo handling */ +int fifo_init(FifoBuffer *f, int size) +{ + f->buffer = malloc(size); + if (!f->buffer) + return -1; + f->end = f->buffer + size; + f->wptr = f->rptr = f->buffer; + return 0; +} + +static int fifo_size(FifoBuffer *f, UINT8 *rptr) +{ + int size; + + if (f->wptr >= rptr) { + size = f->wptr - rptr; + } else { + size = (f->end - rptr) + (f->wptr - f->buffer); + } + return size; +} + +/* get data from the fifo (return -1 if not enough data) */ +static int fifo_read(FifoBuffer *f, UINT8 *buf, int buf_size, UINT8 **rptr_ptr) +{ + UINT8 *rptr = *rptr_ptr; + int size, len; + + if (f->wptr >= rptr) { + size = f->wptr - rptr; + } else { + size = (f->end - rptr) + (f->wptr - f->buffer); + } + + if (size < buf_size) + return -1; + while (buf_size > 0) { + len = f->end - rptr; + if (len > buf_size) + len = buf_size; + memcpy(buf, rptr, len); + buf += len; + rptr += len; + if (rptr >= f->end) + rptr = f->buffer; + buf_size -= len; + } + *rptr_ptr = rptr; + return 0; +} + +static void fifo_write(FifoBuffer *f, UINT8 *buf, int size, UINT8 **wptr_ptr) +{ + int len; + UINT8 *wptr; + wptr = *wptr_ptr; + while (size > 0) { + len = f->end - wptr; + if (len > size) + len = size; + memcpy(wptr, buf, len); + wptr += len; + if (wptr >= f->end) + wptr = f->buffer; + buf += len; + size -= len; + } + *wptr_ptr = wptr; +} + +static long gettime_ms(void) +{ + struct timeval tv; + + gettimeofday(&tv,NULL); + return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000); +} + +static FILE *logfile = NULL; + +static void http_log(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + if (logfile) + vfprintf(logfile, fmt, ap); + va_end(ap); +} + + +/* connect to url 'url' and return the connected socket ready to read data */ +static int url_get(const char *url) +{ + struct sockaddr_in dest_addr; + struct hostent *h; + int s, port, size, line_size, len; + char hostname[1024], *q; + const char *p, *path; + char req[1024]; + unsigned char ch; + + if (!strstart(url, "http://", &p)) + return -1; + q = hostname; + while (*p != ':' && *p != '\0' && *p != '/') { + if ((q - hostname) < (sizeof(hostname) - 1)) + *q++ = *p; + p++; + } + port = 80; + if (*p == ':') { + p++; + port = strtol(p, (char **)&p, 10); + } + path = p; + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + + if (!inet_aton(hostname, &dest_addr.sin_addr)) { + if ((h = gethostbyname(hostname)) == NULL) + return -1; + memcpy(&dest_addr.sin_addr, h->h_addr, sizeof(dest_addr.sin_addr)); + } + + s=socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) + return -1; + + if (connect(s, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) < 0) { + fail: + close(s); + return -1; + } + + /* send http request */ + snprintf(req, sizeof(req), "GET %s HTTP/1.0\r\n\r\n", path); + p = req; + size = strlen(req); + while (size > 0) { + len = write(s, p, size); + if (len == -1) { + if (errno != EAGAIN && errno != EINTR) + goto fail; + } else { + size -= len; + p += len; + } + } + + /* receive answer */ + line_size = 0; + for(;;) { + len = read(s, &ch, 1); + if (len == -1) { + if (errno != EAGAIN && errno != EINTR) + goto fail; + } else if (len == 0) { + goto fail; + } else { + if (ch == '\n') { + if (line_size == 0) + break; + line_size = 0; + } else if (ch != '\r') { + line_size++; + } + } + } + + return s; +} + +/* Each request is served by reading the input FIFO and by adding the + right format headers */ +static int http_server(struct sockaddr_in my_addr) +{ + int server_fd, tmp, ret; + struct sockaddr_in from_addr; + struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry; + HTTPContext *c, **cp; + long cur_time; + int master_fd, master_timeout; + + /* will try to connect to master as soon as possible */ + master_fd = -1; + master_timeout = gettime_ms(); + + server_fd = socket(AF_INET,SOCK_STREAM,0); + if (server_fd < 0) { + perror ("socket"); + return -1; + } + + tmp = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)); + + if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) { + perror ("bind"); + close(server_fd); + return -1; + } + + if (listen (server_fd, 5) < 0) { + perror ("listen"); + close(server_fd); + return -1; + } + + http_log("ffserver started.\n"); + + fcntl(server_fd, F_SETFL, O_NONBLOCK); + first_http_ctx = NULL; + nb_connections = 0; + first_http_ctx = NULL; + for(;;) { + poll_entry = poll_table; + poll_entry->fd = server_fd; + poll_entry->events = POLLIN; + poll_entry++; + + if (master_fd >= 0) { + poll_entry->fd = master_fd; + poll_entry->events = POLLIN; + poll_entry++; + } + + /* wait for events on each HTTP handle */ + c = first_http_ctx; + while (c != NULL) { + int fd; + fd = c->fd; + switch(c->state) { + case HTTPSTATE_WAIT_REQUEST: + c->poll_entry = poll_entry; + poll_entry->fd = fd; + poll_entry->events = POLLIN; + poll_entry++; + break; + case HTTPSTATE_SEND_HEADER: + case HTTPSTATE_SEND_DATA_HEADER: + case HTTPSTATE_SEND_DATA: + case HTTPSTATE_SEND_DATA_TRAILER: + c->poll_entry = poll_entry; + poll_entry->fd = fd; + poll_entry->events = POLLOUT; + poll_entry++; + break; + default: + c->poll_entry = NULL; + break; + } + c = c->next; + } + + /* wait for an event on one connection. We poll at least every + second to handle timeouts */ + do { + ret = poll(poll_table, poll_entry - poll_table, 1000); + } while (ret == -1); + + cur_time = gettime_ms(); + + /* now handle the events */ + + cp = &first_http_ctx; + while ((*cp) != NULL) { + c = *cp; + if (handle_http (c, cur_time) < 0) { + /* close and free the connection */ + close(c->fd); + *cp = c->next; + free(c); + nb_connections--; + } else { + cp = &c->next; + } + } + + /* new connection request ? */ + poll_entry = poll_table; + if (poll_entry->revents & POLLIN) { + int fd, len; + + len = sizeof(from_addr); + fd = accept(server_fd, &from_addr, &len); + if (fd >= 0) { + fcntl(fd, F_SETFL, O_NONBLOCK); + /* XXX: should output a warning page when comming + close to the connection limit */ + if (nb_connections >= nb_max_connections) { + close(fd); + } else { + /* add a new connection */ + c = malloc(sizeof(HTTPContext)); + memset(c, 0, sizeof(*c)); + c->next = first_http_ctx; + first_http_ctx = c; + c->fd = fd; + c->poll_entry = NULL; + c->from_addr = from_addr; + c->state = HTTPSTATE_WAIT_REQUEST; + c->buffer_ptr = c->buffer; + c->buffer_end = c->buffer + IOBUFFER_MAX_SIZE; + c->timeout = cur_time + REQUEST_TIMEOUT; + nb_connections++; + } + } + } + poll_entry++; + + /* master events */ + if (poll_entry->revents & POLLIN) { + if (master_receive(master_fd) < 0) { + close(master_fd); + master_fd = -1; + } + } + + /* master (re)connection handling */ + if (master_url[0] != '\0' && + master_fd < 0 && (master_timeout - cur_time) <= 0) { + master_fd = url_get(master_url); + if (master_fd < 0) { + master_timeout = gettime_ms() + MASTER_CONNECT_TIMEOUT; + http_log("Connection to master: '%s' failed\n", master_url); + } else { + fcntl(master_fd, F_SETFL, O_NONBLOCK); + master_state = MASTERSTATE_RECEIVE_HEADER; + master_count = sizeof(PacketHeader); + master_wptr = http_fifo.wptr; + } + } + } +} + +static int handle_http(HTTPContext *c, long cur_time) +{ + int len; + + switch(c->state) { + case HTTPSTATE_WAIT_REQUEST: + /* timeout ? */ + if ((c->timeout - cur_time) < 0) + return -1; + if (c->poll_entry->revents & (POLLERR | POLLHUP)) + return -1; + + /* no need to read if no events */ + if (!(c->poll_entry->revents & POLLIN)) + return 0; + /* read the data */ + len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr); + if (len < 0) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else if (len == 0) { + return -1; + } else { + /* search for end of request. XXX: not fully correct since garbage could come after the end */ + UINT8 *ptr; + c->buffer_ptr += len; + ptr = c->buffer_ptr; + if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) || + (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) { + /* request found : parse it and reply */ + if (http_parse_request(c) < 0) + return -1; + } else if (ptr >= c->buffer_end) { + /* request too long: cannot do anything */ + return -1; + } + } + break; + + case HTTPSTATE_SEND_HEADER: + if (c->poll_entry->revents & (POLLERR | POLLHUP)) + return -1; + + /* no need to read if no events */ + if (!(c->poll_entry->revents & POLLOUT)) + return 0; + len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr); + if (len < 0) { + if (errno != EAGAIN && errno != EINTR) { + /* error : close connection */ + return -1; + } + } else { + c->buffer_ptr += len; + if (c->buffer_ptr >= c->buffer_end) { + /* if error, exit */ + if (c->http_error) + return -1; + /* all the buffer was send : synchronize to the incoming stream */ + c->state = HTTPSTATE_SEND_DATA_HEADER; + c->buffer_ptr = c->buffer_end = c->buffer; + } + } + break; + + case HTTPSTATE_SEND_DATA: + case HTTPSTATE_SEND_DATA_HEADER: + case HTTPSTATE_SEND_DATA_TRAILER: + /* no need to read if no events */ + if (c->poll_entry->revents & (POLLERR | POLLHUP)) + return -1; + + if (!(c->poll_entry->revents & POLLOUT)) + return 0; + if (http_send_data(c) < 0) + return -1; + break; + default: + return -1; + } + return 0; +} + +/* parse http request and prepare header */ +static int http_parse_request(HTTPContext *c) +{ + const char *p; + char cmd[32]; + char url[1024], *q; + char protocol[32]; + char msg[1024]; + char *mime_type; + FFStream *stream; + + p = c->buffer; + q = cmd; + while (!isspace(*p) && *p != '\0') { + if ((q - cmd) < sizeof(cmd) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (strcmp(cmd, "GET")) + return -1; + + while (isspace(*p)) p++; + q = url; + while (!isspace(*p) && *p != '\0') { + if ((q - url) < sizeof(url) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + + while (isspace(*p)) p++; + q = protocol; + while (!isspace(*p) && *p != '\0') { + if ((q - protocol) < sizeof(protocol) - 1) + *q++ = *p; + p++; + } + *q = '\0'; + if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1")) + return -1; + + /* find the filename in the request */ + p = url; + if (*p == '/') + p++; + + stream = first_stream; + while (stream != NULL) { + if (!strcmp(stream->filename, p)) + break; + stream = stream->next; + } + if (stream == NULL) { + sprintf(msg, "File '%s' not found", url); + goto send_error; + } + c->stream = stream; + + /* should do it after so that the size can be computed */ + { + char buf1[32], buf2[32], *p; + time_t ti; + /* XXX: reentrant function ? */ + p = inet_ntoa(c->from_addr.sin_addr); + strcpy(buf1, p); + ti = time(NULL); + p = ctime(&ti); + strcpy(buf2, p); + p = buf2 + strlen(p) - 1; + if (*p == '\n') + *p = '\0'; + http_log("%s - - [%s] \"%s %s %s\" %d %d\n", + buf1, buf2, cmd, url, protocol, 200, 1024); + } + + if (c->stream->stream_type == STREAM_TYPE_STATUS) + goto send_stats; + + /* prepare http header */ + q = c->buffer; + q += sprintf(q, "HTTP/1.0 200 OK\r\n"); + mime_type = c->stream->fmt->mime_type; + if (!mime_type) + mime_type = "application/x-octet_stream"; + q += sprintf(q, "Content-type: %s\r\n", mime_type); + q += sprintf(q, "Pragma: no-cache\r\n"); + /* for asf, we need extra headers */ + if (!strcmp(c->stream->fmt->name,"asf")) { + q += sprintf(q, "Pragma: features=broadcast\r\n"); + } + q += sprintf(q, "\r\n"); + + /* prepare output buffer */ + c->http_error = 0; + c->buffer_ptr = c->buffer; + c->buffer_end = q; + c->state = HTTPSTATE_SEND_HEADER; + return 0; + send_error: + c->http_error = 404; + q = c->buffer; + q += sprintf(q, "HTTP/1.0 404 Not Found\r\n"); + q += sprintf(q, "Content-type: %s\r\n", "text/html"); + q += sprintf(q, "\r\n"); + q += sprintf(q, "<HTML>\n"); + q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"); + q += sprintf(q, "<BODY>%s</BODY>\n", msg); + q += sprintf(q, "</HTML>\n"); + + /* prepare output buffer */ + c->buffer_ptr = c->buffer; + c->buffer_end = q; + c->state = HTTPSTATE_SEND_HEADER; + return 0; + send_stats: + compute_stats(c); + c->http_error = 200; /* horrible : we use this value to avoid + going to the send data state */ + c->state = HTTPSTATE_SEND_HEADER; + return 0; +} + +static void compute_stats(HTTPContext *c) +{ + AVEncodeContext *enc; + HTTPContext *c1; + FFCodec *ffenc; + FFStream *stream; + float avg; + char buf[1024], *q, *p; + time_t ti; + int i; + + q = c->buffer; + q += sprintf(q, "HTTP/1.0 200 OK\r\n"); + q += sprintf(q, "Content-type: %s\r\n", "text/html"); + q += sprintf(q, "Pragma: no-cache\r\n"); + q += sprintf(q, "\r\n"); + + q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE></HEAD>\n<BODY>"); + q += sprintf(q, "<H1>FFServer Status</H1>\n"); + /* format status */ + q += sprintf(q, "<H1>Available Streams</H1>\n"); + q += sprintf(q, "<TABLE>\n"); + q += sprintf(q, "<TR><TD>Path<TD>Format<TD>Bit rate (kbits/s)<TD>Video<TD>Audio\n"); + stream = first_stream; + while (stream != NULL) { + q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ", + stream->filename, stream->filename); + switch(stream->stream_type) { + case STREAM_TYPE_LIVE: + { + int audio_bit_rate = 0; + int video_bit_rate = 0; + if (stream->audio_enc) + audio_bit_rate = stream->audio_enc->bit_rate; + if (stream->video_enc) + video_bit_rate = stream->video_enc->bit_rate; + + q += sprintf(q, "<TD> %s <TD> %d <TD> %d <TD> %d\n", + stream->fmt->name, + (audio_bit_rate + video_bit_rate) / 1000, + video_bit_rate / 1000, audio_bit_rate / 1000); + } + break; + case STREAM_TYPE_MASTER: + q += sprintf(q, "<TD> %s <TD> - <TD> - <TD> -\n", + "master"); + break; + default: + q += sprintf(q, "<TD> - <TD> - <TD> - <TD> -\n"); + break; + } + stream = stream->next; + } + q += sprintf(q, "</TABLE>\n"); + + /* codec status */ + q += sprintf(q, "<H1>Codec Status</H1>\n"); + q += sprintf(q, "<TABLE>\n"); + q += sprintf(q, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n"); + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + avencoder_string(buf, sizeof(buf), enc); + avg = ffenc->avg_frame_size * (float)enc->rate * 8.0; + if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0) + avg /= enc->frame_size; + q += sprintf(q, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n", + buf, enc->frame_number, ffenc->data_count, avg / 1000.0); + ffenc = ffenc->next; + } + q += sprintf(q, "</TABLE>\n"); + + /* exclude the stat connection */ + q += sprintf(q, "Number of connections: %d / %d<BR>\n", + nb_connections, nb_max_connections); + + /* connection status */ + q += sprintf(q, "<H1>Connection Status</H1>\n"); + q += sprintf(q, "<TABLE>\n"); + q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>Size\n"); + c1 = first_http_ctx; + i = 0; + while (c1 != NULL) { + i++; + p = inet_ntoa(c1->from_addr.sin_addr); + q += sprintf(q, "<TR><TD><B>%d</B><TD>%s <TD> %s <TD> %Ld\n", + i, c1->stream->filename, p, c1->data_count); + c1 = c1->next; + } + q += sprintf(q, "</TABLE>\n"); + + /* date */ + ti = time(NULL); + p = ctime(&ti); + q += sprintf(q, "<HR>Generated at %s", p); + q += sprintf(q, "</BODY>\n</HTML>\n"); + + c->buffer_ptr = c->buffer; + c->buffer_end = q; +} + + +static void http_write_packet(void *opaque, + unsigned char *buf, int size) +{ + HTTPContext *c = opaque; + if (size > IOBUFFER_MAX_SIZE) + abort(); + memcpy(c->buffer, buf, size); + c->buffer_ptr = c->buffer; + c->buffer_end = c->buffer + size; +} + +/* this headers are used to identify a packet for a given codec */ +void mk_header(PacketHeader *h, AVEncodeContext *c, int payload_size) +{ + h->codec_type = c->codec->type; + h->codec_id = c->codec->id; + h->bit_rate = htons(c->bit_rate / 1000); + switch(c->codec->type) { + case CODEC_TYPE_VIDEO: + h->data[0] = c->rate; + h->data[1] = c->width / 16; + h->data[2] = c->height / 16; + break; + case CODEC_TYPE_AUDIO: + h->data[0] = c->rate / 1000; + h->data[1] = c->channels; + h->data[2] = 0; + break; + } + h->data[3] = c->key_frame; + h->payload_size = htons(payload_size); +} + +int test_header(PacketHeader *h, AVEncodeContext *c) +{ + if (!c) + return 0; + + if (h->codec_type == c->codec->type && + h->codec_id == c->codec->id && + h->bit_rate == htons(c->bit_rate / 1000)) { + + switch(c->codec->type) { + case CODEC_TYPE_VIDEO: + if (h->data[0] == c->rate && + h->data[1] == (c->width / 16) && + h->data[2] == (c->height / 16)) + goto found; + break; + case CODEC_TYPE_AUDIO: + if (h->data[0] == (c->rate / 1000) && + (h->data[1] == c->channels)) + goto found; + break; + } + } + return 0; + found: + c->frame_number++; + c->key_frame = h->data[3]; + return 1; +} + +static int http_prepare_data(HTTPContext *c) +{ + PacketHeader hdr; + UINT8 *start_rptr, *payload; + int payload_size, ret; + long long fifo_total_size; + + switch(c->state) { + case HTTPSTATE_SEND_DATA_HEADER: + if (c->stream->stream_type != STREAM_TYPE_MASTER) { + memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx)); + c->fmt_ctx.format = c->stream->fmt; + if (c->fmt_ctx.format->audio_codec != CODEC_ID_NONE) { + /* create a fake new codec instance */ + c->fmt_ctx.audio_enc = malloc(sizeof(AVEncodeContext)); + memcpy(c->fmt_ctx.audio_enc, c->stream->audio_enc, + sizeof(AVEncodeContext)); + c->fmt_ctx.audio_enc->frame_number = 0; + } + if (c->fmt_ctx.format->video_codec != CODEC_ID_NONE) { + c->fmt_ctx.video_enc = malloc(sizeof(AVEncodeContext)); + memcpy(c->fmt_ctx.video_enc, c->stream->video_enc, + sizeof(AVEncodeContext)); + c->fmt_ctx.video_enc->frame_number = 0; + } + init_put_byte(&c->fmt_ctx.pb, c->buffer, IOBUFFER_MAX_SIZE, + c, http_write_packet, NULL); + c->fmt_ctx.is_streamed = 1; + c->got_key_frame[0] = 0; + c->got_key_frame[1] = 0; + /* prepare header */ + c->fmt_ctx.format->write_header(&c->fmt_ctx); + } + c->state = HTTPSTATE_SEND_DATA; + c->last_packet_sent = 0; + c->rptr = http_fifo.wptr; + c->last_http_fifo_write_count = http_fifo_write_count; + break; + case HTTPSTATE_SEND_DATA: + /* find a new packet */ + fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count; + if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) { + /* overflow : resync. We suppose that wptr is at this + point a pointer to a valid packet */ + c->rptr = http_fifo.wptr; + c->got_key_frame[0] = 0; + c->got_key_frame[1] = 0; + } + + start_rptr = c->rptr; + if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0) + return 0; + payload_size = ntohs(hdr.payload_size); + payload = malloc(payload_size); + if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) { + /* cannot read all the payload */ + free(payload); + c->rptr = start_rptr; + return 0; + } + + c->last_http_fifo_write_count = http_fifo_write_count - + fifo_size(&http_fifo, c->rptr); + + if (c->stream->stream_type != STREAM_TYPE_MASTER) { + /* test if the packet can be handled by this format */ + ret = 0; + if (test_header(&hdr, c->fmt_ctx.audio_enc)) { + /* only begin sending when got a key frame */ + if (c->fmt_ctx.audio_enc->key_frame) + c->got_key_frame[1] = 1; + if (c->got_key_frame[1]) { + ret = c->fmt_ctx.format->write_audio_frame(&c->fmt_ctx, + payload, payload_size); + } + } else if (test_header(&hdr, c->fmt_ctx.video_enc)) { + if (c->fmt_ctx.video_enc->key_frame) + c->got_key_frame[0] = 1; + if (c->got_key_frame[0]) { + ret = c->fmt_ctx.format->write_video_picture(&c->fmt_ctx, + payload, payload_size); + } + } + if (ret) { + /* must send trailer now */ + c->state = HTTPSTATE_SEND_DATA_TRAILER; + } + } else { + /* master case : send everything */ + char *q; + q = c->buffer; + memcpy(q, &hdr, sizeof(hdr)); + q += sizeof(hdr); + memcpy(q, payload, payload_size); + q += payload_size; + c->buffer_ptr = c->buffer; + c->buffer_end = q; + } + free(payload); + break; + default: + case HTTPSTATE_SEND_DATA_TRAILER: + /* last packet test ? */ + if (c->last_packet_sent) + return -1; + /* prepare header */ + c->fmt_ctx.format->write_trailer(&c->fmt_ctx); + c->last_packet_sent = 1; + break; + } + return 0; +} + + +/* should convert the format at the same time */ +static int http_send_data(HTTPContext *c) +{ + int len; + + while (c->buffer_ptr >= c->buffer_end) { + if (http_prepare_data(c) < 0) + return -1; + } + + len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr); + if (len < 0) { + if (errno != EAGAIN && errno != EINTR) { + /* error : close connection */ + return -1; + } + } else { + c->buffer_ptr += len; + c->data_count += len; + } + return 0; +} + +static int master_receive(int fd) +{ + int len, size; + FifoBuffer *f = &http_fifo; + UINT8 *rptr; + + size = f->end - f->wptr; + if (size > master_count) + size = master_count; + len = read(fd, f->wptr, size); + if (len == -1) { + if (errno != EAGAIN && errno != EINTR) + return -1; + } else if (len == 0) { + return -1; + } else { + master_wptr += len; + if (master_wptr >= f->end) + master_wptr = f->buffer; + master_count -= len; + if (master_count == 0) { + if (master_state == MASTERSTATE_RECEIVE_HEADER) { + /* XXX: use generic fifo read to extract packet header */ + rptr = master_wptr; + if (rptr == f->buffer) + rptr = f->end - 1; + else + rptr--; + master_count = *rptr; + if (rptr == f->buffer) + rptr = f->end - 1; + else + rptr--; + master_count |= *rptr << 8; + master_state = MASTERSTATE_RECEIVE_DATA; + } else { + /* update fifo wptr */ + f->wptr = master_wptr; + master_state = MASTERSTATE_RECEIVE_HEADER; + } + } + } + return 0; +} + +static void get_arg(char *buf, int buf_size, const char **pp) +{ + const char *p; + char *q; + + p = *pp; + while (isspace(*p)) p++; + q = buf; + while (!isspace(*p) && *p != '\0') { + if ((q - buf) < buf_size - 1) + *q++ = *p; + p++; + } + *q = '\0'; + *pp = p; +} + +/* add a codec and check if it does not already exists */ +AVEncodeContext *add_codec(int codec_id, + AVEncodeContext *av) +{ + AVEncoder *codec; + FFCodec *ctx, **pctx; + AVEncodeContext *av1; + + codec = avencoder_find(codec_id); + if (!codec) + return NULL; + + /* compute default parameters */ + av->codec = codec; + switch(codec->type) { + case CODEC_TYPE_AUDIO: + if (av->bit_rate == 0) + av->bit_rate = 64000; + if (av->rate == 0) + av->rate = 22050; + if (av->channels == 0) + av->channels = 1; + break; + case CODEC_TYPE_VIDEO: + if (av->bit_rate == 0) + av->bit_rate = 64000; + if (av->rate == 0) + av->rate = 5; + if (av->width == 0 || av->height == 0) { + av->width = 160; + av->height = 128; + } + break; + } + + /* find if the codec already exists */ + pctx = &first_codec; + while (*pctx != NULL) { + av1 = &(*pctx)->enc; + if (av1->codec == av->codec && + av1->bit_rate == av->bit_rate && + av1->rate == av->rate) { + + switch(av->codec->type) { + case CODEC_TYPE_AUDIO: + if (av1->channels == av->channels) + goto found; + break; + case CODEC_TYPE_VIDEO: + if (av1->width == av->width && + av1->height == av->height && + av1->gop_size == av->gop_size) + goto found; + break; + } + } + pctx = &(*pctx)->next; + } + + ctx = malloc(sizeof(FFCodec)); + if (!ctx) + return NULL; + memset(ctx, 0, sizeof(FFCodec)); + *pctx = ctx; + + memcpy(&ctx->enc, av, sizeof(AVEncodeContext)); + return &ctx->enc; + found: + ctx = *pctx; + return &ctx->enc; +} + +int parse_ffconfig(const char *filename) +{ + FILE *f; + char line[1024]; + char cmd[64]; + char arg[1024]; + const char *p; + int val, errors, line_num; + FFStream **last_stream, *stream; + AVEncodeContext audio_enc, video_enc; + + f = fopen(filename, "r"); + if (!f) { + perror(filename); + return -1; + } + + errors = 0; + line_num = 0; + first_stream = NULL; + first_codec = NULL; + last_stream = &first_stream; + stream = NULL; + for(;;) { + if (fgets(line, sizeof(line), f) == NULL) + break; + line_num++; + p = line; + while (isspace(*p)) + p++; + if (*p == '\0' || *p == '#') + continue; + + get_arg(cmd, sizeof(cmd), &p); + + if (!strcasecmp(cmd, "Port")) { + get_arg(arg, sizeof(arg), &p); + my_addr.sin_port = htons (atoi(arg)); + } else if (!strcasecmp(cmd, "BindAddress")) { + get_arg(arg, sizeof(arg), &p); + if (!inet_aton(arg, &my_addr.sin_addr)) { + fprintf(stderr, "%s:%d: Invalid IP address: %s\n", + filename, line_num, arg); + errors++; + } + } else if (!strcasecmp(cmd, "MasterServer")) { + get_arg(master_url, sizeof(master_url), &p); + if (!strstart(master_url, "http://", NULL)) { + fprintf(stderr, "%s:%d: Invalid URL for master server: %s\n", + filename, line_num, master_url); + errors++; + } + } else if (!strcasecmp(cmd, "MaxClients")) { + get_arg(arg, sizeof(arg), &p); + val = atoi(arg); + if (val < 1 || val > HTTP_MAX_CONNECTIONS) { + fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", + filename, line_num, arg); + errors++; + } else { + nb_max_connections = val; + } + } else if (!strcasecmp(cmd, "CustomLog")) { + get_arg(logfilename, sizeof(logfilename), &p); + } else if (!strcasecmp(cmd, "<Stream")) { + char *q; + if (stream) { + fprintf(stderr, "%s:%d: Already in a stream tag\n", + filename, line_num); + } else { + stream = malloc(sizeof(FFStream)); + memset(stream, 0, sizeof(FFStream)); + *last_stream = stream; + last_stream = &stream->next; + + get_arg(stream->filename, sizeof(stream->filename), &p); + q = strrchr(stream->filename, '>'); + if (*q) + *q = '\0'; + stream->fmt = guess_format(NULL, stream->filename, NULL); + memset(&audio_enc, 0, sizeof(AVEncodeContext)); + memset(&video_enc, 0, sizeof(AVEncodeContext)); + } + } else if (!strcasecmp(cmd, "Format")) { + get_arg(arg, sizeof(arg), &p); + if (!strcmp(arg, "master")) { + stream->stream_type = STREAM_TYPE_MASTER; + stream->fmt = NULL; + } else if (!strcmp(arg, "status")) { + stream->stream_type = STREAM_TYPE_STATUS; + stream->fmt = NULL; + } else { + stream->stream_type = STREAM_TYPE_LIVE; + stream->fmt = guess_format(arg, NULL, NULL); + if (!stream->fmt) { + fprintf(stderr, "%s:%d: Unknown Format: %s\n", + filename, line_num, arg); + errors++; + } + } + } else if (!strcasecmp(cmd, "AudioBitRate")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + audio_enc.bit_rate = atoi(arg) * 1000; + } + } else if (!strcasecmp(cmd, "AudioChannels")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + audio_enc.channels = atoi(arg); + } + } else if (!strcasecmp(cmd, "AudioSampleRate")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + audio_enc.rate = atoi(arg); + } + } else if (!strcasecmp(cmd, "VideoBitRate")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + video_enc.bit_rate = atoi(arg) * 1000; + } + } else if (!strcasecmp(cmd, "VideoFrameRate")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + video_enc.rate = atoi(arg); + } + } else if (!strcasecmp(cmd, "VideoGopSize")) { + get_arg(arg, sizeof(arg), &p); + if (stream) { + video_enc.gop_size = atoi(arg); + } + } else if (!strcasecmp(cmd, "VideoIntraOnly")) { + if (stream) { + video_enc.gop_size = 1; + } + } else if (!strcasecmp(cmd, "</Stream>")) { + if (!stream) { + fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n", + filename, line_num); + errors++; + } + if (stream->fmt) { + if (stream->fmt->audio_codec != CODEC_ID_NONE) { + stream->audio_enc = add_codec(stream->fmt->audio_codec, + &audio_enc); + } + + if (stream->fmt->video_codec != CODEC_ID_NONE) + stream->video_enc = add_codec(stream->fmt->video_codec, + &video_enc); + } + stream = NULL; + } else { + fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", + filename, line_num, cmd); + errors++; + } + } + + fclose(f); + if (errors) + return -1; + else + return 0; +} + + +void *http_server_thread(void *arg) +{ + http_server(my_addr); + return NULL; +} + +static void write_packet(FFCodec *ffenc, + UINT8 *buf, int size) +{ + PacketHeader hdr; + AVEncodeContext *enc = &ffenc->enc; + UINT8 *wptr; + mk_header(&hdr, enc, size); + wptr = http_fifo.wptr; + fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr); + fifo_write(&http_fifo, buf, size, &wptr); + /* atomic modification of wptr */ + http_fifo.wptr = wptr; + ffenc->data_count += size; + ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF); +} + +#define AUDIO_FIFO_SIZE 8192 + +int av_grab(void) +{ + UINT8 audio_buf[AUDIO_FIFO_SIZE/2]; + UINT8 audio_buf1[AUDIO_FIFO_SIZE/2]; + UINT8 audio_out[AUDIO_FIFO_SIZE/2]; + UINT8 video_buffer[128*1024]; + char buf[256]; + short *samples; + int ret; + int audio_fd; + FFCodec *ffenc; + AVEncodeContext *enc; + int frame_size, frame_bytes; + int use_audio, use_video; + int frame_rate, sample_rate, channels; + int width, height, frame_number; + UINT8 *picture[3]; + + use_audio = 0; + use_video = 0; + frame_rate = 0; + sample_rate = 0; + frame_size = 0; + channels = 1; + width = 0; + height = 0; + frame_number = 0; + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + avencoder_string(buf, sizeof(buf), enc); + fprintf(stderr, " %s\n", buf); + if (avencoder_open(enc, enc->codec) < 0) { + fprintf(stderr, "Incorrect encode parameters\n"); + return -1; + } + switch(enc->codec->type) { + case CODEC_TYPE_AUDIO: + use_audio = 1; + if (enc->rate > sample_rate) + sample_rate = enc->rate; + if (enc->frame_size > frame_size) + frame_size = enc->frame_size; + if (enc->channels > channels) + channels = enc->channels; + fifo_init(&ffenc->fifo, AUDIO_FIFO_SIZE); + break; + case CODEC_TYPE_VIDEO: + use_video = 1; + if (enc->rate > frame_rate) + frame_rate = enc->rate; + if (enc->width > width) + width = enc->width; + if (enc->height > height) + height = enc->height; + break; + } + ffenc = ffenc->next; + } + + /* audio */ + samples = NULL; + audio_fd = -1; + if (use_audio) { + printf("Audio sampling: %d Hz, %s\n", + sample_rate, channels == 2 ? "stereo" : "mono"); + audio_fd = audio_open(sample_rate, channels); + if (audio_fd < 0) { + fprintf(stderr, "Could not open audio device\n"); + exit(1); + } + } + + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + if (enc->codec->type == CODEC_TYPE_AUDIO && + (enc->channels != channels || + enc->rate != sample_rate)) { + audio_resample_init(&ffenc->resample, enc->channels, channels, + enc->rate, sample_rate); + } + ffenc = ffenc->next; + } + + /* video */ + if (use_video) { + printf("Video sampling: %dx%d, %d fps\n", + width, height, frame_rate); + ret = v4l_init(frame_rate, width, height); + if (ret < 0) { + fprintf(stderr,"Could not init video 4 linux capture\n"); + exit(1); + } + } + + for(;;) { + /* read & compress audio frames */ + if (use_audio) { + int ret, nb_samples, nb_samples_out; + UINT8 *buftmp; + + for(;;) { + ret = read(audio_fd, audio_buf, AUDIO_FIFO_SIZE/2); + if (ret <= 0) + break; + /* fill each codec fifo by doing the right sample + rate conversion. This is not optimal because we + do too much work, but it is easy to do */ + nb_samples = ret / (channels * 2); + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + if (enc->codec->type == CODEC_TYPE_AUDIO) { + /* rate & stereo convertion */ + if (enc->channels == channels && + enc->rate == sample_rate) { + buftmp = audio_buf; + nb_samples_out = nb_samples; + } else { + buftmp = audio_buf1; + nb_samples_out = audio_resample(&ffenc->resample, + (short *)buftmp, (short *)audio_buf, + nb_samples); + + } + fifo_write(&ffenc->fifo, buftmp, nb_samples_out * enc->channels * 2, + &ffenc->fifo.wptr); + } + ffenc = ffenc->next; + } + + /* compress as many frame as possible with each audio codec */ + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + if (enc->codec->type == CODEC_TYPE_AUDIO) { + frame_bytes = enc->frame_size * 2 * enc->channels; + + while (fifo_read(&ffenc->fifo, audio_buf, frame_bytes, &ffenc->fifo.rptr) == 0) { + ret = avencoder_encode(enc, + audio_out, sizeof(audio_out), audio_buf); + write_packet(ffenc, audio_out, ret); + } + } + ffenc = ffenc->next; + } + } + } + + if (use_video) { + ret = v4l_read_picture (picture, width, height, + frame_number); + if (ret < 0) + break; + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + if (enc->codec->type == CODEC_TYPE_VIDEO) { + int n1, n2; + /* feed each codec with its requested frame rate */ + n1 = (frame_number * enc->rate) / frame_rate; + n2 = ((frame_number + 1) * enc->rate) / frame_rate; + if (n2 > n1) { + ret = avencoder_encode(enc, video_buffer, sizeof(video_buffer), picture); + write_packet(ffenc, video_buffer, ret); + } + } + ffenc = ffenc->next; + } + frame_number++; + } + } + + ffenc = first_codec; + while (ffenc != NULL) { + enc = &ffenc->enc; + avencoder_close(enc); + ffenc = ffenc->next; + } + close(audio_fd); + return 0; +} + + +void help(void) +{ + printf("ffserver version 1.0, Copyright (c) 2000 Gerard Lantau\n" + "usage: ffserver [-L] [-h] [-f configfile]\n" + "Hyper fast multi format Audio/Video streaming server\n" + "\n" + "-L : print the LICENCE\n" + "-h : this help\n" + "-f configfile : use configfile instead of /etc/ffserver.conf\n" + ); +} + +void licence(void) +{ + printf( + "ffserver version 1.0\n" + "Copyright (c) 2000 Gerard Lantau\n" + "This program is free software; you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation; either version 2 of the License, or\n" + "(at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n" + ); +} + +int main(int argc, char **argv) +{ + pthread_t http_server_tid; + const char *config_filename; + int c; + + /* codecs */ + register_avencoder(&ac3_encoder); + register_avencoder(&mp2_encoder); + register_avencoder(&mpeg1video_encoder); + register_avencoder(&h263_encoder); + register_avencoder(&rv10_encoder); + register_avencoder(&mjpeg_encoder); + + /* audio video formats */ + register_avformat(&mp2_format); + register_avformat(&ac3_format); + register_avformat(&mpeg_mux_format); + register_avformat(&mpeg1video_format); + register_avformat(&h263_format); + register_avformat(&rm_format); + register_avformat(&ra_format); + register_avformat(&asf_format); + register_avformat(&mpjpeg_format); + register_avformat(&jpeg_format); + register_avformat(&swf_format); + + config_filename = "/etc/ffserver.conf"; + + for(;;) { + c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL); + if (c == -1) + break; + switch(c) { + case 'L': + licence(); + exit(1); + case '?': + case 'h': + help(); + exit(1); + case 'f': + config_filename = optarg; + break; + default: + exit(2); + } + } + + /* address on which the server will handle connections */ + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons (8080); + my_addr.sin_addr.s_addr = htonl (INADDR_ANY); + nb_max_connections = 5; + first_stream = NULL; + logfilename[0] = '\0'; + + if (parse_ffconfig(config_filename) < 0) { + fprintf(stderr, "Incorrect config file - exiting.\n"); + exit(1); + } + + /* open log file if needed */ + if (logfilename[0] != '\0') { + if (!strcmp(logfilename, "-")) + logfile = stdout; + else + logfile = fopen(logfilename, "w"); + } + + /* init fifo */ + http_fifo_write_count = 0; + if (fifo_init(&http_fifo, FIFO_MAX_SIZE) < 0) { + fprintf(stderr, "Could not allow receive fifo\n"); + exit(1); + } + + if (master_url[0] == '\0') { + /* no master server: we grab ourself */ + + /* launch server thread */ + if (pthread_create(&http_server_tid, NULL, + http_server_thread, NULL) != 0) { + fprintf(stderr, "Could not create http server thread\n"); + exit(1); + } + + /* launch the audio / video grab */ + if (av_grab() < 0) { + fprintf(stderr, "Could not start audio/video grab\n"); + exit(1); + } + } else { + /* master server : no thread are needed */ + if (http_server(my_addr) < 0) { + fprintf(stderr, "Could start http server\n"); + exit(1); + } + } + + return 0; +} diff --git a/formats.c b/formats.c new file mode 100644 index 0000000000..10aae461ea --- /dev/null +++ b/formats.c @@ -0,0 +1,373 @@ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <linux/videodev.h> +#include <linux/soundcard.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> +#include <string.h> + +#include "mpegenc.h" + +AVFormat *first_format; +/* XXX: suppress it ! */ +int data_out_size; + +const char *comment_string = +"+title=Test Video +author=FFMpeg +copyright=Free +comment=Generated by FFMpeg 1.0"; + +void register_avformat(AVFormat *format) +{ + AVFormat **p; + p = &first_format; + while (*p != NULL) p = &(*p)->next; + *p = format; + format->next = NULL; +} + +AVFormat *guess_format(const char *short_name, const char *filename, const char *mime_type) +{ + AVFormat *fmt, *fmt_found; + int score_max, score; + const char *ext, *p; + char ext1[32], *q; + + /* find the proper file type */ + fmt_found = NULL; + score_max = 0; + fmt = first_format; + while (fmt != NULL) { + score = 0; + if (fmt->name && short_name && !strcmp(fmt->name, short_name)) + score += 100; + if (fmt->mime_type && mime_type && !strcmp(fmt->mime_type, mime_type)) + score += 10; + if (filename && fmt->extensions) { + ext = strrchr(filename, '.'); + if (ext) { + ext++; + p = fmt->extensions; + for(;;) { + q = ext1; + while (*p != '\0' && *p != ',') + *q++ = *p++; + *q = '\0'; + if (!strcasecmp(ext1, ext)) { + score += 5; + break; + } + if (*p == '\0') + break; + p++; + } + } + } + if (score > score_max) { + score_max = score; + fmt_found = fmt; + } + fmt = fmt->next; + } + return fmt_found; +} + +/* return TRUE if val is a prefix of str. If it returns TRUE, ptr is + set to the next character in 'str' after the prefix */ +int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +/* simple formats */ +int raw_write_header(struct AVFormatContext *s) +{ + return 0; +} + +int raw_write_audio(struct AVFormatContext *s, + unsigned char *buf, int size) +{ + put_buffer(&s->pb, buf, size); + put_flush_packet(&s->pb); + return 0; +} + +int raw_write_video(struct AVFormatContext *s, + unsigned char *buf, int size) +{ + put_buffer(&s->pb, buf, size); + put_flush_packet(&s->pb); + return 0; +} + +int raw_write_trailer(struct AVFormatContext *s) +{ + return 0; +} + +AVFormat mp2_format = { + "mp2", + "MPEG audio layer 2", + "audio/x-mpeg", + "mp2,mp3", + CODEC_ID_MP2, + 0, + raw_write_header, + raw_write_audio, + NULL, + raw_write_trailer, +}; + +AVFormat ac3_format = { + "ac3", + "raw ac3", + "audio/x-ac3", + "ac3", + CODEC_ID_AC3, + 0, + raw_write_header, + raw_write_audio, + NULL, + raw_write_trailer, +}; + +AVFormat h263_format = { + "h263", + "raw h263", + "video/x-h263", + "h263", + 0, + CODEC_ID_H263, + raw_write_header, + NULL, + raw_write_video, + raw_write_trailer, +}; + +AVFormat mpeg1video_format = { + "mpeg1video", + "MPEG1 video", + "video/mpeg", + "mpg,mpeg", + 0, + CODEC_ID_MPEG1VIDEO, + raw_write_header, + NULL, + raw_write_video, + raw_write_trailer, +}; + +/* encoder management */ +AVEncoder *first_encoder; + +void register_avencoder(AVEncoder *format) +{ + AVEncoder **p; + p = &first_encoder; + while (*p != NULL) p = &(*p)->next; + *p = format; + format->next = NULL; +} + +int avencoder_open(AVEncodeContext *avctx, AVEncoder *codec) +{ + int ret; + + avctx->codec = codec; + avctx->frame_number = 0; + avctx->priv_data = malloc(codec->priv_data_size); + if (!avctx->priv_data) + return -ENOMEM; + memset(avctx->priv_data, 0, codec->priv_data_size); + ret = avctx->codec->init(avctx); + if (ret < 0) { + free(avctx->priv_data); + avctx->priv_data = NULL; + return ret; + } + return 0; +} + +int avencoder_encode(AVEncodeContext *avctx, UINT8 *buf, int buf_size, void *data) +{ + int ret; + + ret = avctx->codec->encode(avctx, buf, buf_size, data); + avctx->frame_number++; + return ret; +} + +int avencoder_close(AVEncodeContext *avctx) +{ + if (avctx->codec->close) + avctx->codec->close(avctx); + free(avctx->priv_data); + avctx->priv_data = NULL; + return 0; +} + +AVEncoder *avencoder_find(enum CodecID id) +{ + AVEncoder *p; + p = first_encoder; + while (p) { + if (p->id == id) + return p; + p = p->next; + } + return NULL; +} + + +void avencoder_string(char *buf, int buf_size, AVEncodeContext *enc) +{ + switch(enc->codec->type) { + case CODEC_TYPE_VIDEO: + snprintf(buf, buf_size, + "Video: %s, %dx%d, %d fps, %d kb/s", + enc->codec->name, enc->width, enc->height, enc->rate, enc->bit_rate / 1000); + break; + case CODEC_TYPE_AUDIO: + snprintf(buf, buf_size, + "Audio: %s, %d Hz, %s, %d kb/s", + enc->codec->name, enc->rate, + enc->channels == 2 ? "stereo" : "mono", + enc->bit_rate / 1000); + break; + default: + abort(); + } +} + +/* PutByteFormat */ + +int init_put_byte(PutByteContext *s, + unsigned char *buffer, + int buffer_size, + void *opaque, + void (*write_packet)(void *opaque, UINT8 *buf, int buf_size), + int (*write_seek)(void *opaque, long long offset, int whence)) +{ + s->buffer = buffer; + s->buf_ptr = buffer; + s->buf_end = buffer + buffer_size; + s->opaque = opaque; + s->write_packet = write_packet; + s->write_seek = write_seek; + s->pos = 0; + return 0; +} + + +static void flush_buffer(PutByteContext *s) +{ + if (s->buf_ptr > s->buffer) { + if (s->write_packet) + s->write_packet(s->opaque, s->buffer, s->buf_ptr - s->buffer); + s->pos += s->buf_ptr - s->buffer; + } + s->buf_ptr = s->buffer; +} + +void put_byte(PutByteContext *s, int b) +{ + *(s->buf_ptr)++ = b; + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); +} + +void put_buffer(PutByteContext *s, unsigned char *buf, int size) +{ + int len; + + while (size > 0) { + len = (s->buf_end - s->buf_ptr); + if (len > size) + len = size; + memcpy(s->buf_ptr, buf, len); + s->buf_ptr += len; + + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); + + buf += len; + size -= len; + } +} + +void put_flush_packet(PutByteContext *s) +{ + flush_buffer(s); +} + +/* XXX: this seek is not correct if we go after the end of the written data */ +long long put_seek(PutByteContext *s, long long offset, int whence) +{ + long long offset1; + + if (whence != SEEK_CUR && whence != SEEK_SET) + return -1; + if (whence == SEEK_CUR) + offset += s->pos + s->buf_ptr - s->buffer; + + offset1 = offset - s->pos; + if (offset1 >= 0 && offset1 < (s->buf_end - s->buffer)) { + /* can do the seek inside the buffer */ + s->buf_ptr = s->buffer + offset1; + } else { + if (!s->write_seek) + return -1; + flush_buffer(s); + s->write_seek(s->opaque, offset, whence); + } + return offset; +} + +long long put_pos(PutByteContext *s) +{ + return put_seek(s, 0, SEEK_CUR); +} + +void put_le32(PutByteContext *s, unsigned int val) +{ + put_byte(s, val); + put_byte(s, val >> 8); + put_byte(s, val >> 16); + put_byte(s, val >> 24); +} + +void put_le64(PutByteContext *s, unsigned long long val) +{ + put_le32(s, val & 0xffffffff); + put_le32(s, val >> 32); +} + +void put_le16(PutByteContext *s, unsigned int val) +{ + put_byte(s, val); + put_byte(s, val >> 8); +} + +void put_tag(PutByteContext *s, char *tag) +{ + while (*tag) { + put_byte(s, *tag++); + } +} + diff --git a/grab.c b/grab.c new file mode 100644 index 0000000000..807d4c5420 --- /dev/null +++ b/grab.c @@ -0,0 +1,258 @@ +/* + * Linux audio/video grab interface + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <linux/videodev.h> +#include <linux/soundcard.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> + +#include "mpegenc.h" +#include "mpegvideo.h" + +long long gettime(void) +{ + struct timeval tv; + gettimeofday(&tv,NULL); + return (long long)tv.tv_sec * 1000000 + tv.tv_usec; +} + +/* v4l capture */ + +const char *v4l_device = "/dev/video"; + +static struct video_capability video_cap; +int video_fd = -1; +UINT8 *video_buf, *picture_buf; +struct video_mbuf gb_buffers; +struct video_mmap gb_buf; +struct video_audio audio; +int gb_frame = 0; +long long time_frame; +int frame_rate; +int use_mmap = 0; + +int v4l_init(int rate, int width, int height) +{ + frame_rate = rate; + + video_fd = open(v4l_device, O_RDWR); + + if (ioctl(video_fd,VIDIOCGCAP,&video_cap) < 0) { + perror("VIDIOCGCAP"); + return -1; + } + + /* unmute audio */ + ioctl(video_fd, VIDIOCGAUDIO, &audio); + audio.flags &= ~VIDEO_AUDIO_MUTE; + ioctl(video_fd, VIDIOCSAUDIO, &audio); + + if (!(video_cap.type & VID_TYPE_CAPTURE)) { + /* try to use read based access */ + struct video_window win; + int val; + + win.x = 0; + win.y = 0; + win.width = width; + win.height = height; + win.chromakey = -1; + win.flags = 0; + + ioctl(video_fd, VIDIOCSWIN, &win); + + val = 1; + ioctl(video_fd, VIDIOCCAPTURE, &val); + video_buf = malloc( width * height * 2); + picture_buf = malloc( (width * height * 3) / 2); + use_mmap = 0; + return 0; + } + + if (ioctl(video_fd,VIDIOCGMBUF,&gb_buffers) < 0) { + perror("ioctl VIDIOCGMBUF"); + } + + video_buf = mmap(0,gb_buffers.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,0); + if ((unsigned char*)-1 == video_buf) { + perror("mmap"); + return -1; + } + gb_frame = 0; + time_frame = gettime(); + + /* start to grab the first frame */ + gb_buf.frame = 1 - gb_frame; + gb_buf.height = height; + gb_buf.width = width; + gb_buf.format = VIDEO_PALETTE_YUV420P; + + if (ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf) < 0) { + if (errno == EAGAIN) + fprintf(stderr,"Cannot Sync\n"); + else + perror("VIDIOCMCAPTURE"); + return -1; + } + use_mmap = 1; + return 0; +} + +/* test with read call and YUV422 stream */ +static int v4l_basic_read_picture(UINT8 *picture[3], + int width, int height, + int picture_number) +{ + int x, y; + UINT8 *p, *lum, *cb, *cr; + + if (read(video_fd, video_buf, width * height * 2) < 0) + perror("read"); + + picture[0] = picture_buf; + picture[1] = picture_buf + width * height; + picture[2] = picture_buf + (width * height) + (width * height) / 4; + + /* XXX: optimize */ + lum = picture[0]; + cb = picture[1]; + cr = picture[2]; + p = video_buf; + for(y=0;y<height;y+=2) { + for(x=0;x<width;x+=2) { + lum[0] = p[0]; + cb[0] = p[1]; + lum[1] = p[2]; + cr[0] = p[3]; + p += 4; + lum += 2; + cb++; + cr++; + } + for(x=0;x<width;x+=2) { + lum[0] = p[0]; + lum[1] = p[2]; + p += 4; + lum += 2; + } + } + return 0; +} + +static int v4l_mm_read_picture(UINT8 *picture[3], + int width, int height, + int picture_number) +{ + UINT8 *ptr; + int size; + long long curtime; + + /* wait based on the frame rate */ + time_frame += 1000000 / frame_rate; + do { + curtime = gettime(); + } while (curtime < time_frame); + + gb_buf.frame = gb_frame; + if (ioctl(video_fd, VIDIOCMCAPTURE, &gb_buf) < 0) { + if (errno == EAGAIN) + fprintf(stderr,"Cannot Sync\n"); + else + perror("VIDIOCMCAPTURE"); + return -1; + } + gb_frame = 1 - gb_frame; + + if (ioctl(video_fd, VIDIOCSYNC, &gb_frame) < 0) { + if (errno != EAGAIN) { + perror("VIDIOCSYNC"); + } + } + + size = width * height; + ptr = video_buf + gb_buffers.offsets[gb_frame]; + picture[0] = ptr; + picture[1] = ptr + size; + picture[2] = ptr + size + (size / 4); + + return 0; +} + +int v4l_read_picture(UINT8 *picture[3], + int width, int height, + int picture_number) +{ + if (use_mmap) { + return v4l_mm_read_picture(picture, width, height, picture_number); + } else { + return v4l_basic_read_picture(picture, width, height, picture_number); + } +} + +/* open audio device */ +int audio_open(int freq, int channels) +{ + int audio_fd, tmp, err; + + audio_fd = open("/dev/dsp",O_RDONLY); + if (audio_fd == -1) { + perror("/dev/dsp"); + return -1; + } + /* non blocking mode */ + fcntl(audio_fd, F_SETFL, O_NONBLOCK); + +#if 0 + tmp=(NB_FRAGMENTS << 16) | FRAGMENT_BITS; + err=ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &tmp); + if (err < 0) { + perror("SNDCTL_DSP_SETFRAGMENT"); + } +#endif + + /* always set to this size */ + /* XXX: incorrect if big endian */ + tmp=AFMT_S16_LE; + err=ioctl(audio_fd,SNDCTL_DSP_SETFMT,&tmp); + if (err < 0) { + perror("SNDCTL_DSP_SETFMT"); + } + + tmp= (channels == 2); + err=ioctl(audio_fd,SNDCTL_DSP_STEREO,&tmp); + if (err < 0) { + perror("SNDCTL_DSP_STEREO"); + } + + /* should be last */ + tmp = freq; + err=ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp); + if (err < 0) { + perror("SNDCTL_DSP_SPEED"); + } + return audio_fd; +} + diff --git a/jpegenc.c b/jpegenc.c new file mode 100644 index 0000000000..52d759b0f6 --- /dev/null +++ b/jpegenc.c @@ -0,0 +1,102 @@ +/* + * Miscellaneous MJPEG based formats + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include "mpegenc.h" + +/* Multipart JPEG */ + +#define BOUNDARY_TAG "ffserver" + +static int mpjpeg_write_header(AVFormatContext *s) +{ + UINT8 buf1[256]; + + snprintf(buf1, sizeof(buf1), "--%s\n", BOUNDARY_TAG); + put_buffer(&s->pb, buf1, strlen(buf1)); + put_flush_packet(&s->pb); + return 0; +} + +static int mpjpeg_write_video(AVFormatContext *s, UINT8 *buf, int size) +{ + UINT8 buf1[256]; + + snprintf(buf1, sizeof(buf1), "Content-type: image/jpeg\n\n"); + put_buffer(&s->pb, buf1, strlen(buf1)); + put_buffer(&s->pb, buf, size); + + snprintf(buf1, sizeof(buf1), "\n--%s\n", BOUNDARY_TAG); + put_buffer(&s->pb, buf1, strlen(buf1)); + put_flush_packet(&s->pb); + return 0; +} + +static int mpjpeg_write_trailer(AVFormatContext *s) +{ + return 0; +} + +AVFormat mpjpeg_format = { + "mpjpeg", + "Mime multipart JPEG format", + "multipart/x-mixed-replace;boundary=" BOUNDARY_TAG, + "mjpg", + CODEC_ID_NONE, + CODEC_ID_MJPEG, + mpjpeg_write_header, + NULL, + mpjpeg_write_video, + mpjpeg_write_trailer, +}; + + +/* single frame JPEG */ + +static int jpeg_write_header(AVFormatContext *s) +{ + return 0; +} + +static int jpeg_write_video(AVFormatContext *s, UINT8 *buf, int size) +{ + put_buffer(&s->pb, buf, size); + put_flush_packet(&s->pb); + return 1; /* no more data can be sent */ +} + +static int jpeg_write_trailer(AVFormatContext *s) +{ + return 0; +} + +AVFormat jpeg_format = { + "jpeg", + "JPEG image", + "image/jpeg", + "jpg,jpeg", + CODEC_ID_NONE, + CODEC_ID_MJPEG, + jpeg_write_header, + NULL, + jpeg_write_video, + jpeg_write_trailer, +}; diff --git a/libav/Makefile b/libav/Makefile new file mode 100644 index 0000000000..6664e870cb --- /dev/null +++ b/libav/Makefile @@ -0,0 +1,17 @@ +CFLAGS= -O2 -Wall -g +LDFLAGS= -g + +OBJS= common.o mpegvideo.o h263enc.o jrevdct.o jfdctfst.o \ + mpegaudio.o ac3enc.o mjpegenc.o resample.o +LIB= libav.a + +all: $(LIB) + +$(LIB): $(OBJS) + ar rcs $@ $(OBJS) + +%.o: %.c + gcc $(CFLAGS) -c -o $@ $< + +clean: + rm -f *.o *~ *.a diff --git a/libav/ac3enc.c b/libav/ac3enc.c new file mode 100644 index 0000000000..b1126c4943 --- /dev/null +++ b/libav/ac3enc.c @@ -0,0 +1,1460 @@ +/* + * The simplest AC3 encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <math.h> +#include "avcodec.h" + +#include "ac3enc.h" +#include "ac3tab.h" + +//#define DEBUG +//#define DEBUG_BITALLOC +#define NDEBUG +#include <assert.h> + +#define MDCT_NBITS 9 +#define N (1 << MDCT_NBITS) +#define NB_BLOCKS 6 /* number of PCM blocks inside an AC3 frame */ + +/* new exponents are sent if their Norm 1 exceed this number */ +#define EXP_DIFF_THRESHOLD 1000 + +/* exponent encoding strategy */ +#define EXP_REUSE 0 +#define EXP_NEW 1 + +#define EXP_D15 1 +#define EXP_D25 2 +#define EXP_D45 3 + +static void fft_init(int ln); +static void ac3_crc_init(void); + +static inline INT16 fix15(float a) +{ + int v; + v = (int)(a * (float)(1 << 15)); + if (v < -32767) + v = -32767; + else if (v > 32767) + v = 32767; + return v; +} + +static inline int calc_lowcomp1(int a, int b0, int b1) +{ + if ((b0 + 256) == b1) { + a = 384 ; + } else if (b0 > b1) { + a = a - 64; + if (a < 0) a=0; + } + return a; +} + +static inline int calc_lowcomp(int a, int b0, int b1, int bin) +{ + if (bin < 7) { + if ((b0 + 256) == b1) { + a = 384 ; + } else if (b0 > b1) { + a = a - 64; + if (a < 0) a=0; + } + } else if (bin < 20) { + if ((b0 + 256) == b1) { + a = 320 ; + } else if (b0 > b1) { + a= a - 64; + if (a < 0) a=0; + } + } else { + a = a - 128; + if (a < 0) a=0; + } + return a; +} + +/* AC3 bit allocation. The algorithm is the one described in the AC3 + spec with some optimizations because of our simplified encoding + assumptions. */ +void parametric_bit_allocation(AC3EncodeContext *s, UINT8 *bap, + INT8 *exp, int start, int end, + int snroffset, int fgain) +{ + int bin,i,j,k,end1,v,v1,bndstrt,bndend,lowcomp,begin; + int fastleak,slowleak,address,tmp; + INT16 psd[256]; /* scaled exponents */ + INT16 bndpsd[50]; /* interpolated exponents */ + INT16 excite[50]; /* excitation */ + INT16 mask[50]; /* masking value */ + + /* exponent mapping to PSD */ + for(bin=start;bin<end;bin++) { + psd[bin]=(3072 - (exp[bin] << 7)); + } + + /* PSD integration */ + j=start; + k=masktab[start]; + do { + v=psd[j]; + j++; + end1=bndtab[k+1]; + if (end1 > end) end1=end; + for(i=j;i<end1;i++) { + int c,adr; + /* logadd */ + v1=psd[j]; + c=v-v1; + if (c >= 0) { + adr=c >> 1; + if (adr > 255) adr=255; + v=v + latab[adr]; + } else { + adr=(-c) >> 1; + if (adr > 255) adr=255; + v=v1 + latab[adr]; + } + j++; + } + bndpsd[k]=v; + k++; + } while (end > bndtab[k]); + + /* excitation function */ + bndstrt = masktab[start]; + bndend = masktab[end-1] + 1; + + lowcomp = 0; + lowcomp = calc_lowcomp1(lowcomp, bndpsd[0], bndpsd[1]) ; + excite[0] = bndpsd[0] - fgain - lowcomp ; + lowcomp = calc_lowcomp1(lowcomp, bndpsd[1], bndpsd[2]) ; + excite[1] = bndpsd[1] - fgain - lowcomp ; + begin = 7 ; + for (bin = 2; bin < 7; bin++) { + lowcomp = calc_lowcomp1(lowcomp, bndpsd[bin], bndpsd[bin+1]) ; + fastleak = bndpsd[bin] - fgain ; + slowleak = bndpsd[bin] - s->sgain ; + excite[bin] = fastleak - lowcomp ; + if (bndpsd[bin] <= bndpsd[bin+1]) { + begin = bin + 1 ; + break ; + } + } + + end1=bndend; + if (end1 > 22) end1=22; + + for (bin = begin; bin < end1; bin++) { + lowcomp = calc_lowcomp(lowcomp, bndpsd[bin], bndpsd[bin+1], bin) ; + + fastleak -= s->fdecay ; + v = bndpsd[bin] - fgain; + if (fastleak < v) fastleak = v; + + slowleak -= s->sdecay ; + v = bndpsd[bin] - s->sgain; + if (slowleak < v) slowleak = v; + + v=fastleak - lowcomp; + if (slowleak > v) v=slowleak; + + excite[bin] = v; + } + + for (bin = 22; bin < bndend; bin++) { + fastleak -= s->fdecay ; + v = bndpsd[bin] - fgain; + if (fastleak < v) fastleak = v; + slowleak -= s->sdecay ; + v = bndpsd[bin] - s->sgain; + if (slowleak < v) slowleak = v; + + v=fastleak; + if (slowleak > v) v = slowleak; + excite[bin] = v; + } + + /* compute masking curve */ + + for (bin = bndstrt; bin < bndend; bin++) { + v1 = excite[bin]; + tmp = s->dbknee - bndpsd[bin]; + if (tmp > 0) { + v1 += tmp >> 2; + } + v=hth[bin >> s->halfratecod][s->fscod]; + if (v1 > v) v=v1; + mask[bin] = v; + } + + /* compute bit allocation */ + + i = start ; + j = masktab[start] ; + do { + v=mask[j]; + v -= snroffset ; + v -= s->floor ; + if (v < 0) v = 0; + v &= 0x1fe0 ; + v += s->floor ; + + end1=bndtab[j] + bndsz[j]; + if (end1 > end) end1=end; + + for (k = i; k < end1; k++) { + address = (psd[i] - v) >> 5 ; + if (address < 0) address=0; + else if (address > 63) address=63; + bap[i] = baptab[address]; + i++; + } + } while (end > bndtab[j++]) ; +} + +typedef struct IComplex { + short re,im; +} IComplex; + +static void fft_init(int ln) +{ + int i, j, m, n; + float alpha; + + n = 1 << ln; + + for(i=0;i<(n/2);i++) { + alpha = 2 * M_PI * (float)i / (float)n; + costab[i] = fix15(cos(alpha)); + sintab[i] = fix15(sin(alpha)); + } + + for(i=0;i<n;i++) { + m=0; + for(j=0;j<ln;j++) { + m |= ((i >> j) & 1) << (ln-j-1); + } + fft_rev[i]=m; + } +} + +/* butter fly op */ +#define BF(pre, pim, qre, qim, pre1, pim1, qre1, qim1) \ +{\ + int ax, ay, bx, by;\ + bx=pre1;\ + by=pim1;\ + ax=qre1;\ + ay=qim1;\ + pre = (bx + ax) >> 1;\ + pim = (by + ay) >> 1;\ + qre = (bx - ax) >> 1;\ + qim = (by - ay) >> 1;\ +} + +#define MUL16(a,b) ((a) * (b)) + +#define CMUL(pre, pim, are, aim, bre, bim) \ +{\ + pre = (MUL16(are, bre) - MUL16(aim, bim)) >> 15;\ + pim = (MUL16(are, bim) + MUL16(bre, aim)) >> 15;\ +} + + +/* do a 2^n point complex fft on 2^ln points. */ +static void fft(IComplex *z, int ln) +{ + int j, l, np, np2; + int nblocks, nloops; + register IComplex *p,*q; + int tmp_re, tmp_im; + + np = 1 << ln; + + /* reverse */ + for(j=0;j<np;j++) { + int k; + IComplex tmp; + k = fft_rev[j]; + if (k < j) { + tmp = z[k]; + z[k] = z[j]; + z[j] = tmp; + } + } + + /* pass 0 */ + + p=&z[0]; + j=(np >> 1); + do { + BF(p[0].re, p[0].im, p[1].re, p[1].im, + p[0].re, p[0].im, p[1].re, p[1].im); + p+=2; + } while (--j != 0); + + /* pass 1 */ + + p=&z[0]; + j=np >> 2; + do { + BF(p[0].re, p[0].im, p[2].re, p[2].im, + p[0].re, p[0].im, p[2].re, p[2].im); + BF(p[1].re, p[1].im, p[3].re, p[3].im, + p[1].re, p[1].im, p[3].im, -p[3].re); + p+=4; + } while (--j != 0); + + /* pass 2 .. ln-1 */ + + nblocks = np >> 3; + nloops = 1 << 2; + np2 = np >> 1; + do { + p = z; + q = z + nloops; + for (j = 0; j < nblocks; ++j) { + + BF(p->re, p->im, q->re, q->im, + p->re, p->im, q->re, q->im); + + p++; + q++; + for(l = nblocks; l < np2; l += nblocks) { + CMUL(tmp_re, tmp_im, costab[l], -sintab[l], q->re, q->im); + BF(p->re, p->im, q->re, q->im, + p->re, p->im, tmp_re, tmp_im); + p++; + q++; + } + p += nloops; + q += nloops; + } + nblocks = nblocks >> 1; + nloops = nloops << 1; + } while (nblocks != 0); +} + +/* do a 512 point mdct */ +static void mdct512(INT32 *out, INT16 *in) +{ + int i, re, im, re1, im1; + INT16 rot[N]; + IComplex x[N/4]; + + /* shift to simplify computations */ + for(i=0;i<N/4;i++) + rot[i] = -in[i + 3*N/4]; + for(i=N/4;i<N;i++) + rot[i] = in[i - N/4]; + + /* pre rotation */ + for(i=0;i<N/4;i++) { + re = ((int)rot[2*i] - (int)rot[N-1-2*i]) >> 1; + im = -((int)rot[N/2+2*i] - (int)rot[N/2-1-2*i]) >> 1; + CMUL(x[i].re, x[i].im, re, im, -xcos1[i], xsin1[i]); + } + + fft(x, MDCT_NBITS - 2); + + /* post rotation */ + for(i=0;i<N/4;i++) { + re = x[i].re; + im = x[i].im; + CMUL(re1, im1, re, im, xsin1[i], xcos1[i]); + out[2*i] = im1; + out[N/2-1-2*i] = re1; + } +} + +/* XXX: use another norm ? */ +static int calc_exp_diff(UINT8 *exp1, UINT8 *exp2, int n) +{ + int sum, i; + sum = 0; + for(i=0;i<n;i++) { + sum += abs(exp1[i] - exp2[i]); + } + return sum; +} + +static void compute_exp_strategy(UINT8 exp_strategy[NB_BLOCKS][AC3_MAX_CHANNELS], + UINT8 exp[NB_BLOCKS][AC3_MAX_CHANNELS][N/2], + int ch) +{ + int i, j; + int exp_diff; + + /* estimate if the exponent variation & decide if they should be + reused in the next frame */ + exp_strategy[0][ch] = EXP_NEW; + for(i=1;i<NB_BLOCKS;i++) { + exp_diff = calc_exp_diff(exp[i][ch], exp[i-1][ch], N/2); +#ifdef DEBUG + printf("exp_diff=%d\n", exp_diff); +#endif + if (exp_diff > EXP_DIFF_THRESHOLD) + exp_strategy[i][ch] = EXP_NEW; + else + exp_strategy[i][ch] = EXP_REUSE; + } + /* now select the encoding strategy type : if exponents are often + recoded, we use a coarse encoding */ + i = 0; + while (i < NB_BLOCKS) { + j = i + 1; + while (j < NB_BLOCKS && exp_strategy[j][ch] == EXP_REUSE) + j++; + switch(j - i) { + case 1: + exp_strategy[i][ch] = EXP_D45; + break; + case 2: + case 3: + exp_strategy[i][ch] = EXP_D25; + break; + default: + exp_strategy[i][ch] = EXP_D15; + break; + } + i = j; + } +} + +/* set exp[i] to min(exp[i], exp1[i]) */ +static void exponent_min(UINT8 exp[N/2], UINT8 exp1[N/2], int n) +{ + int i; + + for(i=0;i<n;i++) { + if (exp1[i] < exp[i]) + exp[i] = exp1[i]; + } +} + +/* update the exponents so that they are the ones the decoder will + decode. Return the number of bits used to code the exponents */ +static int encode_exp(UINT8 encoded_exp[N/2], + UINT8 exp[N/2], + int nb_exps, + int exp_strategy) +{ + int group_size, nb_groups, i, j, k, recurse, exp_min, delta; + UINT8 exp1[N/2]; + + switch(exp_strategy) { + case EXP_D15: + group_size = 1; + break; + case EXP_D25: + group_size = 2; + break; + default: + case EXP_D45: + group_size = 4; + break; + } + nb_groups = ((nb_exps + (group_size * 3) - 4) / (3 * group_size)) * 3; + + /* for each group, compute the minimum exponent */ + exp1[0] = exp[0]; /* DC exponent is handled separately */ + k = 1; + for(i=1;i<=nb_groups;i++) { + exp_min = exp[k]; + assert(exp_min >= 0 && exp_min <= 24); + for(j=1;j<group_size;j++) { + if (exp[k+j] < exp_min) + exp_min = exp[k+j]; + } + exp1[i] = exp_min; + k += group_size; + } + + /* constraint for DC exponent */ + if (exp1[0] > 15) + exp1[0] = 15; + + /* Iterate until the delta constraints between each groups are + satisfyed. I'm sure it is possible to find a better algorithm, + but I am lazy */ + do { + recurse = 0; + for(i=1;i<=nb_groups;i++) { + delta = exp1[i] - exp1[i-1]; + if (delta > 2) { + /* if delta too big, we encode a smaller exponent */ + exp1[i] = exp1[i-1] + 2; + } else if (delta < -2) { + /* if delta is too small, we must decrease the previous + exponent, which means we must recurse */ + recurse = 1; + exp1[i-1] = exp1[i] + 2; + } + } + } while (recurse); + + /* now we have the exponent values the decoder will see */ + encoded_exp[0] = exp1[0]; + k = 1; + for(i=1;i<=nb_groups;i++) { + for(j=0;j<group_size;j++) { + encoded_exp[k+j] = exp1[i]; + } + k += group_size; + } + +#if defined(DEBUG) + printf("exponents: strategy=%d\n", exp_strategy); + for(i=0;i<=nb_groups * group_size;i++) { + printf("%d ", encoded_exp[i]); + } + printf("\n"); +#endif + + return 4 + (nb_groups / 3) * 7; +} + +/* return the size in bits taken by the mantissa */ +int compute_mantissa_size(AC3EncodeContext *s, UINT8 *m, int nb_coefs) +{ + int bits, mant, i; + + bits = 0; + for(i=0;i<nb_coefs;i++) { + mant = m[i]; + switch(mant) { + case 0: + /* nothing */ + break; + case 1: + /* 3 mantissa in 5 bits */ + if (s->mant1_cnt == 0) + bits += 5; + if (++s->mant1_cnt == 3) + s->mant1_cnt = 0; + break; + case 2: + /* 3 mantissa in 7 bits */ + if (s->mant2_cnt == 0) + bits += 7; + if (++s->mant2_cnt == 3) + s->mant2_cnt = 0; + break; + case 3: + bits += 3; + break; + case 4: + /* 2 mantissa in 7 bits */ + if (s->mant4_cnt == 0) + bits += 7; + if (++s->mant4_cnt == 2) + s->mant4_cnt = 0; + break; + case 14: + bits += 14; + break; + case 15: + bits += 16; + break; + default: + bits += mant - 1; + break; + } + } + return bits; +} + + +static int bit_alloc(AC3EncodeContext *s, + UINT8 bap[NB_BLOCKS][AC3_MAX_CHANNELS][N/2], + UINT8 encoded_exp[NB_BLOCKS][AC3_MAX_CHANNELS][N/2], + UINT8 exp_strategy[NB_BLOCKS][AC3_MAX_CHANNELS], + int frame_bits, int csnroffst, int fsnroffst) +{ + int i, ch; + + /* compute size */ + for(i=0;i<NB_BLOCKS;i++) { + s->mant1_cnt = 0; + s->mant2_cnt = 0; + s->mant4_cnt = 0; + for(ch=0;ch<s->nb_channels;ch++) { + parametric_bit_allocation(s, bap[i][ch], encoded_exp[i][ch], + 0, s->nb_coefs[ch], + (((csnroffst-15) << 4) + + fsnroffst) << 2, + fgaintab[s->fgaincod[ch]]); + frame_bits += compute_mantissa_size(s, bap[i][ch], + s->nb_coefs[ch]); + } + } +#if 0 + printf("csnr=%d fsnr=%d frame_bits=%d diff=%d\n", + csnroffst, fsnroffst, frame_bits, + 16 * s->frame_size - ((frame_bits + 7) & ~7)); +#endif + return 16 * s->frame_size - frame_bits; +} + +#define SNR_INC1 4 + +static int compute_bit_allocation(AC3EncodeContext *s, + UINT8 bap[NB_BLOCKS][AC3_MAX_CHANNELS][N/2], + UINT8 encoded_exp[NB_BLOCKS][AC3_MAX_CHANNELS][N/2], + UINT8 exp_strategy[NB_BLOCKS][AC3_MAX_CHANNELS], + int frame_bits) +{ + int i, ch; + int csnroffst, fsnroffst; + UINT8 bap1[NB_BLOCKS][AC3_MAX_CHANNELS][N/2]; + + /* init default parameters */ + s->sdecaycod = 2; + s->fdecaycod = 1; + s->sgaincod = 1; + s->dbkneecod = 2; + s->floorcod = 4; + for(ch=0;ch<s->nb_channels;ch++) + s->fgaincod[ch] = 4; + + /* compute real values */ + s->sdecay = sdecaytab[s->sdecaycod] >> s->halfratecod; + s->fdecay = fdecaytab[s->fdecaycod] >> s->halfratecod; + s->sgain = sgaintab[s->sgaincod]; + s->dbknee = dbkneetab[s->dbkneecod]; + s->floor = floortab[s->floorcod]; + + /* header size */ + frame_bits += 65; + if (s->acmod == 2) + frame_bits += 2; + + /* audio blocks */ + for(i=0;i<NB_BLOCKS;i++) { + frame_bits += s->nb_channels * 2 + 2; + if (s->acmod == 2) + frame_bits++; + frame_bits += 2 * s->nb_channels; + for(ch=0;ch<s->nb_channels;ch++) { + if (exp_strategy[i][ch] != EXP_REUSE) + frame_bits += 6 + 2; + } + frame_bits++; /* baie */ + frame_bits++; /* snr */ + frame_bits += 2; /* delta / skip */ + } + frame_bits++; /* cplinu for block 0 */ + /* bit alloc info */ + frame_bits += 2*4 + 3 + 6 + s->nb_channels * (4 + 3); + + /* CRC */ + frame_bits += 16; + + /* now the big work begins : do the bit allocation. Modify the snr + offset until we can pack everything in the requested frame size */ + + csnroffst = s->csnroffst; + while (csnroffst >= 0 && + bit_alloc(s, bap, encoded_exp, exp_strategy, frame_bits, csnroffst, 0) < 0) + csnroffst -= SNR_INC1; + if (csnroffst < 0) { + fprintf(stderr, "Error !!!\n"); + return -1; + } + while ((csnroffst + SNR_INC1) <= 63 && + bit_alloc(s, bap1, encoded_exp, exp_strategy, frame_bits, + csnroffst + SNR_INC1, 0) >= 0) { + csnroffst += SNR_INC1; + memcpy(bap, bap1, sizeof(bap1)); + } + while ((csnroffst + 1) <= 63 && + bit_alloc(s, bap1, encoded_exp, exp_strategy, frame_bits, csnroffst + 1, 0) >= 0) { + csnroffst++; + memcpy(bap, bap1, sizeof(bap1)); + } + + fsnroffst = 0; + while ((fsnroffst + SNR_INC1) <= 15 && + bit_alloc(s, bap1, encoded_exp, exp_strategy, frame_bits, + csnroffst, fsnroffst + SNR_INC1) >= 0) { + fsnroffst += SNR_INC1; + memcpy(bap, bap1, sizeof(bap1)); + } + while ((fsnroffst + 1) <= 15 && + bit_alloc(s, bap1, encoded_exp, exp_strategy, frame_bits, + csnroffst, fsnroffst + 1) >= 0) { + fsnroffst++; + memcpy(bap, bap1, sizeof(bap1)); + } + + s->csnroffst = csnroffst; + for(ch=0;ch<s->nb_channels;ch++) + s->fsnroffst[ch] = fsnroffst; +#if defined(DEBUG_BITALLOC) + { + int j; + + for(i=0;i<6;i++) { + for(ch=0;ch<s->nb_channels;ch++) { + printf("Block #%d Ch%d:\n", i, ch); + printf("bap="); + for(j=0;j<s->nb_coefs[ch];j++) { + printf("%d ",bap[i][ch][j]); + } + printf("\n"); + } + } + } +#endif + return 0; +} + +static int AC3_encode_init(AVEncodeContext *avctx) +{ + int freq = avctx->rate; + int bitrate = avctx->bit_rate; + int channels = avctx->channels; + AC3EncodeContext *s = avctx->priv_data; + int i, j, k, l, ch, v; + float alpha; + static unsigned short freqs[3] = { 48000, 44100, 32000 }; + + avctx->frame_size = AC3_FRAME_SIZE; + avctx->key_frame = 1; /* always key frame */ + + /* number of channels */ + if (channels == 1) + s->acmod = 1; + else if (channels == 2) + s->acmod = 2; + else + return -1; + s->nb_channels = channels; + + /* frequency */ + for(i=0;i<3;i++) { + for(j=0;j<3;j++) + if ((freqs[j] >> i) == freq) + goto found; + } + return -1; + found: + s->sample_rate = freq; + s->halfratecod = i; + s->fscod = j; + s->bsid = 8 + s->halfratecod; + s->bsmod = 0; /* complete main audio service */ + + /* bitrate & frame size */ + bitrate /= 1000; + for(i=0;i<19;i++) { + if ((bitratetab[i] >> s->halfratecod) == bitrate) + break; + } + if (i == 19) + return -1; + s->bit_rate = bitrate; + s->frmsizecod = i << 1; + s->frame_size_min = (bitrate * 1000 * AC3_FRAME_SIZE) / (freq * 16); + /* for now we do not handle fractional sizes */ + s->frame_size = s->frame_size_min; + + /* bit allocation init */ + for(ch=0;ch<s->nb_channels;ch++) { + /* bandwidth for each channel */ + /* XXX: should compute the bandwidth according to the frame + size, so that we avoid anoying high freq artefacts */ + s->chbwcod[ch] = 50; /* sample bandwidth as mpeg audio layer 2 table 0 */ + s->nb_coefs[ch] = ((s->chbwcod[ch] + 12) * 3) + 37; + } + /* initial snr offset */ + s->csnroffst = 40; + + /* compute bndtab and masktab from bandsz */ + k = 0; + l = 0; + for(i=0;i<50;i++) { + bndtab[i] = l; + v = bndsz[i]; + for(j=0;j<v;j++) masktab[k++]=i; + l += v; + } + bndtab[50] = 0; + + /* mdct init */ + fft_init(MDCT_NBITS - 2); + for(i=0;i<N/4;i++) { + alpha = 2 * M_PI * (i + 1.0 / 8.0) / (float)N; + xcos1[i] = fix15(-cos(alpha)); + xsin1[i] = fix15(-sin(alpha)); + } + + ac3_crc_init(); + + return 0; +} + +/* output the AC3 frame header */ +static void output_frame_header(AC3EncodeContext *s, unsigned char *frame) +{ + init_put_bits(&s->pb, frame, AC3_MAX_CODED_FRAME_SIZE, NULL, NULL); + + put_bits(&s->pb, 16, 0x0b77); /* frame header */ + put_bits(&s->pb, 16, 0); /* crc1: will be filled later */ + put_bits(&s->pb, 2, s->fscod); + put_bits(&s->pb, 6, s->frmsizecod + (s->frame_size - s->frame_size_min)); + put_bits(&s->pb, 5, s->bsid); + put_bits(&s->pb, 3, s->bsmod); + put_bits(&s->pb, 3, s->acmod); + if (s->acmod == 2) { + put_bits(&s->pb, 2, 0); /* surround not indicated */ + } + put_bits(&s->pb, 1, 0); /* no LFE */ + put_bits(&s->pb, 5, 31); /* dialog norm: -31 db */ + put_bits(&s->pb, 1, 0); /* no compression control word */ + put_bits(&s->pb, 1, 0); /* no lang code */ + put_bits(&s->pb, 1, 0); /* no audio production info */ + put_bits(&s->pb, 1, 0); /* no copyright */ + put_bits(&s->pb, 1, 1); /* original bitstream */ + put_bits(&s->pb, 1, 0); /* no time code 1 */ + put_bits(&s->pb, 1, 0); /* no time code 2 */ + put_bits(&s->pb, 1, 0); /* no addtional bit stream info */ +} + +/* symetric quantization on 'levels' levels */ +static inline int sym_quant(int c, int e, int levels) +{ + int v; + + if (c >= 0) { + v = (levels * (c << e)) >> 25; + v = (levels >> 1) + v; + } else { + v = (levels * ((-c) << e)) >> 25; + v = (levels >> 1) - v; + } + assert (v >= 0 && v < levels); + return v; +} + +/* asymetric quantization on 2^qbits levels */ +static inline int asym_quant(int c, int e, int qbits) +{ + int lshift, m, v; + + lshift = e + qbits - 24; + if (lshift >= 0) + v = c << lshift; + else + v = c >> (-lshift); + /* rounding */ + v = (v + 1) >> 1; + m = (1 << (qbits-1)); + if (v >= m) + v = m - 1; + assert(v >= -m); + return v & ((1 << qbits)-1); +} + +/* Output one audio block. There are NB_BLOCKS audio blocks in one AC3 + frame */ +static void output_audio_block(AC3EncodeContext *s, + UINT8 exp_strategy[AC3_MAX_CHANNELS], + UINT8 encoded_exp[AC3_MAX_CHANNELS][N/2], + UINT8 bap[AC3_MAX_CHANNELS][N/2], + INT32 mdct_coefs[AC3_MAX_CHANNELS][N/2], + INT8 global_exp[AC3_MAX_CHANNELS], + int block_num) +{ + int ch, nb_groups, group_size, i, baie; + UINT8 *p; + UINT16 qmant[AC3_MAX_CHANNELS][N/2]; + int exp0, exp1; + int mant1_cnt, mant2_cnt, mant4_cnt; + UINT16 *qmant1_ptr, *qmant2_ptr, *qmant4_ptr; + int delta0, delta1, delta2; + + for(ch=0;ch<s->nb_channels;ch++) + put_bits(&s->pb, 1, 0); /* 512 point MDCT */ + for(ch=0;ch<s->nb_channels;ch++) + put_bits(&s->pb, 1, 1); /* no dither */ + put_bits(&s->pb, 1, 0); /* no dynamic range */ + if (block_num == 0) { + /* for block 0, even if no coupling, we must say it. This is a + waste of bit :-) */ + put_bits(&s->pb, 1, 1); /* coupling strategy present */ + put_bits(&s->pb, 1, 0); /* no coupling strategy */ + } else { + put_bits(&s->pb, 1, 0); /* no new coupling strategy */ + } + + if (s->acmod == 2) { + put_bits(&s->pb, 1, 0); /* no matrixing (but should be used in the future) */ + } + +#if defined(DEBUG) + { + static int count = 0; + printf("Block #%d (%d)\n", block_num, count++); + } +#endif + /* exponent strategy */ + for(ch=0;ch<s->nb_channels;ch++) { + put_bits(&s->pb, 2, exp_strategy[ch]); + } + + for(ch=0;ch<s->nb_channels;ch++) { + if (exp_strategy[ch] != EXP_REUSE) + put_bits(&s->pb, 6, s->chbwcod[ch]); + } + + /* exponents */ + for (ch = 0; ch < s->nb_channels; ch++) { + switch(exp_strategy[ch]) { + case EXP_REUSE: + continue; + case EXP_D15: + group_size = 1; + break; + case EXP_D25: + group_size = 2; + break; + default: + case EXP_D45: + group_size = 4; + break; + } + nb_groups = (s->nb_coefs[ch] + (group_size * 3) - 4) / (3 * group_size); + p = encoded_exp[ch]; + + /* first exponent */ + exp1 = *p++; + put_bits(&s->pb, 4, exp1); + + /* next ones are delta encoded */ + for(i=0;i<nb_groups;i++) { + /* merge three delta in one code */ + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta0 = exp1 - exp0 + 2; + + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta1 = exp1 - exp0 + 2; + + exp0 = exp1; + exp1 = p[0]; + p += group_size; + delta2 = exp1 - exp0 + 2; + + put_bits(&s->pb, 7, ((delta0 * 5 + delta1) * 5) + delta2); + } + + put_bits(&s->pb, 2, 0); /* no gain range info */ + } + + /* bit allocation info */ + baie = (block_num == 0); + put_bits(&s->pb, 1, baie); + if (baie) { + put_bits(&s->pb, 2, s->sdecaycod); + put_bits(&s->pb, 2, s->fdecaycod); + put_bits(&s->pb, 2, s->sgaincod); + put_bits(&s->pb, 2, s->dbkneecod); + put_bits(&s->pb, 3, s->floorcod); + } + + /* snr offset */ + put_bits(&s->pb, 1, baie); /* always present with bai */ + if (baie) { + put_bits(&s->pb, 6, s->csnroffst); + for(ch=0;ch<s->nb_channels;ch++) { + put_bits(&s->pb, 4, s->fsnroffst[ch]); + put_bits(&s->pb, 3, s->fgaincod[ch]); + } + } + + put_bits(&s->pb, 1, 0); /* no delta bit allocation */ + put_bits(&s->pb, 1, 0); /* no data to skip */ + + /* mantissa encoding : we use two passes to handle the grouping. A + one pass method may be faster, but it would necessitate to + modify the output stream. */ + + /* first pass: quantize */ + mant1_cnt = mant2_cnt = mant4_cnt = 0; + qmant1_ptr = qmant2_ptr = qmant4_ptr = NULL; + + for (ch = 0; ch < s->nb_channels; ch++) { + int b, c, e, v; + + for(i=0;i<s->nb_coefs[ch];i++) { + c = mdct_coefs[ch][i]; + e = encoded_exp[ch][i] - global_exp[ch]; + b = bap[ch][i]; + switch(b) { + case 0: + v = 0; + break; + case 1: + v = sym_quant(c, e, 3); + switch(mant1_cnt) { + case 0: + qmant1_ptr = &qmant[ch][i]; + v = 9 * v; + mant1_cnt = 1; + break; + case 1: + *qmant1_ptr += 3 * v; + mant1_cnt = 2; + v = 128; + break; + default: + *qmant1_ptr += v; + mant1_cnt = 0; + v = 128; + break; + } + break; + case 2: + v = sym_quant(c, e, 5); + switch(mant2_cnt) { + case 0: + qmant2_ptr = &qmant[ch][i]; + v = 25 * v; + mant2_cnt = 1; + break; + case 1: + *qmant2_ptr += 5 * v; + mant2_cnt = 2; + v = 128; + break; + default: + *qmant2_ptr += v; + mant2_cnt = 0; + v = 128; + break; + } + break; + case 3: + v = sym_quant(c, e, 7); + break; + case 4: + v = sym_quant(c, e, 11); + switch(mant4_cnt) { + case 0: + qmant4_ptr = &qmant[ch][i]; + v = 11 * v; + mant4_cnt = 1; + break; + default: + *qmant4_ptr += v; + mant4_cnt = 0; + v = 128; + break; + } + break; + case 5: + v = sym_quant(c, e, 15); + break; + case 14: + v = asym_quant(c, e, 14); + break; + case 15: + v = asym_quant(c, e, 16); + break; + default: + v = asym_quant(c, e, b - 1); + break; + } + qmant[ch][i] = v; + } + } + + /* second pass : output the values */ + for (ch = 0; ch < s->nb_channels; ch++) { + int b, q; + + for(i=0;i<s->nb_coefs[ch];i++) { + q = qmant[ch][i]; + b = bap[ch][i]; + switch(b) { + case 0: + break; + case 1: + if (q != 128) + put_bits(&s->pb, 5, q); + break; + case 2: + if (q != 128) + put_bits(&s->pb, 7, q); + break; + case 3: + put_bits(&s->pb, 3, q); + break; + case 4: + if (q != 128) + put_bits(&s->pb, 7, q); + break; + case 14: + put_bits(&s->pb, 14, q); + break; + case 15: + put_bits(&s->pb, 16, q); + break; + default: + put_bits(&s->pb, b - 1, q); + break; + } + } + } +} + +/* compute the ac3 crc */ + +#define CRC16_POLY ((1 << 0) | (1 << 2) | (1 << 15) | (1 << 16)) + +static void ac3_crc_init(void) +{ + unsigned int c, n, k; + + for(n=0;n<256;n++) { + c = n << 8; + for (k = 0; k < 8; k++) { + if (c & (1 << 15)) + c = ((c << 1) & 0xffff) ^ (CRC16_POLY & 0xffff); + else + c = c << 1; + } + crc_table[n] = c; + } +} + +static unsigned int ac3_crc(UINT8 *data, int n, unsigned int crc) +{ + int i; + for(i=0;i<n;i++) { + crc = (crc_table[data[i] ^ (crc >> 8)] ^ (crc << 8)) & 0xffff; + } + return crc; +} + +static unsigned int mul_poly(unsigned int a, unsigned int b, unsigned int poly) +{ + unsigned int c; + + c = 0; + while (a) { + if (a & 1) + c ^= b; + a = a >> 1; + b = b << 1; + if (b & (1 << 16)) + b ^= poly; + } + return c; +} + +static unsigned int pow_poly(unsigned int a, unsigned int n, unsigned int poly) +{ + unsigned int r; + r = 1; + while (n) { + if (n & 1) + r = mul_poly(r, a, poly); + a = mul_poly(a, a, poly); + n >>= 1; + } + return r; +} + + +/* compute log2(max(abs(tab[]))) */ +static int log2_tab(INT16 *tab, int n) +{ + int i, v; + + v = 0; + for(i=0;i<n;i++) { + v |= abs(tab[i]); + } + return log2(v); +} + +static void lshift_tab(INT16 *tab, int n, int lshift) +{ + int i; + + if (lshift > 0) { + for(i=0;i<n;i++) { + tab[i] <<= lshift; + } + } else if (lshift < 0) { + lshift = -lshift; + for(i=0;i<n;i++) { + tab[i] >>= lshift; + } + } +} + +/* fill the end of the frame and compute the two crcs */ +static int output_frame_end(AC3EncodeContext *s) +{ + int frame_size, frame_size_58, n, crc1, crc2, crc_inv; + UINT8 *frame; + + frame_size = s->frame_size; /* frame size in words */ + /* align to 8 bits */ + flush_put_bits(&s->pb); + /* add zero bytes to reach the frame size */ + frame = s->pb.buf; + n = 2 * s->frame_size - (s->pb.buf_ptr - frame) - 2; + assert(n >= 0); + memset(s->pb.buf_ptr, 0, n); + + /* Now we must compute both crcs : this is not so easy for crc1 + because it is at the beginning of the data... */ + frame_size_58 = (frame_size >> 1) + (frame_size >> 3); + crc1 = ac3_crc(frame + 4, (2 * frame_size_58) - 4, 0); + /* XXX: could precompute crc_inv */ + crc_inv = pow_poly((CRC16_POLY >> 1), (16 * frame_size_58) - 16, CRC16_POLY); + crc1 = mul_poly(crc_inv, crc1, CRC16_POLY); + frame[2] = crc1 >> 8; + frame[3] = crc1; + + crc2 = ac3_crc(frame + 2 * frame_size_58, (frame_size - frame_size_58) * 2 - 2, 0); + frame[2*frame_size - 2] = crc2 >> 8; + frame[2*frame_size - 1] = crc2; + + // printf("n=%d frame_size=%d\n", n, frame_size); + return frame_size * 2; +} + +int AC3_encode_frame(AVEncodeContext *avctx, + unsigned char *frame, int buf_size, void *data) +{ + AC3EncodeContext *s = avctx->priv_data; + short *samples = data; + int i, j, k, v, ch; + INT16 input_samples[N]; + INT32 mdct_coef[NB_BLOCKS][AC3_MAX_CHANNELS][N/2]; + UINT8 exp[NB_BLOCKS][AC3_MAX_CHANNELS][N/2]; + UINT8 exp_strategy[NB_BLOCKS][AC3_MAX_CHANNELS]; + UINT8 encoded_exp[NB_BLOCKS][AC3_MAX_CHANNELS][N/2]; + UINT8 bap[NB_BLOCKS][AC3_MAX_CHANNELS][N/2]; + INT8 exp_samples[NB_BLOCKS][AC3_MAX_CHANNELS]; + int frame_bits; + + frame_bits = 0; + for(ch=0;ch<s->nb_channels;ch++) { + /* fixed mdct to the six sub blocks & exponent computation */ + for(i=0;i<NB_BLOCKS;i++) { + INT16 *sptr; + int sinc; + + /* compute input samples */ + memcpy(input_samples, s->last_samples[ch], N/2 * sizeof(INT16)); + sinc = s->nb_channels; + sptr = samples + (sinc * (N/2) * i) + ch; + for(j=0;j<N/2;j++) { + v = *sptr; + input_samples[j + N/2] = v; + s->last_samples[ch][j] = v; + sptr += sinc; + } + + /* apply the MDCT window */ + for(j=0;j<N/2;j++) { + input_samples[j] = MUL16(input_samples[j], + ac3_window[j]) >> 15; + input_samples[N-j-1] = MUL16(input_samples[N-j-1], + ac3_window[j]) >> 15; + } + + /* Normalize the samples to use the maximum available + precision */ + v = 14 - log2_tab(input_samples, N); + if (v < 0) + v = 0; + exp_samples[i][ch] = v - 8; + lshift_tab(input_samples, N, v); + + /* do the MDCT */ + mdct512(mdct_coef[i][ch], input_samples); + + /* compute "exponents". We take into account the + normalization there */ + for(j=0;j<N/2;j++) { + int e; + v = abs(mdct_coef[i][ch][j]); + if (v == 0) + e = 24; + else { + e = 23 - log2(v) + exp_samples[i][ch]; + if (e >= 24) { + e = 24; + mdct_coef[i][ch][j] = 0; + } + } + exp[i][ch][j] = e; + } + } + + compute_exp_strategy(exp_strategy, exp, ch); + + /* compute the exponents as the decoder will see them. The + EXP_REUSE case must be handled carefully : we select the + min of the exponents */ + i = 0; + while (i < NB_BLOCKS) { + j = i + 1; + while (j < NB_BLOCKS && exp_strategy[j][ch] == EXP_REUSE) { + exponent_min(exp[i][ch], exp[j][ch], s->nb_coefs[ch]); + j++; + } + frame_bits += encode_exp(encoded_exp[i][ch], + exp[i][ch], s->nb_coefs[ch], + exp_strategy[i][ch]); + /* copy encoded exponents for reuse case */ + for(k=i+1;k<j;k++) { + memcpy(encoded_exp[k][ch], encoded_exp[i][ch], + s->nb_coefs[ch] * sizeof(UINT8)); + } + i = j; + } + } + + compute_bit_allocation(s, bap, encoded_exp, exp_strategy, frame_bits); + /* everything is known... let's output the frame */ + output_frame_header(s, frame); + + for(i=0;i<NB_BLOCKS;i++) { + output_audio_block(s, exp_strategy[i], encoded_exp[i], + bap[i], mdct_coef[i], exp_samples[i], i); + } + return output_frame_end(s); +} + +#if 0 +/*************************************************************************/ +/* TEST */ + +#define FN (N/4) + +void fft_test(void) +{ + IComplex in[FN], in1[FN]; + int k, n, i; + float sum_re, sum_im, a; + + /* FFT test */ + + for(i=0;i<FN;i++) { + in[i].re = random() % 65535 - 32767; + in[i].im = random() % 65535 - 32767; + in1[i] = in[i]; + } + fft(in, 7); + + /* do it by hand */ + for(k=0;k<FN;k++) { + sum_re = 0; + sum_im = 0; + for(n=0;n<FN;n++) { + a = -2 * M_PI * (n * k) / FN; + sum_re += in1[n].re * cos(a) - in1[n].im * sin(a); + sum_im += in1[n].re * sin(a) + in1[n].im * cos(a); + } + printf("%3d: %6d,%6d %6.0f,%6.0f\n", + k, in[k].re, in[k].im, sum_re / FN, sum_im / FN); + } +} + +void mdct_test(void) +{ + INT16 input[N]; + INT32 output[N/2]; + float input1[N]; + float output1[N/2]; + float s, a, err, e, emax; + int i, k, n; + + for(i=0;i<N;i++) { + input[i] = (random() % 65535 - 32767) * 9 / 10; + input1[i] = input[i]; + } + + mdct512(output, input); + + /* do it by hand */ + for(k=0;k<N/2;k++) { + s = 0; + for(n=0;n<N;n++) { + a = (2*M_PI*(2*n+1+N/2)*(2*k+1) / (4 * N)); + s += input1[n] * cos(a); + } + output1[k] = -2 * s / N; + } + + err = 0; + emax = 0; + for(i=0;i<N/2;i++) { + printf("%3d: %7d %7.0f\n", i, output[i], output1[i]); + e = output[i] - output1[i]; + if (e > emax) + emax = e; + err += e * e; + } + printf("err2=%f emax=%f\n", err / (N/2), emax); +} + +void test_ac3(void) +{ + AC3EncodeContext ctx; + unsigned char frame[AC3_MAX_CODED_FRAME_SIZE]; + short samples[AC3_FRAME_SIZE]; + int ret, i; + + AC3_encode_init(&ctx, 44100, 64000, 1); + + fft_test(); + mdct_test(); + + for(i=0;i<AC3_FRAME_SIZE;i++) + samples[i] = (int)(sin(2*M_PI*i*1000.0/44100) * 10000); + ret = AC3_encode_frame(&ctx, frame, samples); + printf("ret=%d\n", ret); +} +#endif + +AVEncoder ac3_encoder = { + "ac3", + CODEC_TYPE_AUDIO, + CODEC_ID_AC3, + sizeof(AC3EncodeContext), + AC3_encode_init, + AC3_encode_frame, + NULL, +}; diff --git a/libav/ac3enc.h b/libav/ac3enc.h new file mode 100644 index 0000000000..40cc53aced --- /dev/null +++ b/libav/ac3enc.h @@ -0,0 +1,32 @@ + +#define AC3_FRAME_SIZE (6*256) +#define AC3_MAX_CODED_FRAME_SIZE 3840 /* in bytes */ +#define AC3_MAX_CHANNELS 2 /* we handle at most two channels, although + AC3 allows 6 channels */ + +typedef struct AC3EncodeContext { + PutBitContext pb; + int nb_channels; + int bit_rate; + int sample_rate; + int bsid; + int frame_size_min; /* minimum frame size in case rounding is necessary */ + int frame_size; /* current frame size in words */ + int halfratecod; + int frmsizecod; + int fscod; /* frequency */ + int acmod; + int bsmod; + short last_samples[AC3_MAX_CHANNELS][256]; + int chbwcod[AC3_MAX_CHANNELS]; + int nb_coefs[AC3_MAX_CHANNELS]; + + /* bitrate allocation control */ + int sgaincod, sdecaycod, fdecaycod, dbkneecod, floorcod; + int sgain, sdecay, fdecay, dbknee, floor; + int csnroffst; + int fgaincod[AC3_MAX_CHANNELS]; + int fsnroffst[AC3_MAX_CHANNELS]; + /* mantissa encoding */ + int mant1_cnt, mant2_cnt, mant4_cnt; +} AC3EncodeContext; diff --git a/libav/ac3tab.h b/libav/ac3tab.h new file mode 100644 index 0000000000..2d379f0404 --- /dev/null +++ b/libav/ac3tab.h @@ -0,0 +1,180 @@ +/* tables taken directly from AC3 spec */ + +/* possible bitrates */ +static const UINT16 bitratetab[19] = { + 32, 40, 48, 56, 64, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384, 448, 512, 576, 640 +}; + +/* AC3 MDCT window */ + +/* MDCT window */ +static const INT16 ac3_window[256]= { + 4, 7, 12, 16, 21, 28, 34, 42, + 51, 61, 72, 84, 97, 111, 127, 145, + 164, 184, 207, 231, 257, 285, 315, 347, + 382, 419, 458, 500, 544, 591, 641, 694, + 750, 810, 872, 937, 1007, 1079, 1155, 1235, + 1318, 1406, 1497, 1593, 1692, 1796, 1903, 2016, + 2132, 2253, 2379, 2509, 2644, 2783, 2927, 3076, + 3230, 3389, 3552, 3721, 3894, 4072, 4255, 4444, + 4637, 4835, 5038, 5246, 5459, 5677, 5899, 6127, + 6359, 6596, 6837, 7083, 7334, 7589, 7848, 8112, + 8380, 8652, 8927, 9207, 9491, 9778,10069,10363, +10660,10960,11264,11570,11879,12190,12504,12820, +13138,13458,13780,14103,14427,14753,15079,15407, +15735,16063,16392,16720,17049,17377,17705,18032, +18358,18683,19007,19330,19651,19970,20287,20602, +20914,21225,21532,21837,22139,22438,22733,23025, +23314,23599,23880,24157,24430,24699,24964,25225, +25481,25732,25979,26221,26459,26691,26919,27142, +27359,27572,27780,27983,28180,28373,28560,28742, +28919,29091,29258,29420,29577,29729,29876,30018, +30155,30288,30415,30538,30657,30771,30880,30985, +31086,31182,31274,31363,31447,31528,31605,31678, +31747,31814,31877,31936,31993,32046,32097,32145, +32190,32232,32272,32310,32345,32378,32409,32438, +32465,32490,32513,32535,32556,32574,32592,32608, +32623,32636,32649,32661,32671,32681,32690,32698, +32705,32712,32718,32724,32729,32733,32737,32741, +32744,32747,32750,32752,32754,32756,32757,32759, +32760,32761,32762,32763,32764,32764,32765,32765, +32766,32766,32766,32766,32767,32767,32767,32767, +32767,32767,32767,32767,32767,32767,32767,32767, +32767,32767,32767,32767,32767,32767,32767,32767, +}; + +static UINT8 masktab[253]; + +static const UINT8 latab[260]= { +0x0040,0x003f,0x003e,0x003d,0x003c,0x003b,0x003a,0x0039,0x0038,0x0037, +0x0036,0x0035,0x0034,0x0034,0x0033,0x0032,0x0031,0x0030,0x002f,0x002f, +0x002e,0x002d,0x002c,0x002c,0x002b,0x002a,0x0029,0x0029,0x0028,0x0027, +0x0026,0x0026,0x0025,0x0024,0x0024,0x0023,0x0023,0x0022,0x0021,0x0021, +0x0020,0x0020,0x001f,0x001e,0x001e,0x001d,0x001d,0x001c,0x001c,0x001b, +0x001b,0x001a,0x001a,0x0019,0x0019,0x0018,0x0018,0x0017,0x0017,0x0016, +0x0016,0x0015,0x0015,0x0015,0x0014,0x0014,0x0013,0x0013,0x0013,0x0012, +0x0012,0x0012,0x0011,0x0011,0x0011,0x0010,0x0010,0x0010,0x000f,0x000f, +0x000f,0x000e,0x000e,0x000e,0x000d,0x000d,0x000d,0x000d,0x000c,0x000c, +0x000c,0x000c,0x000b,0x000b,0x000b,0x000b,0x000a,0x000a,0x000a,0x000a, +0x000a,0x0009,0x0009,0x0009,0x0009,0x0009,0x0008,0x0008,0x0008,0x0008, +0x0008,0x0008,0x0007,0x0007,0x0007,0x0007,0x0007,0x0007,0x0006,0x0006, +0x0006,0x0006,0x0006,0x0006,0x0006,0x0006,0x0005,0x0005,0x0005,0x0005, +0x0005,0x0005,0x0005,0x0005,0x0004,0x0004,0x0004,0x0004,0x0004,0x0004, +0x0004,0x0004,0x0004,0x0004,0x0004,0x0003,0x0003,0x0003,0x0003,0x0003, +0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0003,0x0002, +0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002, +0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0002,0x0001,0x0001, +0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001, +0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001, +0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001,0x0001, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +}; + +static const UINT16 hth[50][3]= { +{ 0x04d0,0x04f0,0x0580 }, +{ 0x04d0,0x04f0,0x0580 }, +{ 0x0440,0x0460,0x04b0 }, +{ 0x0400,0x0410,0x0450 }, +{ 0x03e0,0x03e0,0x0420 }, +{ 0x03c0,0x03d0,0x03f0 }, +{ 0x03b0,0x03c0,0x03e0 }, +{ 0x03b0,0x03b0,0x03d0 }, +{ 0x03a0,0x03b0,0x03c0 }, +{ 0x03a0,0x03a0,0x03b0 }, +{ 0x03a0,0x03a0,0x03b0 }, +{ 0x03a0,0x03a0,0x03b0 }, +{ 0x03a0,0x03a0,0x03a0 }, +{ 0x0390,0x03a0,0x03a0 }, +{ 0x0390,0x0390,0x03a0 }, +{ 0x0390,0x0390,0x03a0 }, +{ 0x0380,0x0390,0x03a0 }, +{ 0x0380,0x0380,0x03a0 }, +{ 0x0370,0x0380,0x03a0 }, +{ 0x0370,0x0380,0x03a0 }, +{ 0x0360,0x0370,0x0390 }, +{ 0x0360,0x0370,0x0390 }, +{ 0x0350,0x0360,0x0390 }, +{ 0x0350,0x0360,0x0390 }, +{ 0x0340,0x0350,0x0380 }, +{ 0x0340,0x0350,0x0380 }, +{ 0x0330,0x0340,0x0380 }, +{ 0x0320,0x0340,0x0370 }, +{ 0x0310,0x0320,0x0360 }, +{ 0x0300,0x0310,0x0350 }, +{ 0x02f0,0x0300,0x0340 }, +{ 0x02f0,0x02f0,0x0330 }, +{ 0x02f0,0x02f0,0x0320 }, +{ 0x02f0,0x02f0,0x0310 }, +{ 0x0300,0x02f0,0x0300 }, +{ 0x0310,0x0300,0x02f0 }, +{ 0x0340,0x0320,0x02f0 }, +{ 0x0390,0x0350,0x02f0 }, +{ 0x03e0,0x0390,0x0300 }, +{ 0x0420,0x03e0,0x0310 }, +{ 0x0460,0x0420,0x0330 }, +{ 0x0490,0x0450,0x0350 }, +{ 0x04a0,0x04a0,0x03c0 }, +{ 0x0460,0x0490,0x0410 }, +{ 0x0440,0x0460,0x0470 }, +{ 0x0440,0x0440,0x04a0 }, +{ 0x0520,0x0480,0x0460 }, +{ 0x0800,0x0630,0x0440 }, +{ 0x0840,0x0840,0x0450 }, +{ 0x0840,0x0840,0x04e0 }, +}; + +static const UINT8 baptab[64]= { + 0, 1, 1, 1, 1, 1, 2, 2, 3, 3, + 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, + 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, + 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, + 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, + 15, 15, 15, 15, +}; + +static const UINT8 sdecaytab[4]={ + 0x0f, 0x11, 0x13, 0x15, +}; + +static const UINT8 fdecaytab[4]={ + 0x3f, 0x53, 0x67, 0x7b, +}; + +static const UINT16 sgaintab[4]= { + 0x540, 0x4d8, 0x478, 0x410, +}; + +static const UINT16 dbkneetab[4]= { + 0x000, 0x700, 0x900, 0xb00, +}; + +static const UINT16 floortab[8]= { + 0x2f0, 0x2b0, 0x270, 0x230, 0x1f0, 0x170, 0x0f0, 0xf800, +}; + +static const UINT16 fgaintab[8]= { + 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x400, +}; + +static const UINT8 bndsz[50]={ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, + 3, 6, 6, 6, 6, 6, 6, 12, 12, 12, 12, 24, 24, 24, 24, 24 +}; + +static UINT8 bndtab[51]; + +/* fft & mdct sin cos tables */ +static INT16 costab[64]; +static INT16 sintab[64]; +static INT16 fft_rev[512]; +static INT16 xcos1[128]; +static INT16 xsin1[128]; + +static UINT16 crc_table[256]; diff --git a/libav/avcodec.h b/libav/avcodec.h new file mode 100644 index 0000000000..299f81ab32 --- /dev/null +++ b/libav/avcodec.h @@ -0,0 +1,79 @@ +#include "common.h" + +enum CodecID { + CODEC_ID_NONE, + CODEC_ID_MPEG1VIDEO, + CODEC_ID_H263, + CODEC_ID_RV10, + CODEC_ID_MP2, + CODEC_ID_AC3, + CODEC_ID_MJPEG, +}; + +enum CodecType { + CODEC_TYPE_VIDEO, + CODEC_TYPE_AUDIO, +}; + +typedef struct AVEncodeContext { + int bit_rate; + int rate; /* frames per sec or samples per sec */ + + /* video only */ + int width, height; + int gop_size; /* 0 = intra only */ + + /* audio only */ + int channels; + + /* the following data should not be initialized */ + int frame_size; /* in samples, initialized when calling 'init' */ + int frame_number; /* audio or video frame number */ + int key_frame; /* true if the previous compressed frame was + a key frame (intra, or seekable) */ + struct AVEncoder *codec; + void *priv_data; +} AVEncodeContext; + +typedef struct AVEncoder { + char *name; + int type; + int id; + int priv_data_size; + int (*init)(AVEncodeContext *); + int (*encode)(AVEncodeContext *, UINT8 *buf, int buf_size, void *data); + int (*close)(AVEncodeContext *); + struct AVEncoder *next; +} AVEncoder; + +extern AVEncoder ac3_encoder; +extern AVEncoder mp2_encoder; +extern AVEncoder mpeg1video_encoder; +extern AVEncoder h263_encoder; +extern AVEncoder rv10_encoder; +extern AVEncoder mjpeg_encoder; + +/* resample.c */ + +typedef struct { + /* fractional resampling */ + UINT32 incr; /* fractional increment */ + UINT32 frac; + int last_sample; + /* integer down sample */ + int iratio; /* integer divison ratio */ + int icount, isum; + int inv; +} ReSampleChannelContext; + +typedef struct { + ReSampleChannelContext channel_ctx[2]; + float ratio; + /* channel convert */ + int input_channels, output_channels; +} ReSampleContext; + +int audio_resample_init(ReSampleContext *s, + int output_channels, int input_channels, + int output_rate, int input_rate); +int audio_resample(ReSampleContext *s, short *output, short *input, int nb_samples); diff --git a/libav/common.c b/libav/common.c new file mode 100644 index 0000000000..e60b0dd85b --- /dev/null +++ b/libav/common.c @@ -0,0 +1,174 @@ +/* + * Common bit/dsp utils + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <math.h> +#include "common.h" + +#define NDEBUG +#include <assert.h> + +void init_put_bits(PutBitContext *s, + UINT8 *buffer, int buffer_size, + void *opaque, + void (*write_data)(void *, UINT8 *, int)) +{ + s->buf = buffer; + s->buf_ptr = s->buf; + s->buf_end = s->buf + buffer_size; + s->bit_cnt=0; + s->bit_buf=0; + s->data_out_size = 0; + s->write_data = write_data; + s->opaque = opaque; +} + +static void flush_buffer(PutBitContext *s) +{ + int size; + if (s->write_data) { + size = s->buf_ptr - s->buf; + if (size > 0) + s->write_data(s->opaque, s->buf, size); + s->buf_ptr = s->buf; + s->data_out_size += size; + } +} + +void put_bits(PutBitContext *s, int n, unsigned int value) +{ + unsigned int bit_buf; + int bit_cnt; + + assert(n == 32 || value < (1U << n)); + + bit_buf = s->bit_buf; + bit_cnt = s->bit_cnt; + + // printf("n=%d value=%x cnt=%d buf=%x\n", n, value, bit_cnt, bit_buf); + /* XXX: optimize */ + if (n < (32-bit_cnt)) { + bit_buf |= value << (32 - n - bit_cnt); + bit_cnt+=n; + } else { + bit_buf |= value >> (n + bit_cnt - 32); + *(UINT32 *)s->buf_ptr = htonl(bit_buf); + //printf("bitbuf = %08x\n", bit_buf); + s->buf_ptr+=4; + if (s->buf_ptr >= s->buf_end) + flush_buffer(s); + bit_cnt=bit_cnt + n - 32; + if (bit_cnt == 0) { + bit_buf = 0; + } else { + bit_buf = value << (32 - bit_cnt); + } + } + + s->bit_buf = bit_buf; + s->bit_cnt = bit_cnt; +} + +/* return the number of bits output */ +long long get_bit_count(PutBitContext *s) +{ + return (s->buf_ptr - s->buf + s->data_out_size) * 8 + (long long)s->bit_cnt; +} + +void align_put_bits(PutBitContext *s) +{ + put_bits(s,(8 - s->bit_cnt) & 7,0); +} + +/* pad the end of the output stream with zeros */ +void flush_put_bits(PutBitContext *s) +{ + while (s->bit_cnt > 0) { + /* XXX: should test end of buffer */ + *s->buf_ptr++=s->bit_buf >> 24; + s->bit_buf<<=8; + s->bit_cnt-=8; + } + flush_buffer(s); + s->bit_cnt=0; + s->bit_buf=0; +} + +/* for jpeg : espace 0xff with 0x00 after it */ +void jput_bits(PutBitContext *s, int n, unsigned int value) +{ + unsigned int bit_buf, b; + int bit_cnt, i; + + assert(n == 32 || value < (1U << n)); + + bit_buf = s->bit_buf; + bit_cnt = s->bit_cnt; + + //printf("n=%d value=%x cnt=%d buf=%x\n", n, value, bit_cnt, bit_buf); + /* XXX: optimize */ + if (n < (32-bit_cnt)) { + bit_buf |= value << (32 - n - bit_cnt); + bit_cnt+=n; + } else { + bit_buf |= value >> (n + bit_cnt - 32); + /* handle escape */ + for(i=0;i<4;i++) { + b = (bit_buf >> 24); + *(s->buf_ptr++) = b; + if (b == 0xff) + *(s->buf_ptr++) = 0; + bit_buf <<= 8; + } + /* we flush the buffer sooner to handle worst case */ + if (s->buf_ptr >= (s->buf_end - 8)) + flush_buffer(s); + + bit_cnt=bit_cnt + n - 32; + if (bit_cnt == 0) { + bit_buf = 0; + } else { + bit_buf = value << (32 - bit_cnt); + } + } + + s->bit_buf = bit_buf; + s->bit_cnt = bit_cnt; +} + +/* pad the end of the output stream with zeros */ +void jflush_put_bits(PutBitContext *s) +{ + unsigned int b; + + while (s->bit_cnt > 0) { + b = s->bit_buf >> 24; + *s->buf_ptr++ = b; + if (b == 0xff) + *s->buf_ptr++ = 0; + s->bit_buf<<=8; + s->bit_cnt-=8; + } + flush_buffer(s); + s->bit_cnt=0; + s->bit_buf=0; +} + diff --git a/libav/common.h b/libav/common.h new file mode 100644 index 0000000000..18473eb8e8 --- /dev/null +++ b/libav/common.h @@ -0,0 +1,68 @@ +#ifndef COMMON_H +#define COMMON_H + +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef signed char INT8; +typedef signed short INT16; +typedef signed int INT32; + +/* bit I/O */ + +struct PutBitContext; + +typedef void (*WriteDataFunc)(void *, UINT8 *, int); + +typedef struct PutBitContext { + UINT8 *buf, *buf_ptr, *buf_end; + int bit_cnt; + UINT32 bit_buf; + long long data_out_size; /* in bytes */ + void *opaque; + WriteDataFunc write_data; +} PutBitContext; + +void init_put_bits(PutBitContext *s, + UINT8 *buffer, int buffer_size, + void *opaque, + void (*write_data)(void *, UINT8 *, int)); +void put_bits(PutBitContext *s, int n, unsigned int value); +long long get_bit_count(PutBitContext *s); +void align_put_bits(PutBitContext *s); +void flush_put_bits(PutBitContext *s); + +/* jpeg specific put_bits */ +void jput_bits(PutBitContext *s, int n, unsigned int value); +void jflush_put_bits(PutBitContext *s); + +/* misc math functions */ + +extern inline int log2(unsigned int v) +{ + int n; + + n = 0; + if (v & 0xffff0000) { + v >>= 16; + n += 16; + } + if (v & 0xff00) { + v >>= 8; + n += 8; + } + if (v & 0xf0) { + v >>= 4; + n += 4; + } + if (v & 0xc) { + v >>= 2; + n += 2; + } + if (v & 0x2) { + n++; + } + return n; +} + +#endif diff --git a/libav/h263data.h b/libav/h263data.h new file mode 100644 index 0000000000..1cf6f4d802 --- /dev/null +++ b/libav/h263data.h @@ -0,0 +1,151 @@ +/* DCT coefficients. Four tables, two for last = 0, two for last = 1. + the sign bit must be added afterwards. */ + +/* first part of coeffs for last = 0. Indexed by [run][level-1] */ + +static const UINT8 coeff_tab0[2][12][2] = +{ + /* run = 0 */ + { + {0x02, 2}, {0x0f, 4}, {0x15, 6}, {0x17, 7}, + {0x1f, 8}, {0x25, 9}, {0x24, 9}, {0x21,10}, + {0x20,10}, {0x07,11}, {0x06,11}, {0x20,11} + }, + /* run = 1 */ + { + {0x06, 3}, {0x14, 6}, {0x1e, 8}, {0x0f,10}, + {0x21,11}, {0x50,12}, {0x00, 0}, {0x00, 0}, + {0x00, 0}, {0x00, 0}, {0x00, 0}, {0x00, 0} + } +}; + +/* rest of coeffs for last = 0. indexing by [run-2][level-1] */ + +static const UINT8 coeff_tab1[25][4][2] = +{ + /* run = 2 */ + { + {0x0e, 4}, {0x1d, 8}, {0x0e,10}, {0x51,12} + }, + /* run = 3 */ + { + {0x0d, 5}, {0x23, 9}, {0x0d,10}, {0x00, 0} + }, + /* run = 4-26 */ + { + {0x0c, 5}, {0x22, 9}, {0x52,12}, {0x00, 0} + }, + { + {0x0b, 5}, {0x0c,10}, {0x53,12}, {0x00, 0} + }, + { + {0x13, 6}, {0x0b,10}, {0x54,12}, {0x00, 0} + }, + { + {0x12, 6}, {0x0a,10}, {0x00, 0}, {0x00, 0} + }, + { + {0x11, 6}, {0x09,10}, {0x00, 0}, {0x00, 0} + }, + { + {0x10, 6}, {0x08,10}, {0x00, 0}, {0x00, 0} + }, + { + {0x16, 7}, {0x55,12}, {0x00, 0}, {0x00, 0} + }, + { + {0x15, 7}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x14, 7}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1c, 8}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1b, 8}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x21, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x20, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1f, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1e, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1d, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1c, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1b, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x1a, 9}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x22,11}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x23,11}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x56,12}, {0x00, 0}, {0x00, 0}, {0x00, 0} + }, + { + {0x57,12}, {0x00, 0}, {0x00, 0}, {0x00, 0} + } +}; + +/* first coeffs of last = 1. indexing by [run][level-1] */ + +static const UINT8 coeff_tab2[2][3][2] = +{ + /* run = 0 */ + { + {0x07, 4}, {0x19, 9}, {0x05,11} + }, + /* run = 1 */ + { + {0x0f, 6}, {0x04,11}, {0x00, 0} + } +}; + +/* rest of coeffs for last = 1. indexing by [run-2] */ + +static const UINT8 coeff_tab3[40][2] = +{ + {0x0e, 6}, {0x0d, 6}, {0x0c, 6}, + {0x13, 7}, {0x12, 7}, {0x11, 7}, {0x10, 7}, + {0x1a, 8}, {0x19, 8}, {0x18, 8}, {0x17, 8}, + {0x16, 8}, {0x15, 8}, {0x14, 8}, {0x13, 8}, + {0x18, 9}, {0x17, 9}, {0x16, 9}, {0x15, 9}, + {0x14, 9}, {0x13, 9}, {0x12, 9}, {0x11, 9}, + {0x07,10}, {0x06,10}, {0x05,10}, {0x04,10}, + {0x24,11}, {0x25,11}, {0x26,11}, {0x27,11}, + {0x58,12}, {0x59,12}, {0x5a,12}, {0x5b,12}, + {0x5c,12}, {0x5d,12}, {0x5e,12}, {0x5f,12}, + {0x00, 0} +}; + +/* intra MCBPC, mb_type = 3 */ +static UINT8 intra_MCBPC_code[4] = { 1, 1, 2, 3 }; +static UINT8 intra_MCBPC_bits[4] = { 1, 3, 3, 3 }; + +/* inter MCBPC, mb_type = 0 then 3 */ +static UINT8 inter_MCBPC_code[8] = { 1, 3, 2, 5, 3, 4, 3, 3 }; +static UINT8 inter_MCBPC_bits[8] = { 1, 4, 4, 6, 5, 8, 8, 7 }; + +static UINT8 cbpy_tab[16][2] = +{ + {3,4}, {5,5}, {4,5}, {9,4}, {3,5}, {7,4}, {2,6}, {11,4}, + {2,5}, {3,6}, {5,4}, {10,4}, {4,4}, {8,4}, {6,4}, {3,2} +}; + + diff --git a/libav/h263enc.c b/libav/h263enc.c new file mode 100644 index 0000000000..59db1ee512 --- /dev/null +++ b/libav/h263enc.c @@ -0,0 +1,229 @@ +/* + * H263 backend for ffmpeg encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include "common.h" +#include "mpegvideo.h" +#include "h263data.h" + +void h263_picture_header(MpegEncContext *s, int picture_number) +{ + int format; + + align_put_bits(&s->pb); + put_bits(&s->pb, 22, 0x20); + put_bits(&s->pb, 8, ((s->picture_number * 30) / s->frame_rate) & 0xff); + + put_bits(&s->pb, 1, 1); /* marker */ + put_bits(&s->pb, 1, 0); /* h263 id */ + put_bits(&s->pb, 1, 0); /* split screen off */ + put_bits(&s->pb, 1, 0); /* camera off */ + put_bits(&s->pb, 1, 0); /* freeze picture release off */ + + if (s->width == 128 && s->height == 96) + format = 1; + else if (s->width == 176 && s->height == 144) + format = 2; + else if (s->width == 352 && s->height == 288) + format = 3; + else if (s->width == 704 && s->height == 576) + format = 4; + else if (s->width == 1408 && s->height == 1152) + format = 5; + else + abort(); + + put_bits(&s->pb, 3, format); + + put_bits(&s->pb, 1, (s->pict_type == P_TYPE)); + + put_bits(&s->pb, 1, 0); /* unrestricted motion vector: off */ + + put_bits(&s->pb, 1, 0); /* SAC: off */ + + put_bits(&s->pb, 1, 0); /* advanced prediction mode: off */ + + put_bits(&s->pb, 1, 0); /* not PB frame */ + + put_bits(&s->pb, 5, s->qscale); + + put_bits(&s->pb, 1, 0); /* Continuous Presence Multipoint mode: off */ + + put_bits(&s->pb, 1, 0); /* no PEI */ +} + +static void h263_encode_block(MpegEncContext *s, DCTELEM *block, + int n); + +void h263_encode_mb(MpegEncContext *s, + DCTELEM block[6][64], + int motion_x, int motion_y) +{ + int cbpc, cbpy, i, cbp; + + if (!s->mb_intra) { + /* compute cbp */ + cbp = 0; + for(i=0;i<6;i++) { + if (s->block_last_index[i] >= 0) + cbp |= 1 << (5 - i); + } + if ((cbp | motion_x | motion_y) == 0) { + /* skip macroblock */ + put_bits(&s->pb, 1, 1); + return; + } + + put_bits(&s->pb, 1, 0); /* mb coded */ + cbpc = cbp & 3; + put_bits(&s->pb, + inter_MCBPC_bits[cbpc], + inter_MCBPC_code[cbpc]); + cbpy = cbp >> 2; + cbpy ^= 0xf; + put_bits(&s->pb, cbpy_tab[cbpy][1], cbpy_tab[cbpy][0]); + + /* motion vectors: zero */ + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 1, 1); + + } else { + /* compute cbp */ + cbp = 0; + for(i=0;i<6;i++) { + if (s->block_last_index[i] >= 1) + cbp |= 1 << (5 - i); + } + + cbpc = cbp & 3; + if (s->pict_type == I_TYPE) { + put_bits(&s->pb, + intra_MCBPC_bits[cbpc], + intra_MCBPC_code[cbpc]); + } else { + put_bits(&s->pb, 1, 0); /* mb coded */ + put_bits(&s->pb, + inter_MCBPC_bits[cbpc + 4], + inter_MCBPC_code[cbpc + 4]); + } + cbpy = cbp >> 2; + put_bits(&s->pb, cbpy_tab[cbpy][1], cbpy_tab[cbpy][0]); + } + + /* encode each block */ + for(i=0;i<6;i++) { + h263_encode_block(s, block[i], i); + } +} + +static void h263_encode_block(MpegEncContext *s, DCTELEM *block, int n) +{ + int level, run, last, i, j, last_index, last_non_zero, sign, alevel; + int code, len; + + if (s->mb_intra) { + /* DC coef */ + level = block[0]; + if (level == 128) + put_bits(&s->pb, 8, 0xff); + else + put_bits(&s->pb, 8, level & 0xff); + i = 1; + } else { + i = 0; + } + + /* AC coefs */ + last_index = s->block_last_index[n]; + last_non_zero = i - 1; + for(;i<=last_index;i++) { + j = zigzag_direct[i]; + level = block[j]; + if (level) { + run = i - last_non_zero - 1; + last = (i == last_index); + sign = 0; + alevel = level; + if (level < 0) { + sign = 1; + alevel = -level; + } + len = 0; + code = 0; /* only to disable warning */ + if (last == 0) { + if (run < 2 && alevel < 13 ) { + len = coeff_tab0[run][alevel-1][1]; + code = coeff_tab0[run][alevel-1][0]; + } else if (run >= 2 && run < 27 && alevel < 5) { + len = coeff_tab1[run-2][alevel-1][1]; + code = coeff_tab1[run-2][alevel-1][0]; + } + } else { + if (run < 2 && alevel < 4) { + len = coeff_tab2[run][alevel-1][1]; + code = coeff_tab2[run][alevel-1][0]; + } else if (run >= 2 && run < 42 && alevel == 1) { + len = coeff_tab3[run-2][1]; + code = coeff_tab3[run-2][0]; + } + } + + if (len != 0) { + code = (code << 1) | sign; + put_bits(&s->pb, len + 1, code); + } else { + /* escape */ + put_bits(&s->pb, 7, 3); + put_bits(&s->pb, 1, last); + put_bits(&s->pb, 6, run); + put_bits(&s->pb, 8, level & 0xff); + } + + last_non_zero = i; + } + } +} + +/* write RV 1.0 compatible frame header */ +void rv10_encode_picture_header(MpegEncContext *s, int picture_number) +{ + align_put_bits(&s->pb); + + put_bits(&s->pb, 1, 1); /* marker */ + + put_bits(&s->pb, 1, (s->pict_type == P_TYPE)); + + put_bits(&s->pb, 1, 0); /* not PB frame */ + + put_bits(&s->pb, 5, s->qscale); + + if (s->pict_type == I_TYPE) { + /* specific MPEG like DC coding not used */ + } + + /* if multiple packets per frame are sent, the position at which + to display the macro blocks is coded here */ + put_bits(&s->pb, 6, 0); /* mb_x */ + put_bits(&s->pb, 6, 0); /* mb_y */ + put_bits(&s->pb, 12, s->mb_width * s->mb_height); + + put_bits(&s->pb, 3, 0); /* ignored */ +} + diff --git a/libav/jfdctfst.c b/libav/jfdctfst.c new file mode 100644 index 0000000000..620a03078c --- /dev/null +++ b/libav/jfdctfst.c @@ -0,0 +1,224 @@ +/* + * jfdctfst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#include <stdlib.h> +#include <stdio.h> +#include "common.h" +#include "mpegvideo.h" + +#define DCTSIZE 8 +#define GLOBAL(x) x +#define RIGHT_SHIFT(x, n) ((x) >> (n)) +#define SHIFT_TEMPS + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jfdctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * Again to save a few shifts, the intermediate results between pass 1 and + * pass 2 are not upscaled, but are represented only to integral precision. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#define CONST_BITS 8 + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ +#else +#define FIX_0_382683433 FIX(0.382683433) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_707106781 FIX(0.707106781) +#define FIX_1_306562965 FIX(1.306562965) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_ifast (DCTELEM * data) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z1, z2, z3, z4, z5, z11, z13; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} diff --git a/libav/jrevdct.c b/libav/jrevdct.c new file mode 100644 index 0000000000..26715b0b18 --- /dev/null +++ b/libav/jrevdct.c @@ -0,0 +1,1584 @@ +/* + * jrevdct.c + * + * Copyright (C) 1991, 1992, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the basic inverse-DCT transformation subroutine. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + * + * I've made lots of modifications to attempt to take advantage of the + * sparse nature of the DCT matrices we're getting. Although the logic + * is cumbersome, it's straightforward and the resulting code is much + * faster. + * + * A better way to do this would be to pass in the DCT block as a sparse + * matrix, perhaps with the difference cases encoded. + */ + +typedef int INT32; + +/* Definition of Contant integer scale factor. */ +#define CONST_BITS 13 + +/* Misc DCT definitions */ +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ + +#define GLOBAL /* a function referenced thru EXTERNs */ + +typedef int DCTELEM; +typedef DCTELEM DCTBLOCK[DCTSIZE2]; + +void j_rev_dct (DCTELEM *data); + + +#define GLOBAL /* a function referenced thru EXTERNs */ +#define ORIG_DCT 1 + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +/* + * This routine is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * A 2-D IDCT can be done by 1-D IDCT on each row followed by 1-D IDCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate INT32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#ifdef EIGHT_BIT_SAMPLES +#define PASS1_BITS 2 +#else +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +#define ONE ((INT32) 1) + +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * IMPORTANT: if your compiler doesn't do this arithmetic at compile time, + * you will pay a significant penalty in run time. In that case, figure + * the correct integer constant values and insert them by hand. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) +#define SCALE(x,n) ((INT32)(x) << n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply; + * this provides a useful speedup on many machines. + * There is no way to specify a 16x16->32 multiply in portable C, but + * some C compilers will do the right thing if you provide the correct + * combination of casts. + * NB: for 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#ifdef EIGHT_BIT_SAMPLES +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif +#endif + +#if 0 +/* force a multiplication for x86 where a multiply is fast). We + force the non constant operand to be in a register because + otherwise it may be a 16 bit memory reference, which is not allowed + by imull */ +#define MULTIPLY(a,b) \ +({\ + int res;\ + asm("imull %2,%1,%0" : "=r" (res) : "r" ((int)(a)), "i" (b));\ + res;\ +}) +#endif + +#ifndef MULTIPLY /* default definition */ +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +#ifndef ORIG_DCT + +#undef SSMUL +#define SSMUL(var1,var2) ((INT16)(var1) * (INT32)(INT16)(var2)) + +/* Precomputed idct value arrays. */ + +STATIC DCTELEM PreIDCT[64][64]; + +/* Pre compute singleton coefficient IDCT values. */ +void init_pre_idct() { + int i; + + for (i = 0; i < 64; i++) { + memset ((char *) PreIDCT[i], 0, 64 * sizeof(DCTELEM)); + PreIDCT[i][i] = 2048; + j_rev_dct (PreIDCT[i]); + } +} + +/* + * Perform the inverse DCT on one block of coefficients. + */ + +void j_rev_dct_sparse (data, pos) + DCTBLOCK data; + int pos; +{ + register DCTELEM *dataptr; + short int val; + DCTELEM *ndataptr; + int coeff, rr; + + /* If DC Coefficient. */ + + if (pos == 0) { + register INT32 *dp; + register INT32 v; + + dp = (INT32*)data; + v = *data; + /* Compute 32 bit value to assign. + * This speeds things up a bit */ + if (v < 0) + val = (short)((v - 3) >> 3); + else + val = (short)((v + 4) >> 3); + v = val | ((INT32)val << 16); + dp[0] = v; dp[1] = v; dp[2] = v; dp[3] = v; + dp[4] = v; dp[5] = v; dp[6] = v; dp[7] = v; + dp[8] = v; dp[9] = v; dp[10] = v; dp[11] = v; + dp[12] = v; dp[13] = v; dp[14] = v; dp[15] = v; + dp[16] = v; dp[17] = v; dp[18] = v; dp[19] = v; + dp[20] = v; dp[21] = v; dp[22] = v; dp[23] = v; + dp[24] = v; dp[25] = v; dp[26] = v; dp[27] = v; + dp[28] = v; dp[29] = v; dp[30] = v; dp[31] = v; + return; + } + + /* Some other coefficient. */ + dataptr = (DCTELEM *)data; + coeff = dataptr[pos]; + ndataptr = PreIDCT[pos]; + + for (rr = 0; rr < 4; rr++) { + dataptr[0] = (DCTELEM)(SSMUL (ndataptr[0] , coeff) >> (CONST_BITS-2)); + dataptr[1] = (DCTELEM)(SSMUL (ndataptr[1] , coeff) >> (CONST_BITS-2)); + dataptr[2] = (DCTELEM)(SSMUL (ndataptr[2] , coeff) >> (CONST_BITS-2)); + dataptr[3] = (DCTELEM)(SSMUL (ndataptr[3] , coeff) >> (CONST_BITS-2)); + dataptr[4] = (DCTELEM)(SSMUL (ndataptr[4] , coeff) >> (CONST_BITS-2)); + dataptr[5] = (DCTELEM)(SSMUL (ndataptr[5] , coeff) >> (CONST_BITS-2)); + dataptr[6] = (DCTELEM)(SSMUL (ndataptr[6] , coeff) >> (CONST_BITS-2)); + dataptr[7] = (DCTELEM)(SSMUL (ndataptr[7] , coeff) >> (CONST_BITS-2)); + dataptr[8] = (DCTELEM)(SSMUL (ndataptr[8] , coeff) >> (CONST_BITS-2)); + dataptr[9] = (DCTELEM)(SSMUL (ndataptr[9] , coeff) >> (CONST_BITS-2)); + dataptr[10] = (DCTELEM)(SSMUL (ndataptr[10], coeff) >> (CONST_BITS-2)); + dataptr[11] = (DCTELEM)(SSMUL (ndataptr[11], coeff) >> (CONST_BITS-2)); + dataptr[12] = (DCTELEM)(SSMUL (ndataptr[12], coeff) >> (CONST_BITS-2)); + dataptr[13] = (DCTELEM)(SSMUL (ndataptr[13], coeff) >> (CONST_BITS-2)); + dataptr[14] = (DCTELEM)(SSMUL (ndataptr[14], coeff) >> (CONST_BITS-2)); + dataptr[15] = (DCTELEM)(SSMUL (ndataptr[15], coeff) >> (CONST_BITS-2)); + dataptr += 16; + ndataptr += 16; + } +} + + +void j_rev_dct (data) + DCTBLOCK data; +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + int d0, d1, d2, d3, d4, d5, d6, d7; + register DCTELEM *dataptr; + int rowctr; + SHIFT_TEMPS; + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + + for (rowctr = DCTSIZE - 1; rowctr >= 0; rowctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any row in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * row DCT calculations can be simplified this way. + */ + + register INT32 *idataptr = (INT32*)dataptr; + d0 = dataptr[0]; + d1 = dataptr[1]; + if ((d1 == 0) && (idataptr[1] | idataptr[2] | idataptr[3]) == 0) { + /* AC terms all zero */ + if (d0) { + /* Compute a 32 bit value to assign. */ + DCTELEM dcval = (DCTELEM) (d0 << PASS1_BITS); + register INT32 v = (dcval & 0xffff) | + (((INT32)dcval << 16) & 0xffff0000L); + + idataptr[0] = v; + idataptr[1] = v; + idataptr[2] = v; + idataptr[3] = v; + } + + dataptr += DCTSIZE; /* advance pointer to next row */ + continue; + } + d2 = dataptr[2]; + d3 = dataptr[3]; + d4 = dataptr[4]; + d5 = dataptr[5]; + d6 = dataptr[6]; + d7 = dataptr[7]; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + if (d6) { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, -FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } + } + } else { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = SCALE (d0 + d4, CONST_BITS); + tmp11 = tmp12 = SCALE (d0 - d4, CONST_BITS); + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = SCALE (d4, CONST_BITS); + tmp11 = tmp12 = -tmp10; + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = SCALE (d0, CONST_BITS); + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = 0; + } + } + } + } + + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + if (d7) { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7; + z2 = d5 + d3; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d5, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 = z1 + z4; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5; + z3 = d7; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 = z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */ + tmp0 = MULTIPLY(d7, - FIX(0.601344887)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + tmp1 = MULTIPLY(d5, - FIX(0.509795578)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + z5 = MULTIPLY(d5 + d7, FIX(1.175875602)); + + z3 += z5; + z4 += z5; + + tmp0 += z3; + tmp1 += z4; + tmp2 = z2 + z3; + tmp3 = z1 + z4; + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d1, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(d3, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(d1, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 = z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */ + z3 = d7 + d3; + + tmp0 = MULTIPLY(d7, - FIX(0.601344887)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + tmp2 = MULTIPLY(d3, FIX(0.509795579)); + z2 = MULTIPLY(d3, - FIX(2.562915447)); + z5 = MULTIPLY(z3, FIX(1.175875602)); + z3 = MULTIPLY(z3, - FIX(0.785694958)); + + tmp0 += z3; + tmp1 = z2 + z5; + tmp2 += z3; + tmp3 = z1 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z5 = MULTIPLY(z1, FIX(1.175875602)); + + z1 = MULTIPLY(z1, FIX(0.275899379)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + tmp0 = MULTIPLY(d7, - FIX(1.662939224)); + z4 = MULTIPLY(d1, - FIX(0.390180644)); + tmp3 = MULTIPLY(d1, FIX(1.111140466)); + + tmp0 += z1; + tmp1 = z4 + z5; + tmp2 = z3 + z5; + tmp3 += z1; + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */ + tmp0 = MULTIPLY(d7, - FIX(1.387039845)); + tmp1 = MULTIPLY(d7, FIX(1.175875602)); + tmp2 = MULTIPLY(d7, - FIX(0.785694958)); + tmp3 = MULTIPLY(d7, FIX(0.275899379)); + } + } + } + } else { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(d3 + z4, FIX(1.175875602)); + + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(d1, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(d3, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 = z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + + z5 = MULTIPLY(z2, FIX(1.175875602)); + tmp1 = MULTIPLY(d5, FIX(1.662939225)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + z2 = MULTIPLY(z2, - FIX(1.387039845)); + tmp2 = MULTIPLY(d3, FIX(1.111140466)); + z3 = MULTIPLY(d3, - FIX(1.961570560)); + + tmp0 = z3 + z5; + tmp1 += z2; + tmp2 += z2; + tmp3 = z4 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */ + z4 = d5 + d1; + + z5 = MULTIPLY(z4, FIX(1.175875602)); + z1 = MULTIPLY(d1, - FIX(0.899976223)); + tmp3 = MULTIPLY(d1, FIX(0.601344887)); + tmp1 = MULTIPLY(d5, - FIX(0.509795578)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z4 = MULTIPLY(z4, FIX(0.785694958)); + + tmp0 = z1 + z5; + tmp1 += z4; + tmp2 = z2 + z5; + tmp3 += z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */ + tmp0 = MULTIPLY(d5, FIX(1.175875602)); + tmp1 = MULTIPLY(d5, FIX(0.275899380)); + tmp2 = MULTIPLY(d5, - FIX(1.387039845)); + tmp3 = MULTIPLY(d5, FIX(0.785694958)); + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */ + z5 = d1 + d3; + tmp3 = MULTIPLY(d1, FIX(0.211164243)); + tmp2 = MULTIPLY(d3, - FIX(1.451774981)); + z1 = MULTIPLY(d1, FIX(1.061594337)); + z2 = MULTIPLY(d3, - FIX(2.172734803)); + z4 = MULTIPLY(z5, FIX(0.785694958)); + z5 = MULTIPLY(z5, FIX(1.175875602)); + + tmp0 = z1 - z4; + tmp1 = z2 + z4; + tmp2 += z5; + tmp3 += z5; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d3, - FIX(0.785694958)); + tmp1 = MULTIPLY(d3, - FIX(1.387039845)); + tmp2 = MULTIPLY(d3, - FIX(0.275899379)); + tmp3 = MULTIPLY(d3, FIX(1.175875602)); + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d1, FIX(0.275899379)); + tmp1 = MULTIPLY(d1, FIX(0.785694958)); + tmp2 = MULTIPLY(d1, FIX(1.175875602)); + tmp3 = MULTIPLY(d1, FIX(1.387039845)); + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = tmp1 = tmp2 = tmp3 = 0; + } + } + } + } + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + dataptr[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + dataptr[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + dataptr[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + dataptr = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--) { + /* Columns of zeroes can be exploited in the same way as we did with rows. + * However, the row calculation has created many nonzero AC terms, so the + * simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + + d0 = dataptr[DCTSIZE*0]; + d1 = dataptr[DCTSIZE*1]; + d2 = dataptr[DCTSIZE*2]; + d3 = dataptr[DCTSIZE*3]; + d4 = dataptr[DCTSIZE*4]; + d5 = dataptr[DCTSIZE*5]; + d6 = dataptr[DCTSIZE*6]; + d7 = dataptr[DCTSIZE*7]; + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + if (d6) { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, -FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 != 0 */ + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 != 0 */ + tmp2 = MULTIPLY(d6, - FIX(1.306562965)); + tmp3 = MULTIPLY(d6, FIX(0.541196100)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } + } + } else { + if (d4) { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 != 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp2 - tmp0; + tmp12 = -(tmp0 + tmp2); + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = SCALE (d0 + d4, CONST_BITS); + tmp11 = tmp12 = SCALE (d0 - d4, CONST_BITS); + } else { + /* d0 == 0, d2 == 0, d4 != 0, d6 == 0 */ + tmp10 = tmp13 = SCALE (d4, CONST_BITS); + tmp11 = tmp12 = -tmp10; + } + } + } else { + if (d2) { + if (d0) { + /* d0 != 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp0 = SCALE (d0, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + } else { + /* d0 == 0, d2 != 0, d4 == 0, d6 == 0 */ + tmp2 = MULTIPLY(d2, FIX(0.541196100)); + tmp3 = MULTIPLY(d2, FIX(1.306562965)); + + tmp10 = tmp3; + tmp13 = -tmp3; + tmp11 = tmp2; + tmp12 = -tmp2; + } + } else { + if (d0) { + /* d0 != 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = SCALE (d0, CONST_BITS); + } else { + /* d0 == 0, d2 == 0, d4 == 0, d6 == 0 */ + tmp10 = tmp13 = tmp11 = tmp12 = 0; + } + } + } + } + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + if (d7) { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 != 0 */ + z1 = d7; + z2 = d5 + d3; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d5, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 = z1 + z4; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 != 0 */ + z1 = d7 + d1; + z2 = d5; + z3 = d7; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 = z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 != 0 */ + tmp0 = MULTIPLY(d7, - FIX(0.601344887)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + tmp1 = MULTIPLY(d5, - FIX(0.509795578)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + z5 = MULTIPLY(d5 + d7, FIX(1.175875602)); + + z3 += z5; + z4 += z5; + + tmp0 += z3; + tmp1 += z4; + tmp2 = z2 + z3; + tmp3 = z1 + z4; + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z3 = d7 + d3; + z5 = MULTIPLY(z3 + d1, FIX(1.175875602)); + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(z1, - FIX(0.899976223)); + z2 = MULTIPLY(d3, - FIX(2.562915447)); + z3 = MULTIPLY(z3, - FIX(1.961570560)); + z4 = MULTIPLY(d1, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 = z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 != 0 */ + z3 = d7 + d3; + + tmp0 = MULTIPLY(d7, - FIX(0.601344887)); + z1 = MULTIPLY(d7, - FIX(0.899976223)); + tmp2 = MULTIPLY(d3, FIX(0.509795579)); + z2 = MULTIPLY(d3, - FIX(2.562915447)); + z5 = MULTIPLY(z3, FIX(1.175875602)); + z3 = MULTIPLY(z3, - FIX(0.785694958)); + + tmp0 += z3; + tmp1 = z2 + z5; + tmp2 += z3; + tmp3 = z1 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 != 0 */ + z1 = d7 + d1; + z5 = MULTIPLY(z1, FIX(1.175875602)); + + z1 = MULTIPLY(z1, FIX(0.275899379)); + z3 = MULTIPLY(d7, - FIX(1.961570560)); + tmp0 = MULTIPLY(d7, - FIX(1.662939224)); + z4 = MULTIPLY(d1, - FIX(0.390180644)); + tmp3 = MULTIPLY(d1, FIX(1.111140466)); + + tmp0 += z1; + tmp1 = z4 + z5; + tmp2 = z3 + z5; + tmp3 += z1; + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 != 0 */ + tmp0 = MULTIPLY(d7, - FIX(1.387039845)); + tmp1 = MULTIPLY(d7, FIX(1.175875602)); + tmp2 = MULTIPLY(d7, - FIX(0.785694958)); + tmp3 = MULTIPLY(d7, FIX(0.275899379)); + } + } + } + } else { + if (d5) { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(d3 + z4, FIX(1.175875602)); + + tmp1 = MULTIPLY(d5, FIX(2.053119869)); + tmp2 = MULTIPLY(d3, FIX(3.072711026)); + tmp3 = MULTIPLY(d1, FIX(1.501321110)); + z1 = MULTIPLY(d1, - FIX(0.899976223)); + z2 = MULTIPLY(z2, - FIX(2.562915447)); + z3 = MULTIPLY(d3, - FIX(1.961570560)); + z4 = MULTIPLY(z4, - FIX(0.390180644)); + + z3 += z5; + z4 += z5; + + tmp0 = z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + } else { + /* d1 == 0, d3 != 0, d5 != 0, d7 == 0 */ + z2 = d5 + d3; + + z5 = MULTIPLY(z2, FIX(1.175875602)); + tmp1 = MULTIPLY(d5, FIX(1.662939225)); + z4 = MULTIPLY(d5, - FIX(0.390180644)); + z2 = MULTIPLY(z2, - FIX(1.387039845)); + tmp2 = MULTIPLY(d3, FIX(1.111140466)); + z3 = MULTIPLY(d3, - FIX(1.961570560)); + + tmp0 = z3 + z5; + tmp1 += z2; + tmp2 += z2; + tmp3 = z4 + z5; + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 != 0, d7 == 0 */ + z4 = d5 + d1; + + z5 = MULTIPLY(z4, FIX(1.175875602)); + z1 = MULTIPLY(d1, - FIX(0.899976223)); + tmp3 = MULTIPLY(d1, FIX(0.601344887)); + tmp1 = MULTIPLY(d5, - FIX(0.509795578)); + z2 = MULTIPLY(d5, - FIX(2.562915447)); + z4 = MULTIPLY(z4, FIX(0.785694958)); + + tmp0 = z1 + z5; + tmp1 += z4; + tmp2 = z2 + z5; + tmp3 += z4; + } else { + /* d1 == 0, d3 == 0, d5 != 0, d7 == 0 */ + tmp0 = MULTIPLY(d5, FIX(1.175875602)); + tmp1 = MULTIPLY(d5, FIX(0.275899380)); + tmp2 = MULTIPLY(d5, - FIX(1.387039845)); + tmp3 = MULTIPLY(d5, FIX(0.785694958)); + } + } + } else { + if (d3) { + if (d1) { + /* d1 != 0, d3 != 0, d5 == 0, d7 == 0 */ + z5 = d1 + d3; + tmp3 = MULTIPLY(d1, FIX(0.211164243)); + tmp2 = MULTIPLY(d3, - FIX(1.451774981)); + z1 = MULTIPLY(d1, FIX(1.061594337)); + z2 = MULTIPLY(d3, - FIX(2.172734803)); + z4 = MULTIPLY(z5, FIX(0.785694958)); + z5 = MULTIPLY(z5, FIX(1.175875602)); + + tmp0 = z1 - z4; + tmp1 = z2 + z4; + tmp2 += z5; + tmp3 += z5; + } else { + /* d1 == 0, d3 != 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d3, - FIX(0.785694958)); + tmp1 = MULTIPLY(d3, - FIX(1.387039845)); + tmp2 = MULTIPLY(d3, - FIX(0.275899379)); + tmp3 = MULTIPLY(d3, FIX(1.175875602)); + } + } else { + if (d1) { + /* d1 != 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = MULTIPLY(d1, FIX(0.275899379)); + tmp1 = MULTIPLY(d1, FIX(0.785694958)); + tmp2 = MULTIPLY(d1, FIX(1.175875602)); + tmp3 = MULTIPLY(d1, FIX(1.387039845)); + } else { + /* d1 == 0, d3 == 0, d5 == 0, d7 == 0 */ + tmp0 = tmp1 = tmp2 = tmp3 = 0; + } + } + } + } + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3); + + dataptr++; /* advance pointer to next column */ + } +} + +#else + +/*---- debugging/tracing macros ----*/ + +#if _MSC_VER +#pragma optimize("",on) +#if _MSC_VER > 700 +/*#pragma optimize("l",off)*/ +#endif +#endif + +#define idct_single_pos0() +#define idct_zero_col_stat() +#define idct_zero_row_stat() +#define idct_nonzero_col_stat() +#define idct_nonzero_row_stat() +#define DUMP_COEFS(p) +#define TRACE(args) +#define FAST_DCTPTRS 1 + +#if 0 /* to count cases */ +void idct_single_pos0 (void) { static int count; count++; } +void idct_zero_col_stat (void) { static int count; count++; } +void idct_zero_row_stat (void) { static int count; count++; } +void idct_nonzero_col_stat (void) { static int count; count++; } +void idct_nonzero_row_stat (void) { static int count; count++; } +#undef idct_single_pos0 +#undef idct_zero_col_stat +#undef idct_zero_row_stat +#undef idct_nonzero_col_stat +#undef idct_nonzero_row_stat +#endif + +void init_pre_idct (void) { } + +void j_rev_dct_sparse (DCTBLOCK data, int pos) +{ + /* If just DC Coefficient. */ + + if (pos == 0) { + register DCTELEM *dp, *dq; + DCTELEM dcval; + + idct_single_pos0(); + + dp = data; + dcval = dp[0]; + if (dcval < 0) + dcval = (short)((dcval - 3) >> 3); + else + dcval = (short)((dcval + 4) >> 3); + + if (dcval) { + for (dq = dp + 64; dp < dq; dp += 8) { + dp[3] = dp[2] = dp[1] = dp[0] = dcval; + dp[7] = dp[6] = dp[5] = dp[4] = dcval; + } + } + return; + } + + /* Some other coeff */ + j_rev_dct (data); +} + +#ifndef OPTIMIZE_ASM +void j_rev_dct (DCTBLOCK data) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + register DCTELEM *dp; + int rowctr; + SHIFT_TEMPS; + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + DUMP_COEFS(data); + + dp = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--, dp += DCTSIZE) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any row in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * row DCT calculations can be simplified this way. + */ + +#if FAST_DCTPTRS +#define d0 dp[0] +#define d1 dp[1] +#define d2 dp[2] +#define d3 dp[3] +#define d4 dp[4] +#define d5 dp[5] +#define d6 dp[6] +#define d7 dp[7] +#else + int d0 = dp[0]; + int d1 = dp[1]; + int d2 = dp[2]; + int d3 = dp[3]; + int d4 = dp[4]; + int d5 = dp[5]; + int d6 = dp[6]; + int d7 = dp[7]; +#endif + +#ifndef NO_ZERO_ROW_TEST + if ((d1 | d2 | d3 | d4 | d5 | d6 | d7) == 0) { + /* AC terms all zero */ + DCTELEM dcval = (DCTELEM) (d0 << PASS1_BITS); + + if (d0) { + dp[0] = dcval; + dp[1] = dcval; + dp[2] = dcval; + dp[3] = dcval; + dp[4] = dcval; + dp[5] = dcval; + dp[6] = dcval; + dp[7] = dcval; + } + idct_zero_row_stat(); + continue; + } +#endif + + idct_nonzero_row_stat(); + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(d5, FIX(2.053119869)); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(d3, FIX(3.072711026)); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(d1, FIX(1.501321110)); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX(0.899976223)); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX(2.562915447)); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX(1.961570560)); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX(0.390180644)); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dp[0] = (DCTELEM) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + dp[7] = (DCTELEM) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + dp[1] = (DCTELEM) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + dp[6] = (DCTELEM) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + dp[2] = (DCTELEM) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + dp[5] = (DCTELEM) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + dp[3] = (DCTELEM) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + dp[4] = (DCTELEM) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + } +#if FAST_DCTPTRS +#undef d0 +#undef d1 +#undef d2 +#undef d3 +#undef d4 +#undef d5 +#undef d6 +#undef d7 +#endif + + /* Pass 2: process columns. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + dp = data; + for (rowctr = DCTSIZE-1; rowctr >= 0; rowctr--, dp++) { + /* Columns of zeroes can be exploited in the same way as we did with rows. + * However, the row calculation has created many nonzero AC terms, so the + * simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#if FAST_DCTPTRS +#define d0 dp[DCTSIZE*0] +#define d1 dp[DCTSIZE*1] +#define d2 dp[DCTSIZE*2] +#define d3 dp[DCTSIZE*3] +#define d4 dp[DCTSIZE*4] +#define d5 dp[DCTSIZE*5] +#define d6 dp[DCTSIZE*6] +#define d7 dp[DCTSIZE*7] +#else + int d0 = dp[DCTSIZE*0]; + int d1 = dp[DCTSIZE*1]; + int d2 = dp[DCTSIZE*2]; + int d3 = dp[DCTSIZE*3]; + int d4 = dp[DCTSIZE*4]; + int d5 = dp[DCTSIZE*5]; + int d6 = dp[DCTSIZE*6]; + int d7 = dp[DCTSIZE*7]; +#endif + +#ifndef NO_ZERO_COLUMN_TEST + if ((d1 | d2 | d3 | d4 | d5 | d6 | d7) == 0) { + /* AC terms all zero */ + DCTELEM dcval = (DCTELEM) DESCALE((INT32) d0, PASS1_BITS+3); + + if (d0) { + dp[DCTSIZE*0] = dcval; + dp[DCTSIZE*1] = dcval; + dp[DCTSIZE*2] = dcval; + dp[DCTSIZE*3] = dcval; + dp[DCTSIZE*4] = dcval; + dp[DCTSIZE*5] = dcval; + dp[DCTSIZE*6] = dcval; + dp[DCTSIZE*7] = dcval; + } + idct_zero_col_stat(); + continue; + } +#endif + + idct_nonzero_col_stat(); + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z1 = MULTIPLY(d2 + d6, FIX(0.541196100)); + tmp2 = z1 + MULTIPLY(d6, - FIX(1.847759065)); + tmp3 = z1 + MULTIPLY(d2, FIX(0.765366865)); + + tmp0 = SCALE (d0 + d4, CONST_BITS); + tmp1 = SCALE (d0 - d4, CONST_BITS); + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + z1 = d7 + d1; + z2 = d5 + d3; + z3 = d7 + d3; + z4 = d5 + d1; + z5 = MULTIPLY(z3 + z4, FIX(1.175875602)); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(d7, FIX(0.298631336)); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(d5, FIX(2.053119869)); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(d3, FIX(3.072711026)); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(d1, FIX(1.501321110)); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX(0.899976223)); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX(2.562915447)); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX(1.961570560)); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX(0.390180644)); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + dp[DCTSIZE*0] = (DCTELEM)DESCALE(tmp10 + tmp3, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*7] = (DCTELEM)DESCALE(tmp10 - tmp3, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*1] = (DCTELEM)DESCALE(tmp11 + tmp2, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*6] = (DCTELEM)DESCALE(tmp11 - tmp2, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*2] = (DCTELEM)DESCALE(tmp12 + tmp1, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*5] = (DCTELEM)DESCALE(tmp12 - tmp1, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*3] = (DCTELEM)DESCALE(tmp13 + tmp0, CONST_BITS+PASS1_BITS+3); + dp[DCTSIZE*4] = (DCTELEM)DESCALE(tmp13 - tmp0, CONST_BITS+PASS1_BITS+3); + } +#if FAST_DCTPTRS +#undef d0 +#undef d1 +#undef d2 +#undef d3 +#undef d4 +#undef d5 +#undef d6 +#undef d7 +#endif +} +#endif /* optimize.asm */ + +#endif diff --git a/libav/mjpegenc.c b/libav/mjpegenc.c new file mode 100644 index 0000000000..027287528c --- /dev/null +++ b/libav/mjpegenc.c @@ -0,0 +1,416 @@ +/* + * MJPEG encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include "avcodec.h" +#include "mpegvideo.h" + +typedef struct MJpegContext { + UINT8 huff_size_dc_luminance[12]; + UINT16 huff_code_dc_luminance[12]; + UINT8 huff_size_dc_chrominance[12]; + UINT16 huff_code_dc_chrominance[12]; + + UINT8 huff_size_ac_luminance[256]; + UINT16 huff_code_ac_luminance[256]; + UINT8 huff_size_ac_chrominance[256]; + UINT16 huff_code_ac_chrominance[256]; +} MJpegContext; + +#define SOF0 0xc0 +#define SOI 0xd8 +#define EOI 0xd9 +#define DQT 0xdb +#define DHT 0xc4 +#define SOS 0xda + +#if 0 +/* These are the sample quantization tables given in JPEG spec section K.1. + * The spec says that the values given produce "good" quality, and + * when divided by 2, "very good" quality. + */ +static const unsigned char std_luminance_quant_tbl[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; +static const unsigned char std_chrominance_quant_tbl[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; +#endif + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +static const UINT8 bits_dc_luminance[17] = +{ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; +static const UINT8 val_dc_luminance[] = +{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + +static const UINT8 bits_dc_chrominance[17] = +{ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; +static const UINT8 val_dc_chrominance[] = +{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + +static const UINT8 bits_ac_luminance[17] = +{ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; +static const UINT8 val_ac_luminance[] = +{ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const UINT8 bits_ac_chrominance[17] = +{ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + +static const UINT8 val_ac_chrominance[] = +{ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + + +/* isn't this function nicer than the one in the libjpeg ? */ +static void build_huffman_codes(UINT8 *huff_size, UINT16 *huff_code, + const UINT8 *bits_table, const UINT8 *val_table) +{ + int i, j, k,nb, code, sym; + + code = 0; + k = 0; + for(i=1;i<=16;i++) { + nb = bits_table[i]; + for(j=0;j<nb;j++) { + sym = val_table[k++]; + huff_size[sym] = i; + huff_code[sym] = code; + code++; + } + code <<= 1; + } +} + +int mjpeg_init(MpegEncContext *s) +{ + MJpegContext *m; + + m = malloc(sizeof(MJpegContext)); + if (!m) + return -1; + + /* build all the huffman tables */ + build_huffman_codes(m->huff_size_dc_luminance, + m->huff_code_dc_luminance, + bits_dc_luminance, + val_dc_luminance); + build_huffman_codes(m->huff_size_dc_chrominance, + m->huff_code_dc_chrominance, + bits_dc_chrominance, + val_dc_chrominance); + build_huffman_codes(m->huff_size_ac_luminance, + m->huff_code_ac_luminance, + bits_ac_luminance, + val_ac_luminance); + build_huffman_codes(m->huff_size_ac_chrominance, + m->huff_code_ac_chrominance, + bits_ac_chrominance, + val_ac_chrominance); + + s->mjpeg_ctx = m; + return 0; +} + +void mjpeg_close(MpegEncContext *s) +{ + free(s->mjpeg_ctx); +} + +static inline void put_marker(PutBitContext *p, int code) +{ + put_bits(p, 8, 0xff); + put_bits(p, 8, code); +} + +/* table_class: 0 = DC coef, 1 = AC coefs */ +static int put_huffman_table(MpegEncContext *s, int table_class, int table_id, + const UINT8 *bits_table, const UINT8 *value_table) +{ + PutBitContext *p = &s->pb; + int n, i; + + put_bits(p, 4, table_class); + put_bits(p, 4, table_id); + + n = 0; + for(i=1;i<=16;i++) { + n += bits_table[i]; + put_bits(p, 8, bits_table[i]); + } + + for(i=0;i<n;i++) + put_bits(p, 8, value_table[i]); + + return n + 17; +} + +static void jpeg_table_header(MpegEncContext *s) +{ + PutBitContext *p = &s->pb; + int i, size; + UINT8 *ptr; + + /* quant matrixes */ + put_marker(p, DQT); + put_bits(p, 16, 2 + 1 * (1 + 64)); + put_bits(p, 4, 0); /* 8 bit precision */ + put_bits(p, 4, 0); /* table 0 */ + for(i=0;i<64;i++) { + put_bits(p, 8, s->init_intra_matrix[i]); + } +#if 0 + put_bits(p, 4, 0); /* 8 bit precision */ + put_bits(p, 4, 1); /* table 1 */ + for(i=0;i<64;i++) { + put_bits(p, 8, m->chrominance_matrix[i]); + } +#endif + + /* huffman table */ + put_marker(p, DHT); + flush_put_bits(p); + ptr = p->buf_ptr; + put_bits(p, 16, 0); /* patched later */ + size = 2; + size += put_huffman_table(s, 0, 0, bits_dc_luminance, val_dc_luminance); + size += put_huffman_table(s, 0, 1, bits_dc_chrominance, val_dc_chrominance); + + size += put_huffman_table(s, 1, 0, bits_ac_luminance, val_ac_luminance); + size += put_huffman_table(s, 1, 1, bits_ac_chrominance, val_ac_chrominance); + ptr[0] = size >> 8; + ptr[1] = size; +} + +void mjpeg_picture_header(MpegEncContext *s) +{ + put_marker(&s->pb, SOI); + + jpeg_table_header(s); + + put_marker(&s->pb, SOF0); + + put_bits(&s->pb, 16, 17); + put_bits(&s->pb, 8, 8); /* 8 bits/component */ + put_bits(&s->pb, 16, s->height); + put_bits(&s->pb, 16, s->width); + put_bits(&s->pb, 8, 3); /* 3 components */ + + /* Y component */ + put_bits(&s->pb, 8, 1); /* component number */ + put_bits(&s->pb, 4, 2); /* H factor */ + put_bits(&s->pb, 4, 2); /* V factor */ + put_bits(&s->pb, 8, 0); /* select matrix */ + + /* Cb component */ + put_bits(&s->pb, 8, 2); /* component number */ + put_bits(&s->pb, 4, 1); /* H factor */ + put_bits(&s->pb, 4, 1); /* V factor */ + put_bits(&s->pb, 8, 0); /* select matrix */ + + /* Cr component */ + put_bits(&s->pb, 8, 3); /* component number */ + put_bits(&s->pb, 4, 1); /* H factor */ + put_bits(&s->pb, 4, 1); /* V factor */ + put_bits(&s->pb, 8, 0); /* select matrix */ + + /* scan header */ + put_marker(&s->pb, SOS); + put_bits(&s->pb, 16, 12); /* length */ + put_bits(&s->pb, 8, 3); /* 3 components */ + + /* Y component */ + put_bits(&s->pb, 8, 1); /* index */ + put_bits(&s->pb, 4, 0); /* DC huffman table index */ + put_bits(&s->pb, 4, 0); /* AC huffman table index */ + + /* Cb component */ + put_bits(&s->pb, 8, 2); /* index */ + put_bits(&s->pb, 4, 1); /* DC huffman table index */ + put_bits(&s->pb, 4, 1); /* AC huffman table index */ + + /* Cr component */ + put_bits(&s->pb, 8, 3); /* index */ + put_bits(&s->pb, 4, 1); /* DC huffman table index */ + put_bits(&s->pb, 4, 1); /* AC huffman table index */ + + put_bits(&s->pb, 8, 0); /* Ss (not used) */ + put_bits(&s->pb, 8, 63); /* Se (not used) */ + put_bits(&s->pb, 8, 0); /* (not used) */ +} + +void mjpeg_picture_trailer(MpegEncContext *s) +{ + jflush_put_bits(&s->pb); + put_marker(&s->pb, EOI); +} + +static inline void encode_dc(MpegEncContext *s, int val, + UINT8 *huff_size, UINT16 *huff_code) +{ + int mant, nbits; + + if (val == 0) { + jput_bits(&s->pb, huff_size[0], huff_code[0]); + } else { + mant = val; + if (val < 0) { + val = -val; + mant--; + } + + /* compute the log (XXX: optimize) */ + nbits = 0; + while (val != 0) { + val = val >> 1; + nbits++; + } + + jput_bits(&s->pb, huff_size[nbits], huff_code[nbits]); + + jput_bits(&s->pb, nbits, mant & ((1 << nbits) - 1)); + } +} + +static void encode_block(MpegEncContext *s, DCTELEM *block, int n) +{ + int mant, nbits, code, i, j; + int component, dc, run, last_index, val; + MJpegContext *m = s->mjpeg_ctx; + UINT8 *huff_size_ac; + UINT16 *huff_code_ac; + + /* DC coef */ + component = (n <= 3 ? 0 : n - 4 + 1); + dc = block[0]; /* overflow is impossible */ + val = dc - s->last_dc[component]; + if (n < 4) { + encode_dc(s, val, m->huff_size_dc_luminance, m->huff_code_dc_luminance); + huff_size_ac = m->huff_size_ac_luminance; + huff_code_ac = m->huff_code_ac_luminance; + } else { + encode_dc(s, val, m->huff_size_dc_chrominance, m->huff_code_dc_chrominance); + huff_size_ac = m->huff_size_ac_chrominance; + huff_code_ac = m->huff_code_ac_chrominance; + } + s->last_dc[component] = dc; + + /* AC coefs */ + + run = 0; + last_index = s->block_last_index[n]; + for(i=1;i<=last_index;i++) { + j = zigzag_direct[i]; + val = block[j]; + if (val == 0) { + run++; + } else { + while (run >= 16) { + jput_bits(&s->pb, huff_size_ac[0xf0], huff_code_ac[0xf0]); + run -= 16; + } + mant = val; + if (val < 0) { + val = -val; + mant--; + } + + /* compute the log (XXX: optimize) */ + nbits = 0; + while (val != 0) { + val = val >> 1; + nbits++; + } + code = (run << 4) | nbits; + + jput_bits(&s->pb, huff_size_ac[code], huff_code_ac[code]); + + jput_bits(&s->pb, nbits, mant & ((1 << nbits) - 1)); + run = 0; + } + } + + /* output EOB only if not already 64 values */ + if (last_index < 63 || run != 0) + jput_bits(&s->pb, huff_size_ac[0], huff_code_ac[0]); +} + +void mjpeg_encode_mb(MpegEncContext *s, + DCTELEM block[6][64]) +{ + int i; + for(i=0;i<6;i++) { + encode_block(s, block[i], i); + } +} diff --git a/libav/mpegaudio.c b/libav/mpegaudio.c new file mode 100644 index 0000000000..50ffc3c200 --- /dev/null +++ b/libav/mpegaudio.c @@ -0,0 +1,754 @@ +/* + * The simplest mpeg audio layer 2 encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <math.h> +#include "avcodec.h" +#include "mpegaudio.h" + +#define NDEBUG +#include <assert.h> + +/* define it to use floats in quantization (I don't like floats !) */ +//#define USE_FLOATS + +#define MPA_STEREO 0 +#define MPA_JSTEREO 1 +#define MPA_DUAL 2 +#define MPA_MONO 3 + +#include "mpegaudiotab.h" + +int MPA_encode_init(AVEncodeContext *avctx) +{ + MpegAudioContext *s = avctx->priv_data; + int freq = avctx->rate; + int bitrate = avctx->bit_rate; + int channels = avctx->channels; + int i, v, table; + float a; + + if (channels != 1) + return -1; + + bitrate = bitrate / 1000; + s->freq = freq; + s->bit_rate = bitrate * 1000; + avctx->frame_size = MPA_FRAME_SIZE; + avctx->key_frame = 1; /* always key frame */ + + /* encoding freq */ + s->lsf = 0; + for(i=0;i<3;i++) { + if (freq_tab[i] == freq) + break; + if ((freq_tab[i] / 2) == freq) { + s->lsf = 1; + break; + } + } + if (i == 3) + return -1; + s->freq_index = i; + + /* encoding bitrate & frequency */ + for(i=0;i<15;i++) { + if (bitrate_tab[1-s->lsf][i] == bitrate) + break; + } + if (i == 15) + return -1; + s->bitrate_index = i; + + /* compute total header size & pad bit */ + + a = (float)(bitrate * 1000 * MPA_FRAME_SIZE) / (freq * 8.0); + s->frame_size = ((int)a) * 8; + + /* frame fractional size to compute padding */ + s->frame_frac = 0; + s->frame_frac_incr = (int)((a - floor(a)) * 65536.0); + + /* select the right allocation table */ + if (!s->lsf) { + if ((freq == 48000 && bitrate >= 56) || + (bitrate >= 56 && bitrate <= 80)) + table = 0; + else if (freq != 48000 && bitrate >= 96) + table = 1; + else if (freq != 32000 && bitrate <= 48) + table = 2; + else + table = 3; + } else { + table = 4; + } + /* number of used subbands */ + s->sblimit = sblimit_table[table]; + s->alloc_table = alloc_tables[table]; + +#ifdef DEBUG + printf("%d kb/s, %d Hz, frame_size=%d bits, table=%d, padincr=%x\n", + bitrate, freq, s->frame_size, table, s->frame_frac_incr); +#endif + + s->samples_offset = 0; + + for(i=0;i<512;i++) { + float a = enwindow[i] * 32768.0 * 16.0; + filter_bank[i] = (int)(a); + } + for(i=0;i<64;i++) { + v = (int)(pow(2.0, (3 - i) / 3.0) * (1 << 20)); + if (v <= 0) + v = 1; + scale_factor_table[i] = v; +#ifdef USE_FLOATS + scale_factor_inv_table[i] = pow(2.0, -(3 - i) / 3.0) / (float)(1 << 20); +#else +#define P 15 + scale_factor_shift[i] = 21 - P - (i / 3); + scale_factor_mult[i] = (1 << P) * pow(2.0, (i % 3) / 3.0); +#endif + } + for(i=0;i<128;i++) { + v = i - 64; + if (v <= -3) + v = 0; + else if (v < 0) + v = 1; + else if (v == 0) + v = 2; + else if (v < 3) + v = 3; + else + v = 4; + scale_diff_table[i] = v; + } + + for(i=0;i<17;i++) { + v = quant_bits[i]; + if (v < 0) + v = -v; + else + v = v * 3; + total_quant_bits[i] = 12 * v; + } + + return 0; +} + +/* 32 point floating point IDCT */ +static void idct32(int *out, int *tab, int sblimit, int left_shift) +{ + int i, j; + int *t, *t1, xr; + const int *xp = costab32; + + for(j=31;j>=3;j-=2) tab[j] += tab[j - 2]; + + t = tab + 30; + t1 = tab + 2; + do { + t[0] += t[-4]; + t[1] += t[1 - 4]; + t -= 4; + } while (t != t1); + + t = tab + 28; + t1 = tab + 4; + do { + t[0] += t[-8]; + t[1] += t[1-8]; + t[2] += t[2-8]; + t[3] += t[3-8]; + t -= 8; + } while (t != t1); + + t = tab; + t1 = tab + 32; + do { + t[ 3] = -t[ 3]; + t[ 6] = -t[ 6]; + + t[11] = -t[11]; + t[12] = -t[12]; + t[13] = -t[13]; + t[15] = -t[15]; + t += 16; + } while (t != t1); + + + t = tab; + t1 = tab + 8; + do { + int x1, x2, x3, x4; + + x3 = MUL(t[16], FIX(SQRT2*0.5)); + x4 = t[0] - x3; + x3 = t[0] + x3; + + x2 = MUL(-(t[24] + t[8]), FIX(SQRT2*0.5)); + x1 = MUL((t[8] - x2), xp[0]); + x2 = MUL((t[8] + x2), xp[1]); + + t[ 0] = x3 + x1; + t[ 8] = x4 - x2; + t[16] = x4 + x2; + t[24] = x3 - x1; + t++; + } while (t != t1); + + xp += 2; + t = tab; + t1 = tab + 4; + do { + xr = MUL(t[28],xp[0]); + t[28] = (t[0] - xr); + t[0] = (t[0] + xr); + + xr = MUL(t[4],xp[1]); + t[ 4] = (t[24] - xr); + t[24] = (t[24] + xr); + + xr = MUL(t[20],xp[2]); + t[20] = (t[8] - xr); + t[ 8] = (t[8] + xr); + + xr = MUL(t[12],xp[3]); + t[12] = (t[16] - xr); + t[16] = (t[16] + xr); + t++; + } while (t != t1); + xp += 4; + + for (i = 0; i < 4; i++) { + xr = MUL(tab[30-i*4],xp[0]); + tab[30-i*4] = (tab[i*4] - xr); + tab[ i*4] = (tab[i*4] + xr); + + xr = MUL(tab[ 2+i*4],xp[1]); + tab[ 2+i*4] = (tab[28-i*4] - xr); + tab[28-i*4] = (tab[28-i*4] + xr); + + xr = MUL(tab[31-i*4],xp[0]); + tab[31-i*4] = (tab[1+i*4] - xr); + tab[ 1+i*4] = (tab[1+i*4] + xr); + + xr = MUL(tab[ 3+i*4],xp[1]); + tab[ 3+i*4] = (tab[29-i*4] - xr); + tab[29-i*4] = (tab[29-i*4] + xr); + + xp += 2; + } + + t = tab + 30; + t1 = tab + 1; + do { + xr = MUL(t1[0], *xp); + t1[0] = (t[0] - xr); + t[0] = (t[0] + xr); + t -= 2; + t1 += 2; + xp++; + } while (t >= tab); + + for(i=0;i<32;i++) { + out[i] = tab[bitinv32[i]] << left_shift; + } +} + +static void filter(MpegAudioContext *s, short *samples) +{ + short *p, *q; + int sum, offset, i, j, norm, n; + short tmp[64]; + int tmp1[32]; + int *out; + + // print_pow1(samples, 1152); + + offset = s->samples_offset; + out = &s->sb_samples[0][0][0]; + for(j=0;j<36;j++) { + /* 32 samples at once */ + for(i=0;i<32;i++) + s->samples_buf[offset + (31 - i)] = samples[i]; + + /* filter */ + p = s->samples_buf + offset; + q = filter_bank; + /* maxsum = 23169 */ + for(i=0;i<64;i++) { + sum = p[0*64] * q[0*64]; + sum += p[1*64] * q[1*64]; + sum += p[2*64] * q[2*64]; + sum += p[3*64] * q[3*64]; + sum += p[4*64] * q[4*64]; + sum += p[5*64] * q[5*64]; + sum += p[6*64] * q[6*64]; + sum += p[7*64] * q[7*64]; + tmp[i] = sum >> 14; + p++; + q++; + } + tmp1[0] = tmp[16]; + for( i=1; i<=16; i++ ) tmp1[i] = tmp[i+16]+tmp[16-i]; + for( i=17; i<=31; i++ ) tmp1[i] = tmp[i+16]-tmp[80-i]; + + /* integer IDCT 32 with normalization. XXX: There may be some + overflow left */ + norm = 0; + for(i=0;i<32;i++) { + norm |= abs(tmp1[i]); + } + n = log2(norm) - 12; + if (n > 0) { + for(i=0;i<32;i++) + tmp1[i] >>= n; + } else { + n = 0; + } + + idct32(out, tmp1, s->sblimit, n); + + /* advance of 32 samples */ + samples += 32; + offset -= 32; + out += 32; + /* handle the wrap around */ + if (offset < 0) { + memmove(s->samples_buf + SAMPLES_BUF_SIZE - (512 - 32), + s->samples_buf, (512 - 32) * 2); + offset = SAMPLES_BUF_SIZE - 512; + } + } + s->samples_offset = offset; + + // print_pow(s->sb_samples, 1152); +} + +static void compute_scale_factors(unsigned char scale_code[SBLIMIT], + unsigned char scale_factors[SBLIMIT][3], + int sb_samples[3][12][SBLIMIT], + int sblimit) +{ + int *p, vmax, v, n, i, j, k, code; + int index, d1, d2; + unsigned char *sf = &scale_factors[0][0]; + + for(j=0;j<sblimit;j++) { + for(i=0;i<3;i++) { + /* find the max absolute value */ + p = &sb_samples[i][0][j]; + vmax = abs(*p); + for(k=1;k<12;k++) { + p += SBLIMIT; + v = abs(*p); + if (v > vmax) + vmax = v; + } + /* compute the scale factor index using log 2 computations */ + if (vmax > 0) { + n = log2(vmax); + /* n is the position of the MSB of vmax. now + use at most 2 compares to find the index */ + index = (21 - n) * 3 - 3; + if (index >= 0) { + while (vmax <= scale_factor_table[index+1]) + index++; + } else { + index = 0; /* very unlikely case of overflow */ + } + } else { + index = 63; + } + +#if 0 + printf("%2d:%d in=%x %x %d\n", + j, i, vmax, scale_factor_table[index], index); +#endif + /* store the scale factor */ + assert(index >=0 && index <= 63); + sf[i] = index; + } + + /* compute the transmission factor : look if the scale factors + are close enough to each other */ + d1 = scale_diff_table[sf[0] - sf[1] + 64]; + d2 = scale_diff_table[sf[1] - sf[2] + 64]; + + /* handle the 25 cases */ + switch(d1 * 5 + d2) { + case 0*5+0: + case 0*5+4: + case 3*5+4: + case 4*5+0: + case 4*5+4: + code = 0; + break; + case 0*5+1: + case 0*5+2: + case 4*5+1: + case 4*5+2: + code = 3; + sf[2] = sf[1]; + break; + case 0*5+3: + case 4*5+3: + code = 3; + sf[1] = sf[2]; + break; + case 1*5+0: + case 1*5+4: + case 2*5+4: + code = 1; + sf[1] = sf[0]; + break; + case 1*5+1: + case 1*5+2: + case 2*5+0: + case 2*5+1: + case 2*5+2: + code = 2; + sf[1] = sf[2] = sf[0]; + break; + case 2*5+3: + case 3*5+3: + code = 2; + sf[0] = sf[1] = sf[2]; + break; + case 3*5+0: + case 3*5+1: + case 3*5+2: + code = 2; + sf[0] = sf[2] = sf[1]; + break; + case 1*5+3: + code = 2; + if (sf[0] > sf[2]) + sf[0] = sf[2]; + sf[1] = sf[2] = sf[0]; + break; + default: + abort(); + } + +#if 0 + printf("%d: %2d %2d %2d %d %d -> %d\n", j, + sf[0], sf[1], sf[2], d1, d2, code); +#endif + scale_code[j] = code; + sf += 3; + } +} + +/* The most important function : psycho acoustic module. In this + encoder there is basically none, so this is the worst you can do, + but also this is the simpler. */ +static void psycho_acoustic_model(MpegAudioContext *s, short smr[SBLIMIT]) +{ + int i; + + for(i=0;i<s->sblimit;i++) { + smr[i] = (int)(fixed_smr[i] * 10); + } +} + + +#define SB_NOTALLOCATED 0 +#define SB_ALLOCATED 1 +#define SB_NOMORE 2 + +/* Try to maximize the smr while using a number of bits inferior to + the frame size. I tried to make the code simpler, faster and + smaller than other encoders :-) */ +static void compute_bit_allocation(MpegAudioContext *s, + short smr1[SBLIMIT], + unsigned char bit_alloc[SBLIMIT], + int *padding) +{ + int i, b, max_smr, max_sb, current_frame_size, max_frame_size; + int incr; + short smr[SBLIMIT]; + unsigned char subband_status[SBLIMIT]; + const unsigned char *alloc; + + memcpy(smr, smr1, sizeof(short) * s->sblimit); + memset(subband_status, SB_NOTALLOCATED, s->sblimit); + memset(bit_alloc, 0, s->sblimit); + + /* compute frame size and padding */ + max_frame_size = s->frame_size; + s->frame_frac += s->frame_frac_incr; + if (s->frame_frac >= 65536) { + s->frame_frac -= 65536; + s->do_padding = 1; + max_frame_size += 8; + } else { + s->do_padding = 0; + } + + /* compute the header + bit alloc size */ + current_frame_size = 32; + alloc = s->alloc_table; + for(i=0;i<s->sblimit;i++) { + incr = alloc[0]; + current_frame_size += incr; + alloc += 1 << incr; + } + for(;;) { + /* look for the subband with the largest signal to mask ratio */ + max_sb = -1; + max_smr = 0x80000000; + for(i=0;i<s->sblimit;i++) { + if (smr[i] > max_smr && subband_status[i] != SB_NOMORE) { + max_smr = smr[i]; + max_sb = i; + } + } +#if 0 + printf("current=%d max=%d max_sb=%d alloc=%d\n", + current_frame_size, max_frame_size, max_sb, + bit_alloc[max_sb]); +#endif + if (max_sb < 0) + break; + + /* find alloc table entry (XXX: not optimal, should use + pointer table) */ + alloc = s->alloc_table; + for(i=0;i<max_sb;i++) { + alloc += 1 << alloc[0]; + } + + if (subband_status[max_sb] == SB_NOTALLOCATED) { + /* nothing was coded for this band: add the necessary bits */ + incr = 2 + nb_scale_factors[s->scale_code[max_sb]] * 6; + incr += total_quant_bits[alloc[1]]; + } else { + /* increments bit allocation */ + b = bit_alloc[max_sb]; + incr = total_quant_bits[alloc[b + 1]] - + total_quant_bits[alloc[b]]; + } + + if (current_frame_size + incr <= max_frame_size) { + /* can increase size */ + b = ++bit_alloc[max_sb]; + current_frame_size += incr; + /* decrease smr by the resolution we added */ + smr[max_sb] = smr1[max_sb] - quant_snr[alloc[b]]; + /* max allocation size reached ? */ + if (b == ((1 << alloc[0]) - 1)) + subband_status[max_sb] = SB_NOMORE; + else + subband_status[max_sb] = SB_ALLOCATED; + } else { + /* cannot increase the size of this subband */ + subband_status[max_sb] = SB_NOMORE; + } + } + *padding = max_frame_size - current_frame_size; + assert(*padding >= 0); + +#if 0 + for(i=0;i<s->sblimit;i++) { + printf("%d ", bit_alloc[i]); + } + printf("\n"); +#endif +} + +/* + * Output the mpeg audio layer 2 frame. Note how the code is small + * compared to other encoders :-) + */ +static void encode_frame(MpegAudioContext *s, + unsigned char bit_alloc[SBLIMIT], + int padding) +{ + int i, j, k, l, bit_alloc_bits, b; + unsigned char *sf; + int q[3]; + PutBitContext *p = &s->pb; + + /* header */ + + put_bits(p, 12, 0xfff); + put_bits(p, 1, 1 - s->lsf); /* 1 = mpeg1 ID, 0 = mpeg2 lsf ID */ + put_bits(p, 2, 4-2); /* layer 2 */ + put_bits(p, 1, 1); /* no error protection */ + put_bits(p, 4, s->bitrate_index); + put_bits(p, 2, s->freq_index); + put_bits(p, 1, s->do_padding); /* use padding */ + put_bits(p, 1, 0); /* private_bit */ + put_bits(p, 2, MPA_MONO); + put_bits(p, 2, 0); /* mode_ext */ + put_bits(p, 1, 0); /* no copyright */ + put_bits(p, 1, 1); /* original */ + put_bits(p, 2, 0); /* no emphasis */ + + /* bit allocation */ + j = 0; + for(i=0;i<s->sblimit;i++) { + bit_alloc_bits = s->alloc_table[j]; + put_bits(p, bit_alloc_bits, bit_alloc[i]); + j += 1 << bit_alloc_bits; + } + + /* scale codes */ + for(i=0;i<s->sblimit;i++) { + if (bit_alloc[i]) + put_bits(p, 2, s->scale_code[i]); + } + + /* scale factors */ + sf = &s->scale_factors[0][0]; + for(i=0;i<s->sblimit;i++) { + if (bit_alloc[i]) { + switch(s->scale_code[i]) { + case 0: + put_bits(p, 6, sf[0]); + put_bits(p, 6, sf[1]); + put_bits(p, 6, sf[2]); + break; + case 3: + case 1: + put_bits(p, 6, sf[0]); + put_bits(p, 6, sf[2]); + break; + case 2: + put_bits(p, 6, sf[0]); + break; + } + } + sf += 3; + } + + /* quantization & write sub band samples */ + + for(k=0;k<3;k++) { + for(l=0;l<12;l+=3) { + j = 0; + for(i=0;i<s->sblimit;i++) { + bit_alloc_bits = s->alloc_table[j]; + b = bit_alloc[i]; + if (b) { + int qindex, steps, m, sample, bits; + /* we encode 3 sub band samples of the same sub band at a time */ + qindex = s->alloc_table[j+b]; + steps = quant_steps[qindex]; + for(m=0;m<3;m++) { + sample = s->sb_samples[k][l + m][i]; + /* divide by scale factor */ +#ifdef USE_FLOATS + { + float a; + a = (float)sample * scale_factor_inv_table[s->scale_factors[i][k]]; + q[m] = (int)((a + 1.0) * steps * 0.5); + } +#else + { + int q1, e, shift, mult; + e = s->scale_factors[i][k]; + shift = scale_factor_shift[e]; + mult = scale_factor_mult[e]; + + /* normalize to P bits */ + if (shift < 0) + q1 = sample << (-shift); + else + q1 = sample >> shift; + q1 = (q1 * mult) >> P; + q[m] = ((q1 + (1 << P)) * steps) >> (P + 1); + } +#endif + if (q[m] >= steps) + q[m] = steps - 1; + assert(q[m] >= 0 && q[m] < steps); + } + bits = quant_bits[qindex]; + if (bits < 0) { + /* group the 3 values to save bits */ + put_bits(p, -bits, + q[0] + steps * (q[1] + steps * q[2])); +#if 0 + printf("%d: gr1 %d\n", + i, q[0] + steps * (q[1] + steps * q[2])); +#endif + } else { +#if 0 + printf("%d: gr3 %d %d %d\n", + i, q[0], q[1], q[2]); +#endif + put_bits(p, bits, q[0]); + put_bits(p, bits, q[1]); + put_bits(p, bits, q[2]); + } + } + /* next subband in alloc table */ + j += 1 << bit_alloc_bits; + } + } + } + + /* padding */ + for(i=0;i<padding;i++) + put_bits(p, 1, 0); + + /* flush */ + flush_put_bits(p); +} + +int MPA_encode_frame(AVEncodeContext *avctx, + unsigned char *frame, int buf_size, void *data) +{ + MpegAudioContext *s = avctx->priv_data; + short *samples = data; + short smr[SBLIMIT]; + unsigned char bit_alloc[SBLIMIT]; + int padding; + + filter(s, samples); + compute_scale_factors(s->scale_code, s->scale_factors, + s->sb_samples, s->sblimit); + psycho_acoustic_model(s, smr); + compute_bit_allocation(s, smr, bit_alloc, &padding); + + init_put_bits(&s->pb, frame, MPA_MAX_CODED_FRAME_SIZE, NULL, NULL); + + encode_frame(s, bit_alloc, padding); + + s->nb_samples += MPA_FRAME_SIZE; + return s->pb.buf_ptr - s->pb.buf; +} + + +AVEncoder mp2_encoder = { + "mp2", + CODEC_TYPE_AUDIO, + CODEC_ID_MP2, + sizeof(MpegAudioContext), + MPA_encode_init, + MPA_encode_frame, + NULL, +}; diff --git a/libav/mpegaudio.h b/libav/mpegaudio.h new file mode 100644 index 0000000000..0734d3466b --- /dev/null +++ b/libav/mpegaudio.h @@ -0,0 +1,31 @@ + +/* max compressed frame size */ +#define MPA_MAX_CODED_FRAME_SIZE 1200 + +#define MPA_FRAME_SIZE 1152 + +#define SAMPLES_BUF_SIZE 4096 +#define SBLIMIT 32 /* number of subbands */ +#define DCT_BITS 14 /* number of bits for the DCT */ +#define MUL(a,b) (((a) * (b)) >> DCT_BITS) +#define FIX(a) ((int)((a) * (1 << DCT_BITS))) + +typedef struct MpegAudioContext { + PutBitContext pb; + int freq, bit_rate; + int lsf; /* 1 if mpeg2 low bitrate selected */ + int bitrate_index; /* bit rate */ + int freq_index; + int frame_size; /* frame size, in bits, without padding */ + long long nb_samples; /* total number of samples encoded */ + /* padding computation */ + int frame_frac, frame_frac_incr, do_padding; + short samples_buf[SAMPLES_BUF_SIZE]; /* buffer for filter */ + int samples_offset; /* offset in samples_buf */ + int sb_samples[3][12][SBLIMIT]; + unsigned char scale_factors[SBLIMIT][3]; /* scale factors */ + unsigned char scale_code[SBLIMIT]; /* code to group 3 scale factors */ + int sblimit; /* number of used subbands */ + const unsigned char *alloc_table; +} MpegAudioContext; + diff --git a/libav/mpegaudiotab.h b/libav/mpegaudiotab.h new file mode 100644 index 0000000000..05bdb9eea1 --- /dev/null +++ b/libav/mpegaudiotab.h @@ -0,0 +1,310 @@ +/* + * mpeg audio layer 2 tables. Most of them come from the mpeg audio + * specification. + * + * Copyright (c) 2000 Gerard Lantau. + * + * The licence of this code is contained in file LICENCE found in the + * same archive + */ + +static const unsigned short bitrate_tab[2][15] = { + {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}, /* mpeg2 lsf */ + {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384}, /* mpeg1 */ +}; + +static const unsigned short freq_tab[3] = { 44100, 48000, 32000 }; + +#define SQRT2 1.41421356237309514547 + +static const int costab32[30] = { + FIX(0.54119610014619701222), + FIX(1.3065629648763763537), + + FIX(0.50979557910415917998), + FIX(2.5629154477415054814), + FIX(0.89997622313641556513), + FIX(0.60134488693504528634), + + FIX(0.5024192861881556782), + FIX(5.1011486186891552563), + FIX(0.78815462345125020249), + FIX(0.64682178335999007679), + FIX(0.56694403481635768927), + FIX(1.0606776859903470633), + FIX(1.7224470982383341955), + FIX(0.52249861493968885462), + + FIX(10.19000812354803287), + FIX(0.674808341455005678), + FIX(1.1694399334328846596), + FIX(0.53104259108978413284), + FIX(2.0577810099534108446), + FIX(0.58293496820613388554), + FIX(0.83934964541552681272), + FIX(0.50547095989754364798), + FIX(3.4076084184687189804), + FIX(0.62250412303566482475), + FIX(0.97256823786196078263), + FIX(0.51544730992262455249), + FIX(1.4841646163141661852), + FIX(0.5531038960344445421), + FIX(0.74453627100229857749), + FIX(0.5006029982351962726), +}; + +static const int bitinv32[32] = { + 0, 16, 8, 24, 4, 20, 12, 28, + 2, 18, 10, 26, 6, 22, 14, 30, + 1, 17, 9, 25, 5, 21, 13, 29, + 3, 19, 11, 27, 7, 23, 15, 31 +}; + + +static short filter_bank[512]; + +static const double enwindow[512] = {0.000000000, + -0.000000477, -0.000000477, -0.000000477, -0.000000477, -0.000000477, -0.000000477, -0.000000954, -0.000000954, + -0.000000954, -0.000000954, -0.000001431, -0.000001431, -0.000001907, -0.000001907, -0.000002384, -0.000002384, + -0.000002861, -0.000003338, -0.000003338, -0.000003815, -0.000004292, -0.000004768, -0.000005245, -0.000006199, + -0.000006676, -0.000007629, -0.000008106, -0.000009060, -0.000010014, -0.000011444, -0.000012398, -0.000013828, + -0.000014782, -0.000016689, -0.000018120, -0.000019550, -0.000021458, -0.000023365, -0.000025272, -0.000027657, + -0.000030041, -0.000032425, -0.000034809, -0.000037670, -0.000040531, -0.000043392, -0.000046253, -0.000049591, + -0.000052929, -0.000055790, -0.000059605, -0.000062943, -0.000066280, -0.000070095, -0.000073433, -0.000076771, + -0.000080585, -0.000083923, -0.000087261, -0.000090599, -0.000093460, -0.000096321, -0.000099182, 0.000101566, + 0.000103951, 0.000105858, 0.000107288, 0.000108242, 0.000108719, 0.000108719, 0.000108242, 0.000106812, + 0.000105381, 0.000102520, 0.000099182, 0.000095367, 0.000090122, 0.000084400, 0.000077724, 0.000069618, + 0.000060558, 0.000050545, 0.000039577, 0.000027180, 0.000013828, -0.000000954, -0.000017166, -0.000034332, + -0.000052929, -0.000072956, -0.000093937, -0.000116348, -0.000140190, -0.000165462, -0.000191212, -0.000218868, + -0.000247478, -0.000277042, -0.000307560, -0.000339031, -0.000371456, -0.000404358, -0.000438213, -0.000472546, + -0.000507355, -0.000542164, -0.000576973, -0.000611782, -0.000646591, -0.000680923, -0.000714302, -0.000747204, + -0.000779152, -0.000809669, -0.000838757, -0.000866413, -0.000891685, -0.000915051, -0.000935555, -0.000954151, + -0.000968933, -0.000980854, -0.000989437, -0.000994205, -0.000995159, -0.000991821, -0.000983715, 0.000971317, + 0.000953674, 0.000930786, 0.000902653, 0.000868797, 0.000829220, 0.000783920, 0.000731945, 0.000674248, + 0.000610352, 0.000539303, 0.000462532, 0.000378609, 0.000288486, 0.000191689, 0.000088215, -0.000021458, + -0.000137329, -0.000259876, -0.000388145, -0.000522137, -0.000661850, -0.000806808, -0.000956535, -0.001111031, + -0.001269817, -0.001432419, -0.001597881, -0.001766682, -0.001937389, -0.002110004, -0.002283096, -0.002457142, + -0.002630711, -0.002803326, -0.002974033, -0.003141880, -0.003306866, -0.003467083, -0.003622532, -0.003771782, + -0.003914356, -0.004048824, -0.004174709, -0.004290581, -0.004395962, -0.004489899, -0.004570484, -0.004638195, + -0.004691124, -0.004728317, -0.004748821, -0.004752159, -0.004737377, -0.004703045, -0.004649162, -0.004573822, + -0.004477024, -0.004357815, -0.004215240, -0.004049301, -0.003858566, -0.003643036, -0.003401756, 0.003134727, + 0.002841473, 0.002521515, 0.002174854, 0.001800537, 0.001399517, 0.000971317, 0.000515938, 0.000033379, + -0.000475883, -0.001011848, -0.001573563, -0.002161503, -0.002774239, -0.003411293, -0.004072189, -0.004756451, + -0.005462170, -0.006189346, -0.006937027, -0.007703304, -0.008487225, -0.009287834, -0.010103703, -0.010933399, + -0.011775017, -0.012627602, -0.013489246, -0.014358521, -0.015233517, -0.016112804, -0.016994476, -0.017876148, + -0.018756866, -0.019634247, -0.020506859, -0.021372318, -0.022228718, -0.023074150, -0.023907185, -0.024725437, + -0.025527000, -0.026310921, -0.027073860, -0.027815342, -0.028532982, -0.029224873, -0.029890060, -0.030526638, + -0.031132698, -0.031706810, -0.032248020, -0.032754898, -0.033225536, -0.033659935, -0.034055710, -0.034412861, + -0.034730434, -0.035007000, -0.035242081, -0.035435200, -0.035586357, -0.035694122, -0.035758972, 0.035780907, + 0.035758972, 0.035694122, 0.035586357, 0.035435200, 0.035242081, 0.035007000, 0.034730434, 0.034412861, + 0.034055710, 0.033659935, 0.033225536, 0.032754898, 0.032248020, 0.031706810, 0.031132698, 0.030526638, + 0.029890060, 0.029224873, 0.028532982, 0.027815342, 0.027073860, 0.026310921, 0.025527000, 0.024725437, + 0.023907185, 0.023074150, 0.022228718, 0.021372318, 0.020506859, 0.019634247, 0.018756866, 0.017876148, + 0.016994476, 0.016112804, 0.015233517, 0.014358521, 0.013489246, 0.012627602, 0.011775017, 0.010933399, + 0.010103703, 0.009287834, 0.008487225, 0.007703304, 0.006937027, 0.006189346, 0.005462170, 0.004756451, + 0.004072189, 0.003411293, 0.002774239, 0.002161503, 0.001573563, 0.001011848, 0.000475883, -0.000033379, + -0.000515938, -0.000971317, -0.001399517, -0.001800537, -0.002174854, -0.002521515, -0.002841473, 0.003134727, + 0.003401756, 0.003643036, 0.003858566, 0.004049301, 0.004215240, 0.004357815, 0.004477024, 0.004573822, + 0.004649162, 0.004703045, 0.004737377, 0.004752159, 0.004748821, 0.004728317, 0.004691124, 0.004638195, + 0.004570484, 0.004489899, 0.004395962, 0.004290581, 0.004174709, 0.004048824, 0.003914356, 0.003771782, + 0.003622532, 0.003467083, 0.003306866, 0.003141880, 0.002974033, 0.002803326, 0.002630711, 0.002457142, + 0.002283096, 0.002110004, 0.001937389, 0.001766682, 0.001597881, 0.001432419, 0.001269817, 0.001111031, + 0.000956535, 0.000806808, 0.000661850, 0.000522137, 0.000388145, 0.000259876, 0.000137329, 0.000021458, + -0.000088215, -0.000191689, -0.000288486, -0.000378609, -0.000462532, -0.000539303, -0.000610352, -0.000674248, + -0.000731945, -0.000783920, -0.000829220, -0.000868797, -0.000902653, -0.000930786, -0.000953674, 0.000971317, + 0.000983715, 0.000991821, 0.000995159, 0.000994205, 0.000989437, 0.000980854, 0.000968933, 0.000954151, + 0.000935555, 0.000915051, 0.000891685, 0.000866413, 0.000838757, 0.000809669, 0.000779152, 0.000747204, + 0.000714302, 0.000680923, 0.000646591, 0.000611782, 0.000576973, 0.000542164, 0.000507355, 0.000472546, + 0.000438213, 0.000404358, 0.000371456, 0.000339031, 0.000307560, 0.000277042, 0.000247478, 0.000218868, + 0.000191212, 0.000165462, 0.000140190, 0.000116348, 0.000093937, 0.000072956, 0.000052929, 0.000034332, + 0.000017166, 0.000000954, -0.000013828, -0.000027180, -0.000039577, -0.000050545, -0.000060558, -0.000069618, + -0.000077724, -0.000084400, -0.000090122, -0.000095367, -0.000099182, -0.000102520, -0.000105381, -0.000106812, + -0.000108242, -0.000108719, -0.000108719, -0.000108242, -0.000107288, -0.000105858, -0.000103951, 0.000101566, + 0.000099182, 0.000096321, 0.000093460, 0.000090599, 0.000087261, 0.000083923, 0.000080585, 0.000076771, + 0.000073433, 0.000070095, 0.000066280, 0.000062943, 0.000059605, 0.000055790, 0.000052929, 0.000049591, + 0.000046253, 0.000043392, 0.000040531, 0.000037670, 0.000034809, 0.000032425, 0.000030041, 0.000027657, + 0.000025272, 0.000023365, 0.000021458, 0.000019550, 0.000018120, 0.000016689, 0.000014782, 0.000013828, + 0.000012398, 0.000011444, 0.000010014, 0.000009060, 0.000008106, 0.000007629, 0.000006676, 0.000006199, + 0.000005245, 0.000004768, 0.000004292, 0.000003815, 0.000003338, 0.000003338, 0.000002861, 0.000002384, + 0.000002384, 0.000001907, 0.000001907, 0.000001431, 0.000001431, 0.000000954, 0.000000954, 0.000000954, + 0.000000954, 0.000000477, 0.000000477, 0.000000477, 0.000000477, 0.000000477, 0.000000477 + }; + +static int scale_factor_table[64]; +#ifdef USE_FLOATS +static float scale_factor_inv_table[64]; +#else +static INT8 scale_factor_shift[64]; +static unsigned short scale_factor_mult[64]; +#endif +static unsigned char scale_diff_table[128]; + +static const int sblimit_table[5] = { 27 , 30 , 8, 12 , 30 }; + +static const int quant_steps[17] = { + 3, 5, 7, 9, 15, + 31, 63, 127, 255, 511, + 1023, 2047, 4095, 8191, 16383, + 32767, 65535 +}; + +/* we use a negative value if grouped */ +static const int quant_bits[17] = { + -5, -7, 3, -10, 4, + 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, + 15, 16 +}; + +/* signal to noise ratio of each quantification step (could be + computed from quant_steps[]). The values are dB multiplied by 10 +*/ +static unsigned short quant_snr[17] = { + 70, 110, 160, 208, + 253, 316, 378, 439, + 499, 559, 620, 680, + 740, 800, 861, 920, + 980 +}; + + +/* total number of bits per allocation group */ +static unsigned short total_quant_bits[17]; + +/* encoding tables which give the quantization index. Note how it is + possible to store them efficiently ! */ +static const unsigned char alloc_table_0[] = { + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, +}; + +static const unsigned char alloc_table_1[] = { + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 3, 0, 1, 2, 3, 4, 5, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, + 2, 0, 1, 16, +}; + +static const unsigned char alloc_table_2[] = { + 4, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 4, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, +}; + +static const unsigned char alloc_table_3[] = { + 4, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 4, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, +}; + +static const unsigned char alloc_table_4[] = { + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 3, 0, 1, 3, 4, 5, 6, 7, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, + 2, 0, 1, 3, +}; + +const unsigned char *alloc_tables[5] = +{ alloc_table_0, alloc_table_1, alloc_table_2, alloc_table_3, alloc_table_4, }; + +/* fixed psycho acoustic model. Values of SNR taken from the 'toolame' + project */ +const float fixed_smr[SBLIMIT] = { + 30, 17, 16, 10, 3, 12, 8, 2.5, + 5, 5, 6, 6, 5, 6, 10, 6, + -4, -10, -21, -30, -42, -55, -68, -75, + -75, -75, -75, -75, -91, -107, -110, -108 +}; + +const unsigned char nb_scale_factors[4] = { 3, 2, 1, 2 }; diff --git a/libav/mpegencodevlc.h b/libav/mpegencodevlc.h new file mode 100644 index 0000000000..3952fd0472 --- /dev/null +++ b/libav/mpegencodevlc.h @@ -0,0 +1,311 @@ +/* + * RV 1.0 compatible encoder. + * Copyright (c) 2000 Gerard Lantau. + * + * The licence of this code is contained in file LICENCE found in the + * same archive + */ + +const unsigned char vlc_dc_table[256] = { + 0, 1, 2, 2, + 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +const unsigned char vlc_dc_lum_code[9] = { + 0x4, 0x0, 0x1, 0x5, 0x6, 0xe, 0x1e, 0x3e, 0x7e, +}; +const unsigned char vlc_dc_lum_bits[9] = { + 3, 2, 2, 3, 3, 4, 5, 6, 7, +}; + +const unsigned char vlc_dc_chroma_code[9] = { + 0x0, 0x1, 0x2, 0x6, 0xe, 0x1e, 0x3e, 0x7e, 0xfe, +}; +const unsigned char vlc_dc_chroma_bits[9] = { + 2, 2, 2, 3, 4, 5, 6, 7, 8, +}; + +/* + * Copyright (c) 1995 The Regents of the University of California. + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#define HUFF_MAXRUN 32 +#define HUFF_MAXLEVEL 41 + +static const int huff_maxlevel[HUFF_MAXRUN] = { 41, 19, 6, 5, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + +static const UINT8 huff_table0[41] = { 0x0, 0x6, 0x8, 0xa, 0xc, 0x4c, 0x42, 0x14, 0x3a, 0x30, 0x26, 0x20, 0x34, 0x32, 0x30, 0x2e, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20, 0x30, 0x2e, 0x2c, 0x2a, 0x28, 0x26, 0x24, 0x22, 0x20 }; +static const UINT8 huff_bits0[41] = { 0, 3, 5, 6, 8, 9, 9, 11, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16 }; + +static const UINT8 huff_table1[19] = { 0x0, 0x6, 0xc, 0x4a, 0x18, 0x36, 0x2c, 0x2a, 0x3e, 0x3c, 0x3a, 0x38, 0x36, 0x34, 0x32, 0x26, 0x24, 0x22, 0x20 }; +static const UINT8 huff_bits1[19] = { 0, 4, 7, 9, 11, 13, 14, 14, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17 }; + +static const UINT8 huff_table2[6] = { 0x0, 0xa, 0x8, 0x16, 0x28, 0x28 }; +static const UINT8 huff_bits2[6] = { 0, 5, 8, 11, 13, 14 }; + +static const UINT8 huff_table3[5] = { 0x0, 0xe, 0x48, 0x38, 0x26 }; +static const UINT8 huff_bits3[5] = { 0, 6, 9, 13, 14 }; + +static const UINT8 huff_table4[4] = { 0x0, 0xc, 0x1e, 0x24 }; +static const UINT8 huff_bits4[4] = { 0, 6, 11, 13 }; + +static const UINT8 huff_table5[4] = { 0x0, 0xe, 0x12, 0x24 }; +static const UINT8 huff_bits5[4] = { 0, 7, 11, 14 }; + +static const UINT8 huff_table6[4] = { 0x0, 0xa, 0x3c, 0x28 }; +static const UINT8 huff_bits6[4] = { 0, 7, 13, 17 }; + +static const UINT8 huff_table7[3] = { 0x0, 0x8, 0x2a }; +static const UINT8 huff_bits7[3] = { 0, 7, 13 }; + +static const UINT8 huff_table8[3] = { 0x0, 0xe, 0x22 }; +static const UINT8 huff_bits8[3] = { 0, 8, 13 }; + +static const UINT8 huff_table9[3] = { 0x0, 0xa, 0x22 }; +static const UINT8 huff_bits9[3] = { 0, 8, 14 }; + +static const UINT8 huff_table10[3] = { 0x0, 0x4e, 0x20 }; +static const UINT8 huff_bits10[3] = { 0, 9, 14 }; + +static const UINT8 huff_table11[3] = { 0x0, 0x46, 0x34 }; +static const UINT8 huff_bits11[3] = { 0, 9, 17 }; + +static const UINT8 huff_table12[3] = { 0x0, 0x44, 0x32 }; +static const UINT8 huff_bits12[3] = { 0, 9, 17 }; + +static const UINT8 huff_table13[3] = { 0x0, 0x40, 0x30 }; +static const UINT8 huff_bits13[3] = { 0, 9, 17 }; + +static const UINT8 huff_table14[3] = { 0x0, 0x1c, 0x2e }; +static const UINT8 huff_bits14[3] = { 0, 11, 17 }; + +static const UINT8 huff_table15[3] = { 0x0, 0x1a, 0x2c }; +static const UINT8 huff_bits15[3] = { 0, 11, 17 }; + +static const UINT8 huff_table16[3] = { 0x0, 0x10, 0x2a }; +static const UINT8 huff_bits16[3] = { 0, 11, 17 }; + +static const UINT8 huff_table17[2] = { 0x0, 0x3e }; +static const UINT8 huff_bits17[2] = { 0, 13 }; + +static const UINT8 huff_table18[2] = { 0x0, 0x34 }; +static const UINT8 huff_bits18[2] = { 0, 13 }; + +static const UINT8 huff_table19[2] = { 0x0, 0x32 }; +static const UINT8 huff_bits19[2] = { 0, 13 }; + +static const UINT8 huff_table20[2] = { 0x0, 0x2e }; +static const UINT8 huff_bits20[2] = { 0, 13 }; + +static const UINT8 huff_table21[2] = { 0x0, 0x2c }; +static const UINT8 huff_bits21[2] = { 0, 13 }; + +static const UINT8 huff_table22[2] = { 0x0, 0x3e }; +static const UINT8 huff_bits22[2] = { 0, 14 }; + +static const UINT8 huff_table23[2] = { 0x0, 0x3c }; +static const UINT8 huff_bits23[2] = { 0, 14 }; + +static const UINT8 huff_table24[2] = { 0x0, 0x3a }; +static const UINT8 huff_bits24[2] = { 0, 14 }; + +static const UINT8 huff_table25[2] = { 0x0, 0x38 }; +static const UINT8 huff_bits25[2] = { 0, 14 }; + +static const UINT8 huff_table26[2] = { 0x0, 0x36 }; +static const UINT8 huff_bits26[2] = { 0, 14 }; + +static const UINT8 huff_table27[2] = { 0x0, 0x3e }; +static const UINT8 huff_bits27[2] = { 0, 17 }; + +static const UINT8 huff_table28[2] = { 0x0, 0x3c }; +static const UINT8 huff_bits28[2] = { 0, 17 }; + +static const UINT8 huff_table29[2] = { 0x0, 0x3a }; +static const UINT8 huff_bits29[2] = { 0, 17 }; + +static const UINT8 huff_table30[2] = { 0x0, 0x38 }; +static const UINT8 huff_bits30[2] = { 0, 17 }; + +static const UINT8 huff_table31[2] = { 0x0, 0x36 }; +static const UINT8 huff_bits31[2] = { 0, 17 }; + +static const UINT8 *huff_table[32] = { huff_table0, huff_table1, huff_table2, huff_table3, huff_table4, huff_table5, huff_table6, huff_table7, huff_table8, huff_table9, huff_table10, huff_table11, huff_table12, huff_table13, huff_table14, huff_table15, huff_table16, huff_table17, huff_table18, huff_table19, huff_table20, huff_table21, huff_table22, huff_table23, huff_table24, huff_table25, huff_table26, huff_table27, huff_table28, huff_table29, huff_table30, huff_table31 }; + +static const UINT8 *huff_bits[32] = { huff_bits0, huff_bits1, huff_bits2, huff_bits3, huff_bits4, huff_bits5, huff_bits6, huff_bits7, huff_bits8, huff_bits9, huff_bits10, huff_bits11, huff_bits12, huff_bits13, huff_bits14, huff_bits15, huff_bits16, huff_bits17, huff_bits18, huff_bits19, huff_bits20, huff_bits21, huff_bits22, huff_bits23, huff_bits24, huff_bits25, huff_bits26, huff_bits27, huff_bits28, huff_bits29, huff_bits30, huff_bits31 }; + +static const UINT8 mbAddrIncrTable[][2] = { + {0x0, 0}, + {0x1, 1}, + {0x3, 3}, + {0x2, 3}, + {0x3, 4}, + {0x2, 4}, + {0x3, 5}, + {0x2, 5}, + {0x7, 7}, + {0x6, 7}, + {0xb, 8}, + {0xa, 8}, + {0x9, 8}, + {0x8, 8}, + {0x7, 8}, + {0x6, 8}, + {0x17, 10}, + {0x16, 10}, + {0x15, 10}, + {0x14, 10}, + {0x13, 10}, + {0x12, 10}, + {0x23, 11}, + {0x22, 11}, + {0x21, 11}, + {0x20, 11}, + {0x1f, 11}, + {0x1e, 11}, + {0x1d, 11}, + {0x1c, 11}, + {0x1b, 11}, + {0x1a, 11}, + {0x19, 11}, + {0x18, 11}}; + +static const UINT8 mbPatTable[][2] = { + {0x0, 0}, + {0xb, 5}, + {0x9, 5}, + {0xd, 6}, + {0xd, 4}, + {0x17, 7}, + {0x13, 7}, + {0x1f, 8}, + {0xc, 4}, + {0x16, 7}, + {0x12, 7}, + {0x1e, 8}, + {0x13, 5}, + {0x1b, 8}, + {0x17, 8}, + {0x13, 8}, + {0xb, 4}, + {0x15, 7}, + {0x11, 7}, + {0x1d, 8}, + {0x11, 5}, + {0x19, 8}, + {0x15, 8}, + {0x11, 8}, + {0xf, 6}, + {0xf, 8}, + {0xd, 8}, + {0x3, 9}, + {0xf, 5}, + {0xb, 8}, + {0x7, 8}, + {0x7, 9}, + {0xa, 4}, + {0x14, 7}, + {0x10, 7}, + {0x1c, 8}, + {0xe, 6}, + {0xe, 8}, + {0xc, 8}, + {0x2, 9}, + {0x10, 5}, + {0x18, 8}, + {0x14, 8}, + {0x10, 8}, + {0xe, 5}, + {0xa, 8}, + {0x6, 8}, + {0x6, 9}, + {0x12, 5}, + {0x1a, 8}, + {0x16, 8}, + {0x12, 8}, + {0xd, 5}, + {0x9, 8}, + {0x5, 8}, + {0x5, 9}, + {0xc, 5}, + {0x8, 8}, + {0x4, 8}, + {0x4, 9}, + {0x7, 3}, + {0xa, 5}, /* grrr... 61, 62, 63 added - Kevin */ + {0x8, 5}, + {0xc, 6} +}; + +const UINT8 zigzag_direct[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static unsigned char const default_intra_matrix[64] = { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83 +}; + +/* XXX: could hardcode this matrix */ +static unsigned char const default_non_intra_matrix[64] = { + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, +}; + +static unsigned char const frame_rate_tab[9] = { + 0, 24, 24, 25, 30, 30, 50, 60, 60, +}; diff --git a/libav/mpegvideo.c b/libav/mpegvideo.c new file mode 100644 index 0000000000..4987b38af4 --- /dev/null +++ b/libav/mpegvideo.c @@ -0,0 +1,1098 @@ +/* + * The simplest mpeg encoder + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <math.h> +#include "avcodec.h" +#include "mpegvideo.h" + +//#define DEBUG + +/* depends on JPEG librarie */ +extern void jpeg_fdct_ifast (DCTELEM * data); + +/* depends on mpeg */ +extern void j_rev_dct (DCTELEM *data); + +/* for jpeg fast DCT */ +#define CONST_BITS 14 + +static const unsigned short aanscales[64] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 +}; + +static UINT8 cropTbl[256 + 2 * MAX_NEG_CROP]; +static UINT32 squareTbl[512]; + +static void encode_picture(MpegEncContext *s, int picture_number); +static void rate_control_init(MpegEncContext *s); +static int rate_estimate_qscale(MpegEncContext *s); +static void mpeg1_skip_picture(MpegEncContext *s, int pict_num); + +#include "mpegencodevlc.h" + +static void put_header(MpegEncContext *s, int header) +{ + align_put_bits(&s->pb); + put_bits(&s->pb, 32, header); +} + +static void convert_matrix(int *qmat, const UINT8 *quant_matrix, int qscale) +{ + int i; + + for(i=0;i<64;i++) { + qmat[i] = (int)((1 << 22) * 16384.0 / (aanscales[i] * qscale * quant_matrix[i])); + } +} + + +int MPV_encode_init(AVEncodeContext *avctx) +{ + MpegEncContext *s = avctx->priv_data; + int pict_size, c_size, i; + UINT8 *pict; + + s->bit_rate = avctx->bit_rate; + s->frame_rate = avctx->rate; + s->width = avctx->width; + s->height = avctx->height; + s->gop_size = avctx->gop_size; + if (s->gop_size <= 1) { + s->intra_only = 1; + s->gop_size = 12; + } else { + s->intra_only = 0; + } + + switch(avctx->codec->id) { + case CODEC_ID_MPEG1VIDEO: + s->out_format = FMT_MPEG1; + break; + case CODEC_ID_MJPEG: + s->out_format = FMT_MJPEG; + s->intra_only = 1; /* force intra only for jpeg */ + if (mjpeg_init(s) < 0) + return -1; + break; + case CODEC_ID_H263: + s->out_format = FMT_H263; + break; + case CODEC_ID_RV10: + s->out_format = FMT_H263; + s->h263_rv10 = 1; + break; + default: + return -1; + } + + switch(s->frame_rate) { + case 24: + s->frame_rate_index = 2; + break; + case 25: + s->frame_rate_index = 3; + break; + case 30: + s->frame_rate_index = 5; + break; + case 50: + s->frame_rate_index = 6; + break; + case 60: + s->frame_rate_index = 8; + break; + default: + /* we accept lower frame rates than 24 for low bit rate mpeg */ + if (s->frame_rate >= 1 && s->frame_rate < 24) { + s->frame_rate_index = 2; + } else { + return -1; + } + break; + } + + /* init */ + s->mb_width = s->width / 16; + s->mb_height = s->height / 16; + + c_size = s->width * s->height; + pict_size = (c_size * 3) / 2; + pict = malloc(pict_size); + if (pict == NULL) + return -1; + s->last_picture[0] = pict; + s->last_picture[1] = pict + c_size; + s->last_picture[2] = pict + c_size + (c_size / 4); + + pict = malloc(pict_size); + if (pict == NULL) + return -1; + s->last_picture[0] = pict; + s->last_picture[1] = pict + c_size; + s->last_picture[2] = pict + c_size + (c_size / 4); + + pict = malloc(pict_size); + if (pict == NULL) { + free(s->last_picture[0]); + return -1; + } + s->current_picture[0] = pict; + s->current_picture[1] = pict + c_size; + s->current_picture[2] = pict + c_size + (c_size / 4); + + for(i=0;i<256;i++) cropTbl[i + MAX_NEG_CROP] = i; + for(i=0;i<MAX_NEG_CROP;i++) { + cropTbl[i] = 0; + cropTbl[i + MAX_NEG_CROP + 256] = 255; + } + + for(i=0;i<512;i++) { + squareTbl[i] = (i - 256) * (i - 256); + } + + /* rate control init */ + rate_control_init(s); + + s->picture_number = 0; + s->fake_picture_number = 0; + + return 0; +} + +int MPV_encode_end(AVEncodeContext *avctx) +{ + MpegEncContext *s = avctx->priv_data; +#if 0 + /* end of sequence */ + if (s->out_format == FMT_MPEG1) { + put_header(s, SEQ_END_CODE); + } + + if (!s->flush_frames) + flush_put_bits(&s->pb); +#endif + free(s->last_picture[0]); + free(s->current_picture[0]); + if (s->out_format == FMT_MJPEG) + mjpeg_close(s); + return 0; +} + +int MPV_encode_picture(AVEncodeContext *avctx, + unsigned char *buf, int buf_size, void *data) +{ + MpegEncContext *s = avctx->priv_data; + int i; + + memcpy(s->new_picture, data, 3 * sizeof(UINT8 *)); + + init_put_bits(&s->pb, buf, buf_size, NULL, NULL); + + /* group of picture */ + if (s->out_format == FMT_MPEG1) { + unsigned int vbv_buffer_size; + unsigned int time_code, fps, n; + + if ((s->picture_number % s->gop_size) == 0) { + /* mpeg1 header repeated every gop */ + put_header(s, SEQ_START_CODE); + + put_bits(&s->pb, 12, s->width); + put_bits(&s->pb, 12, s->height); + put_bits(&s->pb, 4, 1); /* 1/1 aspect ratio */ + put_bits(&s->pb, 4, s->frame_rate_index); + put_bits(&s->pb, 18, 0x3ffff); + put_bits(&s->pb, 1, 1); /* marker */ + /* vbv buffer size: slightly greater than an I frame. We add + some margin just in case */ + vbv_buffer_size = (3 * s->I_frame_bits) / (2 * 8); + put_bits(&s->pb, 10, (vbv_buffer_size + 16383) / 16384); + put_bits(&s->pb, 1, 1); /* constrained parameter flag */ + put_bits(&s->pb, 1, 0); /* no custom intra matrix */ + put_bits(&s->pb, 1, 0); /* no custom non intra matrix */ + + put_header(s, GOP_START_CODE); + put_bits(&s->pb, 1, 0); /* do drop frame */ + /* time code : we must convert from the real frame rate to a + fake mpeg frame rate in case of low frame rate */ + fps = frame_rate_tab[s->frame_rate_index]; + time_code = s->fake_picture_number; + s->gop_picture_number = time_code; + put_bits(&s->pb, 5, (time_code / (fps * 3600)) % 24); + put_bits(&s->pb, 6, (time_code / (fps * 60)) % 60); + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 6, (time_code / fps) % 60); + put_bits(&s->pb, 6, (time_code % fps)); + put_bits(&s->pb, 1, 1); /* closed gop */ + put_bits(&s->pb, 1, 0); /* broken link */ + } + + if (s->frame_rate < 24 && s->picture_number > 0) { + /* insert empty P pictures to slow down to the desired + frame rate. Each fake pictures takes about 20 bytes */ + fps = frame_rate_tab[s->frame_rate_index]; + n = ((s->picture_number * fps) / s->frame_rate) - 1; + while (s->fake_picture_number < n) { + mpeg1_skip_picture(s, s->fake_picture_number - + s->gop_picture_number); + s->fake_picture_number++; + } + + } + s->fake_picture_number++; + } + + + if (!s->intra_only) { + /* first picture of GOP is intra */ + if ((s->picture_number % s->gop_size) == 0) + s->pict_type = I_TYPE; + else + s->pict_type = P_TYPE; + } else { + s->pict_type = I_TYPE; + } + avctx->key_frame = (s->pict_type == I_TYPE); + + encode_picture(s, s->picture_number); + + /* swap current and last picture */ + for(i=0;i<3;i++) { + UINT8 *tmp; + + tmp = s->last_picture[i]; + s->last_picture[i] = s->current_picture[i]; + s->current_picture[i] = tmp; + } + s->picture_number++; + + if (s->out_format == FMT_MJPEG) + mjpeg_picture_trailer(s); + + flush_put_bits(&s->pb); + s->total_bits += (s->pb.buf_ptr - s->pb.buf) * 8; + return s->pb.buf_ptr - s->pb.buf; +} + +/* insert a fake P picture */ +static void mpeg1_skip_picture(MpegEncContext *s, int pict_num) +{ + unsigned int mb_incr; + + /* mpeg1 picture header */ + put_header(s, PICTURE_START_CODE); + /* temporal reference */ + put_bits(&s->pb, 10, pict_num & 0x3ff); + + put_bits(&s->pb, 3, P_TYPE); + put_bits(&s->pb, 16, 0xffff); /* non constant bit rate */ + + put_bits(&s->pb, 1, 1); /* integer coordinates */ + put_bits(&s->pb, 3, 1); /* forward_f_code */ + + put_bits(&s->pb, 1, 0); /* extra bit picture */ + + /* only one slice */ + put_header(s, SLICE_MIN_START_CODE); + put_bits(&s->pb, 5, 1); /* quantizer scale */ + put_bits(&s->pb, 1, 0); /* slice extra information */ + + mb_incr = 1; + put_bits(&s->pb, mbAddrIncrTable[mb_incr][1], + mbAddrIncrTable[mb_incr][0]); + + /* empty macroblock */ + put_bits(&s->pb, 3, 1); /* motion only */ + + /* zero motion x & y */ + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 1, 1); + + /* output a number of empty slice */ + mb_incr = s->mb_width * s->mb_height - 1; + while (mb_incr > 33) { + put_bits(&s->pb, 11, 0x008); + mb_incr -= 33; + } + put_bits(&s->pb, mbAddrIncrTable[mb_incr][1], + mbAddrIncrTable[mb_incr][0]); + + /* empty macroblock */ + put_bits(&s->pb, 3, 1); /* motion only */ + + /* zero motion x & y */ + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 1, 1); +} + +static int pix_sum(UINT8 *pix, int line_size) +{ + int s, i, j; + + s = 0; + for(i=0;i<16;i++) { + for(j=0;j<16;j+=8) { + s += pix[0]; + s += pix[1]; + s += pix[2]; + s += pix[3]; + s += pix[4]; + s += pix[5]; + s += pix[6]; + s += pix[7]; + pix += 8; + } + pix += line_size - 16; + } + return s; +} + +static int pix_norm1(UINT8 *pix, int line_size) +{ + int s, i, j; + UINT32 *sq = squareTbl + 256; + + s = 0; + for(i=0;i<16;i++) { + for(j=0;j<16;j+=8) { + s += sq[pix[0]]; + s += sq[pix[1]]; + s += sq[pix[2]]; + s += sq[pix[3]]; + s += sq[pix[4]]; + s += sq[pix[5]]; + s += sq[pix[6]]; + s += sq[pix[7]]; + pix += 8; + } + pix += line_size - 16; + } + return s; +} + +static int pix_norm(UINT8 *pix1, UINT8 *pix2, int line_size) +{ + int s, i, j; + UINT32 *sq = squareTbl + 256; + + s = 0; + for(i=0;i<16;i++) { + for(j=0;j<16;j+=8) { + s += sq[pix1[0] - pix2[0]]; + s += sq[pix1[1] - pix2[1]]; + s += sq[pix1[2] - pix2[2]]; + s += sq[pix1[3] - pix2[3]]; + s += sq[pix1[4] - pix2[4]]; + s += sq[pix1[5] - pix2[5]]; + s += sq[pix1[6] - pix2[6]]; + s += sq[pix1[7] - pix2[7]]; + pix1 += 8; + pix2 += 8; + } + pix1 += line_size - 16; + pix2 += line_size - 16; + } + return s; +} + + +static int estimate_motion(MpegEncContext *s, + int mb_x, int mb_y, + int *mx_ptr, int *my_ptr) +{ + UINT8 *pix, *ppix; + int sum, varc, vard; + + pix = s->new_picture[0] + (mb_y * 16 * s->width) + mb_x * 16; + ppix = s->last_picture[0] + (mb_y * 16 * s->width) + mb_x * 16; + + sum = pix_sum(pix, s->width); + varc = pix_norm1(pix, s->width); + vard = pix_norm(pix, ppix, s->width); + + vard = vard >> 8; + sum = sum >> 8; + varc = (varc >> 8) - sum * sum; + + *mx_ptr = 0; + *my_ptr = 0; + if (vard <= 64) { + return 0; + } else if (vard < varc) { + return 0; + } else { + return 1; + } +} + +static void get_pixels(DCTELEM *block, const UINT8 *pixels, int line_size); +static void put_pixels(const DCTELEM *block, UINT8 *pixels, int line_size); +static void sub_pixels(DCTELEM *block, const UINT8 *pixels, int line_size); +static void add_pixels(DCTELEM *block, const UINT8 *pixels, int line_size); +static int dct_quantize(MpegEncContext *s, DCTELEM *block, int qscale); +static void encode_block(MpegEncContext *s, + DCTELEM *block, + int component); +static void dct_unquantize(MpegEncContext *s, DCTELEM *block, int qscale); +static void mpeg1_encode_mb(MpegEncContext *s, int mb_x, int mb_y, + DCTELEM block[6][64], + int motion_x, int motion_y); + +static void encode_picture(MpegEncContext *s, int picture_number) +{ + int mb_x, mb_y; + UINT8 *ptr; + DCTELEM block[6][64]; + int i, motion_x, motion_y; + + s->picture_number = picture_number; + s->qscale = rate_estimate_qscale(s); + + /* precompute matrix */ + if (s->out_format == FMT_MJPEG) { + /* for mjpeg, we do include qscale in the matrix */ + s->init_intra_matrix[0] = default_intra_matrix[0]; + for(i=1;i<64;i++) + s->init_intra_matrix[i] = (default_intra_matrix[i] * s->qscale) >> 3; + convert_matrix(s->intra_matrix, s->init_intra_matrix, 8); + } else { + convert_matrix(s->intra_matrix, default_intra_matrix, s->qscale); + convert_matrix(s->non_intra_matrix, default_non_intra_matrix, s->qscale); + } + + switch(s->out_format) { + case FMT_MJPEG: + mjpeg_picture_header(s); + break; + case FMT_H263: + if (s->h263_rv10) + rv10_encode_picture_header(s, picture_number); + else + h263_picture_header(s, picture_number); + break; + case FMT_MPEG1: + /* mpeg1 picture header */ + put_header(s, PICTURE_START_CODE); + /* temporal reference */ + put_bits(&s->pb, 10, (s->fake_picture_number - + s->gop_picture_number) & 0x3ff); + + put_bits(&s->pb, 3, s->pict_type); + put_bits(&s->pb, 16, 0xffff); /* non constant bit rate */ + + if (s->pict_type == P_TYPE) { + put_bits(&s->pb, 1, 1); /* integer coordinates */ + put_bits(&s->pb, 3, 1); /* forward_f_code */ + } + + put_bits(&s->pb, 1, 0); /* extra bit picture */ + + /* only one slice */ + put_header(s, SLICE_MIN_START_CODE); + put_bits(&s->pb, 5, s->qscale); /* quantizer scale */ + put_bits(&s->pb, 1, 0); /* slice extra information */ + break; + } + + /* init last dc values */ + /* XXX: quant matrix value is implied here */ + s->last_dc[0] = 128; + s->last_dc[1] = 128; + s->last_dc[2] = 128; + s->mb_incr = 1; + + for(mb_y=0; mb_y < s->mb_height; mb_y++) { + for(mb_x=0; mb_x < s->mb_width; mb_x++) { + /* compute motion vector and macro block type (intra or non intra) */ + motion_x = 0; + motion_y = 0; + if (s->pict_type == P_TYPE) { + s->mb_intra = estimate_motion(s, mb_x, mb_y, + &motion_x, + &motion_y); + } else { + s->mb_intra = 1; + } + + /* reset intra predictors if non intra mb */ + if (!s->mb_intra) { + s->last_dc[0] = 128; + s->last_dc[1] = 128; + s->last_dc[2] = 128; + } + + /* get the pixels */ + ptr = s->new_picture[0] + (mb_y * 16 * s->width) + mb_x * 16; + get_pixels(block[0], ptr, s->width); + get_pixels(block[1], ptr + 8, s->width); + get_pixels(block[2], ptr + 8 * s->width, s->width); + get_pixels(block[3], ptr + 8 * s->width + 8, s->width); + ptr = s->new_picture[1] + (mb_y * 8 * (s->width >> 1)) + mb_x * 8; + get_pixels(block[4],ptr, s->width >> 1); + + ptr = s->new_picture[2] + (mb_y * 8 * (s->width >> 1)) + mb_x * 8; + get_pixels(block[5],ptr, s->width >> 1); + + /* subtract previous frame if non intra */ + if (!s->mb_intra) { + ptr = s->last_picture[0] + + ((mb_y * 16 + motion_y) * s->width) + (mb_x * 16 + motion_x); + + sub_pixels(block[0], ptr, s->width); + sub_pixels(block[1], ptr + 8, s->width); + sub_pixels(block[2], ptr + s->width * 8, s->width); + sub_pixels(block[3], ptr + 8 + s->width * 8, s->width); + ptr = s->last_picture[1] + + ((mb_y * 8 + (motion_y >> 1)) * (s->width >> 1)) + + (mb_x * 8 + (motion_x >> 1)); + sub_pixels(block[4], ptr, s->width >> 1); + ptr = s->last_picture[2] + + ((mb_y * 8 + (motion_y >> 1)) * (s->width >> 1)) + + (mb_x * 8 + (motion_x >> 1)); + sub_pixels(block[5], ptr, s->width >> 1); + } + + /* DCT & quantize */ + for(i=0;i<6;i++) { + int last_index; + last_index = dct_quantize(s, block[i], s->qscale); + s->block_last_index[i] = last_index; + } + + /* huffman encode */ + switch(s->out_format) { + case FMT_MPEG1: + mpeg1_encode_mb(s, mb_x, mb_y, block, motion_x, motion_y); + break; + case FMT_H263: + h263_encode_mb(s, block, motion_x, motion_y); + break; + case FMT_MJPEG: + mjpeg_encode_mb(s, block); + break; + } + + /* decompress blocks so that we keep the state of the decoder */ + if (!s->intra_only) { + for(i=0;i<6;i++) { + if (s->block_last_index[i] >= 0) { + dct_unquantize(s, block[i], s->qscale); + } + } + + if (!s->mb_intra) { + ptr = s->last_picture[0] + + ((mb_y * 16 + motion_y) * s->width) + (mb_x * 16 + motion_x); + + add_pixels(block[0], ptr, s->width); + add_pixels(block[1], ptr + 8, s->width); + add_pixels(block[2], ptr + s->width * 8, s->width); + add_pixels(block[3], ptr + 8 + s->width * 8, s->width); + ptr = s->last_picture[1] + + ((mb_y * 8 + (motion_y >> 1)) * (s->width >> 1)) + + (mb_x * 8 + (motion_x >> 1)); + add_pixels(block[4], ptr, s->width >> 1); + ptr = s->last_picture[2] + + ((mb_y * 8 + (motion_y >> 1)) * (s->width >> 1)) + + (mb_x * 8 + (motion_x >> 1)); + add_pixels(block[5], ptr, s->width >> 1); + } + + /* write the pixels */ + ptr = s->current_picture[0] + (mb_y * 16 * s->width) + mb_x * 16; + put_pixels(block[0], ptr, s->width); + put_pixels(block[1], ptr + 8, s->width); + put_pixels(block[2], ptr + 8 * s->width, s->width); + put_pixels(block[3], ptr + 8 * s->width + 8, s->width); + ptr = s->current_picture[1] + (mb_y * 8 * (s->width >> 1)) + mb_x * 8; + put_pixels(block[4],ptr, s->width >> 1); + + ptr = s->current_picture[2] + (mb_y * 8 * (s->width >> 1)) + mb_x * 8; + put_pixels(block[5],ptr, s->width >> 1); + } + } + } +} + +static void mpeg1_encode_mb(MpegEncContext *s, int mb_x, int mb_y, + DCTELEM block[6][64], + int motion_x, int motion_y) +{ + int mb_incr, i, cbp; + + /* compute cbp */ + cbp = 0; + for(i=0;i<6;i++) { + if (s->block_last_index[i] >= 0) + cbp |= 1 << (5 - i); + } + + /* skip macroblock, except if first or last macroblock of a slice */ + if ((cbp | motion_x | motion_y) == 0 && + (!((mb_x | mb_y) == 0 || + (mb_x == s->mb_width - 1 && mb_y == s->mb_height - 1)))) { + s->mb_incr++; + } else { + /* output mb incr */ + mb_incr = s->mb_incr; + + while (mb_incr > 33) { + put_bits(&s->pb, 11, 0x008); + mb_incr -= 33; + } + put_bits(&s->pb, mbAddrIncrTable[mb_incr][1], + mbAddrIncrTable[mb_incr][0]); + + if (s->pict_type == I_TYPE) { + put_bits(&s->pb, 1, 1); /* macroblock_type : macroblock_quant = 0 */ + } else { + if (s->mb_intra) { + put_bits(&s->pb, 5, 0x03); + } else { + if (motion_x == 0 && motion_y == 0) { + if (cbp != 0) { + put_bits(&s->pb, 2, 1); /* macroblock_pattern only */ + put_bits(&s->pb, mbPatTable[cbp][1], mbPatTable[cbp][0]); + } else { + put_bits(&s->pb, 3, 1); /* motion only & zero motion vectors */ + /* zero motion x & y */ + put_bits(&s->pb, 1, 1); + put_bits(&s->pb, 1, 1); + } + } else { + /* XXX: not used yet */ + put_bits(&s->pb, mbPatTable[cbp][1], mbPatTable[cbp][0]); + } + } + + } + + for(i=0;i<6;i++) { + if (cbp & (1 << (5 - i))) { + encode_block(s, block[i], i); + } + } + s->mb_incr = 1; + } +} + +static void get_pixels(DCTELEM *block, const UINT8 *pixels, int line_size) +{ + DCTELEM *p; + const UINT8 *pix; + int i; + + /* read the pixels */ + p = block; + pix = pixels; + for(i=0;i<8;i++) { + p[0] = pix[0]; + p[1] = pix[1]; + p[2] = pix[2]; + p[3] = pix[3]; + p[4] = pix[4]; + p[5] = pix[5]; + p[6] = pix[6]; + p[7] = pix[7]; + pix += line_size; + p += 8; + } +} + +static void put_pixels(const DCTELEM *block, UINT8 *pixels, int line_size) +{ + const DCTELEM *p; + UINT8 *pix; + int i; + UINT8 *cm = cropTbl + MAX_NEG_CROP; + + /* read the pixels */ + p = block; + pix = pixels; + for(i=0;i<8;i++) { + pix[0] = cm[p[0]]; + pix[1] = cm[p[1]]; + pix[2] = cm[p[2]]; + pix[3] = cm[p[3]]; + pix[4] = cm[p[4]]; + pix[5] = cm[p[5]]; + pix[6] = cm[p[6]]; + pix[7] = cm[p[7]]; + pix += line_size; + p += 8; + } +} + +static void sub_pixels(DCTELEM *block, const UINT8 *pixels, int line_size) +{ + DCTELEM *p; + const UINT8 *pix; + int i; + + /* read the pixels */ + p = block; + pix = pixels; + for(i=0;i<8;i++) { + p[0] -= pix[0]; + p[1] -= pix[1]; + p[2] -= pix[2]; + p[3] -= pix[3]; + p[4] -= pix[4]; + p[5] -= pix[5]; + p[6] -= pix[6]; + p[7] -= pix[7]; + pix += line_size; + p += 8; + } +} + +static void add_pixels(DCTELEM *block, const UINT8 *pixels, int line_size) +{ + DCTELEM *p; + const UINT8 *pix; + int i; + + /* read the pixels */ + p = block; + pix = pixels; + for(i=0;i<8;i++) { + p[0] += pix[0]; + p[1] += pix[1]; + p[2] += pix[2]; + p[3] += pix[3]; + p[4] += pix[4]; + p[5] += pix[5]; + p[6] += pix[6]; + p[7] += pix[7]; + pix += line_size; + p += 8; + } +} + +#define USE_FAST_MUL + +static int dct_quantize(MpegEncContext *s, + DCTELEM *block, + int qscale) +{ + int i, j, level, last_non_zero; +#ifdef USE_FAST_MUL + const int *qmat; +#else + const UINT8 *qmat; +#endif + + jpeg_fdct_ifast (block); + + if (s->mb_intra) { + block[0] = (block[0] + 4 * 8) >> 6; + i = 1; + last_non_zero = 0; + if (s->out_format == FMT_H263) { +#ifdef USE_FAST_MUL + qmat = s->non_intra_matrix; +#else + qmat = default_non_intra_matrix; +#endif + } else { +#ifdef USE_FAST_MUL + qmat = s->intra_matrix; +#else + qmat = default_intra_matrix; +#endif + } + } else { + i = 0; + last_non_zero = -1; +#ifdef USE_FAST_MUL + qmat = s->non_intra_matrix; +#else + qmat = default_non_intra_matrix; +#endif + } + + for(;i<64;i++) { + j = zigzag_direct[i]; + level = block[j]; +#ifdef USE_FAST_MUL + level = (level * qmat[j]) / (1 << 22); +#else + /* post dct normalization */ + level = (level << 11) / aanscales[j]; + /* quantification */ + level = (8 * level) / (qscale * qmat[j]); +#endif + block[j] = level; + if (level) + last_non_zero = i; + } + return last_non_zero; +} + +static void dct_unquantize(MpegEncContext *s, + DCTELEM *block, int qscale) +{ + int i, level, coeff; + const UINT8 *quant_matrix; + + if (s->mb_intra) { + block[0] = block[0] << 3; + if (s->out_format == FMT_H263) { + i = 1; + goto unquant_even; + } + quant_matrix = default_intra_matrix; + for(i=1;i<64;i++) { + block[i] = (block[i] * qscale * quant_matrix[i]) >> 3; + } + } else { + i = 0; + unquant_even: + quant_matrix = default_non_intra_matrix; + for(;i<64;i++) { + level = block[i]; + if (level) { + if (level < 0) { + coeff = (((level << 1) - 1) * qscale * + ((int) (quant_matrix[i]))) >> 4; + coeff += (coeff & 1); + } else { + coeff = (((level << 1) + 1) * qscale * + ((int) (quant_matrix[i]))) >> 4; + coeff -= (coeff & 1); + } + block[i] = coeff; + } + } + } + + j_rev_dct(block); +} + + +static inline void encode_dc(MpegEncContext *s, int diff, int component) +{ + int adiff, index; + + // printf("dc=%d c=%d\n", diff, component); + adiff = abs(diff); + index = vlc_dc_table[adiff]; + if (component == 0) { + put_bits(&s->pb, vlc_dc_lum_bits[index], vlc_dc_lum_code[index]); + } else { + put_bits(&s->pb, vlc_dc_chroma_bits[index], vlc_dc_chroma_code[index]); + } + if (diff > 0) { + put_bits(&s->pb, index, (diff & ((1 << index) - 1))); + } else if (diff < 0) { + put_bits(&s->pb, index, ((diff - 1) & ((1 << index) - 1))); + } +} + +static void encode_block(MpegEncContext *s, + DCTELEM *block, + int n) +{ + int alevel, level, last_non_zero, dc, diff, i, j, run, last_index; + int code, nbits, component; + + last_index = s->block_last_index[n]; + + /* DC coef */ + if (s->mb_intra) { + component = (n <= 3 ? 0 : n - 4 + 1); + dc = block[0]; /* overflow is impossible */ + diff = dc - s->last_dc[component]; + encode_dc(s, diff, component); + s->last_dc[component] = dc; + i = 1; + } else { + /* encode the first coefficient : needs to be done here because + it is handled slightly differently */ + level = block[0]; + if (abs(level) == 1) { + code = ((UINT32)level >> 31); /* the sign bit */ + put_bits(&s->pb, 2, code | 0x02); + i = 1; + } else { + i = 0; + last_non_zero = -1; + goto next_coef; + } + } + + /* now quantify & encode AC coefs */ + last_non_zero = i - 1; + for(;i<=last_index;i++) { + j = zigzag_direct[i]; + level = block[j]; + next_coef: +#if 0 + if (level != 0) + printf("level[%d]=%d\n", i, level); +#endif + /* encode using VLC */ + if (level != 0) { + run = i - last_non_zero - 1; + alevel = abs(level); + // printf("run=%d level=%d\n", run, level); + if ( (run < HUFF_MAXRUN) && (alevel < huff_maxlevel[run])) { + /* encode using the Huffman tables */ + code = (huff_table[run])[alevel]; + nbits = (huff_bits[run])[alevel]; + code |= ((UINT32)level >> 31); /* the sign bit */ + + put_bits(&s->pb, nbits, code); + } else { + /* escape: only clip in this case */ + if (level > 255) + level = 255; + else if (level < -255) + level = -255; + put_bits(&s->pb, 6, 0x1); + put_bits(&s->pb, 6, run); + if (alevel < 128) { + put_bits(&s->pb, 8, level & 0xff); + } else { + if (level < 0) { + put_bits(&s->pb, 16, 0x8001 + level + 255); + } else { + put_bits(&s->pb, 16, level & 0xffff); + } + } + } + last_non_zero = i; + } + } + /* end of block */ + put_bits(&s->pb, 2, 0x2); +} + + +/* rate control */ + +/* an I frame is I_FRAME_SIZE_RATIO bigger than a P frame */ +#define I_FRAME_SIZE_RATIO 1.5 +#define QSCALE_K 20 + +static void rate_control_init(MpegEncContext *s) +{ + s->wanted_bits = 0; + + if (s->intra_only) { + s->I_frame_bits = s->bit_rate / s->frame_rate; + s->P_frame_bits = s->I_frame_bits; + } else { + s->P_frame_bits = (int) ((float)(s->gop_size * s->bit_rate) / + (float)(s->frame_rate * (I_FRAME_SIZE_RATIO + s->gop_size - 1))); + s->I_frame_bits = (int)(s->P_frame_bits * I_FRAME_SIZE_RATIO); + } + +#if defined(DEBUG) + printf("I_frame_size=%d P_frame_size=%d\n", + s->I_frame_bits, s->P_frame_bits); +#endif +} + + +/* + * This heuristic is rather poor, but at least we do not have to + * change the qscale at every macroblock. + */ +static int rate_estimate_qscale(MpegEncContext *s) +{ + long long total_bits = s->total_bits; + float q; + int qscale, diff; + + if (s->pict_type == I_TYPE) { + s->wanted_bits += s->I_frame_bits; + } else { + s->wanted_bits += s->P_frame_bits; + } + diff = s->wanted_bits - total_bits; + q = 31.0 - (float)diff / (QSCALE_K * s->mb_height * s->mb_width); + /* adjust for I frame */ + if (s->pict_type == I_TYPE && !s->intra_only) { + q /= I_FRAME_SIZE_RATIO; + } + + if (q < 1) + q = 1; + else if (q > 31) + q = 31; + qscale = (int)(q + 0.5); +#if defined(DEBUG) + printf("%d: total=%Ld br=%0.1f diff=%d qest=%0.1f\n", + s->picture_number, + total_bits, (float)s->frame_rate * total_bits / s->picture_number, + diff, q); +#endif + return qscale; +} + +AVEncoder mpeg1video_encoder = { + "mpeg1video", + CODEC_TYPE_VIDEO, + CODEC_ID_MPEG1VIDEO, + sizeof(MpegEncContext), + MPV_encode_init, + MPV_encode_picture, + MPV_encode_end, +}; + +AVEncoder h263_encoder = { + "h263", + CODEC_TYPE_VIDEO, + CODEC_ID_H263, + sizeof(MpegEncContext), + MPV_encode_init, + MPV_encode_picture, + MPV_encode_end, +}; + +AVEncoder rv10_encoder = { + "rv10", + CODEC_TYPE_VIDEO, + CODEC_ID_RV10, + sizeof(MpegEncContext), + MPV_encode_init, + MPV_encode_picture, + MPV_encode_end, +}; + +AVEncoder mjpeg_encoder = { + "mjpeg", + CODEC_TYPE_VIDEO, + CODEC_ID_MJPEG, + sizeof(MpegEncContext), + MPV_encode_init, + MPV_encode_picture, + MPV_encode_end, +}; diff --git a/libav/mpegvideo.h b/libav/mpegvideo.h new file mode 100644 index 0000000000..e1fbe044a0 --- /dev/null +++ b/libav/mpegvideo.h @@ -0,0 +1,94 @@ +/* mpegencode.c */ + +/* Start codes. */ +#define SEQ_END_CODE 0x000001b7 +#define SEQ_START_CODE 0x000001b3 +#define GOP_START_CODE 0x000001b8 +#define PICTURE_START_CODE 0x00000100 +#define SLICE_MIN_START_CODE 0x00000101 +#define SLICE_MAX_START_CODE 0x000001af +#define EXT_START_CODE 0x000001b5 +#define USER_START_CODE 0x000001b2 + +/* Macros for picture code type. */ +#define I_TYPE 1 +#define P_TYPE 2 +#define B_TYPE 3 + +typedef int DCTELEM; + +enum OutputFormat { + FMT_MPEG1, + FMT_H263, + FMT_MJPEG, +}; + +#define MAX_NEG_CROP 384 + +#define MPEG_BUF_SIZE (16 * 1024) + +typedef struct MpegEncContext { + /* the following parameters must be initialized before encoding */ + int width, height; /* picture size. must be a multiple of 16 */ + int gop_size; + int frame_rate; /* number of frames per second */ + int intra_only; /* if true, only intra pictures are generated */ + int bit_rate; /* wanted bit rate */ + enum OutputFormat out_format; /* output format */ + int h263_rv10; /* use RV10 variation for H263 */ + + /* the following fields are managed internally by the encoder */ + + /* bit output */ + PutBitContext pb; + + /* sequence parameters */ + int picture_number; + int fake_picture_number; /* picture number at the bitstream frame rate */ + int gop_picture_number; /* index of the first picture of a GOP */ + int mb_width, mb_height; + UINT8 *new_picture[3]; /* picture to be compressed */ + UINT8 *last_picture[3]; /* previous picture */ + UINT8 *current_picture[3]; /* buffer to store the decompressed current picture */ + int last_dc[3]; + int qscale; + int pict_type; + int frame_rate_index; + /* macroblock layer */ + int mb_incr; + int mb_intra; + /* matrix transmitted in the bitstream */ + UINT8 init_intra_matrix[64]; + /* precomputed matrix (combine qscale and DCT renorm) */ + int intra_matrix[64]; + int non_intra_matrix[64]; + int block_last_index[6]; /* last non zero coefficient in block */ + + void *opaque; /* private data for the user */ + + /* bit rate control */ + int I_frame_bits; /* wanted number of bits per I frame */ + int P_frame_bits; /* same for P frame */ + long long wanted_bits; + long long total_bits; + struct MJpegContext *mjpeg_ctx; +} MpegEncContext; + +extern const UINT8 zigzag_direct[64]; + +/* h263enc.c */ + +void h263_encode_mb(MpegEncContext *s, + DCTELEM block[6][64], + int motion_x, int motion_y); +void h263_picture_header(MpegEncContext *s, int picture_number); +void rv10_encode_picture_header(MpegEncContext *s, int picture_number); + +/* mjpegenc.c */ + +int mjpeg_init(MpegEncContext *s); +void mjpeg_close(MpegEncContext *s); +void mjpeg_encode_mb(MpegEncContext *s, + DCTELEM block[6][64]); +void mjpeg_picture_header(MpegEncContext *s); +void mjpeg_picture_trailer(MpegEncContext *s); diff --git a/libav/resample.c b/libav/resample.c new file mode 100644 index 0000000000..008153b0d6 --- /dev/null +++ b/libav/resample.c @@ -0,0 +1,245 @@ +/* + * Sample rate convertion for both audio and video + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <math.h> +#include "avcodec.h" + +#define NDEBUG +#include <assert.h> + +#define FRAC_BITS 16 +#define FRAC (1 << FRAC_BITS) + +static void init_mono_resample(ReSampleChannelContext *s, float ratio) +{ + ratio = 1.0 / ratio; + s->iratio = (int)floor(ratio); + if (s->iratio == 0) + s->iratio = 1; + s->incr = (int)((ratio / s->iratio) * FRAC); + s->frac = 0; + s->last_sample = 0; + s->icount = s->iratio; + s->isum = 0; + s->inv = (FRAC / s->iratio); +} + +/* fractional audio resampling */ +static int fractional_resample(ReSampleChannelContext *s, short *output, short *input, int nb_samples) +{ + unsigned int frac, incr; + int l0, l1; + short *q, *p, *pend; + + l0 = s->last_sample; + incr = s->incr; + frac = s->frac; + + p = input; + pend = input + nb_samples; + q = output; + + l1 = *p++; + for(;;) { + /* interpolate */ + *q++ = (l0 * (FRAC - frac) + l1 * frac) >> FRAC_BITS; + frac = frac + s->incr; + while (frac >= FRAC) { + if (p >= pend) + goto the_end; + frac -= FRAC; + l0 = l1; + l1 = *p++; + } + } + the_end: + s->last_sample = l1; + s->frac = frac; + return q - output; +} + +static int integer_downsample(ReSampleChannelContext *s, short *output, short *input, int nb_samples) +{ + short *q, *p, *pend; + int c, sum; + + p = input; + pend = input + nb_samples; + q = output; + + c = s->icount; + sum = s->isum; + + for(;;) { + sum += *p++; + if (--c == 0) { + *q++ = (sum * s->inv) >> FRAC_BITS; + c = s->iratio; + sum = 0; + } + if (p >= pend) + break; + } + s->isum = sum; + s->icount = c; + return q - output; +} + +/* n1: number of samples */ +static void stereo_to_mono(short *output, short *input, int n1) +{ + short *p, *q; + int n = n1; + + p = input; + q = output; + while (n >= 4) { + q[0] = (p[0] + p[1]) >> 1; + q[1] = (p[2] + p[3]) >> 1; + q[2] = (p[4] + p[5]) >> 1; + q[3] = (p[6] + p[7]) >> 1; + q += 4; + p += 8; + n -= 4; + } + while (n > 0) { + q[0] = (p[0] + p[1]) >> 1; + q++; + p += 2; + n--; + } +} + +/* XXX: should use more abstract 'N' channels system */ +static void stereo_split(short *output1, short *output2, short *input, int n) +{ + int i; + + for(i=0;i<n;i++) { + *output1++ = *input++; + *output2++ = *input++; + } +} + +static void stereo_mux(short *output, short *input1, short *input2, int n) +{ + int i; + + for(i=0;i<n;i++) { + *output++ = *input1++; + *output++ = *input2++; + } +} + +static int mono_resample(ReSampleChannelContext *s, short *output, short *input, int nb_samples) +{ + short buf1[nb_samples]; + short *buftmp; + + /* first downsample by an integer factor with averaging filter */ + if (s->iratio > 1) { + buftmp = buf1; + nb_samples = integer_downsample(s, buftmp, input, nb_samples); + } else { + buftmp = input; + } + + /* then do a fractional resampling with linear interpolation */ + if (s->incr != FRAC) { + nb_samples = fractional_resample(s, output, buftmp, nb_samples); + } else { + memcpy(output, buftmp, nb_samples * sizeof(short)); + } + return nb_samples; +} + +/* ratio = output_rate / input_rate */ +int audio_resample_init(ReSampleContext *s, + int output_channels, int input_channels, + int output_rate, int input_rate) +{ + int i; + + s->ratio = (float)output_rate / (float)input_rate; + + if (output_channels > 2 || input_channels > 2) + return -1; + s->input_channels = input_channels; + s->output_channels = output_channels; + + for(i=0;i<output_channels;i++) { + init_mono_resample(&s->channel_ctx[i], s->ratio); + } + return 0; +} + +/* resample audio. 'nb_samples' is the number of input samples */ +/* XXX: optimize it ! */ +/* XXX: do it with polyphase filters, since the quality here is + HORRIBLE. Return the number of samples available in output */ +int audio_resample(ReSampleContext *s, short *output, short *input, int nb_samples) +{ + int i, nb_samples1; + short buf[5][nb_samples]; + short *buftmp1, *buftmp2[2], *buftmp3[2]; + + if (s->input_channels == s->output_channels && s->ratio == 1.0) { + /* nothing to do */ + memcpy(output, input, nb_samples * s->input_channels * sizeof(short)); + return nb_samples; + } + + if (s->input_channels == 2 && + s->output_channels == 1) { + buftmp1 = buf[0]; + stereo_to_mono(buftmp1, input, nb_samples); + } else if (s->input_channels == 1 && + s->output_channels == 2) { + /* XXX: do it */ + abort(); + } else { + buftmp1 = input; + } + + if (s->output_channels == 2) { + buftmp2[0] = buf[1]; + buftmp2[1] = buf[2]; + buftmp3[0] = buf[3]; + buftmp3[1] = buf[4]; + stereo_split(buftmp2[0], buftmp2[1], buftmp1, nb_samples); + } else { + buftmp2[0] = buftmp1; + buftmp3[0] = output; + } + + /* resample each channel */ + nb_samples1 = 0; /* avoid warning */ + for(i=0;i<s->output_channels;i++) { + nb_samples1 = mono_resample(&s->channel_ctx[i], buftmp3[i], buftmp2[i], nb_samples); + } + + if (s->output_channels == 2) { + stereo_mux(output, buftmp3[0], buftmp3[1], nb_samples1); + } + + return nb_samples1; +} diff --git a/mpegenc.h b/mpegenc.h new file mode 100644 index 0000000000..a7941be463 --- /dev/null +++ b/mpegenc.h @@ -0,0 +1,127 @@ + +#include "avcodec.h" + +/* byte stream handling */ + +typedef struct { + unsigned char *buffer; + unsigned char *buf_ptr, *buf_end; + void *opaque; + void (*write_packet)(void *opaque, UINT8 *buf, int buf_size); + int (*write_seek)(void *opaque, long long offset, int whence); + long long pos; /* position in the file of the current buffer */ +} PutByteContext; + +int init_put_byte(PutByteContext *s, + unsigned char *buffer, + int buffer_size, + void *opaque, + void (*write_packet)(void *opaque, UINT8 *buf, int buf_size), + int (*write_seek)(void *opaque, long long offset, int whence)); + +void put_byte(PutByteContext *s, int b); +void put_buffer(PutByteContext *s, unsigned char *buf, int size); +void put_le32(PutByteContext *s, unsigned int val); +void put_le64(PutByteContext *s, unsigned long long val); +void put_le16(PutByteContext *s, unsigned int val); +void put_tag(PutByteContext *s, char *tag); + +long long put_seek(PutByteContext *s, long long offset, int whence); +long long put_pos(PutByteContext *s); + +void put_flush_packet(PutByteContext *s); + +/* udp.c */ + +typedef struct { + int udp_socket; + int max_payload_size; /* in bytes */ +} UDPContext; + +int udp_tx_open(UDPContext *s, + const char *uri, + int local_port); +void udp_tx_close(UDPContext *s); +void udp_write_data(void *opaque, UINT8 *buf, int size); + +/* generic functions */ + +struct AVFormatContext; + +typedef struct AVFormat { + char *name; + char *long_name; + char *mime_type; + char *extensions; /* comma separated extensions */ + enum CodecID audio_codec; + enum CodecID video_codec; + int (*write_header)(struct AVFormatContext *); + int (*write_audio_frame)(struct AVFormatContext *, + unsigned char *buf, int size); + int (*write_video_picture)(struct AVFormatContext *, + unsigned char *buf, int size); + int (*write_trailer)(struct AVFormatContext *); + struct AVFormat *next; +} AVFormat; + +typedef struct AVFormatContext { + struct AVFormat *format; + void *priv_data; + PutByteContext pb; + AVEncodeContext *video_enc; + AVEncodeContext *audio_enc; + int is_streamed; /* true if the stream is generated as being streamed */ +} AVFormatContext; + +extern AVFormat *first_format; +extern int data_out_size; +extern const char *comment_string; + +/* rv10enc.c */ +extern AVFormat rm_format; +extern AVFormat ra_format; + +/* mpegmux.c */ +extern AVFormat mpeg_mux_format; + +/* asfenc.c */ +extern AVFormat asf_format; + +/* jpegenc.c */ +extern AVFormat mpjpeg_format; +extern AVFormat jpeg_format; + +/* swfenc.c */ +extern AVFormat swf_format; + +/* formats.c */ +void register_avformat(AVFormat *format); +AVFormat *guess_format(const char *short_name, const char *filename, const char *mime_type); + +void register_avencoder(AVEncoder *format); +AVEncoder *avencoder_find(enum CodecID id); +void avencoder_string(char *buf, int buf_size, AVEncodeContext *enc); + +int avencoder_open(AVEncodeContext *avctx, AVEncoder *codec); +int avencoder_encode(AVEncodeContext *avctx, UINT8 *buf, int buf_size, void *data); +int avencoder_close(AVEncodeContext *avctx); + +extern AVFormat mp2_format; +extern AVFormat ac3_format; +extern AVFormat h263_format; +extern AVFormat mpeg1video_format; + +int strstart(const char *str, const char *val, const char **ptr); + + +/* grab.c */ + +extern const char *v4l_device; + +long long gettime(void); +int v4l_init(int rate, int width, int height); +int v4l_read_picture(UINT8 *picture[3], + int width, int height, + int picture_number); + +int audio_open(int freq, int channels); diff --git a/mpegmux.c b/mpegmux.c new file mode 100644 index 0000000000..fff01525a5 --- /dev/null +++ b/mpegmux.c @@ -0,0 +1,301 @@ +/* + * Output a MPEG1 multiplexed video/audio stream + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <linux/videodev.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <errno.h> +#include <sys/time.h> +#include <getopt.h> + +#include "mpegenc.h" +#include "mpegvideo.h" +#include "mpegaudio.h" + +#define MAX_PAYLOAD_SIZE 4096 +#define NB_STREAMS 2 + +typedef struct { + UINT8 buffer[MAX_PAYLOAD_SIZE]; + int buffer_ptr; + UINT8 id; + int max_buffer_size; + int packet_number; + AVEncodeContext *enc; + float pts; +} StreamInfo; + +typedef struct { + int packet_size; /* required packet size */ + int packet_number; + int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ + int system_header_freq; + int mux_rate; /* bitrate in units of 50 bytes/s */ + /* stream info */ + int nb_streams; + StreamInfo streams[NB_STREAMS]; + AVFormatContext *ctx; +} MpegMuxContext; + +#define PACK_START_CODE ((unsigned int)0x000001ba) +#define SYSTEM_HEADER_START_CODE ((unsigned int)0x000001bb) +#define PACKET_START_CODE_MASK ((unsigned int)0xffffff00) +#define PACKET_START_CODE_PREFIX ((unsigned int)0x00000100) +#define ISO_11172_END_CODE ((unsigned int)0x000001b9) + +#define AUDIO_ID 0xc0 +#define VIDEO_ID 0xe0 + +static int put_pack_header(MpegMuxContext *s, UINT8 *buf, long long timestamp) +{ + PutBitContext pb; + + init_put_bits(&pb, buf, 128, NULL, NULL); + + put_bits(&pb, 32, PACK_START_CODE); + put_bits(&pb, 4, 0x2); + put_bits(&pb, 3, (timestamp >> 30) & 0x07); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp >> 15) & 0x7fff); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp) & 0x7fff); + put_bits(&pb, 1, 1); + put_bits(&pb, 1, 1); + put_bits(&pb, 22, s->mux_rate); + + flush_put_bits(&pb); + return pb.buf_ptr - pb.buf; +} + +static int put_system_header(MpegMuxContext *s, UINT8 *buf) +{ + int audio_bound, video_bound; + int size, rate_bound, i; + PutBitContext pb; + + init_put_bits(&pb, buf, 128, NULL, NULL); + + put_bits(&pb, 32, SYSTEM_HEADER_START_CODE); + put_bits(&pb, 16, 0); + put_bits(&pb, 1, 1); + + rate_bound = s->mux_rate; /* maximum bit rate of the multiplexed stream */ + put_bits(&pb, 22, rate_bound); + put_bits(&pb, 1, 1); /* marker */ + audio_bound = 1; /* at most one audio stream */ + put_bits(&pb, 6, audio_bound); + + put_bits(&pb, 1, 0); /* variable bitrate */ + put_bits(&pb, 1, 0); /* non constrainted bit stream */ + + put_bits(&pb, 1, 1); /* audio locked */ + put_bits(&pb, 1, 1); /* video locked */ + put_bits(&pb, 1, 1); /* marker */ + + video_bound = 1; /* at most one video stream */ + put_bits(&pb, 5, video_bound); + put_bits(&pb, 8, 0xff); /* reserved byte */ + + /* audio stream info */ + for(i=0;i<s->nb_streams;i++) { + put_bits(&pb, 8, s->streams[i].id); /* stream ID */ + put_bits(&pb, 2, 3); + put_bits(&pb, 1, 1); /* buffer bound scale = 1024 */ + put_bits(&pb, 13, s->streams[i].max_buffer_size); /* max buffer size */ + } + /* no more streams */ + put_bits(&pb, 1, 0); + flush_put_bits(&pb); + size = pb.buf_ptr - pb.buf; + /* patch packet size */ + buf[4] = (size - 6) >> 8; + buf[5] = (size - 6) & 0xff; + + return size; +} + +/* Format a packet header for a total size of 'total_size'. Return the + header size */ +static int put_packet_header(MpegMuxContext *s, + int id, long long timestamp, + UINT8 *buffer, int total_size) +{ + UINT8 *buf_ptr; + PutBitContext pb; + int size, payload_size; + +#if 0 + printf("packet ID=%2x PTS=%0.3f size=%d\n", + id, timestamp / 90000.0, total_size); +#endif + + buf_ptr = buffer; + + if ((s->packet_number % s->pack_header_freq) == 0) { + /* output pack and systems header */ + size = put_pack_header(s, buf_ptr, timestamp); + buf_ptr += size; + if ((s->packet_number % s->system_header_freq) == 0) { + size = put_system_header(s, buf_ptr); + buf_ptr += size; + } + } + + payload_size = total_size - ((buf_ptr - buffer) + 6 + 5); + /* packet header */ + init_put_bits(&pb, buf_ptr, 128, NULL, NULL); + + put_bits(&pb, 32, PACKET_START_CODE_PREFIX + id); + put_bits(&pb, 16, payload_size + 5); + /* presentation time stamp */ + put_bits(&pb, 4, 0x02); + put_bits(&pb, 3, (timestamp >> 30) & 0x07); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp >> 15) & 0x7fff); + put_bits(&pb, 1, 1); + put_bits(&pb, 15, (timestamp) & 0x7fff); + put_bits(&pb, 1, 1); + + flush_put_bits(&pb); + + s->packet_number++; + return pb.buf_ptr - buffer; +} + +int mpeg_mux_init(AVFormatContext *ctx) +{ + MpegMuxContext *s; + int bitrate, i; + + s = malloc(sizeof(MpegMuxContext)); + if (!s) + return -1; + memset(s, 0, sizeof(MpegMuxContext)); + ctx->priv_data = s; + s->ctx = ctx; + s->packet_number = 0; + + /* XXX: hardcoded */ + s->packet_size = 2048; + s->nb_streams = 2; + s->streams[0].id = AUDIO_ID; + s->streams[0].max_buffer_size = 10; /* in KBytes */ + s->streams[0].enc = ctx->audio_enc; + s->streams[1].id = VIDEO_ID; + s->streams[1].max_buffer_size = 50; /* in KBytes */ + s->streams[1].enc = ctx->video_enc; + + /* we increase slightly the bitrate to take into account the + headers. XXX: compute it exactly */ + bitrate = 2000; + for(i=0;i<s->nb_streams;i++) { + bitrate += s->streams[i].enc->bit_rate; + } + s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50); + /* every 2 seconds */ + s->pack_header_freq = 2 * bitrate / s->packet_size / 8; + /* every 10 seconds */ + s->system_header_freq = s->pack_header_freq * 5; + + for(i=0;i<NB_STREAMS;i++) { + s->streams[i].buffer_ptr = 0; + s->streams[i].packet_number = 0; + s->streams[i].pts = 0; + } + return 0; +} + +int mpeg_mux_end(AVFormatContext *ctx) +{ + PutBitContext pb; + UINT8 buffer[128]; + + /* write the end header */ + init_put_bits(&pb, buffer, sizeof(buffer), NULL, NULL); + put_bits(&pb, 32, ISO_11172_END_CODE); + + put_buffer(&ctx->pb, buffer, pb.buf_ptr - buffer); + put_flush_packet(&ctx->pb); + return 0; +} + +static void write_stream(MpegMuxContext *s, StreamInfo *stream, UINT8 *buf, int size) +{ + int len, len1, header_size; + long long pts; + while (size > 0) { + if (stream->buffer_ptr == 0) { + pts = stream->pts * 90000.0; + header_size = put_packet_header(s, stream->id, pts, stream->buffer, s->packet_size); + stream->buffer_ptr = header_size; + } + len = size; + len1 = s->packet_size - stream->buffer_ptr; + if (len > len1) + len = len1; + memcpy(stream->buffer + stream->buffer_ptr, buf, len); + stream->buffer_ptr += len; + if (stream->buffer_ptr == s->packet_size) { + /* output the packet */ + put_buffer(&s->ctx->pb, stream->buffer, s->packet_size); + put_flush_packet(&s->ctx->pb); + stream->buffer_ptr = 0; + stream->packet_number++; + } + buf += len; + size -= len; + } +} + +static int mpeg_mux_write_audio(AVFormatContext *ctx, UINT8 *buf, int size) +{ + MpegMuxContext *s = ctx->priv_data; + + write_stream(s, &s->streams[0], buf, size); + s->streams[0].pts += (float)s->streams[0].enc->frame_size / s->streams[0].enc->rate; + return 0; +} + +int mpeg_mux_write_video(AVFormatContext *ctx, UINT8 *buf, int size) +{ + MpegMuxContext *s = ctx->priv_data; + + write_stream(s, &s->streams[1], buf, size); + s->streams[1].pts += 1.0 / (float)s->streams[1].enc->rate; + + return 0; +} + +AVFormat mpeg_mux_format = { + "mpeg1", + "MPEG1 multiplex format", + "video/mpeg", + "mpg,mpeg", + CODEC_ID_MP2, + CODEC_ID_MPEG1VIDEO, + mpeg_mux_init, + mpeg_mux_write_audio, + mpeg_mux_write_video, + mpeg_mux_end, +}; diff --git a/rmenc.c b/rmenc.c new file mode 100644 index 0000000000..60a3b2a3ad --- /dev/null +++ b/rmenc.c @@ -0,0 +1,498 @@ +/* + * RV 1.0 compatible encoder. + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include "mpegenc.h" +#include "mpegvideo.h" + +/* in ms */ +#define BUFFER_DURATION 0 + +typedef struct { + int nb_packets; + int packet_total_size; + int packet_max_size; + /* codec related output */ + int bit_rate; + float frame_rate; + int nb_frames; /* current frame number */ + int total_frames; /* total number of frames */ + int num; + AVEncodeContext *enc; +} StreamInfo; + +typedef struct { + StreamInfo streams[2]; + StreamInfo *audio_stream, *video_stream; + int nb_streams; + int data_pos; /* position of the data after the header */ +} RMContext; + +static void put_long(PutByteContext *s, unsigned int val) +{ + put_byte(s, val >> 24); + put_byte(s, val >> 16); + put_byte(s, val >> 8); + put_byte(s, val); +} + +static void put_short(PutByteContext *s, unsigned int val) +{ + put_byte(s, val >> 8); + put_byte(s, val); +} + +static void put_str(PutByteContext *s, const char *tag) +{ + put_short(s,strlen(tag)); + while (*tag) { + put_byte(s, *tag++); + } +} + +static void put_str8(PutByteContext *s, const char *tag) +{ + put_byte(s, strlen(tag)); + while (*tag) { + put_byte(s, *tag++); + } +} + +int find_tag(const char *tag, char *buf, int buf_size, const char *str) +{ + int len = strlen(tag); + char *q; + + buf[0] = '\0'; + while (*str) { + if (*str == '+' && !strncmp(str + 1, tag, len) && str[len+1] == '=') { + str += len + 2; + q = buf; + while (*str && *str != '+' && (q - buf) < (buf_size - 1)) { + *q++ = *str++; + } + /* remove trailing spaces */ + while (q > buf && q[-1] == ' ') q--; + *q = '\0'; + return 1; + } + str++; + } + return 0; +} + +static void rv10_write_header(AVFormatContext *ctx, + int data_size, int index_pos) +{ + RMContext *rm = ctx->priv_data; + PutByteContext *s = &ctx->pb; + StreamInfo *stream; + unsigned char *data_offset_ptr, *start_ptr; + char title[1024], author[1024], copyright[1024], comment[1024]; + const char *desc, *mimetype; + int nb_packets, packet_total_size, packet_max_size, size, packet_avg_size, i; + int bit_rate, v, duration, flags, data_pos; + + start_ptr = s->buf_ptr; + + put_tag(s, ".RMF"); + put_long(s,18); /* header size */ + put_short(s,0); + put_long(s,0); + put_long(s,4 + rm->nb_streams); /* num headers */ + + put_tag(s,"PROP"); + put_long(s, 50); + put_short(s, 0); + packet_max_size = 0; + packet_total_size = 0; + nb_packets = 0; + bit_rate = 0; + duration = 0; + for(i=0;i<rm->nb_streams;i++) { + StreamInfo *stream = &rm->streams[i]; + bit_rate += stream->bit_rate; + if (stream->packet_max_size > packet_max_size) + packet_max_size = stream->packet_max_size; + nb_packets += stream->nb_packets; + packet_total_size += stream->packet_total_size; + /* select maximum duration */ + v = (int) (1000.0 * (float)stream->total_frames / stream->frame_rate); + if (v > duration) + duration = v; + } + put_long(s, bit_rate); /* max bit rate */ + put_long(s, bit_rate); /* avg bit rate */ + put_long(s, packet_max_size); /* max packet size */ + if (nb_packets > 0) + packet_avg_size = packet_total_size / nb_packets; + else + packet_avg_size = 0; + put_long(s, packet_avg_size); /* avg packet size */ + put_long(s, nb_packets); /* num packets */ + put_long(s, duration); /* duration */ + put_long(s, BUFFER_DURATION); /* preroll */ + put_long(s, index_pos); /* index offset */ + /* computation of data the data offset */ + data_offset_ptr = s->buf_ptr; + put_long(s, 0); /* data offset : will be patched after */ + put_short(s, rm->nb_streams); /* num streams */ + flags = 1 | 2; /* save allowed & perfect play */ + if (ctx->is_streamed) + flags |= 4; /* live broadcast */ + put_short(s, flags); + + /* comments */ + find_tag("title", title, sizeof(title), comment_string); + find_tag("author", author, sizeof(author), comment_string); + find_tag("copyright", copyright, sizeof(copyright), comment_string); + find_tag("comment", comment, sizeof(comment), comment_string); + + put_tag(s,"CONT"); + size = strlen(title) + strlen(author) + strlen(copyright) + strlen(comment) + + 4 * 2 + 10; + put_long(s,size); + put_short(s,0); + put_str(s, title); + put_str(s, author); + put_str(s, copyright); + put_str(s, comment); + + for(i=0;i<rm->nb_streams;i++) { + int codec_data_size; + + stream = &rm->streams[i]; + + if (stream->enc->codec->type == CODEC_TYPE_AUDIO) { + desc = "The Audio Stream"; + mimetype = "audio/x-pn-realaudio"; + codec_data_size = 73; + } else { + desc = "The Video Stream"; + mimetype = "video/x-pn-realvideo"; + codec_data_size = 34; + } + + put_tag(s,"MDPR"); + size = 10 + 9 * 4 + strlen(desc) + strlen(mimetype) + codec_data_size; + put_long(s, size); + put_short(s, 0); + + put_short(s, i); /* stream number */ + put_long(s, stream->bit_rate); /* max bit rate */ + put_long(s, stream->bit_rate); /* avg bit rate */ + put_long(s, stream->packet_max_size); /* max packet size */ + if (stream->nb_packets > 0) + packet_avg_size = stream->packet_total_size / + stream->nb_packets; + else + packet_avg_size = 0; + put_long(s, packet_avg_size); /* avg packet size */ + put_long(s, 0); /* start time */ + put_long(s, BUFFER_DURATION); /* preroll */ + /* duration */ + put_long(s, (int)(stream->total_frames * 1000 / stream->frame_rate)); + put_str8(s, desc); + put_str8(s, mimetype); + put_long(s, codec_data_size); + + if (stream->enc->codec->type == CODEC_TYPE_AUDIO) { + int coded_frame_size, fscode, sample_rate; + sample_rate = stream->enc->rate; + coded_frame_size = (stream->enc->bit_rate * + stream->enc->frame_size) / (8 * sample_rate); + /* audio codec info */ + put_tag(s, ".ra"); + put_byte(s, 0xfd); + put_long(s, 0x00040000); /* version */ + put_tag(s, ".ra4"); + put_long(s, 0x01b53530); /* stream length */ + put_short(s, 4); /* unknown */ + put_long(s, 0x39); /* header size */ + + switch(sample_rate) { + case 48000: + case 24000: + case 12000: + fscode = 1; + break; + default: + case 44100: + case 22050: + case 11025: + fscode = 2; + break; + case 32000: + case 16000: + case 8000: + fscode = 3; + } + put_short(s, fscode); /* codec additional info, for AC3, seems + to be a frequency code */ + /* special hack to compensate rounding errors... */ + if (coded_frame_size == 557) + coded_frame_size--; + put_long(s, coded_frame_size); /* frame length */ + put_long(s, 0x51540); /* unknown */ + put_long(s, 0x249f0); /* unknown */ + put_long(s, 0x249f0); /* unknown */ + put_short(s, 0x01); + /* frame length : seems to be very important */ + put_short(s, coded_frame_size); + put_long(s, 0); /* unknown */ + put_short(s, stream->enc->rate); /* sample rate */ + put_long(s, 0x10); /* unknown */ + put_short(s, stream->enc->channels); + put_str8(s, "Int0"); /* codec name */ + put_str8(s, "dnet"); /* codec name */ + put_short(s, 0); /* title length */ + put_short(s, 0); /* author length */ + put_short(s, 0); /* copyright length */ + put_byte(s, 0); /* end of header */ + } else { + /* video codec info */ + put_long(s,34); /* size */ + put_tag(s,"VIDORV10"); + put_short(s, stream->enc->width); + put_short(s, stream->enc->height); + put_short(s, 24); /* frames per seconds ? */ + put_long(s,0); /* unknown meaning */ + put_short(s, 12); /* unknown meaning */ + put_long(s,0); /* unknown meaning */ + put_short(s, 8); /* unknown meaning */ + /* Seems to be the codec version: only use basic H263. The next + versions seems to add a diffential DC coding as in + MPEG... nothing new under the sun */ + put_long(s,0x10000000); + //put_long(s,0x10003000); + } + } + + /* patch data offset field */ + data_pos = s->buf_ptr - start_ptr; + rm->data_pos = data_pos; + data_offset_ptr[0] = data_pos >> 24; + data_offset_ptr[1] = data_pos >> 16; + data_offset_ptr[2] = data_pos >> 8; + data_offset_ptr[3] = data_pos; + + /* data stream */ + put_tag(s,"DATA"); + put_long(s,data_size + 10 + 8); + put_short(s,0); + + put_long(s, nb_packets); /* number of packets */ + put_long(s,0); /* next data header */ +} + +static void write_packet_header(AVFormatContext *ctx, StreamInfo *stream, + int length, int key_frame) +{ + int timestamp; + PutByteContext *s = &ctx->pb; + + stream->nb_packets++; + stream->packet_total_size += length; + if (length > stream->packet_max_size) + stream->packet_max_size = length; + + put_short(s,0); /* version */ + put_short(s,length + 12); + put_short(s, stream->num); /* stream number */ + timestamp = (1000 * (float)stream->nb_frames) / stream->frame_rate; + put_long(s, timestamp); /* timestamp */ + put_byte(s, 0); /* reserved */ + put_byte(s, key_frame ? 2 : 0); /* flags */ +} + +static int rm_write_header(AVFormatContext *s) +{ + StreamInfo *stream; + RMContext *rm; + int n; + + rm = malloc(sizeof(RMContext)); + if (!rm) + return -1; + memset(rm, 0, sizeof(RMContext)); + s->priv_data = rm; + n = 0; + if (s->audio_enc) { + stream = &rm->streams[n]; + memset(stream, 0, sizeof(StreamInfo)); + stream->num = n; + rm->audio_stream = stream; + stream->bit_rate = s->audio_enc->bit_rate; + stream->frame_rate = (float)s->audio_enc->rate / (float)s->audio_enc->frame_size; + stream->enc = s->audio_enc; + /* XXX: dummy values */ + stream->packet_max_size = 1024; + stream->nb_packets = 1000; + stream->total_frames = stream->nb_packets; + n++; + } + if (s->video_enc) { + stream = &rm->streams[n]; + memset(stream, 0, sizeof(StreamInfo)); + stream->num = n; + rm->video_stream = stream; + stream->bit_rate = s->video_enc->bit_rate; + stream->frame_rate = s->video_enc->rate; + stream->enc = s->video_enc; + /* XXX: dummy values */ + stream->packet_max_size = 4096; + stream->nb_packets = 1000; + stream->total_frames = stream->nb_packets; + n++; + } + rm->nb_streams = n; + + rv10_write_header(s, 0, 0); + put_flush_packet(&s->pb); + return 0; +} + +static int rm_write_audio(AVFormatContext *s, UINT8 *buf, int size) +{ + UINT8 buf1[size]; + RMContext *rm = s->priv_data; + PutByteContext *pb = &s->pb; + StreamInfo *stream = rm->audio_stream; + int i; + + write_packet_header(s, stream, size, stream->enc->key_frame); + + /* for AC3, the words seems to be reversed */ + for(i=0;i<size;i+=2) { + buf1[i] = buf[i+1]; + buf1[i+1] = buf[i]; + } + put_buffer(pb, buf1, size); + put_flush_packet(pb); + stream->nb_frames++; + return 0; +} + +static int rm_write_video(AVFormatContext *s, UINT8 *buf, int size) +{ + RMContext *rm = s->priv_data; + PutByteContext *pb = &s->pb; + StreamInfo *stream = rm->video_stream; + int key_frame = stream->enc->key_frame; + + /* XXX: this is incorrect: should be a parameter */ + + /* Well, I spent some time finding the meaning of these bits. I am + not sure I understood everything, but it works !! */ +#if 1 + write_packet_header(s, stream, size + 7, key_frame); + /* bit 7: '1' if final packet of a frame converted in several packets */ + put_byte(pb, 0x81); + /* bit 7: '1' if I frame. bits 6..0 : sequence number in current + frame starting from 1 */ + if (key_frame) { + put_byte(pb, 0x81); + } else { + put_byte(pb, 0x01); + } + put_short(pb, 0x4000 | (size)); /* total frame size */ + put_short(pb, 0x4000 | (size)); /* offset from the start or the end */ +#else + /* seems to be used for prefetch/error correction. Help me ! */ + write_packet_header(s, size + 6); + put_byte(pb, 0xc0); + put_short(pb, 0x4000 | size); /* total frame size */ + put_short(pb, 0x4000 + packet_number * 126); +#endif + put_byte(pb, stream->nb_frames & 0xff); + + put_buffer(pb, buf, size); + put_flush_packet(pb); + + stream->nb_frames++; + return 0; +} + +static int rm_write_trailer(AVFormatContext *s) +{ + RMContext *rm = s->priv_data; + int data_size, index_pos, i; + PutByteContext *pb = &s->pb; + + if (!s->is_streamed) { + /* end of file: finish to write header */ + index_pos = put_seek(pb, 0, SEEK_CUR); + data_size = index_pos - rm->data_pos; + + /* index */ + put_tag(pb, "INDX"); + put_long(pb, 10 + 10 * rm->nb_streams); + put_short(pb, 0); + + for(i=0;i<rm->nb_streams;i++) { + put_long(pb, 0); /* zero indices */ + put_short(pb, i); /* stream number */ + put_long(pb, 0); /* next index */ + } + /* undocumented end header */ + put_long(pb, 0); + put_long(pb, 0); + + put_seek(pb, 0, SEEK_SET); + for(i=0;i<rm->nb_streams;i++) + rm->streams[i].total_frames = rm->streams[i].nb_frames; + rv10_write_header(s, data_size, index_pos); + } else { + /* undocumented end header */ + put_long(pb, 0); + put_long(pb, 0); + } + put_flush_packet(pb); + + free(rm); + return 0; +} + +AVFormat rm_format = { + "rm", + "rm format", + "audio/x-pn-realaudio", + "rm,ra", + CODEC_ID_AC3, + CODEC_ID_RV10, + rm_write_header, + rm_write_audio, + rm_write_video, + rm_write_trailer, +}; + +AVFormat ra_format = { + "ra", + "ra format", + "audio/x-pn-realaudio", + "ra", + CODEC_ID_AC3, + CODEC_ID_NONE, + rm_write_header, + rm_write_audio, + NULL, + rm_write_trailer, +}; diff --git a/swfenc.c b/swfenc.c new file mode 100644 index 0000000000..7a645e8c7a --- /dev/null +++ b/swfenc.c @@ -0,0 +1,347 @@ +/* + * Flash Compatible Streaming Format + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <netinet/in.h> +#include <string.h> +#include "mpegenc.h" + +#include <assert.h> + +/* should have a generic way to indicate probable size */ +#define DUMMY_FILE_SIZE (100 * 1024 * 1024) +#define DUMMY_DURATION 600 /* in seconds */ + +#define TAG_END 0 +#define TAG_SHOWFRAME 1 +#define TAG_DEFINESHAPE 2 +#define TAG_FREECHARACTER 3 +#define TAG_PLACEOBJECT 4 +#define TAG_REMOVEOBJECT 5 +#define TAG_JPEG2 21 + +#define TAG_LONG 0x100 + +/* flags for shape definition */ +#define FLAG_MOVETO 0x01 +#define FLAG_SETFILL0 0x02 +#define FLAG_SETFILL1 0x04 + +/* character id used */ +#define BITMAP_ID 0 +#define SHAPE_ID 1 + +typedef struct { + long long duration_pos; + long long tag_pos; + int tag; +} SWFContext; + +static void put_swf_tag(AVFormatContext *s, int tag) +{ + SWFContext *swf = s->priv_data; + PutByteContext *pb = &s->pb; + + swf->tag_pos = put_pos(pb); + swf->tag = tag; + /* reserve some room for the tag */ + if (tag & TAG_LONG) { + put_le16(pb, 0); + put_le32(pb, 0); + } else { + put_le16(pb, 0); + } +} + +static void put_swf_end_tag(AVFormatContext *s) +{ + SWFContext *swf = s->priv_data; + PutByteContext *pb = &s->pb; + long long pos; + int tag_len, tag; + + pos = put_pos(pb); + tag_len = pos - swf->tag_pos - 2; + tag = swf->tag; + put_seek(pb, swf->tag_pos, SEEK_SET); + if (tag & TAG_LONG) { + tag &= ~TAG_LONG; + put_le16(pb, (tag << 6) | 0x3f); + put_le32(pb, tag_len - 4); + } else { + assert(tag_len < 0x3f); + put_le16(pb, (tag << 6) | tag_len); + } + put_seek(pb, pos, SEEK_SET); +} + +static inline void max_nbits(int *nbits_ptr, int val) +{ + int n; + + if (val == 0) + return; + val = abs(val); + n = 1; + while (val != 0) { + n++; + val >>= 1; + } + if (n > *nbits_ptr) + *nbits_ptr = n; +} + +static void put_swf_rect(PutByteContext *pb, + int xmin, int xmax, int ymin, int ymax) +{ + PutBitContext p; + UINT8 buf[256]; + int nbits, mask; + + init_put_bits(&p, buf, sizeof(buf), NULL, NULL); + + nbits = 0; + max_nbits(&nbits, xmin); + max_nbits(&nbits, xmax); + max_nbits(&nbits, ymin); + max_nbits(&nbits, ymax); + mask = (1 << nbits) - 1; + + /* rectangle info */ + put_bits(&p, 5, nbits); + put_bits(&p, nbits, xmin & mask); + put_bits(&p, nbits, xmax & mask); + put_bits(&p, nbits, ymin & mask); + put_bits(&p, nbits, ymax & mask); + + flush_put_bits(&p); + put_buffer(pb, buf, p.buf_ptr - p.buf); +} + +static void put_swf_line_edge(PutBitContext *pb, int dx, int dy) +{ + int nbits, mask; + + put_bits(pb, 1, 1); /* edge */ + put_bits(pb, 1, 1); /* line select */ + nbits = 2; + max_nbits(&nbits, dx); + max_nbits(&nbits, dy); + + mask = (1 << nbits) - 1; + put_bits(pb, 4, nbits - 2); /* 16 bits precision */ + if (dx == 0) { + put_bits(pb, 1, 0); + put_bits(pb, 1, 1); + put_bits(pb, nbits, dy & mask); + } else if (dy == 0) { + put_bits(pb, 1, 0); + put_bits(pb, 1, 0); + put_bits(pb, nbits, dx & mask); + } else { + put_bits(pb, 1, 1); + put_bits(pb, nbits, dx & mask); + put_bits(pb, nbits, dy & mask); + } +} + +#define FRAC_BITS 16 + +/* put matrix (not size optimized */ +static void put_swf_matrix(PutByteContext *pb, + int a, int b, int c, int d, int tx, int ty) +{ + PutBitContext p; + UINT8 buf[256]; + + init_put_bits(&p, buf, sizeof(buf), NULL, NULL); + + put_bits(&p, 1, 1); /* a, d present */ + put_bits(&p, 5, 20); /* nb bits */ + put_bits(&p, 20, a); + put_bits(&p, 20, d); + + put_bits(&p, 1, 1); /* b, c present */ + put_bits(&p, 5, 20); /* nb bits */ + put_bits(&p, 20, c); + put_bits(&p, 20, b); + + put_bits(&p, 5, 20); /* nb bits */ + put_bits(&p, 20, tx); + put_bits(&p, 20, ty); + + flush_put_bits(&p); + put_buffer(pb, buf, p.buf_ptr - p.buf); +} + +static int swf_write_header(AVFormatContext *s) +{ + SWFContext *swf; + PutByteContext *pb = &s->pb; + AVEncodeContext *enc = s->video_enc; + PutBitContext p; + UINT8 buf1[256]; + + swf = malloc(sizeof(SWFContext)); + if (!swf) + return -1; + s->priv_data = swf; + + put_tag(pb, "FWS"); + put_byte(pb, 3); /* version (should use 4 for mpeg audio support) */ + put_le32(pb, DUMMY_FILE_SIZE); /* dummy size + (will be patched if not streamed) */ + + put_swf_rect(pb, 0, enc->width, 0, enc->height); + put_le16(pb, enc->rate << 8); /* frame rate */ + swf->duration_pos = put_pos(pb); + put_le16(pb, DUMMY_DURATION * enc->rate); /* frame count */ + + /* define a shape with the jpeg inside */ + + put_swf_tag(s, TAG_DEFINESHAPE); + + put_le16(pb, SHAPE_ID); /* ID of shape */ + /* bounding rectangle */ + put_swf_rect(pb, 0, enc->width, 0, enc->height); + /* style info */ + put_byte(pb, 1); /* one fill style */ + put_byte(pb, 0x41); /* clipped bitmap fill */ + put_le16(pb, BITMAP_ID); /* bitmap ID */ + /* position of the bitmap */ + put_swf_matrix(pb, (int)(1.0 * (1 << FRAC_BITS)), 0, + 0, (int)(1.0 * (1 << FRAC_BITS)), 0, 0); + put_byte(pb, 0); /* no line style */ + + /* shape drawing */ + init_put_bits(&p, buf1, sizeof(buf1), NULL, NULL); + put_bits(&p, 4, 1); /* one fill bit */ + put_bits(&p, 4, 0); /* zero line bit */ + + put_bits(&p, 1, 0); /* not an edge */ + put_bits(&p, 5, FLAG_MOVETO | FLAG_SETFILL0); + put_bits(&p, 5, 1); /* nbits */ + put_bits(&p, 1, 0); /* X */ + put_bits(&p, 1, 0); /* Y */ + put_bits(&p, 1, 1); /* set fill style 1 */ + + /* draw the rectangle ! */ + put_swf_line_edge(&p, enc->width, 0); + put_swf_line_edge(&p, 0, enc->height); + put_swf_line_edge(&p, -enc->width, 0); + put_swf_line_edge(&p, 0, -enc->height); + + /* end of shape */ + put_bits(&p, 1, 0); /* not an edge */ + put_bits(&p, 5, 0); + + flush_put_bits(&p); + put_buffer(pb, buf1, p.buf_ptr - p.buf); + + put_swf_end_tag(s); + + put_flush_packet(&s->pb); + return 0; +} + +static int swf_write_video(AVFormatContext *s, UINT8 *buf, int size) +{ + PutByteContext *pb = &s->pb; + AVEncodeContext *enc = s->video_enc; + static int tag_id = 0; + + if (enc->frame_number > 1) { + /* remove the shape */ + put_swf_tag(s, TAG_REMOVEOBJECT); + put_le16(pb, SHAPE_ID); /* shape ID */ + put_le16(pb, 1); /* depth */ + put_swf_end_tag(s); + + /* free the bitmap */ + put_swf_tag(s, TAG_FREECHARACTER); + put_le16(pb, BITMAP_ID); + put_swf_end_tag(s); + } + + put_swf_tag(s, TAG_JPEG2 | TAG_LONG); + + put_le16(pb, tag_id); /* ID of the image */ + + /* a dummy jpeg header seems to be required */ + put_byte(pb, 0xff); + put_byte(pb, 0xd8); + put_byte(pb, 0xff); + put_byte(pb, 0xd9); + /* write the jpeg image */ + put_buffer(pb, buf, size); + + put_swf_end_tag(s); + + /* draw the shape */ + + put_swf_tag(s, TAG_PLACEOBJECT); + put_le16(pb, SHAPE_ID); /* shape ID */ + put_le16(pb, 1); /* depth */ + put_swf_matrix(pb, 1 << FRAC_BITS, 0, 0, 1 << FRAC_BITS, 0, 0); + put_swf_end_tag(s); + + /* output the frame */ + put_swf_tag(s, TAG_SHOWFRAME); + put_swf_end_tag(s); + + put_flush_packet(&s->pb); + return 0; +} + +static int swf_write_trailer(AVFormatContext *s) +{ + SWFContext *swf = s->priv_data; + PutByteContext *pb = &s->pb; + int file_size; + AVEncodeContext *enc = s->video_enc; + + put_swf_tag(s, TAG_END); + put_swf_end_tag(s); + + put_flush_packet(&s->pb); + + /* patch file size and number of frames if not streamed */ + if (!s->is_streamed) { + file_size = put_pos(pb); + put_seek(pb, 4, SEEK_SET); + put_le32(pb, file_size); + put_seek(pb, swf->duration_pos, SEEK_SET); + put_le16(pb, enc->frame_number); + } + free(swf); + return 0; +} + +AVFormat swf_format = { + "swf", + "Flash format", + "application/x-shockwave-flash", + "swf", + CODEC_ID_NONE, + CODEC_ID_MJPEG, + swf_write_header, + NULL, + swf_write_video, + swf_write_trailer, +}; @@ -0,0 +1,123 @@ +/* + * UDP prototype streaming system + * Copyright (c) 2000 Gerard Lantau. + * + * 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. + */ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> + +#include "mpegenc.h" + +#define UDP_TX_BUF_SIZE 32768 +/* put it in UDP context */ +static struct sockaddr_in dest_addr; + +/* return non zero if error */ +int udp_tx_open(UDPContext *s, + const char *uri, + int local_port) +{ + struct sockaddr_in my_addr; + const char *p, *q; + char hostname[1024]; + int port, udp_socket, tmp; + struct hostent *hp; + + /* fill the dest addr */ + p = uri; + if (!strstart(p, "udp:", &p)) + return -1; + q = strchr(p, ':'); + if (!q) + return -1; + memcpy(hostname, p, q - p); + hostname[q - p] = '\0'; + port = strtol(q+1, NULL, 10); + if (port <= 0) + return -1; + + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(port); + if ((inet_aton(hostname, &dest_addr.sin_addr)) == 0) { + hp = gethostbyname(hostname); + if (!hp) + return -1; + memcpy ((char *) &dest_addr.sin_addr, hp->h_addr, hp->h_length); + } + + udp_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (udp_socket < 0) + return -1; + + my_addr.sin_family = AF_INET; + my_addr.sin_port = htons(local_port); + my_addr.sin_addr.s_addr = htonl (INADDR_ANY); + + /* the bind is needed to give a port to the socket now */ + if (bind(udp_socket,(struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) + goto fail; + + /* limit the tx buf size to limit latency */ + + tmp = UDP_TX_BUF_SIZE; + if (setsockopt(udp_socket, SOL_SOCKET, SO_SNDBUF, &tmp, sizeof(tmp)) < 0) { + perror("setsockopt sndbuf"); + goto fail; + } + + s->udp_socket = udp_socket; + s->max_payload_size = 1024; + return 0; + fail: + return -1; +} + +void udp_tx_close(UDPContext *s) +{ + close(s->udp_socket); +} + +extern int data_out_size; + +void udp_write_data(void *opaque, UINT8 *buf, int size) +{ + UDPContext *s = opaque; + int ret, len; + + /* primitive way to avoid big packets */ + data_out_size += size; + while (size > 0) { + len = size; + if (len > s->max_payload_size) + len = s->max_payload_size; + + ret = sendto (s->udp_socket, buf, len, 0, + (struct sockaddr *) &dest_addr, + sizeof (dest_addr)); + if (ret < 0) + perror("sendto"); + buf += len; + size -= len; + } +} + |