# HG changeset patch # User naoyan@johnstown.minaminoshima.org # Date 1285769935 -32400 # Node ID 9c7bc6c0327ee42e27eea34fe6595c3279999496 # Parent 215a51fa3df374161614cf32706cb9c0bad699ba Add DLNA server function test. (from uShare project) diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/COPYING --- a/recpt1/COPYING Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If 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 convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state 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 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 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/Makefile.in --- a/recpt1/Makefile.in Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -DESTDIR = -prefix = @prefix@ -exec_prefix = @exec_prefix@ -bindir = @bindir@ -CC = @CC@ - -TARGET = recpt1 -TARGET2 = recpt1ctl -TARGET3 = checksignal -TARGETS = $(TARGET) $(TARGET2) $(TARGET3) -RELEASE_VERSION = "1.1.0" - -CPPFLAGS = -I../driver -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -CFLAGS = -O2 -g -pthread - -LIBS = @LIBS@ -LIBS2 = -LIBS3 = -lpthread -lm -LDFLAGS = - -OBJS = recpt1.o decoder.o mkpath.o tssplitter_lite.o -OBJS2 = recpt1ctl.o -OBJS3 = checksignal.o -OBJALL = $(OBJS) $(OBJS2) $(OBJS3) -DEPEND = .deps - -all: $(TARGETS) - -clean: - rm -f $(OBJALL) $(TARGETS) $(DEPEND) version.h - -distclean: clean - rm -f Makefile config.h config.log config.status - -maintainer-clean: distclean - rm -fr configure config.h.in aclocal.m4 autom4te.cache *~ - -$(TARGET): $(OBJS) - $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) - -$(TARGET2): $(OBJS2) - $(CC) $(LDFLAGS) -o $@ $(OBJS2) $(LIBS2) - -$(TARGET3): $(OBJS3) - $(CC) $(LDFLAGS) -o $@ $(OBJS3) $(LIBS3) - -$(DEPEND): version.h - $(CC) -MM $(OBJS:.o=.c) $(OBJS2:.o=.c) $(CPPFLAGS) > $@ - -version.h: - revh=`hg parents --template 'const char *version = "r{rev}:{node|short} ({date|shortdate})";\n' 2>/dev/null`; \ - if [ -n "$$revh" ] ; then \ - echo "$$revh" > $@; \ - else \ - echo "const char *version = \"$(RELEASE_VERSION)\";" > $@; \ - fi - -install: $(TARGET) - install -m 755 $(TARGETS) $(DESTDIR)$(bindir) - --include .deps diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/autogen.sh --- a/recpt1/autogen.sh Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -#!/bin/sh - -[ -f configure.ac ] || { - echo "autogen.sh: run this command only at the top of a recpt1 source tree." - exit 1 -} - -DIE=0 - -(autoconf --version) < /dev/null > /dev/null 2>&1 || { - echo - echo "You must have autoconf installed to compile recpt1." - echo "Get ftp://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.62.tar.gz" - echo "(or a newer version if it is available)" - DIE=1 - NO_AUTOCONF=yes -} - -(automake --version) < /dev/null > /dev/null 2>&1 || { - echo - echo "You must have automake installed to compile recpt1." - echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.10.1.tar.gz" - echo "(or a newer version if it is available)" - DIE=1 - NO_AUTOMAKE=yes -} - -# if no automake, don't bother testing for aclocal -test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { - echo - echo "**Error**: Missing \`aclocal'. The version of \`automake'" - echo "installed doesn't appear recent enough." - echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.10.1.tar.gz" - echo "(or a newer version if it is available)" - DIE=1 -} - -# if no autoconf, don't bother testing for autoheader -test -n "$NO_AUTOCONF" || (autoheader --version) < /dev/null > /dev/null 2>&1 || { - echo - echo "**Error**: Missing \`autoheader'. The version of \`autoheader'" - echo "installed doesn't appear recent enough." - echo "Get ftp://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.62.tar.gz" - echo "(or a newer version if it is available)" - DIE=1 -} - -if test "$DIE" -eq 1; then - exit 1 -fi - -echo "Generating configure script and Makefiles for recpt1." - -echo "Running aclocal ..." -aclocal -I . -echo "Running autoheader ..." -autoheader -echo "Running autoconf ..." -autoconf diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/channels/sample.recpt1-channels-chiba --- a/recpt1/channels/sample.recpt1-channels-chiba Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ - 20ch: Tokyo MX TV - 21ch: Fuji TV - 22ch: TBS - 23ch: TV Tokyo - 24ch: TV Asahi - 25ch: Nihon TV - 26ch: NHK Educational - 27ch: NHK General - 28ch: Housou Daigaku - 30ch: CTC Chiba TV diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/channels/sample.recpt1-channels-kanazawa --- a/recpt1/channels/sample.recpt1-channels-kanazawa Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ - 13ch: NHK Educational - 14ch: MRO - 15ch: NHK General - 16ch: Ishikawa TV - 17ch: TV Kanazawa - 23ch: Hokuriku Asahi diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/channels/sample.recpt1-channels-nagoya --- a/recpt1/channels/sample.recpt1-channels-nagoya Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ - 13ch: NHK Educational - 18ch: CBC - 19ch: Chukyo TV - 20ch: NHK Gemeral - 21ch: Tokai TV - 22ch: Nagoya TV (mei tere) - 23ch: TV Aichi - 27ch: Mie TV diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/channels/sample.recpt1-channels-tokyo --- a/recpt1/channels/sample.recpt1-channels-tokyo Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ - 20ch: Tokyo MX TV - 21ch: Fuji TV - 22ch: TBS - 23ch: TV Tokyo - 24ch: TV Asahi - 25ch: Nihon TV - 26ch: NHK Educational - 27ch: NHK General - 28ch: Housou Daigaku diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/checksignal.c --- a/recpt1/checksignal.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,497 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include "pt1_ioctl.h" - -#include "config.h" -#include "decoder.h" -#include "recpt1.h" -#include "version.h" -#include "mkpath.h" - -#include -#include -#include "pt1_dev.h" -#include "tssplitter_lite.h" - -#define MAX_RETRY (2) - -/* type definitions */ -typedef int boolean; - -typedef struct thread_data { - int ch; - int lnb; /* LNB voltage */ - int tfd; /* tuner fd */ - ISDB_T_FREQ_CONV_TABLE *table; -} thread_data; - -/* globals */ -boolean f_exit = FALSE; -boolean use_bell = FALSE; - -/* prototypes */ -int tune(char *channel, thread_data *tdata, char *device); -int close_tuner(thread_data *tdata); - -void -cleanup(thread_data *tdata) -{ - f_exit = TRUE; -} - -/* will be signal handler thread */ -void * -process_signals(void *data) -{ - sigset_t waitset; - int sig; - thread_data *tdata = (thread_data *)data; - - sigemptyset(&waitset); - sigaddset(&waitset, SIGINT); - sigaddset(&waitset, SIGTERM); - sigaddset(&waitset, SIGUSR1); - - sigwait(&waitset, &sig); - - switch(sig) { - case SIGINT: - fprintf(stderr, "\nSIGINT received. cleaning up...\n"); - cleanup(tdata); - break; - case SIGTERM: - fprintf(stderr, "\nSIGTERM received. cleaning up...\n"); - cleanup(tdata); - break; - case SIGUSR1: /* normal exit*/ - cleanup(tdata); - break; - } - - return NULL; /* dummy */ -} - -void -init_signal_handlers(pthread_t *signal_thread, thread_data *tdata) -{ - sigset_t blockset; - - sigemptyset(&blockset); - sigaddset(&blockset, SIGINT); - sigaddset(&blockset, SIGTERM); - sigaddset(&blockset, SIGUSR1); - - if(pthread_sigmask(SIG_BLOCK, &blockset, NULL)) - fprintf(stderr, "pthread_sigmask() failed.\n"); - - pthread_create(signal_thread, NULL, process_signals, tdata); -} - -/* lookup frequency conversion table*/ -ISDB_T_FREQ_CONV_TABLE * -searchrecoff(char *channel) -{ - int lp; - - for(lp = 0; isdb_t_conv_table[lp].parm_freq != NULL; lp++) { - /* return entry number in the table when strings match and - * lengths are same. */ - if((memcmp(isdb_t_conv_table[lp].parm_freq, channel, - strlen(channel)) == 0) && - (strlen(channel) == strlen(isdb_t_conv_table[lp].parm_freq))) { - return &isdb_t_conv_table[lp]; - } - } - return NULL; -} - -void -show_usage(char *cmd) -{ - fprintf(stderr, "Usage: \n%s [--device devicefile] [--lnb voltage] [--bell] channel\n", cmd); - fprintf(stderr, "\n"); -} - -void -show_options(void) -{ - fprintf(stderr, "Options:\n"); - fprintf(stderr, "--device devicefile: Specify devicefile to use\n"); - fprintf(stderr, "--lnb voltage: Specify LNB voltage (0, 11, 15)\n"); - fprintf(stderr, "--bell: Notify signal quality by bell\n"); - fprintf(stderr, "--help: Show this help\n"); - fprintf(stderr, "--version: Show version\n"); - fprintf(stderr, "--list: Show channel list\n"); -} - -void -show_channels(void) -{ - FILE *f; - char *home; - char buf[255], filename[255]; - - fprintf(stderr, "Available Channels:\n"); - - home = getenv("HOME"); - sprintf(filename, "%s/.recpt1-channels", home); - f = fopen(filename, "r"); - if(f) { - while(fgets(buf, 255, f)) - fprintf(stderr, "%s", buf); - fclose(f); - } - else - fprintf(stderr, "13-62: Terrestrial Channels\n"); - - fprintf(stderr, "101ch: NHK BS1\n"); - fprintf(stderr, "102ch: NHK BS2\n"); - fprintf(stderr, "103ch: NHK BShi\n"); - fprintf(stderr, "141ch: BS Nittele\n"); - fprintf(stderr, "151ch: BS Asahi\n"); - fprintf(stderr, "161ch: BS-TBS\n"); - fprintf(stderr, "171ch: BS Japan\n"); - fprintf(stderr, "181ch: BS Fuji\n"); - fprintf(stderr, "191ch: WOWOW\n"); - fprintf(stderr, "192ch: WOWOW2\n"); - fprintf(stderr, "193ch: WOWOW3\n"); - fprintf(stderr, "200ch: Star Channel\n"); - fprintf(stderr, "211ch: BS11 Digital\n"); - fprintf(stderr, "222ch: TwellV\n"); - fprintf(stderr, "C13-C63: CATV Channels\n"); - fprintf(stderr, "CS2-CS24: CS Channels\n"); -} - -float -getsignal_isdb_s(int signal) -{ - /* apply linear interpolation */ - static const float afLevelTable[] = { - 24.07f, // 00 00 0 24.07dB - 24.07f, // 10 00 4096 24.07dB - 18.61f, // 20 00 8192 18.61dB - 15.21f, // 30 00 12288 15.21dB - 12.50f, // 40 00 16384 12.50dB - 10.19f, // 50 00 20480 10.19dB - 8.140f, // 60 00 24576 8.140dB - 6.270f, // 70 00 28672 6.270dB - 4.550f, // 80 00 32768 4.550dB - 3.730f, // 88 00 34816 3.730dB - 3.630f, // 88 FF 35071 3.630dB - 2.940f, // 90 00 36864 2.940dB - 1.420f, // A0 00 40960 1.420dB - 0.000f // B0 00 45056 -0.01dB - }; - - unsigned char sigbuf[4]; - memset(sigbuf, '\0', sizeof(sigbuf)); - sigbuf[0] = (((signal & 0xFF00) >> 8) & 0XFF); - sigbuf[1] = (signal & 0xFF); - - /* calculate signal level */ - if(sigbuf[0] <= 0x10U) { - /* clipped maximum */ - return 24.07f; - } - else if (sigbuf[0] >= 0xB0U) { - /* clipped minimum */ - return 0.0f; - } - else { - /* linear interpolation */ - const float fMixRate = - (float)(((unsigned short)(sigbuf[0] & 0x0FU) << 8) | - (unsigned short)sigbuf[0]) / 4096.0f; - return afLevelTable[sigbuf[0] >> 4] * (1.0f - fMixRate) + - afLevelTable[(sigbuf[0] >> 4) + 0x01U] * fMixRate; - } -} - -void -do_bell(int bell) -{ - int i; - for(i=0; i < bell; i++) { - fprintf(stderr, "\a"); - usleep(400000); - } -} - -void -calc_cn(int fd, int type) -{ - int rc; - double P; - double CNR; - int bell = 0; - - if(ioctl(fd, GET_SIGNAL_STRENGTH, &rc) < 0) { - fprintf(stderr, "Tuner Select Error\n"); - return ; - } - - if(type == CHTYPE_GROUND) { - P = log10(5505024/(double)rc) * 10; - CNR = (0.000024 * P * P * P * P) - (0.0016 * P * P * P) + - (0.0398 * P * P) + (0.5491 * P)+3.0965; - } - else { - CNR = getsignal_isdb_s(rc); - } - - if(CNR >= 30.0) - bell = 3; - else if(CNR >= 15.0 && CNR < 30.0) - bell = 2; - else if(CNR < 15.0) - bell = 1; - - fprintf(stderr, "\rC/N = %fdB", CNR); - if(use_bell) - do_bell(bell); -} - -int -tune(char *channel, thread_data *tdata, char *device) -{ - char **tuner; - int num_devs; - int lp; - FREQUENCY freq; - - /* get channel */ - tdata->table = searchrecoff(channel); - if(tdata->table == NULL) { - fprintf(stderr, "Invalid Channel: %s\n", channel); - return 1; - } - - freq.frequencyno = tdata->table->set_freq; - freq.slot = tdata->table->add_freq; - - /* open tuner */ - /* case 1: specified tuner device */ - if(device) { - tdata->tfd = open(device, O_RDONLY); - if(tdata->tfd < 0) { - fprintf(stderr, "Cannot open tuner device: %s\n", device); - return 1; - } - - /* power on LNB */ - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { - fprintf(stderr, "Power on LNB failed: %s\n", device); - } - } - - /* tune to specified channel */ - while(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { - if(f_exit) { - close_tuner(tdata); - return 1; - } - fprintf(stderr, "No signal. Still trying: %s\n", device); - } - - fprintf(stderr, "device = %s\n", device); - tdata->ch = atoi(channel); - } - else { - /* case 2: loop around available devices */ - if(tdata->table->type == CHTYPE_SATELLITE) { - tuner = bsdev; - num_devs = NUM_BSDEV; - } - else { - tuner = isdb_t_dev; - num_devs = NUM_ISDB_T_DEV; - } - - for(lp = 0; lp < num_devs; lp++) { - int count = 0; - - tdata->tfd = open(tuner[lp], O_RDONLY); - if(tdata->tfd >= 0) { - /* power on LNB */ - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { - fprintf(stderr, "Warning: Power on LNB failed: %s\n", tuner[lp]); - } - } - - /* tune to specified channel */ - while(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0 && - count < MAX_RETRY) { - if(f_exit) { - close_tuner(tdata); - return 1; - } - fprintf(stderr, "No signal. Still trying: %s\n", tuner[lp]); - count++; - } - - if(count >= MAX_RETRY) { - close_tuner(tdata); - count = 0; - continue; - } - - fprintf(stderr, "device = %s\n", tuner[lp]); - break; /* found suitable tuner */ - } - } - - /* all tuners cannot be used */ - if(tdata->tfd < 0) { - fprintf(stderr, "Cannot tune to the specified channel\n"); - return 1; - } - else { - tdata->ch = atoi(channel); - } - } - - return 0; /* success */ -} - -int -close_tuner(thread_data *tdata) -{ - int rv = 0; - - if(tdata->tfd == -1) - return rv; - - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_DISABLE, 0) < 0) { - rv = 1; - } - } - close(tdata->tfd); - tdata->tfd = -1; - - return rv; -} - -int -main(int argc, char **argv) -{ - pthread_t signal_thread; - static thread_data tdata; - int result; - int option_index; - struct option long_options[] = { - { "bell", 0, NULL, 'b'}, - { "help", 0, NULL, 'h'}, - { "version", 0, NULL, 'v'}, - { "list", 0, NULL, 'l'}, - { "LNB", 1, NULL, 'n'}, - { "lnb", 1, NULL, 'n'}, - { "device", 1, NULL, 'd'}, - {0, 0, NULL, 0} /* terminate */ - }; - - char *device = NULL; - int val; - char *voltage[] = {"0V", "11V", "15V"}; - - while((result = getopt_long(argc, argv, "bhvln:d:", - long_options, &option_index)) != -1) { - switch(result) { - case 'b': - use_bell = TRUE; - break; - case 'h': - fprintf(stderr, "\n"); - show_usage(argv[0]); - fprintf(stderr, "\n"); - show_options(); - fprintf(stderr, "\n"); - show_channels(); - fprintf(stderr, "\n"); - exit(0); - break; - case 'v': - fprintf(stderr, "%s %s\n", argv[0], version); - fprintf(stderr, "signal check utility for PT1/2 digital tuner.\n"); - exit(0); - break; - case 'l': - show_channels(); - exit(0); - break; - /* following options require argument */ - case 'n': - val = atoi(optarg); - switch(val) { - case 11: - tdata.lnb = 1; - break; - case 15: - tdata.lnb = 2; - break; - default: - tdata.lnb = 0; - break; - } - fprintf(stderr, "LNB = %s\n", voltage[tdata.lnb]); - break; - case 'd': - device = optarg; - break; - } - } - - if(argc - optind < 1) { - fprintf(stderr, "channel must be specified!\n"); - fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); - return 1; - } - - /* spawn signal handler thread */ - init_signal_handlers(&signal_thread, &tdata); - - /* tune */ - if(tune(argv[optind], &tdata, device) != 0) - return 1; - - while(1) { - if(f_exit) - break; - /* show signal strength */ - calc_cn(tdata.tfd, tdata.table->type); - sleep(1); - } - - /* wait for signal thread */ - pthread_kill(signal_thread, SIGUSR1); - pthread_join(signal_thread, NULL); - - /* close tuner */ - if(close_tuner(&tdata) != 0) - return 1; - - return 0; -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/configure.ac --- a/recpt1/configure.ac Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# -*- Autoconf -*- -# Process this file with autoconf to produce a configure script. - -AC_PREREQ(2.59) -AC_INIT([recpt1], [1.0.0], yaz@honeyplanet.jp) -AC_CONFIG_SRCDIR([recpt1.c]) -AC_CONFIG_HEADERS([config.h]) - -# Checks for programs. -AC_PROG_CC - -# Checks for b25 support. -AC_ARG_ENABLE(b25, - [AS_HELP_STRING([--enable-b25],[enable b25 support])], - [AC_CHECK_LIB([arib25], [create_arib_std_b25], , [AC_MSG_WARN(libarb25 is not available.)], [-lpcsclite])] -) - -# Checks for libraries. -AC_CHECK_LIB([m], [log10]) -AC_CHECK_LIB([pthread], [pthread_kill]) - -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/decoder.c --- a/recpt1/decoder.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -#include -#include - -#include "decoder.h" - -#ifdef HAVE_LIBARIB25 - -decoder * -b25_startup(decoder_options *opt) -{ - decoder *dec = calloc(1, sizeof(decoder)); - int code; - const char *err = NULL; - - dec->b25 = create_arib_std_b25(); - if(!dec->b25) { - err = "create_arib_std_b25 failed"; - goto error; - } - - code = dec->b25->set_multi2_round(dec->b25, opt->round); - if(code < 0) { - err = "set_multi2_round failed"; - goto error; - } - - code = dec->b25->set_strip(dec->b25, opt->strip); - if(code < 0) { - err = "set_strip failed"; - goto error; - } - - code = dec->b25->set_emm_proc(dec->b25, opt->emm); - if(code < 0) { - err = "set_emm_proc failed"; - goto error; - } - - dec->bcas = create_b_cas_card(); - if(!dec->bcas) { - err = "create_b_cas_card failed"; - goto error; - } - code = dec->bcas->init(dec->bcas); - if(code < 0) { - err = "bcas->init failed"; - goto error; - } - - code = dec->b25->set_b_cas_card(dec->b25, dec->bcas); - if(code < 0) { - err = "set_b_cas_card failed"; - goto error; - } - - return dec; - -error: - fprintf(stderr, "%s\n", err); - free(dec); - return NULL; -} - -int -b25_shutdown(decoder *dec) -{ - dec->b25->release(dec->b25); - dec->bcas->release(dec->bcas); - free(dec); - - return 0; -} - -int -b25_decode(decoder *dec, ARIB_STD_B25_BUFFER *sbuf, ARIB_STD_B25_BUFFER *dbuf) -{ - int code; - - code = dec->b25->put(dec->b25, sbuf); - if(code < 0) { - fprintf(stderr, "b25->put failed\n"); - return code; - } - - code = dec->b25->get(dec->b25, dbuf); - if(code < 0) { - fprintf(stderr, "b25->get failed\n"); - return code; - } - - return code; -} - -int -b25_finish(decoder *dec, ARIB_STD_B25_BUFFER *sbuf, ARIB_STD_B25_BUFFER *dbuf) -{ - int code; - - code = dec->b25->flush(dec->b25); - if(code < 0) { - fprintf(stderr, "b25->flush failed\n"); - return code; - } - - code = dec->b25->get(dec->b25, dbuf); - if(code < 0) { - fprintf(stderr, "b25->get failed\n"); - return code; - } - - return code; -} - -#else - -/* functions */ -decoder *b25_startup(decoder_options *opt) -{ - return NULL; -} - -int b25_shutdown(decoder *dec) -{ - return 0; -} - -int b25_decode(decoder *dec, - ARIB_STD_B25_BUFFER *sbuf, - ARIB_STD_B25_BUFFER *dbuf) -{ - return 0; -} - -int b25_finish(decoder *dec, - ARIB_STD_B25_BUFFER *sbuf, - ARIB_STD_B25_BUFFER *dbuf) -{ - return 0; -} - -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/decoder.h --- a/recpt1/decoder.h Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ -#ifndef _DECODER_H_ -#define _DECODER_H_ - -#include "config.h" - -#ifdef HAVE_LIBARIB25 - -#include -#include - -typedef struct decoder { - ARIB_STD_B25 *b25; - B_CAS_CARD *bcas; -} decoder; - -typedef struct decoder_options { - int round; - int strip; - int emm; -} decoder_options; - -#else - -typedef struct { - int size; - void *data; -} ARIB_STD_B25_BUFFER; - -typedef struct decoder { - void *dummy; -} decoder; - -typedef struct decoder_options { - int round; - int strip; - int emm; -} decoder_options; - -#endif - -/* prototypes */ -decoder *b25_startup(decoder_options *opt); -int b25_shutdown(decoder *dec); -int b25_decode(decoder *dec, - ARIB_STD_B25_BUFFER *sbuf, - ARIB_STD_B25_BUFFER *dbuf); -int b25_finish(decoder *dec, - ARIB_STD_B25_BUFFER *sbuf, - ARIB_STD_B25_BUFFER *dbuf); - - -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/mkpath.c --- a/recpt1/mkpath.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* mkpath is originally written by Jonathan Leffler. - * see "http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux" for detail. - * copyright: (C) JLSS 1990-91,1997-98,2001,2005,2008 - */ - -#include -#include -#include -#include -#include - -static int -do_mkdir(const char *path, mode_t mode) -{ - struct stat st; - int status = 0; - - if (stat(path, &st) != 0) { - /* Directory does not exist */ - if (mkdir(path, mode) != 0) - status = -1; - } - else if (!S_ISDIR(st.st_mode)) { - errno = ENOTDIR; - status = -1; - } - - return(status); -} - -int -mkpath(const char *path, mode_t mode) -{ - char *pp; - char *sp; - int status; - char *copypath = strdup(path); - - status = 0; - pp = copypath; - while (status == 0 && (sp = strchr(pp, '/')) != 0) { - if (sp != pp) { - /* Neither root nor double slash in path */ - *sp = '\0'; - status = do_mkdir(copypath, mode); - *sp = '/'; - } - pp = sp + 1; - } - if (status == 0) - status = do_mkdir(path, mode); - free(copypath); - return (status); -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/mkpath.h --- a/recpt1/mkpath.h Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -#ifndef _MKPATH_H_ -#define _MKPATH_H_ - -int mkpath(const char *path, mode_t mode); - -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/pt1_dev.h --- a/recpt1/pt1_dev.h Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ -#ifndef _PT1_DEV_H_ -#define _PT1_DEV_H_ - -char *bsdev[NUM_BSDEV] = { - "/dev/pt1video1", - "/dev/pt1video0", - "/dev/pt1video5", - "/dev/pt1video4", - "/dev/pt1video9", - "/dev/pt1video8", - "/dev/pt1video13", - "/dev/pt1video12" -}; -char *isdb_t_dev[NUM_ISDB_T_DEV] = { - "/dev/pt1video2", - "/dev/pt1video3", - "/dev/pt1video6", - "/dev/pt1video7", - "/dev/pt1video10", - "/dev/pt1video11", - "/dev/pt1video14", - "/dev/pt1video15" -}; - -// 変換テーブル(ISDB-T用) -// 実際にioctl()を行う値の部分はREADMEを参照の事。 -// BS/CSの設定値およびスロット番号は -// http://www5e.biglobe.ne.jp/~kazu_f/digital-sat/index.htmlより取得。 -// - -ISDB_T_FREQ_CONV_TABLE isdb_t_conv_table[] = { - { 0, CHTYPE_SATELLITE, 0, "151"}, /* 151ch:BS朝日 */ - { 0, CHTYPE_SATELLITE, 1, "161"}, /* 161ch:BS-TBS */ - { 1, CHTYPE_SATELLITE, 0, "191"}, /* 191ch:WOWOW */ - { 1, CHTYPE_SATELLITE, 0, "192"}, /* 192ch:WOWOW2 */ - { 1, CHTYPE_SATELLITE, 0, "193"}, /* 193ch:WOWOW3 */ - { 1, CHTYPE_SATELLITE, 1, "171"}, /* 171ch:BSジャパン */ - { 4, CHTYPE_SATELLITE, 0, "211"}, /* 211ch:BS11デジタル */ - { 4, CHTYPE_SATELLITE, 1, "200"}, /* 200ch:スターチャンネル */ - { 4, CHTYPE_SATELLITE, 2, "222"}, /* 222ch:TwellV */ - { 6, CHTYPE_SATELLITE, 0, "141"}, /* 141ch:BS日テレ */ - { 6, CHTYPE_SATELLITE, 1, "181"}, /* 181ch:BSフジ */ - { 7, CHTYPE_SATELLITE, 0, "101"}, /* 101ch:NHK衛星第1放送(BS1) */ - { 7, CHTYPE_SATELLITE, 0, "102"}, /* 102ch:NHK衛星第2放送(BS2) */ - { 7, CHTYPE_SATELLITE, 1, "103"}, /* 103ch:NHKハイビジョン(BShi) */ - { 7, CHTYPE_SATELLITE, 1, "104"}, /* 104ch:NHKハイビジョン(BShi)臨時*/ - { 12, CHTYPE_SATELLITE, 0, "CS2"}, /* ND2: - * 237ch:スター・チャンネル プラス - * 239ch:日本映画専門チャンネルHD - * 306ch:フジテレビCSHD */ - { 13, CHTYPE_SATELLITE, 0, "CS4"}, /* ND4: - * 100ch:e2プロモ - * 256ch:J sports ESPN - * 312ch:FOX - * 322ch:スペースシャワーTV - * 331ch:カートゥーンネットワーク - * 194ch:インターローカルTV - * 334ch:トゥーン・ディズニー */ - { 14, CHTYPE_SATELLITE, 0, "CS6"}, /* ND6: - * 221ch:東映チャンネル - * 222ch:衛星劇場 - * 223ch:チャンネルNECO - * 224ch:洋画★シネフィル・イマジカ - * 292ch:時代劇専門チャンネル - * 238ch:スター・チャンネル クラシック - * 310ch:スーパー!ドラマTV - * 311ch:AXN - * 343ch:ナショナルジオグラフィックチャンネル */ - - { 15, CHTYPE_SATELLITE, 0, "CS8"}, /* ND8: - * 055ch:ショップ チャンネル */ - { 16, CHTYPE_SATELLITE, 0, "CS10"}, /* ND10: - * 228ch:ザ・シネマ - * 800ch:スカチャンHD800 - * 801ch:スカチャン801 - * 802ch:スカチャン802 */ - { 17, CHTYPE_SATELLITE, 0, "CS12"}, /* ND12: - * 260ch:ザ・ゴルフ・チャンネル - * 303ch:テレ朝チャンネル - * 323ch:MTV 324ch:大人の音楽専門TV◆ミュージック・エア - * 352ch:朝日ニュースター - * 353ch:BBCワールドニュース - * 354ch:CNNj - * 361ch:ジャスト・アイ インフォメーション */ - { 18, CHTYPE_SATELLITE, 0, "CS14"}, /* ND14: - * 251ch:J sports 1 - * 252ch:J sports 2 - * 253ch:J sports Plus - * 254ch:GAORA - * 255ch:スカイ・Asports+ */ - { 19, CHTYPE_SATELLITE, 0, "CS16"}, /* ND16: - * 305ch:チャンネル銀河 - * 333ch:アニメシアターX(AT-X) - * 342ch:ヒストリーチャンネル - * 290ch:TAKARAZUKA SKYSTAGE - * 803ch:スカチャン803 - * 804ch:スカチャン804 */ - { 20, CHTYPE_SATELLITE, 0, "CS18"}, /* ND18: - * 240ch:ムービープラスHD - * 262ch:ゴルフネットワーク - * 314ch:LaLa HDHV */ - { 21, CHTYPE_SATELLITE, 0, "CS20"}, /* ND20: - * 258ch:フジテレビ739 - * 302ch:フジテレビ721 - * 332ch:アニマックス - * 340ch:ディスカバリーチャンネル - * 341ch:アニマルプラネット */ - { 22, CHTYPE_SATELLITE, 0, "CS22"}, /* ND22: - * 160ch:C-TBSウェルカムチャンネル - * 161ch:QVC - * 185ch:プライム365.TV - * 293ch:ファミリー劇場 - * 301ch:TBSチャンネル - * 304ch:ディズニー・チャンネル - * 325ch:MUSIC ON! TV - * 330ch:キッズステーション - * 351ch:TBSニュースバード */ - { 23, CHTYPE_SATELLITE, 0, "CS24"}, /* ND24: - * 257ch:日テレG+ - * 291ch:fashiontv - * 300ch:日テレプラス - * 320ch:安らぎの音楽と風景/エコミュージックTV - * 321ch:MusicJapan TV - * 350ch:日テレNEWS24 */ - { 0, CHTYPE_GROUND, 0, "1"}, { 1, CHTYPE_GROUND, 0, "2"}, - { 2, CHTYPE_GROUND, 0, "3"}, { 3, CHTYPE_GROUND, 0, "C13"}, - { 4, CHTYPE_GROUND, 0, "C14"}, { 5, CHTYPE_GROUND, 0, "C15"}, - { 6, CHTYPE_GROUND, 0, "C16"}, { 7, CHTYPE_GROUND, 0, "C17"}, - { 8, CHTYPE_GROUND, 0, "C18"}, { 9, CHTYPE_GROUND, 0, "C19"}, - { 10, CHTYPE_GROUND, 0, "C20"}, { 11, CHTYPE_GROUND, 0, "C21"}, - { 12, CHTYPE_GROUND, 0, "C22"}, { 13, CHTYPE_GROUND, 0, "4"}, - { 14, CHTYPE_GROUND, 0, "5"}, { 15, CHTYPE_GROUND, 0, "6"}, - { 16, CHTYPE_GROUND, 0, "7"}, { 17, CHTYPE_GROUND, 0, "8"}, - { 18, CHTYPE_GROUND, 0, "9"}, { 19, CHTYPE_GROUND, 0, "10"}, - { 20, CHTYPE_GROUND, 0, "11"}, { 21, CHTYPE_GROUND, 0, "12"}, - { 22, CHTYPE_GROUND, 0, "C23"}, { 23, CHTYPE_GROUND, 0, "C24"}, - { 24, CHTYPE_GROUND, 0, "C25"}, { 25, CHTYPE_GROUND, 0, "C26"}, - { 26, CHTYPE_GROUND, 0, "C27"}, { 27, CHTYPE_GROUND, 0, "C28"}, - { 28, CHTYPE_GROUND, 0, "C29"}, { 29, CHTYPE_GROUND, 0, "C30"}, - { 30, CHTYPE_GROUND, 0, "C31"}, { 31, CHTYPE_GROUND, 0, "C32"}, - { 32, CHTYPE_GROUND, 0, "C33"}, { 33, CHTYPE_GROUND, 0, "C34"}, - { 34, CHTYPE_GROUND, 0, "C35"}, { 35, CHTYPE_GROUND, 0, "C36"}, - { 36, CHTYPE_GROUND, 0, "C37"}, { 37, CHTYPE_GROUND, 0, "C38"}, - { 38, CHTYPE_GROUND, 0, "C39"}, { 39, CHTYPE_GROUND, 0, "C40"}, - { 40, CHTYPE_GROUND, 0, "C41"}, { 41, CHTYPE_GROUND, 0, "C42"}, - { 42, CHTYPE_GROUND, 0, "C43"}, { 43, CHTYPE_GROUND, 0, "C44"}, - { 44, CHTYPE_GROUND, 0, "C45"}, { 45, CHTYPE_GROUND, 0, "C46"}, - { 46, CHTYPE_GROUND, 0, "C47"}, { 47, CHTYPE_GROUND, 0, "C48"}, - { 48, CHTYPE_GROUND, 0, "C49"}, { 49, CHTYPE_GROUND, 0, "C50"}, - { 50, CHTYPE_GROUND, 0, "C51"}, { 51, CHTYPE_GROUND, 0, "C52"}, - { 52, CHTYPE_GROUND, 0, "C53"}, { 53, CHTYPE_GROUND, 0, "C54"}, - { 54, CHTYPE_GROUND, 0, "C55"}, { 55, CHTYPE_GROUND, 0, "C56"}, - { 56, CHTYPE_GROUND, 0, "C57"}, { 57, CHTYPE_GROUND, 0, "C58"}, - { 58, CHTYPE_GROUND, 0, "C59"}, { 59, CHTYPE_GROUND, 0, "C60"}, - { 60, CHTYPE_GROUND, 0, "C61"}, { 61, CHTYPE_GROUND, 0, "C62"}, - { 62, CHTYPE_GROUND, 0, "C63"}, { 63, CHTYPE_GROUND, 0, "13"}, - { 64, CHTYPE_GROUND, 0, "14"}, { 65, CHTYPE_GROUND, 0, "15"}, - { 66, CHTYPE_GROUND, 0, "16"}, { 67, CHTYPE_GROUND, 0, "17"}, - { 68, CHTYPE_GROUND, 0, "18"}, { 69, CHTYPE_GROUND, 0, "19"}, - { 70, CHTYPE_GROUND, 0, "20"}, { 71, CHTYPE_GROUND, 0, "21"}, - { 72, CHTYPE_GROUND, 0, "22"}, { 73, CHTYPE_GROUND, 0, "23"}, - { 74, CHTYPE_GROUND, 0, "24"}, { 75, CHTYPE_GROUND, 0, "25"}, - { 76, CHTYPE_GROUND, 0, "26"}, { 77, CHTYPE_GROUND, 0, "27"}, - { 78, CHTYPE_GROUND, 0, "28"}, { 79, CHTYPE_GROUND, 0, "29"}, - { 80, CHTYPE_GROUND, 0, "30"}, { 81, CHTYPE_GROUND, 0, "31"}, - { 82, CHTYPE_GROUND, 0, "32"}, { 83, CHTYPE_GROUND, 0, "33"}, - { 84, CHTYPE_GROUND, 0, "34"}, { 85, CHTYPE_GROUND, 0, "35"}, - { 86, CHTYPE_GROUND, 0, "36"}, { 87, CHTYPE_GROUND, 0, "37"}, - { 88, CHTYPE_GROUND, 0, "38"}, { 89, CHTYPE_GROUND, 0, "39"}, - { 90, CHTYPE_GROUND, 0, "40"}, { 91, CHTYPE_GROUND, 0, "41"}, - { 92, CHTYPE_GROUND, 0, "42"}, { 93, CHTYPE_GROUND, 0, "43"}, - { 94, CHTYPE_GROUND, 0, "44"}, { 95, CHTYPE_GROUND, 0, "45"}, - { 96, CHTYPE_GROUND, 0, "46"}, { 97, CHTYPE_GROUND, 0, "47"}, - { 98, CHTYPE_GROUND, 0, "48"}, { 99, CHTYPE_GROUND, 0, "49"}, - { 100, CHTYPE_GROUND, 0, "50"}, { 101, CHTYPE_GROUND, 0, "51"}, - { 102, CHTYPE_GROUND, 0, "52"}, { 103, CHTYPE_GROUND, 0, "53"}, - { 104, CHTYPE_GROUND, 0, "54"}, { 105, CHTYPE_GROUND, 0, "55"}, - { 106, CHTYPE_GROUND, 0, "56"}, { 107, CHTYPE_GROUND, 0, "57"}, - { 108, CHTYPE_GROUND, 0, "58"}, { 109, CHTYPE_GROUND, 0, "59"}, - { 110, CHTYPE_GROUND, 0, "60"}, { 111, CHTYPE_GROUND, 0, "61"}, - { 112, CHTYPE_GROUND, 0, "62"}, - { 0, 0, 0, NULL} /* terminate */ -}; -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/pt1_lnbd.c --- a/recpt1/pt1_lnbd.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,400 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pt1_ioctl.h" - -#ifdef O_NOFOLLOW -#define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK -#else -#define OPEN_FLAGS O_NONBLOCK -#endif - -/* 腱糸 */ -#define C_RET_OK 0 -#define C_RET_NG -1 -#define MYNAME "pt1_lnb_enabler" -#define PID_DIR "/var/run/" -#define PIDFILENAME PID_DIR MYNAME ".pid" -#define NUMBER "0123456789" -#define device "/dev/pt1video0" -#define ROOT "root" -/* 腱糸 */ - -mode_t umask_val = 0133; - -void fin_action(int); - -int main() { - struct sigaction ign_sigaction; - struct sigaction fin_sigaction; - FILE *fp; - int i; - int i_ret; - int fd; - int pt1_fd; - int string_length; - int number_length; - int i_pid; - struct stat orig_st; - struct stat open_st; - int flags; - char file_name[] = PIDFILENAME; - char buf[1024] = ""; - pid_t mypid; - char *p; - struct passwd *p_st_passwd; - uid_t uid_root; - - /* */ - /* root罔у篏亥篋 */ - p_st_passwd = getpwnam(ROOT); - if (p_st_passwd == NULL) { - printf("faile to get pwent. id=[%s].\n", ROOT); - exit(C_RET_NG); - } - uid_root = p_st_passwd->pw_uid; - if( uid_root != getuid() ) { - /* root 鎀絎茵罔с */ - printf("This process must be run by root. uid=[%d].\n", getuid()); - exit(C_RET_NG); - } - /* fork 荀活罧冴帥若≪ */ - mypid = fork(); - if( mypid == -1 ) { - /* fork */ - printf("fork error.\n"); - exit(C_RET_NG); - } else if ( mypid != 0 ) { - /* 荀祉鴻括篋(潟祉) */ - return(C_RET_OK); - } - umask(umask_val); - ign_sigaction.sa_handler = SIG_IGN; - fin_sigaction.sa_handler = fin_action; - if (sigaction(SIGHUP, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGHUP\n"); - exit(C_RET_NG); - } - if (sigaction(SIGTERM, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGTERM\n"); - exit(C_RET_NG); - } - if (sigaction(SIGPIPE, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGPIPE\n"); - exit(C_RET_NG); - } - if (sigaction(SIGINT, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGINT\n"); - exit(C_RET_NG); - } - if (sigaction(SIGQUIT, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGQUIT\n"); - exit(C_RET_NG); - } - if (sigaction(SIGILL, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGILL\n"); - exit(C_RET_NG); - } - if (sigaction(SIGABRT, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGABRT\n"); - exit(C_RET_NG); - } - if (sigaction(SIGFPE, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGFPE\n"); - exit(C_RET_NG); - } - if (sigaction(SIGSEGV, &fin_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGSEGV\n"); - exit(C_RET_NG); - } - /* sleep 篏帥 SIGALRM 丞舟 - * if (sigaction(SIGALRM, &ign_sigaction, NULL) != 0) { - * printf("failed to set signal handler. SIGALRM\n"); - * exit(C_RET_NG); - * } - */ - if (sigaction(SIGUSR1, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGUSR1\n"); - exit(C_RET_NG); - } - if (sigaction(SIGUSR2, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGUSR2\n"); - exit(C_RET_NG); - } - if (sigaction(SIGCHLD, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGCHLD\n"); - exit(C_RET_NG); - } - if (sigaction(SIGCONT, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGCONT\n"); - exit(C_RET_NG); - } - if (sigaction(SIGTSTP, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGTSTP\n"); - exit(C_RET_NG); - } - if (sigaction(SIGTTIN, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGTTIN\n"); - exit(C_RET_NG); - } - if (sigaction(SIGTTOU, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGTTOU\n"); - exit(C_RET_NG); - } - if (sigaction(SIGPOLL, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGPOLL\n"); - exit(C_RET_NG); - } - if (sigaction(SIGIO, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGIO\n"); - exit(C_RET_NG); - } - if (sigaction(SIGPROF, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGPROF\n"); - exit(C_RET_NG); - } - if (sigaction(SIGSYS, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGSYS\n"); - exit(C_RET_NG); - } - if (sigaction(SIGTRAP, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGTRAP\n"); - exit(C_RET_NG); - } - if (sigaction(SIGURG, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGURG\n"); - exit(C_RET_NG); - } - if (sigaction(SIGVTALRM, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGVTALRM\n"); - exit(C_RET_NG); - } - if (sigaction(SIGXCPU, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGXCPU\n"); - exit(C_RET_NG); - } - if (sigaction(SIGXFSZ, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGXFSZ\n"); - exit(C_RET_NG); - } - /* c - * if (sigaction(SIGEMT, &ign_sigaction, NULL) != 0) { - * printf("failed to set signal handler. SIGEMT\n"); - * exit(C_RET_NG); - * } - */ - if (sigaction(SIGSTKFLT, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGSTKFLT\n"); - exit(C_RET_NG); - } - if (sigaction(SIGIO, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGIO\n"); - exit(C_RET_NG); - } - if (sigaction(SIGCLD, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGCLD\n"); - exit(C_RET_NG); - } - if (sigaction(SIGPWR, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGPWR\n"); - exit(C_RET_NG); - } - /* c - * if (sigaction(SIGINFO, &ign_sigaction, NULL) != 0) { - * printf("failed to set signal handler. SIGINFO\n"); - * exit(C_RET_NG); - * } - */ - /* c - * if (sigaction(SIGLOST, &ign_sigaction, NULL) != 0) { - * printf("failed to set signal handler. SIGLOST\n"); - * exit(C_RET_NG); - * } - */ - if (sigaction(SIGWINCH, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGWINCH\n"); - exit(C_RET_NG); - } - if (sigaction(SIGUNUSED, &ign_sigaction, NULL) != 0) { - printf("failed to set signal handler. SIGUNUSED\n"); - exit(C_RET_NG); - } - - /* <ゃс */ - if ((lstat(file_name, &orig_st) != 0) || - (!S_ISREG(orig_st.st_mode))) - { - /* <ゃ<翫罩e幻腟篋 */ - i_ret = C_RET_OK; - } else { - /* PID<ゃ絖翫憜倶腆肴 - * 私莎桁倶(pid<ゃpidゃゃ祉鴻絖) - * 糸pid<ゃ羔罩祉с障c */ - i_ret = C_RET_NG; - } - /* TOCTOU 腴九倶馹 - * /var/run 祉ャ≪cу馹< */ - if (i_ret != C_RET_OK) { - fd = open(file_name, (OPEN_FLAGS | O_RDWR)); - if (fd == -1) { - /* 弱 */ - printf("open error. file:[%s].\n", file_name); - exit(C_RET_NG); - } - - if (fstat(fd, &open_st) != 0) { - /* 弱 */ - printf("stat error. file:[%s].\n", file_name); - exit(C_RET_NG); - } - - if ((orig_st.st_mode != open_st.st_mode) || - (orig_st.st_ino != open_st.st_ino) || - (orig_st.st_dev != open_st.st_dev)) { - /* <ゃс帥 */ - printf("file switch has occurred. file:[%s].\n", file_name); - close(fd); - exit(C_RET_NG); - } - - /* 馹<ゃс腆肴с O_NONBLOCK - * ≦鴻 (ュ) */ - if ((flags = fcntl(fd, F_GETFL)) == -1) { - /* 弱 */ - printf("fcntl error. file:[%s].\n", file_name); - close(fd); - exit(C_RET_NG); - } - - if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) { - /* 弱 */ - printf("fcntl error. file:[%s].\n", file_name); - close(fd); - exit(C_RET_NG); - } - /* <ゃ泣ゃ冴0篁ュс翫PIDゃс */ - if(open_st.st_size != 0) { - /* <ゃ茯PIDゃ緇 */ - if((fp = fdopen(fd, "w+")) == NULL) { - /* fdopen()紊掩 */ - printf("fdopen error. fd:[%d].\n", fd); - close(fd); - exit(C_RET_NG); - } - if ( fgets(buf, sizeof(buf), fp) == NULL ) { - /* fgets */ - printf("fgets error. fd:[%d].\n", fd); - fclose(fp); - exit(C_RET_NG); - } - buf[sizeof(buf)-1] = '\0'; - p = strchr(buf, (int)'\n'); - if(p != NULL) { - *p = '\0'; - } - string_length = strlen(buf); - number_length = strspn(buf, NUMBER); - if ( string_length != number_length ) { - /* PID<ゃ医幻 */ - printf("invalid pid file[%s] buf[%s].\n", file_name, buf); - fclose(fp); - exit(C_RET_NG); - } - i_pid = atoi(buf); - - /* PID<ゃPIDPIDゃ祉鴻絖鐚 */ - i_ret = kill(i_pid, 0); - if ( i_ret == 0 ) { - /* 絖翫篋莎桁帥腟篋 */ - printf("process already exists. pid[%d]\n", i_pid); - exit(C_RET_NG); - } else { - /* 絖翫医幻腟篋fp祉膓茵 */ - rewind(fp); - } - } - } - if ( i_ret == C_RET_OK ) { - /* PID<ゃ絖翫PID<ゃ域鋎茵 */ - fd = open(file_name, O_CREAT|O_EXCL|O_RDWR, 00644); - if ( fd == -1 ){ - /* open 紊掩 */ - printf("file open error. file_name[%s].\n", file_name); - exit(C_RET_NG); - } - if((fp = fdopen(fd, "w+")) == NULL) { - /* fdopen()紊掩 */ - printf("fdopen error. fd:[%d].\n", fd); - close(fd); - exit(C_RET_NG); - } - } - /* fpPID<ゃ吾莨若倶сPID吾莨若 */ - mypid = getpid(); - snprintf(buf, sizeof(buf), "%d\n", mypid); - i_ret = fputs(buf, fp); - if(i_ret == EOF) { - /* fputs */ - printf("fputs error. file_name[%s].\n", file_name); - fclose(fp); - exit(C_RET_NG); - } - fclose(fp); - /* PID≫腟篋*/ - /* fd pt1 ゃ鴻 open */ - pt1_fd = open(device, O_RDONLY); - if(pt1_fd == -1) { - /* open */ - printf("open error. file_name[%s].\n", device); - exit(C_RET_NG); - } - /* fcntl LNB 紙劫 */ - if(ioctl(pt1_fd, LNB_ENABLE, 0) < 0) { - printf("Power on LNB failed. device:[%s].\n", device); - exit(C_RET_NG); - } - close(pt1_fd); - /* ♂緇(腟篋腟篋signal) */ - while(1){ - sleep(UINT_MAX); - } - return C_RET_OK; -} - -/* 腟篋絎茵∽ */ -/* 祉鴻腟篋鴻signal篏 */ -void fin_action(int sig) { - int pt1_fd; - char file_name[] = PIDFILENAME; - /* fd pt1 ゃ鴻open */ - pt1_fd = open(device, O_RDONLY); - if(pt1_fd == -1) { - /* open */ - printf("open error. file_name[%s].\n", device); - exit(C_RET_NG); - } - /* fcntl LNB 紙 */ - if(ioctl(pt1_fd, LNB_DISABLE, 0) < 0) { - printf("Power on LNB failed. device:[%s].\n", device); - close(pt1_fd); - exit(C_RET_NG); - } - /* fd close */ - close(pt1_fd); - /* pid<ゃゃ */ - if ( unlink(file_name) != 0 ) { - /* pid file unlink */ - printf("unlink error. file_name[%s].\n", device); - exit(C_RET_NG); - } - /* 障 */ - exit(C_RET_OK); -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/recpt1.c --- a/recpt1/recpt1.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1221 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ -/* vim: set ts=4 sts=4 sw=4 expandtab number : */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include "pt1_ioctl.h" - -#include "config.h" -#include "decoder.h" -#include "recpt1.h" -#include "version.h" -#include "mkpath.h" - -#include -#include -#include "pt1_dev.h" -#include "tssplitter_lite.h" - -/* maximum write length at once */ -#define SIZE_CHANK 1316 - -/* ipc message size */ -#define MSGSZ 255 - -/* type definitions */ -typedef int boolean; - -typedef struct sock_data { - int sfd; /* socket fd */ - struct sockaddr_in addr; -} sock_data; - -typedef struct thread_data { - QUEUE_T *queue; - decoder *decoder; - decoder_options *dopt; - int ch; - int lnb; /* LNB voltage */ - int tfd; /* tuner fd */ - int wfd; /* output file fd */ - ISDB_T_FREQ_CONV_TABLE *table; - sock_data *sock_data; - pthread_t signal_thread; - int recsec; - time_t start_time; - boolean indefinite; - int msqid; - splitter *splitter; -} thread_data; - -typedef struct msgbuf { - long mtype; - char mtext[MSGSZ]; -} message_buf; - -/* globals */ -boolean f_exit = FALSE; - -/* prototypes */ -int tune(char *channel, thread_data *tdata, char *device); -int close_tuner(thread_data *tdata); - - -/* ipc message receive */ -void * -mq_recv(void *t) -{ - thread_data *tdata = (thread_data *)t; - message_buf rbuf; - char channel[16]; - int ch = 0, recsec = 0, time_to_add = 0; - - while(1) { - if(msgrcv(tdata->msqid, &rbuf, MSGSZ, 1, 0) < 0) { - return NULL; - } - - sscanf(rbuf.mtext, "ch=%s t=%d e=%d", channel, &recsec, &time_to_add); - ch = atoi(channel); -// fprintf(stderr, "ch=%d time=%d extend=%d\n", ch, recsec, time_to_add); - - if(ch && tdata->ch != ch) { - /* stop stream */ - ioctl(tdata->tfd, STOP_REC, 0); -#if 0 - /* re-initialize decoder */ - if(tdata->decoder) { -// b25_finish(tdata->decoder); - b25_shutdown(tdata->decoder); - tdata->decoder = b25_startup(tdata->dopt); - if(!tdata->decoder) { - fprintf(stderr, "Cannot start b25 decoder\n"); - fprintf(stderr, "Fall back to encrypted recording\n"); - } - } -#endif - /* tune to new channel */ - if(close_tuner(tdata) != 0) - return NULL; - - /* wait for remainder */ - while(tdata->queue->num_used > 0) { - usleep(10000); - } - - tune(channel, tdata, NULL); - - /* restart recording */ - if(ioctl(tdata->tfd, START_REC, 0) < 0) { - fprintf(stderr, "Tuner cannot start recording\n"); - return NULL; - } - } - - if(time_to_add) { - tdata->recsec += time_to_add; - fprintf(stderr, "Extended %d sec\n", time_to_add); - } - - if(recsec) { - time_t cur_time; - time(&cur_time); - if(cur_time - tdata->start_time > recsec) { - f_exit = TRUE; - } - else { - tdata->recsec = recsec; - fprintf(stderr, "Total recording time = %d sec\n", recsec); - } - } - - if(f_exit) - return NULL; - } -} - - -/* lookup frequency conversion table*/ -ISDB_T_FREQ_CONV_TABLE * -searchrecoff(char *channel) -{ - int lp; - - for(lp = 0; isdb_t_conv_table[lp].parm_freq != NULL; lp++) { - /* return entry number in the table when strings match and - * lengths are same. */ - if((memcmp(isdb_t_conv_table[lp].parm_freq, channel, - strlen(channel)) == 0) && - (strlen(channel) == strlen(isdb_t_conv_table[lp].parm_freq))) { - return &isdb_t_conv_table[lp]; - } - } - return NULL; -} - -QUEUE_T * -create_queue(size_t size) -{ - QUEUE_T *p_queue; - int memsize = sizeof(QUEUE_T) + size * sizeof(BUFSZ); - - p_queue = (QUEUE_T*)calloc(memsize, sizeof(char)); - - if(p_queue != NULL) { - p_queue->size = size; - p_queue->num_avail = size; - p_queue->num_used = 0; - pthread_mutex_init(&p_queue->mutex, NULL); - pthread_cond_init(&p_queue->cond_avail, NULL); - pthread_cond_init(&p_queue->cond_used, NULL); - } - - return p_queue; -} - -void -destroy_queue(QUEUE_T *p_queue) -{ - if(!p_queue) - return; - - pthread_mutex_destroy(&p_queue->mutex); - pthread_cond_destroy(&p_queue->cond_avail); - pthread_cond_destroy(&p_queue->cond_used); - free(p_queue); -} - -/* enqueue data. this function will block if queue is full. */ -void -enqueue(QUEUE_T *p_queue, BUFSZ *data) -{ - struct timeval now; - struct timespec spec; - - gettimeofday(&now, NULL); - spec.tv_sec = now.tv_sec + 1; - spec.tv_nsec = now.tv_usec * 1000; - - pthread_mutex_lock(&p_queue->mutex); - /* entered critical section */ - - /* wait while queue is full */ - while(p_queue->num_avail == 0) { - pthread_cond_timedwait(&p_queue->cond_avail, - &p_queue->mutex, &spec); - if(f_exit) { - pthread_mutex_unlock(&p_queue->mutex); - return; - } - } - - p_queue->buffer[p_queue->in] = data; - - /* move position marker for input to next position */ - p_queue->in++; - p_queue->in %= p_queue->size; - - /* update counters */ - p_queue->num_avail--; - p_queue->num_used++; - - /* leaving critical section */ - pthread_mutex_unlock(&p_queue->mutex); - pthread_cond_signal(&p_queue->cond_used); -} - -/* dequeue data. this function will block if queue is empty. */ -BUFSZ * -dequeue(QUEUE_T *p_queue) -{ - struct timeval now; - struct timespec spec; - BUFSZ *buffer; - - gettimeofday(&now, NULL); - spec.tv_sec = now.tv_sec + 1; - spec.tv_nsec = now.tv_usec * 1000; - - pthread_mutex_lock(&p_queue->mutex); - /* entered the critical section*/ - - /* wait while queue is empty */ - while(p_queue->num_used == 0) { - pthread_cond_timedwait(&p_queue->cond_used, - &p_queue->mutex, &spec); - if(f_exit) { - pthread_mutex_unlock(&p_queue->mutex); - return NULL; - } - } - - /* take buffer address */ - buffer = p_queue->buffer[p_queue->out]; - - /* move position marker for output to next position */ - p_queue->out++; - p_queue->out %= p_queue->size; - - /* update counters */ - p_queue->num_avail++; - p_queue->num_used--; - - /* leaving the critical section */ - pthread_mutex_unlock(&p_queue->mutex); - pthread_cond_signal(&p_queue->cond_avail); - - return buffer; -} - -/* this function will be reader thread */ -void * -reader_func(void *p) -{ - thread_data *data = (thread_data *)p; - QUEUE_T *p_queue = data->queue; - decoder *dec = data->decoder; - splitter *splitter = data->splitter; - int wfd = data->wfd; - boolean use_b25 = dec ? TRUE : FALSE; - boolean use_udp = data->sock_data ? TRUE : FALSE; - boolean fileless = FALSE; - boolean use_splitter = splitter ? TRUE : FALSE; - int sfd = -1; - pthread_t signal_thread = data->signal_thread; - struct sockaddr_in *addr = NULL; - BUFSZ *qbuf; - splitbuf_t splitbuf; - ARIB_STD_B25_BUFFER sbuf, dbuf, buf; - int code; - int split_select_finish = TSS_ERROR; - - buf.size = 0; - buf.data = NULL; - splitbuf.size = 0; - - if(wfd == -1) - fileless = TRUE; - - if(use_udp) { - sfd = data->sock_data->sfd; - addr = &data->sock_data->addr; - } - - while(1) { - ssize_t wc = 0; - int file_err = 0; - qbuf = dequeue(p_queue); - /* no entry in the queue */ - if(qbuf == NULL) { - break; - } - - sbuf.data = qbuf->buffer; - sbuf.size = qbuf->size; - - buf = sbuf; /* default */ - - if(use_b25) { - code = b25_decode(dec, &sbuf, &dbuf); - if(code < 0) { - fprintf(stderr, "b25_decode failed (code=%d). fall back to encrypted recording.\n", code); - use_b25 = FALSE; - } - else - buf = dbuf; - } - - - if(use_splitter) { - splitbuf.size = 0; - - while(buf.size) { - /* $BJ,N%BP>](BPID$B$NCj=P(B */ - if(split_select_finish != TSS_SUCCESS) { - split_select_finish = split_select(splitter, &buf); - if(split_select_finish == TSS_NULL) { - /* malloc$B%(%i!](BPID$B$,40A4$KCj=P$G$-$k$^$G=PNO$7$J$$(B - * 1$BICDxEYM>M5$r8+$k$H$$$$$+$b(B - */ - time_t cur_time; - time(&cur_time); - if(cur_time - data->start_time > 4) { - use_splitter = FALSE; - goto fin; - } - break; - } - } - /* $BJ,N%BP>]0J30$r$U$k$$Mn$H$9(B */ - code = split_ts(splitter, &buf, &splitbuf); - if(code != TSS_SUCCESS) { - fprintf(stderr, "split_ts failed\n"); - break; - } - - break; - } /* while */ - - buf.size = splitbuf.size; - buf.data = splitbuf.buffer; - fin: - ; - } /* if */ - - - if(!fileless) { - /* write data to output file */ - int size_remain = buf.size; - int offset = 0; - - while(size_remain > 0) { - int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK; - - wc = write(wfd, buf.data + offset, ws); - if(wc < 0) { - perror("write"); - file_err = 1; - pthread_kill(signal_thread, - errno == EPIPE ? SIGPIPE : SIGUSR2); - break; - } - size_remain -= wc; - offset += wc; - } - } - - if(use_udp && sfd != -1) { - /* write data to socket */ - int size_remain = buf.size; - int offset = 0; - while(size_remain > 0) { - int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK; - wc = write(sfd, buf.data + offset, ws); - if(wc < 0) { - if(errno == EPIPE) - pthread_kill(signal_thread, SIGPIPE); - break; - } - size_remain -= wc; - offset += wc; - } - } - - free(qbuf); - qbuf = NULL; - - /* normal exit */ - if((f_exit && !p_queue->num_used) || file_err) { - - buf = sbuf; /* default */ - - if(use_b25) { - code = b25_finish(dec, &sbuf, &dbuf); - if(code < 0) - fprintf(stderr, "b25_finish failed\n"); - else - buf = dbuf; - } - - if(use_splitter) { - /* $BJ,N%BP>]0J30$r$U$k$$Mn$H$9(B */ - code = split_ts(splitter, &buf, &splitbuf); - if(code != TSS_SUCCESS) { - break; - } - - buf.data = splitbuf.buffer; - buf.size = splitbuf.size; - } - - if(!fileless && !file_err) { - wc = write(wfd, buf.data, buf.size); - if(wc < 0) { - perror("write"); - file_err = 1; - pthread_kill(signal_thread, - errno == EPIPE ? SIGPIPE : SIGUSR2); - } - } - - if(use_udp && sfd != -1) { - wc = write(sfd, buf.data, buf.size); - if(wc < 0) { - if(errno == EPIPE) - pthread_kill(signal_thread, SIGPIPE); - } - } - - break; - } - } - - time_t cur_time; - time(&cur_time); - fprintf(stderr, "Recorded %dsec\n", - (int)(cur_time - data->start_time)); - - return NULL; -} - -void -show_usage(char *cmd) -{ -#ifdef HAVE_LIBARIB25 - fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); -#else - fprintf(stderr, "Usage: \n%s [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); -#endif - fprintf(stderr, "\n"); - fprintf(stderr, "Remarks:\n"); - fprintf(stderr, "if rectime is '-', records indefinitely.\n"); - fprintf(stderr, "if destfile is '-', stdout is used for output.\n"); -} - -void -show_options(void) -{ - fprintf(stderr, "Options:\n"); -#ifdef HAVE_LIBARIB25 - fprintf(stderr, "--b25: Decrypt using BCAS card\n"); - fprintf(stderr, " --round N: Specify round number\n"); - fprintf(stderr, " --strip: Strip null stream\n"); - fprintf(stderr, " --EMM: Instruct EMM operation\n"); -#endif - fprintf(stderr, "--udp: Turn on udp broadcasting\n"); - fprintf(stderr, " --addr hostname: Hostname or address to connect\n"); - fprintf(stderr, " --port portnumber: Port number to connect\n"); - fprintf(stderr, "--device devicefile: Specify devicefile to use\n"); - fprintf(stderr, "--lnb voltage: Specify LNB voltage (0, 11, 15)\n"); - fprintf(stderr, "--sid SID1,SID2,...: Specify SID number in CSV format (101,102,...)\n"); - fprintf(stderr, " --es filename: Specify ES out filename prefix\n"); - fprintf(stderr, " --start_time YYYYMMDDHHMISS: Specify record start datetime\n"); - fprintf(stderr, "--help: Show this help\n"); - fprintf(stderr, "--version: Show version\n"); - fprintf(stderr, "--list: Show channel list\n"); -} - -void -show_channels(void) -{ - FILE *f; - char *home; - char buf[255], filename[255]; - - fprintf(stderr, "Available Channels:\n"); - - home = getenv("HOME"); - sprintf(filename, "%s/.recpt1-channels", home); - f = fopen(filename, "r"); - if(f) { - while(fgets(buf, 255, f)) - fprintf(stderr, "%s", buf); - fclose(f); - } - else - fprintf(stderr, "13-62: Terrestrial Channels\n"); - - fprintf(stderr, "101ch: NHK BS1\n"); - fprintf(stderr, "102ch: NHK BS2\n"); - fprintf(stderr, "103ch: NHK BShi\n"); - fprintf(stderr, "141ch: BS Nittele\n"); - fprintf(stderr, "151ch: BS Asahi\n"); - fprintf(stderr, "161ch: BS-TBS\n"); - fprintf(stderr, "171ch: BS Japan\n"); - fprintf(stderr, "181ch: BS Fuji\n"); - fprintf(stderr, "191ch: WOWOW\n"); - fprintf(stderr, "192ch: WOWOW2\n"); - fprintf(stderr, "193ch: WOWOW3\n"); - fprintf(stderr, "200ch: Star Channel\n"); - fprintf(stderr, "211ch: BS11 Digital\n"); - fprintf(stderr, "222ch: TwellV\n"); - fprintf(stderr, "C13-C63: CATV Channels\n"); - fprintf(stderr, "CS2-CS24: CS Channels\n"); -} - -float -getsignal_isdb_s(int signal) -{ - /* apply linear interpolation */ - static const float afLevelTable[] = { - 24.07f, // 00 00 0 24.07dB - 24.07f, // 10 00 4096 24.07dB - 18.61f, // 20 00 8192 18.61dB - 15.21f, // 30 00 12288 15.21dB - 12.50f, // 40 00 16384 12.50dB - 10.19f, // 50 00 20480 10.19dB - 8.140f, // 60 00 24576 8.140dB - 6.270f, // 70 00 28672 6.270dB - 4.550f, // 80 00 32768 4.550dB - 3.730f, // 88 00 34816 3.730dB - 3.630f, // 88 FF 35071 3.630dB - 2.940f, // 90 00 36864 2.940dB - 1.420f, // A0 00 40960 1.420dB - 0.000f // B0 00 45056 -0.01dB - }; - - unsigned char sigbuf[4]; - memset(sigbuf, '\0', sizeof(sigbuf)); - sigbuf[0] = (((signal & 0xFF00) >> 8) & 0XFF); - sigbuf[1] = (signal & 0xFF); - - /* calculate signal level */ - if(sigbuf[0] <= 0x10U) { - /* clipped maximum */ - return 24.07f; - } - else if (sigbuf[0] >= 0xB0U) { - /* clipped minimum */ - return 0.0f; - } - else { - /* linear interpolation */ - const float fMixRate = - (float)(((unsigned short)(sigbuf[0] & 0x0FU) << 8) | - (unsigned short)sigbuf[0]) / 4096.0f; - return afLevelTable[sigbuf[0] >> 4] * (1.0f - fMixRate) + - afLevelTable[(sigbuf[0] >> 4) + 0x01U] * fMixRate; - } -} - -void -calc_cn(int fd, int type) -{ - int rc ; - double P ; - double CNR; - - if(ioctl(fd, GET_SIGNAL_STRENGTH, &rc) < 0) { - fprintf(stderr, "Tuner Select Error\n"); - return ; - } - - if(type == CHTYPE_GROUND) { - P = log10(5505024/(double)rc) * 10; - CNR = (0.000024 * P * P * P * P) - (0.0016 * P * P * P) + - (0.0398 * P * P) + (0.5491 * P)+3.0965; - fprintf(stderr, "C/N = %fdB\n", CNR); - } - else { - CNR = getsignal_isdb_s(rc); - fprintf(stderr, "C/N = %fdB\n", CNR); - } -} - -void -cleanup(thread_data *tdata) -{ - /* stop recording */ - ioctl(tdata->tfd, STOP_REC, 0); - - /* xxx need mutex? */ - f_exit = TRUE; - - pthread_cond_signal(&tdata->queue->cond_avail); - pthread_cond_signal(&tdata->queue->cond_used); -} - -/* will be signal handler thread */ -void * -process_signals(void *data) -{ - sigset_t waitset; - int sig; - thread_data *tdata = (thread_data *)data; - - sigemptyset(&waitset); - sigaddset(&waitset, SIGPIPE); - sigaddset(&waitset, SIGINT); - sigaddset(&waitset, SIGTERM); - sigaddset(&waitset, SIGUSR1); - sigaddset(&waitset, SIGUSR2); - - sigwait(&waitset, &sig); - - switch(sig) { - case SIGPIPE: - fprintf(stderr, "\nSIGPIPE received. cleaning up...\n"); - cleanup(tdata); - break; - case SIGINT: - fprintf(stderr, "\nSIGINT received. cleaning up...\n"); - cleanup(tdata); - break; - case SIGTERM: - fprintf(stderr, "\nSIGTERM received. cleaning up...\n"); - cleanup(tdata); - break; - case SIGUSR1: /* normal exit*/ - cleanup(tdata); - break; - case SIGUSR2: /* error */ - fprintf(stderr, "Detected an error. cleaning up...\n"); - cleanup(tdata); - break; - } - - return NULL; /* dummy */ -} - -void -init_signal_handlers(pthread_t *signal_thread, thread_data *tdata) -{ - sigset_t blockset; - - sigemptyset(&blockset); - sigaddset(&blockset, SIGPIPE); - sigaddset(&blockset, SIGINT); - sigaddset(&blockset, SIGTERM); - sigaddset(&blockset, SIGUSR1); - sigaddset(&blockset, SIGUSR2); - - if(pthread_sigmask(SIG_BLOCK, &blockset, NULL)) - fprintf(stderr, "pthread_sigmask() failed.\n"); - - pthread_create(signal_thread, NULL, process_signals, tdata); -} - -int -tune(char *channel, thread_data *tdata, char *device) -{ - char **tuner; - int num_devs; - int lp; - FREQUENCY freq; - - /* get channel */ - tdata->table = searchrecoff(channel); - if(tdata->table == NULL) { - fprintf(stderr, "Invalid Channel: %s\n", channel); - return 1; - } - - freq.frequencyno = tdata->table->set_freq; - freq.slot = tdata->table->add_freq; - - /* open tuner */ - /* case 1: specified tuner device */ - if(device) { - tdata->tfd = open(device, O_RDONLY); - if(tdata->tfd < 0) { - fprintf(stderr, "Cannot open tuner device: %s\n", device); - return 1; - } - - /* power on LNB */ - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { - fprintf(stderr, "Power on LNB failed: %s\n", device); - } - } - - /* tune to specified channel */ - if(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { - close(tdata->tfd); - fprintf(stderr, "Cannot tune to the specified channel: %s\n", device); - return 1; - } - else { - tdata->ch = atoi(channel); - } - } - else { - /* case 2: loop around available devices */ - if(tdata->table->type == CHTYPE_SATELLITE) { - tuner = bsdev; - num_devs = NUM_BSDEV; - } - else { - tuner = isdb_t_dev; - num_devs = NUM_ISDB_T_DEV; - } - - for(lp = 0; lp < num_devs; lp++) { - tdata->tfd = open(tuner[lp], O_RDONLY); - if(tdata->tfd >= 0) { - /* power on LNB */ - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { - fprintf(stderr, "Warning: Power on LNB failed: %s\n", tuner[lp]); - } - } - - /* tune to specified channel */ - if(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { - close(tdata->tfd); - tdata->tfd = -1; - continue; - } - - break; /* found suitable tuner */ - } - } - - /* all tuners cannot be used */ - if(tdata->tfd < 0) { - fprintf(stderr, "Cannot tune to the specified channel\n"); - return 1; - } - else { - tdata->ch = atoi(channel); - } - } - - /* show signal strength */ - calc_cn(tdata->tfd, tdata->table->type); - - return 0; /* success */ -} - -int -parse_time(char *rectimestr, thread_data *tdata) -{ - /* indefinite */ - if(!strcmp("-", rectimestr)) { - tdata->indefinite = TRUE; - tdata->recsec = -1; - } - /* colon */ - else if(strchr(rectimestr, ':')) { - int n1, n2, n3; - if(sscanf(rectimestr, "%d:%d:%d", &n1, &n2, &n3) == 3) - tdata->recsec = n1 * 3600 + n2 * 60 + n3; - else if(sscanf(rectimestr, "%d:%d", &n1, &n2) == 2) - tdata->recsec = n1 * 3600 + n2 * 60; - } - /* HMS */ - else { - char *tmpstr; - char *p1, *p2; - - tmpstr = strdup(rectimestr); - p1 = tmpstr; - while(*p1 && !isdigit(*p1)) - p1++; - - /* hour */ - if((p2 = strchr(p1, 'H')) || (p2 = strchr(p1, 'h'))) { - *p2 = '\0'; - tdata->recsec += atoi(p1) * 3600; - p1 = p2 + 1; - while(*p1 && !isdigit(*p1)) - p1++; - } - - /* minute */ - if((p2 = strchr(p1, 'M')) || (p2 = strchr(p1, 'm'))) { - *p2 = '\0'; - tdata->recsec += atoi(p1) * 60; - p1 = p2 + 1; - while(*p1 && !isdigit(*p1)) - p1++; - } - - /* second */ - tdata->recsec += atoi(p1); - - free(tmpstr); - } - - return 0; /* success */ -} - -int -close_tuner(thread_data *tdata) -{ - int rv = 0; - - if(tdata->table->type == CHTYPE_SATELLITE) { - if(ioctl(tdata->tfd, LNB_DISABLE, 0) < 0) { - rv = 1; - } - } - close(tdata->tfd); - - return rv; -} - -int -main(int argc, char **argv) -{ - time_t cur_time; - pthread_t signal_thread; - pthread_t reader_thread; - pthread_t ipc_thread; - QUEUE_T *p_queue = create_queue(MAX_QUEUE); - BUFSZ *bufptr; - decoder *dec = NULL; - splitter *splitter = NULL; - static thread_data tdata; - decoder_options dopt = { - 4, /* round */ - 0, /* strip */ - 0 /* emm */ - }; - tdata.dopt = &dopt; - tdata.lnb = 0; - - int result; - int option_index; - struct option long_options[] = { -#ifdef HAVE_LIBARIB25 - { "b25", 0, NULL, 'b'}, - { "B25", 0, NULL, 'b'}, - { "round", 1, NULL, 'r'}, - { "strip", 0, NULL, 's'}, - { "emm", 0, NULL, 'm'}, - { "EMM", 0, NULL, 'm'}, -#endif - { "LNB", 1, NULL, 'n'}, - { "lnb", 1, NULL, 'n'}, - { "udp", 0, NULL, 'u'}, - { "addr", 1, NULL, 'a'}, - { "port", 1, NULL, 'p'}, - { "device", 1, NULL, 'd'}, - { "help", 0, NULL, 'h'}, - { "version", 0, NULL, 'v'}, - { "list", 0, NULL, 'l'}, - { "sid", 1, NULL, 'i'}, - { "SID", 1, NULL, 'i'}, - { "es", 1, NULL, 'e'}, - { "ES", 1, NULL, 'e'}, - { "start_time", 1, NULL, 'y'}, - {0, 0, NULL, 0} /* terminate */ - }; - - boolean use_b25 = FALSE; - boolean use_udp = FALSE; - boolean fileless = FALSE; - boolean use_stdout = FALSE; - boolean use_splitter = FALSE; - char *host_to = NULL; - int port_to = 1234; - sock_data *sockdata = NULL; - char *device = NULL; - int val; - char *voltage[] = {"0V", "11V", "15V"}; - char *sid_list = NULL; - char *es_name_prefix = NULL; - char *start_time = NULL; - - while((result = getopt_long(argc, argv, "br:smn:ua:p:d:hvli:", - long_options, &option_index)) != -1) { - switch(result) { - case 'b': - use_b25 = TRUE; - fprintf(stderr, "using B25...\n"); - break; - case 's': - dopt.strip = TRUE; - fprintf(stderr, "enable B25 strip\n"); - break; - case 'm': - dopt.emm = TRUE; - fprintf(stderr, "enable B25 emm processing\n"); - break; - case 'u': - use_udp = TRUE; - host_to = "localhost"; - fprintf(stderr, "enable UDP broadcasting\n"); - break; - case 'h': - fprintf(stderr, "\n"); - show_usage(argv[0]); - fprintf(stderr, "\n"); - show_options(); - fprintf(stderr, "\n"); - show_channels(); - fprintf(stderr, "\n"); - exit(0); - break; - case 'v': - fprintf(stderr, "%s %s\n", argv[0], version); - fprintf(stderr, "recorder command for PT1/2 digital tuner.\n"); - exit(0); - break; - case 'l': - show_channels(); - exit(0); - break; - /* following options require argument */ - case 'n': - val = atoi(optarg); - switch(val) { - case 11: - tdata.lnb = 1; - break; - case 15: - tdata.lnb = 2; - break; - default: - tdata.lnb = 0; - break; - } - fprintf(stderr, "LNB = %s\n", voltage[tdata.lnb]); - break; - case 'r': - dopt.round = atoi(optarg); - fprintf(stderr, "set round %d\n", dopt.round); - break; - case 'a': - use_udp = TRUE; - host_to = optarg; - fprintf(stderr, "UDP destination address: %s\n", host_to); - break; - case 'p': - port_to = atoi(optarg); - fprintf(stderr, "UDP port: %d\n", port_to); - break; - case 'd': - device = optarg; - fprintf(stderr, "using device: %s\n", device); - break; - case 'i': - use_splitter = TRUE; - sid_list = optarg; - break; - case 'e': - es_name_prefix = optarg; - break; - case 'y': - start_time = optarg; - break; - } - } - - if(argc - optind < 3) { - if(argc - optind == 2 && use_udp) { - fprintf(stderr, "Fileless UDP broadcasting\n"); - fileless = TRUE; - tdata.wfd = -1; - } - else { - fprintf(stderr, "Arguments are necessary!\n"); - fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); - return 1; - } - } - - fprintf(stderr, "pid = %d\n", getpid()); - - /* tune */ - if(tune(argv[optind], &tdata, device) != 0) - return 1; - - /* set recsec */ - if(parse_time(argv[optind + 1], &tdata) != 0) - return 1; - - /* open output file */ - char *destfile = argv[optind + 2]; - if(destfile && !strcmp("-", destfile)) { - use_stdout = TRUE; - tdata.wfd = 1; /* stdout */ - } - else { - if(!fileless) { - int status; - char *path = strdup(argv[optind + 2]); - char *dir = dirname(path); - status = mkpath(dir, 0777); - if(status == -1) - perror("mkpath"); - free(path); - - tdata.wfd = open(argv[optind + 2], (O_RDWR | O_CREAT | O_TRUNC), 0666); - if(tdata.wfd < 0) { - fprintf(stderr, "Cannot open output file: %s\n", - argv[optind + 2]); - return 1; - } - } - } - - /* initialize decoder */ - if(use_b25) { - dec = b25_startup(&dopt); - if(!dec) { - fprintf(stderr, "Cannot start b25 decoder\n"); - fprintf(stderr, "Fall back to encrypted recording\n"); - use_b25 = FALSE; - } - } - /* initialize splitter */ - if(use_splitter) { - splitter = split_startup(sid_list, es_name_prefix, start_time); - if(splitter->sid_list == NULL) { - fprintf(stderr, "Cannot start TS splitter\n"); - return 1; - } - } - - /* initialize udp connection */ - if(use_udp) { - sockdata = calloc(1, sizeof(sock_data)); - struct in_addr ia; - ia.s_addr = inet_addr(host_to); - if(ia.s_addr == INADDR_NONE) { - struct hostent *hoste = gethostbyname(host_to); - if(!hoste) { - perror("gethostbyname"); - return 1; - } - ia.s_addr = *(in_addr_t*) (hoste->h_addr_list[0]); - } - if((sockdata->sfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { - perror("socket"); - return 1; - } - - sockdata->addr.sin_family = AF_INET; - sockdata->addr.sin_port = htons (port_to); - sockdata->addr.sin_addr.s_addr = ia.s_addr; - - if(connect(sockdata->sfd, (struct sockaddr *)&sockdata->addr, - sizeof(sockdata->addr)) < 0) { - perror("connect"); - return 1; - } - } - - /* prepare thread data */ - tdata.queue = p_queue; - tdata.decoder = dec; - tdata.splitter = splitter; - tdata.sock_data = sockdata; - - /* spawn signal handler thread */ - init_signal_handlers(&signal_thread, &tdata); - - /* spawn reader thread */ - tdata.signal_thread = signal_thread; - pthread_create(&reader_thread, NULL, reader_func, &tdata); - - /* spawn ipc thread */ - key_t key; - key = (key_t)getpid(); - - if ((tdata.msqid = msgget(key, IPC_CREAT | 0666)) < 0) { - perror("msgget"); - } - pthread_create(&ipc_thread, NULL, mq_recv, &tdata); - - /* start recording */ - if(ioctl(tdata.tfd, START_REC, 0) < 0) { - fprintf(stderr, "Tuner cannot start recording\n"); - return 1; - } - - fprintf(stderr, "Recording...\n"); - - time(&tdata.start_time); - - /* read from tuner */ - while(1) { - if(f_exit) - break; - - time(&cur_time); - bufptr = malloc(sizeof(BUFSZ)); - if(!bufptr) { - f_exit = TRUE; - break; - } - bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE); - if(bufptr->size <= 0) { - if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) { - f_exit = TRUE; - enqueue(p_queue, NULL); - break; - } - else { - continue; - } - } - enqueue(p_queue, bufptr); - - /* stop recording */ - time(&cur_time); - if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) { - ioctl(tdata.tfd, STOP_REC, 0); - /* read remaining data */ - while(1) { - bufptr = malloc(sizeof(BUFSZ)); - if(!bufptr) { - f_exit = TRUE; - break; - } - bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE); - if(bufptr->size <= 0) { - f_exit = TRUE; - enqueue(p_queue, NULL); - break; - } - enqueue(p_queue, bufptr); - } - break; - } - } - - /* delete message queue*/ - msgctl(tdata.msqid, IPC_RMID, NULL); - - pthread_kill(signal_thread, SIGUSR1); - - /* wait for threads */ - pthread_join(reader_thread, NULL); - pthread_join(signal_thread, NULL); - pthread_join(ipc_thread, NULL); - - /* close tuner */ - if(close_tuner(&tdata) != 0) - return 1; - - /* release queue */ - destroy_queue(p_queue); - - /* close output file */ - if(!use_stdout) - close(tdata.wfd); - - /* free socket data */ - if(use_udp) { - close(sockdata->sfd); - free(sockdata); - } - - /* release decoder */ - if(use_b25) { - b25_shutdown(dec); - } - if(use_splitter) { - split_shutdown(splitter); - } - - return 0; -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/recpt1.h --- a/recpt1/recpt1.h Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ -#ifndef _RECPT1_H_ -#define _RECPT1_H_ - -#define NUM_BSDEV 8 -#define NUM_ISDB_T_DEV 8 -#define CHTYPE_SATELLITE 0 /* satellite digital */ -#define CHTYPE_GROUND 1 /* terrestrial digital */ -#define MAX_QUEUE 8192 -#define MAX_READ_SIZE (188 * 87) /* 188*87=16356 splitterが188アライメントを期待しているのでこの数字とする*/ -#define WRITE_SIZE (1024 * 1024 * 2) -#define TRUE 1 -#define FALSE 0 - -typedef struct _BUFSZ { - int size; - u_char buffer[MAX_READ_SIZE]; -} BUFSZ; - -typedef struct _QUEUE_T { - unsigned int in; // 次に入れるインデックス - unsigned int out; // 次に出すインデックス - unsigned int size; // キューのサイズ - unsigned int num_avail; // 満タンになると 0 になる - unsigned int num_used; // 空っぽになると 0 になる - pthread_mutex_t mutex; - pthread_cond_t cond_avail; // データが満タンのときに待つための cond - pthread_cond_t cond_used; // データが空のときに待つための cond - BUFSZ *buffer[1]; // バッファポインタ -} QUEUE_T; - -typedef struct _ISDB_T_FREQ_CONV_TABLE { - int set_freq; // 実際にioctl()を行う値 - int type; // チャンネルタイプ - int add_freq; // 追加する周波数(BS/CSの場合はスロット番号) - char *parm_freq; // パラメータで受ける値 -} ISDB_T_FREQ_CONV_TABLE; - -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/recpt1ctl.c --- a/recpt1/recpt1ctl.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,214 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "version.h" - -#define MSGSZ 255 - -typedef struct msgbuf { - long mtype; - char mtext[MSGSZ]; -} message_buf; - -void -show_usage(char *cmd) -{ - fprintf(stderr, "Usage: \n%s --pid pid [--channel channel] [--extend time_to_extend] [--time recording_time]\n", cmd); - fprintf(stderr, "\n"); -} - -void -show_options(void) -{ - fprintf(stderr, "Options:\n"); - fprintf(stderr, "--pid: Process id of recpt1 to control\n"); - fprintf(stderr, "--channel: Tune to specified channel\n"); - fprintf(stderr, "--extend: Extend recording time\n"); - fprintf(stderr, "--time: Set total recording time\n"); - fprintf(stderr, "--help: Show this help\n"); - fprintf(stderr, "--version: Show version\n"); - fprintf(stderr, "--list: Show channel list\n"); -} - -void -show_channels(void) -{ - FILE *f; - char *home; - char buf[255], filename[255]; - - fprintf(stderr, "Available Channels:\n"); - - home = getenv("HOME"); - sprintf(filename, "%s/.recpt1-channels", home); - f = fopen(filename, "r"); - if(f) { - while(fgets(buf, 255, f)) - fprintf(stderr, "%s", buf); - fclose(f); - } - else - fprintf(stderr, "13-62: Terrestrial Channels\n"); - - fprintf(stderr, "101ch: NHK BS1\n"); - fprintf(stderr, "102ch: NHK BS2\n"); - fprintf(stderr, "103ch: NHK BShi\n"); - fprintf(stderr, "141ch: BS Nittele\n"); - fprintf(stderr, "151ch: BS Asahi\n"); - fprintf(stderr, "161ch: BS-TBS\n"); - fprintf(stderr, "171ch: BS Japan\n"); - fprintf(stderr, "181ch: BS Fuji\n"); - fprintf(stderr, "191ch: WOWOW\n"); - fprintf(stderr, "200ch: Star Channel\n"); - fprintf(stderr, "211ch: BS11 Digital\n"); - fprintf(stderr, "222ch: TwellV\n"); - fprintf(stderr, "CS2-CS24: CS Channels\n"); -} - -int -parse_time(char *rectimestr, int *recsec) -{ - /* indefinite */ - if(!strcmp("-", rectimestr)) { - *recsec = -1; - } - /* colon */ - else if(strchr(rectimestr, ':')) { - int n1, n2, n3; - if(sscanf(rectimestr, "%d:%d:%d", &n1, &n2, &n3) == 3) - *recsec = n1 * 3600 + n2 * 60 + n3; - else if(sscanf(rectimestr, "%d:%d", &n1, &n2) == 2) - *recsec = n1 * 3600 + n2 * 60; - } - /* HMS */ - else { - char *tmpstr; - char *p1, *p2; - - tmpstr = strdup(rectimestr); - p1 = tmpstr; - while(*p1 && !isdigit(*p1)) - p1++; - - /* hour */ - if((p2 = strchr(p1, 'H')) || (p2 = strchr(p1, 'h'))) { - *p2 = '\0'; - *recsec += atoi(p1) * 3600; - p1 = p2 + 1; - while(*p1 && !isdigit(*p1)) - p1++; - } - - /* minute */ - if((p2 = strchr(p1, 'M')) || (p2 = strchr(p1, 'm'))) { - *p2 = '\0'; - *recsec += atoi(p1) * 60; - p1 = p2 + 1; - while(*p1 && !isdigit(*p1)) - p1++; - } - - /* second */ - *recsec += atoi(p1); - - free(tmpstr); - } - - return 0; /* success */ -} - -int -main(int argc, char **argv) -{ - int msqid; - int msgflg = IPC_CREAT | 0666; - key_t key = 0; - int channel=0, recsec = 0, extsec=0; - message_buf sbuf; - size_t buf_length; - - int result; - int option_index; - struct option long_options[] = { - { "pid", 1, NULL, 'p'}, - { "channel", 1, NULL, 'c'}, - { "extend", 1, NULL, 'e'}, - { "time", 1, NULL, 't'}, - { "help", 0, NULL, 'h'}, - { "version", 0, NULL, 'v'}, - { "list", 0, NULL, 'l'}, - {0, 0, NULL, 0} /* terminate */ - }; - - while((result = getopt_long(argc, argv, "p:c:e:t:hvl", - long_options, &option_index)) != -1) { - switch(result) { - case 'h': - fprintf(stderr, "\n"); - show_usage(argv[0]); - fprintf(stderr, "\n"); - show_options(); - fprintf(stderr, "\n"); - show_channels(); - fprintf(stderr, "\n"); - exit(0); - break; - case 'v': - fprintf(stderr, "%s %s\n", argv[0], version); - fprintf(stderr, "control command for recpt1.\n"); - exit(0); - break; - case 'l': - show_channels(); - exit(0); - break; - /* following options require argument */ - case 'p': - key = (key_t)atoi(optarg); - fprintf(stderr, "Pid = %d\n", key); - break; - case 'c': - channel = atoi(optarg); - fprintf(stderr, "Channel = %d\n", channel); - break; - case 'e': - parse_time(optarg, &extsec); - fprintf(stderr, "Extend %d sec\n", extsec); - break; - case 't': - parse_time(optarg, &recsec); - fprintf(stderr, "Total recording time = %d sec\n", recsec); - break; - } - } - - if(!key) { - fprintf(stderr, "Arguments are necessary!\n"); - fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); - exit(1); - } - - if ((msqid = msgget(key, msgflg )) < 0) { - perror("msgget"); - exit(1); - } - - sbuf.mtype = 1; - sprintf(sbuf.mtext, "ch=%d t=%d e=%d", channel, recsec, extsec); - - buf_length = strlen(sbuf.mtext) + 1 ; - - if (msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT) < 0) { - perror("msgsnd"); - exit(1); - } - - exit(0); -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/tssplitter_lite.c --- a/recpt1/tssplitter_lite.c Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2133 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: t -*- */ -/* vim: set ts=4 sts=4 sw=4 noexpandtab number : */ -/* tssplitter_lite.c -- split TS stream. - - Copyright 2009 querulous - Copyright 2010 Naoya OYAMA - - 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 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include "decoder.h" -#include "recpt1.h" -#include "tssplitter_lite.h" - -#ifndef AV_RB32 -#define AV_RB32(x) ((((const uint8_t*)(x))[0] << 24) | \ - (((const uint8_t*)(x))[1] << 16) | \ - (((const uint8_t*)(x))[2] << 8) | \ - ((const uint8_t*)(x))[3]) -#endif - -#ifndef AV_RB24 -#define AV_RB24(x) ((((const uint8_t*)(x))[0] << 16) | \ - (((const uint8_t*)(x))[1] << 8) | \ - ((const uint8_t*)(x))[2]) -#endif - -#ifndef AV_RB16 -#define AV_RB16(x) ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1]) -#endif -#define MAX_SERVICE_ID ( 0xffff ) -#define LIST_DECIMAL "0123456789" -#define TSS_STREAM_TYPE_AUDIO (1) -#define TSS_STREAM_TYPE_VIDEO (2) - -/* prototypes */ -static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); -static int AnalyzePat(splitter *sp, unsigned char *buf); -static int RecreatePat(splitter *sp, unsigned char *buf, int *pos); -static char** AnalyzeSid(char *sid); -static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size); -static int GetCrc32(unsigned char *data, int len); -static int GetPid(unsigned char *data); -static int parse_tot( const unsigned char* packet, time_t *t ); -//void dump_packet( const uint8_t *packet ); -static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet); -static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid); -//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator); -static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid); -static int64_t get_pts(const uint8_t *p); -//void search_mpeg_system_header(const uint8_t *p); -static int esbuf_write(splitesbuf_t *esbuf); -static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf); -static int pesbuf_empty(splitpesbuf_t *pesbuf); -void pesbuf_clear(splitpesbuf_t *pesbuf); -static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len); -static int esbuf_empty(splitesbuf_t *esbuf); -void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts); -static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len); -static int get_pmt_version(const uint8_t *p); -static int next_adts_start_code(splitesbuf_t *esbuf, int offset); -static int is_video_stream(const int pid, splitesbuf_t *esbuf); -static int is_audio_stream(const int pid, splitesbuf_t *esbuf); -static int AnalyzeAdifHeader(splitesbuf_t *esbuf); -static int get_adif_id(uint8_t *p); -static int get_adif_layer(uint8_t *p); -static int get_adif_protection_absent(uint8_t *p); -static int get_adif_profile(uint8_t *p); -static int get_adif_sampling_frequency_index(uint8_t *p); -static int get_adif_private_bit(uint8_t *p); -static int get_adif_channel_configuration(uint8_t *p); -static int get_adif_original_copy(uint8_t *p); -static int get_adif_home(uint8_t *p); -static int get_adif_copyright_idication_bit(uint8_t *p); -static int get_adif_copyright_idication_start(uint8_t *p); -static int get_adif_aac_frame_length(uint8_t *p); -static int get_adts_buffer_fullness(uint8_t *p); -static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p); -//static int search_pmt_program(splitter *sp, int pid); -static int search_gop_start_code(splitesbuf_t *esbuf); -static int creat_es_file(splitter *sp, int sid, int pid, int av_flag); -static time_t cue2time(char *yyyymmddhhmiss); -static int search_pcr_pid(splitter *sp, int pid); - -/** - * サービスID解析 - */ -static char** AnalyzeSid( - char* sid) // [in] サービスID(カンマ区切りテキスト) -{ - int i = 0; - char** sid_list = NULL; - char* p; - int CommaNum = 0; - - /* sid は次の形式の引数を許容する */ - /* 指定無し */ - /* SID[0] */ - /* SID[0],SID[1],...,SID[N-1],SID[N] */ - - /*カンマの数を数える*/ - p = sid; - while(*p != '\0') - { - if( *p == C_CHAR_COMMA ){ - CommaNum++; - } - p++; - } - - /* sid_listの数はカンマの数+2(NULL止めするから) */ - sid_list = malloc(sizeof(char*)*(CommaNum+2)); - if ( sid_list == NULL ) - { - fprintf(stderr, "AnalyzeSid() malloc error.\n"); - return NULL; - } - - /* sidが空である場合 */ - p = sid; - if ( strlen(p) == 0 ) - { - sid_list[0] = NULL; - return sid_list; - } - - /* カンマ無し */ - if ( CommaNum == 0 ) - { - sid_list[0] = sid; - sid_list[1] = NULL; - return sid_list; - } - - /* カンマ区切りで複数指定時 */ - i=0; - p = sid; - /* 文字列端に到達するか、カンマ数が数えた数に達したら終了 */ - while((*p != '\0') || i < CommaNum) - { - /* 現在の処理位置をsid_list[i]にセット */ - /* このタイミングの p は - * ・sid先頭 - * ・[,]の次の文字 - * いずれかであるので p を sid_list[i] に代入してよい - */ - sid_list[i] = p; - i++; - - /* 最初に現れる[,]をNULL文字で置換する */ - p = strchr(p, C_CHAR_COMMA); - if ( p == NULL ) - { - /* カンマが見つからない場合は最後の処理対象なので終了 */ - break; - } - *p = '\0'; - /* 処理位置をNULLで置換した文字の次の位置に設定する */ - p++; - } - - /* 最後のsid_list[n]はNULLポインタで止める */ - sid_list[i] = NULL; - - i=0; - while( sid_list[i] != NULL ) - { - i++; - } -#if 0 - for(i=0; sid_list[i] != NULL; i++) - { - printf("sid_list[%d]=[%s].\n",i, sid_list[i]); - } -#endif - return sid_list; -} - -/** - * 初期化処理 - */ -splitter* split_startup( - char *sid, // [in] サービスID(引数で指定した文字列) - char *filename, // [in] 出力ESファイル名(引数で指定したファイル名) - char *arg_cue // [in] 録画開始時刻(引数で指定した文字列 YYYYMMDDHHMISS) -) -{ - splitter* sp; - int i; - sp = malloc(sizeof(splitter)); - if ( sp == NULL ) - { - fprintf(stderr, "split_startup malloc error.\n"); - return NULL; - } - sp->program = malloc( sizeof(program_t) * MAX_SERVICE_ID ); - if ( sp->program == NULL ) - { - fprintf(stderr, "split_startup malloc error.\n"); - return NULL; - } - memset(sp->pids, 0, sizeof(sp->pids)); - memset(sp->pmt_pids, 0, sizeof(sp->pmt_pids)); - memset(sp->cat_pids, 0, sizeof(sp->cat_pids)); - memset(sp->pcr_pids, 0, sizeof(sp->pcr_pids)); - memset(sp->pcr, 0, sizeof(sp->pcr)); - - sp->sid_list = NULL; - sp->pat = NULL; - sp->sid_list = AnalyzeSid(sid); - if ( sp->sid_list == NULL ) - { - free(sp); - return NULL; - } - sp->pat_count = 0xFF; - sp->pmt_retain = -1; - sp->pmt_counter = 0; - sp->time_cue = 0; - sp->time_tot = 0; - sp->pcr_nb = 0; - memset(sp->esbuf, 0, sizeof(splitesbuf_t *)*MAX_PID); - memset(sp->pesbuf, 0, sizeof(splitpesbuf_t *)*MAX_PID); - memset(sp->program, 0, sizeof(program_t *)*MAX_SERVICE_ID); - for ( i=0; i < MAX_PID; i++ ) { - /* pmt_version は (N%32) の値を取るので、0 で初期化してはならない */ - sp->program[i].pmt_version = -1; - /* cue は最大値で初期化(CUE <= STCとなると録画開始するため) */ - sp->program[i].cue = INT64_MAX; - } - memset(sp->pid_sid_table, 0, sizeof(int)*MAX_PID); - if ( filename != NULL ) { - sp->esout = 1; - sp->filename = filename; - } - if ( arg_cue != NULL ) { - sp->arg_cue = arg_cue; - } else { - sp->arg_cue = "00000000000000"; /* とりあえず最小値 */ - } - return sp; -} - -/** - * 落とすPIDを確定させる - */ -int split_select( - splitter *sp, // [in/out] splitter構造体 - ARIB_STD_B25_BUFFER *sbuf // [in] 入力TS -) -{ - int result; - // TS解析 - result = ReadTs(sp, sbuf); - - return result; -} - -/** - * 終了処理 - */ -void split_shutdown(splitter* sp) -{ - int i = 0; - if ( sp != NULL ) { - if ( sp->pat != NULL ) - { - free(sp->pat); - sp->pat = NULL; - } - if ( sp->sid_list != NULL ) - { - free(sp->sid_list); - sp->sid_list = NULL; - } - for(i=0; i < MAX_PID; i++) { - if ( sp->esbuf[i] != NULL ) { - if ( sp->esbuf[i]->fd != -1 ) { - close(sp->esbuf[i]->fd); - sp->esbuf[i]->fd = -1; - } - free(sp->esbuf[i]); - sp->esbuf[i] = NULL; - } - if ( sp->pesbuf[i] != NULL ) { - free(sp->pesbuf[i]); - sp->pesbuf[i] = NULL; - } - } - free(sp); - sp = NULL; - } -} - -/** - * TS 解析処理 - * - * 対象のチャンネル番号のみの PAT の再構築と出力対象 PID の抽出を行う - */ -static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf) -{ -#if 0 - splitter *sp, // [in/out] splitter構造体 - ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS -#endif - - int length = sbuf->size; - int pid; - int result = TSS_ERROR; - int index; - - index = 0; - while(length - index - LENGTH_PACKET > 0) { - pid = GetPid(sbuf->data + index + 1); - // PAT - if(PAT == pid) { - result = AnalyzePat(sp, sbuf->data + index); - if(TSS_SUCCESS != result) { - /* 下位の関数内部でmalloc error発生 */ - return result; - } - } - - // PMT - /* 残すpmt_pidである場合には、pmtに書かれている - * 残すべきPCR/AUDIO/VIDEO PIDを取得する */ - if(sp->pmt_pids[pid] == 1) { - /* この中にはPMT毎に一度しか入らないようにしておく */ - sp->pmt_pids[pid]++; - sp->pmt_counter += 1; - DemuxTs(sbuf->data +index, sp, pid); /* AnalyzePmt より DemuxTs の方がアダプテーションフィールドの処理が良いので変更 */ - } - /* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを得る */ - /* pmt_counter と pmt_retain が一致する場合に条件は満たされる */ - if(sp->pmt_counter == sp->pmt_retain) { - result = TSS_SUCCESS; - break; - } - else { - result = TSS_ERROR; - } - index += LENGTH_PACKET; - } - - return(result); -} - -/** - * TS 分離処理 - */ -int split_ts( - splitter *sp, // [in] splitterパラメータ - ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS - splitbuf_t *dbuf // [out] 出力TS -) -{ - int pid; - unsigned char *sptr, *dptr; - int s_offset = 0; - int d_offset = 0; - int64_t pcr_h = 0; - int pcr_l = 0; - int ret = 0; - int sid = 0; - program_t *program; - static int packet_nb; /* パケット受信数 */ - int i = 0; - - /* 初期化 */ - dbuf->size = 0; - if (sbuf->size < 0) { - return TSS_ERROR; - } - - sptr = sbuf->data; - dptr = dbuf->buffer; - - while(sbuf->size > s_offset) { - pid = GetPid(sptr + s_offset + 1); - sid = sp->pid_sid_table[pid]; /* PIDからSIDを取得 */ - switch(pid) { - - // PAT - case PAT: - // 巡回カウンタカウントアップ - if(0xFF == sp->pat_count) { - sp->pat_count = sp->pat[3]; - } - else { - sp->pat_count += 1; - if(0 == sp->pat_count % 0x10) { - sp->pat_count -= 0x10; - } - } - sp->pat[3] = sp->pat_count; - - memcpy(dptr + d_offset, sp->pat, LENGTH_PACKET); - d_offset += LENGTH_PACKET; - dbuf->size += LENGTH_PACKET; - break; - case TOT: - /* TOT に TDTの情報全てが含まれており、実放送では TOT しか送信されない */ - /* TOT は 500msec の誤差が保証されている - * 閏秒の場合は最大1.5秒の誤差となる - */ - if ( sp->time_tot == 0 ) { - /* splitter構造体の時刻関係(TOT/CUE)のパラメータ初期化 */ - parse_tot(sptr + s_offset, &(sp->time_tot)); - sp->time_cue = cue2time(sp->arg_cue); - sp->tot_packet_nb = packet_nb; - } - break; - default: - /* ■時間管理に関しての実装方針■ */ - /* - * ■時間関係を扱っている変数■ - * PCR : 42Bit @27MHz(ServiceID(ProgramID, sid)毎に独立) - * PTS : 42Bit @90KHz(ES毎に独立) - * DTS : 42Bit @90KHz(ES毎に独立) - * TOT : MJD + 2進化10進数(TSに1つだけ) - * STC : 64Bit @27MHz(ServiceID(ProgramID, sid)の現在時刻(ソースはPCR)) - * CUE : 録画開始時刻 YYYYMMDDHHMISS (引数指定) - * - * ■STCの管理方針■ - * PCR受信時にSTCに時間情報をコピーする - * 1パケットを受信すると経過する時間を加算してSTCを管理する - * (ffmpegのmpegts.cと同じ方針) - * - * ■CUEとTOTとSTC■ - * TOTからSTCと比較するための情報を作成する。 - * ・TOTは time_t - * ・CUEは time_t - * ・STCは42Bitの27MHz周期の数値 - * TOT 受信時に各Program(ServiceID)のSTCを変換基準値とする - * 録画開始時(27MHz)の値の計算式は、 - * Program->CUE = STCの変換基準値 + 27e6*(ARG_CUE-TOT(秒)) - * 録画の開始確認として Program->CUE と PTS を比較すればよい - * - * ※PCR/PTS/DTSはオーバーフローしたときには34Bit目が立っていると見なすこと※ - * オーバーフロー時の処理は未実装 - */ - if ( 2 == sp->pmt_pids[pid] ) { - /* PMT の追跡 */ - DemuxTs((sptr+s_offset), sp, pid); - } - /* pids[pid] が 1 は残すパケットなので書き込む */ - if ( (1 == sp->pids[pid]) ) { - /* PCRとSTCの処理 */ - int pcr_index; - pcr_index = search_pcr_pid(sp, pid); - if ( sp->pcr_pids[pid] == 1 && pcr_index != -1) { /* PCRか否か */ - ret = parse_pcr(&pcr_h, &pcr_l, (sptr+s_offset)); - /* - * PCR は複数 ServiceID(ProgramID)で重複利用される場合がある - * PCR を参照する複数の ServiceID(ProgramID)分ループ - */ - for (i=0; i < sp->pcr[pcr_index].sid_nb; i++) { - sid = sp->pcr[pcr_index].sid[i]; - program = &(sp->program[sid]); - /* こっから */ - if ( ret == 0 ) { /* PCR の解析に成功 */ - program->stc = pcr_h * 300 + pcr_l; /* PCR受信時にSTCを補正*/ - if ( program->pcr1 == 0 ) { - program->pcr1 = program->stc; - printf("pcr1 pid[%d] sid[%d] packet_nb[%d] sid_nb[%d] i[%d]\n", - pid, sid, packet_nb, sp->pcr[pcr_index].sid_nb, i); - } else if ( program->pcr2 == 0 ) { -// printf("pcr2 pid[%d] sid[%d] packet_nb[%d], p_packet_nb[%d] sid_nb[%d] i[%d]\n", -// pid, sid, packet_nb, program->pcr_packet_nb, sp->pcr[pcr_index].sid_nb, i); - program->pcr2 = program->stc; - program->pcr_incr = (program->pcr2 -program->pcr1) - /(packet_nb -program->pcr_packet_nb); - printf("pcr2 pid[%d] sid[%d] pcr_incr[%llu]\n", - pid, sid, program->pcr_incr); - } else { - /* PCR処理済み */ - ; /* 得に処理無し */ - } - if ( (program->cue == INT64_MAX ) && - (sp->arg_cue != NULL) && - (sp->time_tot != 0) ) { /* 録画開始時刻指定時 */ - /* - * 録画開始時刻 = STC +(CUE -TOT)*27MHz - * +(TOT取得時から現在までのパケット数の増分)*パケットあたりの進む時間 - * -0.49秒(この数字は適当) - * TOT/STCで時間調整して、GOP先頭から出すので攻めてしまっていい気がする - */ - program->cue = program->stc - +(sp->time_cue -sp->time_tot)*27e6 - +(packet_nb -sp->tot_packet_nb)*program->pcr_incr - -(27e6*49/100); - printf("STC[%llu] CUE[%llu] SID[%d]\n", - program->stc, program->cue, sid); - } - program->pcr_packet_nb = packet_nb; - program->packet_nb = packet_nb; - } else if ( program->pcr_incr ) { /* PCRの解析に失敗且つpcr_incr変数の計算済み*/ - /* STCを成長させる */ - program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; - program->packet_nb = packet_nb; - } else { - ; /* STCを進める要素が揃ってません */ - } - } /* for */ - } else { /* 処理対象パケットはPCRではない */ - program = &(sp->program[sid]); - if ( program->pcr_incr ) { /* PCRを受信しない且つpcr_incr変数の計算済み */ - /* STCを成長させる */ - program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; - program->packet_nb = packet_nb; - } else { /* それ以外 */ - ; /* STCを進める要素が揃ってません */ - } - } -#if 0 -// NHK Gを ALL とすると SID 1024 しか出ない...orz.. - if ( !(packet_nb % 1000) ) { - program = &(sp->program[sid]); - printf("STC[%llu] SID[%d]\n", program->stc, sid); - } -#endif - /* TS処理 */ - DemuxTs((sptr+s_offset), sp, pid); - memcpy(dptr + d_offset, sptr + s_offset, LENGTH_PACKET); - d_offset += LENGTH_PACKET; - dbuf->size += LENGTH_PACKET; - } - break; - } /* switch */ - s_offset += LENGTH_PACKET; - packet_nb += 1; /* パケット受信数加算 */ - } - return(TSS_SUCCESS); -} - -/** - * PAT 解析処理 - * - * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する - */ -static int AnalyzePat(splitter *sp, unsigned char *buf) -#if 0 - unsigned char* buf, // [in] 読み込んだバッファ - unsigned char** pat, // [out] PAT 情報(再構築後) - unsigned char* pids, // [out] 出力対象 PID 情報 - char** sid_list, // [in] 出力対象サービス ID のリスト - unsigned char* pmt_pids, // [out] サービス ID に対応する PMT の PID - int* pmt_retain // [out] 残すPMTの数 -) -#endif -{ - int pos[MAX_PID]; - int service_id; - int i, j, k; - int size = 0; - int pid; - int result = TSS_SUCCESS; - char **p; - int sid_found = FALSE; - int avail_sids[MAX_SERVICES]; - - unsigned char *pat = sp->pat; - unsigned char *pids = sp->pids; - char **sid_list = sp->sid_list; - unsigned char *pmt_pids = sp->pmt_pids; - - char chosen_sid[512]; - chosen_sid[0] = '\0'; - - if(pat == NULL) { - /* 初期化 */ - sp->pmt_retain = 0; - memset(pos, 0, sizeof(pos)); - size = buf[7]; - - /* prescan SID/PMT */ - for(i = 17, j = 0; i < (size + 8) - 4; i = i + 4, j++) { - avail_sids[j] = (buf[i] << 8) + buf[i+1]; - sp->avail_pmts[j] = GetPid(&buf[i+2]); - } - sp->num_pmts = j; - - // 対象チャンネル判定 - /* size + 8 = パケット全長 */ - /* 最終 4 バイトはCRCなので飛ばす */ - for(i = 17; i < (size + 8) - 4; i = i + 4) { - - service_id = (buf[i] << 8) + buf[i+1]; - p = sid_list; - - while(*p) { - if(service_id == atoi(*p)) { - /* 録画対象の pmt_pids は 1 とする */ - /* 録画対象の pmt の pids は 1 とする */ - /* 対応する pid_sid_table に サービスID(ProgramID) を入れる */ - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - p++; - continue; - } - else if(!strcasecmp(*p, "hd") || !strcasecmp(*p, "sd1")) { - /* hd/sd1 指定時には1番目のサービスを保存する */ - if(service_id == avail_sids[0]) { - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - } - p++; - continue; - } - else if(!strcasecmp(*p, "sd2")) { - /* sd2 指定時には2番目のサービスを保存する */ - if(service_id == avail_sids[1]) { - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - } - p++; - continue; - } - else if(!strcasecmp(*p, "sd3")) { - /* sd3 指定時には3番目のサービスを保存する */ - if(service_id == avail_sids[2]) { - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - } - p++; - continue; - } - else if(!strcasecmp(*p, "1seg")) { - /* 1seg 指定時には PMTPID=0x1FC8 のサービスを保存する */ - pid = GetPid(&buf[i + 2]); - if(pid == 0x1FC8) { - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - } - p++; - continue; - } - else if(!strcasecmp(*p, "all")) { - /* all指定時には全保存する */ - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - break; - } - - p++; - } /* while */ - } - - /* if sid has been specified but no sid found, fall back to all */ - if(*sid_list && !sid_found) { - for(i = 17; i < (size + 8) - 4; i = i + 4) { - service_id = (buf[i] << 8) + buf[i+1]; - pid = GetPid(&buf[i + 2]); - *(pmt_pids+pid) = 1; - *(pids+pid) = 1; - pos[pid] = i; - sid_found = TRUE; - sp->pmt_retain += 1; - sp->program[service_id].pmt_packet_id = pid; - sp->pid_sid_table[pid] = service_id; - sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); - } - } - - /* print SIDs */ - fprintf(stderr, "Available sid = "); - for(k=0; k < sp->num_pmts; k++) - fprintf(stderr, "%d ", avail_sids[k]); - fprintf(stderr, "\n"); - fprintf(stderr, "Chosen sid =%s\n", chosen_sid); - -#if 0 - /* print PMTs */ - fprintf(stderr, "Available PMT = "); - for(k=0; k < sp->num_pmts; k++) - fprintf(stderr, "%d ", sp->avail_pmts[k]); - fprintf(stderr, "\n"); -#endif - - // PAT 再構築 - result = RecreatePat(sp, buf, pos); -#if 0 - int tc; - for(tc=0; tc<188; tc++) - fprintf(stderr, "%02x ", *(pat+tc)); -#endif - } - - return(result); -} - -/** - * PAT 再構築処理 - * - * PMT から出力対象チャンネル以外のチャンネル情報を削除し、PAT を再構築する - */ -static int RecreatePat(splitter *sp, unsigned char *buf, int *pos) -#if 0 - unsigned char* buf, // [in] 読み込んだバッファ - unsigned char** pat, // [out] PAT 情報(再構築後) - unsigned char* pids, // [out] 出力対象 PID 情報 - int *pos) // [in] 取得対象 PMT のバッファ中の位置 -#endif -{ - unsigned char y[LENGTH_CRC_DATA]; - int crc; - int i; - int j; - int pos_i; - int pid_num = 0; - - // CRC 計算のためのデータ - { - // チャンネルによって変わらない部分 - for (i = 0; i < LENGTH_PAT_HEADER; i++) - { - y[i] = buf[i + 5]; - } - // チャンネルによって変わる部分 - for (i = 0; i < MAX_PID; i++) - { - if(pos[i] != 0) - { - /* buf[pos_i] を y にコピー(抽出したPIDの数) */ - pos_i = pos[i]; - for (j = 0; j < 4; j++) - { - y[LENGTH_PAT_HEADER + ((4*pid_num) + j)] = buf[pos_i + j]; - } - pid_num++; - } - } - } - /* パケットサイズ計算 */ - y[2] = pid_num * 4 + 0x0d; - // CRC 計算 - crc = GetCrc32(y, LENGTH_PAT_HEADER + pid_num*4); - - // PAT 再構成 - sp->pat = (unsigned char*)malloc(LENGTH_PACKET); - if(sp->pat == NULL) - { - fprintf(stderr, "RecreatePat() malloc error.\n"); - return(TSS_NULL); - } - memset(sp->pat, 0xFF, LENGTH_PACKET); - for (i = 0; i < 5; i++) - { - (sp->pat)[i] = buf[i]; - } - for (i = 0; i < LENGTH_PAT_HEADER + pid_num*4; i++) - { - (sp->pat)[i + 5] = y[i]; - } - (sp->pat)[5 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 24) & 0xFF; - (sp->pat)[6 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 16) & 0xFF; - (sp->pat)[7 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 8) & 0xFF; - (sp->pat)[8 + LENGTH_PAT_HEADER + pid_num*4] = (crc ) & 0xFF; - - return(TSS_SUCCESS); -} - - -/** - * PMT 解析処理 - * - * PMT を解析し、保存対象の PID を特定する - * TSヘッダとアダプテーションフィールドの処理は DemuxTs に一任するので、 - * この内部では、セクションデータの先頭ポインタをもらってくる - */ -static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size) -#if 0 - unsigned char* buf, // [in] セクション先頭 - unsigned char* pids) // [out] 出力対象 PID 情報 -#endif -{ - unsigned char Nall; - unsigned char N; - int pcr; - int epid; - int av_flag = 0; - int i = 0; - int j = 0; - int pcr_found = 0; -/* デバッグ用 PMT情報表示 */ -#define PmtDebug (1) - -#ifdef PmtDebug - printf("AnalyzePmt start. Tree List enable.\n"); - printf("SID[%d][0x%04x]\n", sid, sid); -#endif - -// Nall = ((buf[2] & 0x0F) << 4) + buf[3]; - Nall = ((buf[2] & 0x0F) << 8) + buf[3]; -// ここで受け取るのはTSパケットではなく、セクションの先頭ポインタであるのでsizeで見る -// if(Nall > LENGTH_PACKET) -// Nall = LENGTH_PACKET - 8; /* xxx workaround --yaz */ - if(Nall > size) - Nall = size -8; - - /* get version */ - sp->program[sid].pmt_version = get_pmt_version(buf); -#ifdef PmtDebug - printf(" pmt_version[%02x]\n", sp->program[sid].pmt_version); -#endif - - // PCR - pcr = GetPid(&buf[9]); - sp->pids[pcr] = 1; - sp->program[sid].pcr_packet_id = pcr; - sp->pid_sid_table[pcr] = sid; /* PCRは重複する可能性があるので方式がよろしくない */ - sp->pcr_pids[pcr] = 1; - - /* PCRの重複チェック(複数ServiceID(ProgramID)) */ - for( i=0; i < sp->pcr_nb; i++ ) { - if ( sp->pcr[i].pid == pcr ) { - /* 発見 */ - for ( j=0; j < sp->pcr[i].sid_nb; j++ ) { - /* 同一SIDが既に登録されているか確認 */ - if ( sp->pcr[i].sid[j] == sid ) { - pcr_found = 1; - break; - } - } - if ( pcr_found ) { - /* 同一SIDが既に登録されている */ -#ifdef PmtDebug - printf(" same sid found pcr[%d] sid[%d]\n", pcr, sid); -#endif - break; - } - /* 重複PCR発見 */ -#ifdef PmtDebug - printf(" same pcr found pcr[%d] sid[%d] sid_nb[%d], i[%d]\n", pcr, sid, sp->pcr[i].sid_nb, i); -#endif - sp->pcr[i].sid[sp->pcr[i].sid_nb] = sid; - sp->pcr[i].sid_nb += 1; - pcr_found = 1; - break; - } - } - - if ( ! pcr_found ) { - /* PCR管理領域更新 */ -#ifdef PmtDebug - printf(" new pcr found pcr[%d] sid[%d], pcr_nb[%d]\n", pcr, sid, sp->pcr_nb); -#endif - sp->pcr[sp->pcr_nb].pid = pcr; - sp->pcr[sp->pcr_nb].sid[0] = sid; - sp->pcr[sp->pcr_nb].sid_nb = 1; - sp->pcr_nb += 1; - } -#ifdef PmtDebug - printf(" PCR PacketID[%d][0x%04x]\n", pcr, pcr); -#endif - -// N = ((buf[11] & 0x0F) << 4) + buf[12] + 16 + 1; - N = ((buf[11] & 0x0F) << 8) + buf[12] + 12 + 1; -// printf("NAll[%d] N[%d]\n", Nall, N); - - // ECM - //int p = 17; - int p = 13; - while(p < N) { - if ( p > size -4) { - break; - } - uint32_t cat_pid; - uint32_t tag; - uint32_t len; - - tag = buf[p]; - len = buf[p+1]; - p += 2; - - if(tag == 0x09 && len >= 4 && p+len <= N) { -// ca_pid = ((buf[p+2] << 8) | buf[p+3]) & 0x1fff; - cat_pid = (AV_RB16(buf+p+2)) & 0x1fff; - sp->pids[cat_pid] = 1; - sp->cat_pids[cat_pid] = 1; - sp->pid_sid_table[cat_pid] = sid; /* CATも複数ServiceIDで重複がある */ -#ifdef PmtDebug - printf(" CAT PacketID[%d][0x%04x]\n", cat_pid, cat_pid); -#endif - } - p += len; - } - - /* - * ISO/IEC 13818-1:2000(E) Table 2-29 - Stream type assignments - * Value Desctiption - * 0x00 ITU-T | ISO/IEC Reserved - * 0x01 ISO/IEC 11172 Video - * 0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream - * 0x03 ISO/IEC 11172 Audio - * 0x04 ISO/IEC 13818-3 Audio - * 0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections - * 0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data - * 0x07 ISO/IEC 13522 MHEG - * 0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC - * 0x09 ITU-T Rec. H.222.1 - * 0x0A ISO/IEC 13818-6 type A - * 0x0B ISO/IEC 13818-6 type B - * 0x0C ISO/IEC 13818-6 type C - * 0x0D ISO/IEC 13818-6 type D - * 0x0E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary - * 0x0F ISO/IEC 13818-7 Audio with ADTS transport syntax - * 0x10 ISO/IEC 14496-2 Visual - * 0x11 ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1 - * 0x12 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets - * 0x13 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections. - * 0x14 ISO/IEC 13818-6 Synchronized Download Protocol - * 0x15-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved - * 0x80-0xFF User Private - * - */ - - // ES PID - while (N < Nall + 8 - 4) { - av_flag = 0; - // ストリーム種別が 0x0D(type D)は出力対象外 - if (0x0D != buf[N]) { - epid = GetPid(&buf[N + 1]); - sp->pids[epid] = 1; - sp->pid_sid_table[epid] = sid; - if ( buf[N] == 0x02 ) { /* 13818-2 Video */ - sp->program[sid].video[sp->program[sid].video_nb] = epid; - sp->program[sid].video_nb += 1; - av_flag = TSS_STREAM_TYPE_VIDEO; -#ifdef PmtDebug - printf(" VIDEO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); -#endif - } else if ( (buf[N] == 0x04) || (buf[N] == 0x0f) ) { - /* 13818-3 Audio or 13818-7 Audio with ADTS transport syntax */ - sp->program[sid].audio[sp->program[sid].audio_nb] = epid; - sp->program[sid].audio_nb += 1; - av_flag = TSS_STREAM_TYPE_AUDIO; -#ifdef PmtDebug - printf(" AUDIO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); -#endif - } else { -#ifdef PmtDebug - printf(" OTHER PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); -#endif - ; /* A/V どちらでもないものはとりあえずスルー */ - } - if ( av_flag && sp->esout ) { - /* ESバッファはNULLか? */ - if ( sp->esbuf[epid] == NULL ) { - sp->esbuf[epid] = malloc(sizeof(splitesbuf_t)); - if ( sp->esbuf[epid] == NULL ) { - fprintf(stderr, "malloc error\n"); - return TSS_NULL; - } - sp->esbuf[epid]->size = 0; - sp->esbuf[epid]->Program = &(sp->program[sid]); - sp->esbuf[epid]->fd = -1; - if ( creat_es_file(sp, sid, epid, av_flag) ) { - return TSS_ERROR; - } - } - /* PESバッファはNULLか? */ - if ( sp->pesbuf[epid] == NULL ) { - sp->pesbuf[epid] = malloc(sizeof(splitpesbuf_t)); - if ( sp->pesbuf[epid] == NULL ) { - fprintf(stderr, "malloc error\n"); - return TSS_NULL; - } - sp->pesbuf[epid]->size = 0; - sp->pesbuf[epid]->Program = &(sp->program[sid]); - } - } - } -// N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1; - N += 4 + (((buf[N + 3]) & 0x0F) << 8) + buf[N + 4] + 1; - } -#ifdef PmtDebug - printf("AnalyzePmt finish.\n"); -#endif - return TSS_SUCCESS; -} - -/** - * CRC 計算 - */ -static int GetCrc32( - unsigned char* data, // [in] CRC 計算対象データ - int len) // [in] CRC 計算対象データ長 -{ - int crc; - int i, j; - int c; - int bit; - - crc = 0xFFFFFFFF; - for (i = 0; i < len; i++) - { - char x; - x = data[i]; - - for (j = 0; j < 8; j++) - { - - bit = (x >> (7 - j)) & 0x1; - - c = 0; - if (crc & 0x80000000) - { - c = 1; - } - - crc = crc << 1; - - if (c ^ bit) - { - crc ^= 0x04C11DB7; - } - - crc &= 0xFFFFFFFF; - } - } - - return crc; -} - -/** - * PID 取得 - */ -static int GetPid( - unsigned char* data) // [in] 取得対象データのポインタ -{ - return ((data[0] & 0x1F) << 8) + data[1]; -} - -/* return the 90kHz PCR and the extension for the 27MHz PCR. return - (-1) if not available */ -static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, - const uint8_t *packet) -{ - int afc, len, flags; - const uint8_t *p; - unsigned int v; - - afc = (packet[3] >> 4) & 3; - if (afc <= 1) - return -1; - p = packet + 4; - len = p[0]; - p++; - if (len == 0) - return -1; - flags = *p++; - len--; - if (!(flags & 0x10)) - return -1; - if (len < 6) - return -1; - v = AV_RB32(p); - *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7); - *ppcr_low = ((p[4] & 1) << 8) | p[5]; - return 0; -} - -/* pesbufが空か判定 (ret. 0:not empty / 1: empty) */ -static int pesbuf_empty(splitpesbuf_t *pesbuf){ - return pesbuf->size == 0; -} - -/* pesbufをクリア */ -void pesbuf_clear(splitpesbuf_t *pesbuf){ - pesbuf->size = 0; -} - -/* pesbufにデータを追加 (ret. 0:success / -1:error) */ -static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len){ - if(pesbuf->size + len > sizeof pesbuf->buffer){ - return -1; - } - memcpy(pesbuf->buffer +pesbuf->size, data, len); - pesbuf->size += len; - return 0; -} - -/* pesbufから、PESの先頭(packet_start_code_prefix)を探す */ -/* (ret. >=0:offset / -1: error) */ -static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf){ - uint8_t packet_start_code_prefix[3] = {0x00, 0x00, 0x01}; - int i = 0; - - /* 小さすぎる */ - if(pesbuf->size < sizeof packet_start_code_prefix){ - return -1; - } - /* 先頭で探す */ - if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ - return 0; - } - -#if 0 - /* 先頭以外からも探す場合は、ここのコードを有効化する。 */ - /* ただし、MPEG-Videoのstart_codeと同じなので、深追いしない方がいいと思う... */ - for(i = 0; i < pesbuf->size - sizeof packet_start_code_prefix; i++){ - if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ - return i; - } - } -#endif - - return -1; -} - -/** - * TSの解析とDemuxを行う - */ -static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid) -{ - /* - * PES先頭までの長さは - * 4byte : continity counter - * 27,28bit が adaptation fileld制御 - * (01:ペイロードのみ, 10:adaptation fileldのみ、11:adaptation fileld+payload、00:reserved) - * ペイロード長 = 188 - TS header(4byte) -adaptation field長 -1 - */ - /* ありがとう */ - - int payload_offset; /* ペイロードオフセット(=パケット先頭からのバイト数) */ - int payload_length; /* ペイロード長 */ - int pes_started; - int adaptation_field_control; - int payload_unit_start_indicator; -// int random_access_indicator = 0; - int sid = sp->pid_sid_table[pid]; /* SIDをPIDから引いているが、PCRとCATは重複しているので注意*/ - - payload_offset = LENGTH_TS_HEADER; - - if ( sp->pesbuf[pid] == NULL ) { - pes_started = 0; /* malloc走る前(セクション解析だったら呼んで良い) */ - } else { - pes_started = !pesbuf_empty(sp->pesbuf[pid]); /* PES蓄積開始済み */ - } - - /* adaptation_field_controlおよびadaptation_fieldを処理する */ - adaptation_field_control = (packet[3] & 0x30) >> 4; - if ( adaptation_field_control == 0x02 || adaptation_field_control == 0x00) { - /* ペイロードなしの場合 */ - return 0; /* 別にエラーではない */ - } else if ( adaptation_field_control == 0x03 ) { - /* アダプテーションフィールド+ペイロードの場合 */ - if ( packet[LENGTH_TS_HEADER] != 0 ) { -// random_access_indicator = (packet[5] & 0x40) >> 6; - } - /* ペイロード開始位置 = TSヘッダ長 + アダプテーションフィールド長 + 1 */ - payload_offset += packet[LENGTH_TS_HEADER] + 1; - } else { - /* ペイロードのみ */ - ; /* 特に処理なし */ - } - - /* ペイロード長を出す */ - payload_length = LENGTH_PACKET - payload_offset; - if( payload_length <= 0 ){ /* payload長が0以下の場合 */ - return -1; /* エラーにすべきかは微妙なところ */ - } - - /* payload_unit_start_indicatorを処理(1) */ - payload_unit_start_indicator = (packet[1] & 0x40) >> 6; - /* (sectionの場合は、ここでpointer_fieldの処理などを行い、payload_offsetに反映する) */ - if ( sp->pmt_pids[pid] == 2 ) { /* PID が録画対象の PMT であるか? */ - if ( get_pmt_version(packet+payload_offset) != sp->program[sid].pmt_version ) { - /* pmt versionに差分あり */ - fprintf(stderr, "pmt version diff found pmt_pid[%d]" - " old_version[0x%02x]" - " new_version[0x%02x].\n", - pid, - sp->program[sid].pmt_version, - get_pmt_version(packet+payload_offset)); - AnalyzePmt(sp, packet +payload_offset, sid, payload_length); - /* payload 何byte処理したか等管理するべき */ - } - return 0; /* PMT の場合は処理終わり */ - } - if ( !sp->esout ) { - /* ES出力しない場合はPES蓄積不要 */ - return 0; - } - if ( sp->cat_pids[pid] == 1 ) { - return 0; /* CATは蓄積しない */ - } - if ( sp->pesbuf[pid] == NULL ) { - /* PES蓄積不要である場合も蓄積しない */ - return 0; - } - - /* payload_unit_start_indicatorを処理(2) */ - /* 必要に応じ、蓄積済みPESの処理と、PES蓄積開始を行う */ - if( payload_unit_start_indicator ){ - /* PES開始 */ - if ( pes_started ) { - /* バッファにデータがあればPES終端なので処理してクリア */ -// pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid, random_access_indicator); - pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid); - pesbuf_clear(sp->pesbuf[pid]); - } - else { - pes_started = 1; - } - } - - /* PES蓄積処理 */ - if ( pes_started ){ - /* PES蓄積開始済み(これからPES蓄積開始を含む)なら、payloadをPESとして追加 */ - pesbuf_add(sp->pesbuf[pid], packet + payload_offset, payload_length); - } - /* おつかれさまでした */ - return 0; -} - -#if 0 -未使用なため削除 -/* PMT_PID から Program(Service ID)を確定させる */ -static int search_pmt_program(splitter *sp, int pid) -{ - /* この関数は大変遅いのでなるべく使用しない */ - int i; - for ( i = 0; i < MAX_SERVICE_ID; i++ ) { - if ( sp->program[i].pmt_packet_id == pid ) { - return i; - } - } - return -1; -} -#endif - -/* esbufが空か判定 (ret. 0:not empty / 1: empty) */ -static int esbuf_empty(splitesbuf_t *esbuf){ - return esbuf->size == 0; -} - -/* esbufをクリア */ -void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts){ - esbuf->size = 0; - esbuf->pts = pts; - esbuf->dts = dts; -} - -/* esbufにデータを追加 (ret. 0:success / -1:error) */ -static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len){ - if(esbuf->size + len > sizeof esbuf->buffer){ - return -1; - } - memcpy(esbuf->buffer +esbuf->size, data, len); - esbuf->size += len; - return 0; -} - -/* - * PESを解析してESを出力する - */ -//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator) -static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) -{ - int len_pesh = 0; - int code = 0; - int flags = 0; - int len_pes = 0; - int len_pesh_supposed = 0; - int pes_extension_flags = 0; - int pes_extension_flags2 = 0; - int program_packet_sequence_counter_flag = 0; - int es_rate = 0; - const uint8_t *p = pesbuf->buffer; - const uint8_t *p_end = pesbuf->buffer +pesbuf->size; - int original_stuffing_length = 0; - int data_alignment_indicator = false; - int es_started; - int payload_offset = 0; - int payload_length = 0; - int audio_lipsync_offset = 0; - int i = 0; - int64_t audio_pts = 0; - int adts_freq = 0; - int64_t adts_frame_time = 0; - int gop_start = -1; - - /* ありがとう */ - /* ありがとうとコメントを書くと、 - * 動作がよくなる - * バグが減る - * バイナリサイズが小さくなる - * 画質がよくなる - * 音質がよくなる - */ - if ( esbuf == NULL ) { - return -1; /* malloc走る前この関数は呼んじゃダメです */ - } else { - es_started = !esbuf_empty(esbuf); /* ES蓄積開始済み */ - } - - payload_offset = pesbuf_packet_start_code_prefix(pesbuf); - if ( payload_offset == -1 ) { - return -1; - } - p += payload_offset; - /* http://dvd.sourceforge.net/dvdinfo/pes-hdr.html - * - * Stream ID : type : extension present? - * (1011 1101) 0xBD : Private stream 1 (non MPEG audio, subpictures) : YES - * (1011 1110) 0xBE : Padding stream : NO - * (1011 1111) 0xBF : Private stream 2 (navigation data) : NO - * (110x xxxx) 0xC0 - 0xDF : MPEG-1 or MPEG-2 audio stream number x xxxx : YES - * (1110 xxxx) 0xE0 - 0xEF : MPEG-1 or MPEG-2 video stream number xxxx : YES - * note: DVD allows only 8 audio streams/DVD allows only 1 video stream - */ - /* http://www2.arib.or.jp/johomem/pdf/2009/2009_0088.pdf - * - * 0xBC : プログラムストリームマップ - * 0xBD : プライベートストリーム1 - * 0xBE : パディングストリーム - * 0xBF : プライベートストリーム2 - * 0xC0 - 0xDF : ISO/IEC 13318 3、ISO/IEC 11172 3、ISO/IEC 13318 7 or ISO/IEC 14496 3 audio xxxx - * 0xE0 - 0xEF : ITU-T H.262、ISO/IEC 11172 2、ISO/IEC 14496 2 or ITU-T H264映像ストリーム - * 0xF0 : ECMストリーム - * 0xF1 : EMMストリーム - * 0xF2 : ITU-T勧告H.222.0 Annex A 又は ISO/IEC 13318 6 のDSMCCストリーム - * 0xF3 : ISO/IEC 13522ストリーム - * 0xF4 : ITU-T勧告 H.222.1 type A - * 0xF5 : ITU-T勧告 H.222.1 type B - * 0xF6 : ITU-T勧告 H.222.1 type C - * 0xF7 : ITU-T勧告 H.222.1 type D - * 0xF8 : ITU-T勧告 H.222.1 type E - * 0xF9 : 補助ストリーム - * 0xFA : ISO/IEC 14496 1SLパケット化ストリーム - * 0xFB : ISO/IEC 14496 1フレックスマックスストリーム - * 0xFC : メタデータストリーム - * 0xFD : 拡張ストリームID - * 0xFE : 未定義 - * 0xFF : プログラムストリームディレクトリ - */ - /* 上記より、ここでは - * MPEG-1 or MPEG-2 audio stream と - * MPEG-1 or MPEG-2 video stream と - * Private stream 1 と - * 0xFD(拡張ストリームID)を抽出する - * ?0xBF Private stream 2 落としてるけどよいの? - * >多分よくない。ffmpegではここに入る前に PRIVATE_STREAM2 のコードが入っている - */ - code = (p[3] &0xff) | 0x100; - if ( !((code >= 0x1c0 && code <= 0x1df) || - (code >= 0x1e0 && code <= 0x1ef) || - (code == 0x1bd) || (code == 0x1fd))) { - return -1; - } - /* PES のデータ長 */ - /* 動画のストリームである場合には、ES長は不定となるので0が許容される */ - len_pes = AV_RB16(p+4); - /* PESヘッダ拡張部(byte 6) */ - flags = p[6] & 0xff; - if ( flags & 0x04 ) { - data_alignment_indicator = true; - /* data alignment indicator */ - /* video start code or audio syncword. */ - /* おそらくここで区切るとピクチャ単位 */ - //printf("data alignment indicator found pid[%d].\n", pid); - } - flags = p[7] & 0xff; - /* PESヘッダデータ長(byte 8) */ - len_pesh = p[8] & 0xff; - p += LENGTH_PES_HEADER; - payload_offset += LENGTH_PES_HEADER +len_pesh; - if ( p +payload_offset >= p_end ) { - /* PESヘッダ長すぎます */ - return -1; - } - - /* flags - * +---------------------------------------------------+ - * name |byte 7(flags) | - * +-----+-----+-----+------+----------+----+----------+ - * Bit |76 |5 |4 |3 |2 |1 |0 | - * +-----+-----+-----+------+----------+----+----------+ - * field|PTS |ESCR |ES |DSM |additional|PES |PES | - * name |DTS |FLAG |RATE |Trick |copy info |CRC |Extension | - * |flag | |flag |mode |flag |flag|flag | - * +-----+-----+-----+------+----------+----+----------+ - * Data |5,5 |6 |3 |1 |1 |2 |1 |(24) - * byte | | | | | | | | - * +-----+-----+-----+------+----------+----+----------+ - */ - if ( flags & PTS_FLAG ) { - if ( p +LENGTH_PTS >= p_end ) { - return -1; - } - pesbuf->pts = get_pts(p); - p += LENGTH_PTS; - len_pesh_supposed += LENGTH_PTS; - } - if ( flags & DTS_FLAG ) { - if ( p +LENGTH_PTS >= p_end ) { - return -1; - } - pesbuf->dts = get_pts(p); - p += LENGTH_PTS; - len_pesh_supposed += LENGTH_PTS; - } - if ( flags & ESCR_FLAG ) { - p += 6; - len_pesh_supposed += 6; - } - if ( flags & ES_RATE_FLAG ) { - es_rate = AV_RB24(p); - es_rate = (es_rate >>1) & 0x3fffff; - es_rate = es_rate * 50; - printf("pid[%d] es_rate[%d]Byte/Sec.\n", pid, es_rate); - p += 3; - len_pesh_supposed += 3; - } - if ( flags & DSM_TRICK_MODE_FLAG ) { - p += 1; - len_pesh_supposed += 1; - } - if ( flags & COPY_INFO_FLAG ) { - p += 1; - len_pesh_supposed += 1; - } - if ( flags & CRC_FLAG ) { - p += 2; - len_pesh_supposed += 2; - } - if ( flags & EXTENSION_FLAG ) { - /* PES Extension flag - * +------------------------------------------------------------------+ - * name |PES Extension flag | - * +-----------+-----------+----------------+------+---+--------------+ - * bit |7 |6 |5 |4 |321|0 | - * +-----------+-----------+----------------+------+---+--------------+ - * field|PES private|pack header|program |P-STD |111|PES extension | - * name |data flag |field flag |packet |buffer| |flag2 | - * | | |sequence counter|flag | | | - * +-----------+-----------+----------------+------+---+--------------+ - * Data |16 |1 |2 |2 | |1 |(23) - * byte | | | | | | | - * +-----------+-----------+----------------+------+---+--------------+ - */ - if ( p >= p_end ) { - return -1; - } - pes_extension_flags = *p & 0xff; - p += 1; - len_pesh_supposed += 1; - if ( pes_extension_flags & PES_PRIVATE_DATA_FLAG ) { - p += 16; - len_pesh_supposed += 16; - } - if ( pes_extension_flags & PACK_HEADER_FIELD_FLAG ) { - p += 1; - len_pesh_supposed += 1; - } - if ( pes_extension_flags & PROGRAM_PACKET_SEQUENCE_COUNTER ) { - if ( p >= p_end ) { - return -1; - } - program_packet_sequence_counter_flag = *p & 0xff; - original_stuffing_length = program_packet_sequence_counter_flag & 0x3f; - p += 2; - len_pesh_supposed += 2; - } - if ( pes_extension_flags & PSTD_BUFFER_FLAG ) { - p += 2; - len_pesh_supposed += 2; - } - if ( pes_extension_flags & PES_EXTENSION_FLAG2 ) { - /* PES Extension flag2 - * +------------------------------------------------------------------+ - * name |PES Extension flag2 | - * +------+-----------------------------------------------------------+ - * bit |7 |6543210 | - * +------+-----------------------------------------------------------+ - * field|marker|PES_extension_field_length | - * name |bit | | - * |'1' | | - * +------+-----------------------------------------------------------+ - * Data |- |0 <= N <= 127 |(127) - * byte | | | - * +------+-----------------------------------------------------------+ - */ - if ( p >= p_end ) { - return -1; - } - pes_extension_flags2 = *p & 0x7f; - p += 1; - len_pesh_supposed += 1; - - p += pes_extension_flags2; - len_pesh_supposed += pes_extension_flags2; - } - } - if ( pid != 6417 && pid != 6418 ) { -// printf("es start? pid[%d]\n", pid); - } - /* ES蓄積管理処理 */ - payload_length = pesbuf->size -payload_offset; -// if ( data_alignment_indicator ) { /* data_alignment_indicator 区切りでESを出力する */ - if ( es_started ) { /* ES にデータが蓄積されている */ - /* - * ビデオをファイル出力し始める条件(1. 2. を満たすこと) - * 1. ESにGOP先頭を含む - * 2. PTSがCUEの時刻を過ぎていること( CUE <= PTS ) - */ - if ( (is_video_stream(pid, esbuf) == 0) && !(esbuf->started) ) { - /* VIDEO0 を同期の基準とする */ - gop_start = search_gop_start_code(esbuf); - if ( (gop_start != -1) && /* ESバッファにGOP_START_CODEが存在するか? */ - (esbuf->Program->cue <= esbuf->pts*300) ) { /* CUEを過ぎている? */ - /* 該当ストリームをファイル出力開始する */ - esbuf->started = 1; - esbuf->Program->video_start = 1; - esbuf->Program->video_pts = esbuf->pts; - printf("video stream. pid[%d] v_pts[%llu].\n", pid, esbuf->pts); - } else { - /* GOP先頭を含まないものはクリア */ - esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); - } - } else if ( (is_video_stream(pid, esbuf) != -1) && !(esbuf->started) ) { - /* VIDEO0 以外のものはVIDEO0 が開始するまでクリア */ - if ( !(esbuf->Program->video_start) ) { - /* - * VIDEO0 が始まってない場合、 - * VIDEON は常にクリア - */ - esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); - } else { - /* - * VIDEO0 が始まっている場合、 - * VIDEON の録画を開始 - */ - esbuf->started = 1; - } - } - /* - * オーディオをファイル出力し始める条件(1. 2. を満たすこと) - * 1. 動画の蓄積は開始されている - * 2. 動画のGOPの1番目のIピクチャのPTSとオーディオのPTSを比較して以下のどちらかを満たすこと - * 2.1. 差分が11msec以内(1000*90k/AACのサンプリング周波数) - * 1000 : ADTSデータの1フレームのサンプル数 - * 90k : PTSの周波数 - * 2.2. 過ぎている(過ぎている場合はオーディオESの蓄積の継続と次のGOP狙いにする?) - * #動画よりオーディオ側の方が先にESバッファの蓄積を始めるハズなので気にすること無いかなぁ… - */ - else if ( (is_audio_stream(pid, esbuf) != -1) && !(esbuf->started) ) { - if ( !(esbuf->Program->video_start) ) { - /* - * VIDEO が始まってない場合、 - * ESバッファの余裕がある限り、オーディオをESバッファに蓄積し続ける - */ - if ( esbuf->size + payload_length > sizeof esbuf->buffer ){ - /* 溢れそうになったらクリア */ - esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); - } - } else if ( esbuf->Program->video_start ) { /* video 蓄積が開始されている?*/ - printf("audio stream. pid[%d] a_pts[%llu] v_pts[%llu] size[%d].\n", pid, esbuf->pts, esbuf->Program->video_pts, esbuf->size); - audio_lipsync_offset = 0; - audio_pts = esbuf->pts; - adts_freq = AnalyzeAdifHeader(esbuf); - adts_frame_time = (int64_t)((float)1000*90000/adts_freq); /* PTSは90KHz */ - /* オーディオをフレーム単位で捨ててPTSを進める */ - while ( (esbuf->Program->video_pts > audio_pts +adts_frame_time/2) ) { - /* オーディオデータを捨てると audio_pts は1フレーム分大きくなる */ - i = next_adts_start_code(esbuf, audio_lipsync_offset); /* 次のAACのデータを取得 */ - if ( i != -1 ) { /* AACデータの終端か? */ - audio_lipsync_offset += i; - } else { - /* バッファ終端まで進めたが、オーディオPTSの方が古い場合ESバッファはクリアする */ - esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); - break; - } - printf("audio stream drop. pid[%d] pts[%llu].\n", pid, audio_pts); - audio_pts += adts_frame_time; /* AACの1フレーム分、時間を進める */ - } - if ( (esbuf->Program->video_pts <= audio_pts +adts_frame_time/2) ) { - printf("lipsync start. v_pts[%llu] a_pts[%llu].\n", esbuf->Program->video_pts, audio_pts); - memmove(esbuf->buffer +audio_lipsync_offset, - esbuf->buffer, - esbuf->size -audio_lipsync_offset); - esbuf->size -= audio_lipsync_offset; - esbuf->started = 1; /* オーディオのファイル出力を有効化 */ - } - } else { - ; /* 該当するものは無いはず */ - } - } else { - /* 得に処理なし */ - ; - } - /* バッファをファイルに出力してクリア */ - if ( esbuf->started ) { /* 該当ストリームはファイル出力の有効化をされている? */ - esbuf_write(esbuf); - esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); - } - } else { - /* ES蓄積を新たに開始 */ - es_started = 1; - esbuf->pts = pesbuf->pts; - esbuf->dts = pesbuf->dts; - } - //} - - /* ES蓄積処理 */ - if ( es_started ) { - /* ES蓄積開始済み(これからES蓄積開始を含む)なら、payloadをESとして追加 */ - esbuf_add(esbuf, pesbuf->buffer +payload_offset, payload_length); - } - /* お疲れさまでした */ - return 0; -} - -/* Program の N 番目の AUDIO STREAM であるかを返却する */ -static int is_audio_stream(const int pid, splitesbuf_t *esbuf) -{ - int i = 0; - program_t* program = esbuf->Program; - while (i < program->audio_nb) - { - if (program->audio[i] == pid) { - return i; - } - i++; - } - return -1; -} - -/* Program の N 番目の VIDEO STREAM であるかを返却する */ -static int is_video_stream(const int pid, splitesbuf_t *esbuf) -{ - int i = 0; - program_t* program = esbuf->Program; - while (i < program->video_nb) - { - if (program->video[i] == pid) { - return i; - } - i++; - } - return -1; -} - -/* - * ESをファイル出力する - * エラーハンドリングしてないね… - */ -static int esbuf_write(splitesbuf_t *esbuf) -{ - int remain = esbuf->size; - while(remain > 0) - { - remain -= write(esbuf->fd, esbuf->buffer+(esbuf->size-remain), remain); - } - return 0; -} - -#if 0 -未使用なため駆除 -/* - * packet dump - */ -void dump_packet( const uint8_t *packet ) -{ - int i = 0; - uint8_t *p = (uint8_t*)packet; - char tmp[17]; - - printf("HEADER 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F \n"); - while(i < LENGTH_PACKET) { - if ( (i%16) == 0 ) { - printf("0x%04X ", i); - } - printf("%02x ", *(p+i)); - if ( isprint(*(p+i)) ){ - tmp[i%16] = *(p+i); - } - else { - tmp[i%16] = '.'; - } - if ((i%16) == 15) { - tmp[sizeof(tmp)-1] = '\0'; - printf(" %s\n", tmp); - } - i++; - } - putchar('\n'); -} -#endif - -/* - * TOT の JST_time を解析する - */ -static int parse_tot( const unsigned char* packet, time_t *t ) -{ - /* 注意事項 - * 本当は TOT が有効かどうかをチェックするべきですがしていません - * サマータイム関係は無視しています - */ - struct tm tm; - time_t t2; - int k; - uint8_t *p = (uint8_t*)packet; - unsigned int MJD; - tm.tm_wday = 0; - tm.tm_yday = 0; - tm.tm_isdst = 0; - - p += 8; - MJD = (*(p) & 0xff) <<8; - p++; - MJD |= *(p) & 0xff; - printf("MJD[%x].\n", MJD); - - /* ARIB STD-B10 第2部 付録C の公式より MJD to YYYYMMDD */ - tm.tm_year = (int)floor((MJD - 15078.2)/365.25); - tm.tm_mon = (int)floor((MJD - 14956.1 - floor(tm.tm_year * 365.25))/30.6001); - tm.tm_mday = MJD - 14956 - floor(tm.tm_year * 365.25) - floor(tm.tm_mon * 30.6001); - if ( tm.tm_mon == 14 || tm.tm_mon == 15 ) - k = 1; - else - k = 0; - tm.tm_year += k; - tm.tm_mon = tm.tm_mon -1 - k * 12; - tm.tm_mon--; - - /* HHMISSは2進化10進数 */ - p++; - tm.tm_hour = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); - p++; - tm.tm_min = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); - p++; - tm.tm_sec = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); - - *t = mktime(&tm); - time(&t2); -// printf("time[%d] TOT[%d].\n", t2, *t); - - return TRUE; -} - -static int64_t get_pts(const uint8_t *p) -{ - int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30; - pts |= (AV_RB16(p + 1) >> 1) << 15; - pts |= AV_RB16(p + 3) >> 1; - return pts; -} - -static int get_pmt_version(const uint8_t *p) -{ - return ((p[6] >> 1) & 0x1f); -} - -#if 0 -未使用なため駆除 -void search_mpeg_system_header(const uint8_t *packet) -{ - int i; - uint8_t *p = (uint8_t*)packet; - i = 0; - for( i=0; i < LENGTH_PACKET-4; i++) { - if( p[i] == 0x00 && p[i+1] == 0x00 && p[i+2] == 0x01 && p[i+3] == 0xb8 ){ - dump_packet(packet ); - } - } -} -#endif - -/* - * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却する - * ret == 0 : 仕様上あり得ない(esbuf先頭はヘッダ先頭であるため) - * ret > 0 : 見つかった場合 - * ret == -1 : 見つからなかった場合 - */ -static int next_adts_start_code(splitesbuf_t *esbuf, int offset) -{ - /* - * start code prefix のうち、先頭12bit は 1 固定 - */ - uint16_t adts_start_code = 0xfff0; - int i = offset +1; - uint16_t startcode = 0; - - /* 小さすぎる */ - if(esbuf->size -offset < sizeof(adts_start_code)){ - return -1; - } - for(; i < esbuf->size - sizeof(adts_start_code); i++) { - startcode = AV_RB16(esbuf->buffer+i); - if( startcode == adts_start_code ) { /* 該当位置から12bit連続1が立っているか? */ -#if 0 - printf("adts start code found.i[%d]. 0[%02x] 1[%02x] 2[%02x] 3[%02x] 4[%02x] 5[%02x] 6[%02x]\n", - i, *(esbuf->buffer+i+0), *(esbuf->buffer+i+1), *(esbuf->buffer+i+2), *(esbuf->buffer+i+3), *(esbuf->buffer+i+4), *(esbuf->buffer+i+5), *(esbuf->buffer+i+6) ); -#endif - return (i-offset); - } - } - return -1; -} - -/* ADIF HEADER解析 */ -static int AnalyzeAdifHeader(splitesbuf_t *esbuf) -{ - int id = 0; /* 0:MPEG-4 1:MPEG-2 */ - int layer = 0; /* 常に 0x00 */ - int protection_absent = 0; /* 保護属性 0:保護なし 1:保護あり */ - int profile = 0; /* 00:MAIN 01:LC 10:SSR 11:(reserved) */ - int sampling_frequency_index = 0; /* サンプリング周波数テーブル値 */ - int private_bit = 0; /* private bit */ - int channel_configuration = 0; /* チャンネル数 */ - int original_copy = 0; - int home = 0; /* homeってなに? */ - int copyright_identification_bit = 0; /* 著作権証明ビット */ - int copyright_identification_start = 0; /* 著作権証明開始ビット */ - int aac_frame_length = 0; /* AACフレーム長 */ - int adts_buffer_fullness = 0; /* ADTSバッファ残量 */ - int no_raw_data_blocks_in_frame = 0; /* データブロックまでの残量 */ - /* - * サンプリング周波数テーブル(ヘッダのsampling_frequency_indexが添字) - * 単位:Hz - */ - int sampling_frequency_table[16] = - { - 96000, - 88200, - 64000, - 48000, - 44100, - 32000, - 24000, - 22050, - 16000, - 12000, - 11025, - 8000, - -1, - -1, - -1, - -1 - }; - - uint8_t *p = esbuf->buffer; - if ( esbuf->size < 8 ) { - return -1; - } - - id = get_adif_id(p+1); - layer = get_adif_layer(p+1); - protection_absent = get_adif_protection_absent(p+1); - profile = get_adif_profile(p+2); - sampling_frequency_index = get_adif_sampling_frequency_index(p+2); - private_bit = get_adif_private_bit(p+2); - channel_configuration = get_adif_channel_configuration(p+3); - original_copy = get_adif_original_copy(p+3); - home = get_adif_home(p+3); - copyright_identification_bit = get_adif_copyright_idication_bit(p+3); - copyright_identification_start = get_adif_copyright_idication_start(p+3); - aac_frame_length = get_adif_aac_frame_length(p+3); - adts_buffer_fullness = get_adts_buffer_fullness(p+5); - no_raw_data_blocks_in_frame = get_adts_no_raw_data_blocks_in_frame(p+5); - - /* - * とりあえず return は サンプリング周波数としておく - * 本当は取得した情報を構造体にして返却する方がいいのだろうけど、 - * 利用する予定もないので取得するだけにしておく - */ - return sampling_frequency_table[sampling_frequency_index]; -} - -static int get_adif_id(uint8_t *p) -{ - return ((*p & 0x08) >>3); -} - -static int get_adif_layer(uint8_t *p) -{ - return ((*p & 0x06) >>1); -} - -static int get_adif_protection_absent(uint8_t *p) -{ - return (*p & 0x01); -} - -static int get_adif_profile(uint8_t *p) -{ - return ((*p & 0xc0) >>6); -} - -static int get_adif_sampling_frequency_index(uint8_t *p) -{ - return ((*p & 0x3c) >>2); -} - -static int get_adif_private_bit(uint8_t *p) -{ - return ((*p & 0x02) >>1); -} - -static int get_adif_channel_configuration(uint8_t *p) -{ - return ((*p & 0x01) <<2 | (*(p+1) & 0xc0 >>6) ); -} - -static int get_adif_original_copy(uint8_t *p) -{ - return (*p & 0x20 >>5 ); -} - -static int get_adif_home(uint8_t *p) -{ - return (*p & 0x10 >>4 ); -} - -static int get_adif_copyright_idication_bit(uint8_t *p) -{ - return (*p & 0x08 >>3 ); -} - -static int get_adif_copyright_idication_start(uint8_t *p) -{ - return (*p & 0x04 >>2 ); -} - -static int get_adif_aac_frame_length(uint8_t *p) -{ - return ( ((*p & 0x02) <<11) || ((*(p+1) & 0xff) <<3) || ((*(p+2) & 0xe0) >>5) ); -} - -static int get_adts_buffer_fullness(uint8_t *p) -{ - return ( ((*p & 0x1f) <<6) || ((*(p+1) &0xfc) >>2)); -} - -static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p) -{ - return (*p & 0x03); -} - -#define GOP_START_CODE (0x000001b8) -/* GOP START CODE を検索する */ -static int search_gop_start_code(splitesbuf_t *esbuf) -{ - uint32_t gop_start_code = GOP_START_CODE; - int i; - - /* 小さすぎる */ - if ( esbuf->size < sizeof gop_start_code ){ - return -1; - } - for(i = 0; i < esbuf->size - sizeof gop_start_code; i++) { - if ( (AV_RB32(esbuf->buffer +i)) == gop_start_code ) { - return i; - } - } - return -1; -} - -/* ES 出力するファイルを作成する */ -static int creat_es_file(splitter *sp, int sid, int pid, int av_flag) -{ - /* - * 出力ESファイルの命名規則は以下とする - * - * ファイル名のベースは --es オプションの引数 - * 以下の形式で命名して、ファイルオープンまで実施する。 - * ファイル名prefix_SID_AVのプログラム内番号.m2v - * ファイル名prefix_SID_AVのプログラム内番号.aac - * - * !!注意!! - * MPEG-2/MPEG-4 AAC 以外のオーディオが来た場合の処理が未実装 - */ - - char filename[PATH_MAX]; - int size = 0; - char *suffix = NULL; - int av_nb = 0; - char suffix_a[] = "aac"; - char suffix_v[] = "m2v"; - filename[0] = '\0'; - - /* ちょっとこの辺のコードイケてないので後から直すかも */ - if ( av_flag == TSS_STREAM_TYPE_VIDEO ) { - suffix = suffix_v; - av_nb = sp->program[sid].video_nb -1; - } else if ( av_flag == TSS_STREAM_TYPE_AUDIO ){ - suffix = suffix_a; - av_nb = sp->program[sid].audio_nb -1; - } else { - /* ここはありえない */ - return -1; - } - size = strlen(sp->filename); - - if ( size +16 < sizeof(filename) ) { - snprintf(filename, sizeof(filename), "%s_%05d_%02d.%s", sp->filename, sid, av_nb, suffix); - filename[PATH_MAX-1] = '\0'; - } else { - /* ファイル名つけられなくて困るでござるの巻 */ - return -1; - } - umask(0133); - if ( !(sp->esbuf[pid]->fd = open(filename, O_CREAT|O_APPEND|O_RDWR, 00644)) ) { - fprintf(stderr, "cannot open es out file. file[%s].\n", filename); - return -1; - } - return 0; -} - -static time_t cue2time(char *yyyymmddhhmiss) -{ - struct tm cue_tm; - time_t cue_time; - char *p; - int i, j; - char str_yyyy[5]; - char str_mm[3]; - char str_dd[3]; - char str_hh[3]; - char str_mi[3]; - char str_ss[3]; - str_yyyy[0] = '\0'; - str_mm[0] = '\0'; - str_dd[0] = '\0'; - str_hh[0] = '\0'; - str_mi[0] = '\0'; - str_ss[0] = '\0'; - - p = yyyymmddhhmiss; - i = strlen(p); - j = strspn(p, LIST_DECIMAL); - if ( i != j && i != 14 ) { - /* 数字以外混ぜるな */ - return -1; - } - strncpy(str_yyyy, yyyymmddhhmiss, 4); - strncpy(str_mm, yyyymmddhhmiss+4, 2); - strncpy(str_dd, yyyymmddhhmiss+6, 2); - strncpy(str_hh, yyyymmddhhmiss+8, 2); - strncpy(str_mi, yyyymmddhhmiss+10, 2); - strncpy(str_ss, yyyymmddhhmiss+12, 2); - str_yyyy[4] = '\0'; - str_mm[2] = '\0'; - str_dd[2] = '\0'; - str_hh[2] = '\0'; - str_mi[2] = '\0'; - str_ss[2] = '\0'; - - cue_tm.tm_sec = atoi(str_ss); - cue_tm.tm_min = atoi(str_mi); - cue_tm.tm_hour = atoi(str_hh); - cue_tm.tm_mday = atoi(str_dd); - cue_tm.tm_mon = atoi(str_mm)-1; - cue_tm.tm_year = atoi(str_yyyy)-1900; - cue_tm.tm_isdst = -1; - cue_time = mktime(&cue_tm); - return cue_time; -} - -/* PCR の PID を検索する */ -static int search_pcr_pid(splitter *sp, int pid) -{ - int i; - for ( i=0; i < MAX_SERVICES; i++ ) { - if ( sp->pcr[i].pid == pid ) { - return i; - } - } - return -1; -} diff -r 215a51fa3df3 -r 9c7bc6c0327e recpt1/tssplitter_lite.h --- a/recpt1/tssplitter_lite.h Sun Jul 25 16:40:42 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,216 +0,0 @@ -/* -*- tab-width: 4; indent-tabs-mode: t -*- */ -/* vim: set ts=4 sts=4 sw=4 noexpandtab number : */ -/* tssplitter_lite.h -- split TS stream program's header. - - Copyright 2009 querulous - Copyright 2010 Naoya OYAMA - - 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 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . */ -#ifndef _TS_SPLITTER_LITE_H_ -#define _TS_SPLITTER_LITE_H_ - -#define __STDC_FORMAT_MACROS -#include -#include -#include - -#define LENGTH_PACKET (188) -#define MAX_PID (8192) -#define MAX_SERVICES (50) -#define LENGTH_CRC_DATA (176) -#define false (0) -#define true (1) - -#define TSS_SUCCESS (0) -#define TSS_ERROR (-1) -#define TSS_NULL (-2) -#define LENGTH_PAT_HEADER (12) -#define C_CHAR_COMMA ',' -#define C_CHAR_DOT '.' -#define LENGTH_TS_HEADER (4) -#define LENGTH_PES_HEADER (9) -#define LENGTH_PTS (5) - -/* 改訂版デジタル放送教科書(上) P101 表1 ARIBでのPSI/SIの種類より参照 */ -#define PAT (0x0000) -//#define PMT /* PATによる間接指定 */ -#define CAT (0x0001) -#define NIT (0x0010) -#define SDT (0x0011) -#define BAT (0x0011) -#define EIT (0x0012) /* 0x0026, 0x0027 */ -#define RST (0x0013) -#define TDT (0x0014) /* 地デジでは使用されない */ -#define TOT (0x0014) -#define LIT (0x0020) /* またはPMTによる間接指定 */ -#define ERT (0x0021) /* またはPMTによる間接指定 */ -//#define ITT /* PMTによる間接指定 */ -#define PCAT (0x0022) -#define BIT (0x0024) -#define NBIT (0x0025) -//#define ECM /* PMTによる間接指定 */ -//#define EMM /* CATによる間接指定 */ -#define LDT (0x0025) -#define DCT (0x0017) -//#define DTL /* DCTによる間接指定 */ -#define DIT (0x001e) -#define SIT (0x001f) -#define SDTT (0x0023) -#define CDT (0x0029) -//#define DSM-CC_Section /* PMTによる間接指定 */ - -/* セクションヘッダ長 */ -/* TS パケットに各セクションを設置する際、該当TSパケットの残サイズが - * いくつ以上あれば書き込めるかの判定に使用する。 - * デコードにはあまり重要では無いかも - */ -#define SECTION_LENGTH_PAT (8) -#define SECTION_LENGTH_PMT (8) -#define SECTION_LENGTH_CAT (8) -#define SECTION_LENGTH_NIT (8) -#define SECTION_LENGTH_BIT (8) -#define SECTION_LENGTH_SDT (11) -#define SECTION_LENGTH_EIT (14) /* H-EIT, M-EIT, L-EIT を示す */ -#define SECTION_LENGTH_SDTT (15) -#define SECTION_LENGTH_CDT (13) -#define SECTION_LENGTH_TOT (10) - -enum { - PTS_FLAG = 0x80, - DTS_FLAG = 0x40, - ESCR_FLAG = 0x20, - ES_RATE_FLAG = 0x10, - DSM_TRICK_MODE_FLAG = 0x08, - COPY_INFO_FLAG = 0x04, - CRC_FLAG = 0x02, - EXTENSION_FLAG = 0x01 -}; - -enum { - PES_PRIVATE_DATA_FLAG = 0x80, - PACK_HEADER_FIELD_FLAG = 0x40, - PROGRAM_PACKET_SEQUENCE_COUNTER = 0x20, - PSTD_BUFFER_FLAG = 0x10, - PES_EXTENSION_FLAG2 = 0x01 -}; - -/* - * PCRからSTCを生成する処理方式(案) - * 1. PCRを二つ取得するまでループ(1.4.までは初期処理で実施すること) - * 2. ループ開始時刻(PCR)と、ループ抜けた時刻(PCR)の差分を取る - * 3. ループ開始〜終了の間に処理したパケット数を数える - * 4. (2.のPCRの進んだ時間/3.のパケット数) の計算によって、1パケットによって進むPCRを想定する - * 5. TS受信時に、1つのパケットを処理する度に4.の想定する経過時刻をPCRに足し込んでSTCとする - * 6. PCRを新規に取得したら、STCを補正(そのまま代入)する - */ -#define MAX_VIDEO (16) -#define MAX_AUDIO (32) -typedef struct _program_t -{ - int64_t stc; - int64_t cue; /* 録画開始時刻 */ - int pmt_packet_id; /* 該当Program(Service ID)に対応するPMT */ - int pmt_version; /* 該当Program(Service ID)に対応するPMTのVersion */ - int pcr_packet_id; /* 該当Program(Service ID)のPCRを保持するPID */ - int64_t pcr1; /* PCR1 */ - int64_t pcr2; /* PCR2 */ - int pcr_packet_nb; /* 直前のPCRを受信したときのパケット数 */ - int packet_nb; /* 直前のProgramの処理を実施したときのパケット数 */ - int64_t pcr_incr; /* 該当Program(Service ID)に於いて、1つのTSパケットを処理した時に経過する(と想定する時間) */ - int video_start; /* VODEO0を蓄積開始している? */ - int64_t video_pts; /* 最後に処理したVODEO0のESのPTS */ - int video_nb; /* PMT に存在するビデオストリームの数 */ - int audio_nb; /* PMT に存在する音声ストリームの数 */ - int video[MAX_VIDEO]; /* PS出力する場合に使うかも */ - int audio[MAX_AUDIO]; /* PS出力する場合に使うかも */ -} program_t; -/* - * program_t をサービスID分準備して、使用するイメージでいたけど、 - * サービスIDの最大値は0xFFFFであるので、静的に確保すると stack が簡単に溢るので、 - * 実行時に malloc とするかなぁ - * 本当は必要なだけallocするのが好ましいのだけど… - */ - -typedef struct _splitpesbuf_t -{ - program_t *Program; - int64_t pts; - int64_t dts; - int size; - u_char buffer[128*1024]; -} splitpesbuf_t; - -typedef struct _splitesbuf_t -{ - program_t *Program; - int64_t pts; - int64_t dts; - int started; /* 該当ESが蓄積開始しているか */ - int random_access_indicator; /* TS の random_access_indicator */ - int fd; /* 該当ESのfd */ - int size; - u_char buffer[3*1024*1024]; -} splitesbuf_t; - -/* PCR 共有用構造体 */ -typedef struct _pcr_t -{ - int pid; - int sid_nb; /* PCRを参照しているServiceID(ProgramID)の数 */ - int sid[MAX_SERVICES]; -} pcr_t; - - -/** - * splitter構造体 - */ -typedef struct splitter { - char *filename; /* ファイル名を上位からもらってくるためのポインタ */ - char *arg_cue; /* 引数で取得してきた録画開始時刻(HHMISS) */ - int esout; /* ES出力する? */ - unsigned char pids[MAX_PID]; - unsigned char pmt_pids[MAX_PID]; - uint8_t cat_pids[MAX_PID]; - uint8_t pcr_pids[MAX_PID]; /* PCRは複数ServiceID(ProgramID)で共有される */ - pcr_t pcr[MAX_SERVICES]; - int pcr_nb; - unsigned char* pat; - char** sid_list; - unsigned char pat_count; - int pmt_retain; - int pmt_counter; - int avail_pmts[MAX_SERVICES]; - int num_pmts; - splitpesbuf_t *pesbuf[MAX_PID]; - splitesbuf_t *esbuf[MAX_PID]; - program_t *program; - int pid_sid_table[MAX_PID]; /* pid to sid の変換を行うためのテーブル */ - time_t time_cue; - time_t time_tot; - int tot_packet_nb; /* TOT受信時のパケット受信数 */ -} splitter; - -/* b25 decoder would hoard up large chank */ -typedef struct _splitbuf_t -{ - int size; - u_char buffer[1024*1024]; -} splitbuf_t; - -splitter* split_startup(char *sid, char *filename, char *cue_time); -int split_select(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); -void split_shutdown(splitter *sp); -int split_ts(splitter *splitter, ARIB_STD_B25_BUFFER *sbuf, splitbuf_t *dbuf); - -#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/COPYING Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state 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 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff -r 215a51fa3df3 -r 9c7bc6c0327e src/Makefile.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.in Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,61 @@ +DESTDIR = +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +CC = @CC@ + +TARGET = recpt1 +TARGET2 = recpt1ctl +TARGET3 = checksignal +TARGETS = $(TARGET) $(TARGET2) $(TARGET3) +RELEASE_VERSION = "1.1.0" + +CPPFLAGS = -I../driver -Wall -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +CFLAGS = -O2 -g -pthread + +LIBS = @LIBS@ +LIBS2 = +LIBS3 = -lpthread -lm +LDFLAGS = + +OBJS = recpt1.o decoder.o mkpath.o tssplitter_lite.o +OBJS2 = recpt1ctl.o +OBJS3 = checksignal.o +OBJALL = $(OBJS) $(OBJS2) $(OBJS3) +DEPEND = .deps + +all: $(TARGETS) + +clean: + rm -f $(OBJALL) $(TARGETS) $(DEPEND) version.h + +distclean: clean + rm -f Makefile config.h config.log config.status + +maintainer-clean: distclean + rm -fr configure config.h.in aclocal.m4 autom4te.cache *~ + +$(TARGET): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) + +$(TARGET2): $(OBJS2) + $(CC) $(LDFLAGS) -o $@ $(OBJS2) $(LIBS2) + +$(TARGET3): $(OBJS3) + $(CC) $(LDFLAGS) -o $@ $(OBJS3) $(LIBS3) + +$(DEPEND): version.h + $(CC) -MM $(OBJS:.o=.c) $(OBJS2:.o=.c) $(CPPFLAGS) > $@ + +version.h: + revh=`hg parents --template 'const char *version = "r{rev}:{node|short} ({date|shortdate})";\n' 2>/dev/null`; \ + if [ -n "$$revh" ] ; then \ + echo "$$revh" > $@; \ + else \ + echo "const char *version = \"$(RELEASE_VERSION)\";" > $@; \ + fi + +install: $(TARGET) + install -m 755 $(TARGETS) $(DESTDIR)$(bindir) + +-include .deps diff -r 215a51fa3df3 -r 9c7bc6c0327e src/autogen.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/autogen.sh Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,59 @@ +#!/bin/sh + +[ -f configure.ac ] || { + echo "autogen.sh: run this command only at the top of a recpt1 source tree." + exit 1 +} + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile recpt1." + echo "Get ftp://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.62.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOCONF=yes +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile recpt1." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.10.1.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake/automake-1.10.1.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +# if no autoconf, don't bother testing for autoheader +test -n "$NO_AUTOCONF" || (autoheader --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`autoheader'. The version of \`autoheader'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/autoconf/autoconf-2.62.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +echo "Generating configure script and Makefiles for recpt1." + +echo "Running aclocal ..." +aclocal -I . +echo "Running autoheader ..." +autoheader +echo "Running autoconf ..." +autoconf diff -r 215a51fa3df3 -r 9c7bc6c0327e src/channels/sample.recpt1-channels-chiba --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/channels/sample.recpt1-channels-chiba Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,10 @@ + 20ch: Tokyo MX TV + 21ch: Fuji TV + 22ch: TBS + 23ch: TV Tokyo + 24ch: TV Asahi + 25ch: Nihon TV + 26ch: NHK Educational + 27ch: NHK General + 28ch: Housou Daigaku + 30ch: CTC Chiba TV diff -r 215a51fa3df3 -r 9c7bc6c0327e src/channels/sample.recpt1-channels-kanazawa --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/channels/sample.recpt1-channels-kanazawa Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,6 @@ + 13ch: NHK Educational + 14ch: MRO + 15ch: NHK General + 16ch: Ishikawa TV + 17ch: TV Kanazawa + 23ch: Hokuriku Asahi diff -r 215a51fa3df3 -r 9c7bc6c0327e src/channels/sample.recpt1-channels-nagoya --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/channels/sample.recpt1-channels-nagoya Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,8 @@ + 13ch: NHK Educational + 18ch: CBC + 19ch: Chukyo TV + 20ch: NHK Gemeral + 21ch: Tokai TV + 22ch: Nagoya TV (mei tere) + 23ch: TV Aichi + 27ch: Mie TV diff -r 215a51fa3df3 -r 9c7bc6c0327e src/channels/sample.recpt1-channels-tokyo --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/channels/sample.recpt1-channels-tokyo Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,9 @@ + 20ch: Tokyo MX TV + 21ch: Fuji TV + 22ch: TBS + 23ch: TV Tokyo + 24ch: TV Asahi + 25ch: Nihon TV + 26ch: NHK Educational + 27ch: NHK General + 28ch: Housou Daigaku diff -r 215a51fa3df3 -r 9c7bc6c0327e src/checksignal.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/checksignal.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,497 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "pt1_ioctl.h" + +#include "config.h" +#include "decoder.h" +#include "recpt1.h" +#include "version.h" +#include "mkpath.h" + +#include +#include +#include "pt1_dev.h" +#include "tssplitter_lite.h" + +#define MAX_RETRY (2) + +/* type definitions */ +typedef int boolean; + +typedef struct thread_data { + int ch; + int lnb; /* LNB voltage */ + int tfd; /* tuner fd */ + ISDB_T_FREQ_CONV_TABLE *table; +} thread_data; + +/* globals */ +boolean f_exit = FALSE; +boolean use_bell = FALSE; + +/* prototypes */ +int tune(char *channel, thread_data *tdata, char *device); +int close_tuner(thread_data *tdata); + +void +cleanup(thread_data *tdata) +{ + f_exit = TRUE; +} + +/* will be signal handler thread */ +void * +process_signals(void *data) +{ + sigset_t waitset; + int sig; + thread_data *tdata = (thread_data *)data; + + sigemptyset(&waitset); + sigaddset(&waitset, SIGINT); + sigaddset(&waitset, SIGTERM); + sigaddset(&waitset, SIGUSR1); + + sigwait(&waitset, &sig); + + switch(sig) { + case SIGINT: + fprintf(stderr, "\nSIGINT received. cleaning up...\n"); + cleanup(tdata); + break; + case SIGTERM: + fprintf(stderr, "\nSIGTERM received. cleaning up...\n"); + cleanup(tdata); + break; + case SIGUSR1: /* normal exit*/ + cleanup(tdata); + break; + } + + return NULL; /* dummy */ +} + +void +init_signal_handlers(pthread_t *signal_thread, thread_data *tdata) +{ + sigset_t blockset; + + sigemptyset(&blockset); + sigaddset(&blockset, SIGINT); + sigaddset(&blockset, SIGTERM); + sigaddset(&blockset, SIGUSR1); + + if(pthread_sigmask(SIG_BLOCK, &blockset, NULL)) + fprintf(stderr, "pthread_sigmask() failed.\n"); + + pthread_create(signal_thread, NULL, process_signals, tdata); +} + +/* lookup frequency conversion table*/ +ISDB_T_FREQ_CONV_TABLE * +searchrecoff(char *channel) +{ + int lp; + + for(lp = 0; isdb_t_conv_table[lp].parm_freq != NULL; lp++) { + /* return entry number in the table when strings match and + * lengths are same. */ + if((memcmp(isdb_t_conv_table[lp].parm_freq, channel, + strlen(channel)) == 0) && + (strlen(channel) == strlen(isdb_t_conv_table[lp].parm_freq))) { + return &isdb_t_conv_table[lp]; + } + } + return NULL; +} + +void +show_usage(char *cmd) +{ + fprintf(stderr, "Usage: \n%s [--device devicefile] [--lnb voltage] [--bell] channel\n", cmd); + fprintf(stderr, "\n"); +} + +void +show_options(void) +{ + fprintf(stderr, "Options:\n"); + fprintf(stderr, "--device devicefile: Specify devicefile to use\n"); + fprintf(stderr, "--lnb voltage: Specify LNB voltage (0, 11, 15)\n"); + fprintf(stderr, "--bell: Notify signal quality by bell\n"); + fprintf(stderr, "--help: Show this help\n"); + fprintf(stderr, "--version: Show version\n"); + fprintf(stderr, "--list: Show channel list\n"); +} + +void +show_channels(void) +{ + FILE *f; + char *home; + char buf[255], filename[255]; + + fprintf(stderr, "Available Channels:\n"); + + home = getenv("HOME"); + sprintf(filename, "%s/.recpt1-channels", home); + f = fopen(filename, "r"); + if(f) { + while(fgets(buf, 255, f)) + fprintf(stderr, "%s", buf); + fclose(f); + } + else + fprintf(stderr, "13-62: Terrestrial Channels\n"); + + fprintf(stderr, "101ch: NHK BS1\n"); + fprintf(stderr, "102ch: NHK BS2\n"); + fprintf(stderr, "103ch: NHK BShi\n"); + fprintf(stderr, "141ch: BS Nittele\n"); + fprintf(stderr, "151ch: BS Asahi\n"); + fprintf(stderr, "161ch: BS-TBS\n"); + fprintf(stderr, "171ch: BS Japan\n"); + fprintf(stderr, "181ch: BS Fuji\n"); + fprintf(stderr, "191ch: WOWOW\n"); + fprintf(stderr, "192ch: WOWOW2\n"); + fprintf(stderr, "193ch: WOWOW3\n"); + fprintf(stderr, "200ch: Star Channel\n"); + fprintf(stderr, "211ch: BS11 Digital\n"); + fprintf(stderr, "222ch: TwellV\n"); + fprintf(stderr, "C13-C63: CATV Channels\n"); + fprintf(stderr, "CS2-CS24: CS Channels\n"); +} + +float +getsignal_isdb_s(int signal) +{ + /* apply linear interpolation */ + static const float afLevelTable[] = { + 24.07f, // 00 00 0 24.07dB + 24.07f, // 10 00 4096 24.07dB + 18.61f, // 20 00 8192 18.61dB + 15.21f, // 30 00 12288 15.21dB + 12.50f, // 40 00 16384 12.50dB + 10.19f, // 50 00 20480 10.19dB + 8.140f, // 60 00 24576 8.140dB + 6.270f, // 70 00 28672 6.270dB + 4.550f, // 80 00 32768 4.550dB + 3.730f, // 88 00 34816 3.730dB + 3.630f, // 88 FF 35071 3.630dB + 2.940f, // 90 00 36864 2.940dB + 1.420f, // A0 00 40960 1.420dB + 0.000f // B0 00 45056 -0.01dB + }; + + unsigned char sigbuf[4]; + memset(sigbuf, '\0', sizeof(sigbuf)); + sigbuf[0] = (((signal & 0xFF00) >> 8) & 0XFF); + sigbuf[1] = (signal & 0xFF); + + /* calculate signal level */ + if(sigbuf[0] <= 0x10U) { + /* clipped maximum */ + return 24.07f; + } + else if (sigbuf[0] >= 0xB0U) { + /* clipped minimum */ + return 0.0f; + } + else { + /* linear interpolation */ + const float fMixRate = + (float)(((unsigned short)(sigbuf[0] & 0x0FU) << 8) | + (unsigned short)sigbuf[0]) / 4096.0f; + return afLevelTable[sigbuf[0] >> 4] * (1.0f - fMixRate) + + afLevelTable[(sigbuf[0] >> 4) + 0x01U] * fMixRate; + } +} + +void +do_bell(int bell) +{ + int i; + for(i=0; i < bell; i++) { + fprintf(stderr, "\a"); + usleep(400000); + } +} + +void +calc_cn(int fd, int type) +{ + int rc; + double P; + double CNR; + int bell = 0; + + if(ioctl(fd, GET_SIGNAL_STRENGTH, &rc) < 0) { + fprintf(stderr, "Tuner Select Error\n"); + return ; + } + + if(type == CHTYPE_GROUND) { + P = log10(5505024/(double)rc) * 10; + CNR = (0.000024 * P * P * P * P) - (0.0016 * P * P * P) + + (0.0398 * P * P) + (0.5491 * P)+3.0965; + } + else { + CNR = getsignal_isdb_s(rc); + } + + if(CNR >= 30.0) + bell = 3; + else if(CNR >= 15.0 && CNR < 30.0) + bell = 2; + else if(CNR < 15.0) + bell = 1; + + fprintf(stderr, "\rC/N = %fdB", CNR); + if(use_bell) + do_bell(bell); +} + +int +tune(char *channel, thread_data *tdata, char *device) +{ + char **tuner; + int num_devs; + int lp; + FREQUENCY freq; + + /* get channel */ + tdata->table = searchrecoff(channel); + if(tdata->table == NULL) { + fprintf(stderr, "Invalid Channel: %s\n", channel); + return 1; + } + + freq.frequencyno = tdata->table->set_freq; + freq.slot = tdata->table->add_freq; + + /* open tuner */ + /* case 1: specified tuner device */ + if(device) { + tdata->tfd = open(device, O_RDONLY); + if(tdata->tfd < 0) { + fprintf(stderr, "Cannot open tuner device: %s\n", device); + return 1; + } + + /* power on LNB */ + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { + fprintf(stderr, "Power on LNB failed: %s\n", device); + } + } + + /* tune to specified channel */ + while(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { + if(f_exit) { + close_tuner(tdata); + return 1; + } + fprintf(stderr, "No signal. Still trying: %s\n", device); + } + + fprintf(stderr, "device = %s\n", device); + tdata->ch = atoi(channel); + } + else { + /* case 2: loop around available devices */ + if(tdata->table->type == CHTYPE_SATELLITE) { + tuner = bsdev; + num_devs = NUM_BSDEV; + } + else { + tuner = isdb_t_dev; + num_devs = NUM_ISDB_T_DEV; + } + + for(lp = 0; lp < num_devs; lp++) { + int count = 0; + + tdata->tfd = open(tuner[lp], O_RDONLY); + if(tdata->tfd >= 0) { + /* power on LNB */ + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { + fprintf(stderr, "Warning: Power on LNB failed: %s\n", tuner[lp]); + } + } + + /* tune to specified channel */ + while(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0 && + count < MAX_RETRY) { + if(f_exit) { + close_tuner(tdata); + return 1; + } + fprintf(stderr, "No signal. Still trying: %s\n", tuner[lp]); + count++; + } + + if(count >= MAX_RETRY) { + close_tuner(tdata); + count = 0; + continue; + } + + fprintf(stderr, "device = %s\n", tuner[lp]); + break; /* found suitable tuner */ + } + } + + /* all tuners cannot be used */ + if(tdata->tfd < 0) { + fprintf(stderr, "Cannot tune to the specified channel\n"); + return 1; + } + else { + tdata->ch = atoi(channel); + } + } + + return 0; /* success */ +} + +int +close_tuner(thread_data *tdata) +{ + int rv = 0; + + if(tdata->tfd == -1) + return rv; + + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_DISABLE, 0) < 0) { + rv = 1; + } + } + close(tdata->tfd); + tdata->tfd = -1; + + return rv; +} + +int +main(int argc, char **argv) +{ + pthread_t signal_thread; + static thread_data tdata; + int result; + int option_index; + struct option long_options[] = { + { "bell", 0, NULL, 'b'}, + { "help", 0, NULL, 'h'}, + { "version", 0, NULL, 'v'}, + { "list", 0, NULL, 'l'}, + { "LNB", 1, NULL, 'n'}, + { "lnb", 1, NULL, 'n'}, + { "device", 1, NULL, 'd'}, + {0, 0, NULL, 0} /* terminate */ + }; + + char *device = NULL; + int val; + char *voltage[] = {"0V", "11V", "15V"}; + + while((result = getopt_long(argc, argv, "bhvln:d:", + long_options, &option_index)) != -1) { + switch(result) { + case 'b': + use_bell = TRUE; + break; + case 'h': + fprintf(stderr, "\n"); + show_usage(argv[0]); + fprintf(stderr, "\n"); + show_options(); + fprintf(stderr, "\n"); + show_channels(); + fprintf(stderr, "\n"); + exit(0); + break; + case 'v': + fprintf(stderr, "%s %s\n", argv[0], version); + fprintf(stderr, "signal check utility for PT1/2 digital tuner.\n"); + exit(0); + break; + case 'l': + show_channels(); + exit(0); + break; + /* following options require argument */ + case 'n': + val = atoi(optarg); + switch(val) { + case 11: + tdata.lnb = 1; + break; + case 15: + tdata.lnb = 2; + break; + default: + tdata.lnb = 0; + break; + } + fprintf(stderr, "LNB = %s\n", voltage[tdata.lnb]); + break; + case 'd': + device = optarg; + break; + } + } + + if(argc - optind < 1) { + fprintf(stderr, "channel must be specified!\n"); + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + return 1; + } + + /* spawn signal handler thread */ + init_signal_handlers(&signal_thread, &tdata); + + /* tune */ + if(tune(argv[optind], &tdata, device) != 0) + return 1; + + while(1) { + if(f_exit) + break; + /* show signal strength */ + calc_cn(tdata.tfd, tdata.table->type); + sleep(1); + } + + /* wait for signal thread */ + pthread_kill(signal_thread, SIGUSR1); + pthread_join(signal_thread, NULL); + + /* close tuner */ + if(close_tuner(&tdata) != 0) + return 1; + + return 0; +} diff -r 215a51fa3df3 -r 9c7bc6c0327e src/configure.ac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/configure.ac Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,23 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.59) +AC_INIT([recpt1], [1.0.0], yaz@honeyplanet.jp) +AC_CONFIG_SRCDIR([recpt1.c]) +AC_CONFIG_HEADERS([config.h]) + +# Checks for programs. +AC_PROG_CC + +# Checks for b25 support. +AC_ARG_ENABLE(b25, + [AS_HELP_STRING([--enable-b25],[enable b25 support])], + [AC_CHECK_LIB([arib25], [create_arib_std_b25], , [AC_MSG_WARN(libarb25 is not available.)], [-lpcsclite])] +) + +# Checks for libraries. +AC_CHECK_LIB([m], [log10]) +AC_CHECK_LIB([pthread], [pthread_kill]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff -r 215a51fa3df3 -r 9c7bc6c0327e src/decoder.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/decoder.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,141 @@ +#include +#include + +#include "decoder.h" + +#ifdef HAVE_LIBARIB25 + +decoder * +b25_startup(decoder_options *opt) +{ + decoder *dec = calloc(1, sizeof(decoder)); + int code; + const char *err = NULL; + + dec->b25 = create_arib_std_b25(); + if(!dec->b25) { + err = "create_arib_std_b25 failed"; + goto error; + } + + code = dec->b25->set_multi2_round(dec->b25, opt->round); + if(code < 0) { + err = "set_multi2_round failed"; + goto error; + } + + code = dec->b25->set_strip(dec->b25, opt->strip); + if(code < 0) { + err = "set_strip failed"; + goto error; + } + + code = dec->b25->set_emm_proc(dec->b25, opt->emm); + if(code < 0) { + err = "set_emm_proc failed"; + goto error; + } + + dec->bcas = create_b_cas_card(); + if(!dec->bcas) { + err = "create_b_cas_card failed"; + goto error; + } + code = dec->bcas->init(dec->bcas); + if(code < 0) { + err = "bcas->init failed"; + goto error; + } + + code = dec->b25->set_b_cas_card(dec->b25, dec->bcas); + if(code < 0) { + err = "set_b_cas_card failed"; + goto error; + } + + return dec; + +error: + fprintf(stderr, "%s\n", err); + free(dec); + return NULL; +} + +int +b25_shutdown(decoder *dec) +{ + dec->b25->release(dec->b25); + dec->bcas->release(dec->bcas); + free(dec); + + return 0; +} + +int +b25_decode(decoder *dec, ARIB_STD_B25_BUFFER *sbuf, ARIB_STD_B25_BUFFER *dbuf) +{ + int code; + + code = dec->b25->put(dec->b25, sbuf); + if(code < 0) { + fprintf(stderr, "b25->put failed\n"); + return code; + } + + code = dec->b25->get(dec->b25, dbuf); + if(code < 0) { + fprintf(stderr, "b25->get failed\n"); + return code; + } + + return code; +} + +int +b25_finish(decoder *dec, ARIB_STD_B25_BUFFER *sbuf, ARIB_STD_B25_BUFFER *dbuf) +{ + int code; + + code = dec->b25->flush(dec->b25); + if(code < 0) { + fprintf(stderr, "b25->flush failed\n"); + return code; + } + + code = dec->b25->get(dec->b25, dbuf); + if(code < 0) { + fprintf(stderr, "b25->get failed\n"); + return code; + } + + return code; +} + +#else + +/* functions */ +decoder *b25_startup(decoder_options *opt) +{ + return NULL; +} + +int b25_shutdown(decoder *dec) +{ + return 0; +} + +int b25_decode(decoder *dec, + ARIB_STD_B25_BUFFER *sbuf, + ARIB_STD_B25_BUFFER *dbuf) +{ + return 0; +} + +int b25_finish(decoder *dec, + ARIB_STD_B25_BUFFER *sbuf, + ARIB_STD_B25_BUFFER *dbuf) +{ + return 0; +} + +#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/decoder.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/decoder.h Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,53 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +#ifndef _DECODER_H_ +#define _DECODER_H_ + +#include "config.h" + +#ifdef HAVE_LIBARIB25 + +#include +#include + +typedef struct decoder { + ARIB_STD_B25 *b25; + B_CAS_CARD *bcas; +} decoder; + +typedef struct decoder_options { + int round; + int strip; + int emm; +} decoder_options; + +#else + +typedef struct { + int size; + void *data; +} ARIB_STD_B25_BUFFER; + +typedef struct decoder { + void *dummy; +} decoder; + +typedef struct decoder_options { + int round; + int strip; + int emm; +} decoder_options; + +#endif + +/* prototypes */ +decoder *b25_startup(decoder_options *opt); +int b25_shutdown(decoder *dec); +int b25_decode(decoder *dec, + ARIB_STD_B25_BUFFER *sbuf, + ARIB_STD_B25_BUFFER *dbuf); +int b25_finish(decoder *dec, + ARIB_STD_B25_BUFFER *sbuf, + ARIB_STD_B25_BUFFER *dbuf); + + +#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/mkpath.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mkpath.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,54 @@ +/* mkpath is originally written by Jonathan Leffler. + * see "http://stackoverflow.com/questions/675039/how-can-i-create-directory-tree-in-c-linux" for detail. + * copyright: (C) JLSS 1990-91,1997-98,2001,2005,2008 + */ + +#include +#include +#include +#include +#include + +static int +do_mkdir(const char *path, mode_t mode) +{ + struct stat st; + int status = 0; + + if (stat(path, &st) != 0) { + /* Directory does not exist */ + if (mkdir(path, mode) != 0) + status = -1; + } + else if (!S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + status = -1; + } + + return(status); +} + +int +mkpath(const char *path, mode_t mode) +{ + char *pp; + char *sp; + int status; + char *copypath = strdup(path); + + status = 0; + pp = copypath; + while (status == 0 && (sp = strchr(pp, '/')) != 0) { + if (sp != pp) { + /* Neither root nor double slash in path */ + *sp = '\0'; + status = do_mkdir(copypath, mode); + *sp = '/'; + } + pp = sp + 1; + } + if (status == 0) + status = do_mkdir(path, mode); + free(copypath); + return (status); +} diff -r 215a51fa3df3 -r 9c7bc6c0327e src/mkpath.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mkpath.h Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,6 @@ +#ifndef _MKPATH_H_ +#define _MKPATH_H_ + +int mkpath(const char *path, mode_t mode); + +#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/pt1_dev.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pt1_dev.h Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,185 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +#ifndef _PT1_DEV_H_ +#define _PT1_DEV_H_ + +char *bsdev[NUM_BSDEV] = { + "/dev/pt1video1", + "/dev/pt1video0", + "/dev/pt1video5", + "/dev/pt1video4", + "/dev/pt1video9", + "/dev/pt1video8", + "/dev/pt1video13", + "/dev/pt1video12" +}; +char *isdb_t_dev[NUM_ISDB_T_DEV] = { + "/dev/pt1video2", + "/dev/pt1video3", + "/dev/pt1video6", + "/dev/pt1video7", + "/dev/pt1video10", + "/dev/pt1video11", + "/dev/pt1video14", + "/dev/pt1video15" +}; + +// 変換テーブル(ISDB-T用) +// 実際にioctl()を行う値の部分はREADMEを参照の事。 +// BS/CSの設定値およびスロット番号は +// http://www5e.biglobe.ne.jp/~kazu_f/digital-sat/index.htmlより取得。 +// + +ISDB_T_FREQ_CONV_TABLE isdb_t_conv_table[] = { + { 0, CHTYPE_SATELLITE, 0, "151"}, /* 151ch:BS朝日 */ + { 0, CHTYPE_SATELLITE, 1, "161"}, /* 161ch:BS-TBS */ + { 1, CHTYPE_SATELLITE, 0, "191"}, /* 191ch:WOWOW */ + { 1, CHTYPE_SATELLITE, 0, "192"}, /* 192ch:WOWOW2 */ + { 1, CHTYPE_SATELLITE, 0, "193"}, /* 193ch:WOWOW3 */ + { 1, CHTYPE_SATELLITE, 1, "171"}, /* 171ch:BSジャパン */ + { 4, CHTYPE_SATELLITE, 0, "211"}, /* 211ch:BS11デジタル */ + { 4, CHTYPE_SATELLITE, 1, "200"}, /* 200ch:スターチャンネル */ + { 4, CHTYPE_SATELLITE, 2, "222"}, /* 222ch:TwellV */ + { 6, CHTYPE_SATELLITE, 0, "141"}, /* 141ch:BS日テレ */ + { 6, CHTYPE_SATELLITE, 1, "181"}, /* 181ch:BSフジ */ + { 7, CHTYPE_SATELLITE, 0, "101"}, /* 101ch:NHK衛星第1放送(BS1) */ + { 7, CHTYPE_SATELLITE, 0, "102"}, /* 102ch:NHK衛星第2放送(BS2) */ + { 7, CHTYPE_SATELLITE, 1, "103"}, /* 103ch:NHKハイビジョン(BShi) */ + { 7, CHTYPE_SATELLITE, 1, "104"}, /* 104ch:NHKハイビジョン(BShi)臨時*/ + { 12, CHTYPE_SATELLITE, 0, "CS2"}, /* ND2: + * 237ch:スター・チャンネル プラス + * 239ch:日本映画専門チャンネルHD + * 306ch:フジテレビCSHD */ + { 13, CHTYPE_SATELLITE, 0, "CS4"}, /* ND4: + * 100ch:e2プロモ + * 256ch:J sports ESPN + * 312ch:FOX + * 322ch:スペースシャワーTV + * 331ch:カートゥーンネットワーク + * 194ch:インターローカルTV + * 334ch:トゥーン・ディズニー */ + { 14, CHTYPE_SATELLITE, 0, "CS6"}, /* ND6: + * 221ch:東映チャンネル + * 222ch:衛星劇場 + * 223ch:チャンネルNECO + * 224ch:洋画★シネフィル・イマジカ + * 292ch:時代劇専門チャンネル + * 238ch:スター・チャンネル クラシック + * 310ch:スーパー!ドラマTV + * 311ch:AXN + * 343ch:ナショナルジオグラフィックチャンネル */ + + { 15, CHTYPE_SATELLITE, 0, "CS8"}, /* ND8: + * 055ch:ショップ チャンネル */ + { 16, CHTYPE_SATELLITE, 0, "CS10"}, /* ND10: + * 228ch:ザ・シネマ + * 800ch:スカチャンHD800 + * 801ch:スカチャン801 + * 802ch:スカチャン802 */ + { 17, CHTYPE_SATELLITE, 0, "CS12"}, /* ND12: + * 260ch:ザ・ゴルフ・チャンネル + * 303ch:テレ朝チャンネル + * 323ch:MTV 324ch:大人の音楽専門TV◆ミュージック・エア + * 352ch:朝日ニュースター + * 353ch:BBCワールドニュース + * 354ch:CNNj + * 361ch:ジャスト・アイ インフォメーション */ + { 18, CHTYPE_SATELLITE, 0, "CS14"}, /* ND14: + * 251ch:J sports 1 + * 252ch:J sports 2 + * 253ch:J sports Plus + * 254ch:GAORA + * 255ch:スカイ・Asports+ */ + { 19, CHTYPE_SATELLITE, 0, "CS16"}, /* ND16: + * 305ch:チャンネル銀河 + * 333ch:アニメシアターX(AT-X) + * 342ch:ヒストリーチャンネル + * 290ch:TAKARAZUKA SKYSTAGE + * 803ch:スカチャン803 + * 804ch:スカチャン804 */ + { 20, CHTYPE_SATELLITE, 0, "CS18"}, /* ND18: + * 240ch:ムービープラスHD + * 262ch:ゴルフネットワーク + * 314ch:LaLa HDHV */ + { 21, CHTYPE_SATELLITE, 0, "CS20"}, /* ND20: + * 258ch:フジテレビ739 + * 302ch:フジテレビ721 + * 332ch:アニマックス + * 340ch:ディスカバリーチャンネル + * 341ch:アニマルプラネット */ + { 22, CHTYPE_SATELLITE, 0, "CS22"}, /* ND22: + * 160ch:C-TBSウェルカムチャンネル + * 161ch:QVC + * 185ch:プライム365.TV + * 293ch:ファミリー劇場 + * 301ch:TBSチャンネル + * 304ch:ディズニー・チャンネル + * 325ch:MUSIC ON! TV + * 330ch:キッズステーション + * 351ch:TBSニュースバード */ + { 23, CHTYPE_SATELLITE, 0, "CS24"}, /* ND24: + * 257ch:日テレG+ + * 291ch:fashiontv + * 300ch:日テレプラス + * 320ch:安らぎの音楽と風景/エコミュージックTV + * 321ch:MusicJapan TV + * 350ch:日テレNEWS24 */ + { 0, CHTYPE_GROUND, 0, "1"}, { 1, CHTYPE_GROUND, 0, "2"}, + { 2, CHTYPE_GROUND, 0, "3"}, { 3, CHTYPE_GROUND, 0, "C13"}, + { 4, CHTYPE_GROUND, 0, "C14"}, { 5, CHTYPE_GROUND, 0, "C15"}, + { 6, CHTYPE_GROUND, 0, "C16"}, { 7, CHTYPE_GROUND, 0, "C17"}, + { 8, CHTYPE_GROUND, 0, "C18"}, { 9, CHTYPE_GROUND, 0, "C19"}, + { 10, CHTYPE_GROUND, 0, "C20"}, { 11, CHTYPE_GROUND, 0, "C21"}, + { 12, CHTYPE_GROUND, 0, "C22"}, { 13, CHTYPE_GROUND, 0, "4"}, + { 14, CHTYPE_GROUND, 0, "5"}, { 15, CHTYPE_GROUND, 0, "6"}, + { 16, CHTYPE_GROUND, 0, "7"}, { 17, CHTYPE_GROUND, 0, "8"}, + { 18, CHTYPE_GROUND, 0, "9"}, { 19, CHTYPE_GROUND, 0, "10"}, + { 20, CHTYPE_GROUND, 0, "11"}, { 21, CHTYPE_GROUND, 0, "12"}, + { 22, CHTYPE_GROUND, 0, "C23"}, { 23, CHTYPE_GROUND, 0, "C24"}, + { 24, CHTYPE_GROUND, 0, "C25"}, { 25, CHTYPE_GROUND, 0, "C26"}, + { 26, CHTYPE_GROUND, 0, "C27"}, { 27, CHTYPE_GROUND, 0, "C28"}, + { 28, CHTYPE_GROUND, 0, "C29"}, { 29, CHTYPE_GROUND, 0, "C30"}, + { 30, CHTYPE_GROUND, 0, "C31"}, { 31, CHTYPE_GROUND, 0, "C32"}, + { 32, CHTYPE_GROUND, 0, "C33"}, { 33, CHTYPE_GROUND, 0, "C34"}, + { 34, CHTYPE_GROUND, 0, "C35"}, { 35, CHTYPE_GROUND, 0, "C36"}, + { 36, CHTYPE_GROUND, 0, "C37"}, { 37, CHTYPE_GROUND, 0, "C38"}, + { 38, CHTYPE_GROUND, 0, "C39"}, { 39, CHTYPE_GROUND, 0, "C40"}, + { 40, CHTYPE_GROUND, 0, "C41"}, { 41, CHTYPE_GROUND, 0, "C42"}, + { 42, CHTYPE_GROUND, 0, "C43"}, { 43, CHTYPE_GROUND, 0, "C44"}, + { 44, CHTYPE_GROUND, 0, "C45"}, { 45, CHTYPE_GROUND, 0, "C46"}, + { 46, CHTYPE_GROUND, 0, "C47"}, { 47, CHTYPE_GROUND, 0, "C48"}, + { 48, CHTYPE_GROUND, 0, "C49"}, { 49, CHTYPE_GROUND, 0, "C50"}, + { 50, CHTYPE_GROUND, 0, "C51"}, { 51, CHTYPE_GROUND, 0, "C52"}, + { 52, CHTYPE_GROUND, 0, "C53"}, { 53, CHTYPE_GROUND, 0, "C54"}, + { 54, CHTYPE_GROUND, 0, "C55"}, { 55, CHTYPE_GROUND, 0, "C56"}, + { 56, CHTYPE_GROUND, 0, "C57"}, { 57, CHTYPE_GROUND, 0, "C58"}, + { 58, CHTYPE_GROUND, 0, "C59"}, { 59, CHTYPE_GROUND, 0, "C60"}, + { 60, CHTYPE_GROUND, 0, "C61"}, { 61, CHTYPE_GROUND, 0, "C62"}, + { 62, CHTYPE_GROUND, 0, "C63"}, { 63, CHTYPE_GROUND, 0, "13"}, + { 64, CHTYPE_GROUND, 0, "14"}, { 65, CHTYPE_GROUND, 0, "15"}, + { 66, CHTYPE_GROUND, 0, "16"}, { 67, CHTYPE_GROUND, 0, "17"}, + { 68, CHTYPE_GROUND, 0, "18"}, { 69, CHTYPE_GROUND, 0, "19"}, + { 70, CHTYPE_GROUND, 0, "20"}, { 71, CHTYPE_GROUND, 0, "21"}, + { 72, CHTYPE_GROUND, 0, "22"}, { 73, CHTYPE_GROUND, 0, "23"}, + { 74, CHTYPE_GROUND, 0, "24"}, { 75, CHTYPE_GROUND, 0, "25"}, + { 76, CHTYPE_GROUND, 0, "26"}, { 77, CHTYPE_GROUND, 0, "27"}, + { 78, CHTYPE_GROUND, 0, "28"}, { 79, CHTYPE_GROUND, 0, "29"}, + { 80, CHTYPE_GROUND, 0, "30"}, { 81, CHTYPE_GROUND, 0, "31"}, + { 82, CHTYPE_GROUND, 0, "32"}, { 83, CHTYPE_GROUND, 0, "33"}, + { 84, CHTYPE_GROUND, 0, "34"}, { 85, CHTYPE_GROUND, 0, "35"}, + { 86, CHTYPE_GROUND, 0, "36"}, { 87, CHTYPE_GROUND, 0, "37"}, + { 88, CHTYPE_GROUND, 0, "38"}, { 89, CHTYPE_GROUND, 0, "39"}, + { 90, CHTYPE_GROUND, 0, "40"}, { 91, CHTYPE_GROUND, 0, "41"}, + { 92, CHTYPE_GROUND, 0, "42"}, { 93, CHTYPE_GROUND, 0, "43"}, + { 94, CHTYPE_GROUND, 0, "44"}, { 95, CHTYPE_GROUND, 0, "45"}, + { 96, CHTYPE_GROUND, 0, "46"}, { 97, CHTYPE_GROUND, 0, "47"}, + { 98, CHTYPE_GROUND, 0, "48"}, { 99, CHTYPE_GROUND, 0, "49"}, + { 100, CHTYPE_GROUND, 0, "50"}, { 101, CHTYPE_GROUND, 0, "51"}, + { 102, CHTYPE_GROUND, 0, "52"}, { 103, CHTYPE_GROUND, 0, "53"}, + { 104, CHTYPE_GROUND, 0, "54"}, { 105, CHTYPE_GROUND, 0, "55"}, + { 106, CHTYPE_GROUND, 0, "56"}, { 107, CHTYPE_GROUND, 0, "57"}, + { 108, CHTYPE_GROUND, 0, "58"}, { 109, CHTYPE_GROUND, 0, "59"}, + { 110, CHTYPE_GROUND, 0, "60"}, { 111, CHTYPE_GROUND, 0, "61"}, + { 112, CHTYPE_GROUND, 0, "62"}, + { 0, 0, 0, NULL} /* terminate */ +}; +#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/pt1_lnbd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pt1_lnbd.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,400 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pt1_ioctl.h" + +#ifdef O_NOFOLLOW +#define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK +#else +#define OPEN_FLAGS O_NONBLOCK +#endif + +/* 腱糸 */ +#define C_RET_OK 0 +#define C_RET_NG -1 +#define MYNAME "pt1_lnb_enabler" +#define PID_DIR "/var/run/" +#define PIDFILENAME PID_DIR MYNAME ".pid" +#define NUMBER "0123456789" +#define device "/dev/pt1video0" +#define ROOT "root" +/* 腱糸 */ + +mode_t umask_val = 0133; + +void fin_action(int); + +int main() { + struct sigaction ign_sigaction; + struct sigaction fin_sigaction; + FILE *fp; + int i; + int i_ret; + int fd; + int pt1_fd; + int string_length; + int number_length; + int i_pid; + struct stat orig_st; + struct stat open_st; + int flags; + char file_name[] = PIDFILENAME; + char buf[1024] = ""; + pid_t mypid; + char *p; + struct passwd *p_st_passwd; + uid_t uid_root; + + /* */ + /* root罔у篏亥篋 */ + p_st_passwd = getpwnam(ROOT); + if (p_st_passwd == NULL) { + printf("faile to get pwent. id=[%s].\n", ROOT); + exit(C_RET_NG); + } + uid_root = p_st_passwd->pw_uid; + if( uid_root != getuid() ) { + /* root 鎀絎茵罔с */ + printf("This process must be run by root. uid=[%d].\n", getuid()); + exit(C_RET_NG); + } + /* fork 荀活罧冴帥若≪ */ + mypid = fork(); + if( mypid == -1 ) { + /* fork */ + printf("fork error.\n"); + exit(C_RET_NG); + } else if ( mypid != 0 ) { + /* 荀祉鴻括篋(潟祉) */ + return(C_RET_OK); + } + umask(umask_val); + ign_sigaction.sa_handler = SIG_IGN; + fin_sigaction.sa_handler = fin_action; + if (sigaction(SIGHUP, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGHUP\n"); + exit(C_RET_NG); + } + if (sigaction(SIGTERM, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGTERM\n"); + exit(C_RET_NG); + } + if (sigaction(SIGPIPE, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGPIPE\n"); + exit(C_RET_NG); + } + if (sigaction(SIGINT, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGINT\n"); + exit(C_RET_NG); + } + if (sigaction(SIGQUIT, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGQUIT\n"); + exit(C_RET_NG); + } + if (sigaction(SIGILL, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGILL\n"); + exit(C_RET_NG); + } + if (sigaction(SIGABRT, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGABRT\n"); + exit(C_RET_NG); + } + if (sigaction(SIGFPE, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGFPE\n"); + exit(C_RET_NG); + } + if (sigaction(SIGSEGV, &fin_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGSEGV\n"); + exit(C_RET_NG); + } + /* sleep 篏帥 SIGALRM 丞舟 + * if (sigaction(SIGALRM, &ign_sigaction, NULL) != 0) { + * printf("failed to set signal handler. SIGALRM\n"); + * exit(C_RET_NG); + * } + */ + if (sigaction(SIGUSR1, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGUSR1\n"); + exit(C_RET_NG); + } + if (sigaction(SIGUSR2, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGUSR2\n"); + exit(C_RET_NG); + } + if (sigaction(SIGCHLD, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGCHLD\n"); + exit(C_RET_NG); + } + if (sigaction(SIGCONT, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGCONT\n"); + exit(C_RET_NG); + } + if (sigaction(SIGTSTP, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGTSTP\n"); + exit(C_RET_NG); + } + if (sigaction(SIGTTIN, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGTTIN\n"); + exit(C_RET_NG); + } + if (sigaction(SIGTTOU, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGTTOU\n"); + exit(C_RET_NG); + } + if (sigaction(SIGPOLL, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGPOLL\n"); + exit(C_RET_NG); + } + if (sigaction(SIGIO, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGIO\n"); + exit(C_RET_NG); + } + if (sigaction(SIGPROF, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGPROF\n"); + exit(C_RET_NG); + } + if (sigaction(SIGSYS, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGSYS\n"); + exit(C_RET_NG); + } + if (sigaction(SIGTRAP, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGTRAP\n"); + exit(C_RET_NG); + } + if (sigaction(SIGURG, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGURG\n"); + exit(C_RET_NG); + } + if (sigaction(SIGVTALRM, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGVTALRM\n"); + exit(C_RET_NG); + } + if (sigaction(SIGXCPU, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGXCPU\n"); + exit(C_RET_NG); + } + if (sigaction(SIGXFSZ, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGXFSZ\n"); + exit(C_RET_NG); + } + /* c + * if (sigaction(SIGEMT, &ign_sigaction, NULL) != 0) { + * printf("failed to set signal handler. SIGEMT\n"); + * exit(C_RET_NG); + * } + */ + if (sigaction(SIGSTKFLT, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGSTKFLT\n"); + exit(C_RET_NG); + } + if (sigaction(SIGIO, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGIO\n"); + exit(C_RET_NG); + } + if (sigaction(SIGCLD, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGCLD\n"); + exit(C_RET_NG); + } + if (sigaction(SIGPWR, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGPWR\n"); + exit(C_RET_NG); + } + /* c + * if (sigaction(SIGINFO, &ign_sigaction, NULL) != 0) { + * printf("failed to set signal handler. SIGINFO\n"); + * exit(C_RET_NG); + * } + */ + /* c + * if (sigaction(SIGLOST, &ign_sigaction, NULL) != 0) { + * printf("failed to set signal handler. SIGLOST\n"); + * exit(C_RET_NG); + * } + */ + if (sigaction(SIGWINCH, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGWINCH\n"); + exit(C_RET_NG); + } + if (sigaction(SIGUNUSED, &ign_sigaction, NULL) != 0) { + printf("failed to set signal handler. SIGUNUSED\n"); + exit(C_RET_NG); + } + + /* <ゃс */ + if ((lstat(file_name, &orig_st) != 0) || + (!S_ISREG(orig_st.st_mode))) + { + /* <ゃ<翫罩e幻腟篋 */ + i_ret = C_RET_OK; + } else { + /* PID<ゃ絖翫憜倶腆肴 + * 私莎桁倶(pid<ゃpidゃゃ祉鴻絖) + * 糸pid<ゃ羔罩祉с障c */ + i_ret = C_RET_NG; + } + /* TOCTOU 腴九倶馹 + * /var/run 祉ャ≪cу馹< */ + if (i_ret != C_RET_OK) { + fd = open(file_name, (OPEN_FLAGS | O_RDWR)); + if (fd == -1) { + /* 弱 */ + printf("open error. file:[%s].\n", file_name); + exit(C_RET_NG); + } + + if (fstat(fd, &open_st) != 0) { + /* 弱 */ + printf("stat error. file:[%s].\n", file_name); + exit(C_RET_NG); + } + + if ((orig_st.st_mode != open_st.st_mode) || + (orig_st.st_ino != open_st.st_ino) || + (orig_st.st_dev != open_st.st_dev)) { + /* <ゃс帥 */ + printf("file switch has occurred. file:[%s].\n", file_name); + close(fd); + exit(C_RET_NG); + } + + /* 馹<ゃс腆肴с O_NONBLOCK + * ≦鴻 (ュ) */ + if ((flags = fcntl(fd, F_GETFL)) == -1) { + /* 弱 */ + printf("fcntl error. file:[%s].\n", file_name); + close(fd); + exit(C_RET_NG); + } + + if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) { + /* 弱 */ + printf("fcntl error. file:[%s].\n", file_name); + close(fd); + exit(C_RET_NG); + } + /* <ゃ泣ゃ冴0篁ュс翫PIDゃс */ + if(open_st.st_size != 0) { + /* <ゃ茯PIDゃ緇 */ + if((fp = fdopen(fd, "w+")) == NULL) { + /* fdopen()紊掩 */ + printf("fdopen error. fd:[%d].\n", fd); + close(fd); + exit(C_RET_NG); + } + if ( fgets(buf, sizeof(buf), fp) == NULL ) { + /* fgets */ + printf("fgets error. fd:[%d].\n", fd); + fclose(fp); + exit(C_RET_NG); + } + buf[sizeof(buf)-1] = '\0'; + p = strchr(buf, (int)'\n'); + if(p != NULL) { + *p = '\0'; + } + string_length = strlen(buf); + number_length = strspn(buf, NUMBER); + if ( string_length != number_length ) { + /* PID<ゃ医幻 */ + printf("invalid pid file[%s] buf[%s].\n", file_name, buf); + fclose(fp); + exit(C_RET_NG); + } + i_pid = atoi(buf); + + /* PID<ゃPIDPIDゃ祉鴻絖鐚 */ + i_ret = kill(i_pid, 0); + if ( i_ret == 0 ) { + /* 絖翫篋莎桁帥腟篋 */ + printf("process already exists. pid[%d]\n", i_pid); + exit(C_RET_NG); + } else { + /* 絖翫医幻腟篋fp祉膓茵 */ + rewind(fp); + } + } + } + if ( i_ret == C_RET_OK ) { + /* PID<ゃ絖翫PID<ゃ域鋎茵 */ + fd = open(file_name, O_CREAT|O_EXCL|O_RDWR, 00644); + if ( fd == -1 ){ + /* open 紊掩 */ + printf("file open error. file_name[%s].\n", file_name); + exit(C_RET_NG); + } + if((fp = fdopen(fd, "w+")) == NULL) { + /* fdopen()紊掩 */ + printf("fdopen error. fd:[%d].\n", fd); + close(fd); + exit(C_RET_NG); + } + } + /* fpPID<ゃ吾莨若倶сPID吾莨若 */ + mypid = getpid(); + snprintf(buf, sizeof(buf), "%d\n", mypid); + i_ret = fputs(buf, fp); + if(i_ret == EOF) { + /* fputs */ + printf("fputs error. file_name[%s].\n", file_name); + fclose(fp); + exit(C_RET_NG); + } + fclose(fp); + /* PID≫腟篋*/ + /* fd pt1 ゃ鴻 open */ + pt1_fd = open(device, O_RDONLY); + if(pt1_fd == -1) { + /* open */ + printf("open error. file_name[%s].\n", device); + exit(C_RET_NG); + } + /* fcntl LNB 紙劫 */ + if(ioctl(pt1_fd, LNB_ENABLE, 0) < 0) { + printf("Power on LNB failed. device:[%s].\n", device); + exit(C_RET_NG); + } + close(pt1_fd); + /* ♂緇(腟篋腟篋signal) */ + while(1){ + sleep(UINT_MAX); + } + return C_RET_OK; +} + +/* 腟篋絎茵∽ */ +/* 祉鴻腟篋鴻signal篏 */ +void fin_action(int sig) { + int pt1_fd; + char file_name[] = PIDFILENAME; + /* fd pt1 ゃ鴻open */ + pt1_fd = open(device, O_RDONLY); + if(pt1_fd == -1) { + /* open */ + printf("open error. file_name[%s].\n", device); + exit(C_RET_NG); + } + /* fcntl LNB 紙 */ + if(ioctl(pt1_fd, LNB_DISABLE, 0) < 0) { + printf("Power on LNB failed. device:[%s].\n", device); + close(pt1_fd); + exit(C_RET_NG); + } + /* fd close */ + close(pt1_fd); + /* pid<ゃゃ */ + if ( unlink(file_name) != 0 ) { + /* pid file unlink */ + printf("unlink error. file_name[%s].\n", device); + exit(C_RET_NG); + } + /* 障 */ + exit(C_RET_OK); +} diff -r 215a51fa3df3 -r 9c7bc6c0327e src/recpt1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/recpt1.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,1487 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +/* vim: set ts=4 sts=4 sw=4 expandtab number : */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "config.h" +#include "decoder.h" +#include "recpt1.h" +#include "version.h" +#include "mkpath.h" + +#include +#include +#include "pt1_dev.h" +#include "tssplitter_lite.h" +#include "ushare.h" +#include "trace.h" + +/* maximum write length at once */ +#define SIZE_CHANK 1316 + +/* ipc message size */ +#define MSGSZ 255 + +typedef struct pt1_msgbuf { + long mtype; + char mtext[MSGSZ]; +} pt1_message_buf; + +/* globals */ +boolean f_exit = FALSE; + +/* prototypes */ +int tune(char *channel, thread_data *tdata, char *device); +int close_tuner(thread_data *tdata); + + +/* ipc message receive */ +void * +mq_recv(void *t) +{ + thread_data *tdata = (thread_data *)t; + pt1_message_buf rbuf; + char channel[16]; + int ch = 0, recsec = 0, time_to_add = 0; + + while(1) { + if(msgrcv(tdata->msqid, &rbuf, MSGSZ, 1, 0) < 0) { + return NULL; + } + + sscanf(rbuf.mtext, "ch=%s t=%d e=%d", channel, &recsec, &time_to_add); + ch = atoi(channel); +// fprintf(stderr, "ch=%d time=%d extend=%d\n", ch, recsec, time_to_add); + + if(ch && tdata->ch != ch) { + /* stop stream */ + ioctl(tdata->tfd, STOP_REC, 0); +#if 0 + /* re-initialize decoder */ + if(tdata->decoder) { +// b25_finish(tdata->decoder); + b25_shutdown(tdata->decoder); + tdata->decoder = b25_startup(tdata->dopt); + if(!tdata->decoder) { + fprintf(stderr, "Cannot start b25 decoder\n"); + fprintf(stderr, "Fall back to encrypted recording\n"); + } + } +#endif + /* tune to new channel */ + if(close_tuner(tdata) != 0) + return NULL; + + /* wait for remainder */ + while(tdata->queue->num_used > 0) { + usleep(10000); + } + + tune(channel, tdata, NULL); + + /* restart recording */ + if(ioctl(tdata->tfd, START_REC, 0) < 0) { + fprintf(stderr, "Tuner cannot start recording\n"); + return NULL; + } + } + + if(time_to_add) { + tdata->recsec += time_to_add; + fprintf(stderr, "Extended %d sec\n", time_to_add); + } + + if(recsec) { + time_t cur_time; + time(&cur_time); + if(cur_time - tdata->start_time > recsec) { + f_exit = TRUE; + } + else { + tdata->recsec = recsec; + fprintf(stderr, "Total recording time = %d sec\n", recsec); + } + } + + if(f_exit) + return NULL; + } +} + + +/* lookup frequency conversion table*/ +ISDB_T_FREQ_CONV_TABLE * +searchrecoff(char *channel) +{ + int lp; + + for(lp = 0; isdb_t_conv_table[lp].parm_freq != NULL; lp++) { + /* return entry number in the table when strings match and + * lengths are same. */ + if((memcmp(isdb_t_conv_table[lp].parm_freq, channel, + strlen(channel)) == 0) && + (strlen(channel) == strlen(isdb_t_conv_table[lp].parm_freq))) { + return &isdb_t_conv_table[lp]; + } + } + return NULL; +} + +QUEUE_T * +create_queue(size_t size) +{ + QUEUE_T *p_queue; + int memsize = sizeof(QUEUE_T) + size * sizeof(BUFSZ); + + p_queue = (QUEUE_T*)calloc(memsize, sizeof(char)); + + if(p_queue != NULL) { + p_queue->size = size; + p_queue->num_avail = size; + p_queue->num_used = 0; + p_queue->in = 0; + p_queue->out = 0; + pthread_mutex_init(&p_queue->mutex, NULL); + pthread_cond_init(&p_queue->cond_avail, NULL); + pthread_cond_init(&p_queue->cond_used, NULL); + } + + return p_queue; +} + +STREAM_QUEUE_T * +create_stream_queue(size_t size) +{ + STREAM_QUEUE_T *p_queue; + int memsize = sizeof(STREAM_QUEUE_T) + size * sizeof(ARIB_STD_B25_BUFFER); + + p_queue = (STREAM_QUEUE_T*)calloc(memsize, sizeof(char)); + + if(p_queue != NULL) { + p_queue->size = size; + p_queue->num_avail = size; + p_queue->num_used = 0; + p_queue->in = 0; + p_queue->out = 0; + pthread_mutex_init(&p_queue->mutex, NULL); + pthread_cond_init(&p_queue->cond_avail, NULL); + pthread_cond_init(&p_queue->cond_used, NULL); + } + + return p_queue; +} + +void +destroy_queue(QUEUE_T *p_queue) +{ + if(!p_queue) + return; + + pthread_mutex_destroy(&p_queue->mutex); + pthread_cond_destroy(&p_queue->cond_avail); + pthread_cond_destroy(&p_queue->cond_used); + free(p_queue); +} + +/* enqueue data. this function will block if queue is full. */ +void +enqueue(QUEUE_T *p_queue, BUFSZ *data) +{ + struct timeval now; + struct timespec spec; + //fprintf (stderr, "enqueue() start.\n"); + + gettimeofday(&now, NULL); + spec.tv_sec = now.tv_sec + 1; + spec.tv_nsec = now.tv_usec * 1000; + + //fprintf (stderr, "enqueue() mutex lock try. num_used[%d] num_avail[%d]\n", p_queue->num_used, p_queue->num_avail); + pthread_mutex_lock(&p_queue->mutex); + //fprintf (stderr, "enqueue() mutex lock success. num_used[%d] num_avail[%d]\n", p_queue->num_used, p_queue->num_avail); + /* entered critical section */ + + /* wait while queue is full */ + while(p_queue->num_avail == 0) { + pthread_cond_timedwait(&p_queue->cond_avail, + &p_queue->mutex, &spec); + if(f_exit) { + pthread_mutex_unlock(&p_queue->mutex); + return; + } + } + + p_queue->buffer[p_queue->in] = data; + + /* move position marker for input to next position */ + p_queue->in++; + p_queue->in %= p_queue->size; + + /* update counters */ + p_queue->num_avail--; + p_queue->num_used++; + + /* leaving critical section */ + //fprintf (stderr, "enqueue() mutex unlock. num_used[%d]\n", p_queue->num_used); + pthread_mutex_unlock(&p_queue->mutex); + pthread_cond_signal(&p_queue->cond_used); +} + + /* + * stream_func()$B$N;HMQ$9$k(B enqueue() $B$O6u$-$,$J$$>l9g$K$O!"L58BBT$A$O$7$F$O$J$i$J$$!#(B + * $B6u$-$,$J$$>l9g$K$O!"(Bqueue$B$r=i4|2=$7$F$7$^$$!"C$7$7$F$7$^$&J}$,%^%7!#(B + * $B$3$l$K$h$j!"Cnum_used); + pthread_mutex_lock(&p_queue->mutex); + //fprintf (stderr, "stream_enqueue() mutex lock success. num_used[%d]\n", p_queue->num_used); + /* entered critical section */ + + if (p_queue->num_avail == 0) { + //fprintf (stderr, "stream_enqueue() num_used reach max[%d].\n", p_queue->num_used); + /* stream queue $B$O0lGU$K$J$C$?$i>C5n$7$F$7$^$&(B */ + for ( i=0; i < p_queue->size; i++ ) { + if ( p_queue->buffer[i] != NULL ) { + free(p_queue->buffer[i]->data); + p_queue->buffer[i]->data = NULL; + free(p_queue->buffer[i]); + p_queue->buffer[i] = NULL; + } + } + p_queue->in = 0; + p_queue->out = 0; + p_queue->num_used = 0; + p_queue->num_avail = p_queue->size; + } + + p_queue->buffer[p_queue->in] = data; + + /* move position marker for input to next position */ + p_queue->in++; + p_queue->in %= p_queue->size; + + /* update counters */ + p_queue->num_avail--; + p_queue->num_used++; + + /* leaving critical section */ + //fprintf (stderr, "stream_enqueue() mutex unlock.\n"); + pthread_mutex_unlock(&p_queue->mutex); + pthread_cond_signal(&p_queue->cond_used); +} + +/* dequeue data. this function will block if queue is empty. */ +BUFSZ * +dequeue(QUEUE_T *p_queue) +{ + struct timeval now; + struct timespec spec; + BUFSZ *buffer; + + //fprintf (stderr, "dequeue() start.\n"); + gettimeofday(&now, NULL); + spec.tv_sec = now.tv_sec + 1; + spec.tv_nsec = now.tv_usec * 1000; + + //fprintf (stderr, "dequeue() mutex lock try. num_used[%d]\n", p_queue->num_used); + pthread_mutex_lock(&p_queue->mutex); + //fprintf (stderr, "dequeue() mutex lock success. num_used[%d]\n", p_queue->num_used); + /* entered the critical section*/ + + /* wait while queue is empty */ + while(p_queue->num_used == 0) { + pthread_cond_timedwait(&p_queue->cond_used, + &p_queue->mutex, &spec); + if(f_exit) { + pthread_mutex_unlock(&p_queue->mutex); + return NULL; + } + } + + /* take buffer address */ + buffer = p_queue->buffer[p_queue->out]; + + /* move position marker for output to next position */ + p_queue->out++; + p_queue->out %= p_queue->size; + + /* update counters */ + p_queue->num_avail++; + p_queue->num_used--; + + /* leaving the critical section */ + //fprintf (stderr, "dequeue() mutex unlock.\n"); + pthread_mutex_unlock(&p_queue->mutex); + pthread_cond_signal(&p_queue->cond_avail); + + return buffer; +} + +ARIB_STD_B25_BUFFER * +stream_dequeue(STREAM_QUEUE_T *p_queue) +{ + struct timeval now; + struct timespec spec; + ARIB_STD_B25_BUFFER *buffer; + + //fprintf (stderr, "stream_dequeue() start.\n"); + gettimeofday(&now, NULL); + spec.tv_sec = now.tv_sec + 1; + spec.tv_nsec = now.tv_usec * 1000; + + //fprintf (stderr, "stream_dequeue() mutex lock try. num_used[%d]\n", p_queue->num_used); + pthread_mutex_lock(&p_queue->mutex); + //fprintf (stderr, "stream_dequeue() mutex lock success. num_used[%d]\n", p_queue->num_used); + /* entered the critical section*/ + + /* wait while queue is empty */ + while(p_queue->num_used == 0) { + pthread_cond_timedwait(&p_queue->cond_used, + &p_queue->mutex, &spec); + if(f_exit) { + pthread_mutex_unlock(&p_queue->mutex); + return NULL; + } + } + + /* take buffer address */ + buffer = p_queue->buffer[p_queue->out]; + + /* move position marker for output to next position */ + p_queue->out++; + p_queue->out %= p_queue->size; + + /* update counters */ + p_queue->num_avail++; + p_queue->num_used--; + + /* leaving the critical section */ + pthread_mutex_unlock(&p_queue->mutex); + //fprintf (stderr, "stream_dequeue() mutex unlock.\n"); + pthread_cond_signal(&p_queue->cond_avail); + //fprintf (stderr, "dequeue() finish.\n"); + + return buffer; +} + +/* this function will be reader thread */ +void * +reader_func(void *p) +{ + thread_data *data = (thread_data *)p; + QUEUE_T *p_queue = data->queue; + decoder *dec = data->decoder; + splitter *splitter = data->splitter; + int wfd = data->wfd; + boolean use_b25 = dec ? TRUE : FALSE; + boolean use_udp = data->sock_data ? TRUE : FALSE; + boolean fileless = FALSE; + boolean use_splitter = splitter ? TRUE : FALSE; + boolean use_streaming = TRUE; // $BK\Ev$O0z?t$K$9$k$3$H(B + int sfd = -1; + pthread_t signal_thread = data->signal_thread; + struct sockaddr_in *addr = NULL; + BUFSZ *qbuf; + ARIB_STD_B25_BUFFER *eqbuf; + splitbuf_t splitbuf; + ARIB_STD_B25_BUFFER sbuf, dbuf, buf; + int code; + int split_select_finish = TSS_ERROR; + + buf.size = 0; + buf.data = NULL; + splitbuf.size = 0; + + if(wfd == -1) + fileless = TRUE; + + if(use_udp) { + sfd = data->sock_data->sfd; + addr = &data->sock_data->addr; + } + + while(1) { +// fprintf (stderr, "reader_func() while loop\n"); + ssize_t wc = 0; + int file_err = 0; + //fprintf (stderr, "reader_func() dequeue() start.\n"); + qbuf = dequeue(p_queue); + //fprintf (stderr, "reader_func() dequeue() finish.\n"); + /* no entry in the queue */ + if(qbuf == NULL) { + break; + } + + sbuf.data = qbuf->buffer; + sbuf.size = qbuf->size; + + buf = sbuf; /* default */ + + if(use_b25) { + code = b25_decode(dec, &sbuf, &dbuf); + if(code < 0) { + fprintf(stderr, "b25_decode failed (code=%d). fall back to encrypted recording.\n", code); + use_b25 = FALSE; + } + else + buf = dbuf; + } + + + if(use_splitter) { + splitbuf.size = 0; + + while(buf.size) { + /* $BJ,N%BP>](BPID$B$NCj=P(B */ + if(split_select_finish != TSS_SUCCESS) { + split_select_finish = split_select(splitter, &buf); + if(split_select_finish == TSS_NULL) { + /* malloc$B%(%i!](BPID$B$,40A4$KCj=P$G$-$k$^$G=PNO$7$J$$(B + * 1$BICDxEYM>M5$r8+$k$H$$$$$+$b(B + */ + time_t cur_time; + time(&cur_time); + if(cur_time - data->start_time > 4) { + use_splitter = FALSE; + goto fin; + } + break; + } + } + /* $BJ,N%BP>]0J30$r$U$k$$Mn$H$9(B */ + code = split_ts(splitter, &buf, &splitbuf); + if(code != TSS_SUCCESS) { + fprintf(stderr, "split_ts failed\n"); + break; + } + + break; + } /* while */ + + buf.size = splitbuf.size; + buf.data = splitbuf.buffer; + fin: + ; + } /* if */ + + /* + * 2. reader_func$B2~B$E@(B + * 2.1 tdata->p_queue $B$+$i(B dequeue() $B$7$F%9%H%j!<%`$J%G!<%?$rp_queue->mutex $B$r(B lock/unlock $B$7$FFI$_9~$_;~$NF1;~99?7$rKI;_$7$F$$$k(B + * 2.2 tdata->stream_queue $B$K(B enqueue() $B$rstream_queue $B$K(B 2.2.1 $B$N%]%$%s%?$r(B enqueue() $B$9$k(B + * 2.2.3.1 enqueue() $B$O(B tdata->stream_queue->mutex $B$r(B lock/unlock $B$7$F=q$-9~$_;~$NF1;~99?7$rKI;_$7$F$$$k(B + */ + //fprintf (stderr, "reader_func() buf.size[%d]\n", buf.size); + if ( use_streaming && buf.size > 0 ) { + do { + eqbuf = malloc(sizeof(ARIB_STD_B25_BUFFER)); + if ( eqbuf == NULL ) { + fprintf (stderr, "Cannot malloc eqbuf memory. streaming abort.\n"); + use_streaming = FALSE; + break; + } + eqbuf->data = malloc(buf.size); + if ( eqbuf->data == NULL ) { + fprintf (stderr, "Cannot malloc eqbuf memory. streaming abort.\n"); + use_streaming = FALSE; + break; + } + eqbuf->size = buf.size; + memcpy(eqbuf->data, buf.data, buf.size); + // $B$3$A$i$b0n$l$?$i>C5n$7$F$7$^$&(B stream_enqueue() $B$r;HMQ(B + stream_enqueue(data->stream_queue, eqbuf); + } while(0); + } + + if(!fileless) { + /* write data to output file */ + int size_remain = buf.size; + int offset = 0; + + while(size_remain > 0) { + int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK; + + wc = write(wfd, buf.data + offset, ws); + if(wc < 0) { + perror("write"); + file_err = 1; + pthread_kill(signal_thread, + errno == EPIPE ? SIGPIPE : SIGUSR2); + break; + } + size_remain -= wc; + offset += wc; + } + } + + if(use_udp && sfd != -1) { + /* write data to socket */ + int size_remain = buf.size; + int offset = 0; + while(size_remain > 0) { + int ws = size_remain < SIZE_CHANK ? size_remain : SIZE_CHANK; + wc = write(sfd, buf.data + offset, ws); + if(wc < 0) { + if(errno == EPIPE) + pthread_kill(signal_thread, SIGPIPE); + break; + } + size_remain -= wc; + offset += wc; + } + } + + free(qbuf); + qbuf = NULL; + + /* normal exit */ + if((f_exit && !p_queue->num_used) || file_err) { + + buf = sbuf; /* default */ + + if(use_b25) { + code = b25_finish(dec, &sbuf, &dbuf); + if(code < 0) + fprintf(stderr, "b25_finish failed\n"); + else + buf = dbuf; + } + + if(use_splitter) { + /* $BJ,N%BP>]0J30$r$U$k$$Mn$H$9(B */ + code = split_ts(splitter, &buf, &splitbuf); + if(code != TSS_SUCCESS) { + break; + } + + buf.data = splitbuf.buffer; + buf.size = splitbuf.size; + } + + if(!fileless && !file_err) { + wc = write(wfd, buf.data, buf.size); + if(wc < 0) { + perror("write"); + file_err = 1; + pthread_kill(signal_thread, + errno == EPIPE ? SIGPIPE : SIGUSR2); + } + } + + if(use_udp && sfd != -1) { + wc = write(sfd, buf.data, buf.size); + if(wc < 0) { + if(errno == EPIPE) + pthread_kill(signal_thread, SIGPIPE); + } + } + + break; + } + } + + time_t cur_time; + time(&cur_time); + fprintf(stderr, "Recorded %dsec\n", + (int)(cur_time - data->start_time)); + + return NULL; +} + +/* + * 3. stream_func() $B$O(B reader_func() $B$+$i%9%H%j!<%`8~$1$K%3%T!<$5$l$?%G!<%?$r!"%9%H%j!<%`%;%C%7%g%sKh$N(Bqueue$B$K%3%T!<$9$k(B + * 3.1 tdata->stream_queue $B$+$i(B dequeue $B$9$k(B + * 3.1.1 tdata->stream_queue->mutex $B$NFI$_9~$_;~$N(B lock/unlokc $B$,H/@8$9$k(B + * 3.2 tdata->streamer->mutex $B$r(B lock + * 3.3 $B0J2<$r(B tdata->streamer->stream_nr $B$N?t$@$1%k!<%W(B + * 3.3.1 tdata->streamer->stream_session[N]->is_valid $B$,M-8z$+3NG'(B + * 3.3.2 tdata->streamer->stream_session[N]->p_queue $B$X$N%3%T!streamer->stream_session[N]->p_queue $B$X(B enqueue() + * 3.3.4.1 tdata->streamer->stream_session[N]->p_queue->mutex $B$N(B lock/unlock $B$,H/@8(B + * 3.4 tdata->streamer->mutex $B$r(B unlock + * stream_func()$B$N(B lock $B$9$k$b$N$H=g=x(B + * #1. tdata->stream_queue->mutex $B$N(Block/unlock + * #2. tdata->streamer->mutex $B$r(B lock + * #2.1 tdata->streamer->stream_session[N]->p_queue->mutex $B$N(B lock/unlock + * #3. tdata->streamer->mutex $B$N(B unlock + * $B>e5-$K4X$7$F!"(Block/unlock$B$,I,MW$JItJ,$H!"NN0h3NJ]$H%3%T!<$NCY$a$N=hM}$H$G(B + * $B@Z$jJ,$1$i$l$kItJ,$K$D$$$F$O@Z$jJ,$1$F$7$^$C$?$[$&$,$$$$$+$b!#(B + * $B%/%j%F%#%+%k%;%/%7%g%s$O!"%]%$%s%?A`:n$@$1$H$9$k$Y$-!#(B + */ +void * +stream_func(void *p) +{ + thread_data *data = (thread_data *)p; + STREAM_QUEUE_T *p_queue = data->stream_queue; + ARIB_STD_B25_BUFFER *qbuf = NULL; + ARIB_STD_B25_BUFFER *buf; + int i; + //fprintf (stderr, "stream_func(): start.\n"); + + while(1) { + // 3.1 tdata->stream_queue $B$+$i(B dequeue $B$9$k(B + // dequeue $B$7$?%G!<%?$O(B ARIB_STD_B25_BUFFER + qbuf = stream_dequeue(p_queue); + /* no entry in the queue */ + if(qbuf == NULL) { + //fprintf (stderr, "stream_func(): dequeue() return NULL pointer. streaming abort.\n"); + continue; + } + // $B%/%j%F%#%+%k%;%/%7%g%sD9$$$N$J$s$H$+$7$?$$$J$!!D(B + // ToDo: memcpy $B$H$+%/%j%F%#%+%k%;%/%7%g%s$N30$K=P$9(B + // 3.2 tdata->streamer->mutex $B$r(B lock + //fprintf (stderr, "stream_func(): mutex lock try.\n"); + pthread_mutex_lock(&data->streamer->mutex); + //fprintf (stderr, "stream_func(): mutex lock success.\n"); + // 3.3 $B0J2<$r(B tdata->streamer->stream_nr $B$N?t$@$1%k!<%W(B + for ( i=0; i < data->streamer->stream_nr; i++ ) { + // 3.3.1 tdata->streamer->stream_session[N]->is_valid $B$,M-8z$+3NG'(B + if ( data->streamer->stream_session[i] != NULL ) { + if ( data->streamer->stream_session[i]->is_valid ) { + // 3.3.2 tdata->streamer->stream_session[N]->p_queue $B$X$N%3%T!streamer->mutex); + log_error ("stream_func(): alloc NULL pointer. streaming abort.\n"); + return NULL; + } + buf->data = NULL; + buf->data = malloc(qbuf->size); + if ( buf->data == NULL ) { + log_error ("Cannot malloc buf memory. streaming session_id[%d] abort.\n", i); + pthread_mutex_unlock(&data->streamer->mutex); + return NULL; + } + // 3.3.3 3.1$B$G(B dequeue $B$7$?%P%C%U%!$r(B3.3.2$B$G(B alloc $B$7$?%P%C%U%!$X(B memcpy() + memcpy(buf->data, qbuf->data, qbuf->size); + buf->size = qbuf->size; + // 3.3.4 tdata->streamer->stream_session[N]->p_queue $B$X(B enqueue() + stream_enqueue(data->streamer->stream_session[i]->p_queue, buf); + } + } + } + // 3.4 tdata->streamer->mutex $B$r(B unlock + pthread_mutex_unlock(&data->streamer->mutex); + free(qbuf->data); + free(qbuf); + //fprintf (stderr, "stream_func(): mutex unlock.\n"); + } + return NULL; +} + +void +show_usage(char *cmd) +{ +#ifdef HAVE_LIBARIB25 + fprintf(stderr, "Usage: \n%s [--b25 [--round N] [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); +#else + fprintf(stderr, "Usage: \n%s [--strip] [--EMM]] [--udp [--addr hostname --port portnumber]] [--device devicefile] [--lnb voltage] [--sid SID1,SID2] [--es filename_suffix] [--start_time YYYYMMDDHHMISS] channel rectime destfile\n", cmd); +#endif + fprintf(stderr, "\n"); + fprintf(stderr, "Remarks:\n"); + fprintf(stderr, "if rectime is '-', records indefinitely.\n"); + fprintf(stderr, "if destfile is '-', stdout is used for output.\n"); +} + +void +show_options(void) +{ + fprintf(stderr, "Options:\n"); +#ifdef HAVE_LIBARIB25 + fprintf(stderr, "--b25: Decrypt using BCAS card\n"); + fprintf(stderr, " --round N: Specify round number\n"); + fprintf(stderr, " --strip: Strip null stream\n"); + fprintf(stderr, " --EMM: Instruct EMM operation\n"); +#endif + fprintf(stderr, "--udp: Turn on udp broadcasting\n"); + fprintf(stderr, " --addr hostname: Hostname or address to connect\n"); + fprintf(stderr, " --port portnumber: Port number to connect\n"); + fprintf(stderr, "--device devicefile: Specify devicefile to use\n"); + fprintf(stderr, "--lnb voltage: Specify LNB voltage (0, 11, 15)\n"); + fprintf(stderr, "--sid SID1,SID2,...: Specify SID number in CSV format (101,102,...)\n"); + fprintf(stderr, " --es filename: Specify ES out filename prefix\n"); + fprintf(stderr, " --start_time YYYYMMDDHHMISS: Specify record start datetime\n"); + fprintf(stderr, "--help: Show this help\n"); + fprintf(stderr, "--version: Show version\n"); + fprintf(stderr, "--list: Show channel list\n"); +} + +void +show_channels(void) +{ + FILE *f; + char *home; + char buf[255], filename[255]; + + fprintf(stderr, "Available Channels:\n"); + + home = getenv("HOME"); + sprintf(filename, "%s/.recpt1-channels", home); + f = fopen(filename, "r"); + if(f) { + while(fgets(buf, 255, f)) + fprintf(stderr, "%s", buf); + fclose(f); + } + else + fprintf(stderr, "13-62: Terrestrial Channels\n"); + + fprintf(stderr, "101ch: NHK BS1\n"); + fprintf(stderr, "102ch: NHK BS2\n"); + fprintf(stderr, "103ch: NHK BShi\n"); + fprintf(stderr, "141ch: BS Nittele\n"); + fprintf(stderr, "151ch: BS Asahi\n"); + fprintf(stderr, "161ch: BS-TBS\n"); + fprintf(stderr, "171ch: BS Japan\n"); + fprintf(stderr, "181ch: BS Fuji\n"); + fprintf(stderr, "191ch: WOWOW\n"); + fprintf(stderr, "192ch: WOWOW2\n"); + fprintf(stderr, "193ch: WOWOW3\n"); + fprintf(stderr, "200ch: Star Channel\n"); + fprintf(stderr, "211ch: BS11 Digital\n"); + fprintf(stderr, "222ch: TwellV\n"); + fprintf(stderr, "C13-C63: CATV Channels\n"); + fprintf(stderr, "CS2-CS24: CS Channels\n"); +} + +float +getsignal_isdb_s(int signal) +{ + /* apply linear interpolation */ + static const float afLevelTable[] = { + 24.07f, // 00 00 0 24.07dB + 24.07f, // 10 00 4096 24.07dB + 18.61f, // 20 00 8192 18.61dB + 15.21f, // 30 00 12288 15.21dB + 12.50f, // 40 00 16384 12.50dB + 10.19f, // 50 00 20480 10.19dB + 8.140f, // 60 00 24576 8.140dB + 6.270f, // 70 00 28672 6.270dB + 4.550f, // 80 00 32768 4.550dB + 3.730f, // 88 00 34816 3.730dB + 3.630f, // 88 FF 35071 3.630dB + 2.940f, // 90 00 36864 2.940dB + 1.420f, // A0 00 40960 1.420dB + 0.000f // B0 00 45056 -0.01dB + }; + + unsigned char sigbuf[4]; + memset(sigbuf, '\0', sizeof(sigbuf)); + sigbuf[0] = (((signal & 0xFF00) >> 8) & 0XFF); + sigbuf[1] = (signal & 0xFF); + + /* calculate signal level */ + if(sigbuf[0] <= 0x10U) { + /* clipped maximum */ + return 24.07f; + } + else if (sigbuf[0] >= 0xB0U) { + /* clipped minimum */ + return 0.0f; + } + else { + /* linear interpolation */ + const float fMixRate = + (float)(((unsigned short)(sigbuf[0] & 0x0FU) << 8) | + (unsigned short)sigbuf[0]) / 4096.0f; + return afLevelTable[sigbuf[0] >> 4] * (1.0f - fMixRate) + + afLevelTable[(sigbuf[0] >> 4) + 0x01U] * fMixRate; + } +} + +void +calc_cn(int fd, int type) +{ + int rc ; + double P ; + double CNR; + + if(ioctl(fd, GET_SIGNAL_STRENGTH, &rc) < 0) { + fprintf(stderr, "Tuner Select Error\n"); + return ; + } + + if(type == CHTYPE_GROUND) { + P = log10(5505024/(double)rc) * 10; + CNR = (0.000024 * P * P * P * P) - (0.0016 * P * P * P) + + (0.0398 * P * P) + (0.5491 * P)+3.0965; + fprintf(stderr, "C/N = %fdB\n", CNR); + } + else { + CNR = getsignal_isdb_s(rc); + fprintf(stderr, "C/N = %fdB\n", CNR); + } +} + +void +cleanup(thread_data *tdata) +{ + /* stop recording */ + ioctl(tdata->tfd, STOP_REC, 0); + + /* xxx need mutex? */ + f_exit = TRUE; + + pthread_cond_signal(&tdata->queue->cond_avail); + pthread_cond_signal(&tdata->queue->cond_used); +} + +/* will be signal handler thread */ +void * +process_signals(void *data) +{ + sigset_t waitset; + int sig; + thread_data *tdata = (thread_data *)data; + + sigemptyset(&waitset); + sigaddset(&waitset, SIGPIPE); + sigaddset(&waitset, SIGINT); + sigaddset(&waitset, SIGTERM); + sigaddset(&waitset, SIGUSR1); + sigaddset(&waitset, SIGUSR2); + + sigwait(&waitset, &sig); + + switch(sig) { + case SIGPIPE: + fprintf(stderr, "\nSIGPIPE received. cleaning up...\n"); + cleanup(tdata); + break; + case SIGINT: + fprintf(stderr, "\nSIGINT received. cleaning up...\n"); + cleanup(tdata); + break; + case SIGTERM: + fprintf(stderr, "\nSIGTERM received. cleaning up...\n"); + cleanup(tdata); + break; + case SIGUSR1: /* normal exit*/ + cleanup(tdata); + break; + case SIGUSR2: /* error */ + fprintf(stderr, "Detected an error. cleaning up...\n"); + cleanup(tdata); + break; + } + + return NULL; /* dummy */ +} + +void +init_signal_handlers(pthread_t *signal_thread, thread_data *tdata) +{ + sigset_t blockset; + + sigemptyset(&blockset); + sigaddset(&blockset, SIGPIPE); + sigaddset(&blockset, SIGINT); + sigaddset(&blockset, SIGTERM); + sigaddset(&blockset, SIGUSR1); + sigaddset(&blockset, SIGUSR2); + + if(pthread_sigmask(SIG_BLOCK, &blockset, NULL)) + fprintf(stderr, "pthread_sigmask() failed.\n"); + + pthread_create(signal_thread, NULL, process_signals, tdata); +} + +int +tune(char *channel, thread_data *tdata, char *device) +{ + char **tuner; + int num_devs; + int lp; + FREQUENCY freq; + + /* get channel */ + tdata->table = searchrecoff(channel); + if(tdata->table == NULL) { + fprintf(stderr, "Invalid Channel: %s\n", channel); + return 1; + } + + freq.frequencyno = tdata->table->set_freq; + freq.slot = tdata->table->add_freq; + + /* open tuner */ + /* case 1: specified tuner device */ + if(device) { + tdata->tfd = open(device, O_RDONLY); + if(tdata->tfd < 0) { + fprintf(stderr, "Cannot open tuner device: %s\n", device); + return 1; + } + + /* power on LNB */ + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { + fprintf(stderr, "Power on LNB failed: %s\n", device); + } + } + + /* tune to specified channel */ + if(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { + close(tdata->tfd); + fprintf(stderr, "Cannot tune to the specified channel: %s\n", device); + return 1; + } + else { + tdata->ch = atoi(channel); + } + } + else { + /* case 2: loop around available devices */ + if(tdata->table->type == CHTYPE_SATELLITE) { + tuner = bsdev; + num_devs = NUM_BSDEV; + } + else { + tuner = isdb_t_dev; + num_devs = NUM_ISDB_T_DEV; + } + + for(lp = 0; lp < num_devs; lp++) { + tdata->tfd = open(tuner[lp], O_RDONLY); + if(tdata->tfd >= 0) { + /* power on LNB */ + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_ENABLE, tdata->lnb) < 0) { + fprintf(stderr, "Warning: Power on LNB failed: %s\n", tuner[lp]); + } + } + + /* tune to specified channel */ + if(ioctl(tdata->tfd, SET_CHANNEL, &freq) < 0) { + close(tdata->tfd); + tdata->tfd = -1; + continue; + } + + break; /* found suitable tuner */ + } + } + + /* all tuners cannot be used */ + if(tdata->tfd < 0) { + fprintf(stderr, "Cannot tune to the specified channel\n"); + return 1; + } + else { + tdata->ch = atoi(channel); + } + } + + /* show signal strength */ + calc_cn(tdata->tfd, tdata->table->type); + + return 0; /* success */ +} + +int +parse_time(char *rectimestr, thread_data *tdata) +{ + /* indefinite */ + if(!strcmp("-", rectimestr)) { + tdata->indefinite = TRUE; + tdata->recsec = -1; + } + /* colon */ + else if(strchr(rectimestr, ':')) { + int n1, n2, n3; + if(sscanf(rectimestr, "%d:%d:%d", &n1, &n2, &n3) == 3) + tdata->recsec = n1 * 3600 + n2 * 60 + n3; + else if(sscanf(rectimestr, "%d:%d", &n1, &n2) == 2) + tdata->recsec = n1 * 3600 + n2 * 60; + } + /* HMS */ + else { + char *tmpstr; + char *p1, *p2; + + tmpstr = strdup(rectimestr); + p1 = tmpstr; + while(*p1 && !isdigit(*p1)) + p1++; + + /* hour */ + if((p2 = strchr(p1, 'H')) || (p2 = strchr(p1, 'h'))) { + *p2 = '\0'; + tdata->recsec += atoi(p1) * 3600; + p1 = p2 + 1; + while(*p1 && !isdigit(*p1)) + p1++; + } + + /* minute */ + if((p2 = strchr(p1, 'M')) || (p2 = strchr(p1, 'm'))) { + *p2 = '\0'; + tdata->recsec += atoi(p1) * 60; + p1 = p2 + 1; + while(*p1 && !isdigit(*p1)) + p1++; + } + + /* second */ + tdata->recsec += atoi(p1); + + free(tmpstr); + } + + return 0; /* success */ +} + +int +close_tuner(thread_data *tdata) +{ + int rv = 0; + + if(tdata->table->type == CHTYPE_SATELLITE) { + if(ioctl(tdata->tfd, LNB_DISABLE, 0) < 0) { + rv = 1; + } + } + close(tdata->tfd); + + return rv; +} + +thread_data *gp_tdata; + +int +main(int argc, char **argv) +{ + time_t cur_time; + pthread_t signal_thread; + pthread_t reader_thread; + pthread_t stream_thread; + pthread_t ipc_thread; + pthread_t dlna_thread; + QUEUE_T *p_queue = create_queue(MAX_QUEUE); + STREAM_QUEUE_T *stream_queue = create_stream_queue(MAX_QUEUE); + BUFSZ *bufptr; + decoder *dec = NULL; + splitter *splitter = NULL; + static thread_data tdata; + gp_tdata = &tdata; + decoder_options dopt = { + 4, /* round */ + 0, /* strip */ + 0 /* emm */ + }; + tdata.dopt = &dopt; + tdata.lnb = 0; + + int result; + int option_index; + struct option long_options[] = { +#ifdef HAVE_LIBARIB25 + { "b25", 0, NULL, 'b'}, + { "B25", 0, NULL, 'b'}, + { "round", 1, NULL, 'r'}, + { "strip", 0, NULL, 's'}, + { "emm", 0, NULL, 'm'}, + { "EMM", 0, NULL, 'm'}, +#endif + { "LNB", 1, NULL, 'n'}, + { "lnb", 1, NULL, 'n'}, + { "udp", 0, NULL, 'u'}, + { "addr", 1, NULL, 'a'}, + { "port", 1, NULL, 'p'}, + { "device", 1, NULL, 'd'}, + { "help", 0, NULL, 'h'}, + { "version", 0, NULL, 'v'}, + { "list", 0, NULL, 'l'}, + { "sid", 1, NULL, 'i'}, + { "SID", 1, NULL, 'i'}, + { "es", 1, NULL, 'e'}, + { "ES", 1, NULL, 'e'}, + { "start_time", 1, NULL, 'y'}, + {0, 0, NULL, 0} /* terminate */ + }; + + boolean use_b25 = FALSE; + boolean use_udp = FALSE; + boolean fileless = FALSE; + boolean use_stdout = FALSE; + boolean use_splitter = FALSE; + char *host_to = NULL; + int port_to = 1234; + sock_data *sockdata = NULL; + char *device = NULL; + int val; + char *voltage[] = {"0V", "11V", "15V"}; + char *sid_list = NULL; + char *es_name_prefix = NULL; + char *start_time = NULL; + + while((result = getopt_long(argc, argv, "br:smn:ua:p:d:hvli:", + long_options, &option_index)) != -1) { + switch(result) { + case 'b': + use_b25 = TRUE; + fprintf(stderr, "using B25...\n"); + break; + case 's': + dopt.strip = TRUE; + fprintf(stderr, "enable B25 strip\n"); + break; + case 'm': + dopt.emm = TRUE; + fprintf(stderr, "enable B25 emm processing\n"); + break; + case 'u': + use_udp = TRUE; + host_to = "localhost"; + fprintf(stderr, "enable UDP broadcasting\n"); + break; + case 'h': + fprintf(stderr, "\n"); + show_usage(argv[0]); + fprintf(stderr, "\n"); + show_options(); + fprintf(stderr, "\n"); + show_channels(); + fprintf(stderr, "\n"); + exit(0); + break; + case 'v': + fprintf(stderr, "%s %s\n", argv[0], version); + fprintf(stderr, "recorder command for PT1/2 digital tuner.\n"); + exit(0); + break; + case 'l': + show_channels(); + exit(0); + break; + /* following options require argument */ + case 'n': + val = atoi(optarg); + switch(val) { + case 11: + tdata.lnb = 1; + break; + case 15: + tdata.lnb = 2; + break; + default: + tdata.lnb = 0; + break; + } + fprintf(stderr, "LNB = %s\n", voltage[tdata.lnb]); + break; + case 'r': + dopt.round = atoi(optarg); + fprintf(stderr, "set round %d\n", dopt.round); + break; + case 'a': + use_udp = TRUE; + host_to = optarg; + fprintf(stderr, "UDP destination address: %s\n", host_to); + break; + case 'p': + port_to = atoi(optarg); + fprintf(stderr, "UDP port: %d\n", port_to); + break; + case 'd': + device = optarg; + fprintf(stderr, "using device: %s\n", device); + break; + case 'i': + use_splitter = TRUE; + sid_list = optarg; + break; + case 'e': + es_name_prefix = optarg; + break; + case 'y': + start_time = optarg; + break; + } + } + + if(argc - optind < 3) { + if(argc - optind == 2 && use_udp) { + fprintf(stderr, "Fileless UDP broadcasting\n"); + fileless = TRUE; + tdata.wfd = -1; + } + else { + fprintf(stderr, "Arguments are necessary!\n"); + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + return 1; + } + } + + fprintf(stderr, "pid = %d\n", getpid()); + + /* tune */ + if(tune(argv[optind], &tdata, device) != 0) + return 1; + + /* set recsec */ + if(parse_time(argv[optind + 1], &tdata) != 0) + return 1; + + /* open output file */ + char *destfile = argv[optind + 2]; + if(destfile && !strcmp("-", destfile)) { + use_stdout = TRUE; + tdata.wfd = 1; /* stdout */ + } + else { + if(!fileless) { + int status; + char *path = strdup(argv[optind + 2]); + char *dir = dirname(path); + status = mkpath(dir, 0777); + if(status == -1) + perror("mkpath"); + free(path); + + tdata.wfd = open(argv[optind + 2], (O_RDWR | O_CREAT | O_TRUNC), 0666); + if(tdata.wfd < 0) { + fprintf(stderr, "Cannot open output file: %s\n", + argv[optind + 2]); + return 1; + } + } + } + + /* initialize decoder */ + if(use_b25) { + dec = b25_startup(&dopt); + if(!dec) { + fprintf(stderr, "Cannot start b25 decoder\n"); + fprintf(stderr, "Fall back to encrypted recording\n"); + use_b25 = FALSE; + } + } + /* initialize splitter */ + if(use_splitter) { + splitter = split_startup(sid_list, es_name_prefix, start_time); + if(splitter->sid_list == NULL) { + fprintf(stderr, "Cannot start TS splitter\n"); + return 1; + } + } + + boolean use_dlna = TRUE; + /* initialize DLNA */ + if(use_dlna) { + do { + tdata.stream_queue = stream_queue; + tdata.streamer = malloc(sizeof(streamer)); + if ( tdata.streamer == NULL ) { + use_dlna = FALSE; + break; + } + tdata.streamer->stream_nr = 0; + tdata.streamer->stream_session[0] = NULL; + pthread_mutex_init(&tdata.streamer->mutex, NULL); + } while(0); + } + + /* initialize udp connection */ + if(use_udp) { + sockdata = calloc(1, sizeof(sock_data)); + struct in_addr ia; + ia.s_addr = inet_addr(host_to); + if(ia.s_addr == INADDR_NONE) { + struct hostent *hoste = gethostbyname(host_to); + if(!hoste) { + perror("gethostbyname"); + return 1; + } + ia.s_addr = *(in_addr_t*) (hoste->h_addr_list[0]); + } + if((sockdata->sfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return 1; + } + + sockdata->addr.sin_family = AF_INET; + sockdata->addr.sin_port = htons (port_to); + sockdata->addr.sin_addr.s_addr = ia.s_addr; + + if(connect(sockdata->sfd, (struct sockaddr *)&sockdata->addr, + sizeof(sockdata->addr)) < 0) { + perror("connect"); + return 1; + } + } + + /* prepare thread data */ + tdata.queue = p_queue; + tdata.decoder = dec; + tdata.splitter = splitter; + tdata.sock_data = sockdata; + + /* spawn signal handler thread */ + init_signal_handlers(&signal_thread, &tdata); + + /* spawn reader thread */ + tdata.signal_thread = signal_thread; + pthread_create(&reader_thread, NULL, reader_func, &tdata); + pthread_create(&stream_thread, NULL, stream_func, &tdata); + pthread_create(&dlna_thread, NULL, dlna_startup, NULL); + + /* spawn ipc thread */ + key_t key; + key = (key_t)getpid(); + + if ((tdata.msqid = msgget(key, IPC_CREAT | 0666)) < 0) { + perror("msgget"); + } + pthread_create(&ipc_thread, NULL, mq_recv, &tdata); + + /* start recording */ + if(ioctl(tdata.tfd, START_REC, 0) < 0) { + fprintf(stderr, "Tuner cannot start recording\n"); + return 1; + } + + fprintf(stderr, "Recording...\n"); + + time(&tdata.start_time); + + /* read from tuner */ + while(1) { + if(f_exit) + break; + //fprintf (stderr, "main() while loop start.\n"); + + time(&cur_time); + bufptr = malloc(sizeof(BUFSZ)); + if(!bufptr) { + f_exit = TRUE; + break; + } + //fprintf (stderr, "main() loop#1.\n"); + bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE); + //fprintf (stderr, "main() loop#2.\n"); + if(bufptr->size <= 0) { + if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) { + //fprintf (stderr, "main() loop#3.\n"); + f_exit = TRUE; + enqueue(p_queue, NULL); + break; + } + else { + //fprintf (stderr, "main() loop#4.\n"); + continue; + } + } + //fprintf (stderr, "main() loop#5.\n"); + //fprintf (stderr, "PT1 enqueue start.\n"); + enqueue(p_queue, bufptr); + //fprintf (stderr, "PT1 enqueue finish.\n"); + + /* stop recording */ + time(&cur_time); + if((cur_time - tdata.start_time) >= tdata.recsec && !tdata.indefinite) { + ioctl(tdata.tfd, STOP_REC, 0); + /* read remaining data */ + while(1) { + bufptr = malloc(sizeof(BUFSZ)); + if(!bufptr) { + f_exit = TRUE; + break; + } + bufptr->size = read(tdata.tfd, bufptr->buffer, MAX_READ_SIZE); + if(bufptr->size <= 0) { + f_exit = TRUE; + enqueue(p_queue, NULL); + break; + } + enqueue(p_queue, bufptr); + } + break; + } + } + //fprintf (stderr, "main() break?.\n"); + + /* delete message queue*/ + msgctl(tdata.msqid, IPC_RMID, NULL); + + pthread_kill(signal_thread, SIGUSR1); + + /* wait for threads */ + pthread_join(reader_thread, NULL); + pthread_join(stream_thread, NULL); + pthread_join(signal_thread, NULL); + pthread_join(ipc_thread, NULL); +// pthread_join(dlna_startup, NULL); + pthread_join(dlna_thread, NULL); + + /* close tuner */ + if(close_tuner(&tdata) != 0) + return 1; + + /* release queue */ + destroy_queue(p_queue); + + /* close output file */ + if(!use_stdout) + close(tdata.wfd); + + /* free socket data */ + if(use_udp) { + close(sockdata->sfd); + free(sockdata); + } + + /* release decoder */ + if(use_b25) { + b25_shutdown(dec); + } + if(use_splitter) { + split_shutdown(splitter); + } + + return 0; +} + diff -r 215a51fa3df3 -r 9c7bc6c0327e src/recpt1.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/recpt1.h Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,104 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil -*- */ +#ifndef _RECPT1_H_ +#define _RECPT1_H_ +#include +#include +#include +#include +#include "decoder.h" +#include "tssplitter_lite.h" + +#define HAVE_LIBARIB25 1 +#define NUM_BSDEV 8 +#define NUM_ISDB_T_DEV 8 +#define CHTYPE_SATELLITE 0 /* satellite digital */ +#define CHTYPE_GROUND 1 /* terrestrial digital */ +#define MAX_QUEUE 8192 +#define MAX_READ_SIZE (188 * 87) /* 188*87=16356 splitterが188アライメントを期待しているのでこの数字とする*/ +#define WRITE_SIZE (1024 * 1024 * 2) +#define TRUE 1 +#define FALSE 0 +#define STREAM_MAX (16) + + +/* type definitions */ +typedef int boolean; + +typedef struct _BUFSZ { + int size; + u_char buffer[MAX_READ_SIZE]; +} BUFSZ; + +typedef struct _QUEUE_T { + unsigned int in; // 次に入れるインデックス + unsigned int out; // 次に出すインデックス + unsigned int size; // キューのサイズ + unsigned int num_avail; // 満タンになると 0 になる + unsigned int num_used; // 空っぽになると 0 になる + pthread_mutex_t mutex; + pthread_cond_t cond_avail; // データが満タンのときに待つための cond + pthread_cond_t cond_used; // データが空のときに待つための cond + BUFSZ *buffer[1]; // バッファポインタ +} QUEUE_T; + +typedef struct _STREAM_QUEUE_T { + unsigned int in; // 次に入れるインデックス + unsigned int out; // 次に出すインデックス + unsigned int size; // キューのサイズ + unsigned int num_avail; // 満タンになると 0 になる + unsigned int num_used; // 空っぽになると 0 になる + pthread_mutex_t mutex; + pthread_cond_t cond_avail; // データが満タンのときに待つための cond + pthread_cond_t cond_used; // データが空のときに待つための cond + ARIB_STD_B25_BUFFER *buffer[1]; // バッファポインタ +} STREAM_QUEUE_T; + +typedef struct _ISDB_T_FREQ_CONV_TABLE { + int set_freq; // 実際にioctl()を行う値 + int type; // チャンネルタイプ + int add_freq; // 追加する周波数(BS/CSの場合はスロット番号) + char *parm_freq; // パラメータで受ける値 +} ISDB_T_FREQ_CONV_TABLE; + +typedef struct sock_data { + int sfd; /* socket fd */ + struct sockaddr_in addr; +} sock_data; + +typedef struct _session { + int id; + int is_valid; + QUEUE_T *p_queue; +} session; + +typedef struct _streamer { + pthread_mutex_t mutex; //open、close、(recpt1からの)write + int stream_nr; + session *stream_session[STREAM_MAX]; //NULL止めの配列 +} streamer; + +typedef struct thread_data { + QUEUE_T *queue; + QUEUE_T *stream_queue; + decoder *decoder; + decoder_options *dopt; + int ch; + int lnb; /* LNB voltage */ + int tfd; /* tuner fd */ + int wfd; /* output file fd */ + ISDB_T_FREQ_CONV_TABLE *table; + sock_data *sock_data; + pthread_t signal_thread; + int recsec; + time_t start_time; + boolean indefinite; + int msqid; + splitter *splitter; + streamer *streamer; +} thread_data; + +QUEUE_T *create_queue(size_t size); +BUFSZ * dequeue(QUEUE_T *p_queue); +void destroy_queue(QUEUE_T *p_queue); + +#endif diff -r 215a51fa3df3 -r 9c7bc6c0327e src/recpt1ctl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/recpt1ctl.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,214 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "version.h" + +#define MSGSZ 255 + +typedef struct msgbuf { + long mtype; + char mtext[MSGSZ]; +} message_buf; + +void +show_usage(char *cmd) +{ + fprintf(stderr, "Usage: \n%s --pid pid [--channel channel] [--extend time_to_extend] [--time recording_time]\n", cmd); + fprintf(stderr, "\n"); +} + +void +show_options(void) +{ + fprintf(stderr, "Options:\n"); + fprintf(stderr, "--pid: Process id of recpt1 to control\n"); + fprintf(stderr, "--channel: Tune to specified channel\n"); + fprintf(stderr, "--extend: Extend recording time\n"); + fprintf(stderr, "--time: Set total recording time\n"); + fprintf(stderr, "--help: Show this help\n"); + fprintf(stderr, "--version: Show version\n"); + fprintf(stderr, "--list: Show channel list\n"); +} + +void +show_channels(void) +{ + FILE *f; + char *home; + char buf[255], filename[255]; + + fprintf(stderr, "Available Channels:\n"); + + home = getenv("HOME"); + sprintf(filename, "%s/.recpt1-channels", home); + f = fopen(filename, "r"); + if(f) { + while(fgets(buf, 255, f)) + fprintf(stderr, "%s", buf); + fclose(f); + } + else + fprintf(stderr, "13-62: Terrestrial Channels\n"); + + fprintf(stderr, "101ch: NHK BS1\n"); + fprintf(stderr, "102ch: NHK BS2\n"); + fprintf(stderr, "103ch: NHK BShi\n"); + fprintf(stderr, "141ch: BS Nittele\n"); + fprintf(stderr, "151ch: BS Asahi\n"); + fprintf(stderr, "161ch: BS-TBS\n"); + fprintf(stderr, "171ch: BS Japan\n"); + fprintf(stderr, "181ch: BS Fuji\n"); + fprintf(stderr, "191ch: WOWOW\n"); + fprintf(stderr, "200ch: Star Channel\n"); + fprintf(stderr, "211ch: BS11 Digital\n"); + fprintf(stderr, "222ch: TwellV\n"); + fprintf(stderr, "CS2-CS24: CS Channels\n"); +} + +int +parse_time(char *rectimestr, int *recsec) +{ + /* indefinite */ + if(!strcmp("-", rectimestr)) { + *recsec = -1; + } + /* colon */ + else if(strchr(rectimestr, ':')) { + int n1, n2, n3; + if(sscanf(rectimestr, "%d:%d:%d", &n1, &n2, &n3) == 3) + *recsec = n1 * 3600 + n2 * 60 + n3; + else if(sscanf(rectimestr, "%d:%d", &n1, &n2) == 2) + *recsec = n1 * 3600 + n2 * 60; + } + /* HMS */ + else { + char *tmpstr; + char *p1, *p2; + + tmpstr = strdup(rectimestr); + p1 = tmpstr; + while(*p1 && !isdigit(*p1)) + p1++; + + /* hour */ + if((p2 = strchr(p1, 'H')) || (p2 = strchr(p1, 'h'))) { + *p2 = '\0'; + *recsec += atoi(p1) * 3600; + p1 = p2 + 1; + while(*p1 && !isdigit(*p1)) + p1++; + } + + /* minute */ + if((p2 = strchr(p1, 'M')) || (p2 = strchr(p1, 'm'))) { + *p2 = '\0'; + *recsec += atoi(p1) * 60; + p1 = p2 + 1; + while(*p1 && !isdigit(*p1)) + p1++; + } + + /* second */ + *recsec += atoi(p1); + + free(tmpstr); + } + + return 0; /* success */ +} + +int +main(int argc, char **argv) +{ + int msqid; + int msgflg = IPC_CREAT | 0666; + key_t key = 0; + int channel=0, recsec = 0, extsec=0; + message_buf sbuf; + size_t buf_length; + + int result; + int option_index; + struct option long_options[] = { + { "pid", 1, NULL, 'p'}, + { "channel", 1, NULL, 'c'}, + { "extend", 1, NULL, 'e'}, + { "time", 1, NULL, 't'}, + { "help", 0, NULL, 'h'}, + { "version", 0, NULL, 'v'}, + { "list", 0, NULL, 'l'}, + {0, 0, NULL, 0} /* terminate */ + }; + + while((result = getopt_long(argc, argv, "p:c:e:t:hvl", + long_options, &option_index)) != -1) { + switch(result) { + case 'h': + fprintf(stderr, "\n"); + show_usage(argv[0]); + fprintf(stderr, "\n"); + show_options(); + fprintf(stderr, "\n"); + show_channels(); + fprintf(stderr, "\n"); + exit(0); + break; + case 'v': + fprintf(stderr, "%s %s\n", argv[0], version); + fprintf(stderr, "control command for recpt1.\n"); + exit(0); + break; + case 'l': + show_channels(); + exit(0); + break; + /* following options require argument */ + case 'p': + key = (key_t)atoi(optarg); + fprintf(stderr, "Pid = %d\n", key); + break; + case 'c': + channel = atoi(optarg); + fprintf(stderr, "Channel = %d\n", channel); + break; + case 'e': + parse_time(optarg, &extsec); + fprintf(stderr, "Extend %d sec\n", extsec); + break; + case 't': + parse_time(optarg, &recsec); + fprintf(stderr, "Total recording time = %d sec\n", recsec); + break; + } + } + + if(!key) { + fprintf(stderr, "Arguments are necessary!\n"); + fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); + exit(1); + } + + if ((msqid = msgget(key, msgflg )) < 0) { + perror("msgget"); + exit(1); + } + + sbuf.mtype = 1; + sprintf(sbuf.mtext, "ch=%d t=%d e=%d", channel, recsec, extsec); + + buf_length = strlen(sbuf.mtext) + 1 ; + + if (msgsnd(msqid, &sbuf, buf_length, IPC_NOWAIT) < 0) { + perror("msgsnd"); + exit(1); + } + + exit(0); +} diff -r 215a51fa3df3 -r 9c7bc6c0327e src/tssplitter_lite.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tssplitter_lite.c Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,2133 @@ +/* -*- tab-width: 4; indent-tabs-mode: t -*- */ +/* vim: set ts=4 sts=4 sw=4 noexpandtab number : */ +/* tssplitter_lite.c -- split TS stream. + + Copyright 2009 querulous + Copyright 2010 Naoya OYAMA + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "decoder.h" +#include "recpt1.h" +#include "tssplitter_lite.h" + +#ifndef AV_RB32 +#define AV_RB32(x) ((((const uint8_t*)(x))[0] << 24) | \ + (((const uint8_t*)(x))[1] << 16) | \ + (((const uint8_t*)(x))[2] << 8) | \ + ((const uint8_t*)(x))[3]) +#endif + +#ifndef AV_RB24 +#define AV_RB24(x) ((((const uint8_t*)(x))[0] << 16) | \ + (((const uint8_t*)(x))[1] << 8) | \ + ((const uint8_t*)(x))[2]) +#endif + +#ifndef AV_RB16 +#define AV_RB16(x) ((((const uint8_t*)(x))[0] << 8) | ((const uint8_t*)(x))[1]) +#endif +#define MAX_SERVICE_ID ( 0xffff ) +#define LIST_DECIMAL "0123456789" +#define TSS_STREAM_TYPE_AUDIO (1) +#define TSS_STREAM_TYPE_VIDEO (2) + +/* prototypes */ +static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); +static int AnalyzePat(splitter *sp, unsigned char *buf); +static int RecreatePat(splitter *sp, unsigned char *buf, int *pos); +static char** AnalyzeSid(char *sid); +static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size); +static int GetCrc32(unsigned char *data, int len); +static int GetPid(unsigned char *data); +static int parse_tot( const unsigned char* packet, time_t *t ); +//void dump_packet( const uint8_t *packet ); +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, const uint8_t *packet); +static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid); +//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator); +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid); +static int64_t get_pts(const uint8_t *p); +//void search_mpeg_system_header(const uint8_t *p); +static int esbuf_write(splitesbuf_t *esbuf); +static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf); +static int pesbuf_empty(splitpesbuf_t *pesbuf); +void pesbuf_clear(splitpesbuf_t *pesbuf); +static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len); +static int esbuf_empty(splitesbuf_t *esbuf); +void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts); +static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len); +static int get_pmt_version(const uint8_t *p); +static int next_adts_start_code(splitesbuf_t *esbuf, int offset); +static int is_video_stream(const int pid, splitesbuf_t *esbuf); +static int is_audio_stream(const int pid, splitesbuf_t *esbuf); +static int AnalyzeAdifHeader(splitesbuf_t *esbuf); +static int get_adif_id(uint8_t *p); +static int get_adif_layer(uint8_t *p); +static int get_adif_protection_absent(uint8_t *p); +static int get_adif_profile(uint8_t *p); +static int get_adif_sampling_frequency_index(uint8_t *p); +static int get_adif_private_bit(uint8_t *p); +static int get_adif_channel_configuration(uint8_t *p); +static int get_adif_original_copy(uint8_t *p); +static int get_adif_home(uint8_t *p); +static int get_adif_copyright_idication_bit(uint8_t *p); +static int get_adif_copyright_idication_start(uint8_t *p); +static int get_adif_aac_frame_length(uint8_t *p); +static int get_adts_buffer_fullness(uint8_t *p); +static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p); +//static int search_pmt_program(splitter *sp, int pid); +static int search_gop_start_code(splitesbuf_t *esbuf); +static int creat_es_file(splitter *sp, int sid, int pid, int av_flag); +static time_t cue2time(char *yyyymmddhhmiss); +static int search_pcr_pid(splitter *sp, int pid); + +/** + * サービスID解析 + */ +static char** AnalyzeSid( + char* sid) // [in] サービスID(カンマ区切りテキスト) +{ + int i = 0; + char** sid_list = NULL; + char* p; + int CommaNum = 0; + + /* sid は次の形式の引数を許容する */ + /* 指定無し */ + /* SID[0] */ + /* SID[0],SID[1],...,SID[N-1],SID[N] */ + + /*カンマの数を数える*/ + p = sid; + while(*p != '\0') + { + if( *p == C_CHAR_COMMA ){ + CommaNum++; + } + p++; + } + + /* sid_listの数はカンマの数+2(NULL止めするから) */ + sid_list = malloc(sizeof(char*)*(CommaNum+2)); + if ( sid_list == NULL ) + { + fprintf(stderr, "AnalyzeSid() malloc error.\n"); + return NULL; + } + + /* sidが空である場合 */ + p = sid; + if ( strlen(p) == 0 ) + { + sid_list[0] = NULL; + return sid_list; + } + + /* カンマ無し */ + if ( CommaNum == 0 ) + { + sid_list[0] = sid; + sid_list[1] = NULL; + return sid_list; + } + + /* カンマ区切りで複数指定時 */ + i=0; + p = sid; + /* 文字列端に到達するか、カンマ数が数えた数に達したら終了 */ + while((*p != '\0') || i < CommaNum) + { + /* 現在の処理位置をsid_list[i]にセット */ + /* このタイミングの p は + * ・sid先頭 + * ・[,]の次の文字 + * いずれかであるので p を sid_list[i] に代入してよい + */ + sid_list[i] = p; + i++; + + /* 最初に現れる[,]をNULL文字で置換する */ + p = strchr(p, C_CHAR_COMMA); + if ( p == NULL ) + { + /* カンマが見つからない場合は最後の処理対象なので終了 */ + break; + } + *p = '\0'; + /* 処理位置をNULLで置換した文字の次の位置に設定する */ + p++; + } + + /* 最後のsid_list[n]はNULLポインタで止める */ + sid_list[i] = NULL; + + i=0; + while( sid_list[i] != NULL ) + { + i++; + } +#if 0 + for(i=0; sid_list[i] != NULL; i++) + { + printf("sid_list[%d]=[%s].\n",i, sid_list[i]); + } +#endif + return sid_list; +} + +/** + * 初期化処理 + */ +splitter* split_startup( + char *sid, // [in] サービスID(引数で指定した文字列) + char *filename, // [in] 出力ESファイル名(引数で指定したファイル名) + char *arg_cue // [in] 録画開始時刻(引数で指定した文字列 YYYYMMDDHHMISS) +) +{ + splitter* sp; + int i; + sp = malloc(sizeof(splitter)); + if ( sp == NULL ) + { + fprintf(stderr, "split_startup malloc error.\n"); + return NULL; + } + sp->program = malloc( sizeof(program_t) * MAX_SERVICE_ID ); + if ( sp->program == NULL ) + { + fprintf(stderr, "split_startup malloc error.\n"); + return NULL; + } + memset(sp->pids, 0, sizeof(sp->pids)); + memset(sp->pmt_pids, 0, sizeof(sp->pmt_pids)); + memset(sp->cat_pids, 0, sizeof(sp->cat_pids)); + memset(sp->pcr_pids, 0, sizeof(sp->pcr_pids)); + memset(sp->pcr, 0, sizeof(sp->pcr)); + + sp->sid_list = NULL; + sp->pat = NULL; + sp->sid_list = AnalyzeSid(sid); + if ( sp->sid_list == NULL ) + { + free(sp); + return NULL; + } + sp->pat_count = 0xFF; + sp->pmt_retain = -1; + sp->pmt_counter = 0; + sp->time_cue = 0; + sp->time_tot = 0; + sp->pcr_nb = 0; + memset(sp->esbuf, 0, sizeof(splitesbuf_t *)*MAX_PID); + memset(sp->pesbuf, 0, sizeof(splitpesbuf_t *)*MAX_PID); + memset(sp->program, 0, sizeof(program_t *)*MAX_SERVICE_ID); + for ( i=0; i < MAX_PID; i++ ) { + /* pmt_version は (N%32) の値を取るので、0 で初期化してはならない */ + sp->program[i].pmt_version = -1; + /* cue は最大値で初期化(CUE <= STCとなると録画開始するため) */ + sp->program[i].cue = INT64_MAX; + } + memset(sp->pid_sid_table, 0, sizeof(int)*MAX_PID); + if ( filename != NULL ) { + sp->esout = 1; + sp->filename = filename; + } + if ( arg_cue != NULL ) { + sp->arg_cue = arg_cue; + } else { + sp->arg_cue = "00000000000000"; /* とりあえず最小値 */ + } + return sp; +} + +/** + * 落とすPIDを確定させる + */ +int split_select( + splitter *sp, // [in/out] splitter構造体 + ARIB_STD_B25_BUFFER *sbuf // [in] 入力TS +) +{ + int result; + // TS解析 + result = ReadTs(sp, sbuf); + + return result; +} + +/** + * 終了処理 + */ +void split_shutdown(splitter* sp) +{ + int i = 0; + if ( sp != NULL ) { + if ( sp->pat != NULL ) + { + free(sp->pat); + sp->pat = NULL; + } + if ( sp->sid_list != NULL ) + { + free(sp->sid_list); + sp->sid_list = NULL; + } + for(i=0; i < MAX_PID; i++) { + if ( sp->esbuf[i] != NULL ) { + if ( sp->esbuf[i]->fd != -1 ) { + close(sp->esbuf[i]->fd); + sp->esbuf[i]->fd = -1; + } + free(sp->esbuf[i]); + sp->esbuf[i] = NULL; + } + if ( sp->pesbuf[i] != NULL ) { + free(sp->pesbuf[i]); + sp->pesbuf[i] = NULL; + } + } + free(sp); + sp = NULL; + } +} + +/** + * TS 解析処理 + * + * 対象のチャンネル番号のみの PAT の再構築と出力対象 PID の抽出を行う + */ +static int ReadTs(splitter *sp, ARIB_STD_B25_BUFFER *sbuf) +{ +#if 0 + splitter *sp, // [in/out] splitter構造体 + ARIB_STD_B25_BUFFER *sbuf, // [in] pt1_drvの入力TS +#endif + + int length = sbuf->size; + int pid; + int result = TSS_ERROR; + int index; + + index = 0; + while(length - index - LENGTH_PACKET > 0) { + pid = GetPid(sbuf->data + index + 1); + // PAT + if(PAT == pid) { + result = AnalyzePat(sp, sbuf->data + index); + if(TSS_SUCCESS != result) { + /* 下位の関数内部でmalloc error発生 */ + return result; + } + } + + // PMT + /* 残すpmt_pidである場合には、pmtに書かれている + * 残すべきPCR/AUDIO/VIDEO PIDを取得する */ + if(sp->pmt_pids[pid] == 1) { + /* この中にはPMT毎に一度しか入らないようにしておく */ + sp->pmt_pids[pid]++; + sp->pmt_counter += 1; + DemuxTs(sbuf->data +index, sp, pid); /* AnalyzePmt より DemuxTs の方がアダプテーションフィールドの処理が良いので変更 */ + } + /* 録画する全てのPMTについて、中にあるPCR/AUDIO/VIDEOのPIDを得る */ + /* pmt_counter と pmt_retain が一致する場合に条件は満たされる */ + if(sp->pmt_counter == sp->pmt_retain) { + result = TSS_SUCCESS; + break; + } + else { + result = TSS_ERROR; + } + index += LENGTH_PACKET; + } + + return(result); +} + +/** + * TS 分離処理 + */ +int split_ts( + splitter *sp, // [in] splitterパラメータ + ARIB_STD_B25_BUFFER *sbuf, // [in] 入力TS + splitbuf_t *dbuf // [out] 出力TS +) +{ + int pid; + unsigned char *sptr, *dptr; + int s_offset = 0; + int d_offset = 0; + int64_t pcr_h = 0; + int pcr_l = 0; + int ret = 0; + int sid = 0; + program_t *program; + static int packet_nb; /* パケット受信数 */ + int i = 0; + + /* 初期化 */ + dbuf->size = 0; + if (sbuf->size < 0) { + return TSS_ERROR; + } + + sptr = sbuf->data; + dptr = dbuf->buffer; + + while(sbuf->size > s_offset) { + pid = GetPid(sptr + s_offset + 1); + sid = sp->pid_sid_table[pid]; /* PIDからSIDを取得 */ + switch(pid) { + + // PAT + case PAT: + // 巡回カウンタカウントアップ + if(0xFF == sp->pat_count) { + sp->pat_count = sp->pat[3]; + } + else { + sp->pat_count += 1; + if(0 == sp->pat_count % 0x10) { + sp->pat_count -= 0x10; + } + } + sp->pat[3] = sp->pat_count; + + memcpy(dptr + d_offset, sp->pat, LENGTH_PACKET); + d_offset += LENGTH_PACKET; + dbuf->size += LENGTH_PACKET; + break; + case TOT: + /* TOT に TDTの情報全てが含まれており、実放送では TOT しか送信されない */ + /* TOT は 500msec の誤差が保証されている + * 閏秒の場合は最大1.5秒の誤差となる + */ + if ( sp->time_tot == 0 ) { + /* splitter構造体の時刻関係(TOT/CUE)のパラメータ初期化 */ + parse_tot(sptr + s_offset, &(sp->time_tot)); + sp->time_cue = cue2time(sp->arg_cue); + sp->tot_packet_nb = packet_nb; + } + break; + default: + /* ■時間管理に関しての実装方針■ */ + /* + * ■時間関係を扱っている変数■ + * PCR : 42Bit @27MHz(ServiceID(ProgramID, sid)毎に独立) + * PTS : 42Bit @90KHz(ES毎に独立) + * DTS : 42Bit @90KHz(ES毎に独立) + * TOT : MJD + 2進化10進数(TSに1つだけ) + * STC : 64Bit @27MHz(ServiceID(ProgramID, sid)の現在時刻(ソースはPCR)) + * CUE : 録画開始時刻 YYYYMMDDHHMISS (引数指定) + * + * ■STCの管理方針■ + * PCR受信時にSTCに時間情報をコピーする + * 1パケットを受信すると経過する時間を加算してSTCを管理する + * (ffmpegのmpegts.cと同じ方針) + * + * ■CUEとTOTとSTC■ + * TOTからSTCと比較するための情報を作成する。 + * ・TOTは time_t + * ・CUEは time_t + * ・STCは42Bitの27MHz周期の数値 + * TOT 受信時に各Program(ServiceID)のSTCを変換基準値とする + * 録画開始時(27MHz)の値の計算式は、 + * Program->CUE = STCの変換基準値 + 27e6*(ARG_CUE-TOT(秒)) + * 録画の開始確認として Program->CUE と PTS を比較すればよい + * + * ※PCR/PTS/DTSはオーバーフローしたときには34Bit目が立っていると見なすこと※ + * オーバーフロー時の処理は未実装 + */ + if ( 2 == sp->pmt_pids[pid] ) { + /* PMT の追跡 */ + DemuxTs((sptr+s_offset), sp, pid); + } + /* pids[pid] が 1 は残すパケットなので書き込む */ + if ( (1 == sp->pids[pid]) ) { + /* PCRとSTCの処理 */ + int pcr_index; + pcr_index = search_pcr_pid(sp, pid); + if ( sp->pcr_pids[pid] == 1 && pcr_index != -1) { /* PCRか否か */ + ret = parse_pcr(&pcr_h, &pcr_l, (sptr+s_offset)); + /* + * PCR は複数 ServiceID(ProgramID)で重複利用される場合がある + * PCR を参照する複数の ServiceID(ProgramID)分ループ + */ + for (i=0; i < sp->pcr[pcr_index].sid_nb; i++) { + sid = sp->pcr[pcr_index].sid[i]; + program = &(sp->program[sid]); + /* こっから */ + if ( ret == 0 ) { /* PCR の解析に成功 */ + program->stc = pcr_h * 300 + pcr_l; /* PCR受信時にSTCを補正*/ + if ( program->pcr1 == 0 ) { + program->pcr1 = program->stc; + printf("pcr1 pid[%d] sid[%d] packet_nb[%d] sid_nb[%d] i[%d]\n", + pid, sid, packet_nb, sp->pcr[pcr_index].sid_nb, i); + } else if ( program->pcr2 == 0 ) { +// printf("pcr2 pid[%d] sid[%d] packet_nb[%d], p_packet_nb[%d] sid_nb[%d] i[%d]\n", +// pid, sid, packet_nb, program->pcr_packet_nb, sp->pcr[pcr_index].sid_nb, i); + program->pcr2 = program->stc; + program->pcr_incr = (program->pcr2 -program->pcr1) + /(packet_nb -program->pcr_packet_nb); + printf("pcr2 pid[%d] sid[%d] pcr_incr[%llu]\n", + pid, sid, program->pcr_incr); + } else { + /* PCR処理済み */ + ; /* 得に処理無し */ + } + if ( (program->cue == INT64_MAX ) && + (sp->arg_cue != NULL) && + (sp->time_tot != 0) ) { /* 録画開始時刻指定時 */ + /* + * 録画開始時刻 = STC +(CUE -TOT)*27MHz + * +(TOT取得時から現在までのパケット数の増分)*パケットあたりの進む時間 + * -0.49秒(この数字は適当) + * TOT/STCで時間調整して、GOP先頭から出すので攻めてしまっていい気がする + */ + program->cue = program->stc + +(sp->time_cue -sp->time_tot)*27e6 + +(packet_nb -sp->tot_packet_nb)*program->pcr_incr + -(27e6*49/100); + printf("STC[%llu] CUE[%llu] SID[%d]\n", + program->stc, program->cue, sid); + } + program->pcr_packet_nb = packet_nb; + program->packet_nb = packet_nb; + } else if ( program->pcr_incr ) { /* PCRの解析に失敗且つpcr_incr変数の計算済み*/ + /* STCを成長させる */ + program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; + program->packet_nb = packet_nb; + } else { + ; /* STCを進める要素が揃ってません */ + } + } /* for */ + } else { /* 処理対象パケットはPCRではない */ + program = &(sp->program[sid]); + if ( program->pcr_incr ) { /* PCRを受信しない且つpcr_incr変数の計算済み */ + /* STCを成長させる */ + program->stc += (packet_nb -program->packet_nb)*program->pcr_incr; + program->packet_nb = packet_nb; + } else { /* それ以外 */ + ; /* STCを進める要素が揃ってません */ + } + } +#if 0 +// NHK Gを ALL とすると SID 1024 しか出ない...orz.. + if ( !(packet_nb % 1000) ) { + program = &(sp->program[sid]); + printf("STC[%llu] SID[%d]\n", program->stc, sid); + } +#endif + /* TS処理 */ + DemuxTs((sptr+s_offset), sp, pid); + memcpy(dptr + d_offset, sptr + s_offset, LENGTH_PACKET); + d_offset += LENGTH_PACKET; + dbuf->size += LENGTH_PACKET; + } + break; + } /* switch */ + s_offset += LENGTH_PACKET; + packet_nb += 1; /* パケット受信数加算 */ + } + return(TSS_SUCCESS); +} + +/** + * PAT 解析処理 + * + * PAT を解析し、出力対象チャンネルが含まれているかチェックを行い、PAT を再構築する + */ +static int AnalyzePat(splitter *sp, unsigned char *buf) +#if 0 + unsigned char* buf, // [in] 読み込んだバッファ + unsigned char** pat, // [out] PAT 情報(再構築後) + unsigned char* pids, // [out] 出力対象 PID 情報 + char** sid_list, // [in] 出力対象サービス ID のリスト + unsigned char* pmt_pids, // [out] サービス ID に対応する PMT の PID + int* pmt_retain // [out] 残すPMTの数 +) +#endif +{ + int pos[MAX_PID]; + int service_id; + int i, j, k; + int size = 0; + int pid; + int result = TSS_SUCCESS; + char **p; + int sid_found = FALSE; + int avail_sids[MAX_SERVICES]; + + unsigned char *pat = sp->pat; + unsigned char *pids = sp->pids; + char **sid_list = sp->sid_list; + unsigned char *pmt_pids = sp->pmt_pids; + + char chosen_sid[512]; + chosen_sid[0] = '\0'; + + if(pat == NULL) { + /* 初期化 */ + sp->pmt_retain = 0; + memset(pos, 0, sizeof(pos)); + size = buf[7]; + + /* prescan SID/PMT */ + for(i = 17, j = 0; i < (size + 8) - 4; i = i + 4, j++) { + avail_sids[j] = (buf[i] << 8) + buf[i+1]; + sp->avail_pmts[j] = GetPid(&buf[i+2]); + } + sp->num_pmts = j; + + // 対象チャンネル判定 + /* size + 8 = パケット全長 */ + /* 最終 4 バイトはCRCなので飛ばす */ + for(i = 17; i < (size + 8) - 4; i = i + 4) { + + service_id = (buf[i] << 8) + buf[i+1]; + p = sid_list; + + while(*p) { + if(service_id == atoi(*p)) { + /* 録画対象の pmt_pids は 1 とする */ + /* 録画対象の pmt の pids は 1 とする */ + /* 対応する pid_sid_table に サービスID(ProgramID) を入れる */ + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + p++; + continue; + } + else if(!strcasecmp(*p, "hd") || !strcasecmp(*p, "sd1")) { + /* hd/sd1 指定時には1番目のサービスを保存する */ + if(service_id == avail_sids[0]) { + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + } + p++; + continue; + } + else if(!strcasecmp(*p, "sd2")) { + /* sd2 指定時には2番目のサービスを保存する */ + if(service_id == avail_sids[1]) { + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + } + p++; + continue; + } + else if(!strcasecmp(*p, "sd3")) { + /* sd3 指定時には3番目のサービスを保存する */ + if(service_id == avail_sids[2]) { + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + } + p++; + continue; + } + else if(!strcasecmp(*p, "1seg")) { + /* 1seg 指定時には PMTPID=0x1FC8 のサービスを保存する */ + pid = GetPid(&buf[i + 2]); + if(pid == 0x1FC8) { + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + } + p++; + continue; + } + else if(!strcasecmp(*p, "all")) { + /* all指定時には全保存する */ + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + break; + } + + p++; + } /* while */ + } + + /* if sid has been specified but no sid found, fall back to all */ + if(*sid_list && !sid_found) { + for(i = 17; i < (size + 8) - 4; i = i + 4) { + service_id = (buf[i] << 8) + buf[i+1]; + pid = GetPid(&buf[i + 2]); + *(pmt_pids+pid) = 1; + *(pids+pid) = 1; + pos[pid] = i; + sid_found = TRUE; + sp->pmt_retain += 1; + sp->program[service_id].pmt_packet_id = pid; + sp->pid_sid_table[pid] = service_id; + sprintf(chosen_sid, "%s %d", *chosen_sid ? chosen_sid : "", service_id); + } + } + + /* print SIDs */ + fprintf(stderr, "Available sid = "); + for(k=0; k < sp->num_pmts; k++) + fprintf(stderr, "%d ", avail_sids[k]); + fprintf(stderr, "\n"); + fprintf(stderr, "Chosen sid =%s\n", chosen_sid); + +#if 0 + /* print PMTs */ + fprintf(stderr, "Available PMT = "); + for(k=0; k < sp->num_pmts; k++) + fprintf(stderr, "%d ", sp->avail_pmts[k]); + fprintf(stderr, "\n"); +#endif + + // PAT 再構築 + result = RecreatePat(sp, buf, pos); +#if 0 + int tc; + for(tc=0; tc<188; tc++) + fprintf(stderr, "%02x ", *(pat+tc)); +#endif + } + + return(result); +} + +/** + * PAT 再構築処理 + * + * PMT から出力対象チャンネル以外のチャンネル情報を削除し、PAT を再構築する + */ +static int RecreatePat(splitter *sp, unsigned char *buf, int *pos) +#if 0 + unsigned char* buf, // [in] 読み込んだバッファ + unsigned char** pat, // [out] PAT 情報(再構築後) + unsigned char* pids, // [out] 出力対象 PID 情報 + int *pos) // [in] 取得対象 PMT のバッファ中の位置 +#endif +{ + unsigned char y[LENGTH_CRC_DATA]; + int crc; + int i; + int j; + int pos_i; + int pid_num = 0; + + // CRC 計算のためのデータ + { + // チャンネルによって変わらない部分 + for (i = 0; i < LENGTH_PAT_HEADER; i++) + { + y[i] = buf[i + 5]; + } + // チャンネルによって変わる部分 + for (i = 0; i < MAX_PID; i++) + { + if(pos[i] != 0) + { + /* buf[pos_i] を y にコピー(抽出したPIDの数) */ + pos_i = pos[i]; + for (j = 0; j < 4; j++) + { + y[LENGTH_PAT_HEADER + ((4*pid_num) + j)] = buf[pos_i + j]; + } + pid_num++; + } + } + } + /* パケットサイズ計算 */ + y[2] = pid_num * 4 + 0x0d; + // CRC 計算 + crc = GetCrc32(y, LENGTH_PAT_HEADER + pid_num*4); + + // PAT 再構成 + sp->pat = (unsigned char*)malloc(LENGTH_PACKET); + if(sp->pat == NULL) + { + fprintf(stderr, "RecreatePat() malloc error.\n"); + return(TSS_NULL); + } + memset(sp->pat, 0xFF, LENGTH_PACKET); + for (i = 0; i < 5; i++) + { + (sp->pat)[i] = buf[i]; + } + for (i = 0; i < LENGTH_PAT_HEADER + pid_num*4; i++) + { + (sp->pat)[i + 5] = y[i]; + } + (sp->pat)[5 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 24) & 0xFF; + (sp->pat)[6 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 16) & 0xFF; + (sp->pat)[7 + LENGTH_PAT_HEADER + pid_num*4] = (crc >> 8) & 0xFF; + (sp->pat)[8 + LENGTH_PAT_HEADER + pid_num*4] = (crc ) & 0xFF; + + return(TSS_SUCCESS); +} + + +/** + * PMT 解析処理 + * + * PMT を解析し、保存対象の PID を特定する + * TSヘッダとアダプテーションフィールドの処理は DemuxTs に一任するので、 + * この内部では、セクションデータの先頭ポインタをもらってくる + */ +static int AnalyzePmt(splitter *sp, const uint8_t *buf, int sid, const int size) +#if 0 + unsigned char* buf, // [in] セクション先頭 + unsigned char* pids) // [out] 出力対象 PID 情報 +#endif +{ + unsigned char Nall; + unsigned char N; + int pcr; + int epid; + int av_flag = 0; + int i = 0; + int j = 0; + int pcr_found = 0; +/* デバッグ用 PMT情報表示 */ +#define PmtDebug (1) + +#ifdef PmtDebug + printf("AnalyzePmt start. Tree List enable.\n"); + printf("SID[%d][0x%04x]\n", sid, sid); +#endif + +// Nall = ((buf[2] & 0x0F) << 4) + buf[3]; + Nall = ((buf[2] & 0x0F) << 8) + buf[3]; +// ここで受け取るのはTSパケットではなく、セクションの先頭ポインタであるのでsizeで見る +// if(Nall > LENGTH_PACKET) +// Nall = LENGTH_PACKET - 8; /* xxx workaround --yaz */ + if(Nall > size) + Nall = size -8; + + /* get version */ + sp->program[sid].pmt_version = get_pmt_version(buf); +#ifdef PmtDebug + printf(" pmt_version[%02x]\n", sp->program[sid].pmt_version); +#endif + + // PCR + pcr = GetPid(&buf[9]); + sp->pids[pcr] = 1; + sp->program[sid].pcr_packet_id = pcr; + sp->pid_sid_table[pcr] = sid; /* PCRは重複する可能性があるので方式がよろしくない */ + sp->pcr_pids[pcr] = 1; + + /* PCRの重複チェック(複数ServiceID(ProgramID)) */ + for( i=0; i < sp->pcr_nb; i++ ) { + if ( sp->pcr[i].pid == pcr ) { + /* 発見 */ + for ( j=0; j < sp->pcr[i].sid_nb; j++ ) { + /* 同一SIDが既に登録されているか確認 */ + if ( sp->pcr[i].sid[j] == sid ) { + pcr_found = 1; + break; + } + } + if ( pcr_found ) { + /* 同一SIDが既に登録されている */ +#ifdef PmtDebug + printf(" same sid found pcr[%d] sid[%d]\n", pcr, sid); +#endif + break; + } + /* 重複PCR発見 */ +#ifdef PmtDebug + printf(" same pcr found pcr[%d] sid[%d] sid_nb[%d], i[%d]\n", pcr, sid, sp->pcr[i].sid_nb, i); +#endif + sp->pcr[i].sid[sp->pcr[i].sid_nb] = sid; + sp->pcr[i].sid_nb += 1; + pcr_found = 1; + break; + } + } + + if ( ! pcr_found ) { + /* PCR管理領域更新 */ +#ifdef PmtDebug + printf(" new pcr found pcr[%d] sid[%d], pcr_nb[%d]\n", pcr, sid, sp->pcr_nb); +#endif + sp->pcr[sp->pcr_nb].pid = pcr; + sp->pcr[sp->pcr_nb].sid[0] = sid; + sp->pcr[sp->pcr_nb].sid_nb = 1; + sp->pcr_nb += 1; + } +#ifdef PmtDebug + printf(" PCR PacketID[%d][0x%04x]\n", pcr, pcr); +#endif + +// N = ((buf[11] & 0x0F) << 4) + buf[12] + 16 + 1; + N = ((buf[11] & 0x0F) << 8) + buf[12] + 12 + 1; +// printf("NAll[%d] N[%d]\n", Nall, N); + + // ECM + //int p = 17; + int p = 13; + while(p < N) { + if ( p > size -4) { + break; + } + uint32_t cat_pid; + uint32_t tag; + uint32_t len; + + tag = buf[p]; + len = buf[p+1]; + p += 2; + + if(tag == 0x09 && len >= 4 && p+len <= N) { +// ca_pid = ((buf[p+2] << 8) | buf[p+3]) & 0x1fff; + cat_pid = (AV_RB16(buf+p+2)) & 0x1fff; + sp->pids[cat_pid] = 1; + sp->cat_pids[cat_pid] = 1; + sp->pid_sid_table[cat_pid] = sid; /* CATも複数ServiceIDで重複がある */ +#ifdef PmtDebug + printf(" CAT PacketID[%d][0x%04x]\n", cat_pid, cat_pid); +#endif + } + p += len; + } + + /* + * ISO/IEC 13818-1:2000(E) Table 2-29 - Stream type assignments + * Value Desctiption + * 0x00 ITU-T | ISO/IEC Reserved + * 0x01 ISO/IEC 11172 Video + * 0x02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream + * 0x03 ISO/IEC 11172 Audio + * 0x04 ISO/IEC 13818-3 Audio + * 0x05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections + * 0x06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data + * 0x07 ISO/IEC 13522 MHEG + * 0x08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC + * 0x09 ITU-T Rec. H.222.1 + * 0x0A ISO/IEC 13818-6 type A + * 0x0B ISO/IEC 13818-6 type B + * 0x0C ISO/IEC 13818-6 type C + * 0x0D ISO/IEC 13818-6 type D + * 0x0E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary + * 0x0F ISO/IEC 13818-7 Audio with ADTS transport syntax + * 0x10 ISO/IEC 14496-2 Visual + * 0x11 ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1 + * 0x12 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets + * 0x13 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections. + * 0x14 ISO/IEC 13818-6 Synchronized Download Protocol + * 0x15-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved + * 0x80-0xFF User Private + * + */ + + // ES PID + while (N < Nall + 8 - 4) { + av_flag = 0; + // ストリーム種別が 0x0D(type D)は出力対象外 + if (0x0D != buf[N]) { + epid = GetPid(&buf[N + 1]); + sp->pids[epid] = 1; + sp->pid_sid_table[epid] = sid; + if ( buf[N] == 0x02 ) { /* 13818-2 Video */ + sp->program[sid].video[sp->program[sid].video_nb] = epid; + sp->program[sid].video_nb += 1; + av_flag = TSS_STREAM_TYPE_VIDEO; +#ifdef PmtDebug + printf(" VIDEO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif + } else if ( (buf[N] == 0x04) || (buf[N] == 0x0f) ) { + /* 13818-3 Audio or 13818-7 Audio with ADTS transport syntax */ + sp->program[sid].audio[sp->program[sid].audio_nb] = epid; + sp->program[sid].audio_nb += 1; + av_flag = TSS_STREAM_TYPE_AUDIO; +#ifdef PmtDebug + printf(" AUDIO PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif + } else { +#ifdef PmtDebug + printf(" OTHER PacketID[%d][0x%04x] StreamType[0x%02x]\n", epid, epid, buf[N]); +#endif + ; /* A/V どちらでもないものはとりあえずスルー */ + } + if ( av_flag && sp->esout ) { + /* ESバッファはNULLか? */ + if ( sp->esbuf[epid] == NULL ) { + sp->esbuf[epid] = malloc(sizeof(splitesbuf_t)); + if ( sp->esbuf[epid] == NULL ) { + fprintf(stderr, "malloc error\n"); + return TSS_NULL; + } + sp->esbuf[epid]->size = 0; + sp->esbuf[epid]->Program = &(sp->program[sid]); + sp->esbuf[epid]->fd = -1; + if ( creat_es_file(sp, sid, epid, av_flag) ) { + return TSS_ERROR; + } + } + /* PESバッファはNULLか? */ + if ( sp->pesbuf[epid] == NULL ) { + sp->pesbuf[epid] = malloc(sizeof(splitpesbuf_t)); + if ( sp->pesbuf[epid] == NULL ) { + fprintf(stderr, "malloc error\n"); + return TSS_NULL; + } + sp->pesbuf[epid]->size = 0; + sp->pesbuf[epid]->Program = &(sp->program[sid]); + } + } + } +// N += 4 + (((buf[N + 3]) & 0x0F) << 4) + buf[N + 4] + 1; + N += 4 + (((buf[N + 3]) & 0x0F) << 8) + buf[N + 4] + 1; + } +#ifdef PmtDebug + printf("AnalyzePmt finish.\n"); +#endif + return TSS_SUCCESS; +} + +/** + * CRC 計算 + */ +static int GetCrc32( + unsigned char* data, // [in] CRC 計算対象データ + int len) // [in] CRC 計算対象データ長 +{ + int crc; + int i, j; + int c; + int bit; + + crc = 0xFFFFFFFF; + for (i = 0; i < len; i++) + { + char x; + x = data[i]; + + for (j = 0; j < 8; j++) + { + + bit = (x >> (7 - j)) & 0x1; + + c = 0; + if (crc & 0x80000000) + { + c = 1; + } + + crc = crc << 1; + + if (c ^ bit) + { + crc ^= 0x04C11DB7; + } + + crc &= 0xFFFFFFFF; + } + } + + return crc; +} + +/** + * PID 取得 + */ +static int GetPid( + unsigned char* data) // [in] 取得対象データのポインタ +{ + return ((data[0] & 0x1F) << 8) + data[1]; +} + +/* return the 90kHz PCR and the extension for the 27MHz PCR. return + (-1) if not available */ +static int parse_pcr(int64_t *ppcr_high, int *ppcr_low, + const uint8_t *packet) +{ + int afc, len, flags; + const uint8_t *p; + unsigned int v; + + afc = (packet[3] >> 4) & 3; + if (afc <= 1) + return -1; + p = packet + 4; + len = p[0]; + p++; + if (len == 0) + return -1; + flags = *p++; + len--; + if (!(flags & 0x10)) + return -1; + if (len < 6) + return -1; + v = AV_RB32(p); + *ppcr_high = ((int64_t)v << 1) | (p[4] >> 7); + *ppcr_low = ((p[4] & 1) << 8) | p[5]; + return 0; +} + +/* pesbufが空か判定 (ret. 0:not empty / 1: empty) */ +static int pesbuf_empty(splitpesbuf_t *pesbuf){ + return pesbuf->size == 0; +} + +/* pesbufをクリア */ +void pesbuf_clear(splitpesbuf_t *pesbuf){ + pesbuf->size = 0; +} + +/* pesbufにデータを追加 (ret. 0:success / -1:error) */ +static int pesbuf_add(splitpesbuf_t *pesbuf, const uint8_t *data, int len){ + if(pesbuf->size + len > sizeof pesbuf->buffer){ + return -1; + } + memcpy(pesbuf->buffer +pesbuf->size, data, len); + pesbuf->size += len; + return 0; +} + +/* pesbufから、PESの先頭(packet_start_code_prefix)を探す */ +/* (ret. >=0:offset / -1: error) */ +static int pesbuf_packet_start_code_prefix(splitpesbuf_t *pesbuf){ + uint8_t packet_start_code_prefix[3] = {0x00, 0x00, 0x01}; + int i = 0; + + /* 小さすぎる */ + if(pesbuf->size < sizeof packet_start_code_prefix){ + return -1; + } + /* 先頭で探す */ + if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ + return 0; + } + +#if 0 + /* 先頭以外からも探す場合は、ここのコードを有効化する。 */ + /* ただし、MPEG-Videoのstart_codeと同じなので、深追いしない方がいいと思う... */ + for(i = 0; i < pesbuf->size - sizeof packet_start_code_prefix; i++){ + if(!memcmp(pesbuf->buffer + i, packet_start_code_prefix, sizeof packet_start_code_prefix)){ + return i; + } + } +#endif + + return -1; +} + +/** + * TSの解析とDemuxを行う + */ +static int DemuxTs(const uint8_t *packet, splitter *sp, const int pid) +{ + /* + * PES先頭までの長さは + * 4byte : continity counter + * 27,28bit が adaptation fileld制御 + * (01:ペイロードのみ, 10:adaptation fileldのみ、11:adaptation fileld+payload、00:reserved) + * ペイロード長 = 188 - TS header(4byte) -adaptation field長 -1 + */ + /* ありがとう */ + + int payload_offset; /* ペイロードオフセット(=パケット先頭からのバイト数) */ + int payload_length; /* ペイロード長 */ + int pes_started; + int adaptation_field_control; + int payload_unit_start_indicator; +// int random_access_indicator = 0; + int sid = sp->pid_sid_table[pid]; /* SIDをPIDから引いているが、PCRとCATは重複しているので注意*/ + + payload_offset = LENGTH_TS_HEADER; + + if ( sp->pesbuf[pid] == NULL ) { + pes_started = 0; /* malloc走る前(セクション解析だったら呼んで良い) */ + } else { + pes_started = !pesbuf_empty(sp->pesbuf[pid]); /* PES蓄積開始済み */ + } + + /* adaptation_field_controlおよびadaptation_fieldを処理する */ + adaptation_field_control = (packet[3] & 0x30) >> 4; + if ( adaptation_field_control == 0x02 || adaptation_field_control == 0x00) { + /* ペイロードなしの場合 */ + return 0; /* 別にエラーではない */ + } else if ( adaptation_field_control == 0x03 ) { + /* アダプテーションフィールド+ペイロードの場合 */ + if ( packet[LENGTH_TS_HEADER] != 0 ) { +// random_access_indicator = (packet[5] & 0x40) >> 6; + } + /* ペイロード開始位置 = TSヘッダ長 + アダプテーションフィールド長 + 1 */ + payload_offset += packet[LENGTH_TS_HEADER] + 1; + } else { + /* ペイロードのみ */ + ; /* 特に処理なし */ + } + + /* ペイロード長を出す */ + payload_length = LENGTH_PACKET - payload_offset; + if( payload_length <= 0 ){ /* payload長が0以下の場合 */ + return -1; /* エラーにすべきかは微妙なところ */ + } + + /* payload_unit_start_indicatorを処理(1) */ + payload_unit_start_indicator = (packet[1] & 0x40) >> 6; + /* (sectionの場合は、ここでpointer_fieldの処理などを行い、payload_offsetに反映する) */ + if ( sp->pmt_pids[pid] == 2 ) { /* PID が録画対象の PMT であるか? */ + if ( get_pmt_version(packet+payload_offset) != sp->program[sid].pmt_version ) { + /* pmt versionに差分あり */ + fprintf(stderr, "pmt version diff found pmt_pid[%d]" + " old_version[0x%02x]" + " new_version[0x%02x].\n", + pid, + sp->program[sid].pmt_version, + get_pmt_version(packet+payload_offset)); + AnalyzePmt(sp, packet +payload_offset, sid, payload_length); + /* payload 何byte処理したか等管理するべき */ + } + return 0; /* PMT の場合は処理終わり */ + } + if ( !sp->esout ) { + /* ES出力しない場合はPES蓄積不要 */ + return 0; + } + if ( sp->cat_pids[pid] == 1 ) { + return 0; /* CATは蓄積しない */ + } + if ( sp->pesbuf[pid] == NULL ) { + /* PES蓄積不要である場合も蓄積しない */ + return 0; + } + + /* payload_unit_start_indicatorを処理(2) */ + /* 必要に応じ、蓄積済みPESの処理と、PES蓄積開始を行う */ + if( payload_unit_start_indicator ){ + /* PES開始 */ + if ( pes_started ) { + /* バッファにデータがあればPES終端なので処理してクリア */ +// pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid, random_access_indicator); + pes2es(sp->pesbuf[pid], sp->esbuf[pid], pid); + pesbuf_clear(sp->pesbuf[pid]); + } + else { + pes_started = 1; + } + } + + /* PES蓄積処理 */ + if ( pes_started ){ + /* PES蓄積開始済み(これからPES蓄積開始を含む)なら、payloadをPESとして追加 */ + pesbuf_add(sp->pesbuf[pid], packet + payload_offset, payload_length); + } + /* おつかれさまでした */ + return 0; +} + +#if 0 +未使用なため削除 +/* PMT_PID から Program(Service ID)を確定させる */ +static int search_pmt_program(splitter *sp, int pid) +{ + /* この関数は大変遅いのでなるべく使用しない */ + int i; + for ( i = 0; i < MAX_SERVICE_ID; i++ ) { + if ( sp->program[i].pmt_packet_id == pid ) { + return i; + } + } + return -1; +} +#endif + +/* esbufが空か判定 (ret. 0:not empty / 1: empty) */ +static int esbuf_empty(splitesbuf_t *esbuf){ + return esbuf->size == 0; +} + +/* esbufをクリア */ +void esbuf_clear(splitesbuf_t *esbuf, uint64_t pts, uint64_t dts){ + esbuf->size = 0; + esbuf->pts = pts; + esbuf->dts = dts; +} + +/* esbufにデータを追加 (ret. 0:success / -1:error) */ +static int esbuf_add(splitesbuf_t *esbuf, const uint8_t *data, int len){ + if(esbuf->size + len > sizeof esbuf->buffer){ + return -1; + } + memcpy(esbuf->buffer +esbuf->size, data, len); + esbuf->size += len; + return 0; +} + +/* + * PESを解析してESを出力する + */ +//static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid, int random_access_indicator) +static int pes2es(splitpesbuf_t *pesbuf, splitesbuf_t *esbuf, const int pid) +{ + int len_pesh = 0; + int code = 0; + int flags = 0; + int len_pes = 0; + int len_pesh_supposed = 0; + int pes_extension_flags = 0; + int pes_extension_flags2 = 0; + int program_packet_sequence_counter_flag = 0; + int es_rate = 0; + const uint8_t *p = pesbuf->buffer; + const uint8_t *p_end = pesbuf->buffer +pesbuf->size; + int original_stuffing_length = 0; + int data_alignment_indicator = false; + int es_started; + int payload_offset = 0; + int payload_length = 0; + int audio_lipsync_offset = 0; + int i = 0; + int64_t audio_pts = 0; + int adts_freq = 0; + int64_t adts_frame_time = 0; + int gop_start = -1; + + /* ありがとう */ + /* ありがとうとコメントを書くと、 + * 動作がよくなる + * バグが減る + * バイナリサイズが小さくなる + * 画質がよくなる + * 音質がよくなる + */ + if ( esbuf == NULL ) { + return -1; /* malloc走る前この関数は呼んじゃダメです */ + } else { + es_started = !esbuf_empty(esbuf); /* ES蓄積開始済み */ + } + + payload_offset = pesbuf_packet_start_code_prefix(pesbuf); + if ( payload_offset == -1 ) { + return -1; + } + p += payload_offset; + /* http://dvd.sourceforge.net/dvdinfo/pes-hdr.html + * + * Stream ID : type : extension present? + * (1011 1101) 0xBD : Private stream 1 (non MPEG audio, subpictures) : YES + * (1011 1110) 0xBE : Padding stream : NO + * (1011 1111) 0xBF : Private stream 2 (navigation data) : NO + * (110x xxxx) 0xC0 - 0xDF : MPEG-1 or MPEG-2 audio stream number x xxxx : YES + * (1110 xxxx) 0xE0 - 0xEF : MPEG-1 or MPEG-2 video stream number xxxx : YES + * note: DVD allows only 8 audio streams/DVD allows only 1 video stream + */ + /* http://www2.arib.or.jp/johomem/pdf/2009/2009_0088.pdf + * + * 0xBC : プログラムストリームマップ + * 0xBD : プライベートストリーム1 + * 0xBE : パディングストリーム + * 0xBF : プライベートストリーム2 + * 0xC0 - 0xDF : ISO/IEC 13318 3、ISO/IEC 11172 3、ISO/IEC 13318 7 or ISO/IEC 14496 3 audio xxxx + * 0xE0 - 0xEF : ITU-T H.262、ISO/IEC 11172 2、ISO/IEC 14496 2 or ITU-T H264映像ストリーム + * 0xF0 : ECMストリーム + * 0xF1 : EMMストリーム + * 0xF2 : ITU-T勧告H.222.0 Annex A 又は ISO/IEC 13318 6 のDSMCCストリーム + * 0xF3 : ISO/IEC 13522ストリーム + * 0xF4 : ITU-T勧告 H.222.1 type A + * 0xF5 : ITU-T勧告 H.222.1 type B + * 0xF6 : ITU-T勧告 H.222.1 type C + * 0xF7 : ITU-T勧告 H.222.1 type D + * 0xF8 : ITU-T勧告 H.222.1 type E + * 0xF9 : 補助ストリーム + * 0xFA : ISO/IEC 14496 1SLパケット化ストリーム + * 0xFB : ISO/IEC 14496 1フレックスマックスストリーム + * 0xFC : メタデータストリーム + * 0xFD : 拡張ストリームID + * 0xFE : 未定義 + * 0xFF : プログラムストリームディレクトリ + */ + /* 上記より、ここでは + * MPEG-1 or MPEG-2 audio stream と + * MPEG-1 or MPEG-2 video stream と + * Private stream 1 と + * 0xFD(拡張ストリームID)を抽出する + * ?0xBF Private stream 2 落としてるけどよいの? + * >多分よくない。ffmpegではここに入る前に PRIVATE_STREAM2 のコードが入っている + */ + code = (p[3] &0xff) | 0x100; + if ( !((code >= 0x1c0 && code <= 0x1df) || + (code >= 0x1e0 && code <= 0x1ef) || + (code == 0x1bd) || (code == 0x1fd))) { + return -1; + } + /* PES のデータ長 */ + /* 動画のストリームである場合には、ES長は不定となるので0が許容される */ + len_pes = AV_RB16(p+4); + /* PESヘッダ拡張部(byte 6) */ + flags = p[6] & 0xff; + if ( flags & 0x04 ) { + data_alignment_indicator = true; + /* data alignment indicator */ + /* video start code or audio syncword. */ + /* おそらくここで区切るとピクチャ単位 */ + //printf("data alignment indicator found pid[%d].\n", pid); + } + flags = p[7] & 0xff; + /* PESヘッダデータ長(byte 8) */ + len_pesh = p[8] & 0xff; + p += LENGTH_PES_HEADER; + payload_offset += LENGTH_PES_HEADER +len_pesh; + if ( p +payload_offset >= p_end ) { + /* PESヘッダ長すぎます */ + return -1; + } + + /* flags + * +---------------------------------------------------+ + * name |byte 7(flags) | + * +-----+-----+-----+------+----------+----+----------+ + * Bit |76 |5 |4 |3 |2 |1 |0 | + * +-----+-----+-----+------+----------+----+----------+ + * field|PTS |ESCR |ES |DSM |additional|PES |PES | + * name |DTS |FLAG |RATE |Trick |copy info |CRC |Extension | + * |flag | |flag |mode |flag |flag|flag | + * +-----+-----+-----+------+----------+----+----------+ + * Data |5,5 |6 |3 |1 |1 |2 |1 |(24) + * byte | | | | | | | | + * +-----+-----+-----+------+----------+----+----------+ + */ + if ( flags & PTS_FLAG ) { + if ( p +LENGTH_PTS >= p_end ) { + return -1; + } + pesbuf->pts = get_pts(p); + p += LENGTH_PTS; + len_pesh_supposed += LENGTH_PTS; + } + if ( flags & DTS_FLAG ) { + if ( p +LENGTH_PTS >= p_end ) { + return -1; + } + pesbuf->dts = get_pts(p); + p += LENGTH_PTS; + len_pesh_supposed += LENGTH_PTS; + } + if ( flags & ESCR_FLAG ) { + p += 6; + len_pesh_supposed += 6; + } + if ( flags & ES_RATE_FLAG ) { + es_rate = AV_RB24(p); + es_rate = (es_rate >>1) & 0x3fffff; + es_rate = es_rate * 50; + printf("pid[%d] es_rate[%d]Byte/Sec.\n", pid, es_rate); + p += 3; + len_pesh_supposed += 3; + } + if ( flags & DSM_TRICK_MODE_FLAG ) { + p += 1; + len_pesh_supposed += 1; + } + if ( flags & COPY_INFO_FLAG ) { + p += 1; + len_pesh_supposed += 1; + } + if ( flags & CRC_FLAG ) { + p += 2; + len_pesh_supposed += 2; + } + if ( flags & EXTENSION_FLAG ) { + /* PES Extension flag + * +------------------------------------------------------------------+ + * name |PES Extension flag | + * +-----------+-----------+----------------+------+---+--------------+ + * bit |7 |6 |5 |4 |321|0 | + * +-----------+-----------+----------------+------+---+--------------+ + * field|PES private|pack header|program |P-STD |111|PES extension | + * name |data flag |field flag |packet |buffer| |flag2 | + * | | |sequence counter|flag | | | + * +-----------+-----------+----------------+------+---+--------------+ + * Data |16 |1 |2 |2 | |1 |(23) + * byte | | | | | | | + * +-----------+-----------+----------------+------+---+--------------+ + */ + if ( p >= p_end ) { + return -1; + } + pes_extension_flags = *p & 0xff; + p += 1; + len_pesh_supposed += 1; + if ( pes_extension_flags & PES_PRIVATE_DATA_FLAG ) { + p += 16; + len_pesh_supposed += 16; + } + if ( pes_extension_flags & PACK_HEADER_FIELD_FLAG ) { + p += 1; + len_pesh_supposed += 1; + } + if ( pes_extension_flags & PROGRAM_PACKET_SEQUENCE_COUNTER ) { + if ( p >= p_end ) { + return -1; + } + program_packet_sequence_counter_flag = *p & 0xff; + original_stuffing_length = program_packet_sequence_counter_flag & 0x3f; + p += 2; + len_pesh_supposed += 2; + } + if ( pes_extension_flags & PSTD_BUFFER_FLAG ) { + p += 2; + len_pesh_supposed += 2; + } + if ( pes_extension_flags & PES_EXTENSION_FLAG2 ) { + /* PES Extension flag2 + * +------------------------------------------------------------------+ + * name |PES Extension flag2 | + * +------+-----------------------------------------------------------+ + * bit |7 |6543210 | + * +------+-----------------------------------------------------------+ + * field|marker|PES_extension_field_length | + * name |bit | | + * |'1' | | + * +------+-----------------------------------------------------------+ + * Data |- |0 <= N <= 127 |(127) + * byte | | | + * +------+-----------------------------------------------------------+ + */ + if ( p >= p_end ) { + return -1; + } + pes_extension_flags2 = *p & 0x7f; + p += 1; + len_pesh_supposed += 1; + + p += pes_extension_flags2; + len_pesh_supposed += pes_extension_flags2; + } + } + if ( pid != 6417 && pid != 6418 ) { +// printf("es start? pid[%d]\n", pid); + } + /* ES蓄積管理処理 */ + payload_length = pesbuf->size -payload_offset; +// if ( data_alignment_indicator ) { /* data_alignment_indicator 区切りでESを出力する */ + if ( es_started ) { /* ES にデータが蓄積されている */ + /* + * ビデオをファイル出力し始める条件(1. 2. を満たすこと) + * 1. ESにGOP先頭を含む + * 2. PTSがCUEの時刻を過ぎていること( CUE <= PTS ) + */ + if ( (is_video_stream(pid, esbuf) == 0) && !(esbuf->started) ) { + /* VIDEO0 を同期の基準とする */ + gop_start = search_gop_start_code(esbuf); + if ( (gop_start != -1) && /* ESバッファにGOP_START_CODEが存在するか? */ + (esbuf->Program->cue <= esbuf->pts*300) ) { /* CUEを過ぎている? */ + /* 該当ストリームをファイル出力開始する */ + esbuf->started = 1; + esbuf->Program->video_start = 1; + esbuf->Program->video_pts = esbuf->pts; + printf("video stream. pid[%d] v_pts[%llu].\n", pid, esbuf->pts); + } else { + /* GOP先頭を含まないものはクリア */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } + } else if ( (is_video_stream(pid, esbuf) != -1) && !(esbuf->started) ) { + /* VIDEO0 以外のものはVIDEO0 が開始するまでクリア */ + if ( !(esbuf->Program->video_start) ) { + /* + * VIDEO0 が始まってない場合、 + * VIDEON は常にクリア + */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } else { + /* + * VIDEO0 が始まっている場合、 + * VIDEON の録画を開始 + */ + esbuf->started = 1; + } + } + /* + * オーディオをファイル出力し始める条件(1. 2. を満たすこと) + * 1. 動画の蓄積は開始されている + * 2. 動画のGOPの1番目のIピクチャのPTSとオーディオのPTSを比較して以下のどちらかを満たすこと + * 2.1. 差分が11msec以内(1000*90k/AACのサンプリング周波数) + * 1000 : ADTSデータの1フレームのサンプル数 + * 90k : PTSの周波数 + * 2.2. 過ぎている(過ぎている場合はオーディオESの蓄積の継続と次のGOP狙いにする?) + * #動画よりオーディオ側の方が先にESバッファの蓄積を始めるハズなので気にすること無いかなぁ… + */ + else if ( (is_audio_stream(pid, esbuf) != -1) && !(esbuf->started) ) { + if ( !(esbuf->Program->video_start) ) { + /* + * VIDEO が始まってない場合、 + * ESバッファの余裕がある限り、オーディオをESバッファに蓄積し続ける + */ + if ( esbuf->size + payload_length > sizeof esbuf->buffer ){ + /* 溢れそうになったらクリア */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } + } else if ( esbuf->Program->video_start ) { /* video 蓄積が開始されている?*/ + printf("audio stream. pid[%d] a_pts[%llu] v_pts[%llu] size[%d].\n", pid, esbuf->pts, esbuf->Program->video_pts, esbuf->size); + audio_lipsync_offset = 0; + audio_pts = esbuf->pts; + adts_freq = AnalyzeAdifHeader(esbuf); + adts_frame_time = (int64_t)((float)1000*90000/adts_freq); /* PTSは90KHz */ + /* オーディオをフレーム単位で捨ててPTSを進める */ + while ( (esbuf->Program->video_pts > audio_pts +adts_frame_time/2) ) { + /* オーディオデータを捨てると audio_pts は1フレーム分大きくなる */ + i = next_adts_start_code(esbuf, audio_lipsync_offset); /* 次のAACのデータを取得 */ + if ( i != -1 ) { /* AACデータの終端か? */ + audio_lipsync_offset += i; + } else { + /* バッファ終端まで進めたが、オーディオPTSの方が古い場合ESバッファはクリアする */ + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + break; + } + printf("audio stream drop. pid[%d] pts[%llu].\n", pid, audio_pts); + audio_pts += adts_frame_time; /* AACの1フレーム分、時間を進める */ + } + if ( (esbuf->Program->video_pts <= audio_pts +adts_frame_time/2) ) { + printf("lipsync start. v_pts[%llu] a_pts[%llu].\n", esbuf->Program->video_pts, audio_pts); + memmove(esbuf->buffer +audio_lipsync_offset, + esbuf->buffer, + esbuf->size -audio_lipsync_offset); + esbuf->size -= audio_lipsync_offset; + esbuf->started = 1; /* オーディオのファイル出力を有効化 */ + } + } else { + ; /* 該当するものは無いはず */ + } + } else { + /* 得に処理なし */ + ; + } + /* バッファをファイルに出力してクリア */ + if ( esbuf->started ) { /* 該当ストリームはファイル出力の有効化をされている? */ + esbuf_write(esbuf); + esbuf_clear(esbuf, pesbuf->pts, pesbuf->dts); + } + } else { + /* ES蓄積を新たに開始 */ + es_started = 1; + esbuf->pts = pesbuf->pts; + esbuf->dts = pesbuf->dts; + } + //} + + /* ES蓄積処理 */ + if ( es_started ) { + /* ES蓄積開始済み(これからES蓄積開始を含む)なら、payloadをESとして追加 */ + esbuf_add(esbuf, pesbuf->buffer +payload_offset, payload_length); + } + /* お疲れさまでした */ + return 0; +} + +/* Program の N 番目の AUDIO STREAM であるかを返却する */ +static int is_audio_stream(const int pid, splitesbuf_t *esbuf) +{ + int i = 0; + program_t* program = esbuf->Program; + while (i < program->audio_nb) + { + if (program->audio[i] == pid) { + return i; + } + i++; + } + return -1; +} + +/* Program の N 番目の VIDEO STREAM であるかを返却する */ +static int is_video_stream(const int pid, splitesbuf_t *esbuf) +{ + int i = 0; + program_t* program = esbuf->Program; + while (i < program->video_nb) + { + if (program->video[i] == pid) { + return i; + } + i++; + } + return -1; +} + +/* + * ESをファイル出力する + * エラーハンドリングしてないね… + */ +static int esbuf_write(splitesbuf_t *esbuf) +{ + int remain = esbuf->size; + while(remain > 0) + { + remain -= write(esbuf->fd, esbuf->buffer+(esbuf->size-remain), remain); + } + return 0; +} + +#if 0 +未使用なため駆除 +/* + * packet dump + */ +void dump_packet( const uint8_t *packet ) +{ + int i = 0; + uint8_t *p = (uint8_t*)packet; + char tmp[17]; + + printf("HEADER 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F \n"); + while(i < LENGTH_PACKET) { + if ( (i%16) == 0 ) { + printf("0x%04X ", i); + } + printf("%02x ", *(p+i)); + if ( isprint(*(p+i)) ){ + tmp[i%16] = *(p+i); + } + else { + tmp[i%16] = '.'; + } + if ((i%16) == 15) { + tmp[sizeof(tmp)-1] = '\0'; + printf(" %s\n", tmp); + } + i++; + } + putchar('\n'); +} +#endif + +/* + * TOT の JST_time を解析する + */ +static int parse_tot( const unsigned char* packet, time_t *t ) +{ + /* 注意事項 + * 本当は TOT が有効かどうかをチェックするべきですがしていません + * サマータイム関係は無視しています + */ + struct tm tm; + time_t t2; + int k; + uint8_t *p = (uint8_t*)packet; + unsigned int MJD; + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + + p += 8; + MJD = (*(p) & 0xff) <<8; + p++; + MJD |= *(p) & 0xff; + printf("MJD[%x].\n", MJD); + + /* ARIB STD-B10 第2部 付録C の公式より MJD to YYYYMMDD */ + tm.tm_year = (int)floor((MJD - 15078.2)/365.25); + tm.tm_mon = (int)floor((MJD - 14956.1 - floor(tm.tm_year * 365.25))/30.6001); + tm.tm_mday = MJD - 14956 - floor(tm.tm_year * 365.25) - floor(tm.tm_mon * 30.6001); + if ( tm.tm_mon == 14 || tm.tm_mon == 15 ) + k = 1; + else + k = 0; + tm.tm_year += k; + tm.tm_mon = tm.tm_mon -1 - k * 12; + tm.tm_mon--; + + /* HHMISSは2進化10進数 */ + p++; + tm.tm_hour = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); + p++; + tm.tm_min = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); + p++; + tm.tm_sec = ((*p & 0xf0) >>4)*10 + (*p & 0x0f); + + *t = mktime(&tm); + time(&t2); +// printf("time[%d] TOT[%d].\n", t2, *t); + + return TRUE; +} + +static int64_t get_pts(const uint8_t *p) +{ + int64_t pts = (int64_t)((p[0] >> 1) & 0x07) << 30; + pts |= (AV_RB16(p + 1) >> 1) << 15; + pts |= AV_RB16(p + 3) >> 1; + return pts; +} + +static int get_pmt_version(const uint8_t *p) +{ + return ((p[6] >> 1) & 0x1f); +} + +#if 0 +未使用なため駆除 +void search_mpeg_system_header(const uint8_t *packet) +{ + int i; + uint8_t *p = (uint8_t*)packet; + i = 0; + for( i=0; i < LENGTH_PACKET-4; i++) { + if( p[i] == 0x00 && p[i+1] == 0x00 && p[i+2] == 0x01 && p[i+3] == 0xb8 ){ + dump_packet(packet ); + } + } +} +#endif + +/* + * この関数では、現在の仕様では、先頭位置の「次の」ADTS start codeまでの長さを返却する + * ret == 0 : 仕様上あり得ない(esbuf先頭はヘッダ先頭であるため) + * ret > 0 : 見つかった場合 + * ret == -1 : 見つからなかった場合 + */ +static int next_adts_start_code(splitesbuf_t *esbuf, int offset) +{ + /* + * start code prefix のうち、先頭12bit は 1 固定 + */ + uint16_t adts_start_code = 0xfff0; + int i = offset +1; + uint16_t startcode = 0; + + /* 小さすぎる */ + if(esbuf->size -offset < sizeof(adts_start_code)){ + return -1; + } + for(; i < esbuf->size - sizeof(adts_start_code); i++) { + startcode = AV_RB16(esbuf->buffer+i); + if( startcode == adts_start_code ) { /* 該当位置から12bit連続1が立っているか? */ +#if 0 + printf("adts start code found.i[%d]. 0[%02x] 1[%02x] 2[%02x] 3[%02x] 4[%02x] 5[%02x] 6[%02x]\n", + i, *(esbuf->buffer+i+0), *(esbuf->buffer+i+1), *(esbuf->buffer+i+2), *(esbuf->buffer+i+3), *(esbuf->buffer+i+4), *(esbuf->buffer+i+5), *(esbuf->buffer+i+6) ); +#endif + return (i-offset); + } + } + return -1; +} + +/* ADIF HEADER解析 */ +static int AnalyzeAdifHeader(splitesbuf_t *esbuf) +{ + int id = 0; /* 0:MPEG-4 1:MPEG-2 */ + int layer = 0; /* 常に 0x00 */ + int protection_absent = 0; /* 保護属性 0:保護なし 1:保護あり */ + int profile = 0; /* 00:MAIN 01:LC 10:SSR 11:(reserved) */ + int sampling_frequency_index = 0; /* サンプリング周波数テーブル値 */ + int private_bit = 0; /* private bit */ + int channel_configuration = 0; /* チャンネル数 */ + int original_copy = 0; + int home = 0; /* homeってなに? */ + int copyright_identification_bit = 0; /* 著作権証明ビット */ + int copyright_identification_start = 0; /* 著作権証明開始ビット */ + int aac_frame_length = 0; /* AACフレーム長 */ + int adts_buffer_fullness = 0; /* ADTSバッファ残量 */ + int no_raw_data_blocks_in_frame = 0; /* データブロックまでの残量 */ + /* + * サンプリング周波数テーブル(ヘッダのsampling_frequency_indexが添字) + * 単位:Hz + */ + int sampling_frequency_table[16] = + { + 96000, + 88200, + 64000, + 48000, + 44100, + 32000, + 24000, + 22050, + 16000, + 12000, + 11025, + 8000, + -1, + -1, + -1, + -1 + }; + + uint8_t *p = esbuf->buffer; + if ( esbuf->size < 8 ) { + return -1; + } + + id = get_adif_id(p+1); + layer = get_adif_layer(p+1); + protection_absent = get_adif_protection_absent(p+1); + profile = get_adif_profile(p+2); + sampling_frequency_index = get_adif_sampling_frequency_index(p+2); + private_bit = get_adif_private_bit(p+2); + channel_configuration = get_adif_channel_configuration(p+3); + original_copy = get_adif_original_copy(p+3); + home = get_adif_home(p+3); + copyright_identification_bit = get_adif_copyright_idication_bit(p+3); + copyright_identification_start = get_adif_copyright_idication_start(p+3); + aac_frame_length = get_adif_aac_frame_length(p+3); + adts_buffer_fullness = get_adts_buffer_fullness(p+5); + no_raw_data_blocks_in_frame = get_adts_no_raw_data_blocks_in_frame(p+5); + + /* + * とりあえず return は サンプリング周波数としておく + * 本当は取得した情報を構造体にして返却する方がいいのだろうけど、 + * 利用する予定もないので取得するだけにしておく + */ + return sampling_frequency_table[sampling_frequency_index]; +} + +static int get_adif_id(uint8_t *p) +{ + return ((*p & 0x08) >>3); +} + +static int get_adif_layer(uint8_t *p) +{ + return ((*p & 0x06) >>1); +} + +static int get_adif_protection_absent(uint8_t *p) +{ + return (*p & 0x01); +} + +static int get_adif_profile(uint8_t *p) +{ + return ((*p & 0xc0) >>6); +} + +static int get_adif_sampling_frequency_index(uint8_t *p) +{ + return ((*p & 0x3c) >>2); +} + +static int get_adif_private_bit(uint8_t *p) +{ + return ((*p & 0x02) >>1); +} + +static int get_adif_channel_configuration(uint8_t *p) +{ + return ((*p & 0x01) <<2 | (*(p+1) & 0xc0 >>6) ); +} + +static int get_adif_original_copy(uint8_t *p) +{ + return (*p & 0x20 >>5 ); +} + +static int get_adif_home(uint8_t *p) +{ + return (*p & 0x10 >>4 ); +} + +static int get_adif_copyright_idication_bit(uint8_t *p) +{ + return (*p & 0x08 >>3 ); +} + +static int get_adif_copyright_idication_start(uint8_t *p) +{ + return (*p & 0x04 >>2 ); +} + +static int get_adif_aac_frame_length(uint8_t *p) +{ + return ( ((*p & 0x02) <<11) || ((*(p+1) & 0xff) <<3) || ((*(p+2) & 0xe0) >>5) ); +} + +static int get_adts_buffer_fullness(uint8_t *p) +{ + return ( ((*p & 0x1f) <<6) || ((*(p+1) &0xfc) >>2)); +} + +static int get_adts_no_raw_data_blocks_in_frame(uint8_t *p) +{ + return (*p & 0x03); +} + +#define GOP_START_CODE (0x000001b8) +/* GOP START CODE を検索する */ +static int search_gop_start_code(splitesbuf_t *esbuf) +{ + uint32_t gop_start_code = GOP_START_CODE; + int i; + + /* 小さすぎる */ + if ( esbuf->size < sizeof gop_start_code ){ + return -1; + } + for(i = 0; i < esbuf->size - sizeof gop_start_code; i++) { + if ( (AV_RB32(esbuf->buffer +i)) == gop_start_code ) { + return i; + } + } + return -1; +} + +/* ES 出力するファイルを作成する */ +static int creat_es_file(splitter *sp, int sid, int pid, int av_flag) +{ + /* + * 出力ESファイルの命名規則は以下とする + * + * ファイル名のベースは --es オプションの引数 + * 以下の形式で命名して、ファイルオープンまで実施する。 + * ファイル名prefix_SID_AVのプログラム内番号.m2v + * ファイル名prefix_SID_AVのプログラム内番号.aac + * + * !!注意!! + * MPEG-2/MPEG-4 AAC 以外のオーディオが来た場合の処理が未実装 + */ + + char filename[PATH_MAX]; + int size = 0; + char *suffix = NULL; + int av_nb = 0; + char suffix_a[] = "aac"; + char suffix_v[] = "m2v"; + filename[0] = '\0'; + + /* ちょっとこの辺のコードイケてないので後から直すかも */ + if ( av_flag == TSS_STREAM_TYPE_VIDEO ) { + suffix = suffix_v; + av_nb = sp->program[sid].video_nb -1; + } else if ( av_flag == TSS_STREAM_TYPE_AUDIO ){ + suffix = suffix_a; + av_nb = sp->program[sid].audio_nb -1; + } else { + /* ここはありえない */ + return -1; + } + size = strlen(sp->filename); + + if ( size +16 < sizeof(filename) ) { + snprintf(filename, sizeof(filename), "%s_%05d_%02d.%s", sp->filename, sid, av_nb, suffix); + filename[PATH_MAX-1] = '\0'; + } else { + /* ファイル名つけられなくて困るでござるの巻 */ + return -1; + } + umask(0133); + if ( !(sp->esbuf[pid]->fd = open(filename, O_CREAT|O_APPEND|O_RDWR, 00644)) ) { + fprintf(stderr, "cannot open es out file. file[%s].\n", filename); + return -1; + } + return 0; +} + +static time_t cue2time(char *yyyymmddhhmiss) +{ + struct tm cue_tm; + time_t cue_time; + char *p; + int i, j; + char str_yyyy[5]; + char str_mm[3]; + char str_dd[3]; + char str_hh[3]; + char str_mi[3]; + char str_ss[3]; + str_yyyy[0] = '\0'; + str_mm[0] = '\0'; + str_dd[0] = '\0'; + str_hh[0] = '\0'; + str_mi[0] = '\0'; + str_ss[0] = '\0'; + + p = yyyymmddhhmiss; + i = strlen(p); + j = strspn(p, LIST_DECIMAL); + if ( i != j && i != 14 ) { + /* 数字以外混ぜるな */ + return -1; + } + strncpy(str_yyyy, yyyymmddhhmiss, 4); + strncpy(str_mm, yyyymmddhhmiss+4, 2); + strncpy(str_dd, yyyymmddhhmiss+6, 2); + strncpy(str_hh, yyyymmddhhmiss+8, 2); + strncpy(str_mi, yyyymmddhhmiss+10, 2); + strncpy(str_ss, yyyymmddhhmiss+12, 2); + str_yyyy[4] = '\0'; + str_mm[2] = '\0'; + str_dd[2] = '\0'; + str_hh[2] = '\0'; + str_mi[2] = '\0'; + str_ss[2] = '\0'; + + cue_tm.tm_sec = atoi(str_ss); + cue_tm.tm_min = atoi(str_mi); + cue_tm.tm_hour = atoi(str_hh); + cue_tm.tm_mday = atoi(str_dd); + cue_tm.tm_mon = atoi(str_mm)-1; + cue_tm.tm_year = atoi(str_yyyy)-1900; + cue_tm.tm_isdst = -1; + cue_time = mktime(&cue_tm); + return cue_time; +} + +/* PCR の PID を検索する */ +static int search_pcr_pid(splitter *sp, int pid) +{ + int i; + for ( i=0; i < MAX_SERVICES; i++ ) { + if ( sp->pcr[i].pid == pid ) { + return i; + } + } + return -1; +} diff -r 215a51fa3df3 -r 9c7bc6c0327e src/tssplitter_lite.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tssplitter_lite.h Wed Sep 29 23:18:55 2010 +0900 @@ -0,0 +1,216 @@ +/* -*- tab-width: 4; indent-tabs-mode: t -*- */ +/* vim: set ts=4 sts=4 sw=4 noexpandtab number : */ +/* tssplitter_lite.h -- split TS stream program's header. + + Copyright 2009 querulous + Copyright 2010 Naoya OYAMA + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ +#ifndef _TS_SPLITTER_LITE_H_ +#define _TS_SPLITTER_LITE_H_ + +#define __STDC_FORMAT_MACROS +#include +#include +#include + +#define LENGTH_PACKET (188) +#define MAX_PID (8192) +#define MAX_SERVICES (50) +#define LENGTH_CRC_DATA (176) +#define false (0) +#define true (1) + +#define TSS_SUCCESS (0) +#define TSS_ERROR (-1) +#define TSS_NULL (-2) +#define LENGTH_PAT_HEADER (12) +#define C_CHAR_COMMA ',' +#define C_CHAR_DOT '.' +#define LENGTH_TS_HEADER (4) +#define LENGTH_PES_HEADER (9) +#define LENGTH_PTS (5) + +/* 改訂版デジタル放送教科書(上) P101 表1 ARIBでのPSI/SIの種類より参照 */ +#define PAT (0x0000) +//#define PMT /* PATによる間接指定 */ +#define CAT (0x0001) +#define NIT (0x0010) +#define SDT (0x0011) +#define BAT (0x0011) +#define EIT (0x0012) /* 0x0026, 0x0027 */ +#define RST (0x0013) +#define TDT (0x0014) /* 地デジでは使用されない */ +#define TOT (0x0014) +#define LIT (0x0020) /* またはPMTによる間接指定 */ +#define ERT (0x0021) /* またはPMTによる間接指定 */ +//#define ITT /* PMTによる間接指定 */ +#define PCAT (0x0022) +#define BIT (0x0024) +#define NBIT (0x0025) +//#define ECM /* PMTによる間接指定 */ +//#define EMM /* CATによる間接指定 */ +#define LDT (0x0025) +#define DCT (0x0017) +//#define DTL /* DCTによる間接指定 */ +#define DIT (0x001e) +#define SIT (0x001f) +#define SDTT (0x0023) +#define CDT (0x0029) +//#define DSM-CC_Section /* PMTによる間接指定 */ + +/* セクションヘッダ長 */ +/* TS パケットに各セクションを設置する際、該当TSパケットの残サイズが + * いくつ以上あれば書き込めるかの判定に使用する。 + * デコードにはあまり重要では無いかも + */ +#define SECTION_LENGTH_PAT (8) +#define SECTION_LENGTH_PMT (8) +#define SECTION_LENGTH_CAT (8) +#define SECTION_LENGTH_NIT (8) +#define SECTION_LENGTH_BIT (8) +#define SECTION_LENGTH_SDT (11) +#define SECTION_LENGTH_EIT (14) /* H-EIT, M-EIT, L-EIT を示す */ +#define SECTION_LENGTH_SDTT (15) +#define SECTION_LENGTH_CDT (13) +#define SECTION_LENGTH_TOT (10) + +enum { + PTS_FLAG = 0x80, + DTS_FLAG = 0x40, + ESCR_FLAG = 0x20, + ES_RATE_FLAG = 0x10, + DSM_TRICK_MODE_FLAG = 0x08, + COPY_INFO_FLAG = 0x04, + CRC_FLAG = 0x02, + EXTENSION_FLAG = 0x01 +}; + +enum { + PES_PRIVATE_DATA_FLAG = 0x80, + PACK_HEADER_FIELD_FLAG = 0x40, + PROGRAM_PACKET_SEQUENCE_COUNTER = 0x20, + PSTD_BUFFER_FLAG = 0x10, + PES_EXTENSION_FLAG2 = 0x01 +}; + +/* + * PCRからSTCを生成する処理方式(案) + * 1. PCRを二つ取得するまでループ(1.4.までは初期処理で実施すること) + * 2. ループ開始時刻(PCR)と、ループ抜けた時刻(PCR)の差分を取る + * 3. ループ開始〜終了の間に処理したパケット数を数える + * 4. (2.のPCRの進んだ時間/3.のパケット数) の計算によって、1パケットによって進むPCRを想定する + * 5. TS受信時に、1つのパケットを処理する度に4.の想定する経過時刻をPCRに足し込んでSTCとする + * 6. PCRを新規に取得したら、STCを補正(そのまま代入)する + */ +#define MAX_VIDEO (16) +#define MAX_AUDIO (32) +typedef struct _program_t +{ + int64_t stc; + int64_t cue; /* 録画開始時刻 */ + int pmt_packet_id; /* 該当Program(Service ID)に対応するPMT */ + int pmt_version; /* 該当Program(Service ID)に対応するPMTのVersion */ + int pcr_packet_id; /* 該当Program(Service ID)のPCRを保持するPID */ + int64_t pcr1; /* PCR1 */ + int64_t pcr2; /* PCR2 */ + int pcr_packet_nb; /* 直前のPCRを受信したときのパケット数 */ + int packet_nb; /* 直前のProgramの処理を実施したときのパケット数 */ + int64_t pcr_incr; /* 該当Program(Service ID)に於いて、1つのTSパケットを処理した時に経過する(と想定する時間) */ + int video_start; /* VODEO0を蓄積開始している? */ + int64_t video_pts; /* 最後に処理したVODEO0のESのPTS */ + int video_nb; /* PMT に存在するビデオストリームの数 */ + int audio_nb; /* PMT に存在する音声ストリームの数 */ + int video[MAX_VIDEO]; /* PS出力する場合に使うかも */ + int audio[MAX_AUDIO]; /* PS出力する場合に使うかも */ +} program_t; +/* + * program_t をサービスID分準備して、使用するイメージでいたけど、 + * サービスIDの最大値は0xFFFFであるので、静的に確保すると stack が簡単に溢るので、 + * 実行時に malloc とするかなぁ + * 本当は必要なだけallocするのが好ましいのだけど… + */ + +typedef struct _splitpesbuf_t +{ + program_t *Program; + int64_t pts; + int64_t dts; + int size; + u_char buffer[128*1024]; +} splitpesbuf_t; + +typedef struct _splitesbuf_t +{ + program_t *Program; + int64_t pts; + int64_t dts; + int started; /* 該当ESが蓄積開始しているか */ + int random_access_indicator; /* TS の random_access_indicator */ + int fd; /* 該当ESのfd */ + int size; + u_char buffer[3*1024*1024]; +} splitesbuf_t; + +/* PCR 共有用構造体 */ +typedef struct _pcr_t +{ + int pid; + int sid_nb; /* PCRを参照しているServiceID(ProgramID)の数 */ + int sid[MAX_SERVICES]; +} pcr_t; + + +/** + * splitter構造体 + */ +typedef struct splitter { + char *filename; /* ファイル名を上位からもらってくるためのポインタ */ + char *arg_cue; /* 引数で取得してきた録画開始時刻(HHMISS) */ + int esout; /* ES出力する? */ + unsigned char pids[MAX_PID]; + unsigned char pmt_pids[MAX_PID]; + uint8_t cat_pids[MAX_PID]; + uint8_t pcr_pids[MAX_PID]; /* PCRは複数ServiceID(ProgramID)で共有される */ + pcr_t pcr[MAX_SERVICES]; + int pcr_nb; + unsigned char* pat; + char** sid_list; + unsigned char pat_count; + int pmt_retain; + int pmt_counter; + int avail_pmts[MAX_SERVICES]; + int num_pmts; + splitpesbuf_t *pesbuf[MAX_PID]; + splitesbuf_t *esbuf[MAX_PID]; + program_t *program; + int pid_sid_table[MAX_PID]; /* pid to sid の変換を行うためのテーブル */ + time_t time_cue; + time_t time_tot; + int tot_packet_nb; /* TOT受信時のパケット受信数 */ +} splitter; + +/* b25 decoder would hoard up large chank */ +typedef struct _splitbuf_t +{ + int size; + u_char buffer[1024*1024]; +} splitbuf_t; + +splitter* split_startup(char *sid, char *filename, char *cue_time); +int split_select(splitter *sp, ARIB_STD_B25_BUFFER *sbuf); +void split_shutdown(splitter *sp); +int split_ts(splitter *splitter, ARIB_STD_B25_BUFFER *sbuf, splitbuf_t *dbuf); + +#endif