pax_global_header00006660000000000000000000000064144257310270014517gustar00rootroot0000000000000052 comment=7da25c86f80c286f588273bc7d293dd3d0f4ce28 ocaml-ogg-0.7.4/000077500000000000000000000000001442573102700133745ustar00rootroot00000000000000ocaml-ogg-0.7.4/.github/000077500000000000000000000000001442573102700147345ustar00rootroot00000000000000ocaml-ogg-0.7.4/.github/workflows/000077500000000000000000000000001442573102700167715ustar00rootroot00000000000000ocaml-ogg-0.7.4/.github/workflows/main.yml000066400000000000000000000007171442573102700204450ustar00rootroot00000000000000name: CI on: [push] jobs: cancel_previous_run: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.4.0 with: access_token: ${{ github.token }} build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] steps: - name: Build and test module uses: savonet/build-and-test-ocaml-module@main ocaml-ogg-0.7.4/.gitignore000066400000000000000000000000721442573102700153630ustar00rootroot00000000000000*~ _build *.byte *.native _tests .merlin *.install .*.sw* ocaml-ogg-0.7.4/.ocamlformat000066400000000000000000000003371442573102700157040ustar00rootroot00000000000000version=0.25.1 profile = conventional break-separators = after space-around-lists = false doc-comments = before match-indent = 2 match-indent-nested = always parens-ite exp-grouping = preserve module-item-spacing = compact ocaml-ogg-0.7.4/CHANGES.md000066400000000000000000000053121442573102700147670ustar00rootroot000000000000000.7.4 (2023-05-07) ===== * Add `Stream.terminate` 0.7.3 (14-08-2022) ===== * Fix build error with OCaml `4.14` 0.7.2 (28-06-2022) ===== * Use `caml_alloc_custom_mem` for packet allocations (#2348) 0.7.1 (07-03-2022) ===== * Added decoder API for audio big array. 0.7.0 (06-03-2021) ===== * Switch to `dune` 0.6.1 (19-10-2020) ===== * Revert back to autoconf (See: savonet/liquidsoap#1378) 0.6.0 (07-10-2020) ===== * Switch to read callbacks with bytes in Ogg.Sync. 0.5.2 (07-10-2017) ===== * Fix compilation with OCaml 4.06 0.5.1 (11-04-2017) ===== * Install .cmx files 0.5.0 (03-08-2015) ===== * Removed old Ogg.Stream backward compatibility functions. * Switch to Bytes API. 0.4.5 (08-05-2013) ===== * Added optional fill parameter to [get_page] to try to limit ogg logical pages size. 0.4.4 (18-02-2013) ===== * Added Ogg.Internal_error exception. * Updated configure. 0.4.3 (04-10-2011) ===== * New Ogg_demuxer.End_of_stream exception, raised at the end of logical streams. The former incorrect behavior was to raise Ogg.End_of_stream, which is intended to signify the end of data. 0.4.2 (02.07.2011) ===== * Added [Ogg_demuxer] module to decode ogg streams. * Added the following functions: - [Sync.sync] - [Stream.{peek_granulepos, skip_packet, packet_granulepos}] * Fixed incorrect mention of INRIA in license headers. 0.4.1 (04-09-2010) ===== * Raise Out_of_sync in Sync.read when data is not synced, e.g. ogg_sync_pageout returned -1. 0.4.0 (19-08-2010) ===== * Removed sync reference in Stream.get_packet and actually raise Out_of_sync exception. No packet should be returned when the stream is out of sync. 0.3.1 (12-10-2009) ===== * Added support for --enable-debugging configure option * Added NO_CUSTOM to build in standard mode. * Added prefix to main compilation variables if passed to configure. * Makefile now honnors LIBDIRS variable for linking against libraries located in other places than then standard ones. 0.3.0 (17-02-2009) ===== * Added file descriptor to Ogg.Sync.create_from_file in order to be able to close it and let the Gc clean everything.. * Added Ogg.Stream.eos * Added Ogg.Stream.peek_packet to peek a packet without advancing the stream. Usefull to test first packet when parsing ogg streams. 0.2.0 (16-04-2008 ===== * More portable invokation of make * Now installs .cmx file * Reworked API, added more functions. + Binding is now ready to decode theora and multiplexed streams + Compatibility functions are available, old code should compile on the new API 0.1.1 (05-11-2007) ===== * Cleared out license headers. Now using LGPL + linking exception Acked-by: smimram 0.1.0 (16-10-2007) ===== * Initial release ocaml-ogg-0.7.4/COPYING000066400000000000000000000655731442573102700144470ustar00rootroot00000000000000The software distributed in this tarball is licenced under the GNU Lesser General Public License, with a special exception: As a special exception to the GNU Library General Public License, you may link, statically or dynamically, a "work that uses the Library" with a publicly distributed version of the Library to produce an executable file containing portions of the Library, and distribute that executable file under terms of your choice, without any of the additional requirements listed in clause 6 of the GNU Library General Public License. By "a publicly distributed version of the Library", we mean either the unmodified Library as distributed by The Savonet Team, or a modified version of the Library that is distributed under the conditions defined in clause 3 of the GNU Library General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU Library General Public License. The GNU Lesser General Public Licence text is: <---------------------------------------------------------------------- GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! ocaml-ogg-0.7.4/README.md000066400000000000000000000010551442573102700146540ustar00rootroot00000000000000ocaml-ogg ========= This package contains an OCaml interface for the `ogg` library Please read the COPYING file before using this software. Prerequisites: ============== - ocaml - libogg - findlib - dune >= 2.0 Compilation: ============ ``` $ dune build ``` This should build both the native and the byte-code version of the extension library. Installation: ============= Via `opam`: ``` $ opam install ogg ``` Via `dune` (for developers): ``` $ dune install ``` This should install the library file (using ocamlfind) in the appropriate place. ocaml-ogg-0.7.4/dune-project000066400000000000000000000007011442573102700157140ustar00rootroot00000000000000(lang dune 2.8) (version 0.7.4) (name ogg) (source (github savonet/ocaml-ogg)) (license GPL-2.0) (authors "The Savonet Team ") (maintainers "The Savonet Team ") (generate_opam_files true) (use_standard_c_and_cxx_flags false) (package (name ogg) (synopsis "Bindings to libogg") (depends conf-libogg conf-pkg-config (ocaml (>= 4.08.0)) dune dune-configurator) ) ocaml-ogg-0.7.4/ogg.opam000066400000000000000000000013611442573102700150270ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "0.7.4" synopsis: "Bindings to libogg" maintainer: ["The Savonet Team "] authors: ["The Savonet Team "] license: "GPL-2.0" homepage: "https://github.com/savonet/ocaml-ogg" bug-reports: "https://github.com/savonet/ocaml-ogg/issues" depends: [ "conf-libogg" "conf-pkg-config" "ocaml" {>= "4.08.0"} "dune" {>= "2.8"} "dune-configurator" "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/savonet/ocaml-ogg.git" ocaml-ogg-0.7.4/src/000077500000000000000000000000001442573102700141635ustar00rootroot00000000000000ocaml-ogg-0.7.4/src/config/000077500000000000000000000000001442573102700154305ustar00rootroot00000000000000ocaml-ogg-0.7.4/src/config/discover.ml000066400000000000000000000011271442573102700176010ustar00rootroot00000000000000module C = Configurator.V1 let () = C.main ~name:"ogg-pkg-config" (fun c -> let default : C.Pkg_config.package_conf = { libs = ["-logg"]; cflags = [] } in let conf = match C.Pkg_config.get c with | None -> default | Some pc -> ( match C.Pkg_config.query_expr_err pc ~package:"ogg" ~expr:"ogg" with | Error msg -> failwith msg | Ok deps -> deps) in C.Flags.write_sexp "c_flags.sexp" conf.cflags; C.Flags.write_sexp "c_library_flags.sexp" conf.libs) ocaml-ogg-0.7.4/src/config/dune000066400000000000000000000000751442573102700163100ustar00rootroot00000000000000(executable (name discover) (libraries dune.configurator)) ocaml-ogg-0.7.4/src/dune000066400000000000000000000010341442573102700150370ustar00rootroot00000000000000(library (name ogg) (public_name ogg) (synopsis "OCaml bindings for libogg") (libraries threads) (modules ogg) (install_c_headers ocaml-ogg) (foreign_stubs (language c) (names ogg_stubs) (flags (:include c_flags.sexp))) (c_library_flags (:include c_library_flags.sexp))) (library (name ogg_decoder) (public_name ogg.decoder) (synopsis "Ogg decoding library with pluggable decoders") (libraries ogg) (modules ogg_decoder)) (rule (targets c_flags.sexp c_library_flags.sexp) (action (run ./config/discover.exe))) ocaml-ogg-0.7.4/src/ocaml-ogg.h000066400000000000000000000037511442573102700162070ustar00rootroot00000000000000/* * Copyright 2007 Samuel Mimram * * This file is part of ocaml-ogg. * * ocaml-ogg is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ocaml-ogg 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 Lesser General Public License * along with ocaml-ogg; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception to the GNU Library General Public License, you may * link, statically or dynamically, a "work that uses the Library" with a * publicly distributed version of the Library to produce an executable file * containing portions of the Library, and distribute that executable file under * terms of your choice, without any of the additional requirements listed in * clause 6 of the GNU Library General Public License. By "a publicly * distributed version of the Library", we mean either the unmodified Library as * distributed by The Savonet Team, or a modified version of the Library that is * distributed under the conditions defined in clause 3 of the GNU Library * General Public License. This exception does not however invalidate any other * reasons why the executable file might be covered by the GNU Library General * Public License. * */ #include #define Sync_state_val(v) (*((ogg_sync_state **)Data_custom_val(v))) #define Stream_state_val(v) (*((ogg_stream_state **)Data_custom_val(v))) #define Packet_val(v) (*((ogg_packet **)Data_custom_val(v))) value value_of_page(ogg_page *op); value value_of_packet(ogg_packet *op); ogg_page *page_of_value(value v, ogg_page *op); ocaml-ogg-0.7.4/src/ogg.ml000066400000000000000000000132141442573102700152720ustar00rootroot00000000000000(* * Copyright 2007-2011 Savonet team * * This file is part of ocaml-ogg. * * ocaml-ogg is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ocaml-ogg 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 Lesser General Public License * along with ocaml-ogg; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception to the GNU Library General Public License, you may * link, statically or dynamically, a "work that uses the Library" with a publicly * distributed version of the Library to produce an executable file containing * portions of the Library, and distribute that executable file under terms of * your choice, without any of the additional requirements listed in clause 6 * of the GNU Library General Public License. * By "a publicly distributed version of the Library", we mean either the unmodified * Library as distributed by The Savonet Team, or a modified version of the Library that is * distributed under the conditions defined in clause 3 of the GNU Library General * Public License. This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU Library General Public License. * *) (* * Functions for manipulating ogg streams files using libogg. * * @author Samuel Mimram *) exception Not_enough_data exception Bad_data exception Out_of_sync exception End_of_stream exception Internal_error let () = Callback.register_exception "ogg_exn_not_enough_data" Not_enough_data; Callback.register_exception "ogg_exn_bad_data" Bad_data; Callback.register_exception "ogg_exn_out_of_sync" Out_of_sync; Callback.register_exception "ogg_exn_eos" End_of_stream; Callback.register_exception "ogg_exn_internal_error" Internal_error module Page = struct type t = string * string external serialno : t -> nativeint = "ocaml_ogg_page_serialno" external eos : t -> bool = "ocaml_ogg_page_eos" external bos : t -> bool = "ocaml_ogg_page_bos" external packets : t -> int = "ocaml_ogg_page_packets" external continued : t -> bool = "ocaml_ogg_page_continued" external version : t -> int = "ocaml_ogg_page_version" external granulepos : t -> Int64.t = "ocaml_ogg_page_granulepos" external pageno : t -> nativeint = "ocaml_ogg_page_pageno" external set_checksum : t -> unit = "ocaml_ogg_page_checksum_set" end module Stream = struct type stream type packet external packet_granulepos : packet -> Int64.t = "ocaml_ogg_stream_packet_granulepos" external create : nativeint -> stream = "ocaml_ogg_stream_init" let create ?(serial = Random.nativeint (Nativeint.of_int 0x3FFFFFFF)) () = create serial external serialno : stream -> nativeint = "ocaml_ogg_stream_serialno" external eos : stream -> bool = "ocaml_ogg_stream_eos" external terminate : stream -> Page.t = "ocaml_ogg_stream_terminate" external get_page : stream -> unit -> Page.t = "ocaml_ogg_stream_pageout" external get_page_fill : stream -> int -> Page.t = "ocaml_ogg_stream_pageout" let get_page ?fill os = match fill with | Some bytes -> get_page_fill os bytes | None -> get_page os () external get_packet : stream -> packet = "ocaml_ogg_stream_packetout" external peek_packet : stream -> packet = "ocaml_ogg_stream_packetpeek" external peek_granulepos : stream -> Int64.t = "ocaml_ogg_stream_granulepospeek" external skip_packet : stream -> unit = "ocaml_ogg_stream_packet_advance" external put_packet : stream -> packet -> unit = "ocaml_ogg_stream_packetin" external put_page : stream -> Page.t -> unit = "ocaml_ogg_stream_pagein" external flush_page : stream -> Page.t = "ocaml_ogg_flush_stream" let terminate os = let rec f pages = try f (flush_page os :: pages) with Not_enough_data -> pages in let pages = f [] in List.rev (terminate os :: pages) end module Sync = struct (** Internal type for sync state *) type sync type read = bytes -> int -> int -> int (** External type for sync state. References the C sync structure, and the read function *) type t = (read * sync) ref external create : unit -> sync = "ocaml_ogg_sync_init" let create f = ref (f, create ()) let create_from_file f = let fd = Unix.openfile f [Unix.O_RDONLY] 0o400 in (create (Unix.read fd), fd) external read : read -> sync -> Page.t = "ocaml_ogg_sync_read" let read s = let f, s = !s in read f s external reset : sync -> unit = "ocaml_ogg_sync_reset" let reset ?read_func x = let f, s = !x in reset s; match read_func with None -> x := (f, s) | Some v -> x := (v, s) external seek : read -> sync -> Page.t = "ocaml_ogg_sync_pageseek" let seek x = let f, s = !x in seek f s end module Skeleton = struct external fishead : Int64.t -> Int64.t -> Int64.t -> Int64.t -> Int32.t -> Stream.packet = "ocaml_ogg_skeleton_fishead" let fishead ?(presentation_numerator = Int64.zero) ?(presentation_denominator = Int64.of_int 1000) ?(basetime_numerator = Int64.zero) ?(basetime_denominator = Int64.of_int 1000) ?(utc = Int32.zero) () = fishead presentation_numerator presentation_denominator basetime_numerator basetime_denominator utc external eos : unit -> Stream.packet = "ocaml_ogg_skeleton_eos" end ocaml-ogg-0.7.4/src/ogg.mli000066400000000000000000000263561442573102700154560ustar00rootroot00000000000000(* * Copyright 2007-2011 Savonet team * * This file is part of ocaml-ogg. * * ocaml-ogg is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ocaml-ogg 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 Lesser General Public License * along with ocaml-ogg; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception to the GNU Library General Public License, you may * link, statically or dynamically, a "work that uses the Library" with a publicly * distributed version of the Library to produce an executable file containing * portions of the Library, and distribute that executable file under terms of * your choice, without any of the additional requirements listed in clause 6 * of the GNU Library General Public License. * By "a publicly distributed version of the Library", we mean either the unmodified * Library as distributed by The Savonet Team, or a modified version of the Library that is * distributed under the conditions defined in clause 3 of the GNU Library General * Public License. This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU Library General Public License. * *) (** * Functions for manipulating ogg streams files using libogg. * * @author Samuel Mimram, Romain Beauxis *) exception Not_enough_data exception Bad_data exception Out_of_sync exception End_of_stream exception Internal_error (** * The [page] struct encapsulates the data for an Ogg page. * * Ogg pages are the fundamental unit of framing and interleave in an ogg * bitstream. They are made up of packet segments of 255 bytes each. There can * be as many as 255 packet segments per page, for a maximum page size of a * little under 64 kB. This is not a practical limitation as the segments can be * joined across page boundaries allowing packets of arbitrary size. In practice * pages are usually around 4 kB. *) module Page : sig (** A page is a header and a body *) type t = string * string (** * Returns the unique serial number for the logical bitstream of this page. * Each page contains the serial number for the logical bitstream that it belongs to.*) val serialno : t -> nativeint (** * Indicates whether this page is at the end of the logical bitstream. *) val eos : t -> bool (** * Indicates whether this page is at the begining of the logical bitstream. *) val bos : t -> bool (** * Indicates whether this page contains packet data which has been * continued from the previous page. *) val continued : t -> bool (** * Returns the number of packets that are completed on this page. * If the leading packet is begun on a previous page, but ends on this page, it's counted. * * If a page consists of a packet begun on a previous page, and a new packet begun * (but not completed) on this page, the return will be: * * [packets page] will return [1], * [continued paged] will return [true] * * If a page happens to be a single packet that was begun on a previous page, * and spans to the next page (in the case of a three or more page packet), the return will be: * * [packets page] will return 0, * [continued page] will return [true].*) val packets : t -> int (** * This function returns the version of ogg_page used in this page. * In current versions of libogg, all ogg_page structs have the same version, * so [0] should always be returned. *) val version : t -> int (** * Returns the exact granular position of the packet data contained at the end of this page. * * This is useful for tracking location when seeking or decoding. * * For example, in audio codecs this position is the pcm sample number and * in video this is the frame number.*) val granulepos : t -> Int64.t (** * Returns the sequential page number. * * This is useful for ordering pages or determining when pages have been lost. *) val pageno : t -> nativeint (** * Checksums an ogg_page. *) val set_checksum : t -> unit end module Sync : sig type t (** Type for read functions. *) type read = bytes -> int -> int -> int (** * This function is used to initialize a [Sync.t] to a known initial value * in preparation for manipulation of an Ogg bitstream. * * The function passed is used to fill the stream with new data. *) val create : read -> t (** * Wrapper around [create] to open a file as the ogg stream. *) val create_from_file : string -> t * Unix.file_descr (** * Read a page from [Sync.t] * * Raises [End_of_stream] if the reading function returned an empty string. * Raises [Out_of_sync] if data is not synced and some byte where skiped. *) val read : t -> Page.t (** * This function is used to reset the internal counters of the * [Sync.t] to initial values. * * [read_func] is optional and is a new function to read new data. *) val reset : ?read_func:read -> t -> unit (** * This function synchronizes the ogg_sync_state struct to the next ogg_page. * * This is useful when seeking within a bitstream. page_seek will synchronize * to the next page in the bitstream and return information about how many bytes * we advanced or skipped in order to do so. *) val seek : t -> Page.t end module Stream : sig (** * The [stream] values track the current encode/decode state of the * current logical bitstream. *) type stream (** * A data packet to pass to the decoder *) type packet (** * Create a [stream]. *) val create : ?serial:nativeint -> unit -> stream (** * Get a stream's serial number. *) val serialno : stream -> nativeint (** Returns true if the end of stream has been reached. *) val eos : stream -> bool (** Terminate the stream and return its final pages. *) val terminate : stream -> Page.t list (** * This function forms packets into pages. Internally, * it assembles the accumulated packet bodies into an Ogg page * suitable for writing to a stream. * * If no [fill] argument is passed, this function will only return * a page when a "reasonable" amount of packet data is available. * Normally this is appropriate since it limits the overhead of the * Ogg page headers in the bitstream. * * If a [fill] argument is passed, this function will return a page when at * least four packets have been accumulated and accumulated packet data meets * or exceeds the specified number of bytes, and/or when the accumulated * packet data meets/exceeds the maximum page size regardless of accumulated * packet count. * * The exception [Not_enough_data] is raised if not enough data is available * to generate the page. * * Call [flush_page] if immediate page generation is desired. This * may be occasionally necessary, for example, to limit the temporal * latency of a variable bitrate stream. *) val get_page : ?fill:int -> stream -> Page.t (** * This function adds a complete page to the bitstream. * * In a typical decoding situation, this function would be called after * using [Sync.read] to create a valid [Page.t] * * Raises [Bad_data] if the serial number of the page did not match the * serial number of the bitstream, or the page version was incorrect. *) val put_page : stream -> Page.t -> unit (** * This function assembles a data packet for output * to the codec decoding engine. * * Each successive call returns the next complete packet built from those segments. * In a typical decoding situation, this should be used after calling * [put_page] to submit a page of data to the bitstream. * * This function should *not* be used. Because of ocaml's paradigm, it is necessary * to copy each packet since they are only valid until this function is called again. * When dealing with many packets, this will lead to multiple unecessary memory allocation * and desallocation. * * Raises [Not_enough_data] if more data is needed and another page should be submitted. * * Raises [Out_of_sync] if we are out of sync and there is a gap in the data. *) val get_packet : stream -> packet (** * This function assembles a data packet for output * to the codec decoding engine without advancing the stream. * * Raises [Not_enough_data] if more data is needed and another page should be submitted. * * Raises [Out_of_sync] if we are out of sync and there is a gap in the data *) val peek_packet : stream -> packet (** This function picks up the granule position * of the next packet in the stream without advancing it. * * Raises [Not_enough_data] if more data is needed and another page should be submitted. * * Raises [Out_of_sync] if we are out of sync and there is a gap in the data *) val peek_granulepos : stream -> Int64.t (** This function discards the next packet in the stream. * * Raises [Not_enough_data] if more data is needed and another page should be submitted. * * Raises [Out_of_sync] if we are out of sync and there is a gap in the data *) val skip_packet : stream -> unit (** * This function submits a packet to the bitstream for page encapsulation. * After this is called, more packets can be submitted, or pages can be written out. * * This function is provided to ease ogg strea multiplexing, where packet submission * order is important. It should not be used to encoder further data. *) val put_packet : stream -> packet -> unit (** * This function checks for remaining packets inside the stream and forces * remaining packets into a page, regardless of the size of the page. * * This should only be used when you want to flush an undersized page from the * middle of the stream. Otherwise, [get_page] should always be used. * * This function can be used to verify that all packets have been flushed. * * Raises [Not_enough_data] if all packet data has already been flushed into pages, * and there are no packets to put into the page. *) val flush_page : stream -> Page.t (** Returns a packet's granule position. *) val packet_granulepos : packet -> Int64.t end module Skeleton : sig (** * Create an initial ogg skeleton packet ('fishead'), * to complete with data packet from the various codecs * in the stream ('fishbone'). * See: http://xiph.org/ogg/doc/skeleton.html. *) val fishead : ?presentation_numerator:Int64.t -> ?presentation_denominator:Int64.t -> ?basetime_numerator:Int64.t -> ?basetime_denominator:Int64.t -> ?utc:Int32.t -> unit -> Stream.packet (** Create an end-of-stream packet for * an ogg skeleton logical stream *) val eos : unit -> Stream.packet end ocaml-ogg-0.7.4/src/ogg_decoder.ml000066400000000000000000000552541442573102700167710ustar00rootroot00000000000000(* * Copyright 2007-2011 Savonet team * * This file is part of ocaml-ogg. * * ocaml-ogg is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ocaml-ogg 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 Lesser General Public License * along with ocaml-ogg; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception to the GNU Library General Public License, you may * link, statically or dynamically, a "work that uses the Library" with * a publicly distributed version of the Library to produce an executable file * containing portions of the Library, and distribute that executable file * under terms of your choice, without any of the additional requirements * listed in clause 6 of the GNU Library General Public License. * By "a publicly distributed version of the Library", we mean either * the unmodified Library as distributed by INRIA, or a modified version of * the Library that is distributed under the conditions defined in clause 3 * of the GNU Library General Public License. This exception does not however * invalidate any other reasons why the executable file might be covered by * the GNU Library General Public License. * *) (** Ogg stream demuxer *) type metadata = string * (string * string) list type ('a, 'b) decoder = { name : string; info : unit -> 'a * metadata; decode : ('b -> unit) -> unit; restart : Ogg.Stream.stream -> unit; samples_of_granulepos : Int64.t -> Int64.t; } type audio_info = { channels : int; sample_rate : int } type audio_data = float array array type audio_ba_data = (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array type video_plane = (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t (** Only supported for now: plannar YUV formats. *) type video_format = | Yuvj_420 (* Planar YCbCr 4:2:0. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) | Yuvj_422 (* Planar YCbCr 4:2:2. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) | Yuvj_444 (* Planar YCbCr 4:4:4. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) type video_info = { fps_numerator : int; fps_denominator : int; width : int; (** Width of the Y' luminance plane *) height : int; (** Height of the luminance plane *) } type video_data = { format : video_format; frame_width : int; frame_height : int; y_stride : int; (** Length, in bytes, per line *) uv_stride : int; (** Length, in bytes, per line *) y : video_plane; (** luminance data *) u : video_plane; (** Cb data *) v : video_plane; (** Cr data *) } type decoders = | Video of (video_info, video_data) decoder | Audio of (audio_info, audio_data) decoder | Audio_ba of (audio_info, audio_ba_data) decoder | Audio_both of (audio_info, audio_data) decoder * (audio_info, audio_ba_data) decoder | Unknown type callbacks = { read : bytes -> int -> int -> int; seek : (int -> int) option; tell : (unit -> int) option; } type index_element = { index_bytes : int; samples : Int64.t; total_samples : Int64.t; } type stream = { mutable os : Ogg.Stream.stream; mutable position : float; index : (Int64.t, index_element) Hashtbl.t; mutable read_samples : Int64.t; dec : decoders; } type t = { sync : Ogg.Sync.t; callbacks : callbacks; mutable started : bool; mutable last_p : int option; log : string -> unit; streams : (nativeint, stream) Hashtbl.t; finished_streams : (nativeint, stream) Hashtbl.t; } type track = | Audio_track of (string * nativeint) | Video_track of (string * nativeint) exception Internal of (Ogg.Page.t * int option) exception Exit of nativeint * Ogg.Stream.stream * decoders exception Track of (bool * nativeint * stream) exception Invalid_stream exception Not_available (* This exception has a different semantics than [Ogg.End_of_stream]. * [Ogg.End_of_stream] is raised when end of data has been reached, * while this exception is raised when end of a logical stream has * been reached.. *) exception End_of_stream type register_decoder = (Ogg.Stream.packet -> bool) * (Ogg.Stream.stream -> decoders) let get_some x = match x with Some x -> x | None -> assert false let ogg_decoders = Hashtbl.create 1 let log dec = Printf.ksprintf dec.log (* End of stream is declared only when * all logical stream have ended (dec.streams = 0) * _and_ all their data has been consumed (dec.finished_streams = 0) *) let eos dec = dec.started && Hashtbl.length dec.streams = 0 && Hashtbl.length dec.finished_streams = 0 let test dec page = let serial = Ogg.Page.serialno page in log dec "Found a ogg logical stream, serial: %nx" serial; let os = Ogg.Stream.create ~serial () in Ogg.Stream.put_page os page; (* Get first packet *) let packet = Ogg.Stream.peek_packet os in try Hashtbl.iter (fun format (check, decode) -> log dec "Trying ogg/%s format" format; if check packet then ( log dec "ogg/%s format detected for stream %nx" format serial; raise (Exit (serial, os, decode os))) else ()) ogg_decoders; log dec "Couldn't find a decoder for ogg logical stream with serial %nx" serial; raise (Exit (serial, os, Unknown)) with Exit (s, o, d) -> (s, o, d) let granuleconv dec granulepos cur = try let ret = match dec with | Audio_ba d -> d.samples_of_granulepos granulepos | Audio_both (d, _) -> d.samples_of_granulepos granulepos | Audio d -> d.samples_of_granulepos granulepos | Video d -> d.samples_of_granulepos granulepos | Unknown -> assert false in if ret > Int64.zero then ret else cur with _ -> cur let feed_page ~position decoder page = let serial = Ogg.Page.serialno page in try let stream = Hashtbl.find decoder.streams serial in if stream.dec <> Unknown then begin Ogg.Stream.put_page stream.os page; let granulepos = Ogg.Page.granulepos page in let total_samples = granuleconv stream.dec granulepos stream.read_samples in if total_samples > stream.read_samples then begin begin match position with | Some p -> if not (Hashtbl.mem stream.index granulepos) then Hashtbl.add stream.index granulepos { index_bytes = p; samples = Int64.sub total_samples stream.read_samples; total_samples = stream.read_samples; } | None -> () end; stream.read_samples <- total_samples end end; if Ogg.Page.eos page then begin log decoder "Reached last page of logical stream %nx" serial; Hashtbl.remove decoder.streams serial; if stream.dec <> Unknown then (* Moving finished stream to decoder.finished_streams *) Hashtbl.add decoder.finished_streams serial stream end with Not_found -> log decoder "Couldn't find a decoder for page in stream %nx" serial; raise Invalid_stream let get_page decoder = if eos decoder then raise End_of_stream; let position = match decoder.callbacks.tell with None -> None | Some f -> Some (f ()) in let page = Ogg.Sync.read decoder.sync in match decoder.callbacks.tell with | Some f -> if Some (f ()) = position then (decoder.last_p, page) else begin let pos = decoder.last_p in decoder.last_p <- position; (pos, page) end | _ -> (None, page) let feed decoder = let position, page = get_page decoder in feed_page ~position decoder page (** This should be called only * when we are near the end of * a stream... *) let abort dec = dec.started <- true; begin try while Hashtbl.length dec.streams > 0 do feed dec done with _ -> Hashtbl.clear dec.streams end; Hashtbl.clear dec.finished_streams let parse dec = assert (not (eos dec)); let rec parse () = try (* Get First page *) let position, page = get_page dec in (* Check wether this is a b_o_s *) if not (Ogg.Page.bos page) then raise (Internal (page, position)); let serial, os, decoder = test dec page in (* Should not happen *) if Hashtbl.mem dec.streams serial then raise Invalid_stream; let stream = { os; position = 0.; read_samples = Int64.zero; index = Hashtbl.create 10; dec = decoder; } in Hashtbl.add dec.streams serial stream; parse () with Internal (p, position) -> feed_page ~position dec p in parse (); dec.started <- true; dec let init ?(log = fun _ -> ()) c = let sync = Ogg.Sync.create c.read in let streams = Hashtbl.create 2 in let finished_streams = Hashtbl.create 2 in let pos = match c.tell with None -> None | Some f -> Some (f ()) in parse { sync; started = false; log; streams; callbacks = c; last_p = pos; finished_streams; } let unix_callbacks fd = { read = Unix.read fd; tell = Some (fun () -> Unix.lseek fd 0 Unix.SEEK_CUR); seek = Some (fun len -> Unix.lseek fd len Unix.SEEK_SET); } let init_from_fd ?log fd = init ?log (unix_callbacks fd) let init_from_file ?log filename = let fd = Unix.openfile filename [Unix.O_RDONLY] 0o640 in (init_from_fd ?log fd, fd) let get_ogg_sync dec = dec.sync let reset dec = if Hashtbl.length dec.streams > 0 || Hashtbl.length dec.finished_streams > 0 then log dec "Reseting a stream that has not ended!"; Hashtbl.clear dec.streams; Hashtbl.clear dec.finished_streams; dec.started <- false; ignore (parse dec) let fold_tracks dec f x = let x = Hashtbl.fold f dec.streams x in Hashtbl.fold f dec.finished_streams x let get_track dec dtype = let test ended id stream = match (stream.dec, dtype) with | Audio_ba _, Audio_track (_, x) when x = id -> raise (Track (ended, id, stream)) | Audio_both _, Audio_track (_, x) when x = id -> raise (Track (ended, id, stream)) | Audio _, Audio_track (_, x) when x = id -> raise (Track (ended, id, stream)) | Video _, Video_track (_, x) when x = id -> raise (Track (ended, id, stream)) | _ -> () in try (* First check active streams *) Hashtbl.iter (test false) dec.streams; (* Now check finished streams *) Hashtbl.iter (test true) dec.finished_streams; raise Not_found with Track t -> t let get_tracks dec = let f id stream l = match stream.dec with | Audio_ba d -> Audio_track (d.name, id) :: l | Audio_both (d, _) -> Audio_track (d.name, id) :: l | Audio d -> Audio_track (d.name, id) :: l | Video d -> Video_track (d.name, id) :: l | Unknown -> l in fold_tracks dec f [] type standard_tracks = { mutable audio_track : track option; mutable video_track : track option; } let drop_track dec dtype = (* Remove all track of this type *) let get_tracks id s l = match (s.dec, dtype) with | Audio_ba _, Audio_track (_, x) when x = id -> (id, s) :: l | Audio_both _, Audio_track (_, x) when x = id -> (id, s) :: l | Audio _, Audio_track (_, x) when x = id -> (id, s) :: l | Video _, Video_track (_, x) when x = id -> (id, s) :: l | _ -> l in let tracks = fold_tracks dec get_tracks [] in let stype = match dtype with Audio_track _ -> "audio" | Video_track _ -> "video" in let f (a, x) = log dec "Dropping %s track with serial %nx." stype a; Hashtbl.replace dec.streams a { os = x.os; index = x.index; read_samples = x.read_samples; position = x.position; dec = Unknown; } in List.iter f tracks let get_standard_tracks ?tracks dec = let f id stream (a_t, v_t, l) = match stream.dec with | Audio_ba d when a_t = None -> (Some (Audio_track (d.name, id)), v_t, l) | Audio_ba d -> (a_t, v_t, Audio_track (d.name, id) :: l) | Audio_both (d, _) when a_t = None -> (Some (Audio_track (d.name, id)), v_t, l) | Audio_both (d, _) -> (a_t, v_t, Audio_track (d.name, id) :: l) | Audio d when a_t = None -> (Some (Audio_track (d.name, id)), v_t, l) | Audio d -> (a_t, v_t, Audio_track (d.name, id) :: l) | Video d when v_t = None -> (a_t, Some (Video_track (d.name, id)), l) | Video d -> (a_t, v_t, Video_track (d.name, id) :: l) | _ -> (a_t, v_t, l) in let a_t, v_t, drop = fold_tracks dec f (None, None, []) in List.iter (drop_track dec) drop; match tracks with | None -> { audio_track = a_t; video_track = v_t } | Some x -> x.audio_track <- a_t; x.video_track <- v_t; x let update_standard_tracks dec tracks = ignore (get_standard_tracks ~tracks dec) let get_standard_tracks dec = get_standard_tracks dec let rec sample_rate_priv d dec = try match d with | Audio_ba d -> ((fst (d.info ())).sample_rate, 1) | Audio_both (d, _) -> ((fst (d.info ())).sample_rate, 1) | Audio d -> ((fst (d.info ())).sample_rate, 1) | Video d -> ((fst (d.info ())).fps_numerator, (fst (d.info ())).fps_denominator) | _ -> assert false with Ogg.Not_enough_data -> feed dec; sample_rate_priv d dec let sample_rate dec dtype = let _, _, stream = get_track dec dtype in sample_rate_priv stream.dec dec let get_track_position dec dtype = let _, _, stream = get_track dec dtype in stream.position let get_position dec = if Hashtbl.length dec.streams = 0 && Hashtbl.length dec.finished_streams = 0 then raise Not_available; let f _ stream pos = match stream.dec with | Audio_ba _ | Audio_both _ | Audio _ | Video _ -> min stream.position pos | _ -> pos in fold_tracks dec f max_float let can_seek dec = dec.callbacks.seek <> None && dec.callbacks.tell <> None type sync_point = { sync_stream : stream; sync_id : nativeint; sync_rate : float; mutable sync_seen : bool; mutable sync_granulepos : Int64.t; mutable sync_skip_samples : int; mutable sync_bytes : int; } (* Function to seek at a given point. *) let sync_seek dec pos = Ogg.Sync.reset dec.sync; let seek = get_some dec.callbacks.seek in ignore (seek pos); Ogg.Sync.seek dec.sync exception Position of (Int64.t * index_element) let find_seek_pos dec time sync_point = let samples = Int64.of_float (time *. sync_point.sync_rate) in while sync_point.sync_stream.read_samples <= samples do feed dec done; let f granulepos index_element = if index_element.total_samples <= samples && Int64.add index_element.total_samples index_element.samples >= samples then raise (Position (granulepos, index_element)) in let granulepos, index_element = try Hashtbl.iter f sync_point.sync_stream.index; raise Not_found with Position x -> x in let skip_samples = Int64.sub samples index_element.total_samples in sync_point.sync_stream.read_samples <- index_element.total_samples; sync_point.sync_granulepos <- granulepos; sync_point.sync_skip_samples <- Int64.to_int skip_samples; sync_point.sync_bytes <- index_element.index_bytes; sync_point.sync_stream.position <- Int64.to_float (Int64.add sync_point.sync_stream.read_samples (Int64.of_int sync_point.sync_skip_samples)) /. sync_point.sync_rate let feed_sync_page sync_point page = if Ogg.Page.granulepos page = sync_point.sync_granulepos then sync_point.sync_seen <- true; if sync_point.sync_seen then Ogg.Stream.put_page sync_point.sync_stream.os page exception Found_sync let feed_sync dec sync_points = let page = Ogg.Sync.read dec.sync in try List.iter (fun sync_point -> if Ogg.Page.serialno page = sync_point.sync_id then begin feed_sync_page sync_point page; raise Found_sync end) sync_points; assert false with Found_sync -> () let sync_forward dec sync_points sync_point = let rec skip (cur, skipped) = try let pos = Ogg.Stream.peek_granulepos sync_point.sync_stream.os in let total_samples = granuleconv sync_point.sync_stream.dec pos cur in let diff = Int64.to_int (Int64.sub total_samples cur) in if skipped + diff < sync_point.sync_skip_samples then begin Ogg.Stream.skip_packet sync_point.sync_stream.os; skip (total_samples, skipped + diff) end else sync_point.sync_stream.position <- (Int64.to_float sync_point.sync_stream.read_samples +. float skipped) /. sync_point.sync_rate with | Ogg.Out_of_sync -> skip (cur, skipped) | Ogg.Not_enough_data -> feed_sync dec sync_points; skip (cur, skipped) in skip (sync_point.sync_stream.read_samples, 0) let seek ?(relative = false) dec time = if (not (can_seek dec)) || get_tracks dec = [] then raise Not_available; if eos dec then raise End_of_stream; let orig_time = get_position dec in if relative then log dec "Seeking to %.02f sec from current position at %.02f sec" time orig_time; let time = if relative then time +. orig_time else time in let time = if time < 0. then 0. else time in log dec "Seeking to absolute position at %.2f sec" time; let f id stream l = let sample_rate () = let x, y = sample_rate_priv stream.dec dec in float x /. float y in match stream.dec with | Audio_ba _ | Audio_both _ | Audio _ -> { sync_id = id; sync_stream = stream; sync_rate = sample_rate (); sync_seen = false; sync_granulepos = Int64.zero; sync_skip_samples = 0; sync_bytes = 0; } :: l | Video _ -> { sync_id = id; sync_stream = stream; sync_rate = sample_rate (); sync_seen = false; sync_granulepos = Int64.zero; sync_skip_samples = 0; sync_bytes = 0; } :: l | _ -> l in let sync_points = Hashtbl.fold f dec.streams [] in (* Resolve each sync_point. *) List.iter (find_seek_pos dec time) sync_points; (* Move all finished streams back to * streams. *) let f x y = Hashtbl.add dec.streams x y in Hashtbl.iter f dec.finished_streams; Hashtbl.clear dec.finished_streams; (* Now finally resync. *) let sync_bytes = let f cur sync_point = if sync_point.sync_bytes < cur then sync_point.sync_bytes else cur in List.fold_left f max_int sync_points in let page = sync_seek dec sync_bytes in (* First, reinitiate all ogg streams. *) let reiniate x = x.sync_stream.os <- Ogg.Stream.create ~serial:x.sync_id (); if Ogg.Page.serialno page = x.sync_id then feed_sync_page x page in List.iter reiniate sync_points; (* Get to the next sync point for * each streams. *) let resync x = sync_forward dec sync_points x; match x.sync_stream.dec with | Audio_ba d -> d.restart x.sync_stream.os | Audio_both (d, _) -> d.restart x.sync_stream.os | Audio d -> d.restart x.sync_stream.os | Video d -> d.restart x.sync_stream.os | _ -> () in List.iter resync sync_points; let sync_time = get_position dec in log dec "Found nearest seek point at %.02f sec" sync_time; if relative then sync_time -. orig_time else sync_time let seek ?relative dec time = try seek ?relative dec time with End_of_stream -> abort dec; raise End_of_stream let incr_pos dec stream len = let x, y = sample_rate_priv stream.dec dec in let rate = float x /. float y in stream.position <- stream.position +. (float len /. rate) let rec audio_info dec dtype = let _, _, stream = get_track dec dtype in try match stream.dec with | Audio_ba d -> d.info () | Audio_both (d, _) -> d.info () | Audio d -> d.info () | _ -> raise Not_found with Ogg.Not_enough_data -> feed dec; audio_info dec dtype let can_decode_ba dec dtype = let _, _, stream = get_track dec dtype in match stream.dec with Audio_ba _ | Audio_both _ -> true | _ -> false let rec video_info dec dtype = let _, _, stream = get_track dec dtype in try match stream.dec with Video d -> d.info () | _ -> raise Not_found with Ogg.Not_enough_data -> feed dec; video_info dec dtype let decode_audio_gen ~get_decoder ~length dec dtype f = let ended, id, stream = get_track dec dtype in try let f x = begin try incr_pos dec stream (length x.(0)) with _ -> () end; f x in (get_decoder stream.dec).decode f with | ( End_of_stream (* In very rare cases (e.g. with a track that * does not have any data to decode), [Ogg.Not_enough_data] * may be raised at the end of the track instead of * [End_of_stream]. Thus, we also catch it here * but re-raise it if the track has not ended yet. *) | Ogg.Not_enough_data ) as e -> if ended then begin log dec "All data from stream %nx has been decoded" id; Hashtbl.remove dec.finished_streams id (* Reraise [Ogg.Not_enough_data] to feed the * decoder. *) end else if e = Ogg.Not_enough_data then raise e; if eos dec then raise End_of_stream let decode_audio = let get_decoder = function | Audio d -> d | Audio_both (d, _) -> d | _ -> raise Not_available in let length = Array.length in decode_audio_gen ~get_decoder ~length let decode_audio_ba = let get_decoder = function | Audio_ba d -> d | Audio_both (_, d) -> d | _ -> raise Not_available in let length = Bigarray.Array1.dim in decode_audio_gen ~get_decoder ~length let decode_video dec dtype f = let ended, id, stream = get_track dec dtype in try let f x = incr_pos dec stream 1; f x in match stream.dec with Video d -> d.decode f | _ -> assert false with (End_of_stream | Ogg.Not_enough_data) as e -> if ended then begin log dec "All data from stream %nx has been decoded: droping stream." id; Hashtbl.remove dec.finished_streams id (* Reraise [Ogg.Not_enough_data] to feed the * decoder. *) end else if e = Ogg.Not_enough_data then raise e; if eos dec then raise End_of_stream let decode_rec g dec dtype f = let rec exec () = try g dec dtype f with Ogg.Not_enough_data -> feed dec; exec () in exec () let decode_audio = decode_rec decode_audio let decode_audio_ba = decode_rec decode_audio_ba let decode_video = decode_rec decode_video ocaml-ogg-0.7.4/src/ogg_decoder.mli000066400000000000000000000223001442573102700171240ustar00rootroot00000000000000(***************************************************************************** Liquidsoap, a programmable audio stream generator. Copyright 2003-2011 Savonet team 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, fully stated in the COPYING file at the root of the liquidsoap distribution. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *****************************************************************************) (** Ogg stream demuxer *) (** This module provides a functional abstract API to * decode and seek in Ogg streams. * * Decoders are also provided in ocaml-vorbis, * ocaml-speex, ocaml-schroedinger, ocaml-flac and * ocaml-theora. * * Functions in this module are not thread safe! *) (** {2 Decoding} *) (** {3 Types} *) (** Type of an ogg stream decoder. *) type t (** Type for callbacks used to acess encoded data. *) type callbacks = { read : bytes -> int -> int -> int; seek : (int -> int) option; tell : (unit -> int) option; } (** Type for a decodable track. * First element is a string describing * the decoder used to decode the track. * Second element is the serial number * associated to the [Ogg.Stream.stream] logical * stream used to pull data packets for that * track. *) type track = | Audio_track of (string * nativeint) | Video_track of (string * nativeint) (** Type for standard tracks (see [get_standard_tracks] below). *) type standard_tracks = { mutable audio_track : track option; mutable video_track : track option; } (** Type for metadata. First element * is a string describing the vendor, second * element is a list of metadata of the form: * [(label,value)]. *) type metadata = string * (string * string) list (** Type for audio information. *) type audio_info = { channels : int; sample_rate : int } (** Type for audio data. *) type audio_data = float array array type audio_ba_data = (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array (** Type of a video plane. *) type video_plane = (int, Bigarray.int8_unsigned_elt, Bigarray.c_layout) Bigarray.Array1.t (** Supported video formats. *) type video_format = (* Planar YCbCr 4:2:0. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) | Yuvj_420 (* Planar YCbCr 4:2:2. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) | Yuvj_422 (* Planar YCbCr 4:4:4. Each component is an uint8_t, * luma and chroma values are full range (0x00 .. 0xff) *) | Yuvj_444 (* Type for video information. *) type video_info = { fps_numerator : int; fps_denominator : int; (* Width of the Y' luminance plane *) width : int; (* Height of the luminance plane *) height : int; } (** Type for video data. *) type video_data = { format : video_format; frame_width : int; frame_height : int; y_stride : int; (** Length, in bytes, per line *) uv_stride : int; (** Length, in bytes, per line *) y : video_plane; (** luminance data *) u : video_plane; (** Cb data *) v : video_plane; (** Cr data *) } (** {3 Exceptions } *) exception Invalid_stream exception Not_available (* This exception has a different semantics than [Ogg.End_of_stream]. * [Ogg.End_of_stream] is raised when end of data has been reached, * while this exception is raised when end of a logical stream has * been reached.. *) exception End_of_stream (** {3 Initialization functions } *) (** Initiate a decoder with the given callbacks. * [log] is an optional functioned used to * return logged messages during the deocding * process. *) val init : ?log:(string -> unit) -> callbacks -> t (** Initiate a decoder from a given file name. *) val init_from_file : ?log:(string -> unit) -> string -> t * Unix.file_descr (** Initate a decoder from a given [Unix.file_descriptor] *) val init_from_fd : ?log:(string -> unit) -> Unix.file_descr -> t (** Get the Ogg.Sync handler associated to * the decoder. Use only if know what you are doing. *) val get_ogg_sync : t -> Ogg.Sync.t (** Reset encoder, try to parse a new sequentialized stream. * To use when end_of_stream has been reached. *) val reset : t -> unit (** Consume all remaining pages of the current * stream. This function may be called to skip * a sequentialized stream but it may be quite * CPU intensive if there are many pages remaining.. * * [eos dec] is [true] after this call. *) val abort : t -> unit (** [true] if the decoder has reached the end of each * logical streams and all data has been decoded. * * If you do not plan on decoding some data, * you should use [drop_track] to indicate it * to the decoder. Otherwise, [eos] will return * [false] until you have decoded all data. *) val eos : t -> bool (** Get all decodable tracks available. *) val get_tracks : t -> track list (** Get the first available audio and * video tracks and drop the other one. *) val get_standard_tracks : t -> standard_tracks (** Update a given record of standard tracks. You should * use this after a [reset] to update the standard tracks * with the newly created tracks. *) val update_standard_tracks : t -> standard_tracks -> unit (** Remove all tracks of the given type. *) val drop_track : t -> track -> unit (** {3 Information functions} *) (** Get informations about the * audio track. *) val audio_info : t -> track -> audio_info * metadata (** [true] if the decoder can decoder to bigarray data. *) val can_decode_ba : t -> track -> bool (** Get informations about the * video track. *) val video_info : t -> track -> video_info * metadata (** Get the sample_rate of the track * of that type. Returns a pair [(numerator,denominator)]. *) val sample_rate : t -> track -> int * int (** Get track absolute position. *) val get_track_position : t -> track -> float (** Get absolute position in the stream. *) val get_position : t -> float (** {3 Seeking functions} *) (** Returns [true] if the decoder * can be used with the [seek] function. *) val can_seek : t -> bool (** Seek to an absolute or relative position in seconds. * * Raises [Not_available] if seeking is * not possible. * * Raises [End_of_stream] if the end of * current stream has been reached while * seeking. You may call [reset] in this * situation to see if there is a new seqentialized * stream available. * * Returns the time actually reached, either in * relative time or absolute time. *) val seek : ?relative:bool -> t -> float -> float (** {3 Decoding functions} *) (** Decode audio data, if possible. * Decoded data is passed to the second argument. * * Raises [End_of_stream] if all stream have ended. * In this case, you can try [reset] to see if there is a * new sequentialized stream. *) val decode_audio : t -> track -> (audio_data -> unit) -> unit (** Decode audio data, if possible. * Decoded data is passed to the second argument. * * Raises [End_of_stream] if all stream have ended. * In this case, you can try [reset] to see if there is a * new sequentialized stream. *) val decode_audio_ba : t -> track -> (audio_ba_data -> unit) -> unit (** Decode video data, if possible. * Decoded data is passed to the second argument. * * Raises [End_of_stream] if all streams have ended. * In this case, you can try [reset] to see if there is a * new sequentialized stream. *) val decode_video : t -> track -> (video_data -> unit) -> unit (** {2 Implementing decoders} *) (** {3 Types } *) (** Generic type for a decoder. *) type ('a, 'b) decoder = { name : string; info : unit -> 'a * metadata; decode : ('b -> unit) -> unit; restart : Ogg.Stream.stream -> unit; (* This function is called after seeking * to notify the decoder of the new [Ogg.Stream.stream] * that is should use to pull data packets. *) samples_of_granulepos : Int64.t -> Int64.t; } (** Type for a generic logical stream decoder. *) type decoders = | Video of (video_info, video_data) decoder | Audio of (audio_info, audio_data) decoder | Audio_ba of (audio_info, audio_ba_data) decoder | Audio_both of (audio_info, audio_data) decoder * (audio_info, audio_ba_data) decoder | Unknown (** Type used to register a new decoder. First * element is a function used to check if the initial [Ogg.Stream.packet] * of an [Ogg.Stream.stream] matches the format decodable by this decoder. * Second element is a function that instanciates the actual decoder * using the initial [Ogg.Stream.stream] used to pull data packets for the * decoder. *) type register_decoder = (Ogg.Stream.packet -> bool) * (Ogg.Stream.stream -> decoders) (** {3 Functions} *) (** Register a new decoder. *) val ogg_decoders : (string, register_decoder) Hashtbl.t ocaml-ogg-0.7.4/src/ogg_stubs.c000066400000000000000000000356211442573102700163320ustar00rootroot00000000000000/* * Copyright 2007 Samuel Mimram * * This file is part of ocaml-ogg. * * ocaml-ogg is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ocaml-ogg 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 Lesser General Public License * along with ocaml-ogg; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * As a special exception to the GNU Library General Public License, you may * link, statically or dynamically, a "work that uses the Library" with a * publicly distributed version of the Library to produce an executable file * containing portions of the Library, and distribute that executable file under * terms of your choice, without any of the additional requirements listed in * clause 6 of the GNU Library General Public License. By "a publicly * distributed version of the Library", we mean either the unmodified Library as * distributed by The Savonet Team, or a modified version of the Library that is * distributed under the conditions defined in clause 3 of the GNU Library * General Public License. This exception does not however invalidate any other * reasons why the executable file might be covered by the GNU Library General * Public License. * */ #include #include #include #include #include #include #include #include #include #include #include #include "ocaml-ogg.h" #ifndef Bytes_val #define Bytes_val String_val #endif /** Page manipulation **/ value value_of_page(ogg_page *op) { CAMLparam0(); CAMLlocal3(v, header, body); header = caml_alloc_string(op->header_len); memcpy(Bytes_val(header), op->header, op->header_len); body = caml_alloc_string(op->body_len); memcpy(Bytes_val(body), op->body, op->body_len); v = caml_alloc_tuple(2); Store_field(v, 0, header); Store_field(v, 1, body); CAMLreturn(v); } ogg_page *page_of_value(value v, ogg_page *page) { page->header = (unsigned char *)String_val(Field(v, 0)); page->header_len = caml_string_length(Field(v, 0)); page->body = (unsigned char *)String_val(Field(v, 1)); page->body_len = caml_string_length(Field(v, 1)); return page; } CAMLprim value ocaml_ogg_page_serialno(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(caml_copy_nativeint(ogg_page_serialno(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_eos(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(Val_bool(ogg_page_eos(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_bos(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(Val_bool(ogg_page_bos(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_packets(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(Val_int(ogg_page_packets(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_continued(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(Val_bool(ogg_page_continued(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_version(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(Val_int(ogg_page_version(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_granulepos(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(caml_copy_int64(ogg_page_granulepos(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_pageno(value page) { CAMLparam1(page); ogg_page op; CAMLreturn(caml_copy_nativeint(ogg_page_pageno(page_of_value(page, &op)))); } CAMLprim value ocaml_ogg_page_checksum_set(value page) { CAMLparam1(page); ogg_page op; ogg_page_checksum_set(page_of_value(page, &op)); CAMLreturn(Val_unit); } /***** Sync state *****/ static void finalize_sync_state(value s) { ogg_sync_destroy(Sync_state_val(s)); } static struct custom_operations sync_state_ops = { "ocaml_ogg_sync_state", finalize_sync_state, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default}; CAMLprim value ocaml_ogg_sync_init() { CAMLparam0(); CAMLlocal1(sync); ogg_sync_state *oy = malloc(sizeof(ogg_sync_state)); ogg_sync_init(oy); sync = caml_alloc_custom(&sync_state_ops, sizeof(ogg_sync_state *), 1, 0); Sync_state_val(sync) = oy; CAMLreturn(sync); } CAMLprim value ocaml_ogg_sync_reset(value oy) { CAMLparam1(oy); ogg_sync_reset(Sync_state_val(oy)); CAMLreturn(Val_unit); } CAMLprim value ocaml_ogg_sync_pageseek(value cb, value oy) { CAMLparam2(cb, oy); CAMLlocal1(bytes); ogg_sync_state *sync = Sync_state_val(oy); int read; int len = 4096; ogg_page page; int err = ogg_sync_pageseek(sync, &page); bytes = caml_alloc_string(len); while (err <= 0) { read = Int_val(caml_callback3(cb, bytes, Val_int(0), Val_int(len))); if (read == 0) caml_raise_constant(*caml_named_value("ogg_exn_eos")); char *buffer = ogg_sync_buffer(sync, read); memcpy(buffer, String_val(bytes), read); ogg_sync_wrote(sync, read); err = ogg_sync_pageseek(sync, &page); } CAMLreturn(value_of_page(&page)); } CAMLprim value ocaml_ogg_sync_read(value cb, value oy) { CAMLparam2(cb, oy); CAMLlocal2(ret, bytes); ogg_sync_state *sync = Sync_state_val(oy); int read; int len = 4096; ogg_page page; int ans = ogg_sync_pageout(sync, &page); bytes = caml_alloc_string(len); while (ans != 1) { if (ans == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); read = Int_val(caml_callback3(cb, bytes, Val_int(0), Val_int(len))); if (read == 0) caml_raise_constant(*caml_named_value("ogg_exn_eos")); char *buffer = ogg_sync_buffer(sync, read); memcpy(buffer, String_val(bytes), read); ogg_sync_wrote(sync, read); ans = ogg_sync_pageout(sync, &page); } CAMLreturn(value_of_page(&page)); } /***** Stream state ******/ static void finalize_stream_state(value s) { // This also free the argument ogg_stream_destroy(Stream_state_val(s)); } static struct custom_operations stream_state_ops = { "ocaml_ogg_stream_state", finalize_stream_state, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default}; static void finalize_packet(value s) { ogg_packet *op = Packet_val(s); free(op->packet); free(op); } static inline ogg_packet *copy_packet(ogg_packet *op) { ogg_packet *nop = malloc(sizeof(ogg_packet)); if (nop == NULL) caml_raise_out_of_memory(); nop->packet = malloc(op->bytes); memcpy(nop->packet, op->packet, op->bytes); nop->bytes = op->bytes; nop->b_o_s = op->b_o_s; nop->e_o_s = op->e_o_s; nop->granulepos = op->granulepos; nop->packetno = op->packetno; return nop; } static struct custom_operations packet_ops = { "ocaml_ogg_packet", finalize_packet, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default}; value value_of_packet(ogg_packet *op) { CAMLparam0(); CAMLlocal1(packet); packet = caml_alloc_custom_mem(&packet_ops, sizeof(ogg_packet *), op->bytes); Packet_val(packet) = copy_packet(op); CAMLreturn(packet); } CAMLprim value ocaml_ogg_stream_init(value serial) { CAMLparam0(); CAMLlocal1(ans); ogg_stream_state *os = malloc(sizeof(ogg_stream_state)); ogg_stream_init(os, Nativeint_val(serial)); ans = caml_alloc_custom(&stream_state_ops, sizeof(ogg_stream_state *), 1, 0); Stream_state_val(ans) = os; CAMLreturn(ans); } CAMLprim value ocaml_ogg_stream_eos(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); CAMLreturn(Val_bool(ogg_stream_eos(os))); } // libogg does not offer any API to generate a final, empty page. // However, some (most!) codecs need a synchronous way to end their // logical bitstream without having to submit an empty packet so we // hack it away.. CAMLprim value ocaml_ogg_stream_terminate(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_page page; ogg_packet op; op.packet = (unsigned char *)NULL; op.bytes = 0; op.b_o_s = 0; op.e_o_s = 1; op.granulepos = os->granulepos + 1; op.packetno = os->packetno + 1; ogg_stream_packetin(os, &op); if (!ogg_stream_pageout(os, &page)) caml_raise_constant(*caml_named_value("ogg_exn_bad_data")); page.header[26] = 0; page.header_len = 27; page.body = NULL; page.body_len = 0; ogg_page_checksum_set(&page); CAMLreturn(value_of_page(&page)); } CAMLprim value ocaml_ogg_stream_pageout(value o_stream_state, value fill) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_page og; int ret; #ifdef HAVE_PAGEOUT_FILL if (fill != Val_unit) ret = ogg_stream_pageout_fill(os, &og, Int_val(fill)); else #endif ret = ogg_stream_pageout(os, &og); if (!ret) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); CAMLreturn(value_of_page(&og)); } CAMLprim value ocaml_ogg_stream_pagein(value o_stream_state, value page) { CAMLparam2(o_stream_state, page); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_page op; if (ogg_stream_pagein(os, page_of_value(page, &op)) != 0) caml_raise_constant(*caml_named_value("ogg_exn_bad_data")); CAMLreturn(Val_unit); } CAMLprim value ocaml_ogg_stream_packetin(value o_stream_state, value packet) { CAMLparam2(o_stream_state, packet); ogg_stream_state *os = Stream_state_val(o_stream_state); if (ogg_stream_packetin(os, Packet_val(packet)) != 0) caml_raise_constant(*caml_named_value("ogg_exn_bad_data")); CAMLreturn(Val_unit); } CAMLprim value ocaml_ogg_stream_packetout(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_packet op; int ret = ogg_stream_packetout(os, &op); if (ret == 0) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); CAMLreturn(value_of_packet(&op)); } CAMLprim value ocaml_ogg_stream_packet_advance(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_packet op; int ret = ogg_stream_packetout(os, &op); if (ret == 0) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); CAMLreturn(Val_unit); } CAMLprim value ocaml_ogg_stream_packetpeek(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_packet op; int ret = ogg_stream_packetpeek(os, &op); if (ret == 0) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); CAMLreturn(value_of_packet(&op)); } CAMLprim value ocaml_ogg_stream_granulepospeek(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_packet op; int ret = ogg_stream_packetpeek(os, &op); if (ret == 0) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); CAMLreturn(caml_copy_int64(op.granulepos)); } CAMLprim value ocaml_ogg_stream_packet_granulepos(value _op) { CAMLparam1(_op); ogg_packet *op = Packet_val(_op); CAMLreturn(caml_copy_int64(op->granulepos)); } CAMLprim value ocaml_ogg_flush_stream(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); ogg_page og; if (!ogg_stream_flush(os, &og)) caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); CAMLreturn(value_of_page(&og)); } CAMLprim value ocaml_ogg_stream_serialno(value o_stream_state) { CAMLparam1(o_stream_state); ogg_stream_state *os = Stream_state_val(o_stream_state); CAMLreturn(caml_copy_nativeint((intnat)os->serialno)); } /* Ogg skeleton helpers */ /* Values from http://xiph.org/ogg/doc/skeleton.html */ #define SKELETON_VERSION_MAJOR 3 #define SKELETON_VERSION_MINOR 0 #define FISHEAD_IDENTIFIER "fishead\0" /* Wrappers */ static void write16le(unsigned char *ptr, ogg_uint16_t v) { ptr[0] = v & 0xff; ptr[1] = (v >> 8) & 0xff; } static void write32le(unsigned char *ptr, ogg_uint32_t v) { ptr[0] = v & 0xff; ptr[1] = (v >> 8) & 0xff; ptr[2] = (v >> 16) & 0xff; ptr[3] = (v >> 24) & 0xff; } static void write64le(unsigned char *ptr, ogg_int64_t v) { ogg_uint32_t hi = v >> 32; ptr[0] = v & 0xff; ptr[1] = (v >> 8) & 0xff; ptr[2] = (v >> 16) & 0xff; ptr[3] = (v >> 24) & 0xff; ptr[4] = hi & 0xff; ptr[5] = (hi >> 8) & 0xff; ptr[6] = (hi >> 16) & 0xff; ptr[7] = (hi >> 24) & 0xff; } /* Code from theorautils.c in ffmpeg2theora */ CAMLprim value ocaml_ogg_skeleton_fishead(value pres_num, value pres_den, value base_num, value base_den, value time) { CAMLparam0(); CAMLlocal1(packet); ogg_packet op; memset(&op, 0, sizeof(op)); op.packet = malloc(64); if (op.packet == NULL) caml_raise_out_of_memory(); memset(op.packet, 0, 64); memcpy(op.packet, FISHEAD_IDENTIFIER, 8); /* identifier */ write16le(op.packet + 8, SKELETON_VERSION_MAJOR); /* version major */ write16le(op.packet + 10, SKELETON_VERSION_MINOR); /* version minor */ write64le(op.packet + 12, (ogg_int64_t)Int64_val(pres_num)); /* presentationtime numerator */ write64le(op.packet + 20, (ogg_int64_t)Int64_val( pres_den)); /* presentationtime denominator */ write64le(op.packet + 28, (ogg_int64_t)Int64_val(base_num)); /* basetime numerator */ write64le(op.packet + 36, (ogg_int64_t)Int64_val(base_den)); /* basetime denominator */ /* both the numerator are zero hence handled by the memset */ write32le(op.packet + 44, Int32_val(time)); /* UTC time, set to zero for now */ op.b_o_s = 1; /* its the first packet of the stream */ op.e_o_s = 0; /* its not the last packet of the stream */ op.bytes = 64; /* length of the packet in bytes */ packet = value_of_packet(&op); free(op.packet); CAMLreturn(packet); } /* Code from theorautils.c from ffmpeg2theora */ CAMLprim value ocaml_ogg_skeleton_eos(value v) { CAMLparam0(); ogg_packet op; /* build the e_o_s packet */ memset(&op, 0, sizeof(op)); op.b_o_s = 0; op.e_o_s = 1; /* its the e_o_s packet */ op.granulepos = 0; op.bytes = 0; /* e_o_s packet is an empty packet */ CAMLreturn(value_of_packet(&op)); }