Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

git-svn-id: http://stream-m.googlecode.com/svn/trunk@1 3b25ada9-4eb4-…

…a6d6-1e5e-85e4663ad80e
  • Loading branch information...
commit 27961cdb608419b74778c3206510d4ac41322e53 0 parents
vbence-gooacc@freemail.hu authored
Showing with 5,302 additions and 0 deletions.
  1. +674 −0 GPL.txt
  2. +115 −0 README.txt
  3. +51 −0 build.xml
  4. +22 −0 server.conf.sample
  5. +76 −0 src/ControlledStream.java
  6. +145 −0 src/EBMLElement.java
  7. +168 −0 src/EventAnalizer.java
  8. +160 −0 src/HeaderDetectionState.java
  9. +85 −0 src/Log.java
  10. +100 −0 src/MovieFragment.java
  11. +68 −0 src/ServerEvent.java
  12. +53 −0 src/ServerStatusEvent.java
  13. +62 −0 src/Stream.java
  14. +154 −0 src/StreamClient.java
  15. +101 −0 src/StreamInput.java
  16. +22 −0 src/StreamInputState.java
  17. +281 −0 src/StreamingServer.java
  18. +143 −0 src/StreamingState.java
  19. +41 −0 src/TransferEvent.java
  20. +122 −0 src/org/czentral/minihttp/ChunkedInputStream.java
  21. +173 −0 src/org/czentral/minihttp/GeneralHTTPRequest.java
  22. +36 −0 src/org/czentral/minihttp/HTTPException.java
  23. +114 −0 src/org/czentral/minihttp/HTTPRequest.java
  24. +390 −0 src/org/czentral/minihttp/HTTPRequestLoader.java
  25. +25 −0 src/org/czentral/minihttp/HTTPResource.java
  26. +96 −0 src/org/czentral/minihttp/HTTPResponse.java
  27. +71 −0 src/org/czentral/minihttp/HTTPZipResource.java
  28. +137 −0 src/org/czentral/minihttp/MiniHTTP.java
  29. +67 −0 src/org/czentral/minihttp/MultimapBuilder.java
  30. +96 −0 src/org/czentral/util/stream/StreamBuffer.java
  31. +59 −0 src/org/czentral/util/stream/StreamProcessor.java
  32. +82 −0 src/org/czentral/util/stream/StreamProcessorFeeder.java
  33. +52 −0 src/threadedevent/Event.java
  34. +88 −0 src/threadedevent/EventDispatcher.java
  35. +29 −0 src/threadedevent/EventListener.java
  36. +103 −0 src/threadedevent/EventQueue.java
  37. +57 −0 src/threadedevent/GeneralEvent.java
  38. +48 −0 src/threadedevent/GeneralEventProducer.java
  39. +39 −0 webclient/client.html
  40. BIN  webclient/static/back-left.png
  41. BIN  webclient/static/back.png
  42. BIN  webclient/static/button-over.png
  43. BIN  webclient/static/button.png
  44. BIN  webclient/static/content-bottom.png
  45. BIN  webclient/static/content-top.png
  46. BIN  webclient/static/content.png
  47. BIN  webclient/static/favicon.png
  48. +160 −0 webclient/static/general.css
  49. +737 −0 webclient/static/script.js
674 GPL.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 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 <http://www.gnu.org/licenses/>.
+
+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:
+
+ <program> Copyright (C) <year> <name of author>
+ 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
+<http://www.gnu.org/licenses/>.
+
+ 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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
115 README.txt
@@ -0,0 +1,115 @@
+README
+=======
+
+"stream.m" is a streaming media server. It currently works much like shoutcast
+but instead of MP3 it relays a live WebM stream.
+
+
+FRAGMENT SIZES
+===============
+
+The live stream consists of fragments (self-contained units of frames without
+referencing any frame outside the fragment). A fragment starts with a keyframe
+therefrore it is important that the encoder put in keyframes regularly as
+this determines the size of the framents.
+
+The ideal fragment size is around 200 kBytes (or 1600 kbits). The keyframe
+interval can be calculated with this forula:
+1600k / <bitrate> * <framerate>
+
+e.g. if you are publishing an 500 kbit stream with 16 fps, then:
+1600 / 500 * 16 = 51.2
+(or 1600000 / 500000 * 16 = 51.2)
+so every 52nd video frame should be a keyframe.
+
+The server splits fragments when it seems necessary. A soft minimum for frame
+size is currently 100k (no new fragment is started if a new keyframe arrives
+within 100 kBytes from a previous keyframe). The hard maximum is 1024 kBytes
+wich currently seems enough.
+
+
+TRANSFER
+=========
+
+All operations are done over HTTP. The stream is POSTed with the password as a
+GET attribute in the URL (so firewalls and throttling ISP's should not be a
+problem).
+
+The port number can be set in the configuration file. Default port is: 8080
+
+
+HTTP ACCES
+===========
+
+Streams are identified by a name. The program refers to this as StreamID or
+streamname. Note that the < and > characters are used just to signal that
+substitution is expected, they must not be included in the real URL.
+
+
+The name and password of each stream is defined in the config file. A stream
+must be POSTed to this URL to start broadcasting:
+
+/publish/<streamname>?password=<streampass>
+
+
+A stream can be accessed (watched) on the following URL. You may want to insert
+this URL into a HTML5 <video> tag:
+
+/consume/<streamname>
+
+
+Realtime information can be acquired thru an AJAX based console for a certain
+stream on the following URL (giving the name and password for the chosen
+stream on the UI):
+
+/console/client.html
+
+
+RUNNING THE SERVER
+===================
+
+java StreamingServer <configfile>
+
+The release version has the classes in a .jar archive. Befire running the
+server you should edit the sample config file (change password and choose a
+stream name). So you will end up with something like:
+
+java -cp lib/stream.m.jar StreamingServer server.conf
+
+
+PUBLISHING ON WINDOWS
+======================
+
+In theory several open source tools can be used for this. Practically you will
+need two of them if you want to stream from your webcam in windows.
+
+VLC can access your recording hardware and encode WebM, but it only saves it to
+a file. (None of VLC-s other output modules currently support it). Neither can
+VLC send a stream out with POST method.
+
+FFmpeg is great on muxing and output, but the windows version can not access
+DirectShow, which your soundcard uses to give access to the microphone data.
+
+So we are going to usee them both together: VLC will access the audio, compress
+it to a temporary format (mp3). FFmpeg will connect to VLC to get the audio
+and VFW for the video, re-encodes audio to ogg-vorbis and video to VP8, mux
+them into a WebM container and POSTs it to the given URL.
+
+
+VLC will listen to the local port: 8081 for FFmpeg to connect, so paranoid
+firewalls should be aware. Assumptions:
+server name: example.com
+stream name: first
+stream password: secret
+
+vlc -I dummy dshow:// --sout "#transcode{vcodec=none,acodec=mp3,ab=128,channels=2,samplerate=44100}:http{mux=ts,dst=127.0.0.1:8081/}" --dshow-vdev=none --no-sout-rtp-sap --no-sout-standard-sap --sout-keep
+ffmpeg -f vfwcap -r 16 -i 0 -i http://localhost:8081/ -g 52 -acodec libvorbis -ab 64k -vcodec libvpx -vb 448k -f matroska http://example.com:8080/publish/first?password=secret
+
+
+PUBLISHING ON LINUX
+====================
+
+...is not tested yet. But you will probably be allright with something like:
+
+ffmpeg -f video4linux2 -s 320x240 -r 16 -i /dev/video0 -f oss -i /dev/dsp -g 52 -acodec libvorbis -ab 64k -vcodec libvpx -vb 448k -f matroska http://example.com:8080/publish/first?password=secret
+
51 build.xml
@@ -0,0 +1,51 @@
+<project name="stream-m" default="dist" basedir=".">
+ <description>
+ stream.m is a video broadcasting tool compatible with Google's WebM format.
+ </description>
+
+ <!-- set global properties for this build -->
+ <property name="src" location="src"/>
+ <property name="classes" location="classes"/>
+ <property name="dist" location="dist"/>
+ <property name="webclient" location="webclient"/>
+
+ <target name="init">
+ <!-- Create the time stamp -->
+ <tstamp/>
+ <!-- Create the build directory structure used by compile -->
+ <mkdir dir="${dist}"/>
+ <mkdir dir="${classes}"/>
+ </target>
+
+ <target name="compile" depends="init" description="compile the source">
+ <!-- Compile the java code from ${src} into ${build} -->
+ <!-- includeantruntime="false" is a workaround for ant 1.8 -->
+ <javac srcdir="${src}" destdir="${classes}" includeantruntime="false"/>
+ </target>
+
+ <target name="dist" depends="compile" description="generate the distribution">
+ <!-- Create the distribution directory -->
+ <mkdir dir="${dist}/lib"/>
+
+ <!-- Create JAR file from all the classes -->
+ <jar jarfile="${dist}/lib/stream.m.jar" basedir="${classes}"/>
+
+ <!-- Create ZIP file for the web client -->
+ <zip destfile="${dist}/console.zip" basedir="${webclient}"/>
+
+ <!-- Copy sample configuration file -->
+ <copy file="server.conf.sample" todir="${dist}"/>
+
+ <!-- Copy GPL license file -->
+ <copy file="GPL.txt" todir="${dist}"/>
+
+ <!-- Copy README file -->
+ <copy file="README.txt" todir="${dist}"/>
+ </target>
+
+ <target name="clean" description="clean up">
+ <!-- Delete the ${build} and ${dist} directory trees -->
+ <delete dir="${classes}"/>
+ <delete dir="${dist}"/>
+ </target>
+</project>
22 server.conf.sample
@@ -0,0 +1,22 @@
+#
+# Sample configuration file
+# Empty lines and lines starting with # and ; are ignored.
+# Format: <key> = <value>
+# Syntactic elements (words) can be separated by linear whitepace.
+#
+
+# server.port
+# listening port
+server.port = 8080
+
+# stream.<streamname>
+# if defined then a stream can be started with this name
+streams.first = true
+
+# stream.<streamname>.password
+# determines the password to accept the stream
+streams.first.password = secret
+
+# stream.<streamname>.limit
+# maximum number of clients for this stream
+streams.first.limit = 100
76 src/ControlledStream.java
@@ -0,0 +1,76 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.util.Vector;
+import threadedevent.EventDispatcher;
+import threadedevent.EventListener;
+import threadedevent.EventQueue;
+
+public class ControlledStream extends Stream {
+
+ private int maxClients;
+ private int numClients;
+
+ private EventDispatcher dispatcher;
+
+ public ControlledStream(int maxClients) {
+ this.maxClients = maxClients;
+ }
+
+ public boolean subscribe(boolean force) {
+ if (force || numClients < maxClients) {
+ numClients++;
+ refresStatus();
+ return true;
+ }
+ return false;
+ }
+
+ public void unsubscribe() {
+ numClients--;
+ refresStatus();
+ }
+
+ protected void refresStatus() {
+ ServerStatusEvent event = new ServerStatusEvent(this);
+ event.setClientCount(numClients);
+ postEvent(event);
+ }
+
+ public EventDispatcher getEventDispatcher() {
+ if (dispatcher == null)
+ bindDispatcher();
+ return dispatcher;
+ }
+
+ private synchronized void bindDispatcher() {
+ if (dispatcher != null)
+ throw new RuntimeException("Listener already bound.");
+
+ EventQueue queue = new EventQueue();
+ dispatcher = new EventDispatcher(queue);
+ setEventQueue(queue);
+
+ // Starting the event dispatcher on a separate thread (it will be
+ // shared thru multiple web clients).
+ dispatcher.start();
+
+ System.out.println("running");
+ }
+}
145 src/EBMLElement.java
@@ -0,0 +1,145 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+class EBMLElement {
+
+ private long id;
+ private long size;
+ private byte[] buffer;
+ private int offset;
+ private int dataOffset;
+
+ protected EBMLElement(byte[] buffer, int offset, int length) {
+ this.buffer = buffer;
+ this.offset = offset;
+
+ long sizeFlag;
+ long num;
+
+ sizeFlag = 0x80;
+ num = 0;
+ while (((num |= buffer[offset++] & 0xff) & sizeFlag) == 0 && sizeFlag != 0) {
+ num <<= 8;
+ sizeFlag <<= 7;
+ }
+
+ id = num;
+
+ sizeFlag = 0x80;
+ num = 0;
+ while (((num |= buffer[offset++] & 0xff) & sizeFlag) == 0 && sizeFlag != 0) {
+ num <<= 8;
+ sizeFlag <<= 7;
+ }
+
+ size = num ^ sizeFlag;
+
+ dataOffset = offset;
+ }
+
+ public static long loadUnsigned(byte[] buffer, int offset, int length) {
+ long num = 0;
+ while (length > 0) {
+ length--;
+ num <<=8;
+ num |= buffer[offset++] & 0xff;
+ }
+
+ return num;
+ }
+
+ public static long loadEBMLUnsigned(byte[] buffer, int offset, int length) {
+
+ long sizeFlag;
+ long num;
+
+ sizeFlag = 0x80;
+ num = 0;
+ while (((num |= buffer[offset++] & 0xff) & sizeFlag) == 0 && sizeFlag != 0) {
+ num <<= 8;
+ sizeFlag <<= 7;
+ }
+
+ return num ^ sizeFlag;
+ }
+
+ public static long loadEBMLSigned(byte[] buffer, int offset, int length) {
+
+ long sizeFlag;
+ long num;
+ long negBits = -1 << 7;
+
+ sizeFlag = 0x80;
+ num = 0;
+ while (((num |= buffer[offset++] & 0xff) & sizeFlag) == 0 && sizeFlag != 0) {
+ num <<= 8;
+ sizeFlag <<= 7;
+ negBits <<= 7;
+ }
+
+ if ((num & sizeFlag >> 1) != 0)
+ num |= negBits;
+
+ return num;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public long getDataSize() {
+ return size;
+ }
+
+ public int getElementSize() {
+ if (size == 0x1ffffffffffffffL)
+ return -1;
+
+ if (size >= 0x100000000l)
+ throw new RuntimeException("Element too long to get array offset.");
+
+ return (int)(dataOffset - offset + size);
+ }
+
+ public byte[] getBuffer() {
+ return buffer;
+ }
+
+ public int getElementOffset() {
+ return offset;
+ }
+
+ public int getDataOffset() {
+ return dataOffset;
+ }
+
+ public int getEndOffset() {
+ if (size == 0x1ffffffffffffffL)
+ return -1;
+
+ if ((dataOffset + size) >= 0x100000000l)
+ throw new RuntimeException("Element too long to get array offset.");
+
+ return (int)(dataOffset + size);
+ }
+
+ public String toString() {
+ return "EBMLElement ID:0x" + Long.toHexString(id) + " size: " + size;
+ }
+}
168 src/EventAnalizer.java
@@ -0,0 +1,168 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Date;
+import java.util.Vector;
+import threadedevent.Event;
+import threadedevent.EventListener;
+import threadedevent.EventDispatcher;
+
+public class EventAnalizer implements EventListener {
+
+ private Stream stream;
+ private EventDispatcher dispatcher;
+ private OutputStream output;
+
+ private boolean runs;
+
+ private TransferAnalizer[] transfers = new TransferAnalizer[10];
+
+ public EventAnalizer(Stream stream, EventDispatcher dispatcher, OutputStream os) {
+ this.stream = stream;
+ this.dispatcher = dispatcher;
+ this.output = os;
+ }
+
+ public void handleEvent(Event event) {
+ if (event instanceof TransferEvent) {
+
+ TransferEvent tEvent = (TransferEvent)event;
+ int type = tEvent.getType();
+
+ if (type >= transfers.length)
+ return;
+
+ TransferAnalizer analizer = transfers[type];
+ if (analizer == null) {
+ analizer = new TransferAnalizer(type);
+ transfers[type] = analizer;
+ }
+ analizer.processTransfer(tEvent);
+ } else {
+ try {
+ output.write(event.toString().getBytes());
+ output.flush();
+ } catch (Exception e) {
+ // socket writing exception (probably client dropped connection)
+ runs = false;
+ }
+ }
+ }
+
+ public void run() {
+ syncTime();
+
+ dispatcher.addListener(this);
+ runs = true;
+
+ while (runs && stream.running()) {
+ try {
+ Thread.sleep(2000);
+ } catch (Exception e) {
+ }
+ }
+
+ dispatcher.removeListener(this);
+ }
+
+ private void syncTime() {
+ StringBuffer sb = new StringBuffer(30);
+ sb.append("{cls:3,time:");
+ sb.append(new Date().getTime());
+ sb.append("},");
+
+ try {
+ output.write(sb.toString().getBytes());
+ output.flush();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ class TransferAnalizer {
+
+ private int id;
+
+ private int[] transfer = new int[40];
+ private int transferOffset = 0;
+ private long transferTime = 0;
+
+ public TransferAnalizer(int id) {
+ this.id = id;
+ }
+
+ public void processTransfer(TransferEvent event) {
+ // event's time in 1/10 sec
+ long eventTime = event.getDate().getTime() / 100;
+
+ // setting transferTime on the first use
+ if (transferTime == 0)
+ transferTime = eventTime;
+
+ // length of data needs to be fushed (buffer space after
+ // transferTime which needs to be reused).
+ int flushLength = Math.min((int)(eventTime - transferTime), transfer.length);
+
+ // making necesary space in the buffer (flushing current content).
+ for (int i=1; i<=flushLength; i++) {
+ int idx = (transferOffset + i) % transfer.length;
+ sendData((transferTime - 40 + i) * 100, transfer[idx]);
+ transfer[idx] = 0;
+ }
+
+ // setting buffer offset to the end of the freed block
+ transferOffset = (transferOffset + flushLength) % transfer.length;
+
+ // setting buffer time to event's time
+ transferTime = eventTime;
+
+ //int graphTime = (int)(event.getDuration() / 100) + 1;
+ int eventDuration = Math.max((int)(event.getDuration() / 100), 1);
+ int graphicDuration = Math.min(eventDuration, transfer.length);
+ int startOffset = (transferOffset - (graphicDuration - 1) + transfer.length) % transfer.length;
+ int avgValue = event.getBytes() / graphicDuration;
+ for (int i=0; i<graphicDuration; i++) {
+ transfer[(startOffset + i + transfer.length) % transfer.length] += avgValue;
+ }
+ }
+
+ private void sendData(long time, int value) {
+ StringBuffer sb = new StringBuffer(30);
+
+ sb.append("{cls:2,type:");
+ sb.append(id);
+ sb.append(",time:");
+ sb.append(time);
+ sb.append(",bytes:");
+ sb.append(value);
+ sb.append("},");
+
+ //System.out.println(sb);
+ try {
+ output.write(sb.toString().getBytes());
+ output.flush();
+ } catch (Exception e) {
+ // socket writing exception (probably client dropped connection)
+ runs = false;
+ }
+ }
+ }
+}
160 src/HeaderDetectionState.java
@@ -0,0 +1,160 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+class HeaderDetectionState implements StreamInputState {
+
+ private static final long ID_EBML = 0x1A45DFA3;
+ private static final long ID_SEGMENT = 0x18538067;
+ private static final long ID_INFO = 0x1549A966;
+ private static final long ID_TRACKS = 0x1654AE6B;
+ private static final long ID_TRACKTYPE = 0x83;
+ private static final long ID_TRACKNUMBER = 0xD7;
+ private static final long TRACK_TYPE_VIDEO = 1;
+
+ private StreamInput input;
+ private Stream stream;
+
+ private static final byte[] infiniteSegment = {0x18, 0x53, (byte)0x80, 0x67, 0x01, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF};
+
+ public HeaderDetectionState(StreamInput input, Stream stream) {
+ this.input = input;
+ this.stream = stream;
+ }
+
+ public int processData(byte[] buffer, int offset, int length) {
+
+ int startOffset = offset;
+ int endOffset = offset + length;
+
+ byte[] headerBuffer = new byte[65536];
+ int headerLength = 0;
+
+ EBMLElement elem;
+
+ // EBML root element
+ elem = new EBMLElement(buffer, offset, length);
+
+ // if not EBML
+ if (elem.getId() != ID_EBML)
+ throw new RuntimeException("First element is not EBML!");
+
+
+ // COPYING: EBML headerBuffer
+ System.arraycopy(buffer, elem.getElementOffset(), headerBuffer, headerLength, elem.getElementSize());
+ headerLength += elem.getElementSize();
+
+ offset = elem.getEndOffset();
+
+ // COPYING: infinite Segment
+ System.arraycopy(infiniteSegment, 0, headerBuffer, headerLength, infiniteSegment.length);
+ headerLength += infiniteSegment.length;
+
+
+ // looking for: Segment
+ do {
+ elem = new EBMLElement(buffer, offset, length);
+ if (elem.getId() == ID_SEGMENT)
+ break;
+ offset = elem.getEndOffset();
+ } while (offset < endOffset);
+
+ // not found
+ if (offset >= endOffset)
+ return startOffset;
+
+
+ System.out.println("Segment found");
+
+ int segmentDataOffset = elem.getDataOffset();
+
+ // looking for: Info
+ offset = segmentDataOffset;
+ do {
+ elem = new EBMLElement(buffer, offset, length);
+ offset = elem.getEndOffset();
+ } while (offset < endOffset && elem.getId() != ID_INFO);
+
+ // not found
+ if (offset >= endOffset)
+ return startOffset;
+
+ System.out.println("Info found");
+
+ // COPYING: Info headerBuffer
+ System.arraycopy(buffer, elem.getElementOffset(), headerBuffer, headerLength, elem.getElementSize());
+ headerLength += elem.getElementSize();
+
+
+ // looking for: Tracks
+ offset = segmentDataOffset;
+ do {
+ elem = new EBMLElement(buffer, offset, length);
+ offset = elem.getEndOffset();
+ } while (offset < endOffset && elem.getId() != ID_TRACKS);
+
+ // not found
+ if (offset >= endOffset)
+ return startOffset;
+
+ System.out.println("Tracks found");
+
+ // COPYING: Tracks headerBuffer
+ System.arraycopy(buffer, elem.getElementOffset(), headerBuffer, headerLength, elem.getElementSize());
+ headerLength += elem.getElementSize();
+
+ // searching for video track's id
+ long videoTrackNumber = 0;
+ int endOfTracks = elem.getEndOffset();
+ offset = elem.getDataOffset();
+ while (offset < endOfTracks) {
+ EBMLElement track = new EBMLElement(buffer, offset, endOfTracks - offset);
+ offset = track.getDataOffset();
+ int endOfTrack = track.getEndOffset();
+
+ long trackType = 0;
+ long trackNumber = 0;
+ while (offset < endOfTrack) {
+ EBMLElement property = new EBMLElement(buffer, offset, endOfTrack - offset);
+ if (property.getId() == ID_TRACKTYPE) {
+ trackType = buffer[property.getDataOffset()] & 0xff;
+ } else if (property.getId() == ID_TRACKNUMBER) {
+ trackNumber = EBMLElement.loadUnsigned(buffer, property.getDataOffset(), (int)property.getDataSize());
+ }
+ offset = property.getEndOffset();
+ }
+ System.out.println("track no: " + trackNumber + ", type: " + trackType);
+ if (trackType == TRACK_TYPE_VIDEO)
+ videoTrackNumber = trackNumber;
+
+ offset = track.getEndOffset();
+ }
+
+ System.out.println("ALL'S WELL");
+
+ // setting header for the stream
+ byte[] header = new byte[headerLength];
+ System.arraycopy(headerBuffer, 0, header, 0, headerLength);
+ stream.setHeader(header);
+
+ // change state
+ input.changeState(new StreamingState(input, stream, videoTrackNumber));
+
+ return segmentDataOffset;
+ }
+}
85 src/Log.java
@@ -0,0 +1,85 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+class Log {
+
+ public static void displaySegment(byte[] buffer, int offset, int maxLength) {
+ final int DISPLAY_MAX_LENGTH = 48;
+
+ String s = "(" + maxLength + ")";
+ for (int i=0; i<Math.min(DISPLAY_MAX_LENGTH, maxLength); i++) {
+ String digit = new String("0" + Integer.toHexString(buffer[offset + i] & 0xff));
+ s += " " + digit.substring(digit.length() - 2);
+ }
+ s += " ";
+ for (int i=0; i<Math.min(DISPLAY_MAX_LENGTH, maxLength); i++) {
+ int num = buffer[offset + i] & 0xff;
+ s += num > 13 ? (char)num : '.';
+ }
+ if (maxLength > DISPLAY_MAX_LENGTH)
+ s += " >>";
+
+ System.out.println(s);
+ }
+
+ public static void reportChunk(byte[] buffer, int offset, int length) {
+
+ int endOffset = offset + length;
+
+ // first NAL Unit
+ for (int i=offset; i<endOffset - 4; i++) {
+ if (buffer[i] == 0 && buffer[i+1] == 1) {
+ offset = i + 2;
+ break;
+ }
+ }
+
+ while (offset <= endOffset - 4) {
+ int startOffset = offset;
+
+ for (int i=offset; i<endOffset - 4; i++) {
+ if (buffer[i + 3] == 1 && buffer[i] == 0 && buffer[i + 1] == 0 && buffer[i + 2] == 0) {
+ offset = i;
+ break;
+ }
+ }
+
+ if (offset == startOffset) {
+ Log.displaySegment(buffer, startOffset, endOffset - startOffset);
+ offset = endOffset;
+ } else {
+ Log.displaySegment(buffer, startOffset, offset - startOffset);
+ }
+
+ offset += 4;
+
+ }
+ }
+
+ public static void displayTime(long time) {
+ float sec = time / 90000f;
+ String s;
+ s = "" + (sec % 60);
+ sec /= 60;
+ s = ((long)sec % 60) + ":" + s;
+ sec /= 60;
+ s = (long)sec + ":" + s;
+ System.out.println(s);
+ }
+}
100 src/MovieFragment.java
@@ -0,0 +1,100 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+class MovieFragment {
+
+ private final int INITIAL_CLUSTER_LENGTH = 10;
+ private final int TIMECODE_LAST_OFFSET = 18;
+ private final int CLUSTER_LENGTH_LAST_OFFSET = 8;
+ private final byte[] clusterHead = {0x1F, 0x43, (byte)0xB6, 0x75, 0x08, 00, 00, 00, 00,
+ (byte)0xe7, (byte)0x88, 00, 00, 00, 00, 00, 00, 00, 00};
+
+ private byte[] data = new byte[1024 * 1024];
+ private int dataLength = 0;
+ private int clusterOffset = -1;
+
+ private int keyframeOffset = -1;
+ private int keyframeLength = -1;
+
+ public MovieFragment() {
+ }
+
+ public void openCluster(long timeCode) {
+
+ if (clusterOffset != -1)
+ closeCluster();
+
+ System.arraycopy(clusterHead, 0, data, dataLength, clusterHead.length);
+ clusterOffset = dataLength;
+ dataLength += clusterHead.length;
+
+ // saving timeCode
+ int offset = clusterOffset + TIMECODE_LAST_OFFSET;
+ long num;
+ num = timeCode;
+ while (num > 0) {
+ data[offset--] = (byte)num;
+ num >>= 8;
+ }
+ }
+
+ public void closeCluster() {
+
+ if (clusterOffset == -1)
+ throw new RuntimeException("No open cluster.");
+
+ // cluster length (including initial TimeCode element)
+ int clusterLength = dataLength - clusterOffset - INITIAL_CLUSTER_LENGTH;
+
+ // saving cluster length to the EBML element's header
+ int offset = clusterOffset + CLUSTER_LENGTH_LAST_OFFSET;
+ int num;
+ num = clusterLength;
+ while (num > 0) {
+ data[offset--] = (byte)num;
+ num >>= 8;
+ }
+
+ clusterOffset = -1;
+ }
+
+ public void appendKeyBlock(byte[] buffer, int offset, int length, int keyframeOffset) {
+ if (keyframeOffset > 0) {
+ this.keyframeOffset = dataLength + (keyframeOffset - offset);
+ this.keyframeLength = length - (keyframeOffset - offset);
+ }
+ appendBlock(buffer, offset, length);
+ }
+
+ public void appendBlock(byte[] buffer, int offset, int length) {
+ if (data.length < dataLength + length)
+ throw new RuntimeException("Buffer full");
+
+ System.arraycopy(buffer, offset, data, dataLength, length);
+ dataLength += length;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public int length() {
+ return dataLength;
+ }
+}
68 src/ServerEvent.java
@@ -0,0 +1,68 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.util.Date;
+import threadedevent.GeneralEvent;
+
+public class ServerEvent extends GeneralEvent {
+
+ public static final int INPUT_START = 1;
+ public static final int INPUT_STOP = 2;
+ public static final int INPUT_FRAGMENT_START = 3;
+ public static final int INPUT_FRAGMENT_END = 4;
+ public static final int INPUT_FIRST_FRAGMENT = 5;
+
+ public static final int CLIET_START = 1001;
+ public static final int CLIET_STOP = 1002;
+ public static final int CLIET_FRAGMENT_START = 1003;
+ public static final int CLIET_FRAGMENT_END = 1004;
+ public static final int CLIET_FRAGMENT_SKIP = 1005;
+
+ private Date startDate;
+
+ private Object sourceStream;
+
+ public ServerEvent(Object source, Object sourceStream, int type) {
+ this(source, sourceStream, type, null);
+ }
+
+ public ServerEvent(Object source, Object sourceStream, int type, Date startDate) {
+ super(source, type);
+ this.sourceStream = sourceStream;
+ this.startDate = startDate;
+ }
+
+ public Object getSourceStreram() {
+ return sourceStream;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer(40);
+ sb.append("{cls:1, type:");
+ sb.append(getType());
+ if (startDate != null) {
+ sb.append(",start:");
+ sb.append(startDate.getTime());
+ }
+ sb.append(",time:");
+ sb.append(getDate().getTime());
+ sb.append("},");
+ return sb.toString();
+ }
+}
53 src/ServerStatusEvent.java
@@ -0,0 +1,53 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.util.Date;
+import threadedevent.GeneralEvent;
+
+public class ServerStatusEvent extends GeneralEvent {
+
+ public static final int DEFAULT_TYPE = 1;
+
+ private int clientCount = -1;
+ private int clientLimit = -1;
+
+ public ServerStatusEvent(Object source) {
+ super(source, DEFAULT_TYPE);
+ }
+
+ public void setClientCount(int newClientCount) {
+ clientCount = newClientCount;
+ }
+
+ public void setClientLimit(int newClientLimit) {
+ clientLimit = newClientLimit;
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer(40);
+ sb.append("{cls:4, clientCount:");
+ sb.append(clientCount);
+ sb.append(", clientLimit:");
+ sb.append(clientLimit);
+ sb.append(",time:");
+ sb.append(getDate().getTime());
+ sb.append("},");
+ return sb.toString();
+ }
+}
62 src/Stream.java
@@ -0,0 +1,62 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.net.*;
+import threadedevent.GeneralEventProducer;
+
+class Stream extends GeneralEventProducer {
+
+ private MovieFragment fragment;
+ private int fragmentAge;
+
+ private byte[] header;
+
+ private boolean runs = true;
+
+ public synchronized boolean running() {
+ return runs;
+ }
+
+ public synchronized void stop() {
+ runs = false;
+ }
+
+ public synchronized int getFragmentAge() {
+ return fragmentAge;
+ }
+
+ public synchronized MovieFragment getFragment() {
+ return fragment;
+ }
+
+ public synchronized byte[] getHeader() {
+ return header;
+ }
+
+ public synchronized void setHeader(byte[] newHeader) {
+ header = newHeader;
+ }
+
+ public synchronized void pushFragment(MovieFragment newFragment) {
+ if (fragmentAge == 0)
+ postEvent(new ServerEvent(this, this, ServerEvent.INPUT_FIRST_FRAGMENT));
+ fragment = newFragment;
+ fragmentAge++;
+ }
+}
154 src/StreamClient.java
@@ -0,0 +1,154 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.net.*;
+import java.io.*;
+import java.util.Date;
+import threadedevent.GeneralEventProducer;
+
+class StreamClient {
+
+ private Stream stream;
+ private OutputStream output;
+
+ private boolean runs = true;
+
+ private long offset = 0;
+ private int fragmentSequence = 1;
+
+ public StreamClient(Stream stream, OutputStream output) {
+ this.stream = stream;
+ this.output = output;
+ }
+
+ public void run() {
+
+ // report the starting of this client
+ stream.postEvent(new ServerEvent(this, stream, ServerEvent.CLIET_START));
+
+ // waiting for header
+ byte[] header = stream.getHeader();
+ while (header == null && stream.running()) {
+
+ // sleeping 200 ms
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+
+ header = stream.getHeader();
+ }
+
+ // writing header
+ try {
+ output.write(header);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+
+ /* Web browsers may try to detect some features of the server and open
+ * multiple connections. They may close the connection eventually so
+ * we wait for a little bit to ensure this request is serious. (And the
+ * client needs more than just the header).
+ */
+
+ // sleeping 1000 ms
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ }
+
+ // sending fragments
+ int myAge = 0;
+ while (runs && stream.running()) {
+
+ // while there is a new fragment is available
+ int streamAge;
+ while (runs && stream.running() && myAge < (streamAge = stream.getFragmentAge())) {
+
+ // notification if a fragment was skipped
+ if (myAge > 0 && streamAge - myAge > 1)
+ stream.postEvent(new ServerEvent(this, stream, ServerEvent.CLIET_FRAGMENT_SKIP));
+
+ myAge = streamAge;
+
+ // getting current movie fragment
+ MovieFragment fragment = stream.getFragment();
+
+ // send the fragment data to the client
+ try {
+ // writing the data for one shot (less cpu usage)
+ //output.write(fragment.getData(), 0, fragment.length());
+
+ /*
+ * Writing the data in packets (cunks of arbitrary size) to
+ * get a better time-resolution for the outputbandwidth
+ * used.
+ */
+
+ final int PACKET_SIZE = 24 * 1024;
+ int fragLength = fragment.length();
+ int offset = 0;
+ while (offset < fragLength) {
+
+ // current packet size
+ int length = fragLength - offset;
+ if (length >= 1.5 * PACKET_SIZE)
+ length = PACKET_SIZE;
+
+ // starting time of the transfer
+ long transferStart = new Date().getTime();
+
+ // writing data packet
+ output.write(fragment.getData(), offset, length);
+
+ // notification about the transfer
+ stream.postEvent(new TransferEvent(this, stream, TransferEvent.STREAM_OUTPUT, length, new Date().getTime() - transferStart));
+
+ // next packet (chunk) start
+ offset += length;
+ }
+
+ } catch (Exception e) {
+
+ // closed connection
+ runs = false;
+ }
+ }
+
+ // currently no new fragment, sleeping 200 ms
+ try {
+ Thread.sleep(200);
+ } catch (Exception e) {
+ }
+
+ }
+
+ // report the stopping of thes client
+ stream.postEvent(new ServerEvent(this, stream, ServerEvent.CLIET_STOP));
+
+ try {
+ output.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
101 src/StreamInput.java
@@ -0,0 +1,101 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.net.*;
+import java.io.*;
+import java.util.Date;
+import threadedevent.EventQueue;
+import threadedevent.GeneralEventProducer;
+
+class StreamInput {
+
+ private Stream stream;
+ private InputStream input;
+ private boolean runs;
+
+ private byte[] header;
+
+ private StreamInputState currentState;
+
+ public StreamInput(Stream stream, InputStream input) {
+ this.stream = stream;
+ this.input = input;
+ }
+
+ public void run() {
+ runs = true;
+
+ // notification about starting the input process
+ stream.postEvent(new ServerEvent(this, stream, ServerEvent.INPUT_START));
+
+ changeState(new HeaderDetectionState(this, stream));
+
+ byte[] buffer = new byte[65535];
+ int offset = 0;
+ int length = 0;
+ while (runs && stream.running()) {
+
+ try {
+
+ // starting time of the transfer
+ long transferStart = new Date().getTime();
+
+ // reading data
+ int red = input.read(buffer, offset, buffer.length - offset);
+
+ // notification about the transfer
+ stream.postEvent(new TransferEvent(this, stream, TransferEvent.STREAM_INPUT, red, new Date().getTime() - transferStart));
+
+ if (red == -1)
+ runs = false;
+ length += red;
+
+ int newOffset = currentState.processData(buffer, 0, length);
+ if (newOffset < offset + length) {
+ length = length - newOffset;
+ System.arraycopy(buffer, newOffset, buffer, 0, length);
+ offset = length;
+ } else {
+ length = 0;
+ offset = 0;
+ }
+
+ } catch (SocketTimeoutException e) {
+ continue;
+ } catch (Exception e) {
+ e.printStackTrace();
+ runs = false;
+ }
+ }
+
+ try {
+ input.close();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // notification about ending the input process
+ stream.postEvent(new ServerEvent(this, stream, ServerEvent.INPUT_STOP));
+ }
+
+ public void changeState(StreamInputState newState) {
+ currentState = newState;
+ }
+
+}
22 src/StreamInputState.java
@@ -0,0 +1,22 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+interface StreamInputState {
+ public int processData(byte[] buffer, int offset, int length);
+}
281 src/StreamingServer.java
@@ -0,0 +1,281 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import threadedevent.EventDispatcher;
+import org.czentral.minihttp.*;
+
+class StreamingServer {
+
+ // configuration settings
+ private static Map<String, String> settings = new HashMap<String, String>(20);
+
+ // streams by name
+ private static Map<String, ControlledStream> streams = new HashMap<String, ControlledStream>();
+
+ public static void main(String args[]) {
+
+ // chacking args
+ if (args.length < 1) {
+ System.err.println("Usage: java StreamingServer <configfile>");
+ System.exit(1);
+ }
+
+ // loading config
+ try {
+ String filename = args[0];
+ BufferedReader reader = new BufferedReader(new FileReader(filename));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.length() == 0 || line.startsWith("#") || line.startsWith(";"))
+ continue;
+
+ int pos = line.indexOf("=");
+ if (pos == -1)
+ throw new RuntimeException("Config lines must vahe a <key> = <value> syntax.");
+
+ settings.put(line.substring(0, pos).trim(), line.substring(pos + 1).trim());
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ // instantiating and running the server
+ new StreamingServer().run();
+ }
+
+ private static void loadConfig(String filename) {
+ }
+
+ public void run() {
+ String serverPort = settings.get("server.port");
+ System.out.println("Stating server on port: " + serverPort);
+ MiniHTTP server = new MiniHTTP(Integer.parseInt(serverPort));
+
+ PublisherResource publish = new PublisherResource();
+ server.registerResource("/publish", publish);
+
+ ConsumerResource consume = new ConsumerResource();
+ server.registerResource("/consume", consume);
+
+ InfoResource info = new InfoResource();
+ server.registerResource("/info", info);
+
+ /*
+ FileResource script = new FileResource("script.js");
+ server.registerResource("/script.js", script);
+ */
+
+ try {
+ HTTPZipResource console = new HTTPZipResource("console.zip");
+ server.registerResource("/console", console);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ server.start();
+ }
+
+ class PublisherResource implements HTTPResource {
+
+ public void serve(HTTPRequest request, HTTPResponse response) throws HTTPException {
+
+ // the part of the request path after the resource's path
+ int resLength = request.getResourcePath().length();
+ String requestPath = request.getPathName();
+ if (requestPath.length() - resLength <= 1)
+ throw new HTTPException(404, "No Stream ID Specified");
+
+ // stream ID
+ String streamID = requestPath.substring(resLength + 1);
+
+ // is a stream with that name defined?
+ if (settings.get("streams." + streamID) == null)
+ throw new HTTPException(403, "Stream Not Registered");
+
+ // check password
+ String requestPassword = request.getParameter("password");
+ if (requestPassword == null)
+ throw new HTTPException(403, "Authentication failed: No password");
+ if (!requestPassword.equals(settings.get("streams." + streamID + ".password")))
+ throw new HTTPException(403, "Authentication failed: Wrong password");
+
+ // stopping stream if already running
+ ControlledStream stream = streams.get(streamID);
+ if (stream != null) {
+ stream.stop();
+ }
+
+ // creating new Sream instance
+ stream = new ControlledStream(Integer.parseInt(settings.get("streams." + streamID + ".limit")));
+
+ // put stream to in the collection
+ streams.put(streamID, stream);
+
+ // setting socket parameters
+ Socket sock = request.getSocket();
+ try {
+ sock.setSoTimeout(100);
+ sock.setReceiveBufferSize(256 * 1024);
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot set socket parameters", e);
+ }
+
+ // creating input reader
+ StreamInput streamInput = new StreamInput(stream, request.getInputStream());
+
+ /*
+ * Start publishing
+ */
+
+ // this thread is working (will be blocked) while the stream is being published
+ streamInput.run();
+
+ /*
+ * End of publishing
+ */
+
+ // stopping stream (clients get notified)
+ stream.stop();
+
+ }
+ }
+
+ class ConsumerResource implements HTTPResource {
+
+ private final String STR_CONTENT_TYPE = "Content-type";
+
+ public void serve(HTTPRequest request, HTTPResponse response) throws HTTPException {
+ // the part of the path after the resource's path
+ int resLength = request.getResourcePath().length();
+ String requestPath = request.getPathName();
+ if (requestPath.length() - resLength <= 1)
+ throw new HTTPException(400, "No Stream ID Specified");
+
+ // Stream ID
+ String streamID = requestPath.substring(resLength + 1);
+
+ // is a stream with that Stream ID defined?
+ if (settings.get("streams." + streamID) == null)
+ throw new HTTPException(404, "Stream Not Registered");
+
+ // getting stream
+ ControlledStream stream = streams.get(streamID);
+ if (stream == null || !stream.running())
+ throw new HTTPException(503, "Stream Not Running");
+
+ // check if there are free slots
+ if (!stream.subscribe(false))
+ throw new HTTPException(503, "Resource Busy");
+
+ // setting rsponse content-type
+ response.setParameter(STR_CONTENT_TYPE, "video/webm");
+
+ // create a client
+ StreamClient client = new StreamClient(stream, response.getOutputStream());
+
+ // serving the request (from this thread)
+ client.run();
+
+ // freeing the slot
+ stream.unsubscribe();
+
+ }
+ }
+
+ class InfoResource implements HTTPResource {
+
+ private final String STR_CONTENT_TYPE = "Content-type";
+
+ public void serve(HTTPRequest request, HTTPResponse response) throws HTTPException {
+
+ System.out.println(0);
+
+ // the part of the path after the resource's path
+ int resLength = request.getResourcePath().length();
+ String requestPath = request.getPathName();
+ if (requestPath.length() - resLength <= 1)
+ throw new HTTPException(400, "No Stream ID Specified");
+
+ // stream ID
+ String streamID = requestPath.substring(resLength + 1);
+
+ // is a stream with that name defined?
+ if (settings.get("streams." + streamID) == null)
+ throw new HTTPException(404, "Stream Not Registered");
+
+ // check password
+ String requestPassword = request.getParameter("password");
+ if (requestPassword == null)
+ throw new HTTPException(403, "Authentication failed: No password");
+ if (!requestPassword.equals(settings.get("streams." + streamID + ".password")))
+ throw new HTTPException(403, "Authentication failed: Wrong password");
+
+ // getting stream
+ ControlledStream stream = streams.get(streamID);
+ if (stream == null || !stream.running())
+ throw new HTTPException(503, "Stream Not Running");
+
+ // setting rsponse content-type
+ response.setParameter(STR_CONTENT_TYPE, "text/plain");
+
+ System.out.println(1);
+
+ // get evet dispatcher (if no current one then it is created)
+ EventDispatcher dispatcher = stream.getEventDispatcher();
+
+ System.out.println(2);
+
+ // creating analizer
+ EventAnalizer analizer = new EventAnalizer(stream, dispatcher, response.getOutputStream());
+
+ // serving the request (from this thread)
+ analizer.run();
+
+ }
+ }
+
+ class FileResource implements HTTPResource {
+
+ private String fileName;
+
+ public FileResource(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public void serve(HTTPRequest request, HTTPResponse response) throws HTTPException {
+ try {
+ File headFile = new File(fileName);
+ FileInputStream fis = new FileInputStream(headFile);
+ byte[] data = new byte[(int)headFile.length()];
+ fis.read(data, 0, data.length);
+ fis.close();
+
+ response.getOutputStream().write(data);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+ }
+
+}
+
143 src/StreamingState.java
@@ -0,0 +1,143 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+class StreamingState implements StreamInputState {
+
+ private static final long ID_CLUSTER = 0x1F43B675;
+ private static final long ID_SIMPLEBLOCK = 0xA3;
+ private static final long ID_TIMECODE = 0xE7;
+
+ private static final long MINIMAL_FRAGMENT_LENGTH = 100 * 1024;
+
+ private long clusterTimeCode = 0;
+
+ private StreamInput input;
+ private Stream stream;
+ private long videoTrackNumber;
+
+ private MovieFragment fragment;
+
+ public StreamingState(StreamInput input, Stream stream, long videoTrackNumber) {
+ this.input = input;
+ this.stream = stream;
+ this.videoTrackNumber = videoTrackNumber;
+ fragment = new MovieFragment();
+ }
+
+ public int processData(byte[] buffer, int offset, int length) {
+
+ int endOffset = offset + length;
+ EBMLElement elem;
+
+ while (offset < endOffset - 12) {
+ elem = new EBMLElement(buffer, offset, length);
+ //System.out.println(elem);
+
+ // clusters do not need to be fully loaded, so that is an exception
+ if (elem.getEndOffset() > endOffset && elem.getId() != ID_CLUSTER) {
+
+ /* The element is not fully loaded: we need more data, so we end
+ * this processing cycle. The StreamInput will fill the buffer
+ * and take care of the yet unprocessed element. We signal this
+ * by sending the element's offset back. The StreamInput will
+ * keep the data beyound this offset and the next processing cycle
+ * will begin with this element.
+ */
+ return elem.getElementOffset();
+ }
+
+ /* Timecode for this cluster. We use a flat processing model (we do
+ * not care about the nesting of elements), so this timecode will
+ * live until we get the next one. It will be the Timecode element
+ * of the next Cluster (according to standard).
+ */
+ if (elem.getId() == ID_TIMECODE) {
+
+ // we have the timecode, so open a new cluster in our movie fragment
+ clusterTimeCode = EBMLElement.loadUnsigned(buffer, elem.getDataOffset(), (int)elem.getDataSize());
+ //System.out.println("tc: " + clusterTimeCode);
+
+ // cluster opened
+ fragment.openCluster(clusterTimeCode);
+
+ } else if (elem.getId() == ID_SIMPLEBLOCK) {
+
+ // sample (video or audio) recieved
+
+ int trackNum = buffer[elem.getDataOffset()] & 0xff;
+ if ((trackNum & 0x80) == 0)
+ throw new RuntimeException("Track numbers > 127 are not implemented.");
+ trackNum ^= 0x80;
+
+ //System.out.print(trackNum + " ");
+
+ // the offset of a video keyframe or -1
+ int videoKeyOffset = -1;
+
+ // check if this is a video frame
+ if (trackNum == videoTrackNumber) {
+ //System.out.print("video ");
+
+ int flags = buffer[elem.getDataOffset() + 3] & 0xff;
+ if ((flags & 0x80) != 0) {
+ // keyframe
+
+ if (fragment.length() >= MINIMAL_FRAGMENT_LENGTH) {
+
+ // closing current cluster (of the curent fragment)
+ fragment.closeCluster();
+
+ // send the complete fragment to the stream coordinator
+ stream.pushFragment(fragment);
+
+ // create a new fragment
+ fragment = new MovieFragment();
+
+ // set up new fragment's timecode
+ fragment.openCluster(clusterTimeCode);
+
+ // notification about starting the input process
+ stream.postEvent(new ServerEvent(input, stream, ServerEvent.INPUT_FRAGMENT_START));
+
+ if ((flags & 0x60) == 0) {
+ // no lacing
+ videoKeyOffset = elem.getDataOffset() + 4;
+ }
+ }
+ }
+ }
+
+ // saving the block
+ //fragment.appendBlock(buffer, elem.getElementOffset(), elem.getElementSize());
+ fragment.appendKeyBlock(buffer, elem.getElementOffset(), elem.getElementSize(), videoKeyOffset);
+
+ //System.out.println();
+
+ } // end: ID_SIMPLEBLOCK
+
+ if (elem.getId() == ID_CLUSTER || elem.getDataSize() >= 0x100000000L) {
+ offset = elem.getDataOffset();
+ } else {
+ offset = elem.getEndOffset();
+ }
+ }
+
+ return offset;
+ }
+}
41 src/TransferEvent.java
@@ -0,0 +1,41 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+public class TransferEvent extends ServerEvent {
+
+ public static final int STREAM_INPUT = 1;
+ public static final int STREAM_OUTPUT = 2;
+
+ private int bytes;
+ private long duration;
+
+ public TransferEvent(Object source, Object sourceStream, int type, int bytes, long duration) {
+ super(source, sourceStream, type);
+ this.bytes = bytes;
+ this.duration = duration;
+ }
+
+ public int getBytes() {
+ return bytes;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+}
122 src/org/czentral/minihttp/ChunkedInputStream.java
@@ -0,0 +1,122 @@
+/*
+ This file is part of "stream.m" software, a video broadcasting tool
+ compatible with Google's WebM format.
+ Copyright (C) 2011 Varga Bence
+
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+package org.czentral.minihttp;
+
+import java.io.*;
+
+/**
+ * On-the-fly decoding of chunked transfer-encoded data from an InputStream.
+ */
+public class ChunkedInputStream extends InputStream {
+
+ private final int BUFFER_SIZE = 32 * 1024;
+ private final int CHUNK_HEAD_MAX_SIZE = 10;
+
+ private InputStream input;
+ private byte[] buffer;
+ private int bufferOffset;
+ private int bufferLength;
+
+ private int chunkLength;
+
+ /**
+ * Constructs an object with a buffer of previously red data and an
+ * InputStream to read the rest of the data from.
+ */
+ public ChunkedInputStream(byte[] data, int offset, int length, InputStream is) {
+ buffer = new byte[BUFFER_SIZE];
+ System.arraycopy(data, offset, buffer, 0, length);
+ bufferLength = length;
+ chunkLength = 0;
+ this.input = is;
+ }
+
+ public int read() throws IOException {
+ byte[] shortBuffer = new byte[1];
+ read(shortBuffer, 0, 1);
+ return shortBuffer[0];
+ }
+
+ public int read(byte[] data) throws IOException {
+ return read(data, 0, data.length);
+ }
+
+ public int read(byte[] data, int offset, int length) throws IOException {
+
+ // new chunk start
+ if (chunkLength == 0) {
+
+ // reading to the buffer until a header can be parsed
+ int headLength = refreshChunkSize(buffer, bufferOffset, bufferLength);
+ while (headLength == -1) {
+ // chunk header not fully loaded
+ // ToDo: make space in the buffer if needed